Skip to content

Commit 0758b24

Browse files
authored
Release v0.47.0 (#326)
1 parent 6326c13 commit 0758b24

File tree

12 files changed

+157
-19
lines changed

12 files changed

+157
-19
lines changed

CHANGELOG.md

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

3+
## [v0.47.0] (2024-07-20)
4+
5+
* Fix recursive logging from OTEL's `BatchSpanProcessor` by @alexmojaki in https://github.com/pydantic/logfire/pull/306
6+
* Set sqlalchemy 'connect' spans to debug level by @alexmojaki in https://github.com/pydantic/logfire/pull/307
7+
* Add type hints to instrument methods by @Kludex in https://github.com/pydantic/logfire/pull/320
8+
* Handle older versions of anthropic by @alexmojaki in https://github.com/pydantic/logfire/pull/316
9+
* Update dependencies, handle change in importlib by @alexmojaki in https://github.com/pydantic/logfire/pull/323
10+
* Summarize db.statement in message by @alexmojaki in https://github.com/pydantic/logfire/pull/308
11+
* Handle and test other OpenAI/Anthropic client methods by @alexmojaki in https://github.com/pydantic/logfire/pull/312
12+
313
## [v0.46.1] (2024-07-05)
414

515
* Fix release process for `logfire-api` by @Kludex in https://github.com/pydantic/logfire/pull/303
@@ -204,6 +214,7 @@ First release from new repo!
204214
* Ensure `logfire.testing` doesn't depend on pydantic and eval_type_backport by @alexmojaki in https://github.com/pydantic/logfire/pull/40
205215
* Allow using pydantic plugin with models defined before calling logfire.configure by @alexmojaki in https://github.com/pydantic/logfire/pull/36
206216

217+
[v0.47.0]: https://github.com/pydantic/logfire/compare/v0.46.1...v0.47.0
207218
[v0.46.1]: https://github.com/pydantic/logfire/compare/v0.46.0...v0.46.1
208219
[v0.46.0]: https://github.com/pydantic/logfire/compare/v0.45.1...v0.46.0
209220
[v0.45.1]: https://github.com/pydantic/logfire/compare/v0.45.0...v0.45.1
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from _typeshed import Incomplete
2+
from typing import Any, Mapping
3+
4+
MAX_QUERY_MESSAGE_LENGTH: int
5+
6+
def message_from_db_statement(attributes: Mapping[str, Any], message: str | None, span_name: str) -> str | None:
7+
"""Try to construct a useful span message from OTel db statement.
8+
9+
Returns: A new string to use as span message or None to keep the original message.
10+
"""
11+
12+
TABLE_RE: str
13+
SELECT_RE: Incomplete
14+
SELECT_CTE_RE: Incomplete
15+
SELECT_SUBQUERY_RE: Incomplete
16+
INSERT_RE: Incomplete
17+
18+
def summarize_query(db_statement: str) -> str | None:
19+
"""Summarize a database statement, specifically SQL queries.
20+
21+
Args:
22+
db_statement: The database statement to summarize.
23+
24+
Returns: A new string to use as span message or None to keep the original message.
25+
26+
"""
27+
def select(expr: str, table: str, *, match_end: int, db_statement: str, ctes: str | None = None, sub_query: str | None = None) -> str: ...
28+
def truncate(s: str, length: int) -> str: ...
29+
30+
FALLBACK_HALF: Incomplete
31+
32+
def fallback(db_statement: str): ...

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from ..constants import 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_SPAN_TYPE_KEY as ATTRIBUTES_SPAN_TYPE_KEY, LEVEL_NUMBERS as LEVEL_NUMBERS, PENDING_SPAN_NAME_SUFFIX as PENDING_SPAN_NAME_SUFFIX, log_level_attributes as log_level_attributes
2+
from ..db_statement_summary import message_from_db_statement as message_from_db_statement
23
from ..scrubbing import BaseScrubber as BaseScrubber
34
from ..utils import ReadableSpanDict as ReadableSpanDict, is_instrumentation_suppressed as is_instrumentation_suppressed, span_to_dict as span_to_dict, truncate_string as truncate_string
45
from .wrapper import WrapperSpanProcessor as WrapperSpanProcessor

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
from flask.app import Flask
2-
from typing import Any
2+
from opentelemetry.trace import Span
3+
from typing_extensions import Protocol, TypedDict, Unpack
4+
from wsgiref.types import WSGIEnvironment
35

4-
def instrument_flask(app: Flask, **kwargs: Any):
6+
class RequestHook(Protocol):
7+
def __call__(self, span: Span, environment: WSGIEnvironment) -> None: ...
8+
9+
class ResponseHook(Protocol):
10+
def __call__(self, span: Span, status: str, response_headers: list[tuple[str, str]]) -> None: ...
11+
12+
class FlaskInstrumentKwargs(TypedDict, total=False):
13+
request_hook: RequestHook | None
14+
response_hook: RequestHook | None
15+
excluded_urls: str | None
16+
enable_commenter: bool | None
17+
commenter_options: dict[str, str] | None
18+
19+
def instrument_flask(app: Flask, **kwargs: Unpack[FlaskInstrumentKwargs]):
520
"""Instrument `app` so that spans are automatically created for each request.
621
722
See the `Logfire.instrument_flask` method for details.
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
from _typeshed import Incomplete
2+
from opentelemetry.instrumentation.psycopg import PsycopgInstrumentor
3+
from opentelemetry.instrumentation.psycopg2 import Psycopg2Instrumentor
24
from typing import Any
5+
from typing_extensions import TypedDict, Unpack
6+
7+
Instrumentor = PsycopgInstrumentor | Psycopg2Instrumentor
8+
9+
class CommenterOptions(TypedDict, total=False):
10+
db_driver: bool
11+
db_framework: bool
12+
opentelemetry_values: bool
13+
14+
class PsycopgInstrumentKwargs(TypedDict, total=False):
15+
enable_commenter: bool
16+
commenter_options: CommenterOptions
317

4-
Instrumentor: Incomplete
518
PACKAGE_NAMES: Incomplete
619

7-
def instrument_psycopg(conn_or_module: Any = None, **kwargs: Any):
20+
def instrument_psycopg(conn_or_module: Any = None, **kwargs: Unpack[PsycopgInstrumentKwargs]) -> None:
821
"""Instrument a `psycopg` connection or module so that spans are automatically created for each query.
922
1023
See the `Logfire.instrument_psycopg` method for details.
1124
"""
12-
def check_version(name: str, version: str, instrumentor: Instrumentor): ...
25+
def check_version(name: str, version: str, instrumentor: Instrumentor) -> bool: ...

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
1+
from pymongo.monitoring import CommandFailedEvent, CommandStartedEvent, CommandSucceededEvent
12
from typing import Any
3+
from typing_extensions import Protocol, TypedDict, Unpack
24

3-
def instrument_pymongo(**kwargs: Any):
5+
class RequestHook(Protocol):
6+
def __call__(self, span: Any, event: CommandStartedEvent) -> None: ...
7+
8+
class ResponseHook(Protocol):
9+
def __call__(self, span: Any, event: CommandSucceededEvent) -> None: ...
10+
11+
class FailedHook(Protocol):
12+
def __call__(self, span: Any, event: CommandFailedEvent) -> None: ...
13+
14+
class PymongoInstrumentKwargs(TypedDict, total=False):
15+
request_hook: RequestHook | None
16+
response_hook: ResponseHook | None
17+
failed_hook: FailedHook | None
18+
capture_statement: bool | None
19+
skip_dep_check: bool
20+
21+
def instrument_pymongo(**kwargs: Unpack[PymongoInstrumentKwargs]) -> None:
422
"""Instrument the `pymongo` module so that spans are automatically created for each operation.
523
624
See the `Logfire.instrument_pymongo` method for details.

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
from opentelemetry.trace import Span
2+
from redis import Connection
13
from typing import Any
4+
from typing_extensions import Protocol, TypedDict, Unpack
25

3-
def instrument_redis(**kwargs: Any):
6+
class RequestHook(Protocol):
7+
def __call__(self, span: Span, instance: Connection, *args: Any, **kwargs: Any) -> None: ...
8+
9+
class ResponseHook(Protocol):
10+
def __call__(self, span: Span, instance: Connection, response: Any) -> None: ...
11+
12+
class RedisInstrumentKwargs(TypedDict, total=False):
13+
request_hook: RequestHook | None
14+
response_hook: ResponseHook | None
15+
skip_dep_check: bool
16+
17+
def instrument_redis(**kwargs: Unpack[RedisInstrumentKwargs]) -> None:
418
"""Instrument the `redis` module so that spans are automatically created for each operation.
519
620
See the `Logfire.instrument_redis` method for details.

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
from typing import Any
1+
from sqlalchemy import Engine
2+
from typing_extensions import TypedDict, Unpack
23

3-
def instrument_sqlalchemy(**kwargs: Any):
4+
class CommenterOptions(TypedDict, total=False):
5+
db_driver: bool
6+
db_framework: bool
7+
opentelemetry_values: bool
8+
9+
class SQLAlchemyInstrumentKwargs(TypedDict, total=False):
10+
engine: Engine | None
11+
enable_commenter: bool | None
12+
commenter_options: CommenterOptions | None
13+
skip_dep_check: bool
14+
15+
def instrument_sqlalchemy(**kwargs: Unpack[SQLAlchemyInstrumentKwargs]) -> None:
416
"""Instrument the `sqlalchemy` module so that spans are automatically created for each query.
517
618
See the `Logfire.instrument_sqlalchemy` method for details.

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,23 @@
1+
from opentelemetry.trace import Span
12
from starlette.applications import Starlette
23
from typing import Any
4+
from typing_extensions import Protocol, TypedDict, Unpack
35

4-
def instrument_starlette(app: Starlette, **kwargs: Any):
6+
class ServerRequestHook(Protocol):
7+
def __call__(self, span: Span, scope: dict[str, Any]): ...
8+
9+
class ClientRequestHook(Protocol):
10+
def __call__(self, span: Span, scope: dict[str, Any]): ...
11+
12+
class ClientResponseHook(Protocol):
13+
def __call__(self, span: Span, message: dict[str, Any]): ...
14+
15+
class StarletteInstrumentKwargs(TypedDict, total=False):
16+
server_request_hook: ServerRequestHook | None
17+
client_request_hook: ClientRequestHook | None
18+
client_response_hook: ClientResponseHook | None
19+
20+
def instrument_starlette(app: Starlette, **kwargs: Unpack[StarletteInstrumentKwargs]):
521
"""Instrument `app` so that spans are automatically created for each request.
622
723
See the `Logfire.instrument_starlette` method for details.

logfire-api/logfire_api/_internal/main.pyi

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ from .config import GLOBAL_CONFIG as GLOBAL_CONFIG, LogfireConfig as LogfireConf
99
from .constants import ATTRIBUTES_JSON_SCHEMA_KEY as ATTRIBUTES_JSON_SCHEMA_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, 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
1010
from .formatter import logfire_format as logfire_format, logfire_format_with_magic as logfire_format_with_magic
1111
from .instrument import LogfireArgs as LogfireArgs, instrument as instrument
12+
from .integrations.flask import FlaskInstrumentKwargs as FlaskInstrumentKwargs
13+
from .integrations.psycopg import PsycopgInstrumentKwargs as PsycopgInstrumentKwargs
14+
from .integrations.pymongo import PymongoInstrumentKwargs as PymongoInstrumentKwargs
15+
from .integrations.redis import RedisInstrumentKwargs as RedisInstrumentKwargs
16+
from .integrations.sqlalchemy import SQLAlchemyInstrumentKwargs as SQLAlchemyInstrumentKwargs
17+
from .integrations.starlette import StarletteInstrumentKwargs as StarletteInstrumentKwargs
1218
from .json_encoder import logfire_json_dumps as logfire_json_dumps
1319
from .json_schema import JsonSchemaProperties as JsonSchemaProperties, attributes_json_schema as attributes_json_schema, attributes_json_schema_properties as attributes_json_schema_properties, create_json_schema as create_json_schema
1420
from .metrics import ProxyMeterProvider as ProxyMeterProvider
@@ -27,7 +33,7 @@ from starlette.requests import Request as Request
2733
from starlette.websockets import WebSocket as WebSocket
2834
from types import TracebackType as TracebackType
2935
from typing import Any, Callable, ContextManager, Iterable, Literal, Sequence, TypeVar
30-
from typing_extensions import LiteralString
36+
from typing_extensions import LiteralString, Unpack
3137

3238
ExcInfo: typing.TypeAlias
3339

@@ -542,7 +548,7 @@ class Logfire:
542548
**kwargs: Additional keyword arguments to pass to the OpenTelemetry `instrument` methods,
543549
particularly `request_hook` and `response_hook`.
544550
"""
545-
def instrument_psycopg(self, conn_or_module: Any = None, **kwargs: Any):
551+
def instrument_psycopg(self, conn_or_module: Any = None, **kwargs: Unpack[PsycopgInstrumentKwargs]) -> None:
546552
"""Instrument a `psycopg` connection or module so that spans are automatically created for each query.
547553
548554
Uses the OpenTelemetry instrumentation libraries for
@@ -561,14 +567,14 @@ class Logfire:
561567
**kwargs: Additional keyword arguments to pass to the OpenTelemetry `instrument` methods,
562568
particularly `enable_commenter` and `commenter_options`.
563569
"""
564-
def instrument_flask(self, app: Flask, **kwargs: Any):
570+
def instrument_flask(self, app: Flask, **kwargs: Unpack[FlaskInstrumentKwargs]) -> None:
565571
"""Instrument `app` so that spans are automatically created for each request.
566572
567573
Uses the
568574
[OpenTelemetry Flask Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/flask/flask.html)
569575
library, specifically `FlaskInstrumentor().instrument_app()`, to which it passes `**kwargs`.
570576
"""
571-
def instrument_starlette(self, app: Starlette, **kwargs: Any):
577+
def instrument_starlette(self, app: Starlette, **kwargs: Unpack[StarletteInstrumentKwargs]) -> None:
572578
"""Instrument `app` so that spans are automatically created for each request.
573579
574580
Uses the
@@ -582,21 +588,21 @@ class Logfire:
582588
[OpenTelemetry aiohttp client Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/aiohttp_client/aiohttp_client.html)
583589
library, specifically `AioHttpClientInstrumentor().instrument()`, to which it passes `**kwargs`.
584590
"""
585-
def instrument_sqlalchemy(self, **kwargs: Any):
591+
def instrument_sqlalchemy(self, **kwargs: Unpack[SQLAlchemyInstrumentKwargs]) -> None:
586592
"""Instrument the `sqlalchemy` module so that spans are automatically created for each query.
587593
588594
Uses the
589595
[OpenTelemetry SQLAlchemy Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/sqlalchemy/sqlalchemy.html)
590596
library, specifically `SQLAlchemyInstrumentor().instrument()`, to which it passes `**kwargs`.
591597
"""
592-
def instrument_pymongo(self, **kwargs: Any):
598+
def instrument_pymongo(self, **kwargs: Unpack[PymongoInstrumentKwargs]) -> None:
593599
"""Instrument the `pymongo` module so that spans are automatically created for each operation.
594600
595601
Uses the
596602
[OpenTelemetry pymongo Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymongo/pymongo.html)
597603
library, specifically `PymongoInstrumentor().instrument()`, to which it passes `**kwargs`.
598604
"""
599-
def instrument_redis(self, **kwargs: Any):
605+
def instrument_redis(self, **kwargs: Unpack[RedisInstrumentKwargs]) -> None:
600606
"""Instrument the `redis` module so that spans are automatically created for each operation.
601607
602608
Uses the

0 commit comments

Comments
 (0)