distributions/direct/example/error-handling-logging-patterns/SKILL.md
Implement structured logging, error hierarchies, and observability patterns for production systems. Covers structured JSON logging, error classification, correlation IDs, and alerting integration. Triggers on logging architecture, error handling strategy, or observability requests.
npx skillsauth add a-organvm/a-i--skills error-handling-logging-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build systems that are debuggable in production through structured logging and intentional error handling.
class AppError(Exception):
"""Base error for the application."""
def __init__(self, message: str, code: str = "INTERNAL_ERROR", status: int = 500):
self.message = message
self.code = code
self.status = status
super().__init__(message)
class NotFoundError(AppError):
def __init__(self, entity: str, id: str):
super().__init__(f"{entity} '{id}' not found", code="NOT_FOUND", status=404)
class ValidationError(AppError):
def __init__(self, field: str, reason: str):
super().__init__(f"Invalid {field}: {reason}", code="VALIDATION_ERROR", status=400)
class ExternalServiceError(AppError):
def __init__(self, service: str, detail: str):
super().__init__(f"{service} error: {detail}", code="EXTERNAL_ERROR", status=502)
| Category | Retry? | Log Level | Alert? | |----------|--------|-----------|--------| | Validation error | No | WARNING | No | | Not found | No | INFO | No | | Auth failure | No | WARNING | Rate-based | | Transient external | Yes | WARNING | After retries | | Persistent external | No | ERROR | Yes | | Internal bug | No | CRITICAL | Immediate |
import structlog
import logging
def configure_logging(log_level: str = "INFO", json_output: bool = True):
processors = [
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
]
if json_output:
processors.append(structlog.processors.JSONRenderer())
else:
processors.append(structlog.dev.ConsoleRenderer())
structlog.configure(
processors=processors,
wrapper_class=structlog.make_filtering_bound_logger(
getattr(logging, log_level.upper())
),
context_class=dict,
logger_factory=structlog.PrintLoggerFactory(),
)
log = structlog.get_logger()
import structlog
from contextvars import ContextVar
request_id_var: ContextVar[str] = ContextVar("request_id", default="")
# Bind context per request
structlog.contextvars.bind_contextvars(
request_id=request_id,
user_id=user.id,
organ="IV",
)
# All subsequent log calls include this context
log.info("processing_request", path="/api/skills", method="GET")
# Output: {"event": "processing_request", "request_id": "abc-123", "user_id": "u42", "path": "/api/skills", ...}
| Level | Purpose | Example | |-------|---------|---------| | DEBUG | Detailed flow tracing | Query parameters, cache hits | | INFO | Business events | User created, skill activated | | WARNING | Recoverable issues | Retry attempt, deprecated usage | | ERROR | Failures needing attention | External service down, data inconsistency | | CRITICAL | System-level failures | Database unreachable, out of memory |
import uuid
from starlette.middleware.base import BaseHTTPMiddleware
class CorrelationMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
structlog.contextvars.bind_contextvars(request_id=request_id)
log.info("request_started",
method=request.method,
path=request.url.path,
)
try:
response = await call_next(request)
log.info("request_completed",
status=response.status_code,
)
response.headers["X-Request-ID"] = request_id
return response
except Exception as e:
log.error("request_failed", error=str(e), exc_info=True)
raise
finally:
structlog.contextvars.unbind_contextvars("request_id")
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(AppError)
async def app_error_handler(request: Request, exc: AppError):
log.warning("app_error", code=exc.code, message=exc.message)
return JSONResponse(
status_code=exc.status,
content={"error": {"code": exc.code, "message": exc.message}},
)
@app.exception_handler(Exception)
async def unhandled_error_handler(request: Request, exc: Exception):
log.error("unhandled_error", error=str(exc), exc_info=True)
return JSONResponse(
status_code=500,
content={"error": {"code": "INTERNAL_ERROR", "message": "An unexpected error occurred"}},
)
async def process_skill(skill_id: str):
log.info("skill_processing_started", skill_id=skill_id)
try:
result = await validate_skill(skill_id)
log.info("skill_processing_completed", skill_id=skill_id, status=result.status)
return result
except ValidationError as e:
log.warning("skill_validation_failed", skill_id=skill_id, error=e.message)
raise
except Exception as e:
log.error("skill_processing_failed", skill_id=skill_id, error=str(e), exc_info=True)
raise
SENSITIVE_KEYS = {"password", "token", "secret", "api_key", "authorization"}
def sanitize_log_data(data: dict) -> dict:
return {
k: "***REDACTED***" if k.lower() in SENSITIVE_KEYS else v
for k, v in data.items()
}
import time
from contextlib import contextmanager
@contextmanager
def log_duration(operation: str, **extra):
start = time.perf_counter()
try:
yield
finally:
duration_ms = (time.perf_counter() - start) * 1000
log.info(f"{operation}_duration", duration_ms=round(duration_ms, 2), **extra)
{
"timestamp": "2026-03-20T10:58:00Z",
"level": "info",
"event": "request_completed",
"request_id": "abc-123",
"method": "GET",
"path": "/api/skills",
"status": 200,
"duration_ms": 42.5,
"service": "a-i--skills",
"organ": "IV"
}
Always include: timestamp, level, event, service, request_id. Optionally: user_id, organ, duration_ms, error.
log.info("x", user=id) not log.info(f"user {id}")except: passtesting
Designs systems for encoding, scoring, and generating choreographic movement using Laban notation, computational geometry, and procedural animation principles.
tools
Manage monorepos and multi-package repositories with workspace tools, dependency management, selective builds, and change detection. Covers npm/pnpm workspaces, Turborepo, and Python monorepo patterns. Triggers on monorepo setup, workspace management, or multi-package repository requests.
development
Curated bundle for managing monorepos with containerized deployment pipelines. Includes monorepo management, Docker containerization, CI/CD deployment, and coding standards. Use when setting up or improving multi-package repository infrastructure.
development
Apply modular synthesis principles to system design, workflow architecture, and conceptual frameworks. Use when designing modular systems, creating architecture diagrams using synthesis metaphors, applying signal flow thinking to data pipelines, or translating between audio engineering and software concepts. Triggers on modular architecture design, signal flow diagrams, synthesis-inspired system thinking, or "oscillator/patch" metaphors.