internal/pkg/logger/logger.go (120 lines of code) (raw):

// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one // or more contributor license agreements. Licensed under the Elastic License; // you may not use this file except in compliance with the Elastic License. // Package logger provides logging utilities for fleet-server. // Currently it wraps rs/zerolog package logger import ( "context" "io" "os" "path/filepath" "sync" "go.elastic.co/ecszerolog" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/elastic/elastic-agent-libs/file" "github.com/elastic/fleet-server/v7/internal/pkg/config" ) var once sync.Once var gLogger *Logger // WriterSync implements a Sync function. type WriterSync interface { // Sync syncs the logger to its output. Sync() error } // Logger for the Fleet Server. // // Logger will manage the zerolog/log.Logger variable. // An instance with TraceLevel is always created and log level is controlled through zerolog.GlobalLevel. type Logger struct { cfg *config.Config sync WriterSync name string log zerolog.Logger } // Reload reloads the logger configuration. // If only the log level has changed then only GlobalLogLevel is set. func (l *Logger) Reload(_ context.Context, cfg *config.Config) error { if levelChanged(cfg) { zerolog.SetGlobalLevel(level(cfg)) } if !l.cfg.Logging.EqualExcludeLevel(cfg.Logging) { // sync before set l.Sync() out, wr, err := getOutput(cfg) if err != nil { return err } l.log = l.log.Output(out) l.sync = wr log.Logger = l.log zerolog.DefaultContextLogger = &l.log // introduces race conditions in integration test? } l.cfg = cfg return nil } // Sync syncs the logger to its output. func (l *Logger) Sync() { if l.sync != nil { l.sync.Sync() //nolint: errcheck // nowhere to report an error } } // Init initializes the logger. func Init(cfg *config.Config, svcName string) (*Logger, error) { var err error once.Do(func() { zerolog.SetGlobalLevel(level(cfg)) out, wr, err := getOutput(cfg) if err != nil { return } l := ecszerolog.New(out) if svcName != "" { l = l.With().Str(ECSServiceName, svcName).Str(ECSServiceType, svcName).Logger() } log.Logger = l zerolog.DefaultContextLogger = &l gLogger = &Logger{ cfg: cfg, sync: wr, name: svcName, log: l, } }) return gLogger, err } func levelChanged(cfg *config.Config) bool { return level(cfg) != zerolog.GlobalLevel() } func level(cfg *config.Config) zerolog.Level { return cfg.Logging.LogLevel() } func stderrOut(cfg *config.Config) (io.Writer, WriterSync) { out := io.Writer(os.Stderr) if cfg.Logging.Pretty { out = zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: "15:04:05.000"} } return out, os.Stderr } func fileRotatorOut(cfg *config.Config) (io.Writer, WriterSync, error) { files := cfg.Logging.Files if files == nil { files = &config.LoggingFiles{} files.InitDefaults() } filename := filepath.Join(files.Path, files.Name) rotator, err := file.NewFileRotator(filename, file.MaxSizeBytes(files.MaxSize), file.MaxBackups(files.MaxBackups), file.Permissions(os.FileMode(files.Permissions)), file.Interval(files.Interval), file.RotateOnStartup(files.RotateOnStartup), file.RedirectStderr(files.RedirectStderr), ) if err != nil { return nil, nil, err } return rotator, rotator, nil } func getOutput(cfg *config.Config) (out io.Writer, wr WriterSync, err error) { switch { case cfg.Logging.ToStderr: out, wr = stderrOut(cfg) case cfg.Logging.ToFiles: out, wr, err = fileRotatorOut(cfg) default: out = io.Discard wr = &nopSync{} } return //nolint:nakedret // short function } type nopSync struct { } // Sync does nothing. func (*nopSync) Sync() error { return nil }