Skip to content

Commit 0ce92db

Browse files
authored
feat(client): allow filtering spans by instrumentation scope name (#1223)
1 parent 60d8499 commit 0ce92db

File tree

4 files changed

+326
-12
lines changed

4 files changed

+326
-12
lines changed

langfuse/_client/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class Langfuse:
102102
media_upload_thread_count (Optional[int]): Number of background threads for handling media uploads. Defaults to 1. Can also be set via LANGFUSE_MEDIA_UPLOAD_THREAD_COUNT environment variable.
103103
sample_rate (Optional[float]): Sampling rate for traces (0.0 to 1.0). Defaults to 1.0 (100% of traces are sampled). Can also be set via LANGFUSE_SAMPLE_RATE environment variable.
104104
mask (Optional[MaskFunction]): Function to mask sensitive data in traces before sending to the API.
105+
blocked_instrumentation_scopes (Optional[List[str]]): List of instrumentation scope names to block from being exported to Langfuse. Spans from these scopes will be filtered out before being sent to the API. Useful for filtering out spans from specific libraries or frameworks. For exported spans, you can see the instrumentation scope name in the span metadata in Langfuse (`metadata.scope.name`)
105106
106107
Example:
107108
```python
@@ -159,6 +160,7 @@ def __init__(
159160
media_upload_thread_count: Optional[int] = None,
160161
sample_rate: Optional[float] = None,
161162
mask: Optional[MaskFunction] = None,
163+
blocked_instrumentation_scopes: Optional[List[str]] = None,
162164
):
163165
self._host = host or os.environ.get(LANGFUSE_HOST, "https://cloud.langfuse.com")
164166
self._environment = environment or os.environ.get(LANGFUSE_TRACING_ENVIRONMENT)
@@ -220,6 +222,7 @@ def __init__(
220222
sample_rate=sample_rate,
221223
mask=mask,
222224
tracing_enabled=self._tracing_enabled,
225+
blocked_instrumentation_scopes=blocked_instrumentation_scopes,
223226
)
224227
self._mask = self._resources.mask
225228

langfuse/_client/resource_manager.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import os
1919
import threading
2020
from queue import Full, Queue
21-
from typing import Any, Dict, Optional, cast
21+
from typing import Any, Dict, List, Optional, cast
2222

2323
import httpx
2424
from opentelemetry import trace as otel_trace_api
@@ -93,6 +93,7 @@ def __new__(
9393
sample_rate: Optional[float] = None,
9494
mask: Optional[MaskFunction] = None,
9595
tracing_enabled: Optional[bool] = None,
96+
blocked_instrumentation_scopes: Optional[List[str]] = None,
9697
) -> "LangfuseResourceManager":
9798
if public_key in cls._instances:
9899
return cls._instances[public_key]
@@ -117,6 +118,7 @@ def __new__(
117118
tracing_enabled=tracing_enabled
118119
if tracing_enabled is not None
119120
else True,
121+
blocked_instrumentation_scopes=blocked_instrumentation_scopes,
120122
)
121123

122124
cls._instances[public_key] = instance
@@ -139,6 +141,7 @@ def _initialize_instance(
139141
sample_rate: Optional[float] = None,
140142
mask: Optional[MaskFunction] = None,
141143
tracing_enabled: bool = True,
144+
blocked_instrumentation_scopes: Optional[List[str]] = None,
142145
):
143146
self.public_key = public_key
144147
self.secret_key = secret_key
@@ -159,6 +162,7 @@ def _initialize_instance(
159162
timeout=timeout,
160163
flush_at=flush_at,
161164
flush_interval=flush_interval,
165+
blocked_instrumentation_scopes=blocked_instrumentation_scopes,
162166
)
163167
tracer_provider.add_span_processor(langfuse_processor)
164168

langfuse/_client/span_processor.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import base64
1515
import os
16-
from typing import Optional
16+
from typing import List, Optional
1717

1818
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
1919
from opentelemetry.sdk.trace import ReadableSpan
@@ -34,10 +34,11 @@ class LangfuseSpanProcessor(BatchSpanProcessor):
3434
3535
This processor extends OpenTelemetry's BatchSpanProcessor with Langfuse-specific functionality:
3636
1. Project-scoped span filtering to prevent cross-project data leakage
37-
2. Configurable batch processing parameters for optimal performance
38-
3. HTTP-based span export to the Langfuse OTLP endpoint
39-
4. Debug logging for span processing operations
40-
5. Authentication with Langfuse API using Basic Auth
37+
2. Instrumentation scope filtering to block spans from specific libraries/frameworks
38+
3. Configurable batch processing parameters for optimal performance
39+
4. HTTP-based span export to the Langfuse OTLP endpoint
40+
5. Debug logging for span processing operations
41+
6. Authentication with Langfuse API using Basic Auth
4142
4243
The processor is designed to efficiently handle large volumes of spans with
4344
minimal overhead, while ensuring spans are only sent to the correct project.
@@ -54,8 +55,14 @@ def __init__(
5455
timeout: Optional[int] = None,
5556
flush_at: Optional[int] = None,
5657
flush_interval: Optional[float] = None,
58+
blocked_instrumentation_scopes: Optional[List[str]] = None,
5759
):
5860
self.public_key = public_key
61+
self.blocked_instrumentation_scopes = (
62+
blocked_instrumentation_scopes
63+
if blocked_instrumentation_scopes is not None
64+
else []
65+
)
5966
flush_at = flush_at or int(os.environ.get(LANGFUSE_FLUSH_AT, 15))
6067
flush_interval = flush_interval or float(
6168
os.environ.get(LANGFUSE_FLUSH_INTERVAL, 0.5)
@@ -93,6 +100,10 @@ def on_end(self, span: ReadableSpan) -> None:
93100
)
94101
return
95102

103+
# Do not export spans from blocked instrumentation scopes
104+
if self._is_blocked_instrumentation_scope(span):
105+
return
106+
96107
langfuse_logger.debug(
97108
f"Trace: Processing span name='{span._name}' | Full details:\n{span_formatter(span)}"
98109
)
@@ -106,6 +117,12 @@ def _is_langfuse_span(span: ReadableSpan) -> bool:
106117
and span.instrumentation_scope.name == LANGFUSE_TRACER_NAME
107118
)
108119

120+
def _is_blocked_instrumentation_scope(self, span: ReadableSpan) -> bool:
121+
return (
122+
span.instrumentation_scope is not None
123+
and span.instrumentation_scope.name in self.blocked_instrumentation_scopes
124+
)
125+
109126
def _is_langfuse_project_span(self, span: ReadableSpan) -> bool:
110127
if not LangfuseSpanProcessor._is_langfuse_span(span):
111128
return False

0 commit comments

Comments
 (0)