Add page operation metrics and expose them in Prometheus text format.

This commit is contained in:
miyuko
2025-09-22 14:33:25 +01:00
parent 584957a92d
commit 1aef0288e7
7 changed files with 79 additions and 9 deletions

View File

@@ -8,6 +8,7 @@ log-format = "datetime+message"
pages = "tcp/:3000"
caddy = "tcp/:3001"
health = "tcp/:3002"
metrics = "tcp/:3003"
[[wildcard]] # non-default section
domain = "codeberg.page"

8
go.mod
View File

@@ -22,6 +22,8 @@ require (
dario.cat/mergo v1.0.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
@@ -36,11 +38,16 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/minio/crc64nvme v1.0.2 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
@@ -48,6 +55,7 @@ require (
github.com/tj/assert v0.0.3 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
golang.org/x/net v0.43.0 // indirect

16
go.sum
View File

@@ -10,8 +10,12 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4=
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
@@ -70,6 +74,8 @@ github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
@@ -84,6 +90,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
@@ -113,6 +127,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=

View File

@@ -45,9 +45,10 @@ type Config struct {
}
type ServerConfig struct {
Pages string `toml:"pages" default:"tcp/:3000"`
Caddy string `toml:"caddy" default:"tcp/:3001"`
Health string `toml:"health" default:"tcp/:3002"`
Pages string `toml:"pages" default:"tcp/:3000"`
Caddy string `toml:"caddy" default:"tcp/:3001"`
Health string `toml:"health" default:"tcp/:3002"`
Metrics string `toml:"metrics" default:"tcp/:3003"`
}
type WildcardConfig struct {

View File

@@ -50,10 +50,8 @@ func panicHandler(handler http.Handler) http.Handler {
})
}
func serve(listener net.Listener, serve func(http.ResponseWriter, *http.Request)) {
func serve(listener net.Listener, handler http.Handler) {
if listener != nil {
var handler http.Handler
handler = http.HandlerFunc(serve)
handler = ObserveHTTPHandler(handler)
handler = panicHandler(handler)
@@ -148,6 +146,7 @@ func main() {
pagesListener := listen("pages", config.Server.Pages)
caddyListener := listen("caddy", config.Server.Caddy)
healthListener := listen("health", config.Server.Health)
metricsListener := listen("metrics", config.Server.Metrics)
if err := ConfigureBackend(&config.Storage); err != nil {
log.Fatalln(err)
@@ -157,9 +156,10 @@ func main() {
log.Fatalln(err)
}
go serve(pagesListener, ServePages)
go serve(caddyListener, ServeCaddy)
go serve(healthListener, ServeHealth)
go serve(pagesListener, http.HandlerFunc(ServePages))
go serve(caddyListener, http.HandlerFunc(ServeCaddy))
go serve(healthListener, http.HandlerFunc(ServeHealth))
go serve(metricsListener, NewMetricsHTTPHandler())
if config.Insecure {
log.Println("serve: ready (INSECURE)")

View File

@@ -6,6 +6,7 @@ import (
"runtime/debug"
"github.com/honeybadger-io/honeybadger-go"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func hasHoneybadger() bool {
@@ -25,3 +26,7 @@ func ObserveHTTPHandler(handler http.Handler) http.Handler {
}
return handler
}
func NewMetricsHTTPHandler() http.Handler {
return promhttp.Handler()
}

View File

@@ -14,14 +14,51 @@ import (
"path"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
const notFoundPage = "404.html"
var (
siteUpdatesCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "git_pages_site_updates",
Help: "Count of site updates in total",
}, []string{"via"})
siteUpdateOkCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "git_pages_site_update_ok",
Help: "Count of successful site updates",
}, []string{"outcome"})
siteUpdateErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "git_pages_site_update_error",
Help: "Count of failed site updates",
}, []string{"cause"})
)
func makeWebRoot(host string, projectName string) string {
return fmt.Sprintf("%s/%s", strings.ToLower(host), projectName)
}
func reportSiteUpdate(via string, result *UpdateResult) {
siteUpdatesCount.With(prometheus.Labels{"via": via}).Inc()
switch result.outcome {
case UpdateError:
siteUpdateErrorCount.With(prometheus.Labels{"cause": "other"}).Inc()
case UpdateTimeout:
siteUpdateErrorCount.With(prometheus.Labels{"cause": "timeout"}).Inc()
case UpdateNoChange:
// nothing to report
case UpdateCreated:
siteUpdateOkCount.With(prometheus.Labels{"outcome": "created"}).Inc()
case UpdateReplaced:
siteUpdateOkCount.With(prometheus.Labels{"outcome": "replaced"}).Inc()
case UpdateDeleted:
siteUpdateOkCount.With(prometheus.Labels{"outcome": "deleted"}).Inc()
}
}
func getPage(w http.ResponseWriter, r *http.Request) error {
var err error
var sitePath string
@@ -266,6 +303,7 @@ func putPage(w http.ResponseWriter, r *http.Request) error {
} else {
fmt.Fprintln(w, "internal error")
}
reportSiteUpdate("rest", &result)
return nil
}
@@ -401,6 +439,7 @@ func postPage(w http.ResponseWriter, r *http.Request) error {
fmt.Fprintf(w, "- %s\n", problem)
}
}
reportSiteUpdate("webhook", &result)
return nil
}