diff --git a/code/allapp/allapp-go-v3/cmd/app/main.go b/code/allapp/allapp-go-v3/cmd/app/main.go index 97a0e3ad..eaf62798 100644 --- a/code/allapp/allapp-go-v3/cmd/app/main.go +++ b/code/allapp/allapp-go-v3/cmd/app/main.go @@ -12,7 +12,7 @@ func main() { defer app.Close() - if err := app.Run(); err != nil { + if err := bootstrap.RunServer(app.Config); err != nil { panic(err) } } diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/app.go b/code/allapp/allapp-go-v3/internal/bootstrap/app.go index 8fb98979..47a73f8c 100644 --- a/code/allapp/allapp-go-v3/internal/bootstrap/app.go +++ b/code/allapp/allapp-go-v3/internal/bootstrap/app.go @@ -4,9 +4,6 @@ import ( "allapp-go/internal/bootstrap/boots" "allapp-go/internal/config" "allapp-go/pkg/logger" - "os" - "os/signal" - "syscall" "github.com/jackc/pgx/v5/pgxpool" ) @@ -16,7 +13,10 @@ type App struct { PgSql *pgxpool.Pool } -// NewApp 初始化(不阻塞) +// ====================== +// 初始化 +// ====================== + func NewApp() (*App, error) { cfg, err := config.Load() if err != nil { @@ -26,6 +26,7 @@ func NewApp() (*App, error) { // 日志 boots.InitLogger(cfg) + // 数据库 pgsql, err := boots.InitPgsql(cfg) if err != nil { return nil, err @@ -41,30 +42,10 @@ func NewApp() (*App, error) { }, nil } -// Run 启动服务(阻塞) -func (a *App) Run() error { - app, err := boots.RunServer(a.Config) - if err != nil { - return err - } +// ====================== +// 释放资源 +// ====================== - // 监听退出信号 - quit := make(chan os.Signal, 1) - signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) - - <-quit - - logger.Log.Info("shutting down server...") - - if err := app.Shutdown(); err != nil { - logger.Log.Error("shutdown failed") - return err - } - - return nil -} - -// Close 释放资源 func (a *App) Close() { if a.PgSql != nil { a.PgSql.Close() diff --git a/code/allapp/allapp-go-v3/internal/bootstrap/boots/server.go b/code/allapp/allapp-go-v3/internal/bootstrap/server.go similarity index 58% rename from code/allapp/allapp-go-v3/internal/bootstrap/boots/server.go rename to code/allapp/allapp-go-v3/internal/bootstrap/server.go index c0789515..6133bbd6 100644 --- a/code/allapp/allapp-go-v3/internal/bootstrap/boots/server.go +++ b/code/allapp/allapp-go-v3/internal/bootstrap/server.go @@ -1,32 +1,30 @@ -package boots +package bootstrap import ( "allapp-go/internal/config" + "allapp-go/internal/middleware" "allapp-go/internal/router" + "allapp-go/pkg/logger" "fmt" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/requestid" ) -func RunServer(cfg *config.Config) (*fiber.App, error) { +func RunServer(cfg *config.Config) error { app := fiber.New() // ===== middleware ===== app.Use(requestid.New()) - - // TODO: 你可以加 logger middleware / recover + app.Use(middleware.Logger()) // ===== router ===== router.Setup(app) addr := fmt.Sprintf(":%d", cfg.App.Port) - go func() { - if err := app.Listen(addr); err != nil { - panic(err) - } - }() + logger.Log.Info("服务启动") - return app, nil + // ❗ 关键:直接阻塞 + return app.Listen(addr) } diff --git a/code/allapp/allapp-go-v3/internal/middleware/logger.go b/code/allapp/allapp-go-v3/internal/middleware/logger.go new file mode 100644 index 00000000..1490fb73 --- /dev/null +++ b/code/allapp/allapp-go-v3/internal/middleware/logger.go @@ -0,0 +1,35 @@ +package middleware + +import ( + "allapp-go/pkg/logger" + "time" + + "github.com/gofiber/fiber/v3" + "go.uber.org/zap" +) + +func Logger() fiber.Handler { + return func(c fiber.Ctx) error { + traceID := c.RequestID() + + logger.SetTraceID(c, traceID) + + start := time.Now() + + err := c.Next() + + // ✅ 计算耗时 + cost := time.Since(start) + + log := logger.FromCtx(c.Context()) + log.Info("HTTP 请求", + zap.String("method", c.Method()), + zap.String("path", c.Path()), + zap.Int("status", c.Response().StatusCode()), + zap.String("ip", c.IP()), + zap.String("duration", cost.String()), + ) + + return err + } +} diff --git a/code/allapp/allapp-go-v3/internal/router/router.go b/code/allapp/allapp-go-v3/internal/router/router.go index 87cb7da1..3e6282f1 100644 --- a/code/allapp/allapp-go-v3/internal/router/router.go +++ b/code/allapp/allapp-go-v3/internal/router/router.go @@ -1,12 +1,18 @@ package router import ( + "allapp-go/pkg/logger" + "github.com/gofiber/fiber/v3" ) func Setup(app *fiber.App) { // 健康检查 app.Get("/ping", func(c fiber.Ctx) error { + log := logger.FromCtx(c) + + log.Info("测试") + return c.SendString("pong") }) diff --git a/code/allapp/allapp-go-v3/logs/info/2026-04-03.log b/code/allapp/allapp-go-v3/logs/info/2026-04-03.log new file mode 100644 index 00000000..a27012bb --- /dev/null +++ b/code/allapp/allapp-go-v3/logs/info/2026-04-03.log @@ -0,0 +1,64 @@ +{"level":"INFO","time":"2026-04-03T15:49:55.765+0800","caller":"app/main.go:15","msg":"服务停止...","service":"allapp-go"} +{"level":"INFO","time":"2026-04-03T16:14:38.128+0800","caller":"app/main.go:15","msg":"服务停止...","service":"allapp-go"} +{"level":"INFO","time":"2026-04-03T16:14:49.107+0800","caller":"v3@v3.1.0/ctx.go:252","msg":"HTTP Request","service":"allapp-go","trace_id":"gft4NtXgApIcOA_VjqiE1rJojH1dMzwZT9CzFYzYAXA"} +{"level":"INFO","time":"2026-04-03T16:15:26.633+0800","caller":"app/main.go:15","msg":"服务停止...","service":"allapp-go"} +{"level":"INFO","time":"2026-04-03T16:15:53.500+0800","msg":"HTTP Request","trace_id":"Y9W3uFZVY21B5TEpJg9yyHCf9NOxArUXvZV_qJlrlCM"} +{"level":"INFO","time":"2026-04-03T16:16:33.816+0800","msg":"服务停止..."} +{"level":"INFO","time":"2026-04-03T16:16:46.436+0800","msg":"测试","trace_id":"5yrg3bI_h76pEzG249pZQXBuiIdbRMX_NZwU5uFR2OE"} +{"level":"INFO","time":"2026-04-03T16:16:46.456+0800","msg":"HTTP Request","trace_id":"5yrg3bI_h76pEzG249pZQXBuiIdbRMX_NZwU5uFR2OE"} +{"level":"INFO","time":"2026-04-03T16:27:21.073+0800","msg":"服务停止..."} +{"level":"INFO","time":"2026-04-03T16:27:30.054+0800","msg":"测试","trace_id":"zL-aFoHmf6ueyOkQB3HlIWR1OCEx4HZNG7qJt7pM7zM"} +{"level":"INFO","time":"2026-04-03T16:27:30.076+0800","msg":"HTTP Request","trace_id":"zL-aFoHmf6ueyOkQB3HlIWR1OCEx4HZNG7qJt7pM7zM"} +{"level":"INFO","time":"2026-04-03T16:27:55.631+0800","msg":"服务停止..."} +{"level":"INFO","time":"2026-04-03T16:45:35.641+0800","msg":"服务停止..."} +{"level":"INFO","time":"2026-04-03T16:45:40.240+0800","msg":"服务停止..."} +{"level":"INFO","time":"2026-04-03T16:45:45.270+0800","msg":"测试","trace_id":"wkEAsYzV99gWsa3kWmLG30kJMQHQEjDu9ocxNxLQzQU"} +{"level":"INFO","time":"2026-04-03T16:45:45.289+0800","msg":"HTTP Request","trace_id":"wkEAsYzV99gWsa3kWmLG30kJMQHQEjDu9ocxNxLQzQU"} +{"level":"INFO","time":"2026-04-03T16:45:48.144+0800","msg":"服务停止..."} +{"level":"INFO","time":"2026-04-03T16:56:48.995+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T16:56:52.533+0800","msg":"测试","trace_id":"Fbc4o1PTmARfNbFYAM-iY43s84GNPdMXWq4c7IgXCds"} +{"level":"INFO","time":"2026-04-03T16:56:52.533+0800","msg":"HTTP Request","trace_id":"Fbc4o1PTmARfNbFYAM-iY43s84GNPdMXWq4c7IgXCds"} +{"level":"INFO","time":"2026-04-03T16:56:56.909+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T16:57:04.658+0800","msg":"测试","trace_id":"Me6jLlSvQGHapO-VJho73fMiOYcOyYThbpAafpGhqE8"} +{"level":"INFO","time":"2026-04-03T16:57:04.658+0800","msg":"HTTP Request","trace_id":"Me6jLlSvQGHapO-VJho73fMiOYcOyYThbpAafpGhqE8"} +{"level":"INFO","time":"2026-04-03T16:57:49.543+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T16:58:32.252+0800","msg":"测试"} +{"level":"INFO","time":"2026-04-03T16:58:32.252+0800","msg":"HTTP Request","trace_id":"kFurkMd7r6FIBAMzNZzEmzIoD81UnqYusKulk-lRXNY"} +{"level":"INFO","time":"2026-04-03T17:02:09.848+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:05:19.925+0800","msg":"测试"} +{"level":"INFO","time":"2026-04-03T17:05:19.926+0800","msg":"HTTP Request","trace_id":"1NK3UDYFdnsUQ_gqapDlrtLuwQHjePUe5-QgDuTUZEY"} +{"level":"INFO","time":"2026-04-03T17:05:25.218+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:05:26.759+0800","msg":"测试"} +{"level":"INFO","time":"2026-04-03T17:05:26.759+0800","msg":"HTTP Request","trace_id":"3wfoCwM25ZVd2ALWs8sgoyj8UBimSTiYNu27x_bCfT4"} +{"level":"INFO","time":"2026-04-03T17:07:25.207+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:07:31.929+0800","msg":"测试","trace_id":"N_1ZQOZcUXnvPG6IWNy3CoqlOCBs3WSWIx8d8-ftY7I"} +{"level":"INFO","time":"2026-04-03T17:07:31.929+0800","msg":"HTTP Request","trace_id":"N_1ZQOZcUXnvPG6IWNy3CoqlOCBs3WSWIx8d8-ftY7I"} +{"level":"INFO","time":"2026-04-03T17:08:17.051+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:08:48.035+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:10:13.495+0800","msg":"测试","trace_id":"poaftsTyHSfOg4sf1iCd76aehpVwgqCC_oV1F3j3uW8"} +{"level":"INFO","time":"2026-04-03T17:10:13.495+0800","msg":"HTTP Request","trace_id":"poaftsTyHSfOg4sf1iCd76aehpVwgqCC_oV1F3j3uW8"} +{"level":"INFO","time":"2026-04-03T17:12:36.069+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:12:39.296+0800","msg":"测试","trace_id":"I_Cu1O_AKGduLgrC5BQ9BmMvjaKzxX2z0ErHTYAp95I"} +{"level":"INFO","time":"2026-04-03T17:12:39.297+0800","msg":"HTTP 请求","trace_id":"I_Cu1O_AKGduLgrC5BQ9BmMvjaKzxX2z0ErHTYAp95I","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":999200} +{"level":"INFO","time":"2026-04-03T17:14:18.164+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:14:20.840+0800","msg":"测试","trace_id":"RaYGTF5vYvcYH510JISeHXT3q9nAMuaX3CVytD5dbG4"} +{"level":"INFO","time":"2026-04-03T17:14:20.840+0800","msg":"HTTP 请求","trace_id":"RaYGTF5vYvcYH510JISeHXT3q9nAMuaX3CVytD5dbG4","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","latency":283600,"latency_ms":0} +{"level":"INFO","time":"2026-04-03T17:16:05.807+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:16:08.888+0800","msg":"测试","trace_id":"6AcgmyWwTVAjeNp-X4kQzrfwin6AMt4H0WNxTUcAndI"} +{"level":"INFO","time":"2026-04-03T17:16:08.888+0800","msg":"HTTP 请求","trace_id":"6AcgmyWwTVAjeNp-X4kQzrfwin6AMt4H0WNxTUcAndI","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","latency_ms":0} +{"level":"INFO","time":"2026-04-03T17:16:38.443+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:16:41.373+0800","msg":"测试","trace_id":"iRArQodllLgkkUoNaLQxx19tnGcMZJCLjb289tZCeLo"} +{"level":"INFO","time":"2026-04-03T17:16:41.373+0800","msg":"HTTP 请求","trace_id":"iRArQodllLgkkUoNaLQxx19tnGcMZJCLjb289tZCeLo","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":"504.7µs"} +{"level":"INFO","time":"2026-04-03T17:22:21.980+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:22:24.708+0800","msg":"测试","trace_id":"-A1bWcH4S56yPgY7QtcNJKVEfk7dZYYO3iE8UwdfGec"} +{"level":"INFO","time":"2026-04-03T17:22:24.708+0800","msg":"HTTP 请求","trace_id":"-A1bWcH4S56yPgY7QtcNJKVEfk7dZYYO3iE8UwdfGec","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":"0s"} +{"level":"INFO","time":"2026-04-03T17:22:33.502+0800","msg":"测试","trace_id":"95otefa9Cf-0DKIn1wJyhtAK137RF39I928RCKw1wXU"} +{"level":"INFO","time":"2026-04-03T17:22:33.502+0800","msg":"HTTP 请求","trace_id":"95otefa9Cf-0DKIn1wJyhtAK137RF39I928RCKw1wXU","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":"0s"} +{"level":"INFO","time":"2026-04-03T17:22:35.893+0800","msg":"测试","trace_id":"mKSzOa9FqlG6tfribi2yZhkXUPUXMYJocWs8TvQ0gzk"} +{"level":"INFO","time":"2026-04-03T17:22:35.893+0800","msg":"HTTP 请求","trace_id":"mKSzOa9FqlG6tfribi2yZhkXUPUXMYJocWs8TvQ0gzk","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":"0s"} +{"level":"INFO","time":"2026-04-03T17:22:54.306+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:23:46.090+0800","msg":"测试","trace_id":"dcgGTmUE4Zhaqm2GPGhKplL2ypgRrHb2yfsPYWpasAk"} +{"level":"INFO","time":"2026-04-03T17:23:46.090+0800","msg":"HTTP 请求","trace_id":"dcgGTmUE4Zhaqm2GPGhKplL2ypgRrHb2yfsPYWpasAk","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":"557.7µs"} +{"level":"INFO","time":"2026-04-03T17:25:10.656+0800","msg":"服务启动"} +{"level":"INFO","time":"2026-04-03T17:25:12.876+0800","msg":"测试","trace_id":"HP6MsYAn3gEeurwlsBPImOgJHuEcwOcaPeTIUiFJnbA"} +{"level":"INFO","time":"2026-04-03T17:25:12.876+0800","msg":"HTTP 请求","trace_id":"HP6MsYAn3gEeurwlsBPImOgJHuEcwOcaPeTIUiFJnbA","method":"GET","path":"/ping","status":200,"ip":"127.0.0.1","duration":"31µs"} diff --git a/code/allapp/allapp-go-v3/logs/info/info.log b/code/allapp/allapp-go-v3/logs/info/info.log deleted file mode 100644 index c1e31c30..00000000 --- a/code/allapp/allapp-go-v3/logs/info/info.log +++ /dev/null @@ -1 +0,0 @@ -{"level":"INFO","time":"2026-04-02T22:07:05.693+0800","caller":"app/main.go:15","msg":"shutting down server..."} diff --git a/code/allapp/allapp-go-v3/pkg/logger/logger.go b/code/allapp/allapp-go-v3/pkg/logger/logger.go index 6b72c79f..8972c6c3 100644 --- a/code/allapp/allapp-go-v3/pkg/logger/logger.go +++ b/code/allapp/allapp-go-v3/pkg/logger/logger.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "time" "github.com/gofiber/fiber/v3" "go.uber.org/zap" @@ -59,10 +60,8 @@ func Init(cfg Config) { // 控制台 consoleWriter := zapcore.AddSync(os.Stdout) - // 文件 writer(info) + // 文件 writer(按天) infoWriter := getWriter(cfg, "info") - - // 文件 writer(error) errorWriter := getWriter(cfg, "error") // info core @@ -85,24 +84,66 @@ func Init(cfg Config) { core := zapcore.NewTee(infoCore, errorCore) - Log = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) + Log = zap.New( + core, + ) } // ====================== -// lumberjack writer +// Daily Writer(核心) +// ====================== + +type DailyWriter struct { + cfg Config + level string + log *lumberjack.Logger + date string +} + +func newDailyWriter(cfg Config, level string) *DailyWriter { + return &DailyWriter{ + cfg: cfg, + level: level, + } +} + +func (w *DailyWriter) Write(p []byte) (n int, err error) { + today := time.Now().Format("2006-01-02") + + // 跨天自动切换 + if w.log == nil || w.date != today { + dir := filepath.Join(w.cfg.Dir, w.level) + _ = os.MkdirAll(dir, os.ModePerm) + + filename := filepath.Join(dir, today+".log") + + w.log = &lumberjack.Logger{ + Filename: filename, + MaxSize: w.cfg.MaxSize, + MaxBackups: w.cfg.MaxBackups, + MaxAge: w.cfg.MaxAge, + Compress: w.cfg.Compress, + } + + w.date = today + } + + return w.log.Write(p) +} + +func (w *DailyWriter) Sync() error { + if w.log != nil { + return w.log.Close() + } + return nil +} + +// ====================== +// writer 封装 // ====================== func getWriter(cfg Config, level string) zapcore.WriteSyncer { - dir := filepath.Join(cfg.Dir, level) - _ = os.MkdirAll(dir, os.ModePerm) - - return zapcore.AddSync(&lumberjack.Logger{ - Filename: filepath.Join(dir, level+".log"), - MaxSize: cfg.MaxSize, - MaxBackups: cfg.MaxBackups, - MaxAge: cfg.MaxAge, - Compress: cfg.Compress, - }) + return zapcore.AddSync(newDailyWriter(cfg, level)) } // ====================== @@ -123,24 +164,19 @@ func parseLevel(lvl string) zapcore.Level { } // ====================== -// trace 支持(核心) +// trace 支持 // ====================== -// Ctx Fiber 中使用 -func Ctx(c fiber.Ctx) *zap.Logger { +func SetTraceID(c fiber.Ctx, traceID string) { if c == nil { - return Log + return } - traceID, _ := c.Locals(TraceIDKey).(string) - if traceID == "" { - return Log - } - - return Log.With(zap.String("trace_id", traceID)) + // context + ctx := context.WithValue(c.Context(), TraceIDKey, traceID) + c.SetContext(ctx) } -// FromCtx Service / Repo 使用 func FromCtx(ctx context.Context) *zap.Logger { if ctx == nil { return Log @@ -154,11 +190,6 @@ func FromCtx(ctx context.Context) *zap.Logger { return Log.With(zap.String("trace_id", traceID)) } -// WithTrace 注入 trace -func WithTrace(ctx context.Context, traceID string) context.Context { - return context.WithValue(ctx, TraceIDKey, traceID) -} - // ====================== // 优雅退出 // ======================