chore(logging): Refactor code (#15556)
This commit is contained in:
parent
6da035ba44
commit
32a5b44d7b
|
|
@ -84,7 +84,7 @@ To migrate the file 'mysettings.conf' use
|
||||||
},
|
},
|
||||||
Action: func(cCtx *cli.Context) error {
|
Action: func(cCtx *cli.Context) error {
|
||||||
// Setup logging
|
// Setup logging
|
||||||
logConfig := logger.Config{Debug: cCtx.Bool("debug")}
|
logConfig := &logger.Config{Debug: cCtx.Bool("debug")}
|
||||||
if err := logger.SetupLogging(logConfig); err != nil {
|
if err := logger.SetupLogging(logConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ func runApp(args []string, outputBuffer io.Writer, pprof Server, c TelegrafConfi
|
||||||
return fmt.Errorf("unknown command %q", cCtx.Args().First())
|
return fmt.Errorf("unknown command %q", cCtx.Args().First())
|
||||||
}
|
}
|
||||||
|
|
||||||
err := logger.SetupLogging(logger.Config{})
|
err := logger.SetupLogging(&logger.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ func (t *Telegraf) runAgent(ctx context.Context, reloadConfig bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup logging as configured.
|
// Setup logging as configured.
|
||||||
logConfig := logger.Config{
|
logConfig := &logger.Config{
|
||||||
Debug: c.Agent.Debug || t.debug,
|
Debug: c.Agent.Debug || t.debug,
|
||||||
Quiet: c.Agent.Quiet || t.quiet,
|
Quiet: c.Agent.Quiet || t.quiet,
|
||||||
LogTarget: c.Agent.LogTarget,
|
LogTarget: c.Agent.LogTarget,
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@ import (
|
||||||
"golang.org/x/sys/windows/svc"
|
"golang.org/x/sys/windows/svc"
|
||||||
"golang.org/x/sys/windows/svc/eventlog"
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
"golang.org/x/sys/windows/svc/mgr"
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getLockedMemoryLimit() uint64 {
|
func getLockedMemoryLimit() uint64 {
|
||||||
|
|
@ -31,11 +29,6 @@ func getLockedMemoryLimit() uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Telegraf) Run() error {
|
func (t *Telegraf) Run() error {
|
||||||
// Register the eventlog logging target for windows.
|
|
||||||
if err := logger.RegisterEventLogger(t.serviceName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the service commands
|
// Process the service commands
|
||||||
if t.service != "" {
|
if t.service != "" {
|
||||||
fmt.Println("The use of --service is deprecated, please use the 'service' command instead!")
|
fmt.Println("The use of --service is deprecated, please use the 'service' command instead!")
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,6 @@ func printPluginDeprecationNotice(level telegraf.LogLevel, name string, info tel
|
||||||
switch level {
|
switch level {
|
||||||
case telegraf.Warn, telegraf.Error:
|
case telegraf.Warn, telegraf.Error:
|
||||||
prefix := deprecationPrefix(level)
|
prefix := deprecationPrefix(level)
|
||||||
|
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"%s: Plugin %q deprecated since version %s and will be removed in %s: %s",
|
"%s: Plugin %q deprecated since version %s and will be removed in %s: %s",
|
||||||
prefix, name, info.Since, info.RemovalIn, info.Notice,
|
prefix, name, info.Since, info.RemovalIn, info.Notice,
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,6 @@ func TestPluginDeprecation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.expected != "" {
|
if tt.expected != "" {
|
||||||
// Remove the time for comparison
|
|
||||||
parts := strings.SplitN(actual, " ", 3)
|
|
||||||
require.Len(t, parts, 3)
|
|
||||||
actual = parts[2]
|
|
||||||
expected := deprecationPrefix(tt.level) + ": " + tt.expected
|
expected := deprecationPrefix(tt.level) + ": " + tt.expected
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -160,10 +156,6 @@ func TestPluginOptionDeprecation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if tt.expected != "" {
|
if tt.expected != "" {
|
||||||
// Remove the time for comparison
|
|
||||||
parts := strings.SplitN(actual, " ", 3)
|
|
||||||
require.Len(t, parts, 3)
|
|
||||||
actual = parts[2]
|
|
||||||
expected := deprecationPrefix(tt.expectedLevel) + ": " + tt.expected
|
expected := deprecationPrefix(tt.expectedLevel) + ": " + tt.expected
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -259,9 +251,7 @@ func TestPluginOptionValueDeprecation(t *testing.T) {
|
||||||
}, timeout, 100*time.Millisecond)
|
}, timeout, 100*time.Millisecond)
|
||||||
|
|
||||||
// Remove the time for comparison
|
// Remove the time for comparison
|
||||||
parts := strings.SplitN(strings.TrimSpace(buf.String()), " ", 3)
|
actual := strings.TrimSpace(buf.String())
|
||||||
require.Len(t, parts, 3)
|
|
||||||
actual := parts[2]
|
|
||||||
expected := deprecationPrefix(tt.expectedLevel) + ": " + tt.expected
|
expected := deprecationPrefix(tt.expectedLevel) + ": " + tt.expected
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package logger
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Config contains the log configuration settings
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -5,22 +5,28 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/internal/rotate"
|
"github.com/influxdata/telegraf/internal/rotate"
|
||||||
"github.com/influxdata/wlog"
|
"github.com/influxdata/wlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var prefixRegex = regexp.MustCompile("^[DIWE]!")
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LogTargetFile = "file"
|
LogTargetFile = "file"
|
||||||
LogTargetStderr = "stderr"
|
LogTargetStderr = "stderr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type defaultLogger struct {
|
type defaultLogger struct {
|
||||||
|
Category string
|
||||||
|
Name string
|
||||||
|
Alias string
|
||||||
|
LogLevel telegraf.LogLevel
|
||||||
|
|
||||||
|
prefix string
|
||||||
|
onError []func()
|
||||||
|
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
internalWriter io.Writer
|
internalWriter io.Writer
|
||||||
timezone *time.Location
|
timezone *time.Location
|
||||||
|
|
@ -39,6 +45,32 @@ func (t *defaultLogger) Write(b []byte) (n int, err error) {
|
||||||
return t.writer.Write(line)
|
return t.writer.Write(line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLogger creates a new logger instance
|
||||||
|
func (t *defaultLogger) New(category, name, alias string) telegraf.Logger {
|
||||||
|
var prefix string
|
||||||
|
if category != "" {
|
||||||
|
prefix = "[" + category
|
||||||
|
if name != "" {
|
||||||
|
prefix += "." + name
|
||||||
|
}
|
||||||
|
if alias != "" {
|
||||||
|
prefix += "::" + alias
|
||||||
|
}
|
||||||
|
prefix += "] "
|
||||||
|
}
|
||||||
|
|
||||||
|
return &defaultLogger{
|
||||||
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Alias: alias,
|
||||||
|
LogLevel: t.LogLevel,
|
||||||
|
prefix: prefix,
|
||||||
|
writer: t.writer,
|
||||||
|
internalWriter: t.internalWriter,
|
||||||
|
timezone: t.timezone,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *defaultLogger) Close() error {
|
func (t *defaultLogger) Close() error {
|
||||||
// avoid closing stderr
|
// avoid closing stderr
|
||||||
if t.internalWriter == os.Stderr {
|
if t.internalWriter == os.Stderr {
|
||||||
|
|
@ -52,49 +84,113 @@ func (t *defaultLogger) Close() error {
|
||||||
return closer.Close()
|
return closer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTelegrafWriter returns a logging-wrapped writer.
|
// OnErr defines a callback that triggers only when errors are about to be written to the log
|
||||||
func newTelegrafWriter(w io.Writer, c Config) (*defaultLogger, error) {
|
func (t *defaultLogger) RegisterErrorCallback(f func()) {
|
||||||
timezoneName := c.LogWithTimezone
|
t.onError = append(t.onError, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *defaultLogger) Level() telegraf.LogLevel {
|
||||||
|
return t.LogLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs an error message, patterned after log.Printf.
|
||||||
|
func (t *defaultLogger) Errorf(format string, args ...interface{}) {
|
||||||
|
log.Printf("E! "+t.prefix+format, args...)
|
||||||
|
for _, f := range t.onError {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs an error message, patterned after log.Print.
|
||||||
|
func (t *defaultLogger) Error(args ...interface{}) {
|
||||||
|
for _, f := range t.onError {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
log.Print(append([]interface{}{"E! " + t.prefix}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a debug message, patterned after log.Printf.
|
||||||
|
func (t *defaultLogger) Debugf(format string, args ...interface{}) {
|
||||||
|
log.Printf("D! "+t.prefix+" "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a debug message, patterned after log.Print.
|
||||||
|
func (t *defaultLogger) Debug(args ...interface{}) {
|
||||||
|
log.Print(append([]interface{}{"D! " + t.prefix}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a warning message, patterned after log.Printf.
|
||||||
|
func (t *defaultLogger) Warnf(format string, args ...interface{}) {
|
||||||
|
log.Printf("W! "+t.prefix+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a warning message, patterned after log.Print.
|
||||||
|
func (t *defaultLogger) Warn(args ...interface{}) {
|
||||||
|
log.Print(append([]interface{}{"W! " + t.prefix}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs an information message, patterned after log.Printf.
|
||||||
|
func (t *defaultLogger) Infof(format string, args ...interface{}) {
|
||||||
|
log.Printf("I! "+t.prefix+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs an information message, patterned after log.Print.
|
||||||
|
func (t *defaultLogger) Info(args ...interface{}) {
|
||||||
|
log.Print(append([]interface{}{"I! " + t.prefix}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDefaultLogger(cfg *Config) (logger, error) {
|
||||||
|
log.SetFlags(0)
|
||||||
|
|
||||||
|
// Set the log-level
|
||||||
|
switch cfg.logLevel {
|
||||||
|
case telegraf.Error:
|
||||||
|
wlog.SetLevel(wlog.ERROR)
|
||||||
|
case telegraf.Warn:
|
||||||
|
wlog.SetLevel(wlog.WARN)
|
||||||
|
case telegraf.Info:
|
||||||
|
wlog.SetLevel(wlog.INFO)
|
||||||
|
case telegraf.Debug:
|
||||||
|
wlog.SetLevel(wlog.DEBUG)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the writer target
|
||||||
|
var writer io.Writer = os.Stderr
|
||||||
|
if cfg.LogTarget == "file" && cfg.Logfile != "" {
|
||||||
|
w, err := rotate.NewFileWriter(
|
||||||
|
cfg.Logfile,
|
||||||
|
cfg.RotationInterval,
|
||||||
|
cfg.RotationMaxSize,
|
||||||
|
cfg.RotationMaxArchives,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
writer = w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get configured timezone
|
||||||
|
timezoneName := cfg.LogWithTimezone
|
||||||
if strings.EqualFold(timezoneName, "local") {
|
if strings.EqualFold(timezoneName, "local") {
|
||||||
timezoneName = "Local"
|
timezoneName = "Local"
|
||||||
}
|
}
|
||||||
|
|
||||||
tz, err := time.LoadLocation(timezoneName)
|
tz, err := time.LoadLocation(timezoneName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error while setting logging timezone: " + err.Error())
|
return nil, errors.New("error while setting logging timezone: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &defaultLogger{
|
// Setup the logger
|
||||||
writer: wlog.NewWriter(w),
|
l := &defaultLogger{
|
||||||
internalWriter: w,
|
writer: wlog.NewWriter(writer),
|
||||||
|
internalWriter: writer,
|
||||||
timezone: tz,
|
timezone: tz,
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createStderrLogger(cfg Config) (io.WriteCloser, error) {
|
|
||||||
return newTelegrafWriter(os.Stderr, cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFileLogger(cfg Config) (io.WriteCloser, error) {
|
|
||||||
if cfg.Logfile == "" {
|
|
||||||
return createStderrLogger(cfg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writer, err := rotate.NewFileWriter(
|
log.SetOutput(l)
|
||||||
cfg.Logfile,
|
return l, nil
|
||||||
cfg.RotationInterval,
|
|
||||||
cfg.RotationMaxSize,
|
|
||||||
cfg.RotationMaxArchives,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("E! Unable to open %s (%s), using stderr", cfg.Logfile, err)
|
|
||||||
return createStderrLogger(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return newTelegrafWriter(writer, cfg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerLogger(LogTargetStderr, createStderrLogger)
|
add("stderr", createDefaultLogger)
|
||||||
registerLogger(LogTargetFile, createFileLogger)
|
add("file", createDefaultLogger)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/influxdata/wlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWriteLogToFile(t *testing.T) {
|
func TestWriteLogToFile(t *testing.T) {
|
||||||
|
|
@ -107,7 +109,7 @@ func TestWriteToFileInRotation(t *testing.T) {
|
||||||
cfg.RotationMaxSize = 30
|
cfg.RotationMaxSize = 30
|
||||||
require.NoError(t, SetupLogging(cfg))
|
require.NoError(t, SetupLogging(cfg))
|
||||||
// Close the writer here, otherwise the temp folder cannot be deleted because the current log file is in use.
|
// Close the writer here, otherwise the temp folder cannot be deleted because the current log file is in use.
|
||||||
t.Cleanup(func() { require.NoError(t, actualLogger.Close()) })
|
t.Cleanup(func() { require.NoError(t, instance.Close()) })
|
||||||
|
|
||||||
log.Printf("I! TEST 1") // Writes 31 bytes, will rotate
|
log.Printf("I! TEST 1") // Writes 31 bytes, will rotate
|
||||||
log.Printf("I! TEST") // Writes 29 byes, no rotation expected
|
log.Printf("I! TEST") // Writes 29 byes, no rotation expected
|
||||||
|
|
@ -117,46 +119,42 @@ func TestWriteToFileInRotation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogTargetSettings(t *testing.T) {
|
func TestLogTargetSettings(t *testing.T) {
|
||||||
actualLogger = nil
|
instance = nil
|
||||||
cfg := Config{
|
cfg := &Config{
|
||||||
LogTarget: "",
|
LogTarget: "",
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
}
|
}
|
||||||
err := SetupLogging(cfg)
|
require.NoError(t, SetupLogging(cfg))
|
||||||
require.NoError(t, err)
|
logger, isTelegrafLogger := instance.(*defaultLogger)
|
||||||
logger, isTelegrafLogger := actualLogger.(*defaultLogger)
|
|
||||||
require.True(t, isTelegrafLogger)
|
require.True(t, isTelegrafLogger)
|
||||||
require.Equal(t, logger.internalWriter, os.Stderr)
|
require.Equal(t, logger.internalWriter, os.Stderr)
|
||||||
|
|
||||||
cfg = Config{
|
cfg = &Config{
|
||||||
LogTarget: "stderr",
|
LogTarget: "stderr",
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
}
|
}
|
||||||
err = SetupLogging(cfg)
|
require.NoError(t, SetupLogging(cfg))
|
||||||
require.NoError(t, err)
|
logger, isTelegrafLogger = instance.(*defaultLogger)
|
||||||
logger, isTelegrafLogger = actualLogger.(*defaultLogger)
|
|
||||||
require.True(t, isTelegrafLogger)
|
require.True(t, isTelegrafLogger)
|
||||||
require.Equal(t, logger.internalWriter, os.Stderr)
|
require.Equal(t, logger.internalWriter, os.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkTelegrafLogWrite(b *testing.B) {
|
func BenchmarkTelegrafLogWrite(b *testing.B) {
|
||||||
var msg = []byte("test")
|
l, err := createDefaultLogger(&Config{})
|
||||||
var buf bytes.Buffer
|
require.NoError(b, err)
|
||||||
w, err := newTelegrafWriter(&buf, Config{})
|
|
||||||
if err != nil {
|
// Discard all logging output
|
||||||
panic("Unable to create log writer.")
|
dl := l.(*defaultLogger)
|
||||||
}
|
dl.writer = wlog.NewWriter(io.Discard)
|
||||||
|
dl.internalWriter = io.Discard
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
buf.Reset()
|
dl.Info("test")
|
||||||
_, err = w.Write(msg)
|
|
||||||
if err != nil {
|
|
||||||
panic("Unable to write message")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBasicConfig(filename string) Config {
|
func createBasicConfig(filename string) *Config {
|
||||||
return Config{
|
return &Config{
|
||||||
Logfile: filename,
|
Logfile: filename,
|
||||||
LogTarget: "file",
|
LogTarget: "file",
|
||||||
RotationMaxArchives: -1,
|
RotationMaxArchives: -1,
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/influxdata/wlog"
|
"github.com/influxdata/telegraf"
|
||||||
"golang.org/x/sys/windows/svc/eventlog"
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -17,63 +17,149 @@ const (
|
||||||
eidError = 3
|
eidError = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
type eventWriter struct {
|
type eventLogger struct {
|
||||||
logger *eventlog.Log
|
Category string
|
||||||
|
Name string
|
||||||
|
Alias string
|
||||||
|
LogLevel telegraf.LogLevel
|
||||||
|
|
||||||
|
prefix string
|
||||||
|
onError []func()
|
||||||
|
|
||||||
|
eventlog *eventlog.Log
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *eventWriter) Write(b []byte) (int, error) {
|
func (e *eventLogger) Write(b []byte) (int, error) {
|
||||||
loc := prefixRegex.FindIndex(b)
|
loc := prefixRegex.FindIndex(b)
|
||||||
n := len(b)
|
n := len(b)
|
||||||
if loc == nil {
|
if loc == nil {
|
||||||
return n, w.logger.Info(1, string(b))
|
return n, e.eventlog.Info(1, string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
//skip empty log messages
|
// Skip empty log messages
|
||||||
if n > 2 {
|
if n <= 2 {
|
||||||
line := strings.Trim(string(b[loc[1]:]), " \t\r\n")
|
return 0, nil
|
||||||
switch rune(b[loc[0]]) {
|
}
|
||||||
case 'I':
|
|
||||||
return n, w.logger.Info(eidInfo, line)
|
line := strings.Trim(string(b[loc[1]:]), " \t\r\n")
|
||||||
case 'W':
|
switch rune(b[loc[0]]) {
|
||||||
return n, w.logger.Warning(eidWarning, line)
|
case 'I':
|
||||||
case 'E':
|
return n, e.eventlog.Info(eidInfo, line)
|
||||||
return n, w.logger.Error(eidError, line)
|
case 'W':
|
||||||
}
|
return n, e.eventlog.Warning(eidWarning, line)
|
||||||
|
case 'E':
|
||||||
|
return n, e.eventlog.Error(eidError, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type eventLogger struct {
|
// NewLogger creates a new logger instance
|
||||||
writer io.Writer
|
func (e *eventLogger) New(category, name, alias string) telegraf.Logger {
|
||||||
eventlog *eventlog.Log
|
var prefix string
|
||||||
}
|
if category != "" {
|
||||||
|
prefix = "[" + category
|
||||||
|
if name != "" {
|
||||||
|
prefix += "." + name
|
||||||
|
}
|
||||||
|
if alias != "" {
|
||||||
|
prefix += "::" + alias
|
||||||
|
}
|
||||||
|
prefix += "] "
|
||||||
|
}
|
||||||
|
|
||||||
func (e *eventLogger) Write(b []byte) (int, error) {
|
return &eventLogger{
|
||||||
return e.writer.Write(b)
|
Category: category,
|
||||||
|
Name: name,
|
||||||
|
Alias: alias,
|
||||||
|
LogLevel: e.LogLevel,
|
||||||
|
prefix: prefix,
|
||||||
|
eventlog: e.eventlog,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *eventLogger) Close() error {
|
func (e *eventLogger) Close() error {
|
||||||
return e.eventlog.Close()
|
return e.eventlog.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createEventLogger(name string) creator {
|
// OnErr defines a callback that triggers only when errors are about to be written to the log
|
||||||
return func(Config) (io.WriteCloser, error) {
|
func (e *eventLogger) RegisterErrorCallback(f func()) {
|
||||||
eventLog, err := eventlog.Open(name)
|
e.onError = append(e.onError, f)
|
||||||
if err != nil {
|
}
|
||||||
log.Printf("E! An error occurred while initializing an event logger. %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
writer := wlog.NewWriter(&eventWriter{logger: eventLog})
|
func (e *eventLogger) Level() telegraf.LogLevel {
|
||||||
return &eventLogger{
|
return e.LogLevel
|
||||||
writer: writer,
|
}
|
||||||
eventlog: eventLog,
|
|
||||||
}, nil
|
// Errorf logs an error message, patterned after log.Printf.
|
||||||
|
func (e *eventLogger) Errorf(format string, args ...interface{}) {
|
||||||
|
e.Error(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs an error message, patterned after log.Print.
|
||||||
|
func (e *eventLogger) Error(args ...interface{}) {
|
||||||
|
if e.LogLevel >= telegraf.Error {
|
||||||
|
if err := e.eventlog.Error(eidError, "E! "+e.prefix+fmt.Sprint(args...)); err != nil {
|
||||||
|
log.Printf("E! Writing log message failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range e.onError {
|
||||||
|
f()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterEventLogger(name string) error {
|
// Warnf logs a warning message, patterned after log.Printf.
|
||||||
registerLogger("eventlog", createEventLogger(name))
|
func (e *eventLogger) Warnf(format string, args ...interface{}) {
|
||||||
return nil
|
e.Warn(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a warning message, patterned after log.Print.
|
||||||
|
func (e *eventLogger) Warn(args ...interface{}) {
|
||||||
|
if e.LogLevel < telegraf.Warn {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := e.eventlog.Warning(eidError, "W! "+e.prefix+fmt.Sprint(args...)); err != nil {
|
||||||
|
log.Printf("E! Writing log message failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs an information message, patterned after log.Printf.
|
||||||
|
func (e *eventLogger) Infof(format string, args ...interface{}) {
|
||||||
|
e.Info(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs an information message, patterned after log.Print.
|
||||||
|
func (e *eventLogger) Info(args ...interface{}) {
|
||||||
|
if e.LogLevel < telegraf.Info {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := e.eventlog.Info(eidError, "I! "+e.prefix+fmt.Sprint(args...)); err != nil {
|
||||||
|
log.Printf("E! Writing log message failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No debugging output for eventlog to not spam the service
|
||||||
|
func (e *eventLogger) Debugf(string, ...interface{}) {}
|
||||||
|
|
||||||
|
// No debugging output for eventlog to not spam the service
|
||||||
|
func (e *eventLogger) Debug(...interface{}) {}
|
||||||
|
|
||||||
|
func createEventLogger(cfg *Config) (logger, error) {
|
||||||
|
eventLog, err := eventlog.Open(cfg.InstanceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &eventLogger{
|
||||||
|
eventlog: eventLog,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetOutput(l)
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
add("eventlog", createEventLogger)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,7 @@ func TestEventLogIntegration(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping integration test in short mode")
|
t.Skip("Skipping integration test in short mode")
|
||||||
}
|
}
|
||||||
registerLogger("eventlog", createEventLogger("telegraf"))
|
config := &Config{
|
||||||
|
|
||||||
config := Config{
|
|
||||||
LogTarget: "eventlog",
|
LogTarget: "eventlog",
|
||||||
Logfile: "",
|
Logfile: "",
|
||||||
}
|
}
|
||||||
|
|
@ -76,9 +74,8 @@ func TestRestrictedEventLogIntegration(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping integration test in short mode")
|
t.Skip("Skipping integration test in short mode")
|
||||||
}
|
}
|
||||||
registerLogger("eventlog", createEventLogger("telegraf"))
|
|
||||||
|
|
||||||
config := Config{
|
config := &Config{
|
||||||
LogTarget: "eventlog",
|
LogTarget: "eventlog",
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger defines a logging structure for plugins.
|
|
||||||
type Logger struct {
|
|
||||||
Category string
|
|
||||||
Name string
|
|
||||||
Alias string
|
|
||||||
LogLevel telegraf.LogLevel
|
|
||||||
|
|
||||||
prefix string
|
|
||||||
onError []func()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewLogger creates a new logger instance
|
|
||||||
func NewLogger(category, name, alias string) telegraf.Logger {
|
|
||||||
var prefix string
|
|
||||||
if category != "" {
|
|
||||||
prefix = "[" + category
|
|
||||||
if name != "" {
|
|
||||||
prefix += "." + name
|
|
||||||
}
|
|
||||||
if alias != "" {
|
|
||||||
prefix += "::" + alias
|
|
||||||
}
|
|
||||||
prefix += "] "
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Logger{
|
|
||||||
Category: category,
|
|
||||||
Name: name,
|
|
||||||
Alias: alias,
|
|
||||||
LogLevel: telegraf.Info,
|
|
||||||
prefix: prefix,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnErr defines a callback that triggers only when errors are about to be written to the log
|
|
||||||
func (l *Logger) RegisterErrorCallback(f func()) {
|
|
||||||
l.onError = append(l.onError, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) Level() telegraf.LogLevel {
|
|
||||||
return l.LogLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf logs an error message, patterned after log.Printf.
|
|
||||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
|
||||||
log.Printf("E! "+l.prefix+format, args...)
|
|
||||||
for _, f := range l.onError {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error logs an error message, patterned after log.Print.
|
|
||||||
func (l *Logger) Error(args ...interface{}) {
|
|
||||||
for _, f := range l.onError {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
log.Print(append([]interface{}{"E! " + l.prefix}, args...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugf logs a debug message, patterned after log.Printf.
|
|
||||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
|
||||||
log.Printf("D! "+l.prefix+" "+format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug logs a debug message, patterned after log.Print.
|
|
||||||
func (l *Logger) Debug(args ...interface{}) {
|
|
||||||
log.Print(append([]interface{}{"D! " + l.prefix}, args...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnf logs a warning message, patterned after log.Printf.
|
|
||||||
func (l *Logger) Warnf(format string, args ...interface{}) {
|
|
||||||
log.Printf("W! "+l.prefix+format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn logs a warning message, patterned after log.Print.
|
|
||||||
func (l *Logger) Warn(args ...interface{}) {
|
|
||||||
log.Print(append([]interface{}{"W! " + l.prefix}, args...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infof logs an information message, patterned after log.Printf.
|
|
||||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
|
||||||
log.Printf("I! "+l.prefix+format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info logs an information message, patterned after log.Print.
|
|
||||||
func (l *Logger) Info(args ...interface{}) {
|
|
||||||
log.Print(append([]interface{}{"I! " + l.prefix}, args...)...)
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +1,81 @@
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"fmt"
|
||||||
"log"
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/wlog"
|
"github.com/influxdata/telegraf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var prefixRegex = regexp.MustCompile("^[DIWE]!")
|
||||||
|
|
||||||
|
type logger interface {
|
||||||
|
telegraf.Logger
|
||||||
|
New(category, name, alias string) telegraf.Logger
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// Keep track what is actually set as a log output, because log package doesn't provide a getter.
|
// Keep track what is actually set as a log output, because log package doesn't provide a getter.
|
||||||
// It allows closing previous writer if re-set and have possibility to test what is actually set
|
// It allows closing previous writer if re-set and have possibility to test what is actually set
|
||||||
var actualLogger io.WriteCloser
|
var instance logger
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
// SetupLogging configures the logging output.
|
// SetupLogging configures the logging output.
|
||||||
func SetupLogging(cfg Config) error {
|
func SetupLogging(cfg *Config) error {
|
||||||
log.SetFlags(0)
|
|
||||||
if cfg.Debug {
|
if cfg.Debug {
|
||||||
wlog.SetLevel(wlog.DEBUG)
|
cfg.logLevel = telegraf.Debug
|
||||||
}
|
}
|
||||||
if cfg.Quiet {
|
if cfg.Quiet {
|
||||||
wlog.SetLevel(wlog.ERROR)
|
cfg.logLevel = telegraf.Error
|
||||||
}
|
}
|
||||||
if !cfg.Debug && !cfg.Quiet {
|
if !cfg.Debug && !cfg.Quiet {
|
||||||
wlog.SetLevel(wlog.INFO)
|
cfg.logLevel = telegraf.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.InstanceName == "" {
|
||||||
|
cfg.InstanceName = "telegraf"
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.LogTarget == "" {
|
if cfg.LogTarget == "" {
|
||||||
cfg.LogTarget = LogTargetStderr
|
cfg.LogTarget = "stderr"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the logging factory
|
// Get the logging factory
|
||||||
logCreator, ok := loggerRegistry[cfg.LogTarget]
|
creator, ok := registry[cfg.LogTarget]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("E! Unsupported logtarget: %s, using stderr", cfg.LogTarget)
|
return fmt.Errorf("unsupported logtarget: %s, using stderr", cfg.LogTarget)
|
||||||
logCreator = createStderrLogger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the root logging instance
|
// Create the root logging instance
|
||||||
logWriter, err := logCreator(cfg)
|
l, err := creator(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -47,16 +86,25 @@ func SetupLogging(cfg Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the new logger and store a reference
|
// Use the new logger and store a reference
|
||||||
log.SetOutput(logWriter)
|
instance = l
|
||||||
actualLogger = logWriter
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CloseLogging() error {
|
func NewLogger(category, name, alias string) telegraf.Logger {
|
||||||
if actualLogger == nil {
|
return instance.New(category, name, alias)
|
||||||
return nil
|
}
|
||||||
}
|
|
||||||
|
func CloseLogging() error {
|
||||||
return actualLogger.Close()
|
if instance != nil {
|
||||||
|
return instance.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
once.Do(func() {
|
||||||
|
//nolint:errcheck // This should always succeed with the default config
|
||||||
|
SetupLogging(&Config{})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ func TestErrorCounting(t *testing.T) {
|
||||||
"errors",
|
"errors",
|
||||||
map[string]string{"input": "test"},
|
map[string]string{"input": "test"},
|
||||||
)
|
)
|
||||||
iLog := Logger{Name: "inputs.test"}
|
iLog := NewLogger("inputs", "test", "")
|
||||||
iLog.RegisterErrorCallback(func() {
|
iLog.RegisterErrorCallback(func() {
|
||||||
reg.Incr(1)
|
reg.Incr(1)
|
||||||
})
|
})
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import "io"
|
type creator func(cfg *Config) (logger, error)
|
||||||
|
|
||||||
type creator func(cfg Config) (io.WriteCloser, error)
|
var registry = make(map[string]creator)
|
||||||
|
|
||||||
var loggerRegistry map[string]creator
|
func add(name string, creator creator) {
|
||||||
|
registry[name] = creator
|
||||||
func registerLogger(name string, loggerCreator creator) {
|
|
||||||
if loggerRegistry == nil {
|
|
||||||
loggerRegistry = make(map[string]creator)
|
|
||||||
}
|
|
||||||
loggerRegistry[name] = loggerCreator
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue