u
This commit is contained in:
1 parent
3420976bd6
commit
ad47e5167b
8 files changed
+184
-70
No files matched your search
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
+8
-10
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
})
|
||||
|
||||
|
||||
@@ -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"}
|
||||
@@ -1 +0,0 @@
|
||||
{"level":"INFO","time":"2026-04-02T22:07:05.693+0800","caller":"app/main.go:15","msg":"shutting down server..."}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
// ======================
|
||||
// 优雅退出
|
||||
// ======================
|
||||
|
||||
Reference in new issue
Block a user