You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

log.go 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package setting
  5. import (
  6. "encoding/json"
  7. golog "log"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "code.gitea.io/gitea/modules/log"
  13. ini "gopkg.in/ini.v1"
  14. )
  15. type defaultLogOptions struct {
  16. levelName string // LogLevel
  17. flags string
  18. filename string //path.Join(LogRootPath, "gitea.log")
  19. bufferLength int64
  20. disableConsole bool
  21. }
  22. func newDefaultLogOptions() defaultLogOptions {
  23. return defaultLogOptions{
  24. levelName: LogLevel,
  25. flags: "stdflags",
  26. filename: filepath.Join(LogRootPath, "gitea.log"),
  27. bufferLength: 10000,
  28. disableConsole: false,
  29. }
  30. }
  31. // SubLogDescription describes a sublogger
  32. type SubLogDescription struct {
  33. Name string
  34. Provider string
  35. Config string
  36. }
  37. // LogDescription describes a named logger
  38. type LogDescription struct {
  39. Name string
  40. SubLogDescriptions []SubLogDescription
  41. }
  42. func getLogLevel(section *ini.Section, key string, defaultValue string) string {
  43. value := section.Key(key).MustString("info")
  44. return log.FromString(value).String()
  45. }
  46. func getStacktraceLogLevel(section *ini.Section, key string, defaultValue string) string {
  47. value := section.Key(key).MustString("none")
  48. return log.FromString(value).String()
  49. }
  50. func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
  51. levelName = getLogLevel(sec, "LEVEL", LogLevel)
  52. level := log.FromString(levelName)
  53. stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel)
  54. stacktraceLevel := log.FromString(stacktraceLevelName)
  55. mode = name
  56. keys := sec.Keys()
  57. logPath := defaults.filename
  58. flags := log.FlagsFromString(defaults.flags)
  59. expression := ""
  60. prefix := ""
  61. for _, key := range keys {
  62. switch key.Name() {
  63. case "MODE":
  64. mode = key.MustString(name)
  65. case "FILE_NAME":
  66. logPath = key.MustString(defaults.filename)
  67. forcePathSeparator(logPath)
  68. if !filepath.IsAbs(logPath) {
  69. logPath = path.Join(LogRootPath, logPath)
  70. }
  71. case "FLAGS":
  72. flags = log.FlagsFromString(key.MustString(defaults.flags))
  73. case "EXPRESSION":
  74. expression = key.MustString("")
  75. case "PREFIX":
  76. prefix = key.MustString("")
  77. }
  78. }
  79. logConfig := map[string]interface{}{
  80. "level": level.String(),
  81. "expression": expression,
  82. "prefix": prefix,
  83. "flags": flags,
  84. "stacktraceLevel": stacktraceLevel.String(),
  85. }
  86. // Generate log configuration.
  87. switch mode {
  88. case "console":
  89. useStderr := sec.Key("STDERR").MustBool(false)
  90. logConfig["stderr"] = useStderr
  91. if useStderr {
  92. logConfig["colorize"] = sec.Key("COLORIZE").MustBool(log.CanColorStderr)
  93. } else {
  94. logConfig["colorize"] = sec.Key("COLORIZE").MustBool(log.CanColorStdout)
  95. }
  96. case "file":
  97. if err := os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
  98. panic(err.Error())
  99. }
  100. logConfig["filename"] = logPath
  101. logConfig["rotate"] = sec.Key("LOG_ROTATE").MustBool(true)
  102. logConfig["maxsize"] = 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28))
  103. logConfig["daily"] = sec.Key("DAILY_ROTATE").MustBool(true)
  104. logConfig["maxdays"] = sec.Key("MAX_DAYS").MustInt(7)
  105. logConfig["compress"] = sec.Key("COMPRESS").MustBool(true)
  106. logConfig["compressionLevel"] = sec.Key("COMPRESSION_LEVEL").MustInt(-1)
  107. case "conn":
  108. logConfig["reconnectOnMsg"] = sec.Key("RECONNECT_ON_MSG").MustBool()
  109. logConfig["reconnect"] = sec.Key("RECONNECT").MustBool()
  110. logConfig["net"] = sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"})
  111. logConfig["addr"] = sec.Key("ADDR").MustString(":7020")
  112. case "smtp":
  113. logConfig["username"] = sec.Key("USER").MustString("example@example.com")
  114. logConfig["password"] = sec.Key("PASSWD").MustString("******")
  115. logConfig["host"] = sec.Key("HOST").MustString("127.0.0.1:25")
  116. logConfig["sendTos"] = sec.Key("RECEIVERS").MustString("[]")
  117. logConfig["subject"] = sec.Key("SUBJECT").MustString("Diagnostic message from Gitea")
  118. }
  119. logConfig["colorize"] = sec.Key("COLORIZE").MustBool(false)
  120. byteConfig, err := json.Marshal(logConfig)
  121. if err != nil {
  122. log.Error("Failed to marshal log configuration: %v %v", logConfig, err)
  123. return
  124. }
  125. jsonConfig = string(byteConfig)
  126. return
  127. }
  128. func generateNamedLogger(key string, options defaultLogOptions) *LogDescription {
  129. description := LogDescription{
  130. Name: key,
  131. }
  132. sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",")
  133. for i := 0; i < len(sections); i++ {
  134. sections[i] = strings.TrimSpace(sections[i])
  135. }
  136. for _, name := range sections {
  137. if len(name) == 0 || (name == "console" && options.disableConsole) {
  138. continue
  139. }
  140. sec, err := Cfg.GetSection("log." + name + "." + key)
  141. if err != nil {
  142. sec, _ = Cfg.NewSection("log." + name + "." + key)
  143. }
  144. provider, config, levelName := generateLogConfig(sec, name, options)
  145. if err := log.NewNamedLogger(key, options.bufferLength, name, provider, config); err != nil {
  146. // Maybe panic here?
  147. log.Error("Could not create new named logger: %v", err.Error())
  148. }
  149. description.SubLogDescriptions = append(description.SubLogDescriptions, SubLogDescription{
  150. Name: name,
  151. Provider: provider,
  152. Config: config,
  153. })
  154. log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName)
  155. }
  156. LogDescriptions[key] = &description
  157. return &description
  158. }
  159. func newMacaronLogService() {
  160. options := newDefaultLogOptions()
  161. options.filename = filepath.Join(LogRootPath, "macaron.log")
  162. options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
  163. Cfg.Section("log").Key("MACARON").MustString("file")
  164. if RedirectMacaronLog {
  165. generateNamedLogger("macaron", options)
  166. }
  167. }
  168. func newAccessLogService() {
  169. EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false)
  170. AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString(
  171. `{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`)
  172. Cfg.Section("log").Key("ACCESS").MustString("file")
  173. if EnableAccessLog {
  174. options := newDefaultLogOptions()
  175. options.filename = filepath.Join(LogRootPath, "access.log")
  176. options.flags = "" // For the router we don't want any prefixed flags
  177. options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
  178. generateNamedLogger("access", options)
  179. }
  180. }
  181. func newRouterLogService() {
  182. Cfg.Section("log").Key("ROUTER").MustString("console")
  183. // Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
  184. DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog)
  185. if !DisableRouterLog && RedirectMacaronLog {
  186. options := newDefaultLogOptions()
  187. options.filename = filepath.Join(LogRootPath, "router.log")
  188. options.flags = "date,time" // For the router we don't want any prefixed flags
  189. options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
  190. generateNamedLogger("router", options)
  191. }
  192. }
  193. func newLogService() {
  194. log.Info("Gitea v%s%s", AppVer, AppBuiltWith)
  195. options := newDefaultLogOptions()
  196. options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
  197. description := LogDescription{
  198. Name: log.DEFAULT,
  199. }
  200. sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
  201. useConsole := false
  202. for i := 0; i < len(sections); i++ {
  203. sections[i] = strings.TrimSpace(sections[i])
  204. if sections[i] == "console" {
  205. useConsole = true
  206. }
  207. }
  208. if !useConsole {
  209. err := log.DelLogger("console")
  210. if err != nil {
  211. log.Fatal("DelLogger: %v", err)
  212. }
  213. }
  214. for _, name := range sections {
  215. if len(name) == 0 {
  216. continue
  217. }
  218. sec, err := Cfg.GetSection("log." + name)
  219. if err != nil {
  220. sec, _ = Cfg.NewSection("log." + name)
  221. }
  222. provider, config, levelName := generateLogConfig(sec, name, options)
  223. log.NewLogger(options.bufferLength, name, provider, config)
  224. description.SubLogDescriptions = append(description.SubLogDescriptions, SubLogDescription{
  225. Name: name,
  226. Provider: provider,
  227. Config: config,
  228. })
  229. log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName)
  230. }
  231. LogDescriptions[log.DEFAULT] = &description
  232. // Finally redirect the default golog to here
  233. golog.SetFlags(0)
  234. golog.SetPrefix("")
  235. golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
  236. }
  237. // NewLogServices creates all the log services
  238. func NewLogServices(disableConsole bool) {
  239. newLogService()
  240. newMacaronLogService()
  241. newRouterLogService()
  242. newAccessLogService()
  243. NewXORMLogService(disableConsole)
  244. }
  245. // NewXORMLogService initializes xorm logger service
  246. func NewXORMLogService(disableConsole bool) {
  247. EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
  248. if EnableXORMLog {
  249. options := newDefaultLogOptions()
  250. options.filename = filepath.Join(LogRootPath, "xorm.log")
  251. options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
  252. options.disableConsole = disableConsole
  253. Cfg.Section("log").Key("XORM").MustString(",")
  254. generateNamedLogger("xorm", options)
  255. }
  256. }