Metrics

Built-in metrics, custom metrics, and thresholds

Built-in HTTP Metrics

http_req_duration - Total request time (ms)

http_req_blocked - Time waiting for connection (ms)

http_req_connecting - TCP connection time (ms)

http_req_tls_handshaking - TLS handshake time (ms)

http_req_sending - Time sending request (ms)

http_req_waiting - Time waiting for response (TTFB) (ms)

http_req_receiving - Time receiving response (ms)

http_req_failed - Rate of failed requests

http_reqs - Total number of requests

data_sent - Total bytes sent

data_received - Total bytes received

Other Built-in Metrics

checks - Rate of passed checks

iterations - Total completed iterations

workers - Current number of active workers

workers_max - Maximum workers during test

Iteration Timing Metrics

iteration - Duration excluding sleep time (actual work)

iteration_total - Duration including sleep time (wall-clock)

Example: If an iteration makes two 50ms requests with a 500ms sleep between them, iteration reports ~100ms (work) while iteration_total reports ~600ms (work + sleep).

Connection Pool Metrics

Fusillade automatically tracks HTTP connection pool reuse. No configuration is needed — pool metrics are collected for every test run.

pool_hits

Number of requests that reused an existing connection from the pool

pool_misses

Number of requests that required a new connection to be established

Example Report Output

┌─────────────────────────────────────────────────┐
│ Connection Pool                                 │
├─────────────────────────────────────────────────┤
│ Reused connections .. 14,823 (92.6%)            │
│ New connections ..... 1,177 (7.4%)              │
└─────────────────────────────────────────────────┘

A high pool_hits percentage indicates efficient connection reuse, reducing TCP/TLS handshake overhead and improving throughput. JSON and HTML exports include pool_hits and pool_misses fields in the summary object.

Thresholds

Define pass/fail criteria for your tests. Failed thresholds cause non-zero exit codes for CI/CD.

export const options = {
  thresholds: {
    // 95th percentile response time under 500ms
    'http_req_duration': ['p95 < 500'],

    // 99th percentile under 1 second
    'http_req_duration': ['p99 < 1000'],

    // Average under 200ms
    'http_req_duration': ['avg < 200'],

    // Error rate under 1%
    'http_req_failed': ['rate < 0.01'],

    // At least 100 requests
    'http_reqs': ['count > 100'],

    // Multiple conditions (all must pass)
    'http_req_duration': ['p95 < 500', 'p99 < 1000'],
  }
};

Threshold Operators

p95 < 500 - 95th percentile under 500

p99 < 1000 - 99th percentile under 1000

avg < 200 - Average under 200

min > 10 - Minimum above 10

max < 5000 - Maximum under 5000

rate < 0.01 - Rate under 1% (for error rates)

count > 100 - Count greater than 100

Note: Spaces around operators are required.

Per-URL Thresholds

export const options = {
  thresholds: {
    // All requests
    'http_req_duration': ['p95 < 500'],

    // Specific endpoint using tags
    'http_req_duration{url:https://api.example.com/users}': ['p95 < 200'],
    'http_req_duration{url:https://api.example.com/search}': ['p95 < 1000'],

    // By name tag
    'http_req_duration{name:GET /users/:id}': ['p95 < 300'],
  }
};

Abort on Threshold Failure

export const options = {
  thresholds: {
    'http_req_failed': ['rate < 0.1'],
  },
  abort_on_fail: true,  // Stop test immediately if threshold breached
};

// Or via CLI:
// fusillade run test.js --abort-on-fail

Custom Metrics

Define custom business-level metrics. All functions accept an optional tags object as the last parameter.

metrics.histogramAdd(name, value, [tags])

Track distribution (timing, size)

metrics.counterAdd(name, value, [tags])

Cumulative sum (events, totals)

metrics.gaugeSet(name, value, [tags])

Current value (queue depth, etc)

metrics.rateAdd(name, success, [tags])

Track success rate (boolean)

export const options = {
  thresholds: {
    'checkout_duration': ['p95 < 500'],
    'items_sold': ['count > 100'],
    'payment_success': ['rate > 0.99'],
  }
};

export default function() {
  const start = Date.now();

  // Your custom logic
  const res = http.get('https://api.example.com/data');
  const data = res.json();

  // Track custom timings (with optional tags)
  metrics.histogramAdd('checkout_duration', Date.now() - start, { region: 'us-east' });

  // Track counts
  metrics.counterAdd('items_sold', 3, { category: 'electronics' });

  // Track current state
  metrics.gaugeSet('queue_depth', 42);

  // Track success rate
  metrics.rateAdd('payment_success', true, { provider: 'stripe' });
}

Thresholds on Custom Metrics

export const options = {
  thresholds: {
    // Histogram thresholds
    'data_processing_time': ['p95 < 100'],

    // Counter thresholds
    'items_processed': ['count > 1000'],

    // Rate thresholds
    'valid_responses': ['rate > 0.99'],
  }
};

Runtime Statistics Access

Query collected statistics during the test to enable adaptive testing scenarios.

stats.get(name)

Query collected statistics for a named request or metric

p95 - 95th percentile latency (ms)

p99 - 99th percentile latency (ms)

avg - Average latency (ms)

count - Total request count

min - Minimum latency (ms)

max - Maximum latency (ms)

# Adaptive Testing with stats.get()

export default function() {
  http.get('https://api.example.com/data', { name: 'getData' });

  // Query stats for a named request
  const s = stats.get('getData');
  print(`p95: ${s.p95}ms, avg: ${s.avg}ms, count: ${s.count}`);

  // Adaptive behavior based on performance
  if (s.p95 > 500) {
    print('Warning: p95 latency exceeds 500ms');
  }
}

Metrics Export

Export metrics to various destinations for analysis and monitoring.

# JSON Export

$ fusillade run test.js --export-json results.json

# HTML Report

$ fusillade run test.js --export-html report.html

# CSV Export

$ fusillade run test.js --out csv=metrics.csv

# JUnit XML (CI/CD)

$ fusillade run test.js --out junit=results.xml

# OpenTelemetry

$ fusillade run test.js --out otlp=http://localhost:4317

# StatsD/Datadog/Graphite

$ fusillade run test.js --out statsd=localhost:8125

Real-time Metrics Streaming

Stream metrics to an external endpoint during test execution. Periodic payloads include aggregate stats and per-endpoint breakdown when endpoint tracking is enabled.

# Stream to metrics endpoint
$ fusillade run test.js --metrics-url https://metrics.example.com/ingest

# With authentication
$ fusillade run test.js \
  --metrics-url https://metrics.example.com/ingest \
  --metrics-auth "Authorization: Bearer TOKEN"

# Disable per-endpoint data in streaming payloads
$ fusillade run test.js --metrics-url https://example.com/ingest --no-endpoint-tracking

Per-Endpoint Metrics

By default, Fusillade tracks metrics for each unique endpoint (URL + method) in your test. This data is streamed in real-time and displayed as time-series charts in the test detail view.

What is tracked per endpoint

requests - Total requests to this endpoint

avg_latency_ms - Average response time

p95_latency_ms - 95th percentile response time

errors - Number of failed requests

Dashboard visualization

The test detail page shows per-endpoint time-series charts for latency, requests, and errors. Each endpoint is displayed as a separate line with its own color. During live tests, the dashboard shows a real-time endpoint latency chart.

Disabling endpoint tracking

For tests with high URL cardinality (e.g., URLs containing unique IDs), disable endpoint tracking to reduce memory usage and data volume.

# Via CLI flag
$ fusillade run test.js --no-endpoint-tracking

# Via script options
export const options = {
  track_endpoints: false,
};

Custom Summary Handler

Generate custom reports using the handleSummary lifecycle hook.

export function handleSummary(data) {
  // data contains all metrics and thresholds
  console.log('Total requests:', data.metrics.http_reqs.count);
  console.log('P95 latency:', data.metrics.http_req_duration.p95);
  console.log('Error rate:', data.metrics.http_req_failed.rate);

  // Return files to write
  return {
    'summary.json': JSON.stringify(data, null, 2),
    'summary.txt': generateTextReport(data),
  };
}

function generateTextReport(data) {
  return `
Load Test Report
================
Total Requests: ${data.metrics.http_reqs.count}
Success Rate: ${((1 - data.metrics.http_req_failed.rate) * 100).toFixed(2)}%
P95 Latency: ${data.metrics.http_req_duration.p95.toFixed(2)}ms
`;
}