Skip to content

Commit 5620c8e

Browse files
committed
feat: add telemetry support with prometheus metrics and opentelemetry tracing
1 parent 34dad0f commit 5620c8e

11 files changed

Lines changed: 324 additions & 6 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.23-alpine AS builder
1+
FROM golang:1.25-alpine AS builder
22

33
WORKDIR /app
44
COPY go.mod ./

cmd/sqlens/main.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@ import (
1212
"github.com/sqlens/sqlens/config"
1313
"github.com/sqlens/sqlens/proxy"
1414
"github.com/sqlens/sqlens/store"
15+
"github.com/sqlens/sqlens/telemetry"
1516
"github.com/sqlens/sqlens/web"
1617
)
1718

1819
func main() {
1920
cfg := config.LoadConfig()
2021

22+
// Initialize Telemetry
23+
tp, err := telemetry.InitTracer()
24+
if err != nil {
25+
slog.Error("Failed to initialize tracer", "err", err)
26+
os.Exit(1)
27+
}
28+
defer telemetry.Shutdown(context.Background(), tp)
29+
2130
// Initialize Storage
2231
memStore := store.NewMemoryStore()
2332

go.mod

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
module github.com/sqlens/sqlens
22

3-
go 1.23.4
3+
go 1.25.0
44

5-
require github.com/lib/pq v1.11.2 // indirect
5+
require (
6+
github.com/lib/pq v1.11.2
7+
github.com/prometheus/client_golang v1.23.2
8+
go.opentelemetry.io/otel v1.42.0
9+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0
10+
go.opentelemetry.io/otel/sdk v1.42.0
11+
go.opentelemetry.io/otel/trace v1.42.0
12+
)
13+
14+
require (
15+
github.com/beorn7/perks v1.0.1 // indirect
16+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
17+
github.com/go-logr/logr v1.4.3 // indirect
18+
github.com/go-logr/stdr v1.2.2 // indirect
19+
github.com/google/uuid v1.6.0 // indirect
20+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
21+
github.com/prometheus/client_model v0.6.2 // indirect
22+
github.com/prometheus/common v0.66.1 // indirect
23+
github.com/prometheus/procfs v0.16.1 // indirect
24+
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
25+
go.opentelemetry.io/otel/metric v1.42.0 // indirect
26+
go.yaml.in/yaml/v2 v2.4.2 // indirect
27+
golang.org/x/sys v0.41.0 // indirect
28+
google.golang.org/protobuf v1.36.8 // indirect
29+
)

go.sum

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,68 @@
1+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
4+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
8+
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
9+
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
10+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
11+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
12+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
13+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
14+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
15+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
16+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
17+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
18+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
19+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
20+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
21+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
22+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
23+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
124
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
225
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
26+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
27+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
28+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
29+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
30+
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
31+
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
32+
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
33+
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
34+
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
35+
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
36+
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
37+
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
38+
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
39+
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
40+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
41+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
42+
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
43+
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
44+
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
45+
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
46+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw=
47+
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
48+
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
49+
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
50+
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
51+
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
52+
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
53+
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
54+
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
55+
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
56+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
57+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
58+
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
59+
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
60+
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
61+
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
62+
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
63+
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
64+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
65+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
66+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
67+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
68+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

metrics/prometheus.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package metrics
2+
3+
import (
4+
"github.com/prometheus/client_golang/prometheus"
5+
"github.com/prometheus/client_golang/prometheus/promauto"
6+
)
7+
8+
var (
9+
QueriesTotal = promauto.NewCounterVec(prometheus.CounterOpts{
10+
Name: "sqlens_queries_total",
11+
Help: "Total number of SQL queries intercepted",
12+
}, []string{"n1_flag", "has_violations"})
13+
14+
QueryLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
15+
Name: "sqlens_query_latency_seconds",
16+
Help: "Latency of SQL queries in seconds",
17+
Buckets: prometheus.DefBuckets,
18+
}, []string{"fingerprint"})
19+
20+
N1IncidentsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
21+
Name: "sqlens_n1_incidents_total",
22+
Help: "Total number of N+1 incidents detected",
23+
}, []string{"fingerprint"})
24+
25+
ViolationsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
26+
Name: "sqlens_violations_total",
27+
Help: "Total number of SQL guardrail violations detected",
28+
}, []string{"violation_type"})
29+
)
30+
31+
func RecordEvent(n1 bool, violations []string, latency float64, fingerprint string) {
32+
hasViolations := "false"
33+
if len(violations) > 0 {
34+
hasViolations = "true"
35+
}
36+
37+
n1Flag := "false"
38+
if n1 {
39+
n1Flag = "true"
40+
N1IncidentsTotal.WithLabelValues(fingerprint).Inc()
41+
}
42+
43+
QueriesTotal.WithLabelValues(n1Flag, hasViolations).Inc()
44+
QueryLatency.WithLabelValues(fingerprint).Observe(latency)
45+
46+
for _, v := range violations {
47+
// Basic violation type extraction (e.g., from "SLOW: ...")
48+
vType := "unknown"
49+
if i := len(v); i > 0 {
50+
if parts := splitViolation(v); len(parts) > 0 {
51+
vType = parts[0]
52+
}
53+
}
54+
ViolationsTotal.WithLabelValues(vType).Inc()
55+
}
56+
}
57+
58+
func splitViolation(v string) []string {
59+
for i := 0; i < len(v); i++ {
60+
if v[i] == ':' {
61+
return []string{v[:i]}
62+
}
63+
}
64+
return nil
65+
}

metrics/prometheus_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package metrics
2+
3+
import (
4+
"testing"
5+
6+
"github.com/prometheus/client_golang/prometheus"
7+
dto "github.com/prometheus/client_model/go"
8+
)
9+
10+
func TestRecordEvent(t *testing.T) {
11+
// We can't easily reset promauto-registered metrics in a simple test,
12+
// but we can check if they increment.
13+
14+
initialQueries := getCounterValue(QueriesTotal.WithLabelValues("false", "false"))
15+
16+
RecordEvent(false, nil, 0.1, "test-fingerprint")
17+
18+
finalQueries := getCounterValue(QueriesTotal.WithLabelValues("false", "false"))
19+
20+
if finalQueries != initialQueries+1 {
21+
t.Errorf("Expected QueriesTotal to increment, got %f -> %f", initialQueries, finalQueries)
22+
}
23+
}
24+
25+
func getCounterValue(counter prometheus.Counter) float64 {
26+
var m dto.Metric
27+
counter.Write(&m)
28+
return m.GetCounter().GetValue()
29+
}

proxy/server.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88
"time"
99

1010
"github.com/sqlens/sqlens/analyzer"
11+
"github.com/sqlens/sqlens/metrics"
12+
"go.opentelemetry.io/otel"
13+
"go.opentelemetry.io/otel/attribute"
14+
"go.opentelemetry.io/otel/trace"
1115
)
1216

1317
type Server struct {
@@ -16,6 +20,7 @@ type Server struct {
1620
pipeline *analyzer.Pipeline
1721
store EventStore
1822
redactSensitive bool
23+
tracer trace.Tracer
1924
}
2025

2126
// EventStore represents an interface to save and retrieve query events
@@ -30,6 +35,7 @@ func NewServer(listen, target string, p *analyzer.Pipeline, s EventStore, redact
3035
pipeline: p,
3136
store: s,
3237
redactSensitive: redact,
38+
tracer: otel.Tracer("sqlens-proxy"),
3339
}
3440
}
3541

@@ -84,6 +90,8 @@ func (s *Server) handleConnection(ctx context.Context, clientConn net.Conn) {
8490

8591
var lastQueryStart time.Time
8692
var lastQueryStr string
93+
var lastQueryCtx context.Context
94+
var lastQuerySpan trace.Span
8795
var mu sync.Mutex
8896

8997
var wg sync.WaitGroup
@@ -107,13 +115,23 @@ func (s *Server) handleConnection(ctx context.Context, clientConn net.Conn) {
107115
// Sniff PostgreSQL 'Q' (Simple Query)
108116
if n > 5 && buf[0] == 'Q' {
109117
mu.Lock()
118+
if lastQuerySpan != nil {
119+
lastQuerySpan.End()
120+
}
110121
lastQueryStart = time.Now()
111122
// Basic safety: limit query string size to avoid huge allocations
112123
end := n - 1
113124
if end > 2048 {
114125
end = 2048
115126
}
116127
lastQueryStr = string(buf[5:end])
128+
129+
// Start OpenTelemetry span
130+
qCtx, span := s.tracer.Start(ctx, "sql_query",
131+
trace.WithAttributes(attribute.String("sql.query", lastQueryStr)))
132+
lastQueryCtx = qCtx
133+
lastQuerySpan = span
134+
117135
mu.Unlock()
118136
}
119137

@@ -143,10 +161,14 @@ func (s *Server) handleConnection(ctx context.Context, clientConn net.Conn) {
143161
if !lastQueryStart.IsZero() {
144162
latency := time.Since(lastQueryStart)
145163
query := lastQueryStr
164+
qCtx := lastQueryCtx
165+
qSpan := lastQuerySpan
146166
lastQueryStart = time.Time{}
167+
lastQueryCtx = nil
168+
lastQuerySpan = nil
147169
mu.Unlock()
148170

149-
go func(q string, l time.Duration) {
171+
go func(q string, l time.Duration, ctx context.Context, span trace.Span) {
150172
defer func() { recover() }() // Async processing safety
151173
event := analyzer.QueryEvent{
152174
ConnectionID: connID,
@@ -156,15 +178,28 @@ func (s *Server) handleConnection(ctx context.Context, clientConn net.Conn) {
156178
}
157179

158180
// Reassign event to the one that's been through the pipeline
159-
event = s.pipeline.Process(context.Background(), event)
181+
event = s.pipeline.Process(ctx, event)
182+
183+
// Record Prometheus metrics
184+
metrics.RecordEvent(event.N1Flag, event.Violations, l.Seconds(), event.Fingerprint)
185+
186+
// Update and End Span
187+
if span != nil {
188+
span.SetAttributes(
189+
attribute.String("sql.fingerprint", event.Fingerprint),
190+
attribute.Bool("sqlens.n1_flag", event.N1Flag),
191+
attribute.StringSlice("sqlens.violations", event.Violations),
192+
)
193+
span.End()
194+
}
160195

161196
// If redaction is enabled, mask the raw SQL
162197
if s.redactSensitive {
163198
event.RawQuery = event.Fingerprint
164199
}
165200

166201
s.store.Save(event)
167-
}(query, latency)
202+
}(query, latency, qCtx, qSpan)
168203
} else {
169204
mu.Unlock()
170205
}

telemetry/otel.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package telemetry
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"go.opentelemetry.io/otel"
9+
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
10+
"go.opentelemetry.io/otel/sdk/resource"
11+
"go.opentelemetry.io/otel/sdk/trace"
12+
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
13+
)
14+
15+
func InitTracer() (*trace.TracerProvider, error) {
16+
exporter, err := stdouttrace.New(
17+
stdouttrace.WithPrettyPrint(),
18+
)
19+
if err != nil {
20+
return nil, fmt.Errorf("creating stdout exporter: %w", err)
21+
}
22+
23+
res, err := resource.Merge(
24+
resource.Default(),
25+
resource.NewWithAttributes(
26+
resource.Default().SchemaURL(),
27+
semconv.ServiceNameKey.String("sqlens"),
28+
),
29+
)
30+
if err != nil {
31+
return nil, fmt.Errorf("creating resource: %w", err)
32+
}
33+
34+
tp := trace.NewTracerProvider(
35+
trace.WithBatcher(exporter),
36+
trace.WithResource(res),
37+
)
38+
otel.SetTracerProvider(tp)
39+
40+
return tp, nil
41+
}
42+
43+
func Shutdown(ctx context.Context, tp *trace.TracerProvider) {
44+
if err := tp.Shutdown(ctx); err != nil {
45+
fmt.Fprintf(os.Stderr, "Error shutting down tracer provider: %v\n", err)
46+
}
47+
}

telemetry/otel_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package telemetry
2+
3+
import (
4+
"context"
5+
"testing"
6+
)
7+
8+
func TestInitTracer(t *testing.T) {
9+
tp, err := InitTracer()
10+
if err != nil {
11+
t.Fatalf("Failed to initialize tracer: %v", err)
12+
}
13+
if tp == nil {
14+
t.Fatal("Expected TracerProvider to be non-nil")
15+
}
16+
defer Shutdown(context.Background(), tp)
17+
}

0 commit comments

Comments
 (0)