Prometheus Metrics #2
87
godoor.go
87
godoor.go
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user