Skip to content

Commit 9bd98a2

Browse files
committed
add alembic instrumentation
1 parent 203f0a5 commit 9bd98a2

File tree

8 files changed

+134
-19
lines changed

8 files changed

+134
-19
lines changed

alembic/env.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
from logging.config import fileConfig
33

4+
import logfire
45
from sqlalchemy import engine_from_config, pool
56

67
from alembic import context
@@ -16,7 +17,9 @@
1617

1718
# add your model's MetaData object here
1819
# for 'autogenerate' support
20+
from strava.config import settings # noqa: E402
1921
from strava.db.base import Base # noqa: E402
22+
from strava.observability import configure_migration_observability # noqa: E402
2023

2124
target_metadata = Base.metadata
2225

@@ -66,6 +69,7 @@ def run_migrations_online():
6669
prefix="sqlalchemy.",
6770
poolclass=pool.NullPool,
6871
)
72+
configure_migration_observability(settings, connectable)
6973

7074
with connectable.connect() as connection:
7175
context.configure(
@@ -75,7 +79,8 @@ def run_migrations_online():
7579
)
7680

7781
with context.begin_transaction():
78-
context.run_migrations()
82+
with logfire.span("alembic run migrations", db_url=configuration["sqlalchemy.url"]):
83+
context.run_migrations()
7984

8085

8186
if context.is_offline_mode():

docker-compose.test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ services:
1818
build: ./
1919
command: [uv, run, alembic, upgrade, head]
2020
environment:
21+
- PROJECT_NAME=strava-test
22+
- ENVIRONMENT=test
2123
- POSTGRES_SERVER=db:5432
2224
- POSTGRES_USER=postgres
2325
- POSTGRES_PASSWORD=password

docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ services:
2222
- ./:/root/app
2323
working_dir: /root/app
2424
environment:
25+
- PROJECT_NAME=strava
26+
- ENVIRONMENT=development
27+
- LOGFIRE_TOKEN=${LOGFIRE_TOKEN}
2528
- POSTGRES_SERVER=db:5432
2629
- POSTGRES_USER=postgres
2730
- POSTGRES_PASSWORD=password

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ dependencies = [
1919
"geoalchemy2",
2020
"alembic",
2121
"psycopg2",
22-
"logfire>=4.29.0",
22+
"logfire[system-metrics]>=4.29.0",
2323
"opentelemetry-instrumentation-fastapi>=0.60b1",
2424
"opentelemetry-instrumentation-sqlalchemy>=0.60b1",
2525
]

strava/observability.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55
from strava.config import Settings
66

77

8-
def configure_observability(app: FastAPI, settings: Settings, engine: Engine) -> None:
8+
def configure_logfire(settings: Settings, *, service_name: str | None = None) -> None:
99
logfire.configure(
10-
service_name=settings.PROJECT_NAME,
10+
service_name=service_name or settings.PROJECT_NAME,
1111
environment=settings.ENVIRONMENT,
1212
send_to_logfire=settings.LOGFIRE_SEND_TO_LOGFIRE,
1313
token=settings.LOGFIRE_TOKEN,
1414
)
15+
16+
17+
def configure_observability(app: FastAPI, settings: Settings, engine: Engine) -> None:
18+
configure_logfire(settings)
1519
logfire.instrument_fastapi(app)
1620
logfire.instrument_sqlalchemy(engine)
21+
logfire.instrument_system_metrics()
22+
23+
24+
def configure_migration_observability(settings: Settings, engine: Engine) -> None:
25+
configure_logfire(settings, service_name=f"{settings.PROJECT_NAME}-alembic")
26+
logfire.instrument_sqlalchemy(engine)

tests/test_config.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,17 @@ def test_environment_default(self):
2727

2828
assert settings.ENVIRONMENT == "development"
2929

30-
def test_logfire_token_default(self):
31-
from strava.config import settings
30+
def test_logfire_token_default(self, monkeypatch):
31+
monkeypatch.setenv("PROJECT_NAME", "test")
32+
monkeypatch.setenv("POSTGRES_SERVER", "localhost")
33+
monkeypatch.setenv("POSTGRES_PASSWORD", "pass")
34+
monkeypatch.setenv("POSTGRES_DB", "db")
35+
monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
36+
37+
from strava.config import Settings
3238

33-
assert settings.LOGFIRE_TOKEN is None
39+
s = Settings()
40+
assert s.LOGFIRE_TOKEN is None
3441

3542
def test_settings_from_env(self, monkeypatch):
3643
monkeypatch.setenv("PROJECT_NAME", "my-project")

tests/test_observability.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,45 @@
33
from fastapi import FastAPI
44

55
from strava.config import Settings
6-
from strava.observability import configure_observability
6+
from strava.observability import (
7+
configure_logfire,
8+
configure_migration_observability,
9+
configure_observability,
10+
)
11+
12+
13+
def build_settings(**overrides):
14+
values = {
15+
"PROJECT_NAME": "strava-test",
16+
"ENVIRONMENT": "test",
17+
"POSTGRES_SERVER": "localhost",
18+
"POSTGRES_PASSWORD": "password",
19+
"POSTGRES_DB": "test",
20+
"LOGFIRE_SEND_TO_LOGFIRE": False,
21+
"LOGFIRE_TOKEN": "test-token",
22+
}
23+
values.update(overrides)
24+
return Settings(**values)
25+
26+
27+
def test_configure_logfire_uses_default_service_name():
28+
settings = build_settings()
29+
30+
with patch("strava.observability.logfire.configure") as configure:
31+
configure_logfire(settings)
32+
33+
configure.assert_called_once_with(
34+
service_name="strava-test",
35+
environment="test",
36+
send_to_logfire=False,
37+
token="test-token",
38+
)
739

840

941
def test_configure_observability_wires_logfire():
1042
app = FastAPI()
1143
engine = MagicMock()
12-
settings = Settings(
13-
PROJECT_NAME="strava-test",
14-
ENVIRONMENT="test",
15-
POSTGRES_SERVER="localhost",
16-
POSTGRES_PASSWORD="password",
17-
POSTGRES_DB="test",
18-
LOGFIRE_SEND_TO_LOGFIRE=False,
19-
LOGFIRE_TOKEN="test-token",
20-
)
44+
settings = build_settings()
2145

2246
with patch("strava.observability.logfire.configure") as configure:
2347
with patch("strava.observability.logfire.instrument_fastapi") as instrument_fastapi:
@@ -34,3 +58,20 @@ def test_configure_observability_wires_logfire():
3458
)
3559
instrument_fastapi.assert_called_once_with(app)
3660
instrument_sqlalchemy.assert_called_once_with(engine)
61+
62+
63+
def test_configure_migration_observability_uses_alembic_service_name():
64+
engine = MagicMock()
65+
settings = build_settings()
66+
67+
with patch("strava.observability.logfire.configure") as configure:
68+
with patch("strava.observability.logfire.instrument_sqlalchemy") as instrument_sqlalchemy:
69+
configure_migration_observability(settings, engine)
70+
71+
configure.assert_called_once_with(
72+
service_name="strava-test-alembic",
73+
environment="test",
74+
send_to_logfire=False,
75+
token="test-token",
76+
)
77+
instrument_sqlalchemy.assert_called_once_with(engine)

uv.lock

Lines changed: 49 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)