Skip to content

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:

json
{
  "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

typescript
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

typescript
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:

typescript
// 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 only
  • LOG_LEVEL=error - Show only errors

Querying Logs in Grafana/Loki

Basic Queries

logql
# 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 > 1000

Advanced Queries

logql
# 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:

typescript
logger.info({ msg: 'Payment processed', amount: 99.99, currency: 'USD', user_id: 123 });

Bad:

typescript
logger.info({ msg: 'Payment processed' }); // Missing context

2. Include Relevant Context

Always include identifiers that help track operations:

  • user_id - User performing the action
  • order_id, report_id, etc. - Resource identifiers
  • transaction_id - For tracking multi-step operations
  • duration_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:

typescript
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:

typescript
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:

  1. Main API Server (src/index.ts)
  2. Cron Worker (src/services/workers/cron/cron-worker.ts)
  3. Cron Scheduler (src/services/workers/cron/cron-scheduler.ts)
  4. Email Service (src/services/workers/emailService/emailService.ts)
  5. 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:

typescript
console.log('User created:', userId);
console.error('Error:', error);

After:

typescript
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

bash
# 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 responses

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.

Syneo/Barcoding Documentation