github.com/newrelic/go-agent@v3.26.0+incompatible/internal_config.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package newrelic 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "net/http" 10 "os" 11 "strings" 12 13 "github.com/newrelic/go-agent/internal" 14 "github.com/newrelic/go-agent/internal/logger" 15 "github.com/newrelic/go-agent/internal/utilization" 16 ) 17 18 func copyDestConfig(c AttributeDestinationConfig) AttributeDestinationConfig { 19 cp := c 20 if nil != c.Include { 21 cp.Include = make([]string, len(c.Include)) 22 copy(cp.Include, c.Include) 23 } 24 if nil != c.Exclude { 25 cp.Exclude = make([]string, len(c.Exclude)) 26 copy(cp.Exclude, c.Exclude) 27 } 28 return cp 29 } 30 31 func copyConfigReferenceFields(cfg Config) Config { 32 cp := cfg 33 if nil != cfg.Labels { 34 cp.Labels = make(map[string]string, len(cfg.Labels)) 35 for key, val := range cfg.Labels { 36 cp.Labels[key] = val 37 } 38 } 39 if nil != cfg.ErrorCollector.IgnoreStatusCodes { 40 ignored := make([]int, len(cfg.ErrorCollector.IgnoreStatusCodes)) 41 copy(ignored, cfg.ErrorCollector.IgnoreStatusCodes) 42 cp.ErrorCollector.IgnoreStatusCodes = ignored 43 } 44 45 cp.Attributes = copyDestConfig(cfg.Attributes) 46 cp.ErrorCollector.Attributes = copyDestConfig(cfg.ErrorCollector.Attributes) 47 cp.TransactionEvents.Attributes = copyDestConfig(cfg.TransactionEvents.Attributes) 48 cp.TransactionTracer.Attributes = copyDestConfig(cfg.TransactionTracer.Attributes) 49 cp.BrowserMonitoring.Attributes = copyDestConfig(cfg.BrowserMonitoring.Attributes) 50 cp.SpanEvents.Attributes = copyDestConfig(cfg.SpanEvents.Attributes) 51 cp.TransactionTracer.Segments.Attributes = copyDestConfig(cfg.TransactionTracer.Segments.Attributes) 52 53 return cp 54 } 55 56 func transportSetting(t http.RoundTripper) interface{} { 57 if nil == t { 58 return nil 59 } 60 return fmt.Sprintf("%T", t) 61 } 62 63 func loggerSetting(lg Logger) interface{} { 64 if nil == lg { 65 return nil 66 } 67 if _, ok := lg.(logger.ShimLogger); ok { 68 return nil 69 } 70 return fmt.Sprintf("%T", lg) 71 } 72 73 const ( 74 // https://source.datanerd.us/agents/agent-specs/blob/master/Custom-Host-Names.md 75 hostByteLimit = 255 76 ) 77 78 type settings Config 79 80 func (s settings) MarshalJSON() ([]byte, error) { 81 c := Config(s) 82 transport := c.Transport 83 c.Transport = nil 84 l := c.Logger 85 c.Logger = nil 86 87 js, err := json.Marshal(c) 88 if nil != err { 89 return nil, err 90 } 91 fields := make(map[string]interface{}) 92 err = json.Unmarshal(js, &fields) 93 if nil != err { 94 return nil, err 95 } 96 // The License field is not simply ignored by adding the `json:"-"` tag 97 // to it since we want to allow consumers to populate Config from JSON. 98 delete(fields, `License`) 99 fields[`Transport`] = transportSetting(transport) 100 fields[`Logger`] = loggerSetting(l) 101 102 // Browser monitoring support. 103 if c.BrowserMonitoring.Enabled { 104 fields[`browser_monitoring.loader`] = "rum" 105 } 106 107 return json.Marshal(fields) 108 } 109 110 func configConnectJSONInternal(c Config, pid int, util *utilization.Data, e internal.Environment, version string, securityPolicies *internal.SecurityPolicies, metadata map[string]string) ([]byte, error) { 111 return json.Marshal([]interface{}{struct { 112 Pid int `json:"pid"` 113 Language string `json:"language"` 114 Version string `json:"agent_version"` 115 Host string `json:"host"` 116 HostDisplayName string `json:"display_host,omitempty"` 117 Settings interface{} `json:"settings"` 118 AppName []string `json:"app_name"` 119 HighSecurity bool `json:"high_security"` 120 Labels internal.Labels `json:"labels,omitempty"` 121 Environment internal.Environment `json:"environment"` 122 Identifier string `json:"identifier"` 123 Util *utilization.Data `json:"utilization"` 124 SecurityPolicies *internal.SecurityPolicies `json:"security_policies,omitempty"` 125 Metadata map[string]string `json:"metadata"` 126 EventData internal.EventHarvestConfig `json:"event_harvest_config"` 127 }{ 128 Pid: pid, 129 Language: internal.AgentLanguage, 130 Version: version, 131 Host: internal.StringLengthByteLimit(util.Hostname, hostByteLimit), 132 HostDisplayName: internal.StringLengthByteLimit(c.HostDisplayName, hostByteLimit), 133 Settings: (settings)(c), 134 AppName: strings.Split(c.AppName, ";"), 135 HighSecurity: c.HighSecurity, 136 Labels: c.Labels, 137 Environment: e, 138 // This identifier field is provided to avoid: 139 // https://newrelic.atlassian.net/browse/DSCORE-778 140 // 141 // This identifier is used by the collector to look up the real 142 // agent. If an identifier isn't provided, the collector will 143 // create its own based on the first appname, which prevents a 144 // single daemon from connecting "a;b" and "a;c" at the same 145 // time. 146 // 147 // Providing the identifier below works around this issue and 148 // allows users more flexibility in using application rollups. 149 Identifier: c.AppName, 150 Util: util, 151 SecurityPolicies: securityPolicies, 152 Metadata: metadata, 153 EventData: internal.DefaultEventHarvestConfig(c), 154 }}) 155 } 156 157 const ( 158 // https://source.datanerd.us/agents/agent-specs/blob/master/Connect-LEGACY.md#metadata-hash 159 metadataPrefix = "NEW_RELIC_METADATA_" 160 ) 161 162 func gatherMetadata(environ func() []string) map[string]string { 163 metadata := make(map[string]string) 164 env := environ() 165 for _, pair := range env { 166 if strings.HasPrefix(pair, metadataPrefix) { 167 idx := strings.Index(pair, "=") 168 if idx >= 0 { 169 metadata[pair[0:idx]] = pair[idx+1:] 170 } 171 } 172 } 173 return metadata 174 } 175 176 // config allows CreateConnectJSON to be a method on a non-public type. 177 type config struct{ Config } 178 179 func (c config) CreateConnectJSON(securityPolicies *internal.SecurityPolicies) ([]byte, error) { 180 env := internal.NewEnvironment() 181 util := utilization.Gather(utilization.Config{ 182 DetectAWS: c.Utilization.DetectAWS, 183 DetectAzure: c.Utilization.DetectAzure, 184 DetectPCF: c.Utilization.DetectPCF, 185 DetectGCP: c.Utilization.DetectGCP, 186 DetectDocker: c.Utilization.DetectDocker, 187 DetectKubernetes: c.Utilization.DetectKubernetes, 188 LogicalProcessors: c.Utilization.LogicalProcessors, 189 TotalRAMMIB: c.Utilization.TotalRAMMIB, 190 BillingHostname: c.Utilization.BillingHostname, 191 }, c.Logger) 192 return configConnectJSONInternal(c.Config, os.Getpid(), util, env, Version, securityPolicies, gatherMetadata(os.Environ)) 193 }