github.com/nikandfor/tlog@v0.21.5-0.20231108111739-3ef89426a96d/ext/tlgin/gin.go (about) 1 package tlgin 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "net/http" 7 "runtime/debug" 8 9 "github.com/gin-gonic/gin" 10 11 "github.com/nikandfor/tlog" 12 "github.com/nikandfor/tlog/ext/tlhttp" 13 "github.com/nikandfor/tlog/low" 14 ) 15 16 func Tracer(c *gin.Context) { 17 tracer(tlog.DefaultLogger, c) 18 } 19 20 func CustomTracer(l *tlog.Logger) func(*gin.Context) { 21 return func(c *gin.Context) { 22 tracer(l, c) 23 } 24 } 25 26 func tracer(l *tlog.Logger, c *gin.Context) { 27 var trid tlog.ID 28 var err error 29 30 xtr := c.GetHeader(tlhttp.TraceIDKey) 31 if xtr != "" { 32 trid, err = tlog.IDFromString(xtr) 33 } 34 35 tr := l.NewSpan(0, trid, "http_request", "client_ip", c.ClientIP(), "method", c.Request.Method, "path", c.Request.URL.Path) 36 defer func() { 37 if p := recover(); p != nil { 38 s := debug.Stack() 39 40 tr.Printw("panic", "panic", p, "panic_type", tlog.FormatNext("%T"), p, "stack_trace", low.UnsafeBytesToString(s), tlog.KeyLogLevel, tlog.Error) 41 42 c.Status(http.StatusInternalServerError) 43 } 44 45 tr.Finish("status_code", c.Writer.Status()) 46 }() 47 48 if err != nil { 49 tr.Printw("bad parent trace id", "id", xtr, "err", err) 50 } 51 52 ctx := c.Request.Context() 53 ctx = tlog.ContextWithSpan(ctx, tr) 54 c.Request = c.Request.WithContext(ctx) 55 56 c.Set("tlog.par", trid) 57 58 c.Set("tlog.span", tr) 59 60 c.Header(tlhttp.TraceIDKey, tr.ID.StringFull()) 61 62 c.Next() 63 } 64 65 func SpanFromContext(c *gin.Context) (tr tlog.Span) { 66 i, _ := c.Get("tlog.span") 67 tr, _ = i.(tlog.Span) 68 69 return 70 } 71 72 func Dumper(c *gin.Context) { 73 tr := SpanFromContext(c) 74 75 if tr.If("rawbody,rawrequest") { 76 data, err := ioutil.ReadAll(c.Request.Body) 77 if err != nil { 78 tr.Printw("read body", "err", err) 79 return 80 } 81 82 err = c.Request.Body.Close() 83 if err != nil { 84 tr.Printw("close body", "err", err) 85 return 86 } 87 88 c.Request.Body = ioutil.NopCloser(bytes.NewReader(data)) 89 90 tr.Printw("request", "len", len(data), "data", data) 91 } 92 93 var rw *respWriter 94 95 if tr.If("rawbody,rawresponse") { 96 rw = &respWriter{ResponseWriter: c.Writer} 97 98 c.Writer = rw 99 } 100 101 c.Next() 102 103 if tr.If("rawbody,rawresponse") { 104 tr.Printw("response", "len", rw.cp.Len(), "data", rw.cp.Bytes()) 105 } 106 } 107 108 type respWriter struct { 109 gin.ResponseWriter 110 cp bytes.Buffer 111 } 112 113 func (w *respWriter) Write(p []byte) (int, error) { 114 _, _ = w.cp.Write(p) 115 return w.ResponseWriter.Write(p) 116 }