Smart Logging

Production-ready logging system with multiple transports, correlation IDs, and performance tracking.

Overview

┌──────────────────────────────────────────────────────────────┐
│                    SmartLogger Service                       │
│                                                              │
│  Log Entry ──▶ Transport Router ──▶ Console (dev)            │
│                                   ──▶ File (prod)            │
│                                   ──▶ MongoDB (errors)       │
└──────────────────────────────────────────────────────────────┘

Log Types

1. Request Logs

{
  "type": "request",
  "level": "info",
  "timestamp": "2025-01-15T10:30:00Z",
  "correlationId": "req_01ABCDE123",
  "service": "bun-core",
  "request": {
    "method": "POST",
    "path": "/api/v1/users/register",
    "query": { "lang": "ms" },
    "ip": "192.168.1.1"
  },
  "response": {
    "statusCode": 201,
    "duration": 145,
    "size": 1024
  },
  "performance": {
    "databaseQueries": 3,
    "databaseTime": 45,
    "cacheHits": 2
  }
}

2. Error Logs

{
  "type": "error",
  "level": "error",
  "timestamp": "2025-01-15T10:30:00Z",
  "correlationId": "req_01ABCDE123",
  "error": {
    "name": "UserNotFoundError",
    "message": "User with ID 123 not found",
    "code": "USER_NOT_FOUND",
    "statusCode": 404,
    "stack": "..."
  },
  "context": {
    "userId": "123",
    "locale": "en"
  }
}

3. Performance Logs

{
  "type": "performance",
  "level": "warn",
  "timestamp": "2025-01-15T10:30:00Z",
  "operation": {
    "name": "UserRepository.findById",
    "type": "database",
    "duration": 250,
    "threshold": 100
  },
  "alert": {
    "level": "warning",
    "reason": "Operation exceeded threshold of 100ms"
  }
}

4. Security Logs

{
  "type": "security",
  "level": "warn",
  "timestamp": "2025-01-15T10:30:00Z",
  "event": "failed_login_attempt",
  "severity": "medium",
  "actor": {
    "email": "user@example.com",
    "ip": "123.45.67.89"
  },
  "details": {
    "attemptCount": 3,
    "reason": "invalid_password"
  }
}

Transports

TransportEnvironmentPurpose
Consoledev + prodReal-time output
Filestaging + prodLong-term storage
MongoDBstaging + prodError tracking & queries

Console Transport

  • Development: Pretty-printed, colored
  • Production: JSON format

File Transport

LOG_TO_FILE=true
LOG_FILE_PATH=/var/log/gremlin/app.log
LOG_FILE_MAX_SIZE=100MB
LOG_FILE_MAX_FILES=30
Output format: JSON lines (one entry per line)
{"type":"request",...}
{"type":"error",...}
{"type":"performance",...}

MongoDB Transport

LOG_TO_DB=true
Indexes for fast queries:
  • timestamp (descending)
  • correlationId
  • type
  • level
TTL: 30 days (automatic expiration)

Usage

Basic Logging

await logger.info('User registered', { userId: 'usr_123' });
await logger.error('Failed to save user', { error: err });
await logger.warn('Slow query detected', { duration: 250 });

Request Logging

Automatic via middleware — no manual logging needed:
// Middleware automatically logs:
// - Incoming request
// - Response (status, duration, size)
// - User info (if authenticated)
// - Performance metrics

Error Logging

Throw errors — middleware handles logging:
throw new UserNotFoundError(userId);

// Logs include:
// - Full error details
// - Stack trace
// - Request context
// - Correlation ID

Performance Tracking

@TrackPerformance({ threshold: 100, type: 'database' })
async findById(id: string) {
  // Automatically logs if > 100ms
  return await this.collection.findOne({ _id: id });
}

Security Events

await logger.logSecurity({
  event: 'suspicious_activity',
  severity: 'high',
  actor: { ip: '1.2.3.4' },
  details: { reason: 'rapid_requests' },
});

Configuration

# Level: debug | info | warn | error
LOG_LEVEL=info

# File
LOG_TO_FILE=false
LOG_FILE_PATH=/var/log/gremlin/app.log

# MongoDB
LOG_TO_DB=false

Correlation ID

Every request gets a correlation ID for end-to-end tracking:
req_01ABCDE123
Passed via:
  • Request: X-Correlation-ID header
  • Response: Same header echoed back
  • Logs: correlationId field

Querying Logs

MongoDB

// Find all errors for a request
db.logs.find({ correlationId: 'req_01ABCDE' });

// Find slow database queries
db.logs.find({
  type: 'performance',
  'operation.type': 'database',
  'operation.duration': { $gt: 1000 },
});

// Aggregation: Error counts by type
db.logs.aggregate([
  { $match: { type: 'error' } },
  { $group: { _id: '$error.code', count: { $sum: 1 } } },
  { $sort: { count: -1 } },
]);

File

# Search for errors
grep '"level":"error"' /var/log/gremlin/app-2025-01-15.log

# Find slow queries
jq 'select(.type=="performance" and .operation.duration > 1000)' \
  /var/log/gremlin/app-2025-01-15.log

Best Practices

DO ✅

  • Use correlation IDs for request tracking
  • Log at appropriate levels (info for actions, warn for warnings, error for failures)
  • Include relevant context in log entries
  • Use structured logging (objects, not strings)

DON’T ❌

  • Log sensitive data (passwords, tokens, credit cards)
  • Log in tight loops (causes log flooding)
  • Use console.log instead of the logger
  • Log without context