Skip to content

Commit 6fbfdeb

Browse files
authored
Implementing logging exporter for Azure Monitor (Azure#23486)
1 parent 7d2cc30 commit 6fbfdeb

File tree

21 files changed

+796
-80
lines changed

21 files changed

+796
-80
lines changed

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

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

55
### Features Added
6+
- Implement log exporter using experimental OT logging sdk
7+
([#23486](https://github.com/Azure/azure-sdk-for-python/pull/23486))
68

79
### Breaking Changes
810

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

Lines changed: 171 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Gitter chat](https://img.shields.io/gitter/room/Microsoft/azure-monitor-python)](https://gitter.im/Azure/azure-sdk-for-python)
44

5-
The exporter for Azure Monitor allows you to export tracing data utilizing the OpenTelemetry SDK and send telemetry data to Azure Monitor for applications written in Python.
5+
The exporter for Azure Monitor allows you to export data utilizing the OpenTelemetry SDK and send telemetry data to Azure Monitor for applications written in Python.
66

77
[Source code](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry-exporter) | [Package (PyPi)][pypi] | [API reference documentation][api_docs] | [Product documentation][product_docs] | [Samples](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry-exporter/samples) | [Changelog](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/CHANGELOG.md)
88

@@ -25,10 +25,19 @@ To use this package, you must have:
2525

2626
### Instantiate the client
2727

28-
Interaction with Azure monitor exporter starts with an instance of the `AzureMonitorTraceExporter` class. You will need a **connection_string** to instantiate the object.
28+
Interaction with Azure monitor exporter starts with an instance of the `AzureMonitorTraceExporter` class for distributed tracing or `AzureMonitorTraceExporter` for logging. You will need a **connection_string** to instantiate the object.
2929
Please find the samples linked below for demonstration as to how to construct the exporter using a connection string.
3030

31-
#### [Create Exporter from connection string][sample_authenticate_client_connstr]
31+
#### Logging
32+
33+
```Python
34+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
35+
exporter = AzureMonitorLogExporter.from_connection_string(
36+
conn_str = os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
37+
)
38+
```
39+
40+
#### Tracing
3241

3342
```Python
3443
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
@@ -39,6 +48,11 @@ exporter = AzureMonitorTraceExporter.from_connection_string(
3948

4049
You can also instantiate the exporter directly via the constructor. In this case, the connection string will be automatically populated from the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable.
4150

51+
```python
52+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
53+
exporter = AzureMonitorLogExporter()
54+
```
55+
4256
```python
4357
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
4458
exporter = AzureMonitorTraceExporter()
@@ -52,26 +66,164 @@ Some of the key concepts for the Azure monitor exporter include:
5266

5367
* [Instrumentation][instrumentation_library]: The ability to call the opentelemetry API directly by any application is facilitated by instrumentation. A library that enables OpenTelemetry observability for another library is called an instrumentation Library.
5468

55-
* [Trace][trace_concept]: Trace refers to distributed tracing. It can be thought of as a directed acyclic graph (DAG) of Spans, where the edges between Spans are defined as parent/child relationship.
69+
* [Log][log_concept]: Log refers to capturing of logging, exception and events.
70+
71+
* [LogRecord][log_record]: Represents a log record emitted from a supported logging library.
72+
73+
* [LogEmitter][log_emitter]: Converts a `LogRecord` into a readable `LogData`, and will be pushed through the SDK to be exported.
74+
75+
* [LogEmitter Provider][log_emitter_provider]: Provides a `LogEmitter` for the given instrumentation library.
76+
77+
* [LogProcessor][log_processor]: Inteface to hook the log record emitting action.
78+
79+
* [LoggingHandler][logging_handler]: A handler class which writes logging records in OpenTelemetry format from the standard Python `logging` library.
80+
81+
* [AzureMonitorLogExporter][log_reference]: This is the class that is initialized to send logging related telemetry to Azure Monitor.
82+
83+
* [Trace][trace_concept]: Trace refers to distributed tracing. It can be thought of as a directed acyclic graph (DAG) of `Span`s, where the edges between `Span`s are defined as parent/child relationship.
84+
85+
* [Span][span]: Represents a single operation within a `Trace`. Can be nested to form a trace tree. Each trace contains a root span, which typically describes the entire operation and, optionally, one ore more sub-spans for its sub-operations.
86+
87+
* [Tracer][tracer]: Responsible for creating `Span`s.
5688

5789
* [Tracer Provider][tracer_provider]: Provides a `Tracer` for use by the given instrumentation library.
5890

5991
* [Span Processor][span_processor]: A span processor allows hooks for SDK's `Span` start and end method invocations. Follow the link for more information.
6092

61-
* [Sampling][sampler_ref]: Sampling is a mechanism to control the noise and overhead introduced by OpenTelemetry by reducing the number of samples of traces collected and sent to the backend.
93+
* [AzureMonitorTraceExporter][trace_reference]: This is the class that is initialized to send tracing related telemetry to Azure Monitor.
6294

63-
* [AzureMonitorTraceExporter][client_reference]: This is the class that is initialized to send tracing related telemetry to Azure Monitor.
95+
* [Sampling][sampler_ref]: Sampling is a mechanism to control the noise and overhead introduced by OpenTelemetry by reducing the number of samples of traces collected and sent to the backend.
6496

6597
For more information about these resources, see [What is Azure Monitor?][product_docs].
6698

6799
## Examples
68100

101+
### Logging
102+
103+
The following sections provide several code snippets covering some of the most common tasks, including:
104+
105+
* [Exporting a log record](#export-hello-world-log)
106+
* [Exporting correlated log record](#export-correlated-log)
107+
* [Exporting log record with custom properties](#export-custom-properties-log)
108+
109+
#### Export Hello World Log
110+
111+
```Python
112+
import os
113+
import logging
114+
115+
from opentelemetry.sdk._logs import (
116+
LogEmitterProvider,
117+
OTLPHandler,
118+
set_log_emitter_provider,
119+
)
120+
from opentelemetry.sdk._logs.export import BatchLogProcessor
121+
122+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
123+
124+
log_emitter_provider = LogEmitterProvider()
125+
set_log_emitter_provider(log_emitter_provider)
126+
127+
exporter = AzureMonitorLogExporter.from_connection_string(
128+
os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
129+
)
130+
131+
log_emitter_provider.add_log_processor(BatchLogProcessor(exporter))
132+
handler = OTLPHandler()
133+
134+
# Attach OTLPhandler to root logger
135+
logging.getLogger().addHandler(handler)
136+
logging.getLogger().setLevel(logging.NOTSET)
137+
138+
logger = logging.getLogger(__name__)
139+
140+
logger.warning("Hello World!")
141+
```
142+
143+
#### Export Correlated Log
144+
145+
```Python
146+
import os
147+
import logging
148+
149+
from opentelemetry import trace
150+
from opentelemetry.sdk._logs import (
151+
LogEmitterProvider,
152+
OTLPHandler,
153+
set_log_emitter_provider,
154+
)
155+
from opentelemetry.sdk._logs.export import BatchLogProcessor
156+
from opentelemetry.sdk.trace import TracerProvider
157+
158+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
159+
160+
trace.set_tracer_provider(TracerProvider())
161+
tracer = trace.get_tracer(__name__)
162+
log_emitter_provider = LogEmitterProvider()
163+
set_log_emitter_provider(LogEmitterProvider())
164+
165+
exporter = AzureMonitorLogExporter.from_connection_string(
166+
os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
167+
)
168+
169+
log_emitter_provider.add_log_processor(BatchLogProcessor(exporter))
170+
handler = OTLPHandler()
171+
172+
# Attach OTel handler to root logger
173+
logging.getLogger().addHandler(handler)
174+
logging.getLogger().setLevel(logging.NOTSET)
175+
176+
logger = logging.getLogger(__name__)
177+
178+
logger.info("INFO: Outside of span")
179+
with tracer.start_as_current_span("foo"):
180+
logger.warning("WARNING: Inside of span")
181+
logger.error("ERROR: After span")
182+
```
183+
184+
#### Export Custom Properties Log
185+
186+
```Python
187+
import os
188+
import logging
189+
190+
from opentelemetry.sdk._logs import (
191+
LogEmitterProvider,
192+
OTLPHandler,
193+
set_log_emitter_provider,
194+
)
195+
from opentelemetry.sdk._logs.export import BatchLogProcessor
196+
197+
from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter
198+
199+
log_emitter_provider = LogEmitterProvider()
200+
set_log_emitter_provider(LogEmitterProvider())
201+
202+
exporter = AzureMonitorLogExporter.from_connection_string(
203+
os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"]
204+
)
205+
206+
log_emitter_provider.add_log_processor(BatchLogProcessor(exporter))
207+
handler = OTLPHandler()
208+
209+
# Attach OTel handler to root logger
210+
logging.getLogger().addHandler(handler)
211+
logging.getLogger().setLevel(logging.NOTSET)
212+
213+
logger = logging.getLogger(__name__)
214+
215+
# Custom properties
216+
logger.debug("DEBUG: Debug with properties", extra={"debug": "true"})
217+
```
218+
219+
### Tracing
220+
69221
The following sections provide several code snippets covering some of the most common tasks, including:
70222

71223
* [Exporting a custom span](#export-hello-world-trace)
72224
* [Using an instrumentation to track a library](#instrumentation-with-requests-library)
73225

74-
### Export Hello World Trace
226+
#### Export Hello World Trace
75227

76228
```Python
77229
import os
@@ -93,7 +245,7 @@ with tracer.start_as_current_span("hello"):
93245
print("Hello, World!")
94246
```
95247

96-
### Instrumentation with requests library
248+
#### Instrumentation with requests library
97249

98250
OpenTelemetry also supports several instrumentations which allows to instrument with third party libraries.
99251

@@ -174,12 +326,19 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
174326
[virtualenv]: https://virtualenv.pypa.io
175327
[ot_sdk_python]: https://github.com/open-telemetry/opentelemetry-python
176328
[application_insights_namespace]: https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview#how-do-i-use-application-insights
177-
[trace_concept]: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/overview.md#trace
178-
[client_reference]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py#L30
179329
[opentelemtry_spec]: https://opentelemetry.io/
180330
[instrumentation_library]: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/overview.md#instrumentation-libraries
331+
[log_concept]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#log-signal
332+
[log_record]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/logs.html#opentelemetry.sdk._logs.LogRecord
333+
[log_emitter]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/logs.html#opentelemetry.sdk._logs.LogEmitter
334+
[log_emitter_provider]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/logs.html#opentelemetry.sdk._logs.LogEmitterProvider
335+
[log_processor]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/logs.html#opentelemetry.sdk._logs.LogProcessor
336+
[logging_handler]: https://opentelemetry-python.readthedocs.io/en/stable/sdk/logs.html#opentelemetry.sdk._logs.OTLPHandler
337+
[log_reference]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/logs/_exporter.py#L30
338+
[trace_concept]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#tracing-signal
339+
[span]: https://opentelemetry-python.readthedocs.io/en/stable/api/trace.html?highlight=TracerProvider#opentelemetry.trace.Span
340+
[tracer]: https://opentelemetry-python.readthedocs.io/en/stable/api/trace.html?highlight=TracerProvider#opentelemetry.trace.Tracer
181341
[tracer_provider]: https://opentelemetry-python.readthedocs.io/en/stable/api/trace.html?highlight=TracerProvider#opentelemetry.trace.TracerProvider
182342
[span_processor]: https://opentelemetry-python.readthedocs.io/en/stable/_modules/opentelemetry/sdk/trace.html?highlight=SpanProcessor#
343+
[trace_reference]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/trace/_exporter.py#L30
183344
[sampler_ref]: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk.md#sampling
184-
185-
[sample_authenticate_client_connstr]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_trace.py

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
# Licensed under the MIT License. See License.txt in the project root for license information.
55
# --------------------------------------------------------------------------
66

7+
from azure.monitor.opentelemetry.exporter.export.logs._exporter import AzureMonitorLogExporter
78
from azure.monitor.opentelemetry.exporter.export.trace._exporter import AzureMonitorTraceExporter
89
from ._version import VERSION
910

10-
__all__ = ["AzureMonitorTraceExporter"]
11+
__all__ = ["AzureMonitorLogExporter", "AzureMonitorTraceExporter"]
1112
__version__ = VERSION

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import pkg_resources
99

10+
from opentelemetry.semconv.resource import ResourceAttributes
11+
1012
from azure.monitor.opentelemetry.exporter._version import VERSION as ext_version
1113

1214

@@ -71,3 +73,22 @@ def run(self):
7173

7274
def cancel(self):
7375
self.finished.set()
76+
77+
def _populate_part_a_fields(resource):
78+
tags = {}
79+
if resource and resource.attributes:
80+
service_name = resource.attributes.get(ResourceAttributes.SERVICE_NAME)
81+
service_namespace = resource.attributes.get(ResourceAttributes.SERVICE_NAMESPACE)
82+
service_instance_id = resource.attributes.get(ResourceAttributes.SERVICE_INSTANCE_ID)
83+
if service_name:
84+
if service_namespace:
85+
tags["ai.cloud.role"] = service_namespace + \
86+
"." + service_name
87+
else:
88+
tags["ai.cloud.role"] = service_name
89+
if service_instance_id:
90+
tags["ai.cloud.roleInstance"] = service_instance_id
91+
else:
92+
tags["ai.cloud.roleInstance"] = platform.node() # hostname default
93+
tags["ai.internal.nodeName"] = tags["ai.cloud.roleInstance"]
94+
return tags

sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/export/_base.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
from typing import List, Any
88
from urllib.parse import urlparse
99

10-
from opentelemetry.sdk.trace.export import SpanExportResult
11-
1210
from azure.core.exceptions import HttpResponseError, ServiceRequestError
1311
from azure.core.pipeline.policies import ContentDecodePolicy, HttpLoggingPolicy, RedirectPolicy, RequestIdPolicy
1412
from azure.monitor.opentelemetry.exporter._generated import AzureMonitorClient
@@ -194,14 +192,3 @@ def _is_retryable_code(response_code: int) -> bool:
194192
503, # Service Unavailable
195193
504, # Gateway timeout
196194
))
197-
198-
199-
def get_trace_export_result(result: ExportResult) -> SpanExportResult:
200-
if result == ExportResult.SUCCESS:
201-
return SpanExportResult.SUCCESS
202-
if result in (
203-
ExportResult.FAILED_RETRYABLE,
204-
ExportResult.FAILED_NOT_RETRYABLE,
205-
):
206-
return SpanExportResult.FAILURE
207-
return None

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

Whitespace-only changes.

0 commit comments

Comments
 (0)