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

bandeng-logger

v1.4.7

Published

Logger from bandeng developer team.

Downloads

418

Readme

Bandeng Logger

English | 中文


English

A powerful logging library from Bandeng developer team.

🚀 Version 1.4.7 updates (2026-06-25)

  1. OpenObserve ingest payload normalization (lib/openobserve_payload.js, reporter.normalizePayload, default true): HTTP reporter extracts configured fields (default rid) to the top level and puts the full second argument in rawData. Local file and console JSON are unchanged. Dual-arg logger.info('tag', {rid, ...}) needs no application changes.
  2. reporter.extractTopLevelFields (default ['rid'], max 10): Promote configured business fields from the second argument to ingest top level; full payload always remains in rawData.
  3. reporter.rawDataAsString (default false): By default rawData is a readable object-literal string (unquoted keys, single-quoted strings — not JSON). Set true to use JSON.stringify instead. Either form is a single string column so OpenObserve does not flatten nested objects.
  4. rawData reserved key: Business object args must not pass rawData (strip + console.warn, same as other OpenObserve reserved keys). rid remains a normal business field.
  5. destroy() / updateOptions() lifecycle: Drains logs queued during startup rotation checks before teardown, preventing empty server.log on fast shutdown or logDir change.

Installation

npm install bandeng-logger

Usage

const Logger = require('bandeng-logger')

Features

  • 📝 Multiple log levels (fatal, error, warn, info, debug, trace)
  • 🔄 Automatic log rotation by size and time
  • 🎨 Colorful console output with customizable colors
  • 📊 Built-in performance monitoring for slow logs (explicitly enabled)
  • 🎯 Force print conditions for selective debugging regardless of log level
  • 🔍 Multi-process support with process ID tracking and concurrent rotation safety
  • 📦 Log compression (gzip)
  • 🏷️ Child loggers for modular logging
  • ⚡ Zero-blocking buffered writing with synchronous semantics
  • 🕒 High-precision UTC timezone support with millisecond timestamps
  • 📈 Advanced log write performance monitoring with timeout protection (requires explicit enablement)
  • 📋 Support for both JSON and text log formats
  • 🔒 Configurable error log storage with custom level separation
  • 🕐 Customizable log rotation interval and buffer flush timing
  • 🛡️ Enhanced error handling with circular reference detection and recursion prevention
  • 🧹 Intelligent automatic file management and concurrent cleanup
  • 📁 Smart log file splitting with size limit enforcement
  • 🔧 Comprehensive process lifecycle management with signal handling
  • ✅ Advanced configuration validation with boundary checking
  • 🚀 Atomic file operations with concurrent safety
  • 📊 Real-time statistics and monitoring
  • 🔄 Intelligent buffer management with dynamic thresholds
  • 🔮 Modern JavaScript data type support (BigInt, Symbol, Map, Set, TypedArray, Promise)
  • 🛡️ Serialization depth protection and memory leak prevention
  • 🔒 Context content sanitization with length limits and security filtering
  • ⚙️ Runtime configuration updates with hot reloading
  • 📈 Advanced request performance tracking with context handling
  • 🔧 Customizable ctx field processing with injection functions
  • 🛡️ Resource leak prevention with automatic stream cleanup
  • 🚫 Path traversal protection with filename sanitization
  • 🔄 Intelligent retry mechanism with exponential backoff
  • 📊 Real-time memory usage monitoring and leak prevention
  • 🎯 Unified error handling strategy across all operations
  • 🌐 HTTP request logging with framework auto-detection
  • 🧠 Smart IP extraction supporting multiple proxy headers
  • 📡 OpenObserve HTTP JSON reporter with batch upload, retry, and circuit breaker (v1.4.0+); ingest payload normalization — rid + rawData (v1.4.7+)
  • 📘 Full TypeScript support with complete type definitions

Quick Start

Basic Usage

const Logger = require('bandeng-logger')
const logger = new Logger({logLevel: 'info', logFormat: 'text'})

logger.info('Info log')
logger.error('Error log')
logger.warn('Warning log')

const moduleLogger = logger.createChildLogger('UserModule')
moduleLogger.info('User login success')

TypeScript Usage

// CommonJS import (recommended for existing projects)
// Note: When package is published, use: const Logger = require('bandeng-logger')
const Logger = require('./index') // Use relative path during development

// Or use ES6 module import
// import Logger from 'bandeng-logger'

// Type definitions (manual declaration needed with CommonJS)
type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'
type LogFormat = 'json' | 'text'
type FrameworkStyle = 'koa' | 'express'

interface LoggerOptions {
    logLevel?: LogLevel
    logFormat?: LogFormat
    /** Service name for JSON logs (v1.4.2+) */
    source?: string
    logDir?: string
    rotate?: number
    maxFiles?: number
    traceLogWrite?: boolean
    /** OpenObserve HTTP reporter (v1.4.0+) */
    reporter?: {
        enabled?: boolean
        url?: string
        authorization?: string
        disableLocalFileWrite?: boolean
        /** Normalize OpenObserve ingest payload (v1.4.7+), default true */
        normalizePayload?: boolean
        /** rawData: default object-literal string; true for JSON.stringify (v1.4.7+) */
        rawDataAsString?: boolean
        extractTopLevelFields?: string[]
    }
}

// Complete type-safe configuration
const loggerOptions: LoggerOptions = {
    logLevel: 'info' as LogLevel,
    logFormat: 'json' as LogFormat,
    logDir: './logs'
}

// Create Logger instance
const logger = new Logger(loggerOptions)

// All methods have complete type hints and comments
logger.info('TypeScript info log')
logger.error('TypeScript error log', {error: 'Something went wrong'})
logger.debug('Debug with structured data', {userId: 123, action: 'login'})

// Static methods also have type hints
const ctxProcessor = Logger.getCtxProcessor('whitelist', ['userId', 'action'])

// Complete type safety and intelligent code completion support
const stats = logger.getStatistics()
const success = logger.updateOptions({logLevel: 'debug'})

Basic Configuration Examples

const Logger = require('bandeng-logger')

// Minimal configuration - uses all default values
const simpleLogger = new Logger()

// Or basic configuration - basic customizations
const basicLogger = new Logger({
    logLevel: 'info',
    logFormat: 'json'
})

Recommended Production Configuration

const Logger = require('bandeng-logger')
const config = require('./config.js')

// Create production-ready logger instance with essential customizations
const defaultLogger = new Logger({
    // Essential: Dynamic log level based on environment
    logLevel: config.isTest ? 'trace' : 'warn',
    // Essential: Dynamic log format based on environment
    logFormat: config.isTest ? 'text' : 'json',

    // Custom: Adjust log rotation size if 100MB is not suitable
    rotate: 20, // 20MB instead of default 100MB
    // Custom: Adjust rotation check interval if 1 hour is not suitable
    interval: 600, // Every 10 minutes instead of default 1 hour

    // Optional: Enable performance monitoring (disabled by default)
    traceLogWrite: config.enablePerformanceMonitoring || false,

    // Optional: Custom color scheme for different environments
    colors: {
        trace: 'gray',
        debug: 'blue',
        info: 'white',
        warn: config.isTest ? 'yellow' : 'white',
        error: 'red',
        fatal: 'magenta'
    },

    // Optional: Custom timezone (default is auto-detected)
    utcFormat: 8 // UTC+8 for timestamps

    // Note: HTTP request logging is enabled via method calls, no configuration needed
    // Use logger.logHttpRequest() to log HTTP requests

    // Note: The following use default values, no need to specify:
    // logDir: './logs' (default)
    // compress: 'none' (default)
    // maxFiles: 15 (default)
    // bufferSize: 65536 (default)
    // flushInterval: 1000 (default)
    // forceSyncLevels: ['fatal'] (default)
    // ctxProcessor: null (default)
})

// Export essential logging utilities
module.exports = {
    // Core logger instance
    logger: defaultLogger,

    // Essential methods for application use
    createChildLogger: defaultLogger.createChildLogger.bind(defaultLogger),
    traceRequestPerformance: defaultLogger.traceRequestPerformance.bind(defaultLogger),

    // HTTP request logging (recommended for production)
    logHttpRequest: defaultLogger.logHttpRequest.bind(defaultLogger),

    // Configuration management (for advanced use)
    getOptions: defaultLogger.getOptions.bind(defaultLogger),
    updateOptions: defaultLogger.updateOptions.bind(defaultLogger),

    // Statistics and monitoring (for operational use)
    getStatistics: defaultLogger.getStatistics.bind(defaultLogger),
    resetStatistics: defaultLogger.resetStatistics.bind(defaultLogger)
}

Parameter Usage Examples

// ✅ Single parameter - any type supported
logger.info('Simple message')
logger.debug(42)
logger.warn({error: 'Something went wrong', code: 500})

// ✅ Two parameters - first must be string, second can be any type
logger.info('User Login', {userId: 123, ip: '192.168.1.100'})
logger.debug('Database Query', 'SELECT * FROM users WHERE id = ?')

// ✅ Complex logging with structured data
logger.info('Payment Processed', {
    transactionId: 'txn_123456',
    amount: 99.99,
    currency: 'USD',
    userId: 456,
    timestamp: new Date(),
    metadata: {
        paymentMethod: 'credit_card',
        gateway: 'stripe'
    }
})

// ❌ Avoid - multiple separate parameters (extra parameters ignored)
logger.debug('Event', 'param1', 'param2', 'param3') // Only first 2 processed

// ❌ Avoid - first parameter not a string (warning issued)
logger.debug(123, 'This will generate a warning')

Parameter Passing Rules in Detail

Basic Parameter Rules

  • Maximum 2 parameters supported, extra parameters are ignored
  • 1 parameter: Supports any type, including strings, numbers, objects, arrays, etc.
  • 2 parameters: First parameter must be string type, otherwise warning is issued

How to Pass Multiple Values

When you need to log multiple related values, wrap them in an object as the second parameter:

// ✅ Recommended: Wrap multiple values in an object
logger.debug('User Login', {
    userId: 123,
    username: 'john_doe',
    ip: '192.168.1.100',
    timestamp: Date.now(),
    metadata: {source: 'web'}
})

// ❌ Avoid: Multiple separate parameters (will be ignored)
logger.debug('User Login', 'john_doe', 123, '192.168.1.100') // Only first 2 processed

// ✅ Single parameter with object
logger.debug({
    event: 'user_login',
    userId: 123,
    username: 'john_doe',
    ip: '192.168.1.100'
})

JSON Format Object Type Special Handling

Important Note: When using JSON format, the second parameter has special handling rules for Object types. Additionally, complex data types in the msg field include type information:

Expansion Rules

  • Object Expansion: When the second parameter is a plain object (not array, not null, not undefined), all its properties are directly expanded into the log object
  • Other Types Use msg Key: Arrays, strings, numbers, booleans, null, undefined and other types are assigned to the msg field

Examples

// ✅ Objects are expanded - properties appear at root level
logger.info('User Login', {userId: 123, ip: '192.168.1.100'})
// Output: {"time":"...", "level":"INFO", "logTag":"User Login", "userId":123, "ip":"192.168.1.100"}

// ✅ Arrays use msg key - preserve array structure
logger.debug('Data List', [1, 2, 3, 4])
// Output: {"time":"...", "level":"DEBUG", "logTag":"Data List", "msg":[1,2,3,4]}

// ✅ Strings use msg key
logger.warn('Warning Message', 'Disk space low')
// Output: {"time":"...", "level":"WARN", "logTag":"Warning Message", "msg":"Disk space low"}

// ✅ Numbers use msg key
logger.error('Error Code', 500)
// Output: {"time":"...", "level":"ERROR", "logTag":"Error Code", "msg":500}

Design Intent

This design enables:

  1. Structured Logging: Object expansion provides clearer field structure for log analysis and querying
  2. Backward Compatibility: Arrays and other types still use msg key for compatibility
  3. Type Safety: Prevents accidental property overwrites, maintaining clear log structure

Configuration Options

| Option | Type | Default | Description | | -------------------- | -------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | logLevel | String | 'warn' | Log level, options: fatal, error, warn, info, debug, trace | | logFormat | String | 'json' | Log format, options: json, text | | source | String | auto | Service name for JSON logs only (serialized as top-level logSource, sibling to pid). If omitted, derived from PM2 name / npm_package_name (without instance suffix); otherwise node. See JSON fixed fields below | | utcFormat | Number | auto | UTC timezone, range: -12 to 14. If not provided, uses server's current timezone | | maxLength | Number | 1000 | Maximum log message length, will be truncated if exceeded | | logDir | String | './logs' | Log directory (will be converted to absolute path automatically), will be created if not exists | | logFileName | String | 'server.log' | Main log filename, must end with .log extension | | errorLogFileName | String | 'error.log' | Error log filename, must end with .log extension, only effective when separateErrorLog is true | | separateErrorLog | Boolean | true | Whether to separate error logs into a different file | | separateErrorLevels | Array | ['error','fatal'] | Which log levels to separate into error file, customizable | | traceLogWrite | Boolean | false | Enable log write performance monitoring (disabled by default, must be explicitly set to true), logs exceeding 100ms will be recorded | | rotate | Number | 100 | Log rotation size in MB. Applies independently to the main log and, when separateErrorLog is true, the error log file | | interval | Number | 3600 | Log rotation check interval (seconds), min: 60, max: 604800 (7 days) | | compress | String | 'none' | Log compression method, options: none, gzip | | maxFiles | Number | 15 | Maximum number of log files to keep, oldest files will be deleted when exceeded | | bufferSize | Number | 65536 | Buffer size in bytes (64KB), min: 1024, max: 10MB | | flushInterval | Number | 1000 | Buffer flush interval (ms), min: 100, max: 60000 | | forceSyncLevels | Array | ['fatal'] | Log levels that should always be written synchronously. Configure as ['error', 'fatal'] to ensure ERROR and FATAL logs are immediately written to disk (useful for debugging and crash scenarios). | | forcePrintConditions | Array | [] | Array of condition functions for forced log printing regardless of log level, useful for business debugging | | ctxProcessor | Function | null | Custom ctx field processor ONLY for traceRequestPerformance method (designed for internal RPC communication), null uses default sanitizeContextForLogging | | colors | Object | see below | Log color configuration, supports custom colors for each level | | reporter | Object | see below | OpenObserve HTTP JSON reporter (v1.4.0+). Async batch upload with retry and circuit breaker. Requires logFormat: 'json'; disabled by default. See OpenObserve HTTP Reporter below for nested options |

JSON fixed fields (v1.4.2+ options.source, v1.4.3+ logTag, v1.4.5+ logSource; logFormat: 'json' only)

Core fields are emitted in this order: timelevellogSourceprocesspidlogTagmsgother business fields. Constructor option source maps to JSON logSource. logTag / logSource are library-managed — object payload must not supply tag/logTag/source/logSource (stripped with console.warn; see lib/openobserve_fields.js).

| Field | How set | Description | | ----------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | time | Auto | Log timestamp with millisecond precision. Timezone from options.utcFormat (default: server local offset). Format: YYYY-MM-DD HH:mm:ss.sss UTC±N. | | level | Auto | Uppercase level string: FATAL, ERROR, WARN, INFO, DEBUG, TRACE. | | logSource | options.source (optional) | Logical service name (no PM2 instance suffix). Default: PM2 name / npm_package_name, or node. Used for OpenObserve / monitoring aggregation. | | process | Auto | Runtime process label: {name}-{instanceId} (PM2 instance suffix included when present). Distinct from logSource. | | pid | Auto | Node.js process.pid of the writing process. | | logTag | Auto / call args | Two-arg: first string arg. Single-arg JSON: [eventName] inside process.on handlers (e.g. [SIGINT]), else [logger] no tag. Not taken from object payload (tag/logTag keys stripped). | | msg | Auto / call args | Primary message body when the payload is not fully expanded as root fields: second arg (non-plain-object), string/number/boolean/array, or msg key on object. Plain object single-arg logs expand properties to root and may omit msg. | | (others) | Expanded object fields | Additional keys from a plain-object argument (e.g. userId, stack) appear after msg in serialized order. |

const logger = new Logger({
    logFormat: 'json',
    source: 'api_server', // optional
    utcFormat: 8 // optional; affects `time` field
})

Color Configuration

| Level | Default Color | Description | | ----- | ------------- | ------------------------------ | | fatal | brightRed | Fatal errors, using bright red | | error | red | Errors, using red | | warn | brightYellow | Warnings, using bright yellow | | info | gray | Information, using gray | | debug | blue | Debug, using blue | | trace | white | Trace, using white |

Available Colors: black, red, green, yellow, blue, magenta, cyan, white, gray, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite

OpenObserve HTTP Reporter (v1.4.0+)

Nested options for the top-level reporter configuration object. Asynchronously ships JSON logs to OpenObserve via native http/https (Node 12+). Disabled by default; does not block local file or console output.

Requirements: logFormat: 'json' and reporter.enabled: true. Text format logs are not reported.

| Option | Type | Default | Description | | ---------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | reporter.enabled | Boolean | false | Enable HTTP reporter | | reporter.url | String | — | OpenObserve ingest URL (required when enabled) | | reporter.authorization | String | — | Full Authorization header value, e.g. Basic xxx (required when enabled) | | reporter.disableLocalFileWrite | Boolean | false | When enabled together with reporter, skip local file writes (console + HTTP only) | | reporter.timeout | Number | 10000 | Single HTTP request timeout (ms) | | reporter.retry.enabled | Boolean | false | Retry on timeout / network / 5xx errors | | reporter.retry.maxRetries | Number | 2 | Max extra retries (excluding first attempt) | | reporter.retry.baseDelay | Number | 500 | Base backoff delay (ms), exponential | | reporter.batch.maxSize | Number | 50 | Max logs per batch POST | | reporter.batch.flushInterval | Number | 1000 | Periodic flush interval (ms) | | reporter.batch.maxQueueSize | Number | 2000 | Queue cap; oldest entries dropped when full | | reporter.batch.maxInFlight | Number | 2 | Max concurrent HTTP requests | | reporter.circuitBreaker.failureThreshold | Number | 5 | Consecutive failures before circuit opens | | reporter.circuitBreaker.recoveryTimeout | Number | 60000 | Circuit recovery wait (ms) | | reporter.normalizePayload | Boolean | true | (v1.4.7+) Before HTTP POST, normalize ingest shape: top-level fields per extractTopLevelFields + rawData (see below). Local/console JSON unchanged. Set false to send formatter output as-is. | | reporter.extractTopLevelFields | String[] | ['rid'] | (v1.4.7+) Field names to promote from the second argument to ingest top level (max 10). Cannot include library fields (time, level, logSource, logTag, process, pid, rawData, …). Full payload always remains in rawData. | | reporter.rawDataAsString | Boolean | false | (v1.4.7+) false (default): rawData is an object-literal string (readable, not JSON). true: JSON.stringify. Both avoid OpenObserve flattening nested objects to rawData_* columns. |

const logger = new Logger({
    logLevel: 'info',
    logFormat: 'json',
    reporter: {
        enabled: true,
        url: 'http://host:5080/api/default/nodejs/_json',
        authorization: 'Basic <access-key>',
        timeout: 10000,
        disableLocalFileWrite: false, // set true for remote-only logging
        normalizePayload: true, // default; OpenObserve ingest only
        rawDataAsString: false, // default: object-literal string; true for JSON.stringify
        extractTopLevelFields: ['rid', 'path'], // max 10 fields
        retry: {enabled: true, maxRetries: 2},
        batch: {maxSize: 50, flushInterval: 1000, maxQueueSize: 2000}
    }
})

logger.info('user.login', {rid: '195629686594408451', userId: 123, ip: '192.168.1.100'})

OpenObserve ingest payload (v1.4.7+, normalizePayload: true default)

Dual-arg convention: first argument → logTag, second argument → business payload. File and console output keep the formatter JSON (business fields may be flattened at top level). HTTP reporter only rewrites the POST object to:

| Ingest field | Description | | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | time, level, logSource, process, pid, logTag | Same as local JSON | | rid | Extracted from the second argument when present (cross-service trace correlation) | | (custom) | Fields listed in reporter.extractTopLevelFields (default ['rid'], max 10) | | msg | Present only when the payload contains a non-object msg key and msg is not in extractTopLevelFields | | rawData | Full second argument as an object-literal string by default (rawDataAsString: false); keys unquoted, strings single-quoted (not JSON). Set rawDataAsString: true for JSON.stringify. Always one string column — OpenObserve will not keep a nested object. |

Example — application code:

logger.error('Middleware.Overall.Dynamic.checkId', {
    rid: '195629686594408451',
    path: '/',
    error: {code: 3004, msg: 'Invalid params', data: 'Invalid ID'},
    msg: 'catch error'
})

OpenObserve receives (simplified):

// POST body is a JSON array; one ingest object (rawData is a string column):
{
    time: '2026-06-24 20:02:31.907 UTC+8',
    level: 'ERROR',
    logSource: 'api_server',
    process: 'api_server-17',
    pid: 2090967,
    logTag: 'Middleware.Overall.Dynamic.checkId',
    rid: '195629686594408451',
    msg: 'catch error',
    rawData: "{rid: '195629686594408451', path: '/', error: {code: 3004, msg: 'Invalid params', data: 'Invalid ID'}, msg: 'catch error'}"
}

Notes:

  • POST body is a JSON array of log objects (normalized when normalizePayload: true).
  • Business object args must not include rawData (library-managed on ingest; stripped with console.warn like other reserved keys).
  • rid is not a reserved business key — pass it in the second argument as usual.
  • Reporter failures are logged via console.warn('[logger-reporter] ...') only — never through the logger itself (prevents recursion).
  • On destroy(), remaining queued logs are flushed before shutdown (including logs queued during startup rotation checks).
  • OpenObserve query tip: Configure ZO_FEATURE_QUICK_MODE_FIELDS with rid, rawData, logtag, time, process, pid, msg. Default object-literal rawData is human-readable in the UI; set rawDataAsString: true if you need JSON.parse in downstream tooling.

Log Levels

logger.fatal('Fatal error')
logger.error('Error')
logger.warn('Warning')
logger.info('Info')
logger.debug('Debug')
logger.trace('Trace')

Note: Business error objects (e.g., {code: 2005, msg: '', data: ''}) are fully supported. All properties are correctly serialized and logged, even when values are empty strings or null.

Advanced Features

Modern JavaScript Data Types Support

Logger fully supports modern JavaScript data types with intelligent serialization:

// BigInt support
logger.debug('BigInt value:', 123n) // Output: "123n"

// Symbol support
logger.debug('Symbol value:', Symbol('test')) // Output: "[Symbol: test]"

// Map and Set support
const myMap = new Map([
    ['key1', 'value1'],
    ['key2', 'value2']
])
const mySet = new Set([1, 2, 3, 'test'])

logger.debug('Map object:', myMap) // Output: {"[Map]": {"key1": "value1", "key2": "value2"}}
logger.debug('Set object:', mySet) // Output: {"[Set]": [1, 2, 3, "test"]}

// TypedArray support
const typedArray = new Uint8Array([1, 2, 3, 4, 5])
logger.debug('TypedArray:', typedArray) // Output: {"[TypedArray]": "Uint8Array", "length": 5}

// Promise support
const promise = Promise.resolve('test')
logger.debug('Promise:', promise) // Output: "[Promise]"

Performance Monitoring

Performance monitoring is disabled by default and must be explicitly enabled:

// Enable performance monitoring
const logger = new Logger({
    traceLogWrite: true // Explicitly enable performance monitoring
})

// Now performance monitoring is active for all log operations
// Note: Performance monitoring uses a default threshold of 100ms for log write operations
logger.debug('This log write will be monitored for performance')
// Recommended approach - advanced request performance tracking for internal RPC communication
// This method is specifically designed for tracking performance between internal services
logger.traceRequestPerformance(
    {
        source: 'client-1',
        path: '/api/users',
        userId: 123,
        method: 'GET',
        ip: '192.168.1.100'
    },
    150,
    100
) // 150ms latency, 100ms threshold

// Force print all requests regardless of log level (v1.3.4+)
logger.traceRequestPerformance(
    {
        source: 'client-1',
        path: '/api/users',
        userId: 123,
        method: 'GET'
    },
    50, // 50ms latency (below threshold)
    100, // 100ms threshold
    true // Force print: true = ignore log level, false = follow log level
)

// Enable force print for debugging all requests
logger.traceRequestPerformance(ctx, latency, threshold, true)

// Custom tag for filtering performance logs (v1.3.5+)
logger.traceRequestPerformance(
    {
        source: 'client-1',
        path: '/api/users',
        userId: 123
    },
    150,
    100,
    false, // forcePrint
    'api-performance' // custom tag
)
// Logs will include {"logTag":"api-performance"} for easy filtering

Context Content Sanitization

Logger automatically sanitizes context content for security and readability:

// Context content is automatically sanitized
logger.traceRequestPerformance(
    {
        userId: 123,
        requestData: 'A'.repeat(2000), // Long string will be truncated
        largeArray: Array(100).fill('item'), // Large array will be truncated
        sensitiveData: () => 'secret', // Functions will be filtered
        buffer: Buffer.from('data'), // Buffers will be filtered
        nested: {
            veryDeep: 'B'.repeat(1500), // Nested long strings also truncated
            complex: {
                data: new Map([['key', 'value']]) // Complex objects handled
            }
        }
    },
    150,
    100
)

// Output:
// - Strings longer than 1000 chars are truncated with "...[truncated X chars]"
// - Arrays larger than 50 items are truncated with "...[truncated X items]"
// - Functions and Buffers are replaced with "[function]" and "[Buffer]"
// - Complex objects are safely serialized

Automatic Error Logging

Logger automatically handles uncaughtException and unhandledRejection events, so applications using this package do not need to manually listen and handle these errors.

  • Automatic FATAL Logging for uncaughtException:

    • Logger automatically registers a handler for uncaughtException events
    • When an unhandled exception occurs, logger automatically generates a FATAL level log with tag [logger] Uncaught exception:
    • The log includes full stack trace information for quick error location
    • The process will exit after logging (as per Node.js default behavior)
  • Automatic ERROR Logging for unhandledRejection:

    • Logger automatically registers a handler for unhandledRejection events
    • When an unhandled promise rejection occurs, logger automatically generates an ERROR level log with tag [logger] Unhandled promise rejection:
    • The log includes stack trace information (extracted from Error objects or auto-generated)
    • The process continues running (does not exit)
  • Automatic FATAL Logging for process.exit:

    • Logger automatically registers a handler for process.exit events
    • When the process exits (via process.exit() or natural termination), logger automatically generates a FATAL level log with tag [logger] Process exit
    • The log includes the exit code and stack trace information (if available) to help locate where the exit was triggered
    • Uses synchronous file writing to ensure the log is written before the process terminates

Note: Since logger handles these events automatically, you don't need to manually add process.on('uncaughtException'), process.on('unhandledRejection'), or process.on('exit') handlers in your application code. The logger will capture and log these events automatically.

Log File Splitting Mechanism

Logger implements a concurrent-safe log file splitting mechanism that automatically splits large log files when they exceed the configured size limit. This is especially important in PM2 multi-process environments where multiple processes write to the same log file.

How It Works:

  1. Timer-Based Periodic Check: Logger uses a timer-based mechanism (not real-time file size monitoring). A periodic check is scheduled using setInterval that runs every interval seconds (default: 3600 seconds / 1 hour, configurable via interval option). The check also runs immediately when the logger is initialized.

  2. Size-Based Trigger: During each periodic check, the logger examines the log file size. Splitting is only triggered when the file size exceeds the configured limit (default: 100MB, configurable via rotate option). If the file size is below the threshold, no splitting occurs even though the check runs.

  3. Concurrent-Safe Locking:

    • Uses a lock file (.splitting.lock) with exclusive write mode (wx) to ensure only one process performs splitting at a time
    • If the lock file exists, other processes detect it and skip splitting (prevents race conditions)
    • Lock file is automatically released in finally block, even if errors occur
    • Stale Lock Detection: Automatically detects and removes stale lock files (e.g., when a process crashes). The system checks:
      • Lock file age: If the lock file is older than 5 minutes, it's considered stale
      • Process existence: Checks if the process ID (PID) stored in the lock file is still running
      • Automatic cleanup: Stale locks are automatically removed before attempting to acquire a new lock
  4. Double-Check Pattern:

    • After acquiring the lock, re-checks file existence and size
    • If file no longer exists or size is below threshold, releases lock and skips splitting
    • Prevents duplicate splitting when multiple processes detect the need simultaneously
  5. File Splitting Process:

    • Reads the entire log file into memory
    • Splits content into multiple files, each not exceeding 90% of the size limit (safety margin)
    • Creates split files with timestamp-based names: YYYY-MM-DD_HH-MM-SS-N-server.log
    • Closes file descriptors before deleting the original file
    • Reinitializes file descriptors for continued logging
  6. Old File Cleanup:

    • After splitting, checks total number of log files
    • Deletes oldest files if count exceeds maxFiles limit (default: 15)
    • Handles concurrent deletion attempts gracefully (detects if file already deleted)

Example Scenario (PM2 with 4 processes):

Time: 18:09:15.890
Process 1: Detects file size exceeded → Acquires lock → Starts splitting
Process 2: Detects file size exceeded → Tries to acquire lock → Fails (EEXIST) → Skips
Process 3: Detects file size exceeded → Tries to acquire lock → Fails (EEXIST) → Skips
Process 4: Detects file size exceeded → Tries to acquire lock → Fails (EEXIST) → Skips

Time: 18:09:17.267
Process 1: Completes splitting → Releases lock → Creates 4 split files
Process 2-4: Continue normal logging operations

Configuration:

const logger = new Logger({
    rotate: 100, // Split when file exceeds 100MB (checked periodically)
    interval: 3600, // Check every 1 hour (3600 seconds) - timer interval
    maxFiles: 15 // Keep maximum 15 log files
})

Important Notes:

  • Timer-Based: File splitting is not triggered in real-time when the file size limit is exceeded. It only happens during the periodic check (default: every 1 hour).
  • 📏 Size Threshold: The rotate option sets the size limit, but splitting only occurs when the periodic check detects the file has exceeded this limit.
  • Initial Check: An immediate check runs when the logger initializes, so splitting can occur right away if the file already exceeds the limit.
  • 🔄 Check Frequency: For high-volume logging scenarios, consider reducing the interval value (minimum: 60 seconds) to check more frequently, but be aware this increases CPU usage.

Benefits:

  • Concurrent Safety: Prevents multiple processes from splitting simultaneously
  • Data Integrity: Ensures log files are not corrupted during splitting
  • Performance: Only one process performs the expensive splitting operation
  • Automatic Cleanup: Old files are automatically removed to save disk space
  • Error Recovery: Lock is always released, even if splitting fails
  • Stale Lock Recovery: Automatically detects and removes stale locks from crashed processes, ensuring splitting continues to work even after process failures

Runtime Configuration Updates

// Update configuration at runtime (hot reloading)
const success = logger.updateOptions({
    logLevel: 'debug', // Change log level
    maxLength: 2000, // Increase max message length
    traceLogWrite: true // Enable performance monitoring
})

if (success) {
    console.log('Configuration updated successfully')
}

// Get current configuration
const currentConfig = logger.getOptions()
console.log('Current configuration:', currentConfig)

Statistics and Monitoring

// Get real-time statistics and monitoring information
const stats = logger.getStatistics()
console.log('Logger Statistics:', stats)
// Output includes: totalLogs (as string), flushedLogs (as string), buffer stats, etc.
// Note: Large numbers are returned as strings to avoid JavaScript number precision limits

// Reset all statistics counters to zero
logger.resetStatistics()
console.log('Statistics have been reset')

Log Reporting

Ship structured JSON logs to OpenObserve over HTTP. Uses Node native http/https (compatible with Node 12~20), async batch upload, optional timeout retry, concurrency limits, queue cap with drop-oldest, and circuit breaker protection.

const logger = new Logger({
    logFormat: 'json', // required for reporter
    reporter: {
        enabled: true,
        url: 'http://host:5080/logtracer/api/default/nodejs/_json',
        authorization: 'Basic <access-key>',
        disableLocalFileWrite: false
    }
})
  • enabled: Whether to enable log reporting.
  • url: Log reporting server URL.
  • authorization: Authorization string for HTTP headers.
  • disableLocalFileWrite: Optional remote-only mode — when reporter.enabled and this flag are both true, local file writes (sync writes, buffers, rotation timers) are skipped; console output and HTTP reporting remain active. Default is false (dual write: local file + remote).
  • normalizePayload (v1.4.7+, default true): Rewrites HTTP ingest to top-level fields per extractTopLevelFields + rawData (full second argument). Does not change local file or console JSON.
  • extractTopLevelFields (v1.4.7+, default ['rid'], max 10): Business field names to promote to ingest top level.
  • rawDataAsString (v1.4.7+, default false): Object-literal string by default; true for JSON.stringify.

Usage Examples

HTTP Request Logging

Koa Framework Usage
const logger = new Logger({utcFormat: 8})

// Basic middleware usage (high-precision timing)
app.use(async (ctx, next) => {
    // Use high-precision timer
    const start = process.hrtime()
    await next()
    // Calculate and format precise response time (milliseconds, max 3 decimal places)
    // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1
    const [seconds, nanoseconds] = process.hrtime(start)
    let responseTime = seconds * 1000 + nanoseconds / 1000000
    // Format: max 3 decimal places, remove trailing zeros
    responseTime = parseFloat(responseTime.toFixed(3))

    // Automatically parse all information from ctx
    logger.logHttpRequest(ctx, {
        frameworkStyle: 'koa', // Required: must specify framework type
        responseTime: responseTime
    })
})

// With extra parameters (high-precision timing)
app.use(async (ctx, next) => {
    // Use high-precision timer
    const start = process.hrtime()
    await next()
    // Calculate and format precise response time (milliseconds, max 3 decimal places)
    // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1
    const [seconds, nanoseconds] = process.hrtime(start)
    let responseTime = seconds * 1000 + nanoseconds / 1000000
    // Format: max 3 decimal places, remove trailing zeros
    responseTime = parseFloat(responseTime.toFixed(3))

    // Log user information and request details
    logger.logHttpRequest(ctx, {
        frameworkStyle: 'koa', // Required: must specify framework type
        responseTime: responseTime,
        extraParams: [`userId:${ctx.state.user?.id || 'anonymous'}`, `userAgent:${ctx.get('User-Agent')}`]
    })
})
Express Framework Usage
const logger = new Logger({utcFormat: 8})

// Basic middleware usage (high-precision timing)
app.use((req, res, next) => {
    // Use high-precision timer
    const start = process.hrtime()

    res.on('finish', () => {
        // Calculate precise response time (milliseconds, max 3 decimal places)
        // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1
        const [seconds, nanoseconds] = process.hrtime(start)
        const responseTime = seconds * 1000 + nanoseconds / 1000000
        // Format: max 3 decimal places, remove trailing zeros
        responseTime = parseFloat(responseTime.toFixed(3))

        // Use logHttpRequest to log HTTP request information
        logger.logHttpRequest(
            {req, res},
            {
                frameworkStyle: 'express',
                responseTime: responseTime
            }
        )
    })

    next()
})

// With extra parameters (high-precision timing)
app.use((req, res, next) => {
    // Use high-precision timer
    const start = process.hrtime()

    res.on('finish', () => {
        // Calculate precise response time (milliseconds, max 3 decimal places)
        // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1
        const [seconds, nanoseconds] = process.hrtime(start)
        const responseTime = seconds * 1000 + nanoseconds / 1000000
        // Format: max 3 decimal places, remove trailing zeros
        responseTime = parseFloat(responseTime.toFixed(3))

        logger.logHttpRequest(
            {req, res},
            {
                frameworkStyle: 'express', // Required: must specify framework type
                responseTime: responseTime,
                extraParams: [`userId:${req.user?.id || 'anonymous'}`, `sessionId:${req.session?.id || 'none'}`]
            }
        )
    })

    next()
})
Output Examples
2025-09-20 17:53:01.113 UTC+8 GET /api/users 200 125.68 ms 192.168.1.100 - 12345 [userId:123]
2025-09-20 17:53:01.114 UTC+8 POST /api/login 401 234.57 ms 10.0.0.50 - 12345 [userId:anonymous]
2025-09-20 17:53:01.115 UTC+8 PUT /api/user 200 100 ms 192.168.1.100 - 12345

Format explanation:

  • Timestamp: UTC+8 timezone, high-precision milliseconds
  • Request method: HTTP method
  • Path: Request path
  • Status code: HTTP response status code
  • Response time: milliseconds (fixed unit), 3 decimal places precision
  • IP address: Real client IP
  • Process ID: Node.js process ID
  • Extra parameters: [Optional] User ID and other information

Error Handling

// Note: Logger automatically handles uncaughtException and unhandledRejection events
// You don't need to manually register these handlers - logger does it automatically

// Business error objects (e.g., {code, msg, data}) are fully supported
logger.error('Controller.RealTime.pay', {
    error: {code: 2005, msg: 'Order not found', data: 'order_id: 12345'},
    data: {type: '2', order_id: '123'},
    msg: 'catch error'
})
// All properties (code, msg, data) are correctly serialized and logged

Database Logging

const dbLogger = logger.createChildLogger('Database')
async function queryDatabase(sql, params) {
    try {
        const start = Date.now()
        const result = await db.query(sql, params)
        const duration = Date.now() - start
        dbLogger.info({sql, params, duration: `${duration}ms`, rows: result.length})
        return result
    } catch (error) {
        dbLogger.error('Database query error', {sql, params, error: error.message})
        throw error
    }
}

Custom Ctx Processing

You can customize how traceRequestPerformance processes the ctx parameter by providing a custom processor function. This is particularly useful for internal RPC communication between services where you need specific context filtering:

const Logger = require('bandeng-logger')

// 1. Using predefined processors
const logger1 = new Logger({
    logLevel: 'info',
    ctxProcessor: Logger.getCtxProcessor('whitelist', ['userId', 'ip', 'method'])
})

// 2. Using whitelist with nested field paths (supports dot notation)
const loggerNested = new Logger({
    logLevel: 'info',
    ctxProcessor: Logger.getCtxProcessor('whitelist', ['user.id', 'user.name', 'request.method'])
})

// 3. Using blacklist to exclude sensitive fields (supports nested paths)
const logger2 = new Logger({
    logLevel: 'info',
    ctxProcessor: Logger.getCtxProcessor('blacklist', ['user.password', 'auth.token', 'sensitive.data'])
})

// 4. Field renaming
const logger3 = new Logger({
    logLevel: 'info',
    ctxProcessor: Logger.getCtxProcessor('rename', {
        userId: 'user_id',
        sessionId: 'session_id'
    })
})

// 4. Complex transformation
const logger4 = new Logger({
    logLevel: 'info',
    ctxProcessor: Logger.getCtxProcessor('transform', {
        include: ['userId', 'ip', 'headers'],
        exclude: ['authorization'],
        rename: {
            userId: 'user_id',
            headers: 'request_headers'
        }
    })
})

// 5. Custom processor function
const logger5 = new Logger({
    logLevel: 'info',
    ctxProcessor: ctx => {
        // Custom processing logic
        const result = {}
        for (const [key, value] of Object.entries(ctx)) {
            if (typeof value === 'string' && key !== 'sensitive') {
                result[key] = value.toUpperCase()
            }
        }
        return result
    }
})

// Usage example
const requestCtx = {
    userId: 'user123',
    ip: '192.168.1.100',
    method: 'POST',
    password: 'secret',
    headers: {'content-type': 'application/json'}
}

logger1.traceRequestPerformance(requestCtx, 150, 100) // Only logs userId, ip, method
logger2.traceRequestPerformance(requestCtx, 150, 100) // Excludes password field

Nested Field Path Support:

Both whitelist and blacklist processors support nested field paths using dot notation:

  • 'user.id' - matches ctx.user.id
  • 'request.headers.authorization' - matches ctx.request.headers.authorization
  • 'data.items[0].name' - matches ctx.data.items[0].name

Examples with nested paths:

// Include nested user fields only
const userLogger = new Logger({
    ctxProcessor: Logger.getCtxProcessor('whitelist', ['user.id', 'user.name', 'user.email'])
})

// Exclude nested sensitive data
const secureLogger = new Logger({
    ctxProcessor: Logger.getCtxProcessor('blacklist', ['user.password', 'auth.token', 'payment.cardNumber'])
})

Force Print Conditions (v1.3.0)

Force print conditions allow you to override log level restrictions for specific logs that match certain criteria. This is extremely useful for business debugging and troubleshooting in production environments.

Key Features:

  • ✅ Override log levels - logs matching conditions print regardless of configured log level
  • ✅ Function-based conditions - support complex logic and multiple criteria
  • ✅ Context information - access to level, message, and timestamp
  • ✅ Error-safe - individual condition failures don't break logging
  • ✅ Performance optimized - conditions only evaluated when log level filtering fails

Basic Usage:

const logger = new Logger({
    logLevel: 'error', // Only error+ levels normally print
    forcePrintConditions: [
        // Force print all logs containing 'payment'
        logData => logData.message.includes('payment'),
        // Force print all debug level login-related logs
        logData => logData.level === 'debug' && logData.message.includes('login')
    ]
})

// Usage examples:
logger.error('Payment processing failed') // ✅ Prints (level >= error)
logger.debug('User payment successful') // ✅ Prints (matches 'payment' condition)
logger.debug('User login successful') // ✅ Prints (debug + login condition)
logger.debug('Normal debug message') // ❌ Doesn't print (no matching condition)
logger.info('Normal info message') // ❌ Doesn't print (level < error)

Advanced Examples:

1. Time-window Based Conditions:

const logger = new Logger({
    logLevel: 'warn',
    forcePrintConditions: [
        // Force print all errors in the last 5 minutes
        logData => logData.level === 'error' && logData.timestamp > Date.now() - 5 * 60 * 1000,
        // Force print payment logs for next 1 hour
        logData => logData.message.includes('payment') && logData.timestamp < Date.now() + 60 * 60 * 1000
    ]
})

2. Complex Business Logic:

const logger = new Logger({
    logLevel: 'error',
    forcePrintConditions: [
        // Force print user authentication failures
        logData => logData.level === 'warn' && logData.message.match(/login.*fail|auth.*error/i),
        // Force print high-value transaction logs
        logData => {
            const msg = logData.message.toLowerCase()
            return msg.includes('transaction') && (msg.includes('amount:') || msg.includes('$'))
        },
        // Force print database errors with specific patterns
        logData => logData.level === 'error' && logData.message.includes('database') && logData.message.includes('timeout')
    ]
})

3. Environment-Based Conditions:

const isProduction = process.env.NODE_ENV === 'production'
const debugUserIds = process.env.DEBUG_USER_IDS?.split(',') || []

const logger = new Logger({
    logLevel: isProduction ? 'warn' : 'debug',
    forcePrintConditions: [
        // In production, force print logs for specific debug users
        ...(isProduction
            ? [
                  logData => {
                      const userIdMatch = logData.message.match(/user[_\s]+(\d+)/i)
                      return userIdMatch && debugUserIds.includes(userIdMatch[1])
                  }
              ]
            : [])
    ]
})

4. Performance Monitoring:

const logger = new Logger({
    logLevel: 'info',
    forcePrintConditions: [
        // Force print slow operations regardless of level
        logData => {
            const durationMatch = logData.message.match(/(\d+)ms/i)
            return durationMatch && parseInt(durationMatch[1]) > 1000 // > 1 second
        },
        // Force print memory warnings
        logData => logData.message.includes('memory') && logData.level === 'warn'
    ]
})

5. Service-Specific Debugging:

const logger = new Logger({
    logLevel: 'error',
    forcePrintConditions: [
        // Debug payment service issues
        logData => logData.message.includes('payment-service') && ['error', 'warn'].includes(logData.level),
        // Debug user session problems
        logData => logData.message.match(/session.*(?:expired|invalid|timeout)/i),
        // Debug API rate limiting
        logData => logData.message.includes('rate limit') || logData.message.includes('429')
    ]
})

Condition Function Parameters:

Each condition function receives a logData object with:

  • level: Log level ('fatal', 'error', 'warn', 'info', 'debug', 'trace')
  • message: Log message string
  • timestamp: Log timestamp (Date.now() format)

Best Practices:

  1. Keep conditions simple - Complex conditions can impact performance
  2. Use specific patterns - Avoid overly broad conditions that print too much
  3. Time-limit conditions - Use timestamps to automatically disable debug logging
  4. Monitor impact - Force printing can generate significant log volume
  5. Clean up - Remove or disable conditions when debugging is complete

Performance Notes:

  • Conditions are only evaluated when log level filtering would normally suppress the log
  • Failed condition functions are safely caught and don't break logging
  • Use efficient string operations (includes, startsWith) over complex regex when possible

Comparison with Other Logging Libraries

| Feature | Bandeng Logger | Winston | Pino | Log4js | Bunyan | | ----------------------------- | -------------- | ----------- | ----------- | ----------- | ----------- | | Log Rotation | ✅ Built-in | ❌ Plugin | ❌ Plugin | ✅ Built-in | ❌ Plugin | | Performance Monitor | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | PM2 Support | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Zero-blocking I/O | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | High-precision Timestamps | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Concurrent Safety | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Real-time Statistics | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Smart Buffering | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Child logger | ✅ Built-in | ✅ Built-in | ✅ Built-in | ✅ Built-in | ✅ Built-in | | Color Console Output | ✅ Built-in | ❌ Plugin | ❌ Plugin | ✅ Built-in | ❌ Plugin | | Log Compression | ✅ Built-in | ❌ Plugin | ❌ Plugin | ✅ Built-in | ❌ Plugin | | UTC Timezone Support | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Error Log Separation | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin | | Log Write Performance Monitor | ✅ Built-in | ❌ Plugin | ❌ Plugin | ❌ Plugin | ❌ Plugin |

API Reference

Logger Methods

  • fatal(message, ...args) - Log fatal error
  • error(message, ...args) - Log error
  • warn(message, ...args) - Log warning
  • info(message, ...args) - Log info
  • debug(message, ...args) - Log debug
  • trace(message, ...args) - Log trace

Child Logger Methods

  • createChildLogger(moduleName) - Create a child logger with module name prefix

Performance Monitoring Methods

  • traceRequestPerformance(ctx, latency, timeThreshold, [forcePrint], [tag]) - Advanced request performance tracking for internal RPC communication between services, with context sanitization and custom tags
  • Logger.getCtxProcessor(type, options) - Get predefined ctx processing functions for traceRequestPerformance
Ctx Processor Types
  • whitelist - Only include specified fields
  • blacklist - Exclude specified fields
  • rename - Rename fields using mapping object
  • transform - Complex transformation with include/