nuts-foundation/nuts-node

View on GitHub
http/requestlogger.go

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
/*
 * Copyright (C) 2022 Nuts community
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package http

import (
    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    "github.com/nuts-foundation/nuts-node/core"
    "github.com/sirupsen/logrus"
    "mime"
)

// requestLoggerMiddleware returns middleware that logs metadata of HTTP requests.
// Should be added as the outer middleware to catch all errors and potential status rewrites
func requestLoggerMiddleware(skipper middleware.Skipper, logger *logrus.Entry) echo.MiddlewareFunc {
    return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
        Skipper:     skipper,
        LogHeaders:  []string{"Authorization", "DPoP"},
        LogURI:      true,
        LogStatus:   true,
        LogMethod:   true,
        LogRemoteIP: true,
        LogError:    true,
        LogValuesFunc: func(c echo.Context, values middleware.RequestLoggerValues) error {
            status := values.Status
            if values.Error != nil {
                status = core.GetHTTPStatusCode(values.Error, c)
            }

            fields := logrus.Fields{
                "remote_ip": values.RemoteIP,
                "method":    values.Method,
                "uri":       values.URI,
                "status":    status,
            }
            if logger.Level >= logrus.DebugLevel {
                fields["headers"] = values.Headers
            }
            logger.WithFields(fields).Info("HTTP request")

            return nil
        },
    })
}

// bodyLoggerMiddleware returns middleware that logs body of HTTP requests and their replies.
// Should be added as the outer middleware to catch all errors and potential status rewrites
func bodyLoggerMiddleware(skipper middleware.Skipper, logger *logrus.Entry) echo.MiddlewareFunc {
    return middleware.BodyDumpWithConfig(middleware.BodyDumpConfig{
        Handler: func(e echo.Context, request []byte, response []byte) {
            requestContentType := e.Request().Header.Get("Content-Type")
            if isLoggableContentType(requestContentType) {
                logger.Infof("HTTP request body: %s", string(request))
            }

            responseContentType := e.Response().Header().Get("Content-Type")
            if isLoggableContentType(responseContentType) {
                logger.Infof("HTTP response body: %s", string(response))
            }
        },
        Skipper: skipper,
    })
}

func isLoggableContentType(contentType string) bool {
    mediaType, _, _ := mime.ParseMediaType(contentType)
    switch mediaType {
    case "application/json":
        fallthrough
    case "application/did+json":
        fallthrough
    case "application/vc+json":
        fallthrough
    case "application/x-www-form-urlencoded":
        return true
    }
    return false
}