Install
$ agentstack add skill-organvm-a-i-skills-error-handling-logging-patterns ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Error Handling & Logging Patterns
Build systems that are debuggable in production through structured logging and intentional error handling.
Error Hierarchy Design
Custom Exception Classes
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)
Error Classification
| 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 |
Structured Logging
Setup with structlog
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()
Contextual Logging
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", ...}
Log Levels by Purpose
| 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 |
Request Middleware
FastAPI Correlation IDs
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")
Error Response Handler
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"}},
)
Logging Patterns
Operation Logging
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 Data Filtering
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()
}
Performance Logging
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)
Log Aggregation Integration
JSON Format for Ingestion
{
"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"
}
Common Fields
Always include: timestamp, level, event, service, request_id. Optionally: user_id, organ, duration_ms, error.
Anti-Patterns
- Logging PII or secrets — Always filter sensitive fields before logging
- String interpolation in log calls — Use structured fields:
log.info("x", user=id)notlog.info(f"user {id}") - Catching and silencing exceptions — Log or re-raise; never
except: pass - Inconsistent log levels — Define team conventions and stick to them
- No correlation IDs — Impossible to trace requests across services without them
- Logging in tight loops — Rate-limit or sample high-frequency events
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: organvm
- Source: organvm/a-i--skills
- License: Apache-2.0
- Homepage: https://organvm.github.io/a-i--skills/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.