mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
269 lines
9.2 KiB
Go
269 lines
9.2 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package setting
|
|
|
|
import (
|
|
"fmt"
|
|
golog "log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/git/modules/log"
|
|
"github.com/hanzoai/git/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(filepath.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 := "hanzo-git.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")
|
|
writerMode.Flags = log.FlagsFromString(ConfigInheritedKeyString(sec, "FLAGS", defaultFlags))
|
|
|
|
switch writerType {
|
|
case "console":
|
|
useStderr := ConfigInheritedKey(sec, "STDERR").MustBool(false)
|
|
defaultCanColor := log.CanColorStdout
|
|
if useStderr {
|
|
defaultCanColor = log.CanColorStderr
|
|
}
|
|
writerOption := log.WriterConsoleOption{Stderr: useStderr}
|
|
writerMode.Colorize = ConfigInheritedKey(sec, "COLORIZE").MustBool(defaultCanColor)
|
|
writerMode.WriterOption = writerOption
|
|
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)
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
|
|
var initLoggerDisabled bool
|
|
|
|
// initAllLoggers creates all the log services
|
|
func initAllLoggers() {
|
|
if initLoggerDisabled {
|
|
return
|
|
}
|
|
initManagedLoggers(log.GetManager(), CfgProvider)
|
|
|
|
golog.SetFlags(0)
|
|
golog.SetPrefix("")
|
|
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
|
|
}
|
|
|
|
func DisableLoggerInit() {
|
|
initLoggerDisabled = true
|
|
}
|
|
|
|
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.SplitSeq(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.SetupStderrLogger("xorm", "console-stderr", level)
|
|
}
|
|
|
|
func IsAccessLogEnabled() bool {
|
|
return log.IsLoggerEnabled("access")
|
|
}
|
|
|
|
func IsRouteLogEnabled() bool {
|
|
return log.IsLoggerEnabled("router")
|
|
}
|