Add healthCheck server
This commit is contained in:
parent
f2df392552
commit
a28fc75610
|
@ -0,0 +1,119 @@
|
||||||
|
package health
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/pprof"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
app *fiber.App
|
||||||
|
port string
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(logger *zap.Logger, port string, pprofEnabled bool, checks ...Check) *Server {
|
||||||
|
|
||||||
|
app := fiber.New(fiber.Config{DisableStartupMessage: true})
|
||||||
|
|
||||||
|
// config use of middlware.
|
||||||
|
if pprofEnabled {
|
||||||
|
app.Use(pprof.New())
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl := NewController(checks, logger)
|
||||||
|
api := app.Group("/api")
|
||||||
|
api.Get("/health", ctrl.HealthCheck)
|
||||||
|
api.Get("/ready", ctrl.ReadinessCheck)
|
||||||
|
|
||||||
|
return &Server{
|
||||||
|
app: app,
|
||||||
|
port: port,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start initiates the serving of HTTP requests.
|
||||||
|
func (s *Server) Start() {
|
||||||
|
|
||||||
|
addr := ":" + s.port
|
||||||
|
s.logger.Info("Monitoring server starting", zap.String("bindAddress", addr))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := s.app.Listen(addr)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to start monitoring server", zap.Error(err), zap.String("bindAddress", addr))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop gracefully shuts down the server.
|
||||||
|
//
|
||||||
|
// Blocks until all active connections are closed.
|
||||||
|
func (s *Server) Stop() {
|
||||||
|
_ = s.app.Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
type controller struct {
|
||||||
|
checks []Check
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewController creates a Controller instance.
|
||||||
|
func NewController(checks []Check, logger *zap.Logger) *controller {
|
||||||
|
return &controller{checks: checks, logger: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheck is the HTTP handler for the route `GET /health`.
|
||||||
|
func (c *controller) HealthCheck(ctx *fiber.Ctx) error {
|
||||||
|
|
||||||
|
response := ctx.JSON(struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
}{
|
||||||
|
Status: "OK",
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadinessCheck is the HTTP handler for the route `GET /ready`.
|
||||||
|
func (c *controller) ReadinessCheck(ctx *fiber.Ctx) error {
|
||||||
|
|
||||||
|
requestCtx := ctx.Context()
|
||||||
|
requestID := fmt.Sprintf("%v", requestCtx.Value("requestid"))
|
||||||
|
|
||||||
|
// For every callback, check whether it is passing
|
||||||
|
for _, check := range c.checks {
|
||||||
|
if err := check(requestCtx); err != nil {
|
||||||
|
|
||||||
|
c.logger.Error(
|
||||||
|
"Readiness check failed",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.String("requestID", requestID),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return error information to the caller
|
||||||
|
response := ctx.
|
||||||
|
Status(fiber.StatusInternalServerError).
|
||||||
|
JSON(struct {
|
||||||
|
Ready string `json:"ready"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}{
|
||||||
|
Ready: "NO",
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All checks passed
|
||||||
|
response := ctx.Status(fiber.StatusOK).
|
||||||
|
JSON(struct {
|
||||||
|
Ready string `json:"ready"`
|
||||||
|
}{
|
||||||
|
Ready: "OK",
|
||||||
|
})
|
||||||
|
return response
|
||||||
|
}
|
|
@ -18,6 +18,13 @@ type Logger struct {
|
||||||
LogLevel string `split_words:"true" default:"INFO"`
|
LogLevel string `split_words:"true" default:"INFO"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Monitoring contains configuration settings for the monitoring endpoints.
|
||||||
|
type Monitoring struct {
|
||||||
|
// MonitoringPort defines the TCP port for the monitoring endpoints.
|
||||||
|
MonitoringPort string `split_words:"true" default:"8000"`
|
||||||
|
PprofEnabled bool `split_words:"true" default:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
// LoadFromEnv loads the configuration settings from environment variables.
|
// LoadFromEnv loads the configuration settings from environment variables.
|
||||||
//
|
//
|
||||||
// If there is a .env file in the current directory, it will be used to
|
// If there is a .env file in the current directory, it will be used to
|
||||||
|
|
|
@ -3,7 +3,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/wormhole-foundation/wormhole-explorer/common/health"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/common/logger"
|
"github.com/wormhole-foundation/wormhole-explorer/common/logger"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/common/mongohelpers"
|
"github.com/wormhole-foundation/wormhole-explorer/common/mongohelpers"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/common/settings"
|
"github.com/wormhole-foundation/wormhole-explorer/common/settings"
|
||||||
|
@ -32,6 +36,27 @@ func main() {
|
||||||
rootLogger.Fatal("Error connecting to MongoDB", zap.Error(err))
|
rootLogger.Fatal("Error connecting to MongoDB", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start serving the monitoring endpoints.
|
||||||
|
plugins := []health.Check{health.Mongo(db.Database)}
|
||||||
|
server := health.NewServer(
|
||||||
|
rootLogger,
|
||||||
|
cfg.MonitoringPort,
|
||||||
|
cfg.PprofEnabled,
|
||||||
|
plugins...,
|
||||||
|
)
|
||||||
|
server.Start()
|
||||||
|
|
||||||
|
// Block until we get a termination signal or the context is cancelled
|
||||||
|
rootLogger.Info("waiting for termination signal or context cancellation...")
|
||||||
|
sigterm := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
select {
|
||||||
|
case <-rootCtx.Done():
|
||||||
|
rootLogger.Warn("terminating (root context cancelled)")
|
||||||
|
case signal := <-sigterm:
|
||||||
|
rootLogger.Info("terminating (signal received)", zap.String("signal", signal.String()))
|
||||||
|
}
|
||||||
|
|
||||||
// Shut down gracefully
|
// Shut down gracefully
|
||||||
rootLogger.Info("disconnecting from MongoDB...")
|
rootLogger.Info("disconnecting from MongoDB...")
|
||||||
db.Disconnect(rootCtx)
|
db.Disconnect(rootCtx)
|
||||||
|
|
|
@ -8,4 +8,5 @@ import (
|
||||||
type ServiceSettings struct {
|
type ServiceSettings struct {
|
||||||
settings.Logger
|
settings.Logger
|
||||||
settings.MongoDB
|
settings.MongoDB
|
||||||
|
settings.Monitoring
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue