@qvlt/core-logger
v0.0.3
Published
Structured logging for web applications
Readme
@qvlt/core-logger
A universal structured logging library for both browser and Node.js environments with a flexible transport layer.
Features
- Universal - Works in both browser and Node.js environments
- Transport-based architecture - Choose your output destination
- Structured logging with JSON output in production
- Component-based logging with child loggers
- Environment-aware (development vs production)
- Performance timing utilities
- Global logger instance management
- Multiple transport support - Console, HTTP, stdout, and custom transports
Installation
pnpm add @qvlt/core-loggerRequires Node ≥18.18.0 (for built-in fetch).
Usage
Basic Setup
import { initializeLogger, getLogger, ConsoleTransport } from '@qvlt/core-logger';
// Initialize the logger with explicit transport
await initializeLogger({
app: 'my-app',
env: 'development', // or 'production' or 'test'
ver: '1.0.0',
level: 'debug', // minimum log level
transport: new ConsoleTransport(), // explicit transport required
});
// Get a logger instance
const logger = getLogger('my-component');
// Log messages
logger.info('user.login', { userId: '123' });
logger.error('api.error', { endpoint: '/users' }, error);Component-based Logging
// Create a child logger for a specific component
const authLogger = getLogger('auth');
authLogger.debug('token.validating', { token: '***' });
authLogger.info('user.authenticated', { userId: '123' });
authLogger.warn('token.expiring', { expiresIn: '5m' });
authLogger.error('auth.failed', { reason: 'invalid_token' }, error);Performance Timing
const result = await logger.time(
'api.request',
async () => {
return await fetch('/api/data');
},
{ endpoint: '/api/data' },
);
// Logs: "api.request.done" with durationTransport Layer
The logger uses a transport-based architecture that allows you to choose where your logs are sent. This makes the logger truly universal across browser and Node.js environments.
Available Transports
ConsoleTransport
Pretty-prints logs to the console for development environments.
import { ConsoleTransport } from '@qvlt/core-logger';
const logger = new Logger({
app: 'my-app',
env: 'development',
transport: new ConsoleTransport(),
});HttpTransport
Sends logs to an HTTP endpoint with browser and Node.js support.
import { HttpTransport } from '@qvlt/core-logger';
const logger = new Logger({
app: 'my-app',
env: 'production',
transport: new HttpTransport('/api/logs'),
});Features:
- Uses
navigator.sendBeaconin browsers for reliable delivery - Falls back to
fetchfor larger payloads or Node.js - Automatically handles browser vs Node.js environments
StdoutTransport
Writes JSONL (JSON Lines) to stdout, perfect for containerized environments.
import { StdoutTransport } from '@qvlt/core-logger';
const logger = new Logger({
app: 'my-app',
env: 'production',
transport: new StdoutTransport(),
});Features:
- Single-line JSON format for log aggregation systems
- Ideal for Kubernetes, Docker, and cloud environments
- Follows 12-factor app principles
Multiple Transports
You can use multiple transports simultaneously:
import { ConsoleTransport, HttpTransport, StdoutTransport } from '@qvlt/core-logger';
const logger = new Logger({
app: 'my-app',
env: 'production',
transport: [
new ConsoleTransport(), // Development console
new HttpTransport('/api/logs'), // HTTP endpoint
new StdoutTransport(), // Container logs
],
});Custom Transports
Create your own transport by implementing the Transport interface:
import type { Transport, LogEvent } from '@qvlt/core-logger';
class CustomTransport implements Transport {
async write(batch: LogEvent[]) {
// Send to your custom destination
await sendToCustomService(batch);
}
async flush() {
// Optional: flush any buffered data
}
destroy() {
// Optional: cleanup resources
}
}
const logger = new Logger({
app: 'my-app',
env: 'production',
transport: new CustomTransport(),
});Global Logger (opt-in)
If you want to use a global getter, explicitly install it after initialization:
import { initializeLogger, installGlobalGetLogger } from '@qvlt/core-logger';
await initializeLogger({
/* ... */
});
installGlobalGetLogger();
const logger = getLogger('global');
logger.info('app.started');Configuration
Environment Detection
import { ConsoleTransport, HttpTransport, StdoutTransport } from '@qvlt/core-logger';
const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
const isNode = typeof process !== 'undefined' && !!process.versions?.node;
// Choose transport based on environment
let transport;
if (isProduction) {
if (isNode) {
transport = new StdoutTransport(); // JSONL to stdout for containers
} else {
transport = new HttpTransport('/api/logs'); // HTTP for browser production
}
} else {
transport = new ConsoleTransport(); // Pretty console for development
}
await initializeLogger({
app: 'my-app',
env: isProduction ? 'production' : isTest ? 'test' : 'development',
level: isProduction ? 'info' : 'debug',
transport,
});Advanced Configuration
import { ConsoleTransport, HttpTransport, StdoutTransport } from '@qvlt/core-logger';
await initializeLogger({
app: 'my-app',
env: 'production',
ver: '1.0.0',
level: 'info',
transport: [
new ConsoleTransport(), // Development console
new HttpTransport('/api/logs'), // HTTP endpoint
new StdoutTransport(), // Container logs
],
maxBatch: 50, // batch size for transport requests
flushIntervalMs: 10000, // flush interval in milliseconds
defaultCtx: {
service: 'api',
region: 'us-east-1',
},
sample: {
debug: 0.1, // Only log 10% of debug messages
info: 1.0, // Log all info messages
warn: 1.0, // Log all warnings
error: 1.0, // Log all errors
},
});Setting Default Context
Use setDefaultLogContext() to add context that will be included in all subsequent logs:
import { setDefaultLogContext } from '@qvlt/core-logger';
// Add user context to all logs
setDefaultLogContext({ userId: 'u_123', session: 's_abc' });
// Add trace context
setDefaultLogContext({ traceId: 't_xyz', orgId: 'o_456' });Note: Child loggers (created with getLogger('component')) don't have setDefaultContext - use the global helper above instead.
Transport Configuration
The logger requires explicit transport configuration. You must choose which transports to use:
import { ConsoleTransport, HttpTransport, StdoutTransport } from '@qvlt/core-logger';
// Browser development: ConsoleTransport
await initializeLogger({
app: 'my-app',
env: 'development',
transport: new ConsoleTransport(),
});
// Browser production with HTTP endpoint: HttpTransport
await initializeLogger({
app: 'my-app',
env: 'production',
transport: new HttpTransport('/api/logs'),
});
// Node.js production: StdoutTransport
await initializeLogger({
app: 'my-app',
env: 'production',
transport: new StdoutTransport(),
});
// Multiple transports: Console + HTTP
await initializeLogger({
app: 'my-app',
env: 'production',
transport: [new ConsoleTransport(), new HttpTransport('/api/logs')],
});Note: You must explicitly configure transports. No default transport is provided to ensure intentional logging configuration.
Global Installation
The package does not auto-install any globals. To enable a global getter:
import { installGlobalGetLogger } from '@qvlt/core-logger';
// Explicitly install global getLogger (opt-in)
installGlobalGetLogger();Cleanup and Shutdown
Use shutdownLogger() to properly clean up resources:
// Clean up when your app shuts down
shutdownLogger();This will:
- Clear any pending intervals
- Flush remaining queued logs
- Remove event listeners
- Clean up the logger instance
Sampling
Control log volume with sampling:
await initializeLogger({
app: 'my-app',
env: 'production',
transport: new HttpTransport('/api/logs'),
sample: {
debug: 0.1, // Only log 10% of debug messages
info: 1.0, // Log all info messages
warn: 1.0, // Log all warnings
error: 1.0, // Log all errors
},
});Log Levels
debug- Detailed debugging informationinfo- General information messageswarn- Warning messageserror- Error messages
Production Features
The logger supports various production scenarios:
- Structured JSON output for log aggregation
- Batched transport for efficient delivery
- Sampling to control log volume
- Session tracking for request correlation
- Multiple transport support for redundancy
Development Features
The logger provides rich development experience:
- Pretty console output with ConsoleTransport
- Component context for better debugging
- Full error details with stack traces
- Flexible transport configuration for testing
License
MIT
