Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/20231010192716.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:sparkles: `[logs]` Added a `PlainStringLogger` to store only logged messages without prefixes or flags
1 change: 1 addition & 0 deletions changes/20231010195032.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:sparkles: `[logs]` Added a `CombinedLoggers` to log into multiple loggers
1 change: 1 addition & 0 deletions changes/20231010200309.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:sparkles: `[subprocess]` Added `Output` to store the output of a process into a string
28 changes: 25 additions & 3 deletions utils/logs/multiple_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ func (c *MultipleLogger) AppendLogger(l ...logr.Logger) error {
}

func (c *MultipleLogger) Append(l ...Loggers) error {
c.mu.Lock()
defer c.mu.Unlock()
c.loggers = append(c.loggers, l...)
return nil
}

type MultipleLoggerWithLoggerSource struct {
MultipleLogger
}

func (c *MultipleLoggerWithLoggerSource) Append(l ...Loggers) error {
c.mu.Lock()
defer c.mu.Unlock()
c.loggers = append(c.loggers, l...)
Expand All @@ -107,24 +118,35 @@ func (c *MultipleLogger) Append(l ...Loggers) error {

// NewMultipleLoggers returns a logger which abstracts and internally manages a list of loggers.
// if no default loggers are provided, the logger will be set to print to the standard output.
func NewMultipleLoggers(loggerSource string, defaultLoggers ...Loggers) (l IMultipleLoggers, err error) {
func NewMultipleLoggers(loggerSource string, loggersList ...Loggers) (l IMultipleLoggers, err error) {
if loggerSource == "" {
err = commonerrors.ErrNoLoggerSource
return
}
list := defaultLoggers
list := loggersList
if len(list) == 0 {
std, err := NewStdLogger(loggerSource)
if err != nil {
return nil, err
}
list = []Loggers{std}
}
l = &MultipleLogger{}
l = &MultipleLoggerWithLoggerSource{}
err = l.Append(list...)
if err != nil {
return
}
err = l.SetLoggerSource(loggerSource)
return
}

// NewCombinedLoggers returns a logger which logs to a list of logger. If list is empty, it will error.
func NewCombinedLoggers(loggersList ...Loggers) (l IMultipleLoggers, err error) {
if len(loggersList) == 0 {
err = commonerrors.ErrNoLogger
return
}
l = &MultipleLogger{}
err = l.Append(loggersList...)
return
}
15 changes: 15 additions & 0 deletions utils/logs/multiple_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ARM-software/golang-utils/utils/commonerrors"
"github.com/ARM-software/golang-utils/utils/commonerrors/errortest"
"github.com/ARM-software/golang-utils/utils/filesystem"
"github.com/ARM-software/golang-utils/utils/logs/logstest"
)

func TestMultipleLogger(t *testing.T) {
Expand All @@ -19,6 +22,18 @@ func TestMultipleLogger(t *testing.T) {
testLog(t, loggers)
}

func TestCombinedLogger(t *testing.T) {
_, err := NewCombinedLoggers()
errortest.RequireError(t, err, commonerrors.ErrNoLogger)
testLogger, err := NewLogrLogger(logstest.NewTestLogger(t), "Test")
require.NoError(t, err)
nl, err := NewNoopLogger("Test2")
require.NoError(t, err)
loggers, err := NewCombinedLoggers(testLogger, nl)
require.NoError(t, err)
testLog(t, loggers)
}

func TestMultipleLoggers(t *testing.T) {
// With default logger
loggers, err := NewMultipleLoggers("Test Multiple")
Expand Down
14 changes: 14 additions & 0 deletions utils/logs/string_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func (l *StringLoggers) Close() (err error) {
}

// NewStringLogger creates a logger to a string builder.
// All messages (whether they are output or error) are merged together.
func NewStringLogger(loggerSource string) (loggers *StringLoggers, err error) {
loggers = &StringLoggers{
LogWriter: StringWriter{},
Expand All @@ -73,6 +74,19 @@ func NewStringLogger(loggerSource string) (loggers *StringLoggers, err error) {
return
}

// NewPlainStringLogger creates a logger to a string builder with no extra flag, prefix or tag, just the logged text.
// All messages (whether they are output or error) are merged together.
func NewPlainStringLogger() (loggers *StringLoggers, err error) {
loggers = &StringLoggers{
LogWriter: StringWriter{},
}
loggers.GenericLoggers = GenericLoggers{
Output: log.New(&loggers.LogWriter, "", 0),
Error: log.New(&loggers.LogWriter, "", 0),
}
return
}

// CreateStringLogger creates a logger to a string builder.
//
// Deprecated: Use NewStringLogger instead
Expand Down
14 changes: 13 additions & 1 deletion utils/logs/string_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,19 @@ func TestStringLogger(t *testing.T) {
loggers.LogError("Test err")
loggers.Log("Test1")
contents := loggers.GetLogContent()
require.NotZero(t, contents)
require.NotEmpty(t, contents)
require.True(t, strings.Contains(contents, "Test err"))
require.True(t, strings.Contains(contents, "Test1"))
}

func TestPlainStringLogger(t *testing.T) {
loggers, err := NewPlainStringLogger()
require.NoError(t, err)
testLog(t, loggers)
loggers.LogError("Test err")
loggers.Log("Test1")
contents := loggers.GetLogContent()
require.NotEmpty(t, contents)
require.True(t, strings.Contains(contents, "Test err"))
require.True(t, strings.Contains(contents, "Test1"))
}
22 changes: 11 additions & 11 deletions utils/subprocess/command_wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (

func TestCmdRun(t *testing.T) {
currentDir, err := os.Getwd()
require.Nil(t, err)
require.NoError(t, err)

tests := []struct {
name string
Expand Down Expand Up @@ -52,7 +52,7 @@ func TestCmdRun(t *testing.T) {
defer goleak.VerifyNone(t)
var cmd *command
loggers, err := logs.NewLogrLogger(logstest.NewTestLogger(t), "test")
require.Nil(t, err)
require.NoError(t, err)
if platform.IsWindows() {
cmd = newCommand(loggers, test.cmdWindows, test.argWindows...)
} else {
Expand All @@ -62,20 +62,20 @@ func TestCmdRun(t *testing.T) {
defer cancel()
wrapper := cmd.GetCmd(ctx)
err = wrapper.Run()
require.Nil(t, err)
require.NoError(t, err)
err = wrapper.Run()
require.NotNil(t, err)
require.Error(t, err)
cmd.Reset()
wrapper = cmd.GetCmd(ctx)
err = wrapper.Run()
require.Nil(t, err)
require.NoError(t, err)
})
}
}

func TestCmdStartStop(t *testing.T) {
currentDir, err := os.Getwd()
require.Nil(t, err)
require.NoError(t, err)

tests := []struct {
name string
Expand Down Expand Up @@ -106,7 +106,7 @@ func TestCmdStartStop(t *testing.T) {
defer goleak.VerifyNone(t)
var cmd *command
loggers, err := logs.NewLogrLogger(logstest.NewTestLogger(t), "test")
require.Nil(t, err)
require.NoError(t, err)

if platform.IsWindows() {
cmd = newCommand(loggers, test.cmdWindows, test.argWindows...)
Expand All @@ -117,14 +117,14 @@ func TestCmdStartStop(t *testing.T) {
defer cancel()
wrapper := cmd.GetCmd(ctx)
err = wrapper.Start()
require.Nil(t, err)
require.NoError(t, err)
pid, err := wrapper.Pid()
require.Nil(t, err)
require.NoError(t, err)
assert.NotZero(t, pid)
err = wrapper.Start()
require.NotNil(t, err)
require.Error(t, err)
err = wrapper.Stop()
require.Nil(t, err)
require.NoError(t, err)
})
}
}
40 changes: 38 additions & 2 deletions utils/subprocess/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
// Package subprocess allows you to spawn new processes, retrieve their output/error pipes, and obtain their return codes.

// Package subprocess allows you to spawn new processes, log their output/error and obtain their return codes.
package subprocess

import (
Expand Down Expand Up @@ -32,6 +33,12 @@ func New(ctx context.Context, loggers logs.Loggers, messageOnStart string, messa
return
}

func newPlainSubProcess(ctx context.Context, loggers logs.Loggers, cmd string, args ...string) (p *Subprocess, err error) {
p = new(Subprocess)
err = p.setup(ctx, loggers, false, "", "", "", cmd, args...)
return
}

// Execute executes a command (i.e. spawns a subprocess)
func Execute(ctx context.Context, loggers logs.Loggers, messageOnStart string, messageOnSuccess, messageOnFailure string, cmd string, args ...string) (err error) {
p, err := New(ctx, loggers, messageOnStart, messageOnSuccess, messageOnFailure, cmd, args...)
Expand All @@ -41,8 +48,37 @@ func Execute(ctx context.Context, loggers logs.Loggers, messageOnStart string, m
return p.Execute()
}

// Output executes a command and returns its output (stdOutput and stdErr are merged) as string.
func Output(ctx context.Context, loggers logs.Loggers, cmd string, args ...string) (output string, err error) {
if loggers == nil {
err = commonerrors.ErrNoLogger
return
}

stringLogger, err := logs.NewPlainStringLogger()
if err != nil {
return
}
mLoggers, err := logs.NewCombinedLoggers(loggers, stringLogger)
if err != nil {
return
}
p, err := newPlainSubProcess(ctx, mLoggers, cmd, args...)
if err != nil {
return
}
err = p.Execute()
output = stringLogger.GetLogContent()
return
}

// Setup sets up a sub-process i.e. defines the command cmd and the messages on start, success and failure.
func (s *Subprocess) Setup(ctx context.Context, loggers logs.Loggers, messageOnStart string, messageOnSuccess, messageOnFailure string, cmd string, args ...string) (err error) {
return s.setup(ctx, loggers, true, messageOnStart, messageOnSuccess, messageOnFailure, cmd, args...)
}

// Setup sets up a sub-process i.e. defines the command cmd and the messages on start, success and failure.
func (s *Subprocess) setup(ctx context.Context, loggers logs.Loggers, withAdditionalMessages bool, messageOnStart string, messageOnSuccess, messageOnFailure string, cmd string, args ...string) (err error) {
if s.IsOn() {
err = s.Stop()
if err != nil {
Expand All @@ -54,7 +90,7 @@ func (s *Subprocess) Setup(ctx context.Context, loggers logs.Loggers, messageOnS
s.isRunning.Store(false)
s.processMonitoring = newSubprocessMonitoring(ctx)
s.command = newCommand(loggers, cmd, args...)
s.messsaging = newSubprocessMessaging(loggers, messageOnSuccess, messageOnFailure, messageOnStart, s.command.GetPath())
s.messsaging = newSubprocessMessaging(loggers, withAdditionalMessages, messageOnSuccess, messageOnFailure, messageOnStart, s.command.GetPath())
s.reset()
return s.check()
}
Expand Down
Loading