summaryrefslogtreecommitdiffstats
path: root/modules/setting/log.go
diff options
context:
space:
mode:
Diffstat (limited to 'modules/setting/log.go')
-rw-r--r--modules/setting/log.go270
1 files changed, 270 insertions, 0 deletions
diff --git a/modules/setting/log.go b/modules/setting/log.go
new file mode 100644
index 0000000..a141188
--- /dev/null
+++ b/modules/setting/log.go
@@ -0,0 +1,270 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package setting
+
+import (
+ "fmt"
+ golog "log"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
+)
+
+type LogGlobalConfig struct {
+ RootPath string
+
+ Mode string
+ Level log.Level
+ StacktraceLogLevel log.Level
+ BufferLen int
+
+ EnableSSHLog bool
+
+ AccessLogTemplate string
+ RequestIDHeaders []string
+}
+
+var Log LogGlobalConfig
+
+const accessLogTemplateDefault = `{{.Ctx.RemoteHost}} - {{.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}}"`
+
+func loadLogGlobalFrom(rootCfg ConfigProvider) {
+ sec := rootCfg.Section("log")
+
+ Log.Level = log.LevelFromString(sec.Key("LEVEL").MustString(log.INFO.String()))
+ Log.StacktraceLogLevel = log.LevelFromString(sec.Key("STACKTRACE_LEVEL").MustString(log.NONE.String()))
+ Log.BufferLen = sec.Key("BUFFER_LEN").MustInt(10000)
+ Log.Mode = sec.Key("MODE").MustString("console")
+
+ Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log"))
+ if !filepath.IsAbs(Log.RootPath) {
+ Log.RootPath = filepath.Join(AppWorkPath, Log.RootPath)
+ }
+ Log.RootPath = util.FilePathJoinAbs(Log.RootPath)
+
+ Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false)
+
+ Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(accessLogTemplateDefault)
+ Log.RequestIDHeaders = sec.Key("REQUEST_ID_HEADERS").Strings(",")
+}
+
+func prepareLoggerConfig(rootCfg ConfigProvider) {
+ sec := rootCfg.Section("log")
+
+ if !sec.HasKey("logger.default.MODE") {
+ sec.Key("logger.default.MODE").MustString(",")
+ }
+
+ deprecatedSetting(rootCfg, "log", "ACCESS", "log", "logger.access.MODE", "1.21")
+ deprecatedSetting(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "logger.access.MODE", "1.21")
+ if val := sec.Key("ACCESS").String(); val != "" {
+ sec.Key("logger.access.MODE").MustString(val)
+ }
+ if sec.HasKey("ENABLE_ACCESS_LOG") && !sec.Key("ENABLE_ACCESS_LOG").MustBool() {
+ sec.Key("logger.access.MODE").SetValue("")
+ }
+
+ deprecatedSetting(rootCfg, "log", "ROUTER", "log", "logger.router.MODE", "1.21")
+ deprecatedSetting(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "logger.router.MODE", "1.21")
+ if val := sec.Key("ROUTER").String(); val != "" {
+ sec.Key("logger.router.MODE").MustString(val)
+ }
+ if !sec.HasKey("logger.router.MODE") {
+ sec.Key("logger.router.MODE").MustString(",") // use default logger
+ }
+ if sec.HasKey("DISABLE_ROUTER_LOG") && sec.Key("DISABLE_ROUTER_LOG").MustBool() {
+ sec.Key("logger.router.MODE").SetValue("")
+ }
+
+ deprecatedSetting(rootCfg, "log", "XORM", "log", "logger.xorm.MODE", "1.21")
+ deprecatedSetting(rootCfg, "log", "ENABLE_XORM_LOG", "log", "logger.xorm.MODE", "1.21")
+ if val := sec.Key("XORM").String(); val != "" {
+ sec.Key("logger.xorm.MODE").MustString(val)
+ }
+ if !sec.HasKey("logger.xorm.MODE") {
+ sec.Key("logger.xorm.MODE").MustString(",") // use default logger
+ }
+ if sec.HasKey("ENABLE_XORM_LOG") && !sec.Key("ENABLE_XORM_LOG").MustBool() {
+ sec.Key("logger.xorm.MODE").SetValue("")
+ }
+}
+
+func LogPrepareFilenameForWriter(fileName, defaultFileName string) string {
+ if fileName == "" {
+ fileName = defaultFileName
+ }
+ if !filepath.IsAbs(fileName) {
+ fileName = filepath.Join(Log.RootPath, fileName)
+ } else {
+ fileName = filepath.Clean(fileName)
+ }
+ if err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm); err != nil {
+ panic(fmt.Sprintf("unable to create directory for log %q: %v", fileName, err.Error()))
+ }
+ return fileName
+}
+
+func loadLogModeByName(rootCfg ConfigProvider, loggerName, modeName string) (writerName, writerType string, writerMode log.WriterMode, err error) {
+ sec := rootCfg.Section("log." + modeName)
+
+ writerMode = log.WriterMode{}
+ writerType = ConfigSectionKeyString(sec, "MODE")
+ if writerType == "" {
+ writerType = modeName
+ }
+
+ writerName = modeName
+ defaultFlags := "stdflags"
+ defaultFilaName := "gitea.log"
+ if loggerName == "access" {
+ // "access" logger is special, by default it doesn't have output flags, so it also needs a new writer name to avoid conflicting with other writers.
+ // so "access" logger's writer name is usually "file.access" or "console.access"
+ writerName += ".access"
+ defaultFlags = "none"
+ defaultFilaName = "access.log"
+ }
+
+ writerMode.Level = log.LevelFromString(ConfigInheritedKeyString(sec, "LEVEL", Log.Level.String()))
+ writerMode.StacktraceLevel = log.LevelFromString(ConfigInheritedKeyString(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel.String()))
+ writerMode.Prefix = ConfigInheritedKeyString(sec, "PREFIX")
+ writerMode.Expression = ConfigInheritedKeyString(sec, "EXPRESSION")
+ // flags are updated and set below
+
+ switch writerType {
+ case "console":
+ // if stderr is on journald, prefer stderr by default
+ useStderr := ConfigInheritedKey(sec, "STDERR").MustBool(log.JournaldOnStderr)
+ defaultCanColor := log.CanColorStdout
+ defaultJournald := log.JournaldOnStdout
+ if useStderr {
+ defaultCanColor = log.CanColorStderr
+ defaultJournald = log.JournaldOnStderr
+ }
+ writerOption := log.WriterConsoleOption{Stderr: useStderr}
+ writerMode.Colorize = ConfigInheritedKey(sec, "COLORIZE").MustBool(defaultCanColor)
+ writerMode.WriterOption = writerOption
+ // if we are ultimately on journald, update default flags
+ if defaultJournald {
+ defaultFlags = "journaldflags"
+ }
+ case "file":
+ fileName := LogPrepareFilenameForWriter(ConfigInheritedKey(sec, "FILE_NAME").String(), defaultFilaName)
+ writerOption := log.WriterFileOption{}
+ writerOption.FileName = fileName + filenameSuffix // FIXME: the suffix doesn't seem right, see its related comments
+ writerOption.LogRotate = ConfigInheritedKey(sec, "LOG_ROTATE").MustBool(true)
+ writerOption.MaxSize = 1 << uint(ConfigInheritedKey(sec, "MAX_SIZE_SHIFT").MustInt(28))
+ writerOption.DailyRotate = ConfigInheritedKey(sec, "DAILY_ROTATE").MustBool(true)
+ writerOption.MaxDays = ConfigInheritedKey(sec, "MAX_DAYS").MustInt(7)
+ writerOption.Compress = ConfigInheritedKey(sec, "COMPRESS").MustBool(true)
+ writerOption.CompressionLevel = ConfigInheritedKey(sec, "COMPRESSION_LEVEL").MustInt(-1)
+ writerMode.WriterOption = writerOption
+ case "conn":
+ writerOption := log.WriterConnOption{}
+ writerOption.ReconnectOnMsg = ConfigInheritedKey(sec, "RECONNECT_ON_MSG").MustBool()
+ writerOption.Reconnect = ConfigInheritedKey(sec, "RECONNECT").MustBool()
+ writerOption.Protocol = ConfigInheritedKey(sec, "PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"})
+ writerOption.Addr = ConfigInheritedKey(sec, "ADDR").MustString(":7020")
+ writerMode.WriterOption = writerOption
+ default:
+ if !log.HasEventWriter(writerType) {
+ return "", "", writerMode, fmt.Errorf("invalid log writer type (mode): %s, maybe it needs something like 'MODE=file' in [log.%s] section", writerType, modeName)
+ }
+ }
+
+ // set flags last because the console writer code may update default flags
+ writerMode.Flags = log.FlagsFromString(ConfigInheritedKeyString(sec, "FLAGS", defaultFlags))
+
+ return writerName, writerType, writerMode, nil
+}
+
+var filenameSuffix = ""
+
+// RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files
+// FIXME: it seems not right, it breaks log rotating or log collectors
+func RestartLogsWithPIDSuffix() {
+ filenameSuffix = fmt.Sprintf(".%d", os.Getpid())
+ initAllLoggers() // when forking, before restarting, rename logger file and re-init all loggers
+}
+
+func InitLoggersForTest() {
+ initAllLoggers()
+}
+
+// initAllLoggers creates all the log services
+func initAllLoggers() {
+ initManagedLoggers(log.GetManager(), CfgProvider)
+
+ golog.SetFlags(0)
+ golog.SetPrefix("")
+ golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
+}
+
+func initManagedLoggers(manager *log.LoggerManager, cfg ConfigProvider) {
+ loadLogGlobalFrom(cfg)
+ prepareLoggerConfig(cfg)
+
+ initLoggerByName(manager, cfg, log.DEFAULT) // default
+ initLoggerByName(manager, cfg, "access")
+ initLoggerByName(manager, cfg, "router")
+ initLoggerByName(manager, cfg, "xorm")
+}
+
+func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, loggerName string) {
+ sec := rootCfg.Section("log")
+ keyPrefix := "logger." + loggerName
+
+ disabled := sec.HasKey(keyPrefix+".MODE") && sec.Key(keyPrefix+".MODE").String() == ""
+ if disabled {
+ return
+ }
+
+ modeVal := sec.Key(keyPrefix + ".MODE").String()
+ if modeVal == "," {
+ modeVal = Log.Mode
+ }
+
+ var eventWriters []log.EventWriter
+ modes := strings.Split(modeVal, ",")
+ for _, modeName := range modes {
+ modeName = strings.TrimSpace(modeName)
+ if modeName == "" {
+ continue
+ }
+ writerName, writerType, writerMode, err := loadLogModeByName(rootCfg, loggerName, modeName)
+ if err != nil {
+ log.FallbackErrorf("Failed to load writer mode %q for logger %s: %v", modeName, loggerName, err)
+ continue
+ }
+ if writerMode.BufferLen == 0 {
+ writerMode.BufferLen = Log.BufferLen
+ }
+ eventWriter := manager.GetSharedWriter(writerName)
+ if eventWriter == nil {
+ eventWriter, err = manager.NewSharedWriter(writerName, writerType, writerMode)
+ if err != nil {
+ log.FallbackErrorf("Failed to create event writer for logger %s: %v", loggerName, err)
+ continue
+ }
+ }
+ eventWriters = append(eventWriters, eventWriter)
+ }
+
+ manager.GetLogger(loggerName).ReplaceAllWriters(eventWriters...)
+}
+
+func InitSQLLoggersForCli(level log.Level) {
+ log.SetConsoleLogger("xorm", "console", level)
+}
+
+func IsAccessLogEnabled() bool {
+ return log.IsLoggerEnabled("access")
+}
+
+func IsRouteLogEnabled() bool {
+ return log.IsLoggerEnabled("router")
+}