npm.io
1.0.0 • Published 7 months ago

@flink-app/debug-plugin

Licence
MIT
Version
1.0.0
Deps
3
Size
41 kB
Vulns
0
Weekly
0

Debug Plugin

A Flink plugin for debugging HTTP requests and responses in your application. Capture and inspect request/response data, enable/disable debugging on demand, and integrate with the Management API for admin panel access.

Installation

Install the plugin to your Flink app project:

npm install @flink-app/debug-plugin

Configuration

Basic Setup

Configure the plugin in your app startup:

import { FlinkApp } from "@flink-app/flink";
import { debugPlugin } from "@flink-app/debug-plugin";

function start() {
  new FlinkApp<AppContext>({
    name: "My app",
    plugins: [
      debugPlugin({
        enabledAtStart: false,  // Start with debugging disabled
        logToConsole: false,    // Don't log to console
        keepLogs: 100          // Keep last 100 requests
      })
    ],
  }).start();
}

Plugin Options:

interface StaticOptions {
  logToConsole: boolean;   // Log requests/responses to console
  enabledAtStart: boolean; // Enable debugging when app starts
  keepLogs?: number;       // Number of requests to keep in memory (default: 100)
}

TypeScript Setup

The debug plugin doesn't require adding types to your context, but it does add properties to ctx.plugins.debugPlugin:

ctx.plugins.debugPlugin: {
  requests: request[];  // Array of captured requests
  enabled: boolean;     // Current enabled state
}

How It Works

The debug plugin uses Express middleware to:

  1. Intercept incoming HTTP requests
  2. Capture request details (method, path, body, headers)
  3. Intercept outgoing responses by wrapping res.write and res.end
  4. Store request/response pairs in memory
  5. Keep a rolling window of the most recent N requests

What's Captured:

  • Request method (GET, POST, etc.)
  • Request path and query parameters
  • Request headers
  • Request body
  • Response body
  • Request start and end timestamps

What's NOT Captured:

  • Management API requests (routes starting with /managementapi)
  • Requests when debugging is disabled

Accessing Debug Data

From Handlers

Access captured requests directly from context:

import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";

const GetDebugInfo: Handler<Ctx, any, any> = async ({ ctx, req }) => {
  const debugData = ctx.plugins.debugPlugin;

  return {
    data: {
      enabled: debugData.enabled,
      requestCount: debugData.requests.length,
      recentRequests: debugData.requests.slice(0, 10)
    }
  };
};

export default GetDebugInfo;
Enabling/Disabling Debug Mode

Toggle debug mode programmatically:

// Enable debugging
ctx.plugins.debugPlugin.enabled = true;

// Disable debugging
ctx.plugins.debugPlugin.enabled = false;

// Clear captured requests
ctx.plugins.debugPlugin.requests = [];

Management API Integration

The debug plugin can be exposed as a Management API module for easy access from admin panels:

Setup Management Module
import { FlinkApp } from "@flink-app/flink";
import { Ctx } from "./Ctx";
import {
  debugPlugin,
  GetManagementModule as GetDebugManagementModule
} from "@flink-app/debug-plugin";
import { managementApiPlugin } from "@flink-app/management-api-plugin";

const debugManagementModule = GetDebugManagementModule({
  ui: true,
  uiSettings: {
    title: "Debug Console"
  }
});

function start() {
  new FlinkApp<Ctx>({
    name: "My flink app",
    debug: true,
    db: {
      uri: "mongodb://localhost:27017/my-flink-app"
    },
    plugins: [
      debugPlugin({
        enabledAtStart: false,
        logToConsole: false,
        keepLogs: 100
      }),
      managementApiPlugin({
        token: process.env.MGMT_TOKEN!,
        jwtSecret: process.env.JWT_SECRET!,
        modules: [debugManagementModule]
      })
    ]
  }).start();
}

start();

Management Module Options:

interface GetManagementModuleConfig {
  pluginId?: string;   // Default: "debug"
  ui: boolean;         // Enable UI in admin panel
  uiSettings?: {
    title: string;     // Module title (default: "Debug")
  };
}
Management Endpoints

When using the Management Module, these endpoints are registered under /managementapi/{pluginId}:

GET /managementapi/debug/

Get debug status and captured requests.

Response:

{
  data: {
    enabled: boolean;    // Current debug state
    requests: request[]; // Array of captured requests
  }
}

Example:

curl http://localhost:3000/managementapi/debug/
POST /managementapi/debug/enable

Enable debug mode.

Response:

{
  data: {
    enabled: true
  }
}

Example:

curl -X POST http://localhost:3000/managementapi/debug/enable
POST /managementapi/debug/disable

Disable debug mode.

Response:

{
  data: {
    enabled: false
  }
}

Example:

curl -X POST http://localhost:3000/managementapi/debug/disable

API Reference

Request Type

Each captured request has the following structure:

interface request {
  start: Date;                      // When request started
  end?: Date;                       // When response finished
  method: string;                   // HTTP method (GET, POST, etc.)
  path: string;                     // Request path with query params
  headers?: IncomingHttpHeaders;    // Request headers
  body?: any;                       // Parsed request body
  response?: string;                // Response body as string
}
Context Properties
ctx.plugins.debugPlugin: {
  requests: request[];  // Array of captured requests (newest first)
  enabled: boolean;     // Whether debugging is currently enabled
}

Complete Example

Here's a complete example showing how to use the debug plugin:

import { FlinkApp } from "@flink-app/flink";
import { Ctx } from "./Ctx";
import {
  debugPlugin,
  GetManagementModule as GetDebugManagementModule
} from "@flink-app/debug-plugin";
import { managementApiPlugin } from "@flink-app/management-api-plugin";

// Create debug management module
const debugManagementModule = GetDebugManagementModule({
  ui: true,
  uiSettings: {
    title: "Request Debugger"
  }
});

function start() {
  new FlinkApp<Ctx>({
    name: "My API",
    debug: true,
    db: {
      uri: process.env.MONGODB_URI!
    },
    plugins: [
      // Debug plugin - starts disabled, keeps last 200 requests
      debugPlugin({
        enabledAtStart: false,
        logToConsole: true,  // Also log to console
        keepLogs: 200
      }),

      // Management API with debug module
      managementApiPlugin({
        token: process.env.MGMT_TOKEN!,
        jwtSecret: process.env.JWT_SECRET!,
        modules: [debugManagementModule]
      })
    ]
  }).start();
}

start();
Custom Debug Handler

Create a custom handler to analyze debug data:

import { Handler } from "@flink-app/flink";
import { Ctx } from "../Ctx";

interface DebugStatsResponse {
  totalRequests: number;
  methodCounts: { [method: string]: number };
  slowestRequests: Array<{
    path: string;
    duration: number;
  }>;
  errorResponses: number;
}

const GetDebugStats: Handler<Ctx, any, DebugStatsResponse> = async ({ ctx }) => {
  const requests = ctx.plugins.debugPlugin.requests;

  // Count requests by method
  const methodCounts: { [method: string]: number } = {};
  for (const req of requests) {
    methodCounts[req.method] = (methodCounts[req.method] || 0) + 1;
  }

  // Find slowest requests
  const slowestRequests = requests
    .filter(req => req.end)
    .map(req => ({
      path: req.path,
      duration: req.end!.getTime() - req.start.getTime()
    }))
    .sort((a, b) => b.duration - a.duration)
    .slice(0, 10);

  // Count error responses (5xx, 4xx)
  const errorResponses = requests.filter(req => {
    if (!req.response) return false;
    try {
      const response = JSON.parse(req.response);
      return response.status >= 400;
    } catch {
      return false;
    }
  }).length;

  return {
    data: {
      totalRequests: requests.length,
      methodCounts,
      slowestRequests,
      errorResponses
    }
  };
};

export default GetDebugStats;

Use Cases

1. Development Debugging

Enable debug mode during development to inspect requests:

debugPlugin({
  enabledAtStart: true,
  logToConsole: true,
  keepLogs: 50
})
2. Production Issue Investigation

Keep debug mode off by default, enable on demand when investigating:

debugPlugin({
  enabledAtStart: false,
  logToConsole: false,
  keepLogs: 200  // Keep more logs for analysis
})

Then enable via Management API when needed.

3. API Testing

Use debug data to verify request/response pairs:

// Make test request
await fetch("http://localhost:3000/api/users", {
  method: "POST",
  body: JSON.stringify({ name: "John" })
});

// Check debug data
const debugData = ctx.plugins.debugPlugin.requests[0];
expect(debugData.body).toEqual({ name: "John" });
expect(JSON.parse(debugData.response!)).toHaveProperty("id");
4. Performance Monitoring

Track request durations to identify slow endpoints:

const slowRequests = ctx.plugins.debugPlugin.requests
  .filter(req => req.end)
  .filter(req => {
    const duration = req.end!.getTime() - req.start.getTime();
    return duration > 1000; // Slower than 1 second
  });

console.log(`Found ${slowRequests.length} slow requests`);

Best Practices

Memory Management

The plugin keeps requests in memory. Be careful with settings:

// Good for development
keepLogs: 50

// Good for temporary production debugging
keepLogs: 200

// Be careful - could use significant memory
keepLogs: 10000  // Avoid in production

Each request object includes:

  • Headers (can be large)
  • Request body (can be large)
  • Response body (can be large)
Security Considerations
  1. Disable in Production: Don't enable debug mode in production by default
  2. Sensitive Data: Be aware that debug logs capture request bodies and headers
  3. Access Control: Use Management API with proper authentication
  4. Clear Logs: Regularly clear captured requests to avoid memory leaks
// Clear debug logs periodically
setInterval(() => {
  if (ctx.plugins.debugPlugin.requests.length > 500) {
    ctx.plugins.debugPlugin.requests = [];
  }
}, 60000); // Every minute
Console Logging

Use logToConsole: true carefully:

// Development - useful
debugPlugin({
  logToConsole: true,
  enabledAtStart: true
})

// Production - avoid (too noisy)
debugPlugin({
  logToConsole: false,
  enabledAtStart: false
})
Filtering Sensitive Data

Consider wrapping the plugin to filter sensitive data:

// In your handler
const sanitizedRequests = ctx.plugins.debugPlugin.requests.map(req => ({
  ...req,
  headers: {
    ...req.headers,
    authorization: req.headers?.authorization ? "[REDACTED]" : undefined
  },
  body: req.body?.password ? { ...req.body, password: "[REDACTED]" } : req.body
}));

return { data: { requests: sanitizedRequests } };

Implementation Details

Middleware Order

The debug plugin middleware is registered during plugin initialization and runs for all requests except:

  • Routes starting with /managementapi (to avoid recursion)
  • Requests when enabled is false
Response Interception

The plugin intercepts responses by:

  1. Saving original res.write and res.end functions
  2. Replacing them with custom implementations
  3. Collecting response chunks in a buffer
  4. Restoring original functions after response completes

This ensures the plugin captures the full response without affecting normal operation.

Rolling Window

When the request array exceeds keepLogs:

requests = requests.splice(0, keep);

This keeps only the most recent N requests, with newest first (unshift).

Troubleshooting

Debug Mode Not Working
  1. Check enabled state:

    console.log(ctx.plugins.debugPlugin.enabled);
  2. Verify plugin initialization:

    console.log(app.ctx.plugins.debugPlugin); // Should exist
  3. Check Management API routes:

    # Should work
    curl http://localhost:3000/managementapi/debug/
Requests Not Being Captured
  1. Enable debug mode:

    curl -X POST http://localhost:3000/managementapi/debug/enable
  2. Check if route is excluded:

    • Management API routes are not captured
    • Verify your route doesn't start with /managementapi
  3. Check keepLogs setting:

    • If you have many requests, older ones get removed
    • Increase keepLogs value
Memory Usage High
  1. Reduce keepLogs:

    keepLogs: 50  // Keep fewer logs
  2. Clear logs periodically:

    ctx.plugins.debugPlugin.requests = [];
  3. Disable when not needed:

    curl -X POST http://localhost:3000/managementapi/debug/disable
Console Too Noisy

Set logToConsole: false:

debugPlugin({
  enabledAtStart: true,
  logToConsole: false,  // No console output
  keepLogs: 100
})

Notes

  • The plugin automatically excludes Management API routes to avoid recursion
  • Request bodies are captured after body-parser middleware processes them
  • Response bodies are captured as strings (includes JSON, HTML, etc.)
  • The plugin adds minimal performance overhead when disabled
  • Debug data is stored in memory only (not persisted to database)
  • The enabled flag can be toggled at runtime without restarting the app