Files
tyapi-server/internal/shared/middleware/comprehensive_logger.go

442 lines
12 KiB
Go
Raw Normal View History

2025-07-28 15:21:37 +08:00
package middleware
import (
"bytes"
"context"
"io"
"strings"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// ComprehensiveLoggerMiddleware 全面日志中间件
type ComprehensiveLoggerMiddleware struct {
logger *zap.Logger
config *ComprehensiveLoggerConfig
}
// ComprehensiveLoggerConfig 全面日志配置
type ComprehensiveLoggerConfig struct {
EnableRequestLogging bool // 是否记录请求日志
EnableResponseLogging bool // 是否记录响应日志
EnableRequestBodyLogging bool // 是否记录请求体
EnableErrorLogging bool // 是否记录错误日志
EnableBusinessLogging bool // 是否记录业务日志
EnablePerformanceLogging bool // 是否记录性能日志
MaxBodySize int64 // 最大记录体大小
ExcludePaths []string // 排除的路径
IncludePaths []string // 包含的路径
}
// NewComprehensiveLoggerMiddleware 创建全面日志中间件
func NewComprehensiveLoggerMiddleware(logger *zap.Logger, config *ComprehensiveLoggerConfig) *ComprehensiveLoggerMiddleware {
if config == nil {
config = &ComprehensiveLoggerConfig{
EnableRequestLogging: true,
EnableResponseLogging: true,
EnableRequestBodyLogging: false, // 生产环境默认关闭
EnableErrorLogging: true,
EnableBusinessLogging: true,
EnablePerformanceLogging: true,
MaxBodySize: 1024 * 10, // 10KB
ExcludePaths: []string{"/health", "/metrics", "/favicon.ico"},
}
}
return &ComprehensiveLoggerMiddleware{
logger: logger,
config: config,
}
}
// GetName 返回中间件名称
func (m *ComprehensiveLoggerMiddleware) GetName() string {
return "comprehensive_logger"
}
// GetPriority 返回中间件优先级
func (m *ComprehensiveLoggerMiddleware) GetPriority() int {
return 90 // 高优先级在panic恢复之后
}
// Handle 返回中间件处理函数
func (m *ComprehensiveLoggerMiddleware) Handle() gin.HandlerFunc {
return func(c *gin.Context) {
// 检查是否应该记录此路径
if !m.shouldLogPath(c.Request.URL.Path) {
c.Next()
return
}
startTime := time.Now()
requestID := c.GetString("request_id")
traceID := c.GetString("trace_id")
userID := c.GetString("user_id")
// 记录请求开始
if m.config.EnableRequestLogging {
m.logRequest(c, startTime, requestID, traceID, userID)
}
// 捕获请求体(如果需要)
var requestBody []byte
if m.config.EnableRequestBodyLogging && m.shouldLogRequestBody(c) {
requestBody = m.captureRequestBody(c)
}
// 创建响应写入器包装器
responseWriter := &responseWriterWrapper{
ResponseWriter: c.Writer,
logger: m.logger,
config: m.config,
requestID: requestID,
traceID: traceID,
userID: userID,
startTime: startTime,
path: c.Request.URL.Path,
method: c.Request.Method,
}
c.Writer = responseWriter
// 处理请求
c.Next()
// 记录响应
if m.config.EnableResponseLogging {
m.logResponse(c, responseWriter, startTime, requestID, traceID, userID, requestBody)
}
// 记录错误
if m.config.EnableErrorLogging && len(c.Errors) > 0 {
m.logErrors(c, requestID, traceID, userID)
}
// 记录性能指标
if m.config.EnablePerformanceLogging {
m.logPerformance(c, startTime, requestID, traceID, userID)
}
}
}
// IsGlobal 是否为全局中间件
func (m *ComprehensiveLoggerMiddleware) IsGlobal() bool {
return true
}
// shouldLogPath 检查是否应该记录此路径
func (m *ComprehensiveLoggerMiddleware) shouldLogPath(path string) bool {
// 检查排除路径
for _, excludePath := range m.config.ExcludePaths {
if strings.HasPrefix(path, excludePath) {
return false
}
}
// 检查包含路径(如果指定了)
if len(m.config.IncludePaths) > 0 {
for _, includePath := range m.config.IncludePaths {
if strings.HasPrefix(path, includePath) {
return true
}
}
return false
}
return true
}
// shouldLogRequestBody 检查是否应该记录请求体
func (m *ComprehensiveLoggerMiddleware) shouldLogRequestBody(c *gin.Context) bool {
contentType := c.GetHeader("Content-Type")
return strings.Contains(contentType, "application/json") ||
strings.Contains(contentType, "application/x-www-form-urlencoded")
}
// captureRequestBody 捕获请求体
func (m *ComprehensiveLoggerMiddleware) captureRequestBody(c *gin.Context) []byte {
if c.Request.Body == nil {
return nil
}
body, err := io.ReadAll(c.Request.Body)
if err != nil {
return nil
}
// 重新设置body
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
// 限制大小
if int64(len(body)) > m.config.MaxBodySize {
return body[:m.config.MaxBodySize]
}
return body
}
// logRequest 记录请求日志
func (m *ComprehensiveLoggerMiddleware) logRequest(c *gin.Context, startTime time.Time, requestID, traceID, userID string) {
logFields := []zap.Field{
zap.String("log_type", "request"),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.String("query", c.Request.URL.RawQuery),
zap.String("client_ip", c.ClientIP()),
zap.String("user_agent", c.Request.UserAgent()),
zap.String("referer", c.Request.Referer()),
zap.Int64("content_length", c.Request.ContentLength),
zap.String("content_type", c.GetHeader("Content-Type")),
zap.Time("timestamp", startTime),
}
m.logger.Info("收到HTTP请求", logFields...)
}
// logResponse 记录响应日志
func (m *ComprehensiveLoggerMiddleware) logResponse(c *gin.Context, responseWriter *responseWriterWrapper, startTime time.Time, requestID, traceID, userID string, requestBody []byte) {
duration := time.Since(startTime)
statusCode := responseWriter.Status()
logFields := []zap.Field{
zap.String("log_type", "response"),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.Int("status_code", statusCode),
zap.Duration("duration", duration),
zap.Int("response_size", responseWriter.Size()),
zap.Time("timestamp", time.Now()),
}
// 添加请求体(如果记录了)
if len(requestBody) > 0 {
logFields = append(logFields, zap.String("request_body", string(requestBody)))
}
// 根据状态码选择日志级别
if statusCode >= 500 {
m.logger.Error("HTTP响应错误", logFields...)
} else if statusCode >= 400 {
m.logger.Warn("HTTP响应警告", logFields...)
} else {
m.logger.Info("HTTP响应成功", logFields...)
}
}
// logErrors 记录错误日志
func (m *ComprehensiveLoggerMiddleware) logErrors(c *gin.Context, requestID, traceID, userID string) {
for _, ginErr := range c.Errors {
logFields := []zap.Field{
zap.String("log_type", "error"),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.String("error_type", string(ginErr.Type)),
zap.Error(ginErr.Err),
zap.Time("timestamp", time.Now()),
}
m.logger.Error("请求处理错误", logFields...)
}
}
// logPerformance 记录性能日志
func (m *ComprehensiveLoggerMiddleware) logPerformance(c *gin.Context, startTime time.Time, requestID, traceID, userID string) {
duration := time.Since(startTime)
// 记录慢请求
if duration > 1*time.Second {
logFields := []zap.Field{
zap.String("log_type", "performance"),
zap.String("performance_type", "slow_request"),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.Duration("duration", duration),
zap.Time("timestamp", time.Now()),
}
m.logger.Warn("检测到慢请求", logFields...)
}
// 记录性能指标
logFields := []zap.Field{
zap.String("log_type", "performance"),
zap.String("performance_type", "request_metrics"),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.String("method", c.Request.Method),
zap.String("path", c.Request.URL.Path),
zap.Duration("duration", duration),
zap.Time("timestamp", time.Now()),
}
m.logger.Debug("请求性能指标", logFields...)
}
// responseWriterWrapper 响应写入器包装器
type responseWriterWrapper struct {
gin.ResponseWriter
logger *zap.Logger
config *ComprehensiveLoggerConfig
requestID string
traceID string
userID string
startTime time.Time
path string
method string
status int
size int
}
// Write 实现Write方法
func (w *responseWriterWrapper) Write(b []byte) (int, error) {
size, err := w.ResponseWriter.Write(b)
w.size += size
return size, err
}
// WriteHeader 实现WriteHeader方法
func (w *responseWriterWrapper) WriteHeader(code int) {
w.status = code
w.ResponseWriter.WriteHeader(code)
}
// WriteString 实现WriteString方法
func (w *responseWriterWrapper) WriteString(s string) (int, error) {
size, err := w.ResponseWriter.WriteString(s)
w.size += size
return size, err
}
// Status 获取状态码
func (w *responseWriterWrapper) Status() int {
return w.status
}
// Size 获取响应大小
func (w *responseWriterWrapper) Size() int {
return w.size
}
// BusinessLogger 业务日志记录器
type BusinessLogger struct {
logger *zap.Logger
}
// NewBusinessLogger 创建业务日志记录器
func NewBusinessLogger(logger *zap.Logger) *BusinessLogger {
return &BusinessLogger{
logger: logger,
}
}
// LogUserAction 记录用户操作
func (bl *BusinessLogger) LogUserAction(ctx context.Context, action string, details map[string]interface{}) {
requestID := bl.getRequestIDFromContext(ctx)
traceID := bl.getTraceIDFromContext(ctx)
userID := bl.getUserIDFromContext(ctx)
logFields := []zap.Field{
zap.String("log_type", "business"),
zap.String("business_type", "user_action"),
zap.String("action", action),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.Time("timestamp", time.Now()),
}
// 添加详细信息
for key, value := range details {
logFields = append(logFields, zap.Any(key, value))
}
bl.logger.Info("用户操作记录", logFields...)
}
// LogBusinessEvent 记录业务事件
func (bl *BusinessLogger) LogBusinessEvent(ctx context.Context, event string, details map[string]interface{}) {
requestID := bl.getRequestIDFromContext(ctx)
traceID := bl.getTraceIDFromContext(ctx)
userID := bl.getUserIDFromContext(ctx)
logFields := []zap.Field{
zap.String("log_type", "business"),
zap.String("business_type", "business_event"),
zap.String("event", event),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.String("user_id", userID),
zap.Time("timestamp", time.Now()),
}
// 添加详细信息
for key, value := range details {
logFields = append(logFields, zap.Any(key, value))
}
bl.logger.Info("业务事件记录", logFields...)
}
// LogSystemEvent 记录系统事件
func (bl *BusinessLogger) LogSystemEvent(ctx context.Context, event string, details map[string]interface{}) {
requestID := bl.getRequestIDFromContext(ctx)
traceID := bl.getTraceIDFromContext(ctx)
logFields := []zap.Field{
zap.String("log_type", "business"),
zap.String("business_type", "system_event"),
zap.String("event", event),
zap.String("request_id", requestID),
zap.String("trace_id", traceID),
zap.Time("timestamp", time.Now()),
}
// 添加详细信息
for key, value := range details {
logFields = append(logFields, zap.Any(key, value))
}
bl.logger.Info("系统事件记录", logFields...)
}
// 辅助方法
func (bl *BusinessLogger) getRequestIDFromContext(ctx context.Context) string {
if requestID := ctx.Value("request_id"); requestID != nil {
if id, ok := requestID.(string); ok {
return id
}
}
return ""
}
func (bl *BusinessLogger) getTraceIDFromContext(ctx context.Context) string {
if traceID := ctx.Value("trace_id"); traceID != nil {
if id, ok := traceID.(string); ok {
return id
}
}
return ""
}
func (bl *BusinessLogger) getUserIDFromContext(ctx context.Context) string {
if userID := ctx.Value("user_id"); userID != nil {
if id, ok := userID.(string); ok {
return id
}
}
return ""
}