[#369] Add response logging
All checks were successful
/ DCO (pull_request) Successful in 45s
/ Builds (1.20) (pull_request) Successful in 1m36s
/ Builds (1.21) (pull_request) Successful in 1m29s
/ Vulncheck (pull_request) Successful in 2m29s
/ Lint (pull_request) Successful in 2m45s
/ Tests (1.20) (pull_request) Successful in 1m45s
/ Tests (1.21) (pull_request) Successful in 1m42s

Signed-off-by: Nikita Zinkevich <n.zinkevich@yadro.com>
This commit is contained in:
Nikita Zinkevich 2024-07-24 11:38:32 +03:00
parent d66d5e7d3f
commit e530031c10
6 changed files with 49 additions and 15 deletions

View file

@ -22,6 +22,7 @@ type LogHTTPConfig struct {
MaxLogSize int MaxLogSize int
OutputPath string OutputPath string
UseGzip bool UseGzip bool
LogResponse bool
} }
type fileLogger struct { type fileLogger struct {
@ -97,6 +98,23 @@ func ReloadFileLogger(conf *LogHTTPConfig) error {
return nil return nil
} }
// responseReadWriter helps read http response body.
type responseReadWriter struct {
http.ResponseWriter
response *bytes.Buffer
}
func (ww *responseReadWriter) Write(data []byte) (int, error) {
ww.response.Write(data)
return ww.ResponseWriter.Write(data)
}
func (ww *responseReadWriter) Flush() {
if f, ok := ww.ResponseWriter.(http.Flusher); ok {
f.Flush()
}
}
// LogHTTP logs http parameters from s3 request. // LogHTTP logs http parameters from s3 request.
func LogHTTP(l *zap.Logger, config *LogHTTPConfig) Func { func LogHTTP(l *zap.Logger, config *LogHTTPConfig) Func {
var err error var err error
@ -129,8 +147,18 @@ func LogHTTP(l *zap.Logger, config *LogHTTPConfig) Func {
l.Warn(logs.FailedToGetRequestBody, zap.Error(err)) l.Warn(logs.FailedToGetRequestBody, zap.Error(err))
} }
} }
httplog.Info(logs.RequestHTTP) if !config.LogResponse {
httplog.Info(logs.LogHTTP)
h.ServeHTTP(w, r) h.ServeHTTP(w, r)
} else {
var resp = bytes.Buffer{}
ww := &responseReadWriter{ResponseWriter: w, response: &resp}
h.ServeHTTP(ww, r)
if int64(resp.Len()) <= config.MaxBody<<20 {
httplog.With(zap.String("response", base64.StdEncoding.EncodeToString(resp.Bytes()))).
Info(logs.LogHTTP)
}
}
}) })
} }
} }

View file

@ -15,6 +15,7 @@ type LogHTTPConfig struct {
MaxLogSize int MaxLogSize int
OutputPath string OutputPath string
UseGzip bool UseGzip bool
LogResponse bool
} }
func LogHTTP(l *zap.Logger, _ *LogHTTPConfig) Func { func LogHTTP(l *zap.Logger, _ *LogHTTPConfig) Func {

View file

@ -573,6 +573,7 @@ func (s *appSettings) updateHTTPLoggingSettings(cfg *viper.Viper, log *zap.Logge
s.httpLogging.MaxLogSize = cfg.GetInt(cfgHTTPLoggingMaxLogSize) s.httpLogging.MaxLogSize = cfg.GetInt(cfgHTTPLoggingMaxLogSize)
s.httpLogging.OutputPath = cfg.GetString(cfgHTTPLoggingDestination) s.httpLogging.OutputPath = cfg.GetString(cfgHTTPLoggingDestination)
s.httpLogging.UseGzip = cfg.GetBool(cfgHTTPLoggingGzip) s.httpLogging.UseGzip = cfg.GetBool(cfgHTTPLoggingGzip)
s.httpLogging.LogResponse = cfg.GetBool(cfgHTTPLoggingLogResponse)
if err := s3middleware.ReloadFileLogger(s.httpLogging); err != nil { if err := s3middleware.ReloadFileLogger(s.httpLogging); err != nil {
log.Error(logs.FailedToReloadHTTPFileLogger, zap.Error(err)) log.Error(logs.FailedToReloadHTTPFileLogger, zap.Error(err))
} }

View file

@ -81,6 +81,7 @@ const ( // Settings.
cfgHTTPLoggingMaxLogSize = "http_logging.max_log_size" cfgHTTPLoggingMaxLogSize = "http_logging.max_log_size"
cfgHTTPLoggingDestination = "http_logging.destination" cfgHTTPLoggingDestination = "http_logging.destination"
cfgHTTPLoggingGzip = "http_logging.gzip" cfgHTTPLoggingGzip = "http_logging.gzip"
cfgHTTPLoggingLogResponse = "http_logging.log_response"
// Wallet. // Wallet.
cfgWalletPath = "wallet.path" cfgWalletPath = "wallet.path"
@ -728,6 +729,7 @@ func newSettings() *viper.Viper {
v.SetDefault(cfgHTTPLoggingMaxLogSize, 50) v.SetDefault(cfgHTTPLoggingMaxLogSize, 50)
v.SetDefault(cfgHTTPLoggingDestination, "stdout") v.SetDefault(cfgHTTPLoggingDestination, "stdout")
v.SetDefault(cfgHTTPLoggingGzip, false) v.SetDefault(cfgHTTPLoggingGzip, false)
v.SetDefault(cfgHTTPLoggingLogResponse, true)
// pool: // pool:
v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold) v.SetDefault(cfgPoolErrorThreshold, defaultPoolErrorThreshold)

View file

@ -384,6 +384,7 @@ http_logging:
max_log_size: 20 max_log_size: 20
gzip: true gzip: true
destination: stdout destination: stdout
log_response: true
``` ```
| Parameter | Type | SIGHUP reload | Default value | Description | | Parameter | Type | SIGHUP reload | Default value | Description |
@ -391,8 +392,9 @@ http_logging:
| `enabled` | bool | yes | false | Flag to enable the logger. | | `enabled` | bool | yes | false | Flag to enable the logger. |
| `max_body` | int | yes | 10 | Max size for request and response bodies that will be attached to the log (in megabytes). | | `max_body` | int | yes | 10 | Max size for request and response bodies that will be attached to the log (in megabytes). |
| `max_log_size` | int | yes | 50 | Log file size threshold (in megabytes) to be moved in backup file. After reaching threshold, initial filename is appended with timestamp. And new empty file with initial name is created. | | `max_log_size` | int | yes | 50 | Log file size threshold (in megabytes) to be moved in backup file. After reaching threshold, initial filename is appended with timestamp. And new empty file with initial name is created. |
| `gzip` | bool | yes | false | Whether to enable Gzip compression for backup log files. | | `gzip` | bool | yes | false | Whether to enable Gzip compression to backup log files. |
| `destination` | string | yes | stdout | Specify path for log output. Accepts log file path, or "stdout" and "stderr" reserved words to print in output streams. | | `destination` | string | yes | stdout | Specify path for log output. Accepts log file path, or "stdout" and "stderr" reserved words to print in output streams. File and folders are creating if necessary. |
| `log_response` | bool | yes | true | Whether to attach response body to the log. |
### `cache` section ### `cache` section

View file

@ -100,7 +100,7 @@ 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" // Info in ../../api/middleware/log_http.go LogHTTP = "http log" // Info in ../../api/middleware/log_http.go
FailedToInitializeHTTPLogger = "failed to initialize http logger" // Warn in ../../api/middleware/log_http.go FailedToInitializeHTTPLogger = "failed to initialize http logger" // Warn in ../../api/middleware/log_http.go
FailedToReloadHTTPFileLogger = "failed to reload http file logger" // Warn in ../../api/middleware/log_http.go FailedToReloadHTTPFileLogger = "failed to reload http file logger" // Warn in ../../api/middleware/log_http.go
FailedToGetRequestBody = "failed to get request body" // Warn in ../../api/middleware/log_http.go FailedToGetRequestBody = "failed to get request body" // Warn in ../../api/middleware/log_http.go