syncthing/cmd/discosrv/stats.go

142 lines
3.0 KiB
Go
Raw Normal View History

2015-03-25 07:16:52 +00:00
// Copyright (C) 2014-2015 Jakob Borg and Contributors (see the CONTRIBUTORS file).
package main
import (
2015-05-31 11:31:28 +00:00
"bytes"
"database/sql"
"fmt"
"io/ioutil"
2015-03-25 07:16:52 +00:00
"log"
2015-05-31 11:31:28 +00:00
"os"
"sync/atomic"
2015-03-25 07:16:52 +00:00
"time"
)
type stats struct {
// Incremented atomically
2015-03-25 07:16:52 +00:00
announces int64
queries int64
answers int64
errors int64
}
func (s *stats) Announce() {
atomic.AddInt64(&s.announces, 1)
2015-03-25 07:16:52 +00:00
}
func (s *stats) Query() {
atomic.AddInt64(&s.queries, 1)
2015-03-25 07:16:52 +00:00
}
func (s *stats) Answer() {
atomic.AddInt64(&s.answers, 1)
2015-03-25 07:16:52 +00:00
}
func (s *stats) Error() {
atomic.AddInt64(&s.errors, 1)
2015-03-25 07:16:52 +00:00
}
// Reset returns a copy of the current stats and resets the counters to
// zero.
2015-03-25 07:16:52 +00:00
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
2015-03-25 07:16:52 +00:00
}
type statssrv struct {
intv time.Duration
2015-05-31 11:31:28 +00:00
file string
db *sql.DB
2015-03-25 07:16:52 +00:00
}
func (s *statssrv) Serve() {
lastReset := time.Now()
2015-03-25 07:16:52 +00:00
for {
time.Sleep(next(s.intv))
stats := globalStats.Reset()
d := time.Since(lastReset).Seconds()
lastReset = time.Now()
2015-03-25 07:16:52 +00:00
log.Printf("Stats: %.02f announces/s, %.02f queries/s, %.02f answers/s, %.02f errors/s",
2015-05-31 11:31:28 +00:00
float64(stats.announces)/d, float64(stats.queries)/d, float64(stats.answers)/d, float64(stats.errors)/d)
if s.file != "" {
s.writeToFile(stats, d)
}
2015-03-25 07:16:52 +00:00
}
}
func (s *statssrv) Stop() {
panic("stop unimplemented")
}
2015-05-31 11:31:28 +00:00
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
}
2016-03-21 00:07:51 +00:00
defer func() {
err = fd.Close()
if err != nil {
log.Println("stats file:", err)
}
}()
2015-05-31 11:31:28 +00:00
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
}
}