telegraf/logger/logger.go

295 lines
6.8 KiB
Go
Raw Normal View History

package logger
import (
2024-06-25 21:48:56 +08:00
"fmt"
"io"
"log"
"strings"
2024-06-25 21:48:56 +08:00
"sync"
"time"
2024-06-25 21:48:56 +08:00
"github.com/influxdata/telegraf"
)
// Central handler for the logs used by the logger to actually output the logs.
// This is necessary to be able to dynamically switch the sink even though
// plugins already instantiated a logger _before_ the final sink is set up.
var (
instance *handler // handler for the actual output
once sync.Once // once token to initialize the handler only once
)
// sink interface that has to be implemented by a logging sink
type sink interface {
Print(telegraf.LogLevel, time.Time, string, ...interface{})
}
// Attr represents an attribute appended to structured logging
type Attr struct {
Key string
Value interface{}
}
// logger is the actual implementation of the telegraf logger interface
type logger struct {
level *telegraf.LogLevel
category string
name string
alias string
suffix string
prefix string
onError []func()
}
// New creates a new logging instance to be used in models
func New(category, name, alias string) *logger {
l := &logger{
category: category,
name: name,
alias: alias,
}
l.formatPrefix()
return l
}
// SubLogger creates a new logger with the given name added as suffix
func (l *logger) SubLogger(name string) telegraf.Logger {
suffix := l.suffix
if suffix != "" && name != "" {
suffix += "."
}
suffix += name
nl := &logger{
level: l.level,
category: l.category,
name: l.name,
alias: l.alias,
suffix: suffix,
}
nl.formatPrefix()
return nl
}
func (l *logger) formatPrefix() {
l.prefix = l.category
if l.prefix != "" && l.name != "" {
l.prefix += "."
}
l.prefix += l.name
if l.prefix != "" && l.alias != "" {
l.prefix += "::"
}
l.prefix += l.alias
if l.suffix != "" {
l.prefix += "(" + l.suffix + ")"
}
if l.prefix != "" {
l.prefix = "[" + l.prefix + "] "
}
}
// Level returns the current log-level of the logger
func (l *logger) Level() telegraf.LogLevel {
if l.level != nil {
return *l.level
}
return instance.level
}
2024-06-25 21:48:56 +08:00
// SetLevel overrides the current log-level of the logger
func (l *logger) SetLevel(level telegraf.LogLevel) {
l.level = &level
}
// SetLevel changes the log-level to the given one
func (l *logger) SetLogLevel(name string) error {
if name == "" {
return nil
}
level := telegraf.LogLevelFromString(name)
if level == telegraf.None {
return fmt.Errorf("invalid log-level %q", name)
}
l.SetLevel(level)
return nil
}
// Register a callback triggered when errors are about to be written to the log
func (l *logger) RegisterErrorCallback(f func()) {
l.onError = append(l.onError, f)
}
// Error logging including callbacks
func (l *logger) Errorf(format string, args ...interface{}) {
l.Error(fmt.Sprintf(format, args...))
}
func (l *logger) Error(args ...interface{}) {
l.Print(telegraf.Error, time.Now(), args...)
for _, f := range l.onError {
f()
}
}
// Warning logging
func (l *logger) Warnf(format string, args ...interface{}) {
l.Warn(fmt.Sprintf(format, args...))
}
func (l *logger) Warn(args ...interface{}) {
l.Print(telegraf.Warn, time.Now(), args...)
}
// Info logging
func (l *logger) Infof(format string, args ...interface{}) {
l.Info(fmt.Sprintf(format, args...))
}
func (l *logger) Info(args ...interface{}) {
l.Print(telegraf.Info, time.Now(), args...)
}
// Debug logging, this is suppressed on console
func (l *logger) Debugf(format string, args ...interface{}) {
l.Debug(fmt.Sprintf(format, args...))
}
func (l *logger) Debug(args ...interface{}) {
l.Print(telegraf.Debug, time.Now(), args...)
}
// Trace logging, this is suppressed on console
func (l *logger) Tracef(format string, args ...interface{}) {
l.Trace(fmt.Sprintf(format, args...))
}
func (l *logger) Trace(args ...interface{}) {
l.Print(telegraf.Trace, time.Now(), args...)
}
func (l *logger) Print(level telegraf.LogLevel, ts time.Time, args ...interface{}) {
// Check if we are in early logging state and store the message in this case
if instance.impl == nil {
instance.add(level, ts, l.prefix, args...)
}
// Skip all messages with insufficient log-levels
if l.level != nil && !l.level.Includes(level) || l.level == nil && !instance.level.Includes(level) {
return
}
if instance.impl != nil {
instance.impl.Print(level, ts.In(instance.timezone), l.prefix, args...)
} else {
msg := append([]interface{}{ts.In(instance.timezone).Format(time.RFC3339), " ", level.Indicator(), " ", l.prefix}, args...)
instance.earlysink.Print(msg...)
}
2024-06-25 21:48:56 +08:00
}
type Config struct {
// will set the log level to DEBUG
Debug bool
// will set the log level to ERROR
Quiet bool
//stderr, stdout, file or eventlog (Windows only)
LogTarget string
// will direct the logging output to a file. Empty string is
// interpreted as stderr. If there is an error opening the file the
// logger will fall back to stderr
Logfile string
// will rotate when current file at the specified time interval
RotationInterval time.Duration
// will rotate when current file size exceeds this parameter.
RotationMaxSize int64
// maximum rotated files to keep (older ones will be deleted)
RotationMaxArchives int
// pick a timezone to use when logging. or type 'local' for local time.
LogWithTimezone string
// Logger instance name
InstanceName string
// internal log-level
logLevel telegraf.LogLevel
}
// SetupLogging configures the logging output.
2024-06-25 21:48:56 +08:00
func SetupLogging(cfg *Config) error {
if cfg.Debug {
2024-06-25 21:48:56 +08:00
cfg.logLevel = telegraf.Debug
}
if cfg.Quiet {
2024-06-25 21:48:56 +08:00
cfg.logLevel = telegraf.Error
}
if !cfg.Debug && !cfg.Quiet {
2024-06-25 21:48:56 +08:00
cfg.logLevel = telegraf.Info
}
if cfg.InstanceName == "" {
cfg.InstanceName = "telegraf"
}
if cfg.LogTarget == "" || cfg.LogTarget == "file" && cfg.Logfile == "" {
2024-06-25 21:48:56 +08:00
cfg.LogTarget = "stderr"
}
// Get configured timezone
timezoneName := cfg.LogWithTimezone
if strings.EqualFold(timezoneName, "local") {
timezoneName = "Local"
}
tz, err := time.LoadLocation(timezoneName)
if err != nil {
return fmt.Errorf("setting logging timezone failed: %w", err)
}
// Get the logging factory
2024-06-25 21:48:56 +08:00
creator, ok := registry[cfg.LogTarget]
if !ok {
return fmt.Errorf("unsupported log target: %s, using stderr", cfg.LogTarget)
}
// Create the root logging instance
2024-06-25 21:48:56 +08:00
l, err := creator(cfg)
if err != nil {
return err
}
// Close the previous logger if possible
if err := CloseLogging(); err != nil {
return err
}
// Update the logging instance
instance.switchSink(l, cfg.logLevel, tz, cfg.LogTarget == "stderr")
return nil
}
func RedirectLogging(w io.Writer) {
instance = redirectHandler(w)
2024-06-25 21:48:56 +08:00
}
func CloseLogging() error {
return instance.close()
2024-06-25 21:48:56 +08:00
}
2024-06-25 21:48:56 +08:00
func init() {
once.Do(func() {
// Create a special logging instance that additionally buffers all
// messages logged before the final logger is up.
instance = defaultHandler()
// Redirect the standard logger output to our logger instance
log.SetFlags(0)
log.SetOutput(&stdlogRedirector{})
2024-06-25 21:48:56 +08:00
})
}