diff --git a/cmd/goupkeep/main.go b/cmd/goupkeep/main.go index 38dde72..adec38f 100644 --- a/cmd/goupkeep/main.go +++ b/cmd/goupkeep/main.go @@ -260,6 +260,7 @@ func runServe(args []string) { defer cancel() eng.InitHistory() + eng.InitLogs() eng.Start(ctx) server.Start(server.ServerConfig{ diff --git a/internal/metrics/prometheus_test.go b/internal/metrics/prometheus_test.go index cd86d26..1e2b72b 100644 --- a/internal/metrics/prometheus_test.go +++ b/internal/metrics/prometheus_test.go @@ -50,6 +50,8 @@ func (m *mockStore) GetNode(string) (models.ProbeNode, error) { return m func (m *mockStore) GetAllNodes() ([]models.ProbeNode, error) { return nil, nil } func (m *mockStore) UpdateNodeLastSeen(string) error { return nil } func (m *mockStore) DeleteNode(string) error { return nil } +func (m *mockStore) SaveLog(string) error { return nil } +func (m *mockStore) LoadLogs(int) ([]string, error) { return nil, nil } func TestMetricsHandler(t *testing.T) { ms := &mockStore{ diff --git a/internal/monitor/monitor.go b/internal/monitor/monitor.go index 8e55d48..07a0f41 100644 --- a/internal/monitor/monitor.go +++ b/internal/monitor/monitor.go @@ -68,6 +68,20 @@ func (e *Engine) AddLog(msg string) { if len(e.logStore) > 100 { e.logStore = e.logStore[:100] } + go func() { _ = e.db.SaveLog(entry) }() +} + +func (e *Engine) InitLogs() { + logs, err := e.db.LoadLogs(100) + if err != nil { + return + } + if len(logs) == 0 { + return + } + e.logMu.Lock() + defer e.logMu.Unlock() + e.logStore = logs } func (e *Engine) GetLogs() []string { diff --git a/internal/store/postgres.go b/internal/store/postgres.go index d6e6dbd..f8b5abc 100644 --- a/internal/store/postgres.go +++ b/internal/store/postgres.go @@ -51,6 +51,11 @@ func (d *PostgresDialect) CreateTablesSQL() []string { last_seen TIMESTAMP DEFAULT NOW(), version TEXT DEFAULT '' )`, + `CREATE TABLE IF NOT EXISTS logs ( + id SERIAL PRIMARY KEY, + message TEXT NOT NULL, + created_at TIMESTAMP DEFAULT NOW() + )`, } } diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index be7ba1d..ea2cacc 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -51,6 +51,11 @@ func (d *SQLiteDialect) CreateTablesSQL() []string { last_seen DATETIME DEFAULT CURRENT_TIMESTAMP, version TEXT DEFAULT '' )`, + `CREATE TABLE IF NOT EXISTS logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + message TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + )`, } } diff --git a/internal/store/sqlstore.go b/internal/store/sqlstore.go index e8b554a..294fd19 100644 --- a/internal/store/sqlstore.go +++ b/internal/store/sqlstore.go @@ -300,6 +300,34 @@ func (s *SQLStore) DeleteNode(id string) error { return err } +func (s *SQLStore) SaveLog(message string) error { + _, err := s.db.Exec(s.q("INSERT INTO logs (message) VALUES (?)"), message) + if err != nil { + return err + } + _, err = s.db.Exec(s.q(`DELETE FROM logs WHERE id NOT IN ( + SELECT id FROM logs ORDER BY created_at DESC LIMIT 200 + )`)) + return err +} + +func (s *SQLStore) LoadLogs(limit int) ([]string, error) { + rows, err := s.db.Query(s.q("SELECT message FROM logs ORDER BY created_at DESC LIMIT ?"), limit) + if err != nil { + return nil, err + } + defer rows.Close() + var logs []string + for rows.Next() { + var msg string + if err := rows.Scan(&msg); err != nil { + return logs, err + } + logs = append(logs, msg) + } + return logs, rows.Err() +} + func (s *SQLStore) LoadAllHistory(limit int) (map[int][]models.CheckRecord, error) { result := make(map[int][]models.CheckRecord) rows, err := s.db.Query(s.q(` diff --git a/internal/store/store.go b/internal/store/store.go index 1340326..fe96e37 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -45,6 +45,10 @@ type Store interface { UpdateNodeLastSeen(id string) error DeleteNode(id string) error + // Logs + SaveLog(message string) error + LoadLogs(limit int) ([]string, error) + // Backup & Restore ExportData() (models.Backup, error) ImportData(data models.Backup) error