-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
121 lines (107 loc) · 3.46 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
import (
"fmt"
"log"
"math/rand"
"net/http"
"os"
"os/exec"
"strings"
"sync/atomic"
"time"
"github.com/alexflint/go-arg"
"github.com/beykansen/internet-speed-monitor/pkg"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
const namespace = "internet_speed_monitor"
const fatalErrorCountThreshold = 5
var (
errorCount uint64
hostname = getHostName()
downloadSpeed = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "download_speed",
}, []string{"host"})
uploadSpeed = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "upload_speed",
}, []string{"host"})
jitter = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "jitter",
}, []string{"host"})
latency = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: namespace,
Name: "latency",
}, []string{"host"})
)
var args struct {
Port int `arg:"required, env:PORT, -p,--port" help:"port to listen"`
CallbackUrl string `arg:"env:CALLBACK, -c,--callback" help:"callback url"`
Interval int `arg:"required, env:INTERVAL, -i,--interval" help:"interval as minutes"`
}
func main() {
arg.MustParse(&args)
log.Printf("Server started on :%d. Callback url: %s Interval: %d\n", args.Port, args.CallbackUrl, args.Interval)
go collectMetrics()
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/metrics", 301)
})
http.Handle("/metrics", promhttp.Handler())
err := http.ListenAndServe(fmt.Sprintf(":%d", args.Port), nil)
if err != nil {
log.Fatalln(err)
}
}
func collectMetrics() {
iteration := func() {
if result, err := runSpeedTest(); err != nil {
atomic.AddUint64(&errorCount, 1)
if errorCount > fatalErrorCountThreshold {
log.Fatalf("Got error while runnig speedtest and threshold exceeded. Terminating... %s \n", err.Error())
}
log.Printf("Got error while runnig speedtest %s \n", err.Error())
} else {
downloadSpeed.WithLabelValues(hostname).Set(result.DownloadSpeed)
uploadSpeed.WithLabelValues(hostname).Set(result.UploadSpeed)
jitter.WithLabelValues(hostname).Set(result.Jitter)
latency.WithLabelValues(hostname).Set(result.Latency)
if err := pkg.Callback(args.CallbackUrl, result); err != nil {
log.Printf("Got error while callback %s\n", err.Error())
}
}
}
for {
iteration()
rand.Seed(time.Now().UnixNano())
time.Sleep(time.Duration(args.Interval)*time.Minute + time.Duration(rand.Intn(120))*time.Second)
}
}
func runSpeedTest() (*pkg.Result, error) {
speedTestPath := "/usr/bin/speedtest"
if output, err := exec.Command("which", "speedtest").Output(); err != nil {
return nil, err
} else {
speedTestPath = strings.ReplaceAll(string(output), "\n", "")
speedTestPath = strings.ReplaceAll(speedTestPath, "\r\n", "")
}
output, err := exec.Command(speedTestPath, "--accept-license", "--accept-gdpr", "--f", "json-pretty").Output()
if err != nil {
return nil, err
}
result, err := pkg.FromBytesToResult(output)
if err != nil {
return nil, err
}
log.Printf("Speed Test Finished. Download Speed: %fmbps Upload Speed: %fmbps Jitter: %fms Latency: %fms\n", result.DownloadSpeed, result.UploadSpeed, result.Jitter, result.Latency)
return result, nil
}
func getHostName() string {
hostname, err := os.Hostname()
if err != nil {
log.Fatalln(err)
}
return hostname
}