Skip to content
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

Remove custom_scope_suffix parameter of Logfire.log #399

Merged
merged 4 commits into from
Sep 2, 2024
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
8 changes: 1 addition & 7 deletions logfire-api/logfire_api/_internal/main.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ class Logfire:
span_name: The span name. If not provided, the `msg_template` will be used.
extract_args: Whether to extract arguments from the function signature and log them as span attributes.
"""
def log(self, level: LevelName | int, msg_template: str, attributes: dict[str, Any] | None = None, tags: Sequence[str] | None = None, exc_info: ExcInfo = False, console_log: bool | None = None, custom_scope_suffix: str | None = None) -> None:
def log(self, level: LevelName | int, msg_template: str, attributes: dict[str, Any] | None = None, tags: Sequence[str] | None = None, exc_info: ExcInfo = False, console_log: bool | None = None) -> None:
"""Log a message.

```py
Expand All @@ -261,12 +261,6 @@ class Logfire:

Set to `True` to use the currently handled exception.
console_log: Whether to log to the console, defaults to `True`.
custom_scope_suffix: A custom suffix to append to `logfire.` e.g. `logfire.loguru`.

It should only be used when instrumenting another library with Logfire, such as structlog or loguru.

See the `instrumenting_module_name` parameter on
[TracerProvider.get_tracer][opentelemetry.sdk.trace.TracerProvider.get_tracer] for more info.
"""
def with_tags(self, *tags: str) -> Logfire:
"""A new Logfire instance which always uses the given tags.
Expand Down
4 changes: 3 additions & 1 deletion logfire-api/logfire_api/integrations/logging.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .. import Logfire as Logfire
from .._internal.constants import ATTRIBUTES_LOGGING_ARGS_KEY as ATTRIBUTES_LOGGING_ARGS_KEY, ATTRIBUTES_MESSAGE_KEY as ATTRIBUTES_MESSAGE_KEY, ATTRIBUTES_MESSAGE_TEMPLATE_KEY as ATTRIBUTES_MESSAGE_TEMPLATE_KEY, LOGGING_TO_OTEL_LEVEL_NUMBERS as LOGGING_TO_OTEL_LEVEL_NUMBERS
from .._internal.utils import is_instrumentation_suppressed as is_instrumentation_suppressed
from _typeshed import Incomplete
Expand All @@ -10,7 +11,8 @@ class LogfireLoggingHandler(LoggingHandler):
"""A logging handler that sends logs to **Logfire**."""
custom_scope_suffix: ClassVar[str]
fallback: Incomplete
def __init__(self, level: int | str = ..., fallback: LoggingHandler = ...) -> None: ...
logfire_instance: Incomplete
def __init__(self, level: int | str = ..., fallback: LoggingHandler = ..., logfire_instance: Logfire | None = None) -> None: ...
def emit(self, record: LogRecord) -> None:
"""Send the log to Logfire.

Expand Down
4 changes: 3 additions & 1 deletion logfire-api/logfire_api/integrations/structlog.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .. import Logfire as Logfire
from .._internal.constants import ATTRIBUTES_MESSAGE_KEY as ATTRIBUTES_MESSAGE_KEY
from _typeshed import Incomplete
from structlog.types import EventDict, WrappedLogger
Expand All @@ -7,6 +8,7 @@ RESERVED_ATTRS: Incomplete
class LogfireProcessor:
"""Logfire processor for structlog."""
console_log: Incomplete
def __init__(self, *, console_log: bool = False) -> None: ...
logfire_instance: Incomplete
def __init__(self, *, console_log: bool = False, logfire_instance: Logfire | None = None) -> None: ...
def __call__(self, logger: WrappedLogger, name: str, event_dict: EventDict) -> EventDict:
"""A middleware to process structlog event, and send it to **Logfire**."""
14 changes: 1 addition & 13 deletions logfire/_internal/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,6 @@ def log(
tags: Sequence[str] | None = None,
exc_info: ExcInfo = False,
console_log: bool | None = None,
custom_scope_suffix: str | None = None,
) -> None:
"""Log a message.

Expand All @@ -575,12 +574,6 @@ def log(

Set to `True` to use the currently handled exception.
console_log: Whether to log to the console, defaults to `True`.
custom_scope_suffix: A custom suffix to append to `logfire.` e.g. `logfire.loguru`.

It should only be used when instrumenting another library with Logfire, such as structlog or loguru.

See the `instrumenting_module_name` parameter on
[TracerProvider.get_tracer][opentelemetry.sdk.trace.TracerProvider.get_tracer] for more info.
"""
with handle_internal_errors():
stack_info = get_user_stack_info()
Expand Down Expand Up @@ -640,12 +633,7 @@ def log(
otlp_attributes[DISABLE_CONSOLE_KEY] = True
start_time = self._config.ns_timestamp_generator()

if custom_scope_suffix:
tracer = self._get_tracer(is_span_tracer=False, otel_scope=f'logfire.{custom_scope_suffix}')
else:
tracer = self._logs_tracer

span = tracer.start_span(
span = self._logs_tracer.start_span(
msg_template,
attributes=otlp_attributes,
start_time=start_time,
Expand Down
18 changes: 14 additions & 4 deletions logfire/integrations/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from logging import NOTSET, Handler as LoggingHandler, LogRecord, StreamHandler
from typing import Any, ClassVar, Mapping, cast
from typing import TYPE_CHECKING, Any, ClassVar, Mapping, cast

import logfire

Expand Down Expand Up @@ -45,15 +45,26 @@
]
)

if TYPE_CHECKING:
from .. import Logfire


class LogfireLoggingHandler(LoggingHandler):
"""A logging handler that sends logs to **Logfire**."""

custom_scope_suffix: ClassVar[str] = 'stdlib.logging'

def __init__(self, level: int | str = NOTSET, fallback: LoggingHandler = StreamHandler()) -> None:
def __init__(
self,
level: int | str = NOTSET,
fallback: LoggingHandler = StreamHandler(),
logfire_instance: Logfire | None = None,
) -> None:
super().__init__(level=level)
self.fallback = fallback
self.logfire_instance = (logfire_instance or logfire.DEFAULT_LOGFIRE_INSTANCE).with_settings(
custom_scope_suffix=self.custom_scope_suffix
)

def emit(self, record: LogRecord) -> None:
"""Send the log to Logfire.
Expand All @@ -67,11 +78,10 @@ def emit(self, record: LogRecord) -> None:

attributes = self.fill_attributes(record)

logfire.log(
self.logfire_instance.log(
msg_template=attributes.pop(ATTRIBUTES_MESSAGE_TEMPLATE_KEY, record.msg),
level=LOGGING_TO_OTEL_LEVEL_NUMBERS.get(record.levelno, record.levelno),
attributes=attributes,
custom_scope_suffix=self.custom_scope_suffix,
exc_info=record.exc_info,
)

Expand Down
15 changes: 12 additions & 3 deletions logfire/integrations/structlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,36 @@
if TYPE_CHECKING:
from structlog.types import EventDict, WrappedLogger

from .. import Logfire

RESERVED_ATTRS = LOGGING_RESERVED_ATTRS | {'level', 'event', 'timestamp'}
"""Attributes to strip from the event before sending to Logfire."""


class LogfireProcessor:
"""Logfire processor for structlog."""

def __init__(self, *, console_log: bool = False) -> None:
def __init__(
self,
*,
console_log: bool = False,
logfire_instance: Logfire | None = None,
) -> None:
self.console_log = console_log
self.logfire_instance = (logfire_instance or logfire.DEFAULT_LOGFIRE_INSTANCE).with_settings(
custom_scope_suffix='structlog'
)

def __call__(self, logger: WrappedLogger, name: str, event_dict: EventDict) -> EventDict:
"""A middleware to process structlog event, and send it to **Logfire**."""
attributes = {k: v for k, v in event_dict.items() if k not in RESERVED_ATTRS}
level = event_dict.get('level', 'info').lower()
# NOTE: An event can be `None` in structlog. We may want to create a default msg in those cases.
attributes[ATTRIBUTES_MESSAGE_KEY] = message = event_dict.get('event') or 'structlog event'
logfire.log(
self.logfire_instance.log(
level=level, # type: ignore
msg_template=message,
attributes=attributes,
console_log=self.console_log,
custom_scope_suffix='structlog',
)
return event_dict
3 changes: 3 additions & 0 deletions tests/test_loguru.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,6 @@ def test_loguru(exporter: TestExporter) -> None:
},
]
)

for span in exporter.exported_spans:
assert span.instrumentation_scope.name == 'logfire.loguru' # type: ignore
3 changes: 3 additions & 0 deletions tests/test_stdlib_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ def test_logging_from_opentelemetry(exporter: TestExporter) -> None:
]
)

for span in exporter.exported_spans:
assert span.instrumentation_scope.name == 'logfire.stdlib.logging' # type: ignore


def test_logging_non_string(exporter: TestExporter, logger: Logger):
logger.error(123)
Expand Down
3 changes: 3 additions & 0 deletions tests/test_structlog.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,6 @@ def test_structlog(exporter: TestExporter, logger: Logger) -> None:
},
]
)

for span in exporter.exported_spans:
assert span.instrumentation_scope.name == 'logfire.structlog' # type: ignore