-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #72 from LucaBernstein/monitoring
Add monitoring endpoint for app stats
- Loading branch information
Showing
5 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,4 +24,6 @@ WORKDIR / | |
|
||
COPY --from=builder /src/app /bin/app | ||
|
||
EXPOSE 8081 | ||
|
||
ENTRYPOINT [ "/bin/app" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |