mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 10:35:51 +00:00
9572a113ae
Adds internal/alerts/eventlog: a SQLite-backed, additive event log that records lifecycle transitions (resolved, acknowledged, unacknowledged, escalated, flapping detected) and notification decisions (dispatched, deferred by quiet hours, suppressed — with the mechanism that held them). Appends never block alert evaluation: a full buffer drops the event and counts the drop; a store that fails to open degrades to recording nothing. 90-day retention, hourly prune. The manager emits at the existing funnels only — dispatchAlert and the safe-call resolve/ack/escalate seams — so no lifecycle behavior changes. Lifecycle "fired" is deliberately not recorded yet: the active-alert store funnel also runs on persisted restore, so firing waits for the explicit activation seam in a later phase. The monitoring bootstrap enables the log per manager; ephemeral managers and tests record nothing unless they opt in. GET /api/alerts/events (monitoring:read) reads the log with alertIdentifier/type/since/limit filters, newest first. Phase 0 of docs/ALERT_ENGINE_EVOLUTION.md (coverage gap alert-engine-suppression-observability).
44 lines
851 B
Go
44 lines
851 B
Go
package alerts
|
|
|
|
import (
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// Stop stops the alert manager and saves history
|
|
func (m *Manager) Stop() {
|
|
m.stopOnce.Do(func() {
|
|
m.stopMu.Lock()
|
|
m.stopping = true
|
|
closeSignalChannel(m.escalationStop)
|
|
closeSignalChannel(m.cleanupStop)
|
|
m.stopMu.Unlock()
|
|
|
|
m.workerWG.Wait()
|
|
|
|
if m.historyManager != nil {
|
|
m.historyManager.Stop()
|
|
}
|
|
|
|
// Save active alerts before stopping
|
|
if err := m.SaveActiveAlerts(); err != nil {
|
|
log.Error().Err(err).Msg("Failed to save active alerts on stop")
|
|
}
|
|
|
|
// Drain and close the event log last so shutdown-time transitions
|
|
// are still recorded.
|
|
m.SetEventLog(nil)
|
|
})
|
|
}
|
|
|
|
func closeSignalChannel(ch chan struct{}) {
|
|
if ch == nil {
|
|
return
|
|
}
|
|
defer func() {
|
|
if recover() != nil {
|
|
// Channel was already closed by another shutdown path.
|
|
}
|
|
}()
|
|
close(ch)
|
|
}
|