Skip to content

Commit

Permalink
feat: recover log error func
Browse files Browse the repository at this point in the history
Log error functions for printing panic errors with Logrus, ZeroLog,
ZapLog or CharmLog.
  • Loading branch information
faabiosr committed Sep 5, 2024
1 parent 18fbdb3 commit 97f8064
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 0 deletions.
14 changes: 14 additions & 0 deletions charm.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,17 @@ func CharmLogWithConfig(cfg CharmLogConfig) echo.MiddlewareFunc {
}
}
}

// CharmLogRecoverFn returns a CharmLog recover log function to print panic
// errors.
func CharmLogRecoverFn(logger *charm.Logger) mw.LogErrorFunc {
return func(_ echo.Context, err error, stack []byte) error {
logger.Error(
"panic recover",
"stacktrace", string(stack),
"error", err,
)

return err
}
}
29 changes: 29 additions & 0 deletions charm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

charm "github.com/charmbracelet/log"
"github.com/labstack/echo/v4"
emw "github.com/labstack/echo/v4/middleware"
)

func TestCharmLogWithConfig(t *testing.T) {
Expand Down Expand Up @@ -103,3 +104,31 @@ func TestCharmLogRetrievesAnError(t *testing.T) {
t.Errorf("invalid log: error not found")
}
}

func TestCharmLogRecoverFn(t *testing.T) {
ec := panicCtx(t)
b := new(bytes.Buffer)

logger := charm.New(b)

rec := emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: CharmLogRecoverFn(logger),
})

config := CharmLogConfig{
Logger: logger,
FieldMap: testFields,
}

_ = CharmLogWithConfig(config)(rec(testHandler))(ec)

res := b.String()

if !strings.Contains(res, "status=500") {
t.Errorf("invalid log: wrong status code")
}

if !strings.Contains(res, `error="unable to call"`) {
t.Errorf("invalid log: error not found")
}
}
54 changes: 54 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

charm "github.com/charmbracelet/log"
"github.com/labstack/echo/v4"
emw "github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog"
"github.com/sirupsen/logrus"
"go.opencensus.io/stats/view"
Expand Down Expand Up @@ -47,6 +48,20 @@ func ExampleZeroLogWithConfig() {
e.Use(middleware.ZeroLogWithConfig(logConfig))
}

// This example register the ZeroLog log error function to echo middleware
// Recover.
func ExampleZeroLogRecoverFn() {
e := echo.New()

// Custom zerolog logger instance
logger := zerolog.New(os.Stderr).With().Timestamp().Logger()

// Middleware
e.Use(emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: middleware.ZeroLogRecoverFn(logger),
}))
}

// This example registers the Logrus middleware with default configuration.
func ExampleLogrus() {
e := echo.New()
Expand Down Expand Up @@ -76,6 +91,20 @@ func ExampleLogrusWithConfig() {
e.Use(middleware.LogrusWithConfig(logConfig))
}

// This example register the Logrus log error function to echo middleware
// Recover.
func ExampleLogrusRecoverFn() {
e := echo.New()

// Custom logrus logger instance
logger := logrus.New()

// Middleware
e.Use(emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: middleware.LogrusRecoverFn(logger),
}))
}

// This example registers the OpenCensus middleware with default configuration.
func ExampleOpenCensus() {
e := echo.New()
Expand Down Expand Up @@ -127,6 +156,20 @@ func ExampleZapLogWithConfig() {
e.Use(middleware.ZapLogWithConfig(logConfig))
}

// This example register the ZapLog log error function to echo middleware
// Recover.
func ExampleZapLogRecoverFn() {
e := echo.New()

// Custom ZapLog logger instance
logger, _ := zap.NewProduction()

// Middleware
e.Use(emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: middleware.ZapLogRecoverFn(logger),
}))
}

// This example registers the CharmBracelet Log middleware with default configuration.
func ExampleCharmLog() {
e := echo.New()
Expand All @@ -153,6 +196,17 @@ func ExampleCharmLogWithConfig() {
e.Use(middleware.CharmLogWithConfig(logConfig))
}

// This example register the CharmLog log error function to echo middleware
// Recover.
func ExampleCharmLogRecoverFn() {
e := echo.New()

// Middleware
e.Use(emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: middleware.CharmLogRecoverFn(charm.Default()),
}))
}

// This example registers the RequestID middleware with default configuration.
func ExampleRequestID() {
e := echo.New()
Expand Down
8 changes: 8 additions & 0 deletions log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func testHandler(ec echo.Context) error {
return errors.New("error")
}

if v := ec.QueryParam("panic"); v != "" {
panic("unable to call")
}

return ec.String(http.StatusOK, "test")
}

Expand Down Expand Up @@ -100,3 +104,7 @@ func postCtx(t *testing.T) echo.Context {

return ec
}

func panicCtx(t *testing.T) echo.Context {
return testCtx(t, "/some?panic=1")
}
11 changes: 11 additions & 0 deletions logrus.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,14 @@ func LogrusWithConfig(cfg LogrusConfig) echo.MiddlewareFunc {
}
}
}

// LogrusRecoverFn returns a Logrus recover log function to print panic errors.
func LogrusRecoverFn(logger *logrus.Logger) mw.LogErrorFunc {
return func(_ echo.Context, err error, stack []byte) error {
logger.WithField("stacktrace", string(stack)).
WithError(err).
Error("panic recover")

return err
}
}
29 changes: 29 additions & 0 deletions logrus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"

"github.com/labstack/echo/v4"
emw "github.com/labstack/echo/v4/middleware"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -105,3 +106,31 @@ func TestLogrusRetrievesAnError(t *testing.T) {
t.Errorf("invalid log: error not found")
}
}

func TestLogrusRecoverFn(t *testing.T) {
ec := panicCtx(t)
b := new(bytes.Buffer)

logger := logrus.StandardLogger()
logger.Out = b

rec := emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: LogrusRecoverFn(logger),
})

config := LogrusConfig{
Logger: logger,
}

_ = LogrusWithConfig(config)(rec(testHandler))(ec)

res := b.String()

if !strings.Contains(res, "status=500") {
t.Errorf("invalid log: wrong status code")
}

if !strings.Contains(res, `error="unable to call"`) {
t.Errorf("invalid log: error not found")
}
}
12 changes: 12 additions & 0 deletions zap.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,15 @@ func ZapLogWithConfig(cfg ZapLogConfig) echo.MiddlewareFunc {
}
}
}

// ZapLogRecoverFn returns a ZapLog recover log function to print panic errors.
func ZapLogRecoverFn(logger *zap.Logger) mw.LogErrorFunc {
return func(_ echo.Context, err error, stack []byte) error {
logger.With(
zap.ByteString("stacktrace", stack),
zap.Error(err),
).Error("panic recover")

return err
}
}
31 changes: 31 additions & 0 deletions zap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"

"github.com/labstack/echo/v4"
emw "github.com/labstack/echo/v4/middleware"
"go.uber.org/zap"
"go.uber.org/zap/zaptest/observer"
)
Expand Down Expand Up @@ -105,3 +106,33 @@ func TestZapLogRetrievesAnError(t *testing.T) {
t.Errorf("invalid log: error not found")
}
}

func TestZapLogRecoverFn(t *testing.T) {
ec := panicCtx(t)
obsLog, logs := observer.New(zap.InfoLevel)
logger := zap.New(obsLog)

rec := emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: ZapLogRecoverFn(logger),
})

config := ZapLogConfig{
Logger: logger,
}

_ = ZapLogWithConfig(config)(rec(testHandler))(ec)

entry := logs.All()[0]
ectx := entry.ContextMap()

if _, ok := ectx["error"]; !ok {
t.Errorf("invalid log: error not found")
}

entry = logs.All()[1]
ectx = entry.ContextMap()

if ectx["status"] != int64(http.StatusInternalServerError) {
t.Errorf("invalid log: wrong status code")
}
}
15 changes: 15 additions & 0 deletions zerolog.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,18 @@ func ZeroLogWithConfig(cfg ZeroLogConfig) echo.MiddlewareFunc {
}
}
}

// ZeroLogRecoverFn returns a ZeroLog recover log function to print panic
// errors.
func ZeroLogRecoverFn(logger zerolog.Logger) mw.LogErrorFunc {
return func(_ echo.Context, err error, stack []byte) error {
logger.Error().
Err(err).
Fields(map[string]interface{}{
"stacktrace": stack,
}).
Msg("panic recover")

return err
}
}
27 changes: 27 additions & 0 deletions zerolog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"

"github.com/labstack/echo/v4"
emw "github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
Expand Down Expand Up @@ -102,3 +103,29 @@ func TestZeroLogRetrievesAnError(t *testing.T) {
t.Errorf("invalid log: error not found")
}
}

func TestZeroLogRecoverFn(t *testing.T) {
ec := panicCtx(t)
b := new(bytes.Buffer)
logger := log.Output(zerolog.ConsoleWriter{Out: b, NoColor: true})

rec := emw.RecoverWithConfig(emw.RecoverConfig{
LogErrorFunc: ZeroLogRecoverFn(logger),
})

config := ZeroLogConfig{
Logger: logger,
}

_ = ZeroLogWithConfig(config)(rec(testHandler))(ec)

res := b.String()

if !strings.Contains(res, "status=500") {
t.Errorf("invalid log: wrong status code")
}

if !strings.Contains(res, `error="unable to call"`) {
t.Errorf("invalid log: error not found")
}
}

0 comments on commit 97f8064

Please sign in to comment.