func addMetricsPusher(job, pushgatewayURL string, interval time.Duration) {
	if pushgatewayURL == "" {
		return
	}
	go func() {
		tick := time.Tick(interval)
		for range tick {
			instance, err := os.Hostname()
			if err != nil {
				log.Warnf("Unable to lookup hostname for metrics: %v", err)
				continue
			}
			if err := push.New(pushgatewayURL, job).Grouping("instance", instance).Push(); err != nil {
				log.Warnf("Unable to push metrics: %v", err)
			}
		}
	}()
}
AddMetricsPusher starts a periodic push of metrics to a prometheus pushgateway.
package cmd
import (
	"fmt"
	"io"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"
	"github.com/onrik/logrus/filename"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/prometheus/client_golang/prometheus/push"
	log "github.com/sirupsen/logrus"
	"github.com/sky-uk/feed/util/metrics"
)
type Pulse interface {
	Health() error
	Stop() error
}
func AddHealthPort(pulse Pulse, healthPort int) {
	http.HandleFunc("/health", healthHandler(pulse))
	http.Handle("/metrics", promhttp.Handler())
	http.HandleFunc("/alive", okHandler)
	go func() {
		log.Error(http.ListenAndServe(":"+strconv.Itoa(healthPort), nil))
		log.Info(pulse.Stop())
		os.Exit(-1)
	}()
}
func healthHandler(pulse Pulse) func(w http.ResponseWriter, r *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := pulse.Health(); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			_, _ = io.WriteString(w, fmt.Sprintf("%v\n", err))
			return
		}
		w.WriteHeader(http.StatusOK)
		_, _ = io.WriteString(w, "ok\n")
	}
}
func okHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	_, _ = io.WriteString(w, "ok\n")
}
const pollInterval = time.Second
func addUnhealthyLogger(pulse Pulse, unhealthyCounter prometheus.Counter) {
	go func() {
		healthy := true
		tickCh := time.Tick(pollInterval)
		for range tickCh {
			if err := pulse.Health(); err != nil {
				unhealthyCounter.Add(pollInterval.Seconds())
				if healthy {
					log.Warnf("Unhealthy: %v", err)
					healthy = false
				}
			} else if !healthy {
				log.Info("Health restored")
				healthy = true
			}
		}
	}()
}
func AddSignalHandler(pulse Pulse) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for sig := range c {
			log.Infof("Signalled %v, shutting down gracefully", sig)
			err := pulse.Stop()
			if err != nil {
				log.Errorf("Error while stopping: %v", err)
				os.Exit(-1)
			}
			os.Exit(0)
		}
	}()
}
func ConfigureLogging(debug bool) {
	log.SetOutput(os.Stdout)
	if debug {
		log.SetLevel(log.DebugLevel)
	}
	filenameHook := filename.NewHook()
	filenameHook.Field = "source"
	log.AddHook(filenameHook)
}
func ConfigureMetrics(job string, prometheusLabels KeyValues, pushgatewayURL string, pushgatewayIntervalSeconds int) {
	labels := make(prometheus.Labels)
	for _, l := range prometheusLabels {
		labels[l.key] = l.value
	}
	metrics.SetConstLabels(labels)
	addMetricsPusher(job, pushgatewayURL, time.Second*time.Duration(pushgatewayIntervalSeconds))
}
func AddHealthMetrics(pulse Pulse, prometheusSubsystem string) {
	unhealthyCounter := createUnhealthyCounter(prometheusSubsystem)
	addUnhealthyLogger(pulse, unhealthyCounter)
}
func createUnhealthyCounter(subsystem string) prometheus.Counter {
	return metrics.RegisterNewDefaultCounter(subsystem, "unhealthy_time",
		fmt.Sprintf("The number of seconds %s-%s has been unhealthy.",
			metrics.PrometheusNamespace, subsystem))
}