Go applications power everything from lightweight services to large-scale distributed systems. With this level of complexity, visibility into performance metrics is non-negotiable. Key metrics such as request latency, throughput, and error rates provide a clear picture of application health, helping you detect and resolve issues faster, optimize performance at scale, and maintain a seamless user experience. However, adding instrumentation to Go applications often feels complex and time-consuming because it requires manual instrumentation across the application code. This effort requires a learning curve for developers and it increases the risk of gaps in the instrumentation process.
Enter New Relic’s Go easy instrumentation, a streamlined way to capture insights from your Go applications without the tedious process of configuring instrumentation manually for each transaction and segment. This blog post introduces four practical examples to help you easily instrument your Go apps, from basic services with a standalone Hello, World! app to advanced distributed systems with gRPC. Leverage Go easy instrumentation to streamline development while ensuring your applications are seamlessly instrumented for comprehensive monitoring without spending hours.
Note: This feature is currently provided as part of a preview program pursuant to our pre-release policies.
Sample scenarios: Progressive monitoring use cases
We’ll discuss four sample applications, showcasing how seamlessly Go easy instrumentation integrates the New Relic for Go SDK into your applications for efficient and hassle-free monitoring.
The examples increase in complexity, showing how to scale monitoring efforts as your application grows. We’ll focus on these sample applications and scenarios:
- Greetings: A simple
Hello,World
! app enhanced to demonstrate foundational concepts effectively. This will focus on the basics of instrumentation coverage with Go easy instrumentation. - REST API: A very simple HTTP API with
net/http
package with a single endpoint. This will focus on adding instrumentation to the HTTP handler. - REST API with Gin: A multi-endpoint HTTP API using
Gin library
, focusing on segmented transactions and detailed monitoring for individual endpoints. - gRPC with client and server: A distributed system example, capturing transactions between a client and server application, and parallel operations with Goroutines.
Each sample showcases specific use cases and the benefits of Go easy instrumentation, highlighting how it scales from simple applications to complex, distributed systems.
Here’s a glimpse of the output you can expect after executing the commands to instrument your Go applications.
Greetings: A simple Go app
This example provides an introduction to basic instrumentation through a simple Hello, World! application. It's an ideal starting point for anyone new to New Relic instrumentation tools for Go applications.
In this scenario, we’ll work with a simple application. You can explore the source code of the sample apps used in this blog post here.
These sample applications are structured so that the Go easy instrumentation program is executed from the root directory of our sample application.
To generate an instrumented version of the application, run the following command from the root of the sample application:
INFO: You can easily set up the Go easy instrumentation program by running go install github.com/newrelic/go-easy-instrumentation@latest
in your terminal
go-easy-instrumentation instrument 1.hello-world -o 1.hello-world/greetings.diff
Let's break down the commands and parameters for the Go easy instrumentation program:
-
instrument:
- <path>: Expects the application path for which the Go easy instrumentation program will be executed.
- –output: This parameter enables you to create a .diff output file for the specified application, with a custom location and file name of your choice. If no location or name is specified, the program will default to generating the file in the root of the specified application, naming it new-relic-instrumentation.diff.
- –debug: This parameter prints warnings about potential instrumentation issues, displayed both as inline comments in the code and in the console. If no debug prints or inline comments appear, it means the app has no identified issues.
- completion: Generate the auto-completion script for Go easy instrumentation for the specified shell (Bash, Fish, Zsh or PowerShell)
NOTE: You must configure an environment variable called NEW_RELIC_APP_NAME
with the name of your application and NEW_RELIC_LICENSE_KEY
with your INGEST LICENSE KEY. The program assumes this environment variable is already configured.
After executing the command, the program generates an output file in the specified application directory with the provided name (if the -–output
parameter is used). This .diff file can be used to review the changes made to your Go application.
Here’s what the .diff file contains:
The Go easy instrumentation program automatically imports the New Relic Go agent and decorates all the functions in the program appropriately with transactions and segments. This eliminates the tedious task of manually decorating the individual functions.
You can apply these changes simply by running the below command from the app directory:
git apply greetings.diff
Now the application starts reporting the data to New Relic without any additional changes.
REST API: Simple API with net/http package
Expanding on the basics, this sample demonstrates integrating monitoring into a single-endpoint HTTP API.
In this example, we’ll use a very simple HTTP API created with the standard net/http
package. This application serves a single purpose: handling GET requests on localhost:8080.
This is what the application looks like before executing Go easy instrumentation:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server is running on http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
Next, we’ll run Go easy instrumentation to add New Relic instrumentation to the application. We need to run the command in the same root directory of the Go easy instrumentation:
go-easy-instrumentation instrument 2.rest-api --output 2.rest-api/rest-api.diff
This provides an output .diff file named rest-api.diff
under the rest-api directory. Here’s what the decorated app now looks like:
Go easy instrumentation simply wraps the HandleFunc()
with New Relic’s WrapHandleFunc()
to capture all the requests and response times for all the incoming requests. This removes the tediousness of refactoring the handler with the SDK’s wrapper.
The process to apply this is straightforward: simply run the git apply
command.
git apply rest-api.diff
This example shows how developers building lightweight HTTP services can quickly set up instrumentation for REST API applications using the standard net/http
package to capture the request-response telemetry.
REST API with Gin: Multiple endpoints with Gin package
In this sample, we’ll take the previous HTTP API application to the next level with one change: we’ll use the Gin library to build our REST API. We'll add multiple endpoints to handle different HTTP methods like GET, POST, PUT, and DELETE, while incorporating advanced features such as middleware for logging
The app structure is shown below. Look at the complete application source code here.
func main() {
// Create a new Gin router
router := gin.Default()
// Logging middleware
router.Use(loggingMiddleware)
// Define routes
router.GET("/items", getItems)
router.POST("/items", addItem)
router.GET("/items/:id", getItemByID)
router.PUT("/items/:id", updateItemByID)
router.DELETE("/items/:id", deleteItemByID)
// Start the server
log.Println("Server is running on http://localhost:8080")
router.Run(":8080")
}
// getItems handles GET /items and returns the list of items
func getItems(ctx *gin.Context) {
ctx.JSON(http.StatusOK, items)
log.Println("Returned all items")
}
// addItem handles POST /items and adds a new item
func addItem(ctx *gin.Context) {
...
...
}
// getItemByID handles GET /items/:id and returns a single item by ID
func getItemByID(ctx *gin.Context) {
...
...
}
// updateItemByID handles PUT /items/:id to update an existing item by ID
func updateItemByID(ctx *gin.Context) {
...
...
}
// deleteItemByID handles DELETE /items/:id to delete an item by ID
func deleteItemByID(ctx *gin.Context) {
...
...
}
In an application that’s set up like this, you’ll typically want to capture metrics for each endpoint individually and focus on transactions and segments to break down complex operations under each request-response cycle.
With a complex application like this, refactoring to include proper instrumentation in the right places without leaving any gaps becomes a challenging task. This task can span multiple sprints for the team or individual developers, which is not ideal.
Simply run the below command and let Go easy instrumentation decorate the code, potentially saving several hours or even days of manual instrumentation work.
go-easy-instrumentation instrument 3.gin -o 3.gin/gin-rest.diff
New Relic Go easy instrumentation simplifies complex instrumentation scenarios, even for applications built using Gin framework with multiple endpoints or services that require granular performance monitoring.
gRPC: Client and server with Goroutines
Distributed applications with Golang often rely on gRPC for communication between services. This example demonstrates monitoring a gRPC client-server architecture. It covers common patterns with gRPC, like single request-response, server-streaming RPC call, client-streaming RPC call, and bidirectional streaming RPC calls.
Look at the complete application source code here.
func doClientCalls(ctx context.Context, client sampleapp.SampleApplicationClient) {
// doUnaryUnary performs a unary RPC call where both the client request and server response
// are single messages. This is similar to a traditional HTTP request/response pattern.
doUnaryUnary(ctx, client)
// doUnaryStream performs a server-streaming RPC call where the client sends a single request
// and receives a stream of responses from the server.
doUnaryStream(ctx, client)
// doStreamUnary performs a client-streaming RPC call where the client sends multiple messages
// to the server and receives a single response.
doStreamUnary(ctx, client)
// doStreamStream performs a bidirectional streaming RPC call where both the client and server
// can send multiple messages in any order.
doStreamStream(ctx, client)
}
Capturing metrics for client-server transactions and monitoring Goroutine-based parallel operations for latency and throughput helps you achieve end-to-end visibility in your distributed applications. It allows you to trace requests end to end, even across asynchronous operations, ensuring visibility in distributed systems.
For this, you need to execute the Go easy instrumentation separately for client and server applications.
Client
go-easy-instrumentation instrument 4.gRpc/client --output 4.gRpc/client/grpc-client.diff
Server
go-easy-instrumentation instrument 4.gRpc/server --output 4.gRpc/server/grpc-server.diff
Similar to the multi-endpoint REST application, Go easy instrumentation decorates all your methods with transactions and segments. Additionally, since it identifies the use of Goroutine, it adds the proper instrumentation for capturing Goroutine tasks and appropriate error handling where necessary.
How New Relic easy instrumentation simplifies Go
New Relic’s Go easy instrumentation provides a simple and seamless experience for instrumenting your Golang applications. Here’s how it helps for each scenario:
- Plug-and-play setup: Quick to integrate with minimal configuration, even for new users.
- Comprehensive metrics: Tracks everything from transaction times, segments, Goroutines, and error rates, giving you full visibility.
- Scalable: Adapts effortlessly from basic apps to distributed apps.
- Customisable: Allows you to modify the instrumentation without disturbing your current application state with the .diff file options.
Conclusion
Instrumentation for modern Go applications should no longer be a manual and painful task, especially given the complexity and scalability challenges developers face today. By addressing these challenges, New Relic’s Go easy instrumentation transforms the process into an efficient and streamlined experience, significantly reducing the manual effort required for instrumentation. New Relic’s Go easy instrumentation makes monitoring almost effortless. It reduces gaps in instrumentation and makes the process easy for anyone to implement. The four samples discussed in this blog post provide a roadmap to start monitoring applications of varying complexity, ensuring you’re prepared to tackle real-world challenges.
Explore these samples, integrate Go easy instrumentation into your applications, and experience the ease of proactive monitoring. With performance insights at your fingertips, you can focus on what matters most—building great software.
This library is currently being updated regularly to include more support for other features like data stores and more. Although it’s still in preview, it can help you scale your instrumentation for your production applications.
Next steps
If you encounter issues, have suggestions, or need support for integrating this library with your Golang applications, don't hesitate to submit an issue or feature request on Go easy instrumentation’s GitHub repository today.
The views expressed on this blog are those of the author and do not necessarily reflect the views of New Relic. Any solutions offered by the author are environment-specific and not part of the commercial solutions or support offered by New Relic. Please join us exclusively at the Explorers Hub (discuss.newrelic.com) for questions and support related to this blog post. This blog may contain links to content on third-party sites. By providing such links, New Relic does not adopt, guarantee, approve or endorse the information, views or products available on such sites.