
Logging Best Practices
Stand up centralized logging with ELK or CloudWatch and wire application log shippers so solo-run services stay debuggable in production.
Install
npx skills add https://github.com/aj-geddes/useful-ai-prompts --skill logging-best-practicesWhat is this skill?
- Docker Compose blueprint for Elasticsearch, Logstash, and Kibana with JSON TCP ingestion on port 5000
- Sample logstash.conf with ISO8601 parsing, optional GeoIP enrichment, and daily Elasticsearch index rotation
- Winston transports for Logstash shipping and AWS CloudWatch Logs integration in TypeScript services
- Patterns for structured JSON logs suitable for centralized search and alerting pipelines
- Reference stack targets operators who self-host ELK or run on AWS without a dedicated platform team
Adoption & trust: 544 installs on skills.sh; 250 GitHub stars; 3/3 security scanners passed (skills.sh audits).
Recommended Skills
Azure Kubernetesmicrosoft/azure-skills
Github Actions Docsxixu-me/skills
Deploy To Vercelvercel-labs/agent-skills
Vercel Cli With Tokensvercel-labs/agent-skills
Turborepovercel/turborepo
Docker Expertsickn33/antigravity-awesome-skills
Journey fit
Primary fit
Operate is the right phase because centralized logging is about running and observing live systems, not greenfield feature design. Monitoring is the canonical subphase for log aggregation, search, dashboards, and cross-service traceability when incidents strike.
Common Questions / FAQ
Is Logging Best Practices safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Logging Best Practices
# Centralized Logging ## Centralized Logging ### ELK Stack (Elasticsearch, Logstash, Kibana) ```yaml # docker-compose.yml version: "3" services: elasticsearch: image: elasticsearch:8.0.0 environment: - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ports: - "9200:9200" logstash: image: logstash:8.0.0 volumes: - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf ports: - "5000:5000" depends_on: - elasticsearch kibana: image: kibana:8.0.0 ports: - "5601:5601" depends_on: - elasticsearch ``` ```conf # logstash.conf input { tcp { port => 5000 codec => json } } filter { # Parse timestamp date { match => ["timestamp", "ISO8601"] } # Add geo-location if IP present if [ip] { geoip { source => "ip" } } } output { elasticsearch { hosts => ["elasticsearch:9200"] index => "app-logs-%{+YYYY.MM.dd}" } } ``` ### Ship Logs to ELK ```typescript // winston-elk.ts import winston from "winston"; import "winston-logstash"; const logger = winston.createLogger({ transports: [ new winston.transports.Logstash({ port: 5000, host: "logstash", node_name: "user-service", max_connect_retries: -1, }), ], }); ``` ### AWS CloudWatch Logs ```typescript // cloudwatch-logger.ts import winston from "winston"; import WinstonCloudWatch from "winston-cloudwatch"; const logger = winston.createLogger({ transports: [ new WinstonCloudWatch({ logGroupName: "/aws/lambda/user-service", logStreamName: () => { const date = new Date().toISOString().split("T")[0]; return `${date}-${process.env.LAMBDA_VERSION}`; }, awsRegion: "us-east-1", jsonMessage: true, }), ], }); ``` # Contextual Logging ## Contextual Logging ### Request Context Middleware ```typescript // request-logger.ts import { v4 as uuidv4 } from "uuid"; import { AsyncLocalStorage } from "async_hooks"; const asyncLocalStorage = new AsyncLocalStorage(); // Middleware to add request context export function requestLogger(req, res, next) { const requestId = req.headers["x-request-id"] || uuidv4(); const context = { requestId, method: req.method, path: req.path, ip: req.ip, userAgent: req.headers["user-agent"], userId: req.user?.id, }; asyncLocalStorage.run(context, () => { logger.info("Request started", context); // Log response when finished res.on("finish", () => { logger.info("Request completed", { ...context, statusCode: res.statusCode, duration: Date.now() - req.startTime, }); }); req.startTime = Date.now(); next(); }); } // Logger wrapper that includes context export function getLogger() { const context = asyncLocalStorage.getStore(); return { info: (message: string, meta?: object) => logger.info(message, { ...context, ...meta }), error: (message: string, error: Error, meta?: object) => logger.error(message, { ...context, error, ...meta }), warn: (message: string, meta?: object) => logger.warn(message, { ...context, ...meta }), debug: (message: string, meta?: object) => logger.debug(message, { ...context, ...meta }), }; } // Usage in route handler app.get("/api/users/:id", async (req, res) => { const log = getLogger(); log.info("Fetching user", { userId: req.params.id }); try { const user = await userService.findById(req.params.id); log.info("User found", { userId: user.id }); res.json(user); } catch (error) { log.error("Failed to fetch user", error, { userId: req.params.id }); res.status(500).json({ error: "Internal server error" }); } }); ``` ### Correlation IDs ```typescript // correlation-id.ts export class CorrelationIdManager { private static storage = new AsyncLocalStorage<string>(); static run<T>(correlationId: string, callback: () => T): T {