From acd9f4fc628e3495d5a500a703ea9be4a0ca85f2 Mon Sep 17 00:00:00 2001 From: efim Date: Sat, 2 Dec 2023 11:26:00 +0000 Subject: [PATCH] feat: custom gesture & room metrics --- flake.nix | 2 +- main.go | 16 ++++++++++------ metrics/metrics.go | 43 +++++++++++++++++++++++++++++++++++++++++++ routes/room_page.go | 24 ++++++++++++++++++++---- routes/routes.go | 5 +++-- 5 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 metrics/metrics.go diff --git a/flake.nix b/flake.nix index f5c8665..5c3874d 100644 --- a/flake.nix +++ b/flake.nix @@ -35,7 +35,7 @@ some-automoderation = pkgs.buildGoModule { inherit pname version; src = pkgs.nix-gitignore.gitignoreSource [ ] ./.; - vendorHash = "sha256-FOIDFJNBViicvcpDG3T1KYABmI5Xyrv+IdQvT2Elhjg="; + vendorHash = "sha256-ID0WG0pa9DkXTJ7aB9VywGO3R85FWkpXaaIuugnG6mg="; preBuild = '' ${pkgs.nodePackages.tailwindcss}/bin/tailwindcss -i routes/in.css -o routes/static/out.css diff --git a/main.go b/main.go index 4cfc4e2..211bb11 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/redis/go-redis/v9" + "sunshine.industries/some-automoderation/metrics" "sunshine.industries/some-automoderation/rooms" "sunshine.industries/some-automoderation/routes" "sunshine.industries/some-automoderation/sessions" @@ -26,7 +27,10 @@ func main() { flag.IntVar(&redisPort, "redisPort", 7777, "Port on which server should connect to redis db") flag.Parse() - promHandler := promhttp.Handler() + metrics := metrics.SetupMetrics() + + promhttp.Handler() + promHandler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{}) promMux := http.NewServeMux() promMux.Handle("/metrics", promHandler) @@ -35,16 +39,16 @@ func main() { }() rdb := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("localhost:%d", redisPort), + Addr: fmt.Sprintf("localhost:%d", redisPort), Password: "", - DB: 0, + DB: 0, }) - roomsM := rooms.RedisRM { Rdb: rdb, } - sessions := sessions.RedisSM{ Rdb: rdb, } + roomsM := rooms.RedisRM{Rdb: rdb} + sessions := sessions.RedisSM{Rdb: rdb} log.Printf("Server will start on port: %d; /metrics on %d; listening to redis on: %d\n", port, metricsPort, redisPort) - routes.RegisterRoutes(sessions, roomsM) + routes.RegisterRoutes(sessions, roomsM, &metrics) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) } diff --git a/metrics/metrics.go b/metrics/metrics.go new file mode 100644 index 0000000..f559005 --- /dev/null +++ b/metrics/metrics.go @@ -0,0 +1,43 @@ +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" +) + +type MetricsContainer struct { + Registry *prometheus.Registry + LiveConnectionsGauge prometheus.Gauge + RaiseGestureCounter *prometheus.CounterVec + SpeakerCounter *prometheus.CounterVec +} + +const GestureNameLabel string = "gestureString" + +func SetupMetrics() MetricsContainer { + registry := prometheus.NewRegistry() + liveConnectionsGauge := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "live_connections_total", + Help: "Total amount of live SSE subscriptions to room state", + }) + raiseGestureCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "raise_gesture_total", + Help: "Total amount of raised hands", + }, []string{GestureNameLabel}) + speakerCounter := prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "speaker_total", + Help: "Total amount of speaker turns", + }, []string{GestureNameLabel}) + + registry.MustRegister(liveConnectionsGauge, raiseGestureCounter, speakerCounter, + collectors.NewGoCollector(), + collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), + ) + + return MetricsContainer{ + Registry: registry, + LiveConnectionsGauge: liveConnectionsGauge, + RaiseGestureCounter: raiseGestureCounter, + SpeakerCounter: speakerCounter, + } +} diff --git a/routes/room_page.go b/routes/room_page.go index b4e76c0..da9ea32 100644 --- a/routes/room_page.go +++ b/routes/room_page.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "sunshine.industries/some-automoderation/metrics" "sunshine.industries/some-automoderation/rooms" "sunshine.industries/some-automoderation/sessions" ) @@ -23,6 +24,7 @@ func registerPageRoutes( templateFs *embed.FS, sessionSM sessions.SessionManagement, roomsM rooms.RoomManager, + metrics *metrics.MetricsContainer, ) { http.Handle(roomPath, // ending in / captures all following path sections, i.e room name http.StripPrefix(roomPath, roomPageRoute(templateFs, roomsM, sessionSM))) @@ -30,15 +32,15 @@ func registerPageRoutes( http.Handle(raiseHandPath, // ending in / captures all following path sections, i.e gesture num authedPageMiddleware( sessionSM, - http.StripPrefix(raiseHandPath, raiseGestureHandRoute(roomsM)))) + http.StripPrefix(raiseHandPath, raiseGestureHandRoute(roomsM, metrics)))) http.Handle("/rooms/releaseHand", - authedPageMiddleware(sessionSM, releaseHandRoute(roomsM))) + authedPageMiddleware(sessionSM, releaseHandRoute(roomsM, metrics))) http.Handle(subscribeRoomPath, authedPageMiddleware( sessionSM, - http.StripPrefix(subscribeRoomPath, streamingRoomStates(templateFs, roomsM)))) + http.StripPrefix(subscribeRoomPath, streamingRoomStates(templateFs, roomsM, metrics)))) http.HandleFunc("/rooms/preview-templates", roomTemplatesPreview(templateFs)) @@ -48,8 +50,11 @@ func registerPageRoutes( func streamingRoomStates( templateFs *embed.FS, roomsM rooms.RoomManager, + metrics *metrics.MetricsContainer, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + metrics.LiveConnectionsGauge.Inc() + defer metrics.LiveConnectionsGauge.Dec() r.ParseForm() roomName := r.FormValue("roomName") defer log.Printf("/rooms/subscribe/%s stream ended\n", roomName) @@ -112,6 +117,7 @@ func streamingRoomStates( // TODO should return control for raised state func raiseGestureHandRoute( roomsM rooms.RoomManager, + metrics *metrics.MetricsContainer, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { gestureInd, err := strconv.Atoi(r.URL.Path) @@ -120,6 +126,7 @@ func raiseGestureHandRoute( log.Printf("/rooms/raiseGesture error %s gettin hand symbol index from path %s\n", err, r.URL.Path) return } + metrics.RaiseGestureCounter.WithLabelValues(selectedGesture.String()).Inc() log.Printf("/rooms/raiseGesture successfully got gesture %d : %s", selectedGesture, selectedGesture.String()) session, found := getContextSession(r.Context()) if !found { @@ -129,6 +136,10 @@ func raiseGestureHandRoute( } var room rooms.Room err = roomsM.Update(r.Context(), session.RoomId, func(fromRoom rooms.Room) (toRoom rooms.Room) { + if fromRoom.CurrentSpeaker == rooms.PersonId(0) { + // room had no speaker, need count new speaker turn + metrics.SpeakerCounter.WithLabelValues(selectedGesture.String()).Inc() + } toRoom = fromRoom.RaiseHand(session.PersonId, selectedGesture) room = toRoom return toRoom @@ -167,6 +178,7 @@ func raiseGestureHandRoute( // TODO should return lowered control for passed hand gesture, i guess optional func releaseHandRoute( roomsM rooms.RoomManager, + metrics *metrics.MetricsContainer, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { session, found := getContextSession(r.Context()) @@ -179,6 +191,10 @@ func releaseHandRoute( err := roomsM.Update(r.Context(), session.RoomId, func(fromRoom rooms.Room) (toRoom rooms.Room) { toRoom = fromRoom toRoom.ReleaseHand(session.PersonId) + if fromRoom.CurrentSpeaker != toRoom.CurrentSpeaker && toRoom.CurrentSpeaker != rooms.PersonId(0) { + gesture := toRoom.ParticipantHands[toRoom.CurrentSpeaker] + metrics.SpeakerCounter.WithLabelValues(gesture.String()).Inc() + } return toRoom }) if err != nil { @@ -268,7 +284,7 @@ func roomPageRoute( } data := pageData{ Base: baseData{ - Title: fmt.Sprintf("Some Automoderation: discussion in '%s'", room.Name), + Title: fmt.Sprintf("Some Automoderation: discussion in '%s'", room.Name), LoggedIn: true, }, Content: contentData, diff --git a/routes/routes.go b/routes/routes.go index 9bcfdc2..4d06710 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -4,6 +4,7 @@ import ( "embed" "net/http" + "sunshine.industries/some-automoderation/metrics" "sunshine.industries/some-automoderation/rooms" "sunshine.industries/some-automoderation/sessions" ) @@ -14,7 +15,7 @@ var templateFs embed.FS //go:embed static var staticFilesFs embed.FS -func RegisterRoutes(sessionsM sessions.SessionManagement, rooms rooms.RoomManager) { +func RegisterRoutes(sessionsM sessions.SessionManagement, rooms rooms.RoomManager, metrics *metrics.MetricsContainer) { // login page registerLoginRoutes(&templateFs, sessionsM, rooms) @@ -22,7 +23,7 @@ func RegisterRoutes(sessionsM sessions.SessionManagement, rooms rooms.RoomManage http.Handle("/", indexPageRoute(&templateFs, sessionsM, rooms)) // main conversation room page - registerPageRoutes(&templateFs, sessionsM, rooms) + registerPageRoutes(&templateFs, sessionsM, rooms, metrics) // static resources route http.Handle("/static/",