feat: expose monitoring tg poll diagnostics
This commit is contained in:
@@ -135,6 +135,7 @@ type componentProbe struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
LatencyMs int64 `json:"latency_ms"`
|
LatencyMs int64 `json:"latency_ms"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
|
Details any `json:"details,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -444,10 +445,22 @@ func (a *app) probePollErrors(ctx context.Context) componentProbe {
|
|||||||
return componentProbe{Name: "poll_errors", Status: "down", LatencyMs: time.Since(start).Milliseconds(), Error: err.Error()}
|
return componentProbe{Name: "poll_errors", Status: "down", LatencyMs: time.Since(start).Milliseconds(), Error: err.Error()}
|
||||||
}
|
}
|
||||||
if total > 0 {
|
if total > 0 {
|
||||||
|
recent, recentErr := a.recentPollErrors(ctx)
|
||||||
status := "degraded"
|
status := "degraded"
|
||||||
if other > 0 {
|
if other > 0 {
|
||||||
status = "down"
|
status = "down"
|
||||||
}
|
}
|
||||||
|
details := map[string]any{
|
||||||
|
"total": total,
|
||||||
|
"flood_wait": floodWait,
|
||||||
|
"unavailable": unavailable,
|
||||||
|
"other": other,
|
||||||
|
}
|
||||||
|
if recentErr != nil {
|
||||||
|
details["recent_error"] = recentErr.Error()
|
||||||
|
} else {
|
||||||
|
details["recent_errors"] = recent
|
||||||
|
}
|
||||||
return componentProbe{
|
return componentProbe{
|
||||||
Name: "poll_errors",
|
Name: "poll_errors",
|
||||||
Status: status,
|
Status: status,
|
||||||
@@ -456,11 +469,64 @@ func (a *app) probePollErrors(ctx context.Context) componentProbe {
|
|||||||
" flood_wait=" + strconv.FormatInt(floodWait, 10) +
|
" flood_wait=" + strconv.FormatInt(floodWait, 10) +
|
||||||
" unavailable=" + strconv.FormatInt(unavailable, 10) +
|
" unavailable=" + strconv.FormatInt(unavailable, 10) +
|
||||||
" other=" + strconv.FormatInt(other, 10),
|
" other=" + strconv.FormatInt(other, 10),
|
||||||
|
Details: details,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return componentProbe{Name: "poll_errors", Status: "ok", LatencyMs: time.Since(start).Milliseconds()}
|
return componentProbe{Name: "poll_errors", Status: "ok", LatencyMs: time.Since(start).Milliseconds()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *app) recentPollErrors(ctx context.Context) ([]map[string]any, error) {
|
||||||
|
rows, err := a.db.Query(ctx, `
|
||||||
|
SELECT
|
||||||
|
c.id,
|
||||||
|
c.identifier,
|
||||||
|
COALESCE(c.title, '') AS title,
|
||||||
|
s.slug,
|
||||||
|
s.title AS section_title,
|
||||||
|
COALESCE(c.last_poll_error_code, '') AS error_code,
|
||||||
|
COALESCE(c.last_poll_error, '') AS error_text,
|
||||||
|
c.last_poll_error_at
|
||||||
|
FROM channels c
|
||||||
|
LEFT JOIN sections s ON s.id = c.section_id
|
||||||
|
WHERE c.is_active = true
|
||||||
|
AND c.source_channel_id IS NULL
|
||||||
|
AND c.last_poll_status = 'error'
|
||||||
|
ORDER BY c.last_poll_error_at DESC NULLS LAST, c.id DESC
|
||||||
|
LIMIT 5`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := make([]map[string]any, 0, 5)
|
||||||
|
for rows.Next() {
|
||||||
|
var (
|
||||||
|
id int64
|
||||||
|
identifier string
|
||||||
|
title string
|
||||||
|
sectionSlug sql.NullString
|
||||||
|
sectionTitle sql.NullString
|
||||||
|
code string
|
||||||
|
text string
|
||||||
|
at sql.NullTime
|
||||||
|
)
|
||||||
|
if err := rows.Scan(&id, &identifier, &title, §ionSlug, §ionTitle, &code, &text, &at); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, map[string]any{
|
||||||
|
"channel_id": id,
|
||||||
|
"identifier": identifier,
|
||||||
|
"title": nullableString(title),
|
||||||
|
"section_slug": nullString(sectionSlug),
|
||||||
|
"section_title": nullString(sectionTitle),
|
||||||
|
"error_code": nullableString(code),
|
||||||
|
"error": nullableString(text),
|
||||||
|
"error_at": nullTime(at),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *app) probeMediaStorage(ctx context.Context) componentProbe {
|
func (a *app) probeMediaStorage(ctx context.Context) componentProbe {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if a.minio == nil || a.cfg.MinioBucket == "" {
|
if a.minio == nil || a.cfg.MinioBucket == "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user