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  }