Skip to content

Commit e9631ed

Browse files
authored
refactor(core, api): update OpenTelemetry tracing implementation (#147)
* refactor(api): update OpenTelemetry dependencies and improve HTTP server configuration * refactor(core): enhance OpenTelemetry integration and update dependencies
1 parent 8133791 commit e9631ed

19 files changed

Lines changed: 420 additions & 1059 deletions

File tree

src/server/api/go/cmd/server/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,13 @@ func main() {
109109
})
110110

111111
addr := fmt.Sprintf("%s:%d", cfg.App.Host, cfg.App.Port)
112-
srv := &http.Server{Addr: addr, Handler: engine}
112+
srv := &http.Server{
113+
Addr: addr,
114+
Handler: engine,
115+
ReadHeaderTimeout: 30 * time.Second,
116+
WriteTimeout: 15 * time.Minute,
117+
IdleTimeout: 120 * time.Second,
118+
}
113119

114120
go func() {
115121
log.Sugar().Infow("starting http server", "addr", addr)

src/server/api/go/go.mod

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ require (
2727
github.com/tiktoken-go/tokenizer v0.7.0
2828
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.63.0
2929
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.63.0
30-
go.opentelemetry.io/otel v1.38.0
30+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0
31+
go.opentelemetry.io/otel v1.39.0
3132
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
32-
go.opentelemetry.io/otel/sdk v1.38.0
33-
go.opentelemetry.io/otel/trace v1.38.0
33+
go.opentelemetry.io/otel/sdk v1.39.0
34+
go.opentelemetry.io/otel/trace v1.39.0
3435
go.uber.org/zap v1.27.1
3536
golang.org/x/crypto v0.45.0
3637
golang.org/x/sync v0.18.0
@@ -78,6 +79,7 @@ require (
7879
github.com/davecgh/go-spew v1.1.1 // indirect
7980
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
8081
github.com/dlclark/regexp2 v1.11.5 // indirect
82+
github.com/felixge/httpsnoop v1.0.4 // indirect
8183
github.com/fsnotify/fsnotify v1.9.0 // indirect
8284
github.com/gin-contrib/sse v1.1.0 // indirect
8385
github.com/go-faster/city v1.0.1 // indirect
@@ -144,15 +146,15 @@ require (
144146
go.opencensus.io v0.24.0 // indirect
145147
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
146148
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
147-
go.opentelemetry.io/otel/metric v1.38.0 // indirect
149+
go.opentelemetry.io/otel/metric v1.39.0 // indirect
148150
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
149151
go.uber.org/mock v0.6.0 // indirect
150152
go.uber.org/multierr v1.11.0 // indirect
151153
go.yaml.in/yaml/v3 v3.0.4 // indirect
152154
golang.org/x/arch v0.23.0 // indirect
153155
golang.org/x/mod v0.30.0 // indirect
154156
golang.org/x/net v0.47.0 // indirect
155-
golang.org/x/sys v0.38.0 // indirect
157+
golang.org/x/sys v0.39.0 // indirect
156158
golang.org/x/text v0.31.0 // indirect
157159
golang.org/x/tools v0.39.0 // indirect
158160
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect

src/server/api/go/go.sum

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
9898
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
9999
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
100100
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
101+
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
102+
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
101103
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
102104
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
103105
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
@@ -353,24 +355,26 @@ go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws
353355
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.63.0/go.mod h1:wIvTiRUU7Pbfqas/5JVjGZcftBeSAGSYVMOHWzWG0qE=
354356
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.63.0 h1:5kSIJ0y8ckZZKoDhZHdVtcyjVi6rXyAwyaR8mp4zLbg=
355357
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.63.0/go.mod h1:i+fIMHvcSQtsIY82/xgiVWRklrNt/O6QriHLjzGeY+s=
358+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
359+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=
356360
go.opentelemetry.io/contrib/propagators/b3 v1.38.0 h1:uHsCCOSKl0kLrV2dLkFK+8Ywk9iKa/fptkytc6aFFEo=
357361
go.opentelemetry.io/contrib/propagators/b3 v1.38.0/go.mod h1:wMRSZJZcY8ya9mApLLhwIMjqmApy2o/Ml+62lhvxyHU=
358-
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
359-
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
362+
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
363+
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
360364
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
361365
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
362366
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
363367
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
364368
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
365369
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
366-
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
367-
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
368-
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
369-
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
370-
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
371-
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
372-
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
373-
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
370+
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
371+
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
372+
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
373+
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
374+
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
375+
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
376+
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
377+
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
374378
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
375379
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
376380
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -437,8 +441,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
437441
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
438442
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
439443
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
440-
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
441-
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
444+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
445+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
442446
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
443447
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
444448
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

src/server/api/go/internal/infra/httpclient/core.go

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import (
1212
"github.com/bytedance/sonic"
1313
"github.com/google/uuid"
1414
"github.com/memodb-io/Acontext/internal/config"
15-
"go.opentelemetry.io/otel"
16-
"go.opentelemetry.io/otel/propagation"
15+
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
1716
"go.uber.org/zap"
1817
)
1918

@@ -22,18 +21,17 @@ type CoreClient struct {
2221
BaseURL string
2322
HTTPClient *http.Client
2423
Logger *zap.Logger
25-
Propagator propagation.TextMapPropagator
2624
}
2725

28-
// NewCoreClient creates a new CoreClient
26+
// NewCoreClient creates a new CoreClient with OpenTelemetry instrumentation
2927
func NewCoreClient(cfg *config.Config, log *zap.Logger) *CoreClient {
3028
return &CoreClient{
3129
BaseURL: cfg.Core.BaseURL,
3230
HTTPClient: &http.Client{
33-
Timeout: 30 * time.Second,
31+
Timeout: 5 * time.Minute,
32+
Transport: otelhttp.NewTransport(http.DefaultTransport),
3433
},
35-
Logger: log,
36-
Propagator: otel.GetTextMapPropagator(), // Get global propagator
34+
Logger: log,
3735
}
3836
}
3937

@@ -81,9 +79,6 @@ func (c *CoreClient) ExperienceSearch(ctx context.Context, projectID, spaceID uu
8179
return nil, fmt.Errorf("create request: %w", err)
8280
}
8381

84-
// Important: propagate trace context to downstream service
85-
c.Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
86-
8782
resp, err := c.HTTPClient.Do(httpReq)
8883
if err != nil {
8984
return nil, fmt.Errorf("do request: %w", err)
@@ -139,9 +134,6 @@ func (c *CoreClient) InsertBlock(ctx context.Context, projectID, spaceID uuid.UU
139134
}
140135
httpReq.Header.Set("Content-Type", "application/json")
141136

142-
// Important: propagate trace context to downstream service
143-
c.Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
144-
145137
resp, err := c.HTTPClient.Do(httpReq)
146138
if err != nil {
147139
return nil, fmt.Errorf("do request: %w", err)
@@ -189,9 +181,6 @@ func (c *CoreClient) SessionFlush(ctx context.Context, projectID, sessionID uuid
189181
return nil, fmt.Errorf("create request: %w", err)
190182
}
191183

192-
// Important: propagate trace context to downstream service
193-
c.Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
194-
195184
resp, err := c.HTTPClient.Do(httpReq)
196185
if err != nil {
197186
return nil, fmt.Errorf("do request: %w", err)
@@ -227,9 +216,6 @@ func (c *CoreClient) GetLearningStatus(ctx context.Context, projectID, sessionID
227216
return nil, fmt.Errorf("create request: %w", err)
228217
}
229218

230-
// Important: propagate trace context to downstream service
231-
c.Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
232-
233219
resp, err := c.HTTPClient.Do(httpReq)
234220
if err != nil {
235221
return nil, fmt.Errorf("do request: %w", err)
@@ -290,9 +276,6 @@ func (c *CoreClient) ToolRename(ctx context.Context, projectID uuid.UUID, rename
290276
}
291277
httpReq.Header.Set("Content-Type", "application/json")
292278

293-
// Important: propagate trace context to downstream service
294-
c.Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
295-
296279
resp, err := c.HTTPClient.Do(httpReq)
297280
if err != nil {
298281
return nil, fmt.Errorf("do request: %w", err)
@@ -328,9 +311,6 @@ func (c *CoreClient) GetToolNames(ctx context.Context, projectID uuid.UUID) ([]T
328311
return nil, fmt.Errorf("create request: %w", err)
329312
}
330313

331-
// Important: propagate trace context to downstream service
332-
c.Propagator.Inject(ctx, propagation.HeaderCarrier(httpReq.Header))
333-
334314
resp, err := c.HTTPClient.Do(httpReq)
335315
if err != nil {
336316
return nil, fmt.Errorf("do request: %w", err)

src/server/api/go/internal/infra/queue/rabbitmq.go

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,33 @@ import (
1111
"github.com/memodb-io/Acontext/internal/config"
1212
amqp "github.com/rabbitmq/amqp091-go"
1313
"go.opentelemetry.io/otel"
14-
"go.opentelemetry.io/otel/attribute"
14+
"go.opentelemetry.io/otel/propagation"
15+
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
1516
"go.opentelemetry.io/otel/trace"
1617
"go.uber.org/zap"
1718
)
1819

19-
// tableCarrier adapts amqp.Table to TextMapCarrier for OpenTelemetry propagation
20-
type tableCarrier struct {
21-
table amqp.Table
22-
}
23-
24-
func (c tableCarrier) Get(key string) string {
25-
if val, ok := c.table[key]; ok {
26-
if str, ok := val.(string); ok {
27-
return str
28-
}
29-
return fmt.Sprintf("%v", val)
20+
// injectTraceContext injects trace context into AMQP headers
21+
func injectTraceContext(ctx context.Context, headers amqp.Table) {
22+
carrier := propagation.MapCarrier{}
23+
otel.GetTextMapPropagator().Inject(ctx, carrier)
24+
for k, v := range carrier {
25+
headers[k] = v
3026
}
31-
return ""
32-
}
33-
34-
func (c tableCarrier) Set(key, value string) {
35-
c.table[key] = value
3627
}
3728

38-
func (c tableCarrier) Keys() []string {
39-
keys := make([]string, 0, len(c.table))
40-
for k := range c.table {
41-
keys = append(keys, k)
29+
// extractTraceContext extracts trace context from AMQP headers
30+
func extractTraceContext(ctx context.Context, headers amqp.Table) context.Context {
31+
if headers == nil {
32+
return ctx
4233
}
43-
return keys
34+
carrier := propagation.MapCarrier{}
35+
for k, v := range headers {
36+
if str, ok := v.(string); ok {
37+
carrier[k] = str
38+
}
39+
}
40+
return otel.GetTextMapPropagator().Extract(ctx, carrier)
4441
}
4542

4643
// DialFunc is a function type for establishing RabbitMQ connections
@@ -207,21 +204,22 @@ func (p *Publisher) PublishJSON(ctx context.Context, exchangeName string, routin
207204
return err
208205
}
209206

210-
// Create a span for the publish operation
207+
// Create producer span using semantic conventions
211208
tracer := otel.Tracer(p.cfg.App.Name)
212-
ctx, span := tracer.Start(ctx, "rabbitmq.publish",
209+
ctx, span := tracer.Start(ctx, fmt.Sprintf("%s publish", exchangeName),
210+
trace.WithSpanKind(trace.SpanKindProducer),
213211
trace.WithAttributes(
214-
attribute.String("messaging.system", "rabbitmq"),
215-
attribute.String("messaging.destination", exchangeName),
216-
attribute.String("messaging.destination_kind", "exchange"),
217-
attribute.String("messaging.rabbitmq.routing_key", routingKey),
212+
semconv.MessagingSystemRabbitmq,
213+
semconv.MessagingDestinationName(exchangeName),
214+
semconv.MessagingRabbitmqDestinationRoutingKey(routingKey),
215+
semconv.MessagingOperationPublish,
216+
semconv.MessagingMessageBodySize(len(b)),
218217
))
219218
defer span.End()
220219

221220
// Inject trace context into message headers
222221
headers := make(amqp.Table)
223-
propagator := otel.GetTextMapPropagator()
224-
propagator.Inject(ctx, tableCarrier{table: headers})
222+
injectTraceContext(ctx, headers)
225223

226224
publishing := amqp.Publishing{
227225
ContentType: "application/json",
@@ -238,13 +236,11 @@ func (p *Publisher) PublishJSON(ctx context.Context, exchangeName string, routin
238236
return fmt.Errorf("failed to get channel: %w", err)
239237
}
240238

241-
err = ch.PublishWithContext(ctx, exchangeName, routingKey, false, false, publishing)
242-
if err != nil {
239+
if err := ch.PublishWithContext(ctx, exchangeName, routingKey, false, false, publishing); err != nil {
243240
span.RecordError(err)
244241
return err
245242
}
246243

247-
span.SetAttributes(attribute.Int("messaging.message.body.size", len(b)))
248244
return nil
249245
}
250246

@@ -276,7 +272,6 @@ func (c *Consumer) Handle(ctx context.Context, handler func([]byte) error) error
276272
}
277273

278274
tracer := otel.Tracer(c.cfg.App.Name)
279-
propagator := otel.GetTextMapPropagator()
280275

281276
for {
282277
select {
@@ -287,34 +282,39 @@ func (c *Consumer) Handle(ctx context.Context, handler func([]byte) error) error
287282
return errors.New("consumer channel closed")
288283
}
289284

290-
// Extract trace context from message headers
291-
msgCtx := ctx
292-
if m.Headers != nil {
293-
msgCtx = propagator.Extract(ctx, tableCarrier{table: m.Headers})
294-
}
285+
c.handleMessage(ctx, m, tracer, handler)
286+
}
287+
}
288+
}
295289

296-
// Create a span for the consume operation
297-
// Note: We don't use the returned context since handler doesn't accept context
298-
_, span := tracer.Start(msgCtx, "rabbitmq.consume",
299-
trace.WithAttributes(
300-
attribute.String("messaging.system", "rabbitmq"),
301-
attribute.String("messaging.destination", c.q.Name),
302-
attribute.String("messaging.destination_kind", "queue"),
303-
attribute.String("messaging.operation", "receive"),
304-
attribute.Int("messaging.message.body.size", len(m.Body)),
305-
))
306-
defer span.End()
307-
308-
// Execute handler with trace context
309-
// Note: handler receives []byte, not context, so trace context is propagated via span
310-
if err := handler(m.Body); err != nil {
311-
span.RecordError(err)
312-
_ = m.Nack(false, true) // Processing failed, requeue.
313-
c.log.Sugar().Errorw("consume error", "err", err)
314-
continue
315-
}
290+
// handleMessage processes a single message with tracing
291+
func (c *Consumer) handleMessage(
292+
ctx context.Context,
293+
m amqp.Delivery,
294+
tracer trace.Tracer,
295+
handler func([]byte) error,
296+
) {
297+
// Extract trace context from message headers
298+
msgCtx := extractTraceContext(ctx, m.Headers)
299+
300+
// Create consumer span using semantic conventions
301+
_, span := tracer.Start(msgCtx, fmt.Sprintf("%s receive", c.q.Name),
302+
trace.WithSpanKind(trace.SpanKindConsumer),
303+
trace.WithAttributes(
304+
semconv.MessagingSystemRabbitmq,
305+
semconv.MessagingDestinationName(c.q.Name),
306+
semconv.MessagingOperationReceive,
307+
semconv.MessagingMessageBodySize(len(m.Body)),
308+
))
309+
defer span.End()
316310

317-
_ = m.Ack(false)
318-
}
311+
// Execute handler
312+
if err := handler(m.Body); err != nil {
313+
span.RecordError(err)
314+
_ = m.Nack(false, true) // Processing failed, requeue.
315+
c.log.Sugar().Errorw("consume error", "err", err)
316+
return
319317
}
318+
319+
_ = m.Ack(false)
320320
}

0 commit comments

Comments
 (0)