[#369] Enhanced http requests logging
Some checks failed
/ Vulncheck (pull_request) Successful in 1m32s
/ DCO (pull_request) Successful in 2m12s
/ Builds (1.20) (pull_request) Successful in 2m46s
/ Builds (1.21) (pull_request) Successful in 2m27s
/ Lint (pull_request) Failing after 2m13s
/ Tests (1.20) (pull_request) Failing after 2m36s
/ Tests (1.21) (pull_request) Failing after 2m30s
Some checks failed
/ Vulncheck (pull_request) Successful in 1m32s
/ DCO (pull_request) Successful in 2m12s
/ Builds (1.20) (pull_request) Successful in 2m46s
/ Builds (1.21) (pull_request) Successful in 2m27s
/ Lint (pull_request) Failing after 2m13s
/ Tests (1.20) (pull_request) Failing after 2m36s
/ Tests (1.21) (pull_request) Failing after 2m30s
Signed-off-by: Nikita Zinkevich <n.zinkevich@yadro.com>
This commit is contained in:
parent
3b83de31d2
commit
91ba725c97
6 changed files with 135 additions and 42 deletions
60
api/middleware/log_http.go
Normal file
60
api/middleware/log_http.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpLogSettings interface {
|
||||||
|
GetHttpLogEnabled() bool
|
||||||
|
GetMaxLogBody() int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogHttp logs http parameters from s3 request
|
||||||
|
func LogHttp(l *zap.Logger, settings HttpLogSettings) Func {
|
||||||
|
return func(h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !settings.GetHttpLogEnabled() {
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var httplog = l.With(
|
||||||
|
zap.String("from", r.RemoteAddr),
|
||||||
|
zap.String("URI", r.RequestURI),
|
||||||
|
zap.String("method", r.Method),
|
||||||
|
)
|
||||||
|
|
||||||
|
httplog = withFieldIfExist(httplog, "query", r.URL.Query())
|
||||||
|
httplog = withFieldIfExist(httplog, "headers", r.Header)
|
||||||
|
if r.ContentLength != 0 && r.ContentLength <= settings.GetMaxLogBody() {
|
||||||
|
var err error
|
||||||
|
httplog, err = withBody(httplog, r)
|
||||||
|
if err != nil {
|
||||||
|
l.Error("read body error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httplog.Info(logs.RequestHTTP)
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func withBody(httplog *zap.Logger, r *http.Request) (*zap.Logger, error) {
|
||||||
|
var body = make([]byte, r.ContentLength)
|
||||||
|
_, err := r.Body.Read(body)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
httplog = httplog.With(zap.String("body", string(body)))
|
||||||
|
|
||||||
|
return httplog, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func withFieldIfExist(log *zap.Logger, label string, data map[string][]string) *zap.Logger {
|
||||||
|
if len(data) != 0 {
|
||||||
|
log = log.With(zap.Any(label, data))
|
||||||
|
}
|
||||||
|
return log
|
||||||
|
}
|
|
@ -97,6 +97,7 @@ type Settings interface {
|
||||||
s3middleware.RequestSettings
|
s3middleware.RequestSettings
|
||||||
s3middleware.PolicySettings
|
s3middleware.PolicySettings
|
||||||
s3middleware.MetricsSettings
|
s3middleware.MetricsSettings
|
||||||
|
s3middleware.HttpLogSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
type FrostFSID interface {
|
type FrostFSID interface {
|
||||||
|
@ -129,6 +130,7 @@ type Config struct {
|
||||||
func NewRouter(cfg Config) *chi.Mux {
|
func NewRouter(cfg Config) *chi.Mux {
|
||||||
api := chi.NewRouter()
|
api := chi.NewRouter()
|
||||||
api.Use(
|
api.Use(
|
||||||
|
s3middleware.LogHttp(cfg.Log, cfg.MiddlewareSettings),
|
||||||
s3middleware.Request(cfg.Log, cfg.MiddlewareSettings),
|
s3middleware.Request(cfg.Log, cfg.MiddlewareSettings),
|
||||||
middleware.ThrottleWithOpts(cfg.Throttle),
|
middleware.ThrottleWithOpts(cfg.Throttle),
|
||||||
middleware.Recoverer,
|
middleware.Recoverer,
|
||||||
|
|
|
@ -83,6 +83,8 @@ type (
|
||||||
|
|
||||||
appSettings struct {
|
appSettings struct {
|
||||||
logLevel zap.AtomicLevel
|
logLevel zap.AtomicLevel
|
||||||
|
httpLoggingEnabled bool
|
||||||
|
maxHttpLogBody int64
|
||||||
maxClient maxClientsConfig
|
maxClient maxClientsConfig
|
||||||
defaultMaxAge int
|
defaultMaxAge int
|
||||||
reconnectInterval time.Duration
|
reconnectInterval time.Duration
|
||||||
|
@ -193,6 +195,8 @@ func newAppSettings(log *Logger, v *viper.Viper) *appSettings {
|
||||||
defaultMaxAge: fetchDefaultMaxAge(v, log.logger),
|
defaultMaxAge: fetchDefaultMaxAge(v, log.logger),
|
||||||
reconnectInterval: fetchReconnectInterval(v),
|
reconnectInterval: fetchReconnectInterval(v),
|
||||||
frostfsidValidation: v.GetBool(cfgFrostfsIDValidationEnabled),
|
frostfsidValidation: v.GetBool(cfgFrostfsIDValidationEnabled),
|
||||||
|
httpLoggingEnabled: v.GetBool(cfgLoggerRequestEnabled),
|
||||||
|
maxHttpLogBody: v.GetInt64(cfgLoggerRequestMaxBody),
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.resolveZoneList = v.GetStringSlice(cfgResolveBucketAllow)
|
settings.resolveZoneList = v.GetStringSlice(cfgResolveBucketAllow)
|
||||||
|
@ -434,6 +438,18 @@ func (s *appSettings) RetryStrategy() handler.RetryStrategy {
|
||||||
return s.retryStrategy
|
return s.retryStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *appSettings) GetMaxLogBody() int64 {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
return s.maxHttpLogBody
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *appSettings) GetHttpLogEnabled() bool {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
return s.httpLoggingEnabled
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) initAPI() {
|
func (a *App) initAPI() {
|
||||||
a.initLayer()
|
a.initLayer()
|
||||||
a.initHandler()
|
a.initHandler()
|
||||||
|
|
|
@ -75,6 +75,9 @@ const ( // Settings.
|
||||||
cfgLoggerLevel = "logger.level"
|
cfgLoggerLevel = "logger.level"
|
||||||
cfgLoggerDestination = "logger.destination"
|
cfgLoggerDestination = "logger.destination"
|
||||||
|
|
||||||
|
cfgLoggerRequestEnabled = "http_logging.enabled"
|
||||||
|
cfgLoggerRequestMaxBody = "http_logging.max_body"
|
||||||
|
|
||||||
// Wallet.
|
// Wallet.
|
||||||
cfgWalletPath = "wallet.path"
|
cfgWalletPath = "wallet.path"
|
||||||
cfgWalletAddress = "wallet.address"
|
cfgWalletAddress = "wallet.address"
|
||||||
|
@ -715,6 +718,10 @@ func newSettings() *viper.Viper {
|
||||||
v.SetDefault(cfgLoggerLevel, "debug")
|
v.SetDefault(cfgLoggerLevel, "debug")
|
||||||
v.SetDefault(cfgLoggerDestination, "stdout")
|
v.SetDefault(cfgLoggerDestination, "stdout")
|
||||||
|
|
||||||
|
// http logger
|
||||||
|
v.SetDefault(cfgLoggerRequestEnabled, false)
|
||||||
|
v.SetDefault(cfgLoggerRequestMaxBody, 1024)
|
||||||
|
|
||||||
// pool:
|
// pool:
|
||||||
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
|
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)
|
||||||
v.SetDefault(cfgStreamTimeout, defaultStreamTimeout)
|
v.SetDefault(cfgStreamTimeout, defaultStreamTimeout)
|
||||||
|
|
|
@ -47,6 +47,12 @@ logger:
|
||||||
level: debug
|
level: debug
|
||||||
destination: stdout
|
destination: stdout
|
||||||
|
|
||||||
|
# log http request data (URI, headers, query, etc)
|
||||||
|
http_logging:
|
||||||
|
enabled: false
|
||||||
|
# max body size to log
|
||||||
|
max_body: 1024
|
||||||
|
|
||||||
# RPC endpoint and order of resolving of bucket names
|
# RPC endpoint and order of resolving of bucket names
|
||||||
rpc_endpoint: http://morph-chain.frostfs.devenv:30333
|
rpc_endpoint: http://morph-chain.frostfs.devenv:30333
|
||||||
resolve_order:
|
resolve_order:
|
||||||
|
|
|
@ -100,6 +100,8 @@ const (
|
||||||
FailedToPassAuthentication = "failed to pass authentication" // Error in ../../api/middleware/auth.go
|
FailedToPassAuthentication = "failed to pass authentication" // Error in ../../api/middleware/auth.go
|
||||||
FailedToResolveCID = "failed to resolve CID" // Debug in ../../api/middleware/metrics.go
|
FailedToResolveCID = "failed to resolve CID" // Debug in ../../api/middleware/metrics.go
|
||||||
RequestStart = "request start" // Info in ../../api/middleware/reqinfo.go
|
RequestStart = "request start" // Info in ../../api/middleware/reqinfo.go
|
||||||
|
|
||||||
|
RequestHTTP = "http request"
|
||||||
FailedToUnescapeObjectName = "failed to unescape object name" // Warn in ../../api/middleware/reqinfo.go
|
FailedToUnescapeObjectName = "failed to unescape object name" // Warn in ../../api/middleware/reqinfo.go
|
||||||
InvalidDefaultMaxAge = "invalid defaultMaxAge" // Fatal in ../../cmd/s3-gw/app_settings.go
|
InvalidDefaultMaxAge = "invalid defaultMaxAge" // Fatal in ../../cmd/s3-gw/app_settings.go
|
||||||
CantShutDownService = "can't shut down service" // Panic in ../../cmd/s3-gw/service.go
|
CantShutDownService = "can't shut down service" // Panic in ../../cmd/s3-gw/service.go
|
||||||
|
|
Loading…
Reference in a new issue