mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-25 17:44:32 +02:00 
			
		
		
		
	Add nakedret, unconvert, wastedassign, stylecheck and nolintlint linters to improve code readability - nakedret - https://github.com/alexkohler/nakedret - nakedret is a Go static analysis tool to find naked returns in functions greater than a specified function length. - unconvert - https://github.com/mdempsky/unconvert - Remove unnecessary type conversions - wastedassign - https://github.com/sanposhiho/wastedassign - wastedassign finds wasted assignment statements. - notlintlint - Reports ill-formed or insufficient nolint directives - stylecheck - https://staticcheck.io/docs/checks/#ST - keep style consistent - excluded: [ST1003 - Poorly chosen identifier](https://staticcheck.io/docs/checks/#ST1003) and [ST1005 - Incorrectly formatted error string](https://staticcheck.io/docs/checks/#ST1005)
		
			
				
	
	
		
			373 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2019 The Gitea Authors. All rights reserved.
 | |
| // Use of this source code is governed by a MIT-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package setting
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	golog "log"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 
 | |
| 	"code.gitea.io/gitea/modules/json"
 | |
| 	"code.gitea.io/gitea/modules/log"
 | |
| 	"code.gitea.io/gitea/modules/util"
 | |
| 
 | |
| 	ini "gopkg.in/ini.v1"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	filenameSuffix  = ""
 | |
| 	descriptionLock = sync.RWMutex{}
 | |
| 	logDescriptions = make(map[string]*LogDescription)
 | |
| )
 | |
| 
 | |
| // GetLogDescriptions returns a race safe set of descriptions
 | |
| func GetLogDescriptions() map[string]*LogDescription {
 | |
| 	descriptionLock.RLock()
 | |
| 	defer descriptionLock.RUnlock()
 | |
| 	descs := make(map[string]*LogDescription, len(logDescriptions))
 | |
| 	for k, v := range logDescriptions {
 | |
| 		subLogDescriptions := make([]SubLogDescription, len(v.SubLogDescriptions))
 | |
| 		copy(subLogDescriptions, v.SubLogDescriptions)
 | |
| 
 | |
| 		descs[k] = &LogDescription{
 | |
| 			Name:               v.Name,
 | |
| 			SubLogDescriptions: subLogDescriptions,
 | |
| 		}
 | |
| 	}
 | |
| 	return descs
 | |
| }
 | |
| 
 | |
| // AddLogDescription adds a set of descriptions to the complete description
 | |
| func AddLogDescription(key string, description *LogDescription) {
 | |
| 	descriptionLock.Lock()
 | |
| 	defer descriptionLock.Unlock()
 | |
| 	logDescriptions[key] = description
 | |
| }
 | |
| 
 | |
| // AddSubLogDescription adds a sub log description
 | |
| func AddSubLogDescription(key string, subLogDescription SubLogDescription) bool {
 | |
| 	descriptionLock.Lock()
 | |
| 	defer descriptionLock.Unlock()
 | |
| 	desc, ok := logDescriptions[key]
 | |
| 	if !ok {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i, sub := range desc.SubLogDescriptions {
 | |
| 		if sub.Name == subLogDescription.Name {
 | |
| 			desc.SubLogDescriptions[i] = subLogDescription
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	desc.SubLogDescriptions = append(desc.SubLogDescriptions, subLogDescription)
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // RemoveSubLogDescription removes a sub log description
 | |
| func RemoveSubLogDescription(key, name string) bool {
 | |
| 	descriptionLock.Lock()
 | |
| 	defer descriptionLock.Unlock()
 | |
| 	desc, ok := logDescriptions[key]
 | |
| 	if !ok {
 | |
| 		return false
 | |
| 	}
 | |
| 	for i, sub := range desc.SubLogDescriptions {
 | |
| 		if sub.Name == name {
 | |
| 			desc.SubLogDescriptions = append(desc.SubLogDescriptions[:i], desc.SubLogDescriptions[i+1:]...)
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| type defaultLogOptions struct {
 | |
| 	levelName      string // LogLevel
 | |
| 	flags          string
 | |
| 	filename       string // path.Join(LogRootPath, "gitea.log")
 | |
| 	bufferLength   int64
 | |
| 	disableConsole bool
 | |
| }
 | |
| 
 | |
| func newDefaultLogOptions() defaultLogOptions {
 | |
| 	return defaultLogOptions{
 | |
| 		levelName:      LogLevel.String(),
 | |
| 		flags:          "stdflags",
 | |
| 		filename:       filepath.Join(LogRootPath, "gitea.log"),
 | |
| 		bufferLength:   10000,
 | |
| 		disableConsole: false,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // SubLogDescription describes a sublogger
 | |
| type SubLogDescription struct {
 | |
| 	Name     string
 | |
| 	Provider string
 | |
| 	Config   string
 | |
| }
 | |
| 
 | |
| // LogDescription describes a named logger
 | |
| type LogDescription struct {
 | |
| 	Name               string
 | |
| 	SubLogDescriptions []SubLogDescription
 | |
| }
 | |
| 
 | |
| func getLogLevel(section *ini.Section, key string, defaultValue log.Level) log.Level {
 | |
| 	value := section.Key(key).MustString(defaultValue.String())
 | |
| 	return log.FromString(value)
 | |
| }
 | |
| 
 | |
| func getStacktraceLogLevel(section *ini.Section, key, defaultValue string) string {
 | |
| 	value := section.Key(key).MustString(defaultValue)
 | |
| 	return log.FromString(value).String()
 | |
| }
 | |
| 
 | |
| func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions) (mode, jsonConfig, levelName string) {
 | |
| 	level := getLogLevel(sec, "LEVEL", LogLevel)
 | |
| 	levelName = level.String()
 | |
| 	stacktraceLevelName := getStacktraceLogLevel(sec, "STACKTRACE_LEVEL", StacktraceLogLevel)
 | |
| 	stacktraceLevel := log.FromString(stacktraceLevelName)
 | |
| 	mode = name
 | |
| 	keys := sec.Keys()
 | |
| 	logPath := defaults.filename
 | |
| 	flags := log.FlagsFromString(defaults.flags)
 | |
| 	expression := ""
 | |
| 	prefix := ""
 | |
| 	for _, key := range keys {
 | |
| 		switch key.Name() {
 | |
| 		case "MODE":
 | |
| 			mode = key.MustString(name)
 | |
| 		case "FILE_NAME":
 | |
| 			logPath = key.MustString(defaults.filename)
 | |
| 			forcePathSeparator(logPath)
 | |
| 			if !filepath.IsAbs(logPath) {
 | |
| 				logPath = path.Join(LogRootPath, logPath)
 | |
| 			}
 | |
| 		case "FLAGS":
 | |
| 			flags = log.FlagsFromString(key.MustString(defaults.flags))
 | |
| 		case "EXPRESSION":
 | |
| 			expression = key.MustString("")
 | |
| 		case "PREFIX":
 | |
| 			prefix = key.MustString("")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	logConfig := map[string]interface{}{
 | |
| 		"level":           level.String(),
 | |
| 		"expression":      expression,
 | |
| 		"prefix":          prefix,
 | |
| 		"flags":           flags,
 | |
| 		"stacktraceLevel": stacktraceLevel.String(),
 | |
| 	}
 | |
| 
 | |
| 	// Generate log configuration.
 | |
| 	switch mode {
 | |
| 	case "console":
 | |
| 		useStderr := sec.Key("STDERR").MustBool(false)
 | |
| 		logConfig["stderr"] = useStderr
 | |
| 		if useStderr {
 | |
| 			logConfig["colorize"] = sec.Key("COLORIZE").MustBool(log.CanColorStderr)
 | |
| 		} else {
 | |
| 			logConfig["colorize"] = sec.Key("COLORIZE").MustBool(log.CanColorStdout)
 | |
| 		}
 | |
| 
 | |
| 	case "file":
 | |
| 		if err := os.MkdirAll(path.Dir(logPath), os.ModePerm); err != nil {
 | |
| 			panic(err.Error())
 | |
| 		}
 | |
| 
 | |
| 		logConfig["filename"] = logPath + filenameSuffix
 | |
| 		logConfig["rotate"] = sec.Key("LOG_ROTATE").MustBool(true)
 | |
| 		logConfig["maxsize"] = 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28))
 | |
| 		logConfig["daily"] = sec.Key("DAILY_ROTATE").MustBool(true)
 | |
| 		logConfig["maxdays"] = sec.Key("MAX_DAYS").MustInt(7)
 | |
| 		logConfig["compress"] = sec.Key("COMPRESS").MustBool(true)
 | |
| 		logConfig["compressionLevel"] = sec.Key("COMPRESSION_LEVEL").MustInt(-1)
 | |
| 	case "conn":
 | |
| 		logConfig["reconnectOnMsg"] = sec.Key("RECONNECT_ON_MSG").MustBool()
 | |
| 		logConfig["reconnect"] = sec.Key("RECONNECT").MustBool()
 | |
| 		logConfig["net"] = sec.Key("PROTOCOL").In("tcp", []string{"tcp", "unix", "udp"})
 | |
| 		logConfig["addr"] = sec.Key("ADDR").MustString(":7020")
 | |
| 	case "smtp":
 | |
| 		logConfig["username"] = sec.Key("USER").MustString("example@example.com")
 | |
| 		logConfig["password"] = sec.Key("PASSWD").MustString("******")
 | |
| 		logConfig["host"] = sec.Key("HOST").MustString("127.0.0.1:25")
 | |
| 		sendTos := strings.Split(sec.Key("RECEIVERS").MustString(""), ",")
 | |
| 		for i, address := range sendTos {
 | |
| 			sendTos[i] = strings.TrimSpace(address)
 | |
| 		}
 | |
| 		logConfig["sendTos"] = sendTos
 | |
| 		logConfig["subject"] = sec.Key("SUBJECT").MustString("Diagnostic message from Gitea")
 | |
| 	}
 | |
| 
 | |
| 	logConfig["colorize"] = sec.Key("COLORIZE").MustBool(false)
 | |
| 	byteConfig, err := json.Marshal(logConfig)
 | |
| 	if err != nil {
 | |
| 		log.Error("Failed to marshal log configuration: %v %v", logConfig, err)
 | |
| 		return
 | |
| 	}
 | |
| 	jsonConfig = string(byteConfig)
 | |
| 	return mode, jsonConfig, levelName
 | |
| }
 | |
| 
 | |
| func generateNamedLogger(key string, options defaultLogOptions) *LogDescription {
 | |
| 	description := LogDescription{
 | |
| 		Name: key,
 | |
| 	}
 | |
| 
 | |
| 	sections := strings.Split(Cfg.Section("log").Key(strings.ToUpper(key)).MustString(""), ",")
 | |
| 
 | |
| 	for i := 0; i < len(sections); i++ {
 | |
| 		sections[i] = strings.TrimSpace(sections[i])
 | |
| 	}
 | |
| 
 | |
| 	for _, name := range sections {
 | |
| 		if len(name) == 0 || (name == "console" && options.disableConsole) {
 | |
| 			continue
 | |
| 		}
 | |
| 		sec, err := Cfg.GetSection("log." + name + "." + key)
 | |
| 		if err != nil {
 | |
| 			sec, _ = Cfg.NewSection("log." + name + "." + key)
 | |
| 		}
 | |
| 
 | |
| 		provider, config, levelName := generateLogConfig(sec, name, options)
 | |
| 
 | |
| 		if err := log.NewNamedLogger(key, options.bufferLength, name, provider, config); err != nil {
 | |
| 			// Maybe panic here?
 | |
| 			log.Error("Could not create new named logger: %v", err.Error())
 | |
| 		}
 | |
| 
 | |
| 		description.SubLogDescriptions = append(description.SubLogDescriptions, SubLogDescription{
 | |
| 			Name:     name,
 | |
| 			Provider: provider,
 | |
| 			Config:   config,
 | |
| 		})
 | |
| 		log.Info("%s Log: %s(%s:%s)", util.ToTitleCase(key), util.ToTitleCase(name), provider, levelName)
 | |
| 	}
 | |
| 
 | |
| 	AddLogDescription(key, &description)
 | |
| 
 | |
| 	return &description
 | |
| }
 | |
| 
 | |
| func newAccessLogService() {
 | |
| 	EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false)
 | |
| 	AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString(
 | |
| 		`{{.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}}"`,
 | |
| 	)
 | |
| 	// the `MustString` updates the default value, and `log.ACCESS` is used by `generateNamedLogger("access")` later
 | |
| 	_ = Cfg.Section("log").Key("ACCESS").MustString("file")
 | |
| 	if EnableAccessLog {
 | |
| 		options := newDefaultLogOptions()
 | |
| 		options.filename = filepath.Join(LogRootPath, "access.log")
 | |
| 		options.flags = "" // For the router we don't want any prefixed flags
 | |
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
 | |
| 		generateNamedLogger("access", options)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newRouterLogService() {
 | |
| 	Cfg.Section("log").Key("ROUTER").MustString("console")
 | |
| 	// Allow [log]  DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
 | |
| 	DisableRouterLog = Cfg.Section("log").Key("DISABLE_ROUTER_LOG").MustBool(DisableRouterLog)
 | |
| 
 | |
| 	if !DisableRouterLog {
 | |
| 		options := newDefaultLogOptions()
 | |
| 		options.filename = filepath.Join(LogRootPath, "router.log")
 | |
| 		options.flags = "date,time" // For the router we don't want any prefixed flags
 | |
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
 | |
| 		generateNamedLogger("router", options)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newLogService() {
 | |
| 	log.Info("Gitea v%s%s", AppVer, AppBuiltWith)
 | |
| 
 | |
| 	options := newDefaultLogOptions()
 | |
| 	options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
 | |
| 	EnableSSHLog = Cfg.Section("log").Key("ENABLE_SSH_LOG").MustBool(false)
 | |
| 
 | |
| 	description := LogDescription{
 | |
| 		Name: log.DEFAULT,
 | |
| 	}
 | |
| 
 | |
| 	sections := strings.Split(Cfg.Section("log").Key("MODE").MustString("console"), ",")
 | |
| 
 | |
| 	useConsole := false
 | |
| 	for i := 0; i < len(sections); i++ {
 | |
| 		sections[i] = strings.TrimSpace(sections[i])
 | |
| 		if sections[i] == "console" {
 | |
| 			useConsole = true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !useConsole {
 | |
| 		err := log.DelLogger("console")
 | |
| 		if err != nil {
 | |
| 			log.Fatal("DelLogger: %v", err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, name := range sections {
 | |
| 		if len(name) == 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		sec, err := Cfg.GetSection("log." + name + ".default")
 | |
| 		if err != nil {
 | |
| 			sec, err = Cfg.GetSection("log." + name)
 | |
| 			if err != nil {
 | |
| 				sec, _ = Cfg.NewSection("log." + name)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		provider, config, levelName := generateLogConfig(sec, name, options)
 | |
| 		log.NewLogger(options.bufferLength, name, provider, config)
 | |
| 		description.SubLogDescriptions = append(description.SubLogDescriptions, SubLogDescription{
 | |
| 			Name:     name,
 | |
| 			Provider: provider,
 | |
| 			Config:   config,
 | |
| 		})
 | |
| 		log.Info("Gitea Log Mode: %s(%s:%s)", util.ToTitleCase(name), util.ToTitleCase(provider), levelName)
 | |
| 	}
 | |
| 
 | |
| 	AddLogDescription(log.DEFAULT, &description)
 | |
| 
 | |
| 	// Finally redirect the default golog to here
 | |
| 	golog.SetFlags(0)
 | |
| 	golog.SetPrefix("")
 | |
| 	golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
 | |
| }
 | |
| 
 | |
| // RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files
 | |
| func RestartLogsWithPIDSuffix() {
 | |
| 	filenameSuffix = fmt.Sprintf(".%d", os.Getpid())
 | |
| 	NewLogServices(false)
 | |
| }
 | |
| 
 | |
| // NewLogServices creates all the log services
 | |
| func NewLogServices(disableConsole bool) {
 | |
| 	newLogService()
 | |
| 	newRouterLogService()
 | |
| 	newAccessLogService()
 | |
| 	NewXORMLogService(disableConsole)
 | |
| }
 | |
| 
 | |
| // NewXORMLogService initializes xorm logger service
 | |
| func NewXORMLogService(disableConsole bool) {
 | |
| 	EnableXORMLog = Cfg.Section("log").Key("ENABLE_XORM_LOG").MustBool(true)
 | |
| 	if EnableXORMLog {
 | |
| 		options := newDefaultLogOptions()
 | |
| 		options.filename = filepath.Join(LogRootPath, "xorm.log")
 | |
| 		options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
 | |
| 		options.disableConsole = disableConsole
 | |
| 
 | |
| 		Cfg.Section("log").Key("XORM").MustString(",")
 | |
| 		generateNamedLogger("xorm", options)
 | |
| 	}
 | |
| }
 |