Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

### Bugs Fixed

- Fixed sampleRate field in ApplicationInsightsSampler, changed attribute to `_MS.sampleRate`
([#26771](https://github.com/Azure/azure-sdk-for-python/pull/26771))

### Other Changes

- Update `README.md`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,7 @@
]
# cSpell:enable
_INSTRUMENTATIONS_BIT_MAP = {_INSTRUMENTATIONS_LIST[i]: _BASE**i for i in range(len(_INSTRUMENTATIONS_LIST))}

# sampleRate

_SAMPLE_RATE_KEY = "_MS.sampleRate"
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def __init__(self, **kwargs: Any) -> None:
"""Azure Monitor base exporter for OpenTelemetry.

:keyword str api_version: The service API version used. Defaults to latest.
:keyword str connection_string: The connection string used for your Application Insights resource.
:keyword bool enable_local_storage: Determines whether to store failed telemetry records for retry. Defaults to `True`.
:keyword str storage_path: Storage path in which to store retry files. Defaults to `<tempfile.gettempdir()>/opentelemetry-python-<your-instrumentation-key>`.
:rtype: None
"""
parsed_connection_string = ConnectionStringParser(kwargs.get('connection_string'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
from opentelemetry.trace import SpanKind

from azure.monitor.opentelemetry.exporter._constants import _SAMPLE_RATE_KEY
from azure.monitor.opentelemetry.exporter import _utils
from azure.monitor.opentelemetry.exporter._generated.models import (
MessageData,
Expand Down Expand Up @@ -45,6 +46,10 @@
"code.",
]

_STANDARD_AZURE_MONITOR_ATTRIBUTES = [
_SAMPLE_RATE_KEY,
]


class AzureMonitorTraceExporter(BaseExporter, SpanExporter):
"""Azure Monitor Trace exporter for OpenTelemetry."""
Expand Down Expand Up @@ -420,9 +425,13 @@ def _convert_span_to_envelope(span: ReadableSpan) -> TelemetryItem:
if target:
data.target = str(target)[:1024]

# sampleRate
if _SAMPLE_RATE_KEY in span.attributes:
envelope.sample_rate = span.attributes[_SAMPLE_RATE_KEY]

data.properties = _utils._filter_custom_properties(
span.attributes,
lambda key, val: not _is_opentelemetry_standard_attribute(key)
lambda key, val: not _is_standard_attribute(key)
)
if span.links:
# Max length for value is 8192
Expand Down Expand Up @@ -450,7 +459,7 @@ def _convert_span_events_to_envelopes(span: ReadableSpan) -> Sequence[TelemetryI
)
properties = _utils._filter_custom_properties(
event.attributes,
lambda key, val: not _is_opentelemetry_standard_attribute(key)
lambda key, val: not _is_standard_attribute(key)
)
if event.name == "exception":
envelope.name = 'Microsoft.ApplicationInsights.Exception'
Expand Down Expand Up @@ -545,11 +554,12 @@ def _check_instrumentation_span(span: ReadableSpan) -> None:
_utils.add_instrumentation(name)


def _is_opentelemetry_standard_attribute(key: str) -> bool:
def _is_standard_attribute(key: str) -> bool:
for prefix in _STANDARD_OPENTELEMETRY_ATTRIBUTE_PREFIXES:
if key.startswith(prefix):
return True
return False
return key in _STANDARD_AZURE_MONITOR_ATTRIBUTES


def _get_azure_sdk_target_source(attributes: Attributes) -> Optional[str]:
# Currently logic only works for ServiceBus and EventHub
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from opentelemetry.trace.span import TraceState
from opentelemetry.util.types import Attributes

from azure.monitor.opentelemetry.exporter._constants import _SAMPLE_RATE_KEY


_HASH = 5381
_INTEGER_MAX = Int32.maxval
Expand Down Expand Up @@ -62,7 +64,7 @@ def should_sample(
# Add sample rate as span attribute
if attributes is None:
attributes = {}
attributes["sampleRate"] = self._sample_rate
attributes[_SAMPLE_RATE_KEY] = self._sample_rate
return SamplingResult(
decision,
attributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ products:

These code samples show common champion scenario operations with the AzureMonitorMetricExporter.

* Attributes: [sample_attributes.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_attributes.py)
* Instruments: [sample_instruments.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_instruments.py)
* Views: [sample_views.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_views.py)

## Installation

Expand All @@ -20,6 +22,17 @@ $ pip install azure-monitor-opentelemetry-exporter --pre

## Run the Applications

### Metrics with attributes

* Update `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable

* Run the sample

```sh
$ # from this directory
$ python sample_attributes.py
```

### Instrument usage

* Update `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable
Expand All @@ -31,6 +44,17 @@ $ # from this directory
$ python sample_instruments.py
```

### Configuring metrics with views

* Update `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable

* Run the sample

```sh
$ # from this directory
$ python sample_views.py
```

## Explore the data

After running the applications, data would be available in [Azure](
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
An example to show an application using different attributes with instruments in the OpenTelemetry SDK.
Metrics created and recorded using the sdk are tracked and telemetry is exported to application insights
with the AzureMonitorMetricsExporter.
with the AzureMonitorMetricExporter.
"""
import os

Expand All @@ -16,7 +16,8 @@
exporter = AzureMonitorMetricExporter.from_connection_string(
os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
)
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
# Metrics are reported every 1 minute
reader = PeriodicExportingMetricReader(exporter)
metrics.set_meter_provider(MeterProvider(metric_readers=[reader]))

attribute_set1 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
# cSpell:disable
"""
An example to show an application using all instruments in the OpenTelemetry SDK. Metrics created
and recorded using the sdk are tracked and telemetry is exported to application insights with the
AzureMonitorMetricsExporter.
AzureMonitorMetricExporter.
"""
import os
from typing import Iterable
Expand All @@ -18,7 +19,8 @@
exporter = AzureMonitorMetricExporter.from_connection_string(
os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
)
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
# Metrics are reported every 1 minute
reader = PeriodicExportingMetricReader(exporter)
metrics.set_meter_provider(MeterProvider(metric_readers=[reader]))

# Create a namespaced meter
Expand Down Expand Up @@ -63,3 +65,5 @@ def observable_gauge_func(options: CallbackOptions) -> Iterable[Observation]:

# Async Gauge
gauge = meter.create_observable_gauge("gauge", [observable_gauge_func])

# cSpell:disable
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
This example shows how to customize the metrics that are output by the SDK using Views. Metrics created
and recorded using the sdk are tracked and telemetry is exported to application insights with the
AzureMonitorMetricsExporter.
AzureMonitorMetricExporter.
"""
import os

Expand All @@ -25,7 +25,8 @@
name="my.counter.total",
)

reader = PeriodicExportingMetricReader(exporter, export_interval_millis=5000)
# Metrics are reported every 1 minute
reader = PeriodicExportingMetricReader(exporter)
provider = MeterProvider(
metric_readers=[
reader,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@
# Approximately 25% of these spans should be sampled out
with tracer.start_as_current_span("hello"):
print("Hello, World!")

input()
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import unittest
from unittest import mock

from azure.monitor.opentelemetry.exporter.export.trace._sampling import (
ApplicationInsightsSampler,
Expand All @@ -21,3 +22,19 @@ def test_invalid_ratio(self):
self.assertRaises(
ValueError, lambda: ApplicationInsightsSampler(-0.01)
)

@mock.patch.object(ApplicationInsightsSampler, '_get_DJB2_sample_score')
def test_should_sample(self, score_mock):
sampler = ApplicationInsightsSampler(0.75)
score_mock.return_value = 0.7
result = sampler.should_sample(None, 0, "test")
self.assertEqual(result.attributes["_MS.sampleRate"], 75)
self.assertTrue(result.decision.is_sampled())

@mock.patch.object(ApplicationInsightsSampler, '_get_DJB2_sample_score')
def test_should_sample_not_sampled(self, score_mock):
sampler = ApplicationInsightsSampler(0.5)
score_mock.return_value = 0.7
result = sampler.should_sample(None, 0, "test")
self.assertEqual(result.attributes["_MS.sampleRate"], 50)
self.assertFalse(result.decision.is_sampled())
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,34 @@ def test_span_to_envelope_success_error(self):
envelope = exporter._span_to_envelope(span)
self.assertFalse(envelope.data.base_data.success)

def test_span_to_envelope_sample_rate(self):
exporter = self._exporter
start_time = 1575494316027613500
end_time = start_time + 1001000000

span = trace._Span(
name="test",
context=SpanContext(
trace_id=36873507687745823477771305566750195431,
span_id=12030755672171557337,
is_remote=False,
),
attributes={
"test": "asd",
"http.method": "GET",
"http.url": "https://www.wikipedia.org/wiki/Rabbit",
"http.status_code": 200,
"_MS.sampleRate": 50,
},
kind=SpanKind.CLIENT,
)
span._status = Status(status_code=StatusCode.OK)
span.start(start_time=start_time)
span.end(end_time=end_time)
envelope = exporter._span_to_envelope(span)
self.assertEqual(envelope.sample_rate, 50)
self.assertIsNone(envelope.data.base_data.properties.get("_MS.sampleRate"))

def test_span_to_envelope_properties(self):
exporter = self._exporter
start_time = 1575494316027613500
Expand Down