github.com/waldiirawan/apm-agent-go/v2@v2.2.2/config.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package apm // import "github.com/waldiirawan/apm-agent-go/v2" 19 20 import ( 21 "fmt" 22 "net/url" 23 "os" 24 "path/filepath" 25 "regexp" 26 "runtime" 27 "strconv" 28 "strings" 29 "sync/atomic" 30 "time" 31 "unsafe" 32 33 "github.com/pkg/errors" 34 35 "github.com/waldiirawan/apm-agent-go/v2/internal/apmlog" 36 "github.com/waldiirawan/apm-agent-go/v2/internal/configutil" 37 "github.com/waldiirawan/apm-agent-go/v2/internal/wildcard" 38 "github.com/waldiirawan/apm-agent-go/v2/transport" 39 ) 40 41 const ( 42 envMetricsInterval = "ELASTIC_APM_METRICS_INTERVAL" 43 envMaxSpans = "ELASTIC_APM_TRANSACTION_MAX_SPANS" 44 envTransactionSampleRate = "ELASTIC_APM_TRANSACTION_SAMPLE_RATE" 45 envSanitizeFieldNames = "ELASTIC_APM_SANITIZE_FIELD_NAMES" 46 envCaptureHeaders = "ELASTIC_APM_CAPTURE_HEADERS" 47 envCaptureBody = "ELASTIC_APM_CAPTURE_BODY" 48 envServiceName = "ELASTIC_APM_SERVICE_NAME" 49 envServiceVersion = "ELASTIC_APM_SERVICE_VERSION" 50 envEnvironment = "ELASTIC_APM_ENVIRONMENT" 51 envSpanStackTraceMinDuration = "ELASTIC_APM_SPAN_STACK_TRACE_MIN_DURATION" 52 deprecatedEnvSpanFramesMinDuration = "ELASTIC_APM_SPAN_FRAMES_MIN_DURATION" 53 envActive = "ELASTIC_APM_ACTIVE" 54 envRecording = "ELASTIC_APM_RECORDING" 55 envAPIRequestSize = "ELASTIC_APM_API_REQUEST_SIZE" 56 envAPIRequestTime = "ELASTIC_APM_API_REQUEST_TIME" 57 envAPIBufferSize = "ELASTIC_APM_API_BUFFER_SIZE" 58 envMetricsBufferSize = "ELASTIC_APM_METRICS_BUFFER_SIZE" 59 envDisableMetrics = "ELASTIC_APM_DISABLE_METRICS" 60 envIgnoreURLs = "ELASTIC_APM_TRANSACTION_IGNORE_URLS" 61 deprecatedEnvIgnoreURLs = "ELASTIC_APM_IGNORE_URLS" 62 envGlobalLabels = "ELASTIC_APM_GLOBAL_LABELS" 63 envStackTraceLimit = "ELASTIC_APM_STACK_TRACE_LIMIT" 64 envCentralConfig = "ELASTIC_APM_CENTRAL_CONFIG" 65 envBreakdownMetrics = "ELASTIC_APM_BREAKDOWN_METRICS" 66 envUseElasticTraceparentHeader = "ELASTIC_APM_USE_ELASTIC_TRACEPARENT_HEADER" 67 envCloudProvider = "ELASTIC_APM_CLOUD_PROVIDER" 68 envContinuationStrategy = "ELASTIC_APM_TRACE_CONTINUATION_STRATEGY" 69 70 // span_compression (default `true`) 71 envSpanCompressionEnabled = "ELASTIC_APM_SPAN_COMPRESSION_ENABLED" 72 // span_compression_exact_match_max_duration (default `50ms`) 73 envSpanCompressionExactMatchMaxDuration = "ELASTIC_APM_SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION" 74 // span_compression_same_kind_max_duration (default `0ms`) 75 envSpanCompressionSameKindMaxDuration = "ELASTIC_APM_SPAN_COMPRESSION_SAME_KIND_MAX_DURATION" 76 77 // exit_span_min_duration (default `1ms`) 78 envExitSpanMinDuration = "ELASTIC_APM_EXIT_SPAN_MIN_DURATION" 79 80 // NOTE(axw) profiling environment variables are experimental. 81 // They may be removed in a future minor version without being 82 // considered a breaking change. 83 envCPUProfileInterval = "ELASTIC_APM_CPU_PROFILE_INTERVAL" 84 envCPUProfileDuration = "ELASTIC_APM_CPU_PROFILE_DURATION" 85 envHeapProfileInterval = "ELASTIC_APM_HEAP_PROFILE_INTERVAL" 86 87 defaultAPIRequestSize = 750 * configutil.KByte 88 defaultAPIRequestTime = 10 * time.Second 89 defaultAPIBufferSize = 1 * configutil.MByte 90 defaultMetricsBufferSize = 750 * configutil.KByte 91 defaultMetricsInterval = 30 * time.Second 92 defaultMaxSpans = 500 93 defaultCaptureHeaders = true 94 defaultCaptureBody = CaptureBodyOff 95 defaultSpanStackTraceMinDuration = 5 * time.Millisecond 96 defaultStackTraceLimit = 50 97 defaultContinuationStrategy = "continue" 98 99 defaultExitSpanMinDuration = time.Millisecond 100 101 minAPIBufferSize = 10 * configutil.KByte 102 maxAPIBufferSize = 100 * configutil.MByte 103 minAPIRequestSize = 1 * configutil.KByte 104 maxAPIRequestSize = 5 * configutil.MByte 105 minMetricsBufferSize = 10 * configutil.KByte 106 maxMetricsBufferSize = 100 * configutil.MByte 107 108 // Span Compressions default setting values 109 defaultSpanCompressionEnabled = true 110 defaultSpanCompressionExactMatchMaxDuration = 50 * time.Millisecond 111 defaultSpanCompressionSameKindMaxDuration = 0 112 ) 113 114 var ( 115 defaultSanitizedFieldNames = configutil.ParseWildcardPatterns(strings.Join([]string{ 116 "password", 117 "passwd", 118 "pwd", 119 "secret", 120 "*key", 121 "*token*", 122 "*session*", 123 "*credit*", 124 "*card*", 125 "*auth*", 126 "set-cookie", 127 "*principal*", 128 }, ",")) 129 ) 130 131 // Regular expression matching comment characters to escape in the User-Agent header value. 132 // 133 // See https://httpwg.org/specs/rfc7230.html#field.components 134 var httpComment = regexp.MustCompile("[^\\t \\x21-\\x27\\x2a-\\x5b\\x5d-\\x7e\\x80-\\xff]") 135 136 func initialTransport(serviceName, serviceVersion string) (transport.Transport, error) { 137 // User-Agent should be "apm-agent-go/<agent-version> (service-name service-version)". 138 service := serviceName 139 if serviceVersion != "" { 140 service += " " + httpComment.ReplaceAllString(serviceVersion, "_") 141 } 142 userAgent := fmt.Sprintf("%s (%s)", transport.DefaultUserAgent(), service) 143 httpTransport, err := transport.NewHTTPTransport(transport.HTTPTransportOptions{ 144 UserAgent: userAgent, 145 }) 146 if err != nil { 147 return nil, err 148 } 149 return httpTransport, nil 150 } 151 152 func initialRequestDuration() (time.Duration, error) { 153 return configutil.ParseDurationEnv(envAPIRequestTime, defaultAPIRequestTime) 154 } 155 156 func initialMetricsInterval() (time.Duration, error) { 157 return configutil.ParseDurationEnv(envMetricsInterval, defaultMetricsInterval) 158 } 159 160 func initialMetricsBufferSize() (int, error) { 161 size, err := configutil.ParseSizeEnv(envMetricsBufferSize, defaultMetricsBufferSize) 162 if err != nil { 163 return 0, err 164 } 165 if size < minMetricsBufferSize || size > maxMetricsBufferSize { 166 return 0, errors.Errorf( 167 "%s must be at least %s and less than %s, got %s", 168 envMetricsBufferSize, minMetricsBufferSize, maxMetricsBufferSize, size, 169 ) 170 } 171 return int(size), nil 172 } 173 174 func initialAPIBufferSize() (int, error) { 175 size, err := configutil.ParseSizeEnv(envAPIBufferSize, defaultAPIBufferSize) 176 if err != nil { 177 return 0, err 178 } 179 if size < minAPIBufferSize || size > maxAPIBufferSize { 180 return 0, errors.Errorf( 181 "%s must be at least %s and less than %s, got %s", 182 envAPIBufferSize, minAPIBufferSize, maxAPIBufferSize, size, 183 ) 184 } 185 return int(size), nil 186 } 187 188 func initialAPIRequestSize() (int, error) { 189 size, err := configutil.ParseSizeEnv(envAPIRequestSize, defaultAPIRequestSize) 190 if err != nil { 191 return 0, err 192 } 193 if size < minAPIRequestSize || size > maxAPIRequestSize { 194 return 0, errors.Errorf( 195 "%s must be at least %s and less than %s, got %s", 196 envAPIRequestSize, minAPIRequestSize, maxAPIRequestSize, size, 197 ) 198 } 199 return int(size), nil 200 } 201 202 func initialMaxSpans() (int, error) { 203 value := os.Getenv(envMaxSpans) 204 if value == "" { 205 return defaultMaxSpans, nil 206 } 207 max, err := strconv.Atoi(value) 208 if err != nil { 209 return 0, errors.Wrapf(err, "failed to parse %s", envMaxSpans) 210 } 211 return max, nil 212 } 213 214 // initialSampler returns a nil Sampler if all transactions should be sampled. 215 func initialSampler() (Sampler, error) { 216 value := os.Getenv(envTransactionSampleRate) 217 return parseSampleRate(envTransactionSampleRate, value) 218 } 219 220 // parseSampleRate parses a numeric sampling rate in the range [0,1.0], returning a Sampler. 221 func parseSampleRate(name, value string) (Sampler, error) { 222 if value == "" { 223 value = "1" 224 } 225 ratio, err := strconv.ParseFloat(value, 64) 226 if err != nil { 227 return nil, errors.Wrapf(err, "failed to parse %s", name) 228 } 229 if ratio < 0.0 || ratio > 1.0 { 230 return nil, errors.Errorf( 231 "invalid value for %s: %s (out of range [0,1.0])", 232 name, value, 233 ) 234 } 235 return NewRatioSampler(ratio), nil 236 } 237 238 func initialSanitizedFieldNames() wildcard.Matchers { 239 return configutil.ParseWildcardPatternsEnv(envSanitizeFieldNames, defaultSanitizedFieldNames) 240 } 241 242 func initContinuationStrategy() (string, error) { 243 value := os.Getenv(envContinuationStrategy) 244 if value == "" { 245 return defaultContinuationStrategy, nil 246 } 247 return value, validateContinuationStrategy(value) 248 } 249 250 func validateContinuationStrategy(value string) error { 251 switch value { 252 case "continue", "restart", "restart_external": 253 return nil 254 default: 255 return fmt.Errorf("unknown continuation strategy: %s", value) 256 } 257 } 258 259 func initialCaptureHeaders() (bool, error) { 260 return configutil.ParseBoolEnv(envCaptureHeaders, defaultCaptureHeaders) 261 } 262 263 func initialCaptureBody() (CaptureBodyMode, error) { 264 value := os.Getenv(envCaptureBody) 265 if value == "" { 266 return defaultCaptureBody, nil 267 } 268 return parseCaptureBody(envCaptureBody, value) 269 } 270 271 func parseCaptureBody(name, value string) (CaptureBodyMode, error) { 272 switch strings.TrimSpace(strings.ToLower(value)) { 273 case "all": 274 return CaptureBodyAll, nil 275 case "errors": 276 return CaptureBodyErrors, nil 277 case "transactions": 278 return CaptureBodyTransactions, nil 279 case "off": 280 return CaptureBodyOff, nil 281 } 282 return -1, errors.Errorf("invalid %s value %q", name, value) 283 } 284 285 func initialService() (name, version, environment string) { 286 name = os.Getenv(envServiceName) 287 version = os.Getenv(envServiceVersion) 288 environment = os.Getenv(envEnvironment) 289 if name == "" { 290 name = filepath.Base(os.Args[0]) 291 if runtime.GOOS == "windows" { 292 name = strings.TrimSuffix(name, filepath.Ext(name)) 293 } 294 } 295 name = sanitizeServiceName(name) 296 return name, version, environment 297 } 298 299 func initialSpanStackTraceMinDuration() (time.Duration, error) { 300 if v, err := configutil.ParseDurationEnv(envSpanStackTraceMinDuration, defaultSpanStackTraceMinDuration); err != nil || v != defaultSpanStackTraceMinDuration { 301 // if envSpanStackTraceMinDuration was provided ignore the deprecated option 302 return v, err 303 } 304 305 v, err := configutil.ParseDurationEnv(deprecatedEnvSpanFramesMinDuration, defaultSpanStackTraceMinDuration) 306 if err != nil { 307 return v, err 308 } 309 310 // The meaning of the value was changed. 311 // convert the old value in span_stack_trace_min_duration 312 if v == 0 { 313 return -1, nil 314 } 315 if v == -1 { 316 return 0, nil 317 } 318 319 return v, nil 320 } 321 322 func initialActive() (bool, error) { 323 return configutil.ParseBoolEnv(envActive, true) 324 } 325 326 func initialRecording() (bool, error) { 327 return configutil.ParseBoolEnv(envRecording, true) 328 } 329 330 func initialDisabledMetrics() wildcard.Matchers { 331 return configutil.ParseWildcardPatternsEnv(envDisableMetrics, nil) 332 } 333 334 func initialIgnoreTransactionURLs() wildcard.Matchers { 335 matchers := configutil.ParseWildcardPatternsEnv(envIgnoreURLs, nil) 336 if len(matchers) == 0 { 337 matchers = configutil.ParseWildcardPatternsEnv(deprecatedEnvIgnoreURLs, nil) 338 } 339 return matchers 340 } 341 342 func initialStackTraceLimit() (int, error) { 343 value := os.Getenv(envStackTraceLimit) 344 if value == "" { 345 return defaultStackTraceLimit, nil 346 } 347 limit, err := strconv.Atoi(value) 348 if err != nil { 349 return 0, errors.Wrapf(err, "failed to parse %s", envStackTraceLimit) 350 } 351 return limit, nil 352 } 353 354 func initialCentralConfigEnabled() (bool, error) { 355 return configutil.ParseBoolEnv(envCentralConfig, true) 356 } 357 358 func initialBreakdownMetricsEnabled() (bool, error) { 359 return configutil.ParseBoolEnv(envBreakdownMetrics, true) 360 } 361 362 func initialUseElasticTraceparentHeader() (bool, error) { 363 return configutil.ParseBoolEnv(envUseElasticTraceparentHeader, true) 364 } 365 366 func initialSpanCompressionEnabled() (bool, error) { 367 return configutil.ParseBoolEnv(envSpanCompressionEnabled, 368 defaultSpanCompressionEnabled, 369 ) 370 } 371 372 func initialSpanCompressionExactMatchMaxDuration() (time.Duration, error) { 373 return configutil.ParseDurationEnv( 374 envSpanCompressionExactMatchMaxDuration, 375 defaultSpanCompressionExactMatchMaxDuration, 376 ) 377 } 378 379 func initialSpanCompressionSameKindMaxDuration() (time.Duration, error) { 380 return configutil.ParseDurationEnv( 381 envSpanCompressionSameKindMaxDuration, 382 defaultSpanCompressionSameKindMaxDuration, 383 ) 384 } 385 386 func initialCPUProfileIntervalDuration() (time.Duration, time.Duration, error) { 387 interval, err := configutil.ParseDurationEnv(envCPUProfileInterval, 0) 388 if err != nil || interval <= 0 { 389 return 0, 0, err 390 } 391 duration, err := configutil.ParseDurationEnv(envCPUProfileDuration, 0) 392 if err != nil || duration <= 0 { 393 return 0, 0, err 394 } 395 return interval, duration, nil 396 } 397 398 func initialHeapProfileInterval() (time.Duration, error) { 399 return configutil.ParseDurationEnv(envHeapProfileInterval, 0) 400 } 401 402 func initialExitSpanMinDuration() (time.Duration, error) { 403 return configutil.ParseDurationEnvOptions( 404 envExitSpanMinDuration, defaultExitSpanMinDuration, 405 configutil.DurationOptions{MinimumDurationUnit: time.Microsecond}, 406 ) 407 } 408 409 // updateRemoteConfig updates t and cfg with changes held in "attrs", and reverts to local 410 // config for config attributes that have been removed (exist in old but not in attrs). 411 // 412 // On return from updateRemoteConfig, unapplied config will have been removed from attrs. 413 func (t *Tracer) updateRemoteConfig(logger Logger, old, attrs map[string]string) { 414 warningf := func(string, ...interface{}) {} 415 debugf := func(string, ...interface{}) {} 416 errorf := func(string, ...interface{}) {} 417 if logger != nil { 418 warningf = logger.Warningf 419 debugf = logger.Debugf 420 errorf = logger.Errorf 421 } 422 envName := func(k string) string { 423 return "ELASTIC_APM_" + strings.ToUpper(k) 424 } 425 426 var updates []func(cfg *instrumentationConfig) 427 for k, v := range attrs { 428 if oldv, ok := old[k]; ok && oldv == v { 429 continue 430 } 431 switch envName(k) { 432 case envCaptureBody: 433 value, err := parseCaptureBody(k, v) 434 if err != nil { 435 errorf("central config failure: %s", err) 436 delete(attrs, k) 437 continue 438 } else { 439 updates = append(updates, func(cfg *instrumentationConfig) { 440 cfg.captureBody = value 441 }) 442 } 443 case envMaxSpans: 444 value, err := strconv.Atoi(v) 445 if err != nil { 446 errorf("central config failure: failed to parse %s: %s", k, err) 447 delete(attrs, k) 448 continue 449 } else { 450 updates = append(updates, func(cfg *instrumentationConfig) { 451 cfg.maxSpans = value 452 }) 453 } 454 case envExitSpanMinDuration: 455 duration, err := configutil.ParseDurationOptions(v, configutil.DurationOptions{ 456 MinimumDurationUnit: time.Microsecond, 457 }) 458 if err != nil { 459 errorf("central config failure: failed to parse %s: %s", k, err) 460 delete(attrs, k) 461 continue 462 } 463 updates = append(updates, func(cfg *instrumentationConfig) { 464 cfg.exitSpanMinDuration = duration 465 }) 466 case envIgnoreURLs: 467 matchers := configutil.ParseWildcardPatterns(v) 468 updates = append(updates, func(cfg *instrumentationConfig) { 469 cfg.ignoreTransactionURLs = matchers 470 }) 471 case envRecording: 472 recording, err := strconv.ParseBool(v) 473 if err != nil { 474 errorf("central config failure: failed to parse %s: %s", k, err) 475 delete(attrs, k) 476 continue 477 } else { 478 updates = append(updates, func(cfg *instrumentationConfig) { 479 cfg.recording = recording 480 }) 481 } 482 case envSanitizeFieldNames: 483 matchers := configutil.ParseWildcardPatterns(v) 484 updates = append(updates, func(cfg *instrumentationConfig) { 485 cfg.sanitizedFieldNames = matchers 486 }) 487 case envContinuationStrategy: 488 if err := validateContinuationStrategy(v); err != nil { 489 errorf("central config failure: failed to parse %s: %s", k, err) 490 delete(attrs, k) 491 continue 492 } 493 updates = append(updates, func(cfg *instrumentationConfig) { 494 cfg.continuationStrategy = v 495 }) 496 case envSpanStackTraceMinDuration: 497 duration, err := configutil.ParseDuration(v) 498 if err != nil { 499 errorf("central config failure: failed to parse %s: %s", k, err) 500 delete(attrs, k) 501 continue 502 } else { 503 updates = append(updates, func(cfg *instrumentationConfig) { 504 cfg.spanStackTraceMinDuration = duration 505 }) 506 } 507 case envStackTraceLimit: 508 limit, err := strconv.Atoi(v) 509 if err != nil { 510 errorf("central config failure: failed to parse %s: %s", k, err) 511 delete(attrs, k) 512 continue 513 } else { 514 updates = append(updates, func(cfg *instrumentationConfig) { 515 cfg.stackTraceLimit = limit 516 }) 517 } 518 case envTransactionSampleRate: 519 sampler, err := parseSampleRate(k, v) 520 if err != nil { 521 errorf("central config failure: %s", err) 522 delete(attrs, k) 523 continue 524 } else { 525 updates = append(updates, func(cfg *instrumentationConfig) { 526 cfg.sampler = sampler 527 }) 528 } 529 case apmlog.EnvLogLevel: 530 level, err := apmlog.ParseLogLevel(v) 531 if err != nil { 532 errorf("central config failure: %s", err) 533 delete(attrs, k) 534 continue 535 } 536 if dl := apmlog.DefaultLogger(); dl != nil && dl == logger { 537 updates = append(updates, func(*instrumentationConfig) { 538 dl.SetLevel(level) 539 }) 540 } else { 541 warningf("central config ignored: %s set to %s, but custom logger in use", k, v) 542 delete(attrs, k) 543 continue 544 } 545 case envSpanCompressionEnabled: 546 val, err := strconv.ParseBool(v) 547 if err != nil { 548 errorf("central config failure: failed to parse %s: %s", k, err) 549 delete(attrs, k) 550 continue 551 } 552 updates = append(updates, func(cfg *instrumentationConfig) { 553 cfg.compressionOptions.enabled = val 554 }) 555 case envSpanCompressionExactMatchMaxDuration: 556 duration, err := configutil.ParseDuration(v) 557 if err != nil { 558 errorf("central config failure: failed to parse %s: %s", k, err) 559 delete(attrs, k) 560 continue 561 } 562 updates = append(updates, func(cfg *instrumentationConfig) { 563 cfg.compressionOptions.exactMatchMaxDuration = duration 564 }) 565 case envSpanCompressionSameKindMaxDuration: 566 duration, err := configutil.ParseDuration(v) 567 if err != nil { 568 errorf("central config failure: failed to parse %s: %s", k, err) 569 delete(attrs, k) 570 continue 571 } 572 updates = append(updates, func(cfg *instrumentationConfig) { 573 cfg.compressionOptions.sameKindMaxDuration = duration 574 }) 575 default: 576 warningf("central config failure: unsupported config: %s", k) 577 delete(attrs, k) 578 continue 579 } 580 debugf("central config update: updated %s to %s", k, v) 581 } 582 for k := range old { 583 if _, ok := attrs[k]; ok { 584 continue 585 } 586 updates = append(updates, func(cfg *instrumentationConfig) { 587 if f, ok := cfg.local[envName(k)]; ok { 588 f(&cfg.instrumentationConfigValues) 589 } 590 }) 591 debugf("central config update: reverted %s to local config", k) 592 } 593 if updates != nil { 594 remote := make(map[string]struct{}) 595 for k := range attrs { 596 remote[envName(k)] = struct{}{} 597 } 598 t.updateInstrumentationConfig(func(cfg *instrumentationConfig) { 599 cfg.remote = remote 600 for _, update := range updates { 601 update(cfg) 602 } 603 }) 604 } 605 } 606 607 // instrumentationConfig returns the current instrumentationConfig. 608 // 609 // The returned value is immutable. 610 func (t *Tracer) instrumentationConfig() *instrumentationConfig { 611 config := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&t.instrumentationConfigInternal))) 612 return (*instrumentationConfig)(config) 613 } 614 615 // setLocalInstrumentationConfig sets local transaction configuration with 616 // the specified environment variable key. 617 func (t *Tracer) setLocalInstrumentationConfig(envKey string, f func(cfg *instrumentationConfigValues)) { 618 t.updateInstrumentationConfig(func(cfg *instrumentationConfig) { 619 cfg.local[envKey] = f 620 if _, ok := cfg.remote[envKey]; !ok { 621 f(&cfg.instrumentationConfigValues) 622 } 623 }) 624 } 625 626 func (t *Tracer) updateInstrumentationConfig(f func(cfg *instrumentationConfig)) { 627 for { 628 oldConfig := t.instrumentationConfig() 629 newConfig := *oldConfig 630 f(&newConfig) 631 if atomic.CompareAndSwapPointer( 632 (*unsafe.Pointer)(unsafe.Pointer(&t.instrumentationConfigInternal)), 633 unsafe.Pointer(oldConfig), 634 unsafe.Pointer(&newConfig), 635 ) { 636 return 637 } 638 } 639 } 640 641 // IgnoredTransactionURL returns whether the given transaction URL should be ignored 642 func (t *Tracer) IgnoredTransactionURL(url *url.URL) bool { 643 return t.instrumentationConfig().ignoreTransactionURLs.MatchAny(url.String()) 644 } 645 646 // instrumentationConfig holds current configuration values, as well as information 647 // required to revert from remote to local configuration. 648 type instrumentationConfig struct { 649 instrumentationConfigValues 650 651 // local holds functions for setting instrumentationConfigValues to the most 652 // recently, locally specified configuration. 653 local map[string]func(*instrumentationConfigValues) 654 655 // remote holds the environment variable keys for applied remote config. 656 remote map[string]struct{} 657 } 658 659 // instrumentationConfigValues holds configuration that is accessible outside of the 660 // tracer loop, for instrumentation: StartTransaction, StartSpan, CaptureError, etc. 661 // 662 // NOTE(axw) when adding configuration here, you must also update `newTracer` to 663 // set the initial entry in instrumentationConfig.local, in order to properly reset 664 // to the local value, even if the default is the zero value. 665 type instrumentationConfigValues struct { 666 recording bool 667 captureBody CaptureBodyMode 668 captureHeaders bool 669 maxSpans int 670 sampler Sampler 671 spanStackTraceMinDuration time.Duration 672 exitSpanMinDuration time.Duration 673 continuationStrategy string 674 stackTraceLimit int 675 propagateLegacyHeader bool 676 sanitizedFieldNames wildcard.Matchers 677 ignoreTransactionURLs wildcard.Matchers 678 compressionOptions compressionOptions 679 }