
Continue from the previous topic…
Application Insights is available on Azure Portal as a way for developers to monitor their live web applications and to detect performance anomalies. It has a dashboard with charts to help developers diagnose issues and understand user behaviors on the applications. It works for apps written on multiple programming languages other than .NET too.
Setup of Application Insights on Azure
It is straightforward to setup Application Insights on Azure Portal. If we have already setup a default CI/CD for simple Golang web app, an Application Insights account will be created automatically.

Once the Application Insights account is created, we need to get its Instrument Key which is required before any telemetry can be sent via the SDK.
Simplicity in ASP .NET Core
In ASP .NET Core projects, we can easily include Application Insights by including the Nuget package Microsoft.ApplicationInsights.AspNetCore and adding the following highlighted code in Program.cs.
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup()
.UseApplicationInsights();
Setup of Application Insights Telemetry in Golang
So, what if we want to monitor our Golang applications which are hosted on Azure App Service? Luckily, Microsoft officially published an Application Insights SDK for Golang which is also open sourced on GitHub.
Since June 2015, Luke Kim, the Principal Group Software Engineering Manger at Microsoft, and other Microsoft engineers have been working on this open source project.
Introducing Application Insights to Golang application is not as straightforward as doing the same in ASP .NET Core project described above. Here, I will cover only how to use Telemetry.
First of all, we need to download and install the relevant package with the following go get command.
go get github.com/Microsoft/ApplicationInsights-Go/appinsights
Tracing Errors
Previously, we already have a centralized checkError function to handle errors returned from different sources in our code. So, we will have the following code added in the function to send traces back to Application Insights when an error occurs.
func checkError(err error) {
if err != nil {
client := appinsights.NewTelemetryClient(os.Getenv("APPINSIGHTS_INSTRUMENTATIONKEY"))
trace := appinsights.NewTraceTelemetry(err.Error(), appinsights.Error)
trace.Timestamp = time.Now()
client.Track(trace)
panic(err)
}
}
So, when there is an error on our application, we will receive a trace record as such on the Metrics of Application Insights as shown below.

However, doing this way doesn’t give us details such as call stack. Hence, if we want to log an exception in our application, we need to use TrackPanic in the SDK as follows.
func checkError(err error) {
if err != nil {
client := appinsights.NewTelemetryClient(os.Getenv("APPINSIGHTS_INSTRUMENTATIONKEY"))
trace := appinsights.NewTraceTelemetry(err.Error(), appinsights.Error)
trace.Timestamp = time.Now()
client.Track(trace)
// false indicates that we should have this handle the panic, and
// not re-throw it.
defer appinsights.TrackPanic(client, false)
panic(err)
}
}
This will capture and report call stack of the panic and display it on Azure Portal. With this, we can easily see which exceptions are occurring and how often.

Tracing Page Views
Besides errors, let’s capture the page views as well so that we can easily tell which handler function is called and how much time is spent in it. To do so, we introduce a new function called handleRequestWithLog.
func handleRequestWithLog(h func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
startTime := time.Now()
h(writer, request)
duration := time.Now().Sub(startTime)
client := appinsights.NewTelemetryClient(
os.Getenv("APPINSIGHTS_INSTRUMENTATIONKEY"))
trace := appinsights.NewRequestTelemetry(
request.Method, request.URL.Path, duration, "200")
trace.Timestamp = time.Now()
client.Track(trace)
})
}
Then we can modify our server.go to be as follows.
mux.HandleFunc("/", handleRequestWithLog(index))
mux.HandleFunc("/addVideo", handleRequestWithLog(addVideo))
mux.HandleFunc("/updateVideo", handleRequestWithLog(updateVideo))
mux.HandleFunc("/deleteVideo", handleRequestWithLog(deleteVideo))
Now whenever we visit a page or perform an action, the behaviour will be logged on Application Insights, as shown in the following screenshot. As you can see, the server response time is logged too.

With these records, the Performance chart in Application Insights will be plotted too.

Tracing Static File Downloads
Besides the web pages, we are also interested at static files, such as understanding how fast the server responses when the static file is retrieved.
To do so, we first need to introduce a new handler function called staticFile.go.
package main
import (
"mime"
"net/http"
"strings"
)
func staticFile(writer http.ResponseWriter, request *http.Request) {
urlComponents := strings.Split(request.URL.Path, "/")
http.ServeFile(
writer, request, "public/"+urlComponents[len(urlComponents)-1])
fileComponents := strings.Split(
urlComponents[len(urlComponents)-1], ".")
fileExtension := fileComponents[len(fileComponents)-1]
writer.Header().Set(
"Content-Type", mime.TypeByExtension(fileExtension))
}
The reason why we need do as such is because we want to apply the handleRequestWithLog function for static files in server.go too.
mux.HandleFunc("/static/", handleRequestWithLog(staticFile))
By doing so, we will start to see the following on Search of Application Insights.

Conclusion
In ASP .NET Core applications, we normally need add the UseApplicationInsights as shown below in Program.cs then all the server actions will be automatically traced. However, this is not the case for Golang applications where there is no such convenience.
References
- What is Application Insights;
- Exploring Metrics in Application Insights;
- In Golang, how to convert an error to a string?
- [Stack Overflow] How to get URL in http.Request?
- [Stack Overflow] How to get request string and method?
- [Stack Overflow] Golang http handler – time taken for request;
- [golang.org] func Split;
- Find the Length of an Array/Slice;
- [GitHub] Microsoft Application Insights SDK for Go;
- Golang 1.6: 使用mime.TypeByExtension来设置Content-Type;
- [Stack Overflow] What to use? http.ServeFile(..) or http.FileServer(..)?
- [Stack Overflow] How do you serve a static html file using a go web server?
