github.com/newrelic/go-agent@v3.26.0+incompatible/browser_header.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  )
     9  
    10  var (
    11  	browserStartTag   = []byte(`<script type="text/javascript">`)
    12  	browserEndTag     = []byte(`</script>`)
    13  	browserInfoPrefix = []byte(`window.NREUM||(NREUM={});NREUM.info=`)
    14  )
    15  
    16  // browserInfo contains the fields that are marshalled into the Browser agent's
    17  // info hash.
    18  //
    19  // https://newrelic.atlassian.net/wiki/spaces/eng/pages/50299103/BAM+Agent+Auto-Instrumentation
    20  type browserInfo struct {
    21  	Beacon                string `json:"beacon"`
    22  	LicenseKey            string `json:"licenseKey"`
    23  	ApplicationID         string `json:"applicationID"`
    24  	TransactionName       string `json:"transactionName"`
    25  	QueueTimeMillis       int64  `json:"queueTime"`
    26  	ApplicationTimeMillis int64  `json:"applicationTime"`
    27  	ObfuscatedAttributes  string `json:"atts"`
    28  	ErrorBeacon           string `json:"errorBeacon"`
    29  	Agent                 string `json:"agent"`
    30  }
    31  
    32  // BrowserTimingHeader encapsulates the JavaScript required to enable New
    33  // Relic's Browser product.
    34  type BrowserTimingHeader struct {
    35  	agentLoader string
    36  	info        browserInfo
    37  }
    38  
    39  func appendSlices(slices ...[]byte) []byte {
    40  	length := 0
    41  	for _, s := range slices {
    42  		length += len(s)
    43  	}
    44  	combined := make([]byte, 0, length)
    45  	for _, s := range slices {
    46  		combined = append(combined, s...)
    47  	}
    48  	return combined
    49  }
    50  
    51  // WithTags returns the browser timing JavaScript which includes the enclosing
    52  // <script> and </script> tags.  This method returns nil if the receiver is
    53  // nil, the feature is disabled, the application is not yet connected, or an
    54  // error occurs.  The byte slice returned is in UTF-8 format.
    55  func (h *BrowserTimingHeader) WithTags() []byte {
    56  	withoutTags := h.WithoutTags()
    57  	if nil == withoutTags {
    58  		return nil
    59  	}
    60  	return appendSlices(browserStartTag, withoutTags, browserEndTag)
    61  }
    62  
    63  // WithoutTags returns the browser timing JavaScript without any enclosing tags,
    64  // which may then be embedded within any JavaScript code.  This method returns
    65  // nil if the receiver is nil, the feature is disabled, the application is not
    66  // yet connected, or an error occurs.  The byte slice returned is in UTF-8
    67  // format.
    68  func (h *BrowserTimingHeader) WithoutTags() []byte {
    69  	if nil == h {
    70  		return nil
    71  	}
    72  
    73  	// We could memoise this, but it seems unnecessary, since most users are
    74  	// going to call this zero or one times.
    75  	info, err := json.Marshal(h.info)
    76  	if err != nil {
    77  		// There's no way to log from here, but this also should be unreachable in
    78  		// practice.
    79  		return nil
    80  	}
    81  
    82  	return appendSlices([]byte(h.agentLoader), browserInfoPrefix, info)
    83  }