Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 62 additions & 12 deletions logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,28 @@ import (
"sync"
"time"

"github.com/google/uuid"

"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
instance *handler // handler for the actual output
callbacks map[string]CallbackFunc // logging callback registry
callbackMu sync.RWMutex
once sync.Once // once token to initialize the handler only once
)

// CallbackFunc is a function to be called when printing messages
type CallbackFunc func(
level telegraf.LogLevel,
timestamp time.Time,
source string,
attributes map[string]interface{},
arguments ...interface{},
)

// sink interface that has to be implemented by a logging sink
Expand All @@ -33,6 +46,7 @@ type logger struct {
name string
alias string

source string
prefix string
onError []func()
attributes map[string]interface{}
Expand All @@ -51,20 +65,19 @@ func New(category, name, alias string) *logger {
}

// Format the prefix
l.prefix = l.category
l.source = l.category

if l.prefix != "" && l.name != "" {
l.prefix += "."
if l.source != "" && l.name != "" {
l.source += "."
}
l.prefix += l.name
l.source += l.name

if l.prefix != "" && l.alias != "" {
l.prefix += "::"
if l.source != "" && l.alias != "" {
l.source += "::"
}
l.prefix += l.alias

if l.prefix != "" {
l.prefix = "[" + l.prefix + "] "
l.source += l.alias
if l.source != "" {
l.prefix = "[" + l.source + "] "
}

return l
Expand Down Expand Up @@ -142,6 +155,14 @@ func (l *logger) Print(level telegraf.LogLevel, ts time.Time, args ...interface{
instance.add(level, ts, l.prefix, l.attributes, args...)
}

// Serve all registered callbacks before checking the log-level. This is
// intentional to allow the callback to apply its own log-level filtering.
callbackMu.RLock()
for _, cb := range callbacks {
cb(level, ts.UTC(), l.source, l.attributes, args...)
}
callbackMu.RUnlock()

// Skip all messages with insufficient log-levels
if l.level != nil && !l.level.Includes(level) || l.level == nil && !instance.level.Includes(level) {
return
Expand Down Expand Up @@ -301,12 +322,41 @@ func CloseLogging() error {
return nil
}

// AddCallback adds the given callback function to the registry and returns an
// ID that can be used for removing the callback later. Callback functions must
// not block or take a lot of time!
func AddCallback(callback CallbackFunc) (string, error) {
// Create a cookie to be returned to the caller in order to be able to
// remove the callback later
rawid, err := uuid.NewRandom()
if err != nil {
return "", err
}
id := rawid.String()

callbackMu.Lock()
callbacks[id] = callback
callbackMu.Unlock()

return id, nil
}

// RemoveCallback removes the callback function with the given ID from the registry
func RemoveCallback(id string) {
callbackMu.Lock()
defer callbackMu.Unlock()
delete(callbacks, id)
}

func init() {
once.Do(func() {
// Create a special logging instance that additionally buffers all
// messages logged before the final logger is up.
instance = defaultHandler()

// Setup callback registry
callbacks = make(map[string]CallbackFunc)

// Redirect the standard logger output to our logger instance
log.SetFlags(0)
log.SetOutput(&stdlogRedirector{})
Expand Down
Loading