npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

unnbound-logger-sdk

v3.2.2

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

Basic 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": "Event received.", ... }

// 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 TEMPER_ENVIRONMENT, WORKFLOW_ENV, UNNBOUND_ENVIRONMENT, or ENVIRONMENT, whichever is set first
  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-20231201

If the environment variables are not set, the fields are omitted from the log entry. 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 follows Pino's argument order. The context object comes first and the message comes second.

The results below show only the fields each call contributes. The leading ... stands for the fields the logger adds itself: level, logId, and source on every entry, plus workflowId, serviceId, deploymentId, and environment when their environment variables are set, and traceId and spanId when the call runs inside a trace or span. See Log Format.

Context Object with a Message

logger.info({ userId: '123', action: 'login' }, 'User action completed');
// Result: { ..., "userId": "123", "action": "login", "message": "User action completed" }

Every property of the object is merged into the top level of the log entry.

A context object always needs a message alongside it. ILogger declares each level as (object, message) or (message) and nothing else, so logger.info({ userId: '123' }) does not compile.

Reversing the Order Drops Your Context

Passing the message first discards the object:

logger.info('User action completed', { userId: '123', action: 'login' });
// Result: { ..., "message": "User action completed" }
//         userId and action are gone. No error, no warning.

TypeScript rejects the reversed order, because info is declared only as (object, message) or (message). Running tsc reports Argument of type 'string' is not assignable to parameter of type 'object', so a build catches the mistake. Transpile-only runners such as tsx do not type-check, so under those the fields disappear at runtime with no error. Always put the object first.

Error Objects

error takes an object containing err plus a message:

logger.error({ err: new Error('Connection refused') }, 'Failed to sync order.');
// Result: { ...,
//           "err": { "type": "Error", "message": "Connection refused", "stack": "..." },
//           "message": "Failed to sync order." }

Errors are serialized by Pino's stdSerializers.err, which emits type, message, and stack. Unlike the other levels, error has no single-argument overload, so the message is required.

Redaction Masks

registerRedactionMasks replaces each registered string with [Redacted] wherever it appears in a log line: messages, fields, errors, and child-logger bindings. @ontemper/resources registers the secrets it resolves; register other secret values yourself:

import { logger, registerRedactionMasks } from 'unnbound-logger-sdk';

registerRedactionMasks([apiKey]);
const partnerLog = logger.child({ partner: 'acme', auth: `Bearer ${apiKey}` });
partnerLog.info('Calling partner.');
// Result: { ..., "partner": "acme", "auth": "Bearer [Redacted]", "message": "Calling partner." }
  • Masks replace text in string values only. A property name that contains a secret is written unchanged.
  • Masks apply to the finished line when it is written. A mask registered after child() or setBindings() still redacts those bindings, and the output of custom serializers and formatters is masked too.
  • A mask registered inside a request scope applies to the whole scope, and stays active for the rest of the process after the scope ends. The process keeps at most 4096 of these retained masks and drops the oldest first; masks held by an active scope are not capped.
  • A value that code transforms before logging (for example, Base64-encodes) no longer matches its mask.

Treat masking as a safeguard, not a guarantee: do not log secrets.

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:

  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 { 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 () => {
      logger.info({ filePath, contentLength: content.length }, 'Uploading file');
      return { success: true, filePath };
    },
    () => ({
      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) => {
  logger.info({ value }, 'Processing 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';

const operation = async (value: number) => {
  logger.info({ value }, 'Processing value');
  return value * 2;
};

const result = await startSpan('Processing operation', () => operation(21), () => ({
  operationType: 'calculation',
}));

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:

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

  await someAsyncWork();

  logger.info({ value }, 'Second step');
  return value * 2;
};

const result = await startSpan('Async operation', () => asyncOperation(21), () => ({
  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): 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

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

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
  • TEMPER_ENVIRONMENT, WORKFLOW_ENV, UNNBOUND_ENVIRONMENT, ENVIRONMENT - Environment name, read in that order (included in all logs). Temper injects TEMPER_ENVIRONMENT; the other names are legacy fallbacks.
  • LOG_LEVEL - Log level (default: 'debug')