Skip to content

Commit

Permalink
Add 6 instrument methods (#207)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmojaki authored Jun 3, 2024
1 parent d27759f commit f600862
Show file tree
Hide file tree
Showing 22 changed files with 242 additions and 29 deletions.
7 changes: 3 additions & 4 deletions docs/integrations/aiohttp.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[AIOHTTP][aiohttp] is an asynchronous HTTP client/server framework for asyncio and Python.

The [OpenTelemetry Instrumentation AIOHTTP][opentelemetry-aiohttp] package can be used to instrument AIOHTTP.
The [`logfire.instrument_aiohttp_client()`][logfire.Logfire.instrument_aiohttp_client] method will create a span for every request made by your AIOHTTP clients.

!!! question "What about AIOHTTP Server?"
The AIOHTTP server instrumentation is not supported yet. You can track the progress [here][aiohttp-server].
Expand All @@ -20,11 +20,10 @@ Let's see a minimal example below. You can run it with `python main.py`:
```py title="main.py"
import logfire
import aiohttp
from opentelemetry.instrumentation.aiohttp_client import AioHttpClientInstrumentor


logfire.configure()
AioHttpClientInstrumentor().instrument()
logfire.instrument_aiohttp_client()


async def main():
Expand All @@ -38,7 +37,7 @@ if __name__ == "__main__":
asyncio.run(main())
```

You can read more about the AIOHTTP OpenTelemetry package [here][opentelemetry-aiohttp].
The keyword arguments of `logfire.instrument_aiohttp_client()` are passed to the `AioHttpClientInstrumentor().instrument()` method of the OpenTelemetry aiohttp client Instrumentation package, read more about it [here][opentelemetry-aiohttp].

[aiohttp]: https://docs.aiohttp.org/en/stable/
[aiohttp-server]: https://github.com/open-telemetry/opentelemetry-python-contrib/issues/501
Expand Down
9 changes: 5 additions & 4 deletions docs/integrations/flask.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Flask

The [OpenTelemetry Instrumentation Flask][opentelemetry-flask] package can be used to instrument [Flask][flask].
The [`logfire.instrument_flask()`][logfire.Logfire.instrument_flask] method
will create a span for every request to your [Flask][flask] application.

## Install

Expand All @@ -15,13 +16,12 @@ Let's see a minimal example below. You can run it with `python main.py`:
```py title="main.py"
import logfire
from flask import Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor


logfire.configure()

app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)
logfire.instrument_flask(app)


@app.route("/")
Expand All @@ -33,7 +33,8 @@ if __name__ == "__main__":
app.run(debug=True)
```

You can read more about the Flask OpenTelemetry package [here][opentelemetry-flask].
The keyword arguments of `logfire.instrument_flask()` are passed to the `FlaskInstrumentor().instrument_app()` method
of the OpenTelemetry Flask Instrumentation package, read more about it [here][opentelemetry-flask].

## Capturing request and response headers
<!-- note that this section is duplicated for different frameworks but with slightly different links -->
Expand Down
7 changes: 3 additions & 4 deletions docs/integrations/pymongo.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PyMongo

The [OpenTelemetry Instrumentation PyMongo][opentelemetry-pymongo] package can be used to instrument [PyMongo][pymongo].
The [`logfire.instrument_pymongo()`][logfire.Logfire.instrument_pymongo] method will create a span for every operation performed using your [PyMongo][pymongo] clients.

## Installation

Expand Down Expand Up @@ -28,10 +28,9 @@ The following script connects to a MongoDB database, inserts a document, and que
```py
import logfire
from pymongo import MongoClient
from opentelemetry.instrumentation.pymongo import PymongoInstrumentor

logfire.configure()
PymongoInstrumentor().instrument(capture_statement=True) # (1)!
logfire.instrument_pymongo(capture_statement=True) # (1)!

client = MongoClient()
db = client["database"]
Expand All @@ -47,7 +46,7 @@ collection.find_one()

---

You can read more about the PyMongo OpenTelemetry package [here][opentelemetry-pymongo].
The keyword arguments of `logfire.instrument_pymongo()` are passed to the `PymongoInstrumentor().instrument()` method of the OpenTelemetry pymongo Instrumentation package, read more about it [here][opentelemetry-pymongo].

[pymongo]: https://pymongo.readthedocs.io/en/stable/
[opentelemetry-pymongo]: https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymongo/pymongo.html
7 changes: 3 additions & 4 deletions docs/integrations/redis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Redis

The [OpenTelemetry Instrumentation Redis][opentelemetry-redis] package can be used to instrument [Redis][redis].
The [`logfire.instrument_redis()`][logfire.Logfire.instrument_redis] method will create a span for every command executed by your [Redis][redis] clients.

## Installation

Expand All @@ -17,11 +17,10 @@ Let's see a minimal example below:
```py title="main.py"
import logfire
import redis
from opentelemetry.instrumentation.redis import RedisInstrumentor


logfire.configure()
RedisInstrumentor().instrument()
logfire.instrument_redis()

# This will report a span with the default settings
client = redis.StrictRedis(host="localhost", port=6379)
Expand All @@ -38,7 +37,7 @@ if __name__ == "__main__":
asyncio.run(main())
```

You can read more about the Redis OpenTelemetry package [here][opentelemetry-redis].
The keyword arguments of `logfire.instrument_redis()` are passed to the `RedisInstrumentor().instrument()` method of the OpenTelemetry Redis Instrumentation package, read more about it [here][opentelemetry-redis].

[redis]: https://redis.readthedocs.io/en/stable/
[opentelemetry-redis]: https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/redis/redis.html
8 changes: 3 additions & 5 deletions docs/integrations/sqlalchemy.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# SQLAlchemy

The [OpenTelemetry Instrumentation SQLAlchemy][opentelemetry-sqlalchemy] package can be used to
instrument [SQLAlchemy][sqlalchemy].
The [`logfire.instrument_sqlalchemy()`][logfire.Logfire.instrument_sqlalchemy] method will create a span for every query executed by a [SQLAlchemy][sqlalchemy] engine.

## Installation

Expand All @@ -16,15 +15,14 @@ Let's see a minimal example below. You can run it with `python main.py`:
```py title="main.py"
import logfire
from sqlalchemy import create_engine
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor

logfire.configure()

engine = create_engine("sqlite:///:memory:")
SQLAlchemyInstrumentor().instrument(engine=engine)
logfire.instrument_sqlalchemy(engine=engine)
```

You can read more about the SQLAlchemy OpenTelemetry package [here][opentelemetry-sqlalchemy].
The keyword arguments of `logfire.instrument_sqlalchemy()` are passed to the `SQLAlchemyInstrumentor().instrument()` method of the OpenTelemetry SQLAlchemy Instrumentation package, read more about it [here][opentelemetry-sqlalchemy].

!!! tip
If you use [SQLModel][sqlmodel], you can use the same `SQLAlchemyInstrumentor` to instrument it.
Expand Down
7 changes: 4 additions & 3 deletions docs/integrations/starlette.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Starlette

The [OpenTelemetry Instrumentation Starlette][opentelemetry-starlette] package can be used to instrument [Starlette][starlette].
The [`logfire.instrument_starlette()`][logfire.Logfire.instrument_starlette] method will create a span for every request to your [Starlette][starlette] application.

## Installation

Expand All @@ -20,7 +20,6 @@ You can run it with `python main.py`:

```py title="main.py"
import logfire
from opentelemetry.instrumentation.starlette import StarletteInstrumentor
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.requests import Request
Expand All @@ -34,14 +33,16 @@ async def home(request: Request) -> PlainTextResponse:


app = Starlette(routes=[Route("/", home)])
StarletteInstrumentor.instrument_app(app)
logfire.instrument_starlette(app)

if __name__ == "__main__":
import uvicorn

uvicorn.run(app)
```

The keyword arguments of `logfire.instrument_starlette()` are passed to the `StarletteInstrumentor.instrument_app()` method of the OpenTelemetry Starlette Instrumentation package, read more about it [here][opentelemetry-starlette].

!!! question "What about the OpenTelemetry ASGI middleware?"
If you are a more experienced user, you might be wondering why we are not using
the [OpenTelemetry ASGI middleware][opentelemetry-asgi]. The reason is that the
Expand Down
6 changes: 6 additions & 0 deletions logfire/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
instrument_requests = DEFAULT_LOGFIRE_INSTANCE.instrument_requests
instrument_psycopg = DEFAULT_LOGFIRE_INSTANCE.instrument_psycopg
instrument_django = DEFAULT_LOGFIRE_INSTANCE.instrument_django
instrument_flask = DEFAULT_LOGFIRE_INSTANCE.instrument_flask
instrument_starlette = DEFAULT_LOGFIRE_INSTANCE.instrument_starlette
instrument_aiohttp_client = DEFAULT_LOGFIRE_INSTANCE.instrument_aiohttp_client
instrument_sqlalchemy = DEFAULT_LOGFIRE_INSTANCE.instrument_sqlalchemy
instrument_redis = DEFAULT_LOGFIRE_INSTANCE.instrument_redis
instrument_pymongo = DEFAULT_LOGFIRE_INSTANCE.instrument_pymongo
shutdown = DEFAULT_LOGFIRE_INSTANCE.shutdown
with_tags = DEFAULT_LOGFIRE_INSTANCE.with_tags
# with_trace_sample_rate = DEFAULT_LOGFIRE_INSTANCE.with_trace_sample_rate
Expand Down
11 changes: 11 additions & 0 deletions logfire/_internal/integrations/aiohttp_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from opentelemetry.instrumentation.aiohttp_client import AioHttpClientInstrumentor


def instrument_aiohttp_client(**kwargs: Any):
"""Instrument the `aiohttp` module so that spans are automatically created for each client request.
See the `Logfire.instrument_aiohttp_client` method for details.
"""
AioHttpClientInstrumentor().instrument(**kwargs) # type: ignore[reportUnknownMemberType]
12 changes: 12 additions & 0 deletions logfire/_internal/integrations/flask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Any

from flask.app import Flask
from opentelemetry.instrumentation.flask import FlaskInstrumentor


def instrument_flask(app: Flask, **kwargs: Any):
"""Instrument `app` so that spans are automatically created for each request.
See the `Logfire.instrument_flask` method for details.
"""
FlaskInstrumentor().instrument_app(app, **kwargs) # type: ignore[reportUnknownMemberType]
11 changes: 11 additions & 0 deletions logfire/_internal/integrations/pymongo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from opentelemetry.instrumentation.pymongo import PymongoInstrumentor


def instrument_pymongo(**kwargs: Any):
"""Instrument the `pymongo` module so that spans are automatically created for each operation.
See the `Logfire.instrument_pymongo` method for details.
"""
PymongoInstrumentor().instrument(**kwargs) # type: ignore[reportUnknownMemberType]
11 changes: 11 additions & 0 deletions logfire/_internal/integrations/redis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from opentelemetry.instrumentation.redis import RedisInstrumentor


def instrument_redis(**kwargs: Any):
"""Instrument the `redis` module so that spans are automatically created for each operation.
See the `Logfire.instrument_redis` method for details.
"""
RedisInstrumentor().instrument(**kwargs) # type: ignore[reportUnknownMemberType]
11 changes: 11 additions & 0 deletions logfire/_internal/integrations/sqlalchemy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import Any

from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor


def instrument_sqlalchemy(**kwargs: Any):
"""Instrument the `sqlalchemy` module so that spans are automatically created for each query.
See the `Logfire.instrument_sqlalchemy` method for details.
"""
SQLAlchemyInstrumentor().instrument(**kwargs) # type: ignore[reportUnknownMemberType]
12 changes: 12 additions & 0 deletions logfire/_internal/integrations/starlette.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import Any

from opentelemetry.instrumentation.starlette import StarletteInstrumentor
from starlette.applications import Starlette


def instrument_starlette(app: Starlette, **kwargs: Any):
"""Instrument `app` so that spans are automatically created for each request.
See the `Logfire.instrument_starlette` method for details.
"""
StarletteInstrumentor().instrument_app(app, **kwargs) # type: ignore[reportUnknownMemberType]
74 changes: 74 additions & 0 deletions logfire/_internal/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@
import openai
from django.http import HttpRequest, HttpResponse
from fastapi import FastAPI
from flask.app import Flask
from opentelemetry.metrics import _Gauge as Gauge
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.websockets import WebSocket

Expand Down Expand Up @@ -1115,6 +1117,78 @@ def instrument_psycopg(self, conn_or_module: Any = None, **kwargs: Any):
self._warn_if_not_initialized_for_instrumentation()
return instrument_psycopg(conn_or_module, **kwargs)

def instrument_flask(self, app: Flask, **kwargs: Any):
"""Instrument `app` so that spans are automatically created for each request.
Uses the
[OpenTelemetry Flask Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/flask/flask.html)
library, specifically `FlaskInstrumentor().instrument_app()`, to which it passes `**kwargs`.
"""
from .integrations.flask import instrument_flask

self._warn_if_not_initialized_for_instrumentation()
return instrument_flask(app, **kwargs)

def instrument_starlette(self, app: Starlette, **kwargs: Any):
"""Instrument `app` so that spans are automatically created for each request.
Uses the
[OpenTelemetry Starlette Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/starlette/starlette.html)
library, specifically `StarletteInstrumentor.instrument_app()`, to which it passes `**kwargs`.
"""
from .integrations.starlette import instrument_starlette

self._warn_if_not_initialized_for_instrumentation()
return instrument_starlette(app, **kwargs)

def instrument_aiohttp_client(self, **kwargs: Any):
"""Instrument the `aiohttp` module so that spans are automatically created for each client request.
Uses the
[OpenTelemetry aiohttp client Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/aiohttp_client/aiohttp_client.html)
library, specifically `AioHttpClientInstrumentor().instrument()`, to which it passes `**kwargs`.
"""
from .integrations.aiohttp_client import instrument_aiohttp_client

self._warn_if_not_initialized_for_instrumentation()
return instrument_aiohttp_client(**kwargs)

def instrument_sqlalchemy(self, **kwargs: Any):
"""Instrument the `sqlalchemy` module so that spans are automatically created for each query.
Uses the
[OpenTelemetry SQLAlchemy Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/sqlalchemy/sqlalchemy.html)
library, specifically `SQLAlchemyInstrumentor().instrument()`, to which it passes `**kwargs`.
"""
from .integrations.sqlalchemy import instrument_sqlalchemy

self._warn_if_not_initialized_for_instrumentation()
return instrument_sqlalchemy(**kwargs)

def instrument_pymongo(self, **kwargs: Any):
"""Instrument the `pymongo` module so that spans are automatically created for each operation.
Uses the
[OpenTelemetry pymongo Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pymongo/pymongo.html)
library, specifically `PymongoInstrumentor().instrument()`, to which it passes `**kwargs`.
"""
from .integrations.pymongo import instrument_pymongo

self._warn_if_not_initialized_for_instrumentation()
return instrument_pymongo(**kwargs)

def instrument_redis(self, **kwargs: Any):
"""Instrument the `redis` module so that spans are automatically created for each operation.
Uses the
[OpenTelemetry Redis Instrumentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/redis/redis.html)
library, specifically `RedisInstrumentor().instrument()`, to which it passes `**kwargs`.
"""
from .integrations.redis import instrument_redis

self._warn_if_not_initialized_for_instrumentation()
return instrument_redis(**kwargs)

def metric_counter(self, name: str, *, unit: str = '', description: str = '') -> Counter:
"""Create a counter metric.
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "logfire"
version = "0.38.0"
version = "0.39.0"
description = "The best Python observability tool! 🪵🔥"
authors = [
{ name = "Pydantic Team", email = "[email protected]" },
Expand Down Expand Up @@ -81,6 +81,9 @@ managed = true
dev-dependencies = [
"anyio",
"httpx",
"aiohttp",
"redis",
"pymongo",
"starlette",
"fastapi",
"Flask",
Expand Down Expand Up @@ -109,6 +112,8 @@ dev-dependencies = [
"opentelemetry-instrumentation-asyncpg",
"opentelemetry-instrumentation-psycopg",
"opentelemetry-instrumentation-psycopg2",
"opentelemetry-instrumentation-redis",
"opentelemetry-instrumentation-pymongo",
"gitpython",
"eval-type-backport",
"requests-mock",
Expand Down
Loading

0 comments on commit f600862

Please sign in to comment.