Skip to content

Commit b16decb

Browse files
authored
feat(loki.secretfilter): Remove redundant secrets_redacted_by_rule_total and secrets_redacted_by_origin metrics (#5970)
The `loki_secretfilter_secrets_redacted_by_category_total` metric (introduced in #5855) partitions by both `rule` and `origin`, making the two single-dimension metrics redundant. Any query with them can be expressed via `sum by (rule|origin) (...)` on the category metric.
1 parent 0ec8a26 commit b16decb

3 files changed

Lines changed: 8 additions & 108 deletions

File tree

docs/sources/reference/components/loki/loki.secretfilter.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ You can use the following arguments with `loki.secretfilter`:
5252
| `drop_on_timeout` | `bool` | When true, drop entries that exceed `processing_timeout` instead of forwarding them unredacted. | `false` | no |
5353
| `gitleaks_config` | `string` | Path to a custom Gitleaks TOML config file. If empty, the default Gitleaks config is used. | `""` | no |
5454
| `label_timed_out` | `bool` | When true, adds `secretfilter="timed-out"` to entries forwarded after a processing timeout. | `false` | no |
55-
| `origin_label` | `string` | Loki label to use as the `origin` dimension in `secrets_redacted_by_origin` and `secrets_redacted_by_category_total`. If empty, `secrets_redacted_by_origin` is not registered and the `origin` label on `secrets_redacted_by_category_total` is set to `""`. | `""` | no |
55+
| `origin_label` | `string` | Loki label to use as the `origin` dimension in `secrets_redacted_by_category_total`. | `""` | no |
5656
| `processing_timeout` | `duration` | Maximum time allowed to process a single log entry. `0` disables the timeout. | `0` | no |
5757
| `rate` | `float` | Entry sampling rate in `[0.0, 1.0]` where `1` processes all entries. Unsampled entries are forwarded unchanged. | `1.0` | no |
5858
| `redact_percent` | `uint` | When `redact_with` is not set: percent of the secret to redact (1–100), where 100 is full redaction. | `80` | no |
@@ -67,7 +67,7 @@ The default configuration may change between {{< param "PRODUCT_NAME" >}} versio
6767
For consistent behavior, use an external configuration file via `gitleaks_config`.
6868
{{< /admonition >}}
6969

70-
If you leave `origin_label` empty, the component doesn't register `secrets_redacted_by_origin` and sets the origin label on `secrets_redacted_by_category_total` to `""`.
70+
If you leave `origin_label` empty, the component sets the origin label on `secrets_redacted_by_category_total` to `""`.
7171

7272
**Redaction behavior:**
7373

@@ -84,9 +84,9 @@ Entries that {{< param "PRODUCT_NAME" >}} does not select based on the sampling
8484
Use a value below `1.0`, for example, `0.1` for 10%, to reduce CPU usage when processing high-volume logs.
8585
Monitor `loki_secretfilter_entries_bypassed_total` to observe how many entries were skipped.
8686

87-
**Origin metric:** The `origin_label` argument specifies the Loki label the component uses as the origin dimension in the `secrets_redacted_by_origin` and `secrets_redacted_by_category_total` metrics.
87+
**Origin metric:** The `origin_label` argument specifies the Loki label the component uses as the origin dimension in `secrets_redacted_by_category_total`.
8888
You can track how many secrets were redacted per source or environment.
89-
When `origin_label` isn’t set, the component doesn’t register `secrets_redacted_by_origin`, and the `origin` label on `secrets_redacted_by_category_total` defaults to an empty string.
89+
When `origin_label` isn’t set, the `origin` label on `secrets_redacted_by_category_total` defaults to an empty string.
9090

9191
**Processing timeout:** The `processing_timeout` argument sets a maximum duration for processing each log entry.
9292
When the timeout is exceeded, the `loki_secretfilter_lines_timed_out_total` metric is incremented.
@@ -133,8 +133,6 @@ The following fields are exported and can be referenced by other components:
133133
| `loki_secretfilter_lines_timed_out_total` | Counter | Total number of log lines that exceeded the processing timeout, whether dropped or forwarded. |
134134
| `loki_secretfilter_processing_duration_seconds` | Summary | Time taken to process and redact logs, in seconds. |
135135
| `loki_secretfilter_secrets_redacted_total` | Counter | Total number of secrets redacted. |
136-
| `loki_secretfilter_secrets_redacted_by_rule_total` | Counter | Number of secrets redacted, partitioned by rule name. |
137-
| `loki_secretfilter_secrets_redacted_by_origin` | Counter | Number of secrets redacted, partitioned by origin label, when `origin_label` is set. |
138136
| `loki_secretfilter_secrets_redacted_by_category_total` | Counter | Number of secrets redacted, partitioned by rule name and origin label value. The `origin` label is empty when `origin_label` is not set or the label is absent on the entry. |
139137

140138
## Example

internal/component/loki/secretfilter/secretfilter.go

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ type secretDetector interface {
120120
// Metrics exposed by this component:
121121
//
122122
// - loki_secretfilter_secrets_redacted_total: Total number of secrets that have been redacted.
123-
// - loki_secretfilter_secrets_redacted_by_rule_total: Number of secrets redacted, partitioned by rule name.
124-
// - loki_secretfilter_secrets_redacted_by_origin: Number of secrets redacted, partitioned by origin label value (only registered when origin_label is set).
125123
// - loki_secretfilter_secrets_redacted_by_category_total: Number of secrets redacted, partitioned by rule name and origin label value.
126124
// - loki_secretfilter_processing_duration_seconds: Summary of time taken to process and redact log entries.
127125
// - loki_secretfilter_entries_bypassed_total: Total number of entries forwarded without processing due to sampling.
@@ -132,12 +130,6 @@ type metrics struct {
132130
// Total number of secrets redacted
133131
secretsRedactedTotal prometheus.Counter
134132

135-
// Number of secrets redacted by rule type
136-
secretsRedactedByRule *prometheus.CounterVec
137-
138-
// Number of secrets redacted by specified labels
139-
secretsRedactedByOrigin *prometheus.CounterVec
140-
141133
// Number of secrets redacted by rule and origin (combined)
142134
secretsRedactedByCategory *prometheus.CounterVec
143135

@@ -155,7 +147,7 @@ type metrics struct {
155147
}
156148

157149
// newMetrics creates a new set of metrics for the secretfilter component.
158-
func newMetrics(reg prometheus.Registerer, originLabel string) *metrics {
150+
func newMetrics(reg prometheus.Registerer) *metrics {
159151
var m metrics
160152

161153
m.secretsRedactedTotal = prometheus.NewCounter(prometheus.CounterOpts{
@@ -164,20 +156,6 @@ func newMetrics(reg prometheus.Registerer, originLabel string) *metrics {
164156
Help: "Total number of secrets that have been redacted.",
165157
})
166158

167-
m.secretsRedactedByRule = prometheus.NewCounterVec(prometheus.CounterOpts{
168-
Subsystem: "loki_secretfilter",
169-
Name: "secrets_redacted_by_rule_total",
170-
Help: "Number of secrets redacted, partitioned by rule name.",
171-
}, []string{"rule"})
172-
173-
if originLabel != "" {
174-
m.secretsRedactedByOrigin = prometheus.NewCounterVec(prometheus.CounterOpts{
175-
Subsystem: "loki_secretfilter",
176-
Name: "secrets_redacted_by_origin",
177-
Help: "Number of secrets redacted, partitioned by origin label value.",
178-
}, []string{"origin"})
179-
}
180-
181159
m.secretsRedactedByCategory = prometheus.NewCounterVec(prometheus.CounterOpts{
182160
Subsystem: "loki_secretfilter",
183161
Name: "secrets_redacted_by_category_total",
@@ -214,10 +192,6 @@ func newMetrics(reg prometheus.Registerer, originLabel string) *metrics {
214192

215193
if reg != nil {
216194
m.secretsRedactedTotal = util.MustRegisterOrGet(reg, m.secretsRedactedTotal).(prometheus.Counter)
217-
m.secretsRedactedByRule = util.MustRegisterOrGet(reg, m.secretsRedactedByRule).(*prometheus.CounterVec)
218-
if originLabel != "" {
219-
m.secretsRedactedByOrigin = util.MustRegisterOrGet(reg, m.secretsRedactedByOrigin).(*prometheus.CounterVec)
220-
}
221195
m.secretsRedactedByCategory = util.MustRegisterOrGet(reg, m.secretsRedactedByCategory).(*prometheus.CounterVec)
222196
m.processingDuration = util.MustRegisterOrGet(reg, m.processingDuration).(prometheus.Summary)
223197
m.entriesBypassedTotal = util.MustRegisterOrGet(reg, m.entriesBypassedTotal).(prometheus.Counter)
@@ -276,7 +250,7 @@ func New(o component.Options, args Arguments) (*Component, error) {
276250
log: o.Logger,
277251
receiver: loki.NewLogsReceiver(loki.WithComponentID(o.ID)),
278252
fanout: loki.NewFanout(args.ForwardTo),
279-
metrics: newMetrics(o.Registerer, args.OriginLabel),
253+
metrics: newMetrics(o.Registerer),
280254
debugDataPublisher: debugDataPublisher.(livedebugging.DebugDataPublisher),
281255
}
282256

@@ -412,12 +386,10 @@ func (c *Component) redactLine(entry loki.Entry, findings []report.Finding) loki
412386
line = strings.ReplaceAll(line, originalSecret, replacement)
413387

414388
c.metrics.secretsRedactedTotal.Inc()
415-
c.metrics.secretsRedactedByRule.WithLabelValues(ruleName).Inc()
416389
originValue := ""
417390
if c.args.OriginLabel != "" && len(entry.Labels) > 0 {
418391
if value, ok := entry.Labels[model.LabelName(c.args.OriginLabel)]; ok {
419392
originValue = string(value)
420-
c.metrics.secretsRedactedByOrigin.WithLabelValues(originValue).Inc()
421393
}
422394
}
423395
c.metrics.secretsRedactedByCategory.WithLabelValues(ruleName, originValue).Inc()
@@ -469,7 +441,7 @@ func (c *Component) Update(args component.Arguments) error {
469441
} else {
470442
c.sampler.Update(newArgs.Rate)
471443
}
472-
c.metrics = newMetrics(c.opts.Registerer, newArgs.OriginLabel)
444+
c.metrics = newMetrics(c.opts.Registerer)
473445

474446
level.Debug(c.log).Log(
475447
"msg", "loki.secretfilter config updated",

internal/component/loki/secretfilter/secretfilter_test.go

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -477,45 +477,6 @@ func TestMetrics(t *testing.T) {
477477
"secretsRedactedTotal metric value is incorrect")
478478
}
479479

480-
// Check secretsRedactedByRule - combine all metrics in a single string
481-
if len(tc.expectedRedactedByRule) > 0 {
482-
var metricStrings strings.Builder
483-
metricStrings.WriteString("# HELP loki_secretfilter_secrets_redacted_by_rule_total Number of secrets redacted, partitioned by rule name.\n")
484-
metricStrings.WriteString("# TYPE loki_secretfilter_secrets_redacted_by_rule_total counter\n")
485-
486-
// Add each rule metric
487-
for ruleName, expectedCount := range tc.expectedRedactedByRule {
488-
metric := fmt.Sprintf(`loki_secretfilter_secrets_redacted_by_rule_total{rule="%s"} %d`,
489-
ruleName, expectedCount)
490-
metricStrings.WriteString(metric + "\n")
491-
}
492-
493-
// Compare all the metrics at once
494-
require.NoError(t,
495-
testutil.GatherAndCompare(registry, strings.NewReader(metricStrings.String()),
496-
"loki_secretfilter_secrets_redacted_by_rule_total"))
497-
}
498-
499-
// Check secretsRedactedByOrigin when redactions occurred
500-
if tc.expectedRedactedTotal > 0 {
501-
// Build expected origin label metric
502-
var metricStrings strings.Builder
503-
metricStrings.WriteString("# HELP loki_secretfilter_secrets_redacted_by_origin Number of secrets redacted, partitioned by origin label value.\n")
504-
metricStrings.WriteString("# TYPE loki_secretfilter_secrets_redacted_by_origin counter\n")
505-
506-
// Add origin label metric
507-
if jobValue, exists := labels[model.LabelName("job")]; exists {
508-
metric := fmt.Sprintf(`loki_secretfilter_secrets_redacted_by_origin{origin="%s"} %d`,
509-
jobValue, tc.expectedRedactedTotal)
510-
metricStrings.WriteString(metric + "\n")
511-
}
512-
513-
// Compare the metrics
514-
require.NoError(t,
515-
testutil.GatherAndCompare(registry, strings.NewReader(metricStrings.String()),
516-
"loki_secretfilter_secrets_redacted_by_origin"))
517-
}
518-
519480
// Check secretsRedactedByCategory
520481
if len(tc.expectedRedactedByRule) > 0 {
521482
jobValue := string(labels[model.LabelName("job")])
@@ -562,8 +523,7 @@ func TestMetrics(t *testing.T) {
562523
}
563524

564525
// TestMetrics_NoOriginLabel verifies that when origin_label is not set,
565-
// secrets_redacted_by_category_total still increments (with origin="") but
566-
// secrets_redacted_by_origin is not registered at all.
526+
// secrets_redacted_by_category_total still increments with origin="".
567527
func TestMetrics_NoOriginLabel(t *testing.T) {
568528
registry := prometheus.NewRegistry()
569529

@@ -590,11 +550,6 @@ func TestMetrics_NoOriginLabel(t *testing.T) {
590550
}
591551
c.processEntry(context.Background(), entry)
592552

593-
// secrets_redacted_by_origin must not be registered
594-
count, err := testutil.GatherAndCount(registry, "loki_secretfilter_secrets_redacted_by_origin")
595-
require.NoError(t, err)
596-
require.Equal(t, 0, count, "secrets_redacted_by_origin should not be registered when origin_label is not set")
597-
598553
// secrets_redacted_by_category_total must increment with origin=""
599554
expected := strings.NewReader(
600555
"# HELP loki_secretfilter_secrets_redacted_by_category_total Number of secrets redacted, partitioned by rule name and origin label value.\n" +
@@ -628,8 +583,6 @@ func TestMetricsRegistration(t *testing.T) {
628583

629584
// Increment all metrics to ensure they will be gathered
630585
c.metrics.secretsRedactedTotal.Inc()
631-
c.metrics.secretsRedactedByRule.WithLabelValues("test_rule").Inc()
632-
c.metrics.secretsRedactedByOrigin.WithLabelValues("test_value").Inc()
633586
c.metrics.secretsRedactedByCategory.WithLabelValues("test_rule", "test_value").Inc()
634587
c.metrics.processingDuration.Observe(0.123)
635588

@@ -640,8 +593,6 @@ func TestMetricsRegistration(t *testing.T) {
640593
// Create a map of expected metrics
641594
expectedMetrics := map[string]bool{
642595
"loki_secretfilter_secrets_redacted_total": false,
643-
"loki_secretfilter_secrets_redacted_by_rule_total": false,
644-
"loki_secretfilter_secrets_redacted_by_origin": false,
645596
"loki_secretfilter_secrets_redacted_by_category_total": false,
646597
"loki_secretfilter_processing_duration_seconds": false,
647598
}
@@ -719,27 +670,6 @@ func TestMetricsMultipleEntries(t *testing.T) {
719670
// We should have 3 redacted secrets (2 grafana-api-key and 1 gcp-api-key)
720671
require.Equal(t, float64(3), testutil.ToFloat64(c.metrics.secretsRedactedTotal),
721672
"secretsRedactedTotal should count all secrets across multiple entries")
722-
723-
// Check secretsRedactedByRule for each rule type
724-
require.NoError(t,
725-
testutil.GatherAndCompare(registry, strings.NewReader(`
726-
# HELP loki_secretfilter_secrets_redacted_by_rule_total Number of secrets redacted, partitioned by rule name.
727-
# TYPE loki_secretfilter_secrets_redacted_by_rule_total counter
728-
loki_secretfilter_secrets_redacted_by_rule_total{rule="grafana-api-key"} 2
729-
loki_secretfilter_secrets_redacted_by_rule_total{rule="gcp-api-key"} 1
730-
`),
731-
"loki_secretfilter_secrets_redacted_by_rule_total"))
732-
733-
// Check secretsRedactedByOrigin values
734-
require.NoError(t,
735-
testutil.GatherAndCompare(registry, strings.NewReader(`
736-
# HELP loki_secretfilter_secrets_redacted_by_origin Number of secrets redacted, partitioned by origin label value.
737-
# TYPE loki_secretfilter_secrets_redacted_by_origin counter
738-
loki_secretfilter_secrets_redacted_by_origin{origin="test1"} 1
739-
loki_secretfilter_secrets_redacted_by_origin{origin="test2"} 1
740-
loki_secretfilter_secrets_redacted_by_origin{origin="test4"} 1
741-
`),
742-
"loki_secretfilter_secrets_redacted_by_origin"))
743673
}
744674

745675
// TestArgumentsUpdate validates that the secretfilter component works correctly

0 commit comments

Comments
 (0)