mirror of
https://github.com/eosswedenorg/antelope-api-healthcheck
synced 2026-06-17 04:50:02 +02:00
208 lines
4.8 KiB
Go
208 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/eosswedenorg-go/pid"
|
|
"github.com/eosswedenorg/antelope-api-healthcheck/internal/server"
|
|
"github.com/eosswedenorg/antelope-api-healthcheck/internal/utils"
|
|
log "github.com/inconshreveable/log15"
|
|
"github.com/pborman/getopt/v2"
|
|
)
|
|
|
|
// Command line flags
|
|
// ---------------------------------------------------------
|
|
|
|
var (
|
|
logFile string
|
|
pidFile string
|
|
)
|
|
|
|
// Global variables
|
|
// ---------------------------------------------------------
|
|
|
|
// Version string, should be updated by the go linker (by passing "-X main.VersionString=value" to the linker)
|
|
// see: https://pkg.go.dev/cmd/link
|
|
var VersionString string = "-"
|
|
|
|
// File descriptor to the current log file.
|
|
var logfd *os.File
|
|
|
|
var (
|
|
logfmt log.Format
|
|
logger log.Logger
|
|
|
|
// TCP Server
|
|
srv *server.Server
|
|
)
|
|
|
|
// argv_listen_addr
|
|
// Parse listen address from command line.
|
|
//
|
|
// ---------------------------------------------------------
|
|
func argv_listen_addr() string {
|
|
var addr string
|
|
|
|
argv := getopt.Args()
|
|
if len(argv) > 0 {
|
|
addr = argv[0]
|
|
} else {
|
|
addr = "127.0.0.1"
|
|
}
|
|
|
|
addr += ":"
|
|
if len(argv) > 1 {
|
|
addr += argv[1]
|
|
} else {
|
|
addr += "1337"
|
|
}
|
|
|
|
return addr
|
|
}
|
|
|
|
func setLogFile() {
|
|
// Open file
|
|
fd, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
|
if err != nil {
|
|
logger.Error(err.Error())
|
|
}
|
|
|
|
// Try close if old descriptor is defined.
|
|
if logfd != nil {
|
|
if err = logfd.Close(); err != nil {
|
|
logger.Error(err.Error())
|
|
}
|
|
}
|
|
|
|
// Update variable and set log writer.
|
|
logfd = fd
|
|
logger.SetHandler(log.StreamHandler(logfd, logfmt))
|
|
}
|
|
|
|
// signalEventLoop()
|
|
// Initialize event channel for OS signals
|
|
// and runs an event loop.
|
|
//
|
|
// ---------------------------------------------------------
|
|
func signalEventLoop() {
|
|
// Setup a channel
|
|
sig_ch := make(chan os.Signal, 1)
|
|
|
|
// subscribe to SIGHUP signal.
|
|
signal.Notify(sig_ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
// Event loop
|
|
var run bool = true
|
|
for run {
|
|
// Block until we get a signal.
|
|
sig := <-sig_ch
|
|
|
|
l := logger.New("signal", sig)
|
|
|
|
switch sig {
|
|
case syscall.SIGINT, syscall.SIGTERM:
|
|
l.Info("Program was asked to terminate.")
|
|
run = false
|
|
|
|
// Tell the server to close.
|
|
err := srv.Close()
|
|
if err != nil {
|
|
l.Error("Failed to close server", "error", err)
|
|
}
|
|
// SIGHUP is sent when logfile is rotated.
|
|
case syscall.SIGHUP:
|
|
msg := "Logfile was rotated: "
|
|
|
|
if logfd != nil {
|
|
setLogFile()
|
|
msg += "Filedescriptor was updated"
|
|
} else {
|
|
msg += "No Filedescriptor to update (most likely uses standard out/err streams)"
|
|
}
|
|
|
|
l.Info(msg)
|
|
default:
|
|
l.Warn("Unknown signal")
|
|
}
|
|
}
|
|
}
|
|
|
|
// main
|
|
//
|
|
// ---------------------------------------------------------
|
|
func main() {
|
|
var version bool
|
|
var usage bool
|
|
var logFormatter *string
|
|
// Set default timeout to 2 sec
|
|
// as haproxy "inter" parameter to healthcheck is set to 2s per default.
|
|
req_timeout := time.Second * 2
|
|
|
|
logger = log.Root()
|
|
|
|
// Command line parsing
|
|
getopt.SetParameters("[ip] [port]")
|
|
getopt.FlagLong(&usage, "help", 'h', "Print this help text")
|
|
getopt.FlagLong(&version, "version", 'v', "Print version")
|
|
getopt.FlagLong(&logFile, "log", 'l', "Path to log file", "file")
|
|
getopt.FlagLong(&pidFile, "pid", 'p', "Path to pid file", "file")
|
|
getopt.FlagLong(&req_timeout, "timeout", 't', "Set the maximum time before a request times out, valid prefixes are 's','ms','us'", "duration")
|
|
logFormatter = getopt.EnumLong("log-format", 0, []string{"term", "logfmt", "json", "json-pretty"}, "", "Log format to use: term,logfmt,json,json-pretty")
|
|
|
|
getopt.Parse()
|
|
|
|
if usage {
|
|
getopt.Usage()
|
|
return
|
|
}
|
|
|
|
if version {
|
|
fmt.Printf("Version: %s\n", VersionString)
|
|
return
|
|
}
|
|
|
|
logfmt = utils.ParseLogFormatter(*logFormatter)
|
|
|
|
// Open logfile.
|
|
if len(logFile) > 0 {
|
|
setLogFile()
|
|
} else {
|
|
logger.SetHandler(log.StreamHandler(os.Stdout, logfmt))
|
|
}
|
|
|
|
logger.Info("Process is starting", "pid", pid.Get())
|
|
|
|
if len(pidFile) > 0 {
|
|
logger.Info("Writing pidfile", "file", pidFile)
|
|
err := pid.Save(pidFile)
|
|
if err != nil {
|
|
logger.Error("Failed to write pidfile", "msg", err)
|
|
}
|
|
}
|
|
|
|
if req_timeout.Seconds() < 2 {
|
|
// Dont alow anything below 2 seconds. that is abit aggressive.
|
|
logger.Warn("Request timeout is less than the minimum. Setting it to 2 seconds", "req_timeout", req_timeout)
|
|
req_timeout = time.Second * 2
|
|
} else if req_timeout.Minutes() > 1.0 {
|
|
// Anything more than 1 min is too long :)
|
|
logger.Warn("Request timeout is more than the maximum. Setting it to 1 minute", "req_timeout", req_timeout)
|
|
req_timeout = time.Minute
|
|
}
|
|
|
|
// Create server
|
|
srv = server.New(argv_listen_addr(), server.WithTick(time.Second*10), server.WithTimeout(req_timeout))
|
|
|
|
// Run signal event loop in its own goroutine
|
|
go signalEventLoop()
|
|
|
|
// Run server
|
|
if err := srv.Run(); err != nil {
|
|
logger.Error("Server error", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|