Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

unnbound-logger-sdk

unnbound604MIT2.0.9TypeScript support: included

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.

logging, typescript, structured-logging, pino, tracing, http-logging, express-middleware, axios-interceptor, observability

readme

Unnbound Logger

A structured logging library with TypeScript support built on Pino. Provides consistent, well-typed logging across different operational contexts. All logs are output in JSON format for better machine readability and parsing.

Installation

npm install unnbound-logger

Basic Usage

import { UnnboundLogger } from 'unnbound-logger';

// Create a new logger instance
const logger = new UnnboundLogger();

// Log with string messages
logger.info('Application started');
logger.warn('Resource usage high');
logger.error(new Error('Database connection failed'));
logger.debug('Debug information');

// Log with object messages
logger.info({
  event: 'user_login',
  userId: '123',
  timestamp: new Date().toISOString()
});

// Log with both string message and metadata
logger.info('User logged in', {
  userId: '123',
  timestamp: new Date().toISOString()
});

// Log with object message and additional metadata
logger.info(
  { event: 'user_login', userId: '123' },
  { timestamp: new Date().toISOString() }
);

Log Format

All logs follow a standardized format:

interface Log<T extends LogType = 'general'> {
  logId: string; // Unique identifier for each log entry
  level: LogLevel; // "info" | "debug" | "error" | "warn"
  type: T; // "general" | "httpRequest" | "httpResponse" | "sftpTransaction" | "dbQueryTransaction"
  message: string;
  workflowId: string;
  serviceId: string;
  traceId: string;
  requestId: string;
  deploymentId: string; // Automatically populated from UNNBOUND_DEPLOYMENT_ID
  error?: SerializableError; // Only present for Error objects
}

interface LogTransaction<T extends LogType> extends Log<T> {
  duration: number; // Duration in milliseconds
}

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
// Create a logger - workflowId and serviceId are automatically set from environment
const logger = new UnnboundLogger();

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-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

HTTP Request/Response Logging

import { UnnboundLogger } from 'unnbound-logger';
import express from 'express';

const app = express();
const logger = new UnnboundLogger();

// Middleware to log requests
app.use((req, res, next) => {
  // Log the request and get the request ID
  const requestId = logger.httpRequest(req, {
    startTime: Date.now()
  });

  // Store the request ID in res.locals for later use
  res.locals.requestId = requestId;

  // Add response listener to log the response
  res.on('finish', () => {
    logger.httpResponse(res, req, {
      requestId,
      startTime: res.locals.startTime
    });
  });

  next();
});

// Example route
app.post('/api/users', (req, res) => {
  // Your route handler code here
  res.status(201).json({ id: '123', status: 'created' });
});

The logger 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 request ID for correlation

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
# 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
import express from 'express';
import { UnnboundLogger } from 'unnbound-logger';

const app = express();
const logger = new UnnboundLogger();

// Apply trace middleware for automatic logging
app.use(logger.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 Transaction Logging

For logging SFTP operations:

import { UnnboundLogger } from 'unnbound-logger';

const logger = new UnnboundLogger();

// Log an SFTP upload
logger.sftpTransaction({
  host: 'sftp.example.com',
  username: 'ftpuser',
  operation: 'upload',
  path: '/uploads/file.txt',
  status: 'success',
  bytesTransferred: 1024
});

// Log an SFTP download with failure
logger.sftpTransaction({
  host: 'sftp.example.com',
  username: 'ftpuser',
  operation: 'download',
  path: '/downloads/file.txt',
  status: 'failure'
}, {
  startTime: Date.now() - 5000 // Started 5 seconds ago
});

Database Query Transaction Logging

For logging database operations:

import { UnnboundLogger } from 'unnbound-logger';

const logger = new UnnboundLogger();

// Log a successful database query
logger.dbQueryTransaction({
  instance: 'localhost:5432',
  vendor: 'postgres',
  query: 'SELECT * FROM users WHERE active = true',
  status: 'success',
  rowsReturned: 150
});

// Log a failed database operation
logger.dbQueryTransaction({
  instance: 'prod-db-cluster',
  vendor: 'mysql',
  query: 'UPDATE users SET last_login = NOW()',
  status: 'failure',
  rowsAffected: 0
}, {
  duration: 2500 // Operation took 2.5 seconds
});

Middleware Usage

Express Trace Middleware

The library provides a comprehensive trace middleware for Express applications that automatically handles trace context and HTTP logging:

import { UnnboundLogger } from 'unnbound-logger';
import express from 'express';

const app = express();
const logger = new UnnboundLogger();

// Apply the comprehensive trace middleware globally
app.use(logger.traceMiddleware);

The trace middleware automatically:

  • Generates and maintains trace IDs across the request lifecycle
  • Logs incoming requests with method, URL, headers, and body (filtered for security)
  • 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

Axios Trace Middleware

For comprehensive logging of outgoing HTTP requests made with Axios:

import { UnnboundLogger } from 'unnbound-logger';
import axios from 'axios';

const logger = new UnnboundLogger();

// Add both request and response interceptors for complete HTTP logging
axios.interceptors.request.use(
  logger.axiosTraceMiddleware.onFulfilled,
  logger.axiosTraceMiddleware.onRejected
);

axios.interceptors.response.use(
  logger.axiosResponseInterceptor.onFulfilled,
  logger.axiosResponseInterceptor.onRejected
);

// All requests made with axios will be automatically logged
axios.get('https://api.example.com/data');

The Axios middleware:

  • Logs outgoing requests with method, URL, headers, and body (filtered for security)
  • 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
  • Supports request/response filtering through configuration

Function Tracing with withTrace

The withTrace higher-order function allows you to wrap any function with automatic trace context. This is particularly useful for maintaining consistent trace IDs across async operations and distributed systems:

import { UnnboundLogger } from 'unnbound-logger';
import { withTrace } from 'unnbound-logger/utils/with-trace';
import { traceContext } from 'unnbound-logger/utils/trace-context';

const logger = new UnnboundLogger();

// Example: Wrapping a function with trace context
const operation = (value: number) => {
  const traceId = traceContext.getTraceId();
  logger.info('Processing value', { value, traceId });
  return value * 2;
};

// Wrap the function with trace context
const tracedOperation = withTrace(operation);

// Execute the function
const result = tracedOperation(21); // Returns 42

Using Custom Trace IDs

You can provide your own trace ID when wrapping a function:

const customTraceId = 'custom-trace-123';
const tracedOperation = withTrace(operation, customTraceId);

Async Operations

The trace context is maintained across async operations:

const asyncOperation = async (value: number) => {
  const traceId1 = traceContext.getTraceId();
  logger.info('First step', { traceId: traceId1 });

  await someAsyncWork();

  const traceId2 = traceContext.getTraceId();
  logger.info('Second step', { traceId: traceId2 });
  // traceId1 and traceId2 will be the same
};

const tracedOperation = withTrace(asyncOperation);
await tracedOperation(42);

Benefits

  • Automatic trace ID generation
  • Consistent trace context across async operations
  • Support for custom trace IDs
  • Type-safe implementation
  • Works with both sync and async functions
  • Maintains separate trace contexts for different operations

Note: When using UnnboundLogger, you don't need to manually call traceContext.getTraceId() in your logging calls. The logger automatically includes the current trace ID in all log entries. The example above shows manual trace ID retrieval for demonstration purposes, but in practice, you can simply use the logger methods directly:

const operation = (value: number) => {
  logger.info('Processing value', { value }); // Trace ID is automatically included
  return value * 2;
};

API Reference

UnnboundLogger

The main logger class that provides all logging functionality using Pino.

Constructor

new UnnboundLogger(options?: LoggerOptions)

LoggerOptions:

  • traceHeaderKey?: string - Custom trace header name (default: 'unnbound-trace-id')
  • ignoreTraceRoutes?: string[] - Routes to ignore in Express middleware
  • ignoreAxiosTraceRoutes?: string[] - Routes to ignore in Axios middleware

Note: workflowId, serviceId, and deploymentId are configured via environment variables (UNNBOUND_WORKFLOW_ID, UNNBOUND_SERVICE_ID, UNNBOUND_DEPLOYMENT_ID). The UNNBOUND_WORKFLOW_URL is used for URL construction in webhook endpoints.

Methods

  • log(level: LogLevel, message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): Log
  • error(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): Log
  • warn(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): Log
  • info(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): Log
  • debug(message: string | Error | Record<string, unknown>, options?: GeneralLogOptions): Log
  • httpRequest(req: Request, options?: HttpRequestLogOptions): HttpRequestLog
  • httpResponse(res: Response, req: Request, options: HttpResponseLogOptions): HttpResponseLog
  • sftpTransaction(operation: SftpOperation, options?: SftpTransactionLogOptions): SftpTransactionLog
  • dbQueryTransaction(query: DbQuery, options?: DbQueryTransactionLogOptions): DbQueryTransactionLog