diff --git a/cmd/ff-proxy/main.go b/cmd/ff-proxy/main.go index 7eb5920c..3dd66a63 100644 --- a/cmd/ff-proxy/main.go +++ b/cmd/ff-proxy/main.go @@ -547,6 +547,7 @@ func main() { middleware.NewEchoLoggingMiddleware(), middleware.NewEchoAuthMiddleware([]byte(authSecret), bypassAuth), middleware.NewPrometheusMiddleware(promReg), + middleware.NewMetricsLoggingMiddleware(logger), ) go func() { diff --git a/log/log.go b/log/log.go index cdcb5cea..3d918566 100644 --- a/log/log.go +++ b/log/log.go @@ -3,11 +3,14 @@ package log import ( "context" - "github.com/harness/ff-proxy/middleware" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) +type contextKey string + +const RequestIDKey contextKey = "requestID" + // Logger defines a logger with multiple logging levels. When using the logger // calls to its methods should include a brief message describing what happened // and then key value pairs that contain additional info e.g. @@ -221,7 +224,7 @@ func (s StructuredLogger) With(keyvals ...interface{}) Logger { // returns them as a slice of strings func ExtractRequestValuesFromContext(ctx context.Context) []interface{} { values := []interface{}{} - reqID := middleware.GetRequestID(ctx) + reqID := GetRequestID(ctx) if reqID != "" { values = append(values, "reqID") values = append(values, reqID) @@ -229,3 +232,9 @@ func ExtractRequestValuesFromContext(ctx context.Context) []interface{} { return values } + +// GetRequestID extracts the requestID value from the context if it exists. +func GetRequestID(ctx context.Context) string { + requestID, _ := ctx.Value(RequestIDKey).(string) + return requestID +} diff --git a/middleware/middleware.go b/middleware/middleware.go index 17c1094c..87144739 100644 --- a/middleware/middleware.go +++ b/middleware/middleware.go @@ -1,8 +1,12 @@ package middleware import ( + "bytes" "context" "errors" + "fmt" + "github.com/harness/ff-proxy/log" + "io" "net/http" "strconv" "strings" @@ -63,10 +67,6 @@ func NewEchoAuthMiddleware(secret []byte, bypassAuth bool) echo.MiddlewareFunc { }) } -type contextKey string - -const requestIDKey contextKey = "requestID" - // NewEchoRequestIDMiddleware returns an echo middleware that either uses a // provided requestID from the header or generates one and adds it to the request // context. @@ -82,7 +82,7 @@ func NewEchoRequestIDMiddleware() echo.MiddlewareFunc { reqID = requestUUID.String() } - req = req.WithContext(context.WithValue(req.Context(), requestIDKey, reqID)) + req = req.WithContext(context.WithValue(req.Context(), log.RequestIDKey, reqID)) c.SetRequest(req) resp.Header().Set(echo.HeaderXRequestID, reqID) @@ -91,12 +91,6 @@ func NewEchoRequestIDMiddleware() echo.MiddlewareFunc { } } -// GetRequestID extracts the requestID value from the context if it exists. -func GetRequestID(ctx context.Context) string { - requestID, _ := ctx.Value(requestIDKey).(string) - return requestID -} - type prometheusMiddleware struct { requestCount *prometheus.CounterVec requestDuration *prometheus.HistogramVec @@ -185,3 +179,26 @@ func NewPrometheusMiddleware(reg prometheus.Registerer) echo.MiddlewareFunc { } } } + +// NewMetricsLoggingMiddleware creates a middleware that logs the raw request body coming in to the /metrics endpoint +func NewMetricsLoggingMiddleware(logger log.Logger) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + if c.Path() != "/metrics/:environment_uuid" { + return next(c) + } + defer c.Request().Body.Close() + + buf := bytes.NewBuffer([]byte{}) + if _, err := io.Copy(buf, c.Request().Body); err != nil { + return fmt.Errorf("failed to read body in metrics logging middleware: %s") + } + + logger.Info("raw metrics payload", "component", "MetricsLoggingMiddleware", "payload", buf.String()) + + // Need to reset the request body so it can be read by our handler. + c.Request().Body = io.NopCloser(buf) + return next(c) + } + } +} diff --git a/services/metric_service.go b/services/metric_service.go index dfb2b93b..6729b224 100644 --- a/services/metric_service.go +++ b/services/metric_service.go @@ -1,8 +1,10 @@ package services import ( + "bytes" "context" "fmt" + "io" "net/http" "sync" @@ -150,11 +152,25 @@ func (m MetricService) sendMetrics(ctx context.Context, envID string, metric dom return nil } + logPayloadFn := func(ctx context.Context, req *http.Request) error { + buf := bytes.NewBuffer([]byte{}) + if _, err := io.Copy(buf, req.Body); err != nil { + return err + } + + req.Body = io.NopCloser(buf) + m.log.Info("posting metrics to saas", "payload", buf.String()) + return nil + } + ctx = context.WithValue(ctx, tokenKey, token) res, err := m.client.PostMetricsWithResponse(ctx, envID, &clientgen.PostMetricsParams{Cluster: &clusterIdentifier}, clientgen.PostMetricsJSONRequestBody{ MetricsData: metric.MetricsData, TargetData: metric.TargetData, - }, addAuthToken) + }, + addAuthToken, + logPayloadFn, + ) if err != nil { return err } diff --git a/transport/http_server_test.go b/transport/http_server_test.go index d17e1c9a..94bac1b0 100644 --- a/transport/http_server_test.go +++ b/transport/http_server_test.go @@ -299,6 +299,7 @@ func setupHTTPServer(t *testing.T, bypassAuth bool, opts ...setupOpts) *HTTPServ middleware.NewEchoLoggingMiddleware(), middleware.NewEchoAuthMiddleware([]byte(`secret`), bypassAuth), middleware.NewPrometheusMiddleware(prometheus.NewRegistry()), + middleware.NewMetricsLoggingMiddleware(logger), ) return server }