Standardized structured logging library for microservices
npm install @gomarbleai/logging-jsStandardized structured logging library for microservices that works seamlessly with Next.js, Express.js, and Fastify.
- Universal Compatibility: Built-in support for Next.js, Express.js, and Fastify frameworks
- Structured Logging: JSON-formatted logs with consistent schema
- Request Tracing: Automatic trace ID extraction and propagation
- Performance Monitoring: Built-in request duration tracking
- Error Handling: Comprehensive error logging with stack traces
- Streaming Support: Special handling for SSE and long-lived connections
- Zero Configuration: Works out of the box with sensible defaults
``bash`
npm install @gomarbleai/logging-js
`typescript
import { withLogging } from '@gomarbleai/logging-js';
async function handler(req: NextApiRequest, res: NextApiResponse) {
// Your API logic here
res.status(200).json({ message: 'Success' });
}
export default withLogging(handler);
`
`typescript
import express from 'express';
import { withLogging, createLogger } from '@gomarbleai/logging-js';
const app = express();
// Apply logging middleware (auto-detects Express)
app.use(withLogging);
app.get('/api/data', (req, res) => {
const logger = createLogger(req);
logger.info('Processing request');
res.json({ data: 'success' });
});
`
`typescript
import Fastify from 'fastify';
import { registerFastifyLogging, createLogger } from '@gomarbleai/logging-js';
const server = Fastify({ logger: true });
const start = async () => {
try {
// Register logging hooks (covers all routes automatically)
registerFastifyLogging(server);
// Register your routes
server.get('/api/data', async (request, reply) => {
const logger = createLogger(request);
logger.info('Processing request');
return { data: 'success' };
});
await server.listen({ port: 3000, host: '0.0.0.0' });
} catch (err) {
server.log.error(err);
process.exit(1);
}
};
start();
`
Alternative Plugin Approach:
`typescript`
// You can also use the plugin approach if preferred
await server.register(withFastifyLogging());
For Server-Sent Events (SSE) or long-lived streaming connections:
`typescript
import { withStreamLogging, createLoggingContext } from '@gomarbleai/logging-js';
// Option 1: Use withStreamLogging middleware
app.get('/api/sse', withStreamLogging((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// Your SSE logic here
const interval = setInterval(() => {
res.write(data: ${JSON.stringify({ timestamp: Date.now() })}\n\n);
}, 1000);
req.on('close', () => clearInterval(interval));
}, {
logStart: true, // Log when connection starts
logEnd: true, // Log when connection closes
timeout: 300000 // Optional: Log timeout after 5 minutes
}));
// Option 2: Use manual logging context for fine-grained control
app.get('/api/sse-manual', (req, res) => {
const loggingContext = createLoggingContext(req);
loggingContext.logStart('SSE connection initiated');
try {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// Your SSE logic here
const interval = setInterval(() => {
res.write(data: ${JSON.stringify({ timestamp: Date.now() })}\n\n);`
}, 1000);
req.on('close', () => {
clearInterval(interval);
loggingContext.logEnd('SSE connection closed by client');
});
req.on('error', (error) => {
clearInterval(interval);
loggingContext.logError(error, 'SSE connection error');
});
} catch (error) {
loggingContext.logError(error, 'Failed to establish SSE connection');
}
});
For Next.js & Express.js:
The withLogging function automatically:
- Detects whether you're using Next.js or Express.js
- Logs request start and completion
- Tracks response time
- Extracts and propagates trace IDs
- Handles errors and exceptions
For Fastify:
The registerFastifyLogging() function automatically:withFastifyLogging()
- Registers hooks directly on the Fastify instance
- Logs request completion with method, path, status, and duration
- Extracts trace IDs from headers
- Works with all registered routes
- Alternative: Use as a plugin
The withStreamLogging function is designed for long-lived connections:
- Logs connection initiation and closure
- Handles connection events (close, error, finish)
- Supports optional timeout warnings
- Doesn't wait for handler completion (non-blocking)
`typescript
import { createLogger, createLoggingContext } from '@gomarbleai/logging-js';
// Basic manual logging
export default withLogging(async (req, res) => {
const logger = createLogger(req);
logger.info('Processing user data');
logger.warn('Rate limit approaching');
logger.error('Database connection failed', {
error: new Error('Connection timeout'),
custom_data: { user_id: 123 }
});
});
// Manual logging with context (for streaming)
app.post('/api/users', (req, res) => {
const context = createLoggingContext(req);
context.logStart('Creating new user');
try {
// Your logic here
context.logEnd('User created successfully');
} catch (error) {
context.logError(error, 'Failed to create user');
}
});
// Fastify manual logging
const start = async () => {
// Register logging hooks
registerFastifyLogging(server);
server.post('/api/users', async (request, reply) => {
const logger = createLogger(request);
logger.info('Creating new user');
try {
// Your logic here
logger.info('User created successfully');
return { success: true };
} catch (error) {
logger.error('Failed to create user', { error });
throw error;
}
});
await server.listen({ port: 3000 });
};
`
`typescript
logger.debug(message, metadata?);
logger.info(message, metadata?);
logger.warn(message, metadata?);
logger.error(message, metadata?);
// Context methods
context.logStart(message?);
context.logEnd(message?);
context.logError(error, message?);
`
Set environment variables to configure the logger:
`bash`
SERVICE_NAME=user-service
ENVIRONMENT=production
NODE_ENV=production
Configure withStreamLogging with these options:
`typescript`
interface StreamLoggingOptions {
logStart?: boolean; // Log connection start (default: true)
logEnd?: boolean; // Log connection end (default: true)
timeout?: number; // Timeout in ms for warning (optional)
}
All logs follow this structured JSON format:
`json`
{
"timestamp": "2024-01-01T12:00:00.000Z",
"level": "INFO",
"message": "request processed",
"service_name": "user-service",
"environment": "production",
"trace_id": "internal-abc123def456",
"http": {
"method": "POST",
"path": "/api/users",
"status_code": 200
},
"duration_ms": 150,
"custom_data": {
"user_id": 123
}
}
The library provides different middleware for each framework:
- Next.js: Use withLogging - automatically detects Next.js API handlerswithLogging
- Express.js: Use - automatically detects Express middleware signatureregisterFastifyLogging()
- Fastify: Use for direct registration or withFastifyLogging() as a plugin
Each framework has optimized integration that respects framework conventions!
Direct Registration (Recommended):
`typescript`
registerFastifyLogging(server);
- Registers hooks directly on the Fastify instance
- Ensures hooks apply to all routes regardless of plugin encapsulation
- Simpler setup for most use cases
Plugin Approach:
`typescript`
await server.register(withFastifyLogging());
- Follows Fastify plugin conventions
- May require careful ordering relative to route registration
- Use when you need plugin-specific encapsulation
for standard request/response cycles
- Fastify: Use registerFastifyLogging(server) for direct hook registration, or withFastifyLogging() plugin
- Use createLogger for manual logging within handlers$3
- Use withStreamLogging for SSE endpoints with automatic lifecycle logging
- Use createLoggingContext` for fine-grained control over streaming logsMIT