github.com/thanos-io/thanos@v0.32.5/pkg/tracing/jaeger/config_yaml.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package jaeger
     5  
     6  import (
     7  	"log"
     8  	"math"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  
    14  	glog "github.com/go-kit/log"
    15  	"github.com/go-kit/log/level"
    16  	"go.opentelemetry.io/contrib/samplers/jaegerremote"
    17  	"go.opentelemetry.io/otel/attribute"
    18  	otel_jaeger "go.opentelemetry.io/otel/exporters/jaeger"
    19  	tracesdk "go.opentelemetry.io/otel/sdk/trace"
    20  )
    21  
    22  const (
    23  	SamplerTypeRemote        = "remote"
    24  	SamplerTypeProbabilistic = "probabilistic"
    25  	SamplerTypeConstant      = "const"
    26  	SamplerTypeRateLimiting  = "ratelimiting"
    27  )
    28  
    29  type ParentBasedSamplerConfig struct {
    30  	LocalParentSampled  bool `yaml:"local_parent_sampled"`
    31  	RemoteParentSampled bool `yaml:"remote_parent_sampled"`
    32  }
    33  
    34  // Config - YAML configuration. For details see to https://github.com/jaegertracing/jaeger-client-go#environment-variables.
    35  type Config struct {
    36  	ServiceName                        string                   `yaml:"service_name"`
    37  	Disabled                           bool                     `yaml:"disabled"`
    38  	RPCMetrics                         bool                     `yaml:"rpc_metrics"`
    39  	Tags                               string                   `yaml:"tags"`
    40  	SamplerType                        string                   `yaml:"sampler_type"`
    41  	SamplerParam                       float64                  `yaml:"sampler_param"`
    42  	SamplerManagerHostPort             string                   `yaml:"sampler_manager_host_port"`
    43  	SamplerMaxOperations               int                      `yaml:"sampler_max_operations"`
    44  	SamplerRefreshInterval             time.Duration            `yaml:"sampler_refresh_interval"`
    45  	SamplerParentConfig                ParentBasedSamplerConfig `yaml:"sampler_parent_config"`
    46  	SamplingServerURL                  string                   `yaml:"sampling_server_url"`
    47  	OperationNameLateBinding           bool                     `yaml:"operation_name_late_binding"`
    48  	InitialSamplingRate                float64                  `yaml:"initial_sampler_rate"`
    49  	ReporterMaxQueueSize               int                      `yaml:"reporter_max_queue_size"`
    50  	ReporterFlushInterval              time.Duration            `yaml:"reporter_flush_interval"`
    51  	ReporterLogSpans                   bool                     `yaml:"reporter_log_spans"`
    52  	ReporterDisableAttemptReconnecting bool                     `yaml:"reporter_disable_attempt_reconnecting"`
    53  	ReporterAttemptReconnectInterval   time.Duration            `yaml:"reporter_attempt_reconnect_interval"`
    54  	Endpoint                           string                   `yaml:"endpoint"`
    55  	User                               string                   `yaml:"user"`
    56  	Password                           string                   `yaml:"password"`
    57  	AgentHost                          string                   `yaml:"agent_host"`
    58  	AgentPort                          int                      `yaml:"agent_port"`
    59  	Gen128Bit                          bool                     `yaml:"traceid_128bit"`
    60  	// Remove the above field. Ref: https://github.com/open-telemetry/opentelemetry-specification/issues/525#issuecomment-605519217
    61  	// Ref: https://opentelemetry.io/docs/reference/specification/trace/api/#spancontext
    62  }
    63  
    64  // getCollectorEndpoints returns Jaeger options populated with collector related options.
    65  func getCollectorEndpoints(config Config) []otel_jaeger.CollectorEndpointOption {
    66  	var collectorOptions []otel_jaeger.CollectorEndpointOption
    67  	if config.User != "" {
    68  		collectorOptions = append(collectorOptions, otel_jaeger.WithUsername(config.User))
    69  	}
    70  	if config.Password != "" {
    71  		collectorOptions = append(collectorOptions, otel_jaeger.WithPassword(config.Password))
    72  	}
    73  	collectorOptions = append(collectorOptions, otel_jaeger.WithEndpoint(config.Endpoint))
    74  
    75  	return collectorOptions
    76  }
    77  
    78  // getAgentEndpointOptions returns Jaeger options populated with agent related options.
    79  func getAgentEndpointOptions(config Config) []otel_jaeger.AgentEndpointOption {
    80  	var endpointOptions []otel_jaeger.AgentEndpointOption
    81  	endpointOptions = append(endpointOptions, otel_jaeger.WithAgentHost(config.AgentHost))
    82  	endpointOptions = append(endpointOptions, otel_jaeger.WithAgentPort(strconv.Itoa(config.AgentPort)))
    83  
    84  	// This option, as part of the Jaeger config, was JAEGER_REPORTER_ATTEMPT_RECONNECTING_DISABLED.
    85  	if config.ReporterDisableAttemptReconnecting {
    86  		endpointOptions = append(endpointOptions, otel_jaeger.WithDisableAttemptReconnecting())
    87  		if config.ReporterAttemptReconnectInterval != 0 {
    88  			endpointOptions = append(endpointOptions, otel_jaeger.WithAttemptReconnectingInterval(config.ReporterAttemptReconnectInterval))
    89  		}
    90  	}
    91  
    92  	if config.ReporterLogSpans {
    93  		var logger *log.Logger
    94  		endpointOptions = append(endpointOptions, otel_jaeger.WithLogger(logger))
    95  	}
    96  
    97  	return endpointOptions
    98  }
    99  
   100  // getSamplingFraction returns the sampling fraction based on the sampler type.
   101  // Ref: https://www.jaegertracing.io/docs/1.35/sampling/#client-sampling-configuration
   102  func getSamplingFraction(samplerType string, samplingFactor float64) float64 {
   103  	switch samplerType {
   104  	case "const":
   105  		if samplingFactor > 1 {
   106  			return 1.0
   107  		} else if samplingFactor < 0 {
   108  			return 0.0
   109  		}
   110  		return math.Round(samplingFactor)
   111  
   112  	case "probabilistic":
   113  		return samplingFactor
   114  
   115  	case "ratelimiting":
   116  		return math.Round(samplingFactor) // Needs to be an integer
   117  	}
   118  
   119  	return samplingFactor
   120  }
   121  
   122  func getSampler(config Config) tracesdk.Sampler {
   123  	samplerType := config.SamplerType
   124  	if samplerType == "" {
   125  		samplerType = SamplerTypeRateLimiting
   126  	}
   127  	samplingFraction := getSamplingFraction(samplerType, config.SamplerParam)
   128  
   129  	var sampler tracesdk.Sampler
   130  	switch samplerType {
   131  	case SamplerTypeProbabilistic:
   132  		sampler = tracesdk.TraceIDRatioBased(samplingFraction)
   133  	case SamplerTypeConstant:
   134  		if samplingFraction == 1.0 {
   135  			sampler = tracesdk.AlwaysSample()
   136  		} else {
   137  			sampler = tracesdk.NeverSample()
   138  		}
   139  	case SamplerTypeRemote:
   140  		remoteOptions := getRemoteOptions(config)
   141  		sampler = jaegerremote.New(config.ServiceName, remoteOptions...)
   142  	// Fallback always to default (rate limiting).
   143  	case SamplerTypeRateLimiting:
   144  		fallthrough
   145  	default:
   146  		// The same config options are applicable to both remote and rate-limiting samplers.
   147  		remoteOptions := getRemoteOptions(config)
   148  		sampler = jaegerremote.New(config.ServiceName, remoteOptions...)
   149  		sampler, ok := sampler.(*rateLimitingSampler)
   150  		if ok {
   151  			sampler.Update(config.SamplerParam)
   152  		}
   153  	}
   154  
   155  	// Use parent-based to make sure we respect the span parent, if
   156  	// it is sampled. Optionally, allow user to specify the
   157  	// parent-based options.
   158  	var parentOptions []tracesdk.ParentBasedSamplerOption
   159  	if config.SamplerParentConfig.LocalParentSampled {
   160  		parentOptions = append(parentOptions, tracesdk.WithLocalParentSampled(sampler))
   161  	}
   162  	if config.SamplerParentConfig.RemoteParentSampled {
   163  		parentOptions = append(parentOptions, tracesdk.WithRemoteParentSampled(sampler))
   164  	}
   165  	sampler = tracesdk.ParentBased(sampler, parentOptions...)
   166  
   167  	return sampler
   168  }
   169  
   170  func getRemoteOptions(config Config) []jaegerremote.Option {
   171  	var remoteOptions []jaegerremote.Option
   172  	if config.SamplerRefreshInterval != 0 {
   173  		remoteOptions = append(remoteOptions, jaegerremote.WithSamplingRefreshInterval(config.SamplerRefreshInterval))
   174  	}
   175  	if config.SamplingServerURL != "" {
   176  		remoteOptions = append(remoteOptions, jaegerremote.WithSamplingServerURL(config.SamplingServerURL))
   177  	}
   178  	if config.SamplerMaxOperations != 0 {
   179  		remoteOptions = append(remoteOptions, jaegerremote.WithMaxOperations(config.SamplerMaxOperations))
   180  	}
   181  	if config.OperationNameLateBinding {
   182  		remoteOptions = append(remoteOptions, jaegerremote.WithOperationNameLateBinding(true))
   183  	}
   184  	// SamplerRefreshInterval is the interval for polling the backend for sampling strategies.
   185  	// Ref: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md#general-sdk-configuration.
   186  	if config.SamplerRefreshInterval != 0 {
   187  		remoteOptions = append(remoteOptions, jaegerremote.WithSamplingRefreshInterval(config.SamplerRefreshInterval))
   188  	}
   189  	// InitialSamplingRate is the sampling probability when the backend is unreachable.
   190  	if config.InitialSamplingRate != 0.0 {
   191  		remoteOptions = append(remoteOptions, jaegerremote.WithInitialSampler(tracesdk.TraceIDRatioBased(config.InitialSamplingRate)))
   192  	}
   193  
   194  	return remoteOptions
   195  }
   196  
   197  // parseTags parses the given string into a collection of attributes.
   198  // Spec for this value:
   199  // - comma separated list of key=value
   200  // - value can be specified using the notation ${envVar:defaultValue}, where `envVar`
   201  // is an environment variable and `defaultValue` is the value to use in case the env var is not set.
   202  // TODO(aditi): when Lighstep and Elastic APM have been migrated, move 'parseTags()' to the common 'tracing' package.
   203  func parseTags(sTags string) []attribute.KeyValue {
   204  	pairs := strings.Split(sTags, ",")
   205  	tags := make([]attribute.KeyValue, 0)
   206  	for _, p := range pairs {
   207  		kv := strings.SplitN(p, "=", 2)
   208  		if len(kv) < 2 {
   209  			continue // to avoid panic
   210  		}
   211  		k, v := strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])
   212  
   213  		if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") {
   214  			ed := strings.SplitN(v[2:len(v)-1], ":", 2)
   215  			e, d := ed[0], ed[1]
   216  			v = os.Getenv(e)
   217  			if v == "" && d != "" {
   218  				v = d
   219  			}
   220  		}
   221  
   222  		tag := attribute.String(k, v)
   223  		tags = append(tags, tag)
   224  	}
   225  
   226  	return tags
   227  }
   228  
   229  // printDeprecationWarnings logs deprecation warnings for config options that are no
   230  // longer supported.
   231  func printDeprecationWarnings(config Config, l glog.Logger) {
   232  	commonDeprecationMessage := " has been deprecated as a config option."
   233  	if config.RPCMetrics {
   234  		level.Info(l).Log("msg", "RPC Metrics"+commonDeprecationMessage)
   235  	}
   236  	if config.Gen128Bit {
   237  		level.Info(l).Log("msg", "Gen128Bit"+commonDeprecationMessage)
   238  	}
   239  	if config.Disabled {
   240  		level.Info(l).Log("msg", "Disabled"+commonDeprecationMessage)
   241  	}
   242  }