From 22487faf06add89c2657c8988a2209a89e0d2af6 Mon Sep 17 00:00:00 2001 From: Arti Zirk Date: Sun, 6 Aug 2023 22:02:14 +0300 Subject: [PATCH] Add prometheus metrics --- godoor.go | 87 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/godoor.go b/godoor.go index c742807..c3dda9d 100644 --- a/godoor.go +++ b/godoor.go @@ -7,6 +7,9 @@ import ( "encoding/json" "fmt" "github.com/joho/godotenv" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" "io" "log" "net/http" @@ -56,6 +59,7 @@ type Config struct { wiegandB int solenoid int } + prometheusMetricsBind string } type KeepDoorOpen struct { @@ -88,17 +92,42 @@ var validUids ValidUids var wiegand Wiegand var keepDoorOpen KeepDoorOpen -var lastSyncedTimestamp *time.Time -var openDoorTimestamps []OpenedTimestamp +var ( + godoorBuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "godoor_build_info", + Help: "Build Information", + }, []string{"version", "revision"}) + lastSyncTimestamp = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "godoor_last_allow_list_sync_timestamp_seconds", + Help: "Last time list of card hashes was pulled from the server", + }) + apiFailuresCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "godoor_api_request_failures_total", + Help: "HTTP API request failures count", + }, []string{"api", "endpoint"}) + nrCardsInAllowList = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "godoor_allowed_card_hashes_total", + Help: "Number of card hashes in memory that can open the door", + }) + doorOpenedCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "godoor_door_opens_total", + Help: "Number of times door was opened", + }, []string{"source"}) + cardSwipesCount = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "godoor_swipes_total", + Help: "Number of times a card has been swiped", + }, []string{"status"}) +) func main() { - log.Printf("GoDoor ver: %s (%s)", Version, Commit) ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() loadConfig() + godoorBuildInfo.WithLabelValues(Version, Commit).Set(1) + go func() { setup() }() @@ -129,6 +158,10 @@ func loadConfig() { config.doorOpenTime = 3 } _, config.mock = os.LookupEnv("KDOORPI_MOCK_HW") + config.prometheusMetricsBind = os.Getenv("KDOORPI_PROMETHEUS_METRICS_BIND") + if config.prometheusMetricsBind == "" { + config.prometheusMetricsBind = ":3334" + } config.pins.wiegandA, err = strconv.Atoi(os.Getenv("KDOORPI_PIN_WIEGAND_A")) if err != nil { @@ -158,16 +191,24 @@ func setup() { } log.Println("HW Setup done") + go runHttpServer() + http.DefaultClient.Timeout = 120 * time.Second go func() { for { err := waitEvents() if err != nil { + apiFailuresCount.WithLabelValues("longpoll", config.api.longpoll).Inc() log.Printf("LongPoll for events failed: %v", err) log.Println("Will try to LongPoll again in 120 seconds") time.Sleep(120 * time.Second) - go reloadTokens() + go func() { + err := reloadTokens() + if err != nil { + apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc() + } + }() } time.Sleep(1 * time.Second) } @@ -181,13 +222,12 @@ func setup() { if err == nil { break } + apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc() log.Printf("Initial token population failed. err: %v", err) log.Println("Retrying in 10 seconds...") time.Sleep(10 * time.Second) } - go runHttpServer() - log.Println("Initial token population success") go cardRunner(wiegand) @@ -196,26 +236,9 @@ func setup() { } func runHttpServer() { - http.HandleFunc("/lastsync", func(w http.ResponseWriter, r *http.Request) { - e := json.NewEncoder(w) - e.Encode(map[string]any{ - "last_synced": lastSyncedTimestamp, - }) - }) - http.HandleFunc("/opened", func(w http.ResponseWriter, r *http.Request) { - e := json.NewEncoder(w) - e.Encode(map[string]any{ - "open_timestamps": openDoorTimestamps, - }) - }) - http.HandleFunc("/isopen", func(w http.ResponseWriter, r *http.Request) { - e := json.NewEncoder(w) - open, _ := wiegand.IsDoorOpen() - e.Encode(map[string]any{ - "open": open, - }) - }) - http.ListenAndServe(":3334", nil) + http.Handle("/metrics", promhttp.Handler()) + log.Printf("Running prometheus metrics on http://%s/metrics", config.prometheusMetricsBind) + log.Fatal(http.ListenAndServe(config.prometheusMetricsBind, nil)) } func OpenAndCloseDoor(w Wiegand) error { @@ -248,7 +271,6 @@ func OpenDoor(w Wiegand) error { return nil } w.OpenDoor() - openDoorTimestamps = append(openDoorTimestamps, OpenedTimestamp{Opened: time.Now(), Closed: nil}) return nil } @@ -258,8 +280,6 @@ func CloseDoor(w Wiegand) error { return nil } w.CloseDoor() - t := time.Now() - openDoorTimestamps[len(openDoorTimestamps)-1].Closed = &t return nil } @@ -281,6 +301,7 @@ func cardRunner(w Wiegand) { go func() { err := sendSwipeEvent(hashedHex, ok) if err != nil { + apiFailuresCount.WithLabelValues("swipe", config.api.swipe).Inc() log.Println("Failed to send swipe event: %v", err) } }() @@ -288,10 +309,13 @@ func cardRunner(w Wiegand) { if ok { log.Println("Opening door") err := OpenAndCloseDoor(w) + cardSwipesCount.WithLabelValues("accepted").Inc() + doorOpenedCount.WithLabelValues("card").Inc() if err != nil { log.Println("There was an error opening and closing the Door") } } else { + cardSwipesCount.WithLabelValues("denied").Inc() log.Println("Unknown card") } @@ -349,6 +373,7 @@ func waitEvents() error { log.Printf("got server data: %q\n", data) if strings.TrimSpace(data) == config.door { err := OpenAndCloseDoor(wiegand) + doorOpenedCount.WithLabelValues("api").Inc() if err != nil { log.Println("There was an error opening and closing the Door") } @@ -397,13 +422,13 @@ func reloadTokens() error { totalCardCount = i } log.Printf("Got %d cards from server", totalCardCount) + nrCardsInAllowList.Set(float64(totalCardCount)) if cl.KeepOpenUntil != nil { updateKeepOpenDoor(*cl.KeepOpenUntil) } - t := time.Now() - lastSyncedTimestamp = &t + lastSyncTimestamp.SetToCurrentTime() return nil }