mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2025-04-24 22:06:57 +00:00
195 lines
4.8 KiB
Go
195 lines
4.8 KiB
Go
|
package log
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
stdLog "log"
|
||
|
"os"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// logger is the variable to which the code can write its output.
|
||
|
logger *stdLog.Logger
|
||
|
// The current absolute path to backend/ is stored in filePrefix; this is
|
||
|
// removed from the filename because the logging doesn't require any of the other junk.
|
||
|
filePrefix string
|
||
|
|
||
|
// logLevel is the variable that decides at which log level the logger will output
|
||
|
// to the logger.
|
||
|
logLevel LogLevel
|
||
|
)
|
||
|
|
||
|
type LogLevel uint8
|
||
|
|
||
|
const (
|
||
|
DebugLevel LogLevel = iota
|
||
|
InfoLevel
|
||
|
WarnLevel
|
||
|
ErrorLevel
|
||
|
FatalLevel
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// Make sure that logger is always initalized, this is to still be able to use the logger
|
||
|
// even when logger isn't yet initilazed with the correct values.
|
||
|
initializeLogger(os.Stdout)
|
||
|
// A quick and dirty way to get the backend/ absolute folder
|
||
|
_, filename, _, _ := runtime.Caller(0)
|
||
|
filePrefix = strings.TrimSuffix(filename, "server/log/log.go")
|
||
|
if filePrefix == filename {
|
||
|
// in case the source code file is moved, we can not trim the suffix, the code above should also be updated.
|
||
|
panic("unable to detect correct package prefix, please update file: " + filename)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// initializeLogger takes a input of multiple writers, which to all writers will be logged to.
|
||
|
func initializeLogger(w ...io.Writer) {
|
||
|
logger = stdLog.New(io.MultiWriter(w...), "", 0)
|
||
|
}
|
||
|
|
||
|
func stringToLogLevel(input string) (LogLevel, error) {
|
||
|
switch strings.ToLower(input) {
|
||
|
case "fatal":
|
||
|
return FatalLevel, nil
|
||
|
case "error":
|
||
|
return ErrorLevel, nil
|
||
|
case "warn":
|
||
|
return WarnLevel, nil
|
||
|
case "info":
|
||
|
return InfoLevel, nil
|
||
|
}
|
||
|
|
||
|
return FatalLevel, errors.New("invalid log level value")
|
||
|
}
|
||
|
|
||
|
// Config takes the ini config and correctly adjust the logger variable.
|
||
|
func Config(outputs []string, outputFile, level string) error {
|
||
|
newLogLevel, err := stringToLogLevel(level)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
logLevel = newLogLevel
|
||
|
|
||
|
isFileLog := false
|
||
|
isConsoleLog := false
|
||
|
|
||
|
for _, output := range outputs {
|
||
|
switch output {
|
||
|
case "file":
|
||
|
isFileLog = true
|
||
|
case "console":
|
||
|
isConsoleLog = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
writers := []io.Writer{}
|
||
|
|
||
|
if isConsoleLog {
|
||
|
writers = append(writers, os.Stdout)
|
||
|
}
|
||
|
if isFileLog {
|
||
|
f, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("cannot open log output's file %q: %v", outputFile, err)
|
||
|
}
|
||
|
writers = append(writers, f)
|
||
|
}
|
||
|
initializeLogger(writers...)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// the Skipper struct can be passed as firt argument to adjust the skip offset.
|
||
|
type Skipper struct {
|
||
|
// SkipOffset will be added on top of the existing "2" skips.
|
||
|
SkipOffset int
|
||
|
}
|
||
|
|
||
|
func log(msg, prefix string, print bool, args ...interface{}) string {
|
||
|
caller := "?()"
|
||
|
skipOffset := 2
|
||
|
// Check if the first argument is Skipper and add the skipoffset accordingly.
|
||
|
if len(args) > 0 {
|
||
|
if skip, ok := args[0].(Skipper); ok {
|
||
|
skipOffset += skip.SkipOffset
|
||
|
args = args[1:]
|
||
|
}
|
||
|
}
|
||
|
pc, filename, line, ok := runtime.Caller(skipOffset)
|
||
|
if ok {
|
||
|
// Get caller function name.
|
||
|
fn := runtime.FuncForPC(pc)
|
||
|
if fn != nil {
|
||
|
caller = fn.Name() + "()"
|
||
|
// Remove prefix of binary's name.
|
||
|
lastIndex := strings.LastIndexByte(caller, '.')
|
||
|
if lastIndex > 0 && len(caller) > lastIndex+1 {
|
||
|
caller = caller[lastIndex+1:]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
filename = strings.TrimPrefix(filename, filePrefix)
|
||
|
// Don't output long file names.
|
||
|
if len(filename) > 40 {
|
||
|
filename = "..." + filename[len(filename)-40:]
|
||
|
}
|
||
|
now := time.Now()
|
||
|
year, month, day := now.Date()
|
||
|
hour, min, sec := now.Clock()
|
||
|
// Output message:
|
||
|
// DATE TIME FILENAME:LINE:CALLER PREFIX: MSG
|
||
|
prefixedMessage := fmt.Sprintf("%d/%02d/%02d %02d:%02d:%02d %s:%d:%s %s: %s", year, month, day, hour, min, sec, filename, line, caller, prefix, msg)
|
||
|
// Only print the message if it has been requested.
|
||
|
if print {
|
||
|
if len(args) > 0 {
|
||
|
logger.Printf(prefixedMessage+"\n", args...)
|
||
|
} else {
|
||
|
logger.Println(prefixedMessage)
|
||
|
}
|
||
|
return ""
|
||
|
} else {
|
||
|
return fmt.Sprintf(prefixedMessage, args...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Debug logs a message with the DEBUG prefix.
|
||
|
func Debug(msg string, args ...interface{}) {
|
||
|
if logLevel > DebugLevel {
|
||
|
return
|
||
|
}
|
||
|
log(msg, "DEBUG", true, args...)
|
||
|
}
|
||
|
|
||
|
// Info logs a message with the INFO prefix.
|
||
|
func Info(msg string, args ...interface{}) {
|
||
|
if logLevel > InfoLevel {
|
||
|
return
|
||
|
}
|
||
|
log(msg, "INFO", true, args...)
|
||
|
}
|
||
|
|
||
|
// Warn logs a message with the WARN prefix.
|
||
|
func Warn(msg string, args ...interface{}) {
|
||
|
if logLevel > WarnLevel {
|
||
|
return
|
||
|
}
|
||
|
log(msg, "WARN", true, args...)
|
||
|
}
|
||
|
|
||
|
// Error logs a message with the ERROR prefix.
|
||
|
func Error(msg string, args ...interface{}) {
|
||
|
if logLevel > ErrorLevel {
|
||
|
return
|
||
|
}
|
||
|
log(msg, "ERROR", true, args...)
|
||
|
}
|
||
|
|
||
|
// Fatal logs a message with the FATAL prefix and then exit the program.
|
||
|
func Fatal(msg string, args ...interface{}) {
|
||
|
log(msg, "FATAL", true, args...)
|
||
|
os.Exit(1)
|
||
|
}
|