Skip to content

Recall functionality on error to log Debug information

License

Notifications You must be signed in to change notification settings

emicklei/recall

Repository files navigation

recall

Go Go Report Card GoDoc codecov

In service-oriented architectures, request processing failures often require detailed logging for effective troubleshooting. Ideally, log records should contain comprehensive context. It can be beneficial to also have access to all debug-level logs leading up to the point of failure. However, enabling debug logging in production environments can result in excessive log volumes and increased CPU utilization.

So, you ideally want debug logging information only when a failure occurs. In general, a failure situation is an exceptional occurrence, so collecting information in that case is not overly costly.

The recall package builds on this idea by encapsulating functionality that can return an error. The following strategies are available to capture debug (slog) logging:

RecallOnErrorStrategy

If an error is detected, a Recaller will again call a Go function, but this time with a different logger configured to capture all debug logging. This strategy requires that your function has no side-effects ; idempotency. This is the default strategy.

RecordingStrategy

Debug logging is recorded by the Recaller directly and only if an error is detected, the log records are replayed from memory using the default logger. This strategy can result in a higher memory consumption (and GC time) because all Debug records are recorded on every function call. The function is not called a second time so no idempotency in processing is required.

Usage (RecallOnErrorStrategy)
recaller := recall.New(context.Background())

err := recaller.Call(func(ctx context.Context) error {
	rlog := recall.Slog(ctx)
	
	rlog.Info("begin")
	rlog.Debug("this will show up on error")
	return errors.New("something went wrong")
})

slog.Error("bummer", "err", err)

will output

2025/02/11 18:55:23 INFO begin
2025/02/11 18:55:23 INFO [RECALL] begin
2025/02/11 18:55:23 INFO [RECALL] this will show up on error
2025/02/11 18:55:23 ERROR bummer err="something went wrong"
Usage (RecordingStrategy in Call)
recaller := recall.New(context.Background()).WithCaptureStrategy(recall.RecordingStrategy)

err := recaller.Call(func(ctx context.Context) error {
	rlog := recall.Slog(ctx)
	
	rlog.Info("begin")
	rlog.Debug("this will show up on error")
	return errors.New("something went wrong")
})

will output

2025/02/12 15:56:07 INFO begin
2025/02/12 15:56:07 INFO [RECALL] this will show up on error
Usage (RecordingStrategy in HTTP handler)

NewRecallHandler returns a http.Handler that inspects the HTTP status code to decide to write recorded Debug log records. The handler is not called a second time so no idempotency in processing is required.

http.ListenAndServe(":8080", recall.NewRecallHandler(http.DefaultServeMux))

See examples for other usages.

Panic

By default, a Recaller will recover from a panic and writes an Error message with stack information, before returning an error with the panic message. You can disable panic recovery using WithPanicRecovery(false).

Not all errors are equal

If your function can return an error for which it makes no sense to retry it then you can set a filter function to check the error before applying the strategy. Use the WithErrorFilter(...) to set the function for the Recaller or WithStatusCodeFilter(...) to set the function for a RecallHandler.

Other work

A different approach in both capturing and visualising logging is offered by the Nanny package.

© 2025, https://ernestmicklei.com. MIT License.

About

Recall functionality on error to log Debug information

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages