github.com/newrelic/go-agent@v3.26.0+incompatible/internal_txn.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  	"errors"
     8  	"fmt"
     9  	"net/http"
    10  	"net/url"
    11  	"reflect"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/newrelic/go-agent/internal"
    17  )
    18  
    19  type txnInput struct {
    20  	// This ResponseWriter should only be accessed using txn.getWriter()
    21  	writer   http.ResponseWriter
    22  	app      Application
    23  	Consumer dataConsumer
    24  	*appRun
    25  }
    26  
    27  type txn struct {
    28  	txnInput
    29  	// This mutex is required since the consumer may call the public API
    30  	// interface functions from different routines.
    31  	sync.Mutex
    32  	// finished indicates whether or not End() has been called.  After
    33  	// finished has been set to true, no recording should occur.
    34  	finished           bool
    35  	numPayloadsCreated uint32
    36  	sampledCalculated  bool
    37  
    38  	ignore bool
    39  
    40  	// wroteHeader prevents capturing multiple response code errors if the
    41  	// user erroneously calls WriteHeader multiple times.
    42  	wroteHeader bool
    43  
    44  	internal.TxnData
    45  
    46  	mainThread   internal.Thread
    47  	asyncThreads []*internal.Thread
    48  }
    49  
    50  type thread struct {
    51  	*txn
    52  	// thread does not have locking because it should only be accessed while
    53  	// the txn is locked.
    54  	thread *internal.Thread
    55  }
    56  
    57  func (txn *txn) markStart(now time.Time) {
    58  	txn.Start = now
    59  	// The mainThread is considered active now.
    60  	txn.mainThread.RecordActivity(now)
    61  
    62  }
    63  
    64  func (txn *txn) markEnd(now time.Time, thread *internal.Thread) {
    65  	txn.Stop = now
    66  	// The thread on which End() was called is considered active now.
    67  	thread.RecordActivity(now)
    68  	txn.Duration = txn.Stop.Sub(txn.Start)
    69  
    70  	// TotalTime is the sum of "active time" across all threads.  A thread
    71  	// was active when it started the transaction, stopped the transaction,
    72  	// started a segment, or stopped a segment.
    73  	txn.TotalTime = txn.mainThread.TotalTime()
    74  	for _, thd := range txn.asyncThreads {
    75  		txn.TotalTime += thd.TotalTime()
    76  	}
    77  	// Ensure that TotalTime is at least as large as Duration so that the
    78  	// graphs look sensible.  This can happen under the following situation:
    79  	// goroutine1: txn.start----|segment1|
    80  	// goroutine2:                                   |segment2|----txn.end
    81  	if txn.Duration > txn.TotalTime {
    82  		txn.TotalTime = txn.Duration
    83  	}
    84  }
    85  
    86  func newTxn(input txnInput, name string) *thread {
    87  	txn := &txn{
    88  		txnInput: input,
    89  	}
    90  	txn.markStart(time.Now())
    91  
    92  	txn.Name = name
    93  	txn.Attrs = internal.NewAttributes(input.AttributeConfig)
    94  
    95  	if input.Config.DistributedTracer.Enabled {
    96  		txn.BetterCAT.Enabled = true
    97  		txn.BetterCAT.Priority = internal.NewPriority()
    98  		txn.TraceIDGenerator = input.Reply.TraceIDGenerator
    99  		txn.BetterCAT.ID = txn.TraceIDGenerator.GenerateTraceID()
   100  		txn.SpanEventsEnabled = txn.Config.SpanEvents.Enabled
   101  		txn.LazilyCalculateSampled = txn.lazilyCalculateSampled
   102  	}
   103  
   104  	txn.Attrs.Agent.Add(internal.AttributeHostDisplayName, txn.Config.HostDisplayName, nil)
   105  	txn.TxnTrace.Enabled = txn.Config.TransactionTracer.Enabled
   106  	txn.TxnTrace.SegmentThreshold = txn.Config.TransactionTracer.SegmentThreshold
   107  	txn.StackTraceThreshold = txn.Config.TransactionTracer.StackTraceThreshold
   108  	txn.SlowQueriesEnabled = txn.Config.DatastoreTracer.SlowQuery.Enabled
   109  	txn.SlowQueryThreshold = txn.Config.DatastoreTracer.SlowQuery.Threshold
   110  
   111  	// Synthetics support is tied up with a transaction's Old CAT field,
   112  	// CrossProcess. To support Synthetics with either BetterCAT or Old CAT,
   113  	// Initialize the CrossProcess field of the transaction, passing in
   114  	// the top-level configuration.
   115  	doOldCAT := txn.Config.CrossApplicationTracer.Enabled
   116  	noGUID := txn.Config.DistributedTracer.Enabled
   117  	txn.CrossProcess.Init(doOldCAT, noGUID, input.Reply)
   118  
   119  	return &thread{
   120  		txn:    txn,
   121  		thread: &txn.mainThread,
   122  	}
   123  }
   124  
   125  // lazilyCalculateSampled calculates and returns whether or not the transaction
   126  // should be sampled.  Sampled is not computed at the beginning of the
   127  // transaction because we want to calculate Sampled only for transactions that
   128  // do not accept an inbound payload.
   129  func (txn *txn) lazilyCalculateSampled() bool {
   130  	if !txn.BetterCAT.Enabled {
   131  		return false
   132  	}
   133  	if txn.sampledCalculated {
   134  		return txn.BetterCAT.Sampled
   135  	}
   136  	txn.BetterCAT.Sampled = txn.Reply.AdaptiveSampler.ComputeSampled(txn.BetterCAT.Priority.Float32(), time.Now())
   137  	if txn.BetterCAT.Sampled {
   138  		txn.BetterCAT.Priority += 1.0
   139  	}
   140  	txn.sampledCalculated = true
   141  	return txn.BetterCAT.Sampled
   142  }
   143  
   144  type requestWrap struct{ request *http.Request }
   145  
   146  func (r requestWrap) Header() http.Header { return r.request.Header }
   147  func (r requestWrap) URL() *url.URL       { return r.request.URL }
   148  func (r requestWrap) Method() string      { return r.request.Method }
   149  
   150  func (r requestWrap) Transport() TransportType {
   151  	if strings.HasPrefix(r.request.Proto, "HTTP") {
   152  		if r.request.TLS != nil {
   153  			return TransportHTTPS
   154  		}
   155  		return TransportHTTP
   156  	}
   157  	return TransportUnknown
   158  
   159  }
   160  
   161  type staticWebRequest struct {
   162  	header    http.Header
   163  	url       *url.URL
   164  	method    string
   165  	transport TransportType
   166  }
   167  
   168  func (r staticWebRequest) Header() http.Header      { return r.header }
   169  func (r staticWebRequest) URL() *url.URL            { return r.url }
   170  func (r staticWebRequest) Method() string           { return r.method }
   171  func (r staticWebRequest) Transport() TransportType { return TransportHTTP }
   172  
   173  func (txn *txn) SetWebRequest(r WebRequest) error {
   174  	txn.Lock()
   175  	defer txn.Unlock()
   176  
   177  	if txn.finished {
   178  		return errAlreadyEnded
   179  	}
   180  
   181  	// Any call to SetWebRequest should indicate a web transaction.
   182  	txn.IsWeb = true
   183  
   184  	if nil == r {
   185  		return nil
   186  	}
   187  	h := r.Header()
   188  	if nil != h {
   189  		txn.Queuing = internal.QueueDuration(h, txn.Start)
   190  
   191  		if p := h.Get(DistributedTracePayloadHeader); p != "" {
   192  			txn.acceptDistributedTracePayloadLocked(r.Transport(), p)
   193  		}
   194  
   195  		txn.CrossProcess.InboundHTTPRequest(h)
   196  	}
   197  
   198  	internal.RequestAgentAttributes(txn.Attrs, r.Method(), h, r.URL())
   199  
   200  	return nil
   201  }
   202  
   203  func (thd *thread) SetWebResponse(w http.ResponseWriter) Transaction {
   204  	txn := thd.txn
   205  	txn.Lock()
   206  	defer txn.Unlock()
   207  
   208  	// Replace the ResponseWriter even if the transaction has ended so that
   209  	// consumers calling ResponseWriter methods on the transactions see that
   210  	// data flowing through as expected.
   211  	txn.writer = w
   212  
   213  	return upgradeTxn(&thread{
   214  		thread: thd.thread,
   215  		txn:    txn,
   216  	})
   217  }
   218  
   219  func (txn *txn) freezeName() {
   220  	if txn.ignore || ("" != txn.FinalName) {
   221  		return
   222  	}
   223  
   224  	txn.FinalName = internal.CreateFullTxnName(txn.Name, txn.Reply, txn.IsWeb)
   225  	if "" == txn.FinalName {
   226  		txn.ignore = true
   227  	}
   228  }
   229  
   230  func (txn *txn) getsApdex() bool {
   231  	return txn.IsWeb
   232  }
   233  
   234  func (txn *txn) shouldSaveTrace() bool {
   235  	if !txn.Config.TransactionTracer.Enabled {
   236  		return false
   237  	}
   238  	if txn.CrossProcess.IsSynthetics() {
   239  		return true
   240  	}
   241  	return txn.Duration >= txn.txnTraceThreshold(txn.ApdexThreshold)
   242  }
   243  
   244  func (txn *txn) MergeIntoHarvest(h *internal.Harvest) {
   245  
   246  	var priority internal.Priority
   247  	if txn.BetterCAT.Enabled {
   248  		priority = txn.BetterCAT.Priority
   249  	} else {
   250  		priority = internal.NewPriority()
   251  	}
   252  
   253  	internal.CreateTxnMetrics(&txn.TxnData, h.Metrics)
   254  	internal.MergeBreakdownMetrics(&txn.TxnData, h.Metrics)
   255  
   256  	if txn.Config.TransactionEvents.Enabled {
   257  		// Allocate a new TxnEvent to prevent a reference to the large transaction.
   258  		alloc := new(internal.TxnEvent)
   259  		*alloc = txn.TxnData.TxnEvent
   260  		h.TxnEvents.AddTxnEvent(alloc, priority)
   261  	}
   262  
   263  	if txn.Reply.CollectErrors {
   264  		internal.MergeTxnErrors(&h.ErrorTraces, txn.Errors, txn.TxnEvent)
   265  	}
   266  
   267  	if txn.Config.ErrorCollector.CaptureEvents {
   268  		for _, e := range txn.Errors {
   269  			errEvent := &internal.ErrorEvent{
   270  				ErrorData: *e,
   271  				TxnEvent:  txn.TxnEvent,
   272  			}
   273  			// Since the stack trace is not used in error events, remove the reference
   274  			// to minimize memory.
   275  			errEvent.Stack = nil
   276  			h.ErrorEvents.Add(errEvent, priority)
   277  		}
   278  	}
   279  
   280  	if txn.shouldSaveTrace() {
   281  		h.TxnTraces.Witness(internal.HarvestTrace{
   282  			TxnEvent: txn.TxnEvent,
   283  			Trace:    txn.TxnTrace,
   284  		})
   285  	}
   286  
   287  	if nil != txn.SlowQueries {
   288  		h.SlowSQLs.Merge(txn.SlowQueries, txn.TxnEvent)
   289  	}
   290  
   291  	if txn.BetterCAT.Sampled && txn.SpanEventsEnabled {
   292  		h.SpanEvents.MergeFromTransaction(&txn.TxnData)
   293  	}
   294  }
   295  
   296  func headersJustWritten(txn *txn, code int, hdr http.Header) {
   297  	txn.Lock()
   298  	defer txn.Unlock()
   299  
   300  	if txn.finished {
   301  		return
   302  	}
   303  	if txn.wroteHeader {
   304  		return
   305  	}
   306  	txn.wroteHeader = true
   307  
   308  	internal.ResponseHeaderAttributes(txn.Attrs, hdr)
   309  	internal.ResponseCodeAttribute(txn.Attrs, code)
   310  
   311  	if txn.appRun.responseCodeIsError(code) {
   312  		e := internal.TxnErrorFromResponseCode(time.Now(), code)
   313  		e.Stack = internal.GetStackTrace()
   314  		txn.noticeErrorInternal(e)
   315  	}
   316  }
   317  
   318  func (txn *txn) responseHeader(hdr http.Header) http.Header {
   319  	txn.Lock()
   320  	defer txn.Unlock()
   321  
   322  	if txn.finished {
   323  		return nil
   324  	}
   325  	if txn.wroteHeader {
   326  		return nil
   327  	}
   328  	if !txn.CrossProcess.Enabled {
   329  		return nil
   330  	}
   331  	if !txn.CrossProcess.IsInbound() {
   332  		return nil
   333  	}
   334  	txn.freezeName()
   335  	contentLength := internal.GetContentLengthFromHeader(hdr)
   336  
   337  	appData, err := txn.CrossProcess.CreateAppData(txn.FinalName, txn.Queuing, time.Since(txn.Start), contentLength)
   338  	if err != nil {
   339  		txn.Config.Logger.Debug("error generating outbound response header", map[string]interface{}{
   340  			"error": err,
   341  		})
   342  		return nil
   343  	}
   344  	return internal.AppDataToHTTPHeader(appData)
   345  }
   346  
   347  func addCrossProcessHeaders(txn *txn, hdr http.Header) {
   348  	// responseHeader() checks the wroteHeader field and returns a nil map if the
   349  	// header has been written, so we don't need a check here.
   350  	if nil != hdr {
   351  		for key, values := range txn.responseHeader(hdr) {
   352  			for _, value := range values {
   353  				hdr.Add(key, value)
   354  			}
   355  		}
   356  	}
   357  }
   358  
   359  // getWriter is used to access the transaction's ResponseWriter. The
   360  // ResponseWriter is mutex protected since it may be changed with
   361  // txn.SetWebResponse, and we want changes to be visible across goroutines.  The
   362  // ResponseWriter is accessed using this getWriter() function rather than directly
   363  // in mutex protected methods since we do NOT want the transaction to be locked
   364  // while calling the ResponseWriter's methods.
   365  func (txn *txn) getWriter() http.ResponseWriter {
   366  	txn.Lock()
   367  	rw := txn.writer
   368  	txn.Unlock()
   369  	return rw
   370  }
   371  
   372  func nilSafeHeader(rw http.ResponseWriter) http.Header {
   373  	if nil == rw {
   374  		return nil
   375  	}
   376  	return rw.Header()
   377  }
   378  
   379  func (txn *txn) Header() http.Header {
   380  	return nilSafeHeader(txn.getWriter())
   381  }
   382  
   383  func (txn *txn) Write(b []byte) (n int, err error) {
   384  	rw := txn.getWriter()
   385  	hdr := nilSafeHeader(rw)
   386  
   387  	// This is safe to call unconditionally, even if Write() is called multiple
   388  	// times; see also the commentary in addCrossProcessHeaders().
   389  	addCrossProcessHeaders(txn, hdr)
   390  
   391  	if rw != nil {
   392  		n, err = rw.Write(b)
   393  	}
   394  
   395  	headersJustWritten(txn, http.StatusOK, hdr)
   396  
   397  	return
   398  }
   399  
   400  func (txn *txn) WriteHeader(code int) {
   401  	rw := txn.getWriter()
   402  	hdr := nilSafeHeader(rw)
   403  
   404  	addCrossProcessHeaders(txn, hdr)
   405  
   406  	if nil != rw {
   407  		rw.WriteHeader(code)
   408  	}
   409  
   410  	headersJustWritten(txn, code, hdr)
   411  }
   412  
   413  func (thd *thread) End() error {
   414  	txn := thd.txn
   415  	txn.Lock()
   416  	defer txn.Unlock()
   417  
   418  	if txn.finished {
   419  		return errAlreadyEnded
   420  	}
   421  
   422  	txn.finished = true
   423  
   424  	r := recover()
   425  	if nil != r {
   426  		e := internal.TxnErrorFromPanic(time.Now(), r)
   427  		e.Stack = internal.GetStackTrace()
   428  		txn.noticeErrorInternal(e)
   429  	}
   430  
   431  	txn.markEnd(time.Now(), thd.thread)
   432  	txn.freezeName()
   433  	// Make a sampling decision if there have been no segments or outbound
   434  	// payloads.
   435  	txn.lazilyCalculateSampled()
   436  
   437  	// Finalise the CAT state.
   438  	if err := txn.CrossProcess.Finalise(txn.Name, txn.Config.AppName); err != nil {
   439  		txn.Config.Logger.Debug("error finalising the cross process state", map[string]interface{}{
   440  			"error": err,
   441  		})
   442  	}
   443  
   444  	// Assign apdexThreshold regardless of whether or not the transaction
   445  	// gets apdex since it may be used to calculate the trace threshold.
   446  	txn.ApdexThreshold = internal.CalculateApdexThreshold(txn.Reply, txn.FinalName)
   447  
   448  	if txn.getsApdex() {
   449  		if txn.HasErrors() {
   450  			txn.Zone = internal.ApdexFailing
   451  		} else {
   452  			txn.Zone = internal.CalculateApdexZone(txn.ApdexThreshold, txn.Duration)
   453  		}
   454  	} else {
   455  		txn.Zone = internal.ApdexNone
   456  	}
   457  
   458  	if txn.Config.Logger.DebugEnabled() {
   459  		txn.Config.Logger.Debug("transaction ended", map[string]interface{}{
   460  			"name":          txn.FinalName,
   461  			"duration_ms":   txn.Duration.Seconds() * 1000.0,
   462  			"ignored":       txn.ignore,
   463  			"app_connected": "" != txn.Reply.RunID,
   464  		})
   465  	}
   466  
   467  	if !txn.ignore {
   468  		txn.Consumer.Consume(txn.Reply.RunID, txn)
   469  	}
   470  
   471  	// Note that if a consumer uses `panic(nil)`, the panic will not
   472  	// propagate.
   473  	if nil != r {
   474  		panic(r)
   475  	}
   476  
   477  	return nil
   478  }
   479  
   480  func (txn *txn) AddAttribute(name string, value interface{}) error {
   481  	txn.Lock()
   482  	defer txn.Unlock()
   483  
   484  	if txn.Config.HighSecurity {
   485  		return errHighSecurityEnabled
   486  	}
   487  
   488  	if !txn.Reply.SecurityPolicies.CustomParameters.Enabled() {
   489  		return errSecurityPolicy
   490  	}
   491  
   492  	if txn.finished {
   493  		return errAlreadyEnded
   494  	}
   495  
   496  	return internal.AddUserAttribute(txn.Attrs, name, value, internal.DestAll)
   497  }
   498  
   499  var (
   500  	errorsDisabled        = errors.New("errors disabled")
   501  	errNilError           = errors.New("nil error")
   502  	errAlreadyEnded       = errors.New("transaction has already ended")
   503  	errSecurityPolicy     = errors.New("disabled by security policy")
   504  	errTransactionIgnored = errors.New("transaction has been ignored")
   505  	errBrowserDisabled    = errors.New("browser disabled by local configuration")
   506  )
   507  
   508  const (
   509  	highSecurityErrorMsg   = "message removed by high security setting"
   510  	securityPolicyErrorMsg = "message removed by security policy"
   511  )
   512  
   513  func (txn *txn) noticeErrorInternal(err internal.ErrorData) error {
   514  	if !txn.Config.ErrorCollector.Enabled {
   515  		return errorsDisabled
   516  	}
   517  
   518  	if nil == txn.Errors {
   519  		txn.Errors = internal.NewTxnErrors(internal.MaxTxnErrors)
   520  	}
   521  
   522  	if txn.Config.HighSecurity {
   523  		err.Msg = highSecurityErrorMsg
   524  	}
   525  
   526  	if !txn.Reply.SecurityPolicies.AllowRawExceptionMessages.Enabled() {
   527  		err.Msg = securityPolicyErrorMsg
   528  	}
   529  
   530  	txn.Errors.Add(err)
   531  	txn.TxnData.TxnEvent.HasError = true //mark transaction as having an error
   532  	return nil
   533  }
   534  
   535  var (
   536  	errTooManyErrorAttributes = fmt.Errorf("too many extra attributes: limit is %d",
   537  		internal.AttributeErrorLimit)
   538  )
   539  
   540  // errorCause returns the error's deepest wrapped ancestor.
   541  func errorCause(err error) error {
   542  	for {
   543  		if unwrapper, ok := err.(interface{ Unwrap() error }); ok {
   544  			if next := unwrapper.Unwrap(); nil != next {
   545  				err = next
   546  				continue
   547  			}
   548  		}
   549  		return err
   550  	}
   551  }
   552  
   553  func errorClassMethod(err error) string {
   554  	if ec, ok := err.(ErrorClasser); ok {
   555  		return ec.ErrorClass()
   556  	}
   557  	return ""
   558  }
   559  
   560  func errorStackTraceMethod(err error) internal.StackTrace {
   561  	if st, ok := err.(StackTracer); ok {
   562  		return st.StackTrace()
   563  	}
   564  	return nil
   565  }
   566  
   567  func errorAttributesMethod(err error) map[string]interface{} {
   568  	if st, ok := err.(ErrorAttributer); ok {
   569  		return st.ErrorAttributes()
   570  	}
   571  	return nil
   572  }
   573  
   574  func errDataFromError(input error) (data internal.ErrorData, err error) {
   575  	cause := errorCause(input)
   576  
   577  	data = internal.ErrorData{
   578  		When: time.Now(),
   579  		Msg:  input.Error(),
   580  	}
   581  
   582  	if c := errorClassMethod(input); "" != c {
   583  		// If the error implements ErrorClasser, use that.
   584  		data.Klass = c
   585  	} else if c := errorClassMethod(cause); "" != c {
   586  		// Otherwise, if the error's cause implements ErrorClasser, use that.
   587  		data.Klass = c
   588  	} else {
   589  		// As a final fallback, use the type of the error's cause.
   590  		data.Klass = reflect.TypeOf(cause).String()
   591  	}
   592  
   593  	if st := errorStackTraceMethod(input); nil != st {
   594  		// If the error implements StackTracer, use that.
   595  		data.Stack = st
   596  	} else if st := errorStackTraceMethod(cause); nil != st {
   597  		// Otherwise, if the error's cause implements StackTracer, use that.
   598  		data.Stack = st
   599  	} else {
   600  		// As a final fallback, generate a StackTrace here.
   601  		data.Stack = internal.GetStackTrace()
   602  	}
   603  
   604  	var unvetted map[string]interface{}
   605  	if ats := errorAttributesMethod(input); nil != ats {
   606  		// If the error implements ErrorAttributer, use that.
   607  		unvetted = ats
   608  	} else {
   609  		// Otherwise, if the error's cause implements ErrorAttributer, use that.
   610  		unvetted = errorAttributesMethod(cause)
   611  	}
   612  	if unvetted != nil {
   613  		if len(unvetted) > internal.AttributeErrorLimit {
   614  			err = errTooManyErrorAttributes
   615  			return
   616  		}
   617  
   618  		data.ExtraAttributes = make(map[string]interface{})
   619  		for key, val := range unvetted {
   620  			val, err = internal.ValidateUserAttribute(key, val)
   621  			if nil != err {
   622  				return
   623  			}
   624  			data.ExtraAttributes[key] = val
   625  		}
   626  	}
   627  
   628  	return data, nil
   629  }
   630  
   631  func (txn *txn) NoticeError(input error) error {
   632  	txn.Lock()
   633  	defer txn.Unlock()
   634  
   635  	if txn.finished {
   636  		return errAlreadyEnded
   637  	}
   638  
   639  	if nil == input {
   640  		return errNilError
   641  	}
   642  
   643  	data, err := errDataFromError(input)
   644  	if nil != err {
   645  		return err
   646  	}
   647  
   648  	if txn.Config.HighSecurity || !txn.Reply.SecurityPolicies.CustomParameters.Enabled() {
   649  		data.ExtraAttributes = nil
   650  	}
   651  
   652  	return txn.noticeErrorInternal(data)
   653  }
   654  
   655  func (txn *txn) SetName(name string) error {
   656  	txn.Lock()
   657  	defer txn.Unlock()
   658  
   659  	if txn.finished {
   660  		return errAlreadyEnded
   661  	}
   662  
   663  	txn.Name = name
   664  	return nil
   665  }
   666  
   667  func (txn *txn) Ignore() error {
   668  	txn.Lock()
   669  	defer txn.Unlock()
   670  
   671  	if txn.finished {
   672  		return errAlreadyEnded
   673  	}
   674  	txn.ignore = true
   675  	return nil
   676  }
   677  
   678  func (thd *thread) StartSegmentNow() SegmentStartTime {
   679  	var s internal.SegmentStartTime
   680  	txn := thd.txn
   681  	txn.Lock()
   682  	if !txn.finished {
   683  		s = internal.StartSegment(&txn.TxnData, thd.thread, time.Now())
   684  	}
   685  	txn.Unlock()
   686  	return SegmentStartTime{
   687  		segment: segment{
   688  			start:  s,
   689  			thread: thd,
   690  		},
   691  	}
   692  }
   693  
   694  const (
   695  	// Browser fields are encoded using the first digits of the license
   696  	// key.
   697  	browserEncodingKeyLimit = 13
   698  )
   699  
   700  func browserEncodingKey(licenseKey string) []byte {
   701  	key := []byte(licenseKey)
   702  	if len(key) > browserEncodingKeyLimit {
   703  		key = key[0:browserEncodingKeyLimit]
   704  	}
   705  	return key
   706  }
   707  
   708  func (txn *txn) BrowserTimingHeader() (*BrowserTimingHeader, error) {
   709  	txn.Lock()
   710  	defer txn.Unlock()
   711  
   712  	if !txn.Config.BrowserMonitoring.Enabled {
   713  		return nil, errBrowserDisabled
   714  	}
   715  
   716  	if txn.Reply.AgentLoader == "" {
   717  		// If the loader is empty, either browser has been disabled
   718  		// by the server or the application is not yet connected.
   719  		return nil, nil
   720  	}
   721  
   722  	if txn.finished {
   723  		return nil, errAlreadyEnded
   724  	}
   725  
   726  	txn.freezeName()
   727  
   728  	// Freezing the name might cause the transaction to be ignored, so check
   729  	// this after txn.freezeName().
   730  	if txn.ignore {
   731  		return nil, errTransactionIgnored
   732  	}
   733  
   734  	encodingKey := browserEncodingKey(txn.Config.License)
   735  
   736  	attrs, err := internal.Obfuscate(internal.BrowserAttributes(txn.Attrs), encodingKey)
   737  	if err != nil {
   738  		return nil, fmt.Errorf("error getting browser attributes: %v", err)
   739  	}
   740  
   741  	name, err := internal.Obfuscate([]byte(txn.FinalName), encodingKey)
   742  	if err != nil {
   743  		return nil, fmt.Errorf("error obfuscating name: %v", err)
   744  	}
   745  
   746  	return &BrowserTimingHeader{
   747  		agentLoader: txn.Reply.AgentLoader,
   748  		info: browserInfo{
   749  			Beacon:                txn.Reply.Beacon,
   750  			LicenseKey:            txn.Reply.BrowserKey,
   751  			ApplicationID:         txn.Reply.AppID,
   752  			TransactionName:       name,
   753  			QueueTimeMillis:       txn.Queuing.Nanoseconds() / (1000 * 1000),
   754  			ApplicationTimeMillis: time.Now().Sub(txn.Start).Nanoseconds() / (1000 * 1000),
   755  			ObfuscatedAttributes:  attrs,
   756  			ErrorBeacon:           txn.Reply.ErrorBeacon,
   757  			Agent:                 txn.Reply.JSAgentFile,
   758  		},
   759  	}, nil
   760  }
   761  
   762  func createThread(txn *txn) *internal.Thread {
   763  	newThread := internal.NewThread(&txn.TxnData)
   764  	txn.asyncThreads = append(txn.asyncThreads, newThread)
   765  	return newThread
   766  }
   767  
   768  func (thd *thread) NewGoroutine() Transaction {
   769  	txn := thd.txn
   770  	txn.Lock()
   771  	defer txn.Unlock()
   772  
   773  	if txn.finished {
   774  		// If the transaction has finished, return the same thread.
   775  		return upgradeTxn(thd)
   776  	}
   777  	return upgradeTxn(&thread{
   778  		thread: createThread(txn),
   779  		txn:    txn,
   780  	})
   781  }
   782  
   783  type segment struct {
   784  	start  internal.SegmentStartTime
   785  	thread *thread
   786  }
   787  
   788  func endSegment(s *Segment) error {
   789  	if nil == s {
   790  		return nil
   791  	}
   792  	thd := s.StartTime.thread
   793  	if nil == thd {
   794  		return nil
   795  	}
   796  	txn := thd.txn
   797  	var err error
   798  	txn.Lock()
   799  	if txn.finished {
   800  		err = errAlreadyEnded
   801  	} else {
   802  		err = internal.EndBasicSegment(&txn.TxnData, thd.thread, s.StartTime.start, time.Now(), s.Name)
   803  	}
   804  	txn.Unlock()
   805  	return err
   806  }
   807  
   808  func endDatastore(s *DatastoreSegment) error {
   809  	if nil == s {
   810  		return nil
   811  	}
   812  	thd := s.StartTime.thread
   813  	if nil == thd {
   814  		return nil
   815  	}
   816  	txn := thd.txn
   817  	txn.Lock()
   818  	defer txn.Unlock()
   819  
   820  	if txn.finished {
   821  		return errAlreadyEnded
   822  	}
   823  	if txn.Config.HighSecurity {
   824  		s.QueryParameters = nil
   825  	}
   826  	if !txn.Config.DatastoreTracer.QueryParameters.Enabled {
   827  		s.QueryParameters = nil
   828  	}
   829  	if txn.Reply.SecurityPolicies.RecordSQL.IsSet() {
   830  		s.QueryParameters = nil
   831  		if !txn.Reply.SecurityPolicies.RecordSQL.Enabled() {
   832  			s.ParameterizedQuery = ""
   833  		}
   834  	}
   835  	if !txn.Config.DatastoreTracer.DatabaseNameReporting.Enabled {
   836  		s.DatabaseName = ""
   837  	}
   838  	if !txn.Config.DatastoreTracer.InstanceReporting.Enabled {
   839  		s.Host = ""
   840  		s.PortPathOrID = ""
   841  	}
   842  	return internal.EndDatastoreSegment(internal.EndDatastoreParams{
   843  		TxnData:            &txn.TxnData,
   844  		Thread:             thd.thread,
   845  		Start:              s.StartTime.start,
   846  		Now:                time.Now(),
   847  		Product:            string(s.Product),
   848  		Collection:         s.Collection,
   849  		Operation:          s.Operation,
   850  		ParameterizedQuery: s.ParameterizedQuery,
   851  		QueryParameters:    s.QueryParameters,
   852  		Host:               s.Host,
   853  		PortPathOrID:       s.PortPathOrID,
   854  		Database:           s.DatabaseName,
   855  	})
   856  }
   857  
   858  func externalSegmentMethod(s *ExternalSegment) string {
   859  	if "" != s.Procedure {
   860  		return s.Procedure
   861  	}
   862  	r := s.Request
   863  	if nil != s.Response && nil != s.Response.Request {
   864  		r = s.Response.Request
   865  	}
   866  
   867  	if nil != r {
   868  		if "" != r.Method {
   869  			return r.Method
   870  		}
   871  		// Golang's http package states that when a client's Request has
   872  		// an empty string for Method, the method is GET.
   873  		return "GET"
   874  	}
   875  
   876  	return ""
   877  }
   878  
   879  func externalSegmentURL(s *ExternalSegment) (*url.URL, error) {
   880  	if "" != s.URL {
   881  		return url.Parse(s.URL)
   882  	}
   883  	r := s.Request
   884  	if nil != s.Response && nil != s.Response.Request {
   885  		r = s.Response.Request
   886  	}
   887  	if r != nil {
   888  		return r.URL, nil
   889  	}
   890  	return nil, nil
   891  }
   892  
   893  func endExternal(s *ExternalSegment) error {
   894  	if nil == s {
   895  		return nil
   896  	}
   897  	thd := s.StartTime.thread
   898  	if nil == thd {
   899  		return nil
   900  	}
   901  	txn := thd.txn
   902  	txn.Lock()
   903  	defer txn.Unlock()
   904  
   905  	if txn.finished {
   906  		return errAlreadyEnded
   907  	}
   908  	u, err := externalSegmentURL(s)
   909  	if nil != err {
   910  		return err
   911  	}
   912  	return internal.EndExternalSegment(internal.EndExternalParams{
   913  		TxnData:  &txn.TxnData,
   914  		Thread:   thd.thread,
   915  		Start:    s.StartTime.start,
   916  		Now:      time.Now(),
   917  		Logger:   txn.Config.Logger,
   918  		Response: s.Response,
   919  		URL:      u,
   920  		Host:     s.Host,
   921  		Library:  s.Library,
   922  		Method:   externalSegmentMethod(s),
   923  	})
   924  }
   925  
   926  func endMessage(s *MessageProducerSegment) error {
   927  	if nil == s {
   928  		return nil
   929  	}
   930  	thd := s.StartTime.thread
   931  	if nil == thd {
   932  		return nil
   933  	}
   934  	txn := thd.txn
   935  	txn.Lock()
   936  	defer txn.Unlock()
   937  
   938  	if txn.finished {
   939  		return errAlreadyEnded
   940  	}
   941  
   942  	if "" == s.DestinationType {
   943  		s.DestinationType = MessageQueue
   944  	}
   945  
   946  	return internal.EndMessageSegment(internal.EndMessageParams{
   947  		TxnData:         &txn.TxnData,
   948  		Thread:          thd.thread,
   949  		Start:           s.StartTime.start,
   950  		Now:             time.Now(),
   951  		Library:         s.Library,
   952  		Logger:          txn.Config.Logger,
   953  		DestinationName: s.DestinationName,
   954  		DestinationType: string(s.DestinationType),
   955  		DestinationTemp: s.DestinationTemporary,
   956  	})
   957  }
   958  
   959  // oldCATOutboundHeaders generates the Old CAT and Synthetics headers, depending
   960  // on whether Old CAT is enabled or any Synthetics functionality has been
   961  // triggered in the agent.
   962  func oldCATOutboundHeaders(txn *txn) http.Header {
   963  	txn.Lock()
   964  	defer txn.Unlock()
   965  
   966  	if txn.finished {
   967  		return http.Header{}
   968  	}
   969  
   970  	metadata, err := txn.CrossProcess.CreateCrossProcessMetadata(txn.Name, txn.Config.AppName)
   971  	if err != nil {
   972  		txn.Config.Logger.Debug("error generating outbound headers", map[string]interface{}{
   973  			"error": err,
   974  		})
   975  
   976  		// It's possible for CreateCrossProcessMetadata() to error and still have a
   977  		// Synthetics header, so we'll still fall through to returning headers
   978  		// based on whatever metadata was returned.
   979  	}
   980  
   981  	return internal.MetadataToHTTPHeader(metadata)
   982  }
   983  
   984  func outboundHeaders(s *ExternalSegment) http.Header {
   985  	thd := s.StartTime.thread
   986  
   987  	if nil == thd {
   988  		return http.Header{}
   989  	}
   990  	txn := thd.txn
   991  	hdr := oldCATOutboundHeaders(txn)
   992  
   993  	// hdr may be empty, or it may contain headers.  If DistributedTracer
   994  	// is enabled, add more to the existing hdr
   995  	if p := thd.CreateDistributedTracePayload().HTTPSafe(); "" != p {
   996  		hdr.Add(DistributedTracePayloadHeader, p)
   997  		return hdr
   998  	}
   999  
  1000  	return hdr
  1001  }
  1002  
  1003  const (
  1004  	maxSampledDistributedPayloads = 35
  1005  )
  1006  
  1007  type shimPayload struct{}
  1008  
  1009  func (s shimPayload) Text() string     { return "" }
  1010  func (s shimPayload) HTTPSafe() string { return "" }
  1011  
  1012  func (thd *thread) CreateDistributedTracePayload() (payload DistributedTracePayload) {
  1013  	payload = shimPayload{}
  1014  
  1015  	txn := thd.txn
  1016  	txn.Lock()
  1017  	defer txn.Unlock()
  1018  
  1019  	if !txn.BetterCAT.Enabled {
  1020  		return
  1021  	}
  1022  
  1023  	if txn.finished {
  1024  		txn.CreatePayloadException = true
  1025  		return
  1026  	}
  1027  
  1028  	if "" == txn.Reply.AccountID || "" == txn.Reply.TrustedAccountKey {
  1029  		// We can't create a payload:  The application is not yet
  1030  		// connected or serverless distributed tracing configuration was
  1031  		// not provided.
  1032  		return
  1033  	}
  1034  
  1035  	txn.numPayloadsCreated++
  1036  
  1037  	var p internal.Payload
  1038  	p.Type = internal.CallerType
  1039  	p.Account = txn.Reply.AccountID
  1040  
  1041  	p.App = txn.Reply.PrimaryAppID
  1042  	p.TracedID = txn.BetterCAT.TraceID()
  1043  	p.Priority = txn.BetterCAT.Priority
  1044  	p.Timestamp.Set(time.Now())
  1045  	p.TransactionID = txn.BetterCAT.ID // Set the transaction ID to the transaction guid.
  1046  
  1047  	if txn.Reply.AccountID != txn.Reply.TrustedAccountKey {
  1048  		p.TrustedAccountKey = txn.Reply.TrustedAccountKey
  1049  	}
  1050  
  1051  	sampled := txn.lazilyCalculateSampled()
  1052  	if sampled && txn.SpanEventsEnabled {
  1053  		p.ID = txn.CurrentSpanIdentifier(thd.thread)
  1054  	}
  1055  
  1056  	// limit the number of outbound sampled=true payloads to prevent too
  1057  	// many downstream sampled events.
  1058  	p.SetSampled(false)
  1059  	if txn.numPayloadsCreated < maxSampledDistributedPayloads {
  1060  		p.SetSampled(sampled)
  1061  	}
  1062  
  1063  	txn.CreatePayloadSuccess = true
  1064  
  1065  	payload = p
  1066  	return
  1067  }
  1068  
  1069  var (
  1070  	errOutboundPayloadCreated   = errors.New("outbound payload already created")
  1071  	errAlreadyAccepted          = errors.New("AcceptDistributedTracePayload has already been called")
  1072  	errInboundPayloadDTDisabled = errors.New("DistributedTracer must be enabled to accept an inbound payload")
  1073  	errTrustedAccountKey        = errors.New("trusted account key missing or does not match")
  1074  )
  1075  
  1076  func (txn *txn) AcceptDistributedTracePayload(t TransportType, p interface{}) error {
  1077  	txn.Lock()
  1078  	defer txn.Unlock()
  1079  
  1080  	return txn.acceptDistributedTracePayloadLocked(t, p)
  1081  }
  1082  
  1083  func (txn *txn) acceptDistributedTracePayloadLocked(t TransportType, p interface{}) error {
  1084  
  1085  	if !txn.BetterCAT.Enabled {
  1086  		return errInboundPayloadDTDisabled
  1087  	}
  1088  
  1089  	if txn.finished {
  1090  		txn.AcceptPayloadException = true
  1091  		return errAlreadyEnded
  1092  	}
  1093  
  1094  	if txn.numPayloadsCreated > 0 {
  1095  		txn.AcceptPayloadCreateBeforeAccept = true
  1096  		return errOutboundPayloadCreated
  1097  	}
  1098  
  1099  	if txn.BetterCAT.Inbound != nil {
  1100  		txn.AcceptPayloadIgnoredMultiple = true
  1101  		return errAlreadyAccepted
  1102  	}
  1103  
  1104  	if nil == p {
  1105  		txn.AcceptPayloadNullPayload = true
  1106  		return nil
  1107  	}
  1108  
  1109  	if "" == txn.Reply.AccountID || "" == txn.Reply.TrustedAccountKey {
  1110  		// We can't accept a payload:  The application is not yet
  1111  		// connected or serverless distributed tracing configuration was
  1112  		// not provided.
  1113  		return nil
  1114  	}
  1115  
  1116  	payload, err := internal.AcceptPayload(p)
  1117  	if nil != err {
  1118  		if _, ok := err.(internal.ErrPayloadParse); ok {
  1119  			txn.AcceptPayloadParseException = true
  1120  		} else if _, ok := err.(internal.ErrUnsupportedPayloadVersion); ok {
  1121  			txn.AcceptPayloadIgnoredVersion = true
  1122  		} else if _, ok := err.(internal.ErrPayloadMissingField); ok {
  1123  			txn.AcceptPayloadParseException = true
  1124  		} else {
  1125  			txn.AcceptPayloadException = true
  1126  		}
  1127  		return err
  1128  	}
  1129  
  1130  	if nil == payload {
  1131  		return nil
  1132  	}
  1133  
  1134  	// now that we have a parsed and alloc'd payload,
  1135  	// let's make  sure it has the correct fields
  1136  	if err := payload.IsValid(); nil != err {
  1137  		txn.AcceptPayloadParseException = true
  1138  		return err
  1139  	}
  1140  
  1141  	// and let's also do our trustedKey check
  1142  	receivedTrustKey := payload.TrustedAccountKey
  1143  	if "" == receivedTrustKey {
  1144  		receivedTrustKey = payload.Account
  1145  	}
  1146  	if receivedTrustKey != txn.Reply.TrustedAccountKey {
  1147  		txn.AcceptPayloadUntrustedAccount = true
  1148  		return errTrustedAccountKey
  1149  	}
  1150  
  1151  	if 0 != payload.Priority {
  1152  		txn.BetterCAT.Priority = payload.Priority
  1153  	}
  1154  
  1155  	// a nul payload.Sampled means the a field wasn't provided
  1156  	if nil != payload.Sampled {
  1157  		txn.BetterCAT.Sampled = *payload.Sampled
  1158  		txn.sampledCalculated = true
  1159  	}
  1160  
  1161  	txn.BetterCAT.Inbound = payload
  1162  
  1163  	// TransportType's name field is not mutable outside of its package
  1164  	// so the only check needed is if the caller is using an empty TransportType
  1165  	txn.BetterCAT.Inbound.TransportType = t.name
  1166  	if t.name == "" {
  1167  		txn.BetterCAT.Inbound.TransportType = TransportUnknown.name
  1168  		txn.Config.Logger.Debug("Invalid transport type, defaulting to Unknown", map[string]interface{}{})
  1169  	}
  1170  
  1171  	if tm := payload.Timestamp.Time(); txn.Start.After(tm) {
  1172  		txn.BetterCAT.Inbound.TransportDuration = txn.Start.Sub(tm)
  1173  	}
  1174  
  1175  	txn.AcceptPayloadSuccess = true
  1176  
  1177  	return nil
  1178  }
  1179  
  1180  func (txn *txn) Application() Application {
  1181  	return txn.app
  1182  }
  1183  
  1184  func (thd *thread) AddAgentSpanAttribute(key internal.SpanAttribute, val string) {
  1185  	thd.thread.AddAgentSpanAttribute(key, val)
  1186  }
  1187  
  1188  var (
  1189  	// Ensure that txn implements AddAgentAttributer to avoid breaking
  1190  	// integration package type assertions.
  1191  	_ internal.AddAgentAttributer = &txn{}
  1192  )
  1193  
  1194  func (txn *txn) AddAgentAttribute(id internal.AgentAttributeID, stringVal string, otherVal interface{}) {
  1195  	txn.Lock()
  1196  	defer txn.Unlock()
  1197  
  1198  	if txn.finished {
  1199  		return
  1200  	}
  1201  	txn.Attrs.Agent.Add(id, stringVal, otherVal)
  1202  }
  1203  
  1204  func (thd *thread) GetTraceMetadata() (metadata TraceMetadata) {
  1205  	txn := thd.txn
  1206  	txn.Lock()
  1207  	defer txn.Unlock()
  1208  
  1209  	if txn.finished {
  1210  		return
  1211  	}
  1212  
  1213  	if txn.BetterCAT.Enabled {
  1214  		metadata.TraceID = txn.BetterCAT.TraceID()
  1215  		if txn.SpanEventsEnabled && txn.lazilyCalculateSampled() {
  1216  			metadata.SpanID = txn.CurrentSpanIdentifier(thd.thread)
  1217  		}
  1218  	}
  1219  
  1220  	return
  1221  }
  1222  
  1223  func (thd *thread) GetLinkingMetadata() (metadata LinkingMetadata) {
  1224  	txn := thd.txn
  1225  	metadata.EntityName = txn.appRun.firstAppName
  1226  	metadata.EntityType = "SERVICE"
  1227  	metadata.EntityGUID = txn.appRun.Reply.EntityGUID
  1228  	metadata.Hostname = internal.ThisHost
  1229  
  1230  	md := thd.GetTraceMetadata()
  1231  	metadata.TraceID = md.TraceID
  1232  	metadata.SpanID = md.SpanID
  1233  
  1234  	return
  1235  }
  1236  
  1237  func (txn *txn) IsSampled() bool {
  1238  	txn.Lock()
  1239  	defer txn.Unlock()
  1240  
  1241  	if txn.finished {
  1242  		return false
  1243  	}
  1244  
  1245  	return txn.lazilyCalculateSampled()
  1246  }