log.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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. "os"
  10. "path/filepath"
  11. "strings"
  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. envServiceType := os.Getenv("SERVICE_TYPE")
  33. if envServiceType == "api" {
  34. serviceType = API
  35. } else if envServiceType == "task" {
  36. serviceType = Task
  37. }
  38. // 根据服务类型和日期生成日志文件名
  39. logFormat := conf.GetString("log.log_format") // 日期格式,如"2006-01-02"
  40. // 确保有默认的日期格式
  41. if logFormat == "" {
  42. logFormat = "2006-01-02"
  43. }
  44. currentDate := time.Now().Format(logFormat)
  45. // 显示调试信息
  46. fmt.Printf("[日志初始化] 服务类型: %s, 日期: %s\n",
  47. serviceTypeToString(serviceType), currentDate)
  48. if serviceType == API && conf.IsSet("log.api_log_file") {
  49. // 使用API日志路径
  50. logPath = conf.GetString("log.api_log_file")
  51. logPath = fmt.Sprintf(logPath, currentDate) // 替换日期占位符
  52. } else if serviceType == Task && conf.IsSet("log.task_log_file") {
  53. // 使用Task日志路径
  54. logPath = conf.GetString("log.task_log_file")
  55. logPath = fmt.Sprintf(logPath, currentDate) // 替换日期占位符
  56. } else if conf.IsSet("log.log_file_name") {
  57. // 即使使用旧配置,也添加日期分区
  58. logPath = conf.GetString("log.log_file_name")
  59. // 截取扩展名前的部分,添加日期,然后加上扩展名
  60. ext := filepath.Ext(logPath)
  61. base := logPath[:len(logPath)-len(ext)]
  62. logPath = fmt.Sprintf("%s-%s%s", base, currentDate, ext)
  63. }
  64. // 处理相对路径,兼容不同的工作目录
  65. if strings.HasPrefix(logPath, "./") {
  66. basePath := "/data/app" // 容器中的日志基础路径
  67. if _, err := os.Stat(basePath); os.IsNotExist(err) {
  68. // 如果/data/app不存在,则使用当前目录
  69. workDir, err := os.Getwd()
  70. if err == nil {
  71. basePath = workDir
  72. } else {
  73. basePath = "."
  74. }
  75. }
  76. // 将相对路径转换为绝对路径
  77. logPath = strings.Replace(logPath, "./", basePath+"/", 1)
  78. }
  79. // 显示实际使用的日志路径
  80. fmt.Printf("[日志路径] %s\n", logPath)
  81. // 确保日志目录存在
  82. if err := ensureDirExists(logPath); err != nil {
  83. // 如果创建目录失败,回退到临时目录
  84. fmt.Printf("Error creating log directory: %v, using default path\n", err)
  85. logPath = "./logs/app.log"
  86. ensureDirExists(logPath)
  87. }
  88. // 获取日志级别
  89. lv := conf.GetString("log.log_level")
  90. var level zapcore.Level
  91. //debug<info<warn<error<fatal<panic
  92. switch lv {
  93. case "debug":
  94. level = zap.DebugLevel
  95. case "info":
  96. level = zap.InfoLevel
  97. case "warn":
  98. level = zap.WarnLevel
  99. case "error":
  100. level = zap.ErrorLevel
  101. default:
  102. level = zap.InfoLevel
  103. }
  104. // 日志轮转配置 - 使用DateRotator代替lumberjack.Logger
  105. hook := NewDateRotator(
  106. logPath,
  107. conf.GetInt("log.max_size"), // 每个日志文件的最大大小单位:M
  108. conf.GetInt("log.max_backups"), // 日志文件最多保存的备份数
  109. conf.GetInt("log.max_age"), // 文件最多保存的天数
  110. conf.GetBool("log.compress"), // 是否压缩
  111. logFormat, // 使用相同的日期格式
  112. )
  113. // 配置日志编码器
  114. var encoder zapcore.Encoder
  115. if conf.GetString("log.encoding") == "console" {
  116. encoder = zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
  117. TimeKey: "ts",
  118. LevelKey: "level",
  119. NameKey: "Logger",
  120. CallerKey: "caller",
  121. MessageKey: "msg",
  122. StacktraceKey: "stacktrace",
  123. LineEnding: zapcore.DefaultLineEnding,
  124. EncodeLevel: zapcore.LowercaseColorLevelEncoder,
  125. EncodeTime: timeEncoder,
  126. EncodeDuration: zapcore.SecondsDurationEncoder,
  127. EncodeCaller: zapcore.FullCallerEncoder,
  128. })
  129. } else {
  130. encoder = zapcore.NewJSONEncoder(zapcore.EncoderConfig{
  131. TimeKey: "time",
  132. LevelKey: "level",
  133. NameKey: "logger",
  134. CallerKey: "caller",
  135. FunctionKey: zapcore.OmitKey,
  136. MessageKey: "msg",
  137. StacktraceKey: "stacktrace",
  138. LineEnding: zapcore.DefaultLineEnding,
  139. EncodeLevel: zapcore.LowercaseLevelEncoder,
  140. EncodeTime: timeEncoder, // 使用自定义时间格式
  141. EncodeDuration: zapcore.StringDurationEncoder,
  142. EncodeCaller: zapcore.ShortCallerEncoder,
  143. })
  144. }
  145. // 创建日志核心
  146. logOutput := zapcore.NewMultiWriteSyncer(
  147. zapcore.AddSync(os.Stdout), // 同时输出到控制台
  148. zapcore.AddSync(hook), // 和文件
  149. )
  150. core := zapcore.NewCore(encoder, logOutput, level)
  151. // 开发环境与生产环境的差异化配置
  152. var logger *zap.Logger
  153. if conf.GetString("env") != "prod" {
  154. logger = zap.New(core,
  155. zap.Development(),
  156. zap.AddCaller(),
  157. zap.AddStacktrace(zap.ErrorLevel),
  158. )
  159. } else {
  160. logger = zap.New(core,
  161. zap.AddCaller(),
  162. zap.AddStacktrace(zap.ErrorLevel),
  163. )
  164. }
  165. // 添加服务类型标识
  166. serviceTypeField := zap.String("service", serviceTypeToString(serviceType))
  167. logger = logger.With(serviceTypeField)
  168. return &Logger{logger}
  169. }
  170. // ensureDirExists 确保日志目录存在
  171. func ensureDirExists(filePath string) error {
  172. dir := filepath.Dir(filePath)
  173. return os.MkdirAll(dir, 0755)
  174. }
  175. // serviceTypeToString 将服务类型转换为字符串
  176. func serviceTypeToString(serviceType ServiceType) string {
  177. switch serviceType {
  178. case API:
  179. return "api"
  180. case Task:
  181. return "task"
  182. default:
  183. return "unknown"
  184. }
  185. }
  186. // timeEncoder 自定义时间编码器,提供高精度时间戳
  187. func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
  188. enc.AppendString(t.Format("2006-01-02 15:04:05.000000000"))
  189. }
  190. // WithValue Adds a field to the specified context
  191. func (l *Logger) WithValue(ctx context.Context, fields ...zapcore.Field) context.Context {
  192. if c, ok := ctx.(*gin.Context); ok {
  193. ctx = c.Request.Context()
  194. c.Request = c.Request.WithContext(context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...)))
  195. return c
  196. }
  197. return context.WithValue(ctx, ctxLoggerKey, l.WithContext(ctx).With(fields...))
  198. }
  199. // WithContext Returns a zap instance from the specified context
  200. func (l *Logger) WithContext(ctx context.Context) *Logger {
  201. if c, ok := ctx.(*gin.Context); ok {
  202. ctx = c.Request.Context()
  203. }
  204. zl := ctx.Value(ctxLoggerKey)
  205. ctxLogger, ok := zl.(*zap.Logger)
  206. if ok {
  207. return &Logger{ctxLogger}
  208. }
  209. return l
  210. }