diff --git a/go.mod b/go.mod index 4d671ea7..b388237e 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/graph-gophers/dataloader/v7 v7.1.0 github.com/hypirion/go-filecache v0.0.0-20160810125507-e3e6ef6981f0 github.com/interline-io/transitland-lib v0.14.0-rc1.0.20231202005632-a9ea742322f7 - github.com/interline-io/transitland-mw v0.0.0-20231209013116-a1adf6a31e93 + github.com/interline-io/transitland-mw v0.0.0-20231209020231-3660286a28cd github.com/jellydator/ttlcache/v2 v2.11.1 github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.7 diff --git a/go.sum b/go.sum index 0717a583..0b89fb39 100644 --- a/go.sum +++ b/go.sum @@ -258,6 +258,8 @@ github.com/interline-io/transitland-lib v0.14.0-rc1.0.20231202005632-a9ea742322f github.com/interline-io/transitland-lib v0.14.0-rc1.0.20231202005632-a9ea742322f7/go.mod h1:UcfuCX6DyKt/yn5GECFn3jQ6NcZEjt5XyPjf8a3tXZ4= github.com/interline-io/transitland-mw v0.0.0-20231209013116-a1adf6a31e93 h1:qurYqyKQAGTtjNyAlGyrNGvZ9TFBcEwwo0X7Tq8XuFU= github.com/interline-io/transitland-mw v0.0.0-20231209013116-a1adf6a31e93/go.mod h1:N+53bL3yiFx3SoDMBJtC2dkmAZNBeRHP9safiAfvykw= +github.com/interline-io/transitland-mw v0.0.0-20231209020231-3660286a28cd h1:Id8Pd4MJOto/z3Tquy3ox996iwJY9gfSG/W24jaGqGQ= +github.com/interline-io/transitland-mw v0.0.0-20231209020231-3660286a28cd/go.mod h1:N+53bL3yiFx3SoDMBJtC2dkmAZNBeRHP9safiAfvykw= github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 44a9871c..00000000 --- a/server/server.go +++ /dev/null @@ -1,123 +0,0 @@ -package server - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "strconv" - "time" - - "github.com/go-redis/redis/v8" - "github.com/interline-io/transitland-lib/log" - "github.com/interline-io/transitland-mw/auth/authn" -) - -// log request and duration -func LoggingMiddleware(longQueryDuration int) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - t1 := time.Now() - userName := "" - if user := authn.ForContext(ctx); user != nil { - userName = user.ID() - } - // Get request body for logging if request is json and length under 20kb - var body []byte - if r.Header.Get("content-type") == "application/json" && r.ContentLength < 1024*20 { - body, _ = ioutil.ReadAll(r.Body) - r.Body = ioutil.NopCloser(bytes.NewBuffer(body)) - } - // Wrap context to get error code and errors - wr := wrapResponseWriter(w) - next.ServeHTTP(wr, r) - // Extra logging of request body if duration > 1s - durationMs := (time.Now().UnixNano() - t1.UnixNano()) / 1e6 - msg := log.Info(). - Int64("duration_ms", durationMs). - Str("method", r.Method). - Str("path", r.URL.EscapedPath()). - Str("query", r.URL.Query().Encode()). - Str("user", userName). - Int("status", wr.status) - // Add duration info - if durationMs > int64(longQueryDuration) { - // Verify it's valid json - msg = msg.Bool("long_query", true) - var x interface{} - if err := json.Unmarshal(body, &x); err == nil { - msg = msg.RawJSON("body", body) - } - } - // Get any GraphQL errors. We need to log these because the response - // code will always be 200. - // var gqlErrs []string - // if len(gqlErrs) > 0 { - // msg = msg.Strs("gql_errors", gqlErrs) - // } - msg.Msg("request") - }) - } -} - -func getRedisOpts(v string) (*redis.Options, error) { - a, err := url.Parse(v) - if err != nil { - return nil, err - } - if a.Scheme != "redis" { - return nil, errors.New("redis URL must begin with redis://") - } - port := a.Port() - if port == "" { - port = "6379" - } - addr := fmt.Sprintf("%s:%s", a.Hostname(), port) - dbNo := 0 - if len(a.Path) > 0 { - var err error - f := a.Path[1:len(a.Path)] - dbNo, err = strconv.Atoi(f) - if err != nil { - return nil, err - } - } - return &redis.Options{Addr: addr, DB: dbNo}, nil -} - -// https://blog.questionable.services/article/guide-logging-middleware-go/ -// responseWriter is a minimal wrapper for http.ResponseWriter that allows the -// written HTTP status code to be captured for logging. -type responseWriter struct { - status int - wroteHeader bool - http.ResponseWriter -} - -func wrapResponseWriter(w http.ResponseWriter) *responseWriter { - return &responseWriter{ResponseWriter: w} -} - -func (rw *responseWriter) Status() int { - return rw.status -} - -func (rw *responseWriter) WriteHeader(code int) { - if !rw.wroteHeader { - rw.status = code - rw.wroteHeader = true - } - rw.ResponseWriter.WriteHeader(code) -} - -func (rw *responseWriter) Write(response []byte) (int, error) { - if !rw.wroteHeader { - rw.status = http.StatusOK - rw.wroteHeader = true - } - return rw.ResponseWriter.Write(response) -} diff --git a/server/server_cmd.go b/server/server_cmd.go index c8334961..b1e1c439 100644 --- a/server/server_cmd.go +++ b/server/server_cmd.go @@ -6,12 +6,14 @@ import ( "flag" "fmt" "os/signal" + "strconv" "strings" "syscall" "time" "net/http" "net/http/pprof" + "net/url" "os" "github.com/go-chi/chi/middleware" @@ -23,6 +25,7 @@ import ( "github.com/interline-io/transitland-lib/tl" "github.com/interline-io/transitland-mw/auth/ancheck" "github.com/interline-io/transitland-mw/auth/azcheck" + "github.com/interline-io/transitland-mw/lmw" "github.com/interline-io/transitland-mw/meters" "github.com/interline-io/transitland-mw/metrics" "github.com/interline-io/transitland-server/config" @@ -252,7 +255,7 @@ func (cmd *Command) Run() error { } // Add logging middleware - must be after auth - root.Use(LoggingMiddleware(cmd.LongQueryDuration)) + root.Use(lmw.LoggingMiddleware(cmd.LongQueryDuration)) // Profiling if cmd.EnableProfiler { @@ -391,3 +394,28 @@ func (i *arrayFlags) Set(value string) error { *i = append(*i, value) return nil } + +func getRedisOpts(v string) (*redis.Options, error) { + a, err := url.Parse(v) + if err != nil { + return nil, err + } + if a.Scheme != "redis" { + return nil, errors.New("redis URL must begin with redis://") + } + port := a.Port() + if port == "" { + port = "6379" + } + addr := fmt.Sprintf("%s:%s", a.Hostname(), port) + dbNo := 0 + if len(a.Path) > 0 { + var err error + f := a.Path[1:len(a.Path)] + dbNo, err = strconv.Atoi(f) + if err != nil { + return nil, err + } + } + return &redis.Options{Addr: addr, DB: dbNo}, nil +}