Track Axios HTTP Metrics in Prometheus and Datadog

Capturing outbound HTTP metrics is essential for diagnosing latency bottlenecks and tracking third-party API reliability across your infrastructure. This guide covers how to use Axios interceptors to capture network performance data—such as request duration, response status codes, and errors—and export those metrics to monitoring systems like Prometheus and Datadog.

Intercepting Axios Requests

To collect metrics without modifying individual API calls, attach interceptors to your Axios instance. The request interceptor records the start time, while the response and error interceptors compute the total duration and record the outcome.

const axios = require('axios');

const apiClient = axios.create({
  baseURL: 'https://api.example.com',
});

// Record start timestamp
apiClient.interceptors.request.use((config) => {
  config.metadata = { startTime: performance.now() };
  return config;
}, (error) => {
  return Promise.reject(error);
});

// Process duration and status
apiClient.interceptors.response.use(
  (response) => {
    const duration = performance.now() - response.config.metadata.startTime;
    recordMetrics({
      method: response.config.method.toUpperCase(),
      route: response.config.url,
      status: response.status,
      duration,
    });
    return response;
  },
  (error) => {
    const config = error.config;
    if (config && config.metadata) {
      const duration = performance.now() - config.metadata.startTime;
      recordMetrics({
        method: config.method ? config.method.toUpperCase() : 'UNKNOWN',
        route: config.url || 'UNKNOWN',
        status: error.response ? error.response.status : 'NETWORK_ERROR',
        duration,
      });
    }
    return Promise.reject(error);
  }
);

Reporting Metrics to Prometheus

Prometheus uses a pull model where the application exposes an endpoint scraped by Prometheus. Using the official prom-client library in Node.js, create a Counter for request counts and a Histogram for request latency.

const client = require('prom-client');

const httpRequestsTotal = new client.Counter({
  name: 'axios_http_requests_total',
  help: 'Total number of outbound HTTP requests made by Axios',
  labelNames: ['method', 'target', 'status_code'],
});

const httpRequestDuration = new client.Histogram({
  name: 'axios_http_request_duration_seconds',
  help: 'Duration of outbound HTTP requests made by Axios in seconds',
  labelNames: ['method', 'target', 'status_code'],
  buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
});

function recordMetrics({ method, route, status, duration }) {
  const normalizedRoute = sanitizeRoute(route);
  const labels = {
    method,
    target: normalizedRoute,
    status_code: status.toString(),
  };

  httpRequestsTotal.inc(labels);
  httpRequestDuration.observe(labels, duration / 1000); // Convert milliseconds to seconds
}

Reporting Metrics to Datadog

Datadog commonly ingests metrics via DogStatsD, a lightweight UDP daemon. The hot-shots client library allows you to publish counters and distributions directly from your interceptor.

const StatsD = require('hot-shots');
const dogstatsd = new StatsD({
  host: process.env.DD_AGENT_HOST || 'localhost',
  port: 8125,
  prefix: 'axios.',
});

function recordMetrics({ method, route, status, duration }) {
  const normalizedRoute = sanitizeRoute(route);
  const tags = [
    `method:${method.toLowerCase()}`,
    `target:${normalizedRoute}`,
    `status_code:${status}`,
    `status_family:${Math.floor(Number(status) / 100) || 'error'}xx`,
  ];

  dogstatsd.increment('http.requests.total', 1, tags);
  dogstatsd.distribution('http.request.duration', duration, tags);
}

Preventing Metric Cardinality Issues

URL paths containing dynamic parameters (such as /users/12345/orders) generate unique labels for every request, which leads to high-cardinality issues that degrade Prometheus and Datadog performance.

Always sanitize routes before assigning them to metric labels:

function sanitizeRoute(url) {
  if (!url) return 'unknown';
  return url
    .replace(/\/[0-9a-fA-F-]{36}/g, '/:uuid') // Replace UUIDs
    .replace(/\/\d+/g, '/:id');                // Replace numeric IDs
}

By structuring route definitions and sanitizing paths, you preserve low cardinality while gaining visibility into third-party latency, timeouts, and error rates across all Axios instances.