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  }