From 569854e1d1fa981b01c16cc56a27cac6a123c0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pos=C5=82uszny?= Date: Thu, 1 Feb 2024 18:58:42 +0100 Subject: [PATCH] sdk/metric/metricdata: Add MarshalJSON for Extrema (#4827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * json marshal for Extrema type, makefile typo fix * MarshalText implementation, cleanup * update changelog * changelog fix * Update CHANGELOG.md * MarshalText behavior update on invalid Extrema * Fix Marshal outputs json text representation of nil * Updated mockData to include example with Min and Max values. * null value test, changelog fix --------- Co-authored-by: Robert PajÄ…k Co-authored-by: Tyler Yahn Co-authored-by: Aaron Clawson <3766680+MadVikingGod@users.noreply.github.com> --- CHANGELOG.md | 1 + exporters/stdout/stdoutmetric/example_test.go | 63 ++++++++++++++++++- sdk/metric/metricdata/data.go | 14 +++++ .../metricdatatest/assertion_test.go | 24 +++++++ 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1437444568a..8b1b82d8296 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix `ContainerID` resource detection on systemd when cgroup path has a colon. (#4449) - Fix `go.opentelemetry.io/otel/sdk/metric` to cache instruments to avoid leaking memory when the same instrument is created multiple times. (#4820) +- Fix missing Mix and Max values for `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` by introducing `MarshalText` and `MarshalJSON` for type `Extrema` in `go.opentelemetry.io/sdk/metric/metricdata`. (#4827) ## [1.23.0-rc.1] 2024-01-18 diff --git a/exporters/stdout/stdoutmetric/example_test.go b/exporters/stdout/stdoutmetric/example_test.go index 683d6ee0fe5..3086c064d01 100644 --- a/exporters/stdout/stdoutmetric/example_test.go +++ b/exporters/stdout/stdoutmetric/example_test.go @@ -77,6 +77,27 @@ var ( }, }, }, + { + Name: "requests.size", + Description: "Size of received requests", + Unit: "kb", + Data: metricdata.Histogram[int64]{ + Temporality: metricdata.DeltaTemporality, + DataPoints: []metricdata.HistogramDataPoint[int64]{ + { + Attributes: attribute.NewSet(attribute.String("server", "central")), + StartTime: now, + Time: now.Add(1 * time.Second), + Count: 10, + Bounds: []float64{1, 5, 10}, + BucketCounts: []uint64{1, 3, 6, 0}, + Sum: 128, + Min: metricdata.NewExtrema[int64](3), + Max: metricdata.NewExtrema[int64](30), + }, + }, + }, + }, { Name: "latency", Description: "Time spend processing received requests", @@ -228,6 +249,44 @@ func Example() { // } // }, // { + // "Name": "requests.size", + // "Description": "Size of received requests", + // "Unit": "kb", + // "Data": { + // "DataPoints": [ + // { + // "Attributes": [ + // { + // "Key": "server", + // "Value": { + // "Type": "STRING", + // "Value": "central" + // } + // } + // ], + // "StartTime": "0001-01-01T00:00:00Z", + // "Time": "0001-01-01T00:00:00Z", + // "Count": 10, + // "Bounds": [ + // 1, + // 5, + // 10 + // ], + // "BucketCounts": [ + // 1, + // 3, + // 6, + // 0 + // ], + // "Min": 3, + // "Max": 30, + // "Sum": 128 + // } + // ], + // "Temporality": "DeltaTemporality" + // } + // }, + // { // "Name": "latency", // "Description": "Time spend processing received requests", // "Unit": "ms", @@ -257,8 +316,8 @@ func Example() { // 6, // 0 // ], - // "Min": {}, - // "Max": {}, + // "Min": null, + // "Max": null, // "Sum": 57 // } // ], diff --git a/sdk/metric/metricdata/data.go b/sdk/metric/metricdata/data.go index 995d42b38f1..32c17934fc4 100644 --- a/sdk/metric/metricdata/data.go +++ b/sdk/metric/metricdata/data.go @@ -15,6 +15,7 @@ package metricdata // import "go.opentelemetry.io/otel/sdk/metric/metricdata" import ( + "encoding/json" "time" "go.opentelemetry.io/otel/attribute" @@ -211,6 +212,19 @@ type Extrema[N int64 | float64] struct { valid bool } +// MarshalText converts the Extrema value to text. +func (e Extrema[N]) MarshalText() ([]byte, error) { + if !e.valid { + return json.Marshal(nil) + } + return json.Marshal(e.value) +} + +// MarshalJSON converts the Extrema value to JSON number. +func (e *Extrema[N]) MarshalJSON() ([]byte, error) { + return e.MarshalText() +} + // NewExtrema returns an Extrema set to v. func NewExtrema[N int64 | float64](v N) Extrema[N] { return Extrema[N]{value: v, valid: true} diff --git a/sdk/metric/metricdata/metricdatatest/assertion_test.go b/sdk/metric/metricdata/metricdatatest/assertion_test.go index 9d647a54004..4219375c4ba 100644 --- a/sdk/metric/metricdata/metricdatatest/assertion_test.go +++ b/sdk/metric/metricdata/metricdatatest/assertion_test.go @@ -15,6 +15,7 @@ package metricdatatest // import "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" import ( + "encoding/json" "testing" "time" @@ -164,6 +165,8 @@ var ( minFloat64C = metricdata.NewExtrema(-1.) minInt64C = metricdata.NewExtrema[int64](-1) + minFloat64D = metricdata.NewExtrema(-9.999999) + histogramDataPointInt64A = metricdata.HistogramDataPoint[int64]{ Attributes: attrA, StartTime: startA, @@ -1010,3 +1013,24 @@ func TestAssertAttributesFail(t *testing.T) { } assert.False(t, AssertHasAttributes(fakeT, sum, attribute.Bool("A", true))) } + +func AssertMarshal[N int64 | float64](t *testing.T, expected string, i *metricdata.Extrema[N]) { + t.Helper() + + b, err := json.Marshal(i) + assert.NoError(t, err) + assert.Equal(t, expected, string(b)) +} + +func TestAssertMarshal(t *testing.T) { + AssertMarshal(t, "null", &metricdata.Extrema[int64]{}) + + AssertMarshal(t, "-1", &minFloat64A) + AssertMarshal(t, "3", &minFloat64B) + AssertMarshal(t, "-9.999999", &minFloat64D) + AssertMarshal(t, "99", &maxFloat64B) + + AssertMarshal(t, "-1", &minInt64A) + AssertMarshal(t, "3", &minInt64B) + AssertMarshal(t, "99", &maxInt64B) +}