diff --git a/cspell.json b/.cspell.json similarity index 100% rename from cspell.json rename to .cspell.json diff --git a/.editorconfig b/.editorconfig index 1a5bfac..2791264 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,7 +9,7 @@ indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = false -insert_final_newline = false +insert_final_newline = true [*.{json,yaml}] -indent_size = 2 \ No newline at end of file +indent_size = 2 diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..8d05e48 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,3 @@ +--- +run: + timeout: 5m diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9d304f4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "[go]": { + "editor.codeActionsOnSave": { + // "source.organizeImports": "always" + }, + "editor.formatOnSave": true + }, + "go.formatTool": "goimports", + "go.lintTool": "golangci-lint" + // "go.lintFlags": ["--fast"] // We only have the default linters and it seems they are not marked as fast, but it's fast enough for me +} diff --git a/main.go b/main.go index 531579c..d844ae6 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,8 @@ type Config struct { Targets []struct { // Name of the target. Used to identify the target from Prometheus. Name string `yaml:"name"` - // URL of the target. The target should be accessible from the machine running the exporter. The URL should contain the protocol (http:// or https://) and the port if it's not the default one. + // URL of the target. The target should be accessible from the machine running the exporter. + // The URL should contain the protocol (http:// or https://) and the port if it's not the default one. Url string `yaml:"url"` // Interval to ping the target. Default is 5 seconds Interval int `yaml:"interval,omitempty"` @@ -51,11 +52,13 @@ func NewConfig(configPath string) (*Config, error) { } func Ping(url string) (int, time.Duration, error) { - req, err := http.NewRequest("HEAD", url, nil) + req, err := http.NewRequest(http.MethodHead, url, nil) if err != nil { return 0, 0, err } + var start, connect, dns, tlsHandshake time.Time + trace := &httptrace.ClientTrace{ DNSStart: func(dsi httptrace.DNSStartInfo) { dns = time.Now() }, DNSDone: func(ddi httptrace.DNSDoneInfo) { @@ -76,20 +79,23 @@ func Ping(url string) (int, time.Duration, error) { fmt.Printf("Time from start to first byte: %v\n", time.Since(start)) }, } + req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) start = time.Now() + resp, err := http.DefaultTransport.RoundTrip(req) if err != nil { return 0, 0, err } - resp.Body.Close() + + defer resp.Body.Close() + return resp.StatusCode, time.Since(start), nil } func main() { - // Load config - // TODO: Use a flag to specify the config file + // TODO: Use a flag to specify the config file config, err := NewConfig("config/config.yaml") if err != nil { log.Fatalf("Error loading config: %s", err) @@ -100,7 +106,7 @@ func main() { // create a scheduler s, err := gocron.NewScheduler() if err != nil { - // handle error + log.Fatalf("Error creating the scheduler: %s",err) } // Prometheus metrics @@ -112,7 +118,7 @@ func main() { // Intercept the signal to stop the program go func() { - sigchan := make(chan os.Signal) + sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, os.Interrupt) <-sigchan log.Println("Program killed !") @@ -122,7 +128,7 @@ func main() { // when you're done, shut it down err = s.Shutdown() if err != nil { - // handle error + log.Fatalf("Error shutting down scheduler: %s", err) } os.Exit(0) @@ -134,8 +140,11 @@ func main() { gocron.DurationJob( func(interval int) time.Duration { if interval == 0 { - return time.Duration(5) * time.Second + const defaultInterval = 5 + + return time.Duration(defaultInterval) * time.Second } + return time.Duration(interval) * time.Second }(target.Interval), ), @@ -144,8 +153,10 @@ func main() { status, elapsedTime, err := Ping(url) if err != nil { fmt.Println(err) + return } + fmt.Printf("%s - Status: %d in %v\n", target.Name, status, elapsedTime.Seconds()) // push to Prometheus responseTimeMonitor.With(prometheus.Labels{"target_name": target.Name}).Set(elapsedTime.Seconds()) @@ -154,7 +165,7 @@ func main() { ), ) if err != nil { - // handle error + log.Fatalf("Error creating job: %s", err) } // each job has a unique id fmt.Printf("Job %s started with ID: %s\n", target.Name, j.ID().String()) @@ -163,12 +174,9 @@ func main() { // start the scheduler s.Start() - // // block until you are ready to shut down - // select { - // // case <-time.After(time.Minute): - // } - // Serve Prometheus metrics http.Handle("/metrics", promhttp.Handler()) - http.ListenAndServe(":2112", nil) + if err := http.ListenAndServe(":2112", nil); err != nil { + log.Fatalf("Error starting server: %s", err) + } }