code.gitea.io/gitea@v1.22.3/modules/setting/log.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package setting 5 6 import ( 7 "fmt" 8 golog "log" 9 "os" 10 "path" 11 "path/filepath" 12 "strings" 13 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/util" 16 ) 17 18 type LogGlobalConfig struct { 19 RootPath string 20 21 Mode string 22 Level log.Level 23 StacktraceLogLevel log.Level 24 BufferLen int 25 26 EnableSSHLog bool 27 28 AccessLogTemplate string 29 RequestIDHeaders []string 30 } 31 32 var Log LogGlobalConfig 33 34 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}}"` 35 36 func loadLogGlobalFrom(rootCfg ConfigProvider) { 37 sec := rootCfg.Section("log") 38 39 Log.Level = log.LevelFromString(sec.Key("LEVEL").MustString(log.INFO.String())) 40 Log.StacktraceLogLevel = log.LevelFromString(sec.Key("STACKTRACE_LEVEL").MustString(log.NONE.String())) 41 Log.BufferLen = sec.Key("BUFFER_LEN").MustInt(10000) 42 Log.Mode = sec.Key("MODE").MustString("console") 43 44 Log.RootPath = sec.Key("ROOT_PATH").MustString(path.Join(AppWorkPath, "log")) 45 if !filepath.IsAbs(Log.RootPath) { 46 Log.RootPath = filepath.Join(AppWorkPath, Log.RootPath) 47 } 48 Log.RootPath = util.FilePathJoinAbs(Log.RootPath) 49 50 Log.EnableSSHLog = sec.Key("ENABLE_SSH_LOG").MustBool(false) 51 52 Log.AccessLogTemplate = sec.Key("ACCESS_LOG_TEMPLATE").MustString(accessLogTemplateDefault) 53 Log.RequestIDHeaders = sec.Key("REQUEST_ID_HEADERS").Strings(",") 54 } 55 56 func prepareLoggerConfig(rootCfg ConfigProvider) { 57 sec := rootCfg.Section("log") 58 59 if !sec.HasKey("logger.default.MODE") { 60 sec.Key("logger.default.MODE").MustString(",") 61 } 62 63 deprecatedSetting(rootCfg, "log", "ACCESS", "log", "logger.access.MODE", "1.21") 64 deprecatedSetting(rootCfg, "log", "ENABLE_ACCESS_LOG", "log", "logger.access.MODE", "1.21") 65 if val := sec.Key("ACCESS").String(); val != "" { 66 sec.Key("logger.access.MODE").MustString(val) 67 } 68 if sec.HasKey("ENABLE_ACCESS_LOG") && !sec.Key("ENABLE_ACCESS_LOG").MustBool() { 69 sec.Key("logger.access.MODE").SetValue("") 70 } 71 72 deprecatedSetting(rootCfg, "log", "ROUTER", "log", "logger.router.MODE", "1.21") 73 deprecatedSetting(rootCfg, "log", "DISABLE_ROUTER_LOG", "log", "logger.router.MODE", "1.21") 74 if val := sec.Key("ROUTER").String(); val != "" { 75 sec.Key("logger.router.MODE").MustString(val) 76 } 77 if !sec.HasKey("logger.router.MODE") { 78 sec.Key("logger.router.MODE").MustString(",") // use default logger 79 } 80 if sec.HasKey("DISABLE_ROUTER_LOG") && sec.Key("DISABLE_ROUTER_LOG").MustBool() { 81 sec.Key("logger.router.MODE").SetValue("") 82 } 83 84 deprecatedSetting(rootCfg, "log", "XORM", "log", "logger.xorm.MODE", "1.21") 85 deprecatedSetting(rootCfg, "log", "ENABLE_XORM_LOG", "log", "logger.xorm.MODE", "1.21") 86 if val := sec.Key("XORM").String(); val != "" { 87 sec.Key("logger.xorm.MODE").MustString(val) 88 } 89 if !sec.HasKey("logger.xorm.MODE") { 90 sec.Key("logger.xorm.MODE").MustString(",") // use default logger 91 } 92 if sec.HasKey("ENABLE_XORM_LOG") && !sec.Key("ENABLE_XORM_LOG").MustBool() { 93 sec.Key("logger.xorm.MODE").SetValue("") 94 } 95 } 96 97 func LogPrepareFilenameForWriter(fileName, defaultFileName string) string { 98 if fileName == "" { 99 fileName = defaultFileName 100 } 101 if !filepath.IsAbs(fileName) { 102 fileName = filepath.Join(Log.RootPath, fileName) 103 } else { 104 fileName = filepath.Clean(fileName) 105 } 106 if err := os.MkdirAll(filepath.Dir(fileName), os.ModePerm); err != nil { 107 panic(fmt.Sprintf("unable to create directory for log %q: %v", fileName, err.Error())) 108 } 109 return fileName 110 } 111 112 func loadLogModeByName(rootCfg ConfigProvider, loggerName, modeName string) (writerName, writerType string, writerMode log.WriterMode, err error) { 113 sec := rootCfg.Section("log." + modeName) 114 115 writerMode = log.WriterMode{} 116 writerType = ConfigSectionKeyString(sec, "MODE") 117 if writerType == "" { 118 writerType = modeName 119 } 120 121 writerName = modeName 122 defaultFlags := "stdflags" 123 defaultFilaName := "gitea.log" 124 if loggerName == "access" { 125 // "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. 126 // so "access" logger's writer name is usually "file.access" or "console.access" 127 writerName += ".access" 128 defaultFlags = "none" 129 defaultFilaName = "access.log" 130 } 131 132 writerMode.Level = log.LevelFromString(ConfigInheritedKeyString(sec, "LEVEL", Log.Level.String())) 133 writerMode.StacktraceLevel = log.LevelFromString(ConfigInheritedKeyString(sec, "STACKTRACE_LEVEL", Log.StacktraceLogLevel.String())) 134 writerMode.Prefix = ConfigInheritedKeyString(sec, "PREFIX") 135 writerMode.Expression = ConfigInheritedKeyString(sec, "EXPRESSION") 136 writerMode.Flags = log.FlagsFromString(ConfigInheritedKeyString(sec, "FLAGS", defaultFlags)) 137 138 switch writerType { 139 case "console": 140 useStderr := ConfigInheritedKey(sec, "STDERR").MustBool(false) 141 defaultCanColor := log.CanColorStdout 142 if useStderr { 143 defaultCanColor = log.CanColorStderr 144 } 145 writerOption := log.WriterConsoleOption{Stderr: useStderr} 146 writerMode.Colorize = ConfigInheritedKey(sec, "COLORIZE").MustBool(defaultCanColor) 147 writerMode.WriterOption = writerOption 148 case "file": 149 fileName := LogPrepareFilenameForWriter(ConfigInheritedKey(sec, "FILE_NAME").String(), defaultFilaName) 150 writerOption := log.WriterFileOption{} 151 writerOption.FileName = fileName + filenameSuffix // FIXME: the suffix doesn't seem right, see its related comments 152 writerOption.LogRotate = ConfigInheritedKey(sec, "LOG_ROTATE").MustBool(true) 153 writerOption.MaxSize = 1 << uint(ConfigInheritedKey(sec, "MAX_SIZE_SHIFT").MustInt(28)) 154 writerOption.DailyRotate = ConfigInheritedKey(sec, "DAILY_ROTATE").MustBool(true) 155 writerOption.MaxDays = ConfigInheritedKey(sec, "MAX_DAYS").MustInt(7) 156 writerOption.Compress = ConfigInheritedKey(sec, "COMPRESS").MustBool(true) 157 writerOption.CompressionLevel = ConfigInheritedKey(sec, "COMPRESSION_LEVEL").MustInt(-1) 158 writerMode.WriterOption = writerOption 159 case "conn": 160 writerOption := log.WriterConnOption{} 161 writerOption.ReconnectOnMsg = ConfigInheritedKey(sec, "RECONNECT_ON_MSG").MustBool() 162 writerOption.Reconnect = ConfigInheritedKey(sec, "RECONNECT").MustBool() 163 writerOption.Protocol = ConfigInheritedKey(sec, "PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"}) 164 writerOption.Addr = ConfigInheritedKey(sec, "ADDR").MustString(":7020") 165 writerMode.WriterOption = writerOption 166 default: 167 if !log.HasEventWriter(writerType) { 168 return "", "", writerMode, fmt.Errorf("invalid log writer type (mode): %s, maybe it needs something like 'MODE=file' in [log.%s] section", writerType, modeName) 169 } 170 } 171 172 return writerName, writerType, writerMode, nil 173 } 174 175 var filenameSuffix = "" 176 177 // RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files 178 // FIXME: it seems not right, it breaks log rotating or log collectors 179 func RestartLogsWithPIDSuffix() { 180 filenameSuffix = fmt.Sprintf(".%d", os.Getpid()) 181 initAllLoggers() // when forking, before restarting, rename logger file and re-init all loggers 182 } 183 184 func InitLoggersForTest() { 185 initAllLoggers() 186 } 187 188 var initLoggerDisabled bool 189 190 // initAllLoggers creates all the log services 191 func initAllLoggers() { 192 if initLoggerDisabled { 193 return 194 } 195 initManagedLoggers(log.GetManager(), CfgProvider) 196 197 golog.SetFlags(0) 198 golog.SetPrefix("") 199 golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info)) 200 } 201 202 func DisableLoggerInit() { 203 initLoggerDisabled = true 204 } 205 206 func initManagedLoggers(manager *log.LoggerManager, cfg ConfigProvider) { 207 loadLogGlobalFrom(cfg) 208 prepareLoggerConfig(cfg) 209 210 initLoggerByName(manager, cfg, log.DEFAULT) // default 211 initLoggerByName(manager, cfg, "access") 212 initLoggerByName(manager, cfg, "router") 213 initLoggerByName(manager, cfg, "xorm") 214 } 215 216 func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, loggerName string) { 217 sec := rootCfg.Section("log") 218 keyPrefix := "logger." + loggerName 219 220 disabled := sec.HasKey(keyPrefix+".MODE") && sec.Key(keyPrefix+".MODE").String() == "" 221 if disabled { 222 return 223 } 224 225 modeVal := sec.Key(keyPrefix + ".MODE").String() 226 if modeVal == "," { 227 modeVal = Log.Mode 228 } 229 230 var eventWriters []log.EventWriter 231 modes := strings.Split(modeVal, ",") 232 for _, modeName := range modes { 233 modeName = strings.TrimSpace(modeName) 234 if modeName == "" { 235 continue 236 } 237 writerName, writerType, writerMode, err := loadLogModeByName(rootCfg, loggerName, modeName) 238 if err != nil { 239 log.FallbackErrorf("Failed to load writer mode %q for logger %s: %v", modeName, loggerName, err) 240 continue 241 } 242 if writerMode.BufferLen == 0 { 243 writerMode.BufferLen = Log.BufferLen 244 } 245 eventWriter := manager.GetSharedWriter(writerName) 246 if eventWriter == nil { 247 eventWriter, err = manager.NewSharedWriter(writerName, writerType, writerMode) 248 if err != nil { 249 log.FallbackErrorf("Failed to create event writer for logger %s: %v", loggerName, err) 250 continue 251 } 252 } 253 eventWriters = append(eventWriters, eventWriter) 254 } 255 256 manager.GetLogger(loggerName).ReplaceAllWriters(eventWriters...) 257 } 258 259 func InitSQLLoggersForCli(level log.Level) { 260 log.SetConsoleLogger("xorm", "console", level) 261 } 262 263 func IsAccessLogEnabled() bool { 264 return log.IsLoggerEnabled("access") 265 } 266 267 func IsRouteLogEnabled() bool { 268 return log.IsLoggerEnabled("router") 269 }