• Log inStart now

Go agent code-level metrics instrumentation

After you've turned on code-level metrics using the steps in Go agent code-level metrics configuration, you can fine-tune how your metrics are collected by using additional instrumentation.

Instrumenting transactions

With code-level metrics enabled, the Go agent will add source code context information to any transaction started by a call to the StartTransaction method of the application. Without changing any of your existing instrumentation code, this means that the source code location will be added based on the function which called StartTransaction. This is true even if StartTransaction is called for you by an instrumentation package.

However, if you wish to exert some manual control over how code-level metrics are collected for a particular transaction, you may add a number of newrelic.TraceOption functions as described below:

Suppressing code-level metrics for a single transaction

If you don't want code-level metrics for a particular transaction (say, to avoid the overhead of collecting the information when you know you won't need that data), add a WithoutCodeLevelMetrics function to the end of the StartTransaction call:

txn := app.StartTransaction("nothing-here-to-see", newrelic.WithoutCodeLevelMetrics())
defer txn.End()

Adjusting the file path prefix

By default, the Go agent will report full (absolute) file pathnames for the source files referenced by code-level metrics. The configuration guide has information about how you can truncate file pathnames globally to start at your choice of directory name via the ConfigCodeLevelMetricsPathPrefixes setting, but you may need to specify a different file pathname prefix for individual transactions. If so, just add a WithPathPrefixes option to the transaction:

txn := app.StartTransaction("mytransaction", newrelic.WithPathPrefixes("otherproject/lib/src"))
defer txn.End()

This means that, for this transaction only, a source file pathname of, for example, "/usr/src/otherproject/lib/src/main.go" is shortened to "otherproject/lib/src/main.go".

If you have multiple path prefixes you wish to use, simply pass them as additional parameters to WithPathPrefixes:

txn := app.StartTransaction("mytransaction", newrelic.WithPathPrefixes("otherproject/lib/src", "myproject/src"))
defer txn.End()

Adjusting the function recognition heuristic's ignore prefix

By default, the Go agent contains logic to skip over functions in the call stack which are internal to the agent itself, in order to arrive at the one you are instrumenting. The configuration guide has information about how you can globally change this heuristic via the ConfigCodeLevelMetricsIgnoredPrefixes setting, but you may wish to provide a custom namespace ignore prefix for a single transaction. You can do so by adding a WithIgnoredPrefixes option to the transaction:

txn := app.StartTransaction("mytransaction", newrelic.WithIgnoredPrefixes("github.com/ignore/this/"))
defer txn.End()

You can specify multiple string arguments to WithIgnoredPrefixes if there are multiple packages whose functions should be ignored:

txn := app.StartTransaction("mytransaction", newrelic.WithIgnoredPrefixes("github.com/ignore/this/", "github.com/ignore/that/"))
defer txn.End()

With this change, any functions from a package whose name begins with github.com/ignore/this/ (or github.com/ignore/that/ in the second example) will be skipped over to find the function being instrumented.

Explicitly mark the code location

To make it easier to identify the point of interest to be reported, add WithThisCodeLocation to the end of the StartTransaction call (for example, if the transaction is actually started inside another framework). This will force the code-level metrics to report the location in the source code where WithThisCodeLocation was invoked.

txn := app.StartTransaction("mytransaction", newrelic.WithThisCodeLocation())
defer txn.End()

Explicitly mark any code location

You can also fully control the location in your source code to be associated with a transaction. In essence you can mark any location by calling ThisCodeLocation to get a "marker" for that location. Then, use that saved marker with the WithCodeLocation option to a StartTransaction call:

here := newrelic.ThisCodeLocation()
.
.
.
txn := app.StartTransaction("mytransaction", newrelic.WithCodeLocation(here))
defer txn.End()

If needed, you can tell ThisCodeLocation to skip a number of calling functions so that the reported location is farther up the call stack. For example, to skip 1 caller, so that here refers to the caller of the function that calls ThisCodeLocation, change the above example to:

here := newrelic.ThisCodeLocation(1)
.
.
.
txn := app.StartTransaction("mytransaction", newrelic.WithCodeLocation(here))
defer txn.End()

Explicitly mark the code location based on a function value

If the code you wish to instrument is available to you as a func type value, such as the name of a function or a function literal value, you can specify that as the location for code-level metrics reporting by adding a WithFunctionLocation option to StartTransaction, passing the func value as a parameter:

func myfunction() { ... }
.
.
.
txn := app.StartTransaction("myfunction", newrelic.WithFunctionLocation(myfunction))

or

someFunction := func() {...}
.
.
.
txn := app.StartTransaction("myfunction", newrelic.WithFunctionLocation(someFunction))

You may also obtain a CodeLocation value from a func value for later use with the WithCodeLocation option. Compare this to the usage shown above to save a code location with ThisCodeLocation for later reference with WithCodeLocation. This time we do the same thing but with a func value instead:

func myFunc() {...}
.
.
.
locationOfMyFunc, err := newrelic.FunctionLocation(myFunc)
if err != nil {
// handle the case that the location could not be determined
// from the value passed to FunctionLocation.
}
.
.
.
txn := app.StartTransaction("mytransaction", newrelic.WithCodeLocation(locationOfMyFunc))

Note that FunctionLocation returns an error if the value passed to it was not a valid function, or the source code location could not be obtained from it. By contrast, the WithFunctionLocation option sets the code-level metrics based on the value passed if possible, but silently does nothing if an error occurs.

Adding custom options to wrapped handlers

The same options described above which may be added to the end of StartTransaction may also be added to WrapHandle and WrapHandleFunc to adjust the code-level metrics collection associated with transactions started by them, if desired (although in most cases the WrapHandle and WrapHandleFunc functions will correctly identify the location of the code being instrumented). For example:

http.HandleFunc(newrelic.WrapHandleFunc(app, "/endpoint", endpointFunction, newrelic.WithThisCodeLocation()))

Changing trace options after a transaction has started

Sometimes you don't have the luxury of knowing the code location until after the transaction has already been started (perhaps by a framework or integration function on your behalf). You may change the transaction options on an existing transaction by calling its SetOption method. The parameters are a set of TraceOption functions just as described above. For example:

txn := newrelic.FromContext(r.context)
txn.SetOption(newrelic.WithThisCodeLocation())

Improving performance with cached code level metrics functions

Often the most convenient way to instrument a transaction with code-level metrics is to call newrelic.WithThisCodeLocation() (or similar function as described above) inside the instrumented function. However, if that function is going to be called many times throughout the runtime of your application, it would be preferable to avoid the overhead of repeatedly computing the source code location. This is especially true if the transaction code will be run concurrently in many goroutines.

To mitigate this, the Go agent provides caching versions of several of these functions. They operate the same as their uncached counterparts do, except that they only do the work of figuring out the source code location the first time they are called, and then simply reuse that value every subsequent time.

In order to make use of this feature, you need to create a cache storage variable by calling NewCachedCodeLocation() and put it where it will persist between executions of the instrumented transaction. This variable will hold the cached value for that code location. Then simply use the methods FunctionLocation(functionValue) or ThisCodeLocation() just as you would use the stand-alone functions of the same names, but in this case they are methods of your CachedCodeLocation variable. These methods are thread-safe, so you may use them in concurrent goroutines without adding any additional concurrency controls to them.

For example, in this code we set up a cache variable which is used in the closure assigned to the myFunc variable.

cache := newrelic.NewCachedCodeLocation()
myFunc := func() {
txn := app.StartTransaction("mytransaction", cache.WithThisCodeLocation())
defer txn.End()
// go on to perform the transaction
}

(This example assumes that app is the agent's Application value created when the agent was started, and is visible to this code snippet.)

Now we can call the myFunc function many times. Each (possibly concurrent) invocation of myFunc has a reference to the cache variable. The first invocation to execute cache.WithThisCodeLocation() will compute the source code location at that point, and store it in the cache variable. Subsequent executions of myFunc will reuse the value previously stored in cache.

Note that you must use a different cache variable for each code location you want to use, since the point is to only evaluate that location once and then use the cached value from there. The cache variable is not intended to be copied or used other than as documented here and in the module package documentation.

Please refer to the full Go module package documentation for more details about all of the cached code level metrics functions and methods you may employ.

Copyright © 2022 New Relic Inc.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.