diff --git a/app/vmagent/datadog/request_handler.go b/app/vmagent/datadog/request_handler.go index 7f98ec955e..269a46bfcf 100644 --- a/app/vmagent/datadog/request_handler.go +++ b/app/vmagent/datadog/request_handler.go @@ -1,9 +1,7 @@ package datadog import ( - "fmt" "net/http" - "strings" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/common" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite" @@ -59,12 +57,7 @@ func insertRows(at *auth.Token, series []parser.Series, extraLabels []prompbmars Value: ss.Host, }) for _, tag := range ss.Tags { - n := strings.IndexByte(tag, ':') - if n < 0 { - return fmt.Errorf("cannot find ':' in tag %q", tag) - } - name := tag[:n] - value := tag[n+1:] + name, value := parser.SplitTag(tag) if name == "host" { name = "exported_host" } diff --git a/app/vminsert/datadog/request_handler.go b/app/vminsert/datadog/request_handler.go index da28b19d7c..8a9a955cd0 100644 --- a/app/vminsert/datadog/request_handler.go +++ b/app/vminsert/datadog/request_handler.go @@ -3,7 +3,6 @@ package datadog import ( "fmt" "net/http" - "strings" "github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/netstorage" "github.com/VictoriaMetrics/VictoriaMetrics/app/vminsert/relabel" @@ -57,15 +56,7 @@ func insertRows(at *auth.Token, series []parser.Series, extraLabels []prompbmars ctx.AddLabel("", ss.Metric) ctx.AddLabel("host", ss.Host) for _, tag := range ss.Tags { - n := strings.IndexByte(tag, ':') - var name, value string - if n < 0 { - name = tag - value = "no_label_value" - } else { - name = tag[:n] - value = tag[n+1:] - } + name, value := parser.SplitTag(tag) if name == "host" { name = "exported_host" } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3c9dacbf95..a4f45dd267 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -63,6 +63,8 @@ scrape_configs: * BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly reload changed `-promscrape.config` file when `-promscrape.configCheckInterval` option is set. The changed config file wasn't reloaded in this case since [v1.69.0](#v1690). See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2786). Thanks to @ttyv for the fix. * BUGFIX: [VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html): assume that the response is complete if `-search.denyPartialResponse` is enabled and up to `-replicationFactor - 1` `vmstorage` nodes are unavailable. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1767). * BUGFIX: [vmselect](https://docs.victoriametrics.com/#vmselect): update `vm_partial_results_total` metric labels to be consistent with `vm_requests_total` labels. +* FEATURE: accept tags without values when reading data in [DataDog format](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-send-data-from-datadog-agent). Thanks to @PerGon for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2839). + ## [v1.78.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.78.0) diff --git a/lib/protoparser/datadog/parser.go b/lib/protoparser/datadog/parser.go index ad539f2932..931617d5e2 100644 --- a/lib/protoparser/datadog/parser.go +++ b/lib/protoparser/datadog/parser.go @@ -3,10 +3,23 @@ package datadog import ( "encoding/json" "fmt" + "strings" "github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime" ) +// SplitTag splits DataDog tag into tag name and value. +// +// See https://docs.datadoghq.com/getting_started/tagging/#define-tags +func SplitTag(tag string) (string, string) { + n := strings.IndexByte(tag, ':') + if n < 0 { + // No tag value. + return tag, "no_label_value" + } + return tag[:n], tag[n+1:] +} + // Request represents DataDog POST request to /api/v1/series // // See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics diff --git a/lib/protoparser/datadog/parser_test.go b/lib/protoparser/datadog/parser_test.go index 4cacf97211..3c472d91cc 100644 --- a/lib/protoparser/datadog/parser_test.go +++ b/lib/protoparser/datadog/parser_test.go @@ -5,6 +5,23 @@ import ( "testing" ) +func TestSplitTag(t *testing.T) { + f := func(s, nameExpected, valueExpected string) { + t.Helper() + name, value := SplitTag(s) + if name != nameExpected { + t.Fatalf("unexpected name obtained from %q; got %q; want %q", s, name, nameExpected) + } + if value != valueExpected { + t.Fatalf("unexpected value obtained from %q; got %q; want %q", s, value, valueExpected) + } + } + f("", "", "no_label_value") + f("foo", "foo", "no_label_value") + f("foo:bar", "foo", "bar") + f(":bar", "", "bar") +} + func TestRequestUnmarshalFailure(t *testing.T) { f := func(s string) { t.Helper()