feat(logging): Allow overriding message key for structured logging (#16242)

This commit is contained in:
Alex Gokhale 2024-12-05 16:38:32 +00:00 committed by GitHub
parent bec49c2ebe
commit 304ab2e780
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 87 additions and 20 deletions

View File

@ -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 = ""

View File

@ -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 {

View File

@ -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"`

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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{