No results for

Powered byAlgolia
⚠️ This documentation is outdated. Please visit grafana.com for the latest k6 documentation.📚

Output Extensions

k6 provides many metrics and output formats, but it cannot directly support all possibilities. To store or alter metrics captured during an active k6 test, you can create a custom output extension.

Output extension binaries can use the --out flag to send metrics to a custom place. Some potential reasons for a custom extension could include:

  • To support a time-series database not already supported
  • To add derived metrics data for storage
  • To filter metrics to only the data you care about

Like JavaScript extensions, output extensions rely on the extension author to implement specific APIs.

Before you start:

To run this tutorial, you'll need the following applications installed:

  • Go
  • Git

You also need to install xk6:

$ go install go.k6.io/xk6/cmd/xk6@latest

Write a simple extension

  1. Set up a directory to work in.

    $ mkdir xk6-output-logger; cd xk6-output-logger; go mod init xk6-output-logger
  2. The core of an Output extension is a struct that implements the output.Output interface.

    Create a simple example that outputs each set of metrics to the console as received by the AddMetricSamples(samples []metrics.SampleContainer) method of the output interface.

    package log
    import (
    "fmt"
    "strings"
    "time"
    "go.k6.io/k6/metrics"
    "go.k6.io/k6/output"
    )
    // AddMetricSamples receives metric samples from the k6 Engine as they're emitted.
    func (l *Logger) AddMetricSamples(samples []metrics.SampleContainer) {
    for _, sample := range samples {
    all := sample.GetSamples()
    fmt.Fprintf(l.out, "%s [%s]\n", all[0].GetTime().Format(time.RFC3339Nano), metricKeyValues(all))
    }
    }
    // metricKeyValues returns a string of key-value pairs for all metrics in the sample.
    func metricKeyValues(samples []metrics.Sample) string {
    names := make([]string, 0, len(samples))
    for _, sample := range samples {
    names = append(names, fmt.Sprintf("%s=%v", sample.Metric.Name, sample.Value))
    }
    return strings.Join(names, ", ")
    }
  3. Register the module to use these from k6 test scripts.

    import "go.k6.io/k6/output"
    // init is called by the Go runtime at application startup.
    func init() {
    output.RegisterExtension("logger", New)
    }
note

You must use the registered with the -o, or --out flag when running k6!

The final extension code looks like this:

log.go
1package log
2
3import (
4 "fmt"
5 "io"
6 "strings"
7 "time"
8
9 "go.k6.io/k6/metrics"
10 "go.k6.io/k6/output"
11)
12
13// init is called by the Go runtime at application startup.
14func init() {
15 output.RegisterExtension("logger", New)
16}
17
18// Logger writes k6 metric samples to stdout.
19type Logger struct {
20 out io.Writer
21}
22
23// New returns a new instance of Logger.
24func New(params output.Params) (output.Output, error) {
25 return &Logger{params.StdOut}, nil
26}
27
28// Description returns a short human-readable description of the output.
29func (*Logger) Description() string {
30 return "logger"
31}
32
33// Start initializes any state needed for the output, establishes network
34// connections, etc.
35func (o *Logger) Start() error {
36 return nil
37}
38
39// AddMetricSamples receives metric samples from the k6 Engine as they're emitted.
40func (l *Logger) AddMetricSamples(samples []metrics.SampleContainer) {
41 for _, sample := range samples {
42 all := sample.GetSamples()
43 fmt.Fprintf(l.out, "%s [%s]\n", all[0].GetTime().Format(time.RFC3339Nano), metricKeyValues(all))
44 }
45}
46
47// metricKeyValues returns a string of key-value pairs for all metrics in the sample.
48func metricKeyValues(samples []metrics.Sample) string {
49 names := make([]string, 0, len(samples))
50 for _, sample := range samples {
51 names = append(names, fmt.Sprintf("%s=%v", sample.Metric.Name, sample.Value))
52 }
53 return strings.Join(names, ", ")
54}
55
56// Stop finalizes any tasks in progress, closes network connections, etc.
57func (*Logger) Stop() error {
58 return nil
59}

Notice a couple of things:

  • The module initializer New() receives an instance of output.Params. With this object, the extension can access the output-specific configuration, interfaces to the filesystem, synchronized stdout and stderr, and more.

  • AddMetricSamples in this example writes to stdout. This output might have to be buffered and flushed periodically in a real-world scenario to avoid memory leaks. Below we'll discuss some helpers you can use for that.

Compile your extended k6

To build a k6 binary with this extension, run:

$ xk6 build --with xk6-output-logger=.
note

xk6-output-logger is the Go module name passed to go mod init

Usually, this would be a URL similar to github.com/grafana/xk6-output-logger.

Use your extension

Now we can use the extension with a test script.

  1. In new JavaScript file, make some simple test logic.

    test.js
    1import http from 'k6/http';
    2import { sleep } from 'k6';
    3
    4export default function () {
    5 http.get('https://test-api.k6.io');
    6 sleep(0.5);
    7}
  2. Now, run the test.

    $ ./k6 run test.js --out logger --quiet --no-summary --iterations 2
note

The --out logger argument tells k6 to use your custom output. The flag --quiet --no-summary configures k6 to show only custom output.

Your output should look something like this:

2022-07-01T08:55:09.59272-05:00 [http_reqs=1, http_req_duration=117.003, http_req_blocked=558.983, http_req_connecting=54.135, http_req_tls_handshaking=477.198, http_req_sending=0.102, http_req_waiting=116.544, http_req_receiving=0.357, http_req_failed=0]
2022-07-01T08:55:09.917036-05:00 [vus=1, vus_max=1]
2022-07-01T08:55:10.094196-05:00 [data_sent=446, data_received=21364, iteration_duration=1177.505083, iterations=1]
2022-07-01T08:55:10.213926-05:00 [http_reqs=1, http_req_duration=119.122, http_req_blocked=0.015, http_req_connecting=0, http_req_tls_handshaking=0, http_req_sending=0.103, http_req_waiting=118.726, http_req_receiving=0.293, http_req_failed=0]
2022-07-01T08:55:10.715323-05:00 [data_sent=102, data_received=15904, iteration_duration=620.862459, iterations=1]

Things to keep in mind

Questions? Feel free to join the discussion on extensions in the k6 Community Forum.