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/20221212133537.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:sparkles: `[logs]` Added a way to manage multiple loggers transparently in the same way as a single logger
2 changes: 1 addition & 1 deletion utils/logs/file_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (

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

err = file.Close()
require.NoError(t, err)
Expand Down
17 changes: 15 additions & 2 deletions utils/logs/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
*/
package logs

import "io"
import (
"io"

//go:generate mockgen -destination=../mocks/mock_$GOPACKAGE.go -package=mocks github.com/ARM-software/golang-utils/utils/$GOPACKAGE Loggers,WriterWithSource,StdLogger
"github.com/go-logr/logr"
)

//go:generate mockgen -destination=../mocks/mock_$GOPACKAGE.go -package=mocks github.com/ARM-software/golang-utils/utils/$GOPACKAGE Loggers,IMultipleLoggers,WriterWithSource,StdLogger

// Loggers define generic loggers.
type Loggers interface {
Expand All @@ -23,6 +27,15 @@ type Loggers interface {
LogError(err ...interface{})
}

// IMultipleLoggers provides an interface to manage multiple loggers the same way as a single logger.
type IMultipleLoggers interface {
Loggers
// AppendLogger appends generic loggers to the internal list of loggers managed by this system.
AppendLogger(l ...logr.Logger) error
// Append appends loggers to the internal list of loggers managed by this system.
Append(l ...Loggers) error
}

type WriterWithSource interface {
io.WriteCloser
SetSource(source string) error
Expand Down
27 changes: 17 additions & 10 deletions utils/logs/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,47 +9,54 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ARM-software/golang-utils/utils/commonerrors"
)

func TestLog(t *testing.T) {
var loggers Loggers = &GenericLoggers{}
err := loggers.Check()
assert.NotNil(t, err)
assert.Error(t, err)
err = loggers.Close()
assert.Nil(t, err)
assert.NoError(t, err)
}

func testLog(t *testing.T, loggers Loggers) {
err := loggers.Check()
require.Nil(t, err)
require.NoError(t, err)
defer func() { _ = loggers.Close() }()

err = loggers.SetLogSource("source1")
require.Nil(t, err)
require.NoError(t, err)
err = loggers.SetLoggerSource("LoggerSource1")
require.Nil(t, err)
require.NoError(t, err)

loggers.Log("Test output1")
loggers.Log("Test output2")
loggers.Log("\"/usr/bin/armlink\" --via=\"/workspace/Objects/aws_mqtt_demo.axf._ld\"\n")
loggers.Log("\n")
loggers.LogError("\n")
err = loggers.SetLogSource("source2")
require.Nil(t, err)
require.NoError(t, err)

loggers.Log("Test output3")
loggers.LogError("Test err1")
err = loggers.SetLogSource("source3")
require.Nil(t, err)
require.NoError(t, err)

err = loggers.SetLoggerSource("LoggerSource2")
require.Nil(t, err)
require.NoError(t, err)

loggers.LogError("Test err2")
err = loggers.SetLogSource("source4")
require.Nil(t, err)
require.NoError(t, err)

loggers.LogError("Test err3")
loggers.LogError(commonerrors.ErrCancelled)
loggers.LogError(nil)
loggers.LogError(commonerrors.ErrUnexpected, "some error")
loggers.LogError("some error", commonerrors.ErrUnexpected)
loggers.LogError(nil, "no error")
err = loggers.Close()
require.Nil(t, err)
require.NoError(t, err)
}
16 changes: 12 additions & 4 deletions utils/logs/logr_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,15 @@ func (l *logrLogger) SetLogSource(source string) error {
if reflection.IsEmpty(source) {
return commonerrors.ErrNoLogSource
}
l.logger.WithValues(KeyLogSource, source)
l.logger = l.logger.WithValues(KeyLogSource, source)
return nil
}

func (l *logrLogger) SetLoggerSource(source string) error {
if reflection.IsEmpty(source) {
return commonerrors.ErrNoLoggerSource
}
l.logger.WithName(source)
l.logger.WithValues(KeyLoggerSource, source)
l.logger = l.logger.WithName(source).WithValues(KeyLoggerSource, source)
return nil
}

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

func (l *logrLogger) LogError(err ...interface{}) {
l.logger.Error(nil, fmt.Sprintln(err...))
if len(err) > 0 {
if subErr, ok := err[0].(error); ok {
l.logger.Error(subErr, fmt.Sprintln(err...))
} else {
l.logger.Error(nil, fmt.Sprintln(err...))
}
} else {
l.logger.Error(nil, "")
}

}

// NewLogrLogger creates loggers based on a logr implementation (https://github.com/go-logr/logr)
Expand Down
8 changes: 6 additions & 2 deletions utils/logs/logr_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ import (

func TestLogrLogger(t *testing.T) {
loggers, err := NewLogrLogger(logstest.NewTestLogger(t), "Test")
require.Nil(t, err)
require.NoError(t, err)
testLog(t, loggers)
loggers.LogError(commonerrors.ErrUnexpected, ": no idea what happened")
loggers.LogError(nil, ": no idea what happened")
loggers.LogError("no idea what happened")
loggers.LogError("no idea what happened", nil)
}

func TestLogrLoggerConversion(t *testing.T) {
loggers, err := NewLogrLogger(logstest.NewTestLogger(t), "Test")
require.Nil(t, err)
require.NoError(t, err)
converted := NewLogrLoggerFromLoggers(loggers)
converted.WithName(faker.Name()).WithValues(faker.Word(), faker.Name()).Error(commonerrors.ErrUnexpected, faker.Sentence())
}
2 changes: 1 addition & 1 deletion utils/logs/logrus_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ import (

func TestLogrusLogger(t *testing.T) {
loggers, err := NewLogrusLogger(logrus.StandardLogger(), "Test")
require.Nil(t, err)
require.NoError(t, err)
testLog(t, loggers)
}
6 changes: 3 additions & 3 deletions utils/logs/message_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ import (

func TestLogMessage(t *testing.T) {
loggers, err := NewJSONLogger(&StdWriter{}, "Test", "TestLogMessage")
require.Nil(t, err)
require.NoError(t, err)
testLog(t, loggers)
}

func TestLogMessageToSlowLogger(t *testing.T) {
stdloggers, err := NewStdLogger("ERR:")
require.Nil(t, err)
require.NoError(t, err)
loggers, err := NewJSONLoggerForSlowWriter(&SlowWriter{}, 1024, 2*time.Millisecond, "Test", "TestLogMessageToSlowLogger", stdloggers)
require.Nil(t, err)
require.NoError(t, err)
testLog(t, loggers)
time.Sleep(100 * time.Millisecond)
}
130 changes: 130 additions & 0 deletions utils/logs/multiple_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package logs

import (
"sync"

"github.com/go-logr/logr"
"golang.org/x/sync/errgroup"

"github.com/ARM-software/golang-utils/utils/commonerrors"
)

type MultipleLogger struct {
mu sync.RWMutex
loggers []Loggers
loggerSource string
}

func (c *MultipleLogger) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
g := new(errgroup.Group)
for i := range c.loggers {
g.Go(c.loggers[i].Close)
}
return g.Wait()
}

func (c *MultipleLogger) Check() error {
c.mu.Lock()
defer c.mu.Unlock()
g := new(errgroup.Group)
for i := range c.loggers {
g.Go(c.loggers[i].Check)
}
return g.Wait()
}

func (c *MultipleLogger) SetLogSource(source string) error {
c.mu.Lock()
defer c.mu.Unlock()
var err error
for i := range c.loggers {
err = c.loggers[i].SetLogSource(source)
}
return err
}

func (c *MultipleLogger) SetLoggerSource(source string) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.setLoggerSource(source)
}

func (c *MultipleLogger) setLoggerSource(source string) error {
var err error
for i := range c.loggers {
err = c.loggers[i].SetLoggerSource(source)
}
if err == nil {
c.loggerSource = source
}
return err
}

func (c *MultipleLogger) Log(output ...interface{}) {
c.mu.RLock()
defer c.mu.RUnlock()
for i := range c.loggers {
c.loggers[i].Log(output...)
}
}

func (c *MultipleLogger) LogError(err ...interface{}) {
c.mu.RLock()
defer c.mu.RUnlock()
for i := range c.loggers {
c.loggers[i].LogError(err...)
}
}

func (c *MultipleLogger) GetLoggerSource() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.loggerSource
}

func (c *MultipleLogger) AppendLogger(l ...logr.Logger) error {
for i := range l {
logger, err := NewLogrLogger(l[i], c.GetLoggerSource())
if err != nil {
return err
}
err = c.Append(logger)
if err != nil {
return err
}
}
return nil
}

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

// 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) {
if loggerSource == "" {
err = commonerrors.ErrNoLoggerSource
return
}
list := defaultLoggers
if len(list) == 0 {
std, err := NewStdLogger(loggerSource)
if err != nil {
return nil, err
}
list = []Loggers{std}
}
l = &MultipleLogger{}
err = l.Append(list...)
if err != nil {
return
}
err = l.SetLoggerSource(loggerSource)
return
}
60 changes: 60 additions & 0 deletions utils/logs/multiple_logger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2020-2022 Arm Limited or its affiliates and Contributors. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package logs

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ARM-software/golang-utils/utils/filesystem"
)

func TestMultipleLogger(t *testing.T) {
loggers, err := NewMultipleLoggers("Test")
require.NoError(t, err)
testLog(t, loggers)
}

func TestMultipleLoggers(t *testing.T) {
// With default logger
loggers, err := NewMultipleLoggers("Test Multiple")
require.NoError(t, err)
testLog(t, loggers)

// Adding a file logger to the mix.
file, err := filesystem.TempFileInTempDir("test-multiplelog-filelog-*.log")
require.NoError(t, err)

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

defer func() { _ = filesystem.Rm(file.Name()) }()

empty, err := filesystem.IsEmpty(file.Name())
require.NoError(t, err)
assert.True(t, empty)

fl, err := NewFileLogger(file.Name(), "Test")
require.NoError(t, err)

require.NoError(t, loggers.Append(fl))

nl, err := NewNoopLogger("Test2")
require.NoError(t, err)

// Adding various loggers
require.NoError(t, loggers.Append(fl, nl))

testLog(t, loggers)

empty, err = filesystem.IsEmpty(file.Name())
require.NoError(t, err)
assert.False(t, empty)

err = filesystem.Rm(file.Name())
require.NoError(t, err)
}
2 changes: 1 addition & 1 deletion utils/logs/noop_logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ import (

func TestNoopLogger(t *testing.T) {
loggers, err := NewNoopLogger("Test")
require.Nil(t, err)
require.NoError(t, err)
testLog(t, loggers)
}
Loading