mirror of
https://codeberg.org/Codeberg/pages-server.git
synced 2025-04-25 06:16:58 +00:00
Improve logging
- Actually log useful information at their respective log level. - Add logs in hot-paths to be able to deep-dive and debug specific requests (see server/handler.go) - Remove zerologger and instead use custom logger that doesn't log JSON (directly inspired by https://codeberg.org/Codeberg/moderation/pulls/7). - Add more information to existing fields(e.g. the host that the user is visiting, this was noted by @fnetX).
This commit is contained in:
parent
00e8a41c89
commit
1e183d7249
13 changed files with 318 additions and 110 deletions
194
server/log/log.go
Normal file
194
server/log/log.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue