goyave.dev/goyave/v5@v5.0.0-rc9.0.20240517145003-d3f977d0b9f3/database/log_test.go (about) 1 package database 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "regexp" 8 "strings" 9 "testing" 10 "time" 11 12 stdslog "log/slog" 13 14 "github.com/stretchr/testify/assert" 15 "goyave.dev/goyave/v5/slog" 16 ) 17 18 func TestLogger(t *testing.T) { 19 20 t.Run("New", func(t *testing.T) { 21 slogger := slog.New(slog.NewHandler(false, &bytes.Buffer{})) 22 f := func() *slog.Logger { return slogger } 23 l := NewLogger(f) 24 25 assert.Equal(t, slogger, l.slogger()) 26 assert.Equal(t, 200*time.Millisecond, l.SlowThreshold) 27 }) 28 29 t.Run("LogMode", func(t *testing.T) { 30 slogger := slog.New(slog.NewHandler(false, &bytes.Buffer{})) 31 l := NewLogger(func() *slog.Logger { return slogger }) 32 l2 := l.LogMode(0).(*Logger) 33 assert.Equal(t, slogger, l2.slogger()) 34 assert.Equal(t, 200*time.Millisecond, l.SlowThreshold) 35 }) 36 37 t.Run("Info", func(t *testing.T) { 38 39 t.Run("nil_slogger", func(t *testing.T) { 40 l := NewLogger(nil) 41 42 assert.NotPanics(t, func() { 43 l.Info(context.Background(), "nil slogger") 44 }) 45 }) 46 47 buf := bytes.NewBuffer(make([]byte, 0, 1024)) 48 slogger := slog.New(slog.NewHandler(false, buf)) 49 l := NewLogger(func() *slog.Logger { return slogger }) 50 51 l.Info(context.Background(), "message %d", 1) 52 53 assert.Regexp(t, regexp.MustCompile(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"INFO","source":{"function":".+","file":".+","line":\d+},"msg":"message 1"}\n`), buf.String()) 54 }) 55 56 t.Run("Warn", func(t *testing.T) { 57 58 t.Run("nil_slogger", func(t *testing.T) { 59 l := NewLogger(nil) 60 61 assert.NotPanics(t, func() { 62 l.Warn(context.Background(), "nil slogger") 63 }) 64 }) 65 66 buf := bytes.NewBuffer(make([]byte, 0, 1024)) 67 slogger := slog.New(slog.NewHandler(false, buf)) 68 l := NewLogger(func() *slog.Logger { return slogger }) 69 70 l.Warn(context.Background(), "message %d", 1) 71 72 assert.Regexp(t, regexp.MustCompile(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"WARN","source":{"function":".+","file":".+","line":\d+},"msg":"message 1"}\n`), buf.String()) 73 }) 74 75 t.Run("Error", func(t *testing.T) { 76 77 t.Run("nil_slogger", func(t *testing.T) { 78 l := NewLogger(nil) 79 80 assert.NotPanics(t, func() { 81 l.Error(context.Background(), "nil slogger") 82 }) 83 }) 84 85 buf := bytes.NewBuffer(make([]byte, 0, 1024)) 86 slogger := slog.New(slog.NewHandler(false, buf)) 87 l := NewLogger(func() *slog.Logger { return slogger }) 88 89 l.Error(context.Background(), "message %d", 1) 90 91 assert.Regexp(t, regexp.MustCompile(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"ERROR","source":{"function":".+","file":".+","line":\d+},"msg":"message 1"}\n`), buf.String()) 92 }) 93 94 t.Run("Trace", func(t *testing.T) { 95 96 t.Run("nil_slogger", func(t *testing.T) { 97 l := NewLogger(nil) 98 99 assert.NotPanics(t, func() { 100 l.Trace(context.Background(), time.Now(), func() (sql string, rowsAffected int64) { 101 return "SELECT * FROM some_table", 4 102 }, nil) 103 }) 104 }) 105 106 cases := []struct { 107 want *regexp.Regexp 108 err error 109 begin time.Time 110 desc string 111 sql string 112 slowThreshold time.Duration 113 rowsAffected int64 114 level stdslog.Level 115 wantEmpty bool 116 }{ 117 { 118 desc: "debug", 119 begin: time.Now(), 120 sql: "SELECT * FROM some_table", 121 rowsAffected: 4, 122 err: nil, 123 level: stdslog.LevelDebug, 124 slowThreshold: -1, 125 want: regexp.MustCompile( 126 fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"DEBUG","msg":"%s"}\n`, 127 fmt.Sprintf(`%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Yellow, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))), 128 ), 129 ), 130 }, 131 { 132 desc: "debug_slow", 133 begin: time.Now().Add(-time.Second), 134 sql: "SELECT * FROM some_table", 135 rowsAffected: 4, 136 err: nil, 137 level: stdslog.LevelDebug, 138 slowThreshold: time.Millisecond * 200, 139 want: regexp.MustCompile( 140 fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"WARN","msg":"%s"}\n`, 141 fmt.Sprintf(`SLOW SQL >= 200ms\\n%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Reset+slog.Red, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))), 142 ), 143 ), 144 }, 145 { 146 desc: "debug_slow_disabled_threshold", 147 begin: time.Now().Add(-time.Second), 148 sql: "SELECT * FROM some_table", 149 rowsAffected: 4, 150 err: nil, 151 level: stdslog.LevelDebug, 152 slowThreshold: 0, 153 want: regexp.MustCompile( 154 fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"DEBUG","msg":"%s"}\n`, 155 fmt.Sprintf(`%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Yellow, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))), 156 ), 157 ), 158 }, 159 { 160 desc: "warn_slow", 161 begin: time.Now().Add(-time.Second), 162 sql: "SELECT * FROM some_table", 163 rowsAffected: 4, 164 err: nil, 165 level: stdslog.LevelWarn, 166 slowThreshold: time.Millisecond * 200, 167 want: regexp.MustCompile( 168 fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"WARN","msg":"%s"}\n`, 169 fmt.Sprintf(`SLOW SQL >= 200ms\\n%s\[\d+\.\d+ms\] %s\[rows:4\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Reset+slog.Red, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))), 170 ), 171 ), 172 }, 173 { 174 desc: "error_slow", 175 begin: time.Now().Add(-time.Second), 176 sql: "SELECT * FROM some_table", 177 rowsAffected: 4, 178 err: nil, 179 level: stdslog.LevelError, 180 slowThreshold: -1, 181 wantEmpty: true, 182 }, 183 { 184 desc: "error", 185 begin: time.Now().Add(-time.Second), 186 sql: "SELECT * FROM some_table", 187 rowsAffected: 0, 188 err: fmt.Errorf("no such table: some_table"), 189 level: stdslog.LevelDebug, 190 slowThreshold: -1, 191 want: regexp.MustCompile( 192 fmt.Sprintf(`{"time":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,9}((\+\d{2}:\d{2})|Z)?","level":"ERROR","msg":"%s"}\n`, 193 fmt.Sprintf(`no such table: some_table\\n%s\[\d+\.\d+ms\] %s\[rows:0\]%s SELECT \* FROM some_table`, regexp.QuoteMeta(strings.ReplaceAll(slog.Reset+slog.Yellow, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Blue, "\033", `\u001b`)), regexp.QuoteMeta(strings.ReplaceAll(slog.Reset, "\033", `\u001b`))), 194 ), 195 ), 196 }, 197 } 198 199 for _, c := range cases { 200 c := c 201 t.Run(c.desc, func(t *testing.T) { 202 buf := bytes.NewBuffer(make([]byte, 0, 1024)) 203 slogger := slog.New(stdslog.NewJSONHandler(buf, &stdslog.HandlerOptions{Level: c.level})) 204 l := NewLogger(func() *slog.Logger { return slogger }) 205 206 if c.slowThreshold > -1 { 207 l.SlowThreshold = c.slowThreshold 208 } 209 210 l.Trace(context.Background(), c.begin, func() (sql string, rowsAffected int64) { return c.sql, c.rowsAffected }, c.err) 211 212 if c.wantEmpty { 213 assert.Empty(t, buf.String()) 214 } else { 215 assert.Regexp(t, c.want, buf.String()) 216 } 217 }) 218 } 219 }) 220 }