-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create metrics.Factory adapter for OTEL Metrics (#5661)
**Which problem is this PR solving?** This PR addresses a part of the issue [#5633 ](#5633) **Description of the changes** This is a Draft PR to bridge the OTEL Metrics instead of using Internal Metrics to minimize code changes. **How was this change tested?** The changes were tested by running the following command: ```bash make test ``` **Checklist** - [x] I have read [CONTRIBUTING_GUIDELINES.md](https://github.com/jaegertracing/jaeger/blob/master/CONTRIBUTING_GUIDELINES.md) - [x] I have signed all commits - [x] I have added unit tests for the new functionality - [x] I have run lint and test steps successfully - `for jaeger: make lint test` - `for jaeger-ui: yarn lint` and `yarn test` --------- Signed-off-by: Wise-Wizard <[email protected]> Signed-off-by: Saransh Shankar <[email protected]> Signed-off-by: Yuri Shkuro <[email protected]> Co-authored-by: Yuri Shkuro <[email protected]> Co-authored-by: Yuri Shkuro <[email protected]>
- Loading branch information
1 parent
afb4c87
commit 301dbec
Showing
7 changed files
with
583 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright (c) 2024 The Jaeger Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package benchmark_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/prometheus/client_golang/prometheus" | ||
"github.com/stretchr/testify/require" | ||
promExporter "go.opentelemetry.io/otel/exporters/prometheus" | ||
"go.opentelemetry.io/otel/sdk/metric" | ||
|
||
"github.com/jaegertracing/jaeger/internal/metrics/otelmetrics" | ||
prom "github.com/jaegertracing/jaeger/internal/metrics/prometheus" | ||
"github.com/jaegertracing/jaeger/pkg/metrics" | ||
"github.com/jaegertracing/jaeger/pkg/testutils" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
testutils.VerifyGoLeaks(m) | ||
} | ||
|
||
func setupPrometheusFactory() metrics.Factory { | ||
reg := prometheus.NewRegistry() | ||
return prom.New(prom.WithRegisterer(reg)) | ||
} | ||
|
||
func setupOTELFactory(b *testing.B) metrics.Factory { | ||
registry := prometheus.NewRegistry() | ||
exporter, err := promExporter.New(promExporter.WithRegisterer(registry)) | ||
require.NoError(b, err) | ||
meterProvider := metric.NewMeterProvider( | ||
metric.WithReader(exporter), | ||
) | ||
return otelmetrics.NewFactory(meterProvider) | ||
} | ||
|
||
func benchmarkCounter(b *testing.B, factory metrics.Factory) { | ||
counter := factory.Counter(metrics.Options{ | ||
Name: "test_counter", | ||
Tags: map[string]string{"tag1": "value1"}, | ||
}) | ||
|
||
for i := 0; i < b.N; i++ { | ||
counter.Inc(1) | ||
} | ||
} | ||
|
||
func benchmarkGauge(b *testing.B, factory metrics.Factory) { | ||
gauge := factory.Gauge(metrics.Options{ | ||
Name: "test_gauge", | ||
Tags: map[string]string{"tag1": "value1"}, | ||
}) | ||
|
||
for i := 0; i < b.N; i++ { | ||
gauge.Update(1) | ||
} | ||
} | ||
|
||
func benchmarkTimer(b *testing.B, factory metrics.Factory) { | ||
timer := factory.Timer(metrics.TimerOptions{ | ||
Name: "test_timer", | ||
Tags: map[string]string{"tag1": "value1"}, | ||
}) | ||
|
||
for i := 0; i < b.N; i++ { | ||
timer.Record(100) | ||
} | ||
} | ||
|
||
func benchmarkHistogram(b *testing.B, factory metrics.Factory) { | ||
histogram := factory.Histogram(metrics.HistogramOptions{ | ||
Name: "test_histogram", | ||
Tags: map[string]string{"tag1": "value1"}, | ||
}) | ||
|
||
for i := 0; i < b.N; i++ { | ||
histogram.Record(1.0) | ||
} | ||
} | ||
|
||
func BenchmarkPrometheusCounter(b *testing.B) { | ||
benchmarkCounter(b, setupPrometheusFactory()) | ||
} | ||
|
||
func BenchmarkOTELCounter(b *testing.B) { | ||
benchmarkCounter(b, setupOTELFactory(b)) | ||
} | ||
|
||
func BenchmarkPrometheusGauge(b *testing.B) { | ||
benchmarkGauge(b, setupPrometheusFactory()) | ||
} | ||
|
||
func BenchmarkOTELGauge(b *testing.B) { | ||
benchmarkGauge(b, setupOTELFactory(b)) | ||
} | ||
|
||
func BenchmarkPrometheusTimer(b *testing.B) { | ||
benchmarkTimer(b, setupPrometheusFactory()) | ||
} | ||
|
||
func BenchmarkOTELTimer(b *testing.B) { | ||
benchmarkTimer(b, setupOTELFactory(b)) | ||
} | ||
|
||
func BenchmarkPrometheusHistogram(b *testing.B) { | ||
benchmarkHistogram(b, setupPrometheusFactory()) | ||
} | ||
|
||
func BenchmarkOTELHistogram(b *testing.B) { | ||
benchmarkHistogram(b, setupOTELFactory(b)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (c) 2024 The Jaeger Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package otelmetrics | ||
|
||
import ( | ||
"context" | ||
|
||
"go.opentelemetry.io/otel/metric" | ||
) | ||
|
||
type otelCounter struct { | ||
counter metric.Int64Counter | ||
fixedCtx context.Context | ||
option metric.AddOption | ||
} | ||
|
||
func (c *otelCounter) Inc(value int64) { | ||
c.counter.Add(c.fixedCtx, value, c.option) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
// Copyright (c) 2024 The Jaeger Authors. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package otelmetrics | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"strings" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/metric" | ||
|
||
"github.com/jaegertracing/jaeger/pkg/metrics" | ||
) | ||
|
||
type otelFactory struct { | ||
meter metric.Meter | ||
scope string | ||
separator string | ||
normalizer *strings.Replacer | ||
tags map[string]string | ||
} | ||
|
||
func NewFactory(meterProvider metric.MeterProvider) metrics.Factory { | ||
return &otelFactory{ | ||
meter: meterProvider.Meter("jaeger-v2"), | ||
separator: ".", | ||
normalizer: strings.NewReplacer(" ", "_", ".", "_", "-", "_"), | ||
tags: make(map[string]string), | ||
} | ||
} | ||
|
||
func (f *otelFactory) Counter(opts metrics.Options) metrics.Counter { | ||
name := f.subScope(opts.Name) | ||
counter, err := f.meter.Int64Counter(name) | ||
if err != nil { | ||
log.Printf("Error creating OTEL counter: %v", err) | ||
return metrics.NullCounter | ||
} | ||
return &otelCounter{ | ||
counter: counter, | ||
fixedCtx: context.Background(), | ||
option: attributeSetOption(f.mergeTags(opts.Tags)), | ||
} | ||
} | ||
|
||
func (f *otelFactory) Gauge(opts metrics.Options) metrics.Gauge { | ||
name := f.subScope(opts.Name) | ||
gauge, err := f.meter.Int64Gauge(name) | ||
if err != nil { | ||
log.Printf("Error creating OTEL gauge: %v", err) | ||
return metrics.NullGauge | ||
} | ||
|
||
return &otelGauge{ | ||
gauge: gauge, | ||
fixedCtx: context.Background(), | ||
option: attributeSetOption(f.mergeTags(opts.Tags)), | ||
} | ||
} | ||
|
||
func (f *otelFactory) Histogram(opts metrics.HistogramOptions) metrics.Histogram { | ||
name := f.subScope(opts.Name) | ||
histogram, err := f.meter.Float64Histogram(name) | ||
if err != nil { | ||
log.Printf("Error creating OTEL histogram: %v", err) | ||
return metrics.NullHistogram | ||
} | ||
|
||
return &otelHistogram{ | ||
histogram: histogram, | ||
fixedCtx: context.Background(), | ||
option: attributeSetOption(f.mergeTags(opts.Tags)), | ||
} | ||
} | ||
|
||
func (f *otelFactory) Timer(opts metrics.TimerOptions) metrics.Timer { | ||
name := f.subScope(opts.Name) | ||
timer, err := f.meter.Float64Histogram(name, metric.WithUnit("s")) | ||
if err != nil { | ||
log.Printf("Error creating OTEL timer: %v", err) | ||
return metrics.NullTimer | ||
} | ||
return &otelTimer{ | ||
histogram: timer, | ||
fixedCtx: context.Background(), | ||
option: attributeSetOption(f.mergeTags(opts.Tags)), | ||
} | ||
} | ||
|
||
func (f *otelFactory) Namespace(opts metrics.NSOptions) metrics.Factory { | ||
return &otelFactory{ | ||
meter: f.meter, | ||
scope: f.subScope(opts.Name), | ||
separator: f.separator, | ||
normalizer: f.normalizer, | ||
tags: f.mergeTags(opts.Tags), | ||
} | ||
} | ||
|
||
func (f *otelFactory) subScope(name string) string { | ||
if f.scope == "" { | ||
return f.normalize(name) | ||
} | ||
if name == "" { | ||
return f.normalize(f.scope) | ||
} | ||
return f.normalize(f.scope + f.separator + name) | ||
} | ||
|
||
func (f *otelFactory) normalize(v string) string { | ||
return f.normalizer.Replace(v) | ||
} | ||
|
||
func (f *otelFactory) mergeTags(tags map[string]string) map[string]string { | ||
merged := make(map[string]string) | ||
for k, v := range f.tags { | ||
merged[k] = v | ||
} | ||
for k, v := range tags { | ||
merged[k] = v | ||
} | ||
return merged | ||
} | ||
|
||
func attributeSetOption(tags map[string]string) metric.MeasurementOption { | ||
attributes := make([]attribute.KeyValue, 0, len(tags)) | ||
for k, v := range tags { | ||
attributes = append(attributes, attribute.String(k, v)) | ||
} | ||
return metric.WithAttributes(attributes...) | ||
} |
Oops, something went wrong.