2021-06-09 06:03:31 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-09-08 09:08:51 +02:00
|
|
|
"context"
|
2021-06-09 06:03:31 +02:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os"
|
2021-08-20 11:45:34 +02:00
|
|
|
"os/signal"
|
2021-09-07 09:28:41 +02:00
|
|
|
"runtime"
|
2021-08-20 12:54:11 +02:00
|
|
|
"sync"
|
2021-08-20 11:45:34 +02:00
|
|
|
"syscall"
|
2021-08-31 15:17:36 +02:00
|
|
|
"time"
|
2021-06-09 06:03:31 +02:00
|
|
|
)
|
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
type MetricConfig struct {
|
|
|
|
Frequency int64 `json:"frequency"`
|
|
|
|
Aggregation string `json:"aggregation"`
|
|
|
|
Scope string `json:"scope"`
|
2021-06-09 06:03:31 +02:00
|
|
|
}
|
|
|
|
|
2021-08-20 11:45:34 +02:00
|
|
|
type Config struct {
|
2021-08-31 15:17:36 +02:00
|
|
|
Metrics map[string]MetricConfig `json:"metrics"`
|
2021-09-07 09:28:41 +02:00
|
|
|
RetentionHours int `json:"retention-hours"`
|
2021-08-31 15:17:36 +02:00
|
|
|
RestoreLastHours int `json:"restore-last-hours"`
|
|
|
|
CheckpointIntervalHours int `json:"checkpoint-interval-hours"`
|
|
|
|
ArchiveRoot string `json:"archive-root"`
|
|
|
|
Nats string `json:"nats"`
|
2021-06-09 06:03:31 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
var conf Config
|
|
|
|
var memoryStore *MemoryStore = nil
|
2021-09-07 09:28:41 +02:00
|
|
|
var lastCheckpoint time.Time
|
2021-06-09 06:03:31 +02:00
|
|
|
|
|
|
|
func loadConfiguration(file string) Config {
|
|
|
|
var config Config
|
|
|
|
configFile, err := os.Open(file)
|
|
|
|
defer configFile.Close()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err.Error())
|
|
|
|
}
|
|
|
|
jsonParser := json.NewDecoder(configFile)
|
|
|
|
jsonParser.Decode(&config)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
func handleLine(line *Line) {
|
2021-08-20 11:45:34 +02:00
|
|
|
cluster, ok := line.Tags["cluster"]
|
|
|
|
if !ok {
|
2021-08-31 15:17:36 +02:00
|
|
|
log.Println("'cluster' tag missing")
|
|
|
|
return
|
2021-08-20 11:45:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
host, ok := line.Tags["host"]
|
|
|
|
if !ok {
|
2021-08-31 15:17:36 +02:00
|
|
|
log.Println("'host' tag missing")
|
2021-08-24 10:40:26 +02:00
|
|
|
return
|
|
|
|
}
|
2021-08-20 11:45:34 +02:00
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
selector := []string{cluster, host}
|
|
|
|
if id, ok := line.Tags[line.Measurement]; ok {
|
2021-09-08 12:26:22 +02:00
|
|
|
selector = append(selector, line.Measurement+id)
|
2021-06-09 06:03:31 +02:00
|
|
|
}
|
2021-08-20 11:45:34 +02:00
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
ts := line.Ts.Unix()
|
2021-09-08 09:08:51 +02:00
|
|
|
// log.Printf("ts=%d, tags=%v\n", ts, selector)
|
2021-08-31 15:17:36 +02:00
|
|
|
err := memoryStore.Write(selector, ts, line.Fields)
|
2021-08-24 10:40:26 +02:00
|
|
|
if err != nil {
|
2021-08-31 15:17:36 +02:00
|
|
|
log.Printf("error: %s\n", err.Error())
|
2021-08-24 10:40:26 +02:00
|
|
|
}
|
2021-08-20 11:45:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2021-08-31 15:17:36 +02:00
|
|
|
startupTime := time.Now()
|
2021-08-20 11:45:34 +02:00
|
|
|
conf = loadConfiguration("config.json")
|
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
memoryStore = NewMemoryStore(conf.Metrics)
|
|
|
|
|
|
|
|
if conf.ArchiveRoot != "" && conf.RestoreLastHours > 0 {
|
|
|
|
d := time.Duration(conf.RestoreLastHours) * time.Hour
|
|
|
|
from := startupTime.Add(-d).Unix()
|
|
|
|
log.Printf("Restoring data since %d from '%s'...\n", from, conf.ArchiveRoot)
|
|
|
|
files, err := memoryStore.FromArchive(conf.ArchiveRoot, from)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Loading archive failed: %s\n", err.Error())
|
|
|
|
} else {
|
|
|
|
log.Printf("Archive loaded (%d files)\n", files)
|
|
|
|
}
|
2021-06-09 06:03:31 +02:00
|
|
|
}
|
|
|
|
|
2021-09-08 09:08:51 +02:00
|
|
|
ctx, shutdown := context.WithCancel(context.Background())
|
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
var wg sync.WaitGroup
|
2021-08-20 11:45:34 +02:00
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
|
|
_ = <-sigs
|
2021-08-31 15:17:36 +02:00
|
|
|
log.Println("Shuting down...")
|
2021-09-08 09:08:51 +02:00
|
|
|
shutdown()
|
2021-08-20 12:54:11 +02:00
|
|
|
}()
|
|
|
|
|
2021-09-07 09:28:41 +02:00
|
|
|
lastCheckpoint = startupTime
|
2021-08-31 15:17:36 +02:00
|
|
|
if conf.ArchiveRoot != "" && conf.CheckpointIntervalHours > 0 {
|
|
|
|
wg.Add(3)
|
|
|
|
go func() {
|
|
|
|
d := time.Duration(conf.CheckpointIntervalHours) * time.Hour
|
|
|
|
ticks := time.Tick(d)
|
|
|
|
for {
|
|
|
|
select {
|
2021-09-08 09:08:51 +02:00
|
|
|
case <-ctx.Done():
|
2021-08-31 15:17:36 +02:00
|
|
|
wg.Done()
|
|
|
|
return
|
|
|
|
case <-ticks:
|
|
|
|
log.Println("Start making checkpoint...")
|
2021-09-07 09:28:41 +02:00
|
|
|
now := time.Now()
|
|
|
|
n, err := memoryStore.ToArchive(conf.ArchiveRoot, lastCheckpoint.Unix(), now.Unix())
|
2021-08-31 15:17:36 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Making checkpoint failed: %s\n", err.Error())
|
|
|
|
} else {
|
2021-09-07 09:28:41 +02:00
|
|
|
log.Printf("Checkpoint successfull (%d files written)\n", n)
|
|
|
|
}
|
|
|
|
lastCheckpoint = now
|
|
|
|
|
|
|
|
if conf.RetentionHours > 0 {
|
|
|
|
log.Println("Freeing up memory...")
|
|
|
|
t := now.Add(-time.Duration(conf.RetentionHours) * time.Hour)
|
2021-09-08 12:17:10 +02:00
|
|
|
freed, err := memoryStore.Free(Selector{}, t.Unix())
|
2021-09-07 09:28:41 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("Freeing up memory failed: %s\n", err.Error())
|
|
|
|
}
|
|
|
|
log.Printf("%d values freed\n", freed)
|
2021-08-31 15:17:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
} else {
|
|
|
|
wg.Add(2)
|
|
|
|
}
|
2021-08-20 12:54:11 +02:00
|
|
|
|
|
|
|
go func() {
|
2021-09-08 09:08:51 +02:00
|
|
|
err := StartApiServer(":8080", ctx)
|
2021-08-31 15:17:36 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2021-08-20 12:54:11 +02:00
|
|
|
wg.Done()
|
2021-08-20 11:45:34 +02:00
|
|
|
}()
|
|
|
|
|
2021-08-31 15:17:36 +02:00
|
|
|
go func() {
|
2021-09-08 09:08:51 +02:00
|
|
|
err := ReceiveNats(conf.Nats, handleLine, runtime.NumCPU()-1, ctx)
|
2021-08-31 15:17:36 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
2021-08-20 12:54:11 +02:00
|
|
|
|
|
|
|
wg.Wait()
|
2021-08-31 15:17:36 +02:00
|
|
|
|
|
|
|
if conf.ArchiveRoot != "" {
|
|
|
|
log.Printf("Writing to '%s'...\n", conf.ArchiveRoot)
|
|
|
|
files, err := memoryStore.ToArchive(conf.ArchiveRoot, lastCheckpoint.Unix(), time.Now().Unix())
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Writing to archive failed: %s\n", err.Error())
|
|
|
|
}
|
|
|
|
log.Printf("Done! (%d files written)\n", files)
|
|
|
|
}
|
2021-06-09 06:03:31 +02:00
|
|
|
}
|