github.com/pinpoint-apm/pinpoint-go-agent@v1.4.1-0.20240110120318-a50c2eb18c8c/config.go (about) 1 package pinpoint 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "math/rand" 8 "os" 9 "regexp" 10 "sort" 11 "strings" 12 "time" 13 14 "github.com/fsnotify/fsnotify" 15 "github.com/spf13/cast" 16 "github.com/spf13/pflag" 17 "github.com/spf13/viper" 18 ) 19 20 // Config option keys 21 const ( 22 CfgAppName = "ApplicationName" 23 CfgAppType = "ApplicationType" 24 CfgAgentID = "AgentID" 25 CfgAgentName = "AgentName" 26 CfgCollectorHost = "Collector.Host" 27 CfgCollectorAgentPort = "Collector.AgentPort" 28 CfgCollectorSpanPort = "Collector.SpanPort" 29 CfgCollectorStatPort = "Collector.StatPort" 30 CfgLogLevelOld = "LogLevel" 31 CfgLogLevel = "Log.Level" 32 CfgLogOutput = "Log.Output" 33 CfgLogMaxSize = "Log.MaxSize" 34 CfgSamplingType = "Sampling.Type" 35 CfgSamplingCounterRate = "Sampling.CounterRate" 36 CfgSamplingPercentRate = "Sampling.PercentRate" 37 CfgSamplingNewThroughput = "Sampling.NewThroughput" 38 CfgSamplingContinueThroughput = "Sampling.ContinueThroughput" 39 CfgSpanQueueSize = "Span.QueueSize" 40 CfgSpanMaxCallStackDepth = "Span.MaxCallStackDepth" 41 CfgSpanMaxCallStackSequence = "Span.MaxCallStackSequence" 42 CfgStatCollectInterval = "Stat.CollectInterval" 43 CfgStatBatchCount = "Stat.BatchCount" 44 CfgIsContainerEnv = "IsContainerEnv" 45 CfgConfigFile = "ConfigFile" 46 CfgActiveProfile = "ActiveProfile" 47 CfgSQLTraceBindValue = "SQL.TraceBindValue" 48 CfgSQLMaxBindValueSize = "SQL.MaxBindValueSize" 49 CfgSQLTraceCommit = "SQL.TraceCommit" 50 CfgSQLTraceRollback = "SQL.TraceRollback" 51 CfgSQLTraceQueryStat = "SQL.TraceQueryStat" 52 CfgEnable = "Enable" 53 CfgHttpUrlStatEnable = "Http.UrlStat.Enable" 54 CfgHttpUrlStatLimitSize = "Http.UrlStat.LimitSize" 55 CfgHttpUrlStatWithMethod = "Http.UrlStat.WithMethod" 56 CfgErrorTraceCallStack = "Error.TraceCallStack" 57 CfgErrorCallStackDepth = "Error.CallStackDepth" 58 ) 59 60 const ( 61 cfgIdPattern = "[a-zA-Z0-9\\._\\-]+" 62 maxApplicationNameLength = 24 63 maxAgentIdLength = 24 64 maxAgentNameLength = 255 65 samplingTypeCounter = "COUNTER" 66 samplingTypePercent = "PERCENT" 67 ) 68 69 // Config value type 70 const ( 71 CfgInt int = iota 72 CfgFloat 73 CfgBool 74 CfgString 75 CfgStringSlice 76 ) 77 78 type cfgMapItem struct { 79 value interface{} 80 defaultValue interface{} 81 valueType int 82 cmdKey string 83 envKey string 84 dynamic bool 85 oldValue interface{} 86 } 87 88 var ( 89 cfgBaseMap map[string]*cfgMapItem 90 ) 91 92 func initConfig() { 93 cfgBaseMap = make(map[string]*cfgMapItem, 0) 94 95 AddConfig(CfgAppName, CfgString, "", false) 96 AddConfig(CfgAppType, CfgInt, ServiceTypeGoApp, false) 97 AddConfig(CfgAgentID, CfgString, "", false) 98 AddConfig(CfgAgentName, CfgString, "", false) 99 AddConfig(CfgCollectorHost, CfgString, "localhost", false) 100 AddConfig(CfgCollectorAgentPort, CfgInt, 9991, false) 101 AddConfig(CfgCollectorSpanPort, CfgInt, 9993, false) 102 AddConfig(CfgCollectorStatPort, CfgInt, 9992, false) 103 AddConfig(CfgLogLevelOld, CfgString, "info", true) 104 AddConfig(CfgLogLevel, CfgString, "info", true) 105 AddConfig(CfgLogOutput, CfgString, "stderr", true) 106 AddConfig(CfgLogMaxSize, CfgInt, 10, true) 107 AddConfig(CfgSamplingType, CfgString, samplingTypeCounter, true) 108 AddConfig(CfgSamplingCounterRate, CfgInt, 1, true) 109 AddConfig(CfgSamplingPercentRate, CfgFloat, 100, true) 110 AddConfig(CfgSamplingNewThroughput, CfgInt, 0, true) 111 AddConfig(CfgSamplingContinueThroughput, CfgInt, 0, true) 112 AddConfig(CfgSpanQueueSize, CfgInt, defaultQueueSize, false) 113 AddConfig(CfgSpanMaxCallStackDepth, CfgInt, defaultEventDepth, true) 114 AddConfig(CfgSpanMaxCallStackSequence, CfgInt, defaultEventSequence, true) 115 AddConfig(CfgStatCollectInterval, CfgInt, 5000, false) 116 AddConfig(CfgStatBatchCount, CfgInt, 6, false) 117 AddConfig(CfgIsContainerEnv, CfgBool, false, false) 118 AddConfig(CfgConfigFile, CfgString, "", false) 119 AddConfig(CfgActiveProfile, CfgString, "", false) 120 AddConfig(CfgSQLTraceBindValue, CfgBool, true, true) 121 AddConfig(CfgSQLMaxBindValueSize, CfgInt, 1024, true) 122 AddConfig(CfgSQLTraceCommit, CfgBool, true, true) 123 AddConfig(CfgSQLTraceRollback, CfgBool, true, true) 124 AddConfig(CfgSQLTraceQueryStat, CfgBool, false, true) 125 AddConfig(CfgEnable, CfgBool, true, false) 126 AddConfig(CfgHttpUrlStatEnable, CfgBool, false, true) 127 AddConfig(CfgHttpUrlStatLimitSize, CfgInt, 1024, true) 128 AddConfig(CfgHttpUrlStatWithMethod, CfgBool, false, true) 129 AddConfig(CfgErrorTraceCallStack, CfgBool, false, true) 130 AddConfig(CfgErrorCallStackDepth, CfgInt, 32, true) 131 } 132 133 // AddConfig adds a configuration item. 134 func AddConfig(cfgName string, valueType int, defaultValue interface{}, dynamic bool) { 135 cfgBaseMap[cfgName] = &cfgMapItem{ 136 defaultValue: defaultValue, 137 valueType: valueType, 138 cmdKey: cmdName(cfgName), 139 envKey: envName(cfgName), 140 dynamic: dynamic, 141 } 142 } 143 144 func cmdName(cfgName string) string { 145 return "pinpoint-" + strings.ReplaceAll(strings.ToLower(cfgName), ".", "-") 146 } 147 148 func envName(cfgName string) string { 149 return strings.ReplaceAll(strings.ToLower(cfgName), ".", "_") 150 } 151 152 // Config holds agent configuration, for passing to NewAgent. 153 type Config struct { 154 cfgMap map[string]*cfgMapItem 155 containerCheck bool 156 useNewLogOpt bool 157 offGrpc bool //for test 158 159 //dynamic config 160 callback []reloadCallback 161 collectUrlStat bool // CfgHttpUrlStatEnable 162 urlStatLimitSize int // CfgHttpUrlStatLimitSize 163 urlStatWithMethod bool // CfgHttpUrlStatWithMethod 164 sqlTraceBindValue bool // CfgSQLTraceBindValue 165 sqlMaxBindValueSize int // CfgSQLMaxBindValueSize 166 sqlTraceCommit bool // CfgSQLTraceCommit 167 sqlTraceRollback bool // CfgSQLTraceRollback 168 sqlTraceQueryStat bool // CfgSQLTraceQueryStat 169 spanMaxEventDepth int32 // CfgSpanMaxCallStackDepth 170 spanMaxEventSequence int32 // CfgSpanMaxCallStackSequence 171 errorTraceCallStack bool // CfgErrorTraceCallStack 172 errorCallStackDepth int // CfgErrorCallStackDepth 173 } 174 175 // ConfigOption represents an option that can be passed to NewConfig. 176 type ConfigOption func(*Config) 177 178 // GetConfig returns a global Config created by NewConfig. 179 func GetConfig() *Config { 180 return GetAgent().Config() 181 } 182 183 // Set stores the specified configuration item value. 184 func (config *Config) Set(cfgName string, value interface{}) { 185 if v, ok := config.cfgMap[cfgName]; ok { 186 v.value = value 187 } 188 } 189 190 // Int returns an integer value for the specified configuration item. 191 func (config *Config) Int(cfgName string) int { 192 if v, ok := config.cfgMap[cfgName]; ok { 193 return cast.ToInt(v.value) 194 } 195 return 0 196 } 197 198 // Float returns a float value for the specified configuration item. 199 func (config *Config) Float(cfgName string) float64 { 200 if v, ok := config.cfgMap[cfgName]; ok { 201 return cast.ToFloat64(v.value) 202 } 203 return 0 204 } 205 206 // String returns a string value for the specified configuration item. 207 func (config *Config) String(cfgName string) string { 208 if v, ok := config.cfgMap[cfgName]; ok { 209 return cast.ToString(v.value) 210 } 211 return "" 212 } 213 214 // StringSlice returns a string slice value for the specified configuration item. 215 func (config *Config) StringSlice(cfgName string) []string { 216 if v, ok := config.cfgMap[cfgName]; ok { 217 return cast.ToStringSlice(v.value) 218 } 219 return []string{} 220 } 221 222 // Bool returns a boolean value for the specified configuration item. 223 func (config *Config) Bool(cfgName string) bool { 224 if v, ok := config.cfgMap[cfgName]; ok { 225 return cast.ToBool(v.value) 226 } 227 return false 228 } 229 230 // NewConfig creates a Config populated with default settings, command line arguments, 231 // environment variables and the given config options. 232 // Config uses the following precedence order. Each item takes precedence over the item below it: 233 // 1. command line flag 234 // 2. environment variable 235 // 3. configuration file 236 // 4. ConfigOption 237 // 5. default 238 // 239 // configuration keys used in config files are case-insensitive. 240 // The generated Config is maintained globally. 241 // 242 // example: 243 // 244 // opts := []pinpoint.ConfigOption{ 245 // pinpoint.WithAppName("GoTestApp"), 246 // pinpoint.WithConfigFile(os.Getenv("HOME") + "/tmp/pinpoint-config.yaml"), 247 // } 248 // cfg, err := pinpoint.NewConfig(opts...) 249 func NewConfig(opts ...ConfigOption) (*Config, error) { 250 config := defaultConfig() 251 if opts != nil { 252 for _, fn := range opts { 253 fn(config) 254 } 255 } 256 257 cmdEnvViper := viper.New() 258 flagSet := config.newFlagSet() 259 if err := flagSet.Parse(filterCmdArgs()); err != nil { 260 Log("config").Errorf("commad line config loading error: %v", err) 261 } 262 cmdEnvViper.BindPFlags(flagSet) 263 cmdEnvViper.SetEnvPrefix("pinpoint_go") 264 cmdEnvViper.AutomaticEnv() 265 266 cfgFileViper := config.loadConfigFile(cmdEnvViper) 267 profileViper := config.loadProfile(cmdEnvViper, cfgFileViper) 268 config.loadConfig(cmdEnvViper, cfgFileViper, profileViper) 269 270 config.callback = make([]reloadCallback, 0) 271 config.applyDynamicConfig() 272 273 if config.containerCheck { 274 config.cfgMap[CfgIsContainerEnv].value = isContainerEnv() 275 } 276 if config.Int(CfgSpanQueueSize) < 1 { 277 config.cfgMap[CfgSpanQueueSize].value = defaultQueueSize 278 } 279 return config, nil 280 } 281 282 func defaultConfig() *Config { 283 config := new(Config) 284 config.cfgMap = make(map[string]*cfgMapItem, 0) 285 for k, v := range cfgBaseMap { 286 config.cfgMap[k] = &cfgMapItem{ 287 defaultValue: v.defaultValue, 288 valueType: v.valueType, 289 cmdKey: v.cmdKey, 290 envKey: v.envKey, 291 dynamic: v.dynamic, 292 } 293 } 294 for _, v := range config.cfgMap { 295 v.value = v.defaultValue 296 } 297 298 config.containerCheck = true 299 config.collectUrlStat = false 300 config.urlStatLimitSize = 1024 301 config.urlStatWithMethod = false 302 config.sqlTraceBindValue = true 303 config.sqlMaxBindValueSize = 1024 304 config.sqlTraceCommit = true 305 config.sqlTraceRollback = true 306 config.sqlTraceQueryStat = false 307 config.spanMaxEventDepth = defaultEventDepth 308 config.spanMaxEventSequence = defaultEventSequence 309 config.errorTraceCallStack = false 310 config.errorCallStackDepth = 32 311 312 return config 313 } 314 315 func (config *Config) newFlagSet() *pflag.FlagSet { 316 flagSet := pflag.NewFlagSet("pinpoint_go_agent", pflag.ContinueOnError) 317 318 for _, v := range config.cfgMap { 319 switch v.valueType { 320 case CfgInt: 321 flagSet.Int(v.cmdKey, 0, "") 322 case CfgFloat: 323 flagSet.Float64(v.cmdKey, 0, "") 324 case CfgBool: 325 flagSet.Bool(v.cmdKey, false, "") 326 case CfgString: 327 flagSet.String(v.cmdKey, "", "") 328 case CfgStringSlice: 329 flagSet.StringSlice(v.cmdKey, nil, "") 330 } 331 } 332 333 return flagSet 334 } 335 336 func filterCmdArgs() []string { 337 cmdArgs := make([]string, 0) 338 339 for _, arg := range os.Args[1:] { 340 if strings.HasPrefix(arg, "--pinpoint-") { 341 cmdArgs = append(cmdArgs, arg) 342 } 343 } 344 return cmdArgs 345 } 346 347 func (config *Config) loadConfigFile(cmdEnvViper *viper.Viper) *viper.Viper { 348 var cfgFile string 349 350 item := config.cfgMap[CfgConfigFile] 351 if cmdEnvViper.IsSet(item.cmdKey) { 352 cfgFile = cmdEnvViper.GetString(item.cmdKey) 353 } else if cmdEnvViper.IsSet(item.envKey) { 354 cfgFile = cmdEnvViper.GetString(item.envKey) 355 } else { 356 cfgFile = item.value.(string) 357 } 358 359 cfgFileViper := viper.New() 360 if cfgFile != "" { 361 cfgFileViper.SetConfigFile(cfgFile) 362 if err := cfgFileViper.ReadInConfig(); err != nil { 363 Log("config").Errorf("config file loading error: %v", err) 364 } 365 366 cfgFileViper.OnConfigChange(func(e fsnotify.Event) { 367 config.reloadConfig(cfgFileViper) 368 }) 369 cfgFileViper.WatchConfig() 370 } 371 372 return cfgFileViper 373 } 374 375 func (config *Config) loadProfile(cmdEnvViper *viper.Viper, cfgFileViper *viper.Viper) *viper.Viper { 376 var profile string 377 378 item := config.cfgMap[CfgActiveProfile] 379 if cmdEnvViper.IsSet(item.cmdKey) { 380 profile = cmdEnvViper.GetString(item.cmdKey) 381 } else if cmdEnvViper.IsSet(item.envKey) { 382 profile = cmdEnvViper.GetString(item.envKey) 383 } else if cfgFileViper.IsSet(CfgActiveProfile) { 384 profile = cfgFileViper.GetString(CfgActiveProfile) 385 } else { 386 profile = item.value.(string) 387 } 388 389 if profile != "" { 390 profileViper := cfgFileViper.Sub("profile." + profile) 391 if profileViper != nil { 392 return profileViper 393 } else { 394 Log("config").Warnf("config file doesn't have the profile: %s", profile) 395 } 396 } 397 398 return viper.New() 399 } 400 401 func (config *Config) loadConfig(cmdEnvViper *viper.Viper, cfgFileViper *viper.Viper, profileViper *viper.Viper) { 402 sortKeys := make([]string, 0) 403 for k := range config.cfgMap { 404 sortKeys = append(sortKeys, k) 405 } 406 sort.Strings(sortKeys) 407 for _, k := range sortKeys { 408 v := config.cfgMap[k] 409 if cmdEnvViper.IsSet(v.cmdKey) { 410 config.setFinalValue(k, v, cmdEnvViper.Get(v.cmdKey)) 411 } else if cmdEnvViper.IsSet(v.envKey) { 412 config.setFinalValue(k, v, cmdEnvViper.Get(v.envKey)) 413 } else if profileViper.IsSet(k) { 414 config.setFinalValue(k, v, profileViper.Get(k)) 415 } else if cfgFileViper.IsSet(k) { 416 config.setFinalValue(k, v, cfgFileViper.Get(k)) 417 } 418 } 419 } 420 421 func (config *Config) setFinalValue(cfgName string, item *cfgMapItem, value interface{}) { 422 if item.valueType == CfgStringSlice { 423 if s, ok := value.(string); ok { 424 value = strings.Split(s, ",") 425 } 426 } 427 428 item.value = value 429 if cfgName == CfgIsContainerEnv { 430 config.containerCheck = false 431 } else if cfgName == CfgLogLevel { 432 config.useNewLogOpt = true 433 } else if cfgName == CfgLogLevelOld && !config.useNewLogOpt { 434 config.cfgMap[CfgLogLevel].value = value 435 } 436 } 437 438 func (config *Config) checkNameAndID() error { 439 r, _ := regexp.Compile(cfgIdPattern) 440 appName := config.String(CfgAppName) 441 if appName == "" { 442 return errors.New("application name is required") 443 } else if len(appName) > maxApplicationNameLength { 444 return errors.New("application name is too long (max length: " + fmt.Sprint(maxApplicationNameLength) + ")") 445 } else if !r.MatchString(appName) { 446 return errors.New("application name has invalid pattern (" + cfgIdPattern + ")") 447 } 448 449 agentId := config.String(CfgAgentID) 450 if agentId == "" || len(agentId) > maxAgentIdLength || !r.MatchString(agentId) { 451 config.cfgMap[CfgAgentID].value = randomString(maxAgentIdLength - 1) 452 Log("config").Infof("auto-generated AgentID: %v", config.cfgMap[CfgAgentID].value) 453 } 454 455 agentName := config.String(CfgAgentName) 456 if agentName != "" { 457 if len(agentName) > maxAgentNameLength { 458 return errors.New("agent name is too long (max length: " + fmt.Sprint(maxAgentNameLength) + ")") 459 } else if !r.MatchString(agentName) { 460 return errors.New("agent name has invalid pattern (" + cfgIdPattern + ")") 461 } 462 } 463 return nil 464 } 465 466 func (config *Config) applyDynamicConfig() { 467 sampleType := strings.ToUpper(strings.TrimSpace(config.String(CfgSamplingType))) 468 if sampleType != samplingTypeCounter && sampleType != samplingTypePercent { 469 config.cfgMap[CfgSamplingType].value = samplingTypeCounter 470 config.cfgMap[CfgSamplingCounterRate].value = 0 471 } 472 473 maxBind := config.Int(CfgSQLMaxBindValueSize) 474 if maxBind > 1024 { 475 config.cfgMap[CfgSQLMaxBindValueSize].value = 1024 476 } else if maxBind < 0 { 477 config.cfgMap[CfgSQLTraceBindValue].value = false 478 config.cfgMap[CfgSQLMaxBindValueSize].value = 0 479 } 480 config.sqlTraceBindValue = config.Bool(CfgSQLTraceBindValue) 481 config.sqlMaxBindValueSize = config.Int(CfgSQLMaxBindValueSize) 482 config.sqlTraceCommit = config.Bool(CfgSQLTraceCommit) 483 config.sqlTraceRollback = config.Bool(CfgSQLTraceRollback) 484 config.sqlTraceQueryStat = config.Bool(CfgSQLTraceQueryStat) 485 486 maxDepth := config.Int(CfgSpanMaxCallStackDepth) 487 if maxDepth == -1 { 488 maxDepth = math.MaxInt32 489 } else if maxDepth < minEventDepth { 490 maxDepth = minEventDepth 491 } 492 config.cfgMap[CfgSpanMaxCallStackDepth].value = maxDepth 493 config.spanMaxEventDepth = int32(config.Int(CfgSpanMaxCallStackDepth)) 494 495 maxSeq := config.Int(CfgSpanMaxCallStackSequence) 496 if maxSeq == -1 { 497 maxSeq = math.MaxInt32 498 } else if maxSeq < minEventSequence { 499 maxSeq = minEventSequence 500 } 501 config.cfgMap[CfgSpanMaxCallStackSequence].value = maxSeq 502 config.spanMaxEventSequence = int32(config.Int(CfgSpanMaxCallStackSequence)) 503 504 if config.Int(CfgLogMaxSize) < 1 { 505 config.cfgMap[CfgLogMaxSize].value = 10 506 } 507 config.collectUrlStat = config.Bool(CfgHttpUrlStatEnable) 508 config.urlStatLimitSize = config.Int(CfgHttpUrlStatLimitSize) 509 config.urlStatWithMethod = config.Bool(CfgHttpUrlStatWithMethod) 510 config.errorTraceCallStack = config.Bool(CfgErrorTraceCallStack) 511 config.errorCallStackDepth = config.Int(CfgErrorCallStackDepth) 512 } 513 514 type reloadCallback struct { 515 cfgNames []string 516 callback func() 517 } 518 519 // AddReloadCallback adds a callback function will be called after reloading config file. 520 func (config *Config) AddReloadCallback(optNames []string, callback func()) { 521 config.callback = append(config.callback, reloadCallback{optNames, callback}) 522 } 523 524 func (config *Config) reloadConfig(cfgFileViper *viper.Viper) { 525 if err := cfgFileViper.ReadInConfig(); err != nil { 526 Log("config").Errorf("config file reloading error: %v", err) 527 return 528 } 529 530 profileViper := config.loadProfile(viper.New(), cfgFileViper) 531 config.loadDynamicConfig(cfgFileViper, profileViper) 532 config.applyDynamicConfig() 533 534 for _, cb := range config.callback { 535 cb.do(config) 536 } 537 } 538 539 func (config *Config) loadDynamicConfig(cfgFileViper *viper.Viper, profileViper *viper.Viper) { 540 sortKeys := make([]string, 0) 541 for k := range config.cfgMap { 542 sortKeys = append(sortKeys, k) 543 } 544 sort.Strings(sortKeys) 545 for _, k := range sortKeys { 546 if v := config.cfgMap[k]; v.dynamic { 547 v.oldValue = nil 548 if profileViper.IsSet(k) { 549 config.reloadFinalValue(k, v, profileViper) 550 } else if cfgFileViper.IsSet(k) { 551 config.reloadFinalValue(k, v, cfgFileViper) 552 } 553 } 554 } 555 } 556 557 func (config *Config) reloadFinalValue(cfgName string, item *cfgMapItem, viper *viper.Viper) { 558 item.oldValue = item.value 559 config.setFinalValue(cfgName, item, viper.Get(cfgName)) 560 } 561 562 func (config *Config) isReloaded(cfgName string) bool { 563 if item, ok := config.cfgMap[cfgName]; ok { 564 return item.oldValue != nil && item.oldValue != item.value 565 } 566 return false 567 } 568 569 func (cb reloadCallback) do(config *Config) { 570 for _, k := range cb.cfgNames { 571 if config.isReloaded(k) { 572 cb.callback() 573 break 574 } 575 } 576 } 577 578 func isContainerEnv() bool { 579 _, err := os.Stat("/.dockerenv") 580 if err == nil || !os.IsNotExist(err) { 581 return true 582 } 583 584 if os.Getenv("KUBERNETES_SERVICE_HOST") != "" { 585 return true 586 } 587 588 return false 589 } 590 591 // WithAppName sets the application name. 592 func WithAppName(name string) ConfigOption { 593 return func(c *Config) { 594 c.cfgMap[CfgAppName].value = name 595 } 596 } 597 598 // WithAppType sets the application type. 599 func WithAppType(typ int32) ConfigOption { 600 return func(c *Config) { 601 c.cfgMap[CfgAppType].value = typ 602 } 603 } 604 605 // WithAgentId sets the agent ID. 606 func WithAgentId(id string) ConfigOption { 607 return func(c *Config) { 608 c.cfgMap[CfgAgentID].value = id 609 } 610 } 611 612 // WithAgentName sets the agent name. 613 func WithAgentName(name string) ConfigOption { 614 return func(c *Config) { 615 c.cfgMap[CfgAgentName].value = name 616 } 617 } 618 619 // WithConfigFile sets the configuration file. 620 func WithConfigFile(filePath string) ConfigOption { 621 return func(c *Config) { 622 c.cfgMap[CfgConfigFile].value = filePath 623 } 624 } 625 626 // WithCollectorHost sets the host address of pinpoint collector. 627 func WithCollectorHost(host string) ConfigOption { 628 return func(c *Config) { 629 c.cfgMap[CfgCollectorHost].value = host 630 } 631 } 632 633 // WithCollectorAgentPort sets the agent port of pinpoint collector. 634 func WithCollectorAgentPort(port int) ConfigOption { 635 return func(c *Config) { 636 c.cfgMap[CfgCollectorAgentPort].value = port 637 } 638 } 639 640 // WithCollectorSpanPort sets the span port of pinpoint collector. 641 func WithCollectorSpanPort(port int) ConfigOption { 642 return func(c *Config) { 643 c.cfgMap[CfgCollectorSpanPort].value = port 644 } 645 } 646 647 // WithCollectorStatPort sets the agent stat of pinpoint collector. 648 func WithCollectorStatPort(port int) ConfigOption { 649 return func(c *Config) { 650 c.cfgMap[CfgCollectorStatPort].value = port 651 } 652 } 653 654 // WithLogLevel sets the logging level for agent logger. 655 func WithLogLevel(level string) ConfigOption { 656 return func(c *Config) { 657 c.cfgMap[CfgLogLevel].value = level 658 } 659 } 660 661 // WithLogOutput sets the output for agent logger. 662 func WithLogOutput(output string) ConfigOption { 663 return func(c *Config) { 664 c.cfgMap[CfgLogOutput].value = output 665 } 666 } 667 668 // WithLogMaxSize sets the max size of output file for agent logger. 669 func WithLogMaxSize(size int) ConfigOption { 670 return func(c *Config) { 671 c.cfgMap[CfgLogMaxSize].value = size 672 } 673 } 674 675 // WithSamplingType sets the type of agent sampler. 676 // Either "COUNTER" or "PERCENT" must be specified. 677 func WithSamplingType(samplingType string) ConfigOption { 678 return func(c *Config) { 679 c.cfgMap[CfgSamplingType].value = samplingType 680 } 681 } 682 683 // WithSamplingRate DEPRECATED: Use WithSamplingCounterRate() 684 func WithSamplingRate(rate int) ConfigOption { 685 return func(c *Config) { 686 c.cfgMap[CfgSamplingCounterRate].value = rate 687 } 688 } 689 690 // WithSamplingCounterRate sets the sampling rate for a 'counter sampler'. 691 func WithSamplingCounterRate(rate int) ConfigOption { 692 return func(c *Config) { 693 c.cfgMap[CfgSamplingCounterRate].value = rate 694 } 695 } 696 697 // WithSamplingPercentRate sets the sampling rate for a 'percent sampler'. 698 func WithSamplingPercentRate(rate float32) ConfigOption { 699 return func(c *Config) { 700 c.cfgMap[CfgSamplingPercentRate].value = rate 701 } 702 } 703 704 // WithSamplingNewThroughput sets the new tps for a 'throughput sampler'. 705 func WithSamplingNewThroughput(tps int) ConfigOption { 706 return func(c *Config) { 707 c.cfgMap[CfgSamplingNewThroughput].value = tps 708 } 709 } 710 711 // WithSamplingContinueThroughput sets the cont tps for a 'throughput sampler'. 712 func WithSamplingContinueThroughput(tps int) ConfigOption { 713 return func(c *Config) { 714 c.cfgMap[CfgSamplingContinueThroughput].value = tps 715 } 716 } 717 718 // WithStatCollectInterval sets the statistics collection cycle for the agent. 719 func WithStatCollectInterval(interval int) ConfigOption { 720 return func(c *Config) { 721 c.cfgMap[CfgStatCollectInterval].value = interval 722 } 723 } 724 725 // WithStatBatchCount sets batch delivery units for collected statistics. 726 func WithStatBatchCount(count int) ConfigOption { 727 return func(c *Config) { 728 c.cfgMap[CfgStatBatchCount].value = count 729 } 730 } 731 732 // WithIsContainerEnv sets whether the application is running in a container environment or not. 733 // If this is not set, the agent automatically checks it. 734 func WithIsContainerEnv(isContainer bool) ConfigOption { 735 return func(c *Config) { 736 c.cfgMap[CfgIsContainerEnv].value = isContainer 737 c.containerCheck = false 738 } 739 } 740 741 // WithActiveProfile sets the configuration profile. 742 func WithActiveProfile(profile string) ConfigOption { 743 return func(c *Config) { 744 c.cfgMap[CfgActiveProfile].value = profile 745 } 746 } 747 748 // WithSQLTraceBindValue enables bind value tracing for SQL Driver. 749 func WithSQLTraceBindValue(trace bool) ConfigOption { 750 return func(c *Config) { 751 c.cfgMap[CfgSQLTraceBindValue].value = trace 752 } 753 } 754 755 // WithSQLMaxBindValueSize sets the max length of traced bind value for SQL Driver. 756 func WithSQLMaxBindValueSize(size int) ConfigOption { 757 return func(c *Config) { 758 c.cfgMap[CfgSQLMaxBindValueSize].value = size 759 } 760 } 761 762 // WithSQLTraceCommit enables commit tracing for SQL Driver. 763 func WithSQLTraceCommit(trace bool) ConfigOption { 764 return func(c *Config) { 765 c.cfgMap[CfgSQLTraceCommit].value = trace 766 } 767 } 768 769 // WithSQLTraceRollback enables rollback tracing for SQL Driver. 770 func WithSQLTraceRollback(trace bool) ConfigOption { 771 return func(c *Config) { 772 c.cfgMap[CfgSQLTraceRollback].value = trace 773 } 774 } 775 776 // WithSQLTraceQueryStat enables to trace SQL query statistics for SQL Driver. 777 func WithSQLTraceQueryStat(collect bool) ConfigOption { 778 return func(c *Config) { 779 c.cfgMap[CfgSQLTraceQueryStat].value = collect 780 } 781 } 782 783 // WithEnable enables the agent is operational state. 784 func WithEnable(enable bool) ConfigOption { 785 return func(c *Config) { 786 c.cfgMap[CfgEnable].value = enable 787 } 788 } 789 790 // WithSpanQueueSize sets the size of the span queue for gRPC. 791 func WithSpanQueueSize(size int) ConfigOption { 792 return func(c *Config) { 793 c.cfgMap[CfgSpanQueueSize].value = size 794 } 795 } 796 797 // WithSpanMaxCallStackDepth sets the max callstack depth of a span. 798 func WithSpanMaxCallStackDepth(depth int) ConfigOption { 799 return func(c *Config) { 800 c.cfgMap[CfgSpanMaxCallStackDepth].value = depth 801 } 802 } 803 804 // WithSpanMaxCallStackSequence sets the max callstack sequence of a span. 805 func WithSpanMaxCallStackSequence(seq int) ConfigOption { 806 return func(c *Config) { 807 c.cfgMap[CfgSpanMaxCallStackSequence].value = seq 808 } 809 } 810 811 // WithHttpUrlStatEnable enables the agent collects the HTTP URL statistics. 812 func WithHttpUrlStatEnable(enable bool) ConfigOption { 813 return func(c *Config) { 814 c.cfgMap[CfgHttpUrlStatEnable].value = enable 815 } 816 } 817 818 // WithHttpUrlStatLimitSize sets the maximum number of URLs that can be stored in one snapshot. 819 func WithHttpUrlStatLimitSize(size int) ConfigOption { 820 return func(c *Config) { 821 c.cfgMap[CfgHttpUrlStatLimitSize].value = size 822 } 823 } 824 825 // WithHttpUrlStatWithMethod adds http method as prefix to uri string key. 826 func WithHttpUrlStatWithMethod(withMethod bool) ConfigOption { 827 return func(c *Config) { 828 c.cfgMap[CfgHttpUrlStatWithMethod].value = withMethod 829 } 830 } 831 832 // WithErrorTraceCallStack enables the agent collects a call stack when error occurs. 833 func WithErrorTraceCallStack(trace bool) ConfigOption { 834 return func(c *Config) { 835 c.cfgMap[CfgErrorTraceCallStack].value = trace 836 } 837 } 838 839 // WithErrorCallStackDepth sets the maximum depth of call stack that can be dumped. 840 func WithErrorCallStackDepth(depth int) ConfigOption { 841 return func(c *Config) { 842 c.cfgMap[CfgErrorCallStackDepth].value = depth 843 } 844 } 845 846 func (config *Config) printConfigString() { 847 sortKeys := make([]string, 0) 848 for k := range config.cfgMap { 849 sortKeys = append(sortKeys, k) 850 } 851 sort.Strings(sortKeys) 852 853 for _, k := range sortKeys { 854 Log("config").Infof("%s = %v", k, config.cfgMap[k].value) 855 } 856 } 857 858 const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 859 860 func randomString(n int) string { 861 r := rand.New(rand.NewSource(time.Now().UnixNano())) 862 l := len(charset) 863 864 b := make([]byte, n) 865 for i := range b { 866 b[i] = charset[r.Intn(l)] 867 } 868 return string(b) 869 }