diff --git a/logfire/_internal/logs.py b/logfire/_internal/logs.py index c3ccfcbe9..3961e29a2 100644 --- a/logfire/_internal/logs.py +++ b/logfire/_internal/logs.py @@ -4,7 +4,7 @@ import functools from dataclasses import dataclass from threading import Lock -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, overload from weakref import WeakSet from opentelemetry import trace @@ -93,7 +93,17 @@ class ProxyLogger(Logger): schema_url: str | None = None attributes: _ExtendedAttributes | None = None - def emit(self, record: LogRecord) -> None: # type: ignore + @overload + def emit(self, record: LogRecord) -> None: ... + + @overload + def emit(self, **kwargs: Any) -> None: ... + + def emit(self, record: LogRecord | None = None, **kwargs: Any) -> None: # type: ignore + # If record is not provided, create one from kwargs + if record is None: + record = LogRecord(**kwargs) + if record.severity_number is not None: if record.severity_number.value < self.min_level: return diff --git a/tests/test_otel_logs.py b/tests/test_otel_logs.py index 07a0afb85..50438594f 100644 --- a/tests/test_otel_logs.py +++ b/tests/test_otel_logs.py @@ -156,3 +156,46 @@ def export(self, batch: Sequence[ReadableLogRecord]): assert not connection_error_exporter.shutdown_called exporter.shutdown() assert connection_error_exporter.shutdown_called + + +def test_log_events_with_kwargs(logs_exporter: TestLogExporter) -> None: + logger = get_logger('scope') + with logfire.span('span'): + logger.emit( + event_name='my_event', + timestamp=2, + severity_number=SeverityNumber.INFO, + body='body', + attributes={'key': 'value'}, + ) + + assert logs_exporter.exported_logs_as_dicts(include_resources=True, include_instrumentation_scope=True) == snapshot( + [ + { + 'body': 'body', + 'severity_number': 9, + 'severity_text': None, + 'attributes': IsPartialDict({'key': 'value'}), + 'timestamp': 2000000000, + 'observed_timestamp': 3000000000, + 'trace_id': 1, + 'span_id': 1, + 'trace_flags': 1, + 'resource': { + 'attributes': { + 'service.instance.id': '00000000000000000000000000000000', + 'telemetry.sdk.language': 'python', + 'telemetry.sdk.name': 'opentelemetry', + 'telemetry.sdk.version': '0.0.0', + 'service.name': 'unknown_service', + 'process.pid': 1234, + 'process.runtime.name': 'cpython', + 'process.runtime.version': IsStr(), + 'process.runtime.description': IsStr(), + 'service.version': IsStr(), + }, + }, + 'instrumentation_scope': 'scope', + } + ] + )