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 | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user