Skip to content

Commit d5a8cec

Browse files
piyush182004xrmx
andauthored
Fix TracerProvider() crash on out-of-range OTEL_TRACES_SAMPLER_ARG (#5594)
* Fix TracerProvider() crash on out-of-range OTEL_TRACES_SAMPLER_ARG _get_from_env_or_default() already caught ValueError/TypeError from float(OTEL_TRACES_SAMPLER_ARG) to gracefully fall back to rate=1.0 with a warning when the value is non-numeric. But TraceIdRatioBased.__init__ raises its own ValueError when the parsed rate is syntactically valid yet outside [0.0, 1.0], and that exception was not covered by the same guard, so TracerProvider() construction crashed instead of degrading gracefully. Per the SDK configuration spec, invalid OTEL_TRACES_SAMPLER_ARG values MUST be logged and otherwise ignored, behaving as if the variable were not set. An out-of-range numeric value is just as invalid as a non-numeric one and should be handled the same way. Moves the sampler construction inside the try block so both failure modes share the existing fallback path. * Rename 0.fixed to 5594.fixed * Expand fallback warning to mention the rate that will be used Per review feedback from @ocelotl: mention the fallback rate (1.0) in the warning message so it's clear what value the sampler actually ends up using when OTEL_TRACES_SAMPLER_ARG is invalid. --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
1 parent 8b5ae99 commit d5a8cec

3 files changed

Lines changed: 67 additions & 3 deletions

File tree

.changelog/5594.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`opentelemetry-sdk`: fix `TracerProvider()` raising `ValueError` when `OTEL_TRACES_SAMPLER_ARG` is a syntactically valid number outside the `[0.0, 1.0]` range, instead of logging a warning and falling back like other invalid values

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,10 @@ def _get_from_env_or_default() -> Sampler:
471471
if trace_sampler in ("traceidratio", "parentbased_traceidratio"):
472472
try:
473473
rate = float(os.getenv(OTEL_TRACES_SAMPLER_ARG, ""))
474+
return _KNOWN_SAMPLERS[trace_sampler](rate)
474475
except (ValueError, TypeError):
475-
_logger.warning("Could not convert TRACES_SAMPLER_ARG to float.")
476-
rate = 1.0
477-
return _KNOWN_SAMPLERS[trace_sampler](rate)
476+
_logger.warning("Could not convert TRACES_SAMPLER_ARG to float. Using default rate 1.0.")
477+
return _KNOWN_SAMPLERS[trace_sampler](1.0)
478478

479479
return _KNOWN_SAMPLERS[trace_sampler]
480480

opentelemetry-sdk/tests/trace/test_trace.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
Decision,
5252
ParentBased,
5353
StaticSampler,
54+
TraceIdRatioBased,
5455
)
5556
from opentelemetry.sdk.util import BoundedDict, BoundedList, ns_to_iso_str
5657
from opentelemetry.sdk.util.instrumentation import (
@@ -353,6 +354,68 @@ def test_ratio_sampler_with_env(self):
353354
self.assertIsInstance(tracer_provider.sampler, ParentBased)
354355
self.assertEqual(tracer_provider.sampler._root.rate, 0.25)
355356

357+
@mock.patch.dict(
358+
"os.environ",
359+
{
360+
OTEL_TRACES_SAMPLER: "traceidratio",
361+
OTEL_TRACES_SAMPLER_ARG: "5.0",
362+
},
363+
)
364+
def test_ratio_sampler_with_out_of_range_env_arg_does_not_raise(self):
365+
# A syntactically valid but out-of-range OTEL_TRACES_SAMPLER_ARG
366+
# (outside [0.0, 1.0]) must not crash TracerProvider() construction.
367+
# It should degrade gracefully, the same way a non-numeric value
368+
# already does.
369+
# pylint: disable=protected-access
370+
reload(trace)
371+
tracer_provider = trace.TracerProvider()
372+
self.assertIsInstance(tracer_provider.sampler, TraceIdRatioBased)
373+
self.assertEqual(tracer_provider.sampler.rate, 1.0)
374+
375+
@mock.patch.dict(
376+
"os.environ",
377+
{
378+
OTEL_TRACES_SAMPLER: "traceidratio",
379+
OTEL_TRACES_SAMPLER_ARG: "-0.5",
380+
},
381+
)
382+
def test_ratio_sampler_with_negative_env_arg_does_not_raise(self):
383+
# pylint: disable=protected-access
384+
reload(trace)
385+
tracer_provider = trace.TracerProvider()
386+
self.assertIsInstance(tracer_provider.sampler, TraceIdRatioBased)
387+
self.assertEqual(tracer_provider.sampler.rate, 1.0)
388+
389+
@mock.patch.dict(
390+
"os.environ",
391+
{
392+
OTEL_TRACES_SAMPLER: "traceidratio",
393+
OTEL_TRACES_SAMPLER_ARG: "0.0",
394+
},
395+
)
396+
def test_ratio_sampler_with_env_arg_lower_boundary(self):
397+
# 0.0 is a valid boundary value and must be used as-is, not
398+
# treated as falsy/invalid and overridden by the fallback.
399+
# pylint: disable=protected-access
400+
reload(trace)
401+
tracer_provider = trace.TracerProvider()
402+
self.assertIsInstance(tracer_provider.sampler, TraceIdRatioBased)
403+
self.assertEqual(tracer_provider.sampler.rate, 0.0)
404+
405+
@mock.patch.dict(
406+
"os.environ",
407+
{
408+
OTEL_TRACES_SAMPLER: "traceidratio",
409+
OTEL_TRACES_SAMPLER_ARG: "1.0",
410+
},
411+
)
412+
def test_ratio_sampler_with_env_arg_upper_boundary(self):
413+
# pylint: disable=protected-access
414+
reload(trace)
415+
tracer_provider = trace.TracerProvider()
416+
self.assertIsInstance(tracer_provider.sampler, TraceIdRatioBased)
417+
self.assertEqual(tracer_provider.sampler.rate, 1.0)
418+
356419
def verify_default_sampler(self, tracer_provider):
357420
self.assertIsInstance(tracer_provider.sampler, ParentBased)
358421
# pylint: disable=protected-access

0 commit comments

Comments
 (0)