This guide is intended to help with upgrading from version 2.x ("github.com/newrelic/go-agent"
) to version 3.x ("github.com/newrelic/go-agent/v3/newrelic"
). This information can also be found on
our documentation website.
The minimum required Go version to run the New Relic Go Agent is now 1.7.
The agent has been placed in a new /v3
directory, leaving the top level directory with the now deprecated v2 agent. More specifically:
- The
newrelic
package has moved from"github.com/newrelic/go-agent"
to"github.com/newrelic/go-agent/v3/newrelic"
. This makes named imports unnecessary. - The underscore in the
_integrations
directory is removed. Thus the"github.com/newrelic/go-agent/_integrations/nrlogrus"
import path becomes"github.com/newrelic/go-agent/v3/integrations/nrlogrus"
. Some of the integration packages have had other changes as well:_integrations/nrawssdk/v1
moves tov3/integrations/nrawssdk-v1
_integrations/nrawssdk/v2
moves tov3/integrations/nrawssdk-v2
_integrations/nrgin/v1
moves tov3/integrations/nrgin
_integrations/nrgorilla/v1
moves tov3/integrations/nrgorilla
_integrations/nrlogxi/v1
moves tov3/integrations/nrlogxi
_integrations/nrecho
moves tov3/integrations/nrecho-v3
and a newv3/integrations/nrecho-v4
has been added to support Echo version 4.
Transaction names created by WrapHandle
,
WrapHandleFunc
,
nrecho-v3,
nrecho-v4,
nrgorilla, and
nrgin now
include the HTTP method. For example, the following code:
http.HandleFunc(newrelic.WrapHandleFunc(app, "/users", usersHandler))
now creates a metric called WebTransaction/Go/GET /users
instead of
WebTransaction/Go/users
.
As a result of this change, you may need to update your alerts and dashboards.
We have added go module support. The top level "github.com/newrelic/go-agent/v3/newrelic"
package now has a go.mod
file. Separate go.mod
files are also included with each integration in the integrations directory.
NewConfig
was removed and the NewApplication
signature has changed to:
func NewApplication(opts ...ConfigOption) (*Application, error)
New ConfigOption
functions are provided to modify the Config
. Here's what your Application creation will look like:
app, err := newrelic.NewApplication(
newrelic.ConfigAppName("My Application"),
newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")),
)
A complete list of ConfigOption
s can be found in the Go Docs.
The location of two Config
fields have been moved. The Config.TransactionTracer.SegmentThreshold
field has moved to Config.TransactionTracer.Segments.Threshold
and the Config.TransactionTracer.StackTraceThreshold
field has moved to to Config.TransactionTracer.Segments.StackTraceThreshold
.
The following method signatures have changed to no longer return an error; instead the error is logged to the agent logs.
func (txn *Transaction) End() {...}
func (txn *Transaction) Ignore() {...}
func (txn *Transaction) SetName(name string) {...}
func (txn *Transaction) NoticeError(err error) {...}
func (txn *Transaction) AddAttribute(key string, value interface{}) {...}
func (txn *Transaction) SetWebRequestHTTP(r *http.Request) {...}
func (txn *Transaction) SetWebRequest(r *WebRequest) {...}
func (txn *Transaction) AcceptDistributedTracePayload(t TransportType, payload interface{}) {...}
func (txn *Transaction) BrowserTimingHeader() *BrowserTimingHeader {...}
func (s *Segment) End() {...}
func (s *DatastoreSegment) End() {...}
func (s *ExternalSegment) End() {...}
func (s *MessageProducerSegment) End() {...}
func (app *Application) RecordCustomEvent(eventType string, params map[string]interface{}) {...}
func (app *Application) RecordCustomMetric(name string, value float64) {...}
The signature of Application.StartTransaction
has changed to no longer take a http.ResponseWriter
or *http.Request
. The new signature just takes a string for
the transaction name:
func (app *Application) StartTransaction(name string) *Transaction
If you previously had code that used all three parameters, such as:
var writer http.ResponseWriter
var req *http.Request
txn := h.App.StartTransaction("server-txn", writer, req)
After the upgrade, it should look like this:
var writer http.ResponseWriter
var req *http.Request
txn := h.App.StartTransaction("server-txn")
writer = txn.SetWebResponse(writer)
txn.SetWebRequestHTTP(req)
Application
and Transaction
have changed from interfaces to structs. All methods on these types have pointer receivers. Methods on these types are now nil-safe. References to these types in your code will need a pointer added. See the checklist for examples.
Two attributes have been renamed. The old names will still be reported, but are deprecated and will be removed entirely in a future release.
Old (deprecated) attribute | New attribute |
---|---|
httpResponseCode |
http.statusCode |
request.headers.User-Agent |
request.headers.userAgent |
Since in v3.0 both the deprecated and the new attribute are being reported, if you have configured your application to ignore one or both of these attributes, such as with Config.Attributes.Exclude
, you will now need to specify both the deprecated and the new attribute name in your configuration.
This version introduces a new configuration option, Config.ErrorCollector.RecordPanics
. This configuration controls whether or not a deferred Transaction.End
will attempt to recover panics, record them as errors, and then re-panic them. By default, this is set to false
. Previous versions of the agent always recovered panics, i.e. a default of true
.
Along with the new format for configuring an application, there is now an option to populate the configuration from environment variables. The full list of environment variables that are considered are included in the Go Docs. The new configuration function is used as follows:
app, err := newrelic.NewApplication(newrelic.ConfigFromEnvironment())
As mentioned above, the Application.StartTransaction
no longer takes a http.ResponseWriter
or http.Request
; instead, after you start the transaction, you can set the ResponseWriter
by calling Transaction.SetWebResponse
:
txn := h.App.StartTransaction("server-txn")
writer = txn.SetWebResponse(writer)
The Transaction.SetWebResponse
method now returns a replacement http.ResponseWriter
that implements the combination of http.CloseNotifier
, http.Flusher
, http.Hijacker
, and io.ReaderFrom
implemented by the input http.ResponseWriter
.
WebRequest
has changed from an interface to a struct, which can be created via code like this:
webReq := newrelic.WebRequest{
Header: hdrs,
URL: url,
Method: method,
Transport: newrelic.TransportHTTP,
}
The Transaction.SetWebRequest
method takes one of these structs.
In addition to the Transaction.SetWebRequest
method discussed in the section above, we have added a method Transaction.SetWebRequestHTTP
that takes an *http.Request
and sets the appropriate fields.
As described in the earlier section, this can be used in your code as part of the signature change of Application.StartTransaction
:
var writer http.ResponseWriter
var req *http.Request
txn := h.App.StartTransaction("server-txn")
writer = txn.SetWebResponse(writer)
txn.SetWebRequestHTTP(req)
The transaction parameter to NewRoundTripper
has been removed. The function signature is now:
func NewRoundTripper(t http.RoundTripper) http.RoundTripper
NewRoundTripper
will look for a transaction in the request's context using FromContext
.
When manually creating or accepting Distributed Tracing payloads, the method signatures have changed.
This Transaction.InsertDistributedTraceHeaders
method will insert the Distributed Tracing headers into the http.Header
object passed as a parameter:
func (txn *Transaction) InsertDistributedTraceHeaders(hdrs http.Header)
This Transaction.AcceptDistributedTraceHeaders
method takes a TransportType
and an http.Header
object that contains Distributed Tracing header(s) and links this transaction to other transactions specified in the headers:
func (txn *Transaction) AcceptDistributedTraceHeaders(t TransportType, hdrs http.Header)
Additionally, the DistributedTracePayload
struct is no longer needed and has been removed from the agent's API. Instead, distributed tracing information is passed around as key/value pairs in the http.Header
object.
The functions StartSegmentNow
and StartSegment
have been marked as deprecated. The preferred new method of starting a segment have moved to Transaction.StartSegmentNow
and Transaction.StartSegment
respectively.
// DEPRECATED:
startTime := newrelic.StartSegmentNow(txn)
// and
sgmt := newrelic.StartSegment(txn, "segment1")
// NEW, PREFERRED WAY:
startTime := txn.StartSegmentNow()
// and
sgmt := txn.StartSegment("segment1")
Additionally the functions NewLogger
and NewDebugLogger
have been marked as deprecated. The preferred new method of configuring agent logging is using the ConfigInfoLogger
and ConfigDebugLogger
ConfigOptions
respectively.
// DEPRECATED:
app, err := newrelic.NewApplication(
...
func(cfg *newrelic.Config) {
cfg.Logger = newrelic.NewLogger(os.Stdout)
}
)
// or
app, err := newrelic.NewApplication(
...
func(cfg *newrelic.Config) {
cfg.Logger = newrelic.NewDebugLogger(os.Stdout)
}
)
// NEW, PREFERRED WAY:
app, err := newrelic.NewApplication(
...
newrelic.ConfigInfoLogger(os.Stdout),
)
// or
app, err := newrelic.NewApplication(
...
newrelic.ConfigDebugLogger(os.Stdout),
)
The interfaces ErrorAttributer
, ErrorClasser
, StackTracer
are no longer exported. Thus, if you have any code checking to ensure that your custom type fulfills these interfaces, that code will no longer work. Example:
// This will no longer compile.
type MyErrorType struct{}
var _ newrelic.ErrorAttributer = MyErrorType{}
DistributedTracePayloadHeader
has been changed to DistributedTraceNewRelicHeader
.
TransportType
type is changed from a struct to a string.
-
Ensure your Go version is at least 1.7 (older versions are no longer supported).
-
Update imports. The v3.x agent now lives at "github.com/newrelic/go-agent/v3/newrelic" and no longer requires a named import.
From:
import newrelic "github.com/newrelic/go-agent"
To:
import "github.com/newrelic/go-agent/v3/newrelic"
Additionally, if you are using any integrations, they too have moved. Each has its own version which matches the version of the 3rd party package it supports.
From:
import "github.com/newrelic/go-agent/_integrations/nrlogrus"
To:
import "github.com/newrelic/go-agent/v3/integrations/nrlogrus"
-
Update how you configure your application. The
NewApplication
function now acceptsConfigOption
s a list of which can be found here. If aConfigOption
is not available for your setting, create one yourself!From:
cfg := newrelic.NewConfig("appName", "__license__") cfg.CrossApplicationTracer.Enabled = false cfg.CustomInsightsEvents.Enabled = false cfg.ErrorCollector.IgnoreStatusCodes = []int{404, 418} cfg.DatastoreTracer.SlowQuery.Threshold = 3 cfg.DistributedTracer.Enabled = true cfg.TransactionTracer.Threshold.Duration = 2 cfg.TransactionTracer.Threshold.IsApdexFailing = false app, err := newrelic.NewApplication(cfg)
To:
app, err := newrelic.NewApplication( newrelic.ConfigAppName("appName"), newrelic.ConfigLicense("__license__"), func(cfg *newrelic.Config) { cfg.CrossApplicationTracer.Enabled = false cfg.CustomInsightsEvents.Enabled = false cfg.ErrorCollector.IgnoreStatusCodes = []int{404, 418} cfg.DatastoreTracer.SlowQuery.Threshold = 3 cfg.DistributedTracer.Enabled = true cfg.TransactionTracer.Threshold.Duration = 2 cfg.TransactionTracer.Threshold.IsApdexFailing = false }, )
You can use
ConfigFromEnvironment
to provide configuration from environment variables:app, err := newrelic.NewApplication(newrelic.ConfigFromEnvironment())
-
Update the Transaction Tracer configuration. Change the fields for the two changed configuration options.
Old Config Field New Config Field Config.TransactionTracer.SegmentThreshold
Config.TransactionTracer.Segments.Threshold
Config.TransactionTracer.StackTraceThreshold
Config.TransactionTracer.Segments.StackTraceThreshold
-
If you choose, set the
Config.ErrorCollector.RecordPanics
configuration option. This is a new configuration option that controls whether or not a deferredTransaction.End
will attempt to recover panics, record them as errors, and then re-panic them. Previously, the agent acted as though this option was set totrue
; with the new configuration it defaults tofalse
. If you wish to maintain the old agent behavior with regards to panics, be sure to set this totrue
. -
Update code to use the new
Application.StartTransaction
signature.From:
txn := app.StartTransaction("name", nil, nil) // or // writer is an http.ResponseWriter // req is an *http.Request txn := app.StartTransaction("name", writer, req) txn.WriteHeader(500)
To, respectively:
txn := app.StartTransaction("name") // or // writer is an http.ResponseWriter // req is an *http.Request txn:= app.StartTransaction("name") writer = txn.SetWebResponse(writer) txn.SetWebRequestHTTP(req) writer.WriteHeader(500)
Notice too that the
Transaction
no longer fulfills thehttp.ResponseWriter
interface. Instead, the writer returned fromTransaction.SetWebResponse
should be used. -
Update code to no longer expect an error returned from these updated methods. Instead, check the agent logs for errors by using one of the
ConfigLogger
,ConfigInfoLogger
, orConfigDebugLogger
configuration options.func (txn *Transaction) End() {...} func (txn *Transaction) Ignore() {...} func (txn *Transaction) SetName(name string) {...} func (txn *Transaction) NoticeError(err error) {...} func (txn *Transaction) AddAttribute(key string, value interface{}) {...} func (txn *Transaction) SetWebRequestHTTP(r *http.Request) {...} func (txn *Transaction) SetWebRequest(r *WebRequest) {...} func (txn *Transaction) AcceptDistributedTracePayload(t TransportType, payload interface{}) {...} func (txn *Transaction) BrowserTimingHeader() *BrowserTimingHeader {...} func (s *Segment) End() {...} func (s *DatastoreSegment) End() {...} func (s *ExternalSegment) End() {...} func (s *MessageProducerSegment) End() {...} func (app *Application) RecordCustomEvent(eventType string, params map[string]interface{}) {...} func (app *Application) RecordCustomMetric(name string, value float64) {...}
-
Update uses of
Application
andTransaction
to be pointers, instead of direct references.From:
func doSomething(txn newrelic.Transaction) {...} func instrumentSomething(app newrelic.Application, h http.Handler, name string) {...}
To:
func doSomething(txn *newrelic.Transaction) {...} func instrumentSomething(app *newrelic.Application, h http.Handler, name string) {...}
-
If you are using a
WebRequest
type, it has changed from an interface to a struct. You can use it as follows:wr := newrelic.WebRequest{ Header: r.Header, URL: r.URL, Method: r.Method, Transport: newrelic.TransportHTTP, } txn.SetWebRequest(wr)
-
Remove the
Transaction
parameter from theNewRoundTripper
, and instead ensure that the transaction is available via the request's context, usingRequestWithTransactionContext
.From:
client := &http.Client{} client.Transport = newrelic.NewRoundTripper(txn, client.Transport) req, _ := http.NewRequest("GET", "https://example.com", nil) client.Do(req)
To:
client := &http.Client{} client.Transport = newrelic.NewRoundTripper(client.Transport) req, _ := http.NewRequest("GET", "http://example.com", nil) req = newrelic.RequestWithTransactionContext(req, txn) client.Do(req)
-
Update any usage of Distributed Tracing accept/create functions. The method for creating a distributed trace payload has changed to
Transaction.InsertDistributedTraceHeaders
. Instead of returning a payload, it now accepts anhttp.Header
and inserts the header(s) directly into it.From:
hdrs := http.Header{} payload := txn.CreateDistributedTracePayload() hdrs.Set(newrelic.DistributedTracePayloadHeader, payload.Text())
To:
hdrs := http.Header{} txn.InsertDistributedTraceHeaders(hdrs)
Similarly, the method for accepting distributed trace payloads has changed to
Transaction.AcceptDistributedTraceHeaders
. Instead of taking an interface representing the payload value, it now accepts anhttp.Header
representing both the keys and values.From:
hdrs := request.Headers() payload := hdrs.Get(newrelic.DistributedTracePayloadHeader) txn.AcceptDistributedTracePayload(newrelic.TransportKafka, payload)
To:
hdrs := request.Headers() txn.AcceptDistributedTraceHeaders(newrelic.TransportKafka, hdrs)
Additionally, the
DistributedTracePayload
struct is no longer needed and has been removed from the agent's API. Instead, distributed tracing information is passed around as key/value pairs in thehttp.Header
object. You should remove all references toDistributedTracePayload
in your code. -
Change
newrelic.DistributedTracePayloadHeader
tonewrelic.DistributedTraceNewRelicHeader
. -
If you have configured your application to ignore either attribute described here, you will now need to specify both the deprecated and the new attribute name in your configuration.
Configuration options where these codes might be used:
Config.TransactionEvents.Attributes Config.ErrorCollector.Attributes Config.TransactionTracer.Attributes Config.TransactionTrace.Segments.Attributes Config.BrowserMonitoring.Attributes Config.SpanEvents.Attributes Config.Attributes
From old configuration example:
config.ErrorCollector.Attributes.Exclude = []string{ "httpResponseCode", "request.headers.User-Agent", } // or config.ErrorCollector.Attributes.Exclude = []string{ newrelic.AttributeResponseCode, newrelic.AttributeRequestUserAgent, }
To:
config.ErrorCollector.Attributes.Exclude = []string{ "http.statusCode", "httpResponseCode", "request.headers.userAgent", "request.headers.User-Agent", } // or config.ErrorCollector.Attributes.Exclude = []string{ newrelic.AttributeResponseCode, newrelic.AttributeResponseCodeDeprecated, newrelic.AttributeRequestUserAgent, newrelic.AttributeRequestUserAgentDeprecated, }
-
Update alerts and dashboards with new transaction names:
Transaction names created by
WrapHandle
,WrapHandleFunc
, nrecho-v3, nrecho-v4, nrgorilla, and nrgin now include the HTTP method. Thus the transaction nameWebTransaction/Go/users
becomesWebTransaction/Go/GET /users
. -
Not required for upgrade, but recommended: update your usages of the now deprecated
StartSegment
andStartSegmentNow
to use the methods on the transaction:Transaction.StartSegment
andTransaction.StartSegmentNow
respectively. This step is optional but highly recommended.From:
startTime := newrelic.StartSegmentNow(txn) // and sgmt := newrelic.StartSegment(txn, "segment1")
To:
startTime := txn.StartSegmentNow() // and sgmt := txn.StartSegment("segment1")
-
Not required for upgrade, but recommended: update your usages of the now deprecated
NewLogger
andNewDebugLogger
. Instead use the newConfigOption
sConfigInfoLogger
andConfigDebugLogger
respectively.From:
app, err := newrelic.NewApplication( ... func(cfg *newrelic.Config) { cfg.Logger = newrelic.NewLogger(os.Stdout) } ) // or app, err := newrelic.NewApplication( ... func(cfg *newrelic.Config) { cfg.Logger = newrelic.NewDebugLogger(os.Stdout) } )
To:
app, err := newrelic.NewApplication( ... newrelic.ConfigInfoLogger(os.Stdout), ) // or app, err := newrelic.NewApplication( ... newrelic.ConfigDebugLogger(os.Stdout), )