Skip to main content

correlation-id-tracking

Manages correlation-id in Node.js/TypeScript applications using AsyncLocalStorage for async context isolation. Use when implementing correlation-id tracking, HTTP request/response correlation, logging integration, or when working with distributed tracing in Node.js applications.

Ir a la instalación

Datos de origen

Repositorio
whitebeardit/.cursor
Última actividad en el origen
31 de enero de 2026 a las 23:09
Idioma detectado de SKILL.md
inglés
Estrellas
12
Forks
6

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
correlation-id-tracking
description
Manages correlation-id in Node.js/TypeScript applications using AsyncLocalStorage for async context isolation. Use when implementing correlation-id tracking, HTTP request/response correlation, logging integration, or when working with distributed tracing in Node.js applications.
# Correlation-ID Tracking for Node.js This skill helps you implement correlation-id tracking in Node.js/TypeScript applications using AsyncLocalStorage for async context preservation. ## Installation ```bash npm install async-local-storage # or yarn add async-local-storage ``` ## Quick Setup ### Express.js - Zero Configuration **Minimal setup:** ```typescript import express from 'express'; import { correlationIdMiddleware } from './middleware/correlation-id'; const app = express(); // Add correlation-ID middleware (must be first) app.use(correlationIdMiddleware); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.get('/api/test', (req, res) => { const correlationId = getCorrelationId(); res.json({ correlationId }); }); app.listen(3000); ``` ### Correlation-ID Middleware ```typescript import { Request, Response, NextFunction } from 'express'; import { AsyncLocalStorage } from 'async_hooks'; const correlationStorage = new AsyncLocalStorage<string>(); export function getCorrelationId(): string { return correlationStorage.getStore() || generateCorrelationId(); } export function setCorrelationId(id: string): void { correlationStorage.enterWith(id); } function generateCorrelationId(): string { return crypto.randomUUID().replace(/-/g, ''); } export function correlationIdMiddleware( req: Request, res: Response, next: NextFunction ): void { const correlationId = req.headers['x-correlation-id'] as string || generateCorrelationId(); correlationStorage.run(correlationId, () => { res.setHeader('X-Correlation-Id', correlationId); next(); }); } ``` ## Using Correlation-ID in Code ### In Controllers ```typescript import { Request, Response } from 'express'; import { getCorrelationId } from '../middleware/correlation-id'; export async function getOrder(req: Request, res: Response) { const correlationId = getCorrelationId(); try { const order = await orderService.getById(req.params.id); res.json({ order, correlationId }); } catch (error) { res.status(500).json({ error: 'Failed to get order', correlationId }); } } ``` ### In Services ```typescript import { getCorrelationId } from '../middleware/correlation-id'; import logger from '../logger'; export class OrderService { async getById(id: string) { const correlationId = getCorrelationId(); logger.info('Getting order', { orderId: id, correlationId }); // Your business logic here return order; } } ``` ## HTTP Client Integration ### Axios Interceptor ```typescript import axios from 'axios'; import { getCorrelationId } from './middleware/correlation-id'; // Add request interceptor axios.interceptors.request.use((config) => { const correlationId = getCorrelationId(); if (correlationId) { config.headers['X-Correlation-Id'] = correlationId; } return config; }); ``` ### Fetch Wrapper ```typescript import { getCorrelationId } from './middleware/correlation-id'; export async function fetchWithCorrelationId( url: string, options?: RequestInit ): Promise<Response> { const correlationId = getCorrelationId(); const headers = { ...options?.headers, 'X-Correlation-Id': correlationId || '', }; return fetch(url, { ...options, headers }); } ``` ## Logging Integration ### Winston Logger ```typescript import winston from 'winston'; import { getCorrelationId } from './middleware/correlation-id'; const logger = winston.createLogger({ format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { get correlationId() { return getCorrelationId(); }, }, transports: [ new winston.transports.Console({ format: winston.format.simple(), }), ], }); // Usage logger.info('Processing order', { orderId: '123' }); // Automatically includes correlationId in metadata ``` ### Pino Logger ```typescript import pino from 'pino'; import { getCorrelationId } from './middleware/correlation-id'; const logger = pino({ mixin() { return { correlationId: getCorrelationId(), }; }, }); // Usage logger.info({ orderId: '123' }, 'Processing order'); // Automatically includes correlationId ``` ## Logging Best Practices ### Log Levels: When to Use Each **DEBUG** - Development and troubleshooting only: - Detailed execution flow - Variable values and intermediate states - Step-by-step process information - **Not visible in production** (typically filtered out) **INFO** - Production-ready, important events: - Request start/completion - Business operations (create, update, delete) - External API calls (start/end) - Important state changes - **Visible in production** **WARNING** - Potential issues that don't break functionality: - Retry attempts - Fallback to default values - Deprecated API usage - Performance degradation **ERROR** - Failures that need attention: - Exceptions and errors - Failed operations - External service failures ### What to Log (and What NOT to Log) **✅ DO Log:** - Request identifiers (correlation-ID, user ID, request ID) - Business operations (what happened) - External service calls (start, end, duration) - Important state changes - Errors with context (correlation-ID, user, operation) **❌ DON'T Log:** - Sensitive data (passwords, tokens, PII, credit cards) - Large payloads (use summaries instead) - Every iteration in loops (log once per operation) - Redundant information (correlation-ID is already in context) - Excessive detail in production (use DEBUG for that) ### Structured Logging with Correlation-ID Always include correlation-ID in logs for traceability: ```typescript // ✅ Good: Structured logging with correlation-ID logger.info('Processing order', { orderId: orderId, userId: userId, correlationId: getCorrelationId(), }); // ✅ Good: Using structured properties logger.info('External API call completed', { endpoint: endpoint, duration: duration, statusCode: statusCode, correlationId: getCorrelationId(), }); // ❌ Bad: Logging without correlation-ID context logger.info(`Processing order ${orderId}`); // Missing correlation-ID! // ❌ Bad: Logging sensitive data logger.info(`User password: ${password}`); // NEVER log passwords! ``` ### Logging Patterns for Debugging **Pattern 1: Request Lifecycle** ```typescript export async function getOrder(req: Request, res: Response) { const correlationId = getCorrelationId(); // ✅ INFO: Request start (production-visible) logger.info('Getting order', { orderId: req.params.id, correlationId }); try { // ✅ DEBUG: Detailed steps (development only) logger.debug('Querying database for order', { orderId: req.params.id, correlationId }); const order = await orderRepository.findById(req.params.id); if (!order) { // ✅ WARNING: Expected but notable condition logger.warn('Order not found', { orderId: req.params.id, correlationId }); return res.status(404).json({ error: 'Order not found' }); } // ✅ INFO: Successful completion (production-visible) logger.info('Order retrieved successfully', { orderId: req.params.id, correlationId }); res.json(order); } catch (error) { // ✅ ERROR: Always log exceptions with correlation-ID logger.error(error, 'Error retrieving order', { orderId: req.params.id, correlationId }); res.status(500).json({ error: 'Internal server error' }); } } ``` **Pattern 2: External Service Calls** ```typescript export async function processPayment(request: PaymentRequest) { const correlationId = getCorrelationId(); // ✅ INFO: External call start (production-visible) logger.info('Calling payment service', { amount: request.amount, currency: request.currency, correlationId, }); const startTime = Date.now(); try { const response = await paymentClient.process(request); const duration = Date.now() - startTime; // ✅ INFO: Success with duration (production-visible) logger.info('Payment service call completed', { status: response.status, duration, correlationId, }); return response; } catch (error) { const duration = Date.now() - startTime; // ✅ ERROR: Failure with context (production-visible) logger.error(error, 'Payment service call failed', { duration, correlationId, }); throw error; } } ``` **Pattern 3: Conditional Debug Logging** ```typescript export async function processItems(items: Item[]) { const correlationId = getCorrelationId(); // ✅ INFO: Operation start (production-visible) logger.info('Processing items', { count: items.length, correlationId, }); for (let i = 0; i < items.length; i++) { // ✅ DEBUG: Detailed per-item info (development only) logger.debug('Processing item', { index: i + 1, total: items.length, itemId: items[i].id, correlationId, }); await processItem(items[i]); } // ✅ INFO: Operation completion (production-visible) logger.info('Processed items successfully', { count: items.length, correlationId, }); } ``` ## Key Features 1. **AsyncLocalStorage**: Uses Node.js `async_hooks` AsyncLocalStorage for context preservation 2. **Automatic Propagation**: HTTP clients automatically include correlation-ID in headers 3. **Logging Integration**: Works with Winston, Pino, and other loggers 4. **Framework Support**: Works with Express, Fastify, NestJS, and other frameworks 5. **Header-Based**: Uses `X-Correlation-Id` header for HTTP propagation ## Important Notes - **UUID Format**: Correlation-IDs are typically 32-character UUIDs without hyphens - **Never Overwrites**: If correlation-ID exists (from headers), it's preserved - **Independent from OpenTelemetry**: Correlation-ID is separate from trace context - both can coexist - **Thread-Safe**: AsyncLocalStorage is safe for concurrent async operations ## Common Patterns ### Pattern 1: Reading Correlation-ID from Request The middleware automatically: 1. Reads `X-Correlation-Id` from incoming request headers 2. If present, uses that value 3. If missing, generates new UUID (32 chars, no hyphens) 4. Sets AsyncLocalStorage context 5. Adds to response headers ### Pattern 2: Preserving Across Async Operations ```typescript const correlationId = getCorrelationId(); // "abc123" await someAsyncMethod(); const sameId = getCorrelationId(); // Still "abc123" - preserved! ``` ### Pattern 3: Manual Propagation ```typescript // Set correlation-ID from external source setCorrelationId(externalCorrelationId); // Now all operations use this correlation-ID await processRequest(); ``` ## Key Principles 1. **Correlation-ID Always**: Every log should include correlation-ID 2. **Structured Properties**: Use structured logging with named properties, not string interpolation 3. **Context Matters**: Include relevant context (user ID, operation, IDs) but not sensitive data 4. **Level Appropriately**: Use DEBUG for detailed troubleshooting, INFO for production visibility 5. **Performance Aware**: Don't log in tight loops; summarize instead 6. **Error Context**: Always include correlation-ID and relevant context in error logs
Ver en GitHub