mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-03-09 07:13:35 -05:00
Previously, `makeLogHandler()` hardcoded "standard" as the logfile name passed to `getLogWriter()`, causing all log categories (`database`, `http`, `events`, `mail`) to write to `standard.log` instead of their own files. Add a logfile parameter to `makeLogHandler()` so each caller specifies its category name, producing `database.log`, `http.log`, `echo.log`, `events.log`, and `mail.log` as expected. Fixes https://github.com/go-vikunja/vikunja/issues/2177
204 lines
5.9 KiB
Go
204 lines
5.9 KiB
Go
// Vikunja is a to-do list application to facilitate your life.
|
|
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package log
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// logInstance is the instance of the logger which is used under the hood to log
|
|
var logInstance *slog.Logger
|
|
|
|
// logpath is the path in which log files will be written.
|
|
// This value is a mere fallback for other modules that could but shouldn't be used before calling ConfigureLogger
|
|
var logPath = "."
|
|
|
|
// InitLogger initializes the global log handler
|
|
func InitLogger() {
|
|
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})
|
|
logInstance = slog.New(handler)
|
|
}
|
|
|
|
func makeLogHandler(enabled bool, output string, logfile string, level string, format string) slog.Handler {
|
|
var slogLevel slog.Level
|
|
switch strings.ToUpper(level) {
|
|
case "CRITICAL", "ERROR":
|
|
slogLevel = slog.LevelError
|
|
case "WARNING":
|
|
slogLevel = slog.LevelWarn
|
|
case "NOTICE", "INFO":
|
|
slogLevel = slog.LevelInfo
|
|
case "DEBUG":
|
|
slogLevel = slog.LevelDebug
|
|
default:
|
|
slogLevel = slog.LevelInfo
|
|
}
|
|
|
|
format = strings.ToLower(format)
|
|
if format == "" {
|
|
format = "text"
|
|
}
|
|
if format != "text" && format != "structured" {
|
|
Fatalf("invalid log format %s", format)
|
|
}
|
|
|
|
writer := io.Discard
|
|
if enabled && output != "off" {
|
|
writer = getLogWriter(output, logfile)
|
|
}
|
|
|
|
return createHandler(writer, slogLevel, format)
|
|
}
|
|
|
|
// createHandler creates a consistent slog handler for all loggers
|
|
func createHandler(writer io.Writer, level slog.Level, format string) slog.Handler {
|
|
handlerOpts := &slog.HandlerOptions{
|
|
Level: level,
|
|
ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr {
|
|
// Remove message attribute when empty
|
|
if a.Key == slog.MessageKey && a.Value.String() == "" {
|
|
return slog.Attr{}
|
|
}
|
|
return a
|
|
},
|
|
}
|
|
if strings.ToLower(format) == "structured" {
|
|
return slog.NewJSONHandler(writer, handlerOpts)
|
|
}
|
|
|
|
return slog.NewTextHandler(writer, handlerOpts)
|
|
}
|
|
|
|
// NewHTTPLogger creates and initializes a new HTTP logger
|
|
func NewHTTPLogger(enabled bool, output string, format string) *slog.Logger {
|
|
handler := makeLogHandler(enabled, output, "http", "DEBUG", format)
|
|
|
|
return slog.New(handler).With("component", "http")
|
|
}
|
|
|
|
// ConfigureStandardLogger configures the global log handler
|
|
func ConfigureStandardLogger(enabled bool, output string, path string, level string, format string) {
|
|
logPath = path
|
|
handler := makeLogHandler(enabled, output, "standard", level, format)
|
|
logInstance = slog.New(handler)
|
|
}
|
|
|
|
// wrapLogger is used for libraries requiring a Debugf method.
|
|
type wrapLogger struct{}
|
|
|
|
func (wrapLogger) Debugf(format string, args ...interface{}) {
|
|
logInstance.Debug(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// GetLogWriter returns the writer to where the normal log goes, depending on the config
|
|
func getLogWriter(logfmt string, logfile string) (writer io.Writer) {
|
|
writer = os.Stdout // Set the default case to prevent nil pointer panics
|
|
switch logfmt {
|
|
case "file":
|
|
if err := os.MkdirAll(logPath, 0744); err != nil {
|
|
Fatalf("Could not create log path: %s", err.Error())
|
|
}
|
|
fullLogFilePath := logPath + "/" + logfile + ".log"
|
|
f, err := os.OpenFile(fullLogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
|
if err != nil {
|
|
Fatalf("Could not create logfile %s: %s", fullLogFilePath, err.Error())
|
|
}
|
|
writer = f
|
|
case "stderr":
|
|
writer = os.Stderr
|
|
case "stdout":
|
|
default:
|
|
writer = os.Stdout
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetLogger returns the logging instance. DO NOT USE THIS TO LOG STUFF.
|
|
// GetLogger returns a logger which can be used by external libraries expecting a Debugf method.
|
|
// It only implements Debugf and forwards to the global logger.
|
|
func GetLogger() interface{ Debugf(string, ...interface{}) } {
|
|
return wrapLogger{}
|
|
}
|
|
|
|
// The following functions are to be used as an "eye-candy", so one can just write log.Error() instead of log.Log.Error()
|
|
|
|
// Debug is for debug messages
|
|
func Debug(args ...interface{}) {
|
|
logInstance.Debug(fmt.Sprint(args...))
|
|
}
|
|
|
|
// Debugf is for debug messages
|
|
func Debugf(format string, args ...interface{}) {
|
|
logInstance.Debug(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// Info is for info messages
|
|
func Info(args ...interface{}) {
|
|
logInstance.Info(fmt.Sprint(args...))
|
|
}
|
|
|
|
// Infof is for info messages
|
|
func Infof(format string, args ...interface{}) {
|
|
logInstance.Info(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// Error is for error messages
|
|
func Error(args ...interface{}) {
|
|
logInstance.Error(fmt.Sprint(args...))
|
|
}
|
|
|
|
// Errorf is for error messages
|
|
func Errorf(format string, args ...interface{}) {
|
|
logInstance.Error(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// Warning is for warning messages
|
|
func Warning(args ...interface{}) {
|
|
logInstance.Warn(fmt.Sprint(args...))
|
|
}
|
|
|
|
// Warningf is for warning messages
|
|
func Warningf(format string, args ...interface{}) {
|
|
logInstance.Warn(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// Critical is for critical messages
|
|
func Critical(args ...interface{}) {
|
|
logInstance.Error(fmt.Sprint(args...))
|
|
}
|
|
|
|
// Criticalf is for critical messages
|
|
func Criticalf(format string, args ...interface{}) {
|
|
logInstance.Error(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
// Fatal is for fatal messages
|
|
func Fatal(args ...interface{}) {
|
|
logInstance.Error(fmt.Sprint(args...))
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Fatalf is for fatal messages
|
|
func Fatalf(format string, args ...interface{}) {
|
|
logInstance.Error(fmt.Sprintf(format, args...))
|
|
os.Exit(1)
|
|
}
|