| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- // Copyright (C) 2014-2015 Jakob Borg and Contributors (see the CONTRIBUTORS file).
- package main
- import (
- "bytes"
- "database/sql"
- "fmt"
- "io/ioutil"
- "log"
- "os"
- "sync/atomic"
- "time"
- )
- type stats struct {
- // Incremented atomically
- announces int64
- queries int64
- answers int64
- errors int64
- }
- func (s *stats) Announce() {
- atomic.AddInt64(&s.announces, 1)
- }
- func (s *stats) Query() {
- atomic.AddInt64(&s.queries, 1)
- }
- func (s *stats) Answer() {
- atomic.AddInt64(&s.answers, 1)
- }
- func (s *stats) Error() {
- atomic.AddInt64(&s.errors, 1)
- }
- // Reset returns a copy of the current stats and resets the counters to
- // zero.
- func (s *stats) Reset() stats {
- // Create a copy of the stats using atomic reads
- copy := stats{
- announces: atomic.LoadInt64(&s.announces),
- queries: atomic.LoadInt64(&s.queries),
- answers: atomic.LoadInt64(&s.answers),
- errors: atomic.LoadInt64(&s.errors),
- }
- // Reset the stats by subtracting the values that we copied
- atomic.AddInt64(&s.announces, -copy.announces)
- atomic.AddInt64(&s.queries, -copy.queries)
- atomic.AddInt64(&s.answers, -copy.answers)
- atomic.AddInt64(&s.errors, -copy.errors)
- return copy
- }
- type statssrv struct {
- intv time.Duration
- file string
- db *sql.DB
- }
- func (s *statssrv) Serve() {
- lastReset := time.Now()
- for {
- time.Sleep(next(s.intv))
- stats := globalStats.Reset()
- d := time.Since(lastReset).Seconds()
- lastReset = time.Now()
- log.Printf("Stats: %.02f announces/s, %.02f queries/s, %.02f answers/s, %.02f errors/s",
- float64(stats.announces)/d, float64(stats.queries)/d, float64(stats.answers)/d, float64(stats.errors)/d)
- if s.file != "" {
- s.writeToFile(stats, d)
- }
- }
- }
- func (s *statssrv) Stop() {
- panic("stop unimplemented")
- }
- func (s *statssrv) writeToFile(stats stats, secs float64) {
- newLine := []byte("\n")
- var addrs int
- row := s.db.QueryRow("SELECT COUNT(*) FROM Addresses")
- if err := row.Scan(&addrs); err != nil {
- log.Println("stats query:", err)
- return
- }
- fd, err := os.OpenFile(s.file, os.O_RDWR|os.O_CREATE, 0666)
- if err != nil {
- log.Println("stats file:", err)
- return
- }
- defer func() {
- err = fd.Close()
- if err != nil {
- log.Println("stats file:", err)
- }
- }()
- bs, err := ioutil.ReadAll(fd)
- if err != nil {
- log.Println("stats file:", err)
- return
- }
- lines := bytes.Split(bytes.TrimSpace(bs), newLine)
- if len(lines) > 12 {
- lines = lines[len(lines)-12:]
- }
- latest := fmt.Sprintf("%v: %6d addresses, %8.02f announces/s, %8.02f queries/s, %8.02f answers/s, %8.02f errors/s\n",
- time.Now().UTC().Format(time.RFC3339), addrs,
- float64(stats.announces)/secs, float64(stats.queries)/secs, float64(stats.answers)/secs, float64(stats.errors)/secs)
- lines = append(lines, []byte(latest))
- _, err = fd.Seek(0, 0)
- if err != nil {
- log.Println("stats file:", err)
- return
- }
- err = fd.Truncate(0)
- if err != nil {
- log.Println("stats file:", err)
- return
- }
- _, err = fd.Write(bytes.Join(lines, newLine))
- if err != nil {
- log.Println("stats file:", err)
- return
- }
- }
|