Why:
- Wanted human-readable console output while developing locally, without
losing a machine-parseable log for later grepping/parsing. A single
renderer chosen by a flag can't do both at once.
- ADR-0011 had no way to correlate an issue with a specific deployment
(build/region/instance) independent of any one request.
Changes:
- configure_logging() now builds two independent handlers: console (always
on, colored unless LOG_JSON_FORMAT=true) and an optional rotating JSON file
(LOG_FILE_PATH, unset by default) -- the same structlog event fans out to
both, so call sites are unaffected.
- A static structlog processor binds env/service_version onto every event.
Deliberately not a contextvar: RequestIdMiddleware's clear_contextvars()
would wipe a value bound there before the first request.
- New settings: APP_SERVICE_VERSION, LOG_FILE_PATH/LOG_FILE_MAX_BYTES/
LOG_FILE_BACKUP_COUNT.
- ADR-0011 amended with both decisions ("console and file are independent
sinks locally"; "bind process-level environment context once at startup").
Impact:
- configure_logging() signature changed to (logging_settings, app_settings);
both call sites (lifespan, qdrant_bootstrap CLI) updated.
55 lines
1.6 KiB
Python
55 lines
1.6 KiB
Python
"""Create the `chunks` collection — the Qdrant analogue of `alembic upgrade head`.
|
|
|
|
uv run python -m src.cli.qdrant_bootstrap
|
|
|
|
A deployment step, deliberately not part of the FastAPI lifespan: collection
|
|
creation is DDL, which ADR-0009 keeps out of application startup for Postgres
|
|
and ADR-0012 keeps out of it for LangGraph's `.setup()`. See
|
|
`src/infrastructure/qdrant/collection.py` for the full reasoning.
|
|
|
|
Idempotent and safe to re-run. Exits non-zero if an existing collection
|
|
diverges from the pinned schema, rather than leaving a silently degraded
|
|
sparse index behind.
|
|
"""
|
|
|
|
import asyncio
|
|
import sys
|
|
|
|
import structlog
|
|
|
|
from src.config import Settings
|
|
from src.infrastructure.observability.logging import configure_logging
|
|
from src.infrastructure.qdrant.client import create_client
|
|
from src.infrastructure.qdrant.collection import (
|
|
CollectionSchemaMismatchError,
|
|
ensure_chunks_collection,
|
|
)
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
|
|
async def bootstrap(settings: Settings | None = None) -> int:
|
|
resolved = settings or Settings()
|
|
configure_logging(resolved.logging, resolved.app)
|
|
client = create_client(resolved.qdrant)
|
|
try:
|
|
created = await ensure_chunks_collection(client, collection=resolved.qdrant.collection)
|
|
except CollectionSchemaMismatchError as exc:
|
|
logger.error("qdrant.bootstrap.schema_mismatch", error=str(exc))
|
|
return 1
|
|
finally:
|
|
await client.close()
|
|
|
|
logger.info(
|
|
"qdrant.bootstrap.completed", collection=resolved.qdrant.collection, created=created
|
|
)
|
|
return 0
|
|
|
|
|
|
def main() -> None:
|
|
sys.exit(asyncio.run(bootstrap()))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|