Skip to content

Commit

Permalink
ref: run metrics webserver as oklog/run routine and do it better
Browse files Browse the repository at this point in the history
The old design sucked. It was an internal lib that had a setup func for
the webserver and handler, and it was just called as a goroutine from
main() so that it didn't block, since it was using the default
`http.ListenAndServe()`. New design:

- runs the HTTP server for metrics/pprof as an oklog/run routine,
  similar to signal handlers, reload handlers, auto reload timer,
manager runner, etc.
- sets up an `http.Server` struct for more control over the server,
  specifically now having access to `s.Shutdown(ctx)`

This commit also now frees the way to remove the internal `metrics`
package, as all of the functionality has been consumed into main
  • Loading branch information
tjhop committed Oct 14, 2023
1 parent f1491f2 commit 52a5ee2
Showing 1 changed file with 77 additions and 10 deletions.
87 changes: 77 additions & 10 deletions cmd/mango/mango.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package main

import (
"context"
"fmt"
"log/slog"
"net/http"
_ "net/http/pprof" // for profiling
"os"
"os/signal"
"path/filepath"
Expand All @@ -14,6 +17,7 @@ import (
"github.com/oklog/run"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"

Expand All @@ -22,11 +26,11 @@ import (
"github.com/tjhop/mango/internal/config"
"github.com/tjhop/mango/internal/inventory"
"github.com/tjhop/mango/internal/manager"
"github.com/tjhop/mango/internal/metrics"
)

const (
programName = "mango"
programName = "mango"
defaultPrometheusPort = 9555
)

var (
Expand All @@ -40,14 +44,31 @@ var (
// TODO: @tjhop move `enrolled` to an inventory pkg metric
// TODO: @tjhop move `manager` to a manager pkg metric
// TODO: @tjhop add labels for: [auto_reload: true|false]
metricMangoRuntimeInfo = promauto.NewGaugeVec(
// metricMangoRuntimeInfo = promauto.NewGaugeVec(
// prometheus.GaugeOpts{
// Name: "mango_runtime_info",
// Help: "A metric with a constant '1' value with labels for information about the mango runtime, such as system hostname.",
// },
// []string{"hostname", "enrolled", "manager"},
// )
)

func init() {
// expose build info metric
promauto.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "mango_runtime_info",
Help: "A metric with a constant '1' value with labels for information about the mango runtime, such as system hostname.",
Name: "mango_build_info",
Help: "A metric with a constant '1' value with labels for version, commit and build_date from which mango was built.",
ConstLabels: prometheus.Labels{
"version": config.Version,
"commit": config.Commit,
"build_date": config.BuildDate,
"goversion": runtime.Version(),
},
},
[]string{"hostname", "enrolled", "manager"},
func() float64 { return 1 },
)
)
}

func mango(ctx context.Context, logger *slog.Logger, inventoryPath, hostname string) {
mangoStart := time.Now()
Expand Down Expand Up @@ -106,9 +127,6 @@ func mango(ctx context.Context, logger *slog.Logger, inventoryPath, hostname str
}
viper.Set("mango.temp-dir", dir)

// serve metrics
go metrics.ExportPrometheusMetrics()

// load inventory
inventoryLogger := logger.With(
slog.String("worker", "inventory"),
Expand Down Expand Up @@ -330,6 +348,55 @@ func mango(ctx context.Context, logger *slog.Logger, inventoryPath, hostname str
},
)
}
{
// web server for metrics/pprof
cancel := make(chan struct{})

viper.SetDefault("metrics.port", defaultPrometheusPort)
iface := viper.GetString("metrics.interface")
port := viper.GetInt("metrics.port")
address := fmt.Sprintf("%s:%d", iface, port)

metricsServer := &http.Server{
Addr: address,
Handler: nil,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 5 * time.Second,
}
http.Handle("/metrics", promhttp.Handler())

g.Add(
func() error {
if err := metricsServer.ListenAndServe(); err != http.ErrServerClosed {
logger.LogAttrs(
ctx,
slog.LevelError,
"Mango failed to open HTTP server for metrics",
slog.String("err", err.Error()),
slog.String("address", address),
)
return err
}

<-cancel

return nil
},
func(error) {
if err := metricsServer.Shutdown(ctx); err != nil {
// Error from closing listeners, or context timeout:
logger.LogAttrs(
ctx,
slog.LevelError,
"Failed to close HTTP server",
slog.String("err", err.Error()),
)
}
close(cancel)
},
)
}

logger.LogAttrs(
ctx,
Expand Down

0 comments on commit 52a5ee2

Please sign in to comment.