Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#26): allow squelching warn logs #27

Merged
merged 2 commits into from Feb 20, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 56 additions & 4 deletions implementation/requestlogging/requestlogging.go
Expand Up @@ -3,34 +3,86 @@ package aurestlogging
import (
"context"
aulogging "github.com/StephanHCB/go-autumn-logging"
auloggingapi "github.com/StephanHCB/go-autumn-logging/api"
aurestclientapi "github.com/StephanHCB/go-autumn-restclient/api"
aurestnontripping "github.com/StephanHCB/go-autumn-restclient/implementation/errors/nontrippingerror"
"time"
)

// RequestLoggingOptions allows overriding the log functions used.
//
// This allows easily changing the log level when setting up request logging.
//
// important: do not cache the LeveledLoggingImplementation, create one each time, or some loggers may use
// cached values.
type RequestLoggingOptions struct {
BeforeRequest func(ctx context.Context) auloggingapi.LeveledLoggingImplementation
Success func(ctx context.Context) auloggingapi.LeveledLoggingImplementation
Failure func(ctx context.Context) auloggingapi.LeveledLoggingImplementation
}

func Debug(ctx context.Context) auloggingapi.LeveledLoggingImplementation {
return aulogging.Logger.Ctx(ctx).Debug()
}

func Info(ctx context.Context) auloggingapi.LeveledLoggingImplementation {
return aulogging.Logger.Ctx(ctx).Info()
}

func Warn(ctx context.Context) auloggingapi.LeveledLoggingImplementation {
return aulogging.Logger.Ctx(ctx).Warn()
}

type RequestLoggingImpl struct {
Wrapped aurestclientapi.Client
Options RequestLoggingOptions
}

func NewWithOptions(wrapped aurestclientapi.Client, opts RequestLoggingOptions) aurestclientapi.Client {
instance := &RequestLoggingImpl{
Wrapped: wrapped,
Options: RequestLoggingOptions{
BeforeRequest: Debug,
Success: Info,
Failure: Warn,
},
}
if opts.BeforeRequest != nil {
instance.Options.BeforeRequest = opts.BeforeRequest
}
if opts.Success != nil {
instance.Options.Success = opts.Success
}
if opts.Failure != nil {
instance.Options.Failure = opts.Failure
}
return instance
}

func New(wrapped aurestclientapi.Client) aurestclientapi.Client {
return &RequestLoggingImpl{
Wrapped: wrapped,
Options: RequestLoggingOptions{
BeforeRequest: Debug,
Success: Info,
Failure: Warn,
},
}
}

func (c *RequestLoggingImpl) Perform(ctx context.Context, method string, requestUrl string, requestBody interface{}, response *aurestclientapi.ParsedResponse) error {
aulogging.Logger.Ctx(ctx).Debug().Printf("downstream %s %s...", method, requestUrl)
c.Options.BeforeRequest(ctx).Printf("downstream %s %s...", method, requestUrl)
before := time.Now()
err := c.Wrapped.Perform(ctx, method, requestUrl, requestBody, response)
millis := time.Now().Sub(before).Milliseconds()
if err != nil {
if aurestnontripping.Is(err) {
aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("downstream %s %s -> %d FAILED (%d ms) (nontripping)", method, requestUrl, response.Status, millis)
c.Options.Failure(ctx).WithErr(err).Printf("downstream %s %s -> %d FAILED (%d ms) (nontripping)", method, requestUrl, response.Status, millis)
} else {
aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("downstream %s %s -> %d FAILED (%d ms)", method, requestUrl, response.Status, millis)
c.Options.Failure(ctx).WithErr(err).Printf("downstream %s %s -> %d FAILED (%d ms)", method, requestUrl, response.Status, millis)
}
} else {
aulogging.Logger.Ctx(ctx).Info().Printf("downstream %s %s -> %d OK (%d ms)", method, requestUrl, response.Status, millis)
c.Options.Success(ctx).Printf("downstream %s %s -> %d OK (%d ms)", method, requestUrl, response.Status, millis)
}
return err
}
34 changes: 33 additions & 1 deletion implementation/retry/retry.go
Expand Up @@ -7,6 +7,14 @@ import (
"time"
)

type RetryOptions struct {
RepeatCount uint8

BeforeRetryOrNil aurestclientapi.BeforeRetryCallback

SilenceGivingUp bool
}

type RetryImpl struct {
Wrapped aurestclientapi.Client
RepeatCount uint8
Expand All @@ -16,6 +24,28 @@ type RetryImpl struct {

RetryingMetricsCallback aurestclientapi.MetricsCallbackFunction
GivingUpMetricsCallback aurestclientapi.MetricsCallbackFunction

SilenceGivingUp bool
}

func NewWithOptions(
wrapped aurestclientapi.Client,
condition aurestclientapi.RetryConditionCallback,
opts RetryOptions,
) aurestclientapi.Client {
repeatCount := uint8(2)
if opts.RepeatCount > 0 {
repeatCount = opts.RepeatCount
}
return &RetryImpl{
Wrapped: wrapped,
RepeatCount: repeatCount,
RetryCondition: condition,
BeforeRetry: opts.BeforeRetryOrNil,
RetryingMetricsCallback: doNothingMetricsCallback,
GivingUpMetricsCallback: doNothingMetricsCallback,
SilenceGivingUp: opts.SilenceGivingUp,
}
}

func New(
Expand Down Expand Up @@ -69,7 +99,9 @@ func (c *RetryImpl) Perform(ctx context.Context, method string, requestUrl strin
// (*)
if attempt == c.RepeatCount+1 {
c.GivingUpMetricsCallback(ctx, method, requestUrl, response.Status, err, 0, 0)
aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("giving up on %s %s after attempt %d", method, requestUrl, attempt)
if !c.SilenceGivingUp {
aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("giving up on %s %s after attempt %d", method, requestUrl, attempt)
}
return err
}
} else {
Expand Down
22 changes: 22 additions & 0 deletions implementation/retry/retry_test.go
Expand Up @@ -127,6 +127,28 @@ func TestFailWithRetry(t *testing.T) {
require.Equal(t, []string{r, r, r, r}, aurestcapture.GetRecording(mock))
}

func TestFailWithRetryWithOpts(t *testing.T) {
aulogging.SetupNoLoggerForTesting()

mock := tstMock()
cut := NewWithOptions(mock,
func(ctx context.Context, response *aurestclientapi.ParsedResponse, err error) bool {
return true
},
RetryOptions{
BeforeRetryOrNil: nil,
SilenceGivingUp: true,
},
)

response := &aurestclientapi.ParsedResponse{}
err := cut.Perform(context.Background(), "GET", "http://err", nil, response)
require.NotNil(t, err)
require.Equal(t, "some transport error", err.Error())
r := "GET http://err <nil>"
require.Equal(t, []string{r, r, r}, aurestcapture.GetRecording(mock))
}

func TestAbortRetry(t *testing.T) {
aulogging.SetupNoLoggerForTesting()

Expand Down