Skip to content

Commit 697a9bf

Browse files
authored
Allow configuration of metric Views in distro (#35932)
* views * test * lint * Update _configure.py
1 parent e6f98bc commit 697a9bf

File tree

8 files changed

+120
-8
lines changed

8 files changed

+120
-8
lines changed

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

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

55
### Features Added
66

7+
- Enable views configuration
8+
([#35932](https://github.com/Azure/azure-sdk-for-python/pull/35932))
79
- Rework autoinstrumentation: Configure exporters and samplers directly
810
([#35890](https://github.com/Azure/azure-sdk-for-python/pull/35890))
911

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ You can use `configure_azure_monitor` to set up instrumentation for your app to
6565
| `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` |
6666
| `resource` | Specifies the OpenTelemetry [Resource][ot_spec_resource] associated with your application. Passed in [Resource Attributes][ot_spec_resource_attributes] take priority over default attributes and those from [Resource Detectors][ot_python_resource_detectors]. | [OTEL_SERVICE_NAME][ot_spec_service_name], [OTEL_RESOURCE_ATTRIBUTES][ot_spec_resource_attributes], [OTEL_EXPERIMENTAL_RESOURCE_DETECTORS][ot_python_resource_detectors] |
6767
| `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` |
68+
| `views` | A list of [views][ot_view] that will be used to customize metrics exported by the SDK. | `N/A` |
6869

6970
You can configure further with [OpenTelemetry environment variables][ot_env_vars].
7071

@@ -224,6 +225,7 @@ contact [[email protected]](mailto:[email protected]) with any additio
224225
[ot_sdk_python]: https://github.com/open-telemetry/opentelemetry-python
225226
[ot_sdk_python_metric_reader]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/metrics.export.html#opentelemetry.sdk.metrics.export.MetricReader
226227
[ot_span_processor]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#span-processor
228+
[ot_view]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view
227229
[ot_sdk_python_view_examples]: https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples/metrics/views
228230
[ot_instrumentation_django]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-django
229231
[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

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
from logging import getLogger
7-
from typing import Dict, cast
7+
from typing import Dict, List, cast
88

99
from opentelemetry._logs import set_logger_provider
1010
from opentelemetry.instrumentation.dependencies import (
@@ -18,6 +18,7 @@
1818
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
1919
from opentelemetry.sdk.metrics import MeterProvider
2020
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
21+
from opentelemetry.sdk.metrics.view import View
2122
from opentelemetry.sdk.resources import Resource
2223
from opentelemetry.sdk.trace import TracerProvider
2324
from opentelemetry.sdk.trace.export import BatchSpanProcessor
@@ -37,6 +38,7 @@
3738
RESOURCE_ARG,
3839
SAMPLING_RATIO_ARG,
3940
SPAN_PROCESSORS_ARG,
41+
VIEWS_ARG,
4042
)
4143
from azure.monitor.opentelemetry._types import ConfigurationValue
4244
from azure.monitor.opentelemetry.exporter._quickpulse import enable_live_metrics # pylint: disable=import-error,no-name-in-module
@@ -88,6 +90,8 @@ def configure_azure_monitor(**kwargs) -> None: # pylint: disable=C4758
8890
Defaults to `False`.
8991
:keyword str storage_directory: Storage directory in which to store retry files. Defaults to
9092
`<tempfile.gettempdir()>/Microsoft/AzureMonitor/opentelemetry-python-<your-instrumentation-key>`.
93+
:keyword list[~opentelemetry.sdk.metrics.view.View] views: List of `View` objects to configure and filter
94+
metric output.
9195
:rtype: None
9296
"""
9397

@@ -163,11 +167,13 @@ def _setup_logging(configurations: Dict[str, ConfigurationValue]):
163167

164168
def _setup_metrics(configurations: Dict[str, ConfigurationValue]):
165169
resource: Resource = configurations[RESOURCE_ARG] # type: ignore
170+
views: List[View] = configurations[VIEWS_ARG] # type: ignore
166171
metric_exporter = AzureMonitorMetricExporter(**configurations)
167172
reader = PeriodicExportingMetricReader(metric_exporter)
168173
meter_provider = MeterProvider(
169174
metric_readers=[reader],
170-
resource=resource
175+
resource=resource,
176+
views=views,
171177
)
172178
set_meter_provider(meter_provider)
173179

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
RESOURCE_ARG = "resource"
2323
SAMPLING_RATIO_ARG = "sampling_ratio"
2424
SPAN_PROCESSORS_ARG = "span_processors"
25+
VIEWS_ARG = "views"
2526

2627

2728
# --------------------Autoinstrumentation Configuration------------------------------------------

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
RESOURCE_ARG,
3838
SAMPLING_RATIO_ARG,
3939
SPAN_PROCESSORS_ARG,
40+
VIEWS_ARG,
4041
)
4142
from azure.monitor.opentelemetry._types import ConfigurationValue
4243
from azure.monitor.opentelemetry._version import VERSION
@@ -70,6 +71,7 @@ def _get_configurations(**kwargs) -> Dict[str, ConfigurationValue]:
7071
_default_instrumentation_options(configurations)
7172
_default_span_processors(configurations)
7273
_default_enable_live_metrics(configurations)
74+
_default_views(configurations)
7375

7476
return configurations
7577

@@ -155,6 +157,10 @@ def _default_enable_live_metrics(configurations):
155157
configurations.setdefault(ENABLE_LIVE_METRICS_ARG, False)
156158

157159

160+
def _default_views(configurations):
161+
configurations.setdefault(VIEWS_ARG, [])
162+
163+
158164
def _get_otel_disabled_instrumentations():
159165
disabled_instrumentation = environ.get(
160166
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, ""
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from azure.monitor.opentelemetry import configure_azure_monitor
5+
from opentelemetry import metrics
6+
from opentelemetry.sdk.metrics import Counter
7+
from opentelemetry.sdk.metrics.view import View
8+
9+
# Create a view matching the counter instrument `my.counter`
10+
# and configure the new name `my.counter.total` for the result metrics stream
11+
change_metric_name_view = View(
12+
instrument_type=Counter,
13+
instrument_name="my.counter",
14+
name="my.counter.total",
15+
)
16+
17+
# Configure Azure monitor collection telemetry pipeline
18+
configure_azure_monitor(
19+
views=[
20+
change_metric_name_view, # Pass in created View into configuration
21+
]
22+
)
23+
24+
meter = metrics.get_meter_provider().get_meter("view-name-change")
25+
my_counter = meter.create_counter("my.counter")
26+
my_counter.add(100)
27+
28+
29+
input()

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

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,56 @@ def test_setup_metrics(
409409
configurations = {
410410
"connection_string": "test_cs",
411411
"resource": TEST_RESOURCE,
412+
"views": [],
412413
}
413414
_setup_metrics(configurations)
414415
mp_mock.assert_called_once_with(
415416
metric_readers=[reader_init_mock],
416-
resource=TEST_RESOURCE
417+
resource=TEST_RESOURCE,
418+
views=[],
419+
)
420+
set_meter_provider_mock.assert_called_once_with(mp_init_mock)
421+
metric_exporter_mock.assert_called_once_with(**configurations)
422+
reader_mock.assert_called_once_with(metric_exp_init_mock)
423+
424+
@patch(
425+
"azure.monitor.opentelemetry._configure.PeriodicExportingMetricReader",
426+
)
427+
@patch(
428+
"azure.monitor.opentelemetry._configure.AzureMonitorMetricExporter",
429+
)
430+
@patch(
431+
"azure.monitor.opentelemetry._configure.set_meter_provider",
432+
)
433+
@patch(
434+
"azure.monitor.opentelemetry._configure.MeterProvider",
435+
autospec=True,
436+
)
437+
def test_setup_metrics_views(
438+
self,
439+
mp_mock,
440+
set_meter_provider_mock,
441+
metric_exporter_mock,
442+
reader_mock,
443+
):
444+
mp_init_mock = Mock()
445+
mp_mock.return_value = mp_init_mock
446+
metric_exp_init_mock = Mock()
447+
metric_exporter_mock.return_value = metric_exp_init_mock
448+
reader_init_mock = Mock()
449+
reader_mock.return_value = reader_init_mock
450+
view_mock = Mock()
451+
452+
configurations = {
453+
"connection_string": "test_cs",
454+
"resource": TEST_RESOURCE,
455+
"views": [view_mock],
456+
}
457+
_setup_metrics(configurations)
458+
mp_mock.assert_called_once_with(
459+
metric_readers=[reader_init_mock],
460+
resource=TEST_RESOURCE,
461+
views=[view_mock],
417462
)
418463
set_meter_provider_mock.assert_called_once_with(mp_init_mock)
419464
metric_exporter_mock.assert_called_once_with(**configurations)

sdk/monitor/azure-monitor-opentelemetry/tests/utils/test_configurations.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
from opentelemetry.sdk.environment_variables import OTEL_EXPERIMENTAL_RESOURCE_DETECTORS
3232
from opentelemetry.sdk.resources import Resource, Attributes
3333

34+
from azure.monitor.opentelemetry._version import VERSION
35+
3436

3537
TEST_DEFAULT_RESOURCE = Resource({
3638
"test.attributes.1": "test_value_1",
@@ -55,31 +57,47 @@ def test_get_configurations(self, resource_create_mock):
5557
configurations = _get_configurations(
5658
connection_string="test_cs",
5759
credential="test_credential",
58-
resource=TEST_CUSTOM_RESOURCE
60+
resource=TEST_CUSTOM_RESOURCE,
61+
storage_directory="test_directory",
62+
sampling_ratio=0.5,
63+
instrumentation_options={
64+
"flask": {
65+
"enabled": False,
66+
}
67+
},
68+
enable_live_metrics=True,
69+
views=["test_view"],
70+
logger_name="test_logger",
71+
span_processors=["test_processor"],
5972
)
6073

6174
self.assertEqual(configurations["connection_string"], "test_cs")
75+
self.assertEqual(configurations["distro_version"], VERSION)
6276
self.assertEqual(configurations["disable_logging"], False)
6377
self.assertEqual(configurations["disable_metrics"], False)
6478
self.assertEqual(configurations["disable_tracing"], False)
6579
self.assertEqual(configurations["resource"].attributes, TEST_MERGED_RESOURCE.attributes)
6680
self.assertEqual(environ[OTEL_EXPERIMENTAL_RESOURCE_DETECTORS], "azure_app_service,azure_vm")
6781
resource_create_mock.assert_called_once_with(TEST_CUSTOM_RESOURCE.attributes)
6882
self.assertEqual(configurations["sampling_ratio"], 1.0)
69-
self.assertEqual(configurations["credential"], ("test_credential"))
83+
self.assertEqual(configurations["credential"], "test_credential")
7084
self.assertEqual(configurations["instrumentation_options"], {
7185
"azure_sdk" : {"enabled": True},
7286
"django": {"enabled": True},
7387
"fastapi": {"enabled": True},
74-
"flask": {"enabled": True},
88+
"flask": {"enabled": False},
7589
"psycopg2": {"enabled": True},
7690
"requests": {"enabled": True},
7791
"urllib": {"enabled": True},
7892
"urllib3": {"enabled": True},
7993
"previewlib1": {"enabled": False},
8094
"previewlib2": {"enabled": False},
8195
})
82-
self.assertTrue("storage_directory" not in configurations)
96+
self.assertEqual(configurations["storage_directory"], "test_directory")
97+
self.assertEqual(configurations["enable_live_metrics"], True)
98+
self.assertEqual(configurations["views"], ["test_view"])
99+
self.assertEqual(configurations["logger_name"], "test_logger")
100+
self.assertEqual(configurations["span_processors"], ["test_processor"])
83101

84102
@patch.dict("os.environ", {}, clear=True)
85103
@patch("opentelemetry.sdk.resources.Resource.create", return_value=TEST_DEFAULT_RESOURCE)
@@ -107,11 +125,14 @@ def test_get_configurations_defaults(self, resource_create_mock):
107125
self.assertTrue("credential" not in configurations)
108126
self.assertTrue("storage_directory" not in configurations)
109127
self.assertEqual(configurations["enable_live_metrics"], False)
128+
self.assertEqual(configurations["logger_name"], "")
129+
self.assertEqual(configurations["span_processors"], [])
130+
self.assertEqual(configurations["views"], [])
110131

111132
@patch.dict(
112133
"os.environ",
113134
{
114-
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS: "flask , requests,fastapi,azure_sdk",
135+
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS: "flask,requests,fastapi,azure_sdk",
115136
SAMPLING_RATIO_ENV_VAR: "0.5",
116137
OTEL_TRACES_EXPORTER: "None",
117138
OTEL_LOGS_EXPORTER: "none",

0 commit comments

Comments
 (0)