1package lightstep 2 3import ( 4 "fmt" 5 "math" 6 "math/rand" 7 "os" 8 "path" 9 "strings" 10 "time" // N.B.(jmacd): Do not use google.golang.org/glog in this package. 11 12 "github.com/lightstep/lightstep-tracer-go/constants" 13 "github.com/opentracing/opentracing-go" 14 "google.golang.org/grpc" 15) 16 17// Default Option values. 18const ( 19 DefaultCollectorPath = "/_rpc/v1/reports/binary" 20 DefaultPlainPort = 80 21 DefaultSecurePort = 443 22 DefaultGRPCCollectorHost = "collector-grpc.lightstep.com" 23 24 DefaultSystemMetricsHost = "ingest.lightstep.com" 25 26 DefaultSystemMetricsMeasurementFrequency = 30 * time.Second 27 DefaultSystemMetricsTimeout = 5 * time.Second 28 29 DefaultMaxReportingPeriod = 2500 * time.Millisecond 30 DefaultMinReportingPeriod = 500 * time.Millisecond 31 DefaultMaxSpans = 1000 32 DefaultReportTimeout = 30 * time.Second 33 DefaultReconnectPeriod = 5 * time.Minute 34 35 DefaultMaxLogKeyLen = 256 36 DefaultMaxLogValueLen = 1024 37 DefaultMaxLogsPerSpan = 500 38 39 DefaultGRPCMaxCallSendMsgSizeBytes = math.MaxInt32 40) 41 42// Tag and Tracer Attribute keys. 43const ( 44 ParentSpanGUIDKey = "parent_span_guid" // ParentSpanGUIDKey is the tag key used to record the relationship between child and parent spans. 45 ComponentNameKey = constants.ComponentNameKey // NOTE: these will be deprecated in favour of the constants package 46 GUIDKey = "lightstep.guid" // <- runtime guid, not span guid 47 HostnameKey = constants.HostnameKey // NOTE: these will be deprecated in favour of the constants package 48 CommandLineKey = "lightstep.command_line" 49 ServiceVersionKey = constants.ServiceVersionKey // NOTE: these will be deprecated in favour of the constants package 50 51 TracerPlatformKey = "lightstep.tracer_platform" 52 TracerPlatformValue = "go" 53 TracerPlatformVersionKey = "lightstep.tracer_platform_version" 54 TracerVersionKey = "lightstep.tracer_version" // Note: TracerVersionValue is generated from ./VERSION 55) 56 57const ( 58 secureScheme = "https" 59 plaintextScheme = "http" 60) 61 62// Validation Errors 63var ( 64 errInvalidGUIDKey = fmt.Errorf("Options invalid: setting the %v tag is no longer supported", GUIDKey) 65) 66 67// A SpanRecorder handles all of the `RawSpan` data generated via an 68// associated `Tracer` instance. 69type SpanRecorder interface { 70 RecordSpan(RawSpan) 71} 72 73// Endpoint describes a collector or web API host/port and whether or 74// not to use plaintext communication. 75type Endpoint struct { 76 Scheme string `yaml:"scheme" json:"scheme" usage:"scheme to use for the endpoint, defaults to appropriate one if no custom one is required"` 77 Host string `yaml:"host" json:"host" usage:"host on which the endpoint is running"` 78 Port int `yaml:"port" json:"port" usage:"port on which the endpoint is listening"` 79 Plaintext bool `yaml:"plaintext" json:"plaintext" usage:"whether or not to encrypt data send to the endpoint"` 80 CustomCACertFile string `yaml:"custom_ca_cert_file" json:"custom_ca_cert_file" usage:"path to a custom CA cert file, defaults to system defined certs if omitted"` 81} 82 83// HostPort use SocketAddress instead. 84// DEPRECATED 85func (e Endpoint) HostPort() string { 86 return e.SocketAddress() 87} 88 89// SocketAddress returns an address suitable for dialing grpc connections 90func (e Endpoint) SocketAddress() string { 91 return fmt.Sprintf("%s:%d", e.Host, e.Port) 92} 93 94// URL returns an address suitable for dialing http connections 95func (e Endpoint) URL() string { 96 return fmt.Sprintf("%s%s", e.urlWithoutPath(), DefaultCollectorPath) 97} 98 99// urlWithoutPath returns an address suitable for grpc connections if a custom scheme is provided 100func (e Endpoint) urlWithoutPath() string { 101 return fmt.Sprintf("%s://%s", e.scheme(), e.SocketAddress()) 102} 103 104func (e Endpoint) scheme() string { 105 if len(e.Scheme) > 0 { 106 return e.Scheme 107 } 108 109 if e.Plaintext { 110 return plaintextScheme 111 } 112 113 return secureScheme 114} 115 116type SystemMetricsOptions struct { 117 Disabled bool `yaml:"disabled"` 118 Endpoint Endpoint `yaml:"endpoint"` 119 MeasurementFrequency time.Duration `yaml:"measurement_frequency"` 120 Timeout time.Duration `yaml:"timeout"` 121} 122 123// Options control how the LightStep Tracer behaves. 124type Options struct { 125 // AccessToken is the unique API key for your LightStep project. It is 126 // available on your account page at https://app.lightstep.com/account 127 AccessToken string `yaml:"access_token" usage:"access token for reporting to LightStep"` 128 129 // Collector is the host, port, and plaintext option to use 130 // for the collector. 131 Collector Endpoint `yaml:"collector"` 132 133 // Tags are arbitrary key-value pairs that apply to all spans generated by 134 // this Tracer. 135 Tags opentracing.Tags 136 137 // LightStep is the host, port, and plaintext option to use 138 // for the LightStep web API. 139 LightStepAPI Endpoint `yaml:"lightstep_api"` 140 141 // MaxBufferedSpans is the maximum number of spans that will be buffered 142 // before sending them to a collector. 143 MaxBufferedSpans int `yaml:"max_buffered_spans"` 144 145 // MaxLogKeyLen is the maximum allowable size (in characters) of an 146 // OpenTracing logging key. Longer keys are truncated. 147 MaxLogKeyLen int `yaml:"max_log_key_len"` 148 149 // MaxLogValueLen is the maximum allowable size (in characters) of an 150 // OpenTracing logging value. Longer values are truncated. Only applies to 151 // variable-length value types (strings, interface{}, etc). 152 MaxLogValueLen int `yaml:"max_log_value_len"` 153 154 // MaxLogsPerSpan limits the number of logs in a single span. 155 MaxLogsPerSpan int `yaml:"max_logs_per_span"` 156 157 // GRPCMaxCallSendMsgSizeBytes limits the size in bytes of grpc messages 158 // sent by a client. 159 GRPCMaxCallSendMsgSizeBytes int `yaml:"grpc_max_call_send_msg_size_bytes"` 160 161 // ReportingPeriod is the maximum duration of time between sending spans 162 // to a collector. If zero, the default will be used. 163 ReportingPeriod time.Duration `yaml:"reporting_period"` 164 165 // MinReportingPeriod is the minimum duration of time between sending spans 166 // to a collector. If zero, the default will be used. It is strongly 167 // recommended to use the default. 168 MinReportingPeriod time.Duration `yaml:"min_reporting_period"` 169 170 ReportTimeout time.Duration `yaml:"report_timeout"` 171 172 // DropSpanLogs turns log events on all Spans into no-ops. 173 DropSpanLogs bool `yaml:"drop_span_logs"` 174 175 // DEPRECATED: The LightStep library prints the first error to stdout by default. 176 // See the documentation on the SetGlobalEventHandler function for guidance on 177 // how to integrate tracer diagnostics with your application's logging and 178 // metrics systems. 179 Verbose bool `yaml:"verbose"` 180 181 // Force the use of a specific transport protocol. If multiple are set to true, 182 // the following order is used to select for the first option: http, grpc. 183 // If none are set to true, HTTP is defaulted to. 184 UseHttp bool `yaml:"use_http"` 185 UseGRPC bool `yaml:"usegrpc"` 186 187 // Propagators allow inject/extract to use custom propagators for different formats. This 188 // package includes a `B3Propagator` that supports B3 headers on text maps and http headers. 189 // Defaults: 190 // opentracing.HTTPHeaders: LightStepPropagator 191 // opentracing.TextMap: LightStepPropagator, 192 // opentracing.Binary: LightStepPropagator 193 Propagators map[opentracing.BuiltinFormat]Propagator `yaml:"-"` 194 195 // CustomCollector allows customizing the Protobuf transport. 196 // This is an advanced feature that avoids reconnect logic. 197 CustomCollector Collector `yaml:"-" json:"-"` 198 199 ReconnectPeriod time.Duration `yaml:"reconnect_period"` 200 201 // DialOptions allows customizing the grpc dial options passed to the grpc.Dial(...) call. 202 // This is an advanced feature added to allow for a custom balancer or middleware. 203 // It can be safely ignored if you have no custom dialing requirements. 204 // If UseGRPC is not set, these dial options are ignored. 205 DialOptions []grpc.DialOption `yaml:"-" json:"-"` 206 207 // A hook for receiving finished span events 208 Recorder SpanRecorder `yaml:"-" json:"-"` 209 210 // For testing purposes only 211 ConnFactory ConnectorFactory `yaml:"-" json:"-"` 212 213 // Enable LightStep Meta Event Logging 214 MetaEventReportingEnabled bool `yaml:"meta_event_reporting_enabled" json:"meta_event_reporting_enabled"` 215 216 SystemMetrics SystemMetricsOptions `yaml:"system_metrics"` 217} 218 219// Initialize validates options, and sets default values for unset options. 220// This is called automatically when creating a new Tracer. 221func (opts *Options) Initialize() error { 222 err := opts.Validate() 223 if err != nil { 224 return err 225 } 226 227 // Note: opts is a copy of the user's data, ok to modify. 228 if opts.MaxBufferedSpans == 0 { 229 opts.MaxBufferedSpans = DefaultMaxSpans 230 } 231 if opts.MaxLogKeyLen == 0 { 232 opts.MaxLogKeyLen = DefaultMaxLogKeyLen 233 } 234 if opts.MaxLogValueLen == 0 { 235 opts.MaxLogValueLen = DefaultMaxLogValueLen 236 } 237 if opts.MaxLogsPerSpan == 0 { 238 opts.MaxLogsPerSpan = DefaultMaxLogsPerSpan 239 } 240 if opts.GRPCMaxCallSendMsgSizeBytes == 0 { 241 opts.GRPCMaxCallSendMsgSizeBytes = DefaultGRPCMaxCallSendMsgSizeBytes 242 } 243 if opts.ReportingPeriod == 0 { 244 opts.ReportingPeriod = DefaultMaxReportingPeriod 245 } 246 if opts.MinReportingPeriod == 0 { 247 opts.MinReportingPeriod = DefaultMinReportingPeriod 248 } 249 if opts.ReportTimeout == 0 { 250 opts.ReportTimeout = DefaultReportTimeout 251 } 252 if opts.ReconnectPeriod == 0 { 253 opts.ReconnectPeriod = DefaultReconnectPeriod 254 } 255 if opts.Tags == nil { 256 opts.Tags = map[string]interface{}{} 257 } 258 259 // Set some default attributes if not found in options 260 if _, found := opts.Tags[constants.ComponentNameKey]; !found { 261 // If not found, use the first command line argument as the default service name 262 defaultService := path.Base(os.Args[0]) 263 264 emitEvent(newEventMissingService(defaultService)) 265 opts.Tags[constants.ComponentNameKey] = defaultService 266 } 267 if _, found := opts.Tags[constants.HostnameKey]; !found { 268 hostname, _ := os.Hostname() 269 opts.Tags[constants.HostnameKey] = hostname 270 } 271 if _, found := opts.Tags[CommandLineKey]; !found { 272 opts.Tags[CommandLineKey] = strings.Join(os.Args, " ") 273 } 274 275 opts.ReconnectPeriod = time.Duration(float64(opts.ReconnectPeriod) * (1 + 0.2*rand.Float64())) 276 277 if opts.Collector.Host == "" { 278 opts.Collector.Host = DefaultGRPCCollectorHost 279 } 280 281 if opts.Collector.Port <= 0 { 282 if opts.Collector.Plaintext { 283 opts.Collector.Port = DefaultPlainPort 284 } else { 285 opts.Collector.Port = DefaultSecurePort 286 } 287 } 288 289 if opts.SystemMetrics.Endpoint.Host == "" { 290 opts.SystemMetrics.Endpoint.Host = DefaultSystemMetricsHost 291 } 292 293 if opts.SystemMetrics.Endpoint.Port <= 0 { 294 opts.SystemMetrics.Endpoint.Port = DefaultSecurePort 295 296 if opts.SystemMetrics.Endpoint.Plaintext { 297 opts.SystemMetrics.Endpoint.Port = DefaultPlainPort 298 } 299 } 300 301 if opts.SystemMetrics.MeasurementFrequency <= 0 { 302 opts.SystemMetrics.MeasurementFrequency = DefaultSystemMetricsMeasurementFrequency 303 } 304 305 if opts.SystemMetrics.Timeout <= 0 { 306 opts.SystemMetrics.Timeout = DefaultSystemMetricsTimeout 307 } 308 309 return nil 310} 311 312// Validate checks that all required fields are set, and no options are incorrectly 313// configured. 314func (opts *Options) Validate() error { 315 if _, found := opts.Tags[GUIDKey]; found { 316 return errInvalidGUIDKey 317 } 318 319 if len(opts.Collector.CustomCACertFile) != 0 { 320 if _, err := os.Stat(opts.Collector.CustomCACertFile); os.IsNotExist(err) { 321 return err 322 } 323 } 324 325 if !opts.SystemMetrics.Disabled && len(opts.SystemMetrics.Endpoint.CustomCACertFile) != 0 { 326 if _, err := os.Stat(opts.SystemMetrics.Endpoint.CustomCACertFile); os.IsNotExist(err) { 327 return err 328 } 329 } 330 331 return nil 332} 333 334// SetSpanID is a opentracing.StartSpanOption that sets an 335// explicit SpanID. It must be used in conjunction with 336// SetTraceID or the result is undefined. 337type SetSpanID uint64 338 339// Apply satisfies the StartSpanOption interface. 340func (sid SetSpanID) Apply(sso *opentracing.StartSpanOptions) {} 341func (sid SetSpanID) applyLS(sso *startSpanOptions) { 342 sso.SetSpanID = uint64(sid) 343} 344 345// SetTraceID is an opentracing.StartSpanOption that sets an 346// explicit TraceID. It must be used in order to set an 347// explicit SpanID or ParentSpanID. If a ChildOf or 348// FollowsFrom span relation is also set in the start options, 349// it will override this value. 350type SetTraceID uint64 351 352// Apply satisfies the StartSpanOption interface. 353func (sid SetTraceID) Apply(sso *opentracing.StartSpanOptions) {} 354func (sid SetTraceID) applyLS(sso *startSpanOptions) { 355 sso.SetTraceID = uint64(sid) 356} 357 358// SetParentSpanID is an opentracing.StartSpanOption that sets 359// an explicit parent SpanID. It must be used in conjunction 360// with SetTraceID or the result is undefined. If the value 361// is zero, it will be disregarded. If a ChildOf or 362// FollowsFrom span relation is also set in the start options, 363// it will override this value. 364type SetParentSpanID uint64 365 366// Apply satisfies the StartSpanOption interface. 367func (sid SetParentSpanID) Apply(sso *opentracing.StartSpanOptions) {} 368func (sid SetParentSpanID) applyLS(sso *startSpanOptions) { 369 sso.SetParentSpanID = uint64(sid) 370} 371 372// lightStepStartSpanOption is used to identify lightstep-specific Span options. 373type lightStepStartSpanOption interface { 374 applyLS(*startSpanOptions) 375} 376 377type SetSampled string 378func (s SetSampled) Apply(sso *opentracing.StartSpanOptions) {} 379 380func (s SetSampled) applyLS(sso *startSpanOptions) { 381 sso.SetSampled = string(s) 382} 383 384type startSpanOptions struct { 385 Options opentracing.StartSpanOptions 386 387 // Options to explicitly set span_id, trace_id, 388 // parent_span_id, expected to be used when exporting spans 389 // from another system into LightStep via opentracing APIs. 390 SetSpanID uint64 391 SetParentSpanID uint64 392 SetTraceID uint64 393 SetSampled string 394} 395 396func newStartSpanOptions(sso []opentracing.StartSpanOption) startSpanOptions { 397 opts := startSpanOptions{} 398 for _, o := range sso { 399 switch o := o.(type) { 400 case lightStepStartSpanOption: 401 o.applyLS(&opts) 402 default: 403 o.Apply(&opts.Options) 404 } 405 } 406 return opts 407} 408