Extended Access Logging
Simplifies access and error logging of Labstack/echo HTTP servers
To get started with access and error logging, it's enough to call eal.Init
, eal.InitDefaultErrorLogging
(both are optional),
and then add the middleware returned by eal.CreateLoggerMiddleware()
to the echo server.
package main
import (
"github.com/labstack/echo/v4"
"github.com/modfin/eal"
)
func main() {
// Initialize logrus JSON logger.
eal.Init(false)
// Initialize eal default error logging for echo.HTTPError and jwt.ValidationError error types.
eal.InitDefaultErrorLogging()
// Create echo instance and set up the access logging middleware.
e := echo.New()
e.Use(eal.CreateLoggerMiddleware())
// Setup endpoints and start echo server the usual way...
// ...
To extend the log entry that is going to be written when the endpoint is about to return, one can use the AddContextFields
method.
e.POST("/user", func(c echo.Context) error {
userID := c.FormValue("user-id")
// Add "user-id" field to context, that will be included in the log entry generated by the middleware when
// handler have returned.
eal.AddContextFields(c, Fields{"user-id": userID})
// ...
})
To generate a stacktrace, the Trace
method can be used. Trace
takes an error and wrap it in a new error that contain a stacktrace.
It is possible to configure what errors and error types that shouldn't generate a stacktrace (see InhibitStacktraceForError
for more information).
If the error provided to Trace
already is, or contain, a wrapped stacktrace-error, the original error will be returned unmodified.
There is a global parameter that can be set that affect when the stacktrace is first logged: LogCallStackDirectly
. If it's
true
, Trace
will write a new log entry directly after the new stacktrace error have been created. This can be useful if there is a chance
that the error returned by Trace
isn't wrapped and returned to the middleware logger.
if err != nil {
// Wrap the original error in a stacktrace, before wrapping it in a new error with more information (GO 1.13 and later)
return fmt.Errorf("encode: %v: %w", data, eal.Trace(err))
}
Some error types may have more information than what's shown in the Error()
string, or if it's desirable to have some error information
logged as a separate field in the log. The RegisterErrorLogFunc
method can be used to extend the log entry with specific error information.
See InitDefaultErrorLogging()
for an example of how to use RegisterErrorLogFunc
.
Normally echo will send back a HTTP status 500 when an error is returned from the echo handlerFunc, unless the error is a echo.HTTPError.
When the eal.CreateLoggerMiddleware
is used, it will look for the earliest echo.HTTPError if can find in the returned error, and return
that to echo, if the returned error don't contain a wrapped echoHTTPError, the error will be passed on to echo unmodified.
var errNope error = echo.NewHTTPError(http.StatusNotFound, "Nope") // Returns 404 {"message":"Nope"}, to caller
...
e.GET("/droids", func(c echo.Context) error {
return errNope
})
or if the error information that we want to send back is caused by an error, eal implement a NewHTTPError
method that wrap an error in a
echo.HTTPError
func errNope(err error) error {
// Wrap the error in a stacktrace, and then wrap it in a echo.HTTPError
return eal.NewHTTPError(eal.Trace(err), http.StatusNotFound, "Nope") // Return 404 {"message":"Nope"}, to caller
}
...
e.GET("/droids", func(c echo.Context) error {
d, err := getDroids()
if err != nil {
return errNope(err)
}
return c.JSON(http.StatusOK, d)
})
it's also possible to send back a custom JSON message to the caller by using a struct as a parameter in the echo.HTTPError
type ErrorMessage struct {
ErrorCode int `json:"error_code"`
ErrorMessage string `json:"error_message"`
}
var ErrSomeMessage error = echo.NewHTTPError(http.StatusNotFound, &ErrorMessage{ErrorCode: 42, ErrorMessage: "common.error.some_message"})