unnbound-logger-sdk
v3.0.34
Published
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.
Readme
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
npm install unnbound-logger-sdkBasic Usage
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:
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:
# 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// 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.
# 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-20231201If 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:
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:
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:
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
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:
- Preferred: Uses
UNNBOUND_WORKFLOW_URL- If set, this becomes the base URL for all logged requests - Fallback: Constructs from request headers - Uses protocol, host, and forwarded headers from the incoming request
# 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/githubimport 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:
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:
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:
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:
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:
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 42Note: 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:
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
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): voidlogger.trace(message: string): voidlogger.debug(object: {}, message: string): voidlogger.debug(message: string): voidlogger.info(object: {}, message: string): voidlogger.info(message: string): voidlogger.warn(object: {}, message: string): voidlogger.warn(message: string): voidlogger.error<O extends { err: unknown }>(object: O, message: string): void
traceMiddleware
Express middleware for automatic HTTP request/response logging and trace context management.
Usage
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
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
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 loggingcallback: The async function to execute within the spangetter: Optional function to generate log payload based on operation result/error
getTraceId
Generates a new trace ID for manual trace context management.
Usage
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 loggingENVIRONMENT- Environment name (included in all logs)LOG_LEVEL- Log level (default: 'info')
