Skip to content

Commit f770e88

Browse files
authored
Add support for OTEL_TRACES_SAMPLER (Azure#44535)
* Add support for OTEL_TRACES_SAMPLER * Added CHANGELOG * Added tests and modified README * Remove extra space * Fix formatting * Added tests and modified README * Fix README * Fix black * Add sample for different sampling configurations * Fix cspell
1 parent 5c5d6eb commit f770e88

File tree

7 files changed

+486
-8
lines changed

7 files changed

+486
-8
lines changed

sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## 1.8.4 (Unreleased)
44

55
### Features Added
6+
- Added support for OTEL_TRACES_SAMPLER
7+
([#44535](https://github.com/Azure/azure-sdk-for-python/pull/44535))
68
- Added ability to add additional Log Record Processors and Metric Readers via configure_azure_monitor
79
([#44367](https://github.com/Azure/azure-sdk-for-python/pull/44367))
810

sdk/monitor/azure-monitor-opentelemetry/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ You can configure further with [OpenTelemetry environment variables][ot_env_vars
8282
| `OTEL_TRACES_EXPORTER` | If set to `None`, disables collection and export of distributed tracing telemetry. |
8383
| `OTEL_BLRP_SCHEDULE_DELAY` | Specifies the logging export interval in milliseconds. Defaults to 5000. |
8484
| `OTEL_BSP_SCHEDULE_DELAY` | Specifies the distributed tracing export interval in milliseconds. Defaults to 5000. |
85-
| `OTEL_TRACES_SAMPLER` | Specifies the sampler to be used for traces. Supports both [application_insights_sampling] and [rate_limited_sampling]. Use `microsoft.fixed.percentage` for the Application Insights sampler or `microsoft.rate_limited` for the Rate Limited sampler. |
86-
| `OTEL_TRACES_SAMPLER_ARG` | Specifies the sampling parameter for the configured sampler. For the Application Insights sampler, this sets the ratio of distributed tracing telemetry to be [sampled][application_insights_sampling] with accepted values in the range [0,1]. Defaults to 1.0 (no sampling). For the Rate Limited sampler, this sets the maximum traces per second to be [sampled][rate_limited_sampler]. For example, 0.5 means one trace every two seconds, while 5.0 means five traces per second. |
85+
| `OTEL_TRACES_SAMPLER` | Specifies the sampler to be used for traces. Supports `always_on`, `always_off`, `trace_id_ratio`, `parentbased_always_on`, `parentbased_always_off`, `parentbased_trace_id_ratio`, [application_insights_sampling] and [rate_limited_sampling]. Use `microsoft.fixed.percentage` for the Application Insights sampler or `microsoft.rate_limited` for the Rate Limited sampler. |
86+
| `OTEL_TRACES_SAMPLER_ARG` | Specifies the sampling parameter for the configured sampler. For the standard OpenTelemetry samplers `trace_id_ratio` and `parentbased_trace_id_ratio`, this is the sampling ratio in the range [0.0, 1.0]. Not needed to be specified for `always_on`, `always_off`, `parentbased_always_on`, or `parentbased_always_off` samplers. For the Application Insights sampler, this sets the ratio of distributed tracing telemetry to be [sampled][application_insights_sampling] with accepted values in the range [0,1]. Defaults to 1.0 (no sampling). For the Rate Limited sampler, this sets the maximum traces per second to be [sampled][rate_limited_sampler]. For example, 0.5 means one trace every two seconds, while 5.0 means five traces per second. |
8787
| `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` | Specifies which of the supported instrumentations to disable. Disabled instrumentations will not be instrumented as part of `configure_azure_monitor`. However, they can still be manually instrumented with `instrument()` directly. Accepts a comma-separated list of lowercase [Library Names](#officially-supported-instrumentations). For example, set to `"psycopg2,fastapi"` to disable the Psycopg2 and FastAPI instrumentations. Defaults to an empty list, enabling all supported instrumentations. |
8888
| `OTEL_EXPERIMENTAL_RESOURCE_DETECTORS` | An experimental OpenTelemetry environment variable used to specify Resource Detectors to be used to generate Resource Attributes. This is an experimental feature and the name of this variable and its behavior can change in a non-backwards compatible way. Defaults to "azure_app_service,azure_vm" to enable the [Azure Resource Detectors][ot_resource_detector_azure] for Azure App Service and Azure VM. To add or remove specific resource detectors, set the environment variable accordingly. See the [OpenTelemetry Python Resource Detector Documentation][ot_python_resource_detectors] for more. |
8989

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_configure.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
METRIC_READERS_ARG,
4343
VIEWS_ARG,
4444
ENABLE_TRACE_BASED_SAMPLING_ARG,
45+
SAMPLING_ARG,
46+
SAMPLER_TYPE,
4547
)
4648
from azure.monitor.opentelemetry._types import ConfigurationValue
4749
from azure.monitor.opentelemetry.exporter._quickpulse import ( # pylint: disable=import-error,no-name-in-module
@@ -75,6 +77,7 @@
7577
from azure.monitor.opentelemetry._utils.configurations import (
7678
_get_configurations,
7779
_is_instrumentation_enabled,
80+
_get_sampler_from_name,
7881
)
7982
from azure.monitor.opentelemetry._utils.instrumentation import (
8083
get_dist_dependency_conflicts,
@@ -155,7 +158,12 @@ def configure_azure_monitor(**kwargs) -> None: # pylint: disable=C4758
155158
def _setup_tracing(configurations: Dict[str, ConfigurationValue]):
156159
resource: Resource = configurations[RESOURCE_ARG] # type: ignore
157160
enable_performance_counters_config = configurations[ENABLE_PERFORMANCE_COUNTERS_ARG]
158-
if SAMPLING_TRACES_PER_SECOND_ARG in configurations:
161+
if SAMPLING_ARG in configurations:
162+
sampler_arg = configurations[SAMPLING_ARG]
163+
sampler_type = configurations[SAMPLER_TYPE]
164+
sampler = _get_sampler_from_name(sampler_type, sampler_arg)
165+
tracer_provider = TracerProvider(sampler=sampler, resource=resource)
166+
elif SAMPLING_TRACES_PER_SECOND_ARG in configurations:
159167
traces_per_second = configurations[SAMPLING_TRACES_PER_SECOND_ARG]
160168
tracer_provider = TracerProvider(
161169
sampler=RateLimitedSampler(target_spans_per_second_limit=cast(float, traces_per_second)), resource=resource

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@
3131
FIXED_PERCENTAGE_SAMPLER = "microsoft.fixed.percentage"
3232
SAMPLING_TRACES_PER_SECOND_ARG = "traces_per_second"
3333
ENABLE_TRACE_BASED_SAMPLING_ARG = "enable_trace_based_sampling_for_logs"
34+
SAMPLER_TYPE = "sampler_type"
35+
SAMPLING_ARG = "sampling_arg"
36+
ALWAYS_ON_SAMPLER = "always_on"
37+
ALWAYS_OFF_SAMPLER = "always_off"
38+
TRACE_ID_RATIO_SAMPLER = "trace_id_ratio"
39+
PARENT_BASED_ALWAYS_ON_SAMPLER = "parentbased_always_on"
40+
PARENT_BASED_ALWAYS_OFF_SAMPLER = "parentbased_always_off"
41+
PARENT_BASED_TRACE_ID_RATIO_SAMPLER = "parentbased_trace_id_ratio"
42+
SUPPORTED_OTEL_SAMPLERS = (
43+
RATE_LIMITED_SAMPLER,
44+
FIXED_PERCENTAGE_SAMPLER,
45+
ALWAYS_ON_SAMPLER,
46+
ALWAYS_OFF_SAMPLER,
47+
TRACE_ID_RATIO_SAMPLER,
48+
PARENT_BASED_ALWAYS_ON_SAMPLER,
49+
PARENT_BASED_ALWAYS_OFF_SAMPLER,
50+
PARENT_BASED_TRACE_ID_RATIO_SAMPLER,
51+
)
3452

3553
# --------------------Autoinstrumentation Configuration------------------------------------------
3654

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_utils/configurations.py

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
)
2424
from opentelemetry.sdk.resources import Resource
2525

26+
from opentelemetry.sdk.trace.sampling import (
27+
TraceIdRatioBased,
28+
ALWAYS_OFF,
29+
ALWAYS_ON,
30+
ParentBased,
31+
)
32+
2633
from azure.monitor.opentelemetry._constants import (
2734
_AZURE_APP_SERVICE_RESOURCE_DETECTOR_NAME,
2835
_AZURE_VM_RESOURCE_DETECTOR_NAME,
@@ -49,6 +56,15 @@
4956
RATE_LIMITED_SAMPLER,
5057
FIXED_PERCENTAGE_SAMPLER,
5158
ENABLE_TRACE_BASED_SAMPLING_ARG,
59+
SUPPORTED_OTEL_SAMPLERS,
60+
ALWAYS_OFF_SAMPLER,
61+
ALWAYS_ON_SAMPLER,
62+
TRACE_ID_RATIO_SAMPLER,
63+
PARENT_BASED_ALWAYS_ON_SAMPLER,
64+
PARENT_BASED_ALWAYS_OFF_SAMPLER,
65+
PARENT_BASED_TRACE_ID_RATIO_SAMPLER,
66+
SAMPLING_ARG,
67+
SAMPLER_TYPE,
5268
)
5369
from azure.monitor.opentelemetry._types import ConfigurationValue
5470
from azure.monitor.opentelemetry._version import VERSION
@@ -149,6 +165,7 @@ def _default_resource(configurations):
149165
configurations[RESOURCE_ARG] = Resource.create(configurations[RESOURCE_ARG].attributes)
150166

151167

168+
# pylint: disable=too-many-statements,too-many-branches
152169
def _default_sampling_ratio(configurations):
153170
default_value = 1.0
154171
sampler_type = environ.get(OTEL_TRACES_SAMPLER)
@@ -158,7 +175,7 @@ def _default_sampling_ratio(configurations):
158175
if sampler_type == RATE_LIMITED_SAMPLER:
159176
try:
160177
sampler_value = float(sampler_arg)
161-
if sampler_value < 0:
178+
if sampler_value < 0.0:
162179
_logger.error("Invalid value for OTEL_TRACES_SAMPLER_ARG. It should be a non-negative number.")
163180
sampler_value = default_value
164181
else:
@@ -177,7 +194,7 @@ def _default_sampling_ratio(configurations):
177194
elif sampler_type == FIXED_PERCENTAGE_SAMPLER:
178195
try:
179196
sampler_value = float(sampler_arg)
180-
if sampler_value < 0:
197+
if sampler_value < 0.0:
181198
_logger.error("Invalid value for OTEL_TRACES_SAMPLER_ARG. It should be a non-negative number.")
182199
sampler_value = default_value
183200
else:
@@ -192,16 +209,75 @@ def _default_sampling_ratio(configurations):
192209
)
193210
configurations[SAMPLING_RATIO_ARG] = default_value
194211

212+
# Handle always_on sampler
213+
elif sampler_type == ALWAYS_ON_SAMPLER:
214+
configurations[SAMPLING_ARG] = 1.0
215+
configurations[SAMPLER_TYPE] = ALWAYS_ON_SAMPLER
216+
217+
# Handle always_off sampler
218+
elif sampler_type == ALWAYS_OFF_SAMPLER:
219+
configurations[SAMPLING_ARG] = 0.0
220+
configurations[SAMPLER_TYPE] = ALWAYS_OFF_SAMPLER
221+
222+
# Handle trace_id_ratio sampler
223+
elif sampler_type == TRACE_ID_RATIO_SAMPLER:
224+
try:
225+
sampler_value = float(sampler_arg) if sampler_arg is not None else default_value
226+
if sampler_value < 0.0 or sampler_value > 1.0:
227+
_logger.error("Invalid value for OTEL_TRACES_SAMPLER_ARG. It should be a value between 0 and 1.")
228+
sampler_value = default_value
229+
else:
230+
_logger.info("Using sampling value: %s", sampler_value)
231+
configurations[SAMPLING_ARG] = sampler_value
232+
except ValueError as e:
233+
_logger.error( # pylint: disable=C
234+
_INVALID_FLOAT_MESSAGE,
235+
OTEL_TRACES_SAMPLER_ARG,
236+
default_value,
237+
e,
238+
)
239+
configurations[SAMPLING_ARG] = default_value
240+
configurations[SAMPLER_TYPE] = TRACE_ID_RATIO_SAMPLER
241+
242+
# Handle parentbased_always_on sampler
243+
elif sampler_type == PARENT_BASED_ALWAYS_ON_SAMPLER:
244+
configurations[SAMPLING_ARG] = 1.0
245+
configurations[SAMPLER_TYPE] = PARENT_BASED_ALWAYS_ON_SAMPLER
246+
247+
# Handle parentbased_always_off sampler
248+
elif sampler_type == PARENT_BASED_ALWAYS_OFF_SAMPLER:
249+
configurations[SAMPLING_ARG] = 0.0
250+
configurations[SAMPLER_TYPE] = PARENT_BASED_ALWAYS_OFF_SAMPLER
251+
252+
# Handle parentbased_trace_id_ratio sampler
253+
elif sampler_type == PARENT_BASED_TRACE_ID_RATIO_SAMPLER:
254+
try:
255+
sampler_value = float(sampler_arg) if sampler_arg is not None else default_value
256+
if sampler_value < 0.0 or sampler_value > 1.0:
257+
_logger.error("Invalid value for OTEL_TRACES_SAMPLER_ARG. It should be a value between 0 and 1.")
258+
sampler_value = default_value
259+
else:
260+
_logger.info("Using sampling value: %s", sampler_value)
261+
configurations[SAMPLING_ARG] = sampler_value
262+
except ValueError as e:
263+
_logger.error( # pylint: disable=C
264+
_INVALID_FLOAT_MESSAGE,
265+
OTEL_TRACES_SAMPLER_ARG,
266+
default_value,
267+
e,
268+
)
269+
configurations[SAMPLING_ARG] = default_value
270+
configurations[SAMPLER_TYPE] = PARENT_BASED_TRACE_ID_RATIO_SAMPLER
271+
195272
# Handle all other cases (no sampler type specified or unsupported sampler type)
196273
else:
197274
if configurations.get(SAMPLING_RATIO_ARG) is None:
198275
configurations[SAMPLING_RATIO_ARG] = default_value
199276
if sampler_type is not None:
200277
_logger.error( # pylint: disable=C
201278
"Invalid argument for the sampler to be used for tracing. "
202-
"Supported values are %s and %s. Defaulting to %s: %s",
203-
RATE_LIMITED_SAMPLER,
204-
FIXED_PERCENTAGE_SAMPLER,
279+
"Supported values are %s. Defaulting to %s: %s",
280+
SUPPORTED_OTEL_SAMPLERS,
205281
FIXED_PERCENTAGE_SAMPLER,
206282
configurations[SAMPLING_RATIO_ARG],
207283
)
@@ -271,3 +347,19 @@ def _is_instrumentation_enabled(configurations, lib_name):
271347

272348
def _default_enable_trace_based_sampling(configurations):
273349
configurations.setdefault(ENABLE_TRACE_BASED_SAMPLING_ARG, False)
350+
351+
352+
def _get_sampler_from_name(sampler_type, sampler_arg):
353+
if sampler_type == ALWAYS_ON_SAMPLER:
354+
return ALWAYS_ON
355+
if sampler_type == ALWAYS_OFF_SAMPLER:
356+
return ALWAYS_OFF
357+
if sampler_type == TRACE_ID_RATIO_SAMPLER:
358+
ratio = float(sampler_arg) if sampler_arg is not None else 1.0
359+
return TraceIdRatioBased(ratio)
360+
if sampler_type == PARENT_BASED_ALWAYS_OFF_SAMPLER:
361+
return ParentBased(ALWAYS_OFF)
362+
if sampler_type == PARENT_BASED_TRACE_ID_RATIO_SAMPLER:
363+
ratio = float(sampler_arg) if sampler_arg is not None else 1.0
364+
return ParentBased(TraceIdRatioBased(ratio))
365+
return ParentBased(ALWAYS_ON)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from azure.monitor.opentelemetry import configure_azure_monitor
2+
from opentelemetry import trace
3+
4+
# Using always_on sampler
5+
# Set the OTEL_TRACES_SAMPLER environment variable to "always_on"
6+
# The sampling rate is 1.0, so 100% of the traces are sampled.
7+
8+
# Using always_off sampler
9+
# Set the OTEL_TRACES_SAMPLER environment variable to "always_off"
10+
# The sampling rate is 0.0, so None of the traces are sampled.
11+
12+
# Using trace_id_ratio sampler
13+
# Set the OTEL_TRACES_SAMPLER environment variable to "trace_id_ratio"
14+
# Set the OTEL_TRACES_SAMPLER_ARG environment variable to 0.1, it has to be a number between 0 and 1, else it will throw an error and default to 1.0
15+
# The sampling rate is 0.1 means approximately 10% of your traces are sent
16+
17+
# Using parentbased_always_on sampler
18+
# Set the OTEL_TRACES_SAMPLER environment variable to "parentbased_always_on"
19+
# The sampling rate is 1.0, so 100% of the traces are sampled.
20+
21+
# Using parentbased_always_off sampler
22+
# Set the OTEL_TRACES_SAMPLER environment variable to "parentbased_always_off"
23+
# The sampling rate is 0.0, so None of the traces are sampled.
24+
25+
# Using parentbased_trace_id_ratio sampler
26+
# Set the OTEL_TRACES_SAMPLER environment variable to "parentbased_trace_id_ratio"
27+
# Set the OTEL_TRACES_SAMPLER_ARG environment variable to 0.45, it has to be a number between 0 and 1, else it will throw an error and default to 1.0
28+
# The sampling rate is 0.45 means approximately 45% of your traces are sent
29+
30+
# Using rate limited sampler
31+
# Set the OTEL_TRACES_SAMPLER environment variable to "microsoft.rate_limited"
32+
# Set the OTEL_TRACES_SAMPLER_ARG environment variable to the desired rate limit (e.g., 0.5 means one trace every two seconds, while 5.0 means five traces per second)
33+
34+
# Using fixed percentage sampler
35+
# Set the OTEL_TRACES_SAMPLER environment variable to "microsoft.fixed.percentage"
36+
# Set the OTEL_TRACES_SAMPLER_ARG environment variable to 0.2, it has to be a number between 0 and 1, else it will throw an error and default to 1.0
37+
38+
# Using trace_based_sampling configuration # cspell: ignore unsampled
39+
# Determines whether the logger should drop log records associated with unsampled traces.
40+
# Passing the enable_trace_based_sampling_for_logs=True argument to configure_azure_monitor ensure that log records associated with unsampled traces are dropped by the `Logger`.
41+
# A log record is considered associated with an unsampled trace if it has a valid `SpanId` and its `TraceFlags` indicate that the trace is unsampled.
42+
# The value of this config is False by default
43+
44+
"""
45+
configure_azure_monitor (
46+
"enable_trace_based_sampling_for_logs": True,
47+
)
48+
"""
49+
50+
configure_azure_monitor()
51+
52+
tracer = trace.get_tracer(__name__)
53+
54+
for i in range(100):
55+
with tracer.start_as_current_span("hello"):
56+
print("Hello, World!")
57+
58+
input()

0 commit comments

Comments
 (0)