func (aggregator *HealthAggregator) ServeHTTP(enabled bool, host string, port int) {
	aggregator.mutex.Lock()
	defer aggregator.mutex.Unlock()
	if enabled {
		logCxt := log.WithFields(log.Fields{
			"host": host,
			"port": port,
		})
		if aggregator.httpServer != nil {
			logCxt.Info("Health enabled.  Server is already running.")
			return
		}
		logCxt.Info("Health enabled.  Starting server.")
		aggregator.httpServer = &http.Server{
			Addr:    net.JoinHostPort(host, strconv.Itoa(port)),
			Handler: aggregator.httpServeMux,
		}
		go func() {
			for {
				server := aggregator.getHTTPServer()
				if server == nil {
					break
				}
				err := server.ListenAndServe()
				log.WithError(err).Error(
					"Health endpoint failed, trying to restart it...")
				time.Sleep(1 * time.Second)
			}
		}()
	} else {
		if aggregator.httpServer != nil {
			log.Info("Health disabled.  Stopping server.")
			_ = aggregator.httpServer.Close()
			aggregator.httpServer = nil
		}
	}
}
ServeHTTP publishes the current overall liveness and readiness at http://HOST:PORT/liveness and
http://HOST:PORT/readiness respectively.  A GET request on those URLs returns StatusGood or
StatusBad, according to the current overall liveness or readiness.  These endpoints are designed
for use by Kubernetes liveness and readiness probes.
package health
import (
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
	log "github.com/sirupsen/logrus"
)
type HealthReport struct {
	Live  bool
	Ready bool
}
type reporterState struct {
	name string
	reports HealthReport
	timeout time.Duration
	latest HealthReport
	timestamp time.Time
}
func (r *reporterState) HasReadinessProblem() bool {
	if !r.reports.Ready {
		return false
	}
	if r.TimedOut() {
		log.WithField("name", r.name).Warn("Report timed out")
		return true
	}
	return !r.latest.Ready
}
func (r *reporterState) HasLivenessProblem() bool {
	if !r.reports.Live {
		return false
	}
	if r.TimedOut() {
		log.WithField("name", r.name).Warn("Report timed out")
		return true
	}
	return !r.latest.Live
}
func (r *reporterState) TimedOut() bool {
	return r.timeout != 0 && time.Since(r.timestamp) > r.timeout
}
type HealthAggregator struct {
	mutex *sync.Mutex
	lastReport *HealthReport
	reporters map[string]*reporterState
	httpServeMux *http.ServeMux
	httpServer *http.Server
}
func (aggregator *HealthAggregator) RegisterReporter(name string, reports *HealthReport, timeout time.Duration) {
	aggregator.mutex.Lock()
	defer aggregator.mutex.Unlock()
	aggregator.reporters[name] = &reporterState{
		name:      name,
		reports:   *reports,
		timeout:   timeout,
		latest:    HealthReport{Live: true},
		timestamp: time.Now(),
	}
	return
}
func (aggregator *HealthAggregator) Report(name string, report *HealthReport) {
	aggregator.mutex.Lock()
	defer aggregator.mutex.Unlock()
	reporter := aggregator.reporters[name]
	logCxt := log.WithFields(log.Fields{
		"name":       name,
		"newReport":  report,
		"lastReport": reporter.latest,
	})
	logCxt.Debug("New health report")
	if reporter.latest != *report {
		logCxt.Info("Health of component changed")
		reporter.latest = *report
	}
	reporter.timestamp = time.Now()
	return
}
func NewHealthAggregator() *HealthAggregator {
	aggregator := &HealthAggregator{
		mutex:        &sync.Mutex{},
		lastReport:   &HealthReport{},
		reporters:    map[string]*reporterState{},
		httpServeMux: http.NewServeMux(),
	}
	aggregator.httpServeMux.HandleFunc("/readiness", func(rsp http.ResponseWriter, req *http.Request) {
		log.Debug("GET /readiness")
		status := StatusBad
		if aggregator.Summary().Ready {
			log.Debug("Health: ready")
			status = StatusGood
		} else {
			log.Warn("Health: not ready")
		}
		rsp.WriteHeader(status)
	})
	aggregator.httpServeMux.HandleFunc("/liveness", func(rsp http.ResponseWriter, req *http.Request) {
		log.Debug("GET /liveness")
		status := StatusBad
		if aggregator.Summary().Live {
			log.Debug("Health: live")
			status = StatusGood
		} else {
			log.Warn("Health: not live")
		}
		rsp.WriteHeader(status)
	})
	return aggregator
}
func (aggregator *HealthAggregator) Summary() *HealthReport {
	aggregator.mutex.Lock()
	defer aggregator.mutex.Unlock()
	summary := &HealthReport{Live: true, Ready: true}
	for _, reporter := range aggregator.reporters {
		log.WithField("reporter", reporter).Debug("Checking state of reporter")
		if reporter.HasLivenessProblem() {
			log.WithField("name", reporter.name).Warn("Reporter is not live.")
			summary.Live = false
		}
		if reporter.HasReadinessProblem() {
			log.WithField("name", reporter.name).Warn("Reporter is not ready.")
			summary.Ready = false
		}
	}
	if aggregator.lastReport == nil || *summary != *aggregator.lastReport {
		aggregator.lastReport = summary
		log.WithField("newStatus", summary).Info("Overall health status changed")
	}
	log.WithField("healthResult", summary).Debug("Calculated health summary")
	return summary
}
const (
	StatusGood = 204
	StatusBad = 503
)