1// Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2// Use of this source code is governed by a MIT style 3// license that can be found in the LICENSE file. 4 5package gin 6 7import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "net/http" 12 "testing" 13 "time" 14 15 "github.com/stretchr/testify/assert" 16) 17 18func init() { 19 SetMode(TestMode) 20} 21 22func TestLogger(t *testing.T) { 23 buffer := new(bytes.Buffer) 24 router := New() 25 router.Use(LoggerWithWriter(buffer)) 26 router.GET("/example", func(c *Context) {}) 27 router.POST("/example", func(c *Context) {}) 28 router.PUT("/example", func(c *Context) {}) 29 router.DELETE("/example", func(c *Context) {}) 30 router.PATCH("/example", func(c *Context) {}) 31 router.HEAD("/example", func(c *Context) {}) 32 router.OPTIONS("/example", func(c *Context) {}) 33 34 performRequest(router, "GET", "/example?a=100") 35 assert.Contains(t, buffer.String(), "200") 36 assert.Contains(t, buffer.String(), "GET") 37 assert.Contains(t, buffer.String(), "/example") 38 assert.Contains(t, buffer.String(), "a=100") 39 40 // I wrote these first (extending the above) but then realized they are more 41 // like integration tests because they test the whole logging process rather 42 // than individual functions. Im not sure where these should go. 43 buffer.Reset() 44 performRequest(router, "POST", "/example") 45 assert.Contains(t, buffer.String(), "200") 46 assert.Contains(t, buffer.String(), "POST") 47 assert.Contains(t, buffer.String(), "/example") 48 49 buffer.Reset() 50 performRequest(router, "PUT", "/example") 51 assert.Contains(t, buffer.String(), "200") 52 assert.Contains(t, buffer.String(), "PUT") 53 assert.Contains(t, buffer.String(), "/example") 54 55 buffer.Reset() 56 performRequest(router, "DELETE", "/example") 57 assert.Contains(t, buffer.String(), "200") 58 assert.Contains(t, buffer.String(), "DELETE") 59 assert.Contains(t, buffer.String(), "/example") 60 61 buffer.Reset() 62 performRequest(router, "PATCH", "/example") 63 assert.Contains(t, buffer.String(), "200") 64 assert.Contains(t, buffer.String(), "PATCH") 65 assert.Contains(t, buffer.String(), "/example") 66 67 buffer.Reset() 68 performRequest(router, "HEAD", "/example") 69 assert.Contains(t, buffer.String(), "200") 70 assert.Contains(t, buffer.String(), "HEAD") 71 assert.Contains(t, buffer.String(), "/example") 72 73 buffer.Reset() 74 performRequest(router, "OPTIONS", "/example") 75 assert.Contains(t, buffer.String(), "200") 76 assert.Contains(t, buffer.String(), "OPTIONS") 77 assert.Contains(t, buffer.String(), "/example") 78 79 buffer.Reset() 80 performRequest(router, "GET", "/notfound") 81 assert.Contains(t, buffer.String(), "404") 82 assert.Contains(t, buffer.String(), "GET") 83 assert.Contains(t, buffer.String(), "/notfound") 84} 85 86func TestLoggerWithConfig(t *testing.T) { 87 buffer := new(bytes.Buffer) 88 router := New() 89 router.Use(LoggerWithConfig(LoggerConfig{Output: buffer})) 90 router.GET("/example", func(c *Context) {}) 91 router.POST("/example", func(c *Context) {}) 92 router.PUT("/example", func(c *Context) {}) 93 router.DELETE("/example", func(c *Context) {}) 94 router.PATCH("/example", func(c *Context) {}) 95 router.HEAD("/example", func(c *Context) {}) 96 router.OPTIONS("/example", func(c *Context) {}) 97 98 performRequest(router, "GET", "/example?a=100") 99 assert.Contains(t, buffer.String(), "200") 100 assert.Contains(t, buffer.String(), "GET") 101 assert.Contains(t, buffer.String(), "/example") 102 assert.Contains(t, buffer.String(), "a=100") 103 104 // I wrote these first (extending the above) but then realized they are more 105 // like integration tests because they test the whole logging process rather 106 // than individual functions. Im not sure where these should go. 107 buffer.Reset() 108 performRequest(router, "POST", "/example") 109 assert.Contains(t, buffer.String(), "200") 110 assert.Contains(t, buffer.String(), "POST") 111 assert.Contains(t, buffer.String(), "/example") 112 113 buffer.Reset() 114 performRequest(router, "PUT", "/example") 115 assert.Contains(t, buffer.String(), "200") 116 assert.Contains(t, buffer.String(), "PUT") 117 assert.Contains(t, buffer.String(), "/example") 118 119 buffer.Reset() 120 performRequest(router, "DELETE", "/example") 121 assert.Contains(t, buffer.String(), "200") 122 assert.Contains(t, buffer.String(), "DELETE") 123 assert.Contains(t, buffer.String(), "/example") 124 125 buffer.Reset() 126 performRequest(router, "PATCH", "/example") 127 assert.Contains(t, buffer.String(), "200") 128 assert.Contains(t, buffer.String(), "PATCH") 129 assert.Contains(t, buffer.String(), "/example") 130 131 buffer.Reset() 132 performRequest(router, "HEAD", "/example") 133 assert.Contains(t, buffer.String(), "200") 134 assert.Contains(t, buffer.String(), "HEAD") 135 assert.Contains(t, buffer.String(), "/example") 136 137 buffer.Reset() 138 performRequest(router, "OPTIONS", "/example") 139 assert.Contains(t, buffer.String(), "200") 140 assert.Contains(t, buffer.String(), "OPTIONS") 141 assert.Contains(t, buffer.String(), "/example") 142 143 buffer.Reset() 144 performRequest(router, "GET", "/notfound") 145 assert.Contains(t, buffer.String(), "404") 146 assert.Contains(t, buffer.String(), "GET") 147 assert.Contains(t, buffer.String(), "/notfound") 148} 149 150func TestLoggerWithFormatter(t *testing.T) { 151 buffer := new(bytes.Buffer) 152 153 d := DefaultWriter 154 DefaultWriter = buffer 155 defer func() { 156 DefaultWriter = d 157 }() 158 159 router := New() 160 router.Use(LoggerWithFormatter(func(param LogFormatterParams) string { 161 return fmt.Sprintf("[FORMATTER TEST] %v | %3d | %13v | %15s | %-7s %#v\n%s", 162 param.TimeStamp.Format("2006/01/02 - 15:04:05"), 163 param.StatusCode, 164 param.Latency, 165 param.ClientIP, 166 param.Method, 167 param.Path, 168 param.ErrorMessage, 169 ) 170 })) 171 router.GET("/example", func(c *Context) {}) 172 performRequest(router, "GET", "/example?a=100") 173 174 // output test 175 assert.Contains(t, buffer.String(), "[FORMATTER TEST]") 176 assert.Contains(t, buffer.String(), "200") 177 assert.Contains(t, buffer.String(), "GET") 178 assert.Contains(t, buffer.String(), "/example") 179 assert.Contains(t, buffer.String(), "a=100") 180} 181 182func TestLoggerWithConfigFormatting(t *testing.T) { 183 var gotParam LogFormatterParams 184 var gotKeys map[string]interface{} 185 buffer := new(bytes.Buffer) 186 187 router := New() 188 router.Use(LoggerWithConfig(LoggerConfig{ 189 Output: buffer, 190 Formatter: func(param LogFormatterParams) string { 191 // for assert test 192 gotParam = param 193 194 return fmt.Sprintf("[FORMATTER TEST] %v | %3d | %13v | %15s | %-7s %s\n%s", 195 param.TimeStamp.Format("2006/01/02 - 15:04:05"), 196 param.StatusCode, 197 param.Latency, 198 param.ClientIP, 199 param.Method, 200 param.Path, 201 param.ErrorMessage, 202 ) 203 }, 204 })) 205 router.GET("/example", func(c *Context) { 206 // set dummy ClientIP 207 c.Request.Header.Set("X-Forwarded-For", "20.20.20.20") 208 gotKeys = c.Keys 209 }) 210 performRequest(router, "GET", "/example?a=100") 211 212 // output test 213 assert.Contains(t, buffer.String(), "[FORMATTER TEST]") 214 assert.Contains(t, buffer.String(), "200") 215 assert.Contains(t, buffer.String(), "GET") 216 assert.Contains(t, buffer.String(), "/example") 217 assert.Contains(t, buffer.String(), "a=100") 218 219 // LogFormatterParams test 220 assert.NotNil(t, gotParam.Request) 221 assert.NotEmpty(t, gotParam.TimeStamp) 222 assert.Equal(t, 200, gotParam.StatusCode) 223 assert.NotEmpty(t, gotParam.Latency) 224 assert.Equal(t, "20.20.20.20", gotParam.ClientIP) 225 assert.Equal(t, "GET", gotParam.Method) 226 assert.Equal(t, "/example?a=100", gotParam.Path) 227 assert.Empty(t, gotParam.ErrorMessage) 228 assert.Equal(t, gotKeys, gotParam.Keys) 229 230} 231 232func TestDefaultLogFormatter(t *testing.T) { 233 timeStamp := time.Unix(1544173902, 0).UTC() 234 235 termFalseParam := LogFormatterParams{ 236 TimeStamp: timeStamp, 237 StatusCode: 200, 238 Latency: time.Second * 5, 239 ClientIP: "20.20.20.20", 240 Method: "GET", 241 Path: "/", 242 ErrorMessage: "", 243 isTerm: false, 244 } 245 246 termTrueParam := LogFormatterParams{ 247 TimeStamp: timeStamp, 248 StatusCode: 200, 249 Latency: time.Second * 5, 250 ClientIP: "20.20.20.20", 251 Method: "GET", 252 Path: "/", 253 ErrorMessage: "", 254 isTerm: true, 255 } 256 termTrueLongDurationParam := LogFormatterParams{ 257 TimeStamp: timeStamp, 258 StatusCode: 200, 259 Latency: time.Millisecond * 9876543210, 260 ClientIP: "20.20.20.20", 261 Method: "GET", 262 Path: "/", 263 ErrorMessage: "", 264 isTerm: true, 265 } 266 267 termFalseLongDurationParam := LogFormatterParams{ 268 TimeStamp: timeStamp, 269 StatusCode: 200, 270 Latency: time.Millisecond * 9876543210, 271 ClientIP: "20.20.20.20", 272 Method: "GET", 273 Path: "/", 274 ErrorMessage: "", 275 isTerm: false, 276 } 277 278 assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 | 200 | 5s | 20.20.20.20 | GET \"/\"\n", defaultLogFormatter(termFalseParam)) 279 assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 | 200 | 2743h29m3s | 20.20.20.20 | GET \"/\"\n", defaultLogFormatter(termFalseLongDurationParam)) 280 281 assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 5s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m \"/\"\n", defaultLogFormatter(termTrueParam)) 282 assert.Equal(t, "[GIN] 2018/12/07 - 09:11:42 |\x1b[97;42m 200 \x1b[0m| 2743h29m3s | 20.20.20.20 |\x1b[97;44m GET \x1b[0m \"/\"\n", defaultLogFormatter(termTrueLongDurationParam)) 283 284} 285 286func TestColorForMethod(t *testing.T) { 287 colorForMethod := func(method string) string { 288 p := LogFormatterParams{ 289 Method: method, 290 } 291 return p.MethodColor() 292 } 293 294 assert.Equal(t, blue, colorForMethod("GET"), "get should be blue") 295 assert.Equal(t, cyan, colorForMethod("POST"), "post should be cyan") 296 assert.Equal(t, yellow, colorForMethod("PUT"), "put should be yellow") 297 assert.Equal(t, red, colorForMethod("DELETE"), "delete should be red") 298 assert.Equal(t, green, colorForMethod("PATCH"), "patch should be green") 299 assert.Equal(t, magenta, colorForMethod("HEAD"), "head should be magenta") 300 assert.Equal(t, white, colorForMethod("OPTIONS"), "options should be white") 301 assert.Equal(t, reset, colorForMethod("TRACE"), "trace is not defined and should be the reset color") 302} 303 304func TestColorForStatus(t *testing.T) { 305 colorForStatus := func(code int) string { 306 p := LogFormatterParams{ 307 StatusCode: code, 308 } 309 return p.StatusCodeColor() 310 } 311 312 assert.Equal(t, green, colorForStatus(http.StatusOK), "2xx should be green") 313 assert.Equal(t, white, colorForStatus(http.StatusMovedPermanently), "3xx should be white") 314 assert.Equal(t, yellow, colorForStatus(http.StatusNotFound), "4xx should be yellow") 315 assert.Equal(t, red, colorForStatus(2), "other things should be red") 316} 317 318func TestResetColor(t *testing.T) { 319 p := LogFormatterParams{} 320 assert.Equal(t, string([]byte{27, 91, 48, 109}), p.ResetColor()) 321} 322 323func TestIsOutputColor(t *testing.T) { 324 // test with isTerm flag true. 325 p := LogFormatterParams{ 326 isTerm: true, 327 } 328 329 consoleColorMode = autoColor 330 assert.Equal(t, true, p.IsOutputColor()) 331 332 ForceConsoleColor() 333 assert.Equal(t, true, p.IsOutputColor()) 334 335 DisableConsoleColor() 336 assert.Equal(t, false, p.IsOutputColor()) 337 338 // test with isTerm flag false. 339 p = LogFormatterParams{ 340 isTerm: false, 341 } 342 343 consoleColorMode = autoColor 344 assert.Equal(t, false, p.IsOutputColor()) 345 346 ForceConsoleColor() 347 assert.Equal(t, true, p.IsOutputColor()) 348 349 DisableConsoleColor() 350 assert.Equal(t, false, p.IsOutputColor()) 351 352 // reset console color mode. 353 consoleColorMode = autoColor 354} 355 356func TestErrorLogger(t *testing.T) { 357 router := New() 358 router.Use(ErrorLogger()) 359 router.GET("/error", func(c *Context) { 360 c.Error(errors.New("this is an error")) // nolint: errcheck 361 }) 362 router.GET("/abort", func(c *Context) { 363 c.AbortWithError(http.StatusUnauthorized, errors.New("no authorized")) // nolint: errcheck 364 }) 365 router.GET("/print", func(c *Context) { 366 c.Error(errors.New("this is an error")) // nolint: errcheck 367 c.String(http.StatusInternalServerError, "hola!") 368 }) 369 370 w := performRequest(router, "GET", "/error") 371 assert.Equal(t, http.StatusOK, w.Code) 372 assert.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String()) 373 374 w = performRequest(router, "GET", "/abort") 375 assert.Equal(t, http.StatusUnauthorized, w.Code) 376 assert.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String()) 377 378 w = performRequest(router, "GET", "/print") 379 assert.Equal(t, http.StatusInternalServerError, w.Code) 380 assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String()) 381} 382 383func TestLoggerWithWriterSkippingPaths(t *testing.T) { 384 buffer := new(bytes.Buffer) 385 router := New() 386 router.Use(LoggerWithWriter(buffer, "/skipped")) 387 router.GET("/logged", func(c *Context) {}) 388 router.GET("/skipped", func(c *Context) {}) 389 390 performRequest(router, "GET", "/logged") 391 assert.Contains(t, buffer.String(), "200") 392 393 buffer.Reset() 394 performRequest(router, "GET", "/skipped") 395 assert.Contains(t, buffer.String(), "") 396} 397 398func TestLoggerWithConfigSkippingPaths(t *testing.T) { 399 buffer := new(bytes.Buffer) 400 router := New() 401 router.Use(LoggerWithConfig(LoggerConfig{ 402 Output: buffer, 403 SkipPaths: []string{"/skipped"}, 404 })) 405 router.GET("/logged", func(c *Context) {}) 406 router.GET("/skipped", func(c *Context) {}) 407 408 performRequest(router, "GET", "/logged") 409 assert.Contains(t, buffer.String(), "200") 410 411 buffer.Reset() 412 performRequest(router, "GET", "/skipped") 413 assert.Contains(t, buffer.String(), "") 414} 415 416func TestDisableConsoleColor(t *testing.T) { 417 New() 418 assert.Equal(t, autoColor, consoleColorMode) 419 DisableConsoleColor() 420 assert.Equal(t, disableColor, consoleColorMode) 421 422 // reset console color mode. 423 consoleColorMode = autoColor 424} 425 426func TestForceConsoleColor(t *testing.T) { 427 New() 428 assert.Equal(t, autoColor, consoleColorMode) 429 ForceConsoleColor() 430 assert.Equal(t, forceColor, consoleColorMode) 431 432 // reset console color mode. 433 consoleColorMode = autoColor 434} 435