github.com/m3db/m3@v1.5.0/src/x/opentelemetry/config.go (about)

     1  // Copyright (c) 2021 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 opentelemetry provides Open Telemetry configuration.
    22  package opentelemetry
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  
    28  	"github.com/uber-go/tally"
    29  	"go.opentelemetry.io/otel"
    30  	"go.opentelemetry.io/otel/attribute"
    31  	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    32  	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    33  	"go.opentelemetry.io/otel/propagation"
    34  	"go.opentelemetry.io/otel/sdk/resource"
    35  	sdktrace "go.opentelemetry.io/otel/sdk/trace"
    36  	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
    37  	"google.golang.org/grpc"
    38  )
    39  
    40  // Configuration configures an OpenTelemetry trace provider.
    41  type Configuration struct {
    42  	ServiceName string            `yaml:"serviceName"`
    43  	Endpoint    string            `yaml:"endpoint"`
    44  	Insecure    bool              `yaml:"insecure"`
    45  	Attributes  map[string]string `yaml:"attributes"`
    46  }
    47  
    48  // TracerProviderOptions is a set of options to use when creating the
    49  // trace provider.
    50  type TracerProviderOptions struct {
    51  	// Attributes is a set of programmatic attributes to add at construction.
    52  	Attributes []attribute.KeyValue
    53  }
    54  
    55  // NewTracerProvider returns a new tracer provider.
    56  func (c Configuration) NewTracerProvider(
    57  	ctx context.Context,
    58  	scope tally.Scope,
    59  	opts TracerProviderOptions,
    60  ) (*sdktrace.TracerProvider, error) {
    61  	attributes := make([]attribute.KeyValue, 0, 1+len(c.Attributes)+len(opts.Attributes))
    62  	attributes = append(attributes, semconv.ServiceNameKey.String(c.ServiceName))
    63  	for k, v := range c.Attributes {
    64  		attributes = append(attributes, attribute.String(k, v))
    65  	}
    66  	attributes = append(attributes, opts.Attributes...)
    67  
    68  	res, err := resource.New(ctx, resource.WithAttributes(attributes...))
    69  	if err != nil {
    70  		return nil, fmt.Errorf("failed to create resource: %w", err)
    71  	}
    72  
    73  	driverOpts := []otlptracegrpc.Option{
    74  		otlptracegrpc.WithEndpoint(c.Endpoint),
    75  		otlptracegrpc.WithDialOption(grpc.WithBlock()),
    76  	}
    77  	if c.Insecure {
    78  		driverOpts = append(driverOpts, otlptracegrpc.WithInsecure())
    79  	}
    80  	driver := otlptracegrpc.NewClient(driverOpts...)
    81  	traceExporter, err := otlptrace.New(ctx, driver)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("failed to trace exporter: %w", err)
    84  	}
    85  
    86  	// Register the trace exporter with a TracerProvider, using a batch
    87  	// span processor to aggregate spans before export.
    88  	batchSpanProcessor := sdktrace.NewBatchSpanProcessor(traceExporter)
    89  	tracerMetricsProcessor := newTraceSpanProcessor(scope)
    90  	tracerProvider := sdktrace.NewTracerProvider(
    91  		sdktrace.WithSampler(sdktrace.AlwaysSample()),
    92  		sdktrace.WithResource(res),
    93  		sdktrace.WithSpanProcessor(batchSpanProcessor),
    94  		sdktrace.WithSpanProcessor(tracerMetricsProcessor),
    95  	)
    96  	otel.SetTracerProvider(tracerProvider)
    97  	otel.SetTextMapPropagator(propagation.TraceContext{})
    98  
    99  	return tracerProvider, nil
   100  }
   101  
   102  type traceSpanProcessor struct {
   103  	traceStart       tally.Counter
   104  	traceEnd         tally.Counter
   105  	tracerShutdown   tally.Counter
   106  	tracerForceFlush tally.Counter
   107  }
   108  
   109  func newTraceSpanProcessor(scope tally.Scope) sdktrace.SpanProcessor {
   110  	traceScope := scope.SubScope("trace")
   111  	tracerScope := scope.SubScope("tracer")
   112  	return &traceSpanProcessor{
   113  		traceStart:       traceScope.Counter("start"),
   114  		traceEnd:         traceScope.Counter("end"),
   115  		tracerShutdown:   tracerScope.Counter("shutdown"),
   116  		tracerForceFlush: tracerScope.Counter("force-flush"),
   117  	}
   118  }
   119  
   120  func (p *traceSpanProcessor) OnStart(parent context.Context, s sdktrace.ReadWriteSpan) {
   121  	p.traceStart.Inc(1)
   122  }
   123  
   124  func (p *traceSpanProcessor) OnEnd(s sdktrace.ReadOnlySpan) {
   125  	p.traceEnd.Inc(1)
   126  }
   127  
   128  func (p *traceSpanProcessor) Shutdown(ctx context.Context) error {
   129  	p.tracerShutdown.Inc(1)
   130  	return nil
   131  }
   132  
   133  func (p *traceSpanProcessor) ForceFlush(ctx context.Context) error {
   134  	p.tracerForceFlush.Inc(1)
   135  	return nil
   136  }