github.com/newrelic/go-agent@v3.26.0+incompatible/app_run.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 "strings" 9 "time" 10 11 "github.com/newrelic/go-agent/internal" 12 ) 13 14 // appRun contains information regarding a single connection session with the 15 // collector. It is immutable after creation at application connect. 16 type appRun struct { 17 Reply *internal.ConnectReply 18 19 // AttributeConfig is calculated on every connect since it depends on 20 // the security policies. 21 AttributeConfig *internal.AttributeConfig 22 Config Config 23 24 // firstAppName is the value of Config.AppName up to the first semicolon. 25 firstAppName string 26 } 27 28 func newAppRun(config Config, reply *internal.ConnectReply) *appRun { 29 convertConfig := func(c AttributeDestinationConfig) internal.AttributeDestinationConfig { 30 return internal.AttributeDestinationConfig{ 31 Enabled: c.Enabled, 32 Include: c.Include, 33 Exclude: c.Exclude, 34 } 35 } 36 run := &appRun{ 37 Reply: reply, 38 AttributeConfig: internal.CreateAttributeConfig(internal.AttributeConfigInput{ 39 Attributes: convertConfig(config.Attributes), 40 ErrorCollector: convertConfig(config.ErrorCollector.Attributes), 41 TransactionEvents: convertConfig(config.TransactionEvents.Attributes), 42 TransactionTracer: convertConfig(config.TransactionTracer.Attributes), 43 BrowserMonitoring: convertConfig(config.BrowserMonitoring.Attributes), 44 SpanEvents: convertConfig(config.SpanEvents.Attributes), 45 TraceSegments: convertConfig(config.TransactionTracer.Segments.Attributes), 46 }, reply.SecurityPolicies.AttributesInclude.Enabled()), 47 Config: config, 48 } 49 50 // Overwrite local settings with any server-side-config settings 51 // present. NOTE! This requires that the Config provided to this 52 // function is a value and not a pointer: We do not want to change the 53 // input Config with values particular to this connection. 54 55 if v := run.Reply.ServerSideConfig.TransactionTracerEnabled; nil != v { 56 run.Config.TransactionTracer.Enabled = *v 57 } 58 if v := run.Reply.ServerSideConfig.ErrorCollectorEnabled; nil != v { 59 run.Config.ErrorCollector.Enabled = *v 60 } 61 if v := run.Reply.ServerSideConfig.CrossApplicationTracerEnabled; nil != v { 62 run.Config.CrossApplicationTracer.Enabled = *v 63 } 64 if v := run.Reply.ServerSideConfig.TransactionTracerThreshold; nil != v { 65 switch val := v.(type) { 66 case float64: 67 run.Config.TransactionTracer.Threshold.IsApdexFailing = false 68 run.Config.TransactionTracer.Threshold.Duration = internal.FloatSecondsToDuration(val) 69 case string: 70 if val == "apdex_f" { 71 run.Config.TransactionTracer.Threshold.IsApdexFailing = true 72 } 73 } 74 } 75 if v := run.Reply.ServerSideConfig.TransactionTracerStackTraceThreshold; nil != v { 76 run.Config.TransactionTracer.StackTraceThreshold = internal.FloatSecondsToDuration(*v) 77 } 78 if v := run.Reply.ServerSideConfig.ErrorCollectorIgnoreStatusCodes; nil != v { 79 run.Config.ErrorCollector.IgnoreStatusCodes = v 80 } 81 82 if !run.Reply.CollectErrorEvents { 83 run.Config.ErrorCollector.CaptureEvents = false 84 } 85 if !run.Reply.CollectAnalyticsEvents { 86 run.Config.TransactionEvents.Enabled = false 87 } 88 if !run.Reply.CollectTraces { 89 run.Config.TransactionTracer.Enabled = false 90 run.Config.DatastoreTracer.SlowQuery.Enabled = false 91 } 92 if !run.Reply.CollectSpanEvents { 93 run.Config.SpanEvents.Enabled = false 94 } 95 96 // Distributed tracing takes priority over cross-app-tracing per: 97 // https://source.datanerd.us/agents/agent-specs/blob/master/Distributed-Tracing.md#distributed-trace-payload 98 if run.Config.DistributedTracer.Enabled { 99 run.Config.CrossApplicationTracer.Enabled = false 100 } 101 102 // Cache the first application name set on the config 103 run.firstAppName = strings.SplitN(config.AppName, ";", 2)[0] 104 105 if "" != run.Reply.RunID { 106 js, _ := json.Marshal(settings(run.Config)) 107 run.Config.Logger.Debug("final configuration", map[string]interface{}{ 108 "config": internal.JSONString(js), 109 }) 110 } 111 112 return run 113 } 114 115 const ( 116 // https://source.datanerd.us/agents/agent-specs/blob/master/Lambda.md#distributed-tracing 117 serverlessDefaultPrimaryAppID = "Unknown" 118 ) 119 120 const ( 121 // https://source.datanerd.us/agents/agent-specs/blob/master/Lambda.md#adaptive-sampling 122 serverlessSamplerPeriod = 60 * time.Second 123 serverlessSamplerTarget = 10 124 ) 125 126 func newServerlessConnectReply(config Config) *internal.ConnectReply { 127 reply := internal.ConnectReplyDefaults() 128 129 reply.ApdexThresholdSeconds = config.ServerlessMode.ApdexThreshold.Seconds() 130 131 reply.AccountID = config.ServerlessMode.AccountID 132 reply.TrustedAccountKey = config.ServerlessMode.TrustedAccountKey 133 reply.PrimaryAppID = config.ServerlessMode.PrimaryAppID 134 135 if "" == reply.TrustedAccountKey { 136 // The trust key does not need to be provided by customers whose 137 // account ID is the same as the trust key. 138 reply.TrustedAccountKey = reply.AccountID 139 } 140 141 if "" == reply.PrimaryAppID { 142 reply.PrimaryAppID = serverlessDefaultPrimaryAppID 143 } 144 145 reply.AdaptiveSampler = internal.NewAdaptiveSampler(serverlessSamplerPeriod, 146 serverlessSamplerTarget, time.Now()) 147 148 return reply 149 } 150 151 func (run *appRun) responseCodeIsError(code int) bool { 152 // Response codes below 100 are allowed to be errors to support gRPC. 153 if code < 400 && code >= 100 { 154 return false 155 } 156 for _, ignoreCode := range run.Config.ErrorCollector.IgnoreStatusCodes { 157 if code == ignoreCode { 158 return false 159 } 160 } 161 return true 162 } 163 164 func (run *appRun) txnTraceThreshold(apdexThreshold time.Duration) time.Duration { 165 if run.Config.TransactionTracer.Threshold.IsApdexFailing { 166 return internal.ApdexFailingThreshold(apdexThreshold) 167 } 168 return run.Config.TransactionTracer.Threshold.Duration 169 } 170 171 func (run *appRun) ptrTxnEvents() *uint { return run.Reply.EventData.Limits.TxnEvents } 172 func (run *appRun) ptrCustomEvents() *uint { return run.Reply.EventData.Limits.CustomEvents } 173 func (run *appRun) ptrErrorEvents() *uint { return run.Reply.EventData.Limits.ErrorEvents } 174 func (run *appRun) ptrSpanEvents() *uint { return run.Reply.EventData.Limits.SpanEvents } 175 176 func (run *appRun) MaxTxnEvents() int { return run.limit(run.Config.MaxTxnEvents(), run.ptrTxnEvents) } 177 func (run *appRun) MaxCustomEvents() int { 178 return run.limit(internal.MaxCustomEvents, run.ptrCustomEvents) 179 } 180 func (run *appRun) MaxErrorEvents() int { 181 return run.limit(internal.MaxErrorEvents, run.ptrErrorEvents) 182 } 183 func (run *appRun) MaxSpanEvents() int { return run.limit(internal.MaxSpanEvents, run.ptrSpanEvents) } 184 185 func (run *appRun) limit(dflt int, field func() *uint) int { 186 if nil != field() { 187 return int(*field()) 188 } 189 return dflt 190 } 191 192 func (run *appRun) ReportPeriods() map[internal.HarvestTypes]time.Duration { 193 fixed := internal.HarvestMetricsTraces 194 configurable := internal.HarvestTypes(0) 195 196 for tp, fn := range map[internal.HarvestTypes]func() *uint{ 197 internal.HarvestTxnEvents: run.ptrTxnEvents, 198 internal.HarvestCustomEvents: run.ptrCustomEvents, 199 internal.HarvestErrorEvents: run.ptrErrorEvents, 200 internal.HarvestSpanEvents: run.ptrSpanEvents, 201 } { 202 if nil != run && fn() != nil { 203 configurable |= tp 204 } else { 205 fixed |= tp 206 } 207 } 208 return map[internal.HarvestTypes]time.Duration{ 209 configurable: run.Reply.ConfigurablePeriod(), 210 fixed: internal.FixedHarvestPeriod, 211 } 212 }