internal_config.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package newrelic
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "os"
  7. "strings"
  8. "github.com/newrelic/go-agent/internal"
  9. "github.com/newrelic/go-agent/internal/logger"
  10. "github.com/newrelic/go-agent/internal/utilization"
  11. )
  12. func copyDestConfig(c AttributeDestinationConfig) AttributeDestinationConfig {
  13. cp := c
  14. if nil != c.Include {
  15. cp.Include = make([]string, len(c.Include))
  16. copy(cp.Include, c.Include)
  17. }
  18. if nil != c.Exclude {
  19. cp.Exclude = make([]string, len(c.Exclude))
  20. copy(cp.Exclude, c.Exclude)
  21. }
  22. return cp
  23. }
  24. func copyConfigReferenceFields(cfg Config) Config {
  25. cp := cfg
  26. if nil != cfg.Labels {
  27. cp.Labels = make(map[string]string, len(cfg.Labels))
  28. for key, val := range cfg.Labels {
  29. cp.Labels[key] = val
  30. }
  31. }
  32. if nil != cfg.ErrorCollector.IgnoreStatusCodes {
  33. ignored := make([]int, len(cfg.ErrorCollector.IgnoreStatusCodes))
  34. copy(ignored, cfg.ErrorCollector.IgnoreStatusCodes)
  35. cp.ErrorCollector.IgnoreStatusCodes = ignored
  36. }
  37. cp.Attributes = copyDestConfig(cfg.Attributes)
  38. cp.ErrorCollector.Attributes = copyDestConfig(cfg.ErrorCollector.Attributes)
  39. cp.TransactionEvents.Attributes = copyDestConfig(cfg.TransactionEvents.Attributes)
  40. cp.TransactionTracer.Attributes = copyDestConfig(cfg.TransactionTracer.Attributes)
  41. return cp
  42. }
  43. const (
  44. agentLanguage = "go"
  45. )
  46. func transportSetting(t http.RoundTripper) interface{} {
  47. if nil == t {
  48. return nil
  49. }
  50. return fmt.Sprintf("%T", t)
  51. }
  52. func loggerSetting(lg Logger) interface{} {
  53. if nil == lg {
  54. return nil
  55. }
  56. if _, ok := lg.(logger.ShimLogger); ok {
  57. return nil
  58. }
  59. return fmt.Sprintf("%T", lg)
  60. }
  61. const (
  62. // https://source.datanerd.us/agents/agent-specs/blob/master/Custom-Host-Names.md
  63. hostByteLimit = 255
  64. )
  65. type settings Config
  66. func (s settings) MarshalJSON() ([]byte, error) {
  67. c := Config(s)
  68. transport := c.Transport
  69. c.Transport = nil
  70. logger := c.Logger
  71. c.Logger = nil
  72. js, err := json.Marshal(c)
  73. if nil != err {
  74. return nil, err
  75. }
  76. fields := make(map[string]interface{})
  77. err = json.Unmarshal(js, &fields)
  78. if nil != err {
  79. return nil, err
  80. }
  81. // The License field is not simply ignored by adding the `json:"-"` tag
  82. // to it since we want to allow consumers to populate Config from JSON.
  83. delete(fields, `License`)
  84. fields[`Transport`] = transportSetting(transport)
  85. fields[`Logger`] = loggerSetting(logger)
  86. return json.Marshal(fields)
  87. }
  88. func configConnectJSONInternal(c Config, pid int, util *utilization.Data, e internal.Environment, version string, securityPolicies *internal.SecurityPolicies) ([]byte, error) {
  89. return json.Marshal([]interface{}{struct {
  90. Pid int `json:"pid"`
  91. Language string `json:"language"`
  92. Version string `json:"agent_version"`
  93. Host string `json:"host"`
  94. HostDisplayName string `json:"display_host,omitempty"`
  95. Settings interface{} `json:"settings"`
  96. AppName []string `json:"app_name"`
  97. HighSecurity bool `json:"high_security"`
  98. Labels internal.Labels `json:"labels,omitempty"`
  99. Environment internal.Environment `json:"environment"`
  100. Identifier string `json:"identifier"`
  101. Util *utilization.Data `json:"utilization"`
  102. SecurityPolicies *internal.SecurityPolicies `json:"security_policies,omitempty"`
  103. }{
  104. Pid: pid,
  105. Language: agentLanguage,
  106. Version: version,
  107. Host: internal.StringLengthByteLimit(util.Hostname, hostByteLimit),
  108. HostDisplayName: internal.StringLengthByteLimit(c.HostDisplayName, hostByteLimit),
  109. Settings: (settings)(c),
  110. AppName: strings.Split(c.AppName, ";"),
  111. HighSecurity: c.HighSecurity,
  112. Labels: internal.Labels(c.Labels),
  113. Environment: e,
  114. // This identifier field is provided to avoid:
  115. // https://newrelic.atlassian.net/browse/DSCORE-778
  116. //
  117. // This identifier is used by the collector to look up the real
  118. // agent. If an identifier isn't provided, the collector will
  119. // create its own based on the first appname, which prevents a
  120. // single daemon from connecting "a;b" and "a;c" at the same
  121. // time.
  122. //
  123. // Providing the identifier below works around this issue and
  124. // allows users more flexibility in using application rollups.
  125. Identifier: c.AppName,
  126. Util: util,
  127. SecurityPolicies: securityPolicies,
  128. }})
  129. }
  130. // config allows CreateConnectJSON to be a method on a non-public type.
  131. type config struct{ Config }
  132. func (c config) CreateConnectJSON(securityPolicies *internal.SecurityPolicies) ([]byte, error) {
  133. env := internal.NewEnvironment()
  134. util := utilization.Gather(utilization.Config{
  135. DetectAWS: c.Utilization.DetectAWS,
  136. DetectDocker: c.Utilization.DetectDocker,
  137. LogicalProcessors: c.Utilization.LogicalProcessors,
  138. TotalRAMMIB: c.Utilization.TotalRAMMIB,
  139. BillingHostname: c.Utilization.BillingHostname,
  140. }, c.Logger)
  141. return configConnectJSONInternal(c.Config, os.Getpid(), util, env, Version, securityPolicies)
  142. }