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".
|
## "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 = ""
|
||||||
|
|
|
||||||
|
|
@ -364,15 +364,16 @@ 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,
|
||||||
LogFormat: c.Agent.LogFormat,
|
LogFormat: c.Agent.LogFormat,
|
||||||
Logfile: c.Agent.Logfile,
|
Logfile: c.Agent.Logfile,
|
||||||
RotationInterval: time.Duration(c.Agent.LogfileRotationInterval),
|
StructuredLogMessageKey: c.Agent.StructuredLogMessageKey,
|
||||||
RotationMaxSize: int64(c.Agent.LogfileRotationMaxSize),
|
RotationInterval: time.Duration(c.Agent.LogfileRotationInterval),
|
||||||
RotationMaxArchives: c.Agent.LogfileRotationMaxArchives,
|
RotationMaxSize: int64(c.Agent.LogfileRotationMaxSize),
|
||||||
LogWithTimezone: c.Agent.LogWithTimezone,
|
RotationMaxArchives: c.Agent.LogfileRotationMaxArchives,
|
||||||
|
LogWithTimezone: c.Agent.LogWithTimezone,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := logger.SetupLogging(logConfig); err != nil {
|
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.
|
// 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"`
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -41,17 +41,19 @@ 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),
|
// Translate the Telegraf log-levels to strings
|
||||||
ReplaceAttr: func(_ []string, attr slog.Attr) slog.Attr {
|
if attr.Key == slog.LevelKey {
|
||||||
// Translate the Telegraf log-levels to strings
|
if level, ok := attr.Value.Any().(slog.Level); ok {
|
||||||
if attr.Key == slog.LevelKey {
|
attr.Value = slog.StringValue(telegraf.LogLevel(level).String())
|
||||||
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() {
|
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
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue