Skip to content

Commit 5f46461

Browse files
Adrian Fernandez De La Torreadri1197
authored andcommitted
Add OTEL logic as part of generic logic implementation (forwarder.go)
Signed-off-by: Adrian Fernandez De La Torre <adri1197@gmail.com>
1 parent 91d68d3 commit 5f46461

5 files changed

Lines changed: 279 additions & 228 deletions

File tree

go.mod

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@ require (
4242
github.com/stretchr/testify v1.10.0
4343
gitlab.com/gitlab-org/api/client-go v0.134.0
4444
go.opentelemetry.io/otel v1.37.0
45-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0
46-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0
47-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0
48-
go.opentelemetry.io/otel/sdk v1.36.0
45+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0
46+
go.opentelemetry.io/otel/sdk v1.37.0
4947
go.opentelemetry.io/otel/trace v1.37.0
5048
golang.org/x/oauth2 v0.30.0
5149
golang.org/x/text v0.27.0
@@ -84,7 +82,7 @@ require (
8482
github.com/blang/semver/v4 v4.0.0 // indirect
8583
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 // indirect
8684
github.com/carapace-sh/carapace-shlex v1.0.1 // indirect
87-
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
85+
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
8886
github.com/cespare/xxhash/v2 v2.3.0 // indirect
8987
github.com/chai2010/gettext-go v1.0.2 // indirect
9088
github.com/cloudevents/sdk-go/v2 v2.15.2 // indirect
@@ -129,7 +127,7 @@ require (
129127
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
130128
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
131129
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
132-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
130+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
133131
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
134132
github.com/hashicorp/go-version v1.7.0 // indirect
135133
github.com/inconshreveable/mousetrap v1.1.0 // indirect
@@ -175,8 +173,9 @@ require (
175173
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
176174
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
177175
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
176+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
178177
go.opentelemetry.io/otel/metric v1.37.0 // indirect
179-
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
178+
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
180179
go.uber.org/multierr v1.11.0 // indirect
181180
go.uber.org/zap v1.27.0 // indirect
182181
go.yaml.in/yaml/v2 v2.4.2 // indirect
@@ -191,7 +190,7 @@ require (
191190
golang.org/x/time v0.12.0 // indirect
192191
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
193192
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
194-
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
193+
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
195194
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
196195
google.golang.org/grpc v1.73.0 // indirect
197196
google.golang.org/protobuf v1.36.6 // indirect

go.sum

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ github.com/carapace-sh/carapace-shlex v1.0.1 h1:ww0JCgWpOVuqWG7k3724pJ18Lq8gh5pH
7474
github.com/carapace-sh/carapace-shlex v1.0.1/go.mod h1:lJ4ZsdxytE0wHJ8Ta9S7Qq0XpjgjU0mdfCqiI2FHx7M=
7575
github.com/cdevents/sdk-go v0.4.1 h1:Cr/iH/I51Z+slxKRx9AV7stn6hr2pjRHQ5wpPJhRLTU=
7676
github.com/cdevents/sdk-go v0.4.1/go.mod h1:3IhWLoY4vsyUEzv7XJbyr0BRQ0KPgvNx+wiD2hQGFNU=
77-
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
78-
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
77+
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
78+
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
7979
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
8080
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
8181
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -262,8 +262,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
262262
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
263263
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
264264
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
265-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
266-
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
265+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
266+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
267267
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
268268
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
269269
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
@@ -422,22 +422,20 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6h
422422
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
423423
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
424424
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
425-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
426-
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
427-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM=
428-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA=
429-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
430-
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
425+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
426+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
427+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
428+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
431429
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
432430
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
433-
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
434-
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
431+
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
432+
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
435433
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
436434
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
437435
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
438436
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
439-
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
440-
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
437+
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
438+
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
441439
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
442440
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
443441
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -532,8 +530,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
532530
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
533531
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
534532
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
535-
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
536-
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
533+
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
534+
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
537535
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
538536
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
539537
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=

internal/notifier/forwarder.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"net/url"
2727

2828
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
29+
"sigs.k8s.io/controller-runtime/pkg/log"
2930

3031
"github.com/hashicorp/go-retryablehttp"
3132
)
@@ -96,6 +97,18 @@ func (f *Forwarder) Post(ctx context.Context, event eventv1.Event) error {
9697
opts = append(opts, withTLSConfig(f.TLSConfig))
9798
}
9899

100+
// Wrap the sender with tracing
101+
OTELTraceNotifier, err := NewOTELTraceNotifier(f.URL, f.ProxyURL, f.Headers, f.TLSConfig)
102+
if err != nil {
103+
// Log warning but continue execution
104+
log.FromContext(ctx).Error(nil, "warning: failed to create OTEL trace notifier", err)
105+
} else if OTELTraceNotifier != nil {
106+
// Only attempt to post if notifier was created successfully
107+
if traceErr := OTELTraceNotifier.Post(ctx, event); traceErr != nil {
108+
log.FromContext(ctx).Error(nil, "warning: failed to send OTEL trace", traceErr)
109+
}
110+
}
111+
99112
if err := postMessage(ctx, f.URL, event, opts...); err != nil {
100113
return fmt.Errorf("postMessage failed: %w", err)
101114
}

internal/notifier/otel.go

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
package notifier
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"crypto/tls"
7+
"fmt"
8+
"net/http"
9+
"net/url"
10+
"strings"
11+
12+
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
13+
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
14+
"go.opentelemetry.io/otel/attribute"
15+
"go.opentelemetry.io/otel/codes"
16+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
17+
"go.opentelemetry.io/otel/sdk/resource"
18+
sdktrace "go.opentelemetry.io/otel/sdk/trace"
19+
semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
20+
"go.opentelemetry.io/otel/trace"
21+
"sigs.k8s.io/controller-runtime/pkg/log"
22+
)
23+
24+
type OTLPNotifier struct {
25+
URL string
26+
ProxyURL string
27+
Headers map[string]string
28+
TLSConfig *tls.Config
29+
}
30+
31+
func NewOTELTraceNotifier(url string, proxyURL string, headers map[string]string, tlsConfig *tls.Config) (*OTLPNotifier, error) {
32+
return &OTLPNotifier{
33+
URL: url,
34+
ProxyURL: proxyURL,
35+
Headers: headers,
36+
TLSConfig: tlsConfig,
37+
}, nil
38+
}
39+
40+
// Post implements the notifier.Interface
41+
func (t *OTLPNotifier) Post(ctx context.Context, event eventv1.Event) error {
42+
logger := log.FromContext(ctx).WithValues(
43+
"event", event.Reason,
44+
"object", fmt.Sprintf("%s/%s/%s", event.InvolvedObject.Kind, event.InvolvedObject.Namespace, event.InvolvedObject.Name),
45+
"severity", event.Severity,
46+
)
47+
48+
// Set up OTLP exporter options
49+
logger.V(1).Info("Configuring OTLP HTTP options", "url", t.URL)
50+
// Parse URL to extract host and port
51+
parsedURL, err := url.Parse(t.URL)
52+
if err != nil {
53+
logger.Error(err, "Failed to parse URL", "url", t.URL)
54+
return fmt.Errorf("failed to parse URL: %w", err)
55+
}
56+
httpOptions := []otlptracehttp.Option{
57+
otlptracehttp.WithEndpoint(parsedURL.Host),
58+
}
59+
60+
// Add headers if available
61+
if len(t.Headers) > 0 {
62+
logger.V(1).Info("Adding headers to OTLP exporter", "headerCount", len(t.Headers))
63+
httpOptions = append(httpOptions, otlptracehttp.WithHeaders(t.Headers))
64+
}
65+
66+
// Add TLS config if available
67+
if t.TLSConfig != nil {
68+
logger.V(1).Info("Configuring TLS for OTLP exporter")
69+
httpOptions = append(httpOptions, otlptracehttp.WithTLSClientConfig(t.TLSConfig))
70+
} else if parsedURL.Scheme == "http" {
71+
logger.V(1).Info("Using insecure connection for OTLP exporter")
72+
httpOptions = append(httpOptions, otlptracehttp.WithInsecure())
73+
}
74+
75+
// Add proxy if available
76+
if t.ProxyURL != "" {
77+
logger.V(1).Info("Setting up Proxy URL for OTLP exporter", "proxyURL", t.ProxyURL)
78+
proxyURL, err := url.Parse(t.ProxyURL)
79+
if err != nil {
80+
logger.Error(err, "Failed to parse proxy URL", "proxyURL", t.ProxyURL)
81+
} else {
82+
httpOptions = append(httpOptions, otlptracehttp.WithProxy(func(*http.Request) (*url.URL, error) {
83+
return proxyURL, nil
84+
}))
85+
}
86+
}
87+
88+
// Create exporter
89+
logger.V(1).Info("Creating OTLP exporter")
90+
exporter, err := otlptracehttp.New(ctx, httpOptions...)
91+
if err != nil {
92+
return fmt.Errorf("failed to create OTLP exporter: %w", err)
93+
}
94+
95+
// Extract revision from event metadata
96+
revision := ""
97+
for k, v := range event.Metadata {
98+
if strings.Contains(k, "revision") {
99+
revision = v
100+
logger.V(1).Info("Found revision in metadata", "revision", revision)
101+
break
102+
}
103+
}
104+
105+
// Get value from context (this would need to be passed in from event_handlers.go)
106+
alertUID, ok := ctx.Value("alertUID").(string)
107+
if !ok {
108+
alertUID = "unknown"
109+
logger.V(1).Info("alertUID not found in context, using default", "alertUID", alertUID)
110+
} else {
111+
logger.V(1).Info("Using alertUID from context", "alertUID", alertUID)
112+
}
113+
alertName, ok := ctx.Value("alertName").(string)
114+
if !ok {
115+
alertUID = "unknown"
116+
logger.V(1).Info("alertName not found in context, using default", "alertName", alertName)
117+
} else {
118+
logger.V(1).Info("Using alertName from context", "alertName", alertName)
119+
}
120+
alertNamespace, ok := ctx.Value("alertNamespace").(string)
121+
if !ok {
122+
alertNamespace = "unknown"
123+
logger.V(1).Info("alertNamespace not found in context, using default", "alertNamespace", alertNamespace)
124+
} else {
125+
logger.V(1).Info("Using alertNamespace from context", "alertNamespace", alertNamespace)
126+
}
127+
128+
// Create trace provider with resource attributes
129+
logger.V(1).Info("Creating trace provider")
130+
serviceName := fmt.Sprintf("%s:%s/%s", apiv1beta3.AlertKind, alertNamespace, alertName)
131+
resource := resource.NewWithAttributes(
132+
semconv.SchemaURL,
133+
semconv.ServiceInstanceID(alertUID),
134+
semconv.ServiceName(serviceName),
135+
semconv.ServiceNamespace(alertNamespace),
136+
)
137+
tp := sdktrace.NewTracerProvider(
138+
sdktrace.WithBatcher(exporter),
139+
sdktrace.WithResource(resource),
140+
)
141+
142+
// Use the trace provider's tracer for span creation
143+
tracer := tp.Tracer("flux:notification-controller")
144+
145+
// alertName, ok := ctx.Value("alert.Name").(string)
146+
// if !ok {
147+
// alertName = "unknown"
148+
// logger.V(1).Info("Alert UID not found in context, using default", "alertUID", alertUID)
149+
// } else {
150+
// logger.V(1).Info("Using alert UID from context", "alertUID", alertUID)
151+
// }
152+
153+
// alertNamespace, ok := ctx.Value("alert.Namespace").(string)
154+
// if !ok {
155+
// alertNamespace = "unknown"
156+
// logger.V(1).Info("Alert UID not found in context, using default", "alertUID", alertUID)
157+
// } else {
158+
// logger.V(1).Info("Using alert UID from context", "alertUID", alertUID)
159+
// }
160+
161+
// Generate root span ID
162+
logger.V(1).Info("Generating trace IDs", "alertUID", alertUID, "revision", revision)
163+
spanIDStr := generateID(string(event.InvolvedObject.UID), revision)
164+
traceIDStr := generateID(alertUID, revision)
165+
166+
var traceID trace.TraceID
167+
var spanID trace.SpanID
168+
copy(traceID[:], traceIDStr[:16])
169+
copy(spanID[:], spanIDStr[:8])
170+
171+
// Create trace context with the generated ID
172+
var spanCtx context.Context = ctx
173+
174+
// Replace trace context to use Alert UID + revision
175+
logger.Info("Trace context", "kind", event.InvolvedObject.Kind)
176+
// Create new context for root span
177+
currentSpanContext := trace.SpanContextFromContext(ctx)
178+
if !currentSpanContext.IsValid() || (currentSpanContext.HasTraceID() &&
179+
currentSpanContext.TraceID() == traceID) {
180+
spanCtx = trace.ContextWithSpanContext(ctx,
181+
trace.NewSpanContext(trace.SpanContextConfig{
182+
TraceID: traceID,
183+
// SpanID: spanID,
184+
TraceFlags: trace.FlagsSampled, // Ensure the trace is sampled
185+
}),
186+
)
187+
} else {
188+
logger.V(1).Info("The current Trace is valid and already exists")
189+
}
190+
191+
// Create single span with proper attributes
192+
spanName := fmt.Sprintf("%s:%s/%s", event.InvolvedObject.Kind, event.InvolvedObject.Namespace, event.InvolvedObject.Name)
193+
_, span := tracer.Start(spanCtx, spanName,
194+
trace.WithAttributes(
195+
attribute.String("flux.object.uid", string(event.InvolvedObject.UID)),
196+
attribute.String("flux.object.kind", event.InvolvedObject.Kind),
197+
attribute.String("flux.object.name", event.InvolvedObject.Name),
198+
attribute.String("flux.object.namespace", event.InvolvedObject.Namespace),
199+
attribute.String("flux.event.severity", event.Severity),
200+
attribute.String("flux.event.reason", event.Reason),
201+
attribute.String("flux.event.message", event.Message),
202+
),
203+
trace.WithTimestamp(event.Timestamp.Time),
204+
)
205+
206+
// Add metadata attributes
207+
for k, v := range event.Metadata {
208+
span.SetAttributes(attribute.String(fmt.Sprintf("flux.event.metadata.%s", k), v))
209+
}
210+
211+
// Set status based on event severity
212+
if event.Severity == eventv1.EventSeverityError {
213+
span.SetStatus(codes.Error, event.Message)
214+
} else {
215+
span.SetStatus(codes.Ok, event.Message)
216+
}
217+
218+
logger.Info("Successfully sent trace to OTLP endpoint",
219+
"url", t.URL,
220+
"object", fmt.Sprintf("%s/%s/%s", event.InvolvedObject.Kind, event.InvolvedObject.Namespace, event.InvolvedObject.Name),
221+
"reason", event.Reason)
222+
223+
defer func() {
224+
span.End()
225+
tp.ForceFlush(ctx)
226+
tp.Shutdown(ctx)
227+
exporter.Shutdown(ctx)
228+
}()
229+
230+
return nil
231+
}
232+
233+
// Add this function to generate trace and span ID
234+
func generateID(alertUID, sourceRevision string) []byte {
235+
input := fmt.Sprintf("%s:%s", alertUID, sourceRevision)
236+
hash := sha256.Sum256([]byte(input))
237+
return hash[:]
238+
}

0 commit comments

Comments
 (0)