2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-05-30 15:03:44 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package process
|
|
|
|
|
|
|
|
import (
|
2018-07-26 15:21:35 +01:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
2018-05-30 15:03:44 +01:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/pprof"
|
2019-06-13 17:29:35 +01:00
|
|
|
"strings"
|
2018-05-30 15:03:44 +01:00
|
|
|
|
|
|
|
"go.uber.org/zap"
|
2019-10-20 08:56:23 +01:00
|
|
|
"gopkg.in/spacemonkeygo/monkit.v2"
|
2018-05-30 15:03:44 +01:00
|
|
|
"gopkg.in/spacemonkeygo/monkit.v2/present"
|
2019-04-10 07:38:26 +01:00
|
|
|
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/version/checker"
|
2018-05-30 15:03:44 +01:00
|
|
|
)
|
|
|
|
|
2018-07-26 15:21:35 +01:00
|
|
|
var (
|
2019-04-05 13:09:16 +01:00
|
|
|
debugAddr = flag.String("debug.addr", "127.0.0.1:0", "address to listen on for debug endpoints")
|
2018-07-26 15:21:35 +01:00
|
|
|
)
|
|
|
|
|
2018-05-30 15:03:44 +01:00
|
|
|
func init() {
|
|
|
|
// zero out the http.DefaultServeMux net/http/pprof so unhelpfully
|
|
|
|
// side-effected.
|
|
|
|
*http.DefaultServeMux = http.ServeMux{}
|
|
|
|
}
|
|
|
|
|
2019-01-24 21:04:29 +00:00
|
|
|
func initDebug(logger *zap.Logger, r *monkit.Registry) (err error) {
|
2018-05-30 15:03:44 +01:00
|
|
|
var mux http.ServeMux
|
|
|
|
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
|
|
|
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
|
|
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
|
|
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
|
|
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
2019-04-03 20:13:39 +01:00
|
|
|
|
2019-10-20 08:56:23 +01:00
|
|
|
mux.Handle("/version/", http.StripPrefix("/version", checker.NewDebugHandler(logger.Named("version"))))
|
2018-05-30 15:03:44 +01:00
|
|
|
mux.Handle("/mon/", http.StripPrefix("/mon", present.HTTP(r)))
|
2019-06-13 17:29:35 +01:00
|
|
|
mux.HandleFunc("/metrics", prometheus)
|
2018-07-26 15:21:35 +01:00
|
|
|
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
_, _ = fmt.Fprintln(w, "OK")
|
|
|
|
})
|
2019-07-03 15:10:51 +01:00
|
|
|
if *debugAddr != "" {
|
|
|
|
ln, err := net.Listen("tcp", *debugAddr)
|
2018-05-30 15:03:44 +01:00
|
|
|
if err != nil {
|
2019-07-03 15:10:51 +01:00
|
|
|
return err
|
2018-05-30 15:03:44 +01:00
|
|
|
}
|
2019-07-03 15:10:51 +01:00
|
|
|
go func() {
|
|
|
|
logger.Debug(fmt.Sprintf("debug server listening on %s", ln.Addr().String()))
|
|
|
|
err := (&http.Server{Handler: &mux}).Serve(ln)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error("debug server died", zap.Error(err))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2018-05-30 15:03:44 +01:00
|
|
|
return nil
|
|
|
|
}
|
2019-06-13 17:29:35 +01:00
|
|
|
|
|
|
|
func sanitize(val string) string {
|
|
|
|
// https://prometheus.io/docs/concepts/data_model/
|
|
|
|
// specifies all metric names must match [a-zA-Z_:][a-zA-Z0-9_:]*
|
|
|
|
// Note: The colons are reserved for user defined recording rules.
|
|
|
|
// They should not be used by exporters or direct instrumentation.
|
|
|
|
if '0' <= val[0] && val[0] <= '9' {
|
|
|
|
val = "_" + val
|
|
|
|
}
|
|
|
|
return strings.Map(func(r rune) rune {
|
|
|
|
switch {
|
|
|
|
case 'a' <= r && r <= 'z':
|
|
|
|
return r
|
|
|
|
case 'A' <= r && r <= 'Z':
|
|
|
|
return r
|
2019-08-30 00:51:03 +01:00
|
|
|
case '0' <= r && r <= '9':
|
|
|
|
return r
|
2019-06-13 17:29:35 +01:00
|
|
|
default:
|
|
|
|
return '_'
|
|
|
|
}
|
|
|
|
}, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func prometheus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
// writes https://prometheus.io/docs/instrumenting/exposition_formats/
|
|
|
|
// TODO(jt): deeper monkit integration so we can expose prometheus types
|
|
|
|
// (https://prometheus.io/docs/concepts/metric_types/)
|
|
|
|
monkit.Default.Stats(func(name string, val float64) {
|
|
|
|
metric := sanitize(name)
|
2019-08-29 22:42:38 +01:00
|
|
|
_, _ = fmt.Fprintf(w, "# TYPE %s gauge\n%s %g\n",
|
|
|
|
metric, metric, val)
|
2019-06-13 17:29:35 +01:00
|
|
|
})
|
|
|
|
}
|