115 lines
3.0 KiB
Go
115 lines
3.0 KiB
Go
package observability
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
"github.com/gofrs/uuid"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/supabase/auth/internal/conf"
|
|
"github.com/supabase/auth/internal/utilities"
|
|
)
|
|
|
|
func AddRequestID(globalConfig *conf.GlobalConfiguration) func(next http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
id := uuid.Must(uuid.NewV4()).String()
|
|
if globalConfig.API.RequestIDHeader != "" {
|
|
id = r.Header.Get(globalConfig.API.RequestIDHeader)
|
|
}
|
|
ctx := r.Context()
|
|
ctx = utilities.WithRequestID(ctx, id)
|
|
next.ServeHTTP(w, r.WithContext(ctx))
|
|
}
|
|
return http.HandlerFunc(fn)
|
|
}
|
|
}
|
|
|
|
func NewStructuredLogger(logger *logrus.Logger, config *conf.GlobalConfiguration) func(next http.Handler) http.Handler {
|
|
return func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/health" {
|
|
next.ServeHTTP(w, r)
|
|
} else {
|
|
chimiddleware.RequestLogger(&structuredLogger{logger, config})(next).ServeHTTP(w, r)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type structuredLogger struct {
|
|
Logger *logrus.Logger
|
|
Config *conf.GlobalConfiguration
|
|
}
|
|
|
|
func (l *structuredLogger) NewLogEntry(r *http.Request) chimiddleware.LogEntry {
|
|
referrer := utilities.GetReferrer(r, l.Config)
|
|
e := &logEntry{Entry: logrus.NewEntry(l.Logger)}
|
|
logFields := logrus.Fields{
|
|
"component": "api",
|
|
"method": r.Method,
|
|
"path": r.URL.Path,
|
|
"remote_addr": utilities.GetIPAddress(r),
|
|
"referer": referrer,
|
|
}
|
|
|
|
if reqID := utilities.GetRequestID(r.Context()); reqID != "" {
|
|
logFields["request_id"] = reqID
|
|
}
|
|
|
|
e.Entry = e.Entry.WithFields(logFields)
|
|
return e
|
|
}
|
|
|
|
// logEntry implements the chiMiddleware.LogEntry interface
|
|
type logEntry struct {
|
|
Entry *logrus.Entry
|
|
}
|
|
|
|
func (e *logEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
|
|
fields := logrus.Fields{
|
|
"status": status,
|
|
"duration": elapsed.Nanoseconds(),
|
|
}
|
|
|
|
errorCode := header.Get("x-sb-error-code")
|
|
if errorCode != "" {
|
|
fields["error_code"] = errorCode
|
|
}
|
|
|
|
entry := e.Entry.WithFields(fields)
|
|
entry.Info("request completed")
|
|
e.Entry = entry
|
|
}
|
|
|
|
func (e *logEntry) Panic(v interface{}, stack []byte) {
|
|
entry := e.Entry.WithFields(logrus.Fields{
|
|
"stack": string(stack),
|
|
"panic": fmt.Sprintf("%+v", v),
|
|
})
|
|
entry.Error("request panicked")
|
|
e.Entry = entry
|
|
}
|
|
|
|
func GetLogEntry(r *http.Request) *logEntry {
|
|
l, _ := chimiddleware.GetLogEntry(r).(*logEntry)
|
|
if l == nil {
|
|
return &logEntry{Entry: logrus.NewEntry(logrus.StandardLogger())}
|
|
}
|
|
return l
|
|
}
|
|
|
|
func LogEntrySetField(r *http.Request, key string, value interface{}) {
|
|
if l, ok := r.Context().Value(chimiddleware.LogEntryCtxKey).(*logEntry); ok {
|
|
l.Entry = l.Entry.WithField(key, value)
|
|
}
|
|
}
|
|
|
|
func LogEntrySetFields(r *http.Request, fields logrus.Fields) {
|
|
if l, ok := r.Context().Value(chimiddleware.LogEntryCtxKey).(*logEntry); ok {
|
|
l.Entry = l.Entry.WithFields(fields)
|
|
}
|
|
}
|