菜单
开源

输出扩展

k6 提供了许多指标输出格式,但无法直接支持所有可能性。要存储或修改在活动 k6 测试期间捕获的指标,您可以创建自定义输出扩展。

输出扩展二进制文件可以使用 --out 标志将指标发送到自定义位置。创建自定义扩展的一些潜在原因可能包括:

  • 支持尚未支持的时序数据库
  • 添加派生指标数据用于存储
  • 过滤指标,仅保留您关心的数据

JavaScript 扩展一样,输出扩展依赖于扩展作者实现特定的 API。

开始之前

要运行本教程,您需要安装以下应用程序:

  • Go
  • Git

您还需要安装 xk6:

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

编写一个简单的扩展

  1. 设置一个工作目录。
bash
mkdir xk6-output-logger; cd xk6-output-logger; go mod init xk6-output-logger
  1. 输出扩展的核心是一个实现了 output.Output 接口的结构体。

创建一个简单示例,该示例将 output 接口的 AddMetricSamples(samples []metrics.SampleContainer) 方法接收到的每组指标输出到控制台。

Go
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, ", ")
}
  1. 注册模块以便在 k6 测试脚本中使用它们。
Go
import "go.k6.io/k6/output"

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

注意

运行 k6 时,您必须使用 -o--out 标志注册的名称!

最终的扩展代码如下所示:

Go
package log

import (
    "fmt"
    "io"
    "strings"
    "time"

    "go.k6.io/k6/metrics"
    "go.k6.io/k6/output"
)

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

// Logger writes k6 metric samples to stdout.
type Logger struct {
    out io.Writer
}

// New returns a new instance of Logger.
func New(params output.Params) (output.Output, error) {
    return &Logger{params.StdOut}, nil
}

// Description returns a short human-readable description of the output.
func (*Logger) Description() string {
    return "logger"
}

// Start initializes any state needed for the output, establishes network
// connections, etc.
func (*Logger) Start() error {
    return nil
}

// 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, ", ")
}

// Stop finalizes any tasks in progress, closes network connections, etc.
func (*Logger) Stop() error {
    return nil
}

请注意以下几点:

  • 模块初始化函数 New() 接收 output.Params 实例。通过此对象,扩展可以访问特定于输出的配置、文件系统接口、同步的标准输出和标准错误输出等。

  • 本例中的 AddMetricSamples 写入标准输出。在实际场景中,为了避免内存泄漏,可能需要定期缓冲和刷新此输出。下面我们将讨论一些可用于此目的的辅助函数。参考 statsd 输出 获取示例。

编译您的扩展 k6

要构建包含此扩展的 k6 二进制文件,请运行:

bash
xk6 build --with xk6-output-logger=.

注意

xk6-output-logger 是传递给 go mod init 的 Go 模块名称。

通常,这将是一个类似于 github.com/grafana/xk6-output-logger 的 URL。

使用您的扩展

现在我们可以将扩展与测试脚本一起使用了。

  1. 在一个新的 JavaScript 文件中,编写一些简单的测试逻辑。
JavaScript
import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
  http.get('https://quickpizza.grafana.com');
  sleep(0.5);
}
  1. 现在,运行测试。
bash
./k6 run test.js --out logger --quiet --no-summary --iterations 2

注意

--out logger 参数告诉 k6 使用您的自定义输出。标志 --quiet --no-summary 配置 k6 只显示自定义输出。

您的输出应该类似于这样:

shell
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]

注意事项

有问题?欢迎加入k6 社区论坛 的扩展讨论。