Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9a1f594

Browse files
authored
Add support for OTEL_TRACE_SAMPLER and OTEL_TRACE_SAMPLER_ARG env variables (open-telemetry#1496)
1 parent 98f7b60 commit 9a1f594

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3434
([#1285](https://github.com/open-telemetry/opentelemetry-python/pull/1285))
3535
- Added `__repr__` for `DefaultSpan`, added `trace_flags` to `__repr__` of
3636
`SpanContext` ([#1485](https://github.com/open-telemetry/opentelemetry-python/pull/1485)])
37+
- `opentelemetry-sdk` Add support for OTEL_TRACE_SAMPLER and OTEL_TRACE_SAMPLER_ARG env variables
38+
([#1496](https://github.com/open-telemetry/opentelemetry-python/pull/1496))
3739

3840
### Changed
3941
- `opentelemetry-exporter-zipkin` Updated zipkin exporter status code and error tag

opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
SPAN_EVENT_COUNT_LIMIT = Configuration().get("SPAN_EVENT_COUNT_LIMIT", 1000)
6161
SPAN_LINK_COUNT_LIMIT = Configuration().get("SPAN_LINK_COUNT_LIMIT", 1000)
6262
VALID_ATTR_VALUE_TYPES = (bool, str, int, float)
63+
# pylint: disable=protected-access
64+
TRACE_SAMPLER = sampling._get_from_env_or_default()
6365

6466

6567
class SpanProcessor:
@@ -891,7 +893,7 @@ def use_span(
891893
class TracerProvider(trace_api.TracerProvider):
892894
def __init__(
893895
self,
894-
sampler: sampling.Sampler = sampling.DEFAULT_ON,
896+
sampler: sampling.Sampler = TRACE_SAMPLER,
895897
resource: Resource = Resource.create({}),
896898
shutdown_on_exit: bool = True,
897899
active_span_processor: Union[

opentelemetry-sdk/src/opentelemetry/sdk/trace/sampling.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,46 @@
5959
# created spans will now be sampled by the TraceIdRatioBased sampler
6060
with trace.get_tracer(__name__).start_as_current_span("Test Span"):
6161
...
62+
63+
The tracer sampler can also be configured via environment variables ``OTEL_TRACE_SAMPLER`` and ``OTEL_TRACE_SAMPLER_ARG`` (only if applicable).
64+
The list of known values for ``OTEL_TRACE_SAMPLER`` are:
65+
66+
* always_on - Sampler that always samples spans, regardless of the parent span's sampling decision.
67+
* always_off - Sampler that never samples spans, regardless of the parent span's sampling decision.
68+
* traceidratio - Sampler that samples probabalistically based on rate.
69+
* parentbased_always_on - (default) Sampler that respects its parent span's sampling decision, but otherwise always samples.
70+
* parentbased_always_off - Sampler that respects its parent span's sampling decision, but otherwise never samples.
71+
* parentbased_traceidratio - Sampler that respects its parent span's sampling decision, but otherwise samples probabalistically based on rate.
72+
73+
Sampling probability can be set with ``OTEL_TRACE_SAMPLER_ARG`` if the sampler is traceidratio or parentbased_traceidratio, when not provided rate will be set to 1.0 (maximum rate possible).
74+
75+
76+
Prev example but with environment vairables. Please make sure to set the env ``OTEL_TRACE_SAMPLER=traceidratio`` and ``OTEL_TRACE_SAMPLER_ARG=0.001``.
77+
78+
.. code:: python
79+
80+
from opentelemetry import trace
81+
from opentelemetry.sdk.trace import TracerProvider
82+
from opentelemetry.sdk.trace.export import (
83+
ConsoleSpanExporter,
84+
SimpleExportSpanProcessor,
85+
)
86+
87+
trace.set_tracer_provider(TracerProvider())
88+
89+
# set up an exporter for sampled spans
90+
trace.get_tracer_provider().add_span_processor(
91+
SimpleExportSpanProcessor(ConsoleSpanExporter())
92+
)
93+
94+
# created spans will now be sampled by the TraceIdRatioBased sampler with rate 1/1000.
95+
with trace.get_tracer(__name__).start_as_current_span("Test Span"):
96+
...
6297
"""
6398
import abc
6499
import enum
100+
import os
101+
from logging import getLogger
65102
from types import MappingProxyType
66103
from typing import Optional, Sequence
67104

@@ -71,6 +108,8 @@
71108
from opentelemetry.trace.span import TraceState
72109
from opentelemetry.util.types import Attributes
73110

111+
_logger = getLogger(__name__)
112+
74113

75114
class Decision(enum.Enum):
76115
# IsRecording() == false, span will not be recorded and all events and attributes will be dropped.
@@ -307,3 +346,43 @@ def get_description(self):
307346

308347
DEFAULT_ON = ParentBased(ALWAYS_ON)
309348
"""Sampler that respects its parent span's sampling decision, but otherwise always samples."""
349+
350+
351+
class ParentBasedTraceIdRatio(ParentBased):
352+
"""
353+
Sampler that respects its parent span's sampling decision, but otherwise
354+
samples probabalistically based on `rate`.
355+
"""
356+
357+
def __init__(self, rate: float):
358+
root = TraceIdRatioBased(rate=rate)
359+
super().__init__(root=root)
360+
361+
362+
_KNOWN_SAMPLERS = {
363+
"always_on": ALWAYS_ON,
364+
"always_off": ALWAYS_OFF,
365+
"parentbased_always_on": DEFAULT_ON,
366+
"parentbased_always_off": DEFAULT_OFF,
367+
"traceidratio": TraceIdRatioBased,
368+
"parentbased_traceidratio": ParentBasedTraceIdRatio,
369+
}
370+
371+
372+
def _get_from_env_or_default() -> Sampler:
373+
trace_sampler = os.getenv(
374+
"OTEL_TRACE_SAMPLER", "parentbased_always_on"
375+
).lower()
376+
if trace_sampler not in _KNOWN_SAMPLERS:
377+
_logger.warning("Couldn't recognize sampler %s.", trace_sampler)
378+
trace_sampler = "parentbased_always_on"
379+
380+
if trace_sampler in ("traceidratio", "parentbased_traceidratio"):
381+
try:
382+
rate = float(os.getenv("OTEL_TRACE_SAMPLER_ARG"))
383+
except ValueError:
384+
_logger.warning("Could not convert TRACE_SAMPLER_ARG to float.")
385+
rate = 1.0
386+
return _KNOWN_SAMPLERS[trace_sampler](rate)
387+
388+
return _KNOWN_SAMPLERS[trace_sampler]

opentelemetry-sdk/tests/trace/test_trace.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ def test_tracer_provider_accepts_concurrent_multi_span_processor(self):
139139

140140

141141
class TestTracerSampling(unittest.TestCase):
142+
def tearDown(self):
143+
reload(trace)
144+
142145
def test_default_sampler(self):
143146
tracer = new_tracer()
144147

@@ -159,6 +162,12 @@ def test_default_sampler(self):
159162
trace_api.TraceFlags.SAMPLED,
160163
)
161164

165+
def test_default_sampler_type(self):
166+
tracer_provider = trace.TracerProvider()
167+
self.assertIsInstance(tracer_provider.sampler, sampling.ParentBased)
168+
# pylint: disable=protected-access
169+
self.assertEqual(tracer_provider.sampler._root, sampling.ALWAYS_ON)
170+
162171
def test_sampler_no_sampling(self):
163172
tracer_provider = trace.TracerProvider(sampling.ALWAYS_OFF)
164173
tracer = tracer_provider.get_tracer(__name__)
@@ -179,6 +188,36 @@ def test_sampler_no_sampling(self):
179188
trace_api.TraceFlags.DEFAULT,
180189
)
181190

191+
@mock.patch.dict("os.environ", {"OTEL_TRACE_SAMPLER": "always_off"})
192+
def test_sampler_with_env(self):
193+
# pylint: disable=protected-access
194+
reload(trace)
195+
tracer_provider = trace.TracerProvider()
196+
self.assertIsInstance(tracer_provider.sampler, sampling.StaticSampler)
197+
self.assertEqual(
198+
tracer_provider.sampler._decision, sampling.Decision.DROP
199+
)
200+
201+
tracer = tracer_provider.get_tracer(__name__)
202+
203+
root_span = tracer.start_span(name="root span", context=None)
204+
# Should be no-op
205+
self.assertIsInstance(root_span, trace_api.DefaultSpan)
206+
207+
@mock.patch.dict(
208+
"os.environ",
209+
{
210+
"OTEL_TRACE_SAMPLER": "parentbased_traceidratio",
211+
"OTEL_TRACE_SAMPLER_ARG": "0.25",
212+
},
213+
)
214+
def test_ratio_sampler_with_env(self):
215+
# pylint: disable=protected-access
216+
reload(trace)
217+
tracer_provider = trace.TracerProvider()
218+
self.assertIsInstance(tracer_provider.sampler, sampling.ParentBased)
219+
self.assertEqual(tracer_provider.sampler._root.rate, 0.25)
220+
182221

183222
class TestSpanCreation(unittest.TestCase):
184223
def test_start_span_invalid_spancontext(self):

0 commit comments

Comments
 (0)