Skip to content

Commit b8218ac

Browse files
authored
[receiver/splunkhec] HEC metrics accepts empty string Event (#38585)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Splunk HEC metrics accepts metrics when `"event": ""`. This aligns with Splunk HEC behavior <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes [38464](#38464) <!--Describe what testing was performed and which tests were added.--> #### Testing Unit tests <!--Describe the documentation added.--> #### Documentation <!--Please delete paragraphs that you did not use before submitting.-->
1 parent 2c479ec commit b8218ac

File tree

5 files changed

+77
-18
lines changed

5 files changed

+77
-18
lines changed

.chloggen/38464.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: splunkhecreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: splunk hec receiver accepts metrics with empty string Event field
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [38464]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: [user]

internal/splunk/common.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ type Event struct {
6666

6767
// IsMetric returns true if the Splunk event is a metric.
6868
func (e *Event) IsMetric() bool {
69-
return e.Event == HecEventMetricType || (e.Event == nil && len(e.GetMetricValues()) > 0)
69+
return e.Event == HecEventMetricType || len(e.GetMetricValues()) > 0
7070
}
7171

7272
// checks if the field name matches the requirements for a metric datapoint field,

internal/splunk/common_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ func TestIsMetric(t *testing.T) {
5252
Event: "metric",
5353
}
5454
assert.True(t, metric.IsMetric())
55+
metric = Event{
56+
Event: "",
57+
Fields: map[string]any{
58+
"metric_name": "foo",
59+
"_value": 123,
60+
},
61+
}
62+
assert.True(t, metric.IsMetric())
63+
metric = Event{
64+
Event: "any value",
65+
Fields: map[string]any{
66+
"metric_name": "foo",
67+
"_value": 123,
68+
},
69+
}
70+
assert.True(t, metric.IsMetric())
5571
arr := Event{
5672
Event: []any{"foo", "bar"},
5773
}

receiver/splunkhecreceiver/receiver.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -427,29 +427,30 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request)
427427
return
428428
}
429429

430-
if msg.Event == nil {
431-
r.failRequest(resp, http.StatusBadRequest, eventRequiredRespBody, nil)
432-
return
433-
}
434-
435-
if msg.Event == "" {
436-
r.failRequest(resp, http.StatusBadRequest, eventBlankRespBody, nil)
437-
return
438-
}
439-
440430
for _, v := range msg.Fields {
441431
if !isFlatJSONField(v) {
442432
r.failRequest(resp, http.StatusBadRequest, []byte(fmt.Sprintf(responseErrHandlingIndexedFields, len(events)+len(metricEvents))), nil)
443433
return
444434
}
445435
}
436+
446437
if msg.IsMetric() {
447438
if r.metricsConsumer == nil {
448439
r.failRequest(resp, http.StatusBadRequest, errUnsupportedMetricEvent, err)
449440
return
450441
}
451442
metricEvents = append(metricEvents, &msg)
452443
} else {
444+
if msg.Event == nil {
445+
r.failRequest(resp, http.StatusBadRequest, eventRequiredRespBody, nil)
446+
return
447+
}
448+
449+
if msg.Event == "" {
450+
r.failRequest(resp, http.StatusBadRequest, eventBlankRespBody, nil)
451+
return
452+
}
453+
453454
if r.logsConsumer == nil {
454455
r.failRequest(resp, http.StatusBadRequest, errUnsupportedLogEvent, err)
455456
return

receiver/splunkhecreceiver/receiver_test.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) {
251251
{
252252
name: "metric_msg_accepted",
253253
req: func() *http.Request {
254-
msgBytes, err := json.Marshal(buildSplunkHecMetricsMsg(3, 4, 3))
254+
msgBytes, err := json.Marshal(buildSplunkHecMetricsMsg("metric", 3, 4, 3))
255255
require.NoError(t, err)
256256
req := httptest.NewRequest(http.MethodPost, "http://localhost/foo", bytes.NewReader(msgBytes))
257257
return req
@@ -368,7 +368,7 @@ func Test_consumer_err(t *testing.T) {
368368

369369
func Test_consumer_err_metrics(t *testing.T) {
370370
currentTime := float64(time.Now().UnixNano()) / 1e6
371-
splunkMsg := buildSplunkHecMetricsMsg(currentTime, 13, 2)
371+
splunkMsg := buildSplunkHecMetricsMsg("metric", currentTime, 13, 2)
372372
assert.True(t, splunkMsg.IsMetric())
373373
config := createDefaultConfig().(*Config)
374374
config.Endpoint = "localhost:0" // Actually not creating the endpoint\
@@ -570,7 +570,7 @@ func Test_splunkhecReceiver_AccessTokenPassthrough(t *testing.T) {
570570
currentTime := float64(time.Now().UnixNano()) / 1e6
571571
var splunkhecMsg *splunk.Event
572572
if tt.metric {
573-
splunkhecMsg = buildSplunkHecMetricsMsg(currentTime, 1.0, 3)
573+
splunkhecMsg = buildSplunkHecMetricsMsg("metric", currentTime, 1.0, 3)
574574
} else {
575575
splunkhecMsg = buildSplunkHecMsg(currentTime, 3)
576576
}
@@ -738,18 +738,33 @@ func Test_Metrics_splunkhecReceiver_IndexSourceTypePassthrough(t *testing.T) {
738738
name string
739739
index string
740740
sourcetype string
741+
event string
741742
}{
742743
{
743-
name: "No index, no source type",
744+
name: "No index, no source type",
745+
event: "metric",
744746
},
745747
{
746748
name: "Index, no source type",
747749
index: "myindex",
750+
event: "metric",
748751
},
749752
{
750753
name: "Index and source type",
751754
index: "myindex",
752755
sourcetype: "source:type",
756+
event: "metric",
757+
},
758+
{
759+
name: "empty event",
760+
event: "",
761+
},
762+
{
763+
name: "any value event",
764+
event: "some event",
765+
},
766+
{
767+
name: "nil event",
753768
},
754769
}
755770

@@ -786,7 +801,7 @@ func Test_Metrics_splunkhecReceiver_IndexSourceTypePassthrough(t *testing.T) {
786801
rcv.metricsConsumer = exporter
787802

788803
currentTime := float64(time.Now().UnixNano()) / 1e6
789-
splunkhecMsg := buildSplunkHecMetricsMsg(currentTime, 42, 3)
804+
splunkhecMsg := buildSplunkHecMetricsMsg(tt.event, currentTime, 42, 3)
790805
splunkhecMsg.Index = tt.index
791806
splunkhecMsg.SourceType = tt.sourcetype
792807
msgBytes, _ := json.Marshal(splunkhecMsg)
@@ -834,10 +849,10 @@ func Test_Metrics_splunkhecReceiver_IndexSourceTypePassthrough(t *testing.T) {
834849
}
835850
}
836851

837-
func buildSplunkHecMetricsMsg(time float64, value int64, dimensions uint) *splunk.Event {
852+
func buildSplunkHecMetricsMsg(event any, time float64, value int64, dimensions uint) *splunk.Event {
838853
ev := &splunk.Event{
839854
Time: time,
840-
Event: "metric",
855+
Event: event,
841856
Fields: map[string]any{
842857
"metric_name:foo": value,
843858
},

0 commit comments

Comments
 (0)