xk6-otel-instrumentation
An enterprise-grade, highly flexible OpenTelemetry extension for Grafana k6.
Instead of using complex JavaScript wrapper functions or generating disconnected synthetic "noise" traces, this extension performs a native monkey-patching intercept on the core k6/http module directly within the Go runtime.
Once initialized, any standard http.get(), http.post(), or http.request() call automatically spawns a real OpenTelemetry client span, injects official W3C traceparent headers across the wire, and exports precise nanosecond-level client-side traces to your APM backend (Jaeger, Tempo, OpenTelemetry Collector, etc.).
π Key Features
- Zero-Code Refactoring: Seamlessly integrates into your existing test repositories. Your teams keep writing standard
k6/http scripts while tracing happens transparently under the hood.
- True E2E Network Hop Visibility: Captures real client-side transport latency (DNS resolution, TLS handshake, and network transit) and displays your downstream application's internal spans nested perfectly inside the k6 root trace.
- Dynamic Resource Attributes: Pass any custom JSON object from JavaScript (e.g.,
domain, service.environment, pipeline.id), and the Go core will dynamically parse and bind them as OTel Resource Attributes.
- Production-Ready Sampling Strategies: Supports adaptive sampling rules (like
TraceIDRatioBased) directly from the test script to optimize network bandwidth and APM storage during high-throughput load tests.
- Agnostic OTLP Exporting: Ships spans natively using the standard OTLP/gRPC protocol to any compliant OpenTelemetry ingestion engine.
π οΈ Installation & Compilation
To build a custom k6 binary packed with this extension, you will need Go installed on your machine.
-
Install the official xk6 bundler tool:
go install go.k6.io/xk6/cmd/xk6@latest
-
Compile your customized k6 binary fetching this extension directly from GitHub:
xk6 build --with [github.com/AleksuKey/xk6-otel-instrumentation@main](https://github.com/AleksuKey/xk6-otel-instrumentation@main)
This will output a native ./k6 executable binary in your current working directory.
π» Usage Quickstart
Simply import the extension and call the .instrument() method at the very beginning of your k6 script (the init context). Pass your native http module along with your custom configuration block.
import { sleep } from 'k6';
// 1. Import native http AND your custom remote extension
import http from 'k6/http';
import otel from 'k6/x/otel-instrumentation';
// 2. Instrument the http module globally BEFORE executing any scenarios
otel.instrument(http, {
endpoint: __ENV.OTLP_ENDPOINT || "localhost:4317",
insecure: true,
sampler: "ratio",
samplingRatio: 0.2, // Traces only 20% of requests to optimize APM storage
resourceAttributes: {
"domain": "benefits",
"service.product": "k6",
"service.name": "k6-agent",
"service.environment": __ENV.ENVIRONMENT || "trn",
"custom.pipeline.id": __ENV.BUILD_BUILDID || "local-run"
}
});
export const options = {
vus: 1,
duration: '10s',
};
export default function (data) {
// 3. Keep using native k6/http syntax!
// This call automatically triggers a Go-level OTel span and propagates headers
http.get('http://localhost:8000/api/v1/catalog/12345');
sleep(1);
}
βοΈ Configuration Options
The .instrument(httpModule, config) method accepts a flexible configuration object with the following parameters:
| Parameter |
Type |
Default |
Description |
endpoint |
String |
(Required) |
The OTLP gRPC endpoint where traces will be sent (e.g., localhost:4317). Do not include protocols like http://. |
insecure |
Boolean |
false |
Disables client transport security (TLS) for the gRPC exporter connection. Set to true for local development. |
sampler |
String |
"always_on" |
Trace sampling strategy. Options: "always_on", "always_off", or "ratio". |
samplingRatio |
Float |
1.0 |
Controlled sample rate ratio. Used only if sampler is set to "ratio". Accepts values between 0.0 (0%) and 1.0 (100%). |
resourceAttributes |
Object |
{} |
A key-value map of custom strings, booleans, integers, or floats to register as OpenTelemetry global process attributes. |
π§ How it Works Under the Hood
When .instrument() is executed, the extension leverages the Sobek JavaScript Runtime engine inside k6 to target and swap out native JavaScript functions (http.get, http.post, etc.) with customized Go interceptors.
[ k6 Script ] ββ> ( Native http.post ) ββ> [ xk6 Interceptor (Go) ]
β
ββββββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββ
βΌ βΌ
[ OpenTelemetry Client Span ] [ HTTP Wire Request ]
- Injects standard W3C 'traceparent' - Transmits HTTP Payload
- Tracks nanosecond metrics (DNS/TLS/Transit) - Propagates Context to Python/Java/Go
- Ships directly via OTLP/gRPC to Jaeger - Triggers seamless downstream cascades
This hybrid execution guarantees that you maintain the incredible raw performance and low memory footprint of k6, while achieving 100% compliant distributed tracing context propagation.
π License
Distributed under the MIT License. See LICENSE for more information.
Copyright (c) 2026 AleksuKey.