Skip to content

Commit ecb6454

Browse files
committed
[exporter/alertmanager] Add alert labels from event attributes list open-telemetry#38063
1 parent 4930224 commit ecb6454

File tree

7 files changed

+94
-2
lines changed

7 files changed

+94
-2
lines changed
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: alertmanagerexporter
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add alert labels based on event attribute list in alermanager exporter
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: [38063]
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]

exporter/alertmanagerexporter/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The following settings are optional:
3030
- `generator_url` is the source of the alerts to be used in Alertmanager's payload. The default value is "opentelemetry-collector", and can be set to the URL of the opentelemetry collector.
3131
- `severity_attribute` is the SpanEvent Attribute name which can be used instead of default severity string in Alert payload
3232
e.g.: If `severity_attribute` is set to "foo" and the SpanEvent has an attribute called foo, foo's attribute value will be used as the severity value for that particular Alert generated from the SpanEvent.
33+
- `event_labels` is the list of Event Attributes that will be captured as Labels in the Alert payload if value exists.
3334

3435

3536
Example config:
@@ -41,6 +42,7 @@ exporters:
4142
endpoint: "https://a.new.alertmanager.target:9093"
4243
severity: "debug"
4344
severity_attribute: "foo"
45+
event_labels: ["foo", "bar"]
4446
tls:
4547
cert_file: /var/lib/mycert.pem
4648
key_file: /var/lib/key.pem

exporter/alertmanagerexporter/alertmanager_exporter.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"io"
1212
"net/http"
13+
"slices"
1314
"time"
1415

1516
"github.com/prometheus/common/model"
@@ -104,15 +105,28 @@ func createAnnotations(event *alertmanagerEvent) model.LabelSet {
104105
return labelMap
105106
}
106107

108+
func (s *alertmanagerExporter) createLabels(event *alertmanagerEvent) model.LabelSet {
109+
labelMap := model.LabelSet{}
110+
for key, attr := range event.spanEvent.Attributes().All() {
111+
if slices.Contains(s.config.EventLabels, key) {
112+
labelMap[model.LabelName(key)] = model.LabelValue(attr.AsString())
113+
}
114+
}
115+
labelMap["severity"] = model.LabelValue(event.severity)
116+
labelMap["event_name"] = model.LabelValue(event.spanEvent.Name())
117+
return labelMap
118+
}
119+
107120
func (s *alertmanagerExporter) convertEventsToAlertPayload(events []*alertmanagerEvent) []model.Alert {
108121
payload := make([]model.Alert, len(events))
109122

110123
for i, event := range events {
111124
annotations := createAnnotations(event)
125+
labels := s.createLabels(event)
112126

113127
alert := model.Alert{
114128
StartsAt: time.Now(),
115-
Labels: model.LabelSet{"severity": model.LabelValue(event.severity), "event_name": model.LabelValue(event.spanEvent.Name())},
129+
Labels: labels,
116130
Annotations: annotations,
117131
GeneratorURL: s.generatorURL,
118132
}

exporter/alertmanagerexporter/alertmanager_exporter_test.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func TestAlertManagerExporterSeverity(t *testing.T) {
151151
factory := NewFactory()
152152
cfg := factory.CreateDefaultConfig().(*Config)
153153
cfg.SeverityAttribute = "foo"
154+
cfg.EventLabels = []string{}
154155
set := exportertest.NewNopSettings(metadata.Type)
155156
am := newAlertManagerExporter(cfg, set.TelemetrySettings)
156157
require.NotNil(t, am)
@@ -199,7 +200,7 @@ func TestAlertManagerExporterNoDefaultSeverity(t *testing.T) {
199200
set := exportertest.NewNopSettings(metadata.Type)
200201
am := newAlertManagerExporter(cfg, set.TelemetrySettings)
201202
require.NotNil(t, am)
202-
203+
cfg.EventLabels = []string{}
203204
// make traces & a span
204205
traces, span := createTracesAndSpan()
205206

@@ -228,6 +229,7 @@ func TestAlertManagerExporterAlertPayload(t *testing.T) {
228229
cfg := factory.CreateDefaultConfig().(*Config)
229230
set := exportertest.NewNopSettings(metadata.Type)
230231
am := newAlertManagerExporter(cfg, set.TelemetrySettings)
232+
cfg.EventLabels = []string{}
231233

232234
require.NotNil(t, am)
233235

@@ -277,6 +279,50 @@ func TestAlertManagerTracesExporterNoErrors(t *testing.T) {
277279
assert.NoError(t, err)
278280
}
279281

282+
func TestAlertManagerExporterEventLabels(t *testing.T) {
283+
factory := NewFactory()
284+
cfg := factory.CreateDefaultConfig().(*Config)
285+
set := exportertest.NewNopSettings(metadata.Type)
286+
am := newAlertManagerExporter(cfg, set.TelemetrySettings)
287+
require.NotNil(t, am)
288+
289+
// make traces & a span
290+
_, span := createTracesAndSpan()
291+
292+
// add a span event w/ 3 attributes
293+
event := span.Events().AppendEmpty()
294+
// add event attributes
295+
startTime := pcommon.Timestamp(time.Now().UnixNano())
296+
event.SetTimestamp(startTime + 3)
297+
event.SetName("unittest-event")
298+
attrs := event.Attributes()
299+
attrs.Clear()
300+
attrs.EnsureCapacity(4)
301+
attrs.PutStr("attr1", "unittest-baz")
302+
attrs.PutInt("attr2", 42)
303+
attrs.PutDouble("attr3", 5.14)
304+
305+
var events []*alertmanagerEvent
306+
events = append(events, &alertmanagerEvent{
307+
spanEvent: event,
308+
severity: am.defaultSeverity,
309+
traceID: "0000000000000002",
310+
spanID: "00000002",
311+
})
312+
313+
got := am.convertEventsToAlertPayload(events)
314+
315+
// test - count of attributes
316+
expect := model.Alert{
317+
Labels: model.LabelSet{"severity": "info", "event_name": "unittest-event", "attr1": "unittest-baz", "attr2": "42"},
318+
Annotations: model.LabelSet{"SpanID": "00000002", "TraceID": "0000000000000002", "attr1": "unittest-baz", "attr2": "42", "attr3": "5.14"},
319+
GeneratorURL: "opentelemetry-collector",
320+
}
321+
assert.Equal(t, expect.Labels, got[0].Labels)
322+
assert.Equal(t, expect.Annotations, got[0].Annotations)
323+
assert.Equal(t, expect.GeneratorURL, got[0].GeneratorURL)
324+
}
325+
280326
type mockServer struct {
281327
mockserver *httptest.Server // this means mockServer aggregates 'httptest.Server', but can it's more like inheritance in C++
282328
fooCalledSuccessfully bool // this is false by default

exporter/alertmanagerexporter/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Config struct {
2222
GeneratorURL string `mapstructure:"generator_url"`
2323
DefaultSeverity string `mapstructure:"severity"`
2424
SeverityAttribute string `mapstructure:"severity_attribute"`
25+
EventLabels []string `mapstructure:"event_labels"`
2526
}
2627

2728
var _ component.Config = (*Config)(nil)

exporter/alertmanagerexporter/config_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestLoadConfig(t *testing.T) {
4646
GeneratorURL: "opentelemetry-collector",
4747
DefaultSeverity: "info",
4848
SeverityAttribute: "foo",
49+
EventLabels: []string{"attr1", "attr2"},
4950
TimeoutSettings: exporterhelper.TimeoutConfig{
5051
Timeout: 10 * time.Second,
5152
},

exporter/alertmanagerexporter/factory.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func createDefaultConfig() component.Config {
3434
return &Config{
3535
GeneratorURL: "opentelemetry-collector",
3636
DefaultSeverity: "info",
37+
EventLabels: []string{"attr1", "attr2"},
3738
TimeoutSettings: exporterhelper.NewDefaultTimeoutConfig(),
3839
BackoffConfig: configretry.NewDefaultBackOffConfig(),
3940
QueueSettings: exporterhelper.NewDefaultQueueConfig(),

0 commit comments

Comments
 (0)