diff --git a/socketserver/cmd/statsweb/.gitignore b/socketserver/cmd/statsweb/.gitignore new file mode 100644 index 00000000..3dddabd8 --- /dev/null +++ b/socketserver/cmd/statsweb/.gitignore @@ -0,0 +1,3 @@ +database.sqlite +gobcache/ +statsweb diff --git a/socketserver/cmd/statsweb/config.go b/socketserver/cmd/statsweb/config.go new file mode 100644 index 00000000..ae882d38 --- /dev/null +++ b/socketserver/cmd/statsweb/config.go @@ -0,0 +1,74 @@ +package main + +import ( + "os" + "fmt" + "encoding/json" +) + +type ConfigFile struct { + ListenAddr string + DatabaseLocation string + GobFilesLocation string +} + +func makeConfig() { + config.ListenAddr = "localhost:3000" + home, ok := os.LookupEnv("HOME") + if ok { + config.DatabaseLocation = fmt.Sprintf("%s/.ffzstatsweb/database.sqlite", home) + config.GobFilesLocation = fmt.Sprintf("%s/.ffzstatsweb/gobcache", home) + os.MkdirAll(config.GobFilesLocation, 0644) + } else { + config.DatabaseLocation = "./database.sqlite" + config.GobFilesLocation = "./gobcache" + os.MkdirAll(config.GobFilesLocation, 0644) + } + file, err := os.Create(*configLocation) + if err != nil { + fmt.Printf("Error: could not create config file: %v\n", err) + os.Exit(ExitCodeBadConfig) + return + } + enc := json.NewEncoder(file) + err = enc.Encode(config) + if err != nil { + fmt.Printf("Error: could not write config file: %v\n", err) + os.Exit(ExitCodeBadConfig) + return + } + err = file.Close() + if err != nil { + fmt.Printf("Error: could not write config file: %v\n", err) + os.Exit(ExitCodeBadConfig) + return + } + return +} + +func loadConfig() { + file, err := os.Open(*configLocation) + if err != nil { + if os.IsNotExist(err) { + fmt.Println("You must create a config file with -genconf") + } else { + fmt.Printf("Error: could not load config file: %v", err) + } + os.Exit(ExitCodeBadConfig) + return + } + dec := json.NewDecoder(file) + err = dec.Decode(&config) + if err != nil { + fmt.Printf("Error: could not load config file: %v\n", err) + os.Exit(ExitCodeBadConfig) + return + } + err = file.Close() + if err != nil { + fmt.Printf("Error: could not load config file: %v\n", err) + os.Exit(ExitCodeBadConfig) + return + } + return +} \ No newline at end of file diff --git a/socketserver/cmd/statsweb/config.json b/socketserver/cmd/statsweb/config.json new file mode 100644 index 00000000..07cfa8e3 --- /dev/null +++ b/socketserver/cmd/statsweb/config.json @@ -0,0 +1 @@ +{"ListenAddr":"localhost:3000","DatabaseLocation":"./database.sqlite","GobFilesLocation":"./gobcache"} diff --git a/socketserver/cmd/statsweb/statsweb.go b/socketserver/cmd/statsweb/statsweb.go index 1110061a..683754a4 100644 --- a/socketserver/cmd/statsweb/statsweb.go +++ b/socketserver/cmd/statsweb/statsweb.go @@ -1,3 +1,40 @@ package main +import ( + "net/http" + "flag" + "github.com/clarkduvall/hyperloglog" + "time" + "bitbucket.org/stendec/frankerfacez/socketserver/server" +) +var configLocation = flag.String("config", "./config.json", "Location of the configuration file. Defaults to ./config.json") +var genConfig = flag.Bool("genconf", false, "Generate a new configuration file.") + +var config ConfigFile + +const ExitCodeBadConfig = 2 + +func main() { + flag.Parse() + + if *genConfig { + makeConfig() + return + } + + loadConfig() + + http.ListenAndServe(config.ListenAddr, http.DefaultServeMux) +} + +func combineDateRange(from time.Time, to time.Time, dest *hyperloglog.HyperLogLogPlus) error { + from = server.TruncateToMidnight(from) + to = server.TruncateToMidnight(to) + year, month, day := from.Date() + for current := from; current.Before(to); day = day + 1 { + current = time.Date(year, month, day, 0, 0, 0, 0, server.CounterLocation) + + } + return nil +} \ No newline at end of file diff --git a/socketserver/server/handlecore.go b/socketserver/server/handlecore.go index 301d8abc..ed0ae1f5 100644 --- a/socketserver/server/handlecore.go +++ b/socketserver/server/handlecore.go @@ -21,7 +21,7 @@ import ( "time" "unicode/utf8" - "./logstasher" + "bitbucket.org/stendec/frankerfacez/socketserver/server/logstasher" ) // SuccessCommand is a Reply Command to indicate success in reply to a C2S Command. diff --git a/socketserver/server/usercount.go b/socketserver/server/usercount.go index fb6e5ad9..d46316d0 100644 --- a/socketserver/server/usercount.go +++ b/socketserver/server/usercount.go @@ -43,13 +43,18 @@ var uniqueCounter PeriodUniqueUsers var uniqueUserChannel chan uuid.UUID var uniqueCtrWritingToken chan usageToken -var counterLocation *time.Location = time.FixedZone("UTC-5", int((time.Hour*-5)/time.Second)) +var CounterLocation *time.Location = time.FixedZone("UTC-5", int((time.Hour*-5)/time.Second)) + +func TruncateToMidnight(at time.Time) time.Time { + year, month, day := at.Date() + return time.Date(year, month, day, 0, 0, 0, 0, CounterLocation) +} // GetCounterPeriod calculates the start and end timestamps for the HLL measurement period that includes the 'at' timestamp. func GetCounterPeriod(at time.Time) (start time.Time, end time.Time) { year, month, day := at.Date() - start = time.Date(year, month, day, 0, 0, 0, 0, counterLocation) - end = time.Date(year, month, day+1, 0, 0, 0, 0, counterLocation) + start = time.Date(year, month, day, 0, 0, 0, 0, CounterLocation) + end = time.Date(year, month, day+1, 0, 0, 0, 0, CounterLocation) return start, end } @@ -143,7 +148,7 @@ func loadUniqueUsers() { log.Panicln("could not make unique users data dir:", err) } - now := time.Now().In(counterLocation) + now := time.Now().In(CounterLocation) uniqueCounter.Start, uniqueCounter.End = GetCounterPeriod(now) err = loadHLL(now, &uniqueCounter) isIgnorableError := err != nil && (false || @@ -194,9 +199,9 @@ func processNewUsers() { } func getNextMidnight() time.Time { - now := time.Now().In(counterLocation) + now := time.Now().In(CounterLocation) year, month, day := now.Date() - return time.Date(year, month, day+1, 0, 0, 1, 0, counterLocation) + return time.Date(year, month, day+1, 0, 0, 1, 0, CounterLocation) } // is_init_func @@ -214,7 +219,7 @@ func rolloverCounters_do() { var now time.Time token = <-uniqueCtrWritingToken - now = time.Now().In(counterLocation) + now = time.Now().In(CounterLocation) // Cycle for period err := writeHLL_do(&uniqueCounter) if err != nil { diff --git a/socketserver/server/usercount_test.go b/socketserver/server/usercount_test.go index c6ed2050..6c5a8d2f 100644 --- a/socketserver/server/usercount_test.go +++ b/socketserver/server/usercount_test.go @@ -12,7 +12,7 @@ import ( func TestUniqueConnections(t *testing.T) { const TestExpectedCount = 1000 - testStart := time.Now().In(counterLocation) + testStart := time.Now().In(CounterLocation) var server *httptest.Server var backendExpected = NewTBackendRequestChecker(t, @@ -35,7 +35,7 @@ func TestUniqueConnections(t *testing.T) { TCheckHLLValue(t, TestExpectedCount, readCurrentHLL()) token := <-uniqueCtrWritingToken - uniqueCounter.End = time.Now().In(counterLocation).Add(-1 * time.Second) + uniqueCounter.End = time.Now().In(CounterLocation).Add(-1 * time.Second) uniqueCtrWritingToken <- token rolloverCounters_do()