-
Notifications
You must be signed in to change notification settings - Fork 8.1k
docs: add a guide for Golang API Monitoring with Prometheus and Grafana #22292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 51 commits
9e29a37
00149c5
e27ab9d
0b9f27a
fd49fbd
d024692
3c49ff5
1f6a7b3
6c09707
df23e79
6b6d835
0966de8
5c716eb
b5970e1
79031a1
03350f6
f14af5a
3b56df0
fa25785
b57999d
2b754b2
ab83a27
f09bce5
4e70508
a2a02de
124887b
ca82136
7b295b3
8bc7818
63d5fb9
bea92d1
7b07000
bb79c2e
f4f70d7
3719708
a70d828
d2e488c
c75e00d
d4c56ff
d838c0f
3ae2c22
ba3a9a9
98f5723
ec830b2
8099a7c
eea0f0f
41b75b6
70b45b8
9d10f0e
00496d3
14e2ec6
a6de397
c83117b
f5ad916
e33a468
dfe8261
4f71213
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 | ||
| 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
|
||
|
|
||
| ## 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. | ||
| 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 | ||
| 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, we have imported the required packages `gin`, `prometheus`, and `promhttp`. Then we 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 we 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 we are registering the metrics with the custom registry. | ||
|
Check warning on line 133 in content/guides/go-prometheus-monitoring/application.md
|
||
Pradumnasaraf marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```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
|
||
|
|
||
| ```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
|
||
|
|
||
| ```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
|
||
|
|
||
| ## Next steps | ||
|
|
||
| In the next section, you'll learn how to containerize your application. | ||
Uh oh!
There was an error while loading. Please reload this page.