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
newrelicpackage 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
_integrationsdirectory 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/v1moves tov3/integrations/nrawssdk-v1_integrations/nrawssdk/v2moves tov3/integrations/nrawssdk-v2_integrations/nrgin/v1moves tov3/integrations/nrgin_integrations/nrgorilla/v1moves tov3/integrations/nrgorilla_integrations/nrlogxi/v1moves tov3/integrations/nrlogxi_integrations/nrechomoves tov3/integrations/nrecho-v3and a newv3/integrations/nrecho-v4has 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 ConfigOptions 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) *TransactionIf 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.RoundTripperNewRoundTripper 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
NewApplicationfunction now acceptsConfigOptions a list of which can be found here. If aConfigOptionis 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
ConfigFromEnvironmentto 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.SegmentThresholdConfig.TransactionTracer.Segments.ThresholdConfig.TransactionTracer.StackTraceThresholdConfig.TransactionTracer.Segments.StackTraceThreshold -
If you choose, set the
Config.ErrorCollector.RecordPanicsconfiguration option. This is a new configuration option that controls whether or not a deferredTransaction.Endwill 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.StartTransactionsignature.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
Transactionno longer fulfills thehttp.ResponseWriterinterface. Instead, the writer returned fromTransaction.SetWebResponseshould 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, orConfigDebugLoggerconfiguration 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
ApplicationandTransactionto 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
WebRequesttype, 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
Transactionparameter 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.Headerand 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.Headerrepresenting 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
DistributedTracePayloadstruct 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.Headerobject. You should remove all references toDistributedTracePayloadin your code. -
Change
newrelic.DistributedTracePayloadHeadertonewrelic.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/usersbecomesWebTransaction/Go/GET /users. -
Not required for upgrade, but recommended: update your usages of the now deprecated
StartSegmentandStartSegmentNowto use the methods on the transaction:Transaction.StartSegmentandTransaction.StartSegmentNowrespectively. 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
NewLoggerandNewDebugLogger. Instead use the newConfigOptionsConfigInfoLoggerandConfigDebugLoggerrespectively.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), )