Skip to content

Commit c293600

Browse files
bacherflf7o
authored andcommitted
[processor/redaction] add support for redacting metrics and logs attributes (open-telemetry#34609)
**Description:** This PR extends the redaction processor to also support the redaction of attributes within logs and metrics. **Link to tracking Issue:** open-telemetry#34479 **Testing:** Extended the existing unit tests to also cover the redaction of logs and metrics **Documentation:** Adapted the readme to reflect the changes --------- Signed-off-by: Florian Bacher <[email protected]>
1 parent 8706e23 commit c293600

File tree

9 files changed

+582
-145
lines changed

9 files changed

+582
-145
lines changed
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: redactionprocessor
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add support for logs and metrics
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: [34479]
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: []

processor/redactionprocessor/README.md

+12-10
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@
33
<!-- status autogenerated section -->
44
| Status | |
55
| ------------- |-----------|
6-
| Stability | [beta]: traces |
6+
| Stability | [alpha]: logs, metrics |
7+
| | [beta]: traces |
78
| Distributions | [contrib] |
89
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aprocessor%2Fredaction%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aprocessor%2Fredaction) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aprocessor%2Fredaction%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aprocessor%2Fredaction) |
910
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@dmitryax](https://www.github.com/dmitryax), [@mx-psi](https://www.github.com/mx-psi), [@TylerHelmuth](https://www.github.com/TylerHelmuth) |
1011
| Emeritus | [@leonsp-ai](https://www.github.com/leonsp-ai) |
1112

13+
[alpha]: https://github.com/open-telemetry/opentelemetry-collector#alpha
1214
[beta]: https://github.com/open-telemetry/opentelemetry-collector#beta
1315
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
1416
<!-- end autogenerated section -->
1517

16-
This processor deletes span attributes that don't match a list of allowed span
17-
attributes. It also masks span attribute values that match a blocked value
18-
list. Span attributes that aren't on the allowed list are removed before any
18+
This processor deletes span, log, and metric datapoint attributes that don't match a list of allowed
19+
attributes. It also masks attribute values that match a blocked value
20+
list. Attributes that aren't on the allowed list are removed before any
1921
value checks are done.
2022

2123
## Use Cases
@@ -57,9 +59,9 @@ processors:
5759
# allowed_keys list. The list of blocked_values is applied regardless. If
5860
# you just want to block values, set this to true.
5961
allow_all_keys: false
60-
# allowed_keys is a list of span attribute keys that are kept on the span and
62+
# allowed_keys is a list of span/log/datapoint attribute keys that are kept on the span/log/datapoint and
6163
# processed. The list is designed to fail closed. If allowed_keys is empty,
62-
# no span attributes are allowed and all span attributes are removed. To
64+
# no attributes are allowed and all span attributes are removed. To
6365
# allow all keys, set allow_all_keys to true.
6466
allowed_keys:
6567
- description
@@ -76,7 +78,7 @@ processors:
7678
- "4[0-9]{12}(?:[0-9]{3})?" ## Visa credit card number
7779
- "(5[1-5][0-9]{14})" ## MasterCard number
7880
# summary controls the verbosity level of the diagnostic attributes that
79-
# the processor adds to the spans when it redacts or masks other
81+
# the processor adds to the spans/logs/datapoints when it redacts or masks other
8082
# attributes. In some contexts a list of redacted attributes leaks
8183
# information, while it is valuable when integrating and testing a new
8284
# configuration. Possible values:
@@ -93,16 +95,16 @@ Ignored attributes are processed first so they're always allowed and never
9395
blocked. This field should only be used where you know the data is always
9496
safe to send to the telemetry system.
9597
96-
Only span attributes included on the list of allowed keys list are retained.
97-
If `allowed_keys` is empty, then no span attributes are allowed. All span
98+
Only span/log/datapoint attributes included on the list of allowed keys list are retained.
99+
If `allowed_keys` is empty, then no attributes are allowed. All
98100
attributes are removed in that case. To keep all span attributes, you should
99101
explicitly set `allow_all_keys` to true.
100102

101103
`blocked_values` applies to the values of the allowed keys. If the value of an
102104
allowed key matches the regular expression for a blocked value, the matching
103105
part of the value is then masked with a fixed length of asterisks.
104106

105-
For example, if `notes` is on the list of allowed keys, then the `notes` span
107+
For example, if `notes` is on the list of allowed keys, then the `notes`
106108
attribute is retained. However, if there is a value such as a credit card
107109
number in the `notes` field that matched a regular expression on the list of
108110
blocked values, then that value is masked.

processor/redactionprocessor/factory.go

+46
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ func NewFactory() processor.Factory {
2323
metadata.Type,
2424
createDefaultConfig,
2525
processor.WithTraces(createTracesProcessor, metadata.TracesStability),
26+
processor.WithLogs(createLogsProcessor, metadata.LogsStability),
27+
processor.WithMetrics(createMetricsProcessor, metadata.MetricsStability),
2628
)
2729
}
2830

@@ -53,3 +55,47 @@ func createTracesProcessor(
5355
redaction.processTraces,
5456
processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}))
5557
}
58+
59+
// createLogsProcessor creates an instance of redaction for processing logs
60+
func createLogsProcessor(
61+
ctx context.Context,
62+
set processor.Settings,
63+
cfg component.Config,
64+
next consumer.Logs) (processor.Logs, error) {
65+
oCfg := cfg.(*Config)
66+
67+
red, err := newRedaction(ctx, oCfg, set.Logger)
68+
if err != nil {
69+
return nil, fmt.Errorf("error creating a redaction processor: %w", err)
70+
}
71+
72+
return processorhelper.NewLogsProcessor(
73+
ctx,
74+
set,
75+
cfg,
76+
next,
77+
red.processLogs,
78+
processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}))
79+
}
80+
81+
// createMetricsProcessor creates an instance of redaction for processing metrics
82+
func createMetricsProcessor(
83+
ctx context.Context,
84+
set processor.Settings,
85+
cfg component.Config,
86+
next consumer.Metrics) (processor.Metrics, error) {
87+
oCfg := cfg.(*Config)
88+
89+
red, err := newRedaction(ctx, oCfg, set.Logger)
90+
if err != nil {
91+
return nil, fmt.Errorf("error creating a redaction processor: %w", err)
92+
}
93+
94+
return processorhelper.NewMetricsProcessor(
95+
ctx,
96+
set,
97+
cfg,
98+
next,
99+
red.processMetrics,
100+
processorhelper.WithCapabilities(consumer.Capabilities{MutatesData: true}))
101+
}

processor/redactionprocessor/factory_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,21 @@ func TestCreateTestProcessor(t *testing.T) {
2626
assert.NotNil(t, tp)
2727
assert.True(t, tp.Capabilities().MutatesData)
2828
}
29+
30+
func TestCreateTestLogsProcessor(t *testing.T) {
31+
cfg := &Config{}
32+
33+
tp, err := createLogsProcessor(context.Background(), processortest.NewNopSettings(), cfg, consumertest.NewNop())
34+
assert.NoError(t, err)
35+
assert.NotNil(t, tp)
36+
assert.Equal(t, true, tp.Capabilities().MutatesData)
37+
}
38+
39+
func TestCreateTestMetricsProcessor(t *testing.T) {
40+
cfg := &Config{}
41+
42+
tp, err := createMetricsProcessor(context.Background(), processortest.NewNopSettings(), cfg, consumertest.NewNop())
43+
assert.NoError(t, err)
44+
assert.NotNil(t, tp)
45+
assert.Equal(t, true, tp.Capabilities().MutatesData)
46+
}

processor/redactionprocessor/generated_component_test.go

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/redactionprocessor/internal/metadata/generated_status.go

+3-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

processor/redactionprocessor/metadata.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ status:
44
class: processor
55
stability:
66
beta: [traces]
7+
alpha: [logs,metrics]
78
distributions: [contrib]
89
codeowners:
910
active: [dmitryax, mx-psi, TylerHelmuth]

processor/redactionprocessor/processor.go

+75
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"strings"
1212

1313
"go.opentelemetry.io/collector/pdata/pcommon"
14+
"go.opentelemetry.io/collector/pdata/plog"
15+
"go.opentelemetry.io/collector/pdata/pmetric"
1416
"go.opentelemetry.io/collector/pdata/ptrace"
1517
"go.uber.org/zap"
1618
)
@@ -59,6 +61,22 @@ func (s *redaction) processTraces(ctx context.Context, batch ptrace.Traces) (ptr
5961
return batch, nil
6062
}
6163

64+
func (s *redaction) processLogs(ctx context.Context, logs plog.Logs) (plog.Logs, error) {
65+
for i := 0; i < logs.ResourceLogs().Len(); i++ {
66+
rl := logs.ResourceLogs().At(i)
67+
s.processResourceLog(ctx, rl)
68+
}
69+
return logs, nil
70+
}
71+
72+
func (s *redaction) processMetrics(ctx context.Context, metrics pmetric.Metrics) (pmetric.Metrics, error) {
73+
for i := 0; i < metrics.ResourceMetrics().Len(); i++ {
74+
rm := metrics.ResourceMetrics().At(i)
75+
s.processResourceMetric(ctx, rm)
76+
}
77+
return metrics, nil
78+
}
79+
6280
// processResourceSpan processes the RS and all of its spans and then returns the last
6381
// view metric context. The context can be used for tests
6482
func (s *redaction) processResourceSpan(ctx context.Context, rs ptrace.ResourceSpans) {
@@ -79,6 +97,63 @@ func (s *redaction) processResourceSpan(ctx context.Context, rs ptrace.ResourceS
7997
}
8098
}
8199

100+
// processResourceLog processes the log resource and all of its logs and then returns the last
101+
// view metric context. The context can be used for tests
102+
func (s *redaction) processResourceLog(ctx context.Context, rl plog.ResourceLogs) {
103+
rsAttrs := rl.Resource().Attributes()
104+
105+
s.processAttrs(ctx, rsAttrs)
106+
107+
for j := 0; j < rl.ScopeLogs().Len(); j++ {
108+
ils := rl.ScopeLogs().At(j)
109+
for k := 0; k < rl.ScopeLogs().Len(); k++ {
110+
log := ils.LogRecords().At(k)
111+
s.processAttrs(ctx, log.Attributes())
112+
}
113+
}
114+
}
115+
116+
func (s *redaction) processResourceMetric(ctx context.Context, rm pmetric.ResourceMetrics) {
117+
rsAttrs := rm.Resource().Attributes()
118+
119+
s.processAttrs(ctx, rsAttrs)
120+
121+
for j := 0; j < rm.ScopeMetrics().Len(); j++ {
122+
ils := rm.ScopeMetrics().At(j)
123+
for k := 0; k < ils.Metrics().Len(); k++ {
124+
metric := ils.Metrics().At(k)
125+
switch metric.Type() {
126+
case pmetric.MetricTypeGauge:
127+
dps := metric.Gauge().DataPoints()
128+
for i := 0; i < dps.Len(); i++ {
129+
s.processAttrs(ctx, dps.At(i).Attributes())
130+
}
131+
case pmetric.MetricTypeSum:
132+
dps := metric.Sum().DataPoints()
133+
for i := 0; i < dps.Len(); i++ {
134+
s.processAttrs(ctx, dps.At(i).Attributes())
135+
}
136+
case pmetric.MetricTypeHistogram:
137+
dps := metric.Histogram().DataPoints()
138+
for i := 0; i < dps.Len(); i++ {
139+
s.processAttrs(ctx, dps.At(i).Attributes())
140+
}
141+
case pmetric.MetricTypeExponentialHistogram:
142+
dps := metric.ExponentialHistogram().DataPoints()
143+
for i := 0; i < dps.Len(); i++ {
144+
s.processAttrs(ctx, dps.At(i).Attributes())
145+
}
146+
case pmetric.MetricTypeSummary:
147+
dps := metric.Summary().DataPoints()
148+
for i := 0; i < dps.Len(); i++ {
149+
s.processAttrs(ctx, dps.At(i).Attributes())
150+
}
151+
case pmetric.MetricTypeEmpty:
152+
}
153+
}
154+
}
155+
}
156+
82157
// processAttrs redacts the attributes of a resource span or a span
83158
func (s *redaction) processAttrs(_ context.Context, attributes pcommon.Map) {
84159
// TODO: Use the context for recording metrics

0 commit comments

Comments
 (0)