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 }