88from opentelemetry import trace as otel_trace_api
99from opentelemetry .sdk .resources import Resource
1010from opentelemetry .sdk .trace import TracerProvider
11+ from opentelemetry .sdk .trace .sampling import Decision , TraceIdRatioBased
1112
1213from langfuse ._task_manager .media_manager import MediaManager
1314from langfuse ._task_manager .media_upload_consumer import MediaUploadConsumer
@@ -47,6 +48,7 @@ def __new__(
4748 flush_interval : Optional [float ] = None ,
4849 httpx_client : Optional [httpx .Client ] = None ,
4950 media_upload_thread_count : Optional [int ] = None ,
51+ sample_rate : Optional [float ] = None ,
5052 ) -> "LangfuseTracer" :
5153 if public_key in cls ._instances :
5254 return cls ._instances [public_key ]
@@ -66,6 +68,7 @@ def __new__(
6668 flush_interval = flush_interval ,
6769 httpx_client = httpx_client ,
6870 media_upload_thread_count = media_upload_thread_count ,
71+ sample_rate = sample_rate ,
6972 )
7073
7174 cls ._instances [public_key ] = instance
@@ -85,10 +88,11 @@ def _initialize_instance(
8588 flush_interval : Optional [float ] = None ,
8689 media_upload_thread_count : Optional [int ] = None ,
8790 httpx_client : Optional [httpx .Client ] = None ,
91+ sample_rate : Optional [float ] = None ,
8892 ):
8993 # OTEL Tracer
9094 tracer_provider = _init_tracer_provider (
91- environment = environment , release = release
95+ environment = environment , release = release , sample_rate = sample_rate
9296 )
9397
9498 langfuse_processor = LangfuseSpanProcessor (
@@ -191,6 +195,14 @@ def _initialize_instance(
191195 # Register shutdown handler
192196 atexit .register (self .shutdown )
193197
198+ langfuse_logger .info (
199+ f"Initialized Langfuse tracer with "
200+ f"public_key={ public_key } , "
201+ f"host={ host } , "
202+ f"environment={ environment } , "
203+ f"sample_rate={ sample_rate } "
204+ )
205+
194206 def _fetch_project_id_background (self ):
195207 try :
196208 projects = self .api .projects .get (
@@ -231,7 +243,22 @@ def project_id(self):
231243
232244 def add_score_task (self , event : dict ):
233245 try :
234- self ._score_ingestion_queue .put (event , block = False )
246+ # Sample scores with the same sampler that is used for tracing
247+ tracer_provider = cast (TracerProvider , otel_trace_api .get_tracer_provider ())
248+ should_sample = (
249+ tracer_provider .sampler .should_sample (
250+ parent_context = None ,
251+ trace_id = int (event ["body" ].trace_id , 16 ),
252+ name = "score" ,
253+ ).decision
254+ == Decision .RECORD_AND_SAMPLE
255+ if hasattr (event ["body" ], "trace_id" )
256+ else True
257+ )
258+
259+ if should_sample :
260+ langfuse_logger .debug (f"Enqueuing score event: { event } " )
261+ self ._score_ingestion_queue .put (event , block = False )
235262
236263 except Full :
237264 langfuse_logger .warning ("Score ingestion queue is full" )
@@ -313,6 +340,7 @@ def _init_tracer_provider(
313340 * ,
314341 environment : Optional [str ] = None ,
315342 release : Optional [str ] = None ,
343+ sample_rate : Optional [float ] = None ,
316344) -> TracerProvider :
317345 environment = environment or os .environ .get (LANGFUSE_TRACING_ENVIRONMENT )
318346 release = release or os .environ .get (LANGFUSE_RELEASE ) or get_common_release_envs ()
@@ -330,7 +358,10 @@ def _init_tracer_provider(
330358 default_provider = cast (TracerProvider , otel_trace_api .get_tracer_provider ())
331359
332360 if isinstance (default_provider , otel_trace_api .ProxyTracerProvider ):
333- provider = TracerProvider (resource = resource )
361+ provider = TracerProvider (
362+ resource = resource ,
363+ sampler = TraceIdRatioBased (sample_rate ) if sample_rate else None ,
364+ )
334365 otel_trace_api .set_tracer_provider (provider )
335366
336367 else :
0 commit comments