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.5KB

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