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 @@ -5,6 +5,8 @@
### Features Added

### Breaking Changes
- Fix to accommodate breaking log changes from Otel
([#43626](https://github.com/Azure/azure-sdk-for-python/pull/43626))
- Pin OpenTelemetry versions to guard against upstream logging breaking changes
([#44220](https://github.com/Azure/azure-sdk-for-python/pull/44220))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from opentelemetry import metrics
from opentelemetry.metrics import CallbackOptions, Observation
from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs import ReadableLogRecord
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.semconv.attributes.exception_attributes import (
EXCEPTION_MESSAGE,
Expand Down Expand Up @@ -632,13 +632,13 @@ def _record_span(self, span: ReadableSpan) -> None:
except Exception: # pylint: disable=broad-except
_logger.exception("Exception occurred while recording span.") # pylint: disable=C4769

def _record_log_record(self, log_data: LogData) -> None:
def _record_log_record(self, readable_log_record: ReadableLogRecord) -> None:
try:
# pylint: disable=global-statement
global _EXCEPTIONS_COUNT
if log_data.log_record:
if readable_log_record.log_record:
exc_type = None
log_record = log_data.log_record
log_record = readable_log_record.log_record
if log_record.attributes:
exc_type = log_record.attributes.get(EXCEPTION_TYPE)
exc_message = log_record.attributes.get(EXCEPTION_MESSAGE)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from opentelemetry.sdk._logs import LogData, LogRecordProcessor
from opentelemetry.sdk._logs import ReadableLogRecord, LogRecordProcessor
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor

from azure.monitor.opentelemetry.exporter._performance_counters._manager import _PerformanceCountersManager
Expand All @@ -13,18 +13,18 @@ def __init__(self):
super().__init__()
self.call_on_emit = hasattr(super(), 'on_emit')

def on_emit(self, log_data: LogData) -> None: # type: ignore
def on_emit(self, readable_log_record: ReadableLogRecord) -> None: # type: ignore # pylint: disable=arguments-renamed
pcm = _PerformanceCountersManager()
if pcm:
pcm._record_log_record(log_data)
pcm._record_log_record(readable_log_record)
if self.call_on_emit:
super().on_emit(log_data) # type: ignore[safe-super]
super().on_emit(readable_log_record) # type: ignore[safe-super]
else:
# this method was removed in opentelemetry-sdk and replaced with on_emit
super().emit(log_data) # type: ignore[safe-super,misc] # pylint: disable=no-member
super().emit(readable_log_record) # type: ignore[safe-super,misc] # pylint: disable=no-member

def emit(self, log_data: LogData) -> None:
self.on_emit(log_data)
def emit(self, readable_log_record: ReadableLogRecord) -> None:
self.on_emit(readable_log_record)

def shutdown(self):
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import psutil

from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs import ReadableLogRecord
from opentelemetry.sdk.metrics import MeterProvider, Meter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import ReadableSpan
Expand Down Expand Up @@ -353,7 +353,7 @@ def _record_span(self, span: ReadableSpan) -> None:
except Exception as e: # pylint: disable=broad-except
_logger.exception("Exception occurred while recording span: %s", e) # pylint: disable=C4769

def _record_log_record(self, log_data: LogData) -> None:
def _record_log_record(self, readable_log_record: ReadableLogRecord) -> None:
# Only record if in post state and manager is initialized
if not (_is_post_state() and self.is_initialized()):
return
Expand All @@ -364,9 +364,9 @@ def _record_log_record(self, log_data: LogData) -> None:
return

try:
if log_data.log_record:
if readable_log_record.log_record:
exc_type = None
log_record = log_data.log_record
log_record = readable_log_record.log_record
if log_record.attributes:
exc_type = log_record.attributes.get(SpanAttributes.EXCEPTION_TYPE)
exc_message = log_record.attributes.get(SpanAttributes.EXCEPTION_MESSAGE)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from opentelemetry.sdk._logs import LogData, LogRecordProcessor
from opentelemetry.sdk._logs import ReadableLogRecord, LogRecordProcessor
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor

from azure.monitor.opentelemetry.exporter._quickpulse._state import get_quickpulse_manager
Expand All @@ -13,18 +13,18 @@ def __init__(self):
super().__init__()
self.call_on_emit = hasattr(super(), 'on_emit')

def on_emit(self, log_data: LogData) -> None: # type: ignore
def on_emit(self, readable_log_record: ReadableLogRecord) -> None: # type: ignore # pylint: disable=arguments-renamed
qpm = get_quickpulse_manager()
if qpm:
qpm._record_log_record(log_data)
qpm._record_log_record(readable_log_record)
if self.call_on_emit:
super().on_emit(log_data) # type: ignore[safe-super]
super().on_emit(readable_log_record) # type: ignore[safe-super]
else:
# this method was removed in opentelemetry-sdk and replaced with on_emit
super().emit(log_data) # type: ignore[safe-super,misc] # pylint: disable=no-member
super().emit(readable_log_record) # type: ignore[safe-super,misc] # pylint: disable=no-member

def emit(self, log_data: LogData) -> None:
self.on_emit(log_data)
def emit(self, readable_log_record: ReadableLogRecord) -> None: # pylint: disable=arguments-renamed
self.on_emit(readable_log_record)

def shutdown(self):
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from dataclasses import dataclass, fields
from typing import Dict, no_type_check

from opentelemetry.sdk._logs import LogRecord
from opentelemetry.sdk.trace import Event, ReadableSpan
from opentelemetry._logs import LogRecord
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.semconv._incubating.attributes import gen_ai_attributes
from opentelemetry.semconv.attributes.http_attributes import (
HTTP_REQUEST_METHOD,
Expand Down Expand Up @@ -177,7 +177,7 @@ def _from_log_record(log_record: LogRecord):

@staticmethod
@no_type_check
def _from_span_event(span_event: Event):
def _from_span_event(span_event: LogRecord):
return _ExceptionData(
message=str(span_event.attributes.get(SpanAttributes.EXCEPTION_MESSAGE, "")),
stack_trace=str(span_event.attributes.get(SpanAttributes.EXCEPTION_STACKTRACE, "")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
EXCEPTION_STACKTRACE,
EXCEPTION_TYPE,
)
from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs.export import LogExporter, LogExportResult
from opentelemetry.sdk._logs import ReadableLogRecord
from opentelemetry.sdk._logs.export import LogRecordExporter, LogRecordExportResult

from azure.monitor.opentelemetry.exporter import _utils
from azure.monitor.opentelemetry.exporter._constants import (
Expand Down Expand Up @@ -53,16 +53,16 @@
__all__ = ["AzureMonitorLogExporter"]


class AzureMonitorLogExporter(BaseExporter, LogExporter):
class AzureMonitorLogExporter(BaseExporter, LogRecordExporter):
"""Azure Monitor Log exporter for OpenTelemetry."""

def export(self, batch: Sequence[LogData], **kwargs: Any) -> LogExportResult: # pylint: disable=unused-argument
def export(self, batch: Sequence[ReadableLogRecord], **kwargs: Any) -> LogRecordExportResult: # pylint: disable=unused-argument
"""Export log data.

:param batch: OpenTelemetry LogData(s) to export.
:type batch: ~typing.Sequence[~opentelemetry._logs.LogData]
:param batch: OpenTelemetry ReadableLogRecord(s) to export.
:type batch: ~typing.Sequence[~opentelemetry._logs.ReadableLogRecord]
:return: The result of the export.
:rtype: ~opentelemetry.sdk._logs.export.LogData
:rtype: ~opentelemetry.sdk._logs.export.ReadableLogRecord
"""
envelopes = [self._log_to_envelope(log) for log in batch]
try:
Expand All @@ -81,8 +81,8 @@ def shutdown(self) -> None:
if self.storage:
self.storage.close()

def _log_to_envelope(self, log_data: LogData) -> TelemetryItem:
envelope = _convert_log_to_envelope(log_data)
def _log_to_envelope(self, readable_log_record: ReadableLogRecord) -> TelemetryItem:
envelope = _convert_log_to_envelope(readable_log_record)
envelope.instrumentation_key = self._instrumentation_key
return envelope

Expand All @@ -106,8 +106,8 @@ def from_connection_string(cls, conn_str: str, **kwargs: Any) -> "AzureMonitorLo
return cls(connection_string=conn_str, **kwargs)


def _log_data_is_event(log_data: LogData) -> bool:
log_record = log_data.log_record
def _log_data_is_event(readable_log_record: ReadableLogRecord) -> bool:
log_record = readable_log_record.log_record
is_event = None
if log_record.attributes:
is_event = log_record.attributes.get(_MICROSOFT_CUSTOM_EVENT_NAME) or \
Expand All @@ -117,11 +117,11 @@ def _log_data_is_event(log_data: LogData) -> bool:

# pylint: disable=protected-access
# pylint: disable=too-many-statements
def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
log_record = log_data.log_record
def _convert_log_to_envelope(readable_log_record: ReadableLogRecord) -> TelemetryItem:
log_record = readable_log_record.log_record
time_stamp = log_record.timestamp if log_record.timestamp is not None else log_record.observed_timestamp
envelope = _utils._create_telemetry_item(time_stamp)
envelope.tags.update(_utils._populate_part_a_fields(log_record.resource)) # type: ignore
envelope.tags.update(_utils._populate_part_a_fields(readable_log_record.resource)) # type: ignore
envelope.tags[ContextTagKeys.AI_OPERATION_ID] = "{:032x}".format( # type: ignore
log_record.trace_id or _DEFAULT_TRACE_ID
)
Expand Down Expand Up @@ -177,7 +177,7 @@ def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
exceptions=[exc_details],
)
envelope.data = MonitorBase(base_data=data, base_type="ExceptionData")
elif _log_data_is_event(log_data): # Event telemetry
elif _log_data_is_event(readable_log_record): # Event telemetry
_set_statsbeat_custom_events_feature()
envelope.name = "Microsoft.ApplicationInsights.Event"
event_name = ""
Expand Down Expand Up @@ -207,10 +207,10 @@ def _convert_log_to_envelope(log_data: LogData) -> TelemetryItem:
return envelope


def _get_log_export_result(result: ExportResult) -> LogExportResult:
def _get_log_export_result(result: ExportResult) -> LogRecordExportResult:
if result == ExportResult.SUCCESS:
return LogExportResult.SUCCESS
return LogExportResult.FAILURE
return LogRecordExportResult.SUCCESS
return LogRecordExportResult.FAILURE


# pylint: disable=line-too-long
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from typing import Optional, Dict, Any

from opentelemetry.sdk._logs import LogData
from opentelemetry.sdk._logs import ReadableLogRecord
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, LogExporter
from opentelemetry.trace import get_current_span

Expand All @@ -26,24 +26,24 @@ def __init__(
self._options = options or {}
self._enable_trace_based_sampling_for_logs = self._options.get("enable_trace_based_sampling_for_logs")

def on_emit(self, log_data: LogData) -> None:
def on_emit(self, readable_log_record: ReadableLogRecord) -> None: # pylint: disable=arguments-renamed
# cspell: disable
""" Determines whether the logger should drop log records associated with unsampled traces.
If `trace_based_sampling` is `true`, log records associated with unsampled traces are dropped by the `Logger`.
A log record is considered associated with an unsampled trace if it has a valid `SpanId` and its
`TraceFlags` indicate that the trace is unsampled. A log record that isn't associated with a trace
context is not affected by this parameter and therefore bypasses trace based sampling filtering.

:param log_data: Contains the log record to be exported
:type log_data: LogData
:param readable_log_record: Contains the log record to be exported
:type readable_log_record: ReadableLogRecord
"""

# cspell: enable
if self._enable_trace_based_sampling_for_logs:
if hasattr(log_data, "log_record") and log_data.log_record is not None:
if hasattr(log_data.log_record, "context") and log_data.log_record.context is not None:
span = get_current_span(log_data.log_record.context)
if hasattr(readable_log_record, "log_record") and readable_log_record.log_record is not None:
if hasattr(readable_log_record.log_record, "context") and readable_log_record.log_record.context is not None: # pylint: disable=line-too-long
span = get_current_span(readable_log_record.log_record.context)
span_context = span.get_span_context()
if span_context.is_valid and not span_context.trace_flags.sampled:
return
super().on_emit(log_data)
super().on_emit(readable_log_record)
4 changes: 2 additions & 2 deletions sdk/monitor/azure-monitor-opentelemetry-exporter/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
"azure-core<2.0.0,>=1.28.0",
"azure-identity~=1.17",
"msrest>=0.6.10",
"opentelemetry-api==1.38",
"opentelemetry-sdk==1.38",
"opentelemetry-api==1.39",
"opentelemetry-sdk==1.39",
"psutil>=5.9,<8",
],
entry_points={
Expand Down
Loading
Loading