github.com/MetalBlockchain/metalgo@v1.11.9/api/health/health.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package health 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/prometheus/client_golang/prometheus" 11 "go.uber.org/zap" 12 13 "github.com/MetalBlockchain/metalgo/utils/logging" 14 ) 15 16 const ( 17 // CheckLabel is the label used to differentiate between health checks. 18 CheckLabel = "check" 19 // TagLabel is the label used to differentiate between health check tags. 20 TagLabel = "tag" 21 // AllTag is automatically added to every registered check. 22 AllTag = "all" 23 // ApplicationTag checks will act as if they specified every tag that has 24 // been registered. 25 // Registering a health check with this tag will ensure that it is always 26 // included in all health query results. 27 ApplicationTag = "application" 28 ) 29 30 var _ Health = (*health)(nil) 31 32 // Health defines the full health service interface for registering, reporting 33 // and refreshing health checks. 34 type Health interface { 35 Registerer 36 Reporter 37 38 // Start running periodic health checks at the specified frequency. 39 // Repeated calls to Start will be no-ops. 40 Start(ctx context.Context, freq time.Duration) 41 42 // Stop running periodic health checks. Stop should only be called after 43 // Start. Once Stop returns, no more health checks will be executed. 44 Stop() 45 } 46 47 // Registerer defines how to register new components to check the health of. 48 type Registerer interface { 49 RegisterReadinessCheck(name string, checker Checker, tags ...string) error 50 RegisterHealthCheck(name string, checker Checker, tags ...string) error 51 RegisterLivenessCheck(name string, checker Checker, tags ...string) error 52 } 53 54 // Reporter returns the current health status. 55 type Reporter interface { 56 Readiness(tags ...string) (map[string]Result, bool) 57 Health(tags ...string) (map[string]Result, bool) 58 Liveness(tags ...string) (map[string]Result, bool) 59 } 60 61 type health struct { 62 log logging.Logger 63 readiness *worker 64 health *worker 65 liveness *worker 66 } 67 68 func New(log logging.Logger, registerer prometheus.Registerer) (Health, error) { 69 failingChecks := prometheus.NewGaugeVec( 70 prometheus.GaugeOpts{ 71 Name: "checks_failing", 72 Help: "number of currently failing health checks", 73 }, 74 []string{CheckLabel, TagLabel}, 75 ) 76 return &health{ 77 log: log, 78 readiness: newWorker(log, "readiness", failingChecks), 79 health: newWorker(log, "health", failingChecks), 80 liveness: newWorker(log, "liveness", failingChecks), 81 }, registerer.Register(failingChecks) 82 } 83 84 func (h *health) RegisterReadinessCheck(name string, checker Checker, tags ...string) error { 85 return h.readiness.RegisterMonotonicCheck(name, checker, tags...) 86 } 87 88 func (h *health) RegisterHealthCheck(name string, checker Checker, tags ...string) error { 89 return h.health.RegisterCheck(name, checker, tags...) 90 } 91 92 func (h *health) RegisterLivenessCheck(name string, checker Checker, tags ...string) error { 93 return h.liveness.RegisterCheck(name, checker, tags...) 94 } 95 96 func (h *health) Readiness(tags ...string) (map[string]Result, bool) { 97 results, healthy := h.readiness.Results(tags...) 98 if !healthy { 99 h.log.Warn("failing check", 100 zap.String("namespace", "readiness"), 101 zap.Reflect("reason", results), 102 ) 103 } 104 return results, healthy 105 } 106 107 func (h *health) Health(tags ...string) (map[string]Result, bool) { 108 results, healthy := h.health.Results(tags...) 109 if !healthy { 110 h.log.Warn("failing check", 111 zap.String("namespace", "health"), 112 zap.Reflect("reason", results), 113 ) 114 } 115 return results, healthy 116 } 117 118 func (h *health) Liveness(tags ...string) (map[string]Result, bool) { 119 results, healthy := h.liveness.Results(tags...) 120 if !healthy { 121 h.log.Warn("failing check", 122 zap.String("namespace", "liveness"), 123 zap.Reflect("reason", results), 124 ) 125 } 126 return results, healthy 127 } 128 129 func (h *health) Start(ctx context.Context, freq time.Duration) { 130 h.readiness.Start(ctx, freq) 131 h.health.Start(ctx, freq) 132 h.liveness.Start(ctx, freq) 133 } 134 135 func (h *health) Stop() { 136 h.readiness.Stop() 137 h.health.Stop() 138 h.liveness.Stop() 139 }