log.go 5.6 KB

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