package unitel import ( "fmt" "time" "github.com/getsentry/sentry-go" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" "github.com/rs/zerolog" "go.opentelemetry.io/otel/trace" ) const requestBufferSize = 1000 func RequestLogger(l zerolog.Logger, requestHeaders, responseHeaders bool) fiber.Handler { type request struct { Method string Path string Status int Duration time.Duration RequestHeaders map[string][]string ResponseHeaders map[string][]string OtelSpanID *trace.SpanID SentrySpanID *sentry.SpanID } reqs := make(chan request, requestBufferSize) go func() { for { r := <-reqs l2 := l.Trace(). Str("method", r.Method). Str("path", r.Path). Int("status", r.Status). Dur("duration", r.Duration) if r.OtelSpanID != nil { l2 = l2.Str("otel_span_id", r.OtelSpanID.String()) } if r.SentrySpanID != nil { l2 = l2.Str("sentry_span_id", r.SentrySpanID.String()) } if requestHeaders { for k, v := range r.RequestHeaders { for i, vv := range v { attr := fmt.Sprintf("request.headers.%s", k) if len(v) != 1 { attr = fmt.Sprintf("%s.%d", attr, i) } l2 = l2.Str(attr, vv) } } } if responseHeaders { for k, v := range r.ResponseHeaders { for i, vv := range v { attr := fmt.Sprintf("response.headers.%s", k) if len(v) != 1 { attr = fmt.Sprintf("%s.%d", attr, i) } l2 = l2.Str(attr, vv) } } } l2.Msg("request") } }() return func(c *fiber.Ctx) error { var otelSpanId *trace.SpanID = nil if spanId := trace.SpanContextFromContext(c.UserContext()).SpanID(); spanId.IsValid() { otelSpanId = &spanId } var sentrySpanId *sentry.SpanID = nil if span := sentry.SpanFromContext(c.UserContext()); span != nil { sentrySpanId = &span.SpanID } start := time.Now() err := c.Next() duration := time.Since(start) status := c.Response().StatusCode() if err != nil { if e, ok := err.(*fiber.Error); ok { status = e.Code } else { status = fiber.StatusInternalServerError } } // pushing the request to the logger, so we don't block the requests req := request{ Method: c.Method(), Path: c.Path(), Status: status, Duration: duration, OtelSpanID: otelSpanId, SentrySpanID: sentrySpanId, } if requestHeaders { req.RequestHeaders = make(map[string][]string) for k, v := range c.GetReqHeaders() { for _, vv := range v { req.RequestHeaders[k] = append(req.RequestHeaders[k], utils.CopyString(vv)) } } } if responseHeaders { req.ResponseHeaders = make(map[string][]string) for k, v := range c.GetRespHeaders() { for _, vv := range v { req.ResponseHeaders[k] = append(req.ResponseHeaders[k], utils.CopyString(vv)) } } } reqs <- req return err } }