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.
npm install unnbound-logger-sdkA 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.
``bash`
npm install unnbound-logger-sdk
`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", ... }
`
All logs follow a standardized format based on Pino with additional context:
`typescript`
interface Log
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;
}
The logger includes a workflowId field in all log entries for tracking operations across services:
`bashSet 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"
`typescript
// Import the logger - workflowId and serviceId are automatically set from environment
import { logger } from 'unnbound-logger-sdk';
`$3
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:
$3
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", ... }
`$3
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", ... }
`$3
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
$3
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
$3
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
$3
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._$3
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',
}));
`$3
- 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
$3
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$3
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'])$3
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);
`$3
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(
spanName: string,
callback: () => Promise,
getter?: LogPayloadGetter
);
`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$3
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"
`$3
-
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')