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  }