diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/logger.go | 35 | ||||
-rw-r--r-- | sql/sql.go | 37 | ||||
-rw-r--r-- | sql/sql_test.go | 26 |
3 files changed, 98 insertions, 0 deletions
diff --git a/sql/logger.go b/sql/logger.go index 0c2fd6a..85e9152 100644 --- a/sql/logger.go +++ b/sql/logger.go @@ -35,6 +35,20 @@ type LogEntry struct { Answers []string } +// LogStats contains log statistics. +type LogStats struct { + Since time.Time + Total int64 + Hijacked int64 + Events []LogEvent +} + +// LogEvent contains the number of requests at a point in time. +type LogEvent struct { + Time time.Time + Count int64 +} + // NewLogger creates a new logger. Persisted entries are kept according to ttl. func NewLogger(client *Client, mode int, ttl time.Duration) *Logger { l := &Logger{ @@ -103,6 +117,27 @@ func (l *Logger) Read(n int) ([]LogEntry, error) { return logEntries, nil } +// Stats returns logger statistics. +func (l *Logger) Stats() (LogStats, error) { + stats, err := l.client.readLogStats() + if err != nil { + return LogStats{}, err + } + events := make([]LogEvent, 0, len(stats.Events)) + for _, le := range stats.Events { + events = append(events, LogEvent{ + Time: time.Unix(le.Time, 0).UTC(), + Count: le.Count, + }) + } + return LogStats{ + Since: time.Unix(stats.Since, 0).UTC(), + Total: stats.Total, + Hijacked: stats.Hijacked, + Events: events, + }, nil +} + func (l *Logger) readQueue(ttl time.Duration) { for e := range l.queue { if err := l.client.writeLog(e.Time, e.RemoteAddr, e.Hijacked, e.Qtype, e.Question, e.Answers...); err != nil { @@ -78,6 +78,18 @@ type logEntry struct { Answer string `db:"answer"` } +type logStats struct { + Since int64 `db:"since"` + Hijacked int64 `db:"hijacked"` + Total int64 `db:"total"` + Events []logEvent +} + +type logEvent struct { + Time int64 `db:"time"` + Count int64 `db:"count"` +} + type cacheEntry struct { Key uint32 `db:"key"` Data string `db:"data"` @@ -227,6 +239,31 @@ func (c *Client) deleteLogBefore(t time.Time) (err error) { return tx.Commit() } +func (c *Client) readLogStats() (logStats, error) { + c.mu.RLock() + defer c.mu.RUnlock() + var stats logStats + q1 := `SELECT COUNT(*) as total, + COUNT(CASE hijacked WHEN 1 THEN 1 ELSE NULL END) as hijacked, + time AS since + FROM log + ORDER BY time ASC LIMIT 1` + if err := c.db.Get(&stats, q1); err != nil { + return logStats{}, err + } + var events []logEvent + q2 := `SELECT time, + COUNT(*) AS count + FROM log + GROUP BY time + ORDER BY time ASC` + if err := c.db.Select(&events, q2); err != nil { + return logStats{}, err + } + stats.Events = events + return stats, nil +} + func (c *Client) writeCacheValue(key uint32, data string) error { c.mu.Lock() defer c.mu.Unlock() diff --git a/sql/sql_test.go b/sql/sql_test.go index 6f8bbbe..5c03c2d 100644 --- a/sql/sql_test.go +++ b/sql/sql_test.go @@ -182,3 +182,29 @@ func TestInterleavedRW(t *testing.T) { t.Fatal(err) } } + +func TestReadLogStats(t *testing.T) { + c := testClient() + writeTests(c, t) + got, err := c.readLogStats() + if err != nil { + t.Fatal(err) + } + want := logStats{ + Since: 1560636910, + Hijacked: 1, + Total: 8, + Events: []logEvent{ + {Time: 1560636910, Count: 1}, + {Time: 1560636980, Count: 1}, + {Time: 1560637050, Count: 1}, + {Time: 1560637120, Count: 1}, + {Time: 1560639880, Count: 1}, + {Time: 1560641700, Count: 2}, + {Time: 1560647100, Count: 1}, + }, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("readLogStats() = (%+v, _), want (%+v, _)", got, want) + } +} |