Add config options for metrics

This commit is contained in:
Daniel 2021-03-10 14:00:51 +01:00
parent 4a05c26339
commit aef3f26ad3
4 changed files with 89 additions and 16 deletions

View file

@ -90,7 +90,7 @@ func writeMetricsTo(ctx context.Context, url string) error {
} }
// Create request // Create request
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, buf) req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, buf)
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
} }
@ -118,6 +118,7 @@ func writeMetricsTo(ctx context.Context, url string) error {
} }
func metricsWriter(ctx context.Context) error { func metricsWriter(ctx context.Context) error {
pushURL := pushOption()
ticker := time.NewTicker(10 * time.Second) ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop() defer ticker.Stop()

69
metrics/config.go Normal file
View file

@ -0,0 +1,69 @@
package metrics
import (
"flag"
"github.com/safing/portbase/config"
)
// Configuration Keys
var (
CfgOptionInstanceKey = "core/metrics/instance"
instanceOption config.StringOption
cfgOptionInstanceOrder = 0
CfgOptionPushKey = "core/metrics/push"
pushOption config.StringOption
cfgOptionPushOrder = 0
pushFlag string
instanceFlag string
)
func init() {
flag.StringVar(&pushFlag, "push-metrics", "", "Set default URL to push prometheus metrics to.")
flag.StringVar(&instanceFlag, "metrics-instance", "", "Set the default global instance label.")
}
func prepConfig() error {
err := config.Register(&config.Option{
Name: "Metrics Instance Name",
Key: CfgOptionInstanceKey,
Description: "Define the prometheus instance label for exported metrics. Please note that changing the instance name will reset persisted metrics.",
OptType: config.OptTypeString,
ExpertiseLevel: config.ExpertiseLevelExpert,
ReleaseLevel: config.ReleaseLevelStable,
DefaultValue: instanceFlag,
RequiresRestart: true,
Annotations: config.Annotations{
config.DisplayOrderAnnotation: cfgOptionInstanceOrder,
config.CategoryAnnotation: "Metrics",
},
ValidationRegex: "^(" + prometheusBaseFormt + ")?$",
})
if err != nil {
return err
}
instanceOption = config.Concurrent.GetAsString(CfgOptionInstanceKey, instanceFlag)
err = config.Register(&config.Option{
Name: "Push Metrics",
Key: CfgOptionPushKey,
Description: "Push metrics to this URL in the prometheus format.",
OptType: config.OptTypeString,
ExpertiseLevel: config.ExpertiseLevelExpert,
ReleaseLevel: config.ReleaseLevelStable,
DefaultValue: pushFlag,
RequiresRestart: true,
Annotations: config.Annotations{
config.DisplayOrderAnnotation: cfgOptionPushOrder,
config.CategoryAnnotation: "Metrics",
},
})
if err != nil {
return err
}
pushOption = config.Concurrent.GetAsString(CfgOptionPushKey, pushFlag)
return nil
}

View file

@ -15,7 +15,8 @@ import (
// PrometheusFormatRequirement is required format defined by prometheus for // PrometheusFormatRequirement is required format defined by prometheus for
// metric and label names. // metric and label names.
const PrometheusFormatRequirement = "[a-zA-Z_][a-zA-Z0-9_]*" const prometheusBaseFormt = "[a-zA-Z_][a-zA-Z0-9_]*"
const PrometheusFormatRequirement = "^" + prometheusBaseFormt + "$"
var prometheusFormat = regexp.MustCompile(PrometheusFormatRequirement) var prometheusFormat = regexp.MustCompile(PrometheusFormatRequirement)
@ -60,7 +61,7 @@ type Options struct {
func newMetricBase(id string, labels map[string]string, opts Options) (*metricBase, error) { func newMetricBase(id string, labels map[string]string, opts Options) (*metricBase, error) {
// Check formats. // Check formats.
if !prometheusFormat.MatchString(id) { if !prometheusFormat.MatchString(strings.ReplaceAll(id, "/", "_")) {
return nil, fmt.Errorf("metric name %q must match %s", id, PrometheusFormatRequirement) return nil, fmt.Errorf("metric name %q must match %s", id, PrometheusFormatRequirement)
} }
for labelName := range labels { for labelName := range labels {
@ -75,6 +76,11 @@ func newMetricBase(id string, labels map[string]string, opts Options) (*metricBa
opts.Permission = api.PermitUser opts.Permission = api.PermitUser
} }
// Ensure that labels is a map.
if labels == nil {
labels = make(map[string]string)
}
// Create metric base. // Create metric base.
base := &metricBase{ base := &metricBase{
Identifier: id, Identifier: id,

View file

@ -2,7 +2,6 @@ package metrics
import ( import (
"errors" "errors"
"flag"
"fmt" "fmt"
"sort" "sort"
"sync" "sync"
@ -20,9 +19,6 @@ var (
metricNamespace string metricNamespace string
globalLabels = make(map[string]string) globalLabels = make(map[string]string)
pushURL string
metricInstance string
// ErrAlreadyStarted is returned when an operation is only valid before the // ErrAlreadyStarted is returned when an operation is only valid before the
// first metric is registered, and is called after. // first metric is registered, and is called after.
ErrAlreadyStarted = errors.New("can only be changed before first metric is registered") ErrAlreadyStarted = errors.New("can only be changed before first metric is registered")
@ -36,29 +32,30 @@ var (
) )
func init() { func init() {
flag.StringVar(&pushURL, "push-metrics", "", "URL to push prometheus metrics to")
flag.StringVar(&metricInstance, "metrics-instance", "", "Set the global instance label")
module = modules.Register("metrics", prep, start, stop, "database", "api") module = modules.Register("metrics", prep, start, stop, "database", "api")
} }
func prep() error { func prep() error {
return prepConfig()
}
func start() error {
// Add metric instance name as global variable if set. // Add metric instance name as global variable if set.
if metricInstance != "" { if instanceOption() != "" {
if err := AddGlobalLabel("instance", metricInstance); err != nil { if err := AddGlobalLabel("instance", instanceOption()); err != nil {
return err return err
} }
} }
return registerInfoMetric() if err := registerInfoMetric(); err != nil {
} return err
}
func start() error {
if err := registerAPI(); err != nil { if err := registerAPI(); err != nil {
return err return err
} }
if pushURL != "" { if pushOption() != "" {
module.StartServiceWorker("metric pusher", 0, metricsWriter) module.StartServiceWorker("metric pusher", 0, metricsWriter)
} }