2024-01-04 20:27:46 +10:30

134 lines
3.7 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"log"
"log/slog"
"net/http"
"os"
"github.com/nxadm/tail"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
type CaddyLogline struct {
Level string `json:"level"`
Ts float64 `json:"ts"`
Logger string `json:"logger"`
Msg string `json:"msg"`
Request struct {
RemoteIP string `json:"remote_ip"`
RemotePort string `json:"remote_port"`
ClientIP string `json:"client_ip"`
Proto string `json:"proto"`
Method string `json:"method"`
Host string `json:"host"`
URI string `json:"uri"`
Headers struct {
SecFetchSite []string `json:"Sec-Fetch-Site"`
Accept []string `json:"Accept"`
AcceptLanguage []string `json:"Accept-Language"`
Connection []string `json:"Connection"`
UpgradeInsecureRequests []string `json:"Upgrade-Insecure-Requests"`
SecFetchMode []string `json:"Sec-Fetch-Mode"`
UserAgent []string `json:"User-Agent"`
SecFetchDest []string `json:"Sec-Fetch-Dest"`
AcceptEncoding []string `json:"Accept-Encoding"`
} `json:"headers"`
} `json:"request"`
BytesRead int `json:"bytes_read"`
UserID string `json:"user_id"`
Duration float64 `json:"duration"`
Size int `json:"size"`
Status int `json:"status"`
RespHeaders struct {
Server []string `json:"Server"`
Etag []string `json:"Etag"`
ContentType []string `json:"Content-Type"`
LastModified []string `json:"Last-Modified"`
AcceptRanges []string `json:"Accept-Ranges"`
ContentLength []string `json:"Content-Length"`
} `json:"resp_headers"`
}
func main() {
requestsCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "",
Subsystem: "",
Name: "requests",
Help: "",
ConstLabels: map[string]string{},
}, []string{"method", "status_code", "host"})
requestsDuration := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "",
Subsystem: "",
Name: "requests_duration",
Help: "",
ConstLabels: map[string]string{},
}, []string{"method", "status_code", "host"})
requestsSize := prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "",
Subsystem: "",
Name: "requests_size",
Help: "",
ConstLabels: map[string]string{},
}, []string{"method", "status_code", "host"})
prometheus.MustRegister(requestsCounter, requestsDuration, requestsSize)
http.Handle("/metrics", promhttp.Handler())
go func() {
log.Fatal(http.ListenAndServe("127.0.0.1:8191", nil))
}()
i := len(os.Args)
if i < 2 {
panic("need names")
}
agg := make(chan *tail.Line)
for _, fn := range os.Args[1:] {
slog.With("file", fn).Info("opening")
t, err := tail.TailFile(fn, tail.Config{
Follow: true,
ReOpen: true,
CompleteLines: true,
Location: &tail.SeekInfo{
Offset: 0,
Whence: io.SeekEnd,
},
})
if err != nil {
panic(err)
}
go func(c chan *tail.Line) {
for msg := range c {
agg <- msg
}
}(t.Lines)
}
for line := range agg {
js := CaddyLogline{}
err := json.Unmarshal([]byte(line.Text), &js)
if err != nil {
slog.With(err).Error("could not unmarshal")
continue
}
if js.Msg != "handled request" {
continue
}
requestsCounter.WithLabelValues(js.Request.Method, fmt.Sprint(js.Status), js.Request.Host).Inc()
requestsDuration.WithLabelValues(js.Request.Method, fmt.Sprint(js.Status), js.Request.Host).Add(js.Duration)
requestsSize.WithLabelValues(js.Request.Method, fmt.Sprint(js.Status), js.Request.Host).Add(float64(js.Size))
}
}