Add prometheus metrics

This commit is contained in:
Arti Zirk 2023-08-06 22:02:14 +03:00
parent 3f70299c36
commit 22487faf06

View File

@ -7,6 +7,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/joho/godotenv" "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" "io"
"log" "log"
"net/http" "net/http"
@ -56,6 +59,7 @@ type Config struct {
wiegandB int wiegandB int
solenoid int solenoid int
} }
prometheusMetricsBind string
} }
type KeepDoorOpen struct { type KeepDoorOpen struct {
@ -88,17 +92,42 @@ var validUids ValidUids
var wiegand Wiegand var wiegand Wiegand
var keepDoorOpen KeepDoorOpen var keepDoorOpen KeepDoorOpen
var lastSyncedTimestamp *time.Time var (
var openDoorTimestamps []OpenedTimestamp 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() { func main() {
log.Printf("GoDoor ver: %s (%s)", Version, Commit) log.Printf("GoDoor ver: %s (%s)", Version, Commit)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel() defer cancel()
loadConfig() loadConfig()
godoorBuildInfo.WithLabelValues(Version, Commit).Set(1)
go func() { go func() {
setup() setup()
}() }()
@ -129,6 +158,10 @@ func loadConfig() {
config.doorOpenTime = 3 config.doorOpenTime = 3
} }
_, config.mock = os.LookupEnv("KDOORPI_MOCK_HW") _, 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")) config.pins.wiegandA, err = strconv.Atoi(os.Getenv("KDOORPI_PIN_WIEGAND_A"))
if err != nil { if err != nil {
@ -158,16 +191,24 @@ func setup() {
} }
log.Println("HW Setup done") log.Println("HW Setup done")
go runHttpServer()
http.DefaultClient.Timeout = 120 * time.Second http.DefaultClient.Timeout = 120 * time.Second
go func() { go func() {
for { for {
err := waitEvents() err := waitEvents()
if err != nil { if err != nil {
apiFailuresCount.WithLabelValues("longpoll", config.api.longpoll).Inc()
log.Printf("LongPoll for events failed: %v", err) log.Printf("LongPoll for events failed: %v", err)
log.Println("Will try to LongPoll again in 120 seconds") log.Println("Will try to LongPoll again in 120 seconds")
time.Sleep(120 * time.Second) 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) time.Sleep(1 * time.Second)
} }
@ -181,13 +222,12 @@ func setup() {
if err == nil { if err == nil {
break break
} }
apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc()
log.Printf("Initial token population failed. err: %v", err) log.Printf("Initial token population failed. err: %v", err)
log.Println("Retrying in 10 seconds...") log.Println("Retrying in 10 seconds...")
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
} }
go runHttpServer()
log.Println("Initial token population success") log.Println("Initial token population success")
go cardRunner(wiegand) go cardRunner(wiegand)
@ -196,26 +236,9 @@ func setup() {
} }
func runHttpServer() { func runHttpServer() {
http.HandleFunc("/lastsync", func(w http.ResponseWriter, r *http.Request) { http.Handle("/metrics", promhttp.Handler())
e := json.NewEncoder(w) log.Printf("Running prometheus metrics on http://%s/metrics", config.prometheusMetricsBind)
e.Encode(map[string]any{ log.Fatal(http.ListenAndServe(config.prometheusMetricsBind, nil))
"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)
} }
func OpenAndCloseDoor(w Wiegand) error { func OpenAndCloseDoor(w Wiegand) error {
@ -248,7 +271,6 @@ func OpenDoor(w Wiegand) error {
return nil return nil
} }
w.OpenDoor() w.OpenDoor()
openDoorTimestamps = append(openDoorTimestamps, OpenedTimestamp{Opened: time.Now(), Closed: nil})
return nil return nil
} }
@ -258,8 +280,6 @@ func CloseDoor(w Wiegand) error {
return nil return nil
} }
w.CloseDoor() w.CloseDoor()
t := time.Now()
openDoorTimestamps[len(openDoorTimestamps)-1].Closed = &t
return nil return nil
} }
@ -281,6 +301,7 @@ func cardRunner(w Wiegand) {
go func() { go func() {
err := sendSwipeEvent(hashedHex, ok) err := sendSwipeEvent(hashedHex, ok)
if err != nil { if err != nil {
apiFailuresCount.WithLabelValues("swipe", config.api.swipe).Inc()
log.Println("Failed to send swipe event: %v", err) log.Println("Failed to send swipe event: %v", err)
} }
}() }()
@ -288,10 +309,13 @@ func cardRunner(w Wiegand) {
if ok { if ok {
log.Println("Opening door") log.Println("Opening door")
err := OpenAndCloseDoor(w) err := OpenAndCloseDoor(w)
cardSwipesCount.WithLabelValues("accepted").Inc()
doorOpenedCount.WithLabelValues("card").Inc()
if err != nil { if err != nil {
log.Println("There was an error opening and closing the Door") log.Println("There was an error opening and closing the Door")
} }
} else { } else {
cardSwipesCount.WithLabelValues("denied").Inc()
log.Println("Unknown card") log.Println("Unknown card")
} }
@ -349,6 +373,7 @@ func waitEvents() error {
log.Printf("got server data: %q\n", data) log.Printf("got server data: %q\n", data)
if strings.TrimSpace(data) == config.door { if strings.TrimSpace(data) == config.door {
err := OpenAndCloseDoor(wiegand) err := OpenAndCloseDoor(wiegand)
doorOpenedCount.WithLabelValues("api").Inc()
if err != nil { if err != nil {
log.Println("There was an error opening and closing the Door") log.Println("There was an error opening and closing the Door")
} }
@ -397,13 +422,13 @@ func reloadTokens() error {
totalCardCount = i totalCardCount = i
} }
log.Printf("Got %d cards from server", totalCardCount) log.Printf("Got %d cards from server", totalCardCount)
nrCardsInAllowList.Set(float64(totalCardCount))
if cl.KeepOpenUntil != nil { if cl.KeepOpenUntil != nil {
updateKeepOpenDoor(*cl.KeepOpenUntil) updateKeepOpenDoor(*cl.KeepOpenUntil)
} }
t := time.Now() lastSyncTimestamp.SetToCurrentTime()
lastSyncedTimestamp = &t
return nil return nil
} }