github.com/newrelic/go-agent@v3.26.0+incompatible/segments.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  )
     9  
    10  // SegmentStartTime is created by Transaction.StartSegmentNow and marks the
    11  // beginning of a segment.  A segment with a zero-valued SegmentStartTime may
    12  // safely be ended.
    13  type SegmentStartTime struct{ segment }
    14  
    15  // Segment is used to instrument functions, methods, and blocks of code.  The
    16  // easiest way use Segment is the StartSegment function.
    17  type Segment struct {
    18  	StartTime SegmentStartTime
    19  	Name      string
    20  }
    21  
    22  // DatastoreSegment is used to instrument calls to databases and object stores.
    23  type DatastoreSegment struct {
    24  	// StartTime should be assigned using StartSegmentNow before each datastore
    25  	// call is made.
    26  	StartTime SegmentStartTime
    27  
    28  	// Product, Collection, and Operation are highly recommended as they are
    29  	// used for aggregate metrics:
    30  	//
    31  	// Product is the datastore type.  See the constants in
    32  	// https://github.com/newrelic/go-agent/blob/master/datastore.go.  Product
    33  	// is one of the fields primarily responsible for the grouping of Datastore
    34  	// metrics.
    35  	Product DatastoreProduct
    36  	// Collection is the table or group being operated upon in the datastore,
    37  	// e.g. "users_table".  This becomes the db.collection attribute on Span
    38  	// events and Transaction Trace segments.  Collection is one of the fields
    39  	// primarily responsible for the grouping of Datastore metrics.
    40  	Collection string
    41  	// Operation is the relevant action, e.g. "SELECT" or "GET".  Operation is
    42  	// one of the fields primarily responsible for the grouping of Datastore
    43  	// metrics.
    44  	Operation string
    45  
    46  	// The following fields are used for extra metrics and added to instance
    47  	// data:
    48  	//
    49  	// ParameterizedQuery may be set to the query being performed.  It must
    50  	// not contain any raw parameters, only placeholders.
    51  	ParameterizedQuery string
    52  	// QueryParameters may be used to provide query parameters.  Care should
    53  	// be taken to only provide parameters which are not sensitive.
    54  	// QueryParameters are ignored in high security mode. The keys must contain
    55  	// fewer than than 255 bytes.  The values must be numbers, strings, or
    56  	// booleans.
    57  	QueryParameters map[string]interface{}
    58  	// Host is the name of the server hosting the datastore.
    59  	Host string
    60  	// PortPathOrID can represent either the port, path, or id of the
    61  	// datastore being connected to.
    62  	PortPathOrID string
    63  	// DatabaseName is name of database instance where the current query is
    64  	// being executed.  This becomes the db.instance attribute on Span events
    65  	// and Transaction Trace segments.
    66  	DatabaseName string
    67  }
    68  
    69  // ExternalSegment instruments external calls.  StartExternalSegment is the
    70  // recommended way to create ExternalSegments.
    71  type ExternalSegment struct {
    72  	StartTime SegmentStartTime
    73  	Request   *http.Request
    74  	Response  *http.Response
    75  
    76  	// URL is an optional field which can be populated in lieu of Request if
    77  	// you don't have an http.Request.  Either URL or Request must be
    78  	// populated.  If both are populated then Request information takes
    79  	// priority.  URL is parsed using url.Parse so it must include the
    80  	// protocol scheme (eg. "http://").
    81  	URL string
    82  	// Host is an optional field that is automatically populated from the
    83  	// Request or URL.  It is used for external metrics, transaction trace
    84  	// segment names, and span event names.  Use this field to override the
    85  	// host in the URL or Request.  This field does not override the host in
    86  	// the "http.url" attribute.
    87  	Host string
    88  	// Procedure is an optional field that can be set to the remote
    89  	// procedure being called.  If set, this value will be used in metrics,
    90  	// transaction trace segment names, and span event names.  If unset, the
    91  	// request's http method is used.
    92  	Procedure string
    93  	// Library is an optional field that defaults to "http".  It is used for
    94  	// external metrics and the "component" span attribute.  It should be
    95  	// the framework making the external call.
    96  	Library string
    97  }
    98  
    99  // MessageProducerSegment instruments calls to add messages to a queueing system.
   100  type MessageProducerSegment struct {
   101  	StartTime SegmentStartTime
   102  
   103  	// Library is the name of the library instrumented.  eg. "RabbitMQ",
   104  	// "JMS"
   105  	Library string
   106  
   107  	// DestinationType is the destination type.
   108  	DestinationType MessageDestinationType
   109  
   110  	// DestinationName is the name of your queue or topic.  eg. "UsersQueue".
   111  	DestinationName string
   112  
   113  	// DestinationTemporary must be set to true if destination is temporary
   114  	// to improve metric grouping.
   115  	DestinationTemporary bool
   116  }
   117  
   118  // MessageDestinationType is used for the MessageSegment.DestinationType field.
   119  type MessageDestinationType string
   120  
   121  // These message destination type constants are used in for the
   122  // MessageSegment.DestinationType field.
   123  const (
   124  	MessageQueue    MessageDestinationType = "Queue"
   125  	MessageTopic    MessageDestinationType = "Topic"
   126  	MessageExchange MessageDestinationType = "Exchange"
   127  )
   128  
   129  // End finishes the segment.
   130  func (s *Segment) End() error { return endSegment(s) }
   131  
   132  // End finishes the datastore segment.
   133  func (s *DatastoreSegment) End() error { return endDatastore(s) }
   134  
   135  // End finishes the external segment.
   136  func (s *ExternalSegment) End() error { return endExternal(s) }
   137  
   138  // End finishes the message segment.
   139  func (s *MessageProducerSegment) End() error { return endMessage(s) }
   140  
   141  // OutboundHeaders returns the headers that should be attached to the external
   142  // request.
   143  func (s *ExternalSegment) OutboundHeaders() http.Header {
   144  	return outboundHeaders(s)
   145  }
   146  
   147  // StartSegmentNow starts timing a segment.  This function is recommended over
   148  // Transaction.StartSegmentNow() because it is nil safe.
   149  func StartSegmentNow(txn Transaction) SegmentStartTime {
   150  	if nil != txn {
   151  		return txn.StartSegmentNow()
   152  	}
   153  	return SegmentStartTime{}
   154  }
   155  
   156  // StartSegment makes it easy to instrument segments.  To time a function, do
   157  // the following:
   158  //
   159  //	func timeMe(txn newrelic.Transaction) {
   160  //		defer newrelic.StartSegment(txn, "timeMe").End()
   161  //		// ... function code here ...
   162  //	}
   163  //
   164  // To time a block of code, do the following:
   165  //
   166  //	segment := StartSegment(txn, "myBlock")
   167  //	// ... code you want to time here ...
   168  //	segment.End()
   169  //
   170  func StartSegment(txn Transaction, name string) *Segment {
   171  	return &Segment{
   172  		StartTime: StartSegmentNow(txn),
   173  		Name:      name,
   174  	}
   175  }
   176  
   177  // StartExternalSegment starts the instrumentation of an external call and adds
   178  // distributed tracing headers to the request.  If the Transaction parameter is
   179  // nil then StartExternalSegment will look for a Transaction in the request's
   180  // context using FromContext.
   181  //
   182  // Using the same http.Client for all of your external requests?  Check out
   183  // NewRoundTripper: You may not need to use StartExternalSegment at all!
   184  //
   185  func StartExternalSegment(txn Transaction, request *http.Request) *ExternalSegment {
   186  	if nil == txn {
   187  		txn = transactionFromRequestContext(request)
   188  	}
   189  	s := &ExternalSegment{
   190  		StartTime: StartSegmentNow(txn),
   191  		Request:   request,
   192  	}
   193  
   194  	if request != nil && request.Header != nil {
   195  		for key, values := range s.OutboundHeaders() {
   196  			for _, value := range values {
   197  				request.Header.Add(key, value)
   198  			}
   199  		}
   200  	}
   201  
   202  	return s
   203  }