Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9e29a37
docs: add guides for containerizing a Golang application with Prometh…
Pradumnasaraf Jan 26, 2025
00149c5
docs: add development guide for containerizing a Golang application w…
Pradumnasaraf Mar 15, 2025
e27ab9d
docs: add images and enhance documentation for Golang application mon…
Pradumnasaraf Mar 22, 2025
0b9f27a
Update content/guides/go-prometheus-monitoring/_index.md
Pradumnasaraf Apr 2, 2025
fd49fbd
Update content/guides/go-prometheus-monitoring/_index.md
Pradumnasaraf Apr 2, 2025
d024692
Update content/guides/go-prometheus-monitoring/_index.md
Pradumnasaraf Apr 2, 2025
3c49ff5
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
1f6a7b3
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
6c09707
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
df23e79
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
6b6d835
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
0966de8
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
5c716eb
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
b5970e1
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
79031a1
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
03350f6
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
f14af5a
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
3b56df0
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
fa25785
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
b57999d
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
2b754b2
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
ab83a27
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
f09bce5
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
4e70508
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 2, 2025
a2a02de
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
124887b
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
ca82136
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
7b295b3
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
8bc7818
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
63d5fb9
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
bea92d1
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
7b07000
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
bb79c2e
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
f4f70d7
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
3719708
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 2, 2025
a70d828
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
d2e488c
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
c75e00d
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
d4c56ff
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
d838c0f
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
3ae2c22
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
ba3a9a9
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
98f5723
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
ec830b2
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
8099a7c
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
eea0f0f
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
41b75b6
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
70b45b8
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 2, 2025
9d10f0e
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
00496d3
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
14e2ec6
Update content/guides/go-prometheus-monitoring/containerize.md
Pradumnasaraf Apr 2, 2025
a6de397
Update content/guides/go-prometheus-monitoring/application.md
Pradumnasaraf Apr 3, 2025
c83117b
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 3, 2025
f5ad916
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 3, 2025
e33a468
Update content/guides/go-prometheus-monitoring/develop.md
Pradumnasaraf Apr 3, 2025
dfe8261
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 3, 2025
4f71213
Update content/guides/go-prometheus-monitoring/compose.md
Pradumnasaraf Apr 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions content/guides/go-prometheus-monitoring/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
description: Containerize a Golang application and monitor it with Prometheus and Grafana.
keywords: golang, prometheus, grafana, monitoring, containerize

Check failure on line 3 in content/guides/go-prometheus-monitoring/_index.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'golang'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'golang'?", "location": {"path": "content/guides/go-prometheus-monitoring/_index.md", "range": {"start": {"line": 3, "column": 11}}}, "severity": "ERROR"}

Check failure on line 3 in content/guides/go-prometheus-monitoring/_index.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Terms] Use 'Grafana' instead of 'grafana'. Raw Output: {"message": "[Vale.Terms] Use 'Grafana' instead of 'grafana'.", "location": {"path": "content/guides/go-prometheus-monitoring/_index.md", "range": {"start": {"line": 3, "column": 31}}}, "severity": "ERROR"}
title: Monitor a Golang application with Prometheus and Grafana
summary: |
Learn how to containerize a Golang application and monitor it with Prometheus and Grafana.
linkTitle: Monitor with Prometheus and Grafana
languages: [go]
params:
time: 45 minutes
---

The guide teaches you how to containerize a Golang application and monitor it with Prometheus and Grafana.

> **Acknowledgment**
>
> Docker would like to thank [Pradumna Saraf](https://twitter.com/pradumna_saraf) for his contribution to this guide.

Check failure on line 17 in content/guides/go-prometheus-monitoring/_index.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'Pradumna'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'Pradumna'?", "location": {"path": "content/guides/go-prometheus-monitoring/_index.md", "range": {"start": {"line": 17, "column": 31}}}, "severity": "ERROR"}

Check failure on line 17 in content/guides/go-prometheus-monitoring/_index.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'Saraf'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'Saraf'?", "location": {"path": "content/guides/go-prometheus-monitoring/_index.md", "range": {"start": {"line": 17, "column": 40}}}, "severity": "ERROR"}

## Overview

To make sure your application is working as intended, monitoring is important. One of the most popular monitoring tools is Prometheus. Prometheus is an open-source monitoring and alerting toolkit that is designed for reliability and scalability. It collects metrics from monitored targets by scraping metrics HTTP endpoints on these targets. To visualize the metrics, you can use Grafana. Grafana is an open-source platform for monitoring and observability that allows you to query, visualize, alert on, and understand your metrics no matter where they are stored.

In this guide, you will be creating a Golang server with some endpoints to simulate a real-world application. Then you will expose metrics from the server using Prometheus. Finally, you will visualize the metrics using Grafana. You will containerize the Golang application, and using the Docker Compose file, you will connect all the services: Golang, Prometheus, and Grafana.

## What will you learn?

* Create a Golang application with custom Prometheus metrics.
* Containerize a Golang application.
* Use Docker Compose to run multiple services and connect them together to monitor a Golang application with Prometheus and Grafana.
* Visualize the metrics using Grafana dashboards.

## Prerequisites

- A good understanding of Golang is assumed.
- You must me familiar with Prometheus and creating dashboards in Grafana.
- You must have familiarity with Docker concepts like containers, images, and Dockerfiles. If you are new to Docker, you can start with the [Docker basics](/get-started/docker-concepts/the-basics/what-is-a-container.md) guide.

## Next steps

You will create a Golang server and expose metrics using Prometheus.
250 changes: 250 additions & 0 deletions content/guides/go-prometheus-monitoring/application.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
---
title: Building the application
linkTitle: Understand the application
weight: 10 #
keywords: go, golang, prometheus, grafana, containerize, monitor

Check failure on line 5 in content/guides/go-prometheus-monitoring/application.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'golang'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'golang'?", "location": {"path": "content/guides/go-prometheus-monitoring/application.md", "range": {"start": {"line": 5, "column": 15}}}, "severity": "ERROR"}

Check failure on line 5 in content/guides/go-prometheus-monitoring/application.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Terms] Use 'Grafana' instead of 'grafana'. Raw Output: {"message": "[Vale.Terms] Use 'Grafana' instead of 'grafana'.", "location": {"path": "content/guides/go-prometheus-monitoring/application.md", "range": {"start": {"line": 5, "column": 35}}}, "severity": "ERROR"}
description: Learn how to create a Golang server to register metrics with Prometheus.
---

## Prerequisites

* You have a [Git client](https://git-scm.com/downloads). The examples in this section use a command-line based Git client, but you can use any client.

You will be creating a Golang server with some endpoints to simulate a real-world application. Then you will expose metrics from the server using Prometheus.

## Getting the sample application

Clone the sample application to use with this guide. Open a terminal, change
directory to a directory that you want to work in, and run the following
command to clone the repository:

```console
$ git clone https://github.com/dockersamples/go-prometheus-monitoring.git
```

Once you cloned you will see the following content structure inside `go-prometheus-monitoring` directory,

```text
go-prometheus-monitoring
├── CONTRIBUTING.md
├── Docker
│ ├── grafana.yml
│ └── prometheus.yml
├── dashboard.json
├── Dockerfile
├── LICENSE
├── README.md
├── compose.yaml
├── go.mod
├── go.sum
└── main.go
```

- **main.go** - The entry point of the application.
- **go.mod and go.sum** - Go module files.
- **Dockerfile** - Dockerfile used to build the app.
- **Docker/** - Contains the Docker Compose configuration files for Grafana and Prometheus.
- **compose.yaml** - Compose file to launch everything (Golang app, Prometheus, and Grafana).
- **dashboard.json** - Grafana dashboard configuration file.
- **Dockerfile** - Dockerfile used to build the Golang app.
- **compose.yaml** - Docker Compose file to launch everything (Golang app, Prometheus, and Grafana).
- Other files are for licensing and documentation purposes.

## Understanding the application

The following is the complete logic of the application you will find in `main.go`.

```go
package main

import (
"strconv"

"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define metrics
var (
HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "api_http_request_total",
Help: "Total number of requests processed by the API",
}, []string{"path", "status"})

HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "api_http_request_error_total",
Help: "Total number of errors returned by the API",
}, []string{"path", "status"})
)

// Custom registry (without default Go metrics)
var customRegistry = prometheus.NewRegistry()

// Register metrics with custom registry
func init() {
customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

func main() {
router := gin.Default()

// Register /metrics before middleware
router.GET("/metrics", PrometheusHandler())

router.Use(RequestMetricsMiddleware())
router.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Up and running!",
})
})
router.GET("/v1/users", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello from /v1/users",
})
})

router.Run(":8000")
}

// Custom metrics handler with custom registry
func PrometheusHandler() gin.HandlerFunc {
h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
return func(c *gin.Context) {
h.ServeHTTP(c.Writer, c.Request)
}
}

// Middleware to record incoming requests metrics
func RequestMetricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
c.Next()
status := c.Writer.Status()
if status < 400 {
HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
} else {
HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
}
}
}
```

In this part of the code, you have imported the required packages `gin`, `prometheus`, and `promhttp`. Then you have defined a couple of variables, `HttpRequestTotal` and `HttpRequestErrorTotal` are Prometheus counter metrics, and `customRegistry` is a custom registry that will be used to register these metrics. The name of the metric is a string that you can use to identify the metric. The help string is a string that will be shown when you query the `/metrics` endpoint to understand the metric. The reason you are using the custom registry is so avoid the default Go metrics that are registered by default by the Prometheus client. Then using the `init` function you are registering the metrics with the custom registry.

```go
import (
"strconv"

"github.com/gin-gonic/gin"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define metrics
var (
HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "api_http_request_total",
Help: "Total number of requests processed by the API",
}, []string{"path", "status"})

HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "api_http_request_error_total",
Help: "Total number of errors returned by the API",
}, []string{"path", "status"})
)

// Custom registry (without default Go metrics)
var customRegistry = prometheus.NewRegistry()

// Register metrics with custom registry
func init() {
customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}
```

In the `main` function, you have created a new instance of the `gin` framework and created three routes. You can see the health endpoint that is on path `/health` that will return a JSON with `{"message": "Up and running!"}` and the `/v1/users` endpoint that will return a JSON with `{"message": "Hello from /v1/users"}`. The third route is for the `/metrics` endpoint that will return the metrics in the Prometheus format. Then you have `RequestMetricsMiddleware` middleware, it will be called for every request made to the API. It will record the incoming requests metrics like status codes and paths. Finally, you are running the gin application on port 8000.

Check failure on line 166 in content/guides/go-prometheus-monitoring/application.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'middleware'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'middleware'?", "location": {"path": "content/guides/go-prometheus-monitoring/application.md", "range": {"start": {"line": 166, "column": 466}}}, "severity": "ERROR"}

```golang
func main() {
router := gin.Default()

// Register /metrics before middleware
router.GET("/metrics", PrometheusHandler())

router.Use(RequestMetricsMiddleware())
router.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Up and running!",
})
})
router.GET("/v1/users", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello from /v1/users",
})
})

router.Run(":8000")
}
```

Now comes the middleware function `RequestMetricsMiddleware`. This function is called for every request made to the API. It increments the `HttpRequestTotal` counter (different counter for different paths and status codes) if the status code is less than or equal to 400. If the status code is greater than 400, it increments the `HttpRequestErrorTotal` counter (different counter for different paths and status codes). The `PrometheusHandler` function is the custom handler that will be called for the `/metrics` endpoint. It will return the metrics in the Prometheus format.

Check failure on line 191 in content/guides/go-prometheus-monitoring/application.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'middleware'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'middleware'?", "location": {"path": "content/guides/go-prometheus-monitoring/application.md", "range": {"start": {"line": 191, "column": 15}}}, "severity": "ERROR"}

```golang
// Custom metrics handler with custom registry
func PrometheusHandler() gin.HandlerFunc {
h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
return func(c *gin.Context) {
h.ServeHTTP(c.Writer, c.Request)
}
}

// Middleware to record incoming requests metrics
func RequestMetricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
c.Next()
status := c.Writer.Status()
if status < 400 {
HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
} else {
HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
}
}
}
```

That's it, this was the complete gist of the application. Now it's time to run and test if the app is registering metrics correctly.

## Running the application

Make sure you are still inside `go-prometheus-monitoring` directory in the terminal, and run the following command. Install the dependencies by running `go mod tidy` and then build and run the application by running `go run main.go`. Then visit `http://localhost:8000/health` or `http://localhost:8000/v1/users`. You should see the output `{"message": "Up and running!"}` or `{"message": "Hello from /v1/users"}`. If you are able to see this then your app is successfully up and running.


Now, check your application's metrics by accessing the `/metrics` endpoint.
Open `http://localhost:8000/metrics` in your browser. You should see similar output to the following.

```sh
# HELP api_http_request_error_total Total number of errors returned by the API
# TYPE api_http_request_error_total counter
api_http_request_error_total{path="/",status="404"} 1
api_http_request_error_total{path="//v1/users",status="404"} 1
api_http_request_error_total{path="/favicon.ico",status="404"} 1
# HELP api_http_request_total Total number of requests processed by the API
# TYPE api_http_request_total counter
api_http_request_total{path="/health",status="200"} 2
api_http_request_total{path="/v1/users",status="200"} 1
```

In the terminal, press `ctrl` + `c` to stop the application.

> [!Note]
> If you don't want to run the application locally, and want to run it in a Docker container, skip to next page where you create a Dockerfile and containerize the application.

## Summary

In this section, you learned how to create a Golang app to register metrics with Prometheus. By implementing middleware functions, you were able to increment the counters based on the request path and status codes.

Check failure on line 246 in content/guides/go-prometheus-monitoring/application.md

View workflow job for this annotation

GitHub Actions / vale

[vale] reported by reviewdog 🐶 [Vale.Spelling] Did you really mean 'middleware'? Raw Output: {"message": "[Vale.Spelling] Did you really mean 'middleware'?", "location": {"path": "content/guides/go-prometheus-monitoring/application.md", "range": {"start": {"line": 246, "column": 110}}}, "severity": "ERROR"}

## Next steps

In the next section, you'll learn how to containerize your application.
Loading
Loading