Skip to content

Logging API accepts optional Context with priority over trace_id etc, and LoggingHandler passes current Context #4597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4599](https://github.com/open-telemetry/opentelemetry-python/pull/4599))
- Drop support for Python 3.8
([#4520](https://github.com/open-telemetry/opentelemetry-python/pull/4520))
- Logging API accepts optional `context`; deprecates `trace_id`, `span_id`, `trace_flags`.
([#4597](https://github.com/open-telemetry/opentelemetry-python/pull/4597))

## Version 1.33.0/0.54b0 (2025-05-09)

Expand Down
41 changes: 39 additions & 2 deletions opentelemetry-api/src/opentelemetry/_logs/_internal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
from logging import getLogger
from os import environ
from time import time_ns
from typing import Optional, cast
from typing import Optional, cast, overload

from typing_extensions import deprecated

from opentelemetry._logs.severity import SeverityNumber
from opentelemetry.context.context import Context
from opentelemetry.environment_variables import _OTEL_PYTHON_LOGGER_PROVIDER
from opentelemetry.trace.span import TraceFlags
from opentelemetry.util._once import Once
Expand All @@ -57,8 +60,26 @@ class LogRecord(ABC):
pertinent to the event being logged.
"""

@overload
def __init__(
self,
*,
timestamp: Optional[int] = None,
observed_timestamp: Optional[int] = None,
context: Optional[Context] = None,
severity_text: Optional[str] = None,
severity_number: Optional[SeverityNumber] = None,
body: AnyValue = None,
attributes: Optional[_ExtendedAttributes] = None,
) -> None: ...

@overload
@deprecated(
"LogRecord init with `trace_id`, `span_id`, and/or `trace_flags` is deprecated. Use `context` instead."
)
def __init__(
self,
*,
timestamp: Optional[int] = None,
observed_timestamp: Optional[int] = None,
trace_id: Optional[int] = None,
Expand All @@ -68,11 +89,27 @@ def __init__(
severity_number: Optional[SeverityNumber] = None,
body: AnyValue = None,
attributes: Optional[_ExtendedAttributes] = None,
):
) -> None: ...

def __init__(
self,
*,
timestamp: Optional[int] = None,
observed_timestamp: Optional[int] = None,
context: Optional[Context] = None,
trace_id: Optional[int] = None,
span_id: Optional[int] = None,
trace_flags: Optional["TraceFlags"] = None,
severity_text: Optional[str] = None,
severity_number: Optional[SeverityNumber] = None,
body: AnyValue = None,
attributes: Optional[_ExtendedAttributes] = None,
) -> None:
self.timestamp = timestamp
if observed_timestamp is None:
observed_timestamp = time_ns()
self.observed_timestamp = observed_timestamp
self.context = context
self.trace_id = trace_id
self.span_id = span_id
self.trace_flags = trace_flags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
from os import environ
from threading import Lock
from time import time_ns
from typing import Any, Callable, Tuple, Union, cast # noqa
from typing import Any, Callable, Tuple, Union, cast, overload # noqa

from typing_extensions import deprecated

from opentelemetry._logs import Logger as APILogger
from opentelemetry._logs import LoggerProvider as APILoggerProvider
Expand All @@ -37,6 +39,8 @@
std_to_otel,
)
from opentelemetry.attributes import _VALID_ANY_VALUE_TYPES, BoundedAttributes
from opentelemetry.context import get_current
from opentelemetry.context.context import Context
from opentelemetry.sdk.environment_variables import (
OTEL_ATTRIBUTE_COUNT_LIMIT,
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
Expand Down Expand Up @@ -172,6 +176,24 @@ class LogRecord(APILogRecord):
pertinent to the event being logged.
"""

@overload
def __init__(
self,
timestamp: int | None = None,
observed_timestamp: int | None = None,
context: Context | None = None,
severity_text: str | None = None,
severity_number: SeverityNumber | None = None,
body: AnyValue | None = None,
resource: Resource | None = None,
attributes: _ExtendedAttributes | None = None,
limits: LogLimits | None = _UnsetLogLimits,
): ...

@overload
@deprecated(
"LogRecord init with `trace_id`, `span_id`, and/or `trace_flags` is deprecated. Use `context` instead."
)
def __init__(
self,
timestamp: int | None = None,
Expand All @@ -185,11 +207,39 @@ def __init__(
resource: Resource | None = None,
attributes: _ExtendedAttributes | None = None,
limits: LogLimits | None = _UnsetLogLimits,
): ...

def __init__(
self,
timestamp: int | None = None,
observed_timestamp: int | None = None,
context: Context | None = None,
trace_id: int | None = None,
span_id: int | None = None,
trace_flags: TraceFlags | None = None,
severity_text: str | None = None,
severity_number: SeverityNumber | None = None,
body: AnyValue | None = None,
resource: Resource | None = None,
attributes: _ExtendedAttributes | None = None,
limits: LogLimits | None = _UnsetLogLimits,
):
if not context:
context = get_current()

if context is not None:
span = get_current_span(context)
span_context = span.get_span_context()
if span_context.is_valid:
trace_id = span_context.trace_id
span_id = span_context.span_id
trace_flags = span_context.trace_flags

super().__init__(
**{
"timestamp": timestamp,
"observed_timestamp": observed_timestamp,
"context": context,
"trace_id": trace_id,
"span_id": span_id,
"trace_flags": trace_flags,
Expand Down
36 changes: 35 additions & 1 deletion opentelemetry-sdk/tests/logs/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
)
from opentelemetry.semconv._incubating.attributes import code_attributes
from opentelemetry.semconv.attributes import exception_attributes
from opentelemetry.trace import INVALID_SPAN_CONTEXT
from opentelemetry.trace import (
INVALID_SPAN_CONTEXT,
set_span_in_context,
)


class TestLoggingHandler(unittest.TestCase):
Expand Down Expand Up @@ -269,6 +272,37 @@ def __str__(self):
def test_log_record_trace_correlation(self):
processor, logger = set_up_test_logging(logging.WARNING)

tracer = trace.TracerProvider().get_tracer(__name__)
with tracer.start_as_current_span("test") as span:
mock_context = set_span_in_context(span)

with patch(
"opentelemetry.sdk._logs._internal.get_current",
return_value=mock_context,
):
with self.assertLogs(level=logging.CRITICAL):
logger.critical("Critical message within span")

log_record = processor.get_log_record(0)

self.assertEqual(
log_record.body, "Critical message within span"
)
self.assertEqual(log_record.severity_text, "CRITICAL")
self.assertEqual(
log_record.severity_number, SeverityNumber.FATAL
)
self.assertEqual(log_record.context, mock_context)
span_context = span.get_span_context()
self.assertEqual(log_record.trace_id, span_context.trace_id)
self.assertEqual(log_record.span_id, span_context.span_id)
self.assertEqual(
log_record.trace_flags, span_context.trace_flags
)

def test_log_record_trace_correlation_deprecated(self):
processor, logger = set_up_test_logging(logging.WARNING)

tracer = trace.TracerProvider().get_tracer(__name__)
with tracer.start_as_current_span("test") as span:
with self.assertLogs(level=logging.CRITICAL):
Expand Down