From 03e08c92dba44e9379fe02b7257239f5bb19e346 Mon Sep 17 00:00:00 2001 From: James Geisler Date: Tue, 5 Nov 2024 11:41:56 -0600 Subject: [PATCH 1/3] add json handling for the api/v2/series endpoint in the datadogreceiver --- .../36079-add-datadog-json-handling.yaml | 27 +++++++ .../internal/translator/series.go | 17 +++- receiver/datadogreceiver/receiver_test.go | 79 +++++++++++++++++++ 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 .chloggen/36079-add-datadog-json-handling.yaml diff --git a/.chloggen/36079-add-datadog-json-handling.yaml b/.chloggen/36079-add-datadog-json-handling.yaml new file mode 100644 index 000000000000..f62b8add25cc --- /dev/null +++ b/.chloggen/36079-add-datadog-json-handling.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: 'enhancement' + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: datadogreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add json handling for the api/v2/series endpoint in the datadogreceiver + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36079] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/receiver/datadogreceiver/internal/translator/series.go b/receiver/datadogreceiver/internal/translator/series.go index 9588839ebdb8..f6fac8b75210 100644 --- a/receiver/datadogreceiver/internal/translator/series.go +++ b/receiver/datadogreceiver/internal/translator/series.go @@ -4,6 +4,7 @@ package translator // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/datadogreceiver/internal/translator" import ( + "encoding/json" "io" "net/http" "strings" @@ -27,7 +28,6 @@ type SeriesList struct { Series []datadogV1.Series `json:"series"` } -// TODO: add handling for JSON format in additional to protobuf? func (mt *MetricsTranslator) HandleSeriesV2Payload(req *http.Request) (mp []*gogen.MetricPayload_MetricSeries, err error) { buf := GetBuffer() defer PutBuffer(buf) @@ -35,11 +35,20 @@ func (mt *MetricsTranslator) HandleSeriesV2Payload(req *http.Request) (mp []*gog return mp, err } + contentType := req.Header.Get("Content-Type") + pl := new(gogen.MetricPayload) - if err := pl.Unmarshal(buf.Bytes()); err != nil { - return mp, err - } + // handle json messages if set, otherwise handle protobuf + if contentType == "application/json" { + if err := json.Unmarshal(buf.Bytes(), &pl); err != nil { + return mp, err + } + } else { + if err := pl.Unmarshal(buf.Bytes()); err != nil { + return mp, err + } + } return pl.GetSeries(), nil } diff --git a/receiver/datadogreceiver/receiver_test.go b/receiver/datadogreceiver/receiver_test.go index 7283c8ba2f77..c9fc5c9b00f9 100644 --- a/receiver/datadogreceiver/receiver_test.go +++ b/receiver/datadogreceiver/receiver_test.go @@ -459,6 +459,85 @@ func TestDatadogMetricsV2_EndToEnd(t *testing.T) { assert.Equal(t, pcommon.Timestamp(1636629071*1_000_000_000), metric.Sum().DataPoints().At(1).StartTimestamp()) } +func TestDatadogMetricsV2_EndToEndJSON(t *testing.T) { + cfg := createDefaultConfig().(*Config) + cfg.Endpoint = "localhost:0" // Using a randomly assigned address + sink := new(consumertest.MetricsSink) + + dd, err := newDataDogReceiver( + cfg, + receivertest.NewNopSettings(), + ) + require.NoError(t, err, "Must not error when creating receiver") + dd.(*datadogReceiver).nextMetricsConsumer = sink + + require.NoError(t, dd.Start(context.Background(), componenttest.NewNopHost())) + defer func() { + require.NoError(t, dd.Shutdown(context.Background())) + }() + + metricsPayloadV2 := []byte(`{ + "series": [ + { + "metric": "system.load.1", + "type": 1, + "points": [ + { + "timestamp": 1636629071, + "value": 1.5 + }, + { + "timestamp": 1636629081, + "value": 2.0 + } + ], + "resources": [ + { + "name": "dummyhost", + "type": "host" + } + ] + } + ] + }`) + + req, err := http.NewRequest( + http.MethodPost, + fmt.Sprintf("http://%s/api/v2/series", dd.(*datadogReceiver).address), + io.NopCloser(bytes.NewReader(metricsPayloadV2)), + ) + + req.Header.Set("Content-Type", "application/json") + + require.NoError(t, err, "Must not error when creating request") + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err, "Must not error performing request") + + body, err := io.ReadAll(resp.Body) + require.NoError(t, multierr.Combine(err, resp.Body.Close()), "Must not error when reading body") + require.JSONEq(t, `{"errors": []}`, string(body), "Expected JSON response to be `{\"errors\": []}`, got %s", string(body)) + require.Equal(t, http.StatusAccepted, resp.StatusCode) + + mds := sink.AllMetrics() + require.Len(t, mds, 1) + got := mds[0] + require.Equal(t, 1, got.ResourceMetrics().Len()) + metrics := got.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics() + assert.Equal(t, 1, metrics.Len()) + metric := metrics.At(0) + assert.Equal(t, pmetric.MetricTypeSum, metric.Type()) + assert.Equal(t, "system.load.1", metric.Name()) + assert.Equal(t, pmetric.AggregationTemporalityDelta, metric.Sum().AggregationTemporality()) + assert.False(t, metric.Sum().IsMonotonic()) + assert.Equal(t, pcommon.Timestamp(1636629071*1_000_000_000), metric.Sum().DataPoints().At(0).Timestamp()) + assert.Equal(t, 1.5, metric.Sum().DataPoints().At(0).DoubleValue()) + assert.Equal(t, pcommon.Timestamp(0), metric.Sum().DataPoints().At(0).StartTimestamp()) + assert.Equal(t, pcommon.Timestamp(1636629081*1_000_000_000), metric.Sum().DataPoints().At(1).Timestamp()) + assert.Equal(t, 2.0, metric.Sum().DataPoints().At(1).DoubleValue()) + assert.Equal(t, pcommon.Timestamp(1636629071*1_000_000_000), metric.Sum().DataPoints().At(1).StartTimestamp()) +} + func TestDatadogSketches_EndToEnd(t *testing.T) { cfg := createDefaultConfig().(*Config) cfg.Endpoint = "localhost:0" // Using a randomly assigned address From 5f22e583608bca79683e7cada8578c3199de2b33 Mon Sep 17 00:00:00 2001 From: James Geisler Date: Tue, 5 Nov 2024 14:27:55 -0600 Subject: [PATCH 2/3] fix changelog case --- .chloggen/36079-add-datadog-json-handling.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/36079-add-datadog-json-handling.yaml b/.chloggen/36079-add-datadog-json-handling.yaml index f62b8add25cc..cafc7f4eb44a 100644 --- a/.chloggen/36079-add-datadog-json-handling.yaml +++ b/.chloggen/36079-add-datadog-json-handling.yaml @@ -7,7 +7,7 @@ change_type: 'enhancement' component: datadogreceiver # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: add json handling for the api/v2/series endpoint in the datadogreceiver +note: Add json handling for the api/v2/series endpoint in the datadogreceiver # Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. issues: [36079] From 470ca798a61c4ff240acf4ca8444167aa7e477cd Mon Sep 17 00:00:00 2001 From: James Geisler Date: Tue, 5 Nov 2024 14:36:46 -0600 Subject: [PATCH 3/3] changelog fixes --- .chloggen/36079-add-datadog-json-handling.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.chloggen/36079-add-datadog-json-handling.yaml b/.chloggen/36079-add-datadog-json-handling.yaml index cafc7f4eb44a..2d55b8e4056e 100644 --- a/.chloggen/36079-add-datadog-json-handling.yaml +++ b/.chloggen/36079-add-datadog-json-handling.yaml @@ -4,10 +4,10 @@ change_type: 'enhancement' # The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) -component: datadogreceiver +component: 'datadogreceiver' # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Add json handling for the api/v2/series endpoint in the datadogreceiver +note: "Add json handling for the `api/v2/series` endpoint in the datadogreceiver" # Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. issues: [36079] @@ -24,4 +24,4 @@ subtext: # Include 'user' if the change is relevant to end users. # Include 'api' if there is a change to a library API. # Default: '[user]' -change_logs: [user] +change_logs: []