Skip to content

Commit 4b06b13

Browse files
authored
Add configuration for custom span processors (#34326)
1 parent cac6852 commit 4b06b13

File tree

9 files changed

+110
-6
lines changed

9 files changed

+110
-6
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
### Features Added
66

7+
- Add custom span processors configuration option
8+
([#34326](https://github.com/Azure/azure-sdk-for-python/pull/34326))
9+
710
### Bugs Fixed
811

912
### Other Changes

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ You can use `configure_azure_monitor` to set up instrumentation for your app to
6060
| `connection_string` | The [connection string][connection_string_doc] for your Application Insights resource. The connection string will be automatically populated from the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable if not explicitly passed in. | `APPLICATIONINSIGHTS_CONNECTION_STRING` |
6161
| `logger_name` | The name of the [Python logger][python_logger] under which telemetry is collected. | `N/A` |
6262
| `instrumentation_options` | A nested dictionary that determines which instrumentations to enable or disable. Instrumentations are referred to by their [Library Names](#officially-supported-instrumentations). For example, `{"azure_sdk": {"enabled": False}, "flask": {"enabled": False}, "django": {"enabled": True}}` will disable Azure Core Tracing and the Flask instrumentation but leave Django and the other default instrumentations enabled. The `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` environment variable explained below can also be used to disable instrumentations. | `N/A` |
63-
63+
| `span_processors` | A list of [span processors][ot_span_processor] that will perform processing on each of your spans before they are exported. Useful for filtering/modifying telemetry. | `N/A` |
6464

6565
You can configure further with [OpenTelemetry environment variables][ot_env_vars] such as:
6666
| Environment Variable | Description |
@@ -217,6 +217,7 @@ contact [[email protected]](mailto:[email protected]) with any additio
217217
[ot_python_docs]: https://opentelemetry.io/docs/instrumentation/python/
218218
[ot_sdk_python]: https://github.com/open-telemetry/opentelemetry-python
219219
[ot_sdk_python_metric_reader]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/metrics.export.html#opentelemetry.sdk.metrics.export.MetricReader
220+
[ot_span_processor]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-processor
220221
[ot_sdk_python_view_examples]: https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples/metrics/views
221222
[ot_instrumentation_django]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-django
222223
[ot_instrumentation_django_version]: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-django/src/opentelemetry/instrumentation/django/package.py#L16
@@ -249,4 +250,4 @@ contact [[email protected]](mailto:[email protected]) with any additio
249250
[python_logger]: https://docs.python.org/3/library/logging.html#logger-objects
250251
[python_logging_level]: https://docs.python.org/3/library/logging.html#levels
251252
[samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples
252-
[samples_manual]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/manual.py
253+
[samples_manual]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/manually_instrumented.py

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
LOGGER_NAME_ARG,
3636
RESOURCE_ARG,
3737
SAMPLING_RATIO_ARG,
38+
SPAN_PROCESSORS_ARG,
3839
)
3940
from azure.monitor.opentelemetry._types import ConfigurationValue
4041
from azure.monitor.opentelemetry.exporter import ( # pylint: disable=import-error,no-name-in-module
@@ -68,6 +69,8 @@ def configure_azure_monitor(**kwargs) -> None:
6869
`{"azure_sdk": {"enabled": False}, "flask": {"enabled": False}, "django": {"enabled": True}}`
6970
will disable Azure Core Tracing and the Flask instrumentation but leave Django and the other default
7071
instrumentations enabled.
72+
:keyword list[~opentelemetry.sdk.trace.SpanProcessor] span_processors: List of `SpanProcessor` objects
73+
to process every span prior to exporting. Will be run sequentially.
7174
:keyword str storage_directory: Storage directory in which to store retry files. Defaults to
7275
`<tempfile.gettempdir()>/Microsoft/AzureMonitor/opentelemetry-python-<your-instrumentation-key>`.
7376
:rtype: None
@@ -105,11 +108,13 @@ def _setup_tracing(configurations: Dict[str, ConfigurationValue]):
105108
resource=resource
106109
)
107110
set_tracer_provider(tracer_provider)
111+
for span_processor in configurations[SPAN_PROCESSORS_ARG]: # type: ignore
112+
get_tracer_provider().add_span_processor(span_processor) # type: ignore
108113
trace_exporter = AzureMonitorTraceExporter(**configurations)
109-
span_processor = BatchSpanProcessor(
114+
bsp = BatchSpanProcessor(
110115
trace_exporter,
111116
)
112-
get_tracer_provider().add_span_processor(span_processor) # type: ignore
117+
get_tracer_provider().add_span_processor(bsp) # type: ignore
113118
if _is_instrumentation_enabled(configurations, _AZURE_SDK_INSTRUMENTATION_NAME):
114119
settings.tracing_implementation = OpenTelemetrySpan
115120

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
INSTRUMENTATION_OPTIONS_ARG = "instrumentation_options"
3030
RESOURCE_ARG = "resource"
3131
SAMPLING_RATIO_ARG = "sampling_ratio"
32+
SPAN_PROCESSORS_ARG = "span_processors"
3233

3334

3435
# --------------------Diagnostic/status logging------------------------------

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
LOGGER_NAME_ARG,
3636
RESOURCE_ARG,
3737
SAMPLING_RATIO_ARG,
38+
SPAN_PROCESSORS_ARG,
3839
)
3940
from azure.monitor.opentelemetry._types import ConfigurationValue
4041
from azure.monitor.opentelemetry._version import VERSION
@@ -66,6 +67,7 @@ def _get_configurations(**kwargs) -> Dict[str, ConfigurationValue]:
6667
_default_resource(configurations)
6768
_default_sampling_ratio(configurations)
6869
_default_instrumentation_options(configurations)
70+
_default_span_processors(configurations)
6971

7072
return configurations
7173

@@ -141,6 +143,11 @@ def _default_instrumentation_options(configurations):
141143
configurations[INSTRUMENTATION_OPTIONS_ARG] = merged_instrumentation_options
142144

143145

146+
def _default_span_processors(configurations):
147+
if SPAN_PROCESSORS_ARG not in configurations:
148+
configurations[SPAN_PROCESSORS_ARG] = []
149+
150+
144151
def _get_otel_disabled_instrumentations():
145152
disabled_instrumentation = environ.get(
146153
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, ""
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
import requests
8+
from azure.monitor.opentelemetry import configure_azure_monitor
9+
from opentelemetry.sdk.trace import SpanProcessor
10+
from opentelemetry.trace import get_tracer, SpanContext, SpanKind, TraceFlags
11+
12+
# Define a custom processor to filter your spans
13+
class SpanFilteringProcessor(SpanProcessor):
14+
15+
# Prevents exporting spans that are of kind INTERNAL
16+
def on_start(self, span, parent_context):
17+
# Check if the span is an internal activity.
18+
if span._kind is SpanKind.INTERNAL:
19+
# Create a new span context with the following properties:
20+
# * The trace ID is the same as the trace ID of the original span.
21+
# * The span ID is the same as the span ID of the original span.
22+
# * The is_remote property is set to `False`.
23+
# * The trace flags are set to `DEFAULT`.
24+
# * The trace state is the same as the trace state of the original span.
25+
span._context = SpanContext(
26+
span.context.trace_id,
27+
span.context.span_id,
28+
span.context.is_remote,
29+
TraceFlags(TraceFlags.DEFAULT),
30+
span.context.trace_state,
31+
)
32+
33+
# Create a SpanFilteringProcessor instance.
34+
span_filter_processor = SpanFilteringProcessor()
35+
36+
# Pass in your processor to configuration options
37+
configure_azure_monitor(
38+
span_processors=[span_filter_processor]
39+
)
40+
41+
tracer = get_tracer(__name__)
42+
43+
with tracer.start_as_current_span("this_span_is_ignored"):
44+
# Requests made using the requests library will be automatically captured
45+
# The span generated from this request call will be tracked since it is not an INTERNAL span
46+
response = requests.get("https://azure.microsoft.com/", timeout=5)
47+
print("Hello, World!")
48+
49+
input()
File renamed without changes.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
from azure.monitor.opentelemetry import configure_azure_monitor
8+
from opentelemetry import trace
9+
from opentelemetry.sdk.trace import SpanProcessor
10+
11+
# Define a custom processor to modify your spans
12+
class SpanEnrichingProcessor(SpanProcessor):
13+
14+
def on_end(self, span):
15+
# Prefix the span name with the string "Updated-".
16+
span._name = "Updated-" + span.name
17+
# Add the custom dimension "CustomDimension1" with the value "Value1".
18+
span._attributes["CustomDimension1"] = "Value1"
19+
# Add the custom dimension "CustomDimension2" with the value "Value2".
20+
span._attributes["CustomDimension2"] = "Value2"
21+
22+
# Create a SpanEnrichingProcessor instance.
23+
span_enrich_processor = SpanEnrichingProcessor()
24+
25+
# Pass in your processor to configuration options
26+
configure_azure_monitor(
27+
span_processors=[span_enrich_processor]
28+
)
29+
30+
tracer = trace.get_tracer(__name__)
31+
32+
with tracer.start_as_current_span("this_span_will_be_modified"):
33+
print("Hello, World!")
34+
35+
input()

sdk/monitor/azure-monitor-opentelemetry/tests/configuration/test_configure.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import unittest
15-
from unittest.mock import Mock, patch
15+
from unittest.mock import Mock, call, patch
1616

1717
from opentelemetry.sdk.resources import Resource
1818

@@ -218,13 +218,15 @@ def test_setup_tracing(
218218
trace_exporter_mock.return_value = trace_exp_init_mock
219219
bsp_init_mock = Mock()
220220
bsp_mock.return_value = bsp_init_mock
221+
custom_sp = Mock()
221222

222223
configurations = {
223224
"connection_string": "test_cs",
224225
"instrumentation_options": {
225226
"azure_sdk": {"enabled": True}
226227
},
227228
"sampling_ratio": 0.5,
229+
"span_processors": [custom_sp],
228230
"resource": TEST_RESOURCE,
229231
}
230232
_setup_tracing(configurations)
@@ -237,7 +239,8 @@ def test_setup_tracing(
237239
get_tracer_provider_mock.assert_called()
238240
trace_exporter_mock.assert_called_once_with(**configurations)
239241
bsp_mock.assert_called_once_with(trace_exp_init_mock)
240-
tp_init_mock.add_span_processor.assert_called_once_with(bsp_init_mock)
242+
self.assertEqual(tp_init_mock.add_span_processor.call_count, 2)
243+
tp_init_mock.add_span_processor.assert_has_calls([call(custom_sp), call(bsp_init_mock)])
241244
self.assertEqual(
242245
azure_core_mock.tracing_implementation, OpenTelemetrySpan
243246
)

0 commit comments

Comments
 (0)