feat(logging): Allow overriding message key for structured logging (#16242)
This commit is contained in:
parent
bec49c2ebe
commit
304ab2e780
|
|
@ -57,6 +57,10 @@
|
|||
## "structured" or, on Windows, "eventlog".
|
||||
# logformat = "text"
|
||||
|
||||
## Message key for structured logs, to override the default of "msg".
|
||||
## Ignored if `logformat` is not "structured".
|
||||
# structured_log_message_key = "message"
|
||||
|
||||
## Name of the file to be logged to or stderr if unset or empty. This
|
||||
## setting is ignored for the "eventlog" format.
|
||||
# logfile = ""
|
||||
|
|
|
|||
|
|
@ -364,15 +364,16 @@ func (t *Telegraf) runAgent(ctx context.Context, reloadConfig bool) error {
|
|||
|
||||
// Setup logging as configured.
|
||||
logConfig := &logger.Config{
|
||||
Debug: c.Agent.Debug || t.debug,
|
||||
Quiet: c.Agent.Quiet || t.quiet,
|
||||
LogTarget: c.Agent.LogTarget,
|
||||
LogFormat: c.Agent.LogFormat,
|
||||
Logfile: c.Agent.Logfile,
|
||||
RotationInterval: time.Duration(c.Agent.LogfileRotationInterval),
|
||||
RotationMaxSize: int64(c.Agent.LogfileRotationMaxSize),
|
||||
RotationMaxArchives: c.Agent.LogfileRotationMaxArchives,
|
||||
LogWithTimezone: c.Agent.LogWithTimezone,
|
||||
Debug: c.Agent.Debug || t.debug,
|
||||
Quiet: c.Agent.Quiet || t.quiet,
|
||||
LogTarget: c.Agent.LogTarget,
|
||||
LogFormat: c.Agent.LogFormat,
|
||||
Logfile: c.Agent.Logfile,
|
||||
StructuredLogMessageKey: c.Agent.StructuredLogMessageKey,
|
||||
RotationInterval: time.Duration(c.Agent.LogfileRotationInterval),
|
||||
RotationMaxSize: int64(c.Agent.LogfileRotationMaxSize),
|
||||
RotationMaxArchives: c.Agent.LogfileRotationMaxArchives,
|
||||
LogWithTimezone: c.Agent.LogWithTimezone,
|
||||
}
|
||||
|
||||
if err := logger.SetupLogging(logConfig); err != nil {
|
||||
|
|
|
|||
|
|
@ -236,6 +236,10 @@ type AgentConfig struct {
|
|||
// Name of the file to be logged to or stderr if empty. Ignored for "eventlog" format.
|
||||
Logfile string `toml:"logfile"`
|
||||
|
||||
// Message key for structured logs, to override the default of "msg".
|
||||
// Ignored if "logformat" is not "structured".
|
||||
StructuredLogMessageKey string `toml:"structured_log_message_key"`
|
||||
|
||||
// The file will be rotated after the time interval specified. When set
|
||||
// to 0 no time based rotation is performed.
|
||||
LogfileRotationInterval Duration `toml:"logfile_rotation_interval"`
|
||||
|
|
|
|||
|
|
@ -307,6 +307,10 @@ The agent table configures Telegraf and the defaults used across all plugins.
|
|||
"structured" or, on Windows, "eventlog". The output file (if any) is
|
||||
determined by the `logfile` setting.
|
||||
|
||||
- **structured_log_message_key**:
|
||||
Message key for structured logs, to override the default of "msg".
|
||||
Ignored if `logformat` is not "structured".
|
||||
|
||||
- **logfile**:
|
||||
Name of the file to be logged to or stderr if unset or empty. This
|
||||
setting is ignored for the "eventlog" format.
|
||||
|
|
|
|||
|
|
@ -195,6 +195,8 @@ type Config struct {
|
|||
LogWithTimezone string
|
||||
// Logger instance name
|
||||
InstanceName string
|
||||
// Structured logging message key
|
||||
StructuredLogMessageKey string
|
||||
|
||||
// internal log-level
|
||||
logLevel telegraf.LogLevel
|
||||
|
|
|
|||
|
|
@ -41,17 +41,19 @@ func (l *structuredLogger) Print(level telegraf.LogLevel, ts time.Time, _ string
|
|||
}
|
||||
}
|
||||
|
||||
var defaultStructuredHandlerOptions = &slog.HandlerOptions{
|
||||
Level: slog.Level(-99),
|
||||
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr {
|
||||
// Translate the Telegraf log-levels to strings
|
||||
if attr.Key == slog.LevelKey {
|
||||
if level, ok := attr.Value.Any().(slog.Level); ok {
|
||||
attr.Value = slog.StringValue(telegraf.LogLevel(level).String())
|
||||
}
|
||||
var defaultReplaceAttr = func(_ []string, attr slog.Attr) slog.Attr {
|
||||
// Translate the Telegraf log-levels to strings
|
||||
if attr.Key == slog.LevelKey {
|
||||
if level, ok := attr.Value.Any().(slog.Level); ok {
|
||||
attr.Value = slog.StringValue(telegraf.LogLevel(level).String())
|
||||
}
|
||||
return attr
|
||||
},
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
var defaultStructuredHandlerOptions = &slog.HandlerOptions{
|
||||
Level: slog.Level(-99),
|
||||
ReplaceAttr: defaultReplaceAttr,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -70,8 +72,20 @@ func init() {
|
|||
writer = w
|
||||
}
|
||||
|
||||
structuredHandlerOptions := defaultStructuredHandlerOptions
|
||||
|
||||
if cfg.StructuredLogMessageKey != "" {
|
||||
structuredHandlerOptions.ReplaceAttr = func(groups []string, attr slog.Attr) slog.Attr {
|
||||
if attr.Key == slog.MessageKey {
|
||||
attr.Key = cfg.StructuredLogMessageKey
|
||||
}
|
||||
|
||||
return defaultReplaceAttr(groups, attr)
|
||||
}
|
||||
}
|
||||
|
||||
return &structuredLogger{
|
||||
handler: slog.NewJSONHandler(writer, defaultStructuredHandlerOptions),
|
||||
handler: slog.NewJSONHandler(writer, structuredHandlerOptions),
|
||||
output: writer,
|
||||
errlog: log.New(os.Stderr, "", 0),
|
||||
}, nil
|
||||
|
|
|
|||
|
|
@ -307,6 +307,44 @@ func TestStructuredWriteToFileInRotation(t *testing.T) {
|
|||
require.Len(t, files, 2)
|
||||
}
|
||||
|
||||
func TestStructuredLogMessageKey(t *testing.T) {
|
||||
instance = defaultHandler()
|
||||
|
||||
tmpfile, err := os.CreateTemp("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
cfg := &Config{
|
||||
Logfile: tmpfile.Name(),
|
||||
LogFormat: "structured",
|
||||
RotationMaxArchives: -1,
|
||||
Debug: true,
|
||||
StructuredLogMessageKey: "message",
|
||||
}
|
||||
require.NoError(t, SetupLogging(cfg))
|
||||
|
||||
l := New("testing", "test", "")
|
||||
l.Info("TEST")
|
||||
|
||||
buf, err := os.ReadFile(tmpfile.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"level": "INFO",
|
||||
"message": "TEST",
|
||||
"category": "testing",
|
||||
"plugin": "test",
|
||||
}
|
||||
|
||||
var actual map[string]interface{}
|
||||
require.NoError(t, json.Unmarshal(buf, &actual))
|
||||
|
||||
require.Contains(t, actual, "time")
|
||||
require.NotEmpty(t, actual["time"])
|
||||
delete(actual, "time")
|
||||
require.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
func BenchmarkTelegrafStructuredLogWrite(b *testing.B) {
|
||||
// Discard all logging output
|
||||
l := &structuredLogger{
|
||||
|
|
|
|||
Loading…
Reference in New Issue