UNPKG

unnbound-logger-sdk

Version:

A structured logging library with TypeScript support using Pino. Provides consistent, well-typed logging with automatic logId, workflowId, traceId, and deploymentId tracking across operational contexts.

457 lines (327 loc) 14.8 kB
# Unnbound Logger A structured logging library with TypeScript support built on Pino. Provides consistent, well-typed logging across different operational contexts with automatic trace and span tracking. All logs are output in JSON format for better machine readability and parsing. ## Installation ```bash npm install unnbound-logger-sdk ``` ## Basic Usage ```typescript import { logger } from 'unnbound-logger-sdk'; // Log with string messages logger.info('Application started'); logger.warn('Resource usage high'); logger.error({ err: new Error('Database connection failed') }, 'Something bad happened'); logger.debug('Debug information'); logger.trace('Trace information'); // Log with object messages (merged into top level) logger.info({ event: 'user_login', userId: '123' }, 'Event received.'); // Results in: { "event": "user_login", "userId": "123", "message": "Application started", ... } // Log with both string message and metadata (metadata merged into top level) logger.info({ userId: '123' }, 'User logged in'); // Results in: { "userId": "123", "message": "User logged in", ... } ``` ## Log Format All logs follow a standardized format based on Pino with additional context: ```typescript interface Log<T extends LogType = 'general'> { logId: string; // Automatically generated unique ID for each log entry level: LogLevel; // "info" | "debug" | "error" | "warn" | "trace" message: string; type: T; // "general" | "http" | "sftp" traceId?: string; // Automatically included when in trace context spanId?: string; // Automatically included when in span context serviceId?: string; // From UNNBOUND_SERVICE_ID environment variable deploymentId?: string; // From UNNBOUND_DEPLOYMENT_ID environment variable workflowId?: string; // From UNNBOUND_WORKFLOW_ID environment variable environment?: string; // From ENVIRONMENT environment variable err?: unknown; // Only present for Error objects duration?: number; // Duration in milliseconds for span operations http?: T extends 'http' ? HttpPayload : never; sftp?: T extends 'sftp' ? SftpPayload : never; } ``` ## Workflow and Deployment Tracking ### Workflow Tracking The logger includes a `workflowId` field in all log entries for tracking operations across services: ```bash # Set the workflow ID in your environment export UNNBOUND_WORKFLOW_ID="order-processing-12345" export UNNBOUND_WORKFLOW_URL="https://workflows.example.com/order-processing-12345" export UNNBOUND_SERVICE_ID="order-service" # Or in your deployment configuration UNNBOUND_WORKFLOW_ID=order-processing-12345 UNNBOUND_WORKFLOW_URL=https://workflows.example.com/order-processing-12345 UNNBOUND_SERVICE_ID=order-service ``` ```typescript // Import the logger - workflowId and serviceId are automatically set from environment import { logger } from 'unnbound-logger-sdk'; ``` ### Deployment Tracking The logger automatically includes a `deploymentId` field in all log entries. This field is populated from the `UNNBOUND_DEPLOYMENT_ID` environment variable, allowing you to track logs per deployment. ```bash # Set the deployment ID in your environment export UNNBOUND_DEPLOYMENT_ID="v1.2.3-prod-20231201" # Or in your deployment configuration UNNBOUND_DEPLOYMENT_ID=v1.2.3-prod-20231201 ``` If the environment variables are not set, the fields will be empty strings. These fields help with: - **Workflow ID**: Unique identifier for the workflow (logged in each entry) - **Workflow URL**: Used internally for URL construction in webhook endpoints (not logged as a field) - **Service ID**: Identifier for the specific service/component (logged in each entry) - **Deployment ID**: Tracking logs across different application deployments (logged in each entry) - **Correlating issues**: Link problems to specific workflows and releases - **Monitoring**: Track health and performance across workflows and deployments ## Object Logging Behavior The logger uses Pino's standard behavior for handling different message types: ### String Messages with Metadata When you pass a string message with additional metadata, the metadata is merged into the top level: ```typescript logger.info('User action completed', { userId: '123', action: 'login' }); // Result: { "message": "User action completed", "userId": "123", "action": "login", ... } ``` ### Object Messages When you pass an object as the message, it's merged into the top level: ```typescript logger.info({ userId: '123', action: 'login' }); // Result: { "userId": "123", "action": "login", "message": "Application started", ... } ``` ### Error Objects Error objects are handled specially and include serialized error information: ```typescript logger.error({ err: new Error('Something went wrong') }); // Result: { "message": "Error", "err": { "name": "Error", "message": "Something went wrong", "stack": "..." }, ... } ``` This follows Pino's standard behavior where all object properties are merged into the top level of the log entry. ## HTTP Request/Response Logging ```typescript import { logger, traceMiddleware } from 'unnbound-logger-sdk'; import express from 'express'; const app = express(); // Apply trace middleware for automatic HTTP logging app.use(traceMiddleware()); // Example route app.post('/api/users', (req, res) => { // Your route handler code here res.status(201).json({ id: '123', status: 'created' }); }); ``` The trace middleware automatically captures: - Request method, URL, body, and headers (filtered for security) - Response status code, body, and headers (filtered for security) - Request duration - Trace ID and span ID for correlation - Automatic span tracking for the entire request lifecycle ### Full URL Logging for Webhook Endpoints When webhook endpoints receive incoming requests, the logger automatically constructs and logs the full URL using a smart fallback strategy: 1. **Preferred: Uses `UNNBOUND_WORKFLOW_URL`** - If set, this becomes the base URL for all logged requests 2. **Fallback: Constructs from request headers** - Uses protocol, host, and forwarded headers from the incoming request ```bash # Set your workflow URL to ensure full URLs in logs export UNNBOUND_WORKFLOW_URL="https://api.yourservice.com" # Example webhook endpoints will be logged as: # POST https://api.yourservice.com/webhooks/stripe # POST https://api.yourservice.com/webhooks/github ``` ```typescript import express from 'express'; import { traceMiddleware } from 'unnbound-logger-sdk'; const app = express(); // Apply trace middleware for automatic logging app.use(traceMiddleware()); // Webhook endpoints - URLs automatically logged with full domain app.post('/webhooks/stripe', (req, res) => { // Request logged as: https://api.yourservice.com/webhooks/stripe res.status(200).send('OK'); }); app.post('/webhooks/github', (req, res) => { // Request logged as: https://api.yourservice.com/webhooks/github res.status(200).send('OK'); }); ``` This ensures webhook logs contain the complete URL for easy debugging and monitoring. ## SFTP Operations Logging The logger supports structured logging for SFTP operations with automatic span tracking: ```typescript import { logger, startSpan } from 'unnbound-logger-sdk'; // Example SFTP operation with automatic logging const uploadFile = async (filePath: string, content: string) => { return await startSpan( 'SFTP upload operation', async () => { // Your SFTP upload logic here logger.info('Uploading file', { filePath, contentLength: content.length }); return { success: true, filePath }; }, (result) => ({ type: 'sftp', sftp: { host: 'sftp.example.com', operation: 'upload', path: filePath, content: content, bytes: content.length, }, }) ); }; ``` The SFTP logging automatically captures: - Host information - Operation type (connect, upload, download, list, delete, etc.) - File paths and content - Byte counts for transfers - Operation duration - Success/failure status ## Middleware Usage ### Express Trace Middleware The library provides a comprehensive trace middleware for Express applications that automatically handles trace context and HTTP logging: ```typescript import { traceMiddleware } from 'unnbound-logger-sdk'; import express from 'express'; const app = express(); // Apply the comprehensive trace middleware globally app.use(traceMiddleware()); ``` The trace middleware automatically: - Generates and maintains trace IDs across the request lifecycle - Logs incoming requests with method, URL, headers, and body - Logs outgoing responses with status code, headers, body, and duration - Measures request duration automatically - Handles errors and logs them appropriately - Captures response bodies for logging - Creates spans for the entire request lifecycle ### Axios Trace Middleware For comprehensive logging of outgoing HTTP requests made with Axios: ```typescript import { traceAxios } from 'unnbound-logger-sdk'; import axios from 'axios'; // Create an axios instance and wrap it with tracing const client = traceAxios(axios.create()); // All requests made with this client will be automatically logged client.get('https://api.example.com/data'); ``` The Axios middleware: - Logs outgoing requests with method, URL, headers, and body - Maintains trace context across requests by propagating trace IDs - Logs successful responses with status, headers, body, and duration - Logs error responses with detailed error information - Creates spans for each HTTP request - Supports request/response filtering through configuration ## Manual tracing In case the function doesn't run inside an HTTP handler (for example a cron job), you can assign a trace id manually by using the `withTrace` function: ```typescript import { withTrace } from 'unnbound-logger-sdk'; const operation = async (value: number) => { // This will log a { traceId, value } logger.info('Processing value', { value }); return value * 2; }; setInterval(() => withTrace(() => operation(13)), 1000); ``` ## Function Tracing with startSpan The `startSpan` function allows you to wrap any async operation with automatic span tracking and logging. This is particularly useful for maintaining consistent trace IDs across async operations and distributed systems: ```typescript import { logger, startSpan } from 'unnbound-logger-sdk'; // Example: Wrapping a function with span tracking const operation = async (value: number) => { logger.info('Processing value', { value }); return value * 2; }; // Wrap the function with span tracking const result = await startSpan('Processing operation', operation, () => ({ operationType: 'calculation', })); // Execute the function const result = await startSpan('Processing operation', () => operation(21)); // Returns 42 ``` _Note: In case the `traceId` is missing from the context, `startSpan` will generate one. It is recommended to use a single `traceId` across your handler though, so always consider using `traceMiddleware` and `withTrace` to inject the `traceId` instead of relying on `startSpan` to create one._ ### Async Operations The span context is maintained across async operations: ```typescript const asyncOperation = async (value: number) => { logger.info('First step', { value }); await someAsyncWork(); logger.info('Second step', { value }); return value * 2; }; const result = await startSpan('Async operation', asyncOperation, () => ({ operationType: 'async_calculation', })); ``` ### Benefits - Automatic span ID generation and tracking - Consistent trace context across async operations - Automatic duration measurement - Error handling and logging - Type-safe implementation - Works with async functions - Maintains separate span contexts for different operations > **Note:** When using the logger within spans, you don't need to manually manage trace IDs. The logger automatically includes the current trace ID and span ID in all log entries. ## API Reference ### logger The main logger instance that provides all logging functionality using Pino. #### Usage ```typescript import { logger } from 'unnbound-logger-sdk'; ``` The logger is a Pino instance with additional context automatically included from environment variables and trace context. #### Methods - `logger.trace(object: {}, message: string): void` - `logger.trace(message: string): void` - `logger.debug(object: {}, message: string): void` - `logger.debug(message: string): void` - `logger.info(object: {}, message: string): void` - `logger.info(message: string): void` - `logger.warn(object: {}, message: string): void` - `logger.warn(message: string): void` - `logger.error<O extends { err: unknown }>(object: O, message: string): void` ### traceMiddleware Express middleware for automatic HTTP request/response logging and trace context management. #### Usage ```typescript import { traceMiddleware } from 'unnbound-logger-sdk'; app.use(traceMiddleware(options?: HttpOptions)); ``` **HttpOptions:** - `traceHeaderKey?: string` - Custom trace header name (default: 'x-unnbound-trace-id') - `messageHeaderKey?: string` - Custom message header name (default: 'x-message-id') - `ignoreTraceRoutes?: string[]` - Routes to ignore in middleware (default: ['/health', '/healthcheck']) ### traceAxios Wraps an Axios instance with automatic request/response logging and trace context propagation. #### Usage ```typescript import { traceAxios } from 'unnbound-logger-sdk'; import axios from 'axios'; const client = traceAxios(axios.create(), options?: HttpOptions); ``` ### startSpan Creates a span for tracking async operations with automatic logging and duration measurement. #### Usage ```typescript import { startSpan } from 'unnbound-logger-sdk'; const result = await startSpan<T>( spanName: string, callback: () => Promise<T>, getter?: LogPayloadGetter<T> ); ``` **Parameters:** - `spanName`: The name of the span for logging - `callback`: The async function to execute within the span - `getter`: Optional function to generate log payload based on operation result/error ### getTraceId Generates a new trace ID for manual trace context management. #### Usage ```typescript import { getTraceId } from 'unnbound-logger-sdk'; const traceId = getTraceId(); console.log(traceId); // "550e8400-e29b-41d4-a716-446655440000" ``` ### Environment Variables - `UNNBOUND_WORKFLOW_ID` - Workflow identifier (included in all logs) - `UNNBOUND_SERVICE_ID` - Service identifier (included in all logs) - `UNNBOUND_DEPLOYMENT_ID` - Deployment identifier (included in all logs) - `UNNBOUND_WORKFLOW_URL` - Base URL for webhook endpoint logging - `ENVIRONMENT` - Environment name (included in all logs) - `LOG_LEVEL` - Log level (default: 'info')