@@ -23,15 +23,34 @@ const databaseConnectRetries = 5
23
23
24
24
var regParseSQLName = regexp .MustCompile (`\-\-\s*name:\s+(\S+)` )
25
25
26
- func New (ctx context.Context , dsn string , log logrus.FieldLogger ) (* pgxpool.Pool , error ) {
27
- conn , err := NewPool (ctx , dsn , log , true )
26
+ type settings struct {
27
+ slowQueryLogger time.Duration
28
+ }
29
+
30
+ type OptFunc func (* settings )
31
+
32
+ // WithSlowQueryLogger enables slow query logging
33
+ // This exposes attributes of the slow query to the logger, so it should not be used in production
34
+ func WithSlowQueryLogger (d time.Duration ) OptFunc {
35
+ return func (s * settings ) {
36
+ s .slowQueryLogger = d
37
+ }
38
+ }
39
+
40
+ func New (ctx context.Context , dsn string , log logrus.FieldLogger , opts ... OptFunc ) (* pgxpool.Pool , error ) {
41
+ conn , err := NewPool (ctx , dsn , log , true , opts ... )
28
42
if err != nil {
29
43
return nil , fmt .Errorf ("failed to connect to the database: %w" , err )
30
44
}
31
45
return conn , nil
32
46
}
33
47
34
- func NewPool (ctx context.Context , dsn string , log logrus.FieldLogger , migrate bool ) (* pgxpool.Pool , error ) {
48
+ func NewPool (ctx context.Context , dsn string , log logrus.FieldLogger , migrate bool , opts ... OptFunc ) (* pgxpool.Pool , error ) {
49
+ settings := & settings {}
50
+ for _ , o := range opts {
51
+ o (settings )
52
+ }
53
+
35
54
if migrate {
36
55
if err := migrateDatabaseSchema ("pgx" , dsn , log ); err != nil {
37
56
return nil , err
@@ -55,6 +74,14 @@ func NewPool(ctx context.Context, dsn string, log logrus.FieldLogger, migrate bo
55
74
}),
56
75
)
57
76
77
+ if settings .slowQueryLogger > 0 {
78
+ config .ConnConfig .Tracer = & slowQueryLogger {
79
+ log : log ,
80
+ sub : config .ConnConfig .Tracer ,
81
+ duration : settings .slowQueryLogger ,
82
+ }
83
+ }
84
+
58
85
config .AfterConnect = func (ctx context.Context , conn * pgx.Conn ) error {
59
86
t , err := conn .LoadType (ctx , "slug" ) // slug type
60
87
if err != nil {
@@ -113,3 +140,45 @@ func migrateDatabaseSchema(driver, dsn string, log logrus.FieldLogger) error {
113
140
114
141
return goose .Up (db , "migrations" )
115
142
}
143
+
144
+ type slowQueryLogger struct {
145
+ log logrus.FieldLogger
146
+ sub pgx.QueryTracer
147
+ duration time.Duration
148
+ }
149
+
150
+ type sqlCtx int
151
+
152
+ const sqlCtxKey sqlCtx = 0
153
+
154
+ type bucket struct {
155
+ pgx.TraceQueryStartData
156
+ start time.Time
157
+ }
158
+
159
+ func (s * slowQueryLogger ) TraceQueryStart (ctx context.Context , conn * pgx.Conn , data pgx.TraceQueryStartData ) context.Context {
160
+ ctx = s .sub .TraceQueryStart (ctx , conn , data )
161
+ return context .WithValue (ctx , sqlCtxKey , & bucket {
162
+ TraceQueryStartData : data ,
163
+ start : time .Now (),
164
+ })
165
+ }
166
+
167
+ func (s * slowQueryLogger ) TraceQueryEnd (ctx context.Context , conn * pgx.Conn , data pgx.TraceQueryEndData ) {
168
+ s .sub .TraceQueryEnd (ctx , conn , data )
169
+
170
+ b , ok := ctx .Value (sqlCtxKey ).(* bucket )
171
+ if ! ok {
172
+ return
173
+ }
174
+
175
+ dur := time .Since (b .start )
176
+ if dur > s .duration {
177
+ s .log .WithFields (logrus.Fields {
178
+ "sql" : b .SQL ,
179
+ "args" : b .Args ,
180
+ "time" : dur ,
181
+ "err" : data .Err ,
182
+ }).Warn ("slow query" )
183
+ }
184
+ }
0 commit comments