Add metrics
This commit is contained in:
148
metrics/metrics.go
Normal file
148
metrics/metrics.go
Normal file
@ -0,0 +1,148 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CommandStats struct {
|
||||
AllTime int `json:"all_time"`
|
||||
Last30 [30]int `json:"last_30"`
|
||||
Last30Day int64 `json:"last_30_day"` // Unix day of the first slot
|
||||
}
|
||||
|
||||
type ChatInfo struct {
|
||||
IsGroup bool `json:"is_group"`
|
||||
LastActive int64 `json:"last_active"`
|
||||
Blocked bool `json:"blocked"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
}
|
||||
|
||||
type Metrics struct {
|
||||
Commands map[string]*CommandStats `json:"commands"`
|
||||
Chats map[int64]*ChatInfo `json:"chats"`
|
||||
}
|
||||
|
||||
var (
|
||||
metricsFile = "metrics/metrics.json"
|
||||
metrics *Metrics
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
func loadMetrics() {
|
||||
if metrics != nil {
|
||||
return
|
||||
}
|
||||
metrics = &Metrics{
|
||||
Commands: make(map[string]*CommandStats),
|
||||
Chats: make(map[int64]*ChatInfo),
|
||||
}
|
||||
f, err := os.Open(metricsFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
json.NewDecoder(f).Decode(metrics)
|
||||
}
|
||||
|
||||
func saveMetrics() {
|
||||
// Do not lock here; caller must hold the lock else it will shitty deadlock
|
||||
loadMetrics()
|
||||
f, err := os.Create(metricsFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
enc := json.NewEncoder(f)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(metrics)
|
||||
}
|
||||
|
||||
func IncrementCommandUsage(cmd string) {
|
||||
fmt.Printf("[metrics] IncrementCommandUsage called with cmd=%s\n", cmd)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
loadMetrics()
|
||||
stat, ok := metrics.Commands[cmd]
|
||||
if !ok {
|
||||
stat = &CommandStats{Last30Day: time.Now().Unix() / 86400}
|
||||
metrics.Commands[cmd] = stat
|
||||
}
|
||||
today := time.Now().Unix() / 86400
|
||||
shift := int(today - stat.Last30Day)
|
||||
if shift > 0 && shift < 30 {
|
||||
copy(stat.Last30[shift:], stat.Last30[:30-shift])
|
||||
for i := 0; i < shift; i++ {
|
||||
stat.Last30[i] = 0
|
||||
}
|
||||
stat.Last30Day = today
|
||||
} else if shift >= 30 {
|
||||
for i := 0; i < 30; i++ {
|
||||
stat.Last30[i] = 0
|
||||
}
|
||||
stat.Last30Day = today
|
||||
}
|
||||
stat.AllTime++
|
||||
stat.Last30[0]++
|
||||
saveMetrics()
|
||||
}
|
||||
|
||||
func UpdateChat(chatID int64, isGroup bool) {
|
||||
fmt.Printf("[metrics] UpdateChat called with chatID=%d isGroup=%v\n", chatID, isGroup)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
loadMetrics()
|
||||
info, ok := metrics.Chats[chatID]
|
||||
if !ok {
|
||||
info = &ChatInfo{IsGroup: isGroup}
|
||||
metrics.Chats[chatID] = info
|
||||
}
|
||||
info.LastActive = time.Now().Unix()
|
||||
info.Blocked = false
|
||||
info.LastError = ""
|
||||
saveMetrics()
|
||||
}
|
||||
|
||||
func MarkChatBlocked(chatID int64, errMsg string) {
|
||||
fmt.Printf("[metrics] MarkChatBlocked called with chatID=%d errMsg=%s\n", chatID, errMsg)
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
loadMetrics()
|
||||
info, ok := metrics.Chats[chatID]
|
||||
if !ok {
|
||||
info = &ChatInfo{}
|
||||
metrics.Chats[chatID] = info
|
||||
}
|
||||
info.Blocked = true
|
||||
info.LastError = errMsg
|
||||
saveMetrics()
|
||||
}
|
||||
|
||||
func GetStats() *Metrics {
|
||||
fmt.Printf("[metrics] GetStats called\n")
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
loadMetrics()
|
||||
copy := *metrics
|
||||
return ©
|
||||
}
|
||||
|
||||
func BroadcastMessage(sendFunc func(chatID int64) error) {
|
||||
fmt.Printf("[metrics] BroadcastMessage called\n")
|
||||
mu.Lock()
|
||||
loadMetrics()
|
||||
ids := make([]int64, 0, len(metrics.Chats))
|
||||
for id := range metrics.Chats {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
mu.Unlock()
|
||||
for _, id := range ids {
|
||||
err := sendFunc(id)
|
||||
if err != nil {
|
||||
MarkChatBlocked(id, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user