@@ -10,15 +10,28 @@ import (
1010 "sync"
1111 "time"
1212
13+ "github.com/google/uuid"
14+
1315 "github.com/influxdata/telegraf"
1416)
1517
1618// Central handler for the logs used by the logger to actually output the logs.
1719// This is necessary to be able to dynamically switch the sink even though
1820// plugins already instantiated a logger _before_ the final sink is set up.
1921var (
20- instance * handler // handler for the actual output
21- once sync.Once // once token to initialize the handler only once
22+ instance * handler // handler for the actual output
23+ callbacks map [string ]CallbackFunc // logging callback registry
24+ callbackMu sync.RWMutex
25+ once sync.Once // once token to initialize the handler only once
26+ )
27+
28+ // CallbackFunc is a function to be called when printing messages
29+ type CallbackFunc func (
30+ level telegraf.LogLevel ,
31+ timestamp time.Time ,
32+ source string ,
33+ attributes map [string ]interface {},
34+ arguments ... interface {},
2235)
2336
2437// sink interface that has to be implemented by a logging sink
@@ -33,6 +46,7 @@ type logger struct {
3346 name string
3447 alias string
3548
49+ source string
3650 prefix string
3751 onError []func ()
3852 attributes map [string ]interface {}
@@ -51,20 +65,19 @@ func New(category, name, alias string) *logger {
5165 }
5266
5367 // Format the prefix
54- l .prefix = l .category
68+ l .source = l .category
5569
56- if l .prefix != "" && l .name != "" {
57- l .prefix += "."
70+ if l .source != "" && l .name != "" {
71+ l .source += "."
5872 }
59- l .prefix += l .name
73+ l .source += l .name
6074
61- if l .prefix != "" && l .alias != "" {
62- l .prefix += "::"
75+ if l .source != "" && l .alias != "" {
76+ l .source += "::"
6377 }
64- l .prefix += l .alias
65-
66- if l .prefix != "" {
67- l .prefix = "[" + l .prefix + "] "
78+ l .source += l .alias
79+ if l .source != "" {
80+ l .prefix = "[" + l .source + "] "
6881 }
6982
7083 return l
@@ -142,6 +155,14 @@ func (l *logger) Print(level telegraf.LogLevel, ts time.Time, args ...interface{
142155 instance .add (level , ts , l .prefix , l .attributes , args ... )
143156 }
144157
158+ // Serve all registered callbacks before checking the log-level. This is
159+ // intentional to allow the callback to apply its own log-level filtering.
160+ callbackMu .RLock ()
161+ for _ , cb := range callbacks {
162+ cb (level , ts .UTC (), l .source , l .attributes , args ... )
163+ }
164+ callbackMu .RUnlock ()
165+
145166 // Skip all messages with insufficient log-levels
146167 if l .level != nil && ! l .level .Includes (level ) || l .level == nil && ! instance .level .Includes (level ) {
147168 return
@@ -301,12 +322,41 @@ func CloseLogging() error {
301322 return nil
302323}
303324
325+ // AddCallback adds the given callback function to the registry and returns an
326+ // ID that can be used for removing the callback later. Callback functions must
327+ // not block or take a lot of time!
328+ func AddCallback (callback CallbackFunc ) (string , error ) {
329+ // Create a cookie to be returned to the caller in order to be able to
330+ // remove the callback later
331+ rawid , err := uuid .NewRandom ()
332+ if err != nil {
333+ return "" , err
334+ }
335+ id := rawid .String ()
336+
337+ callbackMu .Lock ()
338+ callbacks [id ] = callback
339+ callbackMu .Unlock ()
340+
341+ return id , nil
342+ }
343+
344+ // RemoveCallback removes the callback function with the given ID from the registry
345+ func RemoveCallback (id string ) {
346+ callbackMu .Lock ()
347+ defer callbackMu .Unlock ()
348+ delete (callbacks , id )
349+ }
350+
304351func init () {
305352 once .Do (func () {
306353 // Create a special logging instance that additionally buffers all
307354 // messages logged before the final logger is up.
308355 instance = defaultHandler ()
309356
357+ // Setup callback registry
358+ callbacks = make (map [string ]CallbackFunc )
359+
310360 // Redirect the standard logger output to our logger instance
311361 log .SetFlags (0 )
312362 log .SetOutput (& stdlogRedirector {})
0 commit comments