diff --git a/internal/pkg/agent/cmd/install.go b/internal/pkg/agent/cmd/install.go index 3d0179ca881..316ec7571e4 100644 --- a/internal/pkg/agent/cmd/install.go +++ b/internal/pkg/agent/cmd/install.go @@ -193,28 +193,13 @@ func installCmd(streams *cli.IOStreams, cmd *cobra.Command) error { progBar := install.CreateAndStartNewSpinner(streams.Out, "Installing Elastic Agent...") - logCfg := logp.DefaultConfig(logp.DefaultEnvironment) - logCfg.Level = logp.DebugLevel - // Using in memory logger, so we don't write logs to the - // directory we are trying to delete - logp.ToObserverOutput()(&logCfg) - - err = logp.Configure(logCfg) - if err != nil { - return fmt.Errorf("error creating logging config: %w", err) - } - - log := logger.NewWithoutConfig("") - + log, logBuff := logger.NewInMemory("install", logp.ConsoleEncoderConfig()) defer func() { if err == nil { return } - oLogs := logp.ObserverLogs().TakeAll() fmt.Fprintf(os.Stderr, "Error uninstalling. Printing logs\n") - for _, oLog := range oLogs { - fmt.Fprintf(os.Stderr, "%v\n", oLog.Entry) - } + fmt.Fprint(os.Stderr, logBuff.String()) }() var ownership utils.FileOwner diff --git a/internal/pkg/agent/cmd/uninstall.go b/internal/pkg/agent/cmd/uninstall.go index b621a2e5e10..bbe1e329d47 100644 --- a/internal/pkg/agent/cmd/uninstall.go +++ b/internal/pkg/agent/cmd/uninstall.go @@ -11,7 +11,6 @@ import ( "github.com/spf13/cobra" "github.com/elastic/elastic-agent-libs/logp" - "github.com/elastic/elastic-agent/internal/pkg/agent/application/paths" "github.com/elastic/elastic-agent/internal/pkg/agent/install" "github.com/elastic/elastic-agent/internal/pkg/cli" @@ -86,28 +85,13 @@ func uninstallCmd(streams *cli.IOStreams, cmd *cobra.Command) error { progBar := install.CreateAndStartNewSpinner(streams.Out, "Uninstalling Elastic Agent...") - logCfg := logp.DefaultConfig(logp.DefaultEnvironment) - logCfg.Level = logp.DebugLevel - // Using in memory logger, so we don't write logs to the - // directory we are trying to delete - logp.ToObserverOutput()(&logCfg) - - err = logp.Configure(logCfg) - if err != nil { - return fmt.Errorf("error creating logging config: %w", err) - } - - log := logger.NewWithoutConfig("") - + log, logBuff := logger.NewInMemory("uninstall", logp.ConsoleEncoderConfig()) defer func() { if err == nil { return } - oLogs := logp.ObserverLogs().TakeAll() fmt.Fprintf(os.Stderr, "Error uninstalling. Printing logs\n") - for _, oLog := range oLogs { - fmt.Fprintf(os.Stderr, "%v\n", oLog.Entry) - } + fmt.Fprint(os.Stderr, logBuff.String()) }() err = install.Uninstall(paths.ConfigFile(), paths.Top(), uninstallToken, log, progBar) diff --git a/pkg/core/logger/logger.go b/pkg/core/logger/logger.go index b7598f9ef23..7a7fbd8056f 100644 --- a/pkg/core/logger/logger.go +++ b/pkg/core/logger/logger.go @@ -5,6 +5,7 @@ package logger import ( + "bytes" "fmt" "os" "path/filepath" @@ -72,6 +73,31 @@ func NewWithoutConfig(name string) *Logger { return logp.NewLogger(name) } +// NewInMemory returns a new in-memory logger along with the buffer to which i +// logs. +// encCfg configures the log format, use logp.ConsoleEncoderConfig for console +// format, logp.JSONEncoderConfig for JSON or any other valid zapcore.EncoderConfig. +func NewInMemory(selector string, encCfg zapcore.EncoderConfig) (*Logger, *bytes.Buffer) { + buff := bytes.Buffer{} + + encoderConfig := ecszap.ECSCompatibleEncoderConfig(encCfg) + encoderConfig.EncodeTime = UtcTimestampEncode + encoder := zapcore.NewConsoleEncoder(encoderConfig) + + core := zapcore.NewCore( + encoder, + zapcore.AddSync(&buff), + zap.NewAtomicLevelAt(zap.DebugLevel)) + ecszap.ECSCompatibleEncoderConfig(logp.ConsoleEncoderConfig()) + + logger := logp.NewLogger( + selector, + zap.WrapCore(func(in zapcore.Core) zapcore.Core { + return core + })) + return logger, &buff +} + // AddCallerSkip returns new logger with incremented stack frames to skip. // This is needed in order to correctly report the log file lines when the logging statement // is wrapped in some convenience wrapping function for example. @@ -147,7 +173,7 @@ func DefaultLoggingConfig() *Config { return &cfg } -// makeInternalFileOutput creates a zapcore.Core logger that cannot be changed with configuration. +// MakeInternalFileOutput creates a zapcore.Core logger that cannot be changed with configuration. // // This is the logger that the spawned filebeat expects to read the log file from and ship to ES. func MakeInternalFileOutput(cfg *Config) (zapcore.Core, error) { diff --git a/pkg/core/logger/logger_test.go b/pkg/core/logger/logger_test.go index 187a2f6a7f1..4a4378ba5a7 100644 --- a/pkg/core/logger/logger_test.go +++ b/pkg/core/logger/logger_test.go @@ -5,8 +5,10 @@ package logger import ( + "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" @@ -56,3 +58,31 @@ func Test_SetLevel(t *testing.T) { require.Equal(t, tc.ErrEnabled, internalLevelEnabler.Enabled(zapcore.ErrorLevel)) } } + +func TestNewInMemory(t *testing.T) { + log, buff := NewInMemory("in_memory", logp.ConsoleEncoderConfig()) + + log.Debugw("a debug message", "debug_key", "debug_val") + log.Infow("a info message", "info_key", "info_val") + log.Warnw("a warn message", "warn_key", "warn_val") + log.Errorw("an error message", "error_key", "error_val") + + logs := strings.Split(strings.TrimSpace(buff.String()), "\n") + assert.Len(t, logs, 4, "expected 4 log entries") + + assert.Contains(t, logs[0], "a debug message") + assert.Contains(t, logs[0], "debug_key") + assert.Contains(t, logs[0], "debug_val") + + assert.Contains(t, logs[1], "a info message") + assert.Contains(t, logs[1], "info_key") + assert.Contains(t, logs[1], "info_val") + + assert.Contains(t, logs[2], "a warn message") + assert.Contains(t, logs[2], "warn_key") + assert.Contains(t, logs[2], "warn_val") + + assert.Contains(t, logs[3], "an error message") + assert.Contains(t, logs[3], "error_key") + assert.Contains(t, logs[3], "error_val") +}