开放接口增加详细调试日志,并支持控制台与文件双写
- OPEN_API_DEBUG 记录鉴权、查单、推送业务字段与 req_id - LOG_FILE 默认 logs/app.log,标准日志/Gin/GORM 同时输出控制台与文件
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package applog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// Setup 将标准库 log、Gin 请求日志同时写到控制台与文件。
|
||||
// logFile 为空则仅控制台。返回文件句柄供进程结束时 Close(可为 nil)。
|
||||
func Setup(logFile string) (*os.File, error) {
|
||||
writers := []io.Writer{os.Stdout}
|
||||
var f *os.File
|
||||
if logFile != "" {
|
||||
dir := filepath.Dir(logFile)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
f, err = os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writers = append(writers, f)
|
||||
}
|
||||
|
||||
mw := io.MultiWriter(writers...)
|
||||
log.SetOutput(mw)
|
||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||
|
||||
// Gin access / recovery 日志
|
||||
gin.DefaultWriter = mw
|
||||
gin.DefaultErrorWriter = mw
|
||||
|
||||
if logFile != "" {
|
||||
abs, _ := filepath.Abs(logFile)
|
||||
log.Printf("[applog] 日志同时输出到控制台与文件: %s", abs)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// NewGormLogger 返回写到当前 log 输出(已含 MultiWriter)的 GORM 日志
|
||||
func NewGormLogger(mode string) logger.Interface {
|
||||
level := logger.Info
|
||||
if mode == "release" {
|
||||
level = logger.Warn
|
||||
}
|
||||
return logger.New(
|
||||
log.New(log.Writer(), "\r\n", log.LstdFlags),
|
||||
logger.Config{
|
||||
SlowThreshold: 200 * time.Millisecond,
|
||||
LogLevel: level,
|
||||
IgnoreRecordNotFoundError: true,
|
||||
Colorful: false,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package openlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
CtxReqID = "open_req_id"
|
||||
CtxDebug = "open_debug"
|
||||
CtxBody = "open_body"
|
||||
CtxStart = "open_start"
|
||||
CtxAPIKey = "open_api_key"
|
||||
)
|
||||
|
||||
// Enabled 是否开启开放接口详细日志
|
||||
var Enabled bool
|
||||
|
||||
func Init(enabled bool) {
|
||||
Enabled = enabled
|
||||
if enabled {
|
||||
log.Printf("[open] 开放接口调试日志已开启 (OPEN_API_DEBUG)")
|
||||
}
|
||||
}
|
||||
|
||||
func NewReqID() string {
|
||||
return strings.ReplaceAll(uuid.NewString(), "-", "")[:12]
|
||||
}
|
||||
|
||||
func GetReqID(c *gin.Context) string {
|
||||
if v, ok := c.Get(CtxReqID); ok {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return "-"
|
||||
}
|
||||
|
||||
func EnsureReqID(c *gin.Context) string {
|
||||
if id := GetReqID(c); id != "-" {
|
||||
return id
|
||||
}
|
||||
id := NewReqID()
|
||||
c.Set(CtxReqID, id)
|
||||
return id
|
||||
}
|
||||
|
||||
func IsDebug(c *gin.Context) bool {
|
||||
if !Enabled {
|
||||
return false
|
||||
}
|
||||
if v, ok := c.Get(CtxDebug); ok {
|
||||
if b, ok := v.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return Enabled
|
||||
}
|
||||
|
||||
// MaskKey 脱敏 api_key:保留前缀与后 4 位
|
||||
func MaskKey(key string) string {
|
||||
if key == "" {
|
||||
return "(empty)"
|
||||
}
|
||||
if len(key) <= 8 {
|
||||
return key[:2] + "***"
|
||||
}
|
||||
return key[:8] + "***" + key[len(key)-4:]
|
||||
}
|
||||
|
||||
// MaskSign 脱敏签名:只保留前 8 位
|
||||
func MaskSign(sign string) string {
|
||||
if sign == "" {
|
||||
return "(empty)"
|
||||
}
|
||||
if len(sign) <= 8 {
|
||||
return sign + "..."
|
||||
}
|
||||
return sign[:8] + "..."
|
||||
}
|
||||
|
||||
// Truncate 截断过长字符串
|
||||
func Truncate(s string, max int) string {
|
||||
if max <= 0 || len(s) <= max {
|
||||
return s
|
||||
}
|
||||
return s[:max] + fmt.Sprintf("...(%d bytes)", len(s))
|
||||
}
|
||||
|
||||
func Info(c *gin.Context, format string, args ...interface{}) {
|
||||
if !IsDebug(c) {
|
||||
return
|
||||
}
|
||||
prefix := fmt.Sprintf("[open] req_id=%s ", GetReqID(c))
|
||||
log.Printf(prefix+format, args...)
|
||||
}
|
||||
|
||||
func Warn(c *gin.Context, format string, args ...interface{}) {
|
||||
// 鉴权失败等也值得在 debug 时打出
|
||||
if !Enabled {
|
||||
return
|
||||
}
|
||||
prefix := fmt.Sprintf("[open] req_id=%s ", GetReqID(c))
|
||||
log.Printf(prefix+format, args...)
|
||||
}
|
||||
|
||||
func Elapsed(c *gin.Context) time.Duration {
|
||||
if v, ok := c.Get(CtxStart); ok {
|
||||
if t, ok := v.(time.Time); ok {
|
||||
return time.Since(t)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user