loadgen/config/config.go (134 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
// Package loadgencfg contains configuration related code for load generator.
package loadgencfg
import (
"flag"
"fmt"
"net/url"
"os"
"strconv"
"strings"
"time"
)
var Config struct {
ServerURL *url.URL
SecretToken string
APIKey string
Secure bool
EventRate RateFlag
IgnoreErrors bool
RewriteIDs bool
RewriteTimestamps bool
RewriteServiceNames bool
RewriteServiceNodeNames bool
RewriteServiceTargetNames bool
RewriteSpanNames bool
RewriteTransactionNames bool
RewriteTransactionTypes bool
Headers map[string]string
}
type RateFlag struct {
Burst int
Interval time.Duration
}
func (f *RateFlag) String() string {
return fmt.Sprintf("%d/%s", f.Burst, f.Interval)
}
func (f *RateFlag) Set(s string) error {
before, after, ok := strings.Cut(s, "/")
if !ok || before == "" || after == "" {
return fmt.Errorf("invalid rate %q, expected format burst/duration", s)
}
burst, err := strconv.Atoi(before)
if err != nil {
return fmt.Errorf("invalid burst %s in event rate: %w", before, err)
}
if !(after[0] >= '0' && after[0] <= '9') {
after = "1" + after
}
interval, err := time.ParseDuration(after)
if err != nil {
return fmt.Errorf("invalid interval %q in event rate: %w", after, err)
}
if interval <= 0 {
return fmt.Errorf("invalid interval %q, must be positive", after)
}
f.Burst = burst
f.Interval = interval
return nil
}
func init() {
// Server config
flag.Func(
"server",
"server URL (default http://127.0.0.1:8200)",
func(server string) (err error) {
if server != "" {
Config.ServerURL, err = url.Parse(server)
}
return
})
flag.StringVar(&Config.SecretToken, "secret-token", "", "secret token for APM Server")
flag.StringVar(&Config.APIKey, "api-key", "", "API key for APM Server")
flag.BoolVar(&Config.Secure, "secure", false, "validate the remote server TLS certificates")
flag.BoolVar(
&Config.RewriteTimestamps,
"rewrite-timestamps",
true,
"rewrite event timestamps every iteration, maintaining relative offsets",
)
flag.BoolVar(
&Config.RewriteIDs,
"rewrite-ids",
true,
"rewrite event IDs every iteration, maintaining event relationships",
)
flag.Func("header",
"extra headers to use when sending data to the server",
func(s string) error {
k, v, ok := strings.Cut(s, "=")
if !ok {
return fmt.Errorf("invalid header '%s': format must be key=value", s)
}
if len(Config.Headers) == 0 {
Config.Headers = make(map[string]string)
}
Config.Headers[k] = v
return nil
},
)
flag.Var(&Config.EventRate, "event-rate", "Event rate in format of {burst}/{interval}. For example, 200/5s, <= 0 values evaluate to Inf (default 0/s)")
flag.BoolVar(&Config.IgnoreErrors, "ignore-errors", false, "Ignore HTTP errors while sending events")
rewriteNames := map[string]*bool{
"service.name": &Config.RewriteServiceNames,
"service.node.name": &Config.RewriteServiceNodeNames,
"service.target.name": &Config.RewriteServiceTargetNames,
"span.name": &Config.RewriteSpanNames,
"transaction.name": &Config.RewriteTransactionNames,
"transaction.type": &Config.RewriteTransactionTypes,
}
for field, config := range rewriteNames {
flag.BoolVar(
config,
fmt.Sprintf("rewrite-%ss", strings.Replace(field, ".", "-", -1)),
false,
fmt.Sprintf("replace `%s` in events", field),
)
}
// For configs that can be set via environment variables, set the required
// flags from env if they are not explicitly provided via command line
setFlagsFromEnv()
}
func setFlagsFromEnv() {
// value[0] is environment key
// value[1] is default value
flagEnvMap := map[string][]string{
"server": {"ELASTIC_APM_SERVER_URL", "http://127.0.0.1:8200"},
"secret-token": {"ELASTIC_APM_SECRET_TOKEN", ""},
"api-key": {"ELASTIC_APM_API_KEY", ""},
"secure": {"ELASTIC_APM_VERIFY_SERVER_CERT", "false"},
}
for k, v := range flagEnvMap {
flag.Set(k, getEnvOrDefault(v[0], v[1]))
}
}
func getEnvOrDefault(name, defaultValue string) string {
value := os.Getenv(name)
if value != "" {
return value
}
return defaultValue
}