mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Remove opentracing and use opentelemetry instead (#67200)
* remove opentracing and use otel instead * add various samplers for jaeger * remove useless test that is covered in otel now * we do not need a struct there * remove old tests * restore tests that parse various configurations * check errors in tests * Update pkg/infra/tracing/tracing_test.go fix typo Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> * add test for both legacy and new config formats * use named constants --------- Co-authored-by: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com>
This commit is contained in:
parent
69f1116f59
commit
6d8f9c5bf4
5
go.mod
5
go.mod
@ -81,7 +81,7 @@ require (
|
|||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4
|
github.com/matttproud/golang_protobuf_extensions v1.0.4
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
|
||||||
github.com/opentracing/opentracing-go v1.2.0
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
@ -95,7 +95,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.8.2
|
||||||
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
|
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
|
||||||
github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f
|
github.com/ua-parser/uap-go v0.0.0-20211112212520-00c877edfe0f
|
||||||
github.com/uber/jaeger-client-go v2.29.1+incompatible
|
github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect
|
||||||
github.com/urfave/cli/v2 v2.24.4
|
github.com/urfave/cli/v2 v2.24.4
|
||||||
github.com/vectordotdev/go-datemath v0.1.1-0.20220323213446-f3954d0b18ae
|
github.com/vectordotdev/go-datemath v0.1.1-0.20220323213446-f3954d0b18ae
|
||||||
github.com/yalue/merged_fs v1.2.2
|
github.com/yalue/merged_fs v1.2.2
|
||||||
@ -271,6 +271,7 @@ require (
|
|||||||
github.com/redis/go-redis/v9 v9.0.2
|
github.com/redis/go-redis/v9 v9.0.2
|
||||||
github.com/weaveworks/common v0.0.0-20230208133027-16871410fca4
|
github.com/weaveworks/common v0.0.0-20230208133027-16871410fca4
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f
|
||||||
|
go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
2
go.sum
2
go.sum
@ -2428,6 +2428,8 @@ go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.
|
|||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0/go.mod h1:JQAtechjxLEL81EjmbRwxBq/XEzGaHcsPuDHAx54hg4=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.21.0/go.mod h1:JQAtechjxLEL81EjmbRwxBq/XEzGaHcsPuDHAx54hg4=
|
||||||
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 h1:xdJjwy5t/8I+TZehMMQ+r2h50HREihH2oMUhimQ+jug=
|
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0 h1:xdJjwy5t/8I+TZehMMQ+r2h50HREihH2oMUhimQ+jug=
|
||||||
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0/go.mod h1:tU0nwW4QTvKceNUP60/PQm0FI8zDSwey7gIFt3RR/yw=
|
go.opentelemetry.io/contrib/propagators/jaeger v1.15.0/go.mod h1:tU0nwW4QTvKceNUP60/PQm0FI8zDSwey7gIFt3RR/yw=
|
||||||
|
go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0 h1:zRi6a8uX+cJGTPLXRPjFEzN27a26k5R7LiLK87ntXgg=
|
||||||
|
go.opentelemetry.io/contrib/samplers/jaegerremote v0.9.0/go.mod h1:pzJOLTppaPbiPjPZEwGRf0VWx6G07hhOqznjKXIMkEk=
|
||||||
go.opentelemetry.io/contrib/zpages v0.0.0-20210722161726-7668016acb73/go.mod h1:NAkejuYm41lpyL43Fu1XdnCOYxN5NVV80/MJ03JQ/X8=
|
go.opentelemetry.io/contrib/zpages v0.0.0-20210722161726-7668016acb73/go.mod h1:NAkejuYm41lpyL43Fu1XdnCOYxN5NVV80/MJ03JQ/X8=
|
||||||
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
go.opentelemetry.io/otel v1.0.0-RC1/go.mod h1:x9tRa9HK4hSSq7jf2TKbqFbtt58/TGk0f9XiEYISI1I=
|
||||||
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg=
|
||||||
|
@ -1,338 +0,0 @@
|
|||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-kit/log/level"
|
|
||||||
"go.etcd.io/etcd/api/v3/version"
|
|
||||||
jaegerpropagator "go.opentelemetry.io/contrib/propagators/jaeger"
|
|
||||||
"go.opentelemetry.io/otel"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/codes"
|
|
||||||
"go.opentelemetry.io/otel/exporters/jaeger"
|
|
||||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
|
||||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
|
||||||
"go.opentelemetry.io/otel/propagation"
|
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
|
||||||
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
|
||||||
trace "go.opentelemetry.io/otel/trace"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
jaegerExporter string = "jaeger"
|
|
||||||
otlpExporter string = "otlp"
|
|
||||||
noopExporter string = "noop"
|
|
||||||
|
|
||||||
jaegerPropagator string = "jaeger"
|
|
||||||
w3cPropagator string = "w3c"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Opentelemetry struct {
|
|
||||||
Enabled string
|
|
||||||
Address string
|
|
||||||
Propagation string
|
|
||||||
customAttribs []attribute.KeyValue
|
|
||||||
|
|
||||||
log log.Logger
|
|
||||||
|
|
||||||
tracerProvider tracerProvider
|
|
||||||
tracer trace.Tracer
|
|
||||||
|
|
||||||
Cfg *setting.Cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
type tracerProvider interface {
|
|
||||||
trace.TracerProvider
|
|
||||||
|
|
||||||
Shutdown(ctx context.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpentelemetrySpan struct {
|
|
||||||
span trace.Span
|
|
||||||
}
|
|
||||||
|
|
||||||
type EventValue struct {
|
|
||||||
Str string
|
|
||||||
Num int64
|
|
||||||
}
|
|
||||||
|
|
||||||
type otelErrHandler func(err error)
|
|
||||||
|
|
||||||
func (o otelErrHandler) Handle(err error) {
|
|
||||||
o(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type noopTracerProvider struct {
|
|
||||||
trace.TracerProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func (noopTracerProvider) Shutdown(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) parseSettingsOpentelemetry() error {
|
|
||||||
section := ots.Cfg.Raw.Section("tracing.opentelemetry")
|
|
||||||
var err error
|
|
||||||
ots.customAttribs, err = splitCustomAttribs(section.Key("custom_attributes").MustString(""))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
section = ots.Cfg.Raw.Section("tracing.opentelemetry.jaeger")
|
|
||||||
ots.Enabled = noopExporter
|
|
||||||
|
|
||||||
ots.Address = section.Key("address").MustString("")
|
|
||||||
ots.Propagation = section.Key("propagation").MustString("")
|
|
||||||
if ots.Address != "" {
|
|
||||||
ots.Enabled = jaegerExporter
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
section = ots.Cfg.Raw.Section("tracing.opentelemetry.otlp")
|
|
||||||
ots.Address = section.Key("address").MustString("")
|
|
||||||
if ots.Address != "" {
|
|
||||||
ots.Enabled = otlpExporter
|
|
||||||
}
|
|
||||||
ots.Propagation = section.Key("propagation").MustString("")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) OTelExporterEnabled() bool {
|
|
||||||
return ots.Enabled == otlpExporter
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitCustomAttribs(s string) ([]attribute.KeyValue, error) {
|
|
||||||
res := []attribute.KeyValue{}
|
|
||||||
|
|
||||||
attribs := strings.Split(s, ",")
|
|
||||||
for _, v := range attribs {
|
|
||||||
parts := strings.SplitN(v, ":", 2)
|
|
||||||
if len(parts) > 1 {
|
|
||||||
res = append(res, attribute.String(parts[0], parts[1]))
|
|
||||||
} else if v != "" {
|
|
||||||
return nil, fmt.Errorf("custom attribute malformed - must be in 'key:value' form: %q", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) initJaegerTracerProvider() (*tracesdk.TracerProvider, error) {
|
|
||||||
// Create the Jaeger exporter
|
|
||||||
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(ots.Address)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := resource.New(
|
|
||||||
context.Background(),
|
|
||||||
resource.WithAttributes(
|
|
||||||
// TODO: why are these attributes different from ones added to the
|
|
||||||
// OTLP provider?
|
|
||||||
semconv.ServiceNameKey.String("grafana"),
|
|
||||||
attribute.String("environment", "production"),
|
|
||||||
),
|
|
||||||
resource.WithAttributes(ots.customAttribs...),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tp := tracesdk.NewTracerProvider(
|
|
||||||
tracesdk.WithBatcher(exp),
|
|
||||||
tracesdk.WithResource(res),
|
|
||||||
)
|
|
||||||
|
|
||||||
return tp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) initOTLPTracerProvider() (*tracesdk.TracerProvider, error) {
|
|
||||||
client := otlptracegrpc.NewClient(otlptracegrpc.WithEndpoint(ots.Address), otlptracegrpc.WithInsecure())
|
|
||||||
exp, err := otlptrace.New(context.Background(), client)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return initTracerProvider(exp, ots.customAttribs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initTracerProvider(exp tracesdk.SpanExporter, customAttribs ...attribute.KeyValue) (*tracesdk.TracerProvider, error) {
|
|
||||||
res, err := resource.New(
|
|
||||||
context.Background(),
|
|
||||||
resource.WithAttributes(
|
|
||||||
semconv.ServiceNameKey.String("grafana"),
|
|
||||||
semconv.ServiceVersionKey.String(version.Version),
|
|
||||||
),
|
|
||||||
resource.WithAttributes(customAttribs...),
|
|
||||||
resource.WithProcessRuntimeDescription(),
|
|
||||||
resource.WithTelemetrySDK(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tp := tracesdk.NewTracerProvider(
|
|
||||||
tracesdk.WithBatcher(exp),
|
|
||||||
tracesdk.WithSampler(tracesdk.ParentBased(
|
|
||||||
tracesdk.AlwaysSample(),
|
|
||||||
)),
|
|
||||||
tracesdk.WithResource(res),
|
|
||||||
)
|
|
||||||
return tp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) initNoopTracerProvider() (tracerProvider, error) {
|
|
||||||
return &noopTracerProvider{TracerProvider: trace.NewNoopTracerProvider()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) initOpentelemetryTracer() error {
|
|
||||||
var tp tracerProvider
|
|
||||||
var err error
|
|
||||||
switch ots.Enabled {
|
|
||||||
case jaegerExporter:
|
|
||||||
tp, err = ots.initJaegerTracerProvider()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case otlpExporter:
|
|
||||||
tp, err = ots.initOTLPTracerProvider()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
tp, err = ots.initNoopTracerProvider()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register our TracerProvider as the global so any imported
|
|
||||||
// instrumentation in the future will default to using it
|
|
||||||
// only if tracing is enabled
|
|
||||||
if ots.Enabled != "" {
|
|
||||||
otel.SetTracerProvider(tp)
|
|
||||||
}
|
|
||||||
|
|
||||||
propagators := []propagation.TextMapPropagator{}
|
|
||||||
for _, p := range strings.Split(ots.Propagation, ",") {
|
|
||||||
switch p {
|
|
||||||
case w3cPropagator:
|
|
||||||
propagators = append(propagators, propagation.TraceContext{}, propagation.Baggage{})
|
|
||||||
case jaegerPropagator:
|
|
||||||
propagators = append(propagators, jaegerpropagator.Jaeger{})
|
|
||||||
case "":
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported OpenTelemetry propagator: %q", p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch len(propagators) {
|
|
||||||
case 0:
|
|
||||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
|
||||||
propagation.TraceContext{}, propagation.Baggage{},
|
|
||||||
))
|
|
||||||
case 1:
|
|
||||||
otel.SetTextMapPropagator(propagators[0])
|
|
||||||
default:
|
|
||||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagators...))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ots.tracerProvider == nil {
|
|
||||||
ots.tracerProvider = tp
|
|
||||||
}
|
|
||||||
|
|
||||||
ots.tracer = otel.GetTracerProvider().Tracer("component-main")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) Run(ctx context.Context) error {
|
|
||||||
otel.SetErrorHandler(otelErrHandler(func(err error) {
|
|
||||||
err = level.Error(ots.log).Log("msg", "OpenTelemetry handler returned an error", "err", err)
|
|
||||||
if err != nil {
|
|
||||||
ots.log.Error("OpenTelemetry log returning error", err)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
<-ctx.Done()
|
|
||||||
|
|
||||||
ots.log.Info("Closing tracing")
|
|
||||||
if ots.tracerProvider == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ctxShutdown, cancel := context.WithTimeout(ctx, time.Second*5)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := ots.tracerProvider.Shutdown(ctxShutdown); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
|
|
||||||
ctx, span := ots.tracer.Start(ctx, spanName, opts...)
|
|
||||||
opentelemetrySpan := OpentelemetrySpan{
|
|
||||||
span: span,
|
|
||||||
}
|
|
||||||
|
|
||||||
if traceID := span.SpanContext().TraceID(); traceID.IsValid() {
|
|
||||||
ctx = context.WithValue(ctx, traceKey{}, traceValue{traceID.String(), span.SpanContext().IsSampled()})
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx, opentelemetrySpan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ots *Opentelemetry) Inject(ctx context.Context, header http.Header, _ Span) {
|
|
||||||
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(header))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) End() {
|
|
||||||
s.span.End()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
|
|
||||||
s.span.SetAttributes(kv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) SetName(name string) {
|
|
||||||
s.span.SetName(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) SetStatus(code codes.Code, description string) {
|
|
||||||
s.span.SetStatus(code, description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) RecordError(err error, options ...trace.EventOption) {
|
|
||||||
s.span.RecordError(err, options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) AddEvents(keys []string, values []EventValue) {
|
|
||||||
for i, v := range values {
|
|
||||||
if v.Str != "" {
|
|
||||||
s.span.AddEvent(keys[i], trace.WithAttributes(attribute.Key(keys[i]).String(v.Str)))
|
|
||||||
}
|
|
||||||
if v.Num != 0 {
|
|
||||||
s.span.AddEvent(keys[i], trace.WithAttributes(attribute.Key(keys[i]).Int64(v.Num)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s OpentelemetrySpan) contextWithSpan(ctx context.Context) context.Context {
|
|
||||||
if s.span != nil {
|
|
||||||
ctx = trace.ContextWithSpan(ctx, s.span)
|
|
||||||
// Grafana also manages its own separate traceID in the context in addition to what opentracing handles.
|
|
||||||
// It's derived from the span. Ensure that we propagate this too.
|
|
||||||
if traceID := s.span.SpanContext().TraceID(); traceID.IsValid() {
|
|
||||||
ctx = context.WithValue(ctx, traceKey{}, traceValue{traceID.String(), s.span.SpanContext().IsSampled()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
package tracing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSplitCustomAttribs(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
expected []attribute.KeyValue
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: "key1:value:1",
|
|
||||||
expected: []attribute.KeyValue{attribute.String("key1", "value:1")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "key1:value1,key2:value2",
|
|
||||||
expected: []attribute.KeyValue{
|
|
||||||
attribute.String("key1", "value1"),
|
|
||||||
attribute.String("key2", "value2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "",
|
|
||||||
expected: []attribute.KeyValue{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
attribs, err := splitCustomAttribs(test.input)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.EqualValues(t, test.expected, attribs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitCustomAttribs_Malformed(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
expected []attribute.KeyValue
|
|
||||||
}{
|
|
||||||
{input: "key1=value1"},
|
|
||||||
{input: "key1"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := splitCustomAttribs(test.input)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOptentelemetry_ParseSettingsOpentelemetry(t *testing.T) {
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
otel := &Opentelemetry{Cfg: cfg}
|
|
||||||
|
|
||||||
otelsect := cfg.Raw.Section("tracing.opentelemetry")
|
|
||||||
jaegersect := cfg.Raw.Section("tracing.opentelemetry.jaeger")
|
|
||||||
otlpsect := cfg.Raw.Section("tracing.opentelemetry.otlp")
|
|
||||||
|
|
||||||
assert.NoError(t, otel.parseSettingsOpentelemetry())
|
|
||||||
assert.Equal(t, noopExporter, otel.Enabled)
|
|
||||||
|
|
||||||
otelsect.Key("custom_attributes")
|
|
||||||
assert.NoError(t, otel.parseSettingsOpentelemetry())
|
|
||||||
assert.Empty(t, otel.customAttribs)
|
|
||||||
|
|
||||||
otelsect.Key("custom_attributes").SetValue("key1:value1,key2:value2")
|
|
||||||
assert.NoError(t, otel.parseSettingsOpentelemetry())
|
|
||||||
expected := []attribute.KeyValue{
|
|
||||||
attribute.String("key1", "value1"),
|
|
||||||
attribute.String("key2", "value2"),
|
|
||||||
}
|
|
||||||
assert.Equal(t, expected, otel.customAttribs)
|
|
||||||
|
|
||||||
jaegersect.Key("address").SetValue("somehost:6831")
|
|
||||||
assert.NoError(t, otel.parseSettingsOpentelemetry())
|
|
||||||
assert.Equal(t, "somehost:6831", otel.Address)
|
|
||||||
assert.Equal(t, jaegerExporter, otel.Enabled)
|
|
||||||
|
|
||||||
jaegersect.Key("address").SetValue("")
|
|
||||||
otlpsect.Key("address").SetValue("somehost:4317")
|
|
||||||
assert.NoError(t, otel.parseSettingsOpentelemetry())
|
|
||||||
assert.Equal(t, "somehost:4317", otel.Address)
|
|
||||||
assert.Equal(t, otlpExporter, otel.Enabled)
|
|
||||||
}
|
|
@ -3,22 +3,30 @@ package tracing
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"math"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
opentracing "github.com/opentracing/opentracing-go"
|
"go.etcd.io/etcd/api/v3/version"
|
||||||
"github.com/opentracing/opentracing-go/ext"
|
jaegerpropagator "go.opentelemetry.io/contrib/propagators/jaeger"
|
||||||
ol "github.com/opentracing/opentracing-go/log"
|
"go.opentelemetry.io/contrib/samplers/jaegerremote"
|
||||||
"github.com/uber/jaeger-client-go"
|
"go.opentelemetry.io/otel"
|
||||||
jaegercfg "github.com/uber/jaeger-client-go/config"
|
|
||||||
"github.com/uber/jaeger-client-go/zipkin"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
tracesdk "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||||
trace "go.opentelemetry.io/otel/trace"
|
trace "go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
|
"github.com/go-kit/log/level"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
@ -28,6 +36,48 @@ const (
|
|||||||
envJaegerAgentPort = "JAEGER_AGENT_PORT"
|
envJaegerAgentPort = "JAEGER_AGENT_PORT"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
jaegerExporter string = "jaeger"
|
||||||
|
otlpExporter string = "otlp"
|
||||||
|
noopExporter string = "noop"
|
||||||
|
|
||||||
|
jaegerPropagator string = "jaeger"
|
||||||
|
w3cPropagator string = "w3c"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Opentelemetry struct {
|
||||||
|
enabled string
|
||||||
|
Address string
|
||||||
|
Propagation string
|
||||||
|
customAttribs []attribute.KeyValue
|
||||||
|
|
||||||
|
sampler string
|
||||||
|
samplerParam float64
|
||||||
|
samplerRemoteURL string
|
||||||
|
|
||||||
|
log log.Logger
|
||||||
|
|
||||||
|
tracerProvider tracerProvider
|
||||||
|
tracer trace.Tracer
|
||||||
|
|
||||||
|
Cfg *setting.Cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
type tracerProvider interface {
|
||||||
|
trace.TracerProvider
|
||||||
|
|
||||||
|
Shutdown(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpentelemetrySpan struct {
|
||||||
|
span trace.Span
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventValue struct {
|
||||||
|
Str string
|
||||||
|
Num int64
|
||||||
|
}
|
||||||
|
|
||||||
// Tracer defines the service used to create new spans.
|
// Tracer defines the service used to create new spans.
|
||||||
type Tracer interface {
|
type Tracer interface {
|
||||||
// Run implements registry.BackgroundService.
|
// Run implements registry.BackgroundService.
|
||||||
@ -83,7 +133,7 @@ type Span interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg) (Tracer, error) {
|
func ProvideService(cfg *setting.Cfg) (Tracer, error) {
|
||||||
ts, ots, err := parseSettings(cfg)
|
ots, err := ParseSettings(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -95,43 +145,18 @@ func ProvideService(cfg *setting.Cfg) (Tracer, error) {
|
|||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
})
|
})
|
||||||
|
if err := ots.initOpentelemetryTracer(); err != nil {
|
||||||
if ts.enabled {
|
return nil, err
|
||||||
return ts, ts.initJaegerGlobalTracer()
|
|
||||||
}
|
}
|
||||||
|
return ots, nil
|
||||||
return ots, ots.initOpentelemetryTracer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSettings(cfg *setting.Cfg) (*Opentracing, *Opentelemetry, error) {
|
func ParseSettings(cfg *setting.Cfg) (*Opentelemetry, error) {
|
||||||
ts, err := parseSettingsOpentracing(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return ts, nil, err
|
|
||||||
}
|
|
||||||
ots, err := ParseSettingsOpentelemetry(cfg)
|
|
||||||
return ts, ots, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSettingsOpentracing(cfg *setting.Cfg) (*Opentracing, error) {
|
|
||||||
ts := &Opentracing{
|
|
||||||
Cfg: cfg,
|
|
||||||
log: log.New("tracing"),
|
|
||||||
}
|
|
||||||
if err := ts.parseSettings(); err != nil {
|
|
||||||
return ts, err
|
|
||||||
}
|
|
||||||
if ts.enabled {
|
|
||||||
cfg.Logger.Warn("[Deprecated] the configuration setting 'tracing.jaeger' is deprecated, please use 'tracing.opentelemetry.jaeger' instead")
|
|
||||||
}
|
|
||||||
return ts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseSettingsOpentelemetry(cfg *setting.Cfg) (*Opentelemetry, error) {
|
|
||||||
ots := &Opentelemetry{
|
ots := &Opentelemetry{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
log: log.New("tracing"),
|
log: log.New("tracing"),
|
||||||
}
|
}
|
||||||
err := ots.parseSettingsOpentelemetry()
|
err := ots.parseSettings()
|
||||||
return ots, err
|
return ots, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,10 +178,6 @@ func TraceIDFromContext(c context.Context, requireSampled bool) string {
|
|||||||
// SpanFromContext returns the Span previously associated with ctx, or nil, if no such span could be found.
|
// SpanFromContext returns the Span previously associated with ctx, or nil, if no such span could be found.
|
||||||
// It is the equivalent of opentracing.SpanFromContext and trace.SpanFromContext.
|
// It is the equivalent of opentracing.SpanFromContext and trace.SpanFromContext.
|
||||||
func SpanFromContext(ctx context.Context) Span {
|
func SpanFromContext(ctx context.Context) Span {
|
||||||
// Look for both opentracing and opentelemetry spans.
|
|
||||||
if span := opentracing.SpanFromContext(ctx); span != nil {
|
|
||||||
return OpentracingSpan{span: span}
|
|
||||||
}
|
|
||||||
if span := trace.SpanFromContext(ctx); span != nil {
|
if span := trace.SpanFromContext(ctx); span != nil {
|
||||||
return OpentelemetrySpan{span: span}
|
return OpentelemetrySpan{span: span}
|
||||||
}
|
}
|
||||||
@ -173,219 +194,345 @@ func ContextWithSpan(ctx context.Context, span Span) context.Context {
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
type Opentracing struct {
|
type noopTracerProvider struct {
|
||||||
enabled bool
|
trace.TracerProvider
|
||||||
address string
|
|
||||||
customTags map[string]string
|
|
||||||
samplerType string
|
|
||||||
samplerParam float64
|
|
||||||
samplingServerURL string
|
|
||||||
log log.Logger
|
|
||||||
closer io.Closer
|
|
||||||
zipkinPropagation bool
|
|
||||||
disableSharedZipkinSpans bool
|
|
||||||
|
|
||||||
Cfg *setting.Cfg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpentracingSpan struct {
|
func (noopTracerProvider) Shutdown(ctx context.Context) error {
|
||||||
span opentracing.Span
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *Opentracing) parseSettings() error {
|
|
||||||
var section, err = ts.Cfg.Raw.GetSection("tracing.jaeger")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.address = section.Key("address").MustString("")
|
|
||||||
if ts.address == "" {
|
|
||||||
host := os.Getenv(envJaegerAgentHost)
|
|
||||||
port := os.Getenv(envJaegerAgentPort)
|
|
||||||
if host != "" || port != "" {
|
|
||||||
ts.address = fmt.Sprintf("%s:%s", host, port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ts.address != "" {
|
|
||||||
ts.enabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.customTags = splitTagSettings(section.Key("always_included_tag").MustString(""))
|
|
||||||
ts.samplerType = section.Key("sampler_type").MustString("")
|
|
||||||
ts.samplerParam = section.Key("sampler_param").MustFloat64(1)
|
|
||||||
ts.zipkinPropagation = section.Key("zipkin_propagation").MustBool(false)
|
|
||||||
ts.disableSharedZipkinSpans = section.Key("disable_shared_zipkin_spans").MustBool(false)
|
|
||||||
ts.samplingServerURL = section.Key("sampling_server_url").MustString("")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *Opentracing) initJaegerCfg() (jaegercfg.Configuration, error) {
|
func (ots *Opentelemetry) parseSettings() error {
|
||||||
cfg := jaegercfg.Configuration{
|
legacyAddress, legacyTags := "", ""
|
||||||
ServiceName: "grafana",
|
if section, err := ots.Cfg.Raw.GetSection("tracing.jaeger"); err == nil {
|
||||||
Disabled: !ts.enabled,
|
legacyAddress = section.Key("address").MustString("")
|
||||||
Sampler: &jaegercfg.SamplerConfig{
|
if legacyAddress == "" {
|
||||||
Type: ts.samplerType,
|
host, port := os.Getenv(envJaegerAgentHost), os.Getenv(envJaegerAgentPort)
|
||||||
Param: ts.samplerParam,
|
if host != "" || port != "" {
|
||||||
SamplingServerURL: ts.samplingServerURL,
|
legacyAddress = fmt.Sprintf("%s:%s", host, port)
|
||||||
},
|
}
|
||||||
Reporter: &jaegercfg.ReporterConfig{
|
|
||||||
LogSpans: false,
|
|
||||||
LocalAgentHostPort: ts.address,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := cfg.FromEnv()
|
|
||||||
if err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *Opentracing) initJaegerGlobalTracer() error {
|
|
||||||
cfg, err := ts.initJaegerCfg()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
jLogger := &jaegerLogWrapper{logger: log.New("jaeger")}
|
|
||||||
|
|
||||||
options := []jaegercfg.Option{}
|
|
||||||
options = append(options, jaegercfg.Logger(jLogger))
|
|
||||||
|
|
||||||
for tag, value := range ts.customTags {
|
|
||||||
options = append(options, jaegercfg.Tag(tag, value))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ts.zipkinPropagation {
|
|
||||||
zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator()
|
|
||||||
options = append(options,
|
|
||||||
jaegercfg.Injector(opentracing.HTTPHeaders, zipkinPropagator),
|
|
||||||
jaegercfg.Extractor(opentracing.HTTPHeaders, zipkinPropagator),
|
|
||||||
)
|
|
||||||
|
|
||||||
if !ts.disableSharedZipkinSpans {
|
|
||||||
options = append(options, jaegercfg.ZipkinSharedRPCSpan(true))
|
|
||||||
}
|
}
|
||||||
|
legacyTags = section.Key("always_included_tag").MustString("")
|
||||||
|
ots.sampler = section.Key("sampler_type").MustString("")
|
||||||
|
ots.samplerParam = section.Key("sampler_param").MustFloat64(1)
|
||||||
|
ots.samplerRemoteURL = section.Key("sampling_server_url").MustString("")
|
||||||
}
|
}
|
||||||
|
section := ots.Cfg.Raw.Section("tracing.opentelemetry")
|
||||||
tracer, closer, err := cfg.NewTracer(options...)
|
var err error
|
||||||
|
// we default to legacy tag set (attributes) if the new config format is absent
|
||||||
|
ots.customAttribs, err = splitCustomAttribs(section.Key("custom_attributes").MustString(legacyTags))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
opentracing.SetGlobalTracer(tracer)
|
section = ots.Cfg.Raw.Section("tracing.opentelemetry.jaeger")
|
||||||
|
ots.enabled = noopExporter
|
||||||
|
|
||||||
ts.closer = closer
|
// we default to legacy Jaeger agent address if the new config value is empty
|
||||||
|
ots.Address = section.Key("address").MustString(legacyAddress)
|
||||||
|
ots.Propagation = section.Key("propagation").MustString("")
|
||||||
|
if ots.Address != "" {
|
||||||
|
ots.enabled = jaegerExporter
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
section = ots.Cfg.Raw.Section("tracing.opentelemetry.otlp")
|
||||||
|
ots.Address = section.Key("address").MustString("")
|
||||||
|
if ots.Address != "" {
|
||||||
|
ots.enabled = otlpExporter
|
||||||
|
}
|
||||||
|
ots.Propagation = section.Key("propagation").MustString("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *Opentracing) Run(ctx context.Context) error {
|
func (ots *Opentelemetry) OTelExporterEnabled() bool {
|
||||||
|
return ots.enabled == otlpExporter
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitCustomAttribs(s string) ([]attribute.KeyValue, error) {
|
||||||
|
res := []attribute.KeyValue{}
|
||||||
|
|
||||||
|
attribs := strings.Split(s, ",")
|
||||||
|
for _, v := range attribs {
|
||||||
|
parts := strings.SplitN(v, ":", 2)
|
||||||
|
if len(parts) > 1 {
|
||||||
|
res = append(res, attribute.String(parts[0], parts[1]))
|
||||||
|
} else if v != "" {
|
||||||
|
return nil, fmt.Errorf("custom attribute malformed - must be in 'key:value' form: %q", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ots *Opentelemetry) initJaegerTracerProvider() (*tracesdk.TracerProvider, error) {
|
||||||
|
var ep jaeger.EndpointOption
|
||||||
|
// Create the Jaeger exporter: address can be either agent address (host:port) or collector URL
|
||||||
|
if host, port, err := net.SplitHostPort(ots.Address); err == nil {
|
||||||
|
ep = jaeger.WithAgentEndpoint(jaeger.WithAgentHost(host), jaeger.WithAgentPort(port))
|
||||||
|
} else {
|
||||||
|
ep = jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(ots.Address))
|
||||||
|
}
|
||||||
|
exp, err := jaeger.New(ep)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := resource.New(
|
||||||
|
context.Background(),
|
||||||
|
resource.WithAttributes(
|
||||||
|
// TODO: why are these attributes different from ones added to the
|
||||||
|
// OTLP provider?
|
||||||
|
semconv.ServiceNameKey.String("grafana"),
|
||||||
|
attribute.String("environment", "production"),
|
||||||
|
),
|
||||||
|
resource.WithAttributes(ots.customAttribs...),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sampler := tracesdk.AlwaysSample()
|
||||||
|
if ots.sampler == "const" || ots.sampler == "probabilistic" {
|
||||||
|
sampler = tracesdk.TraceIDRatioBased(ots.samplerParam)
|
||||||
|
} else if ots.sampler == "rateLimiting" {
|
||||||
|
sampler = newRateLimiter(ots.samplerParam)
|
||||||
|
} else if ots.sampler == "remote" {
|
||||||
|
sampler = jaegerremote.New("grafana", jaegerremote.WithSamplingServerURL(ots.samplerRemoteURL),
|
||||||
|
jaegerremote.WithInitialSampler(tracesdk.TraceIDRatioBased(ots.samplerParam)))
|
||||||
|
} else if ots.sampler != "" {
|
||||||
|
return nil, fmt.Errorf("invalid sampler type: %s", ots.sampler)
|
||||||
|
}
|
||||||
|
|
||||||
|
tp := tracesdk.NewTracerProvider(
|
||||||
|
tracesdk.WithBatcher(exp),
|
||||||
|
tracesdk.WithResource(res),
|
||||||
|
tracesdk.WithSampler(sampler),
|
||||||
|
)
|
||||||
|
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ots *Opentelemetry) initOTLPTracerProvider() (*tracesdk.TracerProvider, error) {
|
||||||
|
client := otlptracegrpc.NewClient(otlptracegrpc.WithEndpoint(ots.Address), otlptracegrpc.WithInsecure())
|
||||||
|
exp, err := otlptrace.New(context.Background(), client)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return initTracerProvider(exp, ots.customAttribs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initTracerProvider(exp tracesdk.SpanExporter, customAttribs ...attribute.KeyValue) (*tracesdk.TracerProvider, error) {
|
||||||
|
res, err := resource.New(
|
||||||
|
context.Background(),
|
||||||
|
resource.WithAttributes(
|
||||||
|
semconv.ServiceNameKey.String("grafana"),
|
||||||
|
semconv.ServiceVersionKey.String(version.Version),
|
||||||
|
),
|
||||||
|
resource.WithAttributes(customAttribs...),
|
||||||
|
resource.WithProcessRuntimeDescription(),
|
||||||
|
resource.WithTelemetrySDK(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tp := tracesdk.NewTracerProvider(
|
||||||
|
tracesdk.WithBatcher(exp),
|
||||||
|
tracesdk.WithSampler(tracesdk.ParentBased(
|
||||||
|
tracesdk.AlwaysSample(),
|
||||||
|
)),
|
||||||
|
tracesdk.WithResource(res),
|
||||||
|
)
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ots *Opentelemetry) initNoopTracerProvider() (tracerProvider, error) {
|
||||||
|
return &noopTracerProvider{TracerProvider: trace.NewNoopTracerProvider()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ots *Opentelemetry) initOpentelemetryTracer() error {
|
||||||
|
var tp tracerProvider
|
||||||
|
var err error
|
||||||
|
switch ots.enabled {
|
||||||
|
case jaegerExporter:
|
||||||
|
tp, err = ots.initJaegerTracerProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case otlpExporter:
|
||||||
|
tp, err = ots.initOTLPTracerProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
tp, err = ots.initNoopTracerProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register our TracerProvider as the global so any imported
|
||||||
|
// instrumentation in the future will default to using it
|
||||||
|
// only if tracing is enabled
|
||||||
|
if ots.enabled != "" {
|
||||||
|
otel.SetTracerProvider(tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
propagators := []propagation.TextMapPropagator{}
|
||||||
|
for _, p := range strings.Split(ots.Propagation, ",") {
|
||||||
|
switch p {
|
||||||
|
case w3cPropagator:
|
||||||
|
propagators = append(propagators, propagation.TraceContext{}, propagation.Baggage{})
|
||||||
|
case jaegerPropagator:
|
||||||
|
propagators = append(propagators, jaegerpropagator.Jaeger{})
|
||||||
|
case "":
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported OpenTelemetry propagator: %q", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(propagators) {
|
||||||
|
case 0:
|
||||||
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||||||
|
propagation.TraceContext{}, propagation.Baggage{},
|
||||||
|
))
|
||||||
|
case 1:
|
||||||
|
otel.SetTextMapPropagator(propagators[0])
|
||||||
|
default:
|
||||||
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagators...))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ots.tracerProvider == nil {
|
||||||
|
ots.tracerProvider = tp
|
||||||
|
}
|
||||||
|
|
||||||
|
ots.tracer = otel.GetTracerProvider().Tracer("component-main")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ots *Opentelemetry) Run(ctx context.Context) error {
|
||||||
|
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
|
||||||
|
err = level.Error(ots.log).Log("msg", "OpenTelemetry handler returned an error", "err", err)
|
||||||
|
if err != nil {
|
||||||
|
ots.log.Error("OpenTelemetry log returning error", err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|
||||||
if ts.closer != nil {
|
ots.log.Info("Closing tracing")
|
||||||
ts.log.Info("Closing tracing")
|
if ots.tracerProvider == nil {
|
||||||
return ts.closer.Close()
|
return nil
|
||||||
|
}
|
||||||
|
ctxShutdown, cancel := context.WithTimeout(ctx, time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ots.tracerProvider.Shutdown(ctxShutdown); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *Opentracing) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
|
func (ots *Opentelemetry) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, Span) {
|
||||||
span, ctx := opentracing.StartSpanFromContext(ctx, spanName)
|
ctx, span := ots.tracer.Start(ctx, spanName, opts...)
|
||||||
opentracingSpan := OpentracingSpan{span: span}
|
opentelemetrySpan := OpentelemetrySpan{
|
||||||
if sctx, ok := span.Context().(jaeger.SpanContext); ok {
|
span: span,
|
||||||
ctx = context.WithValue(ctx, traceKey{}, traceValue{sctx.TraceID().String(), sctx.IsSampled()})
|
|
||||||
}
|
}
|
||||||
return ctx, opentracingSpan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ts *Opentracing) Inject(ctx context.Context, header http.Header, span Span) {
|
if traceID := span.SpanContext().TraceID(); traceID.IsValid() {
|
||||||
opentracingSpan, ok := span.(OpentracingSpan)
|
ctx = context.WithValue(ctx, traceKey{}, traceValue{traceID.String(), span.SpanContext().IsSampled()})
|
||||||
if !ok {
|
|
||||||
logger.Error("Failed to cast opentracing span")
|
|
||||||
}
|
}
|
||||||
err := opentracing.GlobalTracer().Inject(
|
|
||||||
opentracingSpan.span.Context(),
|
|
||||||
opentracing.HTTPHeaders,
|
|
||||||
opentracing.HTTPHeadersCarrier(header))
|
|
||||||
|
|
||||||
if err != nil {
|
return ctx, opentelemetrySpan
|
||||||
logger.Error("Failed to inject span context instance", "err", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) End() {
|
func (ots *Opentelemetry) Inject(ctx context.Context, header http.Header, _ Span) {
|
||||||
s.span.Finish()
|
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(header))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
|
func (s OpentelemetrySpan) End() {
|
||||||
s.span.SetTag(key, value)
|
s.span.End()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) SetName(name string) {
|
func (s OpentelemetrySpan) SetAttributes(key string, value interface{}, kv attribute.KeyValue) {
|
||||||
s.span.SetOperationName(name)
|
s.span.SetAttributes(kv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) SetStatus(code codes.Code, description string) {
|
func (s OpentelemetrySpan) SetName(name string) {
|
||||||
if code == codes.Error {
|
s.span.SetName(name)
|
||||||
ext.Error.Set(s.span, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) RecordError(err error, options ...trace.EventOption) {
|
func (s OpentelemetrySpan) SetStatus(code codes.Code, description string) {
|
||||||
ext.Error.Set(s.span, true)
|
s.span.SetStatus(code, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) AddEvents(keys []string, values []EventValue) {
|
func (s OpentelemetrySpan) RecordError(err error, options ...trace.EventOption) {
|
||||||
fields := []ol.Field{}
|
s.span.RecordError(err, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s OpentelemetrySpan) AddEvents(keys []string, values []EventValue) {
|
||||||
for i, v := range values {
|
for i, v := range values {
|
||||||
if v.Str != "" {
|
if v.Str != "" {
|
||||||
field := ol.String(keys[i], v.Str)
|
s.span.AddEvent(keys[i], trace.WithAttributes(attribute.Key(keys[i]).String(v.Str)))
|
||||||
fields = append(fields, field)
|
|
||||||
}
|
}
|
||||||
if v.Num != 0 {
|
if v.Num != 0 {
|
||||||
field := ol.Int64(keys[i], v.Num)
|
s.span.AddEvent(keys[i], trace.WithAttributes(attribute.Key(keys[i]).Int64(v.Num)))
|
||||||
fields = append(fields, field)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.span.LogFields(fields...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s OpentracingSpan) contextWithSpan(ctx context.Context) context.Context {
|
func (s OpentelemetrySpan) contextWithSpan(ctx context.Context) context.Context {
|
||||||
if s.span != nil {
|
if s.span != nil {
|
||||||
ctx = opentracing.ContextWithSpan(ctx, s.span)
|
ctx = trace.ContextWithSpan(ctx, s.span)
|
||||||
// Grafana also manages its own separate traceID in the context in addition to what opentracing handles.
|
// Grafana also manages its own separate traceID in the context in addition to what opentracing handles.
|
||||||
// It's derived from the span. Ensure that we propagate this too.
|
// It's derived from the span. Ensure that we propagate this too.
|
||||||
if sctx, ok := s.span.Context().(jaeger.SpanContext); ok {
|
if traceID := s.span.SpanContext().TraceID(); traceID.IsValid() {
|
||||||
ctx = context.WithValue(ctx, traceKey{}, traceValue{sctx.TraceID().String(), sctx.IsSampled()})
|
ctx = context.WithValue(ctx, traceKey{}, traceValue{traceID.String(), s.span.SpanContext().IsSampled()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitTagSettings(input string) map[string]string {
|
type rateLimiter struct {
|
||||||
res := map[string]string{}
|
sync.Mutex
|
||||||
|
rps float64
|
||||||
|
balance float64
|
||||||
|
maxBalance float64
|
||||||
|
lastTick time.Time
|
||||||
|
|
||||||
tags := strings.Split(input, ",")
|
now func() time.Time
|
||||||
for _, v := range tags {
|
}
|
||||||
kv := strings.Split(v, ":")
|
|
||||||
if len(kv) > 1 {
|
func newRateLimiter(rps float64) *rateLimiter {
|
||||||
res[kv[0]] = kv[1]
|
return &rateLimiter{
|
||||||
}
|
rps: rps,
|
||||||
|
balance: math.Max(rps, 1),
|
||||||
|
maxBalance: math.Max(rps, 1),
|
||||||
|
lastTick: time.Now(),
|
||||||
|
now: time.Now,
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type jaegerLogWrapper struct {
|
func (rl *rateLimiter) ShouldSample(p tracesdk.SamplingParameters) tracesdk.SamplingResult {
|
||||||
logger log.Logger
|
rl.Lock()
|
||||||
|
defer rl.Unlock()
|
||||||
|
psc := trace.SpanContextFromContext(p.ParentContext)
|
||||||
|
if rl.balance >= 1 {
|
||||||
|
rl.balance -= 1
|
||||||
|
return tracesdk.SamplingResult{Decision: tracesdk.RecordAndSample, Tracestate: psc.TraceState()}
|
||||||
|
}
|
||||||
|
currentTime := rl.now()
|
||||||
|
elapsedTime := currentTime.Sub(rl.lastTick).Seconds()
|
||||||
|
rl.lastTick = currentTime
|
||||||
|
rl.balance = math.Min(rl.maxBalance, rl.balance+elapsedTime*rl.rps)
|
||||||
|
if rl.balance >= 1 {
|
||||||
|
rl.balance -= 1
|
||||||
|
return tracesdk.SamplingResult{Decision: tracesdk.RecordAndSample, Tracestate: psc.TraceState()}
|
||||||
|
}
|
||||||
|
return tracesdk.SamplingResult{Decision: tracesdk.Drop, Tracestate: psc.TraceState()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jlw *jaegerLogWrapper) Error(msg string) {
|
func (rl *rateLimiter) Description() string { return "RateLimitingSampler" }
|
||||||
jlw.logger.Error(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (jlw *jaegerLogWrapper) Infof(format string, args ...interface{}) {
|
|
||||||
msg := fmt.Sprintf(format, args...)
|
|
||||||
jlw.logger.Info(msg)
|
|
||||||
}
|
|
||||||
|
@ -5,137 +5,163 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGroupSplit(t *testing.T) {
|
// TODO(zserge) Add proper tests for opentelemetry
|
||||||
|
|
||||||
|
func TestSplitCustomAttribs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expected map[string]string
|
expected []attribute.KeyValue
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: "tag1:value1,tag2:value2",
|
input: "key1:value:1",
|
||||||
expected: map[string]string{
|
expected: []attribute.KeyValue{attribute.String("key1", "value:1")},
|
||||||
"tag1": "value1",
|
},
|
||||||
"tag2": "value2",
|
{
|
||||||
|
input: "key1:value1,key2:value2",
|
||||||
|
expected: []attribute.KeyValue{
|
||||||
|
attribute.String("key1", "value1"),
|
||||||
|
attribute.String("key2", "value2"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: "",
|
input: "",
|
||||||
expected: map[string]string{},
|
expected: []attribute.KeyValue{},
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "tag1",
|
|
||||||
expected: map[string]string{},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tags := splitTagSettings(test.input)
|
attribs, err := splitCustomAttribs(test.input)
|
||||||
for k, v := range test.expected {
|
assert.NoError(t, err)
|
||||||
value, exists := tags[k]
|
assert.EqualValues(t, test.expected, attribs)
|
||||||
assert.Truef(t, exists, "Tag %q not found for input %q", k, test.input)
|
|
||||||
assert.Equalf(t, v, value, "Tag %q has wrong value for input %q", k, test.input)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInitJaegerCfg_Default(t *testing.T) {
|
func TestSplitCustomAttribs_Malformed(t *testing.T) {
|
||||||
ts := &Opentracing{}
|
tests := []struct {
|
||||||
cfg, err := ts.initJaegerCfg()
|
input string
|
||||||
require.NoError(t, err)
|
}{
|
||||||
|
{input: "key1=value1"},
|
||||||
|
{input: "key1"},
|
||||||
|
}
|
||||||
|
|
||||||
assert.True(t, cfg.Disabled)
|
for _, test := range tests {
|
||||||
|
_, err := splitCustomAttribs(test.input)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInitJaegerCfg_Enabled(t *testing.T) {
|
func TestTracingConfig(t *testing.T) {
|
||||||
ts := &Opentracing{enabled: true}
|
for _, test := range []struct {
|
||||||
cfg, err := ts.initJaegerCfg()
|
Name string
|
||||||
require.NoError(t, err)
|
Cfg string
|
||||||
|
Env map[string]string
|
||||||
assert.False(t, cfg.Disabled)
|
ExpectedExporter string
|
||||||
assert.Equal(t, "localhost:6831", cfg.Reporter.LocalAgentHostPort)
|
ExpectedAddress string
|
||||||
}
|
ExpectedPropagator string
|
||||||
|
ExpectedAttrs []attribute.KeyValue
|
||||||
func TestInitJaegerCfg_DisabledViaEnv(t *testing.T) {
|
}{
|
||||||
err := os.Setenv("JAEGER_DISABLED", "true")
|
{
|
||||||
require.NoError(t, err)
|
Name: "default config uses noop exporter",
|
||||||
defer func() {
|
Cfg: "",
|
||||||
err := os.Unsetenv("JAEGER_DISABLED")
|
ExpectedExporter: noopExporter,
|
||||||
require.NoError(t, err)
|
ExpectedAttrs: []attribute.KeyValue{},
|
||||||
}()
|
},
|
||||||
|
{
|
||||||
ts := &Opentracing{enabled: true}
|
Name: "custom attributes are parsed",
|
||||||
cfg, err := ts.initJaegerCfg()
|
Cfg: `
|
||||||
require.NoError(t, err)
|
[tracing.opentelemetry]
|
||||||
|
custom_attributes = key1:value1,key2:value2
|
||||||
assert.True(t, cfg.Disabled)
|
`,
|
||||||
}
|
ExpectedExporter: noopExporter,
|
||||||
|
ExpectedAttrs: []attribute.KeyValue{attribute.String("key1", "value1"), attribute.String("key2", "value2")},
|
||||||
func TestInitJaegerCfg_EnabledViaEnv(t *testing.T) {
|
},
|
||||||
err := os.Setenv("JAEGER_DISABLED", "false")
|
{
|
||||||
require.NoError(t, err)
|
Name: "jaeger address is parsed",
|
||||||
defer func() {
|
Cfg: `
|
||||||
err := os.Unsetenv("JAEGER_DISABLED")
|
[tracing.opentelemetry.jaeger]
|
||||||
require.NoError(t, err)
|
address = jaeger.example.com:6831
|
||||||
}()
|
`,
|
||||||
|
ExpectedExporter: jaegerExporter,
|
||||||
ts := &Opentracing{enabled: false}
|
ExpectedAddress: "jaeger.example.com:6831",
|
||||||
cfg, err := ts.initJaegerCfg()
|
ExpectedAttrs: []attribute.KeyValue{},
|
||||||
require.NoError(t, err)
|
},
|
||||||
|
{
|
||||||
assert.False(t, cfg.Disabled)
|
Name: "OTLP address is parsed",
|
||||||
}
|
Cfg: `
|
||||||
|
[tracing.opentelemetry.otlp]
|
||||||
func TestInitJaegerCfg_InvalidEnvVar(t *testing.T) {
|
address = otlp.example.com:4317
|
||||||
err := os.Setenv("JAEGER_DISABLED", "totallybogus")
|
`,
|
||||||
require.NoError(t, err)
|
ExpectedExporter: otlpExporter,
|
||||||
defer func() {
|
ExpectedAddress: "otlp.example.com:4317",
|
||||||
err := os.Unsetenv("JAEGER_DISABLED")
|
ExpectedAttrs: []attribute.KeyValue{},
|
||||||
require.NoError(t, err)
|
},
|
||||||
}()
|
{
|
||||||
|
Name: "legacy config format is supported",
|
||||||
ts := &Opentracing{}
|
Cfg: `
|
||||||
_, err = ts.initJaegerCfg()
|
[tracing.jaeger]
|
||||||
require.EqualError(t, err, "cannot parse env var JAEGER_DISABLED=totallybogus: strconv.ParseBool: parsing \"totallybogus\": invalid syntax")
|
address = jaeger.example.com:6831
|
||||||
}
|
`,
|
||||||
|
ExpectedExporter: jaegerExporter,
|
||||||
func TestInitJaegerCfg_EnabledViaHost(t *testing.T) {
|
ExpectedAddress: "jaeger.example.com:6831",
|
||||||
require.NoError(t, os.Setenv("JAEGER_AGENT_HOST", "example.com"))
|
ExpectedAttrs: []attribute.KeyValue{},
|
||||||
defer func() {
|
},
|
||||||
require.NoError(t, os.Unsetenv("JAEGER_AGENT_HOST"))
|
{
|
||||||
}()
|
Name: "legacy env variables are supproted",
|
||||||
|
Cfg: `[tracing.jaeger]`,
|
||||||
cfg := setting.NewCfg()
|
Env: map[string]string{
|
||||||
ts := &Opentracing{Cfg: cfg}
|
"JAEGER_AGENT_HOST": "example.com",
|
||||||
_, err := ts.Cfg.Raw.NewSection("tracing.jaeger")
|
"JAEGER_AGENT_PORT": "12345",
|
||||||
require.NoError(t, err)
|
},
|
||||||
require.NoError(t, ts.parseSettings())
|
ExpectedExporter: jaegerExporter,
|
||||||
jaegerCfg, err := ts.initJaegerCfg()
|
ExpectedAddress: "example.com:12345",
|
||||||
require.NoError(t, err)
|
ExpectedAttrs: []attribute.KeyValue{},
|
||||||
|
},
|
||||||
assert.False(t, jaegerCfg.Disabled)
|
{
|
||||||
assert.Equal(t, "example.com:6831", jaegerCfg.Reporter.LocalAgentHostPort)
|
Name: "opentelemetry config format is prioritised over legacy jaeger",
|
||||||
}
|
Cfg: `
|
||||||
|
[tracing.jaeger]
|
||||||
func TestInitJaegerCfg_EnabledViaHostPort(t *testing.T) {
|
address = foo.com:6831
|
||||||
require.NoError(t, os.Setenv("JAEGER_AGENT_HOST", "example.com"))
|
custom_tags = a:b
|
||||||
require.NoError(t, os.Setenv("JAEGER_AGENT_PORT", "12345"))
|
[tracing.opentelemetry]
|
||||||
defer func() {
|
custom_attributes = c:d
|
||||||
require.NoError(t, os.Unsetenv("JAEGER_AGENT_HOST"))
|
[tracing.opentelemetry.jaeger]
|
||||||
require.NoError(t, os.Unsetenv("JAEGER_AGENT_PORT"))
|
address = bar.com:6831
|
||||||
}()
|
`,
|
||||||
|
ExpectedExporter: jaegerExporter,
|
||||||
cfg := setting.NewCfg()
|
ExpectedAddress: "bar.com:6831",
|
||||||
ts := &Opentracing{Cfg: cfg}
|
ExpectedAttrs: []attribute.KeyValue{attribute.String("c", "d")},
|
||||||
_, err := ts.Cfg.Raw.NewSection("tracing.jaeger")
|
},
|
||||||
require.NoError(t, err)
|
} {
|
||||||
require.NoError(t, ts.parseSettings())
|
t.Run(test.Name, func(t *testing.T) {
|
||||||
jaegerCfg, err := ts.initJaegerCfg()
|
// export envioronment variables
|
||||||
require.NoError(t, err)
|
if test.Env != nil {
|
||||||
|
for k, v := range test.Env {
|
||||||
assert.False(t, jaegerCfg.Disabled)
|
assert.NoError(t, os.Setenv(k, v))
|
||||||
assert.Equal(t, "example.com:12345", jaegerCfg.Reporter.LocalAgentHostPort)
|
}
|
||||||
|
defer func() {
|
||||||
|
for k := range test.Env {
|
||||||
|
assert.NoError(t, os.Unsetenv(k))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// parse config sections
|
||||||
|
cfg := setting.NewCfg()
|
||||||
|
err := cfg.Raw.Append([]byte(test.Cfg))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// create tracer
|
||||||
|
tracer, err := ProvideService(cfg)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// make sure tracker is properly configured
|
||||||
|
otel := tracer.(*Opentelemetry)
|
||||||
|
assert.Equal(t, test.ExpectedExporter, otel.enabled)
|
||||||
|
assert.Equal(t, test.ExpectedAddress, otel.Address)
|
||||||
|
assert.Equal(t, test.ExpectedPropagator, otel.Propagation)
|
||||||
|
assert.Equal(t, test.ExpectedAttrs, otel.customAttribs)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
// newTracingCfg creates a plugins tracing configuration based on the provided Grafana tracing config.
|
// newTracingCfg creates a plugins tracing configuration based on the provided Grafana tracing config.
|
||||||
// If OpenTelemetry (OTLP) is disabled, a zero-value OpenTelemetryCfg is returned.
|
// If OpenTelemetry (OTLP) is disabled, a zero-value OpenTelemetryCfg is returned.
|
||||||
func newTracingCfg(grafanaCfg *setting.Cfg) (pCfg.Tracing, error) {
|
func newTracingCfg(grafanaCfg *setting.Cfg) (pCfg.Tracing, error) {
|
||||||
ots, err := tracing.ParseSettingsOpentelemetry(grafanaCfg)
|
ots, err := tracing.ParseSettings(grafanaCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pCfg.Tracing{}, fmt.Errorf("parse settings: %w", err)
|
return pCfg.Tracing{}, fmt.Errorf("parse settings: %w", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user