From d71b375ae7e1b560da8acd534bf6fe15f9c9aa4d Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Thu, 1 Oct 2026 11:35:00 +0200 Subject: [PATCH 1/9] feat: streamline otel conventions --- examples/telemetry/main.go | 32 ++++---- integration/nats/connect.go | 13 +-- integration/nats/jetstream.go | 7 +- integration/temporal/client.go | 5 +- integration/temporal/doc.go | 5 +- integration/temporal/metrics.go | 115 ++------------------------- keeltest/servicehttp.go | 2 +- log/fields_messaging.go | 2 +- log/fields_net.go | 2 +- log/logger.go | 2 +- log/with.go | 2 +- net/http/client.go | 4 + net/http/middleware/logger.go | 2 +- net/http/middleware/referrer.go | 2 +- net/http/middleware/requestid.go | 2 +- net/http/middleware/serverheader.go | 2 +- net/http/middleware/sessionid.go | 2 +- net/http/middleware/trackingid.go | 2 +- net/http/roundtripware/logger.go | 2 +- net/http/roundtripware/metric.go | 2 +- net/http/roundtripware/referer.go | 2 +- net/http/roundtripware/requestid.go | 2 +- net/http/roundtripware/retry.go | 6 +- net/http/roundtripware/sessionid.go | 2 +- net/http/roundtripware/trackingid.go | 2 +- net/stream/jetstream/stream.go | 2 +- service/http.go | 2 +- service/httphealthz.go | 2 +- telemetry/code.go | 2 +- telemetry/context.go | 8 +- telemetry/logger.go | 2 +- telemetry/meter.go | 6 +- telemetry/profiler.go | 2 +- telemetry/resource.go | 2 +- telemetry/span.go | 19 +++-- telemetry/span_test.go | 3 + telemetry/tracer.go | 6 +- utils/net/http/errors.go | 2 +- 38 files changed, 103 insertions(+), 176 deletions(-) diff --git a/examples/telemetry/main.go b/examples/telemetry/main.go index 7ad7541c..44cabd6f 100644 --- a/examples/telemetry/main.go +++ b/examples/telemetry/main.go @@ -61,8 +61,9 @@ func main() { { // counter counter, err := meter.Int64Counter( - "a.counter", - metric.WithDescription("Count things"), + "demo.count", + metric.WithDescription("Number of calls to the count endpoint."), + metric.WithUnit("{call}"), ) log.Must(l, err, "failed to create counter meter") @@ -83,8 +84,9 @@ func main() { { // up down upDown, err := meter.Int64UpDownCounter( - "a.updown", - metric.WithDescription("Up down values"), + "demo.level", + metric.WithDescription("Current level raised by the up and lowered by the down endpoint."), + metric.WithUnit("{level}"), ) log.Must(l, err, "failed to create up down meter") @@ -101,26 +103,26 @@ func main() { } { // histogram - histogram, err := meter.Int64Histogram( - "a.histogram", - metric.WithDescription("Up down values"), - metric.WithUnit("ms"), + histogram, err := meter.Float64Histogram( + "demo.request.duration", + metric.WithDescription("Duration of histogram endpoint requests."), + metric.WithUnit("s"), ) - log.Must(l, err, "failed to create up down meter") + log.Must(l, err, "failed to create histogram meter") svs.HandleFunc("/histogram", func(w http.ResponseWriter, r *http.Request) { start := time.Now() time.Sleep(time.Second) - traceID := trace.SpanContextFromContext(r.Context()) - histogram.Record(r.Context(), int64(rand.Int()), - metric.WithAttributes( - attribute.String("key", "value"), - attribute.String("traceID", traceID.TraceID().String()), - ), + time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) + + histogram.Record(r.Context(), time.Since(start).Seconds(), + metric.WithAttributes(attribute.String("key", "value")), ) + traceID := trace.SpanContextFromContext(r.Context()) + metricRequestLatency.(prometheus.ExemplarObserver).ObserveWithExemplar( time.Since(start).Seconds(), prometheus.Labels{"traceID": traceID.TraceID().String()}, ) diff --git a/integration/nats/connect.go b/integration/nats/connect.go index 5b3dc052..44ea532f 100644 --- a/integration/nats/connect.go +++ b/integration/nats/connect.go @@ -11,8 +11,9 @@ import ( "github.com/foomo/keel/log" "github.com/foomo/opentelemetry-go/semconv/natsconv" "github.com/nats-io/nats.go" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" ) @@ -28,21 +29,21 @@ func Connect(s keel.Runtime, rawURL string, opts ...nats.Option) (*nats.Conn, er l := s.Logger().Named("nats") m := s.Meter() - // Build instruments once; each returns a noop on nil meter, so errors here - // only surface real meter-provider problems and should not block Connect. + // Build instruments once; each falls back to a noop on error, so errors are + // only reported and never block Connect. disconnects, err := natsconv.NewClientDisconnects(m) if err != nil { - return nil, err + otel.Handle(err) } reconnects, err := natsconv.NewClientReconnects(m) if err != nil { - return nil, err + otel.Handle(err) } asyncErrors, err := natsconv.NewClientAsyncErrors(m) if err != nil { - return nil, err + otel.Handle(err) } // Background context for callbacks — NATS invokes these outside any request diff --git a/integration/nats/jetstream.go b/integration/nats/jetstream.go index 2e92a0c6..586d2fff 100644 --- a/integration/nats/jetstream.go +++ b/integration/nats/jetstream.go @@ -8,6 +8,7 @@ import ( "github.com/foomo/opentelemetry-go/semconv/natsconv" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + "go.opentelemetry.io/otel" "go.uber.org/zap" ) @@ -24,7 +25,7 @@ func NewJetStream(s keel.Runtime, nc *nats.Conn, opts ...jetstream.JetStreamOpt) asyncErrors, err := natsconv.NewClientAsyncErrors(m) if err != nil { - return nil, err + otel.Handle(err) } ctx := context.Background() @@ -35,9 +36,7 @@ func NewJetStream(s keel.Runtime, nc *nats.Conn, opts ...jetstream.JetStreamOpt) jetstream.WithPublishAsyncMaxPending(256), jetstream.WithPublishAsyncTimeout(10 * time.Second), jetstream.WithPublishAsyncErrHandler(func(_ jetstream.JetStream, msg *nats.Msg, err error) { - asyncErrors.Add(ctx, 1, natsconv.AsyncErrorKindOther, - asyncErrors.AttrSubject(msg.Subject), - ) + asyncErrors.Add(ctx, 1, natsconv.AsyncErrorKindOther) l.Error("publish async error", zap.Error(err), zap.String("subject", msg.Subject), diff --git a/integration/temporal/client.go b/integration/temporal/client.go index bb100a4f..df29a7c8 100644 --- a/integration/temporal/client.go +++ b/integration/temporal/client.go @@ -173,7 +173,10 @@ func NewClient(ctx context.Context, endpoint string, opts ...ClientOption) (clie } clientOpts.Interceptors = append(clientOpts.Interceptors, tracingInterceptor) - clientOpts.MetricsHandler = NewMetricsHandler(telemetry.Meter()) + clientOpts.MetricsHandler = opentelemetry.NewMetricsHandler(opentelemetry.MetricsHandlerOptions{ + Meter: telemetry.Meter(), + OnError: otel.Handle, + }) } return client.Dial(clientOpts) diff --git a/integration/temporal/doc.go b/integration/temporal/doc.go index c5b8aa38..4b639aa4 100644 --- a/integration/temporal/doc.go +++ b/integration/temporal/doc.go @@ -3,9 +3,8 @@ // // It covers client construction with optional namespace registration and // OpenTelemetry instrumentation ([NewClient]), a keel service wrapper for -// Temporal workers ([NewService]), adapters bridging Temporal's logger and -// metrics interfaces to zap and OpenTelemetry ([NewLogger], -// [NewMetricsHandler]), activity option helpers ([WithActivityOptions]) and +// Temporal workers ([NewService]), an adapter bridging Temporal's logger to +// zap ([NewLogger]), activity option helpers ([WithActivityOptions]) and // error classification helpers ([NewActivityError], [IsActivityError], // [AsApplicationError]). // diff --git a/integration/temporal/metrics.go b/integration/temporal/metrics.go index 702e9dea..9391be80 100644 --- a/integration/temporal/metrics.go +++ b/integration/temporal/metrics.go @@ -1,119 +1,20 @@ package keeltemporal import ( - "context" - "time" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.temporal.io/sdk/client" + "go.temporal.io/sdk/contrib/opentelemetry" ) -// metricsHandler implements [client.MetricsHandler] on top of an -// OpenTelemetry meter. -type metricsHandler struct { - meter metric.Meter - attr []attribute.KeyValue -} - // NewMetricsHandler returns a Temporal metrics handler that records counters, -// gauges and timers with meter. Instrument creation errors are reported via -// [otel.Handle]. +// gauges and timers (in seconds) with meter. Instrument creation errors are +// reported via [otel.Handle]. +// +// Deprecated: Use [opentelemetry.NewMetricsHandler] instead. func NewMetricsHandler(meter metric.Meter) client.MetricsHandler { - return metricsHandler{meter: meter} -} - -// WithTags returns a handler whose instruments record tags as string -// attributes. Tags replace any previously set tags. -func (m metricsHandler) WithTags(tags map[string]string) client.MetricsHandler { - attr := make([]attribute.KeyValue, 0, len(tags)) - for k, v := range tags { - attr = append(attr, attribute.String(k, v)) - } - - return metricsHandler{meter: m.meter, attr: attr} -} - -// counter adapts an Int64Counter to [client.MetricsCounter]. -type counter struct { - inst metric.Int64Counter - attr []attribute.KeyValue -} - -// Inc adds v to the counter. -func (c *counter) Inc(v int64) { - c.inst.Add(context.Background(), v, metric.WithAttributes(c.attr...)) -} - -// Counter returns an Int64Counter instrument with the given name. -func (m metricsHandler) Counter(name string) client.MetricsCounter { - c, err := m.meter.Int64Counter(name) - if err != nil { - otel.Handle(err) - } - - return &counter{ - attr: m.attr, - inst: c, - } -} - -// gauge adapts a Float64ObservableGauge to [client.MetricsGauge]; the last -// updated value is reported on each collection. -type gauge struct { - inst metric.Float64ObservableGauge - value float64 -} - -// Update sets the value reported by the gauge. -func (c *gauge) Update(v float64) { - c.value = v -} - -// Gauge returns a Float64ObservableGauge instrument with the given name. -func (m metricsHandler) Gauge(name string) client.MetricsGauge { - c, err := m.meter.Float64ObservableGauge(name) - if err != nil { - otel.Handle(err) - } - - inst := &gauge{ - inst: c, - } - - _, err = m.meter.RegisterCallback(func(ctx context.Context, o metric.Observer) error { - o.ObserveFloat64(c, inst.value, metric.WithAttributes(m.attr...)) - return nil + return opentelemetry.NewMetricsHandler(opentelemetry.MetricsHandlerOptions{ + Meter: meter, + OnError: otel.Handle, }) - if err != nil { - otel.Handle(err) - } - - return inst -} - -// timer adapts an Int64Histogram to [client.MetricsTimer]. -type timer struct { - inst metric.Int64Histogram - attr []attribute.KeyValue -} - -// Record records v in milliseconds. -func (c *timer) Record(v time.Duration) { - c.inst.Record(context.TODO(), v.Milliseconds(), metric.WithAttributes(c.attr...)) -} - -// Timer returns an Int64Histogram instrument with the given name that records -// durations in milliseconds. -func (m metricsHandler) Timer(name string) client.MetricsTimer { - c, err := m.meter.Int64Histogram(name) - if err != nil { - otel.Handle(err) - } - - return &timer{ - inst: c, - attr: m.attr, - } } diff --git a/keeltest/servicehttp.go b/keeltest/servicehttp.go index da9b0db1..cfe94000 100644 --- a/keeltest/servicehttp.go +++ b/keeltest/servicehttp.go @@ -8,7 +8,7 @@ import ( "strings" keelhttp "github.com/foomo/keel/net/http" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "github.com/foomo/keel/log" diff --git a/log/fields_messaging.go b/log/fields_messaging.go index 59049267..52963506 100644 --- a/log/fields_messaging.go +++ b/log/fields_messaging.go @@ -1,7 +1,7 @@ package log import ( - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" ) diff --git a/log/fields_net.go b/log/fields_net.go index 8651b211..e41fca88 100644 --- a/log/fields_net.go +++ b/log/fields_net.go @@ -1,7 +1,7 @@ package log import ( - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" ) diff --git a/log/logger.go b/log/logger.go index 51dc0ac1..159f7883 100644 --- a/log/logger.go +++ b/log/logger.go @@ -1,7 +1,7 @@ package log import ( - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/log/with.go b/log/with.go index e4315588..85c29871 100644 --- a/log/with.go +++ b/log/with.go @@ -9,7 +9,7 @@ import ( foomosemconv "github.com/foomo/opentelemetry-go/semconv" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/client.go b/net/http/client.go index f2630886..5f26cd6f 100644 --- a/net/http/client.go +++ b/net/http/client.go @@ -274,6 +274,10 @@ func HTTPClientWithRoundTripware(l *zap.Logger, roundTripware ...roundtripware.R // // If a retrying round tripper is also in play, put telemetry outermost so it // reports one span per logical call rather than one per attempt. +// +// Spans are named "HTTP GET" by default. Pass [otelhttp.WithSpanNameFormatter] +// returning "{method} {url.template}" (e.g. "GET /prices/{sku}"); never use +// the URL path, as it makes span names unbounded. func HTTPClientWithTelemetry(opts ...otelhttp.Option) HTTPClientOption { return func(v *http.Client) { v.Transport = otelhttp.NewTransport(v.Transport, opts...) diff --git a/net/http/middleware/logger.go b/net/http/middleware/logger.go index 67116bc6..3988511f 100644 --- a/net/http/middleware/logger.go +++ b/net/http/middleware/logger.go @@ -6,7 +6,7 @@ import ( keelhttp "github.com/foomo/keel/net/http" httplog "github.com/foomo/keel/net/http/log" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/middleware/referrer.go b/net/http/middleware/referrer.go index 5ad673f8..58539c64 100644 --- a/net/http/middleware/referrer.go +++ b/net/http/middleware/referrer.go @@ -6,7 +6,7 @@ import ( keelhttp "github.com/foomo/keel/net/http" "github.com/foomo/keel/net/http/context" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) diff --git a/net/http/middleware/requestid.go b/net/http/middleware/requestid.go index 367ffab8..94c21847 100644 --- a/net/http/middleware/requestid.go +++ b/net/http/middleware/requestid.go @@ -5,7 +5,7 @@ import ( "strings" keelhttp "github.com/foomo/keel/net/http" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/middleware/serverheader.go b/net/http/middleware/serverheader.go index be9e49d0..16da31be 100644 --- a/net/http/middleware/serverheader.go +++ b/net/http/middleware/serverheader.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/middleware/sessionid.go b/net/http/middleware/sessionid.go index 0529bcc6..7b81c2d7 100644 --- a/net/http/middleware/sessionid.go +++ b/net/http/middleware/sessionid.go @@ -6,7 +6,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/middleware/trackingid.go b/net/http/middleware/trackingid.go index 581b6f27..ba3287ec 100644 --- a/net/http/middleware/trackingid.go +++ b/net/http/middleware/trackingid.go @@ -7,7 +7,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/roundtripware/logger.go b/net/http/roundtripware/logger.go index fc469c46..061fcd94 100644 --- a/net/http/roundtripware/logger.go +++ b/net/http/roundtripware/logger.go @@ -4,7 +4,7 @@ import ( "net/http" "time" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/roundtripware/metric.go b/net/http/roundtripware/metric.go index dcad4406..1743c516 100644 --- a/net/http/roundtripware/metric.go +++ b/net/http/roundtripware/metric.go @@ -44,7 +44,7 @@ func Metric(meter metric.Meter, name, description string) RoundTripware { attributes := append(labeler.Get(), attribute.String("method", r.Method)) if resp != nil { - attributes = append(labeler.Get(), attribute.Int("status_code", resp.StatusCode)) + attributes = append(attributes, attribute.Int("status_code", resp.StatusCode)) } histogram.Record(ctx, duration.Seconds(), metric.WithAttributes(attributes...)) diff --git a/net/http/roundtripware/referer.go b/net/http/roundtripware/referer.go index 922d699a..68d64e59 100644 --- a/net/http/roundtripware/referer.go +++ b/net/http/roundtripware/referer.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/roundtripware/requestid.go b/net/http/roundtripware/requestid.go index 51a7a5cf..ae1e8ab1 100644 --- a/net/http/roundtripware/requestid.go +++ b/net/http/roundtripware/requestid.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/roundtripware/retry.go b/net/http/roundtripware/retry.go index 748ab4ef..9947f732 100644 --- a/net/http/roundtripware/retry.go +++ b/net/http/roundtripware/retry.go @@ -7,7 +7,7 @@ import ( "time" "github.com/avast/retry-go/v4" - semconv "go.opentelemetry.io/otel/semconv/v1.10.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) @@ -124,8 +124,8 @@ func Retry(opts ...RetryOption) RoundTripware { var err error - if span.IsRecording() { - span.SetAttributes(semconv.HTTPRetryCountKey.Int(attempt)) + if attempt > 1 && span.IsRecording() { + span.SetAttributes(semconv.HTTPRequestResendCount(attempt - 1)) } resp, err = next(req) //nolint:bodyclose diff --git a/net/http/roundtripware/sessionid.go b/net/http/roundtripware/sessionid.go index e52e1a48..21ab9e5f 100644 --- a/net/http/roundtripware/sessionid.go +++ b/net/http/roundtripware/sessionid.go @@ -3,7 +3,7 @@ package roundtripware import ( "net/http" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/http/roundtripware/trackingid.go b/net/http/roundtripware/trackingid.go index 9f053dc4..fbf68b5c 100644 --- a/net/http/roundtripware/trackingid.go +++ b/net/http/roundtripware/trackingid.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" diff --git a/net/stream/jetstream/stream.go b/net/stream/jetstream/stream.go index 5510baad..c2a55833 100644 --- a/net/stream/jetstream/stream.go +++ b/net/stream/jetstream/stream.go @@ -7,7 +7,7 @@ import ( "github.com/nats-io/nats.go" "github.com/pkg/errors" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "github.com/foomo/keel/log" diff --git a/service/http.go b/service/http.go index 23d3e202..06fe069e 100644 --- a/service/http.go +++ b/service/http.go @@ -11,7 +11,7 @@ import ( keelhttp "github.com/foomo/keel/net/http" keelsemconv "github.com/foomo/keel/semconv" "github.com/pkg/errors" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "github.com/foomo/keel/log" diff --git a/service/httphealthz.go b/service/httphealthz.go index 6d5fbe2c..439215f9 100644 --- a/service/httphealthz.go +++ b/service/httphealthz.go @@ -8,7 +8,7 @@ import ( "github.com/foomo/keel/healthz" "github.com/foomo/keel/interfaces" "github.com/foomo/keel/log" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" ) diff --git a/telemetry/code.go b/telemetry/code.go index 49096212..512140fe 100644 --- a/telemetry/code.go +++ b/telemetry/code.go @@ -3,7 +3,7 @@ package telemetry import ( goruntime "github.com/foomo/go/runtime" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) // CodeCaller returns the code.function.name, code.file.path and diff --git a/telemetry/context.go b/telemetry/context.go index 611a4267..4c0f5d5e 100644 --- a/telemetry/context.go +++ b/telemetry/context.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/pyroscope-go" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap/zapcore" ) @@ -152,7 +153,8 @@ func (c Context) DeferEndSpan(err *error, opts ...trace.SpanEndOption) { DeferEndSpan(c.Span(), err, opts...) } -// SetSpanStatusOK sets the status of the current span to ok. +// SetSpanStatusOK sets the status of the current span to ok, see +// [SetSpanStatusOK] for when to use it. func (c Context) SetSpanStatusOK() { c.Span().SetStatus(codes.Ok, "") } @@ -160,7 +162,6 @@ func (c Context) SetSpanStatusOK() { // SetSpanStatusError sets the status of the current span to error with the // given description. func (c Context) SetSpanStatusError(description string) { - c.Span().SetStatus(codes.Error, description) SetSpanStatusError(c.Span(), description) } @@ -176,7 +177,7 @@ func (c Context) SetSpanAttributes(kv ...attribute.KeyValue) { // RecordError records a non-nil err with kv and a stack trace on the // current span and sets the span status to error with the root cause -// message. It does not log the error. +// message and error.type. It does not log the error. func (c Context) RecordError(err error, kv ...attribute.KeyValue) { if err != nil { sp := c.Span() @@ -185,6 +186,7 @@ func (c Context) RecordError(err error, kv ...attribute.KeyValue) { trace.WithAttributes(CodeStacktrace(5, 1)), ) sp.SetStatus(codes.Error, goerrors.Cause(err).Error()) + sp.SetAttributes(semconv.ErrorType(err)) } } diff --git a/telemetry/logger.go b/telemetry/logger.go index 4a8a1a70..fb47426c 100644 --- a/telemetry/logger.go +++ b/telemetry/logger.go @@ -8,7 +8,7 @@ import ( "github.com/foomo/keel/log" foomosemconv "github.com/foomo/opentelemetry-go/semconv" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/telemetry/meter.go b/telemetry/meter.go index 5066f48b..a78dbb6d 100644 --- a/telemetry/meter.go +++ b/telemetry/meter.go @@ -2,11 +2,13 @@ package telemetry import ( "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) // Meter returns a [metric.Meter] named [Name] from the global // [MeterProvider]. The instrumentation scope version defaults to [Version]; -// pass [metric.WithInstrumentationVersion] to override it. +// pass [metric.WithInstrumentationVersion] to override it. The schema URL +// defaults to the semconv version used by keel. func Meter(opts ...metric.MeterOption) metric.Meter { - return MeterProvider().Meter(Name, append([]metric.MeterOption{metric.WithInstrumentationVersion(Version())}, opts...)...) + return MeterProvider().Meter(Name, append([]metric.MeterOption{metric.WithInstrumentationVersion(Version()), metric.WithSchemaURL(semconv.SchemaURL)}, opts...)...) } diff --git a/telemetry/profiler.go b/telemetry/profiler.go index a1258de4..b2bf4369 100644 --- a/telemetry/profiler.go +++ b/telemetry/profiler.go @@ -10,7 +10,7 @@ import ( otelpyroscope "github.com/grafana/otel-profiling-go" "github.com/grafana/pyroscope-go" "go.opentelemetry.io/otel" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) // NewProfiler starts a pyroscope profiler named after the service and tagged diff --git a/telemetry/resource.go b/telemetry/resource.go index 2a6273da..80ac5f35 100644 --- a/telemetry/resource.go +++ b/telemetry/resource.go @@ -6,7 +6,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) // envAttributes maps environment variables to semantic attributes. diff --git a/telemetry/span.go b/telemetry/span.go index 62501e83..733c539c 100644 --- a/telemetry/span.go +++ b/telemetry/span.go @@ -9,7 +9,7 @@ import ( keelsemconv "github.com/foomo/keel/semconv" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" ) @@ -20,8 +20,9 @@ func Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) return StartSpanWithSkip(ctx, 1, opts...) } -// StartSpan starts a span named after the calling package, annotated with -// the caller's function, file and line, from the global [Tracer]. +// StartSpan starts a span named . after the caller (e.g. +// "keelmongo.(*Collection).Find"), annotated with the caller's function, file +// and line, from the global [Tracer]. func StartSpan(ctx context.Context, opts ...trace.SpanStartOption) (context.Context, trace.Span) { return StartSpanWithSkip(ctx, 1, opts...) } @@ -71,6 +72,12 @@ func SetSpanDebug(sp trace.Span) { } // SetSpanStatusOK sets the status of sp to ok. +// +// Leave the status unset on success; backends treat unset as ok. Statuses +// override in the order Ok > Error > Unset: once set to ok, later error +// statuses are ignored. Use it only to explicitly mark an operation as +// successful despite recorded errors, e.g. a retry that succeeded after +// failed attempts set the status to error. func SetSpanStatusOK(sp trace.Span) { sp.SetStatus(codes.Ok, "") } @@ -87,17 +94,19 @@ func End(sp trace.Span, err error) { if err != nil { sp.RecordError(err, trace.WithAttributes(CodeStacktrace(3, 0))) sp.SetStatus(codes.Error, goerrors.Cause(err).Error()) + sp.SetAttributes(semconv.ErrorType(err)) } sp.End() } // EndSpan ends sp. A non-nil err is recorded with a stack trace and sets the -// span status to error with the root cause message. +// span status to error with the root cause message and error.type. func EndSpan(sp trace.Span, err error, opts ...trace.SpanEndOption) { if err != nil { sp.RecordError(err, trace.WithAttributes(CodeStacktrace(3, 0))) sp.SetStatus(codes.Error, goerrors.Cause(err).Error()) + sp.SetAttributes(semconv.ErrorType(err)) } sp.End(opts...) @@ -123,7 +132,7 @@ func StartSpanWithSkip(ctx context.Context, skip int, opts ...trace.SpanStartOpt name := "runtime.go" if fr := goruntime.CallFrame(skip + 1); !fr.Zero() { - name = path.Base(fr.Pkg) + name = path.Base(fr.Pkg) + "." + fr.Short() opts = append(opts, trace.WithAttributes( semconv.CodeFunctionName(fr.Name()), semconv.CodeLineNumber(fr.Line), diff --git a/telemetry/span_test.go b/telemetry/span_test.go index 230c4919..4556e73d 100644 --- a/telemetry/span_test.go +++ b/telemetry/span_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" ) @@ -69,6 +70,8 @@ func TestEndSpanVsDeferEndSpan(t *testing.T) { assert.Equal(t, "boom", span.Status().Description) require.Len(t, span.Events(), 1) assert.Equal(t, "exception", span.Events()[0].Name) + assert.Equal(t, "telemetry_test.withDeferEndSpan", span.Name()) + assert.Contains(t, span.Attributes(), semconv.ErrorType(errors.New("boom"))) }) t.Run("EndSpan called directly records the error", func(t *testing.T) { diff --git a/telemetry/tracer.go b/telemetry/tracer.go index 74eb3a56..8320e38f 100644 --- a/telemetry/tracer.go +++ b/telemetry/tracer.go @@ -1,12 +1,14 @@ package telemetry import ( + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" ) // Tracer returns a [trace.Tracer] named [Name] from the global // [TracerProvider]. The instrumentation scope version defaults to [Version]; -// pass [trace.WithInstrumentationVersion] to override it. +// pass [trace.WithInstrumentationVersion] to override it. The schema URL +// defaults to the semconv version used by keel. func Tracer(opts ...trace.TracerOption) trace.Tracer { - return TracerProvider().Tracer(Name, append([]trace.TracerOption{trace.WithInstrumentationVersion(Version())}, opts...)...) + return TracerProvider().Tracer(Name, append([]trace.TracerOption{trace.WithInstrumentationVersion(Version()), trace.WithSchemaURL(semconv.SchemaURL)}, opts...)...) } diff --git a/utils/net/http/errors.go b/utils/net/http/errors.go index b20b1952..d8dc3c3f 100644 --- a/utils/net/http/errors.go +++ b/utils/net/http/errors.go @@ -7,7 +7,7 @@ import ( httplog "github.com/foomo/keel/net/http/log" "github.com/foomo/keel/telemetry" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "github.com/foomo/keel/log" From 82f206a8983ea3dc9a0754f891fe30962485270a Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 14:25:18 +0200 Subject: [PATCH 2/9] chore: fix cache key --- .github/actions/cache-golangci-lint/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/cache-golangci-lint/action.yml b/.github/actions/cache-golangci-lint/action.yml index 072e526a..d50c022b 100644 --- a/.github/actions/cache-golangci-lint/action.yml +++ b/.github/actions/cache-golangci-lint/action.yml @@ -10,4 +10,4 @@ runs: path: ~/.cache/golangci-lint key: golangci-lint-${{ runner.os }}-${{ hashFiles('**/go.mod') }} restore-keys: | - golangci-lint-${{ runner.os }}-- + golangci-lint-${{ runner.os }}- From 66f5c0e33357e567e8d940e672325ded058d2169 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 14:25:32 +0200 Subject: [PATCH 3/9] chore: run generate --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c3df1a66..999db877 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,9 @@ jobs: - name: Run tidy run: make tidy && git diff --exit-code + - name: Run generate + run: make generate && git diff --exit-code + - name: Run lint run: make lint From 55089a622ead6a7c88465b6f07bc14303df9a2ab Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 14:26:06 +0200 Subject: [PATCH 4/9] feat: bump deps --- examples/go.mod | 14 +++++++------- examples/go.sum | 32 ++++++++++++++++---------------- go.mod | 14 +++++++------- go.sum | 32 ++++++++++++++++---------------- integration/nats/go.mod | 16 ++++++++-------- integration/nats/go.sum | 32 ++++++++++++++++---------------- integration/temporal/go.mod | 14 +++++++------- integration/temporal/go.sum | 32 ++++++++++++++++---------------- net/stream/go.mod | 9 +++++---- net/stream/go.sum | 26 ++++++++++++++------------ persistence/mongo/go.mod | 10 ++++++---- persistence/mongo/go.sum | 26 ++++++++++++++------------ persistence/postgres/go.mod | 6 +++--- persistence/postgres/go.sum | 12 ++++++------ 14 files changed, 141 insertions(+), 134 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index d562f72d..58ff6d6d 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -21,9 +21,9 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.24.1 go.mongodb.org/mongo-driver/v2 v2.9.1 - go.opentelemetry.io/otel v1.46.0 - go.opentelemetry.io/otel/metric v1.46.0 - go.opentelemetry.io/otel/trace v1.46.0 + go.opentelemetry.io/otel v1.47.0 + go.opentelemetry.io/otel/metric v1.47.0 + go.opentelemetry.io/otel/trace v1.47.0 go.uber.org/zap v1.28.0 ) @@ -50,7 +50,7 @@ require ( github.com/foomo/gofuncy v0.4.0 // indirect github.com/foomo/gostandards v0.3.0 // indirect github.com/foomo/mongo-lock v0.2.0 // indirect - github.com/foomo/opentelemetry-go v0.4.0 // indirect + github.com/foomo/opentelemetry-go v0.5.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -136,10 +136,10 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.22.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 // indirect - go.opentelemetry.io/otel/log v0.22.0 // indirect - go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/sdk v1.47.0 // indirect go.opentelemetry.io/otel/sdk/log v0.22.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.47.0 // indirect go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect diff --git a/examples/go.sum b/examples/go.sum index 5a67c04c..c688db7f 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -112,8 +112,8 @@ github.com/foomo/gostandards v0.3.0 h1:Ev88Tdu0+52UH/d0l2KcAWx542A13o42d++LSTG0D github.com/foomo/gostandards v0.3.0/go.mod h1:8hJ56tpyFAVwavs5FuUF3po7Bq7hOTkQ1+DzFi4GCWg= github.com/foomo/mongo-lock v0.2.0 h1:xURP2FijZ/feACB5YL1UKUXk23M+x5L+yltEK+sJm0g= github.com/foomo/mongo-lock v0.2.0/go.mod h1:5LMCz8IeaTMCWcgh+aPoDeIjY1qpu8MXbx+FNrGAUFA= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/foomo/opentelemetry-go/exporters/glossy/glossymetric v0.4.0 h1:o2EScdH3ZgIU2sTHbOnCRXsMr6wTHjAYx63rriQRmtU= github.com/foomo/opentelemetry-go/exporters/glossy/glossymetric v0.4.0/go.mod h1:RIWQzc+4Y8ur6bs3v9eWduKTPTUSu1WZjcgi8oRwGjs= github.com/foomo/opentelemetry-go/exporters/glossy/glossytrace v0.4.0 h1:ZbA7PgNO0st/KOAwiJCb7qG5hzIFlSMd6WpozsX28hs= @@ -457,8 +457,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90U go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0 h1:v4KkRLVvE1cWqTJDfZZkTCG+Z4aolsa6RVos0FX7vqE= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0/go.mod h1:g/xbuPC0XbgwMdKuyF5sKOUUEsorSkN6APydyFP/H9E= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0 h1:Bu39F5tzJct+f2IZbB8989fwyTps3c8e7EsUQsz+vs8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0/go.mod h1:dJUwod88EsFgYCqrDHaSPzhiY9pBUpt0d85/qSfua7k= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 h1:lYk7RmxdLK865qLwibroNGldHa1U7SWKYYvNjlK7PIo= @@ -481,24 +481,24 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 h1:PR9eAf7o0dQs3h go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0/go.mod h1:2Z4KyNdH1uuzivdinyfGsxzNNT/Rl45pwtVwfYVI0xk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 h1:KdRxPiAoMptR3vfWzvjjvutTsSiwbC2uG0496rzZNfo= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0/go.mod h1:K/qSA+3G7Eovxi4K09wzrAgkWRnosS0DAOZeEpve7sM= -go.opentelemetry.io/otel/log v0.22.0 h1:5DBNnfvaJ6CVdkJ+Jle8Tzs50aSSv49TXGj9XRsEYw0= -go.opentelemetry.io/otel/log v0.22.0/go.mod h1:gzOt/R67vF2GniAqWu8Qv0SXy89f71muHcrkz76PCdc= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= go.opentelemetry.io/otel/log/logtest v0.22.0 h1:0pvI8BwoRN7c0KVXqzSdZQgkFdsNBL/aokbSp3boQec= go.opentelemetry.io/otel/log/logtest v0.22.0/go.mod h1:9+PjkCcSiKB2CEn3LYZ6Y3c37KJs7fziPXNiuyQGmRQ= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU= -go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4= -go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= -go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM= +go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= go.opentelemetry.io/otel/sdk/log v0.22.0 h1:PRL+s6P63XT4E/bheEflopPUpVxuvANqZwtt89yhoGk= go.opentelemetry.io/otel/sdk/log v0.22.0/go.mod h1:JNp0sBELrjCTcu5W3GzABVypeU6vDJjBS+X0JISuz+g= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0 h1:infPnfNrhCNgOUZRs3gWUg8vhoBUHihq02gwK05gzlg= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0/go.mod h1:gkQZA3z15Bv3KU9vigBTi8dFechSozRP7v94X4VZv+s= -go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= -go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/go.mod b/go.mod index a27186ea..221c0466 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/foomo/goflux v0.10.0 github.com/foomo/gofuncy v0.4.0 github.com/foomo/gostandards v0.3.0 - github.com/foomo/opentelemetry-go v0.4.0 + github.com/foomo/opentelemetry-go v0.5.0 github.com/go-logr/logr v1.4.4 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/uuid v1.6.0 @@ -30,7 +30,7 @@ require ( go.opentelemetry.io/contrib/instrumentation/host v0.71.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0 - go.opentelemetry.io/otel v1.46.0 + go.opentelemetry.io/otel v1.47.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0 @@ -41,13 +41,13 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.22.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 - go.opentelemetry.io/otel/log v0.22.0 + go.opentelemetry.io/otel/log v1.47.0 go.opentelemetry.io/otel/log/logtest v0.22.0 - go.opentelemetry.io/otel/metric v1.46.0 - go.opentelemetry.io/otel/sdk v1.46.0 + go.opentelemetry.io/otel/metric v1.47.0 + go.opentelemetry.io/otel/sdk v1.47.0 go.opentelemetry.io/otel/sdk/log v0.22.0 - go.opentelemetry.io/otel/sdk/metric v1.46.0 - go.opentelemetry.io/otel/trace v1.46.0 + go.opentelemetry.io/otel/sdk/metric v1.47.0 + go.opentelemetry.io/otel/trace v1.47.0 go.uber.org/goleak v1.3.0 go.uber.org/zap v1.28.0 golang.org/x/crypto v0.57.0 diff --git a/go.sum b/go.sum index 854d4d3d..43397bfa 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,8 @@ github.com/foomo/gofuncy v0.4.0 h1:wxfFOQSp+eZYy9YFXY7A8PPu0n51t4Xhvd6tOm0OSSs= github.com/foomo/gofuncy v0.4.0/go.mod h1:ulEUpkz38K6trv71GM7fHjaUqa/NbWOZUE5MAbfe/Kk= github.com/foomo/gostandards v0.3.0 h1:Ev88Tdu0+52UH/d0l2KcAWx542A13o42d++LSTG0DC4= github.com/foomo/gostandards v0.3.0/go.mod h1:8hJ56tpyFAVwavs5FuUF3po7Bq7hOTkQ1+DzFi4GCWg= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/foomo/opentelemetry-go/exporters/glossy/glossymetric v0.4.0 h1:o2EScdH3ZgIU2sTHbOnCRXsMr6wTHjAYx63rriQRmtU= github.com/foomo/opentelemetry-go/exporters/glossy/glossymetric v0.4.0/go.mod h1:RIWQzc+4Y8ur6bs3v9eWduKTPTUSu1WZjcgi8oRwGjs= github.com/foomo/opentelemetry-go/exporters/glossy/glossytrace v0.4.0 h1:ZbA7PgNO0st/KOAwiJCb7qG5hzIFlSMd6WpozsX28hs= @@ -378,8 +378,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90U go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0 h1:v4KkRLVvE1cWqTJDfZZkTCG+Z4aolsa6RVos0FX7vqE= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0/go.mod h1:g/xbuPC0XbgwMdKuyF5sKOUUEsorSkN6APydyFP/H9E= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0 h1:Bu39F5tzJct+f2IZbB8989fwyTps3c8e7EsUQsz+vs8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0/go.mod h1:dJUwod88EsFgYCqrDHaSPzhiY9pBUpt0d85/qSfua7k= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 h1:lYk7RmxdLK865qLwibroNGldHa1U7SWKYYvNjlK7PIo= @@ -402,24 +402,24 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 h1:PR9eAf7o0dQs3h go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0/go.mod h1:2Z4KyNdH1uuzivdinyfGsxzNNT/Rl45pwtVwfYVI0xk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 h1:KdRxPiAoMptR3vfWzvjjvutTsSiwbC2uG0496rzZNfo= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0/go.mod h1:K/qSA+3G7Eovxi4K09wzrAgkWRnosS0DAOZeEpve7sM= -go.opentelemetry.io/otel/log v0.22.0 h1:5DBNnfvaJ6CVdkJ+Jle8Tzs50aSSv49TXGj9XRsEYw0= -go.opentelemetry.io/otel/log v0.22.0/go.mod h1:gzOt/R67vF2GniAqWu8Qv0SXy89f71muHcrkz76PCdc= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= go.opentelemetry.io/otel/log/logtest v0.22.0 h1:0pvI8BwoRN7c0KVXqzSdZQgkFdsNBL/aokbSp3boQec= go.opentelemetry.io/otel/log/logtest v0.22.0/go.mod h1:9+PjkCcSiKB2CEn3LYZ6Y3c37KJs7fziPXNiuyQGmRQ= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU= -go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4= -go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= -go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM= +go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= go.opentelemetry.io/otel/sdk/log v0.22.0 h1:PRL+s6P63XT4E/bheEflopPUpVxuvANqZwtt89yhoGk= go.opentelemetry.io/otel/sdk/log v0.22.0/go.mod h1:JNp0sBELrjCTcu5W3GzABVypeU6vDJjBS+X0JISuz+g= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0 h1:infPnfNrhCNgOUZRs3gWUg8vhoBUHihq02gwK05gzlg= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0/go.mod h1:gkQZA3z15Bv3KU9vigBTi8dFechSozRP7v94X4VZv+s= -go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= -go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/integration/nats/go.mod b/integration/nats/go.mod index 14cfdcaa..285c0f21 100644 --- a/integration/nats/go.mod +++ b/integration/nats/go.mod @@ -8,11 +8,15 @@ require ( github.com/foomo/go v0.17.1 github.com/foomo/gofuncy v0.4.0 github.com/foomo/keel v0.31.0 - github.com/foomo/opentelemetry-go v0.4.0 + github.com/foomo/opentelemetry-go v0.5.0 github.com/nats-io/nats-server/v2 v2.15.0 github.com/nats-io/nats.go v1.54.0 + github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.12.1 - go.opentelemetry.io/otel v1.46.0 + go.opentelemetry.io/otel v1.47.0 + go.opentelemetry.io/otel/metric v1.47.0 + go.opentelemetry.io/otel/sdk/metric v1.47.0 + go.opentelemetry.io/otel/trace v1.47.0 go.uber.org/zap v1.28.0 ) @@ -93,7 +97,6 @@ require ( github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/spf13/viper v1.21.0 // indirect github.com/spf13/viper/remote v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tinylib/msgp v1.6.5 // indirect @@ -121,12 +124,9 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.22.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 // indirect - go.opentelemetry.io/otel/log v0.22.0 // indirect - go.opentelemetry.io/otel/metric v1.46.0 // indirect - go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/sdk v1.47.0 // indirect go.opentelemetry.io/otel/sdk/log v0.22.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect - go.opentelemetry.io/otel/trace v1.46.0 // indirect go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect diff --git a/integration/nats/go.sum b/integration/nats/go.sum index e768e3c3..2423a8d4 100644 --- a/integration/nats/go.sum +++ b/integration/nats/go.sum @@ -82,8 +82,8 @@ github.com/foomo/gofuncy v0.4.0 h1:wxfFOQSp+eZYy9YFXY7A8PPu0n51t4Xhvd6tOm0OSSs= github.com/foomo/gofuncy v0.4.0/go.mod h1:ulEUpkz38K6trv71GM7fHjaUqa/NbWOZUE5MAbfe/Kk= github.com/foomo/gostandards v0.3.0 h1:Ev88Tdu0+52UH/d0l2KcAWx542A13o42d++LSTG0DC4= github.com/foomo/gostandards v0.3.0/go.mod h1:8hJ56tpyFAVwavs5FuUF3po7Bq7hOTkQ1+DzFi4GCWg= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/foomo/opentelemetry-go/exporters/glossy/glossymetric v0.4.0 h1:o2EScdH3ZgIU2sTHbOnCRXsMr6wTHjAYx63rriQRmtU= github.com/foomo/opentelemetry-go/exporters/glossy/glossymetric v0.4.0/go.mod h1:RIWQzc+4Y8ur6bs3v9eWduKTPTUSu1WZjcgi8oRwGjs= github.com/foomo/opentelemetry-go/exporters/glossy/glossytrace v0.4.0 h1:ZbA7PgNO0st/KOAwiJCb7qG5hzIFlSMd6WpozsX28hs= @@ -378,8 +378,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90U go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0 h1:v4KkRLVvE1cWqTJDfZZkTCG+Z4aolsa6RVos0FX7vqE= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0/go.mod h1:g/xbuPC0XbgwMdKuyF5sKOUUEsorSkN6APydyFP/H9E= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0 h1:Bu39F5tzJct+f2IZbB8989fwyTps3c8e7EsUQsz+vs8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0/go.mod h1:dJUwod88EsFgYCqrDHaSPzhiY9pBUpt0d85/qSfua7k= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 h1:lYk7RmxdLK865qLwibroNGldHa1U7SWKYYvNjlK7PIo= @@ -402,24 +402,24 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 h1:PR9eAf7o0dQs3h go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0/go.mod h1:2Z4KyNdH1uuzivdinyfGsxzNNT/Rl45pwtVwfYVI0xk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 h1:KdRxPiAoMptR3vfWzvjjvutTsSiwbC2uG0496rzZNfo= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0/go.mod h1:K/qSA+3G7Eovxi4K09wzrAgkWRnosS0DAOZeEpve7sM= -go.opentelemetry.io/otel/log v0.22.0 h1:5DBNnfvaJ6CVdkJ+Jle8Tzs50aSSv49TXGj9XRsEYw0= -go.opentelemetry.io/otel/log v0.22.0/go.mod h1:gzOt/R67vF2GniAqWu8Qv0SXy89f71muHcrkz76PCdc= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= go.opentelemetry.io/otel/log/logtest v0.22.0 h1:0pvI8BwoRN7c0KVXqzSdZQgkFdsNBL/aokbSp3boQec= go.opentelemetry.io/otel/log/logtest v0.22.0/go.mod h1:9+PjkCcSiKB2CEn3LYZ6Y3c37KJs7fziPXNiuyQGmRQ= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU= -go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4= -go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= -go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM= +go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= go.opentelemetry.io/otel/sdk/log v0.22.0 h1:PRL+s6P63XT4E/bheEflopPUpVxuvANqZwtt89yhoGk= go.opentelemetry.io/otel/sdk/log v0.22.0/go.mod h1:JNp0sBELrjCTcu5W3GzABVypeU6vDJjBS+X0JISuz+g= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0 h1:infPnfNrhCNgOUZRs3gWUg8vhoBUHihq02gwK05gzlg= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0/go.mod h1:gkQZA3z15Bv3KU9vigBTi8dFechSozRP7v94X4VZv+s= -go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= -go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/integration/temporal/go.mod b/integration/temporal/go.mod index 7fffbbbb..da17522b 100644 --- a/integration/temporal/go.mod +++ b/integration/temporal/go.mod @@ -8,8 +8,8 @@ require ( github.com/foomo/go v0.17.1 github.com/foomo/keel v0.31.0 github.com/pkg/errors v0.9.1 - go.opentelemetry.io/otel v1.46.0 - go.opentelemetry.io/otel/metric v1.46.0 + go.opentelemetry.io/otel v1.47.0 + go.opentelemetry.io/otel/metric v1.47.0 go.temporal.io/api v1.63.6 go.temporal.io/sdk v1.49.0 go.temporal.io/sdk/contrib/opentelemetry v0.8.1 @@ -23,7 +23,7 @@ require ( github.com/ebitengine/purego v0.10.2 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/fbiville/markdown-table-formatter v0.3.0 // indirect - github.com/foomo/opentelemetry-go v0.4.0 // indirect + github.com/foomo/opentelemetry-go v0.5.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -68,11 +68,11 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.22.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 // indirect - go.opentelemetry.io/otel/log v0.22.0 // indirect - go.opentelemetry.io/otel/sdk v1.46.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/sdk v1.47.0 // indirect go.opentelemetry.io/otel/sdk/log v0.22.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect - go.opentelemetry.io/otel/trace v1.46.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect diff --git a/integration/temporal/go.sum b/integration/temporal/go.sum index 859b481b..ee3bda61 100644 --- a/integration/temporal/go.sum +++ b/integration/temporal/go.sum @@ -12,8 +12,8 @@ github.com/fbiville/markdown-table-formatter v0.3.0 h1:PIm1UNgJrFs8q1htGTw+wnnNY github.com/fbiville/markdown-table-formatter v0.3.0/go.mod h1:q89TDtSEVDdTaufgSbfHpNVdPU/bmfvqNkrC5HagmLY= github.com/foomo/go v0.17.1 h1:tDrovWbh1dOzsBX19pII6nAPIECswhBmNSPEGBYNawY= github.com/foomo/go v0.17.1/go.mod h1:Zz5aBFte6ayLEJ17n4L1bqYmEEja9xTeLKC0Z1wAy+0= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -95,8 +95,8 @@ go.opentelemetry.io/contrib/instrumentation/host v0.71.0 h1:w4LFwIdxvbuLJFS4GgUS go.opentelemetry.io/contrib/instrumentation/host v0.71.0/go.mod h1:3bLYpeWrDSzFnG5Hvuz7EON6f+ZbcRo/NriKeIXh72E= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0 h1:v4KkRLVvE1cWqTJDfZZkTCG+Z4aolsa6RVos0FX7vqE= go.opentelemetry.io/contrib/instrumentation/runtime v0.71.0/go.mod h1:g/xbuPC0XbgwMdKuyF5sKOUUEsorSkN6APydyFP/H9E= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0 h1:Bu39F5tzJct+f2IZbB8989fwyTps3c8e7EsUQsz+vs8= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0/go.mod h1:dJUwod88EsFgYCqrDHaSPzhiY9pBUpt0d85/qSfua7k= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 h1:lYk7RmxdLK865qLwibroNGldHa1U7SWKYYvNjlK7PIo= @@ -119,24 +119,24 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 h1:PR9eAf7o0dQs3h go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0/go.mod h1:2Z4KyNdH1uuzivdinyfGsxzNNT/Rl45pwtVwfYVI0xk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 h1:KdRxPiAoMptR3vfWzvjjvutTsSiwbC2uG0496rzZNfo= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0/go.mod h1:K/qSA+3G7Eovxi4K09wzrAgkWRnosS0DAOZeEpve7sM= -go.opentelemetry.io/otel/log v0.22.0 h1:5DBNnfvaJ6CVdkJ+Jle8Tzs50aSSv49TXGj9XRsEYw0= -go.opentelemetry.io/otel/log v0.22.0/go.mod h1:gzOt/R67vF2GniAqWu8Qv0SXy89f71muHcrkz76PCdc= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= go.opentelemetry.io/otel/log/logtest v0.22.0 h1:0pvI8BwoRN7c0KVXqzSdZQgkFdsNBL/aokbSp3boQec= go.opentelemetry.io/otel/log/logtest v0.22.0/go.mod h1:9+PjkCcSiKB2CEn3LYZ6Y3c37KJs7fziPXNiuyQGmRQ= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU= -go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4= -go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= -go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM= +go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= go.opentelemetry.io/otel/sdk/log v0.22.0 h1:PRL+s6P63XT4E/bheEflopPUpVxuvANqZwtt89yhoGk= go.opentelemetry.io/otel/sdk/log v0.22.0/go.mod h1:JNp0sBELrjCTcu5W3GzABVypeU6vDJjBS+X0JISuz+g= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0 h1:infPnfNrhCNgOUZRs3gWUg8vhoBUHihq02gwK05gzlg= go.opentelemetry.io/otel/sdk/log/logtest v0.22.0/go.mod h1:gkQZA3z15Bv3KU9vigBTi8dFechSozRP7v94X4VZv+s= -go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= -go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= go.temporal.io/api v1.63.6 h1:LoV2ubLM9M5uiGeBcYCSCTmHAPGQPy18dC8Vcw8TciQ= diff --git a/net/stream/go.mod b/net/stream/go.mod index 2e8286bb..1b6f93fa 100644 --- a/net/stream/go.mod +++ b/net/stream/go.mod @@ -7,11 +7,12 @@ replace github.com/foomo/keel => ../../ require ( github.com/foomo/go v0.17.1 github.com/foomo/keel v0.31.0 + github.com/foomo/opentelemetry-go v0.5.0 github.com/nats-io/nats.go v1.54.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.12.1 github.com/testcontainers/testcontainers-go/modules/nats v0.44.0 - go.opentelemetry.io/otel v1.46.0 + go.opentelemetry.io/otel v1.47.0 go.uber.org/zap v1.28.0 ) @@ -32,7 +33,6 @@ require ( github.com/ebitengine/purego v0.10.2 // indirect github.com/fbiville/markdown-table-formatter v0.3.0 // indirect github.com/felixge/httpsnoop v1.1.0 // indirect - github.com/foomo/opentelemetry-go v0.4.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -62,8 +62,9 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect - go.opentelemetry.io/otel/metric v1.46.0 // indirect - go.opentelemetry.io/otel/trace v1.46.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/metric v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/crypto v0.57.0 // indirect diff --git a/net/stream/go.sum b/net/stream/go.sum index c4000ba5..8f265d5e 100644 --- a/net/stream/go.sum +++ b/net/stream/go.sum @@ -36,8 +36,8 @@ github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeO github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/foomo/go v0.17.1 h1:tDrovWbh1dOzsBX19pII6nAPIECswhBmNSPEGBYNawY= github.com/foomo/go v0.17.1/go.mod h1:Zz5aBFte6ayLEJ17n4L1bqYmEEja9xTeLKC0Z1wAy+0= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -110,16 +110,18 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= -go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= -go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= -go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/persistence/mongo/go.mod b/persistence/mongo/go.mod index 99686071..a7c62037 100644 --- a/persistence/mongo/go.mod +++ b/persistence/mongo/go.mod @@ -16,8 +16,9 @@ require ( require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fbiville/markdown-table-formatter v0.3.0 // indirect - github.com/foomo/opentelemetry-go v0.4.0 // indirect + github.com/foomo/opentelemetry-go v0.5.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/klauspost/compress v1.20.1 // indirect @@ -28,9 +29,10 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.46.0 // indirect - go.opentelemetry.io/otel/metric v1.46.0 // indirect - go.opentelemetry.io/otel/trace v1.46.0 // indirect + go.opentelemetry.io/otel v1.47.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/metric v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.57.0 // indirect golang.org/x/sync v0.23.0 // indirect diff --git a/persistence/mongo/go.sum b/persistence/mongo/go.sum index daf6ac6b..b0170420 100644 --- a/persistence/mongo/go.sum +++ b/persistence/mongo/go.sum @@ -34,8 +34,8 @@ github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeO github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE= github.com/foomo/mongo-lock v0.2.0 h1:xURP2FijZ/feACB5YL1UKUXk23M+x5L+yltEK+sJm0g= github.com/foomo/mongo-lock v0.2.0/go.mod h1:5LMCz8IeaTMCWcgh+aPoDeIjY1qpu8MXbx+FNrGAUFA= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -116,16 +116,18 @@ go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/v2/mongo go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/v2/mongo/otelmongo v0.0.0-20260826095902-c4c6248ec228/go.mod h1:tfjajq3dcdhtCQCuU9YBOCckNUTOE1p1OZV5qRVLs2A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= -go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8= -go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o= -go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI= -go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM= -go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE= -go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/persistence/postgres/go.mod b/persistence/postgres/go.mod index 2c8264ab..6f68aeda 100644 --- a/persistence/postgres/go.mod +++ b/persistence/postgres/go.mod @@ -14,8 +14,8 @@ require ( require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/fbiville/markdown-table-formatter v0.3.0 // indirect - github.com/foomo/opentelemetry-go v0.4.0 // indirect - go.opentelemetry.io/otel v1.46.0 // indirect - go.opentelemetry.io/otel/trace v1.46.0 // indirect + github.com/foomo/opentelemetry-go v0.5.0 // indirect + go.opentelemetry.io/otel v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect go.uber.org/multierr v1.11.0 // indirect ) diff --git a/persistence/postgres/go.sum b/persistence/postgres/go.sum index db96cddf..ca9abbed 100644 --- a/persistence/postgres/go.sum +++ b/persistence/postgres/go.sum @@ -2,8 +2,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/fbiville/markdown-table-formatter v0.3.0 h1:PIm1UNgJrFs8q1htGTw+wnnNYvwXQMMMIKNZop2SSho= github.com/fbiville/markdown-table-formatter v0.3.0/go.mod h1:q89TDtSEVDdTaufgSbfHpNVdPU/bmfvqNkrC5HagmLY= -github.com/foomo/opentelemetry-go v0.4.0 h1:UQneCUSokIYJXzFUKQSI8rlelbUuf6atgSZMhvD+Nzo= -github.com/foomo/opentelemetry-go v0.4.0/go.mod h1:R3Se/rc5FRAs4ThE6JQ9Oq3cOLo2MOsUzZLJxZquwxA= +github.com/foomo/opentelemetry-go v0.5.0 h1:4MHCj7cRB+Jr8AZsrhQ+J3M/4Gka/mXG0Xv+uv6RNDI= +github.com/foomo/opentelemetry-go v0.5.0/go.mod h1:3jSEAGFXOJMtFLiLXepEQcr9Fkz1g/JqoWqlcihW/pk= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= @@ -12,10 +12,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= -go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc= -go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE= -go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c= -go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= From 2a940a3c3039bf10acb15fc70b5bbf09a8a56f2d Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 14:26:47 +0200 Subject: [PATCH 5/9] chore: fix deprecations --- .goreleaser.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 18aea6f6..be3bfae6 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json version: 2 project_name: keel @@ -9,11 +10,14 @@ builds: - skip: true archives: - - format: tar.gz + - formats: [ tar.gz ] format_overrides: - goos: windows - format: zip + formats: [ zip ] files: [ LICENSE, README.md ] +checksum: + name_template: checksums.txt + changelog: use: github-native From b44e36b276269e718b34cae3540469b599eb0c9c Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 14:40:56 +0200 Subject: [PATCH 6/9] feat: use semconv attr instead of log fields --- closer.go | 6 +- examples/persistence/mongo/main.go | 20 +++---- examples/stream/jetstream/main.go | 6 +- examples/stream/jetstreamraw/main.go | 4 +- integration/nats/jetstream.go | 8 +-- job.go | 17 +++--- keeltest/server.go | 4 +- log/configure.go | 4 +- log/doc.go | 17 ++++-- log/must.go | 2 +- log/with.go | 24 +++++--- net/http/middleware/logger.go | 11 ++-- net/http/middleware/rbac.go | 18 +++--- net/http/middleware/recover.go | 5 +- net/http/middleware/responsecontroller.go | 2 +- net/http/middleware/responsetime.go | 3 +- net/http/middleware/telemetry.go | 5 +- net/http/rbac/helper_test.go | 4 +- net/http/roundtripware/circuitbreaker.go | 57 +++++++++++-------- net/http/roundtripware/logger.go | 3 +- net/http/roundtripware/metric.go | 6 +- net/http/roundtripware/recover.go | 4 +- net/stream/jetstream/stream.go | 58 ++++++++++++++----- net/stream/jetstream/subscriber.go | 2 +- semconv/circuitbreaker.go | 22 ++++++++ semconv/debug.go | 4 ++ semconv/doc.go | 7 ++- semconv/generic.go | 69 +++++++++++++++++++++++ semconv/keel.go | 51 ++++++++++++++++- semconv/rbac.go | 36 ++++++++++++ server.go | 8 +-- service/goroutine.go | 2 +- service/goroutine_test.go | 10 ++-- service/http.go | 2 +- service/http_test.go | 10 ++-- service/subscription.go | 7 ++- serviceenabler.go | 4 +- telemetry/context.go | 3 +- telemetry/loggerprovider.go | 6 +- telemetry/span.go | 22 ++++++-- utils/net/http/errors.go | 5 +- 41 files changed, 414 insertions(+), 144 deletions(-) create mode 100644 semconv/circuitbreaker.go create mode 100644 semconv/generic.go create mode 100644 semconv/rbac.go diff --git a/closer.go b/closer.go index 99d48883..9a59cc1d 100644 --- a/closer.go +++ b/closer.go @@ -2,10 +2,10 @@ package keel import ( "context" - "fmt" "github.com/foomo/keel/interfaces" "github.com/foomo/keel/log" + keelsemconv "github.com/foomo/keel/semconv" "go.uber.org/zap" ) @@ -21,7 +21,7 @@ func closeAll(ctx context.Context, l *zap.Logger, closers []any) { var err error - cl := l.With(log.FName(fmt.Sprintf("%T", closer))) + cl := log.WithAttributes(l, keelsemconv.KeelCloserType(closer)) switch c := closer.(type) { case interfaces.Closer: c.Close() @@ -58,7 +58,7 @@ func closeAll(ctx context.Context, l *zap.Logger, closers []any) { } if err != nil { - cl.Warn("keel closer failed", zap.Error(err)) + log.WithError(cl, err).Warn("keel closer failed") } else { cl.Debug("keel closer closed") } diff --git a/examples/persistence/mongo/main.go b/examples/persistence/mongo/main.go index 43a9d4f5..186693bd 100644 --- a/examples/persistence/mongo/main.go +++ b/examples/persistence/mongo/main.go @@ -77,9 +77,9 @@ func main() { if err := repo.Insert(context.Background(), &store.Dummy{ Entity: store.NewEntity(newEntity.ID), }); mongo.IsDuplicateKeyError(err) { - l.Info("OK: expected error", log.FValue(err.Error())) + log.WithError(l, err).Info("OK: expected error") } else if err != nil { - l.Error("unexpected error", log.FValue(err.Error())) + log.WithError(l, err).Error("unexpected error") } else { l.Error("unexpected success") } @@ -90,9 +90,9 @@ func main() { if err := repo.Upsert(context.Background(), &store.Dummy{ Entity: store.NewEntity(newEntity.ID), }); mongo.IsDuplicateKeyError(err) { - l.Info("OK: expected error", log.FValue(err.Error())) + log.WithError(l, err).Info("OK: expected error") } else if err != nil { - l.Error("unexpected error", log.FValue(err.Error())) + log.WithError(l, err).Error("unexpected error") } else { l.Error("unexpected success") } @@ -102,9 +102,9 @@ func main() { if err := repo.UpsertMany(context.Background(), []*store.Dummy{{ Entity: store.NewEntity(newEntity.ID), }}); mongo.IsDuplicateKeyError(err) { - l.Info("OK: expected error", log.FValue(err.Error())) + log.WithError(l, err).Info("OK: expected error") } else if err != nil { - l.Error("unexpected error", log.FValue(err.Error())) + log.WithError(l, err).Error("unexpected error") } else { l.Error("unexpected success") } @@ -123,9 +123,9 @@ func main() { // update entity B if err := repo.Upsert(context.Background(), newEntityB); errors.Is(err, keelpersistence.ErrDirtyWrite) { - l.Info("OK: expected error", log.FValue(err.Error())) + log.WithError(l, err).Info("OK: expected error") } else if err != nil { - l.Error("unexpected error", log.FValue(err.Error())) + log.WithError(l, err).Error("unexpected error") } else { l.Error("unexpected success") } @@ -144,9 +144,9 @@ func main() { l.Info("Try to upsert many with dirty write") if err := repo.UpsertMany(context.Background(), []*store.Dummy{newEntityB}); errors.Is(err, keelpersistence.ErrDirtyWrite) { - l.Info("OK: expected error", log.FValue(err.Error())) + log.WithError(l, err).Info("OK: expected error") } else if err != nil { - l.Error("unexpected error", log.FValue(err.Error())) + log.WithError(l, err).Error("unexpected error") } else { l.Error("unexpected success") } diff --git a/examples/stream/jetstream/main.go b/examples/stream/jetstream/main.go index 4768be63..6103d122 100644 --- a/examples/stream/jetstream/main.go +++ b/examples/stream/jetstream/main.go @@ -13,7 +13,9 @@ import ( "github.com/foomo/keel" "github.com/foomo/keel/log" "github.com/foomo/keel/net/stream/jetstream" + keelsemconv "github.com/foomo/keel/semconv" httputils "github.com/foomo/keel/utils/net/http" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) func main() { @@ -71,7 +73,7 @@ func main() { return } - l.Info("sent message", log.FValue(msg.Name)) + l.Info("sent message", log.Attribute(keelsemconv.Value(msg.Name))) w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("OK")) }) @@ -83,7 +85,7 @@ func main() { return errors.Wrap(err, "failed to unmarshall message data") } - l.Info("received message", log.FValue(data.Name), log.FMessagingDestination(msg.Subject)) + l.Info("received message", log.Attribute(keelsemconv.Value(data.Name)), log.Attribute(semconv.MessagingDestinationName(msg.Subject))) return nil }) diff --git a/examples/stream/jetstreamraw/main.go b/examples/stream/jetstreamraw/main.go index 939f6a85..a493db0c 100644 --- a/examples/stream/jetstreamraw/main.go +++ b/examples/stream/jetstreamraw/main.go @@ -10,7 +10,9 @@ import ( "github.com/foomo/keel" "github.com/foomo/keel/log" "github.com/foomo/keel/net/stream/jetstream" + keelsemconv "github.com/foomo/keel/semconv" httputils "github.com/foomo/keel/utils/net/http" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) func main() { @@ -67,7 +69,7 @@ func main() { // subscribe to the subject subscription, err := sub.JS().Subscribe(sub.Subject(), func(msg *nats.Msg) { - l.Info("received message", log.FValue(string(msg.Data)), log.FMessagingDestination(msg.Subject)) + l.Info("received message", log.Attribute(keelsemconv.Value(string(msg.Data))), log.Attribute(semconv.MessagingDestinationName(msg.Subject))) }, sub.SubOpts()...) log.Must(l, err, "failed to subscribe to subject") diff --git a/integration/nats/jetstream.go b/integration/nats/jetstream.go index 586d2fff..e737676b 100644 --- a/integration/nats/jetstream.go +++ b/integration/nats/jetstream.go @@ -5,11 +5,12 @@ import ( "time" "github.com/foomo/keel" + "github.com/foomo/keel/log" "github.com/foomo/opentelemetry-go/semconv/natsconv" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" "go.opentelemetry.io/otel" - "go.uber.org/zap" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) // NewJetStream creates a JetStream context on nc with production-sensible @@ -37,9 +38,8 @@ func NewJetStream(s keel.Runtime, nc *nats.Conn, opts ...jetstream.JetStreamOpt) jetstream.WithPublishAsyncTimeout(10 * time.Second), jetstream.WithPublishAsyncErrHandler(func(_ jetstream.JetStream, msg *nats.Msg, err error) { asyncErrors.Add(ctx, 1, natsconv.AsyncErrorKindOther) - l.Error("publish async error", - zap.Error(err), - zap.String("subject", msg.Subject), + log.WithError(l, err).Error("publish async error", + log.Attribute(semconv.MessagingDestinationName(msg.Subject)), ) }), }, opts...) diff --git a/job.go b/job.go index 3444f8bc..cb8deefe 100644 --- a/job.go +++ b/job.go @@ -2,7 +2,6 @@ package keel import ( "context" - "fmt" "os" "os/signal" "slices" @@ -87,7 +86,7 @@ func NewJob(opts ...JobOption) *Job { inst.name = env.Get("OTEL_SERVICE_NAME", telemetry.DefaultServiceName) } - inst.l = log.WithAttributes(inst.l, keelsemconv.KeelServiceType("job"), keelsemconv.KeelServiceName(inst.name)) + inst.l = log.WithAttributes(inst.l, keelsemconv.KeelServiceType(keelsemconv.KeelServiceTypeJob), keelsemconv.KeelServiceName(inst.name)) { // setup telemetry otel.SetLogger(logr.New(internalotel.NewLogger(inst.l))) @@ -150,7 +149,7 @@ func (j *Job) AddStep(name string, fn StepFn) { // twice is a no-op. func (j *Job) AddCloser(closer any) { if !IsCloser(closer) { - j.l.Warn("unable to add closer", log.FValue(fmt.Sprintf("%T", closer))) + j.l.Warn("unable to add closer", log.Attribute(keelsemconv.KeelCloserType(closer))) return } @@ -203,9 +202,9 @@ func (j *Job) RunE() error { err := j.run(ctx) if err != nil { - log.WithError(j.l, err).Error("keel job failed", log.FDuration(time.Since(start))) + log.WithError(j.l, err).Error("keel job failed", log.Attribute(keelsemconv.Duration(time.Since(start)))) } else { - j.l.Info("keel job completed", log.FDuration(time.Since(start))) + j.l.Info("keel job completed", log.Attribute(keelsemconv.Duration(time.Since(start)))) } return err @@ -265,17 +264,17 @@ func (j *Job) runSteps(ctx context.Context) error { // execStep adapts a StepFn to gofuncy's Func, adding the per-step logger and // completion logging. The span, panic recovery, and metrics are gofuncy's. func (j *Job) execStep(ctx context.Context, step jobStep) error { - l := j.l.With(zap.String("keel_job_step", step.name)) + l := j.l.With(log.Attribute(keelsemconv.KeelJobStep(step.name))) l.Info("starting keel job step") start := time.Now() if err := step.fn(ctx, l); err != nil { - log.WithError(l, err).Error("keel job step failed", log.FDuration(time.Since(start))) + log.WithError(l, err).Error("keel job step failed", log.Attribute(keelsemconv.Duration(time.Since(start)))) return err } - l.Info("keel job step completed", log.FDuration(time.Since(start))) + l.Info("keel job step completed", log.Attribute(keelsemconv.Duration(time.Since(start)))) return nil } @@ -287,7 +286,7 @@ func (j *Job) finalize() { ctx, cancel := context.WithTimeout(context.WithoutCancel(j.ctx), j.gracefulPeriod) defer cancel() - j.l.Info("keel job finalize", zap.Duration("graceful_period", j.gracefulPeriod)) + j.l.Info("keel job finalize", log.Attribute(keelsemconv.KeelGracefulPeriod(j.gracefulPeriod))) for _, push := range j.pushers { if err := push(ctx); err != nil { diff --git a/keeltest/server.go b/keeltest/server.go index 1165dd5b..e1b42b9d 100644 --- a/keeltest/server.go +++ b/keeltest/server.go @@ -119,7 +119,7 @@ func (s *Server) Start() { for _, service := range s.services { s.serviceMap[service.Name()] = service if err := service.Start(s.Context()); err != nil { - s.l.Error("failed to start service", log.FError(err)) + log.WithError(s.l, err).Error("failed to start service") } } } @@ -129,7 +129,7 @@ func (s *Server) Start() { func (s *Server) Close() { for _, service := range s.services { if err := service.Close(context.WithoutCancel(s.Context())); err != nil { - s.l.Error("failed to close service", log.FError(err)) + log.WithError(s.l, err).Error("failed to close service") } } } diff --git a/log/configure.go b/log/configure.go index c76a6a25..1c49270c 100644 --- a/log/configure.go +++ b/log/configure.go @@ -27,9 +27,9 @@ func (c *Config) Logger() *zap.Logger { return c.l } -// Error adds err using [FError]. +// Error adds err using [WithError]. func (c *Config) Error(err error) *Config { - c.l = c.l.With(FError(err)) + c.l = WithError(c.l, err) return c } diff --git a/log/doc.go b/log/doc.go index 93ae91e8..6890b013 100644 --- a/log/doc.go +++ b/log/doc.go @@ -8,13 +8,20 @@ // // # Fields // -// F* functions create single [zap.Field] values with well-known keys, and -// [Attribute] and [Attributes] convert OpenTelemetry attributes to fields. -// With* functions return a child logger with fields added, e.g. from an -// [net/http.Request]: +// [Attribute] and [Attributes] convert OpenTelemetry attributes to fields, so +// the same semconv attributes can be shared between logs, metrics and traces. +// Use the upstream go.opentelemetry.io/otel/semconv package or +// github.com/foomo/keel/semconv for keel specific keys. With* functions return +// a child logger with fields added, e.g. from an [net/http.Request]: // // l := log.WithHTTPRequest(nil, r) -// l.Info("handled", log.FDuration(time.Since(start))) +// l.Info("handled", log.Attribute(keelsemconv.Duration(time.Since(start)))) +// +// F* functions create single [zap.Field] values with legacy keys and are kept +// for backward compatibility. +// +// TODO: deprecate the remaining F* functions and *Key constants in the next +// version in favor of semconv attributes with [Attribute] and [Attributes]. // // A [Labeler] stored in a context collects fields that are added while a // request is processed. diff --git a/log/must.go b/log/must.go index d0327646..09853e02 100644 --- a/log/must.go +++ b/log/must.go @@ -20,6 +20,6 @@ func Must(l *zap.Logger, err error, msgAndArgs ...any) { msg, msgAndArgs = fmt.Sprintf("%v", msgAndArgs[0]), msgAndArgs[1:] } - l.WithOptions(zap.AddCallerSkip(1)).Fatal(fmt.Sprintf(msg, msgAndArgs...), FError(err)) + WithError(l, err).WithOptions(zap.AddCallerSkip(1)).Fatal(fmt.Sprintf(msg, msgAndArgs...)) } } diff --git a/log/with.go b/log/with.go index 85c29871..0378cb88 100644 --- a/log/with.go +++ b/log/with.go @@ -14,6 +14,7 @@ import ( "go.uber.org/zap" keelhttpcontext "github.com/foomo/keel/net/http/context" + keelsemconv "github.com/foomo/keel/semconv" ) // With returns a child of l with fields added. A nil l selects [Logger]. @@ -40,10 +41,14 @@ func WithAttributes(l *zap.Logger, attrs ...attribute.KeyValue) *zap.Logger { return l.With(fields...) } -// WithError returns a child of l with the error type and message of err. -// It panics if err is nil. +// WithError returns a child of l with the semconv error type and exception +// message of err. A nil err adds nothing. func WithError(l *zap.Logger, err error) *zap.Logger { - return With(l, FErrorType(err), FError(err)) + if err == nil { + return With(l) + } + + return WithAttributes(l, foomosemconv.ErrorType(err), semconv.ExceptionMessage(err.Error())) } // WithServiceName returns a child of l with the semconv service name. @@ -55,7 +60,10 @@ func WithServiceName(l *zap.Logger, name string) *zap.Logger { // in ctx, or l unchanged if that span is invalid or not sampled. func WithTraceID(l *zap.Logger, ctx context.Context) *zap.Logger { if spanCtx := trace.SpanContextFromContext(ctx); spanCtx.IsValid() && spanCtx.IsSampled() { - l = With(l, FTraceID(spanCtx.TraceID().String()), FSpanID(spanCtx.SpanID().String())) + l = WithAttributes(l, + keelsemconv.TraceID(spanCtx.TraceID().String()), + keelsemconv.SpanID(spanCtx.SpanID().String()), + ) } return l @@ -98,9 +106,9 @@ func WithHTTPSessionID(l *zap.Logger, r *http.Request) *zap.Logger { // X-Request-ID header or the request context, or l unchanged if none is set. func WithHTTPRequestID(l *zap.Logger, r *http.Request) *zap.Logger { if id := r.Header.Get("X-Request-ID"); id != "" { - return With(l, Attribute(foomosemconv.HTTPXRequestID(id))) + return WithAttributes(l, semconv.HTTPRequestHeader("x-request-id", id)) } else if id, ok := keelhttpcontext.GetRequestID(r.Context()); ok && id != "" { - return With(l, Attribute(foomosemconv.HTTPXRequestID(id))) + return WithAttributes(l, semconv.HTTPRequestHeader("x-request-id", id)) } else { return l } @@ -110,9 +118,9 @@ func WithHTTPRequestID(l *zap.Logger, r *http.Request) *zap.Logger { // or Referer header, or l unchanged if none is set. func WithHTTPReferer(l *zap.Logger, r *http.Request) *zap.Logger { if value := r.Header.Get("X-Referer"); value != "" { - return With(l, Attribute(foomosemconv.HTTPXRequestReferer(value))) + return WithAttributes(l, semconv.HTTPRequestHeader("referer", value)) } else if value := r.Referer(); value != "" { - return With(l, Attribute(foomosemconv.HTTPXRequestReferer(value))) + return WithAttributes(l, semconv.HTTPRequestHeader("referer", value)) } else { return l } diff --git a/net/http/middleware/logger.go b/net/http/middleware/logger.go index 3988511f..564009be 100644 --- a/net/http/middleware/logger.go +++ b/net/http/middleware/logger.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" "github.com/foomo/keel/log" + keelsemconv "github.com/foomo/keel/semconv" ) type ( @@ -112,10 +113,10 @@ func LoggerWithOptions(opts LoggerOptions) keelhttp.Middleware { next.ServeHTTP(wr, r) - l = l.With( - log.FDuration(time.Since(start)), - log.Attribute(semconv.HTTPResponseStatusCode(wr.StatusCode())), - log.Attribute(semconv.HTTPResponseSize(wr.Size())), + l = log.WithAttributes(l, + keelsemconv.Duration(time.Since(start)), + semconv.HTTPResponseStatusCode(wr.StatusCode()), + semconv.HTTPResponseSize(wr.Size()), ) if labeler != nil { @@ -123,7 +124,7 @@ func LoggerWithOptions(opts LoggerOptions) keelhttp.Middleware { } if err := r.Context().Err(); err != nil { - l = l.With(zap.String("error_context", err.Error())) + l = log.WithAttributes(l, keelsemconv.ContextError(err)) } switch { diff --git a/net/http/middleware/rbac.go b/net/http/middleware/rbac.go index e34f5fb5..a7d9d65f 100644 --- a/net/http/middleware/rbac.go +++ b/net/http/middleware/rbac.go @@ -3,8 +3,12 @@ package middleware import ( "net/http" + "github.com/foomo/keel/log" keelhttp "github.com/foomo/keel/net/http" "github.com/foomo/keel/net/http/rbac" + keelsemconv "github.com/foomo/keel/semconv" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) @@ -57,17 +61,17 @@ func RBAC(m *rbac.Matcher, extract rbac.RolesExtractor) keelhttp.Middleware { } if l != nil { - fields := []zap.Field{ - zap.String("path", r.URL.Path), - zap.String("outcome", string(d.Outcome)), - zap.Bool("authenticated", d.Authed), - zap.Strings("roles", d.Roles), + attrs := []attribute.KeyValue{ + semconv.URLPath(r.URL.Path), + keelsemconv.KeelRBACOutcome(string(d.Outcome)), + keelsemconv.KeelRBACAuthenticated(d.Authed), + keelsemconv.KeelRBACRoles(d.Roles), } if d.Rule != nil { - fields = append(fields, zap.String("rule_path", d.Rule.Raw.Path)) + attrs = append(attrs, keelsemconv.KeelRBACRulePath(d.Rule.Raw.Path)) } - l.Warn("rbac denied request", fields...) + l.Warn("rbac denied request", log.Attributes(attrs...)...) } status := http.StatusForbidden diff --git a/net/http/middleware/recover.go b/net/http/middleware/recover.go index d25d32e1..b1766f47 100644 --- a/net/http/middleware/recover.go +++ b/net/http/middleware/recover.go @@ -9,8 +9,11 @@ import ( "go.opentelemetry.io/otel/trace" "go.uber.org/zap" + "runtime/debug" + "github.com/foomo/keel/log" httputils "github.com/foomo/keel/utils/net/http" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) type ( @@ -74,7 +77,7 @@ func RecoverWithOptions(opts RecoverOptions) keelhttp.Middleware { ll := log.WithError(l, err) if !opts.DisablePrintStack { - ll = ll.With(log.FStackSkip(3)) + ll = ll.With(log.Attribute(semconv.ExceptionStacktrace(string(debug.Stack())))) } httputils.InternalServerError(ll, w, r, errors.Wrap(err, "recovering from panic")) diff --git a/net/http/middleware/responsecontroller.go b/net/http/middleware/responsecontroller.go index 42cb58f6..3b73afb7 100644 --- a/net/http/middleware/responsecontroller.go +++ b/net/http/middleware/responsecontroller.go @@ -38,7 +38,7 @@ func ResponseController(h func(rc *http.ResponseController) error) keelhttp.Midd return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if h != nil { if err := h(http.NewResponseController(w)); err != nil { - l.Warn("failed to apply response controller", log.FError(err)) + log.WithError(l, err).Warn("failed to apply response controller") } } diff --git a/net/http/middleware/responsetime.go b/net/http/middleware/responsetime.go index 8ac430c1..d7dc24c2 100644 --- a/net/http/middleware/responsetime.go +++ b/net/http/middleware/responsetime.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" "github.com/foomo/keel/log" + keelsemconv "github.com/foomo/keel/semconv" ) type ( @@ -92,7 +93,7 @@ func ResponseTimeWithOptions(opts ResponseTimeOptions) keelhttp.Middleware { duration := time.Since(start) if opts.MaxDuration > 0 && duration > opts.MaxDuration { - l.Warn(opts.MaxDurationMessage, log.FDuration(opts.MaxDuration), log.FValue(duration.Microseconds())) + l.Warn(opts.MaxDurationMessage, log.Attributes(keelsemconv.MaxDuration(opts.MaxDuration), keelsemconv.Duration(duration))...) } }) } diff --git a/net/http/middleware/telemetry.go b/net/http/middleware/telemetry.go index a42eb6f6..471103d8 100644 --- a/net/http/middleware/telemetry.go +++ b/net/http/middleware/telemetry.go @@ -6,6 +6,7 @@ import ( "github.com/foomo/keel/log" keelhttp "github.com/foomo/keel/net/http" httplog "github.com/foomo/keel/net/http/log" + keelsemconv "github.com/foomo/keel/semconv" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/propagation" @@ -95,8 +96,8 @@ func TelemetryWithOptions(opts TelemetryOptions) keelhttp.Middleware { if labeler, ok := httplog.LabelerFromRequest(r); ok { if spanCtx := trace.SpanContextFromContext(r.Context()); spanCtx.IsValid() && spanCtx.IsSampled() { - labeler.Add(log.FTraceID(spanCtx.TraceID().String())) - labeler.Add(log.FSpanID(spanCtx.SpanID().String())) + labeler.Add(log.Attribute(keelsemconv.TraceID(spanCtx.TraceID().String()))) + labeler.Add(log.Attribute(keelsemconv.SpanID(spanCtx.SpanID().String()))) } } diff --git a/net/http/rbac/helper_test.go b/net/http/rbac/helper_test.go index 077922c9..48c962c7 100644 --- a/net/http/rbac/helper_test.go +++ b/net/http/rbac/helper_test.go @@ -106,7 +106,7 @@ func TestRBAC_outcomes(t *testing.T) { entry := logs.All()[0] assert.Equal(t, "rbac denied request", entry.Message) assert.Equal(t, zap.WarnLevel, entry.Level) - assert.Equal(t, tc.path, entry.ContextMap()["path"]) + assert.Equal(t, tc.path, entry.ContextMap()["url_path"]) } else { assert.Equal(t, 0, logs.Len(), "expected no log entries") } @@ -204,7 +204,7 @@ func TestRBAC_pathMatching(t *testing.T) { if tc.wantRule != "" { require.Equal(t, 1, logs.Len(), "expected one deny log entry") - assert.Equal(t, tc.wantRule, logs.All()[0].ContextMap()["rule_path"]) + assert.Equal(t, tc.wantRule, logs.All()[0].ContextMap()["keel_rbac_rule_path"]) } }) } diff --git a/net/http/roundtripware/circuitbreaker.go b/net/http/roundtripware/circuitbreaker.go index 2761b7fd..50cc3c2a 100644 --- a/net/http/roundtripware/circuitbreaker.go +++ b/net/http/roundtripware/circuitbreaker.go @@ -6,6 +6,9 @@ import ( "time" "github.com/foomo/keel/log" + keelsemconv "github.com/foomo/keel/semconv" + foomosemconv "github.com/foomo/opentelemetry-go/semconv" + "github.com/foomo/opentelemetry-go/semconv/circuitbreakerconv" "github.com/sony/gobreaker" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -159,24 +162,23 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R toState := circuitBreaker.State() if fromState != toState { l.Warn("state change occurred", - zap.String("state_from", fromState.String()), - zap.String("state_to", toState.String()), + log.Attributes( + keelsemconv.KeelCircuitBreakerPreviousState(string(circuitBreakerState(fromState))), + foomosemconv.CircuitBreakerState(string(circuitBreakerState(toState))), + )..., ) } - attributes := []attribute.KeyValue{ - attribute.String("current_state", toState.String()), - attribute.String("previous_state", fromState.String()), - attribute.Bool("state_change", fromState != toState), - } - - if err != nil { - if o.Counter != nil { - attributes := append(attributes, attribute.Bool("error", true)) - o.Counter.Add(r.Context(), 1, metric.WithAttributes(attributes...)) + if o.Counter != nil { + attributes := []attribute.KeyValue{ + foomosemconv.CircuitBreakerState(string(circuitBreakerState(toState))), + keelsemconv.KeelCircuitBreakerPreviousState(string(circuitBreakerState(fromState))), + keelsemconv.KeelCircuitBreakerStateChange(fromState != toState), } - } else if o.Counter != nil { - attributes := append(attributes, attribute.Bool("error", false)) + if err != nil { + attributes = append(attributes, foomosemconv.ErrorType(err)) + } + o.Counter.Add(r.Context(), 1, metric.WithAttributes(attributes...)) } }() @@ -186,7 +188,7 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R reqCopy, errCopy := copyRequest(r, o.CopyReqBody) if errCopy != nil { - l.Error("unable to copy request", log.FError(errCopy)) + log.WithError(l, errCopy).Error("unable to copy request") return nil, errCopy } else if o.CopyReqBody && reqCopy.Body != nil { // make sure the body is closed again - since it is a NopCloser it does not make a difference though @@ -200,9 +202,8 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R if errors.Is(err, gobreaker.ErrTooManyRequests) || errors.Is(err, gobreaker.ErrOpenState) { return nil, errors.Join(ErrCircuitBreaker, err) } else if err != nil { - l.Error("unexpected error in circuit breaker", - log.FError(err), - zap.String("state", fromState.String()), + log.WithError(l, err).Error("unexpected error in circuit breaker", + log.Attribute(foomosemconv.CircuitBreakerState(string(circuitBreakerState(fromState)))), ) return nil, err @@ -216,7 +217,7 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R // clone the response and the body if wanted respCopy, errCopy = copyResponse(resp, o.CopyRespBody) if errCopy != nil { - l.Error("unable to copy response", log.FError(errCopy)) + log.WithError(l, errCopy).Error("unable to copy response") return nil, errCopy } else if o.CopyRespBody && respCopy.Body != nil { // make sure the body is closed again - since it is a NopCloser it does not make a difference though @@ -225,10 +226,7 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R } if errSuccess := o.IsSuccessful(err, reqCopy, respCopy); errors.Is(errSuccess, errNoBody) { - l.Error("encountered read from not previously copied request/response body", - zap.Bool("copy_request", o.CopyReqBody), - zap.Bool("copy_response", o.CopyRespBody), - ) + l.Error("encountered read from not previously copied request/response body") // we actually want to return an error instead of the original request and error since the user // should be made aware that there is a misconfiguration return nil, ErrReadFromActualBody @@ -241,3 +239,16 @@ func CircuitBreaker(set *CircuitBreakerSettings, opts ...CircuitBreakerOption) R } } } + +// circuitBreakerState maps a gobreaker state to the circuit_breaker.state +// enum value. +func circuitBreakerState(s gobreaker.State) circuitbreakerconv.StateAttr { + switch s { + case gobreaker.StateOpen: + return circuitbreakerconv.StateOpen + case gobreaker.StateHalfOpen: + return circuitbreakerconv.StateHalfOpen + default: + return circuitbreakerconv.StateClosed + } +} diff --git a/net/http/roundtripware/logger.go b/net/http/roundtripware/logger.go index 061fcd94..56af56e7 100644 --- a/net/http/roundtripware/logger.go +++ b/net/http/roundtripware/logger.go @@ -9,6 +9,7 @@ import ( "go.uber.org/zap" "github.com/foomo/keel/log" + keelsemconv "github.com/foomo/keel/semconv" ) type ( @@ -109,7 +110,7 @@ func Logger(opts ...LoggerOption) RoundTripware { statusCode = resp.StatusCode } - l = l.With(log.FDuration(time.Since(start))) + l = log.WithAttributes(l, keelsemconv.Duration(time.Since(start))) switch { case err != nil: diff --git a/net/http/roundtripware/metric.go b/net/http/roundtripware/metric.go index 1743c516..299687c5 100644 --- a/net/http/roundtripware/metric.go +++ b/net/http/roundtripware/metric.go @@ -4,8 +4,8 @@ import ( "net/http" "time" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) @@ -41,10 +41,10 @@ func Metric(meter metric.Meter, name, description string) RoundTripware { return resp, err } - attributes := append(labeler.Get(), attribute.String("method", r.Method)) + attributes := append(labeler.Get(), semconv.HTTPRequestMethodKey.String(r.Method)) if resp != nil { - attributes = append(attributes, attribute.Int("status_code", resp.StatusCode)) + attributes = append(attributes, semconv.HTTPResponseStatusCode(resp.StatusCode)) } histogram.Record(ctx, duration.Seconds(), metric.WithAttributes(attributes...)) diff --git a/net/http/roundtripware/recover.go b/net/http/roundtripware/recover.go index 639925df..d9ae08ab 100644 --- a/net/http/roundtripware/recover.go +++ b/net/http/roundtripware/recover.go @@ -3,11 +3,13 @@ package roundtripware import ( "fmt" "net/http" + "runtime/debug" "go.opentelemetry.io/otel/trace" "go.uber.org/zap" "github.com/foomo/keel/log" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" ) type ( @@ -68,7 +70,7 @@ func RecoverWithOptions(opts RecoverOptions) RoundTripware { ll := log.WithError(l, err) if !opts.DisablePrintStack { - ll = ll.With(log.FStackSkip(3)) + ll = log.WithAttributes(ll, semconv.ExceptionStacktrace(string(debug.Stack()))) } ll.Error("recovering from panic") diff --git a/net/stream/jetstream/stream.go b/net/stream/jetstream/stream.go index c2a55833..af4c44c2 100644 --- a/net/stream/jetstream/stream.go +++ b/net/stream/jetstream/stream.go @@ -2,15 +2,19 @@ package jetstream import ( "encoding/json" + "net/url" "slices" + "strconv" "time" "github.com/nats-io/nats.go" "github.com/pkg/errors" + "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "github.com/foomo/keel/log" + foomosemconv "github.com/foomo/opentelemetry-go/semconv" ) type ( @@ -147,9 +151,9 @@ func SubscriberWithUnmarshal(unmarshal UnmarshalFn) SubscriberOption { // reconnect options. It returns an error if the initial connect fails. func New(l *zap.Logger, name, addr string, opts ...Option) (*Stream, error) { stream := &Stream{ - l: l.With( - log.Attribute(semconv.MessagingSystemKey.String("jetstream")), - log.FName(name), + l: log.WithAttributes(l, + foomosemconv.MessagingSystemNats, + foomosemconv.MessagingNATSStream(name), ), name: name, addr: addr, @@ -274,6 +278,34 @@ func (s *Stream) Close() { s.conn.Close() } +// serverAttrs returns OTel semconv attributes describing the server at +// rawURL. Returns nil if rawURL is empty or cannot be parsed. +func serverAttrs(rawURL string) []attribute.KeyValue { + addr, port := serverAddrPort(rawURL) + if addr == "" { + return nil + } + + return []attribute.KeyValue{ + semconv.ServerAddress(addr), + semconv.ServerPort(port), + semconv.NetworkProtocolName("jetstream"), + } +} + +// serverAddrPort extracts just the address and port from rawURL. +// Returns zero values if parsing fails. +func serverAddrPort(rawURL string) (string, int) { + u, err := url.Parse(rawURL) + if err != nil { + return "", 0 + } + + port, _ := strconv.Atoi(u.Port()) + + return u.Hostname(), port +} + // connect establishes the NATS connection and JetStream context and creates // or updates the stream if a config is set. func (s *Stream) connect() error { @@ -283,14 +315,15 @@ func (s *Stream) connect() error { return errors.Wrap(err, "failed to connect to nats addr "+s.addr) } - s.l.Info("nats connected", zap.String("addr", s.addr)) + l := log.WithAttributes(s.l, serverAttrs(conn.ConnectedUrlRedacted())...) + l.Debug("jetstream connected") // create jet stream js, err := conn.JetStream( append( []nats.JSOpt{ nats.PublishAsyncErrHandler(func(js nats.JetStream, msg *nats.Msg, err error) { - s.l.Error("nats async publish error", log.FError(err)) + log.WithError(l, err).Error("nats async publish error") }), }, s.jsOptions..., @@ -300,7 +333,7 @@ func (s *Stream) connect() error { return err } - s.l.Info("jetstream created", zap.String("namespace", s.namespace)) + l.Debug("jetstream created") // create / update stream if config exists if s.config != nil { @@ -308,8 +341,6 @@ func (s *Stream) connect() error { if _, err = js.StreamInfo(s.Name(), s.configJSOptions...); errors.Is(err, nats.ErrStreamNotFound) { if info, err := js.AddStream(s.config, s.configJSOptions...); err != nil { return errors.Wrap(err, "failed to add stream") - } else if err != nil { - return errors.Wrap(err, "failed to retrieve stream info") } else { s.info = info } @@ -322,7 +353,7 @@ func (s *Stream) connect() error { } } - s.l.Info("jetstream configured") + l.Info("jetstream configured") s.js = js s.conn = conn @@ -335,15 +366,14 @@ func (s *Stream) connect() error { func (s *Stream) initNatsOptions() { natsOpts := append([]nats.Option{ nats.ErrorHandler(func(conn *nats.Conn, subscription *nats.Subscription, err error) { - s.l.Error("nats error", - log.FError(err), + log.WithError(s.l, err).Error("nats error", log.Attribute(semconv.MessagingDestinationName(subscription.Queue)), log.Attribute(semconv.MessagingDestinationSubscriptionName(subscription.Subject)), ) }), nats.ClosedHandler(func(conn *nats.Conn) { if err := conn.LastError(); err != nil { - s.l.Error("nats closed", log.FError(err)) + log.WithError(s.l, err).Error("nats closed") } else { s.l.Info("nats closed") } @@ -353,13 +383,13 @@ func (s *Stream) initNatsOptions() { }), nats.DisconnectErrHandler(func(conn *nats.Conn, err error) { if err != nil { - s.l.Error("nats disconnected error", log.FError(err)) + log.WithError(s.l, err).Error("nats disconnected error") var errRetry error for range s.reconnectMaxRetries { errRetry = s.connect() if errRetry != nil { - s.l.Error("nats reconnect failed", log.FError(errRetry)) + log.WithError(s.l, errRetry).Error("nats reconnect failed") time.Sleep(s.reconnectTimeout) } else { break diff --git a/net/stream/jetstream/subscriber.go b/net/stream/jetstream/subscriber.go index 28ed550b..1a9cc8f8 100644 --- a/net/stream/jetstream/subscriber.go +++ b/net/stream/jetstream/subscriber.go @@ -81,5 +81,5 @@ func (s *Subscriber) Unmarshal(msg *nats.Msg, v any) error { // errorHandler logs a handler error. func (s *Subscriber) errorHandler(err error) { - s.stream.l.Error("failed to handle message", log.FError(err)) + log.WithError(s.stream.l, err).Error("failed to handle message") } diff --git a/semconv/circuitbreaker.go b/semconv/circuitbreaker.go new file mode 100644 index 00000000..e40837cf --- /dev/null +++ b/semconv/circuitbreaker.go @@ -0,0 +1,22 @@ +package semconv + +import ( + "go.opentelemetry.io/otel/attribute" +) + +const ( + // CircuitBreakerPreviousStateKey is the key for circuit_breaker.previous_state. + CircuitBreakerPreviousStateKey = attribute.Key("circuit_breaker.previous_state") + // CircuitBreakerStateChangeKey is the key for circuit_breaker.state_change. + CircuitBreakerStateChangeKey = attribute.Key("circuit_breaker.state_change") +) + +// KeelCircuitBreakerPreviousState returns a new attribute.KeyValue for circuit_breaker.previous_state. +func KeelCircuitBreakerPreviousState(v string) attribute.KeyValue { + return CircuitBreakerPreviousStateKey.String(v) +} + +// KeelCircuitBreakerStateChange returns a new attribute.KeyValue for circuit_breaker.state_change. +func KeelCircuitBreakerStateChange(v bool) attribute.KeyValue { + return CircuitBreakerStateChangeKey.Bool(v) +} diff --git a/semconv/debug.go b/semconv/debug.go index 38527aa5..a44f2a6b 100644 --- a/semconv/debug.go +++ b/semconv/debug.go @@ -6,10 +6,14 @@ import ( const ( // DebugEnabledKey is the key for debug.enabled. + // + // Deprecated: use foomosemconv.SpanSamplingPriorityKey. DebugEnabledKey = attribute.Key("debug.enabled") ) // DebugEnabled returns a new attribute.KeyValue for debug.enabled. +// +// Deprecated: use foomosemconv.SetSpanSamplingPriority. func DebugEnabled(v bool) attribute.KeyValue { return DebugEnabledKey.Bool(v) } diff --git a/semconv/doc.go b/semconv/doc.go index 9c5c7568..1f440e47 100644 --- a/semconv/doc.go +++ b/semconv/doc.go @@ -5,5 +5,10 @@ // [go.opentelemetry.io/otel/attribute.Key] constant (for example // [KeelServiceNameKey]) and a constructor returning a // [go.opentelemetry.io/otel/attribute.KeyValue] (for example -// [KeelServiceName]). Attributes cover keel services and debug state. +// [KeelServiceName]). Attributes cover keel services, jobs, closers, circuit +// breakers, RBAC, streams, debug state and generic values (name, +// value, duration, trace and span ids) that replace the legacy log fields. +// Use them with log.Attribute and log.Attributes to share keys between logs, +// metrics and traces. Prefer upstream go.opentelemetry.io/otel/semconv keys +// where they exist. package semconv diff --git a/semconv/generic.go b/semconv/generic.go new file mode 100644 index 00000000..9ebb994a --- /dev/null +++ b/semconv/generic.go @@ -0,0 +1,69 @@ +package semconv + +import ( + "fmt" + "time" + + "go.opentelemetry.io/otel/attribute" +) + +const ( + // NameKey is the key for name. + NameKey = attribute.Key("name") + // ValueKey is the key for value. + ValueKey = attribute.Key("value") + // NumKey is the key for num. + NumKey = attribute.Key("num") + // DurationKey is the key for duration, in seconds. + DurationKey = attribute.Key("duration") + // MaxDurationKey is the key for max_duration, in seconds. + MaxDurationKey = attribute.Key("max_duration") + // TraceIDKey is the key for trace.id. + TraceIDKey = attribute.Key("trace.id") + // SpanIDKey is the key for span.id. + SpanIDKey = attribute.Key("span.id") + // ContextErrorKey is the key for context.error. + ContextErrorKey = attribute.Key("context.error") +) + +// Name returns a new attribute.KeyValue for name. +func Name(v string) attribute.KeyValue { + return NameKey.String(v) +} + +// Value returns a new attribute.KeyValue for value with v formatted +// by %v. +func Value(v any) attribute.KeyValue { + return ValueKey.String(fmt.Sprintf("%v", v)) +} + +// Num returns a new attribute.KeyValue for num. +func Num(v int) attribute.KeyValue { + return NumKey.Int(v) +} + +// Duration returns a new attribute.KeyValue for duration in seconds. +func Duration(v time.Duration) attribute.KeyValue { + return DurationKey.Float64(v.Seconds()) +} + +// MaxDuration returns a new attribute.KeyValue for max_duration in seconds. +func MaxDuration(v time.Duration) attribute.KeyValue { + return MaxDurationKey.Float64(v.Seconds()) +} + +// TraceID returns a new attribute.KeyValue for trace.id. +func TraceID(v string) attribute.KeyValue { + return TraceIDKey.String(v) +} + +// SpanID returns a new attribute.KeyValue for span.id. +func SpanID(v string) attribute.KeyValue { + return SpanIDKey.String(v) +} + +// ContextError returns a new attribute.KeyValue for context.error +// with the message of the given context error. +func ContextError(err error) attribute.KeyValue { + return ContextErrorKey.String(err.Error()) +} diff --git a/semconv/keel.go b/semconv/keel.go index 64c4113b..f93ede63 100644 --- a/semconv/keel.go +++ b/semconv/keel.go @@ -1,6 +1,9 @@ package semconv import ( + "fmt" + "time" + "go.opentelemetry.io/otel/attribute" ) @@ -11,11 +14,55 @@ const ( KeelServiceNameKey = attribute.Key("keel.service.name") // KeelServiceInstKey is the key for keel.service.inst. KeelServiceInstKey = attribute.Key("keel.service.inst") + // KeelGracefulPeriodKey is the key for keel.graceful_period, in seconds. + KeelGracefulPeriodKey = attribute.Key("keel.graceful_period") + // KeelJobStepKey is the key for keel.job.step. + KeelJobStepKey = attribute.Key("keel.job.step") + // KeelCloserTypeKey is the key for keel.closer.type. + KeelCloserTypeKey = attribute.Key("keel.closer.type") + // KeelHealthzProbeTypeKey is the key for keel.healthz.probe.type. + KeelHealthzProbeTypeKey = attribute.Key("keel.healthz.probe.type") +) + +// KeelGracefulPeriod returns a new attribute.KeyValue for keel.graceful_period in seconds. +func KeelGracefulPeriod(v time.Duration) attribute.KeyValue { + return KeelGracefulPeriodKey.Float64(v.Seconds()) +} + +// KeelJobStep returns a new attribute.KeyValue for keel.job.step. +func KeelJobStep(v string) attribute.KeyValue { + return KeelJobStepKey.String(v) +} + +// KeelCloserType returns a new attribute.KeyValue for keel.closer.type with +// the Go type name of closer. +func KeelCloserType(closer any) attribute.KeyValue { + return KeelCloserTypeKey.String(fmt.Sprintf("%T", closer)) +} + +// KeelHealthzProbeType returns a new attribute.KeyValue for +// keel.healthz.probe.type with the Go type name of probe. +func KeelHealthzProbeType(probe any) attribute.KeyValue { + return KeelHealthzProbeTypeKey.String(fmt.Sprintf("%T", probe)) +} + +// KeelServiceTypeAttr is an enum value of keel.service.type. +type KeelServiceTypeAttr string + +const ( + // KeelServiceTypeHTTP is the keel.service.type of an HTTP service. + KeelServiceTypeHTTP KeelServiceTypeAttr = "http" + // KeelServiceTypeGoRoutine is the keel.service.type of a goroutine service. + KeelServiceTypeGoRoutine KeelServiceTypeAttr = "goroutine" + // KeelServiceTypeSubscription is the keel.service.type of a subscription service. + KeelServiceTypeSubscription KeelServiceTypeAttr = "sub" + // KeelServiceTypeJob is the keel.service.type of a job. + KeelServiceTypeJob KeelServiceTypeAttr = "job" ) // KeelServiceType returns a new attribute.KeyValue for keel.service.type. -func KeelServiceType(v string) attribute.KeyValue { - return KeelServiceTypeKey.String(v) +func KeelServiceType(v KeelServiceTypeAttr) attribute.KeyValue { + return KeelServiceTypeKey.String(string(v)) } // KeelServiceName returns a new attribute.KeyValue for keel.service.name. diff --git a/semconv/rbac.go b/semconv/rbac.go new file mode 100644 index 00000000..dcbafa95 --- /dev/null +++ b/semconv/rbac.go @@ -0,0 +1,36 @@ +package semconv + +import ( + "go.opentelemetry.io/otel/attribute" +) + +const ( + // KeelRBACOutcomeKey is the key for keel.rbac.outcome. + KeelRBACOutcomeKey = attribute.Key("keel.rbac.outcome") + // KeelRBACAuthenticatedKey is the key for keel.rbac.authenticated. + KeelRBACAuthenticatedKey = attribute.Key("keel.rbac.authenticated") + // KeelRBACRolesKey is the key for keel.rbac.roles. + KeelRBACRolesKey = attribute.Key("keel.rbac.roles") + // KeelRBACRulePathKey is the key for keel.rbac.rule.path. + KeelRBACRulePathKey = attribute.Key("keel.rbac.rule.path") +) + +// KeelRBACOutcome returns a new attribute.KeyValue for keel.rbac.outcome. +func KeelRBACOutcome(v string) attribute.KeyValue { + return KeelRBACOutcomeKey.String(v) +} + +// KeelRBACAuthenticated returns a new attribute.KeyValue for keel.rbac.authenticated. +func KeelRBACAuthenticated(v bool) attribute.KeyValue { + return KeelRBACAuthenticatedKey.Bool(v) +} + +// KeelRBACRoles returns a new attribute.KeyValue for keel.rbac.roles. +func KeelRBACRoles(v []string) attribute.KeyValue { + return KeelRBACRolesKey.StringSlice(v) +} + +// KeelRBACRulePath returns a new attribute.KeyValue for keel.rbac.rule.path. +func KeelRBACRulePath(v string) attribute.KeyValue { + return KeelRBACRulePathKey.String(v) +} diff --git a/server.go b/server.go index 1a54d7dc..e740f56f 100644 --- a/server.go +++ b/server.go @@ -3,7 +3,6 @@ package keel import ( "context" "errors" - "fmt" "net/http" "os" "os/signal" @@ -22,6 +21,7 @@ import ( "github.com/foomo/keel/log" "github.com/foomo/keel/markdown" keelhttp "github.com/foomo/keel/net/http" + keelsemconv "github.com/foomo/keel/semconv" "github.com/foomo/keel/service" "github.com/foomo/keel/telemetry" "github.com/go-logr/logr" @@ -119,7 +119,7 @@ func NewServer(opts ...Option) *Server { defer timeoutCancel() inst.l.Info("keel closer closed", - zap.Duration("graceful_period", inst.gracefulPeriod), + log.Attribute(keelsemconv.KeelGracefulPeriod(inst.gracefulPeriod)), ) // append internal closers @@ -257,7 +257,7 @@ func (s *Server) AddServices(services ...Service) { // [IsCloser]; adding the same closer twice is a no-op. func (s *Server) AddCloser(closer any) { if !IsCloser(closer) { - s.l.Warn("unable to add closer", log.FValue(fmt.Sprintf("%T", closer))) + s.l.Warn("unable to add closer", log.Attribute(keelsemconv.KeelCloserType(closer))) } if slices.Contains(s.closers(), closer) { @@ -296,7 +296,7 @@ func (s *Server) AddHealthzer(typ healthz.Type, probe any) { if IsHealthz(probe) { s.addProbes(typ, probe) } else { - s.l.Debug("not a healthz probe", log.FValue(fmt.Sprintf("%T", probe))) + s.l.Debug("not a healthz probe", log.Attribute(keelsemconv.KeelHealthzProbeType(probe))) } } diff --git a/service/goroutine.go b/service/goroutine.go index b327acc0..1ddd03fd 100644 --- a/service/goroutine.go +++ b/service/goroutine.go @@ -42,7 +42,7 @@ func NewGoRoutine(l *zap.Logger, name string, handler GoRoutineFn, opts ...GoRou } // enrich the log l = log.WithAttributes(l, - keelsemconv.KeelServiceType("goroutine"), + keelsemconv.KeelServiceType(keelsemconv.KeelServiceTypeGoRoutine), keelsemconv.KeelServiceName(name), ) diff --git a/service/goroutine_test.go b/service/goroutine_test.go index 72ef4537..f21a0b1c 100644 --- a/service/goroutine_test.go +++ b/service/goroutine_test.go @@ -47,14 +47,14 @@ func ExampleNewGoRoutine() { // {"level":"info","msg":"starting keel service","keel_service_type":"goroutine","keel_service_name":"demo"} // {"level":"info","msg":"ping","keel_service_type":"goroutine","keel_service_name":"demo","keel_service_inst":0} // {"level":"info","msg":"ping","keel_service_type":"goroutine","keel_service_name":"demo","keel_service_inst":0} - // {"level":"info","msg":"keel closer closed","graceful_period":"3s"} + // {"level":"info","msg":"keel closer closed","keel_graceful_period":3} // {"level":"info","msg":"keel closer closed: closers"} // {"level":"info","msg":"stopping keel service","keel_service_type":"goroutine","keel_service_name":"demo"} // {"level":"info","msg":"context has been canceled du to graceful shutdown","keel_service_type":"goroutine","keel_service_name":"demo","keel_service_inst":0} - // {"level":"debug","msg":"keel closer closed","name":"*service.GoRoutine"} - // {"level":"debug","msg":"keel closer closed","name":"noop.TracerProvider"} - // {"level":"debug","msg":"keel closer closed","name":"noop.MeterProvider"} - // {"level":"debug","msg":"keel closer closed","name":"noop.LoggerProvider"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"*service.GoRoutine"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"noop.TracerProvider"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"noop.MeterProvider"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"noop.LoggerProvider"} // {"level":"info","msg":"keel closer closed: complete"} // {"level":"info","msg":"keel server stopped"} } diff --git a/service/http.go b/service/http.go index 06fe069e..89c3a6f9 100644 --- a/service/http.go +++ b/service/http.go @@ -38,7 +38,7 @@ func NewHTTP(l *zap.Logger, name, addr string, handler http.Handler, middlewares } // enrich the log l = log.WithAttributes(l, - keelsemconv.KeelServiceType("http"), + keelsemconv.KeelServiceType(keelsemconv.KeelServiceTypeHTTP), keelsemconv.KeelServiceName(name), ) diff --git a/service/http_test.go b/service/http_test.go index 44fd37a7..7e665f85 100644 --- a/service/http_test.go +++ b/service/http_test.go @@ -89,13 +89,13 @@ func _ExampleNewHTTP() { // {"level":"info","msg":"starting keel server"} // {"level":"info","msg":"starting keel service","keel_service_type":"http","keel_service_name":"demo","net_host_ip":"localhost","net_host_port":"8080"} // {"level":"info","msg":"OK"} - // {"level":"info","msg":"keel closer closed","graceful_period":"10s"} + // {"level":"info","msg":"keel closer closed","keel_graceful_period":10} // {"level":"info","msg":"keel closer closed: closers"} // {"level":"info","msg":"stopping keel service","keel_service_type":"http","keel_service_name":"demo"} - // {"level":"debug","msg":"keel closer closed","name":"*service.HTTP"} - // {"level":"debug","msg":"keel closer closed","name":"noop.TracerProvider"} - // {"level":"debug","msg":"keel closer closed","name":"noop.MeterProvider"} - // {"level":"debug","msg":"keel closer closed","name":"noop.LoggerProvider"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"*service.HTTP"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"noop.TracerProvider"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"noop.MeterProvider"} + // {"level":"debug","msg":"keel closer closed","keel_closer_type":"noop.LoggerProvider"} // {"level":"info","msg":"keel closer closed: complete"} // {"level":"info","msg":"keel server stopped"} } diff --git a/service/subscription.go b/service/subscription.go index 406b54cf..19bc9f62 100644 --- a/service/subscription.go +++ b/service/subscription.go @@ -9,6 +9,7 @@ import ( "github.com/foomo/goflux" "github.com/foomo/keel/log" keelsemconv "github.com/foomo/keel/semconv" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) @@ -46,7 +47,7 @@ func NewSubscription[T any]( } l = log.WithAttributes(l, - keelsemconv.KeelServiceType("sub"), + keelsemconv.KeelServiceType(keelsemconv.KeelServiceTypeSubscription), keelsemconv.KeelServiceName(name), ) @@ -102,7 +103,7 @@ func (s *Subscription[T]) Start(ctx context.Context) error { l := s.l s.wg.Go(func() error { - l.Info("subscribing", zap.String("subject", s.subject)) + l.Info("subscribing", log.Attribute(semconv.MessagingDestinationName(s.subject))) return s.subscriber.Subscribe(ctx, s.subject, s.handler) }) @@ -126,7 +127,7 @@ func (s *Subscription[T]) Close(ctx context.Context) error { s.cancelLock.Unlock() if err := s.subscriber.Close(); err != nil { - s.l.Warn("subscriber close failed", zap.Error(err)) + log.WithError(s.l, err).Warn("subscriber close failed") } return s.wg.Wait() diff --git a/serviceenabler.go b/serviceenabler.go index ff975c0d..1d89f83f 100644 --- a/serviceenabler.go +++ b/serviceenabler.go @@ -159,12 +159,12 @@ func (w *ServiceEnabler) watch(ctx context.Context) { if value { go func() { if err := w.enable(ctx); err != nil { - w.l.Fatal("failed to dynamically start service", log.FError(err)) + log.WithError(w.l, err).Fatal("failed to dynamically start service") } }() } else { if err := w.disable(context.TODO()); err != nil { //nolint:contextcheck - w.l.Fatal("failed to dynamically close service", log.FError(err)) + log.WithError(w.l, err).Fatal("failed to dynamically close service") } } } diff --git a/telemetry/context.go b/telemetry/context.go index 4c0f5d5e..2d98a02d 100644 --- a/telemetry/context.go +++ b/telemetry/context.go @@ -10,7 +10,6 @@ import ( "github.com/grafana/pyroscope-go" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.43.0" "go.opentelemetry.io/otel/trace" "go.uber.org/zap/zapcore" ) @@ -186,7 +185,7 @@ func (c Context) RecordError(err error, kv ...attribute.KeyValue) { trace.WithAttributes(CodeStacktrace(5, 1)), ) sp.SetStatus(codes.Error, goerrors.Cause(err).Error()) - sp.SetAttributes(semconv.ErrorType(err)) + sp.SetAttributes(foomosemconv.ErrorType(err)) } } diff --git a/telemetry/loggerprovider.go b/telemetry/loggerprovider.go index a73356b1..b5805caf 100644 --- a/telemetry/loggerprovider.go +++ b/telemetry/loggerprovider.go @@ -6,11 +6,11 @@ import ( "github.com/foomo/keel/env" otelzap "github.com/foomo/keel/internal/otel/exporters/zap" otelzapbridge "go.opentelemetry.io/contrib/bridges/otelzap" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" "go.opentelemetry.io/otel/exporters/stdout/stdoutlog" "go.opentelemetry.io/otel/log" - "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/log/noop" sdklog "go.opentelemetry.io/otel/sdk/log" "go.uber.org/zap" @@ -22,7 +22,7 @@ const instrumentationName = "github.com/foomo/keel" // LoggerProvider returns the global logger provider instance used throughout the application. func LoggerProvider() log.LoggerProvider { - return global.GetLoggerProvider() + return otel.GetLoggerProvider() } // NewNoopLoggerProvider returns a no-op [log.LoggerProvider]. @@ -113,7 +113,7 @@ func newLoggerProvider(ctx context.Context, p sdklog.Processor) (log.LoggerProvi sdklog.WithResource(resource), sdklog.WithProcessor(p), ) - global.SetLoggerProvider(provider) + otel.SetLoggerProvider(provider) return provider, nil } diff --git a/telemetry/span.go b/telemetry/span.go index 733c539c..2b6ab163 100644 --- a/telemetry/span.go +++ b/telemetry/span.go @@ -7,6 +7,7 @@ import ( goerrors "github.com/foomo/go/errors" goruntime "github.com/foomo/go/runtime" keelsemconv "github.com/foomo/keel/semconv" + foomosemconv "github.com/foomo/opentelemetry-go/semconv" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.43.0" @@ -29,7 +30,12 @@ func StartSpan(ctx context.Context, opts ...trace.SpanStartOption) (context.Cont // StartDebugSpan is like [StartSpan] but marks the span as a debug span. func StartDebugSpan(ctx context.Context, opts ...trace.SpanStartOption) (context.Context, trace.Span) { - return StartSpanWithSkip(ctx, 1, append(opts, trace.WithAttributes(keelsemconv.DebugEnabled(true)))...) + return StartSpanWithSkip(ctx, 1, append(opts, + trace.WithAttributes( + keelsemconv.DebugEnabled(true), //nolint:staticcheck // TODO deprecated in favor of sampling priority. + foomosemconv.SamplingPriority(1), + ), + )...) } // SpanFromContext returns the current span of ctx, or a non-recording span @@ -68,7 +74,15 @@ func IsSpanRecording(sp trace.Span) bool { // SetSpanDebug marks sp as a debug span. func SetSpanDebug(sp trace.Span) { - sp.SetAttributes(keelsemconv.DebugEnabled(true)) + sp.SetAttributes( + keelsemconv.DebugEnabled(true), //nolint:staticcheck // TODO deprecated in favor of sampling priority. + foomosemconv.SamplingPriority(1), + ) +} + +// SetSpanSamplingPriority sets the sampling priority of span. +func SetSpanSamplingPriority(sp trace.Span, v int) { + sp.SetAttributes(foomosemconv.SamplingPriority(v)) } // SetSpanStatusOK sets the status of sp to ok. @@ -94,7 +108,7 @@ func End(sp trace.Span, err error) { if err != nil { sp.RecordError(err, trace.WithAttributes(CodeStacktrace(3, 0))) sp.SetStatus(codes.Error, goerrors.Cause(err).Error()) - sp.SetAttributes(semconv.ErrorType(err)) + sp.SetAttributes(foomosemconv.ErrorType(err)) } sp.End() @@ -106,7 +120,7 @@ func EndSpan(sp trace.Span, err error, opts ...trace.SpanEndOption) { if err != nil { sp.RecordError(err, trace.WithAttributes(CodeStacktrace(3, 0))) sp.SetStatus(codes.Error, goerrors.Cause(err).Error()) - sp.SetAttributes(semconv.ErrorType(err)) + sp.SetAttributes(foomosemconv.ErrorType(err)) } sp.End(opts...) diff --git a/utils/net/http/errors.go b/utils/net/http/errors.go index d8dc3c3f..496f1d5b 100644 --- a/utils/net/http/errors.go +++ b/utils/net/http/errors.go @@ -11,6 +11,7 @@ import ( "go.uber.org/zap" "github.com/foomo/keel/log" + foomosemconv "github.com/foomo/opentelemetry-go/semconv" ) // InternalServerError responds with status [http.StatusInternalServerError] using [ServerError]. @@ -224,7 +225,7 @@ func NetworkAuthenticationRequiredServerError(l *zap.Logger, w http.ResponseWrit // When errs is empty or contains only nil errors, nothing is written to w. func ServerError(l *zap.Logger, w http.ResponseWriter, r *http.Request, code int, errs ...error) { if err := errors.Join(errs...); err != nil { - errType := semconv.ErrorType(err) + errType := foomosemconv.ErrorType(err) telemetry.Ctx(r.Context()).RecordError(err) if labeler, ok := otelhttp.LabelerFromContext(r.Context()); ok { @@ -233,7 +234,7 @@ func ServerError(l *zap.Logger, w http.ResponseWriter, r *http.Request, code int // add log entry if labeler, ok := httplog.LabelerFromRequest(r); ok { - labeler.Add(log.Attribute(errType), log.FError(err)) + labeler.Add(log.Attributes(errType, semconv.ExceptionMessage(err.Error()))...) } else if l != nil { l = log.WithError(l, err) l = log.WithHTTPRequest(l, r) From 8f213fa34af5ca2922cafd89656405b4c0052087 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 14:41:20 +0200 Subject: [PATCH 7/9] fix: conntect logs --- integration/nats/connect.go | 89 ++++++++++++++++++-------------- integration/nats/connect_test.go | 88 +++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 integration/nats/connect_test.go diff --git a/integration/nats/connect.go b/integration/nats/connect.go index 44ea532f..2deb8074 100644 --- a/integration/nats/connect.go +++ b/integration/nats/connect.go @@ -5,6 +5,7 @@ import ( "errors" "net/url" "strconv" + "sync/atomic" "time" "github.com/foomo/keel" @@ -14,7 +15,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.43.0" - "go.uber.org/zap" ) // Connect establishes a connection to the NATS server at rawURL with OTel @@ -50,6 +50,23 @@ func Connect(s keel.Runtime, rawURL string, opts ...nats.Option) (*nats.Conn, er // context, and metric recording shouldn't be cancelled by request teardown. ctx := context.Background() + // ConnectedUrlRedacted is empty unless connected, so remember the last + // connected server for the disconnect, reconnect error and closed handlers. + var lastURL atomic.Pointer[string] + + serverURL := func(conn *nats.Conn) string { + if v := conn.ConnectedUrlRedacted(); v != "" { + lastURL.Store(&v) + return v + } + + if v := lastURL.Load(); v != nil { + return *v + } + + return "" + } + opts = append(opts, nats.MaxReconnects(-1), nats.ReconnectWait(2*time.Second), @@ -57,25 +74,25 @@ func Connect(s keel.Runtime, rawURL string, opts ...nats.Option) (*nats.Conn, er nats.PingInterval(20*time.Second), nats.ConnectHandler(func(conn *nats.Conn) { - l.Debug("connected", log.Attributes(serverAttrs(conn)...)...) + l.Debug("connected", log.Attributes(serverAttrs(serverURL(conn))...)...) }), nats.DisconnectErrHandler(func(conn *nats.Conn, err error) { - addr, port := serverAddrPort(conn) + u := serverURL(conn) + addr, port := serverAddrPort(u) disconnects.Add(ctx, 1, addr, disconnects.AttrServerPort(port)) - l.Warn("disconnected", zap.Error(err), - zap.String("addr", conn.ConnectedUrlRedacted()), - zap.String("server_id", conn.ConnectedServerId()), - ) + log.WithError(l, err).Warn("disconnected", log.Attributes(serverAttrs(u)...)...) }), - nats.ReconnectErrHandler(func(conn *nats.Conn, err error) { - addr, port := serverAddrPort(conn) + nats.ReconnectHandler(func(conn *nats.Conn) { + u := serverURL(conn) + addr, port := serverAddrPort(u) reconnects.Add(ctx, 1, addr, reconnects.AttrServerPort(port)) - l.Info("reconnect", zap.Error(err), - zap.String("addr", conn.ConnectedUrlRedacted()), - zap.String("server_id", conn.ConnectedServerId()), - ) + l.Info("reconnected", log.Attributes(serverAttrs(u)...)...) + }), + + nats.ReconnectErrHandler(func(conn *nats.Conn, err error) { + log.WithError(l, err).Warn("reconnect failed", log.Attributes(serverAttrs(serverURL(conn))...)...) }), nats.ErrorHandler(func(conn *nats.Conn, sub *nats.Subscription, err error) { @@ -86,35 +103,31 @@ func Connect(s keel.Runtime, rawURL string, opts ...nats.Option) (*nats.Conn, er extraAttrs = append(extraAttrs, asyncErrors.AttrSubject(sub.Subject)) } - if addr, _ := serverAddrPort(conn); addr != "" { + u := serverURL(conn) + if addr, _ := serverAddrPort(u); addr != "" { extraAttrs = append(extraAttrs, asyncErrors.AttrServerAddress(addr)) } asyncErrors.Add(ctx, 1, kind, extraAttrs...) - fields := []zap.Field{ - zap.Error(err), - zap.String("kind", string(kind)), - zap.String("addr", conn.ConnectedUrlRedacted()), - } + // kind is the error.type; WithError would add the Go type name instead + attrs := append(serverAttrs(u), + semconv.ErrorTypeKey.String(string(kind)), + semconv.ExceptionMessage(err.Error()), + ) if sub != nil { - fields = append(fields, zap.String("subject", sub.Subject)) + attrs = append(attrs, semconv.MessagingDestinationName(sub.Subject)) } - l.Warn("async error", fields...) + l.Warn("async error", log.Attributes(attrs...)...) }), nats.ClosedHandler(func(conn *nats.Conn) { - l.Debug("closed", - zap.String("addr", conn.ConnectedUrlRedacted()), - zap.String("server_id", conn.ConnectedServerId()), - ) + l.Debug("closed", log.Attributes(serverAttrs(serverURL(conn))...)...) }), nats.LameDuckModeHandler(func(conn *nats.Conn) { - l.Info("server lame-duck mode", - zap.String("addr", conn.ConnectedUrlRedacted()), - ) + l.Info("server lame-duck mode", log.Attributes(serverAttrs(serverURL(conn))...)...) }), nats.NoCallbacksAfterClientClose(), @@ -130,27 +143,25 @@ func Connect(s keel.Runtime, rawURL string, opts ...nats.Option) (*nats.Conn, er return conn, nil } -// serverAttrs returns OTel semconv attributes describing the connected server. -// Returns nil if the connected URL cannot be parsed. -func serverAttrs(nc *nats.Conn) []attribute.KeyValue { - u, err := url.Parse(nc.ConnectedUrlRedacted()) - if err != nil { +// serverAttrs returns OTel semconv attributes describing the server at +// rawURL. Returns nil if rawURL is empty or cannot be parsed. +func serverAttrs(rawURL string) []attribute.KeyValue { + addr, port := serverAddrPort(rawURL) + if addr == "" { return nil } - port, _ := strconv.Atoi(u.Port()) - return []attribute.KeyValue{ - semconv.ServerAddress(u.Hostname()), + semconv.ServerAddress(addr), semconv.ServerPort(port), semconv.NetworkProtocolName("nats"), } } -// serverAddrPort extracts just the address and port from the connected URL. +// serverAddrPort extracts just the address and port from rawURL. // Returns zero values if parsing fails. -func serverAddrPort(nc *nats.Conn) (string, int) { - u, err := url.Parse(nc.ConnectedUrlRedacted()) +func serverAddrPort(rawURL string) (string, int) { + u, err := url.Parse(rawURL) if err != nil { return "", 0 } diff --git a/integration/nats/connect_test.go b/integration/nats/connect_test.go new file mode 100644 index 00000000..19127069 --- /dev/null +++ b/integration/nats/connect_test.go @@ -0,0 +1,88 @@ +package nats_test + +import ( + "context" + "testing" + "time" + + testingx "github.com/foomo/go/testing" + keelnats "github.com/foomo/keel/integration/nats" + "github.com/foomo/keel/integration/nats/service" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + semconv "go.opentelemetry.io/otel/semconv/v1.43.0" + "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace/noop" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +type testRuntime struct { + ctx context.Context + l *zap.Logger + meter metric.Meter +} + +func (r *testRuntime) Logger() *zap.Logger { return r.l } +func (r *testRuntime) Config() *viper.Viper { return viper.New() } +func (r *testRuntime) Context() context.Context { return r.ctx } +func (r *testRuntime) Meter() metric.Meter { return r.meter } +func (r *testRuntime) Tracer() trace.Tracer { return noop.NewTracerProvider().Tracer("") } +func (r *testRuntime) AddCloser(any) {} +func (r *testRuntime) AddClosers(...any) {} + +// TestConnect_disconnectServerAttrs asserts that the disconnect metric and log +// carry the server address although the connection is no longer connected. +func TestConnect_disconnectServerAttrs(t *testing.T) { + t.Parallel() + + srv, err := service.NewEmbeddedServer( + service.EmbeddedServerWithPort(testingx.FreePort(t)), + service.EmbeddedServerWithHost("localhost"), + ) + require.NoError(t, err) + + go func() { _ = srv.Start(t.Context()) }() + + require.Eventually(t, srv.Server().Running, time.Second, 10*time.Millisecond) + + reader := sdkmetric.NewManualReader() + core, logs := observer.New(zap.DebugLevel) + rt := &testRuntime{ + ctx: t.Context(), + l: zap.New(core), + meter: sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)).Meter("test"), + } + + conn, err := keelnats.Connect(rt, srv.ClientURL()) + require.NoError(t, err) + t.Cleanup(conn.Close) + + require.NoError(t, srv.Close(t.Context())) + + require.Eventually(t, func() bool { + return logs.FilterMessage("disconnected").Len() > 0 + }, 5*time.Second, 10*time.Millisecond) + + assert.Equal(t, "localhost", logs.FilterMessage("disconnected").All()[0].ContextMap()["server_address"]) + + var rm metricdata.ResourceMetrics + require.NoError(t, reader.Collect(t.Context(), &rm)) + + var addr attribute.Value + + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + if sum, ok := m.Data.(metricdata.Sum[int64]); ok && m.Name == "nats.client.disconnects" { + addr, _ = sum.DataPoints[0].Attributes.Value(semconv.ServerAddressKey) + } + } + } + + assert.Equal(t, "localhost", addr.AsString()) +} From 20b09585b32fa80dcad25d65d7e90b2107278e5c Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Tue, 6 Oct 2026 15:15:50 +0200 Subject: [PATCH 8/9] docs: update badges and links --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ee7243c..53afac82 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -[![GoDoc](https://img.shields.io/badge/GoDoc-✓-informational.svg?style=flat-square&logo=go)](https://godoc.org/github.com/foomo/keel) +[![CodeQL](https://github.com/foomo/keel/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/foomo/keel/actions/workflows/github-code-scanning/codeql) [![Coverage](https://img.shields.io/codecov/c/github/foomo/keel?style=flat-square&logo=github)](https://app.codecov.io/gh/foomo/keel) +[![GoDoc](https://godoc.org/github.com/foomo/keel?status.svg)](https://godoc.org/github.com/foomo/keel) [![GitHub Stars](https://img.shields.io/github/stars/foomo/keel.svg?style=flat-square&logo=github)](https://github.com/foomo/keel)

From 97eeab327d02357b488aaa811febb51fda5762da Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Wed, 7 Oct 2026 12:06:53 +0200 Subject: [PATCH 9/9] fix: duplicate sleep --- examples/telemetry/main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/telemetry/main.go b/examples/telemetry/main.go index 44cabd6f..868ca8b9 100644 --- a/examples/telemetry/main.go +++ b/examples/telemetry/main.go @@ -113,9 +113,7 @@ func main() { svs.HandleFunc("/histogram", func(w http.ResponseWriter, r *http.Request) { start := time.Now() - time.Sleep(time.Second) - - time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) + time.Sleep(time.Second + time.Duration(rand.Intn(100))*time.Millisecond) histogram.Record(r.Context(), time.Since(start).Seconds(), metric.WithAttributes(attribute.String("key", "value")),