Skip to content

Commit 28e7882

Browse files
authored
Release v3.25.0 (#1261)
1 parent bcd390f commit 28e7882

File tree

11 files changed

+108
-11
lines changed

11 files changed

+108
-11
lines changed

CHANGELOG.md

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

3+
## [v3.25.0] (2025-07-18)
4+
5+
* Use local timezone in console logging by @fswair in [#1255](https://github.com/pydantic/logfire/pull/1255)
6+
* Add `logfire run` command by @Kludex in [#1139](https://github.com/pydantic/logfire/pull/1139)
7+
* Allow removing extra FastAPI spans by @alexmojaki in [#1258](https://github.com/pydantic/logfire/pull/1258)
8+
* Fix `litellm` instrumentation by @alexmojaki in [#1249](https://github.com/pydantic/logfire/pull/1249)
9+
* Add `logfire.exception.fingerprint` attribute to spans with exceptions by @alexmojaki in [#1253](https://github.com/pydantic/logfire/pull/1253)
10+
311
## [v3.24.2] (2025-07-14)
412

513
* Fix auto-tracing Python 3.12 ParamSpec syntax by @alexmojaki in [#1247](https://github.com/pydantic/logfire/pull/1247)
@@ -792,3 +800,4 @@ First release from new repo!
792800
[v3.24.0]: https://github.com/pydantic/logfire/compare/v3.23.0...v3.24.0
793801
[v3.24.1]: https://github.com/pydantic/logfire/compare/v3.24.0...v3.24.1
794802
[v3.24.2]: https://github.com/pydantic/logfire/compare/v3.24.1...v3.24.2
803+
[v3.25.0]: https://github.com/pydantic/logfire/compare/v3.24.2...v3.25.0
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import argparse
2+
from collections.abc import Sequence
3+
from typing import Any
4+
5+
__all__ = ['main', 'logfire_info']
6+
7+
def logfire_info() -> str:
8+
"""Show versions of logfire, OS and related packages."""
9+
10+
class SplitArgs(argparse.Action):
11+
def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None): ...
12+
13+
def main(args: list[str] | None = None) -> None:
14+
"""Run the CLI."""
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import argparse
2+
from _typeshed import Incomplete
3+
from collections.abc import Generator
4+
from contextlib import contextmanager
5+
from dataclasses import dataclass
6+
from rich.console import Console
7+
from rich.text import Text
8+
9+
STANDARD_LIBRARY_PACKAGES: Incomplete
10+
OTEL_INSTRUMENTATION_MAP: Incomplete
11+
12+
@dataclass
13+
class InstrumentationContext:
14+
instrument_pkg_map: dict[str, str]
15+
installed_pkgs: set[str]
16+
installed_otel_pkgs: set[str]
17+
recommendations: set[tuple[str, str]]
18+
19+
def parse_run(args: argparse.Namespace) -> None: ...
20+
@contextmanager
21+
def alter_sys_argv(argv: list[str], cmd: str) -> Generator[None, None, None]: ...
22+
def is_uv_installed() -> bool:
23+
"""Check if uv package manager is installed and available in the PATH."""
24+
def instrument_packages(installed_otel_packages: set[str], instrument_pkg_map: dict[str, str]) -> list[str]:
25+
"""Call every `logfire.instrument_x()` we can based on what's installed.
26+
27+
Returns a list of packages that were successfully instrumented.
28+
"""
29+
def instrument_package(import_name: str): ...
30+
def find_recommended_instrumentations_to_install(instrument_pkg_map: dict[str, str], installed_otel_pkgs: set[str], installed_pkgs: set[str]) -> set[tuple[str, str]]:
31+
"""Determine which OpenTelemetry instrumentation packages are recommended for installation.
32+
33+
Args:
34+
instrument_pkg_map: Mapping of instrumentation package names to the packages they instrument.
35+
installed_otel_pkgs: Set of already installed instrumentation package names.
36+
installed_pkgs: Set of all installed package names.
37+
38+
Returns:
39+
Set of tuples where each tuple is (instrumentation_package, target_package) that
40+
is recommended for installation.
41+
"""
42+
def instrumented_packages_text(installed_otel_pkgs: set[str], instrumented_packages: list[str], installed_pkgs: set[str]) -> Text: ...
43+
def get_recommendation_texts(recommendations: set[tuple[str, str]]) -> tuple[Text, Text]:
44+
"""Return (recommended_packages_text, install_all_text) as Text objects."""
45+
def print_otel_summary(*, console: Console, instrumented_packages_text: Text | None = None, recommendations: set[tuple[str, str]]) -> None: ...
46+
def installed_packages() -> set[str]:
47+
"""Get a set of all installed packages."""
48+
def collect_instrumentation_context(exclude: set[str]) -> InstrumentationContext:
49+
"""Collects all relevant context for instrumentation and recommendations."""

logfire-api/logfire_api/_internal/constants.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ CONTEXT_ATTRIBUTES_KEY: Incomplete
3535
CONTEXT_SAMPLE_RATE_KEY: Incomplete
3636
MESSAGE_FORMATTED_VALUE_LENGTH_LIMIT: int
3737
ONE_SECOND_IN_NANOSECONDS: int
38+
ATTRIBUTES_EXCEPTION_FINGERPRINT_KEY: str

logfire-api/logfire_api/_internal/integrations/fastapi.pyi

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
from ..main import Logfire as Logfire, set_user_attributes_on_raw_span as set_user_attributes_on_raw_span
1+
from ..constants import ONE_SECOND_IN_NANOSECONDS as ONE_SECOND_IN_NANOSECONDS
2+
from ..main import Logfire as Logfire, NoopSpan as NoopSpan, set_user_attributes_on_raw_span as set_user_attributes_on_raw_span
23
from ..stack_info import StackInfo as StackInfo, get_code_object_info as get_code_object_info
34
from ..utils import handle_internal_errors as handle_internal_errors, maybe_capture_server_headers as maybe_capture_server_headers
45
from .asgi import tweak_asgi_spans_tracer_provider as tweak_asgi_spans_tracer_provider
56
from _typeshed import Incomplete
67
from collections.abc import Awaitable, Iterable
7-
from contextlib import AbstractContextManager
8+
from contextlib import AbstractContextManager, contextmanager
89
from fastapi import FastAPI
910
from functools import lru_cache
11+
from opentelemetry.trace import Span
1012
from starlette.requests import Request
1113
from starlette.websockets import WebSocket
1214
from typing import Any, Callable
1315

1416
def find_mounted_apps(app: FastAPI) -> list[FastAPI]:
1517
"""Fetch all sub-apps mounted to a FastAPI app, including nested sub-apps."""
16-
def instrument_fastapi(logfire_instance: Logfire, app: FastAPI, *, capture_headers: bool = False, request_attributes_mapper: Callable[[Request | WebSocket, dict[str, Any]], dict[str, Any] | None] | None = None, excluded_urls: str | Iterable[str] | None = None, record_send_receive: bool = False, **opentelemetry_kwargs: Any) -> AbstractContextManager[None]:
18+
def instrument_fastapi(logfire_instance: Logfire, app: FastAPI, *, capture_headers: bool = False, request_attributes_mapper: Callable[[Request | WebSocket, dict[str, Any]], dict[str, Any] | None] | None = None, excluded_urls: str | Iterable[str] | None = None, record_send_receive: bool = False, extra_spans: bool = True, **opentelemetry_kwargs: Any) -> AbstractContextManager[None]:
1719
"""Instrument a FastAPI app so that spans and logs are automatically created for each request.
1820
1921
See `Logfire.instrument_fastapi` for more details.
@@ -24,8 +26,13 @@ def patch_fastapi():
2426

2527
class FastAPIInstrumentation:
2628
logfire_instance: Incomplete
29+
timestamp_generator: Incomplete
2730
request_attributes_mapper: Incomplete
28-
def __init__(self, logfire_instance: Logfire, request_attributes_mapper: Callable[[Request | WebSocket, dict[str, Any]], dict[str, Any] | None]) -> None: ...
31+
extra_spans: Incomplete
32+
def __init__(self, logfire_instance: Logfire, request_attributes_mapper: Callable[[Request | WebSocket, dict[str, Any]], dict[str, Any] | None], extra_spans: bool) -> None: ...
33+
@contextmanager
34+
def pseudo_span(self, namespace: str, root_span: Span):
35+
"""Record start and end timestamps in the root span, and possibly exceptions."""
2936
async def solve_dependencies(self, request: Request | WebSocket, original: Awaitable[Any]) -> Any: ...
3037
async def run_endpoint_function(self, original_run_endpoint_function: Any, request: Request, dependant: Any, values: dict[str, Any], **kwargs: Any) -> Any: ...
3138

logfire-api/logfire_api/_internal/main.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ class Logfire:
432432
def instrument_pydantic_ai(self, obj: pydantic_ai.Agent | None = None, /, *, event_mode: Literal['attributes', 'logs'] = 'attributes', include_binary_content: bool | None = None, **kwargs: Any) -> None: ...
433433
@overload
434434
def instrument_pydantic_ai(self, obj: pydantic_ai.models.Model, /, *, event_mode: Literal['attributes', 'logs'] = 'attributes', include_binary_content: bool | None = None, **kwargs: Any) -> pydantic_ai.models.Model: ...
435-
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, excluded_urls: str | Iterable[str] | None = None, record_send_receive: bool = False, **opentelemetry_kwargs: Any) -> AbstractContextManager[None]:
435+
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, excluded_urls: str | Iterable[str] | None = None, record_send_receive: bool = False, extra_spans: bool = True, **opentelemetry_kwargs: Any) -> AbstractContextManager[None]:
436436
"""Instrument a FastAPI app so that spans and logs are automatically created for each request.
437437
438438
Uses the [OpenTelemetry FastAPI Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/fastapi/fastapi.html)
@@ -465,6 +465,7 @@ class Logfire:
465465
These are disabled by default to reduce overhead and the number of spans created,
466466
since many can be created for a single request, and they are not often useful.
467467
If enabled, they will be set to debug level, meaning they will usually still be hidden in the UI.
468+
extra_spans: Whether to include the extra 'FastAPI arguments' and 'endpoint function' spans.
468469
opentelemetry_kwargs: Additional keyword arguments to pass to the OpenTelemetry FastAPI instrumentation.
469470
470471
Returns:

logfire-api/logfire_api/_internal/tracer.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import opentelemetry.trace as trace_api
22
from .config import LogfireConfig as LogfireConfig
3-
from .constants import ATTRIBUTES_MESSAGE_KEY as ATTRIBUTES_MESSAGE_KEY, ATTRIBUTES_PENDING_SPAN_REAL_PARENT_KEY as ATTRIBUTES_PENDING_SPAN_REAL_PARENT_KEY, ATTRIBUTES_SAMPLE_RATE_KEY as ATTRIBUTES_SAMPLE_RATE_KEY, ATTRIBUTES_SPAN_TYPE_KEY as ATTRIBUTES_SPAN_TYPE_KEY, ATTRIBUTES_VALIDATION_ERROR_KEY as ATTRIBUTES_VALIDATION_ERROR_KEY, log_level_attributes as log_level_attributes
4-
from .utils import handle_internal_errors as handle_internal_errors
3+
from .constants import ATTRIBUTES_EXCEPTION_FINGERPRINT_KEY as ATTRIBUTES_EXCEPTION_FINGERPRINT_KEY, ATTRIBUTES_MESSAGE_KEY as ATTRIBUTES_MESSAGE_KEY, ATTRIBUTES_PENDING_SPAN_REAL_PARENT_KEY as ATTRIBUTES_PENDING_SPAN_REAL_PARENT_KEY, ATTRIBUTES_SAMPLE_RATE_KEY as ATTRIBUTES_SAMPLE_RATE_KEY, ATTRIBUTES_SPAN_TYPE_KEY as ATTRIBUTES_SPAN_TYPE_KEY, ATTRIBUTES_VALIDATION_ERROR_KEY as ATTRIBUTES_VALIDATION_ERROR_KEY, log_level_attributes as log_level_attributes
4+
from .utils import canonicalize_exception_traceback as canonicalize_exception_traceback, handle_internal_errors as handle_internal_errors, sha256_string as sha256_string
55
from _typeshed import Incomplete
66
from collections.abc import Mapping, Sequence
77
from dataclasses import dataclass, field

logfire-api/logfire_api/_internal/utils.pyi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ from requests import RequestException, Response
1717
from types import TracebackType
1818
from typing import Any, Callable, ParamSpec, TypeVar, TypedDict
1919

20+
_ = BaseExceptionGroup
21+
22+
class BaseExceptionGroup(BaseException):
23+
"""Stub for BaseExceptionGroup for Python < 3.11."""
2024
SysExcInfo = tuple[type[BaseException], BaseException, TracebackType | None] | tuple[None, None, None]
2125
P = ParamSpec('P')
2226
T = TypeVar('T')
@@ -125,3 +129,15 @@ def platform_is_emscripten() -> bool:
125129
126130
Threads cannot be created on Emscripten, so we need to avoid any code that creates threads.
127131
"""
132+
def canonicalize_exception_traceback(exc: BaseException) -> str:
133+
"""Return a canonical string representation of an exception traceback.
134+
135+
Exceptions with the same representation are considered the same for fingerprinting purposes.
136+
The source line is used, but not the line number, so that changes elsewhere in a file are irrelevant.
137+
The module is used instead of the filename.
138+
The same line appearing multiple times in a stack is ignored.
139+
Exception group sub-exceptions are sorted and deduplicated.
140+
If the exception has a cause or (not suppressed) context, it is included in the representation.
141+
Cause and context are treated as different.
142+
"""
143+
def sha256_string(s: str) -> str: ...

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.24.2"
7+
version = "3.25.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.24.2"
7+
version = "3.25.0"
88
description = "The best Python observability tool! 🪵🔥"
99
requires-python = ">=3.9"
1010
authors = [

0 commit comments

Comments
 (0)