@jshow/logger
v1.0.11
Published
<p align="center"> <a href="https://jshow.org" target="_blank"> <img width="100" src="https://jshow.org/images/jshow.png" alt="jShow logo" /> </a> </p> <h1 align="center">@jshow/logger</h1>
Readme
Introduction
@jshow/logger is a powerful, flexible, and feature-rich logging library for TypeScript/JavaScript applications. It provides a clean API for structured logging with support for namespaces, tags, extra information, and customizable output formats.
Features
- Multiple Log Levels: Support for
error,warn,info, anddebuglevels - Namespace Support: Organize logs by module or feature using hierarchical namespaces
- Tags & Extra Info: Add structured metadata to logs for better filtering and analysis
- Color Support: Beautiful colored output in both Node.js (ANSI) and Browser (CSS) environments
- Flexible Output: Support for both text and JSON output formats
- Highly Configurable: Custom transformers, filters, and hooks for advanced use cases
- Hooks Support: Execute custom logic after log output (e.g., send to monitoring systems)
- Filtering: Built-in support for filtering logs by namespace using
DEBUG_IGNOREenvironment variable - TypeScript: Full TypeScript support with comprehensive type definitions
- Cross-Platform: Works in both Node.js and Browser environments
- Lightweight: Minimal dependencies, optimized for performance
Supporting
jShow is an MIT-Licensed open source project with its ongoing development made possible entirely by the support of these awesome backers. If you'd like to join them, please consider:
What's the difference between Patreon and OpenCollective?
Funds donated via Patreon go directly to support @jshow/logger You's full-time work on jShow. Funds donated via OpenCollective are managed with transparent expenses and will be used for compensating work and expenses for core team members or sponsoring community events. Your name/logo will receive proper recognition and exposure by donating on either platform.
Installation
npm install @jshow/logger
# or
pnpm add @jshow/logger
# or
yarn add @jshow/loggerRequirements
- Node.js >= 14 (for Node.js environment)
- Modern browsers with ES6+ support (for browser environment)
- TypeScript >= 4.0 (optional, for TypeScript projects)
Quick Start
import { configure, logger } from '@jshow/logger';
// Configure the logger (only need to call once at application startup)
configure();
// Use different log levels
logger.error('This is an error message');
logger.warn('This is a warning message');
logger.info('This is an info message');
logger.debug('This is a debug message');Basic Usage
Log Levels
@jshow/logger supports four log levels, ordered by severity from high to low:
error- Error levelwarn- Warning levelinfo- Info level (default)debug- Debug level
logger.error('Error message');
logger.warn('Warning message');
logger.info('Info message');
logger.debug('Debug message');Namespace
Use namespaces to organize and categorize logs:
// Create a logger with namespace
const appLogger = logger.fork({ namespace: 'app' });
appLogger.info('Application started');
const dbLogger = logger.fork({ namespace: 'database' });
dbLogger.info('Database connected');
// Nested namespaces
const apiLogger = logger.fork({ namespace: 'api' });
const userApiLogger = apiLogger.fork({ namespace: 'user' });
userApiLogger.info('Get user information');Scope
Use the scope method to execute code in a specific context:
logger.scope({ namespace: 'request' }, log => {
log.info('Request processing started');
log.info('Processing request...');
log.info('Request processing completed');
});Raw Write
If you want a "stream-like" write (bypassing level selection but still applying filter/hook), use logger.write(XX).
logger.write('hello');
logger.write('world\n'); // You need to manually add the newline character at the endAdvanced Features
Tags
Use tags to categorize and filter logs:
const taggedLogger = logger.fork({
namespace: 'payment',
tags: { module: 'payment', version: '1.0.0' }
});
taggedLogger.info('Processing payment request', { amount: 100, currency: 'USD' });Extra Information
Use extra information to add structured data:
const loggerWithExtra = logger.fork({
namespace: 'api',
extra: { requestId: 'req-123', userId: 'user-456' }
});
loggerWithExtra.info('API request processing');Dynamic Log Level
Each logger instance can have its own log level settings, which work together with the global log level. You can dynamically change the log level for a specific logger instance without affecting other loggers.
Using setLevel()
setLevel() sets a threshold level. Only logs with severity equal to or higher than the threshold will be output. The severity order is: error > warn > info > debug.
const debugLogger = logger.fork({ namespace: 'debug' });
debugLogger.setLevel('debug');
debugLogger.debug('This debug message will be shown'); // ✓ Output
debugLogger.info('This info message will be shown'); // ✓ Output
debugLogger.warn('This warn message will be shown'); // ✓ Output
debugLogger.error('This error message will be shown'); // ✓ Output
debugLogger.setLevel('info');
debugLogger.debug('This debug message will NOT be shown'); // ✗ Filtered
debugLogger.info('This info message will be shown'); // ✓ Output
debugLogger.warn('This warn message will be shown'); // ✓ Output
debugLogger.error('This error message will be shown'); // ✓ Output
debugLogger.setLevel('error');
debugLogger.debug('This debug message will NOT be shown'); // ✗ Filtered
debugLogger.info('This info message will NOT be shown'); // ✗ Filtered
debugLogger.warn('This warn message will NOT be shown'); // ✗ Filtered
debugLogger.error('This error message will be shown'); // ✓ OutputUsing setLevels()
setLevels() sets a whitelist of allowed log levels. Only logs with levels in the whitelist will be output.
const customLogger = logger.fork({ namespace: 'custom' });
customLogger.setLevels('info', 'error');
customLogger.debug('This debug message will NOT be shown'); // ✗ Not in whitelist
customLogger.info('This info message will be shown'); // ✓ In whitelist
customLogger.warn('This warn message will NOT be shown'); // ✗ Not in whitelist
customLogger.error('This error message will be shown'); // ✓ In whitelist
// Clear the whitelist (use all levels)
customLogger.setLevels();
customLogger.debug('This debug message will be shown'); // ✓ Output (if global level allows)Interaction with Global Log Level
Instance-level log level settings work together with global log level settings. Both conditions must be satisfied for a log to be output:
import { setLogLevel, logger } from '@jshow/logger';
// Set global log level to 'warn'
setLogLevel('warn');
const appLogger = logger.fork({ namespace: 'app' });
appLogger.setLevel('debug'); // Instance level: debug
// Even though instance level is 'debug', global level is 'warn'
appLogger.debug('This will NOT be shown'); // ✗ Filtered by global level
appLogger.info('This will NOT be shown'); // ✗ Filtered by global level
appLogger.warn('This will be shown'); // ✓ Both levels allow
appLogger.error('This will be shown'); // ✓ Both levels allowInheritance in Forked Loggers
When you create a child logger using fork(), the child logger inherits the parent's log level settings:
const parentLogger = logger.fork({ namespace: 'parent' });
parentLogger.setLevel('info');
const childLogger = parentLogger.fork({ namespace: 'child' });
// childLogger inherits the 'info' level from parentLogger
childLogger.debug('This will NOT be shown'); // ✗ Filtered
childLogger.info('This will be shown'); // ✓ Output
// You can override the level for the child logger
childLogger.setLevel('debug');
childLogger.debug('This will be shown'); // ✓ Output (if global level allows)Resetting Log Level
To reset a logger instance to use only the global log level, call setLevels() with no arguments to clear the instance-level whitelist:
const testLogger = logger.fork({ namespace: 'test' });
testLogger.setLevel('debug');
// ... use logger ...
// Reset to use global level only (clear instance-level settings)
testLogger.setLevels(); // Clear the whitelist, now uses global level onlyConfiguration
Basic Configuration
import { configure, LoggerFactoryOfConsole } from '@jshow/logger';
configure({
createCoreLogger: LoggerFactoryOfConsole,
config: {
format: 'text', // 'text' or 'json'
enableNamespacePrefix: true,
enableNamespacePrefixColors: true,
appendTagsForTextPrint: true,
appendExtraForTextPrint: true
}
});JSON Format
configure({
config: {
format: 'json'
}
});Custom Transformers
configure({
config: {
format: 'text',
transformTagsForTextPrint: (tags, context) => {
return `[Tags: ${Object.keys(tags).join(', ')}]`;
},
transformExtraForTextPrint: (extra, context) => {
return `[Extra: ${JSON.stringify(extra)}]`;
}
}
});Filter
Use filters to control which logs are output:
configure({
config: {
filter: (namespace, tags) => {
// Only show logs from production environment
return tags.env === 'production';
}
}
});Hook
Use hook functions to execute custom logic after log output:
configure({
config: {
hook: (level, context, ...messages) => {
if (level === 'error') {
// Send errors to monitoring system
sendToMonitoring(level, context, messages);
}
}
}
});Global Log Level
Set global log level:
import { setLogLevel, setLogLevels } from '@jshow/logger';
// Set a single log level threshold
setLogLevel('debug'); // Show all log levels
setLogLevel('info'); // Show only info, warn, error
setLogLevel('warn'); // Show only warn, error
setLogLevel('error'); // Show only error
// Set multiple allowed log levels
setLogLevels('debug', 'info', 'warn', 'error'); // Show all log levels
setLogLevels('info', 'warn', 'error'); // Show only info, warn, error
setLogLevels('warn', 'error'); // Show only warn, error
setLogLevels('error'); // Show only erroruseLogger Hook
Use the useLogger hook to automatically create loggers with namespaces extracted from context:
import { useLogger } from '@jshow/logger';
// Use string as namespace
const logger = useLogger('UserService');
logger.info('User created'); // Output: [UserService] User created
// Use class as context
class UserService {
constructor() {
this.logger = useLogger(this);
}
}
// logger namespace will be 'UserService'
// Use function as context
function handleRequest() {
const logger = useLogger(handleRequest);
logger.debug('Processing request');
}
// logger namespace will be 'handleRequest'Filtering Logs with DEBUG_IGNORE
You can filter out specific namespaces using the DEBUG_IGNORE environment variable:
# Ignore logs from UserService and ApiClient namespaces
DEBUG_IGNORE=UserService,ApiClient node app.jsWhen a namespace is in the ignore list, useLogger returns a no-op logger that won't output anything.
Color Module
The Color module provides utilities for color manipulation and display:
import { Color } from '@jshow/logger';
// Generate color from text
const color = Color.makeColorHexFromText('error'); // Returns [r, g, b]
// Invert color
const inverted = Color.invertHex([255, 128, 64]);
// Check if a color is "bright enough"
// Note: the function name is historical; it returns true when the color is bright.
const isBright = Color.isDarkColor([50, 50, 50]);
// Optimize color for better log display
const optimized = Color.betterLogColor([255, 0, 0]);
// Wrap text with ANSI colors (Node.js)
const ansiText = Color.wrapColorANSI('Hello', {
contentColor: [255, 0, 0],
backgroundColor: [0, 0, 0]
});
// Wrap text with CSS colors (Browser)
const [cssContent, cssStyle] = Color.wrapColorCSS('Hello', {
contentColor: [255, 0, 0],
backgroundColor: [0, 0, 0]
});API Reference
Logger Methods
logger.error(...msg: unknown[])
Log error level messages
logger.warn(...msg: unknown[])
Log warning level messages
logger.info(...msg: unknown[])
Log info level messages
logger.debug(...msg: unknown[])
Log debug level messages
logger.write(msg: string)
Write output with "raw write" semantics (Node: stdout.write, Browser: fallback to info)
logger.fork(context: LoggerSubContext)
Create a new child logger instance
logger.scope(context: LoggerSubContext, fn: (logger: Logger) => void)
Execute callback function in specified context
logger.setLevel(level: LogLevel)
Set the output level for current logger
logger.setLevels(...levels: LogLevel[])
Set multiple allowed log levels for current logger
useLogger(ctx: string | Function | object)
Create a logger instance with namespace extracted from context. Returns a no-op logger if namespace is in DEBUG_IGNORE environment variable.
setLoggerIgnore(ignore: string)
Programmatically set the list of namespaces to ignore. Takes a comma-separated string of namespace names (case-insensitive).
import { setLoggerIgnore } from '@jshow/logger';
// Ignore logs from UserService and ApiClient namespaces
setLoggerIgnore('UserService,ApiClient');Functions
configure(options?)
Configure the logger. Can only be called once. If called multiple times, throws an error.
import { configure, LoggerFactoryOfConsole } from '@jshow/logger';
// Use default console logger with default config
configure();
// Use default console logger with custom config
configure({
config: {
format: 'json',
enableNamespacePrefix: true
}
});
// Use custom logger factory with custom config
configure({
createCoreLogger: LoggerFactoryOfConsole,
config: {
format: 'text',
enableNamespacePrefix: true
}
});setLogLevel(level: LogLevel)
Set global log level threshold
setLogLevels(...levels: LogLevel[])
Set global allowed log levels
useLogger(ctx: string | Function | object)
Create a logger with namespace extracted from context
setLoggerIgnore(ignore: string)
Programmatically set the list of namespaces to ignore
Types
LogLevel
type LogLevel = 'error' | 'warn' | 'info' | 'debug';LoggerContext
interface LoggerContext {
tags?: { [x: string]: unknown };
extra?: { [x: string]: string | number | boolean | undefined | null };
namespace?: string[];
readonly config: LoggerConfig;
}LoggerConfig
interface LoggerConfig {
format: 'text' | 'json';
enableNamespacePrefix: boolean;
enableNamespacePrefixColors: boolean;
appendTagsForTextPrint: boolean;
appendExtraForTextPrint: boolean;
transformTagsForTextPrint?: (
tags: LoggerContext['tags'],
context: LoggerContext
) => unknown;
transformExtraForTextPrint?: (
extra: LoggerContext['extra'],
context: LoggerContext
) => unknown;
filter?: (
namespace: NonNullable<LoggerContext['namespace']>,
tags: NonNullable<LoggerContext['tags']>
) => boolean;
hook?: (
level: LogLevel,
context: LoggerContext,
...messages: Array<unknown>
) => unknown;
}Best Practices
- Configure once at startup: Call
configure()only once when your application starts - Use namespaces: Organize logs by feature or module using namespaces
- Use tags for filtering: Use tags to categorize logs that can be filtered later
- Use extra for context: Use extra information for structured data like request IDs
- Set appropriate log levels: Use
setLogLevel()orsetLogLevels()to control verbosity in different environments - Use hooks for monitoring: Implement hooks to send critical logs to monitoring systems
- Use filters for production: Use filters to reduce log noise in production environments
- Use TypeScript: Take advantage of full TypeScript support for better type safety and IDE autocomplete
- Leverage useLogger: Use
useLoggerhook for automatic namespace extraction from classes and functions - Environment-based configuration: Use different configurations for development, staging, and production
Examples
For more usage examples, see the examples directory:
- Basic Usage Example
- Advanced Features Example
- Configuration Example
- Color Features Example
- Real-world Scenario Example
Environment Variables
DEBUG_IGNORE
Comma-separated list of namespace names (case-insensitive) to ignore. When using useLogger, loggers with namespaces in this list will return no-op loggers.
DEBUG_IGNORE=UserService,ApiClient,DatabaseYou can also set this programmatically:
import { setLoggerIgnore } from '@jshow/logger';
setLoggerIgnore('UserService,ApiClient,Database');TypeScript Support
@jshow/logger is written in TypeScript and provides full type definitions out of the box. All APIs are fully typed, providing excellent IDE autocomplete and type safety.
import { logger, type Logger, type LogLevel } from '@jshow/logger';
// All methods are fully typed
const appLogger: Logger = logger.fork({ namespace: 'app' });
const level: LogLevel = 'info';Browser Support
The library works in both Node.js and browser environments:
- Node.js: Uses ANSI color codes for terminal output
- Browser: Uses CSS styles for console output (works with browser DevTools)
The same API works in both environments without any code changes.
Performance
@jshow/logger is designed with performance in mind:
- Minimal overhead when logs are filtered out
- Efficient namespace and context management
- No-op loggers for ignored namespaces have zero overhead
- Safe JSON serialization handles circular references efficiently
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Testing
pnpm testQuestions
The issue list of this repo is exclusively for bug reports and feature requests.
License
Copyright (c) 2022 jShow.org
