github.com/lulzWill/go-agent@v2.1.2+incompatible/internal/txn_events.go (about)

     1  package internal
     2  
     3  import (
     4  	"bytes"
     5  	"math/rand"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  // DatastoreExternalTotals contains overview of external and datastore calls
    12  // made during a transaction.
    13  type DatastoreExternalTotals struct {
    14  	externalCallCount  uint64
    15  	externalDuration   time.Duration
    16  	datastoreCallCount uint64
    17  	datastoreDuration  time.Duration
    18  }
    19  
    20  // WriteJSON prepares JSON in the format expected by the collector.
    21  func (e *TxnEvent) WriteJSON(buf *bytes.Buffer) {
    22  	w := jsonFieldsWriter{buf: buf}
    23  	buf.WriteByte('[')
    24  	buf.WriteByte('{')
    25  	w.stringField("type", "Transaction")
    26  	w.stringField("name", e.FinalName)
    27  	w.floatField("timestamp", timeToFloatSeconds(e.Start))
    28  	w.floatField("duration", e.Duration.Seconds())
    29  	if ApdexNone != e.Zone {
    30  		w.stringField("nr.apdexPerfZone", e.Zone.label())
    31  	}
    32  	if e.Queuing > 0 {
    33  		w.floatField("queueDuration", e.Queuing.Seconds())
    34  	}
    35  	if e.externalCallCount > 0 {
    36  		w.intField("externalCallCount", int64(e.externalCallCount))
    37  		w.floatField("externalDuration", e.externalDuration.Seconds())
    38  	}
    39  	if e.datastoreCallCount > 0 {
    40  		// Note that "database" is used for the keys here instead of
    41  		// "datastore" for historical reasons.
    42  		w.intField("databaseCallCount", int64(e.datastoreCallCount))
    43  		w.floatField("databaseDuration", e.datastoreDuration.Seconds())
    44  	}
    45  	if e.CrossProcess.Used() {
    46  		if e.CrossProcess.ClientID != "" {
    47  			w.stringField("client_cross_process_id", e.CrossProcess.ClientID)
    48  		}
    49  		if e.CrossProcess.TripID != "" {
    50  			w.stringField("nr.tripId", e.CrossProcess.TripID)
    51  		}
    52  		if e.CrossProcess.PathHash != "" {
    53  			w.stringField("nr.pathHash", e.CrossProcess.PathHash)
    54  		}
    55  		if e.CrossProcess.ReferringPathHash != "" {
    56  			w.stringField("nr.referringPathHash", e.CrossProcess.ReferringPathHash)
    57  		}
    58  		if e.CrossProcess.GUID != "" {
    59  			w.stringField("nr.guid", e.CrossProcess.GUID)
    60  		}
    61  		if e.CrossProcess.ReferringTxnGUID != "" {
    62  			w.stringField("nr.referringTransactionGuid", e.CrossProcess.ReferringTxnGUID)
    63  		}
    64  		if len(e.CrossProcess.AlternatePathHashes) > 0 {
    65  			hashes := make([]string, 0, len(e.CrossProcess.AlternatePathHashes))
    66  			for hash := range e.CrossProcess.AlternatePathHashes {
    67  				hashes = append(hashes, hash)
    68  			}
    69  			sort.Strings(hashes)
    70  			w.stringField("nr.alternatePathHashes", strings.Join(hashes, ","))
    71  		}
    72  		if e.CrossProcess.IsSynthetics() {
    73  			w.stringField("nr.syntheticsResourceId", e.CrossProcess.Synthetics.ResourceID)
    74  			w.stringField("nr.syntheticsJobId", e.CrossProcess.Synthetics.JobID)
    75  			w.stringField("nr.syntheticsMonitorId", e.CrossProcess.Synthetics.MonitorID)
    76  		}
    77  	}
    78  	buf.WriteByte('}')
    79  	buf.WriteByte(',')
    80  	userAttributesJSON(e.Attrs, buf, destTxnEvent, nil)
    81  	buf.WriteByte(',')
    82  	agentAttributesJSON(e.Attrs, buf, destTxnEvent)
    83  	buf.WriteByte(']')
    84  }
    85  
    86  // MarshalJSON is used for testing.
    87  func (e *TxnEvent) MarshalJSON() ([]byte, error) {
    88  	buf := bytes.NewBuffer(make([]byte, 0, 256))
    89  
    90  	e.WriteJSON(buf)
    91  
    92  	return buf.Bytes(), nil
    93  }
    94  
    95  type txnEvents struct {
    96  	events *analyticsEvents
    97  }
    98  
    99  func newTxnEvents(max int) *txnEvents {
   100  	return &txnEvents{
   101  		events: newAnalyticsEvents(max),
   102  	}
   103  }
   104  
   105  func (events *txnEvents) AddTxnEvent(e *TxnEvent) {
   106  	stamp := eventStamp(rand.Float32())
   107  
   108  	// Synthetics events always get priority: normal event stamps are in the
   109  	// range [0.0,1.0), so adding 1 means that a Synthetics event will always
   110  	// win.
   111  	if e.CrossProcess.IsSynthetics() {
   112  		stamp += 1.0
   113  	}
   114  
   115  	events.events.addEvent(analyticsEvent{stamp, e})
   116  }
   117  
   118  func (events *txnEvents) MergeIntoHarvest(h *Harvest) {
   119  	h.TxnEvents.events.mergeFailed(events.events)
   120  }
   121  
   122  func (events *txnEvents) Data(agentRunID string, harvestStart time.Time) ([]byte, error) {
   123  	return events.events.CollectorJSON(agentRunID)
   124  }
   125  
   126  func (events *txnEvents) numSeen() float64  { return events.events.NumSeen() }
   127  func (events *txnEvents) numSaved() float64 { return events.events.NumSaved() }