Cross application tracing with Go

The Go agent supports cross application tracing (CAT). Cross application tracing links transactions between APM-monitored app, which is vital for applications implementing a service-oriented or microservices architecture.

Distributed tracing is now available. Distributed tracing is an improvement on cross application tracing and is recommended for large, distributed systems.

Enable cross application tracing with Go

You can enable or disable cross application tracing in the Go agent by using the CrossApplicationTracer.Enabled configuration flag.

Transaction and HTTP request guidelines with Go

Even with cross application tracing enabled, you'll need to make sure your application follows a few simple conventions when responding to HTTP(s) requests, making its own HTTP(s) requests, or creating its own transactions.

Creating transactions via http.ServeMux

If you're using Go's http.ServeMux and want CAT support, you'll need to use the agent's WrapHandle and WrapHandleFunc wrappers. These wrappers automatically start and end transactions with the request and response writer, which will automatically add the correct CAT headers. Your code should look similar to the following:

    http.HandleFunc("/users", usersHandler)
    http.HandleFunc(newrelic.WrapHandleFunc(app, "/users", usersHandler))

You can read more about these wrappers in Instrument Go transactions.

Creating web transactions manually

When creating your own transactions with app.StartTransaction, ensure that you also call Transaction.SetWebRequest and Transaction.SetWebResponse. For example, a transaction started with the following code will have CAT enabled:

txn := app.StartTransaction("transactionName")
// req is a *http.Request, this marks the transaction as a web transaction
txn.SetWebRequestHTTP(req)

// writer is a http.ResponseWriter, use the returned writer in place of the original
writer = txn.SetWebResponse(writer)
writer.WriteHeader(500)

defer txn.End()

However, a transaction started without the calls to Transaction.SetWebRequest and Transaction.SetWebResponse will not have CAT enabled:

// Cross application tracing not enabled
txn := app.StartTransaction("transactionName")
defer txn.End()

Additionally, if you're setting HTTP response codes, use the Go agent's txn.WriteHeader method rather than the standard library's http.ResponseWriter.WriteHeader method.

// old code
// writer.WriteHeader(http.StatusInternalServerError)

// replace with this
txn := app.StartTransaction("transactionName")
txn.SetWebRequest(req)
writer = txn.SetWebResponse(writer)
writer.WriteHeader(http.StatusInternalServerError)
Making HTTP requests

In order to have your outbound HTTP(s) requests eligible for CAT, you'll need to create an external segment.

The easiest way to create an external segment for your outbound HTTP(s) request is to use the newrelic.NewRoundTripper method. For example, this code will make a request to http://api.example.com/ that includes the outgoing CAT headers.

func useNewRoundTripper(txn *newrelic.Transaction) (*http.Response, error) {
    client := &http.Client{}
    client.Transport = newrelic.NewRoundTripper(client.Transport)
    req, _ := http.NewRequest("GET", "http://api.example.com/", nil)
    req = newrelic.RequestWithTransactionContext(req, txn)
    return client.Do(req)
}

If you have a more complex request that uses the Go standard library's http.Request, you'll need to use the newrelic.StartExternalSegment method to ensure your outbound request is eligible for CAT.

func external(txn *newrelic.Transaction, req *http.Request) (*http.Response, error) {
    s := newrelic.StartExternalSegment(txn, req)
    response, err := http.DefaultClient.Do(req)
    s.Response = response
    s.End()
    return response, err
}

While it's also possible to create an ExternalSegment via a struct literal, this segment will not be eligible for CAT. Because of this, New Relic recommends using newrelic.NewRoundTripper or newrelic.ExternalSegment.

func noGoodForCat(txn *newrelic.Transaction, url string) (*http.Response, error) {
    // CAT headers not inserted
    defer newrelic.ExternalSegment{
        StartTime: txn.StartSegmentNow(),
        URL:       url,
    }.End()

    return http.Get(url)
}

Get distributed tracing

New Relic also offers distributed tracing. Distributed tracing is an improvement on cross application tracing and is recommended for large, distributed systems.

For more help

If you need more help, check out these support and learning resources: