2019-02-10 04:37:37 +03:00
// 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 (
2019-10-15 16:39:51 +03:00
"fmt"
2019-04-02 10:48:31 +03:00
golog "log"
2019-02-10 04:37:37 +03:00
"os"
"path"
"path/filepath"
"strings"
2020-07-06 03:07:07 +03:00
"sync"
2019-02-10 04:37:37 +03:00
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2019-02-10 04:37:37 +03:00
"code.gitea.io/gitea/modules/log"
2019-04-02 10:48:31 +03:00
ini "gopkg.in/ini.v1"
2019-02-10 04:37:37 +03:00
)
2019-10-15 16:39:51 +03:00
var filenameSuffix = ""
2020-07-06 03:07:07 +03:00
var descriptionLock = sync . RWMutex { }
var 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 ) )
for i , s := range v . SubLogDescriptions {
subLogDescriptions [ i ] = s
}
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 string , 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
}
2019-04-02 10:48:31 +03:00
type defaultLogOptions struct {
levelName string // LogLevel
flags string
filename string //path.Join(LogRootPath, "gitea.log")
bufferLength int64
disableConsole bool
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
func newDefaultLogOptions ( ) defaultLogOptions {
return defaultLogOptions {
2021-03-28 22:08:19 +03:00
levelName : LogLevel . String ( ) ,
2019-04-02 10:48:31 +03:00
flags : "stdflags" ,
filename : filepath . Join ( LogRootPath , "gitea.log" ) ,
bufferLength : 10000 ,
disableConsole : false ,
}
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
// SubLogDescription describes a sublogger
type SubLogDescription struct {
Name string
Provider string
Config string
}
2019-02-10 04:37:37 +03:00
2019-04-02 10:48:31 +03:00
// LogDescription describes a named logger
type LogDescription struct {
Name string
SubLogDescriptions [ ] SubLogDescription
}
2019-02-10 04:37:37 +03:00
2021-03-28 22:08:19 +03:00
func getLogLevel ( section * ini . Section , key string , defaultValue log . Level ) log . Level {
value := section . Key ( key ) . MustString ( defaultValue . String ( ) )
return log . FromString ( value )
2019-04-02 10:48:31 +03:00
}
func getStacktraceLogLevel ( section * ini . Section , key string , defaultValue string ) string {
value := section . Key ( key ) . MustString ( "none" )
return log . FromString ( value ) . String ( )
}
func generateLogConfig ( sec * ini . Section , name string , defaults defaultLogOptions ) ( mode , jsonConfig , levelName string ) {
2021-03-28 22:08:19 +03:00
level := getLogLevel ( sec , "LEVEL" , LogLevel )
2019-04-02 10:48:31 +03:00
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 ( "" )
2019-02-10 04:37:37 +03:00
}
}
2019-04-02 10:48:31 +03:00
logConfig := map [ string ] interface { } {
"level" : level . String ( ) ,
"expression" : expression ,
"prefix" : prefix ,
"flags" : flags ,
"stacktraceLevel" : stacktraceLevel . String ( ) ,
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
// 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 )
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
case "file" :
if err := os . MkdirAll ( path . Dir ( logPath ) , os . ModePerm ) ; err != nil {
panic ( err . Error ( ) )
2019-02-10 04:37:37 +03:00
}
2019-10-15 16:39:51 +03:00
logConfig [ "filename" ] = logPath + filenameSuffix
2019-04-02 10:48:31 +03:00
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" )
2019-11-25 16:38:57 +03:00
sendTos := strings . Split ( sec . Key ( "RECEIVERS" ) . MustString ( "" ) , "," )
for i , address := range sendTos {
sendTos [ i ] = strings . TrimSpace ( address )
}
logConfig [ "sendTos" ] = sendTos
2019-04-02 10:48:31 +03:00
logConfig [ "subject" ] = sec . Key ( "SUBJECT" ) . MustString ( "Diagnostic message from Gitea" )
}
2019-02-10 04:37:37 +03:00
2019-04-02 10:48:31 +03:00
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
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
jsonConfig = string ( byteConfig )
return
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
func generateNamedLogger ( key string , options defaultLogOptions ) * LogDescription {
description := LogDescription {
Name : key ,
}
sections := strings . Split ( Cfg . Section ( "log" ) . Key ( strings . ToUpper ( key ) ) . MustString ( "" ) , "," )
2019-02-10 04:37:37 +03:00
2019-04-02 10:48:31 +03:00
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 ) {
2019-02-10 04:37:37 +03:00
continue
}
2019-04-02 10:48:31 +03:00
sec , err := Cfg . GetSection ( "log." + name + "." + key )
2019-02-10 04:37:37 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
sec , _ = Cfg . NewSection ( "log." + name + "." + key )
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
provider , config , levelName := generateLogConfig ( sec , name , options )
2019-02-10 04:37:37 +03:00
2019-06-12 22:41:28 +03:00
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 ( ) )
}
2019-04-02 10:48:31 +03:00
description . SubLogDescriptions = append ( description . SubLogDescriptions , SubLogDescription {
Name : name ,
Provider : provider ,
Config : config ,
} )
log . Info ( "%s Log: %s(%s:%s)" , strings . Title ( key ) , strings . Title ( name ) , provider , levelName )
}
2020-07-06 03:07:07 +03:00
AddLogDescription ( key , & description )
2019-04-02 10:48:31 +03:00
return & description
}
func newAccessLogService ( ) {
EnableAccessLog = Cfg . Section ( "log" ) . Key ( "ENABLE_ACCESS_LOG" ) . MustBool ( false )
AccessLogTemplate = Cfg . Section ( "log" ) . Key ( "ACCESS_LOG_TEMPLATE" ) . MustString (
2019-12-24 03:11:12 +03:00
` {{ .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 }} " ` )
2019-04-02 10:48:31 +03:00
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" )
2019-05-11 12:13:57 +03:00
// Allow [log] DISABLE_ROUTER_LOG to override [server] DISABLE_ROUTER_LOG
DisableRouterLog = Cfg . Section ( "log" ) . Key ( "DISABLE_ROUTER_LOG" ) . MustBool ( DisableRouterLog )
2019-04-02 10:48:31 +03:00
2021-01-29 18:35:30 +03:00
if ! DisableRouterLog {
2019-04-02 10:48:31 +03:00
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 )
2021-05-22 00:37:16 +03:00
EnableSSHLog = Cfg . Section ( "log" ) . Key ( "ENABLE_SSH_LOG" ) . MustBool ( false )
2019-04-02 10:48:31 +03:00
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
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
}
2019-02-10 04:37:37 +03:00
2019-04-02 10:48:31 +03:00
if ! useConsole {
2019-06-12 22:41:28 +03:00
err := log . DelLogger ( "console" )
if err != nil {
log . Fatal ( "DelLogger: %v" , err )
}
2019-04-02 10:48:31 +03:00
}
for _ , name := range sections {
if len ( name ) == 0 {
continue
2019-02-10 04:37:37 +03:00
}
2020-05-16 05:38:52 +03:00
sec , err := Cfg . GetSection ( "log." + name + ".default" )
2019-04-02 10:48:31 +03:00
if err != nil {
2020-05-16 05:38:52 +03:00
sec , err = Cfg . GetSection ( "log." + name )
if err != nil {
sec , _ = Cfg . NewSection ( "log." + name )
}
2019-02-10 04:37:37 +03:00
}
2019-04-02 10:48:31 +03:00
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)" , strings . Title ( name ) , strings . Title ( provider ) , levelName )
2019-02-10 04:37:37 +03:00
}
2020-07-06 03:07:07 +03:00
AddLogDescription ( log . DEFAULT , & description )
2019-04-02 10:48:31 +03:00
// Finally redirect the default golog to here
golog . SetFlags ( 0 )
golog . SetPrefix ( "" )
golog . SetOutput ( log . NewLoggerAsWriter ( "INFO" , log . GetLogger ( log . DEFAULT ) ) )
}
2019-10-15 16:39:51 +03:00
// RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files
func RestartLogsWithPIDSuffix ( ) {
filenameSuffix = fmt . Sprintf ( ".%d" , os . Getpid ( ) )
NewLogServices ( false )
}
2019-04-07 03:25:14 +03:00
// NewLogServices creates all the log services
func NewLogServices ( disableConsole bool ) {
newLogService ( )
newRouterLogService ( )
newAccessLogService ( )
NewXORMLogService ( disableConsole )
}
2019-04-02 10:48:31 +03:00
// 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 )
2019-02-10 04:37:37 +03:00
}
}