Skip to content

Commit

Permalink
Release v3.0.0 (#777)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmojaki authored Jan 7, 2025
1 parent 5f9dd90 commit 15718a0
Show file tree
Hide file tree
Showing 19 changed files with 218 additions and 185 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Release Notes

## [v3.0.0] (2025-01-07)

* **BREAKING CHANGE**: Removed `capture_request_json_body`, `capture_request_text_body`, `capture_request_form_data`, and `capture_response_json_body` parameters from `logfire.instrument_httpx()`, replaced with `capture_request_body` `capture_response_body` by @Kludex in [#769](https://github.com/pydantic/logfire/pull/769)

Other changes:

* Add `distributed_tracing` argument to `logfire.configure()` and warn by default when trace context is extracted by @alexmojaki in [#773](https://github.com/pydantic/logfire/pull/773)
* Don't show `urllib3` when `requests` is installed on `logfire inspect` by @Kludex in [#744](https://github.com/pydantic/logfire/pull/744)
* Add `--ignore` to `logfire inspect` by @Kludex in [#748](https://github.com/pydantic/logfire/pull/748)
* Access `model_fields` on the model class by @Viicos in [#761](https://github.com/pydantic/logfire/pull/761)
* Remove double record exception by @dmontagu in [#712](https://github.com/pydantic/logfire/pull/712)

## [v2.11.1] (2024-12-30)

* Handle errors from `sqlalchemy.inspect` by @alexmojaki in [#733](https://github.com/pydantic/logfire/pull/733)
Expand Down Expand Up @@ -504,3 +516,4 @@ First release from new repo!
[v2.10.0]: https://github.com/pydantic/logfire/compare/v2.9.0...v2.10.0
[v2.11.0]: https://github.com/pydantic/logfire/compare/v2.10.0...v2.11.0
[v2.11.1]: https://github.com/pydantic/logfire/compare/v2.11.0...v2.11.1
[v3.0.0]: https://github.com/pydantic/logfire/compare/v2.11.1...v3.0.0
6 changes: 6 additions & 0 deletions logfire-api/logfire_api/_internal/cli.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ from .utils import read_toml_file as read_toml_file
from _typeshed import Incomplete
from logfire.exceptions import LogfireConfigError as LogfireConfigError
from logfire.propagate import ContextCarrier as ContextCarrier, get_context as get_context
from typing import Any, Sequence

BASE_OTEL_INTEGRATION_URL: str
BASE_DOCS_URL: str
Expand All @@ -24,6 +25,7 @@ def parse_whoami(args: argparse.Namespace) -> None:
def parse_clean(args: argparse.Namespace) -> None:
"""Remove the contents of the Logfire data directory."""

STANDARD_LIBRARY_PACKAGES: Incomplete
OTEL_PACKAGES: set[str]
OTEL_PACKAGE_LINK: Incomplete

Expand All @@ -42,5 +44,9 @@ def parse_use_project(args: argparse.Namespace) -> None:
"""Use an existing project."""
def parse_info(_args: argparse.Namespace) -> None:
"""Show versions of logfire, OS and related packages."""

class SplitArgs(argparse.Action):
def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, values: str | Sequence[Any] | None, option_string: str | None = None): ...

def main(args: list[str] | None = None) -> None:
"""Run the CLI."""
14 changes: 11 additions & 3 deletions logfire-api/logfire_api/_internal/config.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import dataclasses
import requests
from ..propagate import NoExtractTraceContextPropagator as NoExtractTraceContextPropagator, WarnOnExtractTraceContextPropagator as WarnOnExtractTraceContextPropagator
from .auth import DEFAULT_FILE as DEFAULT_FILE, DefaultFile as DefaultFile, is_logged_in as is_logged_in
from .config_params import ParamManager as ParamManager, PydanticPluginRecordValues as PydanticPluginRecordValues
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
Expand Down Expand Up @@ -78,7 +79,7 @@ class CodeSource:

class DeprecatedKwargs(TypedDict): ...

def configure(*, local: bool = False, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, environment: 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, code_source: CodeSource | None = None, advanced: AdvancedOptions | None = None, **deprecated_kwargs: Unpack[DeprecatedKwargs]) -> Logfire:
def configure(*, local: bool = False, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, environment: 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, code_source: CodeSource | None = None, distributed_tracing: bool | None = None, advanced: AdvancedOptions | None = None, **deprecated_kwargs: Unpack[DeprecatedKwargs]) -> Logfire:
"""Configure the logfire SDK.
Args:
Expand Down Expand Up @@ -127,6 +128,12 @@ def configure(*, local: bool = False, send_to_logfire: bool | Literal['if-token-
sampling: Sampling options. See the [sampling guide](https://logfire.pydantic.dev/docs/guides/advanced/sampling/).
code_source: Settings for the source code of the project.
distributed_tracing: By default, incoming trace context is extracted, but generates a warning.
Set to `True` to disable the warning.
Set to `False` to suppress extraction of incoming trace context.
See [Unintentional Distributed Tracing](https://logfire.pydantic.dev/docs/how-to-guides/distributed-tracing/#unintentional-distributed-tracing)
for more information.
This setting always applies globally, and the last value set is used, including the default value.
advanced: Advanced options primarily used for testing by Logfire developers.
"""

Expand All @@ -153,17 +160,18 @@ class _LogfireConfigData:
inspect_arguments: bool
sampling: SamplingOptions
code_source: CodeSource | None
distributed_tracing: bool | None
advanced: AdvancedOptions

class LogfireConfig(_LogfireConfigData):
def __init__(self, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, environment: 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, code_source: CodeSource | None = None, advanced: AdvancedOptions | None = None) -> None:
def __init__(self, send_to_logfire: bool | Literal['if-token-present'] | None = None, token: str | None = None, service_name: str | None = None, service_version: str | None = None, environment: 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, code_source: CodeSource | None = None, distributed_tracing: bool | None = None, advanced: AdvancedOptions | None = None) -> None:
"""Create a new LogfireConfig.
Users should never need to call this directly, instead use `logfire.configure`.
See `_LogfireConfigData` for parameter documentation.
"""
def configure(self, send_to_logfire: bool | Literal['if-token-present'] | None, token: str | None, service_name: str | None, service_version: str | None, environment: 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, code_source: CodeSource | None, advanced: AdvancedOptions | None) -> None: ...
def configure(self, send_to_logfire: bool | Literal['if-token-present'] | None, token: str | None, service_name: str | None, service_version: str | None, environment: 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, code_source: CodeSource | None, distributed_tracing: bool | None, advanced: AdvancedOptions | None) -> None: ...
def initialize(self) -> None:
"""Configure internals to start exporting traces and metrics."""
def force_flush(self, timeout_millis: int = 30000) -> bool:
Expand Down
1 change: 1 addition & 0 deletions logfire-api/logfire_api/_internal/config_params.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ TRACE_SAMPLE_RATE: Incomplete
INSPECT_ARGUMENTS: Incomplete
IGNORE_NO_CONFIG: Incomplete
BASE_URL: Incomplete
DISTRIBUTED_TRACING: Incomplete
CONFIG_PARAMS: Incomplete

@dataclass
Expand Down
92 changes: 38 additions & 54 deletions logfire-api/logfire_api/_internal/integrations/httpx.pyi
Original file line number Diff line number Diff line change
@@ -1,90 +1,74 @@
import httpx
from _typeshed import Incomplete
from collections.abc import Generator
from email.headerregistry import ContentTypeHeader
from logfire import Logfire as Logfire
from functools import cached_property
from logfire import Logfire as Logfire, LogfireSpan as LogfireSpan
from logfire._internal.main import set_user_attributes_on_raw_span as set_user_attributes_on_raw_span
from logfire._internal.stack_info import warn_at_user_stacklevel as warn_at_user_stacklevel
from logfire._internal.utils import handle_internal_errors as handle_internal_errors
from logfire.integrations.httpx import AsyncRequestHook as AsyncRequestHook, AsyncResponseHook as AsyncResponseHook, RequestHook as RequestHook, RequestInfo as RequestInfo, ResponseHook as ResponseHook, ResponseInfo as ResponseInfo
from logfire.propagate import attach_context as attach_context, get_context as get_context
from opentelemetry.trace import Span
from typing import Any, Callable, Literal, Mapping, ParamSpec, TypeVar, TypedDict
from typing import Any, Awaitable, Callable, Literal, Mapping, ParamSpec

class AsyncClientKwargs(TypedDict, total=False):
request_hook: RequestHook | AsyncRequestHook
response_hook: ResponseHook | AsyncResponseHook
skip_dep_check: bool

class ClientKwargs(TypedDict, total=False):
request_hook: RequestHook
response_hook: ResponseHook
skip_dep_check: bool

class HTTPXInstrumentKwargs(TypedDict, total=False):
request_hook: RequestHook
response_hook: ResponseHook
async_request_hook: AsyncRequestHook
async_response_hook: AsyncResponseHook
skip_dep_check: bool
AnyRequestHook = TypeVar('AnyRequestHook', RequestHook, AsyncRequestHook)
AnyResponseHook = TypeVar('AnyResponseHook', ResponseHook, AsyncResponseHook)
Hook = TypeVar('Hook', RequestHook, ResponseHook)
AsyncHook = TypeVar('AsyncHook', AsyncRequestHook, AsyncResponseHook)
P = ParamSpec('P')

def instrument_httpx(logfire_instance: Logfire, client: httpx.Client | httpx.AsyncClient | None, capture_headers: bool, capture_request_json_body: bool, capture_request_text_body: bool, capture_response_json_body: bool, capture_request_form_data: bool, **kwargs: Any) -> None:
def instrument_httpx(logfire_instance: Logfire, client: httpx.Client | httpx.AsyncClient | None, capture_headers: bool, capture_request_body: bool, capture_response_body: bool, request_hook: RequestHook | AsyncRequestHook | None, response_hook: ResponseHook | AsyncResponseHook | None, async_request_hook: AsyncRequestHook | None, async_response_hook: AsyncResponseHook | None, **kwargs: Any) -> None:
"""Instrument the `httpx` module so that spans are automatically created for each request.
See the `Logfire.instrument_httpx` method for details.
"""

class LogfireHttpxRequestInfo(RequestInfo):
span: Span
def capture_headers(self) -> None: ...
def capture_body_if_json(self, attr_name: str = 'http.request.body.json'): ...
def capture_body_if_text(self, attr_name: str = 'http.request.body.text'): ...
def capture_body_if_form(self, attr_name: str = 'http.request.body.form'): ...
def capture_text_as_json(self, attr_name: str = 'http.request.body.json', text: str | None = None): ...
@property
def body_is_streaming(self): ...
class LogfireHttpxInfoMixin:
headers: httpx.Headers
@property
def content_type_header_object(self) -> ContentTypeHeader: ...
@property
def content_type_header_string(self) -> str: ...

class LogfireHttpxRequestInfo(RequestInfo, LogfireHttpxInfoMixin):
span: Span
def capture_headers(self) -> None: ...
def capture_body(self) -> None: ...
def capture_body_if_text(self, attr_name: str = 'http.request.body.text'): ...
def capture_body_if_form(self, attr_name: str = 'http.request.body.form') -> bool: ...
def capture_text_as_json(self, attr_name: str, text: str): ...
@property
def content_type_is_json(self): ...
@property
def content_type_is_text(self): ...
@property
def content_type_is_form(self): ...
def body_is_streaming(self): ...
@property
def content_type_charset(self): ...
@property
def content(self) -> bytes: ...
@property
def text(self) -> str: ...
@property
@cached_property
def form_data(self) -> Mapping[str, Any] | None: ...
def set_complex_span_attributes(self, attributes: dict[str, Any]): ...

def make_request_hook(hook: RequestHook | None, should_capture_headers: bool, should_capture_json: bool, should_capture_text: bool, should_capture_form_data: bool) -> RequestHook | None: ...
def make_async_request_hook(hook: AsyncRequestHook | RequestHook | None, should_capture_headers: bool, should_capture_json: bool, should_capture_text: bool, should_capture_form_data: bool) -> AsyncRequestHook | None: ...
def capture_request(request: LogfireHttpxRequestInfo, should_capture_headers: bool, should_capture_json: bool, should_capture_text: bool, should_capture_form_data: bool) -> None: ...
def make_response_hook(hook: ResponseHook | None, should_capture_headers: bool, should_capture_json: bool, logfire_instance: Logfire) -> ResponseHook | None: ...
def make_async_response_hook(hook: ResponseHook | AsyncResponseHook | None, should_capture_headers: bool, should_capture_json: bool, logfire_instance: Logfire) -> AsyncResponseHook | None: ...
def capture_response_json(logfire_instance: Logfire, response_info: ResponseInfo, is_async: bool) -> None: ...
class LogfireHttpxResponseInfo(ResponseInfo, LogfireHttpxInfoMixin):
span: Span
logfire_instance: Logfire
is_async: bool
def capture_headers(self) -> None: ...
def capture_body_if_text(self, attr_name: str = 'http.response.body.text'): ...
@cached_property
def response(self) -> httpx.Response: ...
def on_response_read(self, hook: Callable[[LogfireSpan], None]): ...
def wrap_response_read(self, hook: Callable[[Callable[[], bytes]], bytes]): ...
def wrap_response_aread(self, hook: Callable[[Callable[[], Awaitable[bytes]]], Awaitable[bytes]]): ...
def attach_original_span_context(self) -> Generator[None]: ...
def capture_text_as_json(self, span: LogfireSpan, *, text: str, attr_name: str): ...

def make_request_hook(hook: RequestHook | None, capture_headers: bool, capture_body: bool) -> RequestHook | None: ...
def make_async_request_hook(hook: AsyncRequestHook | RequestHook | None, should_capture_headers: bool, should_capture_body: bool) -> AsyncRequestHook | None: ...
def make_response_hook(hook: ResponseHook | None, capture_headers: bool, capture_body: bool, logfire_instance: Logfire) -> ResponseHook | None: ...
def make_async_response_hook(hook: ResponseHook | AsyncResponseHook | None, should_capture_headers: bool, should_capture_body: bool, logfire_instance: Logfire) -> AsyncResponseHook | None: ...
def capture_request(span: Span, request: RequestInfo, should_capture_headers: bool, should_capture_body: bool) -> LogfireHttpxRequestInfo: ...
def capture_response(span: Span, request: RequestInfo, response: ResponseInfo, logfire_instance: Logfire, capture_headers: bool, capture_body: bool, *, is_async: bool) -> tuple[LogfireHttpxRequestInfo, LogfireHttpxResponseInfo]: ...
async def run_async_hook(hook: Callable[P, Any] | None, *args: P.args, **kwargs: P.kwargs) -> None: ...
def run_hook(hook: Callable[P, Any] | None, *args: P.args, **kwargs: P.kwargs) -> None: ...
def capture_response_headers(span: Span, response: ResponseInfo) -> None: ...
def capture_headers(span: Span, headers: httpx.Headers, request_or_response: Literal['request', 'response']) -> None: ...
def decode_body(body: bytes, charset: str): ...
def capture_request_or_response_headers(span: Span, headers: httpx.Headers, request_or_response: Literal['request', 'response']) -> None: ...

CODES_FOR_METHODS_WITH_DATA_PARAM: Incomplete

def content_type_header_from_string(content_type: str) -> ContentTypeHeader: ...
def content_type_subtypes(subtype: str) -> set[str]: ...
def is_json_type(content_type: str) -> bool: ...

TEXT_SUBTYPES: Incomplete

def is_text_type(content_type: str) -> bool: ...
13 changes: 5 additions & 8 deletions logfire-api/logfire_api/_internal/integrations/mysql.pyi
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
from mysql.connector.abstracts import MySQLConnectionAbstract
from mysql.connector.pooling import PooledMySQLConnection
from mysql.connector.abstracts import MySQLConnectionAbstract as MySQLConnectionAbstract
from mysql.connector.pooling import PooledMySQLConnection as PooledMySQLConnection
from opentelemetry.trace import TracerProvider
from typing_extensions import TypeVar, TypedDict, Unpack
from typing import Any, TypeVar

MySQLConnection = TypeVar('MySQLConnection', bound=PooledMySQLConnection | MySQLConnectionAbstract | None)
MySQLConnection = TypeVar('MySQLConnection', 'PooledMySQLConnection | MySQLConnectionAbstract', None)

class MySQLInstrumentKwargs(TypedDict, total=False):
skip_dep_check: bool

def instrument_mysql(*, conn: MySQLConnection = None, tracer_provider: TracerProvider, **kwargs: Unpack[MySQLInstrumentKwargs]) -> MySQLConnection:
def instrument_mysql(*, conn: MySQLConnection = None, tracer_provider: TracerProvider, **kwargs: Any) -> MySQLConnection:
"""Instrument the `mysql` module or a specific MySQL connection so that spans are automatically created for each operation.
See the `Logfire.instrument_mysql` method for details.
Expand Down
Loading

0 comments on commit 15718a0

Please sign in to comment.