diff --git a/implementation/requestlogging/requestlogging.go b/implementation/requestlogging/requestlogging.go index e942c65..de1951c 100644 --- a/implementation/requestlogging/requestlogging.go +++ b/implementation/requestlogging/requestlogging.go @@ -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 } diff --git a/implementation/retry/retry.go b/implementation/retry/retry.go index c4dd536..1bc2f7e 100644 --- a/implementation/retry/retry.go +++ b/implementation/retry/retry.go @@ -7,6 +7,14 @@ import ( "time" ) +type RetryOptions struct { + RepeatCount uint8 + + BeforeRetryOrNil aurestclientapi.BeforeRetryCallback + + SilenceGivingUp bool +} + type RetryImpl struct { Wrapped aurestclientapi.Client RepeatCount uint8 @@ -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( @@ -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 { diff --git a/implementation/retry/retry_test.go b/implementation/retry/retry_test.go index 3a5fa97..271c507 100644 --- a/implementation/retry/retry_test.go +++ b/implementation/retry/retry_test.go @@ -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 " + require.Equal(t, []string{r, r, r}, aurestcapture.GetRecording(mock)) +} + func TestAbortRetry(t *testing.T) { aulogging.SetupNoLoggerForTesting()