Skip to content

Commit c177def

Browse files
authored
[logs] multiple loggers (#172)
<!-- Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved. SPDX-License-Identifier: Apache-2.0 --> ### Description - Added a way to have the same `Loggers` interface for multiple loggers - Fixed few issues seen along the way (logger source and log source setup) ### Test Coverage <!-- Please put an `x` in the correct box e.g. `[x]` to indicate the testing coverage of this change. --> - [x] This change is covered by existing or additional automated tests. - [ ] Manual testing has been performed (and evidence provided) as automated testing was not feasible. - [ ] Additional tests are not required for this change (e.g. documentation update).
1 parent 8312eee commit c177def

15 files changed

+402
-29
lines changed

changes/20221212133537.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:sparkles: `[logs]` Added a way to manage multiple loggers transparently in the same way as a single logger

utils/logs/file_logger_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515

1616
func TestFileLogger(t *testing.T) {
1717
file, err := filesystem.TempFileInTempDir("test-filelog-*.log")
18-
require.Nil(t, err)
18+
require.NoError(t, err)
1919

2020
err = file.Close()
2121
require.NoError(t, err)

utils/logs/interfaces.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
*/
55
package logs
66

7-
import "io"
7+
import (
8+
"io"
89

9-
//go:generate mockgen -destination=../mocks/mock_$GOPACKAGE.go -package=mocks github.com/ARM-software/golang-utils/utils/$GOPACKAGE Loggers,WriterWithSource,StdLogger
10+
"github.com/go-logr/logr"
11+
)
12+
13+
//go:generate mockgen -destination=../mocks/mock_$GOPACKAGE.go -package=mocks github.com/ARM-software/golang-utils/utils/$GOPACKAGE Loggers,IMultipleLoggers,WriterWithSource,StdLogger
1014

1115
// Loggers define generic loggers.
1216
type Loggers interface {
@@ -23,6 +27,15 @@ type Loggers interface {
2327
LogError(err ...interface{})
2428
}
2529

30+
// IMultipleLoggers provides an interface to manage multiple loggers the same way as a single logger.
31+
type IMultipleLoggers interface {
32+
Loggers
33+
// AppendLogger appends generic loggers to the internal list of loggers managed by this system.
34+
AppendLogger(l ...logr.Logger) error
35+
// Append appends loggers to the internal list of loggers managed by this system.
36+
Append(l ...Loggers) error
37+
}
38+
2639
type WriterWithSource interface {
2740
io.WriteCloser
2841
SetSource(source string) error

utils/logs/log_test.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,54 @@ import (
99

1010
"github.com/stretchr/testify/assert"
1111
"github.com/stretchr/testify/require"
12+
13+
"github.com/ARM-software/golang-utils/utils/commonerrors"
1214
)
1315

1416
func TestLog(t *testing.T) {
1517
var loggers Loggers = &GenericLoggers{}
1618
err := loggers.Check()
17-
assert.NotNil(t, err)
19+
assert.Error(t, err)
1820
err = loggers.Close()
19-
assert.Nil(t, err)
21+
assert.NoError(t, err)
2022
}
2123

2224
func testLog(t *testing.T, loggers Loggers) {
2325
err := loggers.Check()
24-
require.Nil(t, err)
26+
require.NoError(t, err)
2527
defer func() { _ = loggers.Close() }()
2628

2729
err = loggers.SetLogSource("source1")
28-
require.Nil(t, err)
30+
require.NoError(t, err)
2931
err = loggers.SetLoggerSource("LoggerSource1")
30-
require.Nil(t, err)
32+
require.NoError(t, err)
3133

3234
loggers.Log("Test output1")
3335
loggers.Log("Test output2")
3436
loggers.Log("\"/usr/bin/armlink\" --via=\"/workspace/Objects/aws_mqtt_demo.axf._ld\"\n")
3537
loggers.Log("\n")
3638
loggers.LogError("\n")
3739
err = loggers.SetLogSource("source2")
38-
require.Nil(t, err)
40+
require.NoError(t, err)
3941

4042
loggers.Log("Test output3")
4143
loggers.LogError("Test err1")
4244
err = loggers.SetLogSource("source3")
43-
require.Nil(t, err)
45+
require.NoError(t, err)
4446

4547
err = loggers.SetLoggerSource("LoggerSource2")
46-
require.Nil(t, err)
48+
require.NoError(t, err)
4749

4850
loggers.LogError("Test err2")
4951
err = loggers.SetLogSource("source4")
50-
require.Nil(t, err)
52+
require.NoError(t, err)
5153

5254
loggers.LogError("Test err3")
55+
loggers.LogError(commonerrors.ErrCancelled)
56+
loggers.LogError(nil)
57+
loggers.LogError(commonerrors.ErrUnexpected, "some error")
58+
loggers.LogError("some error", commonerrors.ErrUnexpected)
59+
loggers.LogError(nil, "no error")
5360
err = loggers.Close()
54-
require.Nil(t, err)
61+
require.NoError(t, err)
5562
}

utils/logs/logr_logger.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,15 @@ func (l *logrLogger) SetLogSource(source string) error {
3838
if reflection.IsEmpty(source) {
3939
return commonerrors.ErrNoLogSource
4040
}
41-
l.logger.WithValues(KeyLogSource, source)
41+
l.logger = l.logger.WithValues(KeyLogSource, source)
4242
return nil
4343
}
4444

4545
func (l *logrLogger) SetLoggerSource(source string) error {
4646
if reflection.IsEmpty(source) {
4747
return commonerrors.ErrNoLoggerSource
4848
}
49-
l.logger.WithName(source)
50-
l.logger.WithValues(KeyLoggerSource, source)
49+
l.logger = l.logger.WithName(source).WithValues(KeyLoggerSource, source)
5150
return nil
5251
}
5352

@@ -56,7 +55,16 @@ func (l *logrLogger) Log(output ...interface{}) {
5655
}
5756

5857
func (l *logrLogger) LogError(err ...interface{}) {
59-
l.logger.Error(nil, fmt.Sprintln(err...))
58+
if len(err) > 0 {
59+
if subErr, ok := err[0].(error); ok {
60+
l.logger.Error(subErr, fmt.Sprintln(err...))
61+
} else {
62+
l.logger.Error(nil, fmt.Sprintln(err...))
63+
}
64+
} else {
65+
l.logger.Error(nil, "")
66+
}
67+
6068
}
6169

6270
// NewLogrLogger creates loggers based on a logr implementation (https://github.com/go-logr/logr)

utils/logs/logr_logger_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@ import (
1616

1717
func TestLogrLogger(t *testing.T) {
1818
loggers, err := NewLogrLogger(logstest.NewTestLogger(t), "Test")
19-
require.Nil(t, err)
19+
require.NoError(t, err)
2020
testLog(t, loggers)
21+
loggers.LogError(commonerrors.ErrUnexpected, ": no idea what happened")
22+
loggers.LogError(nil, ": no idea what happened")
23+
loggers.LogError("no idea what happened")
24+
loggers.LogError("no idea what happened", nil)
2125
}
2226

2327
func TestLogrLoggerConversion(t *testing.T) {
2428
loggers, err := NewLogrLogger(logstest.NewTestLogger(t), "Test")
25-
require.Nil(t, err)
29+
require.NoError(t, err)
2630
converted := NewLogrLoggerFromLoggers(loggers)
2731
converted.WithName(faker.Name()).WithValues(faker.Word(), faker.Name()).Error(commonerrors.ErrUnexpected, faker.Sentence())
2832
}

utils/logs/logrus_logger_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ import (
1313

1414
func TestLogrusLogger(t *testing.T) {
1515
loggers, err := NewLogrusLogger(logrus.StandardLogger(), "Test")
16-
require.Nil(t, err)
16+
require.NoError(t, err)
1717
testLog(t, loggers)
1818
}

utils/logs/message_logger_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import (
1313

1414
func TestLogMessage(t *testing.T) {
1515
loggers, err := NewJSONLogger(&StdWriter{}, "Test", "TestLogMessage")
16-
require.Nil(t, err)
16+
require.NoError(t, err)
1717
testLog(t, loggers)
1818
}
1919

2020
func TestLogMessageToSlowLogger(t *testing.T) {
2121
stdloggers, err := NewStdLogger("ERR:")
22-
require.Nil(t, err)
22+
require.NoError(t, err)
2323
loggers, err := NewJSONLoggerForSlowWriter(&SlowWriter{}, 1024, 2*time.Millisecond, "Test", "TestLogMessageToSlowLogger", stdloggers)
24-
require.Nil(t, err)
24+
require.NoError(t, err)
2525
testLog(t, loggers)
2626
time.Sleep(100 * time.Millisecond)
2727
}

utils/logs/multiple_logger.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package logs
2+
3+
import (
4+
"sync"
5+
6+
"github.com/go-logr/logr"
7+
"golang.org/x/sync/errgroup"
8+
9+
"github.com/ARM-software/golang-utils/utils/commonerrors"
10+
)
11+
12+
type MultipleLogger struct {
13+
mu sync.RWMutex
14+
loggers []Loggers
15+
loggerSource string
16+
}
17+
18+
func (c *MultipleLogger) Close() error {
19+
c.mu.Lock()
20+
defer c.mu.Unlock()
21+
g := new(errgroup.Group)
22+
for i := range c.loggers {
23+
g.Go(c.loggers[i].Close)
24+
}
25+
return g.Wait()
26+
}
27+
28+
func (c *MultipleLogger) Check() error {
29+
c.mu.Lock()
30+
defer c.mu.Unlock()
31+
g := new(errgroup.Group)
32+
for i := range c.loggers {
33+
g.Go(c.loggers[i].Check)
34+
}
35+
return g.Wait()
36+
}
37+
38+
func (c *MultipleLogger) SetLogSource(source string) error {
39+
c.mu.Lock()
40+
defer c.mu.Unlock()
41+
var err error
42+
for i := range c.loggers {
43+
err = c.loggers[i].SetLogSource(source)
44+
}
45+
return err
46+
}
47+
48+
func (c *MultipleLogger) SetLoggerSource(source string) error {
49+
c.mu.Lock()
50+
defer c.mu.Unlock()
51+
return c.setLoggerSource(source)
52+
}
53+
54+
func (c *MultipleLogger) setLoggerSource(source string) error {
55+
var err error
56+
for i := range c.loggers {
57+
err = c.loggers[i].SetLoggerSource(source)
58+
}
59+
if err == nil {
60+
c.loggerSource = source
61+
}
62+
return err
63+
}
64+
65+
func (c *MultipleLogger) Log(output ...interface{}) {
66+
c.mu.RLock()
67+
defer c.mu.RUnlock()
68+
for i := range c.loggers {
69+
c.loggers[i].Log(output...)
70+
}
71+
}
72+
73+
func (c *MultipleLogger) LogError(err ...interface{}) {
74+
c.mu.RLock()
75+
defer c.mu.RUnlock()
76+
for i := range c.loggers {
77+
c.loggers[i].LogError(err...)
78+
}
79+
}
80+
81+
func (c *MultipleLogger) GetLoggerSource() string {
82+
c.mu.RLock()
83+
defer c.mu.RUnlock()
84+
return c.loggerSource
85+
}
86+
87+
func (c *MultipleLogger) AppendLogger(l ...logr.Logger) error {
88+
for i := range l {
89+
logger, err := NewLogrLogger(l[i], c.GetLoggerSource())
90+
if err != nil {
91+
return err
92+
}
93+
err = c.Append(logger)
94+
if err != nil {
95+
return err
96+
}
97+
}
98+
return nil
99+
}
100+
101+
func (c *MultipleLogger) Append(l ...Loggers) error {
102+
c.mu.Lock()
103+
defer c.mu.Unlock()
104+
c.loggers = append(c.loggers, l...)
105+
return c.setLoggerSource(c.loggerSource)
106+
}
107+
108+
// NewMultipleLoggers returns a logger which abstracts and internally manages a list of loggers.
109+
// if no default loggers are provided, the logger will be set to print to the standard output.
110+
func NewMultipleLoggers(loggerSource string, defaultLoggers ...Loggers) (l IMultipleLoggers, err error) {
111+
if loggerSource == "" {
112+
err = commonerrors.ErrNoLoggerSource
113+
return
114+
}
115+
list := defaultLoggers
116+
if len(list) == 0 {
117+
std, err := NewStdLogger(loggerSource)
118+
if err != nil {
119+
return nil, err
120+
}
121+
list = []Loggers{std}
122+
}
123+
l = &MultipleLogger{}
124+
err = l.Append(list...)
125+
if err != nil {
126+
return
127+
}
128+
err = l.SetLoggerSource(loggerSource)
129+
return
130+
}

utils/logs/multiple_logger_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package logs
6+
7+
import (
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/ARM-software/golang-utils/utils/filesystem"
14+
)
15+
16+
func TestMultipleLogger(t *testing.T) {
17+
loggers, err := NewMultipleLoggers("Test")
18+
require.NoError(t, err)
19+
testLog(t, loggers)
20+
}
21+
22+
func TestMultipleLoggers(t *testing.T) {
23+
// With default logger
24+
loggers, err := NewMultipleLoggers("Test Multiple")
25+
require.NoError(t, err)
26+
testLog(t, loggers)
27+
28+
// Adding a file logger to the mix.
29+
file, err := filesystem.TempFileInTempDir("test-multiplelog-filelog-*.log")
30+
require.NoError(t, err)
31+
32+
err = file.Close()
33+
require.NoError(t, err)
34+
35+
defer func() { _ = filesystem.Rm(file.Name()) }()
36+
37+
empty, err := filesystem.IsEmpty(file.Name())
38+
require.NoError(t, err)
39+
assert.True(t, empty)
40+
41+
fl, err := NewFileLogger(file.Name(), "Test")
42+
require.NoError(t, err)
43+
44+
require.NoError(t, loggers.Append(fl))
45+
46+
nl, err := NewNoopLogger("Test2")
47+
require.NoError(t, err)
48+
49+
// Adding various loggers
50+
require.NoError(t, loggers.Append(fl, nl))
51+
52+
testLog(t, loggers)
53+
54+
empty, err = filesystem.IsEmpty(file.Name())
55+
require.NoError(t, err)
56+
assert.False(t, empty)
57+
58+
err = filesystem.Rm(file.Name())
59+
require.NoError(t, err)
60+
}

0 commit comments

Comments
 (0)