labtime/internal/monitors/httpmonitor.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())
}