1
0

log.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package conf
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. "github.com/pkg/errors"
  7. "gopkg.in/ini.v1"
  8. log "unknwon.dev/clog/v2"
  9. )
  10. type loggerConf struct {
  11. Buffer int64
  12. Config any
  13. }
  14. type logConf struct {
  15. RootPath string
  16. Modes []string
  17. Configs []*loggerConf
  18. }
  19. // Log settings
  20. var Log *logConf
  21. // initLogConf returns parsed logging configuration from given INI file. When the
  22. // argument "hookMode" is true, it only initializes the root path for log files.
  23. // NOTE: Because we always create a console logger as the primary logger at init time,
  24. // we need to remove it in case the user doesn't configure to use it after the logging
  25. // service is initialized.
  26. func initLogConf(cfg *ini.File, hookMode bool) (_ *logConf, hasConsole bool, _ error) {
  27. rootPath := cfg.Section("log").Key("ROOT_PATH").MustString(filepath.Join(WorkDir(), "log"))
  28. if hookMode {
  29. return &logConf{
  30. RootPath: ensureAbs(rootPath),
  31. }, false, nil
  32. }
  33. modes := strings.Split(cfg.Section("log").Key("MODE").MustString("console"), ",")
  34. lc := &logConf{
  35. RootPath: ensureAbs(rootPath),
  36. Modes: make([]string, 0, len(modes)),
  37. Configs: make([]*loggerConf, 0, len(modes)),
  38. }
  39. // Iterate over [log.*] sections to initialize individual logger.
  40. levelMappings := map[string]log.Level{
  41. "trace": log.LevelTrace,
  42. "info": log.LevelInfo,
  43. "warn": log.LevelWarn,
  44. "error": log.LevelError,
  45. "fatal": log.LevelFatal,
  46. }
  47. for i := range modes {
  48. modes[i] = strings.ToLower(strings.TrimSpace(modes[i]))
  49. secName := "log." + modes[i]
  50. sec, err := cfg.GetSection(secName)
  51. if err != nil {
  52. return nil, hasConsole, errors.Errorf("missing configuration section [%s] for %q logger", secName, modes[i])
  53. }
  54. level := levelMappings[strings.ToLower(sec.Key("LEVEL").MustString("trace"))]
  55. buffer := sec.Key("BUFFER_LEN").MustInt64(100)
  56. var c *loggerConf
  57. switch modes[i] {
  58. case log.DefaultConsoleName:
  59. hasConsole = true
  60. c = &loggerConf{
  61. Buffer: buffer,
  62. Config: log.ConsoleConfig{
  63. Level: level,
  64. },
  65. }
  66. case log.DefaultFileName:
  67. logPath := filepath.Join(lc.RootPath, "gogs.log")
  68. c = &loggerConf{
  69. Buffer: buffer,
  70. Config: log.FileConfig{
  71. Level: level,
  72. Filename: logPath,
  73. FileRotationConfig: log.FileRotationConfig{
  74. Rotate: sec.Key("LOG_ROTATE").MustBool(true),
  75. Daily: sec.Key("DAILY_ROTATE").MustBool(true),
  76. MaxSize: 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28)),
  77. MaxLines: sec.Key("MAX_LINES").MustInt64(1000000),
  78. MaxDays: sec.Key("MAX_DAYS").MustInt64(7),
  79. },
  80. },
  81. }
  82. case log.DefaultSlackName:
  83. c = &loggerConf{
  84. Buffer: buffer,
  85. Config: log.SlackConfig{
  86. Level: level,
  87. URL: sec.Key("URL").String(),
  88. },
  89. }
  90. case log.DefaultDiscordName:
  91. c = &loggerConf{
  92. Buffer: buffer,
  93. Config: log.DiscordConfig{
  94. Level: level,
  95. URL: sec.Key("URL").String(),
  96. Username: sec.Key("USERNAME").String(),
  97. },
  98. }
  99. default:
  100. continue
  101. }
  102. lc.Modes = append(lc.Modes, modes[i])
  103. lc.Configs = append(lc.Configs, c)
  104. }
  105. return lc, hasConsole, nil
  106. }
  107. // InitLogging initializes the logging service of the application. When the
  108. // "hookMode" is true, it only initializes the root path for log files without
  109. // creating any logger. It will also not remove the primary logger in "hookMode"
  110. // and is up to the caller to decide when to remove it.
  111. func InitLogging(hookMode bool) {
  112. logConf, hasConsole, err := initLogConf(File, hookMode)
  113. if err != nil {
  114. log.Fatal("Failed to init logging configuration: %v", err)
  115. }
  116. defer func() {
  117. Log = logConf
  118. }()
  119. if hookMode {
  120. return
  121. }
  122. err = os.MkdirAll(logConf.RootPath, os.ModePerm)
  123. if err != nil {
  124. log.Fatal("Failed to create log directory: %v", err)
  125. }
  126. for i, mode := range logConf.Modes {
  127. c := logConf.Configs[i]
  128. var err error
  129. var level log.Level
  130. switch mode {
  131. case log.DefaultConsoleName:
  132. level = c.Config.(log.ConsoleConfig).Level
  133. err = log.NewConsole(c.Buffer, c.Config)
  134. case log.DefaultFileName:
  135. level = c.Config.(log.FileConfig).Level
  136. err = log.NewFile(c.Buffer, c.Config)
  137. case log.DefaultSlackName:
  138. level = c.Config.(log.SlackConfig).Level
  139. err = log.NewSlack(c.Buffer, c.Config)
  140. case log.DefaultDiscordName:
  141. level = c.Config.(log.DiscordConfig).Level
  142. err = log.NewDiscord(c.Buffer, c.Config)
  143. default:
  144. panic("unreachable")
  145. }
  146. if err != nil {
  147. log.Fatal("Failed to init %s logger: %v", mode, err)
  148. return
  149. }
  150. log.Trace("Log mode: %s (%s)", strings.Title(mode), strings.Title(strings.ToLower(level.String())))
  151. }
  152. // ⚠️ WARNING: It is only safe to remove the primary logger until
  153. // there are other loggers that are initialized. Otherwise, the
  154. // application will print errors to nowhere.
  155. if !hasConsole {
  156. log.Remove(log.DefaultConsoleName)
  157. }
  158. }