log.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package log
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/gin-gonic/gin"
  6. "github.com/spf13/viper"
  7. "go.uber.org/zap"
  8. "go.uber.org/zap/zapcore"
  9. "gopkg.in/natefinch/lumberjack.v2"
  10. "os"
  11. "path/filepath"
  12. "time"
  13. )
  14. const ctxLoggerKey = "zapLogger"
  15. // ServiceType 服务类型
  16. type ServiceType int
  17. const (
  18. API ServiceType = iota // API服务
  19. Task // Task服务
  20. )
  21. type Logger struct {
  22. *zap.Logger
  23. }
  24. // NewLog 创建一个新的日志实例,兼容旧版本配置
  25. func NewLog(conf *viper.Viper) *Logger {
  26. return NewServiceLog(conf, API)
  27. }
  28. // NewServiceLog 创建特定服务类型的日志实例
  29. func NewServiceLog(conf *viper.Viper, serviceType ServiceType) *Logger {
  30. var logPath string
  31. // 根据服务类型和日期生成日志文件名
  32. logFormat := conf.GetString("log.log_format") // 日期格式,如"2006-01-02"
  33. currentDate := time.Now().Format(logFormat)
  34. if serviceType == API && conf.IsSet("log.api_log_file") {
  35. // 使用API日志路径
  36. logPath = conf.GetString("log.api_log_file")
  37. logPath = fmt.Sprintf(logPath, currentDate) // 替换日期占位符
  38. } else if serviceType == Task && conf.IsSet("log.task_log_file") {
  39. // 使用Task日志路径
  40. logPath = conf.GetString("log.task_log_file")
  41. logPath = fmt.Sprintf(logPath, currentDate) // 替换日期占位符
  42. } else {
  43. // 如果未配置专用日志路径,则使用旧配置
  44. logPath = conf.GetString("log.log_file_name")
  45. }
  46. // 确保日志目录存在
  47. if err := ensureDirExists(logPath); err != nil {
  48. // 如果创建目录失败,回退到临时目录
  49. fmt.Printf("Error creating log directory: %v, using default path\n", err)
  50. logPath = "./logs/app.log"
  51. ensureDirExists(logPath)
  52. }
  53. // 获取日志级别
  54. lv := conf.GetString("log.log_level")
  55. var level zapcore.Level
  56. //debug<info<warn<error<fatal<panic
  57. switch lv {
  58. case "debug":
  59. level = zap.DebugLevel
  60. case "info":
  61. level = zap.InfoLevel
  62. case "warn":
  63. level = zap.WarnLevel
  64. case "error":
  65. level = zap.ErrorLevel
  66. default:
  67. level = zap.InfoLevel
  68. }
  69. // 日志轮转配置
  70. hook := lumberjack.Logger{
  71. Filename: logPath, // 日志文件路径
  72. MaxSize: conf.GetInt("log.max_size"), // 每个日志文件的最大大小单位:M
  73. MaxBackups: conf.GetInt("log.max_backups"), // 日志文件最多保存的备份数
  74. MaxAge: conf.GetInt("log.max_age"), // 文件最多保存的天数
  75. Compress: conf.GetBool("log.compress"), // 是否压缩
  76. }
  77. // 配置日志编码器
  78. var encoder zapcore.Encoder
  79. if conf.GetString("log.encoding") == "console" {
  80. encoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
  81. TimeKey: "ts",
  82. LevelKey: "level",
  83. NameKey: "Logger",
  84. CallerKey: "caller",
  85. MessageKey: "msg",
  86. StacktraceKey: "stacktrace",
  87. LineEnding: zapcore.DefaultLineEnding,
  88. EncodeLevel: zapcore.LowercaseColorLevelEncoder,
  89. EncodeTime: timeEncoder,
  90. EncodeDuration: zapcore.SecondsDurationEncoder,
  91. EncodeCaller: zapcore.FullCallerEncoder,
  92. })
  93. } else {
  94. encoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{
  95. TimeKey: "time",
  96. LevelKey: "level",
  97. NameKey: "logger",
  98. CallerKey: "caller",
  99. FunctionKey: zapcore.OmitKey,
  100. MessageKey: "msg",
  101. StacktraceKey: "stacktrace",
  102. LineEnding: zapcore.DefaultLineEnding,
  103. EncodeLevel: zapcore.LowercaseLevelEncoder,
  104. EncodeTime: timeEncoder, // 使用自定义时间格式
  105. EncodeDuration: zapcore.StringDurationEncoder,
  106. EncodeCaller: zapcore.ShortCallerEncoder,
  107. })
  108. }
  109. // 创建日志核心
  110. logOutput := zapcore.NewMultiWriteSyncer(
  111. zapcore.AddSync(os.Stdout), // 同时输出到控制台
  112. zapcore.AddSync(&hook), // 和文件
  113. )
  114. core := zapcore.NewCore(encoder, logOutput, level)
  115. // 开发环境与生产环境的差异化配置
  116. var logger *zap.Logger
  117. if conf.GetString("env") != "prod" {
  118. logger = zap.New(core,
  119. zap.Development(),
  120. zap.AddCaller(),
  121. zap.AddStacktrace(zap.ErrorLevel),
  122. )
  123. } else {
  124. logger = zap.New(core,
  125. zap.AddCaller(),
  126. zap.AddStacktrace(zap.ErrorLevel),
  127. )
  128. }
  129. // 添加服务类型标识
  130. serviceTypeField := zap.String("service", serviceTypeToString(serviceType))
  131. logger = logger.With(serviceTypeField)
  132. return &Logger{logger}
  133. }
  134. // ensureDirExists 确保日志目录存在
  135. func ensureDirExists(filePath string) error {
  136. dir := filepath.Dir(filePath)
  137. return os.MkdirAll(dir, 0755)
  138. }
  139. // serviceTypeToString 将服务类型转换为字符串
  140. func serviceTypeToString(serviceType ServiceType) string {
  141. switch serviceType {
  142. case API:
  143. return "api"
  144. case Task:
  145. return "task"
  146. default:
  147. return "unknown"
  148. }
  149. }
  150. // timeEncoder 自定义时间编码器,提供高精度时间戳
  151. func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
  152. enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
  153. }
  154. // WithValue Adds a field to the specified context
  155. func (l *Logger) WithValue(ctx context.Context, fields ...zapcore.Field) context.Context {
  156. if c, ok := ctx.(*gin.Context); ok {
  157. ctx = c.Request.Context()
  158. c.Request = c.Request.WithContext(context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...)))
  159. return c
  160. }
  161. return context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...))
  162. }
  163. // WithContext Returns a zap instance from the specified context
  164. func (l *Logger) WithContext(ctx context.Context) *Logger {
  165. if c, ok := ctx.(*gin.Context); ok {
  166. ctx = c.Request.Context()
  167. }
  168. zl := ctx.Value(ctxLoggerKey)
  169. ctxLogger, ok := zl.(*zap.Logger)
  170. if ok {
  171. return &Logger{ctxLogger}
  172. }
  173. return l
  174. }