aboutsummaryrefslogtreecommitdiffstats
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/logger.go35
-rw-r--r--sql/sql.go37
-rw-r--r--sql/sql_test.go26
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 {
diff --git a/sql/sql.go b/sql/sql.go
index 5bc07e4..0ac86fa 100644
--- a/sql/sql.go
+++ b/sql/sql.go
@@ -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)
+ }
+}