33from __future__ import annotations
44
55import inspect
6- import warnings
76from logging import LogRecord
7+ from pathlib import Path
88from typing import Any
99
10- from loguru import logger
10+ import loguru
1111
1212from .._internal .constants import ATTRIBUTES_LOGGING_ARGS_KEY , ATTRIBUTES_MESSAGE_KEY , ATTRIBUTES_MESSAGE_TEMPLATE_KEY
13+ from .._internal .stack_info import warn_at_user_stacklevel
1314from .logging import LogfireLoggingHandler
1415
16+ LOGURU_PATH = Path (loguru .__file__ ).parent
17+
18+
19+ class LoguruInspectionFailed (RuntimeWarning ):
20+ """Warning raised when magic introspection of loguru stack frames fails.
21+
22+ This may happen if the loguru library changes in a way that breaks the introspection.
23+ """
24+
1525
1626class LogfireHandler (LogfireLoggingHandler ):
1727 """A loguru handler that sends logs to **Logfire**."""
@@ -38,51 +48,49 @@ def fill_attributes(self, record: LogRecord) -> dict[str, Any]:
3848 while frame : # pragma: no branch
3949 if frame .f_code is _LOG_METHOD_CODE :
4050 frame_locals = frame .f_locals
41- if 'message' in frame_locals :
51+ if 'message' in frame_locals : # pragma: no branch
4252 attributes [ATTRIBUTES_MESSAGE_TEMPLATE_KEY ] = frame_locals ['message' ]
4353 else : # pragma: no cover
44- _warn_inspection_failure ()
54+ warn_at_user_stacklevel (
55+ 'Failed to extract message template (span name) for loguru log.' , LoguruInspectionFailed
56+ )
4557
4658 args = frame_locals .get ('args' )
47- if isinstance (args , (tuple , list )):
59+ if isinstance (args , (tuple , list )): # pragma: no branch
4860 if args :
4961 attributes [ATTRIBUTES_LOGGING_ARGS_KEY ] = args
5062 else : # pragma: no cover
51- _warn_inspection_failure ()
52-
53- if record .exc_info :
54- original_record = frame_locals .get ('log_record' )
55- if isinstance (original_record , dict ):
56- message = original_record .get ('message' ) # type: ignore
57- if isinstance (message , str ) and record .msg .startswith (
58- message + '\n Traceback (most recent call last):'
59- ):
60- # `record.msg` includes a traceback added by Loguru,
61- # replace it with the original message.
62- attributes [ATTRIBUTES_MESSAGE_KEY ] = message
63- else : # pragma: no cover
64- _warn_inspection_failure ()
65- else : # pragma: no cover
66- _warn_inspection_failure ()
63+ warn_at_user_stacklevel ('Failed to extract args for loguru log.' , LoguruInspectionFailed )
64+
65+ original_record : dict [str , Any ] | None = frame_locals .get ('log_record' )
66+ if (
67+ isinstance (original_record , dict )
68+ and isinstance (message := original_record .get ('message' ), str )
69+ and message in record .msg
70+ ): # pragma: no branch
71+ # `record.msg` may include a traceback added by Loguru,
72+ # replace it with the original message.
73+ attributes [ATTRIBUTES_MESSAGE_KEY ] = message
74+ else : # pragma: no cover
75+ warn_at_user_stacklevel (
76+ 'Failed to extract original message for loguru log.' , LoguruInspectionFailed
77+ )
6778
6879 break
6980
7081 frame = frame .f_back
82+ else : # pragma: no cover
83+ warn_at_user_stacklevel (
84+ 'Failed to find loguru log frame to extract detailed information' , LoguruInspectionFailed
85+ )
7186
7287 return attributes
7388
7489
75- def _warn_inspection_failure () -> None : # pragma: no cover
76- warnings .warn (
77- 'Failed to extract info from loguru logger. '
78- 'This may affect span names and/or positional arguments. '
79- 'Please report an issue to logfire.' ,
80- RuntimeWarning ,
81- )
82-
83-
8490try :
85- _LOG_METHOD_CODE = inspect .unwrap (type (logger )._log ).__code__ # type: ignore
91+ _LOG_METHOD_CODE = inspect .unwrap (type (loguru . logger )._log ).__code__ # type: ignore
8692except Exception : # pragma: no cover
8793 _LOG_METHOD_CODE = None # type: ignore
88- _warn_inspection_failure ()
94+ warn_at_user_stacklevel (
95+ 'Failed to find loguru log method code to extract detailed information' , LoguruInspectionFailed
96+ )
0 commit comments