Are you building modern apps in Golang? This multi-part blog series will help you instrument Golang faster by providing tips, guidance, and best practices with New Relic.

Typically, the content in this blog would be presented in a workshop, but we've made it a blog series so anyone can follow along "hands on," using sample code, Instruqt, this blog, and videos. For the best experience:

The following two videos will give an overview of Instruqt and some instructions about how to use it if you haven't done that before.

Overview of Instruqt

Instruqt UI navigation

Now that you're set up, let's learn a little more about the instrumentation of Golang.

Tip 9: Tracing is your Friend

The Go programming language is increasingly popular for building distributed systems and microservices. However, this approach presents some challenges, as a single problematic service along the path can negatively impact the overall response time for that request, potentially driving customers away or creating issues.

Hence, tracing has emerged as a valuable technique to address this issue. It collects data as requests travel from one service to another by recording each segment of the journey as a span. These spans contain essential details about each request segment, such as the time it took to process and the HTTP status code returned. Eventually, these spans are combined into one trace, which gives you a complete picture of the entire request. Distributed Tracing is a specialized form designed to work in complex architectures using monoliths and microservices. It benefits software teams working in modern environments, where complex architectures are becoming increasingly common.

Distributed Tracing in New Relic is designed to give these software teams an easy way to capture, visualize, and analyze traces, enabling them to understand better how their applications are performing and quickly identify any issues that might arise. Distributed Tracing is enabled by default in Go agent versions 3.16.0 and higher. This means that the agent will automatically add distributed tracing headers to outbound requests and scan incoming requests for distributed tracing headers.

In some cases, auto-instrumentation may not be available for distributed tracing, but manual instrumentation is still possible. One simple way to create an external segment for an outbound HTTP request is to use the newrelic.NewRoundTripper method.

Here is an example of making a request to http://api.example.com that includes the distributed tracing headers as described in the documentation.

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

This is an example where we can see Distributed Tracing with gRPC in Golang.

Using gRPC for Distributed Tracing in New Relic

External Segments in New Relic

External Segments in New Relic

Infinite Tracing is a great option to consider if you have a large amount of data to analyze and want to find the most relevant traces. With this feature, you can send your trace data to our cloud-based service and rely on Infinite Tracing to make sampling decisions. This ensures you're keeping the trace data you need to see.

Unlike our standard distributed tracing options, Infinite Tracing can process more trace data. It uses tail-based sampling, a superior method of sampling data after it's collected. Unlike head-based sampling, which our standard tracing feature uses, tail-based sampling reduces the chance of missing important data.

Infinite Tracing is highly configurable, and you can set it up in various ways to ensure that it keeps the trace data you need to see. This way, you can be sure you're getting the most relevant traces for analysis. So, if you're dealing with data and want to ensure you get all vital information, Infinite Tracing is an excellent solution. You explore further with the introduction and setting up Infinite Tracing for more details.

Tip 10: Tracking Performance Changes

Deploying an application can be risky, especially when the app breaks or doesn't perform as expected. Many factors can contribute to a bad deployment, including code changes, environment configuration, and infrastructure issues. This is why it's crucial to have visibility into your app's performance before, during, and after deployment. New Relic Change Tracking allow you to track deployments and correlate them with changes in your app's performance. Using these markers, you can monitor how your app responds to changes made during deployment and detect any issues that may arise in Golang.

Markers are created automatically by New Relic whenever you deploy a new app version. These markers appear in APM charts and dashboards, allowing you to quickly see the impact of deployments on your app's performance. You can also view change tracking in context with your app's logs, metrics, and traces, making it easier to understand the root cause of any issues.

By using markers in New Relic, you can reduce the risk of bad deployments and ensure that your app always performs at its best. With better visibility into your app's performance, you can make more informed decisions about when and how to deploy new changes. To document a change, such as a deployment, it is necessary to include the mandatory fields, and then decide whether to include any optional fields related to Golang. The following GraphQL mutations provide examples of different ways to create deployment markers.

mutation {
  changeTrackingCreateDeployment(
    deployment: { version: "0.0.1", entityGuid: "INSERT_YOUR_GUID" }
  ) {
    deploymentId
    entityGuid
  }
}

Once you've completed the steps for recording a deployment, you are ready to see its performance in the UI. For more information, you can explore track changes using NerdGraph (GraphQL).

Change Tracking in New Relic

Change Tracking in New Relic

Bonus Tip 11: Logs in Context

Software developers rely on log data to troubleshoot issues, making it an invaluable source of information. However, in today's world, developers face a new challenge of dealing with an overwhelming amount of log data from various sources. As the volume of log data increases, it becomes more challenging to collect, manage, and analyze this data.

New Relic logs in context is designed to address this challenge. It correlates your log data with data collected from our APM and infrastructure agents and services instrumented with OpenTelemetry. This means you can see log data or links to log data in other UI experiences, such as APM, Distributed Tracing, and Errors Inbox.

With the Go language APM agent, you can also get logs in context and see your app logs in the context of your other New Relic data. There are two ways to get logs into New Relic.

Option 1: using native APM agent to collect logs

For the majority of users, the best solution for log forwarding is the automatic feature built into the Go agent. It is simple to set up and works well for most use cases. This feature is now turned on by default in versions v3.20.0+ of the Go agent, but it is still possible to modify its configuration to enable application log forwarding.

app, err := newrelic.NewApplication(
  newrelic.ConfigAppLogForwardingEnabled(true),
)

After configuring your agent to send logs to New Relic, you can instrument your logging library using the Logs in Context plugin. This can be done by using supported plugins such as Zerolog, Logrus, or the Standard Library Log from the Golang ecosystem.

	// Create a logWriter, then pass it to the log.Logger
	writer := logWriter.New(os.Stdout, app)
	logger := log.New(&writer, "Background:  ", log.Default().Flags())

	logger.Print("Hello world!")

	txnName := "logsInContext Sample Transaction"
	txn := app.StartTransaction("logsInContext")

	// Always create a new log object in order to avoid changing the context of the logger for
	// other threads that may be logging outside of this transaction
	txnLogger := log.New(writer.WithTransaction(txn), "Transaction: ", log.Default().Flags())
	txnLogger.Printf("In transaction %s.", txnName)

	// Random sleep to simulate delays
	randomDelay := rand.Intn(300)
	time.Sleep(time.Duration(randomDelay) * time.Millisecond)

	txnLogger.Printf("Ending transaction %s.", txnName)
	txn.End()

	logger.Print("Goodbye!")

Logs in Context for Golang

Option 2: using a log forwarder to collect logs

Our infrastructure monitoring agent enables you to forward your logs to New Relic, which consolidates all of your logging data in one location. This provides you with deeper visibility into both your application and platform performance.

By forwarding your logs to New Relic, you can enhance your log management capabilities by collecting, processing, exploring, querying, and setting alerts on your log data from the client side.

To forward your logs through the New Relic infrastructure monitoring agent, follow these steps:

  1. Create a New Relic account if you haven't already. It's free, forever.
  2. Check the system requirements to ensure your configuration is correct.
  3. Install the infrastructure agent (version 1.11.4 or higher).
  4. Create a logging.yml configuration file in the infrastructure agent's logging.d directory.
  5. Configure your log sources and other parameters.
  6. Generate some traffic and wait a few minutes, then check your account for data.
  7. Explore your log data in the Logs UI and benefit from the log attributes automatically inserted by the infrastructure agent.

Logs in Context with New Relic

Logs in Context with New Relic

For more information, don't forget to visit the official page for the New Relic Go agent.