Skip to content

Commit 950bfd0

Browse files
committed
http: add opt-in slow query and request logging
Add two configuration knobs to help diagnose performance issues: [http] slow-request = "50ms" slow-query = "5ms" When set, HTTP requests and SQL queries exceeding the threshold are logged at WARN level with their duration. Both are disabled by default (zero value). Signed-off-by: Robin Jarry <robin@jarry.cc>
1 parent a4cad4d commit 950bfd0

3 files changed

Lines changed: 64 additions & 5 deletions

File tree

‎pkg/config/config.go‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ type IngressConfig struct {
3131
}
3232

3333
type HttpConfig struct {
34-
BaseURL string `help:"Base URL of the patchwork instance."`
35-
Listen string `help:"HTTP listen address." default:"127.0.0.1:8080"`
36-
CustomCSS string `help:"Path to a custom CSS file served after the built-in stylesheet." type:"existingfile"`
37-
NavHTML string `help:"Path to an HTML file whose content is inserted in the navigation bar." type:"existingfile"`
38-
FooterHTML string `help:"Path to an HTML file whose content is inserted in the footer." type:"existingfile"`
34+
BaseURL string `help:"Base URL of the patchwork instance."`
35+
Listen string `help:"HTTP listen address." default:"127.0.0.1:8080"`
36+
SlowRequest Duration `help:"Log HTTP requests slower than this threshold."`
37+
SlowQuery Duration `help:"Log SQL queries slower than this threshold."`
38+
CustomCSS string `help:"Path to a custom CSS file served after the built-in stylesheet." type:"existingfile"`
39+
NavHTML string `help:"Path to an HTML file whose content is inserted in the navigation bar." type:"existingfile"`
40+
FooterHTML string `help:"Path to an HTML file whose content is inserted in the footer." type:"existingfile"`
3941
}
4042

4143
type SMTPConfig struct {

‎pkg/db/queryhook.go‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Patchwork - automated patch tracking system
2+
// Copyright (C) The Patchwork Contributors (see CONTRIBUTORS)
3+
//
4+
// SPDX-License-Identifier: GPL-2.0-or-later
5+
6+
package db
7+
8+
import (
9+
"context"
10+
"time"
11+
12+
"github.com/uptrace/bun"
13+
14+
"github.com/getpatchwork/patchwork/pkg/log"
15+
)
16+
17+
type slowQueryHook struct {
18+
threshold time.Duration
19+
}
20+
21+
type startTimeKey struct{}
22+
23+
func NewSlowQueryHook(threshold time.Duration) bun.QueryHook {
24+
return &slowQueryHook{threshold: threshold}
25+
}
26+
27+
func (h *slowQueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent) context.Context {
28+
return context.WithValue(ctx, startTimeKey{}, time.Now())
29+
}
30+
31+
func (h *slowQueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
32+
dur := time.Since(event.StartTime)
33+
if dur >= h.threshold {
34+
log.Warnf("slow query (%s): %s", dur.Round(time.Millisecond), event.Query)
35+
}
36+
}

‎pkg/web/web.go‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"regexp"
2020
"strconv"
2121
"strings"
22+
"time"
2223

2324
"github.com/go-chi/chi/v5"
2425
"github.com/go-chi/chi/v5/middleware"
@@ -36,6 +37,12 @@ func NewRouter(cfg *config.Config, database *bun.DB, bus db.EventBus, version st
3637
r := chi.NewRouter()
3738
r.Use(middleware.Recoverer)
3839
r.Use(middleware.StripSlashes)
40+
if t := cfg.Http.SlowRequest.Duration(); t > 0 {
41+
r.Use(slowRequestLogger(t))
42+
}
43+
if t := cfg.Http.SlowQuery.Duration(); t > 0 {
44+
database.AddQueryHook(db.NewSlowQueryHook(t))
45+
}
3946
r.Use(db.Middleware(database, bus))
4047

4148
csrfKey := make([]byte, 32)
@@ -272,6 +279,20 @@ func isURL(s string) bool {
272279
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
273280
}
274281

282+
func slowRequestLogger(threshold time.Duration) func(http.Handler) http.Handler {
283+
return func(next http.Handler) http.Handler {
284+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
285+
start := time.Now()
286+
next.ServeHTTP(w, r)
287+
dur := time.Since(start)
288+
if dur >= threshold {
289+
log.Warnf("slow request (%s): %s %s",
290+
dur.Round(time.Millisecond), r.Method, r.URL.Path)
291+
}
292+
})
293+
}
294+
}
295+
275296
func readOptionalFile(path string) string {
276297
if path == "" {
277298
return ""

0 commit comments

Comments
 (0)