github.com/m3db/m3@v1.5.0/src/x/opentracing/tracing.go (about)

     1  // Copyright (c) 2019 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package opentracing
    22  
    23  import (
    24  	"context"
    25  	"fmt"
    26  	"io"
    27  	"runtime"
    28  	"strings"
    29  	"time"
    30  
    31  	lightstep "github.com/lightstep/lightstep-tracer-go"
    32  	"github.com/opentracing/opentracing-go"
    33  	"github.com/uber-go/tally"
    34  	jaegercfg "github.com/uber/jaeger-client-go/config"
    35  	jaegerzap "github.com/uber/jaeger-client-go/log/zap"
    36  	jaegertally "github.com/uber/jaeger-lib/metrics/tally"
    37  	"go.opentelemetry.io/otel/attribute"
    38  	otelopentracing "go.opentelemetry.io/otel/bridge/opentracing"
    39  	sdktrace "go.opentelemetry.io/otel/sdk/trace"
    40  	"go.uber.org/zap"
    41  
    42  	"github.com/m3db/m3/src/x/instrument"
    43  	"github.com/m3db/m3/src/x/opentelemetry"
    44  )
    45  
    46  const (
    47  	spanTagBuildRevision  = "build.revision"
    48  	spanTagBuildVersion   = "build.version"
    49  	spanTagBuildBranch    = "build.branch"
    50  	spanTagBuildDate      = "build.date"
    51  	spanTagBuildTimeUnix  = "build.time_unix"
    52  	spanTagBuildGoVersion = "build.go_version"
    53  )
    54  
    55  var (
    56  	// TracingBackendOpenTelemetry indicates the OpenTelemetry backend should be used.
    57  	TracingBackendOpenTelemetry = "opentelemetry"
    58  	// TracingBackendJaeger indicates the Jaeger backend should be used.
    59  	TracingBackendJaeger = "jaeger"
    60  	// TracingBackendLightstep indicates the LightStep backend should be used.
    61  	TracingBackendLightstep = "lightstep"
    62  
    63  	supportedBackends = []string{
    64  		TracingBackendOpenTelemetry,
    65  		TracingBackendJaeger,
    66  		TracingBackendLightstep,
    67  	}
    68  
    69  	tracerSpanTags = map[string]string{
    70  		spanTagBuildRevision:  instrument.Revision,
    71  		spanTagBuildBranch:    instrument.Branch,
    72  		spanTagBuildVersion:   instrument.Version,
    73  		spanTagBuildDate:      instrument.BuildDate,
    74  		spanTagBuildTimeUnix:  instrument.BuildTimeUnix,
    75  		spanTagBuildGoVersion: runtime.Version(),
    76  	}
    77  )
    78  
    79  // TracingConfiguration configures an opentracing backend for m3query to use. Currently only jaeger is supported.
    80  // Tracing is disabled if no backend is specified.
    81  type TracingConfiguration struct {
    82  	ServiceName   string                      `yaml:"serviceName"`
    83  	Backend       string                      `yaml:"backend"`
    84  	OpenTelemetry opentelemetry.Configuration `yaml:"opentelemetry"`
    85  	Jaeger        jaegercfg.Configuration     `yaml:"jaeger"`
    86  	Lightstep     lightstep.Options           `yaml:"lightstep"`
    87  }
    88  
    89  // NewTracer returns a tracer configured with the configuration provided by this struct. The tracer's concrete
    90  // type is determined by cfg.Backend. Currently only `"jaeger"` is supported. `""` implies
    91  // disabled (NoopTracer).
    92  func (cfg *TracingConfiguration) NewTracer(
    93  	defaultServiceName string,
    94  	scope tally.Scope,
    95  	logger *zap.Logger,
    96  ) (opentracing.Tracer, io.Closer, error) {
    97  	switch cfg.Backend {
    98  	case "":
    99  		return opentracing.NoopTracer{}, noopCloser{}, nil
   100  
   101  	case TracingBackendOpenTelemetry:
   102  		logger.Info("initializing OpenTelemetry tracer")
   103  		return cfg.newOpenTelemetryTracer(defaultServiceName, scope)
   104  
   105  	case TracingBackendJaeger:
   106  		logger.Info("initializing Jaeger tracer")
   107  		return cfg.newJaegerTracer(defaultServiceName, scope, logger)
   108  
   109  	case TracingBackendLightstep:
   110  		logger.Info("initializing LightStep tracer")
   111  		return cfg.newLightstepTracer(defaultServiceName)
   112  
   113  	default:
   114  		return nil, nil, fmt.Errorf("unknown tracing backend: %s. Supported backends are: [%s]",
   115  			cfg.Backend,
   116  			strings.Join(supportedBackends, ","))
   117  	}
   118  }
   119  
   120  func (cfg *TracingConfiguration) newOpenTelemetryTracer(
   121  	defaultServiceName string,
   122  	scope tally.Scope,
   123  ) (opentracing.Tracer, io.Closer, error) {
   124  	if cfg.OpenTelemetry.ServiceName == "" {
   125  		cfg.OpenTelemetry.ServiceName = defaultServiceName
   126  	}
   127  
   128  	ctx := context.Background()
   129  	attrs := make([]attribute.KeyValue, 0, len(tracerSpanTags))
   130  	for k, v := range tracerSpanTags {
   131  		attrs = append(attrs, attribute.String(k, v))
   132  	}
   133  	opts := opentelemetry.TracerProviderOptions{Attributes: attrs}
   134  	tracerProvider, err := cfg.OpenTelemetry.NewTracerProvider(ctx, scope, opts)
   135  	if err != nil {
   136  		return nil, nil, err
   137  	}
   138  
   139  	tracer := tracerProvider.Tracer("")
   140  	ctx, openTracingBridgeTracer, _ := otelopentracing.NewTracerPairWithContext(ctx, tracer)
   141  	closer := newTraceProviderCloser(ctx, tracerProvider)
   142  	return openTracingBridgeTracer, closer, err
   143  }
   144  
   145  type traceProviderCloser struct {
   146  	ctx            context.Context
   147  	tracerProvider *sdktrace.TracerProvider
   148  }
   149  
   150  func newTraceProviderCloser(
   151  	ctx context.Context,
   152  	tracerProvider *sdktrace.TracerProvider,
   153  ) io.Closer {
   154  	return &traceProviderCloser{
   155  		ctx:            ctx,
   156  		tracerProvider: tracerProvider,
   157  	}
   158  }
   159  
   160  func (c *traceProviderCloser) Close() error {
   161  	return c.tracerProvider.Shutdown(c.ctx)
   162  }
   163  
   164  func (cfg *TracingConfiguration) newJaegerTracer(
   165  	defaultServiceName string,
   166  	scope tally.Scope,
   167  	logger *zap.Logger,
   168  ) (opentracing.Tracer, io.Closer, error) {
   169  	if cfg.Jaeger.ServiceName == "" {
   170  		cfg.Jaeger.ServiceName = defaultServiceName
   171  	}
   172  
   173  	for k, v := range tracerSpanTags {
   174  		cfg.Jaeger.Tags = append(cfg.Jaeger.Tags, opentracing.Tag{
   175  			Key:   k,
   176  			Value: v,
   177  		})
   178  	}
   179  
   180  	tracer, jaegerCloser, err := cfg.Jaeger.NewTracer(
   181  		jaegercfg.Logger(jaegerzap.NewLogger(logger)),
   182  		jaegercfg.Metrics(jaegertally.Wrap(scope)))
   183  	if err != nil {
   184  		return nil, nil, fmt.Errorf("failed to initialize jaeger: %s", err.Error())
   185  	}
   186  
   187  	return tracer, jaegerCloser, nil
   188  }
   189  
   190  func (cfg *TracingConfiguration) newLightstepTracer(
   191  	serviceName string,
   192  ) (opentracing.Tracer, io.Closer, error) {
   193  	if cfg.Lightstep.Tags == nil {
   194  		cfg.Lightstep.Tags = opentracing.Tags{}
   195  	}
   196  
   197  	tags := cfg.Lightstep.Tags
   198  	if _, ok := tags[lightstep.ComponentNameKey]; !ok {
   199  		tags[lightstep.ComponentNameKey] = serviceName
   200  	}
   201  
   202  	for k, v := range tracerSpanTags {
   203  		tags[k] = v
   204  	}
   205  
   206  	tracer, err := lightstep.CreateTracer(cfg.Lightstep)
   207  	if err != nil {
   208  		return nil, nil, fmt.Errorf("failed to create lightstep tracer: %v", err)
   209  	}
   210  
   211  	closer := &lightstepCloser{tracer: tracer}
   212  	return tracer, closer, nil
   213  }
   214  
   215  type lightstepCloser struct {
   216  	tracer lightstep.Tracer
   217  }
   218  
   219  func (l *lightstepCloser) Close() error {
   220  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
   221  	l.tracer.Close(ctx)
   222  	cancel()
   223  	return ctx.Err()
   224  }
   225  
   226  type noopCloser struct{}
   227  
   228  func (noopCloser) Close() error {
   229  	return nil
   230  }