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  }