From 721ca5e12ac86e0620a642b0ba0d6b125d6b707c Mon Sep 17 00:00:00 2001 From: Dmitrii Anoshin Date: Tue, 16 May 2023 20:54:18 -0700 Subject: [PATCH] [chore] [internal/splunk] Don't use pointer for Event.Time field value (#22030) The pointer was added to avoid sending zero timestamp by Splunk HEC exporter. But using pointer is not required for that `json:omitempty` tag does the same for float64 zero values. After this change, we cannot differentiate `nil`/`0` values of `Time` field in the internal `splunk.Event` struct, but it doesn't matter because both versions produce the same JSON string without `time` field and translates to/from the same OTLP `0` values. --- .../splunkhecexporter/logdata_to_splunk.go | 14 ++---------- .../logdata_to_splunk_test.go | 8 +++---- .../splunkhecexporter/metricdata_to_splunk.go | 14 ++---------- .../metricdata_to_splunk_test.go | 10 ++++----- internal/splunk/common.go | 6 ++--- internal/splunk/common_test.go | 6 ++--- receiver/splunkhecreceiver/receiver_test.go | 4 ++-- .../splunkhecreceiver/splunk_to_logdata.go | 4 +--- .../splunk_to_logdata_test.go | 22 +++++++++---------- .../splunkhec_to_metricdata.go | 8 ++----- .../splunkhec_to_metricdata_test.go | 16 +++++++------- 11 files changed, 42 insertions(+), 70 deletions(-) diff --git a/exporter/splunkhecexporter/logdata_to_splunk.go b/exporter/splunkhecexporter/logdata_to_splunk.go index 027d3c2b03e69..e228694b04822 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk.go +++ b/exporter/splunkhecexporter/logdata_to_splunk.go @@ -106,18 +106,8 @@ func mapLogRecordToSplunkEvent(res pcommon.Resource, lr plog.LogRecord, config * } // nanoTimestampToEpochMilliseconds transforms nanoseconds into .. For example, 1433188255.500 indicates 1433188255 seconds and 500 milliseconds after epoch. -func nanoTimestampToEpochMilliseconds(ts pcommon.Timestamp) *float64 { - duration := time.Duration(ts) - if duration == 0 { - // some telemetry sources send data with timestamps set to 0 by design, as their original target destinations - // (i.e. before Open Telemetry) are setup with the know-how on how to consume them. In this case, - // we want to omit the time field when sending data to the Splunk HEC so that the HEC adds a timestamp - // at indexing time, which will be much more useful than a 0-epoch-time value. - return nil - } - - val := duration.Round(time.Millisecond).Seconds() - return &val +func nanoTimestampToEpochMilliseconds(ts pcommon.Timestamp) float64 { + return time.Duration(ts).Round(time.Millisecond).Seconds() } func mergeValue(dst map[string]any, k string, v any) { diff --git a/exporter/splunkhecexporter/logdata_to_splunk_test.go b/exporter/splunkhecexporter/logdata_to_splunk_test.go index a60d80fb7f094..02fa2ad98145b 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk_test.go +++ b/exporter/splunkhecexporter/logdata_to_splunk_test.go @@ -460,7 +460,7 @@ func commonLogSplunkEvent( func Test_emptyLogRecord(t *testing.T) { event := mapLogRecordToSplunkEvent(pcommon.NewResource(), plog.NewLogRecord(), &Config{}) - assert.Nil(t, event.Time) + assert.Zero(t, event.Time) assert.Equal(t, event.Host, "unknown") assert.Zero(t, event.Source) assert.Zero(t, event.SourceType) @@ -471,11 +471,11 @@ func Test_emptyLogRecord(t *testing.T) { func Test_nanoTimestampToEpochMilliseconds(t *testing.T) { splunkTs := nanoTimestampToEpochMilliseconds(1001000000) - assert.Equal(t, 1.001, *splunkTs) + assert.Equal(t, 1.001, splunkTs) splunkTs = nanoTimestampToEpochMilliseconds(1001990000) - assert.Equal(t, 1.002, *splunkTs) + assert.Equal(t, 1.002, splunkTs) splunkTs = nanoTimestampToEpochMilliseconds(0) - assert.True(t, nil == splunkTs) + assert.Zero(t, splunkTs) } func Test_mergeValue(t *testing.T) { diff --git a/exporter/splunkhecexporter/metricdata_to_splunk.go b/exporter/splunkhecexporter/metricdata_to_splunk.go index cbea2bcb92a23..0e07cd744a41e 100644 --- a/exporter/splunkhecexporter/metricdata_to_splunk.go +++ b/exporter/splunkhecexporter/metricdata_to_splunk.go @@ -276,18 +276,8 @@ func cloneMapWithSelector(fields map[string]interface{}, selector func(string) b return newFields } -func timestampToSecondsWithMillisecondPrecision(ts pcommon.Timestamp) *float64 { - if ts == 0 { - // some telemetry sources send data with timestamps set to 0 by design, as their original target destinations - // (i.e. before Open Telemetry) are setup with the know-how on how to consume them. In this case, - // we want to omit the time field when sending data to the Splunk HEC so that the HEC adds a timestamp - // at indexing time, which will be much more useful than a 0-epoch-time value. - return nil - } - - val := math.Round(float64(ts)/1e6) / 1e3 - - return &val +func timestampToSecondsWithMillisecondPrecision(ts pcommon.Timestamp) float64 { + return math.Round(float64(ts)/1e6) / 1e3 } func float64ToDimValue(f float64) string { diff --git a/exporter/splunkhecexporter/metricdata_to_splunk_test.go b/exporter/splunkhecexporter/metricdata_to_splunk_test.go index 4896a81351579..12d6d0920ff39 100644 --- a/exporter/splunkhecexporter/metricdata_to_splunk_test.go +++ b/exporter/splunkhecexporter/metricdata_to_splunk_test.go @@ -667,7 +667,7 @@ func Test_mergeEventsToMultiMetricFormat(t *testing.T) { func commonSplunkMetric( metricName string, - ts *float64, + ts float64, keys []string, values []interface{}, val interface{}, @@ -695,22 +695,22 @@ func commonSplunkMetric( func TestTimestampFormat(t *testing.T) { ts := pcommon.Timestamp(32001000345) - assert.Equal(t, 32.001, *timestampToSecondsWithMillisecondPrecision(ts)) + assert.Equal(t, 32.001, timestampToSecondsWithMillisecondPrecision(ts)) } func TestTimestampFormatRounding(t *testing.T) { ts := pcommon.Timestamp(32001999345) - assert.Equal(t, 32.002, *timestampToSecondsWithMillisecondPrecision(ts)) + assert.Equal(t, 32.002, timestampToSecondsWithMillisecondPrecision(ts)) } func TestTimestampFormatRoundingWithNanos(t *testing.T) { ts := pcommon.Timestamp(9999999999991500001) - assert.Equal(t, 9999999999.992, *timestampToSecondsWithMillisecondPrecision(ts)) + assert.Equal(t, 9999999999.992, timestampToSecondsWithMillisecondPrecision(ts)) } func TestNilTimeWhenTimestampIsZero(t *testing.T) { ts := pcommon.Timestamp(0) - assert.Nil(t, timestampToSecondsWithMillisecondPrecision(ts)) + assert.Zero(t, timestampToSecondsWithMillisecondPrecision(ts)) } func TestMergeEvents(t *testing.T) { diff --git a/internal/splunk/common.go b/internal/splunk/common.go index 7d662c556386a..475e73d657756 100644 --- a/internal/splunk/common.go +++ b/internal/splunk/common.go @@ -49,7 +49,7 @@ type AccessTokenPassthroughConfig struct { // Event represents a metric in Splunk HEC format type Event struct { - Time *float64 `json:"time,omitempty"` // optional epoch time - set to nil if the event timestamp is missing or unknown + Time float64 `json:"time,omitempty"` // optional epoch time - set to zero if the event timestamp is missing or unknown (will be added at indexing time) Host string `json:"host"` // hostname Source string `json:"source,omitempty"` // optional description of the source of the event; typically the app's name SourceType string `json:"sourcetype,omitempty"` // optional name of a Splunk parsing configuration; this is usually inferred by Splunk @@ -99,14 +99,14 @@ func (e *Event) UnmarshalJSON(b []byte) error { } switch t := rawEvent.Time.(type) { case float64: - e.Time = &t + e.Time = t case string: { time, err := strconv.ParseFloat(t, 64) if err != nil { return err } - e.Time = &time + e.Time = time } } return nil diff --git a/internal/splunk/common_test.go b/internal/splunk/common_test.go index f285c11a88628..cc013d54fa426 100644 --- a/internal/splunk/common_test.go +++ b/internal/splunk/common_test.go @@ -76,7 +76,7 @@ func TestDecodeJsonWithNoTime(t *testing.T) { var msg Event err := dec.Decode(&msg) assert.NoError(t, err) - assert.Nil(t, msg.Time) + assert.Zero(t, msg.Time) } func TestDecodeJsonWithNumberTime(t *testing.T) { @@ -86,7 +86,7 @@ func TestDecodeJsonWithNumberTime(t *testing.T) { var msg Event err := dec.Decode(&msg) assert.NoError(t, err) - assert.Equal(t, 1610760752.606, *msg.Time) + assert.Equal(t, 1610760752.606, msg.Time) } func TestDecodeJsonWithStringTime(t *testing.T) { @@ -96,7 +96,7 @@ func TestDecodeJsonWithStringTime(t *testing.T) { var msg Event err := dec.Decode(&msg) assert.NoError(t, err) - assert.Equal(t, 1610760752.606, *msg.Time) + assert.Equal(t, 1610760752.606, msg.Time) } func TestDecodeJsonWithInvalidStringTime(t *testing.T) { diff --git a/receiver/splunkhecreceiver/receiver_test.go b/receiver/splunkhecreceiver/receiver_test.go index 4d96e93e1132e..481c7efdcdaf9 100644 --- a/receiver/splunkhecreceiver/receiver_test.go +++ b/receiver/splunkhecreceiver/receiver_test.go @@ -827,7 +827,7 @@ func Test_Metrics_splunkhecReceiver_IndexSourceTypePassthrough(t *testing.T) { func buildSplunkHecMetricsMsg(time float64, value int64, dimensions uint) *splunk.Event { ev := &splunk.Event{ - Time: &time, + Time: time, Event: "metric", Fields: map[string]interface{}{ "metric_name:foo": value, @@ -842,7 +842,7 @@ func buildSplunkHecMetricsMsg(time float64, value int64, dimensions uint) *splun func buildSplunkHecMsg(time float64, dimensions uint) *splunk.Event { ev := &splunk.Event{ - Time: &time, + Time: time, Event: "foo", Fields: map[string]interface{}{}, Index: "myindex", diff --git a/receiver/splunkhecreceiver/splunk_to_logdata.go b/receiver/splunkhecreceiver/splunk_to_logdata.go index edb3a67a1c882..6095b6bcd6f4c 100644 --- a/receiver/splunkhecreceiver/splunk_to_logdata.go +++ b/receiver/splunkhecreceiver/splunk_to_logdata.go @@ -65,9 +65,7 @@ func splunkHecToLogData(logger *zap.Logger, events []*splunk.Event, resourceCust // Splunk timestamps are in seconds so convert to nanos by multiplying // by 1 billion. - if event.Time != nil { - logRecord.SetTimestamp(pcommon.Timestamp(*event.Time * 1e9)) - } + logRecord.SetTimestamp(pcommon.Timestamp(event.Time * 1e9)) // Set event fields first, so the specialized attributes overwrite them if needed. keys := make([]string, 0, len(event.Fields)) diff --git a/receiver/splunkhecreceiver/splunk_to_logdata_test.go b/receiver/splunkhecreceiver/splunk_to_logdata_test.go index 991f59ff00e1e..506e3098cfc14 100644 --- a/receiver/splunkhecreceiver/splunk_to_logdata_test.go +++ b/receiver/splunkhecreceiver/splunk_to_logdata_test.go @@ -55,7 +55,7 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "happy_path", events: []*splunk.Event{ { - Time: &time, + Time: time, Host: "localhost", Source: "mysource", SourceType: "mysourcetype", @@ -76,7 +76,7 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "double", events: []*splunk.Event{ { - Time: &time, + Time: time, Host: "localhost", Source: "mysource", SourceType: "mysourcetype", @@ -99,7 +99,7 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "array", events: []*splunk.Event{ { - Time: &time, + Time: time, Host: "localhost", Source: "mysource", SourceType: "mysourcetype", @@ -126,7 +126,7 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "complex_structure", events: []*splunk.Event{ { - Time: &time, + Time: time, Host: "localhost", Source: "mysource", SourceType: "mysourcetype", @@ -158,7 +158,6 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "nil_timestamp", events: []*splunk.Event{ { - Time: new(float64), Host: "localhost", Source: "mysource", SourceType: "mysourcetype", @@ -179,7 +178,6 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "custom_config_mapping", events: []*splunk.Event{ { - Time: new(float64), Host: "localhost", Source: "mysource", SourceType: "mysourcetype", @@ -220,7 +218,7 @@ func Test_SplunkHecToLogData(t *testing.T) { name: "group_events_by_resource_attributes", events: []*splunk.Event{ { - Time: &time, + Time: time, Host: "1", Source: "1", SourceType: "1", @@ -231,7 +229,7 @@ func Test_SplunkHecToLogData(t *testing.T) { }, }, { - Time: &time, + Time: time, Host: "2", Source: "2", SourceType: "2", @@ -242,7 +240,7 @@ func Test_SplunkHecToLogData(t *testing.T) { }, }, { - Time: &time, + Time: time, Host: "1", Source: "1", SourceType: "1", @@ -253,7 +251,7 @@ func Test_SplunkHecToLogData(t *testing.T) { }, }, { - Time: &time, + Time: time, Host: "2", Source: "2", SourceType: "2", @@ -264,7 +262,7 @@ func Test_SplunkHecToLogData(t *testing.T) { }, }, { - Time: &time, + Time: time, Host: "1", Source: "2", SourceType: "1", @@ -275,7 +273,7 @@ func Test_SplunkHecToLogData(t *testing.T) { }, }, { - Time: &time, + Time: time, Host: "2", Source: "1", SourceType: "2", diff --git a/receiver/splunkhecreceiver/splunkhec_to_metricdata.go b/receiver/splunkhecreceiver/splunkhec_to_metricdata.go index 3933a3f5a0750..5454820d1ceac 100644 --- a/receiver/splunkhecreceiver/splunkhec_to_metricdata.go +++ b/receiver/splunkhecreceiver/splunkhec_to_metricdata.go @@ -129,12 +129,8 @@ func addDoubleGauge(metrics pmetric.MetricSlice, metricName string, value float6 attributes.CopyTo(doublePt.Attributes()) } -func convertTimestamp(sec *float64) pcommon.Timestamp { - if sec == nil { - return 0 - } - - return pcommon.Timestamp(*sec * 1e9) +func convertTimestamp(sec float64) pcommon.Timestamp { + return pcommon.Timestamp(sec * 1e9) } // Extract dimensions from the Splunk event fields to populate metric data point attributes. diff --git a/receiver/splunkhecreceiver/splunkhec_to_metricdata_test.go b/receiver/splunkhecreceiver/splunkhec_to_metricdata_test.go index a530d77d1fcfe..c758ffa293b21 100644 --- a/receiver/splunkhecreceiver/splunkhec_to_metricdata_test.go +++ b/receiver/splunkhecreceiver/splunkhec_to_metricdata_test.go @@ -36,7 +36,7 @@ func Test_splunkV2ToMetricsData(t *testing.T) { buildDefaultSplunkDataPt := func() *splunk.Event { return &splunk.Event{ - Time: &sec, + Time: sec, Host: "localhost", Source: "source", SourceType: "sourcetype", @@ -243,7 +243,7 @@ func Test_splunkV2ToMetricsData(t *testing.T) { name: "zero_timestamp", splunkDataPoint: func() *splunk.Event { pt := buildDefaultSplunkDataPt() - pt.Time = new(float64) + pt.Time = 0 return pt }(), wantMetricsData: func() pmetric.Metrics { @@ -319,7 +319,7 @@ func TestGroupMetricsByResource(t *testing.T) { nanoseconds := int64(sec * 1e9) events := []*splunk.Event{ { - Time: &sec, + Time: sec, Host: "1", Source: "1", SourceType: "1", @@ -330,7 +330,7 @@ func TestGroupMetricsByResource(t *testing.T) { }, }, { - Time: &sec, + Time: sec, Host: "2", Source: "2", SourceType: "2", @@ -341,7 +341,7 @@ func TestGroupMetricsByResource(t *testing.T) { }, }, { - Time: &sec, + Time: sec, Host: "1", Source: "1", SourceType: "1", @@ -352,7 +352,7 @@ func TestGroupMetricsByResource(t *testing.T) { }, }, { - Time: &sec, + Time: sec, Host: "2", Source: "2", SourceType: "2", @@ -364,7 +364,7 @@ func TestGroupMetricsByResource(t *testing.T) { }, }, { - Time: &sec, + Time: sec, Host: "1", Source: "2", SourceType: "1", @@ -375,7 +375,7 @@ func TestGroupMetricsByResource(t *testing.T) { }, }, { - Time: &sec, + Time: sec, Host: "2", Source: "1", SourceType: "2",