154 lines
4.2 KiB
Go
154 lines
4.2 KiB
Go
package main
|
||
|
||
import (
|
||
"backend/common"
|
||
logincommon "backend/sdk/login/common"
|
||
"backend/sdk/login/model/test"
|
||
"backend/sdk/login/model/tuyou"
|
||
"backend/util"
|
||
"context"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"os/signal"
|
||
"runtime/debug"
|
||
"syscall"
|
||
"time"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||
)
|
||
|
||
var (
|
||
rl *rotatelogs.RotateLogs
|
||
logWriter io.Writer
|
||
errWriter io.Writer
|
||
)
|
||
|
||
func init() {
|
||
// 确保日志目录存在
|
||
if err := os.MkdirAll("./log", 0755); err != nil {
|
||
log.Fatalf("failed to create log dir: %v", err)
|
||
}
|
||
|
||
// 使用按天轮转的日志文件,保留最近 30 个文件
|
||
var err error
|
||
rl, err = rotatelogs.New(
|
||
"./log/login.%Y-%m-%d.log",
|
||
rotatelogs.WithRotationTime(24*time.Hour),
|
||
rotatelogs.WithRotationCount(30),
|
||
)
|
||
if err != nil {
|
||
log.Fatalf("failed to initialize log rotator: %v", err)
|
||
}
|
||
|
||
// 打开一个普通的最新日志文件(不使用 symlink),用于提供固定路径的最新日志
|
||
currFile, err := os.OpenFile("./log/login.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||
if err != nil {
|
||
// 如果打开失败,仍然继续使用轮转器+控制台
|
||
util.LogStructured("warn", "failed to open current log file", map[string]any{"error": err.Error()})
|
||
logWriter = io.MultiWriter(rl, os.Stdout)
|
||
errWriter = io.MultiWriter(rl, os.Stderr)
|
||
} else {
|
||
// 同时输出到轮转日志、固定最新日志文件和控制台
|
||
logWriter = io.MultiWriter(rl, currFile, os.Stdout)
|
||
errWriter = io.MultiWriter(rl, currFile, os.Stderr)
|
||
}
|
||
|
||
log.SetOutput(logWriter)
|
||
|
||
// 保持全局默认 writer(以兼容其他调用)
|
||
gin.DefaultWriter = logWriter
|
||
gin.DefaultErrorWriter = errWriter
|
||
common.Init()
|
||
util.InitBBolt()
|
||
go util.MonitorServerList()
|
||
}
|
||
|
||
func requestLogger() gin.HandlerFunc {
|
||
return func(c *gin.Context) {
|
||
startedAt := time.Now()
|
||
c.Next()
|
||
|
||
util.LogStructured("info", "http request", map[string]any{
|
||
"clientIp": c.ClientIP(),
|
||
"latencyMs": time.Since(startedAt).Milliseconds(),
|
||
"method": c.Request.Method,
|
||
"path": c.Request.URL.Path,
|
||
"query": c.Request.URL.RawQuery,
|
||
"status": c.Writer.Status(),
|
||
"userAgent": c.Request.UserAgent(),
|
||
})
|
||
}
|
||
}
|
||
|
||
func recoveryLogger() gin.HandlerFunc {
|
||
return gin.CustomRecovery(func(c *gin.Context, recovered any) {
|
||
util.LogStructured("error", "panic recovered", map[string]any{
|
||
"clientIp": c.ClientIP(),
|
||
"method": c.Request.Method,
|
||
"path": c.Request.URL.Path,
|
||
"query": c.Request.URL.RawQuery,
|
||
"panic": fmt.Sprint(recovered),
|
||
"stackTrace": string(debug.Stack()),
|
||
"userAgent": c.Request.UserAgent(),
|
||
})
|
||
c.AbortWithStatus(http.StatusInternalServerError)
|
||
})
|
||
}
|
||
|
||
func main() {
|
||
r := gin.New()
|
||
r.Use(requestLogger())
|
||
r.Use(recoveryLogger())
|
||
loginApi := r.Group("/api/auth")
|
||
{
|
||
// 认证
|
||
loginApi.GET("/tuyou/login", tuyou.Login)
|
||
loginApi.GET("/test/login", test.Login)
|
||
}
|
||
server := &http.Server{
|
||
Addr: fmt.Sprintf(":%d", logincommon.AppConf.Port),
|
||
Handler: r,
|
||
ReadHeaderTimeout: 5 * time.Second,
|
||
ReadTimeout: 15 * time.Second,
|
||
WriteTimeout: 30 * time.Second,
|
||
IdleTimeout: 60 * time.Second,
|
||
}
|
||
|
||
shutdownCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||
defer stop()
|
||
|
||
serverErr := make(chan error, 1)
|
||
go func() {
|
||
util.LogStructured("info", "login sdk started", map[string]any{
|
||
"port": logincommon.AppConf.Port,
|
||
"version": logincommon.AppConf.Version,
|
||
})
|
||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||
serverErr <- err
|
||
}
|
||
close(serverErr)
|
||
}()
|
||
|
||
select {
|
||
case err, ok := <-serverErr:
|
||
if ok && err != nil {
|
||
util.LogStructured("error", "login sdk start failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
case <-shutdownCtx.Done():
|
||
util.LogStructured("info", "login sdk shutting down", map[string]any{"reason": shutdownCtx.Err().Error()})
|
||
}
|
||
|
||
gracefulCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
defer cancel()
|
||
if err := server.Shutdown(gracefulCtx); err != nil {
|
||
util.LogStructured("error", "login sdk shutdown failed", map[string]any{"error": err.Error()})
|
||
os.Exit(1)
|
||
}
|
||
util.LogStructured("info", "login sdk stopped", nil)
|
||
}
|