Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor code: request/response logging middleware, entity, standard logging for troubleshooting #118

Merged
merged 1 commit into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions common/string.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package common

import (
"encoding/json"
"fmt"
)

func PrettifyJSON(input string) (string, error) {
// First unmarshal the input string into a generic interface{}
var temp interface{}
err := json.Unmarshal([]byte(input), &temp)
if err != nil {
return "", fmt.Errorf("invalid JSON input: %v", err)
}

// Marshal back to JSON with indentation
pretty, err := json.MarshalIndent(temp, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal JSON: %v", err)
}

return string(pretty), nil
}
19 changes: 19 additions & 0 deletions entity/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package entity

import "registry-backend/ent"

// NodeFilter holds optional parameters for filtering node results
type NodeFilter struct {
PublisherID string
Search string
IncludeBanned bool
}

// ListNodesResult is the structure that holds the paginated result of nodes
type ListNodesResult struct {
Total int `json:"total"`
Nodes []*ent.Node `json:"nodes"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"totalPages"`
}
24 changes: 24 additions & 0 deletions entity/node_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package entity

import (
"registry-backend/ent"
"registry-backend/ent/schema"
"time"
)

type NodeVersionFilter struct {
NodeId string
Status []schema.NodeVersionStatus
IncludeStatusReason bool
MinAge time.Duration
PageSize int
Page int
}

type ListNodeVersionsResult struct {
Total int `json:"total"`
NodeVersions []*ent.NodeVersion `json:"nodes"`
Page int `json:"page"`
Limit int `json:"limit"`
TotalPages int `json:"totalPages"`
}
5 changes: 5 additions & 0 deletions entity/publisher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package entity

type PublisherFilter struct {
UserID string
}
2 changes: 1 addition & 1 deletion common/types.go → entity/token.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// File: common/types.go

package common
package entity

type InviteTokenStatus string

Expand Down
65 changes: 11 additions & 54 deletions server/implementation/registry.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion server/middleware/authentication/firebase_auth.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_authentication
package authentication

import (
"context"
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/authentication/firebase_auth_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_authentication
package authentication

import (
"net/http"
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/authentication/jwt_admin_auth.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_authentication
package authentication

import (
"context"
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/authentication/jwt_admin_auth_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_authentication
package authentication

import (
"net/http"
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/authentication/service_account_auth.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_authentication
package authentication

import (
"net/http"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_authentication
package authentication

import (
"net/http"
Expand Down
24 changes: 0 additions & 24 deletions server/middleware/error_logger.go

This file was deleted.

2 changes: 1 addition & 1 deletion server/middleware/metric/metric.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_metric
package metric

import (
"context"
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/metric/metric_middleware.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_metric
package metric

import (
"context"
Expand Down
38 changes: 38 additions & 0 deletions server/middleware/request_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package middleware

import (
"bytes"
"fmt"
"github.com/labstack/echo/v4"
echo_middleware "github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog/log"
"io"
)

func RequestLoggerMiddleware() echo.MiddlewareFunc {
return echo_middleware.RequestLoggerWithConfig(echo_middleware.RequestLoggerConfig{
LogURI: true,
LogStatus: true,
LogValuesFunc: func(c echo.Context, v echo_middleware.RequestLoggerValues) error {
// Read the request body for logging
requestBody, err := io.ReadAll(c.Request().Body)
if err != nil {
log.Ctx(c.Request().Context()).Error().Err(err).Msg("Failed to read request body")
return err
}
// Reset the body for further use
c.Request().Body = io.NopCloser(bytes.NewReader(requestBody))

// Log request details including query parameters
log.Ctx(c.Request().Context()).
Info().
Str("Method", c.Request().Method).
Str("Path", c.Path()).
Str("QueryParams", fmt.Sprintf("%v", c.QueryParams())).
Str("RequestBody", string(requestBody)).
Str("Headers", fmt.Sprintf("%v", c.Request().Header)).
Msg("Request received")
return nil
},
})
}
63 changes: 63 additions & 0 deletions server/middleware/response_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package middleware

import (
"bytes"
"fmt"
"net/http"

"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
)

// Custom response writer to capture response body
type responseWriter struct {
http.ResponseWriter
body *bytes.Buffer
}

func (rw *responseWriter) Write(p []byte) (n int, err error) {
// Capture the response body in the buffer
n, err = rw.body.Write(p)
if err != nil {
return n, err
}
// Write to the actual ResponseWriter
return rw.ResponseWriter.Write(p)
}

// ResponseLoggerMiddleware will log response details and errors.
func ResponseLoggerMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Create a custom response writer to capture the response body
rw := &responseWriter{
ResponseWriter: c.Response().Writer,
body: new(bytes.Buffer),
}
c.Response().Writer = rw

// Call the next handler in the chain
err := next(c)

// Log any errors that occur during handling
if err != nil {
log.Ctx(c.Request().Context()).
Error().
Err(err).
Str("Method", c.Request().Method).
Str("Path", c.Path()).
Msg("Error occurred during request handling")
}

// Log the response details
log.Ctx(c.Request().Context()).
Info().
Int("Status", c.Response().Status).
Str("ResponseBody", rw.body.String()).
Str("ResponseHeaders", fmt.Sprintf("%v", c.Response().Header())).
Msg("Response sent")

return err
}
}
}
2 changes: 1 addition & 1 deletion server/middleware/tracing_middleware.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package drip_middleware
package middleware

import (
"context"
Expand Down
57 changes: 14 additions & 43 deletions server/server.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package server

import (
monitoring "cloud.google.com/go/monitoring/apiv3/v2"
"context"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
"registry-backend/config"
generated "registry-backend/drip"
"registry-backend/ent"
Expand All @@ -12,18 +15,10 @@ import (
"registry-backend/gateways/storage"
handler "registry-backend/server/handlers"
"registry-backend/server/implementation"
drip_middleware "registry-backend/server/middleware"
drip_authentication "registry-backend/server/middleware/authentication"
drip_authorization "registry-backend/server/middleware/authorization"
drip_metric "registry-backend/server/middleware/metric"
"strings"

monitoring "cloud.google.com/go/monitoring/apiv3/v2"

"github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog/log"

"github.com/labstack/echo/v4"
"registry-backend/server/middleware"
"registry-backend/server/middleware/authentication"
"registry-backend/server/middleware/authorization"
"registry-backend/server/middleware/metric"
)

type ServerDependencies struct {
Expand Down Expand Up @@ -97,30 +92,13 @@ func (s *Server) Start() error {
e.HideBanner = true

// Apply middleware
e.Use(drip_middleware.TracingMiddleware)
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{"*"},
AllowHeaders: []string{"*"},
AllowOriginFunc: func(origin string) (bool, error) {
return true, nil
},
AllowCredentials: true,
}))
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogURI: true,
LogStatus: true,
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
if strings.HasPrefix(c.Request().URL.Path, "/vm/") {
return nil
}

log.Ctx(c.Request().Context()).Debug().
Str("URI: ", v.URI).
Int("status", v.Status).Msg("")
return nil
},
}))
e.Use(middleware.TracingMiddleware)
e.Use(middleware.RequestLoggerMiddleware())
e.Use(middleware.ResponseLoggerMiddleware())
e.Use(metric.MetricsMiddleware(&s.Dependencies.MonitoringClient, s.Config))
e.Use(authentication.FirebaseAuthMiddleware(s.Client))
e.Use(authentication.ServiceAccountAuthMiddleware())
e.Use(authentication.JWTAdminAuthMiddleware(s.Client, s.Config.JWTSecret))

// Attach implementation of the generated OAPI strict server
impl := implementation.NewStrictServerImplementation(
Expand All @@ -144,13 +122,6 @@ func (s *Server) Start() error {
e.GET("/openapi", handler.SwaggerHandler)
e.GET("/health", s.HealthCheckHandler)

// Apply global middlewares
e.Use(drip_metric.MetricsMiddleware(&s.Dependencies.MonitoringClient, s.Config))
e.Use(drip_authentication.FirebaseAuthMiddleware(s.Client))
e.Use(drip_authentication.ServiceAccountAuthMiddleware())
e.Use(drip_authentication.JWTAdminAuthMiddleware(s.Client, s.Config.JWTSecret))
e.Use(drip_middleware.ErrorLoggingMiddleware())

// Start the server
return e.Start(":8080")
}
Expand Down
Loading
Loading