Repository: prometheus/statsd_exporter (go)

Issue:
panic in dogstatsd extended aggregation parsing
https://github.com/prometheus/statsd_exporter/pull/558#pullrequestreview-2259676259 – breaking this out into an issue by itself.

From @m-barthelemy:

> After updating to 0.27.1, [this line](https://github.com/prometheus/statsd_exporter/pull/558/files#diff-a9794bf96da36f26e5b9f52911da6346f9d53f8039ed1adf2e804b6782b04febR235) is triggering a panic: runtime error: index out of range [1] with length 1 for us.

Could you run the exporter with `--log.level=debug` and give us an example of a line that's triggering this? Ideally, add it as a test case and see if that also fails.

@GrgDev it seems that we need to check whether the splitting actually resulted in enough parts, do you have capacity to look into that?

Pull request description:
Fix panic when parsing invalid lines
The line

```
|h|#consumer:Kafka::SharedConfigurationConsumer,topic:shared_configuration_update,partition:1,consumer_group:tc_rc_us
```

caused a panic because the line parsing _first_ splits by `:` and then failed to
find a `|` to split on.

Check that we get at least two "line parts" (i.e. splits around `|`) when we
expect them, and if not, gracefully reject the line instead of crashing.

Fixes #572.

Signed-off-by: Matthias Rampke <matthias@prometheus.io>

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28bb9ff..4c3690c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.27.2-pr579 / 2024-09-16
+
+* [BUGFIX] Fix panic on certain invalid lines ([#579](https://github.com/prometheus/statsd_exporter/pull/579))
+
 ## 0.27.1 / 2024-08-18
 
 * [FEATURE] Support [dogstatsd extended aggregation](https://github.com/DataDog/datadog-go/blob/master/README.md#extended-aggregation) ([#558](https://github.com/prometheus/statsd_exporter/pull/558))
diff --git a/VERSION b/VERSION
index 83b4730..ff3932d 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-0.27.1
+0.27.2-pr579
diff --git a/pkg/line/line.go b/pkg/line/line.go
index 949a5ea..97d669f 100644
--- a/pkg/line/line.go
+++ b/pkg/line/line.go
@@ -211,7 +211,7 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
 	elements := strings.SplitN(line, ":", 2)
 	if len(elements) < 2 || len(elements[0]) == 0 || !utf8.ValidString(line) {
 		sampleErrors.WithLabelValues("malformed_line").Inc()
-		level.Debug(logger).Log("msg", "Bad line from StatsD", "line", line)
+		level.Debug(logger).Log("msg", "bad line", "line", line)
 		return events
 	}
 
@@ -223,12 +223,17 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
 
 		// don't allow mixed tagging styles
 		sampleErrors.WithLabelValues("mixed_tagging_styles").Inc()
-		level.Debug(logger).Log("msg", "Bad line (multiple tagging styles) from StatsD", "line", line)
+		level.Debug(logger).Log("msg", "bad line: multiple tagging styles", "line", line)
 		return events
 	}
 
 	var samples []string
 	lineParts := strings.SplitN(elements[1], "|", 3)
+	if len(lineParts) < 2 {
+		sampleErrors.WithLabelValues("not_enough_parts_after_colon").Inc()
+		level.Debug(logger).Log("msg", "bad line: not enough '|'-delimited parts after first ':'", "line", line)
+		return events
+	}
 	if strings.Contains(lineParts[0], ":") {
 		// handle DogStatsD extended aggregation
 		isValidAggType := false
@@ -251,7 +256,7 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
 			samples = aggLines
 		} else {
 			sampleErrors.WithLabelValues("invalid_extended_aggregate_type").Inc()
-			level.Debug(logger).Log("msg", "Bad line (invalid extended aggregate type) from StatsD", "line", line)
+			level.Debug(logger).Log("msg", "bad line: invalid extended aggregate type", "line", line)
 			return events
 		}
 	} else if usingDogStatsDTags {
@@ -267,7 +272,7 @@ samples:
 		components := strings.Split(sample, "|")
 		if len(components) < 2 || len(components) > 4 {
 			sampleErrors.WithLabelValues("malformed_component").Inc()
-			level.Debug(logger).Log("msg", "Bad component", "line", line)
+			level.Debug(logger).Log("msg", "bad component", "line", line)
 			continue
 		}
 		valueStr, statType := components[0], components[1]
@@ -279,7 +284,7 @@ samples:
 
 		value, err := strconv.ParseFloat(valueStr, 64)
 		if err != nil {
-			level.Debug(logger).Log("msg", "Bad value", "value", valueStr, "line", line)
+			level.Debug(logger).Log("msg", "bad value", "value", valueStr, "line", line)
 			sampleErrors.WithLabelValues("malformed_value").Inc()
 			continue
 		}


Test patch:
diff --git a/pkg/line/line_test.go b/pkg/line/line_test.go
index 970320c..305d790 100644
--- a/pkg/line/line_test.go
+++ b/pkg/line/line_test.go
@@ -823,6 +823,12 @@ func TestLineToEvents(t *testing.T) {
 				},
 			},
 		},
+		"invalid event split over lines part 1": {
+			in: "karafka.consumer.consume.cpu_idle_second:  0.111090  -0.055903  -0.195390 (  2.419002)",
+		},
+		"invalid event split over lines part 2": {
+			in: "|h|#consumer:Kafka::SharedConfigurationConsumer,topic:shared_configuration_update,partition:1,consumer_group:tc_rc_us",
+		},
 	}
 
 	parser := NewParser()