Lightweight, type-safe logger with history, transports, and JSON support for Node.js and React Native.
- ✅ TypeScript First: Full type safety with strict mode
- ✅ Zero Dependencies: No external runtime dependencies
- ✅ Platform Agnostic: Works in Node.js, React Native, and browsers
- ✅ Pluggable Transports: Console, file, or custom transports
- ✅ Multiple Formatters: Text or JSON output
- ✅ Log History: Configurable in-memory history
- ✅ Subscriptions: Listen to log events in real-time
- ✅ Log Filtering: Environment-based log level filtering
- ✅ File Rotation: Automatic file rotation for Node.js
- ✅ Safe Serialization: Handles circular references and Error objects
- ✅ Async Support: Non-blocking file writes with buffering
npm install @2run/logger
# or
yarn add @2run/logger
# or
pnpm add @2run/loggerimport { logger } from '@2run/logger';
// Simple logging
logger.info('Application started');
logger.debug('Debug information', { userId: 123 });
logger.warn('Warning message');
logger.error('Error occurred', { error: new Error('Something went wrong') });import { createLogger, JSONFormatter, FileTransport } from '@2run/logger';
const logger = createLogger({
prefix: 'MyApp',
minLogLevel: 'info',
maxHistory: 500,
defaultMetadata: { appVersion: '1.0.0' },
});
logger.info('Configured logger ready');import { createLogger, JSONFormatter, ConsoleTransport } from '@2run/logger';
const logger = createLogger({
transports: [new ConsoleTransport(new JSONFormatter())],
});
logger.info('User action', { userId: 123, action: 'login' });
// Output: {"timestamp":"2025-12-13T10:30:00.000Z","level":"info","message":"User action","id":"...","metadata":{"userId":123,"action":"login"}}import { createLogger, FileTransport } from '@2run/logger';
const logger = createLogger({
transports: [
new FileTransport({
filePath: './logs/app.log',
maxFileSize: 10 * 1024 * 1024, // 10MB
maxFiles: 5,
}),
],
});
logger.info('This will be written to file');import { createLogger, ConsoleTransport, FileTransport, JSONFormatter } from '@2run/logger';
const logger = createLogger({
transports: [
new ConsoleTransport(), // Console output
new FileTransport({ filePath: './logs/app.log' }), // File output
new FileTransport(
{ filePath: './logs/app.json.log' },
new JSONFormatter() // JSON file output
),
],
});
logger.info('Logged to console and two files');import { logger } from '@2run/logger';
// Subscribe to all log events
const unsubscribe = logger.subscribe((entry) => {
console.log('Log event:', entry);
// Send to analytics, remote logging service, etc.
});
logger.info('This will trigger the subscription');
// Unsubscribe when done
unsubscribe();import { createLogger } from '@2run/logger';
// Logger automatically detects React Native platform
const logger = createLogger({
prefix: 'MyApp',
platformInfo: true, // Includes iOS/Android version info
});
logger.info('Running on React Native');
// Output: [MyApp][INFO] Running on React Native [ios/14.5]Creates a new logger instance with the given configuration.
Parameters:
config(optional):LoggerConfigprefix(string): Prefix for log messages (default: '')maxHistory(number): Maximum history size (default: 200)minLogLevel(LogLevel): Minimum log level to emit (default: 'debug' in dev, 'warn' in production)platformInfo(boolean): Include platform info (default: true)transports(Transport[]): Custom transports (default: [ConsoleTransport])formatter(Formatter): Custom formatter (default: TextFormatter)defaultMetadata(Record<string, unknown>): Default metadata for all logscorrelationIdGenerator(() => string): Custom ID generator
Returns: Logger instance
Logs a debug message.
Logs an info message.
Logs a warning message.
Logs an error message.
Returns a copy of the log history.
Subscribes to log events. Returns an unsubscribe function.
Flushes all history to console with [History] tag.
Closes all transports and flushes pending logs.
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
interface LogEntry {
id: string;
level: LogLevel;
message: string;
metadata?: Record<string, unknown>;
timestamp: number;
}
interface Transport {
log(entry: LogEntry): void | Promise<void>;
flush?(): void | Promise<void>;
close?(): void | Promise<void>;
}
interface Formatter {
format(entry: LogEntry, platformInfo?: PlatformInfo): string;
}import { createLogger } from '@2run/logger';
import type { Transport, LogEntry } from '@2run/logger';
class RemoteTransport implements Transport {
async log(entry: LogEntry) {
await fetch('https://api.example.com/logs', {
method: 'POST',
body: JSON.stringify(entry),
});
}
}
const logger = createLogger({
transports: [new RemoteTransport()],
});import { createLogger } from '@2run/logger';
const logger = createLogger({
minLogLevel: process.env.NODE_ENV === 'production' ? 'warn' : 'debug',
});
// This will only log in development
logger.debug('Debug info');
// This will log in all environments
logger.error('Critical error');import { createLogger } from '@2run/logger';
import { v4 as uuidv4 } from 'uuid';
const logger = createLogger({
correlationIdGenerator: () => uuidv4(),
});
logger.info('Request started'); // ID: abc-123-def
logger.info('Processing'); // ID: xyz-456-ghi- Async File Writes: File transport uses buffering and async writes to avoid blocking
- Log History: Limited to configurable size (default: 200 entries)
- Lazy Serialization: Metadata is only serialized when needed
- Level Filtering: Logs below minimum level are filtered before transport
- Zero Dependencies: No runtime overhead from external packages
- Use appropriate log levels: debug < info < warn < error
- Include contextual metadata: Add relevant data to help debugging
- Configure per environment: Use debug in development, warn+ in production
- Handle sensitive data: Don't log passwords, tokens, or PII
- Use structured logging: JSON format for production logs
- Implement log rotation: Prevent disk space issues in long-running apps
- Clean up subscriptions: Always unsubscribe when components unmount
Contributions are welcome! Please read our Contributing Guide for details.
MIT © 2Run
- 📧 Email: halil@2run.be
- 🐛 Issues: GitHub Issues
- 📖 Documentation: API Docs
Made with ❤️ by the 2Run team