Logging System Overview
The B3API uses a unified Pino-based JSON logging system that provides structured, queryable logs for all application components. All logs are output in JSON format for seamless integration with Loki/Grafana observability stack.
Key Features
- Structured JSON Logging: All logs use consistent JSON format with rich context
- Global Error Handling: Automatic error catching and logging at the API sequence level
- Console Interception: Automatic redirection of console.log/error/warn to Pino
- Loki/Grafana Integration: Production-ready logging for monitoring and alerting
- Zero Configuration: Works automatically for all endpoints and services
- Rich Context: Includes service name, timestamps, PID, hostname, and custom fields
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Application Code │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Controllers, Services, Repositories │ │
│ │ - Direct logger usage │ │
│ │ - console.log/error/warn (auto-intercepted) │ │
│ └────────────────────┬──────────────────────────────────┘ │
└─────────────────────────┼──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Pino Logger (utils/logger.ts) │
│ - Formats logs as JSON │
│ - Adds context (service, pid, hostname, time) │
│ - Handles log levels (debug, info, warn, error) │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Global Error Handler (sequence.ts) │
│ - Catches all API errors automatically │
│ - Logs request/response context │
│ - Tracks request duration │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Console Interceptor │
│ - Redirects console.* calls to Pino │
│ - Preserves backward compatibility │
│ - No code changes needed │
└────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ JSON Logs → Loki/Grafana │
│ - Queryable structured logs │
│ - Real-time monitoring and alerting │
│ - Historical log analysis │
└─────────────────────────────────────────────────────────────┘Log Output Format
Every log entry includes:
{
"level": "info", // Log level: debug, info, warn, error
"time": "2025-12-16T10:30:45.123Z", // ISO timestamp
"pid": 1234, // Process ID
"hostname": "api-server-01", // Server hostname
"service": "b3api", // Service name
"msg": "User created successfully", // Human-readable message
"user_id": 123, // Custom context fields
"email": "user@example.com"
}Components
1. Logger Utility
Located at src/utils/logger.ts, provides:
- Global Logger: Default logger instance for quick usage
- createLogger(context): Creates context-specific loggers for services/controllers
- createChildLogger(parent, bindings): Creates child loggers with additional context
→ Learn more about Logger Usage
2. Global Error Handling
Implemented in src/sequence.ts (MySequence class):
- Automatically catches all API errors
- Logs every incoming request with context
- Logs successful responses with status codes and duration
- Logs failed requests with error details and stack traces
→ Learn more about Error Handling
3. Console Interception
Implemented in src/utils/console-interceptor.ts:
- Automatically intercepts
console.log,console.error,console.warn,console.debug - Redirects to Pino with appropriate log levels
- Zero code changes needed - existing console calls work as-is
- Enabled globally at application startup
→ Learn more about Console Interception
Quick Start
Basic Usage
import { logger } from './utils/logger';
// Simple info log
logger.info({ msg: 'User logged in', user_id: 123 });
// Error log with stack trace
logger.error({
msg: 'Database connection failed',
error: err.message,
stack: err.stack
});
// Warning
logger.warn({ msg: 'API rate limit approaching', current: 950, limit: 1000 });
// Debug (only visible if LOG_LEVEL=debug)
logger.debug({ msg: 'Cache hit', key: 'user:123' });Service-Level Logging
import { createLogger } from './utils/logger';
export class UserService {
private logger = createLogger('UserService');
async createUser(data: any) {
this.logger.info({ msg: 'Creating user', email: data.email });
try {
const user = await this.userRepository.create(data);
this.logger.info({ msg: 'User created', user_id: user.id });
return user;
} catch (err: any) {
this.logger.error({
msg: 'User creation failed',
error: err.message,
stack: err.stack,
email: data.email
});
throw err;
}
}
}Using Existing console.* Calls
No changes needed! All console calls are automatically converted to JSON:
// This already works and outputs JSON!
console.log('Server started', { port: 3000 });
console.error('Error occurred', error);
console.warn('Deprecated API call');Log Levels
Control logging verbosity with the LOG_LEVEL environment variable:
LOG_LEVEL=debug- Show all logs (debug, info, warn, error)LOG_LEVEL=info- Show info, warn, error (default)LOG_LEVEL=warn- Show warn and error onlyLOG_LEVEL=error- Show only errors
Querying Logs in Grafana/Loki
Basic Queries
# All error logs
{service="b3api"} | json | level="error"
# Logs from specific context
{service="b3api"} | json | context="UserService"
# Logs for specific user
{service="b3api"} | json | user_id="123"
# Slow requests (> 1 second)
{service="b3api"} | json | duration_ms > 1000Advanced Queries
# Error rate over time
sum(rate({service="b3api"} | json | level="error" [5m]))
# Top error types
topk(10, sum by (error_name) (count_over_time({service="b3api"} | json | level="error" [1h])))
# Requests by endpoint
sum by (path) (count_over_time({service="b3api"} | json | path!="" [1h]))Best Practices
1. Always Use Structured Data
✅ Good:
logger.info({ msg: 'Payment processed', amount: 99.99, currency: 'USD', user_id: 123 });❌ Bad:
logger.info({ msg: 'Payment processed' }); // Missing context2. Include Relevant Context
Always include identifiers that help track operations:
user_id- User performing the actionorder_id,report_id, etc. - Resource identifierstransaction_id- For tracking multi-step operationsduration_ms- For performance tracking
3. Use Appropriate Log Levels
- debug: Detailed diagnostic info (disabled by default in production)
- info: Normal operations, state changes, successful completions
- warn: Non-critical issues, deprecations, approaching limits
- error: Errors requiring attention, failures
4. Include Error Context
When logging errors, always include:
logger.error({
msg: 'Operation failed',
error: err.message,
stack: err.stack,
// Plus any relevant context
user_id: userId,
operation: 'createOrder'
});5. Track Operation Duration
For performance monitoring:
const startTime = Date.now();
// ... operation ...
const duration = Date.now() - startTime;
logger.info({ msg: 'Operation completed', duration_ms: duration });Integration Points
Where Logging is Enabled
The logging system is automatically active in:
- Main API Server (
src/index.ts) - Cron Worker (
src/services/workers/cron/cron-worker.ts) - Cron Scheduler (
src/services/workers/cron/cron-scheduler.ts) - Email Service (
src/services/workers/emailService/emailService.ts) - PM2 Report Service (
src/services/pm2.service.ts)
All code running in these processes automatically benefits from:
- Global error handling
- Console interception
- Structured JSON logging
Migration Guide
Migrating Existing Code
To migrate from console.* to direct logger usage:
Before:
console.log('User created:', userId);
console.error('Error:', error);After:
import { logger } from './utils/logger';
logger.info({ msg: 'User created', user_id: userId });
logger.error({ msg: 'Error occurred', error: error.message, stack: error.stack });Benefits of migrating:
- Better TypeScript typing
- More control over log structure
- No "console_data" wrapper in logs
- Explicit log levels
Note: Migration is optional - console.* calls already work and output JSON!
Environment Variables
# Log level control
LOG_LEVEL=info # Default: info, warn, error
LOG_LEVEL=debug # Enable debug logs
LOG_LEVEL=error # Only errors
# Node environment (affects error response formatting)
NODE_ENV=production # Hides stack traces in API responses (still logged)
NODE_ENV=development # Shows detailed errors in responsesRelated Documentation
- Logger Usage Guide - Detailed usage examples and patterns
- Global Error Handling - How automatic error catching works
- Console Interception - How console.* redirection works
- Cron Worker System - Logging in background jobs
- Report Scheduling - Logging in report generation
Benefits
✅ Complete Visibility: Every operation is logged with full context ✅ Zero Configuration: Works automatically for all code ✅ Backward Compatible: Existing console.* calls work unchanged ✅ Production Ready: JSON format perfect for Loki/Grafana ✅ Performance Tracking: Request duration logged automatically ✅ Easy Debugging: Stack traces and error context always available ✅ Queryable Logs: Structured JSON enables powerful log queries
Summary
The B3API logging system provides comprehensive, production-ready observability with minimal developer effort. Whether you use the direct logger API or existing console statements, all logs are captured in structured JSON format and sent to your observability stack for monitoring, alerting, and debugging.