Skip to content

Commit 4d9bf58

Browse files
authored
Implement customEvents via special microsoft marker (Azure#39886)
1 parent 67c3f50 commit 4d9bf58

File tree

5 files changed

+91
-14
lines changed

5 files changed

+91
-14
lines changed

sdk/monitor/azure-monitor-opentelemetry-exporter/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+
- Support sending `customEvent` telemetry through special `microsoft` marker
8+
([#39886](https://github.com/Azure/azure-sdk-for-python/pull/39886))
9+
710
### Breaking Changes
811

912
### Bugs Fixed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
# Feature constants
6464
_APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE = "APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE"
6565
_AZURE_MONITOR_DISTRO_VERSION_ARG = "distro_version"
66+
_MICROSOFT_CUSTOM_EVENT_NAME = "microsoft.custom_event.name"
6667

6768
# Statsbeat
6869

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
)
3535
from azure.monitor.opentelemetry.exporter._constants import (
3636
_APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE,
37+
_MICROSOFT_CUSTOM_EVENT_NAME,
3738
)
3839
from azure.monitor.opentelemetry.exporter.statsbeat._state import (
3940
get_statsbeat_shutdown,
@@ -103,12 +104,13 @@ def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "AzureMonitorLo
103104
return cls(connection_string=conn_str, **kwargs)
104105

105106

106-
def _log_data_is_event(log_data: LogData):
107+
def _log_data_is_event(log_data: LogData) -> bool:
107108
log_record = log_data.log_record
108-
is_event = False
109+
is_event = None
109110
if log_record.attributes:
110-
is_event = log_record.attributes.get(_APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE, False) # type: ignore
111-
return is_event is True
111+
is_event = log_record.attributes.get(_MICROSOFT_CUSTOM_EVENT_NAME) or \
112+
log_record.attributes.get(_APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE) # type: ignore
113+
return is_event is not None
112114

113115

114116
# pylint: disable=protected-access
@@ -133,17 +135,8 @@ def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
133135
stack_trace = log_record.attributes.get(EXCEPTION_STACKTRACE)
134136
severity_level = _get_severity_level(log_record.severity_number)
135137

136-
# Event telemetry
137-
if _log_data_is_event(log_data):
138-
_set_statsbeat_custom_events_feature()
139-
envelope.name = "Microsoft.ApplicationInsights.Event"
140-
data = TelemetryEventData(
141-
name=_map_body_to_message(log_record.body),
142-
properties=properties,
143-
)
144-
envelope.data = MonitorBase(base_data=data, base_type="EventData")
145138
# Exception telemetry
146-
elif exc_type is not None or exc_message is not None:
139+
if exc_type is not None or exc_message is not None:
147140
envelope.name = _EXCEPTION_ENVELOPE_NAME
148141
has_full_stack = stack_trace is not None
149142
if not exc_type:
@@ -167,6 +160,19 @@ def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
167160
exceptions=[exc_details],
168161
)
169162
envelope.data = MonitorBase(base_data=data, base_type="ExceptionData")
163+
elif _log_data_is_event(log_data): # Event telemetry
164+
_set_statsbeat_custom_events_feature()
165+
envelope.name = "Microsoft.ApplicationInsights.Event"
166+
event_name = ""
167+
if log_record.attributes.get(_MICROSOFT_CUSTOM_EVENT_NAME): # type: ignore
168+
event_name = str(log_record.attributes.get(_MICROSOFT_CUSTOM_EVENT_NAME)) # type: ignore
169+
else:
170+
event_name = _map_body_to_message(log_record.body)
171+
data = TelemetryEventData( # type: ignore
172+
name=event_name,
173+
properties=properties,
174+
)
175+
envelope.data = MonitorBase(base_data=data, base_type="EventData")
170176
else: # Message telemetry
171177
envelope.name = _MESSAGE_ENVELOPE_NAME
172178
# pylint: disable=line-too-long
@@ -223,6 +229,7 @@ def _is_ignored_attribute(key: str) -> bool:
223229
EXCEPTION_STACKTRACE,
224230
EXCEPTION_ESCAPED,
225231
_APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE,
232+
_MICROSOFT_CUSTOM_EVENT_NAME,
226233
)
227234
)
228235

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
"""
4+
An example to show an application using Opentelemetry logging sdk. Logging calls to the standard Python
5+
logging library are tracked and telemetry is exported to application insights with the AzureMonitorLogExporter.
6+
"""
7+
# mypy: disable-error-code="attr-defined"
8+
import os
9+
import logging
10+
11+
from opentelemetry._logs import (
12+
get_logger_provider,
13+
set_logger_provider,
14+
)
15+
from opentelemetry.sdk._logs import (
16+
LoggerProvider,
17+
LoggingHandler,
18+
)
19+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
20+
21+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
22+
23+
logger_provider = LoggerProvider()
24+
set_logger_provider(logger_provider)
25+
exporter = AzureMonitorLogExporter.from_connection_string(os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"])
26+
get_logger_provider().add_log_record_processor(BatchLogRecordProcessor(exporter, schedule_delay_millis=60000))
27+
28+
# Attach LoggingHandler to namespaced logger
29+
handler = LoggingHandler()
30+
logger = logging.getLogger(__name__)
31+
logger.addHandler(handler)
32+
logger.setLevel(logging.INFO)
33+
34+
# You can send `customEvent`` telemetry using a special `microsoft` attribute key through logging
35+
# The name of the `customEvent` will correspond to the value of the attribute`
36+
logger.info("Hello World!", extra={"microsoft.custom_event.name": "test-event-name", "additional_attrs": "val1"})
37+
38+
input()

sdk/monitor/azure-monitor-opentelemetry-exporter/tests/logs/test_logs.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
)
2929
from azure.monitor.opentelemetry.exporter._constants import (
3030
_APPLICATION_INSIGHTS_EVENT_MARKER_ATTRIBUTE,
31+
_MICROSOFT_CUSTOM_EVENT_NAME,
3132
)
3233
from azure.monitor.opentelemetry.exporter._generated.models import ContextTagKeys
3334
from azure.monitor.opentelemetry.exporter._utils import (
@@ -181,6 +182,23 @@ def setUpClass(cls):
181182
),
182183
InstrumentationScope("test_name"),
183184
)
185+
cls._log_data_custom_event = _logs.LogData(
186+
_logs.LogRecord(
187+
timestamp=1646865018558419456,
188+
trace_id=125960616039069540489478540494783893221,
189+
span_id=2909973987304607650,
190+
severity_text="INFO",
191+
trace_flags=None,
192+
severity_number=SeverityNumber.INFO,
193+
body="Test Event",
194+
resource=Resource.create(attributes={"asd": "test_resource"}),
195+
attributes={
196+
"event_key": "event_attribute",
197+
_MICROSOFT_CUSTOM_EVENT_NAME: "event_name",
198+
},
199+
),
200+
InstrumentationScope("test_name"),
201+
)
184202
cls._exc_data = _logs.LogData(
185203
_logs.LogRecord(
186204
timestamp=1646865018558419456,
@@ -515,6 +533,16 @@ def test_log_to_envelope_event_complex_body_not_serializeable(self):
515533
self.assertEqual(envelope.data.base_data.name, str(record.body))
516534
self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute")
517535

536+
def test_log_to_envelope_custom_event(self):
537+
exporter = self._exporter
538+
envelope = exporter._log_to_envelope(self._log_data_custom_event)
539+
record = self._log_data_custom_event.log_record
540+
self.assertEqual(envelope.name, "Microsoft.ApplicationInsights.Event")
541+
self.assertEqual(envelope.time, ns_to_iso_str(record.timestamp))
542+
self.assertEqual(envelope.data.base_type, "EventData")
543+
self.assertEqual(envelope.data.base_data.name, "event_name")
544+
self.assertEqual(envelope.data.base_data.properties["event_key"], "event_attribute")
545+
518546
def test_log_to_envelope_timestamp(self):
519547
exporter = self._exporter
520548
old_record = self._log_data.log_record

0 commit comments

Comments
 (0)