-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🛠️ new
logger/zerologadapter
for retryable requests (#4903)
We use the `retryablehttp` package to retry http requests and we always want the logs to go to `debug` level so, we need an adapter. We have implemented this adapter many times, this change removes all the duplicated code and creates a util package that all providers can use. Example: ```go retryClient := retryablehttp.NewClient() retryClient.Logger = zerologadapter.New(log.Logger) ``` * ↪️ move package to `logger/` --------- Signed-off-by: Salim Afiune Maya <afiune@mondoo.com>
- Loading branch information
Showing
7 changed files
with
210 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright (c) Mondoo, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package zerologadapter | ||
|
||
import "github.com/rs/zerolog" | ||
|
||
// New returns a new adapter for the zerolog logger to the LeveledLogger interface. This struct is | ||
// mainly used in conjunction with a retryable http client to convert all retry logs to debug logs. | ||
// | ||
// NOTE that all messages will go to debug level. | ||
// | ||
// e.g. | ||
// ```go | ||
// retryClient := retryablehttp.NewClient() | ||
// retryClient.Logger = zerologadapter.New(log.Logger) | ||
// ``` | ||
func New(logger zerolog.Logger) *Adapter { | ||
return &Adapter{logger} | ||
} | ||
|
||
type Adapter struct { | ||
logger zerolog.Logger | ||
} | ||
|
||
func (z *Adapter) Msg(msg string, keysAndValues ...interface{}) { | ||
z.logger.Debug().Fields(convertToFields(keysAndValues...)).Msg(msg) | ||
} | ||
|
||
func (z *Adapter) Error(msg string, keysAndValues ...interface{}) { | ||
z.logger.Debug().Fields(convertToFields(keysAndValues...)).Msg(msg) | ||
} | ||
|
||
func (z *Adapter) Info(msg string, keysAndValues ...interface{}) { | ||
z.logger.Debug().Fields(convertToFields(keysAndValues...)).Msg(msg) | ||
} | ||
|
||
func (z *Adapter) Debug(msg string, keysAndValues ...interface{}) { | ||
z.logger.Debug().Fields(convertToFields(keysAndValues...)).Msg(msg) | ||
} | ||
|
||
func (z *Adapter) Warn(msg string, keysAndValues ...interface{}) { | ||
z.logger.Debug().Fields(convertToFields(keysAndValues...)).Msg(msg) | ||
} | ||
|
||
func convertToFields(keysAndValues ...interface{}) map[string]interface{} { | ||
fields := make(map[string]interface{}) | ||
for i := 0; i < len(keysAndValues); i += 2 { | ||
if i+1 < len(keysAndValues) { | ||
keyString, ok := keysAndValues[i].(string) | ||
if ok { // safety first, eventhough we always expect a string | ||
fields[keyString] = keysAndValues[i+1] | ||
} | ||
} | ||
} | ||
return fields | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright (c) Mondoo, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package zerologadapter | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestConvertToFields(t *testing.T) { | ||
t.Run("Valid key-value pairs", func(t *testing.T) { | ||
input := []interface{}{"key1", "value1", "key2", 42, "key3", true} | ||
expected := map[string]interface{}{ | ||
"key1": "value1", | ||
"key2": 42, | ||
"key3": true, | ||
} | ||
|
||
result := convertToFields(input...) | ||
assert.Equal(t, expected, result) | ||
}) | ||
|
||
t.Run("Odd number of elements", func(t *testing.T) { | ||
input := []interface{}{"key1", "value1", "key2"} | ||
expected := map[string]interface{}{ | ||
"key1": "value1", | ||
} | ||
|
||
result := convertToFields(input...) | ||
assert.Equal(t, expected, result) | ||
}) | ||
|
||
t.Run("Non-string keys are ignored", func(t *testing.T) { | ||
input := []interface{}{123, "value1", "key2", 42, 3.14, "value3", "key3", true} | ||
expected := map[string]interface{}{ | ||
"key2": 42, | ||
"key3": true, | ||
} | ||
|
||
result := convertToFields(input...) | ||
assert.Equal(t, expected, result) | ||
}) | ||
|
||
t.Run("Empty input", func(t *testing.T) { | ||
input := []interface{}{} | ||
expected := map[string]interface{}{} | ||
|
||
result := convertToFields(input...) | ||
assert.Equal(t, expected, result) | ||
}) | ||
|
||
t.Run("Nil input", func(t *testing.T) { | ||
var input []interface{} | ||
expected := map[string]interface{}{} | ||
|
||
result := convertToFields(input...) | ||
assert.Equal(t, expected, result) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Copyright (c) Mondoo, Inc. | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
|
||
package zerologadapter_test | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
subject "go.mondoo.com/cnquery/v11/logger/zerologadapter" | ||
|
||
"github.com/rs/zerolog" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestNewAdapter(t *testing.T) { | ||
var logOutput bytes.Buffer | ||
logger := zerolog.New(&logOutput).Level(zerolog.DebugLevel) | ||
adapter := subject.New(logger) | ||
|
||
t.Run("Msg method logs correctly", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Msg("Test message", "key1", "value1", "key2", 42) | ||
|
||
expectedLog := `{"level":"debug","key1":"value1","key2":42,"message":"Test message"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Error method logs correctly", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Error("Error occurred", "error_code", 500) | ||
|
||
expectedLog := `{"level":"debug","error_code":500,"message":"Error occurred"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Info method logs correctly", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Info("Info message", "key", "value") | ||
|
||
expectedLog := `{"level":"debug","key":"value","message":"Info message"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Debug method logs correctly", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Debug("Debugging issue", "context", "test") | ||
|
||
expectedLog := `{"level":"debug","context":"test","message":"Debugging issue"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Warn method logs correctly", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Warn("Warning issued", "warning_level", "high") | ||
|
||
expectedLog := `{"level":"debug","warning_level":"high","message":"Warning issued"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Handles non-string keys gracefully", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Info("Non-string key test", 123, "value", "key2", 42) | ||
|
||
expectedLog := `{"level":"debug","key2":42,"message":"Non-string key test"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Handles odd number of key-value pairs gracefully", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Debug("Odd number test", "key1", "value1", "key2") | ||
|
||
expectedLog := `{"level":"debug","key1":"value1","message":"Odd number test"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
|
||
t.Run("Empty key-value pairs", func(t *testing.T) { | ||
logOutput.Reset() | ||
adapter.Warn("Empty key-value test") | ||
|
||
expectedLog := `{"level":"debug","message":"Empty key-value test"}` | ||
assert.JSONEq(t, expectedLog, logOutput.String()) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.