feat: request logging
This commit is contained in:
parent
cfafefa35a
commit
c85193ce0c
@ -70,6 +70,8 @@ func addServerFlags(flags *pflag.FlagSet) {
|
|||||||
flags.Bool("disable-preview-resize", false, "disable resize of image previews")
|
flags.Bool("disable-preview-resize", false, "disable resize of image previews")
|
||||||
flags.Bool("disable-exec", false, "disables Command Runner feature")
|
flags.Bool("disable-exec", false, "disables Command Runner feature")
|
||||||
flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers")
|
flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers")
|
||||||
|
flags.Bool("enable-request-log", false, "enable logging all the request")
|
||||||
|
flags.String("request-log-format", "%{user_name} %{ip} %{method} %{path} %{response_size}", "logging format for the request")
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
@ -262,6 +264,12 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
|
|||||||
_, disableExec := getParamB(flags, "disable-exec")
|
_, disableExec := getParamB(flags, "disable-exec")
|
||||||
server.EnableExec = !disableExec
|
server.EnableExec = !disableExec
|
||||||
|
|
||||||
|
_, enableRequestLog := getParamB(flags, "enable-request-log")
|
||||||
|
server.EnableRequestLog = enableRequestLog
|
||||||
|
|
||||||
|
requestLogFormat := getParam(flags, "request-log-format")
|
||||||
|
server.RequestLogFormat = requestLogFormat
|
||||||
|
|
||||||
if val, set := getParamB(flags, "token-expiration-time"); set {
|
if val, set := getParamB(flags, "token-expiration-time"); set {
|
||||||
server.TokenExpirationTime = val
|
server.TokenExpirationTime = val
|
||||||
}
|
}
|
||||||
|
|||||||
15
http/data.go
15
http/data.go
@ -4,6 +4,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tomasen/realip"
|
"github.com/tomasen/realip"
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ func (d *data) Check(path string) bool {
|
|||||||
|
|
||||||
func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
|
func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
begin := time.Now()
|
||||||
for k, v := range globalHeaders {
|
for k, v := range globalHeaders {
|
||||||
w.Header().Set(k, v)
|
w.Header().Set(k, v)
|
||||||
}
|
}
|
||||||
@ -59,12 +61,21 @@ func handle(fn handleFunc, prefix string, store *storage.Storage, server *settin
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
status, err := fn(w, r, &data{
|
d := data{
|
||||||
Runner: &runner.Runner{Enabled: server.EnableExec, Settings: settings},
|
Runner: &runner.Runner{Enabled: server.EnableExec, Settings: settings},
|
||||||
store: store,
|
store: store,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
server: server,
|
server: server,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
status, err := fn(w, r, &d)
|
||||||
|
if server.EnableRequestLog {
|
||||||
|
LogRequest(w, r, server.RequestLogFormat, RequestLog{
|
||||||
|
user: d.user,
|
||||||
|
status: status,
|
||||||
|
elapsed: time.Now().Sub(begin).Seconds(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if status >= 400 || err != nil {
|
if status >= 400 || err != nil {
|
||||||
clientIP := realip.FromRequest(r)
|
clientIP := realip.FromRequest(r)
|
||||||
|
|||||||
129
http/request_log.go
Normal file
129
http/request_log.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestLog struct {
|
||||||
|
user *users.User
|
||||||
|
ip string
|
||||||
|
time time.Time
|
||||||
|
request_size int64
|
||||||
|
response_size int64
|
||||||
|
path string
|
||||||
|
method string
|
||||||
|
status int
|
||||||
|
elapsed float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestLog) user_name() string {
|
||||||
|
if r.user != nil {
|
||||||
|
return r.user.Username
|
||||||
|
}
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestLog) user_id() string {
|
||||||
|
if r.user != nil {
|
||||||
|
return fmt.Sprintf("%d", r.user.ID)
|
||||||
|
}
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestLog) user_scope() string {
|
||||||
|
if r.user != nil {
|
||||||
|
return r.user.Scope
|
||||||
|
}
|
||||||
|
return "."
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RequestLog) time_string() string {
|
||||||
|
return r.time.Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogRequest(w http.ResponseWriter, r *http.Request, format string, log_ RequestLog) {
|
||||||
|
if log_.status == 0 {
|
||||||
|
log_.status = 200
|
||||||
|
}
|
||||||
|
log_.ip = getRealIp(r)
|
||||||
|
log_.time = time.Now()
|
||||||
|
log_.request_size = r.ContentLength
|
||||||
|
if log_.response_size == 0 {
|
||||||
|
log_.response_size = parseSize(w.Header().Get("Content-Length"))
|
||||||
|
}
|
||||||
|
log_.path = r.URL.Path
|
||||||
|
log_.method = r.Method
|
||||||
|
log.Println(formatLog(format, log_))
|
||||||
|
}
|
||||||
|
|
||||||
|
// support placeholders:
|
||||||
|
//
|
||||||
|
// %{user_name}
|
||||||
|
// %{user_id}
|
||||||
|
// %{user_scope}
|
||||||
|
// %{ip}
|
||||||
|
// %{time}
|
||||||
|
// %{request_size}
|
||||||
|
// %{response_size}: 0 for Transfer-Encoding=chunked
|
||||||
|
// %{path}
|
||||||
|
// %{method}
|
||||||
|
// %{status}
|
||||||
|
// %{elapsed}
|
||||||
|
func formatLog(format string, log RequestLog) string {
|
||||||
|
format = strings.ReplaceAll(format, "%{user_name}", log.user_name())
|
||||||
|
format = strings.ReplaceAll(format, "%{user_id}", log.user_id())
|
||||||
|
format = strings.ReplaceAll(format, "%{user_scope}", log.user_scope())
|
||||||
|
format = strings.ReplaceAll(format, "%{ip}", log.ip)
|
||||||
|
format = strings.ReplaceAll(format, "%{time}", log.time_string())
|
||||||
|
format = strings.ReplaceAll(format, "%{request_size}", int2string(log.request_size))
|
||||||
|
format = strings.ReplaceAll(format, "%{response_size}", int2string(log.response_size))
|
||||||
|
format = strings.ReplaceAll(format, "%{path}", log.path)
|
||||||
|
format = strings.ReplaceAll(format, "%{method}", log.method)
|
||||||
|
format = strings.ReplaceAll(format, "%{status}", int2string(log.status))
|
||||||
|
format = strings.ReplaceAll(format, "%{elapsed}", float2string(log.elapsed))
|
||||||
|
return format
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRealIp(r *http.Request) string {
|
||||||
|
remoteAddr := r.RemoteAddr
|
||||||
|
forwardedFor := parseFirstItem(r.Header.Get("X-Forwarded-For"))
|
||||||
|
realIp := parseFirstItem(r.Header.Get("X-Real-IP"))
|
||||||
|
if forwardedFor != "" {
|
||||||
|
return forwardedFor
|
||||||
|
}
|
||||||
|
if realIp != "" {
|
||||||
|
return realIp
|
||||||
|
}
|
||||||
|
return remoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFirstItem(s string) string {
|
||||||
|
items := strings.Split(s, ",")
|
||||||
|
if len(items) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return items[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSize(d string) int64 {
|
||||||
|
val, err := strconv.ParseInt(d, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func int2string(val any) string {
|
||||||
|
return fmt.Sprintf("%d", val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func float2string(val float64) string {
|
||||||
|
return fmt.Sprintf("%f", val)
|
||||||
|
}
|
||||||
@ -50,6 +50,8 @@ type Server struct {
|
|||||||
TypeDetectionByHeader bool `json:"typeDetectionByHeader"`
|
TypeDetectionByHeader bool `json:"typeDetectionByHeader"`
|
||||||
AuthHook string `json:"authHook"`
|
AuthHook string `json:"authHook"`
|
||||||
TokenExpirationTime string `json:"tokenExpirationTime"`
|
TokenExpirationTime string `json:"tokenExpirationTime"`
|
||||||
|
EnableRequestLog bool `json:"enableRequestLog"`
|
||||||
|
RequestLogFormat string `json:"requestLogFormat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean cleans any variables that might need cleaning.
|
// Clean cleans any variables that might need cleaning.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user