Files
opencloud/pkg/log/log.go
Ralf Haferkamp e07f0154bb Rebrand pkg
2025-01-20 10:59:08 +01:00

146 lines
3.6 KiB
Go

package log
import (
"context"
"fmt"
"os"
"runtime"
"strings"
"time"
chimiddleware "github.com/go-chi/chi/v5/middleware"
mzlog "github.com/go-micro/plugins/v4/logger/zerolog"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"go-micro.dev/v4/logger"
)
var (
RequestIDString = "request-id"
)
func init() {
// this is ugly, but "logger.DefaultLogger" is a global variable, and we need to set it _before_ anybody uses it
setMicroLogger()
}
// for logging reasons we don't want the same logging level on both OpenCloud and micro. As a framework builder we do not
// want to expose to the end user the internal framework logs unless explicitly specified.
func setMicroLogger() {
if os.Getenv("MICRO_LOG_LEVEL") == "" {
_ = os.Setenv("MICRO_LOG_LEVEL", "error")
}
lev, err := zerolog.ParseLevel(os.Getenv("MICRO_LOG_LEVEL"))
if err != nil {
lev = zerolog.ErrorLevel
}
logger.DefaultLogger = mzlog.NewLogger(
logger.WithLevel(logger.Level(lev)),
logger.WithFields(map[string]interface{}{
"system": "go-micro",
}),
)
}
// Logger simply wraps the zerolog logger.
type Logger struct {
zerolog.Logger
}
// NopLogger initializes a no-operation logger.
func NopLogger() Logger {
return Logger{zerolog.Nop()}
}
type LineInfoHook struct{}
// Run is a hook to add line info to log messages.
// I found the zerolog example for this here:
// https://github.com/rs/zerolog/issues/22#issuecomment-1127295489
func (h LineInfoHook) Run(e *zerolog.Event, _ zerolog.Level, _ string) {
_, file, line, ok := runtime.Caller(3)
if ok {
e.Str("line", fmt.Sprintf("%s:%d", file, line))
}
}
// NewLogger initializes a new logger instance.
func NewLogger(opts ...Option) Logger {
options := newOptions(opts...)
// set GlobalLevel() to the minimum value -1 = TraceLevel, so that only the services' log level matter
zerolog.SetGlobalLevel(zerolog.TraceLevel)
var logLevel zerolog.Level
switch strings.ToLower(options.Level) {
case "panic":
logLevel = zerolog.PanicLevel
case "fatal":
logLevel = zerolog.FatalLevel
case "error":
logLevel = zerolog.ErrorLevel
case "warn":
logLevel = zerolog.WarnLevel
case "info":
logLevel = zerolog.InfoLevel
case "debug":
logLevel = zerolog.DebugLevel
case "trace":
logLevel = zerolog.TraceLevel
default:
logLevel = zerolog.ErrorLevel
}
var l zerolog.Logger
if options.Pretty {
l = log.Output(
zerolog.NewConsoleWriter(
func(w *zerolog.ConsoleWriter) {
w.TimeFormat = time.RFC3339
w.Out = os.Stderr
w.NoColor = !options.Color
},
),
)
} else if options.File != "" {
f, err := os.OpenFile(options.File, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
if err != nil {
print(fmt.Sprintf("file could not be opened for writing: %s. error: %v", options.File, err))
os.Exit(1)
}
l = l.Output(f)
} else {
l = zerolog.New(os.Stderr)
}
l = l.With().
Str("service", options.Name).
Timestamp().
Logger().Level(logLevel)
if logLevel <= zerolog.InfoLevel {
var lineInfoHook LineInfoHook
l = l.Hook(lineInfoHook)
}
return Logger{
l,
}
}
// SubloggerWithRequestID returns a sub-logger with the x-request-id added to all events
func (l Logger) SubloggerWithRequestID(c context.Context) Logger {
return Logger{
l.With().Str(RequestIDString, chimiddleware.GetReqID(c)).Logger(),
}
}
// Deprecation logs a deprecation message,
// it is used to inform the user that a certain feature is deprecated and will be removed in the future.
// Do not use a logger here because the message MUST be visible independent of the log level.
func Deprecation(a ...any) {
fmt.Printf("\033[1;31mDEPRECATION: %s\033[0m\n", a...)
}