panic错误导致系统异常、返回给前端一堆乱码,可以通过中间件处理panic错误,代码:
// GinRecovery recover掉项目可能出现的panic
func GinRecovery(stack bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
httpRequest, _ := httputil.DumpRequest(c.Request, false)
if brokenPipe {
global.Logger.Errorf(string(httpRequest), err, c.Request.URL.Path)
// If the connection is dead, we can't write a status to it.
_ = c.Error(err.(error)) // nolint: err
c.Abort()
return
}
if stack {
//打印日志
log.Errorf("[Recovery from panic]", err, "[ERROR]:"+string(debug.Stack()[:]))
} else {
log.Errorf("[Recovery from panic]", err, "[ERROR]:"+string(httpRequest))
}
//前台返回
response := app.NewResponse(c)
response.ToErrorResponse(errcode.ServerError.WithDetails(fmt.Sprintf("%v", err)))
c.Abort()
}
}()
c.Next()
}
}
每个请求调用时长中间件,代码如下:
type AccessLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w AccessLogWriter) Write(p []byte) (int, error) {
if n, err := w.body.Write(p); err != nil {
return n, err
}
return w.ResponseWriter.Write(p)
}
func AccessLog() gin.HandlerFunc {
return func(context *gin.Context) {
bodyWriter := &AccessLogWriter{body: bytes.NewBufferString(""), ResponseWriter: context.Writer}
context.Writer = bodyWriter
beginTime := time.Now().Unix()
context.Next()
endTime := time.Now().Unix()
log.Info("AccessLog", fmt.Sprintf("method:%v,url:%v,status_code:%d,begin_time:%d,end_time:%d,info:%v",
context.Request.Method,
context.Request.URL,
bodyWriter.Status(),
beginTime,
endTime,
bodyWriter.body.String()))
}
}
3、链路追踪中间件,代码如下:
func setupTracer() error {
jaegerTracer, _, err := tracer.NewJaegerTracer("blog_service", "127.0.0.1:6831")
if err != nil {
return err
}
global.Tracer = jaegerTracer
return nil
}
package middleware
import (
"context"
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"xxxxx/blog-service/global"
)
func Tracing() func(c *gin.Context) {
return func(c *gin.Context) {
var ctx context.Context
span := opentracing.SpanFromContext(c.Request.Context())
if span != nil {
span, ctx = opentracing.StartSpanFromContextWithTracer(c.Request.Context(), global.Tracer, c.Request.URL.Path)
defer span.Finish()
c.Request = c.Request.WithContext(ctx)
c.Next()
}
var traceID string
var spanID string
var spanContext = span.Context()
switch spanContext.(type) {
case jaeger.SpanContext:
traceID = spanContext.(jaeger.SpanContext).TraceID().String()
spanID = spanContext.(jaeger.SpanContext).SpanID().String()
c.Set("X-Trace-ID", traceID)
c.Set("X-Span-ID", spanID)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
}