April 25, 2019 By IBM Instana Team 4 min read

Go, also known as Golang, is a general-purpose language created and backed by Google, but developed in the open. Started back in 2009, Go reached version 1.0 in 2012 and is available in version 1.12.1 as of the day of writing.

Being used in quite a few bigger systems like Docker and Kubernetes, Go is the prime language to implement microservices, as well as gateway or proxy implementations. That said, programs written in Go become a major contributor to today’s highly distributed and extremely dynamic application infrastructures. It also means that monitoring those integral parts of the systems is critically important, too.

The IBM® Instana® platform provides simple and straightforward monitoring for applications written in Go. In comparison to systems running on virtual machines (VMs) like Java, .NET, Ruby and so on, though, Go is a natively compiled language. Full automatic instrumentation of Go processes is, therefore, neither implemented in the Instana platform nor recommended, as changes require deep changes in the programs method lookup table. We also understand that Go developer—like all developers using natively compiled languages—prefer to have more control. Therefore, the Instana platform makes integration a simple, one-shot operation for development teams by still providing full control over operations or requests being traced or not.

The Instana Go Sensor

The Instana Go Sensor provides a quick and easy API, using middleware functionalities built into the Go standard library.

Based on the OpenTracing API, Instana users always have the ability to create their own traces using the API offered by OpenTracing. To ease the integration and handle the distributed specifics of the Instana platform, a small helper library available from GitHub is provided by the Instana solution. It can be used just as any other known Go lib, by just adding it as a dependency:

import "github.com/instana/golang-sensor"

The API is meant to be as simple as possible and supports the most common use cases like request handling and HTTP client requests.

To see how to integrate the Instana platform into Go applications, we’ll create a small proxy application that uses the Instana Go Sensor API to trace incoming and outgoing HTTP calls.

To begin with, we need to create an instana.Sensor instance. It’s recommended to create one per application and store it into a public-free variable:

var sensor = instana.NewSensor("go-proxy-demo")

The instana.Sensor instance itself already provides the basic information about the Go process into the Instana dashboard, which includes metrics like garbage collection and memory or CPU usage:

However, the Instana platform can do more. The real benefit for developers is comprehensive, end-to-end traces throughout all the systems infrastructure.

That said, now that we have an instana.Sensor instance, we’re able to build what makes a proxy a proxy, request handling and forwarding.

But before we get into the Go Sensor specifics, we need a bit of preparation to make our application behave like a simple proxy—not a real reverse proxy, though. To make things easier, we use a single Go application to handle the original and the proxied request using two different request handlers.

That said, we need a http.Client instance and one small helper method, which copies the original http.Request:

var client = http.Client{}

func adaptRequest(req *http.Request, uri string) (*http.Request, error) {
    copy := new(http.Request)
    *copy = *req
    u, err := url.Parse(uri)
    if err != nil {
        return nil, err
    }
    copy.URL = u
    copy.RequestURI = ""
    return copy, nil
}

Done. Now we can create a method to handle the proxied request. The handler does nothing else other than answering any request with a plain Hello World:

func proxiedRequestHandler(w http.ResponseWriter, req *http.Request) {
    http.ServeContent(w, req, "index", time.Now(),
        strings.NewReader("Hello World"),
    )
}

The actual request handler doesn’t have to be modified from standard Go handler functions. To integrate the request handler into the Instana distributed tracing capabilities, we now register the handler by wrapping it into an Instana TracingHandler provided by the instana.Sensor instance:

func main() {
    http.HandleFunc("/bar",
         sensor.TracingHandler("bar", proxiedRequestHandler),
    )
    http.ListenAndServe(":8080", nil)
}

The above code creates a tracing request handler, managing all specific behavior of Instana and passing along the request to the user-provided, wrapped request handler. Finally, we’re listening for requests—all to make sure that proxied requests get into the Instana platform.

And, for your own convenience, you can even simplify it slightly more and squeeze initialization and registration into a single command:

func main() {
    http.HandleFunc(
        sensor.TraceHandler("bar", "/bar", proxiedRequestHandler),
    )
    http.ListenAndServe(":8080", nil)
}

The actual request proxying is as easy as the basic request tracing, it just is a twofold step, trace the incoming and tell the Instana platform about the outgoing request:

func proxyRequestHandler(w http.ResponseWriter, origReq *http.Request) {
    proxyReq, _ := adaptRequest(origReq, "http://localhost:8080/bar")
    resp, _ := sensor.TracingHttpRequest("proxy", origReq, proxyReq, client)
    resp.Write(w)
    w.Write([]byte("nAdded by the Proxy"))
}

That time we use a little bit more code, however, only a few lines are really special here. We already know about the adaptRequest function that we created before. It’s used to copy and adjust our request for sending it onwards.

The really interesting bit is the sensor.TracingHttpRequest, which takes multiple parameters.

The first parameter defines the name for the trace in the Instana dashboard, then the original request as it includes all tracing headers to read, the new request, which will be patched to include the tracing headers and the http.Client we created before. The client is used to execute the http.Request. In our case, we just wrapped the existing request and adjusted the URL.

All the necessary pieces to make this an external call appear in the Instana platform are handled by the underlying sensor implementation.

The last step is to add the proxyRequestHandler to our already existing HTTP server:

http.HandleFunc(
    sensor.TraceHandler("foo", "/foo", proxyRequestHandler),
)

Starting the application and creating the instana.Sensor instance makes the Go application connect to the locally running Instana agent.

Calling http://localhost:8080/foo will pass the request onwards to http://localhost:8080/bar via the HTTP request we created and executed. The traces are now visible from the Instana dashboard:

We’ve seen that the integration of the Instana platform into Go applications—even though not fully automatic—is quick and easy. The minimal amount of code that must be added, as well as a few changes necessary to existing applications, is the most pleasant experience added monitoring today.

The project code can be found in the examples of the Instana Go Sensor.

Get started with IBM Instana
Was this article helpful?
YesNo

More from IBM Instana

Achieving operational efficiency through Instana’s Intelligent Remediation

3 min read - With digital transformation all around us, application environments are ever growing leading to greater complexity. Organizations are turning to observability to help them proactively address performance issues efficiently and are leveraging generative AI to gain a competitive edge in delivering exceptional user experiences. This is where Instana’s Intelligent Remediation comes in, as it enhances application performance and resolves issues, before they have a chance to impact customers. Now generally available: Instana’s Intelligent Remediation Announced at IBM Think 2024, I’m happy…

Probable Root Cause: Accelerating incident remediation with causal AI 

5 min read - It has been proven time and time again that a business application’s outages are very costly. The estimated cost of an average downtime can run USD 50,000 to 500,000 per hour, and more as businesses are actively moving to digitization. The complexity of applications is growing as well, so Site Reliability Engineers (SREs) require hours—and sometimes days—to identify and resolve problems.   To alleviate this problem, we have introduced the new feature Probable Root Cause as part of Intelligent Incident…

Observe GenAI with IBM Instana Observability

6 min read - The emergence of generative artificial intelligence (GenAI), powered by large language models (LLMs) has accelerated the widespread adoption of artificial intelligence. GenAI is proving to be very effective in tackling a variety of complex use cases with AI systems operating at levels that are comparable to humans. Organisations are quickly realizing the value of AI and its transformative potential for business, adding trillions of dollars to the economy. Given this emerging landscape, IBM Instana Observability is on a mission to…

IBM Newsletters

Get our newsletters and topic updates that deliver the latest thought leadership and insights on emerging trends.
Subscribe now More newsletters