22Celery integration example for the PostHog Python SDK.
33
44Demonstrates how to use ``PosthogCeleryIntegration`` with:
5- - producer-side instrumentation (publishing events and context propagation)
6- - worker-side instrumentation via ``worker_process_init`` (prefork-safe)
5+ - producer-side and worker-side instrumentation (publishing events and context propagation)
76- context propagation (distinct ID, session ID, tags) from producer to worker
87- task lifecycle events (published, started, success, failure, retry)
98- exception capture from failed tasks
109- ``task_filter`` customization hook
1110
1211Setup:
13- 1. Update POSTHOG_PROJECT_API_KEY and POSTHOG_HOST here with your credentials
14- (environment variables won't work as it's better if Celery forks worker into
15- separate process for the example to prove context propagation)
12+ 1. Set ``POSTHOG_PROJECT_API_KEY`` and ``POSTHOG_HOST`` in your environment
1613 2. Install dependencies: pip install posthog celery redis
1714 3. Start Redis: redis-server
1815 4. Start the worker: celery -A examples.celery_integration worker --loglevel=info
1916 5. Run the producer: python -m examples.celery_integration
2017"""
2118
19+ import os
2220import time
2321from typing import Any , Optional
2422
2523from celery import Celery
2624from celery .signals import worker_process_init , worker_process_shutdown
2725
2826import posthog
29- from posthog .client import Client
3027from posthog .integrations .celery import PosthogCeleryIntegration
3128
3229
3330# --- Configuration ---
3431
35- POSTHOG_PROJECT_API_KEY = " phc_..."
36- POSTHOG_HOST = " http://localhost:8000"
32+ POSTHOG_PROJECT_API_KEY = os . getenv ( "POSTHOG_PROJECT_API_KEY" , " phc_...")
33+ POSTHOG_HOST = os . getenv ( "POSTHOG_HOST" , " http://localhost:8000")
3734
3835app = Celery (
3936 "examples.celery_integration" ,
4340
4441# --- Integration wiring ---
4542
46- def create_client () -> Client :
47- return Client (
48- project_api_key = POSTHOG_PROJECT_API_KEY ,
49- host = POSTHOG_HOST
50- )
43+ def configure_posthog () -> None :
44+ posthog . api_key = POSTHOG_PROJECT_API_KEY
45+ posthog . host = POSTHOG_HOST
46+ posthog . enable_local_evaluation = False # to not require personal_api_key for this example
47+ posthog . setup ( )
5148
5249
5350def task_filter (task_name : Optional [str ], task_properties : dict [str , Any ]) -> bool :
@@ -56,40 +53,42 @@ def task_filter(task_name: Optional[str], task_properties: dict[str, Any]) -> bo
5653 return True
5754
5855
59- def create_integration (client : Client ) -> PosthogCeleryIntegration :
56+ def create_integration () -> PosthogCeleryIntegration :
6057 return PosthogCeleryIntegration (
61- client = client ,
6258 capture_exceptions = True ,
6359 capture_task_lifecycle_events = True ,
6460 propagate_context = True ,
6561 task_filter = task_filter ,
6662 )
6763
68-
69- # Worker process setup.
70- # Celery's default prefork pool runs tasks in child processes, so initialize
71- # PostHog per child using worker_process_init.
64+ configure_posthog ()
65+ integration = create_integration ()
66+ integration .instrument ()
7267
7368
69+ # --- Worker process setup ---
70+ # Celery's default prefork pool runs tasks in child processes. This example
71+ # runs on a single host, so the inherited PostHog client and Celery
72+ # integration are fork-safe and do not need to be recreated in each child.
73+ # If workers run across multiple hosts, configure PostHog and instrument a
74+ # worker-local integration in worker_process_init.
7475@worker_process_init .connect
7576def on_worker_process_init (** kwargs ) -> None :
76- worker_posthog_client = create_client ()
77- worker_integration = create_integration ( worker_posthog_client )
78- worker_integration . instrument ()
79-
80- app . _posthog_client = worker_posthog_client
81- app . _posthog_integration = worker_integration
77+ # global integration
78+
79+ # configure_posthog ()
80+ # integration = create_integration()
81+ # integration.instrument()
82+ return
8283
8384
85+ # Use this signal to shutdown the integration and PostHog client
86+ # Calling shutdown() is important to flush any pending events
8487@worker_process_shutdown .connect
8588def on_worker_process_shutdown (** kwargs ) -> None :
86- worker_integration = getattr (app , "_posthog_integration" , None )
87- if worker_integration :
88- worker_integration .uninstrument ()
89+ integration .shutdown ()
90+ posthog .shutdown ()
8991
90- worker_posthog_client = getattr (app , "_posthog_client" , None )
91- if worker_posthog_client :
92- worker_posthog_client .shutdown ()
9392
9493# --- Example tasks ---
9594
@@ -98,8 +97,8 @@ def health_check() -> dict[str, str]:
9897 return {"status" : "ok" }
9998
10099
101- @app .task (bind = True , max_retries = 3 )
102- def process_order (self , order_id : str ) -> dict :
100+ @app .task (max_retries = 3 )
101+ def process_order (order_id : str ) -> dict :
103102 """A task that processes an order successfully."""
104103
105104 # simulate work
@@ -108,7 +107,7 @@ def process_order(self, order_id: str) -> dict:
108107 # Custom event inside the task - context tags propagated from the
109108 # producer (e.g. "source", "release") should appear on this event
110109 # and this should be attributed to the correct distinct ID and session.
111- app . _posthog_client .capture (
110+ posthog .capture (
112111 "celery example order processed" ,
113112 properties = {"order_id" : order_id , "amount" : 99.99 },
114113 )
@@ -136,17 +135,13 @@ def failing_task() -> None:
136135# --- Producer code ---
137136
138137if __name__ == "__main__" :
139- posthog_client = create_client ()
140- integration = create_integration (posthog_client )
141- integration .instrument ()
142-
143138 print ("PostHog Celery Integration Example" )
144139 print ("=" * 40 )
145140 print ()
146141
147142 # Set up PostHog context before dispatching tasks.
148143 # The integration propagates this context to workers via task headers.
149- with posthog .new_context (fresh = True , client = posthog_client ):
144+ with posthog .new_context (fresh = True ):
150145 posthog .identify_context ("user-123" )
151146 posthog .set_context_session ("session-user-123-abc" )
152147 posthog .tag ("source" , "celery_integration_example_script" )
@@ -186,6 +181,5 @@ def failing_task() -> None:
186181 print ("Tasks dispatched. Check your Celery worker logs and PostHog for events." )
187182 print ()
188183
189- posthog_client .flush ()
190- integration .uninstrument ()
191- posthog_client .shutdown ()
184+ integration .shutdown ()
185+ posthog .shutdown ()
0 commit comments