Metrics are the heart of k6 performance testing. k6 automatically collects built-in metrics and allows you to define custom metrics for your specific testing needs.
Metric Types
k6 supports four metric types, defined in metrics/metric_type.go:
Counter
A metric that sums all added values. From metrics/metric_type.go:
import { Counter } from 'k6/metrics';
const myCounter = new Counter('my_counter');
export default function() {
myCounter.add(1); // Increment by 1
myCounter.add(5); // Increment by 5
// Total: 6
}
Aggregation methods: count, rate
Gauge
A metric that stores the latest value. From metrics/metric_type.go:
import { Gauge } from 'k6/metrics';
const myGauge = new Gauge('my_gauge');
let maxResponseTime = 0.0;
export default function() {
const res = http.get('https://quickpizza.grafana.com/');
maxResponseTime = Math.max(maxResponseTime, res.timings.duration);
myGauge.add(maxResponseTime); // Store current max
}
Aggregation methods: value
Trend
A metric that tracks all values and calculates statistics. From metrics/metric_type.go:
import { Trend } from 'k6/metrics';
const myTrend = new Trend('my_trend');
export default function() {
const res = http.get('https://quickpizza.grafana.com/');
// Track connection + TLS time
myTrend.add(res.timings.connecting + res.timings.tls_handshaking);
}
Aggregation methods: avg, min, max, med, p(N) (percentiles)
Rate
A metric that tracks the percentage of non-zero values. From metrics/metric_type.go:
import { Rate } from 'k6/metrics';
import { check } from 'k6';
const myRate = new Rate('my_rate');
export default function() {
const res = http.get('https://quickpizza.grafana.com/');
const passed = check(res, { 'status is 200': (r) => r.status === 200 });
myRate.add(passed); // Add true (1) or false (0)
// Rate calculates: (number of trues) / (total samples)
}
Aggregation methods: rate
Built-in Metrics
k6 automatically collects comprehensive metrics defined in metrics/builtin.go:
HTTP Metrics
From metrics/builtin.go, HTTP-related metrics:
http_reqs
Type: Counter
Description: Total number of HTTP requests
http_req_failed
Type: Rate
Description: Rate of failed requests (status >= 400 or network error)
export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // Less than 1% failures
},
};
http_req_duration
Type: Trend (Time)
Description: Total request time (sending + waiting + receiving)
export const options = {
thresholds: {
http_req_duration: ['p(95)<500'], // 95% under 500ms
},
};
http_req_blocked
Type: Trend (Time)
Description: Time blocked before initiating request (waiting for free TCP connection)
http_req_connecting
Type: Trend (Time)
Description: Time spent establishing TCP connection
http_req_tls_handshaking
Type: Trend (Time)
Description: Time spent in TLS handshake
http_req_sending
Type: Trend (Time)
Description: Time spent sending request data
http_req_waiting
Type: Trend (Time)
Description: Time spent waiting for response (TTFB - Time To First Byte)
http_req_receiving
Type: Trend (Time)
Description: Time spent receiving response data
VU and Iteration Metrics
vus
Type: Gauge
Description: Current number of active virtual users
vus_max
Type: Gauge
Description: Maximum number of VUs initialized
iterations
Type: Counter
Description: Total number of completed iterations
iteration_duration
Type: Trend (Time)
Description: Time to complete one full iteration
dropped_iterations
Type: Counter
Description: Iterations that couldn’t start due to time constraints
Check Metrics
checks
Type: Rate
Description: Success rate of checks
import { check } from 'k6';
export const options = {
thresholds: {
checks: ['rate>0.95'], // 95% of checks must pass
},
};
export default function() {
const res = http.get('https://quickpizza.grafana.com/');
check(res, {
'status is 200': (r) => r.status === 200,
});
}
Group Metrics
group_duration
Type: Trend (Time)
Description: Time spent inside a group
import { group } from 'k6';
export default function() {
group('user flow', function() {
http.get('https://quickpizza.grafana.com/');
http.get('https://quickpizza.grafana.com/api/pizza');
});
}
WebSocket Metrics
From metrics/builtin.go, WebSocket-specific metrics:
ws_sessions (Counter) - Total WebSocket sessions
ws_msgs_sent (Counter) - Messages sent
ws_msgs_received (Counter) - Messages received
ws_ping (Trend, Time) - Ping duration
ws_session_duration (Trend, Time) - Session duration
ws_connecting (Trend, Time) - Connection time
gRPC Metrics
grpc_req_duration
Type: Trend (Time)
Description: gRPC request duration
Network Metrics
data_sent
Type: Counter (Data)
Description: Amount of data sent (bytes)
data_received
Type: Counter (Data)
Description: Amount of data received (bytes)
Custom Metrics
Create custom metrics to track application-specific data. From examples/custom_metrics.js:
import http from 'k6/http';
import { Counter, Gauge, Rate, Trend } from 'k6/metrics';
import { check } from 'k6';
// Define custom metrics in init context
const myCounter = new Counter('my_counter');
const myGauge = new Gauge('my_gauge');
const myRate = new Rate('my_rate');
const myTrend = new Trend('my_trend');
let maxResponseTime = 0.0;
export default function () {
const res = http.get('https://quickpizza.grafana.com/');
const passed = check(res, { 'status is 200': (r) => r.status === 200 });
// Add one for number of requests
myCounter.add(1);
// Set max response time seen
maxResponseTime = Math.max(maxResponseTime, res.timings.duration);
myGauge.add(maxResponseTime);
// Add check success or failure to keep track of rate
myRate.add(passed);
// Keep track of TCP-connecting and TLS handshaking part of response time
myTrend.add(res.timings.connecting + res.timings.tls_handshaking);
}
From metrics/tags.go, metrics can be filtered using tags:
Automatic tags from metrics/system_tag.go:
method - HTTP method
status - HTTP status code
url - Request URL
name - Request name
group - Group name
check - Check name
scenario - Scenario name
service - Service name (for gRPC)
Add custom tags to requests:
import http from 'k6/http';
export default function() {
// Tag individual requests
http.get('https://quickpizza.grafana.com/api/pizza', {
tags: { my_tag: 'pizza_api' },
});
// Filter metrics by tag in thresholds
}
export const options = {
thresholds: {
'http_req_duration{my_tag:pizza_api}': ['p(95)<200'],
},
};
Apply tags to all metrics:
export const options = {
tags: {
environment: 'production',
team: 'backend',
},
};
Metric Output
End-of-Test Summary
k6 displays metric statistics at test completion:
✓ status is 200
checks.........................: 100.00% ✓ 150 ✗ 0
data_received..................: 4.5 MB 75 kB/s
data_sent......................: 13 kB 217 B/s
http_req_blocked...............: avg=1.5ms min=1µs med=5µs max=145ms p(95)=7µs
http_req_connecting............: avg=516µs min=0s med=0s max=48ms p(95)=0s
http_req_duration..............: avg=123ms min=101ms med=115ms max=245ms p(95)=187ms
http_req_failed................: 0.00% ✓ 0 ✗ 150
http_req_receiving.............: avg=128µs min=22µs med=85µs max=1.5ms p(95)=294µs
http_req_sending...............: avg=34µs min=7µs med=24µs max=208µs p(95)=80µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(95)=0s
http_req_waiting...............: avg=122ms min=101ms med=115ms max=244ms p(95)=186ms
http_reqs......................: 150 2.5/s
iteration_duration.............: avg=1.12s min=1.1s med=1.11s max=1.24s p(95)=1.18s
iterations.....................: 150 2.5/s
vus............................: 3 min=3 max=3
vus_max........................: 3 min=3 max=3
Metric Value Types
From metrics/value_type.go, metrics can represent:
- Default - Generic numeric values
- Time - Duration values (milliseconds)
- Data - Data size values (bytes)
import { Trend } from 'k6/metrics';
// Time metric
const responseTime = new Trend('response_time', true);
// Default metric
const itemCount = new Trend('item_count');
Advanced Metric Patterns
Tracking Business Metrics
import { Counter, Trend } from 'k6/metrics';
const ordersCreated = new Counter('orders_created');
const orderValue = new Trend('order_value');
export default function() {
const res = http.post('https://api.example.com/orders', JSON.stringify({
items: ['pizza', 'soda'],
total: 25.50,
}));
if (res.status === 201) {
ordersCreated.add(1);
orderValue.add(25.50);
}
}
Tracking Error Types
import { Counter } from 'k6/metrics';
const errors4xx = new Counter('errors_4xx');
const errors5xx = new Counter('errors_5xx');
export default function() {
const res = http.get('https://quickpizza.grafana.com/api/pizza');
if (res.status >= 400 && res.status < 500) {
errors4xx.add(1);
} else if (res.status >= 500) {
errors5xx.add(1);
}
}
Conditional Metrics
import { Trend } from 'k6/metrics';
const fastRequests = new Trend('fast_requests');
const slowRequests = new Trend('slow_requests');
export default function() {
const res = http.get('https://quickpizza.grafana.com/');
if (res.timings.duration < 200) {
fastRequests.add(res.timings.duration);
} else {
slowRequests.add(res.timings.duration);
}
}
Best Practices
Use Built-in Metrics First
Built-in metrics cover most use cases. Only add custom metrics when necessary.
Choose the Right Metric Type
Use Counter for totals, Gauge for latest values, Trend for statistics, and Rate for success rates.
Add Meaningful Tags
Tag metrics to enable filtering and detailed analysis.
Combine Metrics with Thresholds
Use metrics with thresholds to define pass/fail criteria.
Custom metrics are defined in the init context but recorded in the default function.
Too many custom metrics can impact test performance and increase memory usage.
Metrics provide the quantitative data you need to understand system performance and make informed decisions about scalability and reliability.