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
Markdown
# 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')