From 960a40d7367dd61c3cdc9164a75a4d6cd4e57904 Mon Sep 17 00:00:00 2001 From: Catherine Date: Sat, 20 Sep 2025 14:12:55 +0000 Subject: [PATCH] Add Honeybadger.io observability support. It's not yet clear how useful it is, but it's at least something. --- flake.nix | 2 +- go.mod | 5 +++++ go.sum | 19 +++++++++++++++++++ src/health.go | 13 +++++++++++-- src/main.go | 28 +++++++++++++++++++++++++--- src/observe.go | 27 +++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 src/observe.go diff --git a/flake.nix b/flake.nix index 655f73c..0365fb7 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,7 @@ "-s -w" ]; - vendorHash = "sha256-RCuX+z74m+G+Ptg/DF727VxnV0WEQWLaQPdN9+Ma9Do="; + vendorHash = "sha256-8hT8TC7I1jdfve9WOdwN3C51sjbLSRvwIj7a/1qCmVA="; fixupPhase = '' # Apparently `go install` doesn't support renaming the binary, so country girls make do. diff --git a/go.mod b/go.mod index 22c7d4d..9b0a174 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/KimMachineGun/automemlimit v0.7.4 github.com/go-git/go-billy/v6 v6.0.0-20250627091229-31e2a16eef30 github.com/go-git/go-git/v6 v6.0.0-20250910120214-3a68d0404116 + github.com/honeybadger-io/honeybadger-go v0.8.0 github.com/maypok86/otter/v2 v2.2.1 github.com/minio/minio-go/v7 v7.0.95 github.com/pelletier/go-toml/v2 v2.2.4 @@ -24,6 +25,7 @@ require ( github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg/v2 v2.0.2 // indirect github.com/go-ini/ini v1.67.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/uuid v1.6.0 // indirect @@ -33,14 +35,17 @@ require ( github.com/minio/crc64nvme v1.0.2 // indirect github.com/minio/md5-simd v1.1.2 // 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/rs/xid v1.6.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect + github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/tinylib/msgp v1.3.0 // indirect github.com/tj/assert v0.0.3 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // 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 diff --git a/go.sum b/go.sum index f4a00d7..036f8c2 100644 --- a/go.sum +++ b/go.sum @@ -35,14 +35,19 @@ github.com/go-git/go-git/v6 v6.0.0-20250910120214-3a68d0404116 h1:YtWRF4qTPh9YEh github.com/go-git/go-git/v6 v6.0.0-20250910120214-3a68d0404116/go.mod h1:qikYwcUCOy1+Pq2SPaUmoubCmJ2PT+Lg9ti8sgwtmJg= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/honeybadger-io/honeybadger-go v0.8.0 h1:yoB+kYMMV4LVXgyh0Stz6cC45w2EQDuN8gkHAwaxLmk= +github.com/honeybadger-io/honeybadger-go v0.8.0/go.mod h1:YaVKI0eSWUwNOzWa4xr5INoBTWGGz6trlcA3a9gzZ6I= github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -63,6 +68,8 @@ github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYE github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo= 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= +github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= @@ -77,9 +84,17 @@ 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= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= @@ -92,12 +107,16 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 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= 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= golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= diff --git a/src/health.go b/src/health.go index 76625db..c0249bf 100644 --- a/src/health.go +++ b/src/health.go @@ -6,6 +6,15 @@ import ( ) func ServeHealth(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - fmt.Fprintln(w, "ok") + switch r.URL.Path { + case "/": + w.WriteHeader(http.StatusOK) + fmt.Fprintln(w, "ok") + + case "/panic": + panic("explicit panic request") + + default: + http.Error(w, "not found", http.StatusNotFound) + } } diff --git a/src/main.go b/src/main.go index 13d462f..48c4083 100644 --- a/src/main.go +++ b/src/main.go @@ -2,11 +2,13 @@ package main import ( "flag" + "fmt" "log" "log/slog" "net" "net/http" "os" + "runtime/debug" "strings" "github.com/KimMachineGun/automemlimit/memlimit" @@ -32,17 +34,37 @@ func listen(name string, listen string) net.Listener { return listener } +func panicHandler(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { + if err := recover(); err != nil { + log.Printf("panic: %s %s %s: %s\n%s", + r.Method, r.Host, r.URL.Path, err, string(debug.Stack())) + http.Error(w, + fmt.Sprintf("internal server error: %s", err), + http.StatusInternalServerError, + ) + } + }() + handler.ServeHTTP(w, r) + }) +} + func serve(listener net.Listener, serve func(http.ResponseWriter, *http.Request)) { if listener != nil { - if err := http.Serve(listener, http.HandlerFunc(serve)); err != nil { - log.Fatalln(err) - } + var handler http.Handler + handler = http.HandlerFunc(serve) + handler = ObserveHTTPHandler(handler) + handler = panicHandler(handler) + log.Fatalln(http.Serve(listener, handler)) } } func main() { var err error + InitObservability() + configPath := flag.String("config", "config.toml", "path to configuration file") migrateV1Path := flag.String("migrate-v1", "", "path to v1 data directory to upload") flag.Parse() diff --git a/src/observe.go b/src/observe.go new file mode 100644 index 0000000..c8167cb --- /dev/null +++ b/src/observe.go @@ -0,0 +1,27 @@ +package main + +import ( + "net/http" + "os" + "runtime/debug" + + "github.com/honeybadger-io/honeybadger-go" +) + +func hasHoneybadger() bool { + return os.Getenv("HONEYBADGER_API_KEY") != "" +} + +func InitObservability() { + if hasHoneybadger() { + honeybadger.Configure(honeybadger.Configuration{}) + debug.SetPanicOnFault(true) + } +} + +func ObserveHTTPHandler(handler http.Handler) http.Handler { + if hasHoneybadger() { + handler = honeybadger.Handler(handler) + } + return handler +}