Skip to content

NimTechnology

Trình bày các công nghệ CLOUD một cách dễ hiểu.

  • Kubernetes & Container
    • Docker
    • Kubernetes
      • Ingress
      • Pod
    • Helm Chart
    • OAuth2 Proxy
    • Isito-EnvoyFilter
    • Apache Kafka
      • Kafka
      • Kafka Connect
      • Lenses
    • Vault
    • Longhorn – Storage
    • VictoriaMetrics
    • MetalLB
    • Kong Gateway
  • CI/CD
    • ArgoCD
    • ArgoWorkflows
    • Argo Events
    • Spinnaker
    • Jenkins
    • Harbor
    • TeamCity
    • Git
      • Bitbucket
  • Coding
    • DevSecOps
    • Terraform
      • GCP – Google Cloud
      • AWS – Amazon Web Service
      • Azure Cloud
    • Golang
    • Laravel
    • Python
    • Jquery & JavaScript
    • Selenium
  • Log, Monitor & Tracing
    • DataDog
    • Prometheus
    • Grafana
    • ELK
      • Kibana
      • Logstash
  • BareMetal
    • NextCloud
  • Toggle search form

[Golang] Writing a metrics exporter through Golang and Prometheus

Posted on April 30, 2023May 4, 2023 By nim No Comments on [Golang] Writing a metrics exporter through Golang and Prometheus

Ờ bài này mình muốn tìn hiểu làm sao có thể view page để expose các metrics theo ý mình và sử dụng cho prometheus

Bạn có thể follow this video

Sau đây mình sẽ thực hiện write theo video.

Contents

Toggle
  • 1) Using Demo of Prometheus Client.
  • Gauge
    • keep all the golang default metrics
  • GaugeVec

1) Using Demo of Prometheus Client.

Đầu tiên chúng ta sẽ expose default golang metrics.

file: main.go

package main

import (
    "net/http"

    "github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":8081", nil)
}

Bạn start chúng lên. và access browser http://localhost:8081/metrics

Bạn sẽ được 1 page như này.
và chúng ta sẽ không dừ ở đây. vì chúng ta cần tạo ra custom metrics according to ý chúng ta



Gauge

Nó sẽ biểu một giá trị số duy nhất mà nó có thể up hoặc down. Bạn có thể dùng do lường memory or cpu usage.
We’ll use it to keep track of the number of connected hardware devices to our app

main.go

Tạo 1 struct để và represent the hardware device. nó bao gồm ID, Mac và Firmware.

type Device struct {
    ID       int    `json:"id"`
    Mac      string `json:"mac"`
    Firmware string `json:"firmware"`
}

Khai báo 1 silde of devices as gobal variable to hold tất cả các deviced đã connected

main.go

var dvs []Device

Using special init() function để tạo 1 cặp devices.

main.go

func init() {
    dvs = []Device{
        {1, "5F-33-CC-1F-43-82", "2.1.6"},
        {2, "EF-2B-C4-F5-D6-34", "2.1.6"},
    }
}

Giờ tạo 1 http handler function để return tất cả các connected devices to code của bạn.

main.go

func getDevices(w http.ResponseWriter, r *http.Request) {
    b, err := json.Marshal(dvs)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write(b)
}

Chúng ta sẽ tạo 1 new enpoids mới là /devices and use getDevices function.

main.go

func main() {
    http.Handle("/metrics", promhttp.Handler())
    http.HandleFunc("/devices", getDevices) ###<<<=== look at
    http.ListenAndServe(":8081", nil)
}

Hoặc curl

curl -i localhost:8081/devices

HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 01 Dec 2022 12:11:30 GMT
Content-Length: 109

[{"id":1,"mac":"5F-33-CC-1F-43-82","firmware":"2.1.6"},{"id":2,"mac":"EF-2B-C4-F5-D6-34","firmware":"2.1.6"}]

Tạo struct là metrics sau đó set devices property to prometheus.Gauge. Bạn có thể thấy là bạn có thể sử dụng Gauge or a GaugeVec. Sự khác biệt khá đơn giản:
+ Gauge represents a single numerical value
+ GaugeVec is a collection that bundles a set of Gauges with the same name but different labels.

trong ví dụ, nếu chúng ta muốn count all connected devices và không care about the different types, thì sử dụng Gause và cách khác nếu bạn có sự khác nhau về device types, such as routers, switches, and access points. và bạn muốn count chúng một cách riêng biệt bạn sử dụng GaugeVec with a type label

main.go

type metrics struct {
    devices prometheus.Gauge
}

Then create a NewMetrics function that defines metrics. It accepts the prometheus register and returns the pointer to the metrics struct.

  • We need to create devices metric using the NewGauge function.
  • A Namespace is just a metric prefix; usually, you use a single word that matches the name of your app. In my case, it’s myapp.
  • Then the metric Name. It’s very important to follow the naming conventions provided by Prometheus. You can find it on the official website. Let’s call it connected_devices.
  • You also need to include a metric description.
  • Then register it with the prometheus registry and return a pointer.

main.go

func NewMetrics(reg prometheus.Registerer) *metrics {
    m := &metrics{
        devices: prometheus.NewGauge(prometheus.GaugeOpts{
            Namespace: "myapp",
            Name:      "connected_devices",
            Help:      "Number of currently connected devices.",
        }),
    }
    reg.MustRegister(m.devices)
    return m
}
  • In the main() function, create a non-global registry without any pre-registered Collectors.
  • Then create metrics using the NewMetrics function.
  • Now we can use the devices property of the metrics struct and set it to the current number of connected devices. For that, we simply set it to the number of items in the devices slice.
  • Let’s also create a custom prometheus handler with the newly created register.
  • We also need to update the /metrics handler to promHandler.

main.go

func main() {
    #add them
    reg := prometheus.NewRegistry()
    m := NewMetrics(reg)

    m.devices.Set(float64(len(dvs)))

    promHandler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
    #end add them

    http.Handle("/metrics", promHandler) #<<<< look at promHandler
    http.HandleFunc("/devices", getDevices)
    http.ListenAndServe(":8081", nil)
}

Khi bạn curl thì bạn chỉ thấy metrics về devices connected.

curl localhost:8081/metrics

# HELP myapp_connected_devices Number of currently connected devices.
# TYPE myapp_connected_devices gauge
myapp_connected_devices 2

keep all the golang default metrics

Optionally, if you still want to keep all the golang default metrics, you can use a built-in collector to register it with the custom Prometheus register.
Also, you can expose the prometheus handler metric as well by adding setting the Registry field.

main.go

func main() {
    reg := prometheus.NewRegistry()
    reg.MustRegister(collectors.NewGoCollector()) #<<<< look at
    m := NewMetrics(reg)

    m.devices.Set(float64(len(dvs)))

    promHandler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}) #<<<< look at

    http.Handle("/metrics", promHandler)
    http.HandleFunc("/devices", getDevices)
    http.ListenAndServe(":8081", nil)
}

GaugeVec

tiếp theo info metric sẽ represent the metadata of the app. You can expose any number of arbitrary key-value pairs from your application. As an example, we’ll expose the version of the currently running app.

Lần nay thì chúng ta sử dụng GaugeVec type since we need to set a version label with the actual version of the application.

main.go

type metrics struct {
    devices prometheus.Gauge
    info    *prometheus.GaugeVec
}
  • Let’s declare info metric using NewGaugeVec function.
  • All the metrics will get the same Namespace with the name of the app.
  • Using the same naming convention, let’s call it info as well and give it a description.
  • Don’t forget to register it using MustRegister function.

main.go

func NewMetrics(reg prometheus.Registerer) *metrics {
    m := &metrics{
        devices: prometheus.NewGauge(prometheus.GaugeOpts{
            Namespace: "myapp",
            Name:      "connected_devices",
            Help:      "Number of currently connected devices.",
        }),
        ###add them
        info: prometheus.NewGaugeVec(prometheus.GaugeOpts{
            Namespace: "myapp",
            Name:      "info",
            Help:      "Information about the My App environment.",
        },
            []string{"version"}),
        ###end add them
    }
    reg.MustRegister(m.devices, m.info) #<<<< look at
    return m
}

Khai báo a golbal version varibale.

var version string

Typically this variable will be set using the environment variable or by your CI tool. For the demo, let’s just hardcode it in the init() function.

main.go

func init() {
    version = "2.10.5" #<<<< look at

    dvs = []Device{
        {1, "5F-33-CC-1F-43-82", "2.1.6"},
        {2, "EF-2B-C4-F5-D6-34", "2.1.6"},
    }
}

Then in the main() function, we can use the version Prometheus label to set the application version and use a constant value of 1. If you check the default golang info metric, it uses the same convention.

main.go

func main() {
    reg := prometheus.NewRegistry()
    m := NewMetrics(reg)

    m.devices.Set(float64(len(dvs)))
    m.info.With(prometheus.Labels{"version": version}).Set(1) #<<<< look at

    promHandler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})

    http.Handle("/metrics", promHandler)
    http.HandleFunc("/devices", getDevices)
    http.ListenAndServe(":8081", nil)
}

Khi curl

curl localhost:8081/metrics

...
# HELP myapp_info Information about the My App environment.
# TYPE myapp_info gauge
myapp_info{version="2.10.5"} 1

Sau khi code và apply vào hệ thống thì mình nhận ra 1 điều là trang này nó sẽ không tự động cập nhật các metrics mà mình tự custom.
Cho dù mình refesh page hay F5 cỡ mấy thì value cũng không được làm mới.
sau đó thì mình tìm hiểu tiếp.

Sau khi mình soi lại code thì mình thấy là các giá trị chỉ được set 1 lần khi mà file main. go chạy và nó sẽ chạy function: m := NewMetrics(reg)
==> suy ra một điều là nếu bạn muốn value luôn được cập nhật thì bạn cần phải tạo ra action nào đó để set giá trị mới vào

Mình soi tiếp bài viết thì mình thấy:
https://antonputra.com/monitoring/monitor-golang-with-prometheus/#gauge

Khi một thiết bị mới nó connect vào thì ông sẽ gọi hàm createDevice nó set số mới vào trong page /metrics

Trong bài toán của mình thì mình đang draw data từ k8s nên mình Go Func

Golang

Post navigation

Previous Post: [DevSecOps] Dependency Check Basics and Vulnerabilities
Next Post: [Terraform] Manage Secrets in Terraform (2023)

More Related Articles

[Golang] List the files in a directory with Go Golang
[Golang] Checking a string inside or in another string by Golang Golang
[issue/alpine] docker 20.10.2 -> golang:1-alpine3.14 error: make: go: Operation not permitted Docker
[GRPC/Golang] Learning GRPC and Golang through the easy and actual lessons. Golang
[Golang] Design config file or environment file with Golang Golang
[Golang] Approach Channel in Calling another function with Golang Golang

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Tham Gia Group DevOps nhé!
Để Nim có nhiều động lực ra nhiều bài viết.
Để nhận được những thông báo mới nhất.

Recent Posts

  • [Laravel] Laravel Helpful June 26, 2025
  • [VScode] Hướng dẫn điều chỉnh font cho terminal June 20, 2025
  • [WordPress] Hướng dấn gửi mail trên WordPress thông qua gmail. June 15, 2025
  • [Bitbucket] Git Clone/Pull/Push with Bitbucket through API Token. June 12, 2025
  • [Teamcity] How to transfer the value from pipeline A to pipeline B June 9, 2025

Archives

  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • April 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • June 2021

Categories

  • BareMetal
    • NextCloud
  • CI/CD
    • Argo Events
    • ArgoCD
    • ArgoWorkflows
    • Git
      • Bitbucket
    • Harbor
    • Jenkins
    • Spinnaker
    • TeamCity
  • Coding
    • DevSecOps
    • Golang
    • Jquery & JavaScript
    • Laravel
    • NextJS 14 & ReactJS & Type Script
    • Python
    • Selenium
    • Terraform
      • AWS – Amazon Web Service
      • Azure Cloud
      • GCP – Google Cloud
  • Kubernetes & Container
    • Apache Kafka
      • Kafka
      • Kafka Connect
      • Lenses
    • Docker
    • Helm Chart
    • Isito-EnvoyFilter
    • Kong Gateway
    • Kubernetes
      • Ingress
      • Pod
    • Longhorn – Storage
    • MetalLB
    • OAuth2 Proxy
    • Vault
    • VictoriaMetrics
  • Log, Monitor & Tracing
    • DataDog
    • ELK
      • Kibana
      • Logstash
    • Fluent
    • Grafana
    • Prometheus
  • Uncategorized
  • Admin

Copyright © 2025 NimTechnology.