Skip to content

Commit bb7913b

Browse files
dmontagualexmojaki
andauthored
Add non-user-code-prefix stuff (#829)
Co-authored-by: Alex Hall <[email protected]>
1 parent 3715989 commit bb7913b

File tree

7 files changed

+44
-6
lines changed

7 files changed

+44
-6
lines changed

logfire-api/logfire_api/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ def loguru_handler() -> dict[str, Any]: ...
193193
def no_auto_trace(x):
194194
return x
195195

196+
def add_non_user_code_prefix(*args, **kwargs) -> None: ...
197+
196198
@contextmanager
197199
def suppress_instrumentation():
198200
yield

logfire-api/logfire_api/__init__.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ from ._internal.config import AdvancedOptions as AdvancedOptions, CodeSource as
44
from ._internal.constants import LevelName as LevelName
55
from ._internal.main import Logfire as Logfire, LogfireSpan as LogfireSpan
66
from ._internal.scrubbing import ScrubMatch as ScrubMatch, ScrubbingOptions as ScrubbingOptions
7+
from ._internal.stack_info import add_non_user_code_prefix as add_non_user_code_prefix
78
from ._internal.utils import suppress_instrumentation as suppress_instrumentation
89
from .integrations.logging import LogfireLoggingHandler as LogfireLoggingHandler
910
from .integrations.structlog import LogfireProcessor as StructlogProcessor
1011
from .version import VERSION as VERSION
1112
from logfire.sampling import SamplingOptions as SamplingOptions
1213
from typing import Any
1314

14-
__all__ = ['Logfire', 'LogfireSpan', 'LevelName', 'AdvancedOptions', 'ConsoleOptions', 'CodeSource', 'PydanticPlugin', 'configure', 'span', 'instrument', 'log', 'trace', 'debug', 'notice', 'info', 'warn', 'warning', 'error', 'exception', 'fatal', 'force_flush', 'log_slow_async_callbacks', 'install_auto_tracing', 'instrument_asgi', 'instrument_wsgi', 'instrument_pydantic', 'instrument_fastapi', 'instrument_openai', 'instrument_anthropic', 'instrument_asyncpg', 'instrument_httpx', 'instrument_celery', 'instrument_requests', 'instrument_psycopg', 'instrument_django', 'instrument_flask', 'instrument_starlette', 'instrument_aiohttp_client', 'instrument_sqlalchemy', 'instrument_sqlite3', 'instrument_aws_lambda', 'instrument_redis', 'instrument_pymongo', 'instrument_mysql', 'instrument_system_metrics', 'AutoTraceModule', 'with_tags', 'with_settings', 'suppress_scopes', 'shutdown', 'no_auto_trace', 'ScrubMatch', 'ScrubbingOptions', 'VERSION', 'suppress_instrumentation', 'StructlogProcessor', 'LogfireLoggingHandler', 'loguru_handler', 'SamplingOptions', 'MetricsOptions']
15+
__all__ = ['Logfire', 'LogfireSpan', 'LevelName', 'AdvancedOptions', 'ConsoleOptions', 'CodeSource', 'PydanticPlugin', 'configure', 'span', 'instrument', 'log', 'trace', 'debug', 'notice', 'info', 'warn', 'warning', 'error', 'exception', 'fatal', 'force_flush', 'log_slow_async_callbacks', 'install_auto_tracing', 'instrument_asgi', 'instrument_wsgi', 'instrument_pydantic', 'instrument_fastapi', 'instrument_openai', 'instrument_anthropic', 'instrument_asyncpg', 'instrument_httpx', 'instrument_celery', 'instrument_requests', 'instrument_psycopg', 'instrument_django', 'instrument_flask', 'instrument_starlette', 'instrument_aiohttp_client', 'instrument_sqlalchemy', 'instrument_sqlite3', 'instrument_aws_lambda', 'instrument_redis', 'instrument_pymongo', 'instrument_mysql', 'instrument_system_metrics', 'AutoTraceModule', 'with_tags', 'with_settings', 'suppress_scopes', 'shutdown', 'no_auto_trace', 'ScrubMatch', 'ScrubbingOptions', 'VERSION', 'add_non_user_code_prefix', 'suppress_instrumentation', 'StructlogProcessor', 'LogfireLoggingHandler', 'loguru_handler', 'SamplingOptions', 'MetricsOptions']
1516

1617
DEFAULT_LOGFIRE_INSTANCE = Logfire()
1718
span = DEFAULT_LOGFIRE_INSTANCE.span

logfire-api/logfire_api/_internal/stack_info.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from _typeshed import Incomplete
2+
from pathlib import Path
23
from types import CodeType, FrameType
34
from typing import TypedDict
45

@@ -39,3 +40,8 @@ def is_user_code(code: CodeType) -> bool:
3940
On the other hand, generator expressions and lambdas might be called far away from where they are defined.
4041
"""
4142
def warn_at_user_stacklevel(msg: str, category: type[Warning]): ...
43+
def add_non_user_code_prefix(path: str | Path) -> None:
44+
"""Add a path to the list of prefixes that are considered non-user code.
45+
46+
This prevents the stack info from including frames from the given path.
47+
"""

logfire/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ._internal.constants import LevelName
1313
from ._internal.main import Logfire, LogfireSpan
1414
from ._internal.scrubbing import ScrubbingOptions, ScrubMatch
15+
from ._internal.stack_info import add_non_user_code_prefix
1516
from ._internal.utils import suppress_instrumentation
1617
from .integrations.logging import LogfireLoggingHandler
1718
from .integrations.structlog import LogfireProcessor as StructlogProcessor
@@ -142,6 +143,7 @@ def loguru_handler() -> Any:
142143
'ScrubMatch',
143144
'ScrubbingOptions',
144145
'VERSION',
146+
'add_non_user_code_prefix',
145147
'suppress_instrumentation',
146148
'StructlogProcessor',
147149
'LogfireLoggingHandler',

logfire/_internal/stack_info.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,33 @@
1919
STACK_INFO_KEYS = set(StackInfo.__annotations__.keys())
2020
assert STACK_INFO_KEYS == {'code.filepath', 'code.lineno', 'code.function'}
2121

22-
SITE_PACKAGES_DIR = str(Path(opentelemetry.sdk.trace.__file__).parent.parent.parent.parent.absolute())
23-
PYTHON_LIB_DIR = str(Path(inspect.__file__).parent.absolute())
24-
LOGFIRE_DIR = str(Path(logfire.__file__).parent.absolute())
25-
NON_USER_CODE_PREFIXES = (SITE_PACKAGES_DIR, PYTHON_LIB_DIR, LOGFIRE_DIR)
22+
NON_USER_CODE_PREFIXES: tuple[str, ...] = ()
23+
24+
25+
def add_non_user_code_prefix(path: str | Path) -> None:
26+
"""Add a path to the list of prefixes that are considered non-user code.
27+
28+
This prevents the stack info from including frames from the given path.
29+
30+
This is for advanced users and shouldn't often be needed.
31+
By default, the following prefixes are already included:
32+
33+
- The standard library
34+
- site-packages (specifically wherever opentelemetry is installed)
35+
- The logfire package
36+
37+
This function is useful if you're writing a library that uses logfire and you want to exclude your library's frames.
38+
Since site-packages is already included, this is already the case by default for users of your library.
39+
But this is useful when testing your library since it's not installed in site-packages.
40+
"""
41+
global NON_USER_CODE_PREFIXES
42+
path = str(Path(path).absolute())
43+
NON_USER_CODE_PREFIXES += (path,) # pyright: ignore[reportConstantRedefinition]
44+
45+
46+
add_non_user_code_prefix(Path(opentelemetry.sdk.trace.__file__).parent.parent.parent.parent)
47+
add_non_user_code_prefix(Path(inspect.__file__).parent)
48+
add_non_user_code_prefix(Path(logfire.__file__).parent)
2649

2750

2851
def get_filepath_attribute(file: str) -> StackInfo:

tests/test_logfire_api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ def test_runtime(logfire_api_factory: Callable[[], ModuleType], module_name: str
9494
logfire_api.no_auto_trace(lambda: None) # pragma: no branch
9595
logfire__all__.remove('no_auto_trace')
9696

97+
assert hasattr(logfire_api, 'add_non_user_code_prefix')
98+
logfire_api.add_non_user_code_prefix('/foo/bar')
99+
logfire__all__.remove('add_non_user_code_prefix')
100+
97101
assert hasattr(logfire_api, 'suppress_instrumentation')
98102
with logfire_api.suppress_instrumentation():
99103
...

tests/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_reraise_internal_exception():
3131
def test_internal_exception_tb(caplog: pytest.LogCaptureFixture):
3232
# Pretend that `internal_logfire_code_example` is a module within logfire,
3333
# so all frames from it should be included.
34-
logfire._internal.stack_info.NON_USER_CODE_PREFIXES += (internal_logfire_code_example.__file__,)
34+
logfire.add_non_user_code_prefix(internal_logfire_code_example.__file__)
3535

3636
user_code_example.user1()
3737

0 commit comments

Comments
 (0)