feat(middlewares): Add stacktrace in recovery log
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
7bd178c462
commit
2259c587e1
@ -13,5 +13,18 @@ func AddMainLogger(ctx context.Context, logger *logrus.Entry) context.Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetLogger(ctx context.Context) *logrus.Entry {
|
func GetLogger(ctx context.Context) *logrus.Entry {
|
||||||
return ctx.Value(LoggerKey).(*logrus.Entry)
|
if log := ctx.Value(LoggerKey); log != nil {
|
||||||
|
return log.(*logrus.Entry)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLoggerWithFields(ctx context.Context, fields logrus.Fields) *logrus.Entry {
|
||||||
|
if log := ctx.Value(LoggerKey); log != nil {
|
||||||
|
log := log.(*logrus.Entry)
|
||||||
|
log = log.WithFields(fields)
|
||||||
|
AddMainLogger(ctx, log)
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
const envPrefix = "LOG_"
|
const envPrefix = "LOG_"
|
||||||
|
|
||||||
// CONFIG_FILE is the default file that will be searched to apply configuration from the filesystem.
|
// CONFIG_FILE is the default file that will be searched to apply configuration from the filesystem.
|
||||||
const defaultConfigFile = "log.yaml"
|
const defaultConfigFile = "conf/log.yaml"
|
||||||
|
|
||||||
// SECRET_NAME is the default name of the secret that will be searched in vault.
|
// SECRET_NAME is the default name of the secret that will be searched in vault.
|
||||||
const defaultSecretName = "log"
|
const defaultSecretName = "log"
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
type ConfigStruct struct {
|
type ConfigStruct struct {
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
Port int `yaml:"port"`
|
Port int `yaml:"port"`
|
||||||
ExtrasFields map[string]interface{} `yaml:"extra_fields"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid will check that the Gelf configuration is valid.
|
// IsValid will check that the Gelf configuration is valid.
|
||||||
|
@ -19,7 +19,7 @@ func NewHook(c *ConfigStruct) (logrus.Hook, error) {
|
|||||||
|
|
||||||
hook := graylog.NewAsyncGraylogHook(
|
hook := graylog.NewAsyncGraylogHook(
|
||||||
fmt.Sprintf("%s:%d", c.Host, c.Port),
|
fmt.Sprintf("%s:%d", c.Host, c.Port),
|
||||||
c.ExtrasFields,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
defer hook.Flush()
|
defer hook.Flush()
|
||||||
|
@ -3,8 +3,11 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.dev.m-and-m.ovh/mderasse/gocommon/commonctx"
|
"git.dev.m-and-m.ovh/mderasse/gocommon/commonctx"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type recoveryFuncSignature func(w http.ResponseWriter, r *http.Request)
|
type recoveryFuncSignature func(w http.ResponseWriter, r *http.Request)
|
||||||
@ -17,13 +20,14 @@ type recoveryMiddleware struct {
|
|||||||
func (rm *recoveryMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (rm *recoveryMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
|
|
||||||
// log the error
|
// log the error
|
||||||
log := commonctx.GetLogger(r.Context())
|
log := commonctx.GetLoggerWithFields(r.Context(), logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
"stacktrace": fmt.Sprintf("%v\n%s", err, getStackrace()),
|
||||||
|
})
|
||||||
if log == nil {
|
if log == nil {
|
||||||
fmt.Print("Impossible to log the panic as the logger is not part of the context\n")
|
fmt.Print("Impossible to log the panic as the logger is not part of the context\n")
|
||||||
} else {
|
} else {
|
||||||
// Probably have to catch a stacktrace + Info about the error
|
|
||||||
log.Error("A Panic happened and has been caught")
|
log.Error("A Panic happened and has been caught")
|
||||||
}
|
}
|
||||||
// execute the recovery function
|
// execute the recovery function
|
||||||
@ -33,9 +37,15 @@ func (rm *recoveryMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request)
|
|||||||
rm.handler.ServeHTTP(w, r)
|
rm.handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getStackrace() string {
|
||||||
|
stackLines := strings.Split(string(debug.Stack()), "\n")
|
||||||
|
// removing a few first line as they are either that function or the middleware
|
||||||
|
return strings.Join(stackLines[9:], "\n")
|
||||||
|
}
|
||||||
|
|
||||||
// NewRecoveryMiddleware will declare a new middleware on the provided handler. It will recover any panic and log it.
|
// NewRecoveryMiddleware will declare a new middleware on the provided handler. It will recover any panic and log it.
|
||||||
// By default, the middleware will return a default error object in json with a 500 Http code. That can be overide by providing a recoveryFunc.
|
// By default, the middleware will return a default error object in json with a 500 Http code. That can be overide by providing a recoveryFunc.
|
||||||
func NewRecoveryMiddleware(h http.Handler, recoveryFunc recoveryFuncSignature) *recoveryMiddleware {
|
func NewRecoveryMiddleware(h http.Handler, recoveryFunc recoveryFuncSignature) http.Handler {
|
||||||
recovery := &recoveryMiddleware{
|
recovery := &recoveryMiddleware{
|
||||||
handler: h,
|
handler: h,
|
||||||
respFunc: recoveryFunc,
|
respFunc: recoveryFunc,
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
const envPrefix = "TRACING_"
|
const envPrefix = "TRACING_"
|
||||||
|
|
||||||
// CONFIG_FILE is the default file that will be searched to apply configuration from the filesystem.
|
// CONFIG_FILE is the default file that will be searched to apply configuration from the filesystem.
|
||||||
const defaultConfigFile = "tracing.yaml"
|
const defaultConfigFile = "conf/tracing.yaml"
|
||||||
|
|
||||||
// SECRET_NAME is the default name of the secret that will be searched in vault.
|
// SECRET_NAME is the default name of the secret that will be searched in vault.
|
||||||
const defaultSecretName = "tracing"
|
const defaultSecretName = "tracing"
|
||||||
|
Loading…
Reference in New Issue
Block a user