log.go 6.5 KB

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