github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/tracing/tracer.go (about)

     1  package tracing
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"os"
     7  	"strings"
     8  
     9  	instana "github.com/instana/go-sensor"
    10  	"github.com/uber/jaeger-client-go"
    11  
    12  	"github.com/opentracing/opentracing-go"
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/ory/x/logrusx"
    16  
    17  	zipkinOT "github.com/openzipkin-contrib/zipkin-go-opentracing"
    18  	"github.com/openzipkin/zipkin-go"
    19  	zipkinHttp "github.com/openzipkin/zipkin-go/reporter/http"
    20  
    21  	jaegerConf "github.com/uber/jaeger-client-go/config"
    22  	jaegerZipkin "github.com/uber/jaeger-client-go/zipkin"
    23  
    24  	datadogOpentracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer"
    25  	datadogTracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
    26  
    27  	"go.opentelemetry.io/otel"
    28  	otelOpentracing "go.opentelemetry.io/otel/bridge/opentracing"
    29  	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    30  	"go.opentelemetry.io/otel/sdk/resource"
    31  	otelSdkTrace "go.opentelemetry.io/otel/sdk/trace"
    32  	semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
    33  
    34  	"go.elastic.co/apm"
    35  	"go.elastic.co/apm/module/apmot"
    36  )
    37  
    38  // Tracer encapsulates tracing abilities.
    39  type Tracer struct {
    40  	Config *Config
    41  
    42  	l      *logrusx.Logger
    43  	tracer opentracing.Tracer
    44  	closer io.Closer
    45  }
    46  
    47  func New(l *logrusx.Logger, c *Config) (*Tracer, error) {
    48  	t := &Tracer{Config: c, l: l}
    49  
    50  	if err := t.setup(); err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return t, nil
    55  }
    56  
    57  // setup sets up the tracer. Currently supports jaeger.
    58  func (t *Tracer) setup() error {
    59  	switch strings.ToLower(t.Config.Provider) {
    60  	case "jaeger":
    61  		jc, err := jaegerConf.FromEnv()
    62  
    63  		if err != nil {
    64  			return err
    65  		}
    66  
    67  		if t.Config.Providers.Jaeger.Sampling.ServerURL != "" {
    68  			jc.Sampler.SamplingServerURL = t.Config.Providers.Jaeger.Sampling.ServerURL
    69  		}
    70  
    71  		if t.Config.Providers.Jaeger.Sampling.Type != "" {
    72  			jc.Sampler.Type = t.Config.Providers.Jaeger.Sampling.Type
    73  		}
    74  
    75  		if t.Config.Providers.Jaeger.Sampling.Value != 0 {
    76  			jc.Sampler.Param = t.Config.Providers.Jaeger.Sampling.Value
    77  		}
    78  
    79  		if t.Config.Providers.Jaeger.LocalAgentAddress != "" {
    80  			jc.Reporter.LocalAgentHostPort = t.Config.Providers.Jaeger.LocalAgentAddress
    81  		}
    82  
    83  		var configs []jaegerConf.Option
    84  
    85  		if t.Config.Providers.Jaeger.MaxTagValueLength != jaeger.DefaultMaxTagValueLength {
    86  			configs = append(configs, jaegerConf.MaxTagValueLength(t.Config.Providers.Jaeger.MaxTagValueLength))
    87  		}
    88  
    89  		// This works in other jaeger clients, but is not part of jaeger-client-go
    90  		if t.Config.Providers.Jaeger.Propagation == "b3" {
    91  			zipkinPropagator := jaegerZipkin.NewZipkinB3HTTPHeaderPropagator()
    92  			configs = append(
    93  				configs,
    94  				jaegerConf.Injector(opentracing.HTTPHeaders, zipkinPropagator),
    95  				jaegerConf.Extractor(opentracing.HTTPHeaders, zipkinPropagator),
    96  			)
    97  		}
    98  
    99  		closer, err := jc.InitGlobalTracer(
   100  			t.Config.ServiceName,
   101  			configs...,
   102  		)
   103  
   104  		if err != nil {
   105  			return err
   106  		}
   107  
   108  		t.closer = closer
   109  		t.tracer = opentracing.GlobalTracer()
   110  		t.l.Infof("Jaeger tracer configured!")
   111  	case "zipkin":
   112  		if t.Config.Providers.Zipkin.ServerURL == "" {
   113  			return errors.Errorf("Zipkin's server url is required")
   114  		}
   115  
   116  		reporter := zipkinHttp.NewReporter(t.Config.Providers.Zipkin.ServerURL)
   117  
   118  		endpoint, err := zipkin.NewEndpoint(t.Config.ServiceName, "")
   119  
   120  		if err != nil {
   121  			return err
   122  		}
   123  
   124  		nativeTracer, err := zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(endpoint))
   125  
   126  		if err != nil {
   127  			return err
   128  		}
   129  
   130  		opentracing.SetGlobalTracer(zipkinOT.Wrap(nativeTracer))
   131  
   132  		t.closer = reporter
   133  		t.tracer = opentracing.GlobalTracer()
   134  		t.l.Infof("Zipkin tracer configured!")
   135  	case "datadog":
   136  		var serviceName = os.Getenv("DD_SERVICE")
   137  		if serviceName == "" {
   138  			serviceName = t.Config.ServiceName
   139  		}
   140  
   141  		opentracing.SetGlobalTracer(datadogOpentracer.New(datadogTracer.WithService(serviceName)))
   142  
   143  		t.closer = datadogCloser{}
   144  		t.tracer = opentracing.GlobalTracer()
   145  		t.l.Infof("DataDog tracer configured!")
   146  	case "elastic-apm":
   147  		var serviceName = os.Getenv("ELASTIC_APM_SERVICE_NAME")
   148  		if serviceName == "" {
   149  			serviceName = t.Config.ServiceName
   150  		}
   151  
   152  		tr, err := apm.NewTracer(serviceName, "")
   153  		if err != nil {
   154  			return err
   155  		}
   156  		opentracing.SetGlobalTracer(apmot.New(apmot.WithTracer(tr)))
   157  
   158  		//t.closer = tr.Close
   159  		t.tracer = opentracing.GlobalTracer()
   160  		t.l.Infof("Elastic APM tracer configured!")
   161  
   162  	case "instana":
   163  		opts := instana.DefaultOptions()
   164  		var serviceName = os.Getenv("INSTANA_SERVICE_NAME")
   165  		if serviceName == "" {
   166  			serviceName = t.Config.ServiceName
   167  		}
   168  		opts.Service = serviceName
   169  		// all other settings can be configured using environment variables
   170  
   171  		t.tracer = instana.NewTracerWithOptions(opts)
   172  		opentracing.SetGlobalTracer(t.tracer)
   173  
   174  		t.l.Infof("Instana tracer configured!")
   175  	case "otel":
   176  		ctx := context.Background()
   177  		var serviceName = os.Getenv("OTEL_SERVICE_NAME")
   178  		if serviceName == "" {
   179  			serviceName = t.Config.ServiceName
   180  		}
   181  
   182  		res, err := resource.New(ctx,
   183  			resource.WithAttributes(
   184  				semconv.ServiceNameKey.String(serviceName),
   185  			),
   186  		)
   187  		if err != nil {
   188  			return errors.Wrap(err, "new otel resource")
   189  		}
   190  
   191  		exporter, err := otlptracehttp.New(ctx)
   192  		if err != nil {
   193  			return errors.Wrap(err, "new otel exporter")
   194  		}
   195  
   196  		tp := otelSdkTrace.NewTracerProvider(
   197  			otelSdkTrace.WithResource(res),
   198  			otelSdkTrace.WithSpanProcessor(
   199  				otelSdkTrace.NewSimpleSpanProcessor(exporter),
   200  			),
   201  		)
   202  
   203  		otel.SetTracerProvider(tp)
   204  
   205  		bridge := otelOpentracing.NewBridgeTracer()
   206  		bridge.SetOpenTelemetryTracer(otel.Tracer(""))
   207  
   208  		t.tracer = bridge
   209  		opentracing.SetGlobalTracer(t.tracer)
   210  
   211  		t.l.Infof("OTEL tracer configured!")
   212  	case "":
   213  		t.l.Infof("No tracer configured - skipping tracing setup")
   214  	default:
   215  		return errors.Errorf("unknown tracer: %s", t.Config.Provider)
   216  	}
   217  	return nil
   218  }
   219  
   220  // IsLoaded returns true if the tracer has been loaded.
   221  func (t *Tracer) IsLoaded() bool {
   222  	if t == nil || t.tracer == nil {
   223  		return false
   224  	}
   225  	return true
   226  }
   227  
   228  // Tracer returns the wrapped tracer
   229  func (t *Tracer) Tracer() opentracing.Tracer {
   230  	return t.tracer
   231  }
   232  
   233  // Close closes the tracer.
   234  func (t *Tracer) Close() {
   235  	if t.closer != nil {
   236  		err := t.closer.Close()
   237  		if err != nil {
   238  			t.l.WithError(err).Error("Unable to close tracer.")
   239  		}
   240  	}
   241  }