diff options
Diffstat (limited to 'internal/router')
-rw-r--r-- | internal/router/logger.go | 97 | ||||
-rw-r--r-- | internal/router/router.go | 6 | ||||
-rw-r--r-- | internal/router/template.go | 2 |
3 files changed, 63 insertions, 42 deletions
diff --git a/internal/router/logger.go b/internal/router/logger.go index 7a92da8f1..692a616a4 100644 --- a/internal/router/logger.go +++ b/internal/router/logger.go @@ -19,55 +19,78 @@ package router import ( + "fmt" "net/http" + "os" "time" + "codeberg.org/gruf/go-bytesize" + "codeberg.org/gruf/go-errors/v2" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) -var skipPaths = map[string]interface{}{ - "/api/v1/streaming": nil, -} +// loggingMiddleware provides a request logging and panic recovery gin handler. +func loggingMiddleware(c *gin.Context) { + // Initialize the logging fields + fields := make(logrus.Fields, 7) + + // Determine pre-handler time + before := time.Now() -func loggingMiddleware() gin.HandlerFunc { - logHandler := func(c *gin.Context) { - start := time.Now() + defer func() { + code := c.Writer.Status() path := c.Request.URL.Path - raw := c.Request.URL.RawQuery - - // Process request - c.Next() - - // Log only when path is not being skipped - if _, ok := skipPaths[path]; !ok { - latency := time.Since(start) - clientIP := c.ClientIP() - userAgent := c.Request.UserAgent() - method := c.Request.Method - statusCode := c.Writer.Status() - errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String() - bodySize := c.Writer.Size() - if raw != "" { - path = path + "?" + raw + + if r := recover(); r != nil { + if c.Writer.Status() == 0 { + // No response was written, send a generic Internal Error + c.Writer.WriteHeader(http.StatusInternalServerError) } - l := logrus.WithFields(logrus.Fields{ - "latency": latency, - "clientIP": clientIP, - "userAgent": userAgent, - "method": method, - "statusCode": statusCode, - "path": path, - }) - - if errorMessage == "" { - l.Infof("[%s] %s: wrote %d bytes", latency, http.StatusText(statusCode), bodySize) - } else { - l.Errorf("[%s] %s: %s", latency, http.StatusText(statusCode), errorMessage) + // Append panic information to the request ctx + _ = c.Error(fmt.Errorf("recovered panic: %v", r)) + + // Dump a stacktrace to stderr + callers := errors.GetCallers(3, 10) + fmt.Fprintf(os.Stderr, "recovered panic: %v\n%s", r, callers) + } + + // NOTE: + // It is very important here that we are ONLY logging + // the request path, and none of the query parameters. + // Query parameters can contain sensitive information + // and could lead to storing plaintext API keys in logs + + // Set request logging fields + fields["latency"] = time.Since(before) + fields["clientIP"] = c.ClientIP() + fields["userAgent"] = c.Request.UserAgent() + fields["method"] = c.Request.Method + fields["statusCode"] = code + fields["path"] = path + + // Create a log entry with fields + l := logrus.WithFields(fields) + l.Level = logrus.InfoLevel + + if code >= 500 { + // This is a server error + l.Level = logrus.ErrorLevel + + if len(c.Errors) > 0 { + // Add an error string log field + fields["error"] = c.Errors.String() } } - } - return logHandler + // Generate a nicer looking bytecount + size := bytesize.Size(c.Writer.Size()) + + // Finally, write log entry with status text body size + l.Logf(l.Level, "%s: wrote %s", http.StatusText(code), size) + }() + + // Process request + c.Next() } diff --git a/internal/router/router.go b/internal/router/router.go index ee7024cd1..3917de314 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -143,9 +143,7 @@ func New(ctx context.Context, db db.DB) (Router, error) { // create the actual engine here -- this is the core request routing handler for gts engine := gin.New() - - engine.Use(gin.RecoveryWithWriter(logrus.StandardLogger().Writer())) - engine.Use(loggingMiddleware()) + engine.Use(loggingMiddleware) // 8 MiB engine.MaxMultipartMemory = 8 << 20 @@ -175,7 +173,7 @@ func New(ctx context.Context, db db.DB) (Router, error) { LoadTemplateFunctions(engine) // load templates onto the engine - if err := loadTemplates(engine); err != nil { + if err := LoadTemplates(engine); err != nil { return nil, err } diff --git a/internal/router/template.go b/internal/router/template.go index c033c8f07..ebd8629e8 100644 --- a/internal/router/template.go +++ b/internal/router/template.go @@ -31,7 +31,7 @@ import ( ) // LoadTemplates loads html templates for use by the given engine -func loadTemplates(engine *gin.Engine) error { +func LoadTemplates(engine *gin.Engine) error { templateBaseDir := config.GetWebTemplateBaseDir() if templateBaseDir == "" { return fmt.Errorf("%s cannot be empty and must be a relative or absolute path", config.WebTemplateBaseDirFlag()) |