Psycopg
See every query your app sends to PostgreSQL through Psycopg (the statement, how long it took, and which ones failed) as a span (one unit of work with a name, a start, and a duration) in Logfire. Related spans link together into a trace (the full journey of one request), so a slow query shows up right next to the code that triggered it.
This works with both the psycopg (Psycopg 3) and psycopg2 packages.
- Each query as a span, with its duration and any errors
- The SQL statement that ran
- Which database the query went to
You’ll need a Logfire project. Open Add data in your project (top navigation) and follow the
setup for your language: it signs your machine in with logfire auth (a browser sign-in, no token
to copy) and, for production or other languages, creates a write token (the credential your app
uses to send data). New to Logfire? Start with Getting Started.
Install logfire with the psycopg extra:
pip install 'logfire[psycopg]'
uv add 'logfire[psycopg]'
Or, if you use psycopg2, install the psycopg2 extra instead:
pip install 'logfire[psycopg2]'
uv add 'logfire[psycopg2]'
Add two lines to your app: logfire.configure() to connect to your project, and
logfire.instrument_psycopg() to record every query.
The example below connects to a local PostgreSQL database. If you don’t have one running, you can start one with Docker:
docker run --rm --name postgres \
-e POSTGRES_USER=user \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=database \
-p 5432:5432 \
-d postgres
This gives you a database you can reach at postgres://user:secret@127.0.0.1:5432/database.
import psycopg
import logfire
logfire.configure()
logfire.instrument_psycopg() # instrument whichever of psycopg/psycopg2 is installed
connection = psycopg.connect('dbname=database user=user password=secret host=127.0.0.1 port=5432')
with logfire.span('Create table and insert data'), connection.cursor() as cursor:
cursor.execute('CREATE TABLE IF NOT EXISTS test (id serial PRIMARY KEY, num integer, data varchar);')
# Insert some data
cursor.execute('INSERT INTO test (num, data) VALUES (%s, %s)', (100, 'abc'))
cursor.execute('INSERT INTO test (num, data) VALUES (%s, %s)', (200, 'def'))
# Query the data
cursor.execute('SELECT * FROM test')
Run it with python main.py.
Run your program, then open your project in the Logfire web app and go to the Live view. Within a few seconds you should see a span for each query the script ran. Click one to see the SQL statement and how long it took.
Not seeing your queries in Logfire? Check these first:
logfire.configure()runs beforelogfire.instrument_psycopg(). Configure the connection first, then instrument.- You call
instrument_psycopg()exactly once. With no argument it instruments the whole module; pass a connection to instrument just that one. - Your write token is set. In local development, run
logfire projects use <your-project>; in production, set theLOGFIRE_TOKENenvironment variable. See Getting Started. - You actually ran a query. Spans appear only after a statement executes.
You can instrument the whole module, a single package by name, or one connection:
import psycopg
import logfire
logfire.configure()
# Instrument the whole module:
logfire.instrument_psycopg(psycopg)
# or by name:
logfire.instrument_psycopg('psycopg')
# or instrument whichever modules (psycopg and/or psycopg2) are installed:
logfire.instrument_psycopg()
connection = psycopg.connect('dbname=database user=user password=secret host=127.0.0.1 port=5432')
# Or instrument just one connection:
logfire.instrument_psycopg(connection)
SQL Commenter appends a comment to the end of each query with extra context (for example, the driver
name and version). This can help tools that read your database’s own query logs correlate them back to
your app. Turn it on with enable_commenter=True:
import logfire
logfire.configure()
logfire.instrument_psycopg(enable_commenter=True)
This can only be used when instrumenting the whole module, not individual connections.
By default the SQL comments include values for these keys:
db_driverdbapi_threadsafetydbapi_levellibpq_versiondriver_paramstyleopentelemetry_values
You can exclude any of these by passing a dictionary of keys mapped to False in commenter_options:
import logfire
logfire.configure()
logfire.instrument_psycopg(
enable_commenter=True,
commenter_options={'db_driver': False, 'dbapi_threadsafety': False},
)
logfire.instrument_psycopg() accepts additional keyword
arguments and passes them to the OpenTelemetry Psycopg instrumentation. See the
OpenTelemetry Psycopg and
OpenTelemetry Psycopg2 documentation for the full list.
def instrument_psycopg(
conn_or_module: PsycopgConnection | Psycopg2Connection,
**kwargs: Any,
) -> None
def instrument_psycopg(
conn_or_module: None | Literal['psycopg', 'psycopg2'] | ModuleType = None,
enable_commenter: bool = False,
commenter_options: PsycopgCommenterOptions | None = None,
**kwargs: Any,
) -> None
Instrument a psycopg connection or module so that spans are automatically created for each query.
Uses the OpenTelemetry instrumentation libraries for
psycopg
and
psycopg2.
conn_or_module : Any Default: None
Can be:
- The
psycopg(version 3) orpsycopg2module. - The string
'psycopg'or'psycopg2'to instrument the module. None(the default) to instrument whichever module(s) are installed.- A
psycopgorpsycopg2connection.
enable_commenter : bool Default: False
Adds comments to SQL queries performed by Psycopg, so that database logs have additional context.
commenter_options : PsycopgCommenterOptions | None Default: None
Configure the tags to be added to the SQL comments.
**kwargs : Any Default: {}
Additional keyword arguments to pass to the OpenTelemetry instrument methods,
for future compatibility.
Bases: TypedDict
The commenter_options parameter for instrument_psycopg.
Include the database driver name in the comment e.g. ‘psycopg2’.
Type: bool
Include the DB-API threadsafety value in the comment.
Type: bool
Include the DB-API level in the comment.
Type: bool
Include the libpq version in the comment.
Type: bool
Include the driver paramstyle in the comment e.g. ‘driver_paramstyle=pyformat’
Type: bool
Enabling this flag will add traceparent values to the comment.
Type: bool