feat: make push metrics labels generic and repeatable

Signed-off-by: François Gouteroux <francois.gouteroux@gmail.com>
This commit is contained in:
François Gouteroux 2023-05-24 10:55:49 +02:00
parent 3524a16aa0
commit 934c5ddb8d
5 changed files with 30 additions and 19 deletions

View file

@ -187,7 +187,7 @@ func main() {
"metric-files",
"The metric files to push, default is read from standard input (STDIN).",
).ExistingFiles()
metricJobLabel := pushMetricsCmd.Flag("job-label", "Job label to attach to metrics.").Default("promtool").String()
pushMetricsLabels := pushMetricsCmd.Flag("label", "Label to attach to metrics. Can be specified multiple times.").Default("job=promtool").StringMap()
pushMetricsTimeout := pushMetricsCmd.Flag("timeout", "The time to wait for pushing metrics.").Default("30s").Duration()
pushMetricsHeaders := pushMetricsCmd.Flag("header", "Prometheus remote write header.").StringMap()
@ -315,7 +315,7 @@ func main() {
os.Exit(CheckMetrics(*checkMetricsExtended))
case pushMetricsCmd.FullCommand():
os.Exit(PushMetrics(remoteWriteURL, httpRoundTripper, *pushMetricsHeaders, *pushMetricsTimeout, *metricJobLabel, *metricFiles...))
os.Exit(PushMetrics(remoteWriteURL, httpRoundTripper, *pushMetricsHeaders, *pushMetricsTimeout, *pushMetricsLabels, *metricFiles...))
case queryInstantCmd.FullCommand():
os.Exit(QueryInstant(serverURL, httpRoundTripper, *queryInstantExpr, *queryInstantTime, p))

View file

@ -32,7 +32,7 @@ import (
)
// Push metrics to a prometheus remote write (for testing purpose only).
func PushMetrics(url *url.URL, roundTripper http.RoundTripper, headers map[string]string, timeout time.Duration, jobLabel string, files ...string) int {
func PushMetrics(url *url.URL, roundTripper http.RoundTripper, headers map[string]string, timeout time.Duration, labels map[string]string, files ...string) int {
failed := false
addressURL, err := url.Parse(url.String())
@ -76,32 +76,32 @@ func PushMetrics(url *url.URL, roundTripper http.RoundTripper, headers map[strin
if file == "" {
data, err = io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, " FAILED:", err)
failed = true
break
}
fmt.Printf("Parsing stdin\n")
fmt.Printf("Parsing input from stdin\n")
} else {
data, err = os.ReadFile(file)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, " FAILED:", err)
failed = true
continue
}
fmt.Printf("Parsing metric file %s\n", file)
fmt.Printf("Parsing input from metric file %s\n", file)
}
metricsData, err := fmtutil.ParseMetricsTextAndFormat(bytes.NewReader(data), jobLabel)
metricsData, err := fmtutil.ParseMetricsTextAndFormat(bytes.NewReader(data), labels)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, " FAILED:", err)
failed = true
continue
}
raw, err := metricsData.Marshal()
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, " FAILED:", err)
failed = true
continue
}
@ -110,11 +110,16 @@ func PushMetrics(url *url.URL, roundTripper http.RoundTripper, headers map[strin
compressed := snappy.Encode(nil, raw)
err = client.Store(context.Background(), compressed)
if err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, " FAILED:", err)
failed = true
continue
}
fmt.Printf("Successfully pushed metric file %s\n", file)
if file == "" {
fmt.Printf(" SUCCESS: metric pushed to remote write.\n")
} else {
fmt.Printf(" SUCCESS: metric file %s pushed to remote write.\n", file)
}
}
if failed {

View file

@ -398,7 +398,7 @@ Push metrics to a prometheus remote write (for testing purpose only).
| Flag | Description | Default |
| --- | --- | --- |
| <code class="text-nowrap">--job-label</code> | Job label to attach to metrics. | `promtool` |
| <code class="text-nowrap">--label</code> | Label to attach to metrics. Can be specified multiple times. | `job=promtool` |
| <code class="text-nowrap">--timeout</code> | The time to wait for pushing metrics. | `30s` |
| <code class="text-nowrap">--header</code> | Prometheus remote write header. | |

View file

@ -38,7 +38,7 @@ var MetricMetadataTypeValue = map[string]int32{
}
// FormatMetrics convert metric family to a writerequest.
func FormatMetrics(mf map[string]*dto.MetricFamily, jobLabel string) (*prompb.WriteRequest, error) {
func FormatMetrics(mf map[string]*dto.MetricFamily, extraLabels map[string]string) (*prompb.WriteRequest, error) {
wr := &prompb.WriteRequest{}
// build metric list
@ -63,10 +63,15 @@ func FormatMetrics(mf map[string]*dto.MetricFamily, jobLabel string) (*prompb.Wr
var timeserie prompb.TimeSeries
// build labels map
labels := make(map[string]string, len(metric.Label)+2)
labels := make(map[string]string, len(metric.Label)+len(extraLabels))
labels[model.MetricNameLabel] = metricName
labels[model.JobLabel] = jobLabel
// add extra labels
for key, value := range extraLabels {
labels[key] = value
}
// add metric labels
for _, label := range metric.Label {
labelname := label.GetName()
if labelname == model.JobLabel {
@ -133,10 +138,10 @@ func ParseMetricsTextReader(input io.Reader) (map[string]*dto.MetricFamily, erro
}
// ParseMetricsTextAndFormat return the data in the expected prometheus metrics write request format.
func ParseMetricsTextAndFormat(input io.Reader, jobLabel string) (*prompb.WriteRequest, error) {
func ParseMetricsTextAndFormat(input io.Reader, labels map[string]string) (*prompb.WriteRequest, error) {
mf, err := ParseMetricsTextReader(input)
if err != nil {
return nil, err
}
return FormatMetrics(mf, jobLabel)
return FormatMetrics(mf, labels)
}

View file

@ -63,8 +63,9 @@ func TestParseMetricsTextAndFormat(t *testing.T) {
test_metric1{b="c",baz="qux",d="e",foo="bar"} 1 1
test_metric1{b="c",baz="qux",d="e",foo="bar"} 2 1
`))
labels := map[string]string{"job": "promtool"}
expected, err := ParseMetricsTextAndFormat(input, "promtool")
expected, err := ParseMetricsTextAndFormat(input, labels)
require.NoError(t, err)
require.Equal(t, writeRequestFixture, expected)