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 11KB

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