A modern, enterprise-grade logging library for Next.js with Edge Runtime support, automatic data sanitization, and distributed tracing. Built for production-scale applications with GDPR compliance, correlation IDs, and zero-configuration security.
- π Edge Runtime Compatible - Works seamlessly in Next.js middleware, Vercel Edge Functions, Cloudflare Workers
- π‘οΈ Automatic Data Sanitization - GDPR-compliant PII protection with smart field detection
- π Correlation IDs - W3C Trace Context standard for distributed tracing
- β‘ Advanced Error Serialization - Proper cause chain support with circular reference handling
- π¨ Performance Optimized - Memory leak prevention, circular buffers, and efficient batching
- π¦ Rate Limiting & Sampling - Enterprise-grade volume control with per-level configuration
- π Client/Server Separation - Clear patterns for secure logging architectures
- π·οΈ Smart Data Masking - Auto-detects passwords, tokens, emails, credit cards, and more
- π Context Management - AsyncLocalStorage-based context persistence in Node.js
- π Smart Module Detection - Auto-namespacing from stack traces for better debugging
- π― Pretty Print Formatter - Beautiful development console output with emojis
- π Zero Configuration - Works out-of-the-box with intelligent defaults
- π Universal Runtime - Server, browser, edge, Node.js, Bun - everything works
- πͺ TypeScript First - Full type safety and IntelliSense support
- βοΈ React Integration - Hooks, providers, and components for React apps
- ποΈ Dynamic Configuration - Runtime adjustments via environment variables
# Recommended: Bun (blazing fast)
bun add nexlog
# Also works with npm/yarn/pnpm
npm install nexlog
yarn add nexlog
pnpm add nexlog// middleware.ts - Works in Edge Runtime!
import { EdgeLogger } from 'nexlog/edge';
const logger = new EdgeLogger({
structured: true,
sanitize: true, // Auto-mask sensitive data
});
export function middleware(request: NextRequest) {
logger.info('Request received', {
path: request.nextUrl.pathname,
method: request.method,
userAgent: request.headers.get('user-agent'),
// PII automatically sanitized!
email: 'user@example.com', // β us***@example.com
password: 'secret123', // β [REDACTED]
});
}// app/api/users/route.ts
import logger from 'nexlog';
import { context } from 'nexlog/context';
export async function POST(request: Request) {
return context()
.withRequestId(crypto.randomUUID())
.withUserId(await getUserId(request))
.runAsync(async () => {
logger.info('Creating user', {
email: await request.json().email, // Auto-sanitized
});
const user = await createUser(data);
logger.success('User created successfully', { userId: user.id });
return Response.json(user);
});
}// Only non-sensitive logs reach the client
import logger from 'nexlog';
// This will be filtered out on client
logger.debug('Debug info with API key', { apiKey: 'sk_test_123' });
// This reaches the client safely
logger.info('User action', { action: 'button_click', page: '/dashboard' });import { Logger } from 'nexlog';
import { defaultSanitizer } from 'nexlog/sanitizer';
const logger = new Logger({
level: 'info',
structured: true,
sanitize: true,
// Performance optimizations
sampling: {
trace: 0.01, // 1% of trace logs
debug: 0.1, // 10% of debug logs
info: 1.0, // All info logs
},
// Rate limiting
rateLimit: {
maxLogs: 1000, // Max 1000 logs per minute
windowMs: 60000,
},
// Memory management
bufferSize: 1000,
autoFlush: true,
});
// Add custom sanitization rules
defaultSanitizer.addPattern('custom-token', {
pattern: /token_[a-zA-Z0-9]+/g,
replacement: '[TOKEN_REDACTED]',
});# .env.production
NEXLOG_LEVEL=warn
NEXLOG_STRUCTURED=true
NEXLOG_SANITIZE=true
NEXLOG_EDGE_ENABLED=true
NEXLOG_SAMPLING_INFO=0.1
NEXLOG_CONTEXT_SERVICE=api
NEXLOG_CONTEXT_VERSION=5.2.1Track requests across your entire application:
import { correlationManager } from 'nexlog/correlation';
import { contextManager } from 'nexlog/context';
// Middleware automatically extracts trace context
app.use(correlationManager.middleware());
// In your route handlers - correlation IDs are automatically included
logger.info('Processing payment', {
amount: 100,
// requestId, traceId, spanId automatically added from context
});
// Create child spans for operations
const childContext = correlationManager.createChildSpan();
contextManager.run(childContext, () => {
logger.info('Calling external API'); // New span ID
});Built-in GDPR compliance with intelligent field detection:
import { sanitize, defaultSanitizer } from 'nexlog/sanitizer';
// Automatic detection of sensitive fields
logger.info('User data', {
email: 'user@example.com', // β us***@example.com
password: 'secret123', // β [REDACTED]
creditCard: '4532123456789012', // β ****9012
apiKey: 'sk_live_xyz123', // β [REDACTED]
ssn: '123-45-6789', // β [REDACTED]
});
// Custom sanitization rules
defaultSanitizer.addPattern('custom', {
pattern: /CUSTOM_[A-Z0-9]+/g,
replacement: '[CUSTOM_REDACTED]',
});Persistent context across async operations:
import { context, contextManager } from 'nexlog/context';
// Express middleware
app.use((req, res, next) => {
context()
.withRequestId(req.id)
.withUserId(req.user?.id)
.withSessionId(req.session.id)
.run(() => next());
});
// Anywhere in your request handler
logger.info('User action'); // Automatically includes requestId, userId, sessionId
// Manual context management
contextManager.run({ requestId: '123', operation: 'checkout' }, () => {
logger.info('Starting checkout'); // Context automatically included
processPayment(); // Context propagated to child calls
});Proper error handling with cause chains:
import { ErrorSerializer } from 'nexlog/utils';
try {
await riskyOperation();
} catch (error) {
// Automatically serializes error chains, stack traces, and custom properties
logger.error('Operation failed', {
error, // Full error serialization with cause chain
operation: 'user_signup',
});
// Manual serialization
const serialized = ErrorSerializer.serialize(error);
console.log(serialized.stack); // Parsed stack frames
console.log(serialized.cause); // Cause chain
}Enterprise-grade volume control:
import { RateLimiter, AdvancedSampler } from 'nexlog/utils';
// Per-level sampling
const sampler = new AdvancedSampler({
trace: 0.01, // 1% of trace logs
debug: 0.1, // 10% of debug logs
info: 1.0, // All info logs
error: 1.0, // All error logs
});
// Rate limiting
const rateLimiter = new RateLimiter({
maxLogs: 100,
windowMs: 60000, // 100 logs per minute
});
// Per-message rate limiting
logger.info('High frequency event', {
_rateLimit: '5/minute', // Only log this message 5 times per minute
_sample: 0.1, // Only sample 10% of these logs
});Beautiful console output during development:
import { PrettyFormatter } from 'nexlog/formatters';
const formatter = new PrettyFormatter({
colors: true,
emoji: true, // Use emoji for log levels
timestamps: 'relative', // Show relative timestamps
groupCollapsed: true, // Collapse metadata groups
});
// Development logger with pretty printing
const devLogger = new Logger({
formatter: process.env.NODE_ENV === 'development' ? formatter : undefined,
});Built-in utilities for performance measurement:
// Time async operations
const result = await logger.time('database-query', async () => {
return await db.query('SELECT * FROM users');
});
// Automatically logs: "database-query completed" with duration
// Profile code sections
const endProfile = logger.profile('heavy-computation');
// ... perform computation ...
endProfile();
// Logs: "Profile: heavy-computation" with duration
// Manual performance logging in React
const { start, end } = usePerformanceLogger('component-render');
start();
// ... component logic ...
end({ componentName: 'UserList' });Optimize logging performance with batching:
import { BatchedTransport, ConsoleTransport } from 'nexlog';
const batchedLogger = new Logger({
transports: [
new BatchedTransport(
new ConsoleTransport(),
{
maxBatchSize: 100, // Flush after 100 logs
flushInterval: 5000 // Flush every 5 seconds
}
)
]
});
// Logs are batched and flushed efficiently
for (let i = 0; i < 1000; i++) {
batchedLogger.info(`Processing item ${i}`);
}
// Manually flush all pending logs
await batchedLogger.flush();import { LoggerProvider } from 'nexlog/react';
export default function App() {
return (
<LoggerProvider
initialLevel="debug"
namespace="my-app"
config={{
context: { version: '1.0.0' },
structured: process.env.NODE_ENV === 'production'
}}
logLifecycle={true} // Log app start/stop, network events, errors
>
<YourApp />
</LoggerProvider>
);
}import { useLogger, useChildLogger, usePerformanceLogger } from 'nexlog/react';
function MyComponent() {
// Access the logger instance
const { logger, level, setLevel, stats } = useLogger();
// Create a child logger for this component
const componentLogger = useChildLogger('MyComponent');
// Performance logging
const { start, end } = usePerformanceLogger('data-fetch');
useEffect(() => {
start();
fetchData().then(data => {
end({ recordCount: data.length });
componentLogger.info('Data loaded', { count: data.length });
});
}, []);
return (
<div>
<p>Log Level: {level}</p>
<p>Total Logs: {stats.logCount}</p>
<button onClick={() => setLevel('debug')}>Set Debug</button>
</div>
);
}import { withLogger } from 'nexlog/react';
interface Props {
logger?: Logger;
}
const MyComponent: React.FC<Props> = ({ logger }) => {
logger?.info('Component rendered');
return <div>My Component</div>;
};
export default withLogger(MyComponent, 'MyComponent');Add visual logger controls in development:
import { LoggerProvider, LoggerDevTools } from 'nexlog/react';
function App() {
return (
<LoggerProvider>
<YourApp />
{process.env.NODE_ENV === 'development' && (
<LoggerDevTools position="bottom-right" />
)}
</LoggerProvider>
);
}The DevTools component provides:
- Real-time log level adjustment
- Enable/disable logging
- View logger statistics
- Monitor active transports and child loggers
Levels from least to most severe:
trace- Detailed debugging informationdebug- Debug information for developmentinfo- General informational messageswarn- Warning messages for potential issueserror- Error messages for failuresfatal- Critical failures requiring immediate attention
Universal compatibility across all JavaScript runtimes:
import { detectRuntime, IS_EDGE, IS_NODE, IS_BROWSER } from 'nexlog';
// Automatic runtime detection and optimization
console.log(detectRuntime()); // 'node' | 'edge' | 'browser' | 'worker' | 'bun'
// Edge Runtime (Vercel Edge Functions, Cloudflare Workers)
if (IS_EDGE) {
// Optimized for edge with structured logging
import('nexlog/edge').then(({ EdgeLogger }) => {
const logger = new EdgeLogger({ structured: true });
});
}
// Node.js (full feature set)
if (IS_NODE) {
// Full features including AsyncLocalStorage context
import('nexlog').then(({ Logger }) => {
const logger = new Logger({
useAsyncContext: true,
colors: true,
});
});
}
// Browser (security-focused)
if (IS_BROWSER) {
// Client-safe logging with automatic sensitive data filtering
import('nexlog/browser').then(({ default: logger }) => {
logger.setLevel('warn'); // Don't send debug info to client
});
}Enable structured logging for production environments:
const logger = new Logger({
structured: true,
level: 'info'
});
logger.info('User action', { userId: 123, action: 'login' });
// Output: {"timestamp":"2024-01-01T00:00:00.000Z","level":"INFO","message":"User action","userId":123,"action":"login"}Monitor logger performance and usage:
const stats = logger.getStats();
console.log({
logCount: stats.logCount, // Total logs written
uptime: stats.uptime, // Logger uptime in ms
children: stats.children, // Number of child loggers
transports: stats.transports // Number of active transports
});nexlog can be fully configured via environment variables, making it perfect for production deployments without code changes.
# Copy the example configuration
cp node_modules/nexlog/.env.example .env.local
# Or create your own
echo "NEXLOG_LEVEL=warn" >> .env.local
echo "NEXLOG_SSR_ONLY=true" >> .env.localNEXLOG_LEVEL=warn
NEXLOG_STRUCTURED=true
NEXLOG_SSR_ONLY=true
NEXLOG_SAMPLING_RATE=0.1 # Log only 10% of messages
NEXLOG_BATCH_SIZE=500NEXLOG_LEVEL=trace
NEXLOG_COLORS=true
NEXLOG_DEV_TOOLS=true
NEXLOG_PERFORMANCE=true
NEXLOG_DEBUG=trueNEXLOG_CLIENT_ENABLED=false
NEXLOG_SSR_ONLY=trueNEXLOG_LEVEL=trace
NEXLOG_INCLUDE_PATTERNS=AuthService.*,Database.*
NEXLOG_EXCLUDE_PATTERNS=health.*,ping| Variable | Description | Default |
|---|---|---|
| Core Settings | ||
NEXLOG_LEVEL |
Log level (trace/debug/info/warn/error/fatal) | info |
NEXLOG_ENABLED |
Enable/disable all logging | true |
NEXLOG_SSR_ONLY |
Only log on server-side | false |
NEXLOG_CLIENT_ENABLED |
Enable browser logging | true |
NEXLOG_EDGE_ENABLED |
Enable edge runtime logging | true |
| Output Settings | ||
NEXLOG_STRUCTURED |
JSON structured logging | false |
NEXLOG_COLORS |
Colored console output | true |
NEXLOG_NAMESPACE |
Default namespace | - |
| Performance | ||
NEXLOG_SAMPLING_RATE |
Log sampling (0-1) | 1 |
NEXLOG_BATCH_SIZE |
Batch size | 100 |
NEXLOG_FLUSH_INTERVAL |
Flush interval (ms) | 1000 |
| Features | ||
NEXLOG_DEV_TOOLS |
Enable DevTools in prod | false |
NEXLOG_LIFECYCLE |
Log app lifecycle | true |
NEXLOG_PERFORMANCE |
Include perf metrics | false |
NEXLOG_STACK_TRACES |
Include stack traces | true |
| Filtering | ||
NEXLOG_INCLUDE_PATTERNS |
Include regex patterns | - |
NEXLOG_EXCLUDE_PATTERNS |
Exclude regex patterns | - |
| Context | ||
NEXLOG_CONTEXT_* |
Add to log context | - |
Add any context to all logs via environment variables:
NEXLOG_CONTEXT_VERSION=1.0.0
NEXLOG_CONTEXT_ENVIRONMENT=production
NEXLOG_CONTEXT_REGION=us-west-2
NEXLOG_CONTEXT_SERVICE=apiAll logs will include: {version: "1.0.0", environment: "production", ...}
Configuration follows this priority (highest wins):
- Runtime changes (
logger.setLevel()) - Environment variables (
NEXLOG_*) - Code configuration (
new Logger({...})) - Default values
import { configManager, ENV_VARS } from 'nexlog';
// Check current configuration
const config = configManager.getConfig();
console.log('Current log level:', config.level);
// Check if should log in current environment
if (configManager.shouldLog('browser')) {
console.log('Browser logging is enabled');
}
// Runtime configuration changes
configManager.set('level', 'debug');
configManager.set('samplingRate', 0.5);
// Get environment variable names
console.log(ENV_VARS.NEXLOG_LEVEL); // "NEXLOG_LEVEL"// next.config.js
const nextConfig = {
// No transpilePackages needed for v5.2.1+!
experimental: {
runtime: 'edge', // nexlog works seamlessly
},
};
export default nextConfig;// app/api/secure/route.ts - Server-side only
import logger from 'nexlog';
export async function POST(request: Request) {
// This will be automatically sanitized
logger.info('API call', {
apiKey: request.headers.get('authorization'), // β [REDACTED]
userId: getUserId(request),
timestamp: Date.now(),
});
}// app/components/ClientComponent.tsx - Client-safe
'use client';
import logger from 'nexlog';
export default function ClientComponent() {
const handleClick = () => {
// Only non-sensitive logs reach the client
logger.info('Button clicked', {
button: 'submit',
timestamp: Date.now(),
// Any sensitive data is automatically filtered
});
};
}class Logger {
constructor(config?: LoggerConfig)
// Logging methods
trace(message: string, metadata?: LogMetadata): void
debug(message: string, metadata?: LogMetadata): void
info(message: string, metadata?: LogMetadata): void
warn(message: string, metadata?: LogMetadata): void
error(message: string, metadata?: LogMetadata): void
fatal(message: string, metadata?: LogMetadata): void
// Configuration
setLevel(level: LogLevel): void
getLevel(): LogLevel
enable(): void
disable(): void
isEnabled(): boolean
setSSROnly(ssrOnly: boolean): void
setContext(context: LogMetadata): void
// Child loggers
child(namespace: string, config?: Partial<LoggerConfig>): Logger
// Transports
addTransport(transport: Transport): void
removeTransport(name: string): void
// Plugins
use(plugin: LoggerPlugin): void
// Performance
time<T>(label: string, fn: () => T | Promise<T>, metadata?: LogMetadata): Promise<T>
profile(label: string): () => void
// Utilities
flush(): Promise<void>
getStats(): LoggerStats
}type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
type Environment = "server" | "edge" | "browser" | "unknown";
type LogMetadata = Record<string, unknown>;
interface LoggerConfig {
level?: LogLevel;
enabled?: boolean;
ssrOnly?: boolean;
namespace?: string;
context?: LogMetadata;
batchSize?: number;
flushInterval?: number;
transports?: Transport[];
structured?: boolean;
samplingRate?: number;
}
interface LogEntry {
timestamp: string;
level: LogLevel;
message: string;
namespace?: string;
metadata?: LogMetadata;
context?: LogMetadata;
environment: Environment;
stack?: string;
performance?: {
memory?: NodeJS.MemoryUsage;
timestamp: number;
};
}
interface Transport {
name: string;
enabled?: boolean;
level?: LogLevel;
log(entry: LogEntry): void | Promise<void>;
flush?(): void | Promise<void>;
}
interface LoggerPlugin {
name: string;
init?(logger: Logger): void;
transform?(entry: LogEntry): LogEntry;
beforeLog?(entry: LogEntry): void | false;
afterLog?(entry: LogEntry): void;
}nexlog is designed for maximum performance with Bun:
- Zero dependencies - No external dependencies for minimal bundle size
- Bun-optimized - Leverages Bun's fast runtime and APIs
- Lazy evaluation - Logs are only formatted when needed
- Batching support - Reduce I/O overhead with batched writes
- Sampling - Control log volume in high-traffic scenarios
- Efficient child loggers - Cached instances with shared configuration
- Minimal overhead - Disabled logs have near-zero performance impact
- High-precision timing - Uses Bun.nanoseconds() for accurate measurements
Benchmark results with Bun (10,000 logs):
- Simple logging: ~80,000 ops/sec
- With metadata: ~65,000 ops/sec
- Child logger creation: 15,000+ ops/sec
- Batched logging: ~120,000 ops/sec
# Run tests with Bun's built-in test runner
bun test
# Watch mode
bun test --watch
# Coverage
bun test --coverage
# Test environment configuration
bun run test-env.tsContributions are welcome! Please feel free to submit a Pull Request.
nexlog automatically protects personally identifiable information:
- Email addresses:
user@example.comβus***@example.com - Credit cards:
4532123456789012β****9012 - API keys:
sk_live_xyz123β[REDACTED] - Passwords: Any field named
password,pass,pwdβ[REDACTED] - Tokens:
access_token,refresh_token, etc. β[REDACTED]
// Server-side: Full logging with sensitive data sanitization
import logger from 'nexlog'; // Full server logger
// Client-side: Automatically filtered logging
import logger from 'nexlog/browser'; // Client-safe loggerFor comprehensive production implementation guidance, see our Implementation Guide.
MIT License - see LICENSE file for details.
If you find nexlog helpful, consider sponsoring me. Your support helps maintain and improve this project.
The v5.2.1+ release is backward compatible with enhanced features:
// v4.x code still works
import logger from 'nexlog';
logger.info('Hello');
// v5.2.1+ enhanced features
import logger from 'nexlog';
import { context } from 'nexlog/context';
// Now with automatic data sanitization and context
context()
.withRequestId('req_123')
.run(() => {
logger.info('Hello', {
password: 'secret123', // β [REDACTED]
userId: 12345, // β preserved
});
});Key improvements in v5.2.1+:
- Edge Runtime compatibility for Next.js middleware
- Automatic data sanitization with GDPR compliance
- Correlation IDs with W3C Trace Context support
- Advanced error serialization with cause chains
- Performance improvements with memory leak prevention
- Context management with AsyncLocalStorage
- Security by default with client/server separation