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