442 lines
12 KiB
Go
442 lines
12 KiB
Go
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 ""
|
||
} |