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". ## "structured" or, on Windows, "eventlog".
# logformat = "text" # 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 ## Name of the file to be logged to or stderr if unset or empty. This
## setting is ignored for the "eventlog" format. ## setting is ignored for the "eventlog" format.
# logfile = "" # logfile = ""

View File

@ -369,6 +369,7 @@ func (t *Telegraf) runAgent(ctx context.Context, reloadConfig bool) error {
LogTarget: c.Agent.LogTarget, LogTarget: c.Agent.LogTarget,
LogFormat: c.Agent.LogFormat, LogFormat: c.Agent.LogFormat,
Logfile: c.Agent.Logfile, Logfile: c.Agent.Logfile,
StructuredLogMessageKey: c.Agent.StructuredLogMessageKey,
RotationInterval: time.Duration(c.Agent.LogfileRotationInterval), RotationInterval: time.Duration(c.Agent.LogfileRotationInterval),
RotationMaxSize: int64(c.Agent.LogfileRotationMaxSize), RotationMaxSize: int64(c.Agent.LogfileRotationMaxSize),
RotationMaxArchives: c.Agent.LogfileRotationMaxArchives, RotationMaxArchives: c.Agent.LogfileRotationMaxArchives,

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. // Name of the file to be logged to or stderr if empty. Ignored for "eventlog" format.
Logfile string `toml:"logfile"` 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 // The file will be rotated after the time interval specified. When set
// to 0 no time based rotation is performed. // to 0 no time based rotation is performed.
LogfileRotationInterval Duration `toml:"logfile_rotation_interval"` 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 "structured" or, on Windows, "eventlog". The output file (if any) is
determined by the `logfile` setting. 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**: - **logfile**:
Name of the file to be logged to or stderr if unset or empty. This Name of the file to be logged to or stderr if unset or empty. This
setting is ignored for the "eventlog" format. setting is ignored for the "eventlog" format.

View File

@ -195,6 +195,8 @@ type Config struct {
LogWithTimezone string LogWithTimezone string
// Logger instance name // Logger instance name
InstanceName string InstanceName string
// Structured logging message key
StructuredLogMessageKey string
// internal log-level // internal log-level
logLevel telegraf.LogLevel logLevel telegraf.LogLevel

View File

@ -41,9 +41,7 @@ func (l *structuredLogger) Print(level telegraf.LogLevel, ts time.Time, _ string
} }
} }
var defaultStructuredHandlerOptions = &slog.HandlerOptions{ var defaultReplaceAttr = func(_ []string, attr slog.Attr) slog.Attr {
Level: slog.Level(-99),
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr {
// Translate the Telegraf log-levels to strings // Translate the Telegraf log-levels to strings
if attr.Key == slog.LevelKey { if attr.Key == slog.LevelKey {
if level, ok := attr.Value.Any().(slog.Level); ok { if level, ok := attr.Value.Any().(slog.Level); ok {
@ -51,7 +49,11 @@ var defaultStructuredHandlerOptions = &slog.HandlerOptions{
} }
} }
return attr return attr
}, }
var defaultStructuredHandlerOptions = &slog.HandlerOptions{
Level: slog.Level(-99),
ReplaceAttr: defaultReplaceAttr,
} }
func init() { func init() {
@ -70,8 +72,20 @@ func init() {
writer = w 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{ return &structuredLogger{
handler: slog.NewJSONHandler(writer, defaultStructuredHandlerOptions), handler: slog.NewJSONHandler(writer, structuredHandlerOptions),
output: writer, output: writer,
errlog: log.New(os.Stderr, "", 0), errlog: log.New(os.Stderr, "", 0),
}, nil }, nil

View File

@ -307,6 +307,44 @@ func TestStructuredWriteToFileInRotation(t *testing.T) {
require.Len(t, files, 2) 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) { func BenchmarkTelegrafStructuredLogWrite(b *testing.B) {
// Discard all logging output // Discard all logging output
l := &structuredLogger{ l := &structuredLogger{