Skip to content

Commit f8d4c39

Browse files
authored
Replace pydantic_plugin in logfire.configure() with logfire.instrument_pydantic() (#453)
1 parent a996819 commit f8d4c39

File tree

18 files changed

+215
-97
lines changed

18 files changed

+215
-97
lines changed

docs/integrations/pydantic.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Pydantic
22

3-
Logfire has a [Pydantic plugin][pydantic-plugin] to instrument [Pydantic][pydantic] models.
3+
Logfire has a Pydantic plugin to instrument [Pydantic][pydantic] models.
44
The plugin provides logs and metrics about model validation.
55

66
To enable the plugin, do one of the following:
@@ -13,35 +13,36 @@ To enable the plugin, do one of the following:
1313
pydantic_plugin_record = "all"
1414
```
1515

16-
- Use the [`pydantic_plugin`][logfire.configure(pydantic_plugin)] parameter in `logfire.configure`, e.g:
16+
- Call [`logfire.instrument_pydantic`][logfire.Logfire.instrument_pydantic] with the desired configuration, e.g:
1717

1818
```py
1919
import logfire
2020

21-
logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record='all'))
21+
logfire.instrument_pydantic() # Defaults to record='all'
2222
```
2323

24-
Note that if you only use the last option then only models defined and imported *after* calling `logfire.configure`
24+
Note that if you only use the last option then only model classes defined and imported *after* calling `logfire.instrument_pydantic`
2525
will be instrumented.
2626

27+
!!! note
28+
Remember to call [`logfire.configure()`][logfire.configure] at some point, whether before or after
29+
calling `logfire.instrument_pydantic` and defining model classes.
30+
Model validations will only start being logged after calling `logfire.configure()`.
31+
2732
## Third party modules
2833

2934
By default, third party modules are not instrumented by the plugin to avoid noise. You can enable instrumentation for those
3035
using the [`include`][logfire.PydanticPlugin.include] configuration.
3136

3237
```py
33-
import logfire
34-
35-
logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record='all', include={'openai'}))
38+
logfire.instrument_pydantic(include={'openai'})
3639
```
3740

3841
You can also disable instrumentation for your own modules using the
3942
[`exclude`][logfire.PydanticPlugin.exclude] configuration.
4043

4144
```py
42-
import logfire
43-
44-
logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record='all', exclude={'app.api.v1'}))
45+
logfire.instrument_pydantic(exclude={'app.api.v1'})
4546
```
4647

4748
## Model configuration
@@ -60,13 +61,13 @@ class Foo(BaseModel, plugin_settings=PluginSettings(logfire={'record': 'failure'
6061

6162
### Record
6263

63-
The [`record`][logfire.integrations.pydantic.LogfireSettings.record] is used to configure what to record.
64+
The [`record`][logfire.integrations.pydantic.LogfireSettings.record] argument is used to configure what to record.
6465
It can be one of the following values:
6566

66-
* `off`: Disable instrumentation. This is default value.
67-
* `all`: Send traces and metrics for all events.
67+
* `all`: Send traces and metrics for all events. This is default value for `logfire.instrument_pydantic`.
6868
* `failure`: Send metrics for all validations and traces only for validation failures.
6969
* `metrics`: Send only metrics.
70+
* `off`: Disable instrumentation.
7071

7172
<!--
7273
[Sampling](../usage/sampling.md) can be configured by `trace_sample_rate` key in
@@ -98,4 +99,3 @@ class Foo(
9899
```
99100

100101
[pydantic]: https://docs.pydantic.dev/latest/
101-
[pydantic-plugin]: https://docs.pydantic.dev/latest/concepts/plugins/

docs/integrations/third-party/mirascope.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ This will give you:
3232
<figcaption>Mirascope Anthropic call span and Anthropic span and conversation</figcaption>
3333
</figure>
3434

35-
Since Mirascope is built on top of [Pydantic][pydantic], you can use the [Pydantic plugin][pydantic-plugin] to track additional logs and metrics about model validation, which you can enable using the [`pydantic_plugin`][logfire.configure(pydantic_plugin)] configuration.
35+
Since Mirascope is built on top of [Pydantic][pydantic], you can use the [Pydantic plugin](../pydantic.md) to track additional logs and metrics about model validation.
3636

3737
This can be particularly useful when [extracting structured information][mirascope-extracting-structured-information] using LLMs:
3838

@@ -44,7 +44,8 @@ from mirascope.core import openai, prompt_template
4444
from mirascope.integrations.logfire import with_logfire
4545
from pydantic import BaseModel
4646

47-
logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record="all"))
47+
logfire.configure()
48+
logfire.instrument_pydantic()
4849

4950

5051
class TaskDetails(BaseModel):

docs/why-logfire/pydantic.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ from datetime import date
3131
import logfire
3232
from pydantic import BaseModel
3333

34-
logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record='all')) # (1)!
34+
logfire.configure()
35+
logfire.instrument_pydantic() # (1)!
3536

3637
class User(BaseModel):
3738
name: str
@@ -43,7 +44,7 @@ User(name='Ben', country_code='USA', dob='2000-02-02')
4344
User(name='Charlie', country_code='GBR', dob='1990-03-03')
4445
```
4546

46-
1. This configuration means details about all Pydantic model validations will be recorded. You can also record details about validation failures only, or just metrics; see the [pydantic plugin docs][logfire.PydanticPlugin].
47+
1. This configuration means details about all Pydantic model validations will be recorded. You can also record details about validation failures only, or just metrics; see the [pydantic plugin docs](../integrations/pydantic.md).
4748
2. Since we've enabled the Pydantic Plugin, all Pydantic validations will be recorded in Logfire.
4849

4950
Learn more about the [Pydantic Plugin here](../integrations/pydantic.md).

logfire-api/logfire_api/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ def decorator(func):
9595
def instrument_fastapi(self, *args, **kwargs) -> ContextManager[None]:
9696
return nullcontext()
9797

98+
def instrument_pydantic(self, *args, **kwargs) -> None: ...
99+
98100
def instrument_pymongo(self, *args, **kwargs) -> None: ...
99101

100102
def instrument_sqlalchemy(self, *args, **kwargs) -> None: ...
@@ -144,6 +146,7 @@ def shutdown(self, *args, **kwargs) -> None: ...
144146
log_slow_async_callbacks = DEFAULT_LOGFIRE_INSTANCE.log_slow_async_callbacks
145147
install_auto_tracing = DEFAULT_LOGFIRE_INSTANCE.install_auto_tracing
146148
instrument = DEFAULT_LOGFIRE_INSTANCE.instrument
149+
instrument_pydantic = DEFAULT_LOGFIRE_INSTANCE.instrument_pydantic
147150
instrument_fastapi = DEFAULT_LOGFIRE_INSTANCE.instrument_fastapi
148151
instrument_openai = DEFAULT_LOGFIRE_INSTANCE.instrument_openai
149152
instrument_anthropic = DEFAULT_LOGFIRE_INSTANCE.instrument_anthropic

logfire-api/logfire_api/__init__.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ from .integrations.structlog import LogfireProcessor as StructlogProcessor
1111
from .version import VERSION as VERSION
1212
from logfire.sampling import SamplingOptions as SamplingOptions
1313

14-
__all__ = ['Logfire', 'LogfireSpan', 'LevelName', 'AdvancedOptions', 'ConsoleOptions', 'PydanticPlugin', 'configure', 'span', 'instrument', 'log', 'trace', 'debug', 'notice', 'info', 'warn', 'error', 'exception', 'fatal', 'force_flush', 'log_slow_async_callbacks', 'install_auto_tracing', '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_redis', 'instrument_pymongo', 'instrument_mysql', 'instrument_system_metrics', 'AutoTraceModule', 'with_tags', 'with_settings', 'shutdown', 'load_spans_from_file', 'no_auto_trace', 'ScrubMatch', 'ScrubbingOptions', 'VERSION', 'suppress_instrumentation', 'StructlogProcessor', 'LogfireLoggingHandler', 'SamplingOptions', 'MetricsOptions']
14+
__all__ = ['Logfire', 'LogfireSpan', 'LevelName', 'AdvancedOptions', 'ConsoleOptions', 'PydanticPlugin', 'configure', 'span', 'instrument', 'log', 'trace', 'debug', 'notice', 'info', 'warn', 'error', 'exception', 'fatal', 'force_flush', 'log_slow_async_callbacks', 'install_auto_tracing', '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_redis', 'instrument_pymongo', 'instrument_mysql', 'instrument_system_metrics', 'AutoTraceModule', 'with_tags', 'with_settings', 'shutdown', 'load_spans_from_file', 'no_auto_trace', 'ScrubMatch', 'ScrubbingOptions', 'VERSION', 'suppress_instrumentation', 'StructlogProcessor', 'LogfireLoggingHandler', 'SamplingOptions', 'MetricsOptions']
1515

1616
DEFAULT_LOGFIRE_INSTANCE = Logfire()
1717
span = DEFAULT_LOGFIRE_INSTANCE.span
1818
instrument = DEFAULT_LOGFIRE_INSTANCE.instrument
1919
force_flush = DEFAULT_LOGFIRE_INSTANCE.force_flush
2020
log_slow_async_callbacks = DEFAULT_LOGFIRE_INSTANCE.log_slow_async_callbacks
2121
install_auto_tracing = DEFAULT_LOGFIRE_INSTANCE.install_auto_tracing
22+
instrument_pydantic = DEFAULT_LOGFIRE_INSTANCE.instrument_pydantic
2223
instrument_fastapi = DEFAULT_LOGFIRE_INSTANCE.instrument_fastapi
2324
instrument_openai = DEFAULT_LOGFIRE_INSTANCE.instrument_openai
2425
instrument_anthropic = DEFAULT_LOGFIRE_INSTANCE.instrument_anthropic

logfire-api/logfire_api/_internal/config.pyi

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ from .metrics import ProxyMeterProvider as ProxyMeterProvider
1717
from .scrubbing import BaseScrubber as BaseScrubber, NOOP_SCRUBBER as NOOP_SCRUBBER, Scrubber as Scrubber, ScrubbingOptions as ScrubbingOptions
1818
from .stack_info import warn_at_user_stacklevel as warn_at_user_stacklevel
1919
from .tracer import PendingSpanProcessor as PendingSpanProcessor, ProxyTracerProvider as ProxyTracerProvider
20-
from .utils import UnexpectedResponse as UnexpectedResponse, ensure_data_dir_exists as ensure_data_dir_exists, get_version as get_version, read_toml_file as read_toml_file, suppress_instrumentation as suppress_instrumentation
20+
from .utils import SeededRandomIdGenerator as SeededRandomIdGenerator, UnexpectedResponse as UnexpectedResponse, ensure_data_dir_exists as ensure_data_dir_exists, read_toml_file as read_toml_file, suppress_instrumentation as suppress_instrumentation
2121
from _typeshed import Incomplete
2222
from dataclasses import dataclass
2323
from functools import cached_property
@@ -59,7 +59,10 @@ class AdvancedOptions:
5959

6060
@dataclass
6161
class PydanticPlugin:
62-
"""Options for the Pydantic plugin."""
62+
"""Options for the Pydantic plugin.
63+
64+
This class is deprecated for external use. Use `logfire.instrument_pydantic()` instead.
65+
"""
6366
record: PydanticPluginRecordValues = ...
6467
include: set[str] = ...
6568
exclude: set[str] = ...
@@ -74,7 +77,7 @@ class MetricsOptions:
7477

7578
class DeprecatedKwargs(TypedDict): ...
7679

77-
def configure(*, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | str | None = None, data_dir: Path | str | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, pydantic_plugin: PydanticPlugin | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, advanced: AdvancedOptions | None = None, **deprecated_kwargs: Unpack[DeprecatedKwargs]) -> None:
80+
def configure(*, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | str | None = None, data_dir: Path | str | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, advanced: AdvancedOptions | None = None, **deprecated_kwargs: Unpack[DeprecatedKwargs]) -> None:
7881
"""Configure the logfire SDK.
7982
8083
Args:
@@ -94,8 +97,6 @@ def configure(*, send_to_logfire: bool | Literal['if-token-present'] | None = No
9497
additional_span_processors: Span processors to use in addition to the default processor which exports spans to Logfire's API.
9598
metrics: Set to `False` to disable sending all metrics,
9699
or provide a `MetricsOptions` object to configure metrics, e.g. additional metric readers.
97-
pydantic_plugin: Configuration for the Pydantic plugin. If `None` uses the `LOGFIRE_PYDANTIC_PLUGIN_*` environment
98-
variables, otherwise defaults to `PydanticPlugin(record='off')`.
99100
scrubbing: Options for scrubbing sensitive data. Set to `False` to disable.
100101
inspect_arguments: Whether to enable
101102
[f-string magic](https://logfire.pydantic.dev/docs/guides/onboarding-checklist/add-manual-tracing/#f-strings).
@@ -123,21 +124,20 @@ class _LogfireConfigData:
123124
console: ConsoleOptions | Literal[False] | None
124125
data_dir: Path
125126
additional_span_processors: Sequence[SpanProcessor] | None
126-
pydantic_plugin: PydanticPlugin
127127
scrubbing: ScrubbingOptions | Literal[False]
128128
inspect_arguments: bool
129129
sampling: SamplingOptions
130130
advanced: AdvancedOptions
131131

132132
class LogfireConfig(_LogfireConfigData):
133-
def __init__(self, send_to_logfire: bool | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | None = None, data_dir: Path | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, pydantic_plugin: PydanticPlugin | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, advanced: AdvancedOptions | None = None) -> None:
133+
def __init__(self, send_to_logfire: bool | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, console: ConsoleOptions | Literal[False] | None = None, config_dir: Path | None = None, data_dir: Path | None = None, additional_span_processors: Sequence[SpanProcessor] | None = None, metrics: MetricsOptions | Literal[False] | None = None, scrubbing: ScrubbingOptions | Literal[False] | None = None, inspect_arguments: bool | None = None, sampling: SamplingOptions | None = None, advanced: AdvancedOptions | None = None) -> None:
134134
"""Create a new LogfireConfig.
135135
136136
Users should never need to call this directly, instead use `logfire.configure`.
137137
138138
See `_LogfireConfigData` for parameter documentation.
139139
"""
140-
def configure(self, send_to_logfire: bool | Literal['if-token-present'] | None, token: str | None, service_name: str | None, service_version: str | None, console: ConsoleOptions | Literal[False] | None, config_dir: Path | None, data_dir: Path | None, additional_span_processors: Sequence[SpanProcessor] | None, metrics: MetricsOptions | Literal[False] | None, pydantic_plugin: PydanticPlugin | None, scrubbing: ScrubbingOptions | Literal[False] | None, inspect_arguments: bool | None, sampling: SamplingOptions | None, advanced: AdvancedOptions | None) -> None: ...
140+
def configure(self, send_to_logfire: bool | Literal['if-token-present'] | None, token: str | None, service_name: str | None, service_version: str | None, console: ConsoleOptions | Literal[False] | None, config_dir: Path | None, data_dir: Path | None, additional_span_processors: Sequence[SpanProcessor] | None, metrics: MetricsOptions | Literal[False] | None, scrubbing: ScrubbingOptions | Literal[False] | None, inspect_arguments: bool | None, sampling: SamplingOptions | None, advanced: AdvancedOptions | None) -> None: ...
141141
def initialize(self) -> ProxyTracerProvider:
142142
"""Configure internals to start exporting traces and metrics."""
143143
def force_flush(self, timeout_millis: int = 30000) -> bool:

logfire-api/logfire_api/_internal/config_params.pyi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,3 @@ class ParamManager:
7575
"""
7676
@cached_property
7777
def pydantic_plugin(self): ...
78-
79-
def default_param_manager(): ...

logfire-api/logfire_api/_internal/main.pyi

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ from . import async_ as async_
55
from ..version import VERSION as VERSION
66
from .auto_trace import AutoTraceModule as AutoTraceModule, install_auto_tracing as install_auto_tracing
77
from .config import GLOBAL_CONFIG as GLOBAL_CONFIG, LogfireConfig as LogfireConfig, OPEN_SPANS as OPEN_SPANS
8+
from .config_params import PydanticPluginRecordValues as PydanticPluginRecordValues
89
from .constants import ATTRIBUTES_JSON_SCHEMA_KEY as ATTRIBUTES_JSON_SCHEMA_KEY, ATTRIBUTES_LOG_LEVEL_NUM_KEY as ATTRIBUTES_LOG_LEVEL_NUM_KEY, ATTRIBUTES_MESSAGE_KEY as ATTRIBUTES_MESSAGE_KEY, ATTRIBUTES_MESSAGE_TEMPLATE_KEY as ATTRIBUTES_MESSAGE_TEMPLATE_KEY, ATTRIBUTES_SAMPLE_RATE_KEY as ATTRIBUTES_SAMPLE_RATE_KEY, ATTRIBUTES_SPAN_TYPE_KEY as ATTRIBUTES_SPAN_TYPE_KEY, ATTRIBUTES_TAGS_KEY as ATTRIBUTES_TAGS_KEY, ATTRIBUTES_VALIDATION_ERROR_KEY as ATTRIBUTES_VALIDATION_ERROR_KEY, DISABLE_CONSOLE_KEY as DISABLE_CONSOLE_KEY, LEVEL_NUMBERS as LEVEL_NUMBERS, LevelName as LevelName, NULL_ARGS_KEY as NULL_ARGS_KEY, OTLP_MAX_INT_SIZE as OTLP_MAX_INT_SIZE, log_level_attributes as log_level_attributes
910
from .formatter import logfire_format as logfire_format, logfire_format_with_magic as logfire_format_with_magic
1011
from .instrument import LogfireArgs as LogfireArgs, instrument as instrument
@@ -24,7 +25,7 @@ from .json_schema import JsonSchemaProperties as JsonSchemaProperties, attribute
2425
from .metrics import ProxyMeterProvider as ProxyMeterProvider
2526
from .stack_info import get_user_stack_info as get_user_stack_info
2627
from .tracer import ProxyTracerProvider as ProxyTracerProvider
27-
from .utils import SysExcInfo as SysExcInfo, handle_internal_errors as handle_internal_errors, log_internal_error as log_internal_error, uniquify_sequence as uniquify_sequence
28+
from .utils import SysExcInfo as SysExcInfo, get_version as get_version, handle_internal_errors as handle_internal_errors, log_internal_error as log_internal_error, uniquify_sequence as uniquify_sequence
2829
from django.http import HttpRequest as HttpRequest, HttpResponse as HttpResponse
2930
from fastapi import FastAPI
3031
from flask.app import Flask
@@ -367,6 +368,24 @@ class Logfire:
367368
modules in `sys.modules` (i.e. modules that have already been imported) match the modules to trace.
368369
Set to `'warn'` to issue a warning instead, or `'ignore'` to skip the check.
369370
"""
371+
def instrument_pydantic(self, record: PydanticPluginRecordValues = 'all', include: Iterable[str] = (), exclude: Iterable[str] = ()):
372+
"""Instrument Pydantic model validations.
373+
374+
This must be called before defining and importing the model classes you want to instrument.
375+
See the [Pydantic integration guide](https://logfire.pydantic.dev/docs/integrations/pydantic/) for more info.
376+
377+
Args:
378+
record: The record mode for the Pydantic plugin. It can be one of the following values:
379+
380+
- `all`: Send traces and metrics for all events. This is default value.
381+
- `failure`: Send metrics for all validations and traces only for validation failures.
382+
- `metrics`: Send only metrics.
383+
- `off`: Disable instrumentation.
384+
include:
385+
By default, third party modules are not instrumented. This option allows you to include specific modules.
386+
exclude:
387+
Exclude specific modules from instrumentation.
388+
"""
370389
def instrument_fastapi(self, app: FastAPI, *, capture_headers: bool = False, request_attributes_mapper: Callable[[Request | WebSocket, dict[str, Any]], dict[str, Any] | None] | None = None, use_opentelemetry_instrumentation: bool = True, excluded_urls: str | Iterable[str] | None = None, record_send_receive: bool = False, **opentelemetry_kwargs: Any) -> ContextManager[None]:
371390
"""Instrument a FastAPI app so that spans and logs are automatically created for each request.
372391

0 commit comments

Comments
 (0)