From scaffolding
Structured logging standards using structlog and Seq. Use when adding log statements, configuring logging, or reviewing log output.
npx claudepluginhub komluk/scaffolding --plugin scaffoldingThis skill uses the workspace's default tool permissions.
Standards for consistent, useful logging across all application layers.
Integrates Mem0 persistent memory for Claude Code tasks using MCP tools. Retrieves relevant memories on new tasks, stores learnings like decisions and strategies, captures session states.
Creates web-based slidedecks for developers using Slidev with Markdown, Vue components, code highlighting, animations, interactive demos, and presenter notes. Use for technical presentations, conference talks, code walkthroughs, and workshops.
Standards for consistent, useful logging across all application layers.
| Level | Use Case | Example |
|---|---|---|
| TRACE | Detailed debugging | Variable values, loop iterations |
| DEBUG | Diagnostic info | Method entry/exit, state changes |
| INFO | Normal operations | Request completed, job started |
| WARN | Potential issues | Retry attempt, deprecated usage |
| ERROR | Failures | Exception caught, operation failed |
| FATAL | System failure | Cannot start, out of memory |
[timestamp] [level] [correlation-id] [logger] - message {structured-data}
2024-01-15T10:30:45.123Z INFO abc-123-def UserService - User created {"userId": 42, "email": "user@example.com"}
| Field | Description | Example |
|---|---|---|
| timestamp | ISO 8601 format | 2024-01-15T10:30:45.123Z |
| level | Log level | INFO, ERROR |
| message | Human-readable | "User created" |
| correlationId | Request trace ID | abc-123-def |
| logger | Source class/module | UserService |
| Field | When to Include |
|---|---|
| userId | Authenticated request |
| requestPath | HTTP request |
| requestMethod | HTTP request |
| duration | Operation timing |
| errorCode | Error situations |
| stackTrace | ERROR level only |
| Field | Masking | Example |
|---|---|---|
| Partial | j***@example.com | |
| Phone | Partial | +1***456 |
| Credit Card | Partial | --****-1234 |
| SSN | Full | *** |
| Password | Never log | - |
| Token | Never log | - |
| Metric | Level | When |
|---|---|---|
| Request duration | INFO | Every request |
| Slow query | WARN | > 1 second |
| External call | DEBUG | Every call |
| Cache hit/miss | DEBUG | When relevant |
| Operation | Warn Threshold |
|---|---|
| HTTP request | > 1s |
| Database query | > 500ms |
| External API | > 2s |
| Cache operation | > 100ms |
| Tool | Use Case |
|---|---|
| ELK Stack | Self-hosted, full-featured |
| Datadog | Cloud, APM integration |
| Splunk | Enterprise, compliance |
| Loki | Kubernetes-native |
| Environment | Retention |
|---|---|
| Development | 7 days |
| Staging | 30 days |
| Production | 90 days |
| Audit logs | 1 year+ |
core/logging/config.py)This project uses structlog with stdlib integration, routing all logs through Python's logging module with per-handler formatters.
Processor chain (shared across all handlers):
shared_processors = [
structlog.contextvars.merge_contextvars,
_add_log_context, # Injects task_id, session_id, project_id, user_id
_inject_context_to_record, # Copies context to LogRecord for OTel attributes
structlog.stdlib.add_log_level,
structlog.stdlib.add_logger_name,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.UnicodeDecoder(),
]
Two output pipelines:
ConsoleRenderer(colors=True) for dev, JSONRenderer for prodJSONRenderer always (no ANSI codes), via OTLPLogExporterFour contextvars.ContextVar fields are automatically injected into every log event:
from core.logging import set_log_context, clear_log_context
set_log_context(task_id="abc", session_id="sess", project_id="proj", user_id="123")
# All subsequent logs in this async context include these fields
from core import get_logger # Re-exported from core/__init__.py
logger = get_logger(__name__) # Returns structlog.stdlib.BoundLogger
Events use dot-separated domain.action format:
logger.info("app.startup_complete", max_concurrent_tasks=3)
logger.info("redis.connected")
logger.warning("db.schema_mismatch", migration_commands=[...])
logger.info("contributor.added", project_id=pid, user_id=uid, role="owner")
logger.error("redis.queue_push_error", queue=name, error=str(e))
logger.debug("redis.subscribed", channel=ch, task_id=tid)
Configured in main.py at startup:
setup_logging(
log_level=LOG_LEVEL, # From core.config (env: LOG_LEVEL)
log_format=LOG_FORMAT, # "console" or "json" (env: LOG_FORMAT)
log_file=LOG_FILE, # Optional rotating file (10MB, 5 backups)
seq_enabled=SEQ_ENABLED, # env: SEQ_ENABLED
seq_endpoint=SEQ_ENDPOINT,# Default: https://localhost:5341/ingest/otlp/v1/logs
seq_api_key=SEQ_API_KEY, # env: SEQ_API_KEY (X-Seq-ApiKey header)
service_name=OTEL_SERVICE_NAME, # Resource: service.name, namespace: "scaffolding"
)
Shutdown flushes pending logs: shutdown_logging() called in app lifespan teardown.
| File | Purpose |
|---|---|
core/logging/__init__.py | Public API: get_logger, set_log_context, TaskLogger |
core/logging/config.py | structlog setup, OTel/Seq integration, context vars |
core/logging/task_logger.py | Task-specific step logging |
core/middleware/ | CorrelationIdMiddleware, LoggingMiddleware, UserContextMiddleware |