Skip to main content

Overview

Development environments benefit from verbose logging to help debug issues and understand application behavior. This guide shows how to configure HTTP Ledger for optimal development experience.

Development Configuration

1

Install the middleware

npm install http-ledger
2

Enable verbose logging

const express = require('express');
const logger = require('http-ledger');

const app = express();

app.use(
  logger({
    // Enable all logging for debugging
    logBody: true,
    logResponse: true,
    logQueryParams: true,

    // Exclude noisy headers
    excludedHeaders: ['user-agent', 'accept-encoding'],

    // Custom formatting for readability
    customFormatter: (logData) => ({
      ...logData,
      // Add color coding for different status codes
      color: logData.statusCode >= 400 ? 'red' : 'green',
      // Add request timing category
      timing: logData.timeTaken > 1000 ? 'slow' : 'fast',
    }),
  }),
);

app.listen(3000);
3

Test the configuration

Start your server and make a test request:
curl -X POST http://localhost:3000/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"John","email":"[email protected]"}'

Full Logging Setup

Capture all request and response details:
const express = require('express');
const logger = require('http-ledger');

const app = express();
app.use(express.json());

app.use(
  logger({
    // Log everything
    logBody: true,
    logResponse: true,
    logQueryParams: true,

    // Only exclude truly noisy headers
    excludedHeaders: ['accept-encoding', 'connection'],

    // Generate request IDs for tracking
    autoGenerateRequestId: true,
  }),
);

app.listen(3000, () => {
  console.log('Development server running on port 3000');
});

Comparing Environments

app.use(
  logger({
    // Enable all logging for debugging
    logBody: true,
    logResponse: true,
    logQueryParams: true,

    // Exclude noisy headers
    excludedHeaders: ['user-agent', 'accept-encoding'],

    // Custom formatting for readability
    customFormatter: (logData) => ({
      ...logData,
      // Add color coding for different status codes
      color: logData.statusCode >= 400 ? 'red' : 'green',
      // Add request timing category
      timing: logData.timeTaken > 1000 ? 'slow' : 'fast',
    }),
  }),
);

Environment-Based Configuration

Automatically adjust settings based on NODE_ENV:
const isDevelopment = process.env.NODE_ENV === 'development';

app.use(
  logger({
    // Verbose logging in development only
    logBody: isDevelopment,
    logResponse: isDevelopment,
    logQueryParams: true,

    // Mask sensitive fields in all environments
    maskFields: ['password', 'token', 'secret'],

    // No sampling in development
    logSampling: isDevelopment ? 1.0 : 0.1,

    // Different header exclusions per environment
    excludedHeaders: isDevelopment
      ? ['accept-encoding', 'connection']
      : ['authorization', 'cookie', 'x-api-key'],

    // Custom log levels based on response
    customLogLevel: (logData) => {
      if (logData.statusCode >= 500) return 'error';
      if (logData.statusCode >= 400) return 'warn';
      if (isDevelopment && logData.timeTaken > 500) return 'warn';
      return 'info';
    },
  }),
);

Debugging Specific Routes

Enable detailed logging only for routes under development:
app.use(
  logger({
    shouldLog: (req, res) => {
      // Always log API routes
      if (req.path.startsWith('/api/')) return true;

      // Log errors
      if (res.statusCode >= 400) return true;

      // Skip health checks and static files
      if (req.path === '/health' || req.path.startsWith('/static/')) {
        return false;
      }

      return true;
    },
    logBody: true,
    logResponse: true,
  }),
);

Local File Logging

Save logs to a file for later analysis:
const fs = require('fs');
const path = require('path');

const logFile = path.join(__dirname, 'logs', 'api.log');

// Ensure logs directory exists
if (!fs.existsSync(path.dirname(logFile))) {
  fs.mkdirSync(path.dirname(logFile), { recursive: true });
}

app.use(
  logger({
    logBody: true,
    logResponse: true,

    onLog: (logData) => {
      // Append to log file
      fs.appendFileSync(
        logFile,
        JSON.stringify(logData, null, 2) + '\n---\n',
        'utf8',
      );
    },
  }),
);

Pretty-Printed Console Output

Format logs for better readability in the console:
app.use(
  logger({
    logBody: true,
    logResponse: true,

    customFormatter: (logData) => {
      // Add visual separators and metadata
      return {
        'πŸ”Ή REQUEST': `${logData.method} ${logData.url}`,
        '⏱️  Timing': `${logData.timeTaken}ms`,
        'πŸ“Š Status': logData.statusCode,
        'πŸ“¦ Request Size': `${logData.requestSize} bytes`,
        'πŸ“¦ Response Size': `${logData.responseSize} bytes`,
        'πŸ“‹ Headers': logData.headers,
        'πŸ” Query': logData.queryParams,
        'πŸ“ Body': logData.body,
        'πŸ“€ Response': logData.responseBody,
        'πŸ• Timestamp': logData.timestamp,
      };
    },
  }),
);

Testing Error Scenarios

Log detailed error information during development:
app.use(
  logger({
    logBody: true,
    logResponse: true,

    customLogLevel: (logData) => {
      // Always use error level for 4xx and 5xx in development
      if (logData.statusCode >= 400) return 'error';
      return 'info';
    },

    customFormatter: (logData) => {
      if (logData.error) {
        return {
          ...logData,
          errorDetails: {
            message: logData.error.message,
            stack: logData.error.stack,
            code: logData.error.code,
          },
        };
      }
      return logData;
    },
  }),
);

// Error handling middleware
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({
    error: err.message,
    stack: process.env.NODE_ENV === 'development' ? err.stack : undefined,
  });
});

Integration with Nodemon

Add scripts to your package.json:
{
  "scripts": {
    "dev": "NODE_ENV=development nodemon src/server.js",
    "dev:verbose": "NODE_ENV=development DEBUG=* nodemon src/server.js",
    "start": "NODE_ENV=production node src/server.js"
  },
  "devDependencies": {
    "nodemon": "^3.0.0"
  }
}
Create nodemon.json configuration:
{
  "watch": ["src"],
  "ext": "js,json",
  "ignore": ["logs/*", "*.test.js"],
  "env": {
    "NODE_ENV": "development"
  }
}

Complete Development Example

const express = require('express');
const logger = require('http-ledger');
const fs = require('fs');
const path = require('path');

const app = express();
app.use(express.json());

const isDevelopment = process.env.NODE_ENV === 'development';
const logDir = path.join(__dirname, 'logs');

// Ensure logs directory exists
if (isDevelopment && !fs.existsSync(logDir)) {
  fs.mkdirSync(logDir, { recursive: true });
}

app.use(
  logger({
    // Full logging in development
    logBody: true,
    logResponse: true,
    logQueryParams: true,

    // Minimal header exclusion
    excludedHeaders: ['accept-encoding'],

    // Auto-generate request IDs
    autoGenerateRequestId: true,

    // Skip only health checks
    shouldLog: (req, res) => req.path !== '/health',

    // Enhanced formatting
    customFormatter: (logData) => ({
      ...logData,
      environment: process.env.NODE_ENV,
      timing: logData.timeTaken > 500 ? 'slow' : 'fast',
      timestamp: new Date().toLocaleString(),
    }),

    // Log to file in development
    onLog: isDevelopment
      ? (logData) => {
          const logFile = path.join(logDir, 'api.log');
          fs.appendFileSync(
            logFile,
            JSON.stringify(logData, null, 2) + '\n---\n',
          );
        }
      : undefined,
  }),
);

// Your routes here
app.get('/api/test', (req, res) => {
  res.json({ message: 'Test successful' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`πŸš€ Development server running on http://localhost:${PORT}`);
  console.log(`πŸ“ Logs are being written to ${logDir}/api.log`);
});

Tips for Development

  • Enable logBody and logResponse to see full request/response data
  • Use autoGenerateRequestId to track requests across your application
  • Set logSampling to 1.0 (or omit it) to log every request
  • Use customFormatter to add visual cues and make logs more readable
  • Log to files with onLog for analysis after testing
Don’t commit development log files to version control. Add logs/ to your .gitignore:
logs/
*.log

Build docs developers (and LLMs) love