Skip to content

Commit

Permalink
Merge pull request #72 from LucaBernstein/monitoring
Browse files Browse the repository at this point in the history
Add monitoring endpoint for app stats
  • Loading branch information
LucaBernstein authored Dec 23, 2021
2 parents 4fc9944 + 411e7ed commit 214e4b5
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ WORKDIR /

COPY --from=builder /src/app /bin/app

EXPOSE 8081

ENTRYPOINT [ "/bin/app" ]
92 changes: 92 additions & 0 deletions db/crud/monitoring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package crud

import "github.com/LucaBernstein/beancount-bot-tg/helpers"

func (r *Repo) HealthGetLogs(lastHours int) (errors int, warnings int, err error) {
rows, err := r.db.Query(`
SELECT "level", COUNT(*) "c"
FROM "app::log"
WHERE "level" IN ($1, $2)
AND "created" + INTERVAL '1 hour' * $3 > NOW()
GROUP BY "level"`,
helpers.ERROR, helpers.WARN, lastHours)
if err != nil {
return
}
var (
level int
count int
)
for rows.Next() {
rows.Scan(&level, &count)
switch level {
case int(helpers.ERROR):
errors = count
case int(helpers.WARN):
warnings = count
}
}
return
}

func (r *Repo) HealthGetTransactions() (open int, archived int, err error) {
rows, err := r.db.Query(`
SELECT "archived", COUNT(*) "c"
FROM "bot::transaction"
GROUP BY "archived"`)
if err != nil {
return
}
var (
isArchived bool
count int
)
for rows.Next() {
rows.Scan(&isArchived, &count)
if isArchived {
archived = count
} else {
open = count
}
}
return
}

func (r *Repo) HealthGetUserCount() (count int, err error) {
rows, err := r.db.Query(`
SELECT COUNT(*) "c"
FROM "auth::user"`)
if err != nil {
return
}
if rows.Next() {
rows.Scan(&count)
}
return
}

func (r *Repo) HealthGetCacheStats() (accTo int, accFrom int, txDesc int, err error) {
rows, err := r.db.Query(`
SELECT "type", COUNT(*) "c"
FROM "bot::cache"
GROUP BY "type"`)
if err != nil {
return
}
var (
t string
count int
)
for rows.Next() {
rows.Scan(&t, &count)
switch t {
case helpers.STX_ACCT:
accTo = count
case helpers.STX_ACCF:
accFrom = count
case helpers.STX_DESC:
txDesc = count
}
}
return
}
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"github.com/LucaBernstein/beancount-bot-tg/bot"
"github.com/LucaBernstein/beancount-bot-tg/db"
"github.com/LucaBernstein/beancount-bot-tg/web"
)

func main() {
Expand All @@ -12,6 +13,8 @@ func main() {
bc := bot.NewBotController(db)
bc.ConfigureCronScheduler()

go web.StartWebServer(bc)

bot := bot.CreateBot(bc)
bc.AddBotAndStart(bot)
}
98 changes: 98 additions & 0 deletions web/health/monitoring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package health

import (
"fmt"
"net/http"

"github.com/LucaBernstein/beancount-bot-tg/bot"
"github.com/LucaBernstein/beancount-bot-tg/helpers"
)

type MonitoringResult struct {
logs_daily_warning int
logs_daily_error int

transactions_count_open int
transactions_count_archived int

users_count int

cache_entries_accTo int
cache_entries_accFrom int
cache_entries_txDesc int
}

// TODO: Use package?
// https://pkg.go.dev/github.com/prometheus/client_golang/prometheus?utm_source=godoc#pkg-overview
func MonitoringEndpoint(bc *bot.BotController) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
m := gatherMetrics(bc)
fmt.Fprintf(w, `
# HELP logs_daily Count of logs of specified type in the previous 24h
# TYPE logs_daily gauge
logs_daily{level="error"} %d
logs_daily{level="warning"} %d
# HELP transactions_count Count of transactions in the database per status
# TYPE transactions_count gauge
transactions_count{type="open"} %d
transactions_count{type="archived"} %d
# HELP users_count Count of unique users by user ID in the database
# TYPE users_count gauge
users_count %d
# HELP cache_entries Count of cache entries per type
# TYPE cache_entries gauge
cache_entries{type="accTo"} %d
cache_entries{type="accFrom"} %d
cache_entries{type="txDesc"} %d
`,
m.logs_daily_error,
m.logs_daily_warning,

m.transactions_count_open,
m.transactions_count_archived,

m.users_count,

m.cache_entries_accTo,
m.cache_entries_accFrom,
m.cache_entries_txDesc,
)
}
}

func gatherMetrics(bc *bot.BotController) (result *MonitoringResult) {
result = &MonitoringResult{}

errors, warnings, err := bc.Repo.HealthGetLogs(24)
if err != nil {
bc.Logf(helpers.ERROR, nil, "Error getting health logs: %s", err.Error())
}
result.logs_daily_error = errors
result.logs_daily_warning = warnings

open, archived, err := bc.Repo.HealthGetTransactions()
if err != nil {
bc.Logf(helpers.ERROR, nil, "Error getting health transactions: %s", err.Error())
}
result.transactions_count_open = open
result.transactions_count_archived = archived

count, err := bc.Repo.HealthGetUserCount()
if err != nil {
bc.Logf(helpers.ERROR, nil, "Error getting health users: %s", err.Error())
}
result.users_count = count

accTo, accFrom, txDesc, err := bc.Repo.HealthGetCacheStats()
if err != nil {
bc.Logf(helpers.ERROR, nil, "Error getting health transactions: %s", err.Error())
}
result.cache_entries_accTo = accTo
result.cache_entries_accFrom = accFrom
result.cache_entries_txDesc = txDesc

return
}
14 changes: 14 additions & 0 deletions web/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package web

import (
"log"
"net/http"

"github.com/LucaBernstein/beancount-bot-tg/bot"
"github.com/LucaBernstein/beancount-bot-tg/web/health"
)

func StartWebServer(bc *bot.BotController) {
http.HandleFunc("/health", health.MonitoringEndpoint(bc))
log.Fatal(http.ListenAndServe(":8081", nil))
}

0 comments on commit 214e4b5

Please sign in to comment.