Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Jaeger internal packages to OTEL contrib and update import paths #37999

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
Added OTEL Metric SDK for receiver/jaegerreceiver/internal/pkg/metrics
  • Loading branch information
AdityaS8804 committed Feb 19, 2025
commit c0502a3b107cc9810719012fc6bcac754d3f8d8d
4 changes: 2 additions & 2 deletions receiver/jaegerreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ require (
go.opentelemetry.io/collector/receiver v0.120.0
go.opentelemetry.io/collector/receiver/receivertest v0.120.0
go.opentelemetry.io/collector/semconv v0.120.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/metric v1.34.0
go.uber.org/goleak v1.3.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
Expand Down Expand Up @@ -90,8 +92,6 @@ require (
go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
Expand Down
22 changes: 19 additions & 3 deletions receiver/jaegerreceiver/internal/pkg/metrics/counter.go
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,31 @@
// Copyright (c) 2022 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
// SPDX-License-Identifier: Apache-2.0

package metrics

// Counter tracks the number of times an event has occurred
import (
"context"

"go.opentelemetry.io/otel/metric"
)

// Counter tracks the number of times an event has occurred.
type Counter interface {
// Inc adds the given value to the counter.
// Inc adds the given value to the counter.
Inc(int64)
}

// otelCounter is an OTEL-based implementation of Counter.
type otelCounter struct {
counter metric.Int64Counter
tags map[string]string
}

// Inc increments the counter by the specified delta.
func (c *otelCounter) Inc(delta int64) {
c.counter.Add(context.Background(), delta, metric.WithAttributes(convertTags(c.tags)...))
}

// NullCounter counter that does nothing
var NullCounter Counter = nullCounter{}

Expand Down
123 changes: 107 additions & 16 deletions receiver/jaegerreceiver/internal/pkg/metrics/factory.go
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// Copyright (c) 2022 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
// SPDX-License-Identifier: Apache-2.0

package metrics

import (
"context"
"time"

"go.opentelemetry.io/otel/metric"
)

// NSOptions defines the name and tags map associated with a factory namespace
Expand Down Expand Up @@ -38,35 +40,124 @@ type HistogramOptions struct {
Buckets []float64
}

// Factory creates new metrics
// Factory creates new metrics.
type Factory interface {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @yurishkuro, you recommended replacing the metrics with the OTEL metrics SDK. With respect to that I have a quick question—should we be relying on the metric tags (like service=my_service) for setting the OTEL service, or do we have a dedicated service input (maybe from config/env) that sets the OTEL resource’s service.name?
Could you please point to where we might be getting this service information.
And could you clarify if I am on the right track.
Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In agent metrics the service is from the observed spans, not the collector service.

Copy link
Author

@AdityaS8804 AdityaS8804 Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I looked into it and I tried an implementation with otel. Please review the same

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove all the metrics packages from this PR? Leave only the business logic you're adding.

Copy link
Author

@AdityaS8804 AdityaS8804 Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, so I reverted to the old metric implementation and kept the rest of the business logic as is.
Does that align with the requirements?

Counter(metric Options) Counter
Timer(metric TimerOptions) Timer
Gauge(metric Options) Gauge
Histogram(metric HistogramOptions) Histogram

// Namespace returns a nested metrics factory.
// Namespace returns a nested metrics factory.
Namespace(scope NSOptions) Factory
}

// NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge.
var NullFactory Factory = nullFactory{}
// otelFactory is an OTEL-backed implementation of Factory.
type otelFactory struct {
meter metric.Meter
globalTags map[string]string
}

type nullFactory struct{}
// NewFactory creates a new OTEL-backed factory with the provided meter and global tags.
func NewFactory(meter metric.Meter, globalTags map[string]string) Factory {
// Copy globalTags to avoid side-effects.
copyTags := make(map[string]string)
for k, v := range globalTags {
copyTags[k] = v
}
return &otelFactory{
meter: meter,
globalTags: copyTags,
}
}

func (nullFactory) Counter(Options) Counter {
return NullCounter
// mergeTags merges two maps of tags.
func mergeTags(base, tags map[string]string) map[string]string {
merged := make(map[string]string)
for k, v := range base {
merged[k] = v
}
for k, v := range tags {
merged[k] = v
}
return merged
}

func (nullFactory) Timer(TimerOptions) Timer {
return NullTimer
// Counter creates an OTEL-backed counter instrument.
func (f *otelFactory) Counter(opts Options) Counter {
merged := mergeTags(f.globalTags, opts.Tags)
counter, err := f.meter.Int64Counter(opts.Name, metric.WithDescription(opts.Help))
if err != nil {
return NullCounter
}
return &otelCounter{
counter: counter,
tags: merged,
}
}

func (nullFactory) Gauge(Options) Gauge {
return NullGauge
// Timer creates an OTEL-backed timer instrument using an Int64Histogram.
func (f *otelFactory) Timer(opts TimerOptions) Timer {
merged := mergeTags(f.globalTags, opts.Tags)
hist, err := f.meter.Int64Histogram(opts.Name, metric.WithDescription(opts.Help))
if err != nil {
return NullTimer
}
return &otelTimer{
histogram: hist,
tags: merged,
}
}

func (nullFactory) Histogram(HistogramOptions) Histogram {
return NullHistogram
// Gauge creates an OTEL-backed gauge instrument using an observable gauge.
func (f *otelFactory) Gauge(opts Options) Gauge {
merged := mergeTags(f.globalTags, opts.Tags)
obsGauge, err := f.meter.Int64ObservableGauge(opts.Name, metric.WithDescription(opts.Help))
if err != nil {
return NullGauge
}
g := &otelGauge{
gauge: obsGauge,
tags: merged,
}
// Register a callback to observe the gauge's value.
f.meter.RegisterCallback(
func(ctx context.Context, observer metric.Observer) error {
observer.ObserveInt64(obsGauge, g.getValue(), metric.WithAttributes(convertTags(g.tags)...))
return nil
},
obsGauge,
)
return g
}
func (nullFactory) Namespace(NSOptions /* scope */) Factory { return NullFactory }

// Histogram creates an OTEL-backed histogram instrument.
func (f *otelFactory) Histogram(opts HistogramOptions) Histogram {
merged := mergeTags(f.globalTags, opts.Tags)
hist, err := f.meter.Float64Histogram(opts.Name, metric.WithDescription(opts.Help))
if err != nil {
return NullHistogram
}
return &otelHistogram{
histogram: hist,
tags: merged,
}
}

// Namespace returns a new factory with additional namespaced attributes.
func (f *otelFactory) Namespace(scope NSOptions) Factory {
merged := mergeTags(f.globalTags, scope.Tags)
merged["namespace"] = scope.Name
return &otelFactory{
meter: f.meter,
globalTags: merged,
}
}
// NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge.
var NullFactory Factory = nullFactory{}

type nullFactory struct{}

func (nullFactory) Counter(Options) Counter { return NullCounter }
func (nullFactory) Timer(TimerOptions) Timer { return NullTimer }
func (nullFactory) Gauge(Options) Gauge { return NullGauge }
func (nullFactory) Histogram(HistogramOptions) Histogram { return NullHistogram }
func (nullFactory) Namespace(NSOptions) Factory { return NullFactory }
26 changes: 24 additions & 2 deletions receiver/jaegerreceiver/internal/pkg/metrics/gauge.go
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,37 @@
// Copyright (c) 2022 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
// SPDX-License-Identifier: Apache-2.0

package metrics

import (
"sync/atomic"

"go.opentelemetry.io/otel/metric"
)

// Gauge returns instantaneous measurements of something as an int64 value
type Gauge interface {
// Update the gauge to the value passed in.
// Update the gauge to the value passed in.
Update(int64)
}

// otelGauge implements Gauge using an OTEL observable gauge.
type otelGauge struct {
gauge metric.Int64ObservableGauge
value int64
tags map[string]string
}

// Update sets the gauge to the provided value.
func (g *otelGauge) Update(newValue int64) {
atomic.StoreInt64(&g.value, newValue)
}

// getValue returns the current gauge value.
func (g *otelGauge) getValue() int64 {
return atomic.LoadInt64(&g.value)
}

// NullGauge gauge that does nothing
var NullGauge Gauge = nullGauge{}

Expand Down
20 changes: 18 additions & 2 deletions receiver/jaegerreceiver/internal/pkg/metrics/histogram.go
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
// Copyright The OpenTelemetry Authors
// Copyright (c) 2018 The Jaeger Authors
// SPDX-License-Identifier: Apache-2.0

package metrics

import (
"context"

"go.opentelemetry.io/otel/metric"
)

// Histogram that keeps track of a distribution of values.
type Histogram interface {
// Records the value passed in.
// Records the value passed in.
Record(float64)
}

// otelHistogram implements Histogram using an OTEL Float64Histogram.
type otelHistogram struct {
histogram metric.Float64Histogram
tags map[string]string
}

// Record logs the given value into the histogram.
func (h *otelHistogram) Record(value float64) {
h.histogram.Record(context.Background(), value, metric.WithAttributes(convertTags(h.tags)...))
}

// NullHistogram that does nothing
var NullHistogram Histogram = nullHistogram{}

Expand Down
Empty file modified receiver/jaegerreceiver/internal/pkg/metrics/package.go
100755 → 100644
Empty file.
1 change: 0 additions & 1 deletion receiver/jaegerreceiver/internal/pkg/metrics/stopwatch.go
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Copyright (c) 2022 The Jaeger Authors.
// Copyright (c) 2017 Uber Technologies, Inc.
// SPDX-License-Identifier: Apache-2.0

package metrics

import (
Expand Down
16 changes: 15 additions & 1 deletion receiver/jaegerreceiver/internal/pkg/metrics/timer.go
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,30 @@
package metrics

import (
"context"
"time"

"go.opentelemetry.io/otel/metric"
)

// Timer accumulates observations about how long some operation took,
// and also maintains a historgam of percentiles.
type Timer interface {
// Records the time passed in.
// Records the time passed in.
Record(time.Duration)
}

// otelTimer implements Timer using an OTEL Int64Histogram.
type otelTimer struct {
histogram metric.Int64Histogram
tags map[string]string
}

// Record logs the duration (in nanoseconds) for the operation.
func (t *otelTimer) Record(duration time.Duration) {
t.histogram.Record(context.Background(), int64(duration), metric.WithAttributes(convertTags(t.tags)...))
}

// NullTimer timer that does nothing
var NullTimer Timer = nullTimer{}

Expand Down
16 changes: 16 additions & 0 deletions receiver/jaegerreceiver/internal/pkg/metrics/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// utils.go
// Utility functions for the metrics package.
package metrics

import (
"go.opentelemetry.io/otel/attribute"
)

// convertTags converts a map of tags to a slice of attribute.KeyValue.
func convertTags(tags map[string]string) []attribute.KeyValue {
var attrs []attribute.KeyValue
for k, v := range tags {
attrs = append(attrs, attribute.String(k, v))
}
return attrs
}