github.com/newrelic/go-agent@v3.26.0+incompatible/internal/connect_reply.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package internal 5 6 import ( 7 "encoding/json" 8 "strings" 9 "time" 10 ) 11 12 // AgentRunID identifies the current connection with the collector. 13 type AgentRunID string 14 15 func (id AgentRunID) String() string { 16 return string(id) 17 } 18 19 // PreconnectReply contains settings from the preconnect endpoint. 20 type PreconnectReply struct { 21 Collector string `json:"redirect_host"` 22 SecurityPolicies SecurityPolicies `json:"security_policies"` 23 } 24 25 // ConnectReply contains all of the settings and state send down from the 26 // collector. It should not be modified after creation. 27 type ConnectReply struct { 28 RunID AgentRunID `json:"agent_run_id"` 29 RequestHeadersMap map[string]string `json:"request_headers_map"` 30 MaxPayloadSizeInBytes int `json:"max_payload_size_in_bytes"` 31 EntityGUID string `json:"entity_guid"` 32 33 // Transaction Name Modifiers 34 SegmentTerms segmentRules `json:"transaction_segment_terms"` 35 TxnNameRules metricRules `json:"transaction_name_rules"` 36 URLRules metricRules `json:"url_rules"` 37 MetricRules metricRules `json:"metric_name_rules"` 38 39 // Cross Process 40 EncodingKey string `json:"encoding_key"` 41 CrossProcessID string `json:"cross_process_id"` 42 TrustedAccounts trustedAccountSet `json:"trusted_account_ids"` 43 44 // Settings 45 KeyTxnApdex map[string]float64 `json:"web_transactions_apdex"` 46 ApdexThresholdSeconds float64 `json:"apdex_t"` 47 CollectAnalyticsEvents bool `json:"collect_analytics_events"` 48 CollectCustomEvents bool `json:"collect_custom_events"` 49 CollectTraces bool `json:"collect_traces"` 50 CollectErrors bool `json:"collect_errors"` 51 CollectErrorEvents bool `json:"collect_error_events"` 52 CollectSpanEvents bool `json:"collect_span_events"` 53 54 // RUM 55 AgentLoader string `json:"js_agent_loader"` 56 Beacon string `json:"beacon"` 57 BrowserKey string `json:"browser_key"` 58 AppID string `json:"application_id"` 59 ErrorBeacon string `json:"error_beacon"` 60 JSAgentFile string `json:"js_agent_file"` 61 62 // PreconnectReply fields are not in the connect reply, this embedding 63 // is done to simplify code. 64 PreconnectReply `json:"-"` 65 66 Messages []struct { 67 Message string `json:"message"` 68 Level string `json:"level"` 69 } `json:"messages"` 70 71 AdaptiveSampler AdaptiveSampler 72 // TraceIDGenerator creates random IDs for distributed tracing. It 73 // exists here in the connect reply so it can be modified to create 74 // deterministic identifiers in tests. 75 TraceIDGenerator *TraceIDGenerator `json:"-"` 76 77 // BetterCAT/Distributed Tracing 78 AccountID string `json:"account_id"` 79 TrustedAccountKey string `json:"trusted_account_key"` 80 PrimaryAppID string `json:"primary_application_id"` 81 SamplingTarget uint64 `json:"sampling_target"` 82 SamplingTargetPeriodInSeconds int `json:"sampling_target_period_in_seconds"` 83 84 // rulesCache caches the results of calling CreateFullTxnName. It 85 // exists here in ConnectReply since it is specific to a set of rules 86 // and is shared between transactions. 87 rulesCache *rulesCache 88 89 ServerSideConfig struct { 90 TransactionTracerEnabled *bool `json:"transaction_tracer.enabled"` 91 // TransactionTracerThreshold should contain either a number or 92 // "apdex_f" if it is non-nil. 93 TransactionTracerThreshold interface{} `json:"transaction_tracer.transaction_threshold"` 94 TransactionTracerStackTraceThreshold *float64 `json:"transaction_tracer.stack_trace_threshold"` 95 ErrorCollectorEnabled *bool `json:"error_collector.enabled"` 96 ErrorCollectorIgnoreStatusCodes []int `json:"error_collector.ignore_status_codes"` 97 CrossApplicationTracerEnabled *bool `json:"cross_application_tracer.enabled"` 98 } `json:"agent_config"` 99 100 // Faster Event Harvest 101 EventData EventHarvestConfig `json:"event_harvest_config"` 102 } 103 104 // EventHarvestConfig contains fields relating to faster event harvest. 105 // This structure is used in the connect request (to send up defaults) 106 // and in the connect response (to get the server values). 107 // 108 // https://source.datanerd.us/agents/agent-specs/blob/master/Connect-LEGACY.md#event_harvest_config-hash 109 // https://source.datanerd.us/agents/agent-specs/blob/master/Connect-LEGACY.md#event-harvest-config 110 type EventHarvestConfig struct { 111 ReportPeriodMs int `json:"report_period_ms,omitempty"` 112 Limits struct { 113 TxnEvents *uint `json:"analytic_event_data,omitempty"` 114 CustomEvents *uint `json:"custom_event_data,omitempty"` 115 ErrorEvents *uint `json:"error_event_data,omitempty"` 116 SpanEvents *uint `json:"span_event_data,omitempty"` 117 } `json:"harvest_limits"` 118 } 119 120 // ConfigurablePeriod returns the Faster Event Harvest configurable reporting period if it is set, or the default 121 // report period otherwise. 122 func (r *ConnectReply) ConfigurablePeriod() time.Duration { 123 ms := DefaultConfigurableEventHarvestMs 124 if nil != r && r.EventData.ReportPeriodMs > 0 { 125 ms = r.EventData.ReportPeriodMs 126 } 127 return time.Duration(ms) * time.Millisecond 128 } 129 130 func uintPtr(x uint) *uint { return &x } 131 132 // DefaultEventHarvestConfig provides faster event harvest defaults. 133 func DefaultEventHarvestConfig(eventer MaxTxnEventer) EventHarvestConfig { 134 cfg := EventHarvestConfig{} 135 cfg.ReportPeriodMs = DefaultConfigurableEventHarvestMs 136 cfg.Limits.TxnEvents = uintPtr(uint(eventer.MaxTxnEvents())) 137 cfg.Limits.CustomEvents = uintPtr(uint(MaxCustomEvents)) 138 cfg.Limits.ErrorEvents = uintPtr(uint(MaxErrorEvents)) 139 return cfg 140 } 141 142 type trustedAccountSet map[int]struct{} 143 144 func (t *trustedAccountSet) IsTrusted(account int) bool { 145 _, exists := (*t)[account] 146 return exists 147 } 148 149 func (t *trustedAccountSet) UnmarshalJSON(data []byte) error { 150 accounts := make([]int, 0) 151 if err := json.Unmarshal(data, &accounts); err != nil { 152 return err 153 } 154 155 *t = make(trustedAccountSet) 156 for _, account := range accounts { 157 (*t)[account] = struct{}{} 158 } 159 160 return nil 161 } 162 163 // ConnectReplyDefaults returns a newly allocated ConnectReply with the proper 164 // default settings. A pointer to a global is not used to prevent consumers 165 // from changing the default settings. 166 func ConnectReplyDefaults() *ConnectReply { 167 return &ConnectReply{ 168 ApdexThresholdSeconds: 0.5, 169 CollectAnalyticsEvents: true, 170 CollectCustomEvents: true, 171 CollectTraces: true, 172 CollectErrors: true, 173 CollectErrorEvents: true, 174 CollectSpanEvents: true, 175 MaxPayloadSizeInBytes: maxPayloadSizeInBytes, 176 // No transactions should be sampled before the application is 177 // connected. 178 AdaptiveSampler: SampleNothing{}, 179 180 SamplingTarget: 10, 181 SamplingTargetPeriodInSeconds: 60, 182 183 TraceIDGenerator: NewTraceIDGenerator(int64(time.Now().UnixNano())), 184 } 185 } 186 187 // CalculateApdexThreshold calculates the apdex threshold. 188 func CalculateApdexThreshold(c *ConnectReply, txnName string) time.Duration { 189 if t, ok := c.KeyTxnApdex[txnName]; ok { 190 return FloatSecondsToDuration(t) 191 } 192 return FloatSecondsToDuration(c.ApdexThresholdSeconds) 193 } 194 195 // CreateFullTxnName uses collector rules and the appropriate metric prefix to 196 // construct the full transaction metric name from the name given by the 197 // consumer. 198 func CreateFullTxnName(input string, reply *ConnectReply, isWeb bool) string { 199 if name := reply.rulesCache.find(input, isWeb); "" != name { 200 return name 201 } 202 name := constructFullTxnName(input, reply, isWeb) 203 if "" != name { 204 // Note that we don't cache situations where the rules say 205 // ignore. It would increase complication (we would need to 206 // disambiguate not-found vs ignore). Also, the ignore code 207 // path is probably extremely uncommon. 208 reply.rulesCache.set(input, isWeb, name) 209 } 210 return name 211 } 212 213 func constructFullTxnName(input string, reply *ConnectReply, isWeb bool) string { 214 var afterURLRules string 215 if "" != input { 216 afterURLRules = reply.URLRules.Apply(input) 217 if "" == afterURLRules { 218 return "" 219 } 220 } 221 222 prefix := backgroundMetricPrefix 223 if isWeb { 224 prefix = webMetricPrefix 225 } 226 227 var beforeNameRules string 228 if strings.HasPrefix(afterURLRules, "/") { 229 beforeNameRules = prefix + afterURLRules 230 } else { 231 beforeNameRules = prefix + "/" + afterURLRules 232 } 233 234 afterNameRules := reply.TxnNameRules.Apply(beforeNameRules) 235 if "" == afterNameRules { 236 return "" 237 } 238 239 return reply.SegmentTerms.apply(afterNameRules) 240 }