Skip to main content

Shared logging and metrics library for Road24 FastAPI microservices

Project description

road24-sdk

Shared Python SDK for structured JSON logging and Prometheus metrics in Road24 microservices.

Provides auto-instrumentation for FastAPI, httpx, SQLAlchemy, and Redis with zero boilerplate.

Installation

# Core only
pip install road24-sdk

# With specific integrations
pip install road24-sdk[fastapi]
pip install road24-sdk[httpx]
pip install road24-sdk[sqlalchemy]
pip install road24-sdk[redis]

# All integrations
pip install road24-sdk[all]

Requires Python 3.12+.

Quick Start

import road24_sdk
from road24_sdk.integrations.fastapi import FastApiLoggingIntegration
from road24_sdk.integrations.httpx import HttpxLoggingIntegration
from road24_sdk.integrations.sqlalchemy import SqlalchemyLoggingIntegration
from road24_sdk.integrations.redis import RedisLoggingIntegration

road24_sdk.init(
    service_name="payment-service",
    log_level="INFO",
    integrations=[
        FastApiLoggingIntegration(),
        HttpxLoggingIntegration(),
        SqlalchemyLoggingIntegration(),
        RedisLoggingIntegration(),
    ],
)

After init(), all new httpx clients, SQLAlchemy engines, and Redis clients are automatically instrumented. FastAPI requires one extra call:

from fastapi import FastAPI

app = FastAPI()
FastApiLoggingIntegration().setup(app)

Log Format

Every log line is a single JSON object written to stdout:

{
  "timestamp": "2025-01-15T10:30:45.123Z",
  "trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "log_level": "INFO",
  "type": "http_request",
  "service_name": "payment-service",
  "attributes": { "..." : "..." }
}
Field Description
timestamp UTC ISO 8601 with milliseconds
trace_id 32-char hex from x-trace-id header or auto-generated
log_level INFO, WARNING, or ERROR (based on status code)
type http_request, db_query, or redis_command
service_name Value passed to init()
attributes Type-specific fields (see below)

Integrations

FastAPI (HTTP Input)

Logs every incoming HTTP request. Adds x-trace-id response header. Catches and formats exceptions.

from fastapi import FastAPI
import road24_sdk
from road24_sdk.integrations.fastapi import FastApiLoggingIntegration

road24_sdk.init(
    service_name="user-service",
    integrations=[FastApiLoggingIntegration()],
)

app = FastAPI()
FastApiLoggingIntegration().setup(app)

Example log input:

{
  "timestamp": "2025-01-15T10:30:45.123Z",
  "trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "log_level": "INFO",
  "type": "http_request",
  "service_name": "user-service",
  "attributes": {
    "direction": "input",
    "method": "GET",
    "url": "https://user-service.local/api/v1/users/42",
    "target": "/api/v1/users/42",
    "status_code": 200,
    "duration_seconds": 0.023,
    "request_headers": {},
    "request_body": "",
    "response_body": ""
  }
}

Log level mapping: 2xx/3xx -> INFO, 4xx -> WARNING, 5xx -> ERROR.

When an exception occurs, error_class and error_message are added to attributes:

{
  "attributes": {
    "direction": "input",
    "method": "GET",
    "url": "https://user-service.local/api/v1/process",
    "target": "/api/v1/process",
    "status_code": 500,
    "duration_seconds": 0.012,
    "request_headers": {},
    "request_body": "",
    "response_body": "",
    "error_class": "RuntimeError",
    "error_message": "Something went wrong"
  }
}

Exception responses include trace_id for debugging:

{"detail": "User not found", "trace_id": "a1b2c3d4e5f6a1b2..."}

Options:

FastApiLoggingIntegration(
    enable_logging=True,           # Structured JSON logs
    enable_metrics=True,           # Prometheus metrics
    enable_exception_handlers=True # Catch and format exceptions
)

Skipped paths: /metrics, /docs, /redoc, /openapi.json


httpx (HTTP Output)

Logs every outgoing HTTP request made via httpx.AsyncClient.

import httpx
import road24_sdk
from road24_sdk.integrations.httpx import HttpxLoggingIntegration

road24_sdk.init(
    service_name="payment-service",
    integrations=[HttpxLoggingIntegration()],
)

# All new clients are auto-instrumented after init()
async with httpx.AsyncClient() as client:
    response = await client.get("https://api.example.com/users/42")

Example log output:

{
  "timestamp": "2025-01-15T10:30:45.456Z",
  "trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "log_level": "INFO",
  "type": "http_request",
  "service_name": "payment-service",
  "attributes": {
    "direction": "output",
    "method": "GET",
    "url": "https://api.example.com/users/42",
    "target": "/users/42",
    "status_code": 200,
    "duration_seconds": 0.145,
    "request_headers": {},
    "request_body": "",
    "response_body": ""
  }
}

Alternative usage (instance-level):

integration = HttpxLoggingIntegration()

# Patch a specific client
client = httpx.AsyncClient()
integration.setup(client)

# Or use the factory method
client = integration.create_client(base_url="https://api.example.com")

SQLAlchemy (Database)

Logs every database query with operation type, table name, and duration.

from sqlalchemy.ext.asyncio import create_async_engine
import road24_sdk
from road24_sdk.integrations.sqlalchemy import SqlalchemyLoggingIntegration

road24_sdk.init(
    service_name="order-service",
    integrations=[SqlalchemyLoggingIntegration()],
)

# All engines are auto-instrumented after init()
engine = create_async_engine("postgresql+asyncpg://...")

Example log output:

{
  "timestamp": "2025-01-15T10:30:45.789Z",
  "trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "log_level": "INFO",
  "type": "db_query",
  "service_name": "order-service",
  "attributes": {
    "direction": "sqlalchemy",
    "operation": "select",
    "table": "orders",
    "duration_seconds": 0.008,
    "statement": "SELECT orders.id, orders.total FROM orders WHERE orders.user_id = ?"
  }
}

Supported operations: select, insert, update, delete, other. Table name is auto-extracted from SQL. Statements longer than 500 characters are truncated.

Instance-level patching:

integration = SqlalchemyLoggingIntegration()
integration.setup(engine)  # Works with both sync and async engines

Redis

Logs every Redis command with operation, key, and duration.

from redis.asyncio import Redis
import road24_sdk
from road24_sdk.integrations.redis import RedisLoggingIntegration

road24_sdk.init(
    service_name="cache-service",
    integrations=[RedisLoggingIntegration()],
)

# All Redis clients are auto-instrumented after init()
redis = Redis(host="localhost")
await redis.set("user:42:name", "Alice")
await redis.get("user:42:name")

Example log output:

{
  "timestamp": "2025-01-15T10:30:46.012Z",
  "trace_id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "log_level": "INFO",
  "type": "redis_command",
  "service_name": "cache-service",
  "attributes": {
    "direction": "redis",
    "operation": "set",
    "key": "user:42:name",
    "duration_seconds": 0.002,
    "command_args": "value=Alice"
  }
}

Supported operations: get, set, delete, expire, hget, hset, lpush, rpush, lpop, rpop, publish, subscribe, other.

Alternative: explicit wrapper with typed methods:

from road24_sdk.loggers.redis import LoggedRedis

logged = LoggedRedis(redis_client, record_metrics=True)
await logged.set("key", "value", ex=300)
await logged.get("key")
await logged.delete("key")
await logged.hset("hash", "field", "value")
await logged.lpush("list", "item1", "item2")

Prometheus Metrics

All integrations automatically record Prometheus metrics. Expose them with any Prometheus-compatible endpoint.

HTTP Metrics

Metric Type Labels
http_requests_total Counter method, domain, status_code, direction
http_request_duration_seconds Histogram method, domain, status_code, direction

direction: "input" (FastAPI) or "output" (httpx). domain: extracted hostname from URL (e.g. api.example.com).

Database Metrics

Metric Type Labels
db_queries_total Counter operation, direction
db_query_duration_seconds Histogram operation, direction

operation: select, insert, update, delete, other. direction: "sqlalchemy".

Redis Metrics

Metric Type Labels
redis_commands_total Counter operation, direction
redis_command_duration_seconds Histogram operation, direction

direction: "redis".

URL and Key Normalization

Dynamic segments are replaced with {id} to prevent metric cardinality explosion:

Type Raw Normalized
URL path /api/v1/users/42 /api/v1/users/{id}
URL path /orders/550e8400-e29b-41d4-a716-446655440000 /orders/{id}
Redis key user:42:sessions user:{id}:sessions
Redis key cache:abc123def456 cache:abc123def456 (short hex not replaced)

Trace ID Propagation

  1. Incoming request has x-trace-id header -> SDK uses it
  2. No header -> SDK generates a random 32-char hex ID
  3. Response includes x-trace-id header
  4. All logs within the request share the same trace_id
  5. Trace context is propagated across async operations via ContextVar

Configuration

road24_sdk.init(
    service_name="my-service",  # Appears in every log line
    log_level="INFO",           # DEBUG, INFO, WARNING, ERROR
    debug=False,                # True = less library silencing
    log_format="json",          # Log output format
    integrations=[...],         # List of integrations to activate
)

Per-Integration Options

Every integration accepts enable_logging and enable_metrics:

# Metrics only, no log lines
HttpxLoggingIntegration(enable_logging=False, enable_metrics=True)

# Logs only, no metrics
SqlalchemyLoggingIntegration(enable_logging=True, enable_metrics=False)

Body Sanitization

Request/response bodies are automatically sanitized. Fields matching sensitive patterns are replaced with ***REDACTED***:

password, token, secret, api_key, auth, credential, private_key, access_key, session_id, cookie, bearer, pin, cvv, card_number

Library Silencing

In production (debug=False), noisy library loggers are silenced: uvicorn, sqlalchemy, httpx, httpcore, asyncio.

In debug mode (debug=True), only uvicorn.access and asyncio are silenced.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

road24_sdk-0.1.5.tar.gz (97.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

road24_sdk-0.1.5-py3-none-any.whl (21.1 kB view details)

Uploaded Python 3

File details

Details for the file road24_sdk-0.1.5.tar.gz.

File metadata

  • Download URL: road24_sdk-0.1.5.tar.gz
  • Upload date:
  • Size: 97.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for road24_sdk-0.1.5.tar.gz
Algorithm Hash digest
SHA256 f7b775b66459ddcf62f58150ff9139ba518dc4cababb8fee6e6ba185a52990fc
MD5 12b9ec5367f9108d8a8d3c87880e36db
BLAKE2b-256 c447d1e0644797216e038e9a9f9ac40054c5346dd63d5a2b33ce88f5c212d99d

See more details on using hashes here.

File details

Details for the file road24_sdk-0.1.5-py3-none-any.whl.

File metadata

  • Download URL: road24_sdk-0.1.5-py3-none-any.whl
  • Upload date:
  • Size: 21.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for road24_sdk-0.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 aa1661c48e7cbc14e460bb5bb57fb96187758fc772a6aef8c1c0bc726ee80ce6
MD5 238e114a06ec4c925b2538c280c53241
BLAKE2b-256 4fc17a4ded8d4014f35e7926e3518fc99492f5b664b0670deed277894e88e833

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page