89 lines
2.0 KiB
Go
89 lines
2.0 KiB
Go
|
package monitors
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
|
||
|
aireoneHttp "aireone.xyz/labtime/internal/http"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/prometheus/client_golang/prometheus"
|
||
|
)
|
||
|
|
||
|
var ErrInvalidStatusCode = errors.New("expected status code 200")
|
||
|
|
||
|
type HTTPMonitor struct {
|
||
|
Label string
|
||
|
URL string
|
||
|
|
||
|
Logger *log.Logger
|
||
|
|
||
|
ResponseTimeMonitor *prometheus.GaugeVec
|
||
|
}
|
||
|
|
||
|
func (h *HTTPMonitor) ID() string {
|
||
|
return h.Label
|
||
|
}
|
||
|
|
||
|
func (h *HTTPMonitor) Run() error {
|
||
|
d, err := h.httpHealthCheck()
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "error running http health check")
|
||
|
}
|
||
|
|
||
|
h.pushToPrometheus(d)
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func newHTTPDurationMiddleware(duration *time.Duration, proxied http.RoundTripper) *aireoneHttp.RoundTripperMiddleware {
|
||
|
var t time.Time
|
||
|
|
||
|
return &aireoneHttp.RoundTripperMiddleware{
|
||
|
Proxied: proxied,
|
||
|
OnBefore: func(_ *http.Request) {
|
||
|
t = time.Now()
|
||
|
},
|
||
|
OnAfter: func(_ *http.Response) {
|
||
|
*duration = time.Since(t)
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type HTTPHealthCheckerData struct {
|
||
|
Duration time.Duration
|
||
|
}
|
||
|
|
||
|
func (h *HTTPMonitor) httpHealthCheck() (*HTTPHealthCheckerData, error) {
|
||
|
r, err := http.NewRequest(http.MethodHead, h.URL, http.NoBody)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "error creating http request")
|
||
|
}
|
||
|
req := r.WithContext(context.TODO())
|
||
|
|
||
|
var duration time.Duration
|
||
|
client := &http.Client{
|
||
|
Transport: aireoneHttp.NewLoggerMiddleware(h.Logger, newHTTPDurationMiddleware(&duration, http.DefaultTransport)),
|
||
|
}
|
||
|
resp, err := client.Do(req)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
if resp.StatusCode != http.StatusOK {
|
||
|
return nil, errors.Wrap(ErrInvalidStatusCode, fmt.Sprintf("got status code %d", resp.StatusCode))
|
||
|
}
|
||
|
|
||
|
return &HTTPHealthCheckerData{
|
||
|
Duration: duration,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (h *HTTPMonitor) pushToPrometheus(d *HTTPHealthCheckerData) {
|
||
|
h.Logger.Printf("Push metrics to Prometheus: Response time for %s: %v", h.Label, d.Duration.Seconds())
|
||
|
h.ResponseTimeMonitor.With(prometheus.Labels{"target_name": h.Label}).Set(d.Duration.Seconds())
|
||
|
}
|