Get the FREE Ultimate OpenClaw Setup Guide →

python-observability-patterns

npx machina-cli add skill aiskillstore/marketplace/python-observability-patterns --openclaw
Files (1)
SKILL.md
5.0 KB

Python Observability Patterns

Logging, metrics, and tracing for production applications.

Structured Logging with structlog

import structlog

# Configure structlog
structlog.configure(
    processors=[
        structlog.contextvars.merge_contextvars,
        structlog.processors.add_log_level,
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.JSONRenderer(),
    ],
    wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
    context_class=dict,
    logger_factory=structlog.PrintLoggerFactory(),
)

logger = structlog.get_logger()

# Usage
logger.info("user_created", user_id=123, email="test@example.com")
# Output: {"event": "user_created", "user_id": 123, "email": "test@example.com", "level": "info", "timestamp": "2024-01-15T10:00:00Z"}

Request Context Propagation

import structlog
from contextvars import ContextVar
from uuid import uuid4

request_id_var: ContextVar[str] = ContextVar("request_id", default="")

def bind_request_context(request_id: str | None = None):
    """Bind request ID to logging context."""
    rid = request_id or str(uuid4())
    request_id_var.set(rid)
    structlog.contextvars.bind_contextvars(request_id=rid)
    return rid

# FastAPI middleware
@app.middleware("http")
async def request_context_middleware(request, call_next):
    request_id = request.headers.get("X-Request-ID") or str(uuid4())
    bind_request_context(request_id)
    response = await call_next(request)
    response.headers["X-Request-ID"] = request_id
    structlog.contextvars.clear_contextvars()
    return response

Prometheus Metrics

from prometheus_client import Counter, Histogram, Gauge, generate_latest
from fastapi import FastAPI, Response

# Define metrics
REQUEST_COUNT = Counter(
    "http_requests_total",
    "Total HTTP requests",
    ["method", "endpoint", "status"]
)

REQUEST_LATENCY = Histogram(
    "http_request_duration_seconds",
    "HTTP request latency",
    ["method", "endpoint"],
    buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 5.0]
)

ACTIVE_CONNECTIONS = Gauge(
    "active_connections",
    "Number of active connections"
)

# Middleware to record metrics
@app.middleware("http")
async def metrics_middleware(request, call_next):
    ACTIVE_CONNECTIONS.inc()
    start = time.perf_counter()

    response = await call_next(request)

    duration = time.perf_counter() - start
    REQUEST_COUNT.labels(
        method=request.method,
        endpoint=request.url.path,
        status=response.status_code
    ).inc()
    REQUEST_LATENCY.labels(
        method=request.method,
        endpoint=request.url.path
    ).observe(duration)
    ACTIVE_CONNECTIONS.dec()

    return response

# Metrics endpoint
@app.get("/metrics")
async def metrics():
    return Response(
        content=generate_latest(),
        media_type="text/plain"
    )

OpenTelemetry Tracing

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

# Setup
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="localhost:4317"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

# Manual instrumentation
async def process_order(order_id: int):
    with tracer.start_as_current_span("process_order") as span:
        span.set_attribute("order_id", order_id)

        with tracer.start_as_current_span("validate_order"):
            await validate(order_id)

        with tracer.start_as_current_span("charge_payment"):
            await charge(order_id)

Quick Reference

LibraryPurpose
structlogStructured logging
prometheus-clientMetrics collection
opentelemetryDistributed tracing
Metric TypeUse Case
CounterTotal requests, errors
HistogramLatencies, sizes
GaugeCurrent connections, queue size

Additional Resources

  • ./references/structured-logging.md - structlog configuration, formatters
  • ./references/metrics.md - Prometheus patterns, custom metrics
  • ./references/tracing.md - OpenTelemetry, distributed tracing

Assets

  • ./assets/logging-config.py - Production logging configuration

See Also

Prerequisites:

  • python-async-patterns - Async context propagation

Related Skills:

  • python-fastapi-patterns - API middleware for metrics/tracing
  • python-cli-patterns - CLI logging patterns

Integration Skills:

  • python-database-patterns - Database query tracing

Source

git clone https://github.com/aiskillstore/marketplace/blob/main/skills/0xdarkmatter/python-observability-patterns/SKILL.mdView on GitHub

Overview

This skill teaches practical observability patterns for Python applications, covering structured logging with structlog, per-request correlation IDs, Prometheus metrics, and OpenTelemetry tracing. It shows how these pieces come together to produce actionable telemetry for production systems.

How This Skill Works

Logging uses structlog with a fixed processor chain producing JSON logs. A ContextVar-bound correlation ID is attached to logs via structlog.contextvars in a FastAPI middleware. Metrics are collected with Prometheus (counters, histograms, gauges) and OpenTelemetry tracing is wired with a tracer provider and OTLP exporter to emit spans to destinations like localhost:4317.

When to Use It

  • You need structured, context-rich logs across Python services.
  • You operate FastAPI or similar apps and want per-request correlation IDs.
  • You want to collect Prometheus metrics such as request counts and latency.
  • You want distributed tracing with OpenTelemetry for end-to-end visibility.
  • You’re building production-grade observability patterns for Python apps.

Quick Start

  1. Step 1: Install dependencies: structlog, opentelemetry-api, opentelemetry-sdk, opentelemetry-exporter-otlp-proto-grpc, prometheus-client
  2. Step 2: Configure structlog with a JSON renderer and bind a request context using a ContextVar in middleware
  3. Step 3: Add Prometheus metrics and OpenTelemetry tracing, then expose /metrics and run the app

Best Practices

  • Use a consistent structlog processor chain (TimeStamper, JSONRenderer) to produce structured, query-friendly logs.
  • Bind a per-request correlation ID to a ContextVar and propagate it with structlog.contextvars.
  • Instrument standard metrics (http_requests_total, http_request_duration_seconds, active_connections) with meaningful labels.
  • Configure OpenTelemetry with a BatchSpanProcessor and an OTLP exporter for scalable tracing.
  • Avoid logging sensitive data; redact or omit PII and keep logs lightweight.

Example Use Cases

  • Structured logging example using structlog with JSON output and context heritage.
  • A FastAPI middleware that binds and propagates a request_id across logs.
  • Prometheus metrics setup with REQUEST_COUNT, REQUEST_LATENCY, and ACTIVE_CONNECTIONS labels.
  • Metrics endpoint at /metrics serving Prometheus-formatted data.
  • OpenTelemetry tracing setup with an OTLP exporter sending spans to localhost:4317.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers