119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"git.apinb.com/bsm-sdk/core/infra"
|
|
"git.apinb.com/bsm-sdk/core/middleware"
|
|
"git.apinb.com/ops/logs/internal/config"
|
|
"git.apinb.com/ops/logs/internal/impl"
|
|
"git.apinb.com/ops/logs/internal/ingest"
|
|
"git.apinb.com/ops/logs/internal/models"
|
|
"git.apinb.com/ops/logs/internal/routers"
|
|
settingssync "git.apinb.com/ops/logs/internal/systemsettings"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
var ServiceKey = "Logs"
|
|
|
|
func main() {
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
var backgroundJobs sync.WaitGroup
|
|
|
|
config.New(ServiceKey)
|
|
settingsClient, err := settingssync.LoadInitial(ctx)
|
|
if err != nil {
|
|
panic(fmt.Errorf("logs: 初始化系统参数失败: %w", err))
|
|
}
|
|
impl.NewImpl()
|
|
if err := models.RequireSchemaVersion(impl.DBService, "logs"); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := ingest.StartRefresher(ctx); err != nil {
|
|
shutdownAfterStartupFailure(stop, err)
|
|
}
|
|
ingest.StartAlertDispatcher(ctx)
|
|
if err := ingest.StartSyslogUDP(ctx); err != nil {
|
|
shutdownAfterStartupFailure(stop, err)
|
|
}
|
|
if err := ingest.StartTrapUDP(ctx); err != nil {
|
|
shutdownAfterStartupFailure(stop, err)
|
|
}
|
|
backgroundJobs.Add(1)
|
|
go func() {
|
|
defer backgroundJobs.Done()
|
|
settingssync.Start(ctx, settingsClient)
|
|
}()
|
|
|
|
app := gin.Default()
|
|
middleware.Mode(app)
|
|
app.Use(middleware.Cors())
|
|
app.Use(gin.Recovery())
|
|
app.HEAD("/", infra.Health)
|
|
routers.Register(ServiceKey, app)
|
|
|
|
server := &http.Server{Addr: config.Spec.Addr, Handler: app, ReadHeaderTimeout: 10 * time.Second}
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
errCh <- server.ListenAndServe()
|
|
}()
|
|
|
|
var serveErr error
|
|
select {
|
|
case err := <-errCh:
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
serveErr = err
|
|
}
|
|
case <-ctx.Done():
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
|
serveErr = err
|
|
}
|
|
}
|
|
stop()
|
|
if !ingest.Wait(20 * time.Second) {
|
|
serveErr = errors.Join(serveErr, fmt.Errorf("后台 worker 未在退出超时内结束"))
|
|
}
|
|
if !waitForBackgroundJobs(&backgroundJobs, 20*time.Second) {
|
|
serveErr = errors.Join(serveErr, fmt.Errorf("系统参数任务未在退出超时内结束"))
|
|
}
|
|
if err := errors.Join(serveErr, impl.Close()); err != nil {
|
|
log.Printf("logs: 服务退出失败: %v", err)
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func waitForBackgroundJobs(wg *sync.WaitGroup, timeout time.Duration) bool {
|
|
done := make(chan struct{})
|
|
go func() {
|
|
wg.Wait()
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-done:
|
|
return true
|
|
case <-time.After(timeout):
|
|
return false
|
|
}
|
|
}
|
|
|
|
func shutdownAfterStartupFailure(stop context.CancelFunc, startErr error) {
|
|
stop()
|
|
if !ingest.Wait(20 * time.Second) {
|
|
startErr = errors.Join(startErr, fmt.Errorf("后台 worker 未在退出超时内结束"))
|
|
}
|
|
panic(errors.Join(startErr, impl.Close()))
|
|
}
|