You can inject custom timing and metrics into your Go applications with the New Relic agent, but it’s not always obvious how to do it without the agent automatically instrumenting everything.

Here’s how you can manually instrument your Go code to get finer-grained control over what New Relic tracks.

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/newrelic/go-agent/v3/newrelic"
)

// Define a custom transaction name
const customTxName = "Custom/MyGolangApp/ProcessRequest"

func main() {
	// Initialize the New Relic agent. Replace "My Golang Application" with your app name.
	// The license key can be provided via the NEW_RELIC_LICENSE_KEY environment variable.
	config := newrelic.NewConfig("My Golang Application", "")
	agent, err := newrelic.NewApplication(config)
	if err != nil {
		panic(err)
	}

	// Create a handler that manually creates a transaction.
	http.HandleFunc("/manual", func(w http.ResponseWriter, r *http.Request) {
		// Start a new transaction.
		// This creates a segment that represents the entire request.
		txn := agent.StartTransaction("ManualHandler", w, r)
		defer txn.End() // Ensure the transaction is ended when the handler returns

		// Add custom attributes to the transaction.
		txn.AddAttribute("request.method", r.Method)
		txn.AddAttribute("request.url", r.URL.Path)

		// Simulate some work.
		time.Sleep(100 * time.Millisecond)

		// Create a custom segment for a specific piece of work.
		// This allows you to time and attribute specific operations within your transaction.
		segment := newrelic.DatastoreSegment{
			StartTime:      time.Now(),
			Product:        newrelic.DatastoreMySQL, // Example: MySQL
			Collection:     "users",
			Operation:      "SELECT",
			Parameterized:  "SELECT * FROM users WHERE id = ?",
			QueryParameters: []interface{}{123},
		}
		segment.End() // End the segment to record its duration

		// Add a custom event.
		agent.RecordCustomEvent("MyCustomEvent", map[string]interface{}{
			"event_type": "processing_complete",
			"duration_ms": 150,
			"user_id": 456,
		})

		fmt.Fprintln(w, "Processed request manually!")
	})

	// Create a handler that uses automatic instrumentation for comparison.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(50 * time.Millisecond)
		fmt.Fprintln(w, "Hello, World!")
	})

	fmt.Println("Starting server on :8080")
	http.ListenAndServe(":8080", nil)
}

When you instrument code manually, you’re essentially telling the New Relic agent exactly what to track and when. Instead of relying on the agent to infer transaction boundaries and important operations from your code’s structure, you explicitly define them. This is powerful for several reasons:

First, it allows you to create transactions that don’t map directly to HTTP requests. For example, you might have background workers or scheduled jobs that you want to monitor as distinct units of work. The agent.StartTransaction("MyBackgroundJob", nil, nil) pattern lets you do this.

Second, you gain precise control over transaction naming. By default, the Go agent often names transactions based on the matched route (e.g., WebTransaction/Go/GET//manual). Manual instrumentation lets you use more descriptive, hierarchical names like Custom/MyGolangApp/ProcessRequest, which can be invaluable for organizing and filtering data in New Relic.

Third, and perhaps most importantly, you can create custom segments within a transaction. A segment is a timed portion of code. The example above shows newrelic.DatastoreSegment for database calls, but you can also create generic newrelic.Segment instances. This is how you track specific functions, external API calls, or any other discrete operation within your request handler. When you create a segment, you give it a name, and defer segment.End() ensures its duration is recorded.

// Example of a generic custom segment
func doSomeWork(txn *newrelic.Transaction) {
    segment := newrelic.Segment{
        Name: "MyCustomOperation",
        StartTime: time.Now(),
    }
    defer segment.End()

    // ... actual work ...
    time.Sleep(75 * time.Millisecond)
}

The agent.RecordCustomEvent function is another way to inject data. Unlike transaction traces, custom events are aggregated and can be used for analysis and alerting. They are ideal for tracking occurrences of specific business events, like a successful payment, a failed login attempt, or the completion of a particular processing step.

The most surprising thing about manual instrumentation is how much of the agent’s behavior you can override. You’re not just adding a few metrics; you’re dictating the entire structure of the trace data that gets sent to New Relic for that specific transaction. This means you can tailor the data to exactly match your application’s critical paths and business logic, even if those paths are complex or don’t fit standard patterns.

The next step is understanding how to use these custom events and segments for custom alerts and dashboards in New Relic.

Want structured learning?

Take the full Newrelic course →