Skip to content

Commit 7340b5a

Browse files
authored
Release v3.8.0 (#921)
1 parent 660efbd commit 7340b5a

File tree

10 files changed

+136
-11
lines changed

10 files changed

+136
-11
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Release Notes
22

3+
## [v3.8.0] (2025-03-11)
4+
5+
* OpenAI Agents Framework instrumentation by @alexmojaki in [#917](https://github.com/pydantic/logfire/pull/917)
6+
* OTel log scrubbing by @alexmojaki in [#903](https://github.com/pydantic/logfire/pull/903)
7+
38
## [v3.7.1] (2025-03-05)
49

510
* Handle errors in OpenAI response by @alexmojaki in [#910](https://github.com/pydantic/logfire/pull/910)
@@ -616,3 +621,4 @@ First release from new repo!
616621
[v3.6.4]: https://github.com/pydantic/logfire/compare/v3.6.3...v3.6.4
617622
[v3.7.0]: https://github.com/pydantic/logfire/compare/v3.6.4...v3.7.0
618623
[v3.7.1]: https://github.com/pydantic/logfire/compare/v3.7.0...v3.7.1
624+
[v3.8.0]: https://github.com/pydantic/logfire/compare/v3.7.1...v3.8.0

logfire-api/logfire_api/__init__.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ from .version import VERSION as VERSION
1313
from logfire.sampling import SamplingOptions as SamplingOptions
1414
from typing import Any
1515

16-
__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', 'logfire_info']
16+
__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_openai_agents', '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', 'logfire_info']
1717

1818
DEFAULT_LOGFIRE_INSTANCE = Logfire()
1919
span = DEFAULT_LOGFIRE_INSTANCE.span
@@ -26,6 +26,7 @@ instrument_asgi = DEFAULT_LOGFIRE_INSTANCE.instrument_asgi
2626
instrument_wsgi = DEFAULT_LOGFIRE_INSTANCE.instrument_wsgi
2727
instrument_fastapi = DEFAULT_LOGFIRE_INSTANCE.instrument_fastapi
2828
instrument_openai = DEFAULT_LOGFIRE_INSTANCE.instrument_openai
29+
instrument_openai_agents = DEFAULT_LOGFIRE_INSTANCE.instrument_openai_agents
2930
instrument_anthropic = DEFAULT_LOGFIRE_INSTANCE.instrument_anthropic
3031
instrument_asyncpg = DEFAULT_LOGFIRE_INSTANCE.instrument_asyncpg
3132
instrument_httpx = DEFAULT_LOGFIRE_INSTANCE.instrument_httpx

logfire-api/logfire_api/_internal/config.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ from .auth import DEFAULT_FILE as DEFAULT_FILE, DefaultFile as DefaultFile, is_l
55
from .config_params import ParamManager as ParamManager, PydanticPluginRecordValues as PydanticPluginRecordValues
66
from .constants import LevelName as LevelName, OTLP_MAX_BODY_SIZE as OTLP_MAX_BODY_SIZE, RESOURCE_ATTRIBUTES_CODE_ROOT_PATH as RESOURCE_ATTRIBUTES_CODE_ROOT_PATH, RESOURCE_ATTRIBUTES_CODE_WORK_DIR as RESOURCE_ATTRIBUTES_CODE_WORK_DIR, RESOURCE_ATTRIBUTES_DEPLOYMENT_ENVIRONMENT_NAME as RESOURCE_ATTRIBUTES_DEPLOYMENT_ENVIRONMENT_NAME, RESOURCE_ATTRIBUTES_VCS_REPOSITORY_REF_REVISION as RESOURCE_ATTRIBUTES_VCS_REPOSITORY_REF_REVISION, RESOURCE_ATTRIBUTES_VCS_REPOSITORY_URL as RESOURCE_ATTRIBUTES_VCS_REPOSITORY_URL
77
from .exporters.console import ConsoleColorsValues as ConsoleColorsValues, ConsoleLogExporter as ConsoleLogExporter, IndentedConsoleSpanExporter as IndentedConsoleSpanExporter, ShowParentsConsoleSpanExporter as ShowParentsConsoleSpanExporter, SimpleConsoleSpanExporter as SimpleConsoleSpanExporter
8-
from .exporters.logs import CheckSuppressInstrumentationLogProcessorWrapper as CheckSuppressInstrumentationLogProcessorWrapper
8+
from .exporters.logs import CheckSuppressInstrumentationLogProcessorWrapper as CheckSuppressInstrumentationLogProcessorWrapper, MainLogProcessorWrapper as MainLogProcessorWrapper
99
from .exporters.otlp import OTLPExporterHttpSession as OTLPExporterHttpSession, QuietLogExporter as QuietLogExporter, QuietSpanExporter as QuietSpanExporter, RetryFewerSpansSpanExporter as RetryFewerSpansSpanExporter
1010
from .exporters.processor_wrapper import CheckSuppressInstrumentationProcessorWrapper as CheckSuppressInstrumentationProcessorWrapper, MainSpanProcessorWrapper as MainSpanProcessorWrapper
1111
from .exporters.quiet_metrics import QuietMetricExporter as QuietMetricExporter

logfire-api/logfire_api/_internal/exporters/logs.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from dataclasses import dataclass
12
from logfire._internal.exporters.wrapper import WrapperLogProcessor as WrapperLogProcessor
3+
from logfire._internal.scrubbing import BaseScrubber as BaseScrubber
24
from logfire._internal.utils import is_instrumentation_suppressed as is_instrumentation_suppressed
35
from opentelemetry.sdk._logs import LogData
46

@@ -8,3 +10,8 @@ class CheckSuppressInstrumentationLogProcessorWrapper(WrapperLogProcessor):
810
Placed at the root of the tree of processors.
911
"""
1012
def emit(self, log_data: LogData): ...
13+
14+
@dataclass
15+
class MainLogProcessorWrapper(WrapperLogProcessor):
16+
scrubber: BaseScrubber
17+
def emit(self, log_data: LogData): ...
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import contextvars
2+
from _typeshed import Incomplete
3+
from abc import abstractmethod
4+
from agents import Span, Trace
5+
from agents.tracing import ResponseSpanData
6+
from agents.tracing.setup import TraceProvider
7+
from agents.tracing.spans import SpanData, SpanError, TSpanData
8+
from dataclasses import dataclass
9+
from logfire import Logfire as Logfire, LogfireSpan as LogfireSpan
10+
from logfire._internal.formatter import logfire_format as logfire_format
11+
from logfire._internal.scrubbing import NOOP_SCRUBBER as NOOP_SCRUBBER
12+
from logfire._internal.utils import handle_internal_errors as handle_internal_errors, log_internal_error as log_internal_error
13+
from openai.types.responses import Response
14+
from types import TracebackType
15+
from typing import Any, Generic, TypeVar
16+
from typing_extensions import Self
17+
18+
class LogfireTraceProviderWrapper:
19+
wrapped: Incomplete
20+
logfire_instance: Incomplete
21+
def __init__(self, wrapped: TraceProvider, logfire_instance: Logfire) -> None: ...
22+
def create_trace(self, name: str, trace_id: str | None = None, disabled: bool = False, **kwargs: Any) -> Trace: ...
23+
def create_span(self, span_data: TSpanData, span_id: str | None = None, parent: Trace | Span[Any] | None = None, disabled: bool = False) -> Span[TSpanData]: ...
24+
def __getattr__(self, item: Any) -> Any: ...
25+
@classmethod
26+
def install(cls, logfire_instance: Logfire) -> None: ...
27+
28+
@dataclass
29+
class LogfireSpanHelper:
30+
span: LogfireSpan
31+
def start(self, mark_as_current: bool): ...
32+
def end(self, reset_current: bool): ...
33+
def maybe_detach(self, reset_current: bool): ...
34+
def __enter__(self) -> None: ...
35+
def __exit__(self, exc_type: type[BaseException], exc_val: BaseException, exc_tb: TracebackType): ...
36+
T = TypeVar('T', Trace, Span[TSpanData])
37+
38+
@dataclass
39+
class LogfireWrapperBase(Generic[T]):
40+
wrapped: Any
41+
span_helper: LogfireSpanHelper
42+
token: contextvars.Token[T | None] | None = ...
43+
def start(self, mark_as_current: bool = False): ...
44+
def finish(self, reset_current: bool = False): ...
45+
def __enter__(self) -> Self: ...
46+
def __exit__(self, exc_type: type[BaseException], exc_val: BaseException, exc_tb: TracebackType): ...
47+
@abstractmethod
48+
def on_ending(self): ...
49+
@abstractmethod
50+
def attach(self): ...
51+
@abstractmethod
52+
def detach(self): ...
53+
def __getattr__(self, item: str): ...
54+
55+
@dataclass
56+
class LogfireTraceWrapper(LogfireWrapperBase[Trace], Trace):
57+
def on_ending(self) -> None: ...
58+
token = ...
59+
def attach(self) -> None: ...
60+
def detach(self) -> None: ...
61+
@property
62+
def trace_id(self) -> str: ...
63+
@property
64+
def name(self) -> str: ...
65+
def export(self) -> dict[str, Any] | None: ...
66+
67+
@dataclass
68+
class LogfireSpanWrapper(LogfireWrapperBase[Span[TSpanData]], Span[TSpanData]):
69+
token = ...
70+
def attach(self) -> None: ...
71+
def detach(self) -> None: ...
72+
def on_ending(self) -> None: ...
73+
@property
74+
def trace_id(self) -> str: ...
75+
@property
76+
def span_id(self) -> str: ...
77+
@property
78+
def span_data(self) -> TSpanData: ...
79+
@property
80+
def parent_id(self) -> str | None: ...
81+
def set_error(self, error: SpanError) -> None: ...
82+
@property
83+
def error(self) -> SpanError | None: ...
84+
def export(self) -> dict[str, Any] | None: ...
85+
@property
86+
def started_at(self) -> str | None: ...
87+
@property
88+
def ended_at(self) -> str | None: ...
89+
90+
def attributes_from_span_data(span_data: SpanData, msg_template: str) -> dict[str, Any]: ...
91+
def get_basic_response_attributes(response: Response): ...
92+
def get_magic_response_attributes() -> dict[str, Any]: ...
93+
def get_response_span_events(span: ResponseSpanData): ...
94+
def input_to_events(inp: dict[str, Any]): ...
95+
def unknown_event(inp: dict[str, Any]): ...

logfire-api/logfire_api/_internal/main.pyi

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,10 @@ class Logfire:
458458
i.e. it's not necessary to use this as a context manager.
459459
"""
460460
def instrument_openai(self, openai_client: openai.OpenAI | openai.AsyncOpenAI | type[openai.OpenAI] | type[openai.AsyncOpenAI] | None = None, *, suppress_other_instrumentation: bool = True) -> ContextManager[None]:
461-
"""Instrument an OpenAI client so that spans are automatically created for each request.
461+
'''Instrument an OpenAI client so that spans are automatically created for each request.
462+
463+
This instruments the [standard OpenAI SDK](https://pypi.org/project/openai/) package, for instrumentation
464+
of the OpenAI "agents" framework, see [`instrument_openai_agents()`][logfire.Logfire.instrument_openai_agents].
462465
463466
The following methods are instrumented for both the sync and the async clients:
464467
@@ -480,13 +483,13 @@ class Logfire:
480483
logfire.instrument_openai(client)
481484
482485
response = client.chat.completions.create(
483-
model='gpt-4',
486+
model=\'gpt-4\',
484487
messages=[
485-
{'role': 'system', 'content': 'You are a helpful assistant.'},
486-
{'role': 'user', 'content': 'What is four plus five?'},
488+
{\'role\': \'system\', \'content\': \'You are a helpful assistant.\'},
489+
{\'role\': \'user\', \'content\': \'What is four plus five?\'},
487490
],
488491
)
489-
print('answer:', response.choices[0].message.content)
492+
print(\'answer:\', response.choices[0].message.content)
490493
```
491494
492495
Args:
@@ -505,6 +508,12 @@ class Logfire:
505508
Returns:
506509
A context manager that will revert the instrumentation when exited.
507510
Use of this context manager is optional.
511+
'''
512+
def instrument_openai_agents(self) -> None:
513+
"""Instrument the [`agents`](https://github.com/openai/openai-agents-python) framework from OpenAI.
514+
515+
For instrumentation of the standard OpenAI SDK package,
516+
see [`instrument_openai()`][logfire.Logfire.instrument_openai].
508517
"""
509518
def instrument_anthropic(self, anthropic_client: anthropic.Anthropic | anthropic.AsyncAnthropic | anthropic.AnthropicBedrock | anthropic.AsyncAnthropicBedrock | type[anthropic.Anthropic] | type[anthropic.AsyncAnthropic] | type[anthropic.AnthropicBedrock] | type[anthropic.AsyncAnthropicBedrock] | None = None, *, suppress_other_instrumentation: bool = True) -> ContextManager[None]:
510519
"""Instrument an Anthropic client so that spans are automatically created for each request.

logfire-api/logfire_api/_internal/scrubbing.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ from .utils import ReadableSpanDict as ReadableSpanDict
66
from _typeshed import Incomplete
77
from abc import ABC, abstractmethod
88
from dataclasses import dataclass
9+
from opentelemetry.sdk._logs import LogRecord
910
from opentelemetry.sdk.trace import Event
1011
from typing import Any, Callable, Sequence, TypedDict
1112

@@ -35,17 +36,21 @@ class BaseScrubber(ABC):
3536
@abstractmethod
3637
def scrub_span(self, span: ReadableSpanDict): ...
3738
@abstractmethod
39+
def scrub_log(self, log: LogRecord) -> LogRecord: ...
40+
@abstractmethod
3841
def scrub_value(self, path: JsonPath, value: Any) -> tuple[Any, list[ScrubbedNote]]: ...
3942

4043
class NoopScrubber(BaseScrubber):
4144
def scrub_span(self, span: ReadableSpanDict): ...
45+
def scrub_log(self, log: LogRecord) -> LogRecord: ...
4246
def scrub_value(self, path: JsonPath, value: Any) -> tuple[Any, list[ScrubbedNote]]: ...
4347

4448
NOOP_SCRUBBER: Incomplete
4549

4650
class Scrubber(BaseScrubber):
4751
"""Redacts potentially sensitive data."""
4852
def __init__(self, patterns: Sequence[str] | None, callback: ScrubCallback | None = None) -> None: ...
53+
def scrub_log(self, log: LogRecord) -> LogRecord: ...
4954
def scrub_span(self, span: ReadableSpanDict): ...
5055
def scrub_value(self, path: JsonPath, value: Any) -> tuple[Any, list[ScrubbedNote]]: ...
5156

@@ -56,8 +61,10 @@ class SpanScrubber:
5661
and hold and mutate state about the span being scrubbed, specifically the scrubbed notes.
5762
"""
5863
scrubbed: Incomplete
64+
did_scrub: bool
5965
def __init__(self, parent: Scrubber) -> None: ...
6066
def scrub_span(self, span: ReadableSpanDict): ...
67+
def scrub_log(self, log: LogRecord) -> LogRecord: ...
6168
def scrub_event_attributes(self, event: Event, index: int): ...
6269
def scrub(self, path: JsonPath, value: Any) -> Any:
6370
"""Redacts sensitive data from `value`, recursing into nested sequences and mappings.

logfire-api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "logfire-api"
7-
version = "3.7.1"
7+
version = "3.8.0"
88
description = "Shim for the Logfire SDK which does nothing unless Logfire is installed"
99
authors = [
1010
{ name = "Pydantic Team", email = "[email protected]" },

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "logfire"
7-
version = "3.7.1"
7+
version = "3.8.0"
88
description = "The best Python observability tool! 🪵🔥"
99
requires-python = ">=3.8"
1010
authors = [

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)