github.com/newrelic/go-agent@v3.26.0+incompatible/transaction.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  	"net/http"
     8  	"net/url"
     9  )
    10  
    11  // Transaction instruments one logical unit of work: either an inbound web
    12  // request or background task.  Start a new Transaction with the
    13  // Application.StartTransaction() method.
    14  type Transaction interface {
    15  	// The transaction's http.ResponseWriter methods delegate to the
    16  	// http.ResponseWriter provided as a parameter to
    17  	// Application.StartTransaction or Transaction.SetWebResponse.  This
    18  	// allows instrumentation of the response code and response headers.
    19  	// These methods may be called safely if the transaction does not have a
    20  	// http.ResponseWriter.
    21  	http.ResponseWriter
    22  
    23  	// End finishes the Transaction.  After that, subsequent calls to End or
    24  	// other Transaction methods have no effect.  All segments and
    25  	// instrumentation must be completed before End is called.
    26  	End() error
    27  
    28  	// Ignore prevents this transaction's data from being recorded.
    29  	Ignore() error
    30  
    31  	// SetName names the transaction.  Use a limited set of unique names to
    32  	// ensure that Transactions are grouped usefully.
    33  	SetName(name string) error
    34  
    35  	// NoticeError records an error.  The Transaction saves the first five
    36  	// errors.  For more control over the recorded error fields, see the
    37  	// newrelic.Error type.  In certain situations, using this method may
    38  	// result in an error being recorded twice:  Errors are automatically
    39  	// recorded when Transaction.WriteHeader receives a status code above
    40  	// 400 or below 100 that is not in the IgnoreStatusCodes configuration
    41  	// list.  This method is unaffected by the IgnoreStatusCodes
    42  	// configuration list.
    43  	NoticeError(err error) error
    44  
    45  	// AddAttribute adds a key value pair to the transaction event, errors,
    46  	// and traces.
    47  	//
    48  	// The key must contain fewer than than 255 bytes.  The value must be a
    49  	// number, string, or boolean.
    50  	//
    51  	// For more information, see:
    52  	// https://docs.newrelic.com/docs/agents/manage-apm-agents/agent-metrics/collect-custom-attributes
    53  	AddAttribute(key string, value interface{}) error
    54  
    55  	// SetWebRequest marks the transaction as a web transaction.  If
    56  	// WebRequest is non-nil, SetWebRequest will additionally collect
    57  	// details on request attributes, url, and method.  If headers are
    58  	// present, the agent will look for a distributed tracing header.  Use
    59  	// NewWebRequest to transform a *http.Request into a WebRequest.
    60  	SetWebRequest(WebRequest) error
    61  
    62  	// SetWebResponse sets transaction's http.ResponseWriter.  After calling
    63  	// this method, the transaction may be used in place of the
    64  	// ResponseWriter to intercept the response code.  This method is useful
    65  	// when the ResponseWriter is not available at the beginning of the
    66  	// transaction (if so, it can be given as a parameter to
    67  	// Application.StartTransaction).  This method will return a reference
    68  	// to the transaction which implements the combination of
    69  	// http.CloseNotifier, http.Flusher, http.Hijacker, and io.ReaderFrom
    70  	// implemented by the ResponseWriter.
    71  	SetWebResponse(http.ResponseWriter) Transaction
    72  
    73  	// StartSegmentNow starts timing a segment.  The SegmentStartTime
    74  	// returned can be used as the StartTime field in Segment,
    75  	// DatastoreSegment, or ExternalSegment.  We recommend using the
    76  	// StartSegmentNow function instead of this method since it checks if
    77  	// the Transaction is nil.
    78  	StartSegmentNow() SegmentStartTime
    79  
    80  	// CreateDistributedTracePayload creates a payload used to link
    81  	// transactions.  CreateDistributedTracePayload should be called every
    82  	// time an outbound call is made since the payload contains a timestamp.
    83  	//
    84  	// StartExternalSegment calls CreateDistributedTracePayload, so you
    85  	// don't need to use it for outbound HTTP calls: Just use
    86  	// StartExternalSegment!
    87  	//
    88  	// This method never returns nil.  If the application is disabled or not
    89  	// yet connected then this method returns a shim implementation whose
    90  	// methods return empty strings.
    91  	CreateDistributedTracePayload() DistributedTracePayload
    92  
    93  	// AcceptDistributedTracePayload links transactions by accepting a
    94  	// distributed trace payload from another transaction.
    95  	//
    96  	// Application.StartTransaction calls this method automatically if a
    97  	// payload is present in the request headers.  Therefore, this method
    98  	// does not need to be used for typical HTTP transactions.
    99  	//
   100  	// AcceptDistributedTracePayload should be used as early in the
   101  	// transaction as possible.  It may not be called after a call to
   102  	// CreateDistributedTracePayload.
   103  	//
   104  	// The payload parameter may be a DistributedTracePayload, a string, or
   105  	// a []byte.
   106  	AcceptDistributedTracePayload(t TransportType, payload interface{}) error
   107  
   108  	// Application returns the Application which started the transaction.
   109  	Application() Application
   110  
   111  	// BrowserTimingHeader generates the JavaScript required to enable New
   112  	// Relic's Browser product.  This code should be placed into your pages
   113  	// as close to the top of the <head> element as possible, but after any
   114  	// position-sensitive <meta> tags (for example, X-UA-Compatible or
   115  	// charset information).
   116  	//
   117  	// This function freezes the transaction name: any calls to SetName()
   118  	// after BrowserTimingHeader() will be ignored.
   119  	//
   120  	// The *BrowserTimingHeader return value will be nil if browser
   121  	// monitoring is disabled, the application is not connected, or an error
   122  	// occurred.  It is safe to call the pointer's methods if it is nil.
   123  	BrowserTimingHeader() (*BrowserTimingHeader, error)
   124  
   125  	// NewGoroutine allows you to use the Transaction in multiple
   126  	// goroutines.
   127  	//
   128  	// Each goroutine must have its own Transaction reference returned by
   129  	// NewGoroutine.  You must call NewGoroutine to get a new Transaction
   130  	// reference every time you wish to pass the Transaction to another
   131  	// goroutine. It does not matter if you call this before or after the
   132  	// other goroutine has started.
   133  	//
   134  	// All Transaction methods can be used in any Transaction reference.
   135  	// The Transaction will end when End() is called in any goroutine.
   136  	//
   137  	// Example passing a new Transaction reference directly to another
   138  	// goroutine:
   139  	//
   140  	//	go func(txn newrelic.Transaction) {
   141  	//		defer newrelic.StartSegment(txn, "async").End()
   142  	//		time.Sleep(100 * time.Millisecond)
   143  	//	}(txn.NewGoroutine())
   144  	//
   145  	// Example passing a new Transaction reference on a channel to another
   146  	// goroutine:
   147  	//
   148  	//	ch := make(chan newrelic.Transaction)
   149  	//	go func() {
   150  	//		txn := <-ch
   151  	//		defer newrelic.StartSegment(txn, "async").End()
   152  	//		time.Sleep(100 * time.Millisecond)
   153  	//	}()
   154  	//	ch <- txn.NewGoroutine()
   155  	//
   156  	NewGoroutine() Transaction
   157  
   158  	// GetTraceMetadata returns distributed tracing identifiers.  Empty
   159  	// string identifiers are returned if the transaction has finished.
   160  	GetTraceMetadata() TraceMetadata
   161  
   162  	// GetLinkingMetadata returns the fields needed to link data to a trace or
   163  	// entity.
   164  	GetLinkingMetadata() LinkingMetadata
   165  
   166  	// IsSampled indicates if the Transaction is sampled.  A sampled
   167  	// Transaction records a span event for each segment.  Distributed tracing
   168  	// must be enabled for transactions to be sampled.  False is returned if
   169  	// the transaction has finished.
   170  	IsSampled() bool
   171  }
   172  
   173  // DistributedTracePayload traces requests between applications or processes.
   174  // DistributedTracePayloads are automatically added to HTTP requests by
   175  // StartExternalSegment, so you only need to use this if you are tracing through
   176  // a message queue or another non-HTTP communication library.  The
   177  // DistributedTracePayload may be marshalled in one of two formats: HTTPSafe or
   178  // Text.  All New Relic agents can accept payloads in either format.
   179  type DistributedTracePayload interface {
   180  	// HTTPSafe serializes the payload into a string containing http safe
   181  	// characters.
   182  	HTTPSafe() string
   183  	// Text serializes the payload into a string.  The format is slightly
   184  	// more compact than HTTPSafe.
   185  	Text() string
   186  }
   187  
   188  const (
   189  	// DistributedTracePayloadHeader is the header used by New Relic agents
   190  	// for automatic trace payload instrumentation.
   191  	DistributedTracePayloadHeader = "Newrelic"
   192  )
   193  
   194  // TransportType is used in Transaction.AcceptDistributedTracePayload() to
   195  // represent the type of connection that the trace payload was transported over.
   196  type TransportType struct{ name string }
   197  
   198  // TransportType names used across New Relic agents:
   199  var (
   200  	TransportUnknown = TransportType{name: "Unknown"}
   201  	TransportHTTP    = TransportType{name: "HTTP"}
   202  	TransportHTTPS   = TransportType{name: "HTTPS"}
   203  	TransportKafka   = TransportType{name: "Kafka"}
   204  	TransportJMS     = TransportType{name: "JMS"}
   205  	TransportIronMQ  = TransportType{name: "IronMQ"}
   206  	TransportAMQP    = TransportType{name: "AMQP"}
   207  	TransportQueue   = TransportType{name: "Queue"}
   208  	TransportOther   = TransportType{name: "Other"}
   209  )
   210  
   211  // WebRequest may be implemented to provide request information to
   212  // Transaction.SetWebRequest.
   213  type WebRequest interface {
   214  	// Header may return nil if you don't have any headers or don't want to
   215  	// transform them to http.Header format.
   216  	Header() http.Header
   217  	// URL may return nil if you don't have a URL or don't want to transform
   218  	// it to *url.URL.
   219  	URL() *url.URL
   220  	Method() string
   221  	// If a distributed tracing header is found in the headers returned by
   222  	// Header(), this TransportType will be used in the distributed tracing
   223  	// metrics.
   224  	Transport() TransportType
   225  }
   226  
   227  // NewWebRequest turns a *http.Request into a WebRequest for input into
   228  // Transaction.SetWebRequest.
   229  func NewWebRequest(request *http.Request) WebRequest {
   230  	if nil == request {
   231  		return nil
   232  	}
   233  	return requestWrap{request: request}
   234  }
   235  
   236  // NewStaticWebRequest takes the minimum necessary information and creates a static WebRequest out of it
   237  func NewStaticWebRequest(hdrs http.Header, url *url.URL, method string, transport TransportType) WebRequest {
   238  	return staticWebRequest{hdrs, url, method, transport}
   239  }
   240  
   241  // LinkingMetadata is returned by Transaction.GetLinkingMetadata.  It contains
   242  // identifiers needed link data to a trace or entity.
   243  type LinkingMetadata struct {
   244  	// TraceID identifies the entire distributed trace.  This field is empty
   245  	// if distributed tracing is disabled.
   246  	TraceID string
   247  	// SpanID identifies the currently active segment.  This field is empty
   248  	// if distributed tracing is disabled or the transaction is not sampled.
   249  	SpanID string
   250  	// EntityName is the Application name as set on the newrelic.Config.  If
   251  	// multiple application names are specified, only the first is returned.
   252  	EntityName string
   253  	// EntityType is the type of this entity and is always the string
   254  	// "SERVICE".
   255  	EntityType string
   256  	// EntityGUID is the unique identifier for this entity.
   257  	EntityGUID string
   258  	// Hostname is the hostname this entity is running on.
   259  	Hostname string
   260  }
   261  
   262  // TraceMetadata is returned by Transaction.GetTraceMetadata.  It contains
   263  // distributed tracing identifiers.
   264  type TraceMetadata struct {
   265  	// TraceID identifies the entire distributed trace.  This field is empty
   266  	// if distributed tracing is disabled.
   267  	TraceID string
   268  	// SpanID identifies the currently active segment.  This field is empty
   269  	// if distributed tracing is disabled or the transaction is not sampled.
   270  	SpanID string
   271  }