mirror of https://github.com/gofiber/fiber.git
✨ Add customTags in logger middleware Config (#2188)
* ✨ Add customTags in logger middleware Config
* improve tags with parameter
* improve logger performance
* improve logger performance
* improve logger performance
* improve logger performance
Co-authored-by: René Werner <rene@gofiber.io>
pull/2224/head
parent
3157fb5f1c
commit
e8f8cb647b
|
@ -692,7 +692,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -691,7 +691,6 @@ For more articles, middlewares, examples or tools check our [awesome list](https
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -661,7 +661,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -661,7 +661,6 @@ Copyright (c) 2019-presente [Fenny](https://github.com/fenny) y [contribuyentes]
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -813,7 +813,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -663,7 +663,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -838,7 +838,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -664,7 +664,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -687,7 +687,6 @@ Copyright (c) 2019-ora [Fenny](https://github.com/fenny) e [Contributors](https:
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -666,7 +666,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -667,7 +667,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -667,7 +667,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -663,7 +663,6 @@ O logo oficial foi criado por [Vic Shóstak](https://github.com/koddr) e distrib
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -670,7 +670,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -732,7 +732,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -660,7 +660,6 @@ Telif (c) 2019-günümüz [Fenny](https://github.com/fenny) ve [katkıda bulunan
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -670,7 +670,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -663,7 +663,6 @@ Fiber 是一個以贊助維生的開源專案,像是: 網域、gitbook、netli
|
||||||
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE)
|
||||||
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE)
|
||||||
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE)
|
||||||
- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE)
|
|
||||||
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md)
|
||||||
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE)
|
||||||
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 Aliaksandr Valialkin
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
|
@ -1,437 +0,0 @@
|
||||||
// Package fasttemplate implements simple and fast template library.
|
|
||||||
//
|
|
||||||
// Fasttemplate is faster than text/template, strings.Replace
|
|
||||||
// and strings.Replacer.
|
|
||||||
//
|
|
||||||
// Fasttemplate ideally fits for fast and simple placeholders' substitutions.
|
|
||||||
package fasttemplate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ExecuteFunc calls f on each template tag (placeholder) occurrence.
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written to w.
|
|
||||||
//
|
|
||||||
// This function is optimized for constantly changing templates.
|
|
||||||
// Use Template.ExecuteFunc for frozen templates.
|
|
||||||
func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) {
|
|
||||||
s := unsafeString2Bytes(template)
|
|
||||||
a := unsafeString2Bytes(startTag)
|
|
||||||
b := unsafeString2Bytes(endTag)
|
|
||||||
|
|
||||||
var nn int64
|
|
||||||
var ni int
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
n := bytes.Index(s, a)
|
|
||||||
if n < 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ni, err = w.Write(s[:n])
|
|
||||||
nn += int64(ni)
|
|
||||||
if err != nil {
|
|
||||||
return nn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s[n+len(a):]
|
|
||||||
n = bytes.Index(s, b)
|
|
||||||
if n < 0 {
|
|
||||||
// cannot find end tag - just write it to the output.
|
|
||||||
ni, _ = w.Write(a)
|
|
||||||
nn += int64(ni)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
ni, err = f(w, unsafeBytes2String(s[:n]))
|
|
||||||
nn += int64(ni)
|
|
||||||
if err != nil {
|
|
||||||
return nn, err
|
|
||||||
}
|
|
||||||
s = s[n+len(b):]
|
|
||||||
}
|
|
||||||
ni, err = w.Write(s)
|
|
||||||
nn += int64(ni)
|
|
||||||
|
|
||||||
return nn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute substitutes template tags (placeholders) with the corresponding
|
|
||||||
// values from the map m and writes the result to the given writer w.
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written to w.
|
|
||||||
//
|
|
||||||
// This function is optimized for constantly changing templates.
|
|
||||||
// Use Template.Execute for frozen templates.
|
|
||||||
func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
|
|
||||||
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
|
|
||||||
// This can be used as a drop-in replacement for strings.Replacer
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written to w.
|
|
||||||
//
|
|
||||||
// This function is optimized for constantly changing templates.
|
|
||||||
// Use Template.ExecuteStd for frozen templates.
|
|
||||||
func ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
|
|
||||||
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
|
||||||
// and substitutes it with the data written to TagFunc's w.
|
|
||||||
//
|
|
||||||
// Returns the resulting string.
|
|
||||||
//
|
|
||||||
// This function is optimized for constantly changing templates.
|
|
||||||
// Use Template.ExecuteFuncString for frozen templates.
|
|
||||||
func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
|
|
||||||
s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("unexpected error: %s", err))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString
|
|
||||||
// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
|
|
||||||
// it just returns an empty string and the error f returned
|
|
||||||
func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
|
|
||||||
tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
|
|
||||||
if tagsCount == 0 {
|
|
||||||
return template, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
bb := byteBufferPool.Get()
|
|
||||||
if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
|
|
||||||
bb.Reset()
|
|
||||||
byteBufferPool.Put(bb)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
s := string(bb.B)
|
|
||||||
bb.Reset()
|
|
||||||
byteBufferPool.Put(bb)
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var byteBufferPool bytebufferpool.Pool
|
|
||||||
|
|
||||||
// ExecuteString substitutes template tags (placeholders) with the corresponding
|
|
||||||
// values from the map m and returns the result.
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// This function is optimized for constantly changing templates.
|
|
||||||
// Use Template.ExecuteString for frozen templates.
|
|
||||||
func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string {
|
|
||||||
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
|
|
||||||
// This can be used as a drop-in replacement for strings.Replacer
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// This function is optimized for constantly changing templates.
|
|
||||||
// Use Template.ExecuteStringStd for frozen templates.
|
|
||||||
func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string {
|
|
||||||
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Template implements simple template engine, which can be used for fast
|
|
||||||
// tags' (aka placeholders) substitution.
|
|
||||||
type Template struct {
|
|
||||||
template string
|
|
||||||
startTag string
|
|
||||||
endTag string
|
|
||||||
|
|
||||||
texts [][]byte
|
|
||||||
tags []string
|
|
||||||
byteBufferPool bytebufferpool.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// New parses the given template using the given startTag and endTag
|
|
||||||
// as tag start and tag end.
|
|
||||||
//
|
|
||||||
// The returned template can be executed by concurrently running goroutines
|
|
||||||
// using Execute* methods.
|
|
||||||
//
|
|
||||||
// New panics if the given template cannot be parsed. Use NewTemplate instead
|
|
||||||
// if template may contain errors.
|
|
||||||
func New(template, startTag, endTag string) *Template {
|
|
||||||
t, err := NewTemplate(template, startTag, endTag)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTemplate parses the given template using the given startTag and endTag
|
|
||||||
// as tag start and tag end.
|
|
||||||
//
|
|
||||||
// The returned template can be executed by concurrently running goroutines
|
|
||||||
// using Execute* methods.
|
|
||||||
func NewTemplate(template, startTag, endTag string) (*Template, error) {
|
|
||||||
var t Template
|
|
||||||
err := t.Reset(template, startTag, endTag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TagFunc can be used as a substitution value in the map passed to Execute*.
|
|
||||||
// Execute* functions pass tag (placeholder) name in 'tag' argument.
|
|
||||||
//
|
|
||||||
// TagFunc must be safe to call from concurrently running goroutines.
|
|
||||||
//
|
|
||||||
// TagFunc must write contents to w and return the number of bytes written.
|
|
||||||
type TagFunc func(w io.Writer, tag string) (int, error)
|
|
||||||
|
|
||||||
// Reset resets the template t to new one defined by
|
|
||||||
// template, startTag and endTag.
|
|
||||||
//
|
|
||||||
// Reset allows Template object re-use.
|
|
||||||
//
|
|
||||||
// Reset may be called only if no other goroutines call t methods at the moment.
|
|
||||||
func (t *Template) Reset(template, startTag, endTag string) error {
|
|
||||||
// Keep these vars in t, so GC won't collect them and won't break
|
|
||||||
// vars derived via unsafe*
|
|
||||||
t.template = template
|
|
||||||
t.startTag = startTag
|
|
||||||
t.endTag = endTag
|
|
||||||
t.texts = t.texts[:0]
|
|
||||||
t.tags = t.tags[:0]
|
|
||||||
|
|
||||||
if len(startTag) == 0 {
|
|
||||||
panic("startTag cannot be empty")
|
|
||||||
}
|
|
||||||
if len(endTag) == 0 {
|
|
||||||
panic("endTag cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
s := unsafeString2Bytes(template)
|
|
||||||
a := unsafeString2Bytes(startTag)
|
|
||||||
b := unsafeString2Bytes(endTag)
|
|
||||||
|
|
||||||
tagsCount := bytes.Count(s, a)
|
|
||||||
if tagsCount == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if tagsCount+1 > cap(t.texts) {
|
|
||||||
t.texts = make([][]byte, 0, tagsCount+1)
|
|
||||||
}
|
|
||||||
if tagsCount > cap(t.tags) {
|
|
||||||
t.tags = make([]string, 0, tagsCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
n := bytes.Index(s, a)
|
|
||||||
if n < 0 {
|
|
||||||
t.texts = append(t.texts, s)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t.texts = append(t.texts, s[:n])
|
|
||||||
|
|
||||||
s = s[n+len(a):]
|
|
||||||
n = bytes.Index(s, b)
|
|
||||||
if n < 0 {
|
|
||||||
return fmt.Errorf("cannot find end tag=%q in the template=%q starting from %q", endTag, template, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.tags = append(t.tags, unsafeBytes2String(s[:n]))
|
|
||||||
s = s[n+len(b):]
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteFunc calls f on each template tag (placeholder) occurrence.
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written to w.
|
|
||||||
//
|
|
||||||
// This function is optimized for frozen templates.
|
|
||||||
// Use ExecuteFunc for constantly changing templates.
|
|
||||||
func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) {
|
|
||||||
var nn int64
|
|
||||||
|
|
||||||
n := len(t.texts) - 1
|
|
||||||
if n == -1 {
|
|
||||||
ni, err := w.Write(unsafeString2Bytes(t.template))
|
|
||||||
return int64(ni), err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
ni, err := w.Write(t.texts[i])
|
|
||||||
nn += int64(ni)
|
|
||||||
if err != nil {
|
|
||||||
return nn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ni, err = f(w, t.tags[i])
|
|
||||||
nn += int64(ni)
|
|
||||||
if err != nil {
|
|
||||||
return nn, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ni, err := w.Write(t.texts[n])
|
|
||||||
nn += int64(ni)
|
|
||||||
return nn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute substitutes template tags (placeholders) with the corresponding
|
|
||||||
// values from the map m and writes the result to the given writer w.
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written to w.
|
|
||||||
func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) {
|
|
||||||
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
|
|
||||||
// This can be used as a drop-in replacement for strings.Replacer
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// Returns the number of bytes written to w.
|
|
||||||
func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) {
|
|
||||||
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
|
||||||
// and substitutes it with the data written to TagFunc's w.
|
|
||||||
//
|
|
||||||
// Returns the resulting string.
|
|
||||||
//
|
|
||||||
// This function is optimized for frozen templates.
|
|
||||||
// Use ExecuteFuncString for constantly changing templates.
|
|
||||||
func (t *Template) ExecuteFuncString(f TagFunc) string {
|
|
||||||
s, err := t.ExecuteFuncStringWithErr(f)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("unexpected error: %s", err))
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence
|
|
||||||
// and substitutes it with the data written to TagFunc's w.
|
|
||||||
//
|
|
||||||
// Returns the resulting string.
|
|
||||||
//
|
|
||||||
// This function is optimized for frozen templates.
|
|
||||||
// Use ExecuteFuncString for constantly changing templates.
|
|
||||||
func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) {
|
|
||||||
bb := t.byteBufferPool.Get()
|
|
||||||
if _, err := t.ExecuteFunc(bb, f); err != nil {
|
|
||||||
bb.Reset()
|
|
||||||
t.byteBufferPool.Put(bb)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
s := string(bb.Bytes())
|
|
||||||
bb.Reset()
|
|
||||||
t.byteBufferPool.Put(bb)
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteString substitutes template tags (placeholders) with the corresponding
|
|
||||||
// values from the map m and returns the result.
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// This function is optimized for frozen templates.
|
|
||||||
// Use ExecuteString for constantly changing templates.
|
|
||||||
func (t *Template) ExecuteString(m map[string]interface{}) string {
|
|
||||||
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
|
|
||||||
// This can be used as a drop-in replacement for strings.Replacer
|
|
||||||
//
|
|
||||||
// Substitution map m may contain values with the following types:
|
|
||||||
// - []byte - the fastest value type
|
|
||||||
// - string - convenient value type
|
|
||||||
// - TagFunc - flexible value type
|
|
||||||
//
|
|
||||||
// This function is optimized for frozen templates.
|
|
||||||
// Use ExecuteStringStd for constantly changing templates.
|
|
||||||
func (t *Template) ExecuteStringStd(m map[string]interface{}) string {
|
|
||||||
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
|
|
||||||
}
|
|
||||||
|
|
||||||
func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
|
|
||||||
v := m[tag]
|
|
||||||
if v == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
switch value := v.(type) {
|
|
||||||
case []byte:
|
|
||||||
return w.Write(value)
|
|
||||||
case string:
|
|
||||||
return w.Write([]byte(value))
|
|
||||||
case TagFunc:
|
|
||||||
return value(w, tag)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) {
|
|
||||||
v, ok := m[tag]
|
|
||||||
if !ok {
|
|
||||||
if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if _, err := w.Write(unsafeString2Bytes(tag)); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return len(startTag) + len(tag) + len(endTag), nil
|
|
||||||
}
|
|
||||||
if v == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
switch value := v.(type) {
|
|
||||||
case []byte:
|
|
||||||
return w.Write(value)
|
|
||||||
case string:
|
|
||||||
return w.Write([]byte(value))
|
|
||||||
case TagFunc:
|
|
||||||
return value(w, tag)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
//go:build !appengine
|
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
package fasttemplate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func unsafeBytes2String(b []byte) string {
|
|
||||||
return *(*string)(unsafe.Pointer(&b))
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsafeString2Bytes(s string) (b []byte) {
|
|
||||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
|
||||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
|
||||||
bh.Data = sh.Data
|
|
||||||
bh.Cap = sh.Len
|
|
||||||
bh.Len = sh.Len
|
|
||||||
return b
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
//go:build appengine
|
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package fasttemplate
|
|
||||||
|
|
||||||
func unsafeBytes2String(b []byte) string {
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unsafeString2Bytes(s string) []byte {
|
|
||||||
return []byte(s)
|
|
||||||
}
|
|
|
@ -11,6 +11,7 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r
|
||||||
- [Logging Request ID](#logging-request-id)
|
- [Logging Request ID](#logging-request-id)
|
||||||
- [Changing TimeZone & TimeFormat](#changing-timezone--timeformat)
|
- [Changing TimeZone & TimeFormat](#changing-timezone--timeformat)
|
||||||
- [Custom File Writer](#custom-file-writer)
|
- [Custom File Writer](#custom-file-writer)
|
||||||
|
- [Add Custom Tags](#add-custom-tags)
|
||||||
- [Config](#config)
|
- [Config](#config)
|
||||||
- [Default Config](#default-config-1)
|
- [Default Config](#default-config-1)
|
||||||
- [Constants](#constants)
|
- [Constants](#constants)
|
||||||
|
@ -75,6 +76,16 @@ app.Use(logger.New(logger.Config{
|
||||||
Output: file,
|
Output: file,
|
||||||
}))
|
}))
|
||||||
```
|
```
|
||||||
|
### Add Custom Tags
|
||||||
|
```go
|
||||||
|
app.Use(logger.New(logger.Config{
|
||||||
|
CustomTags: map[string]logger.LogFunc{
|
||||||
|
"custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString("it is a custom tag")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
## Config
|
## Config
|
||||||
```go
|
```go
|
||||||
|
@ -85,6 +96,11 @@ type Config struct {
|
||||||
// Optional. Default: nil
|
// Optional. Default: nil
|
||||||
Next func(c *fiber.Ctx) bool
|
Next func(c *fiber.Ctx) bool
|
||||||
|
|
||||||
|
// CustomTags defines the custom tag action
|
||||||
|
//
|
||||||
|
// Optional. Default: map[string]LogFunc{}
|
||||||
|
CustomTags map[string]LogFunc
|
||||||
|
|
||||||
// Format defines the logging tags
|
// Format defines the logging tags
|
||||||
//
|
//
|
||||||
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
|
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config defines the config for middleware.
|
// Config defines the config for middleware.
|
||||||
|
@ -22,6 +23,11 @@ type Config struct {
|
||||||
// Optional. Default: a function that does nothing.
|
// Optional. Default: a function that does nothing.
|
||||||
Done func(c *fiber.Ctx, logString []byte)
|
Done func(c *fiber.Ctx, logString []byte)
|
||||||
|
|
||||||
|
// tagFunctions defines the custom tag action
|
||||||
|
//
|
||||||
|
// Optional. Default: map[string]LogFunc
|
||||||
|
CustomTags map[string]LogFunc
|
||||||
|
|
||||||
// Format defines the logging tags
|
// Format defines the logging tags
|
||||||
//
|
//
|
||||||
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
|
// Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n
|
||||||
|
@ -52,6 +58,14 @@ type Config struct {
|
||||||
timeZoneLocation *time.Location
|
timeZoneLocation *time.Location
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
startTag = "${"
|
||||||
|
endTag = "}"
|
||||||
|
paramSeparator = ":"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error)
|
||||||
|
|
||||||
// ConfigDefault is the default config
|
// ConfigDefault is the default config
|
||||||
var ConfigDefault = Config{
|
var ConfigDefault = Config{
|
||||||
Next: nil,
|
Next: nil,
|
||||||
|
@ -65,11 +79,8 @@ var ConfigDefault = Config{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to check if the logger format is compatible for coloring
|
// Function to check if the logger format is compatible for coloring
|
||||||
func validCustomFormat(format string) bool {
|
func checkColorEnable(format string) bool {
|
||||||
validTemplates := []string{"${status}", "${method}"}
|
validTemplates := []string{"${status}", "${method}"}
|
||||||
if format == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, template := range validTemplates {
|
for _, template := range validTemplates {
|
||||||
if strings.Contains(format, template) {
|
if strings.Contains(format, template) {
|
||||||
return true
|
return true
|
||||||
|
@ -88,11 +99,6 @@ func configDefault(config ...Config) Config {
|
||||||
// Override default config
|
// Override default config
|
||||||
cfg := config[0]
|
cfg := config[0]
|
||||||
|
|
||||||
// Enable colors if no custom format or output is given
|
|
||||||
if validCustomFormat(cfg.Format) && cfg.Output == nil {
|
|
||||||
cfg.enableColors = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default values
|
// Set default values
|
||||||
if cfg.Next == nil {
|
if cfg.Next == nil {
|
||||||
cfg.Next = ConfigDefault.Next
|
cfg.Next = ConfigDefault.Next
|
||||||
|
@ -103,6 +109,7 @@ func configDefault(config ...Config) Config {
|
||||||
if cfg.Format == "" {
|
if cfg.Format == "" {
|
||||||
cfg.Format = ConfigDefault.Format
|
cfg.Format = ConfigDefault.Format
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.TimeZone == "" {
|
if cfg.TimeZone == "" {
|
||||||
cfg.TimeZone = ConfigDefault.TimeZone
|
cfg.TimeZone = ConfigDefault.TimeZone
|
||||||
}
|
}
|
||||||
|
@ -115,5 +122,11 @@ func configDefault(config ...Config) Config {
|
||||||
if cfg.Output == nil {
|
if cfg.Output == nil {
|
||||||
cfg.Output = ConfigDefault.Output
|
cfg.Output = ConfigDefault.Output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable colors if no custom format or output is given
|
||||||
|
if cfg.Output == nil && checkColorEnable(cfg.Format) {
|
||||||
|
cfg.enableColors = true
|
||||||
|
}
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DataPool = sync.Pool{New: func() interface{} { return new(Data) }}
|
||||||
|
|
||||||
|
// Data is a struct to define some variables to use in custom logger function.
|
||||||
|
type Data struct {
|
||||||
|
Pid string
|
||||||
|
ErrPaddingStr string
|
||||||
|
ChainErr error
|
||||||
|
Start time.Time
|
||||||
|
Stop time.Time
|
||||||
|
Timestamp atomic.Value
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -10,56 +9,13 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/utils"
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
|
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
|
||||||
"github.com/gofiber/fiber/v2/internal/fasttemplate"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger variables
|
|
||||||
const (
|
|
||||||
TagPid = "pid"
|
|
||||||
TagTime = "time"
|
|
||||||
TagReferer = "referer"
|
|
||||||
TagProtocol = "protocol"
|
|
||||||
TagPort = "port"
|
|
||||||
TagIP = "ip"
|
|
||||||
TagIPs = "ips"
|
|
||||||
TagHost = "host"
|
|
||||||
TagMethod = "method"
|
|
||||||
TagPath = "path"
|
|
||||||
TagURL = "url"
|
|
||||||
TagUA = "ua"
|
|
||||||
TagLatency = "latency"
|
|
||||||
TagStatus = "status"
|
|
||||||
TagResBody = "resBody"
|
|
||||||
TagReqHeaders = "reqHeaders"
|
|
||||||
TagQueryStringParams = "queryParams"
|
|
||||||
TagBody = "body"
|
|
||||||
TagBytesSent = "bytesSent"
|
|
||||||
TagBytesReceived = "bytesReceived"
|
|
||||||
TagRoute = "route"
|
|
||||||
TagError = "error"
|
|
||||||
// DEPRECATED: Use TagReqHeader instead
|
|
||||||
TagHeader = "header:"
|
|
||||||
TagReqHeader = "reqHeader:"
|
|
||||||
TagRespHeader = "respHeader:"
|
|
||||||
TagLocals = "locals:"
|
|
||||||
TagQuery = "query:"
|
|
||||||
TagForm = "form:"
|
|
||||||
TagCookie = "cookie:"
|
|
||||||
TagBlack = "black"
|
|
||||||
TagRed = "red"
|
|
||||||
TagGreen = "green"
|
|
||||||
TagYellow = "yellow"
|
|
||||||
TagBlue = "blue"
|
|
||||||
TagMagenta = "magenta"
|
|
||||||
TagCyan = "cyan"
|
|
||||||
TagWhite = "white"
|
|
||||||
TagReset = "reset"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a new middleware handler
|
// New creates a new middleware handler
|
||||||
|
@ -76,17 +32,14 @@ func New(config ...Config) fiber.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if format contains latency
|
// Check if format contains latency
|
||||||
cfg.enableLatency = strings.Contains(cfg.Format, "${latency}")
|
cfg.enableLatency = strings.Contains(cfg.Format, "${"+TagLatency+"}")
|
||||||
|
|
||||||
// Create template parser
|
|
||||||
tmpl := fasttemplate.New(cfg.Format, "${", "}")
|
|
||||||
|
|
||||||
// Create correct timeformat
|
|
||||||
var timestamp atomic.Value
|
var timestamp atomic.Value
|
||||||
|
// Create correct timeformat
|
||||||
timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat))
|
timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat))
|
||||||
|
|
||||||
// Update date/time every 500 milliseconds in a separate go routine
|
// Update date/time every 500 milliseconds in a separate go routine
|
||||||
if strings.Contains(cfg.Format, "${time}") {
|
if strings.Contains(cfg.Format, "${"+TagTime+"}") {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(cfg.TimeInterval)
|
time.Sleep(cfg.TimeInterval)
|
||||||
|
@ -114,6 +67,14 @@ func New(config ...Config) fiber.Handler {
|
||||||
}
|
}
|
||||||
errPadding := 15
|
errPadding := 15
|
||||||
errPaddingStr := strconv.Itoa(errPadding)
|
errPaddingStr := strconv.Itoa(errPadding)
|
||||||
|
|
||||||
|
// instead of analyzing the template inside(handler) each time, this is done once before
|
||||||
|
// and we create several slices of the same length with the functions to be executed and fixed parts.
|
||||||
|
templateChain, logFunChain, err := buildLogFuncChain(&cfg, createTagMap(&cfg))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Return new handler
|
// Return new handler
|
||||||
return func(c *fiber.Ctx) (err error) {
|
return func(c *fiber.Ctx) (err error) {
|
||||||
// Don't execute middleware if Next returns true
|
// Don't execute middleware if Next returns true
|
||||||
|
@ -140,16 +101,24 @@ func New(config ...Config) fiber.Handler {
|
||||||
errHandler = c.App().ErrorHandler
|
errHandler = c.App().ErrorHandler
|
||||||
})
|
})
|
||||||
|
|
||||||
var start, stop time.Time
|
// Logger data
|
||||||
|
data := DataPool.Get().(*Data)
|
||||||
|
// no need for a reset, as long as we always override everything
|
||||||
|
data.Pid = pid
|
||||||
|
data.ErrPaddingStr = errPaddingStr
|
||||||
|
data.Timestamp = timestamp
|
||||||
|
// put data back in the pool
|
||||||
|
defer DataPool.Put(data)
|
||||||
|
|
||||||
// Set latency start time
|
// Set latency start time
|
||||||
if cfg.enableLatency {
|
if cfg.enableLatency {
|
||||||
start = time.Now()
|
data.Start = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle request, store err for logging
|
// Handle request, store err for logging
|
||||||
chainErr := c.Next()
|
chainErr := c.Next()
|
||||||
|
|
||||||
|
data.ChainErr = chainErr
|
||||||
// Manually call error handler
|
// Manually call error handler
|
||||||
if chainErr != nil {
|
if chainErr != nil {
|
||||||
if err := errHandler(c, chainErr); err != nil {
|
if err := errHandler(c, chainErr); err != nil {
|
||||||
|
@ -159,7 +128,7 @@ func New(config ...Config) fiber.Handler {
|
||||||
|
|
||||||
// Set latency stop time
|
// Set latency stop time
|
||||||
if cfg.enableLatency {
|
if cfg.enableLatency {
|
||||||
stop = time.Now()
|
data.Stop = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get new buffer
|
// Get new buffer
|
||||||
|
@ -177,7 +146,7 @@ func New(config ...Config) fiber.Handler {
|
||||||
_, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n",
|
_, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n",
|
||||||
timestamp.Load().(string),
|
timestamp.Load().(string),
|
||||||
statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset,
|
statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset,
|
||||||
stop.Sub(start).Round(time.Millisecond),
|
data.Stop.Sub(data.Start).Round(time.Millisecond),
|
||||||
c.IP(),
|
c.IP(),
|
||||||
methodColor(c.Method(), colors), c.Method(), colors.Reset,
|
methodColor(c.Method(), colors), c.Method(), colors.Reset,
|
||||||
c.Path(),
|
c.Path(),
|
||||||
|
@ -196,114 +165,20 @@ func New(config ...Config) fiber.Handler {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop over template tags to replace it with the correct value
|
// Loop over template parts execute dynamic parts and add fixed parts to the buffer
|
||||||
_, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
|
for i, logFunc := range logFunChain {
|
||||||
switch tag {
|
if logFunc == nil {
|
||||||
case TagTime:
|
_, _ = buf.Write(templateChain[i])
|
||||||
return buf.WriteString(timestamp.Load().(string))
|
} else if templateChain[i] == nil {
|
||||||
case TagReferer:
|
_, err = logFunc(buf, c, data, "")
|
||||||
return buf.WriteString(c.Get(fiber.HeaderReferer))
|
} else {
|
||||||
case TagProtocol:
|
_, err = logFunc(buf, c, data, utils.UnsafeString(templateChain[i]))
|
||||||
return buf.WriteString(c.Protocol())
|
|
||||||
case TagPid:
|
|
||||||
return buf.WriteString(pid)
|
|
||||||
case TagPort:
|
|
||||||
return buf.WriteString(c.Port())
|
|
||||||
case TagIP:
|
|
||||||
return buf.WriteString(c.IP())
|
|
||||||
case TagIPs:
|
|
||||||
return buf.WriteString(c.Get(fiber.HeaderXForwardedFor))
|
|
||||||
case TagHost:
|
|
||||||
return buf.WriteString(c.Hostname())
|
|
||||||
case TagPath:
|
|
||||||
return buf.WriteString(c.Path())
|
|
||||||
case TagURL:
|
|
||||||
return buf.WriteString(c.OriginalURL())
|
|
||||||
case TagUA:
|
|
||||||
return buf.WriteString(c.Get(fiber.HeaderUserAgent))
|
|
||||||
case TagLatency:
|
|
||||||
return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond)))
|
|
||||||
case TagBody:
|
|
||||||
return buf.Write(c.Body())
|
|
||||||
case TagBytesReceived:
|
|
||||||
return appendInt(buf, len(c.Request().Body()))
|
|
||||||
case TagBytesSent:
|
|
||||||
return appendInt(buf, len(c.Response().Body()))
|
|
||||||
case TagRoute:
|
|
||||||
return buf.WriteString(c.Route().Path)
|
|
||||||
case TagStatus:
|
|
||||||
if cfg.enableColors {
|
|
||||||
return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
|
|
||||||
}
|
|
||||||
return appendInt(buf, c.Response().StatusCode())
|
|
||||||
case TagResBody:
|
|
||||||
return buf.Write(c.Response().Body())
|
|
||||||
case TagReqHeaders:
|
|
||||||
reqHeaders := make([]string, 0)
|
|
||||||
for k, v := range c.GetReqHeaders() {
|
|
||||||
reqHeaders = append(reqHeaders, k+"="+v)
|
|
||||||
}
|
|
||||||
return buf.Write([]byte(strings.Join(reqHeaders, "&")))
|
|
||||||
case TagQueryStringParams:
|
|
||||||
return buf.WriteString(c.Request().URI().QueryArgs().String())
|
|
||||||
case TagMethod:
|
|
||||||
if cfg.enableColors {
|
|
||||||
return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
|
|
||||||
}
|
|
||||||
return buf.WriteString(c.Method())
|
|
||||||
case TagBlack:
|
|
||||||
return buf.WriteString(colors.Black)
|
|
||||||
case TagRed:
|
|
||||||
return buf.WriteString(colors.Red)
|
|
||||||
case TagGreen:
|
|
||||||
return buf.WriteString(colors.Green)
|
|
||||||
case TagYellow:
|
|
||||||
return buf.WriteString(colors.Yellow)
|
|
||||||
case TagBlue:
|
|
||||||
return buf.WriteString(colors.Blue)
|
|
||||||
case TagMagenta:
|
|
||||||
return buf.WriteString(colors.Magenta)
|
|
||||||
case TagCyan:
|
|
||||||
return buf.WriteString(colors.Cyan)
|
|
||||||
case TagWhite:
|
|
||||||
return buf.WriteString(colors.White)
|
|
||||||
case TagReset:
|
|
||||||
return buf.WriteString(colors.Reset)
|
|
||||||
case TagError:
|
|
||||||
if chainErr != nil {
|
|
||||||
return buf.WriteString(chainErr.Error())
|
|
||||||
}
|
|
||||||
return buf.WriteString("-")
|
|
||||||
default:
|
|
||||||
// Check if we have a value tag i.e.: "reqHeader:x-key"
|
|
||||||
switch {
|
|
||||||
case strings.HasPrefix(tag, TagReqHeader):
|
|
||||||
return buf.WriteString(c.Get(tag[10:]))
|
|
||||||
case strings.HasPrefix(tag, TagHeader):
|
|
||||||
return buf.WriteString(c.Get(tag[7:]))
|
|
||||||
case strings.HasPrefix(tag, TagRespHeader):
|
|
||||||
return buf.WriteString(c.GetRespHeader(tag[11:]))
|
|
||||||
case strings.HasPrefix(tag, TagQuery):
|
|
||||||
return buf.WriteString(c.Query(tag[6:]))
|
|
||||||
case strings.HasPrefix(tag, TagForm):
|
|
||||||
return buf.WriteString(c.FormValue(tag[5:]))
|
|
||||||
case strings.HasPrefix(tag, TagCookie):
|
|
||||||
return buf.WriteString(c.Cookies(tag[7:]))
|
|
||||||
case strings.HasPrefix(tag, TagLocals):
|
|
||||||
switch v := c.Locals(tag[7:]).(type) {
|
|
||||||
case []byte:
|
|
||||||
return buf.Write(v)
|
|
||||||
case string:
|
|
||||||
return buf.WriteString(v)
|
|
||||||
case nil:
|
|
||||||
return 0, nil
|
|
||||||
default:
|
|
||||||
return buf.WriteString(fmt.Sprintf("%v", v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0, nil
|
if err != nil {
|
||||||
})
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Also write errors to the buffer
|
// Also write errors to the buffer
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = buf.WriteString(err.Error())
|
_, _ = buf.WriteString(err.Error())
|
||||||
|
|
|
@ -287,30 +287,59 @@ func Test_Logger_Data_Race(t *testing.T) {
|
||||||
|
|
||||||
// go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4
|
// go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4
|
||||||
func Benchmark_Logger(b *testing.B) {
|
func Benchmark_Logger(b *testing.B) {
|
||||||
app := fiber.New()
|
benchSetup := func(bb *testing.B, app *fiber.App) {
|
||||||
|
h := app.Handler()
|
||||||
|
|
||||||
app.Use(New(Config{
|
fctx := &fasthttp.RequestCtx{}
|
||||||
Format: "${bytesReceived} ${bytesSent} ${status}",
|
fctx.Request.Header.SetMethod("GET")
|
||||||
Output: io.Discard,
|
fctx.Request.SetRequestURI("/")
|
||||||
}))
|
|
||||||
app.Get("/", func(c *fiber.Ctx) error {
|
|
||||||
return c.SendString("Hello, World!")
|
|
||||||
})
|
|
||||||
|
|
||||||
h := app.Handler()
|
bb.ReportAllocs()
|
||||||
|
bb.ResetTimer()
|
||||||
|
|
||||||
fctx := &fasthttp.RequestCtx{}
|
for n := 0; n < bb.N; n++ {
|
||||||
fctx.Request.Header.SetMethod("GET")
|
h(fctx)
|
||||||
fctx.Request.SetRequestURI("/")
|
}
|
||||||
|
|
||||||
b.ReportAllocs()
|
utils.AssertEqual(bb, 200, fctx.Response.Header.StatusCode())
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
h(fctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode())
|
b.Run("Base", func(bb *testing.B) {
|
||||||
|
app := fiber.New()
|
||||||
|
app.Use(New(Config{
|
||||||
|
Format: "${bytesReceived} ${bytesSent} ${status}",
|
||||||
|
Output: io.Discard,
|
||||||
|
}))
|
||||||
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
|
c.Set("test", "test")
|
||||||
|
return c.SendString("Hello, World!")
|
||||||
|
})
|
||||||
|
benchSetup(bb, app)
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("DefaultFormat", func(bb *testing.B) {
|
||||||
|
app := fiber.New()
|
||||||
|
app.Use(New(Config{
|
||||||
|
Output: io.Discard,
|
||||||
|
}))
|
||||||
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
|
return c.SendString("Hello, World!")
|
||||||
|
})
|
||||||
|
benchSetup(bb, app)
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("WithTagParameter", func(bb *testing.B) {
|
||||||
|
app := fiber.New()
|
||||||
|
app.Use(New(Config{
|
||||||
|
Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}",
|
||||||
|
Output: io.Discard,
|
||||||
|
}))
|
||||||
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
|
c.Set("test", "test")
|
||||||
|
return c.SendString("Hello, World!")
|
||||||
|
})
|
||||||
|
benchSetup(bb, app)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// go test -run Test_Response_Header
|
// go test -run Test_Response_Header
|
||||||
|
@ -383,3 +412,32 @@ func Test_ReqHeader_Header(t *testing.T) {
|
||||||
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
|
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
|
||||||
utils.AssertEqual(t, "Hello fiber!", buf.String())
|
utils.AssertEqual(t, "Hello fiber!", buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go test -run Test_CustomTags
|
||||||
|
func Test_CustomTags(t *testing.T) {
|
||||||
|
customTag := "it is a custom tag"
|
||||||
|
|
||||||
|
buf := bytebufferpool.Get()
|
||||||
|
defer bytebufferpool.Put(buf)
|
||||||
|
|
||||||
|
app := fiber.New()
|
||||||
|
app.Use(New(Config{
|
||||||
|
Format: "${custom_tag}",
|
||||||
|
CustomTags: map[string]LogFunc{
|
||||||
|
"custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(customTag)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Output: buf,
|
||||||
|
}))
|
||||||
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
|
return c.SendString("Hello fiber!")
|
||||||
|
})
|
||||||
|
reqHeaderReq := httptest.NewRequest("GET", "/", nil)
|
||||||
|
reqHeaderReq.Header.Add("test", "Hello fiber!")
|
||||||
|
resp, err := app.Test(reqHeaderReq)
|
||||||
|
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode)
|
||||||
|
utils.AssertEqual(t, customTag, buf.String())
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/internal/bytebufferpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger variables
|
||||||
|
const (
|
||||||
|
TagPid = "pid"
|
||||||
|
TagTime = "time"
|
||||||
|
TagReferer = "referer"
|
||||||
|
TagProtocol = "protocol"
|
||||||
|
TagPort = "port"
|
||||||
|
TagIP = "ip"
|
||||||
|
TagIPs = "ips"
|
||||||
|
TagHost = "host"
|
||||||
|
TagMethod = "method"
|
||||||
|
TagPath = "path"
|
||||||
|
TagURL = "url"
|
||||||
|
TagUA = "ua"
|
||||||
|
TagLatency = "latency"
|
||||||
|
TagStatus = "status"
|
||||||
|
TagResBody = "resBody"
|
||||||
|
TagReqHeaders = "reqHeaders"
|
||||||
|
TagQueryStringParams = "queryParams"
|
||||||
|
TagBody = "body"
|
||||||
|
TagBytesSent = "bytesSent"
|
||||||
|
TagBytesReceived = "bytesReceived"
|
||||||
|
TagRoute = "route"
|
||||||
|
TagError = "error"
|
||||||
|
// DEPRECATED: Use TagReqHeader instead
|
||||||
|
TagHeader = "header:"
|
||||||
|
TagReqHeader = "reqHeader:"
|
||||||
|
TagRespHeader = "respHeader:"
|
||||||
|
TagLocals = "locals:"
|
||||||
|
TagQuery = "query:"
|
||||||
|
TagForm = "form:"
|
||||||
|
TagCookie = "cookie:"
|
||||||
|
TagBlack = "black"
|
||||||
|
TagRed = "red"
|
||||||
|
TagGreen = "green"
|
||||||
|
TagYellow = "yellow"
|
||||||
|
TagBlue = "blue"
|
||||||
|
TagMagenta = "magenta"
|
||||||
|
TagCyan = "cyan"
|
||||||
|
TagWhite = "white"
|
||||||
|
TagReset = "reset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// createTagMap function merged the default with the custom tags
|
||||||
|
func createTagMap(cfg *Config) map[string]LogFunc {
|
||||||
|
// Set default tags
|
||||||
|
tagFunctions := map[string]LogFunc{
|
||||||
|
TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Get(fiber.HeaderReferer))
|
||||||
|
},
|
||||||
|
TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Protocol())
|
||||||
|
},
|
||||||
|
TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Port())
|
||||||
|
},
|
||||||
|
TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.IP())
|
||||||
|
},
|
||||||
|
TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Get(fiber.HeaderXForwardedFor))
|
||||||
|
},
|
||||||
|
TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Hostname())
|
||||||
|
},
|
||||||
|
TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Path())
|
||||||
|
},
|
||||||
|
TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.OriginalURL())
|
||||||
|
},
|
||||||
|
TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Get(fiber.HeaderUserAgent))
|
||||||
|
},
|
||||||
|
TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.Write(c.Body())
|
||||||
|
},
|
||||||
|
TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return appendInt(buf, len(c.Request().Body()))
|
||||||
|
},
|
||||||
|
TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return appendInt(buf, len(c.Response().Body()))
|
||||||
|
},
|
||||||
|
TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Route().Path)
|
||||||
|
},
|
||||||
|
TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.Write(c.Response().Body())
|
||||||
|
},
|
||||||
|
TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
reqHeaders := make([]string, 0)
|
||||||
|
for k, v := range c.GetReqHeaders() {
|
||||||
|
reqHeaders = append(reqHeaders, k+"="+v)
|
||||||
|
}
|
||||||
|
return buf.Write([]byte(strings.Join(reqHeaders, "&")))
|
||||||
|
},
|
||||||
|
TagQueryStringParams: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Request().URI().QueryArgs().String())
|
||||||
|
},
|
||||||
|
|
||||||
|
TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Black)
|
||||||
|
},
|
||||||
|
TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Red)
|
||||||
|
},
|
||||||
|
TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Green)
|
||||||
|
},
|
||||||
|
TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Yellow)
|
||||||
|
},
|
||||||
|
TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Blue)
|
||||||
|
},
|
||||||
|
TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Magenta)
|
||||||
|
},
|
||||||
|
TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Cyan)
|
||||||
|
},
|
||||||
|
TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.White)
|
||||||
|
},
|
||||||
|
TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.App().Config().ColorScheme.Reset)
|
||||||
|
},
|
||||||
|
TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
if data.ChainErr != nil {
|
||||||
|
return buf.WriteString(data.ChainErr.Error())
|
||||||
|
}
|
||||||
|
return buf.WriteString("-")
|
||||||
|
},
|
||||||
|
TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Get(extraParam))
|
||||||
|
},
|
||||||
|
TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Get(extraParam))
|
||||||
|
},
|
||||||
|
TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.GetRespHeader(extraParam))
|
||||||
|
},
|
||||||
|
TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Query(extraParam))
|
||||||
|
},
|
||||||
|
TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.FormValue(extraParam))
|
||||||
|
},
|
||||||
|
TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(c.Cookies(extraParam))
|
||||||
|
},
|
||||||
|
TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
switch v := c.Locals(extraParam).(type) {
|
||||||
|
case []byte:
|
||||||
|
return buf.Write(v)
|
||||||
|
case string:
|
||||||
|
return buf.WriteString(v)
|
||||||
|
case nil:
|
||||||
|
return 0, nil
|
||||||
|
default:
|
||||||
|
return buf.WriteString(fmt.Sprintf("%v", v))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
if cfg.enableColors {
|
||||||
|
colors := c.App().Config().ColorScheme
|
||||||
|
return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset))
|
||||||
|
}
|
||||||
|
return appendInt(buf, c.Response().StatusCode())
|
||||||
|
},
|
||||||
|
TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
if cfg.enableColors {
|
||||||
|
colors := c.App().Config().ColorScheme
|
||||||
|
return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset))
|
||||||
|
}
|
||||||
|
return buf.WriteString(c.Method())
|
||||||
|
},
|
||||||
|
TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(data.Pid)
|
||||||
|
},
|
||||||
|
TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
latency := data.Stop.Sub(data.Start).Round(time.Millisecond)
|
||||||
|
return buf.WriteString(fmt.Sprintf("%7v", latency))
|
||||||
|
},
|
||||||
|
TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) {
|
||||||
|
return buf.WriteString(data.Timestamp.Load().(string))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// merge with custom tags from user
|
||||||
|
if cfg.CustomTags != nil {
|
||||||
|
for k, v := range cfg.CustomTags {
|
||||||
|
tagFunctions[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagFunctions
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildLogFuncChain analyzes the template and creates slices with the functions for execution and
|
||||||
|
// slices with the fixed parts of the template and the parameters
|
||||||
|
//
|
||||||
|
// fixParts contains the fixed parts of the template or parameters if a function is stored in the funcChain at this position
|
||||||
|
// funcChain contains for the parts which exist the functions for the dynamic parts
|
||||||
|
// funcChain and fixParts always have the same length and contain nil for the parts where no data is required in the chain,
|
||||||
|
// if a function exists for the part, a parameter for it can also exist in the fixParts slice
|
||||||
|
func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [][]byte, funcChain []LogFunc, err error) {
|
||||||
|
// process flow is copied from the fasttemplate flow https://github.com/valyala/fasttemplate/blob/2a2d1afadadf9715bfa19683cdaeac8347e5d9f9/template.go#L23-L62
|
||||||
|
templateB := utils.UnsafeBytes(cfg.Format)
|
||||||
|
startTagB := utils.UnsafeBytes(startTag)
|
||||||
|
endTagB := utils.UnsafeBytes(endTag)
|
||||||
|
paramSeparatorB := utils.UnsafeBytes(paramSeparator)
|
||||||
|
|
||||||
|
for {
|
||||||
|
currentPos := bytes.Index(templateB, startTagB)
|
||||||
|
if currentPos < 0 {
|
||||||
|
// no starting tag found in the existing template part
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// add fixed part
|
||||||
|
funcChain = append(funcChain, nil)
|
||||||
|
fixParts = append(fixParts, templateB[:currentPos])
|
||||||
|
|
||||||
|
templateB = templateB[currentPos+len(startTagB):]
|
||||||
|
currentPos = bytes.Index(templateB, endTagB)
|
||||||
|
if currentPos < 0 {
|
||||||
|
// cannot find end tag - just write it to the output.
|
||||||
|
funcChain = append(funcChain, nil)
|
||||||
|
fixParts = append(fixParts, startTagB)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// ## function block ##
|
||||||
|
// first check for tags with parameters
|
||||||
|
if index := bytes.Index(templateB[:currentPos], paramSeparatorB); index != -1 {
|
||||||
|
if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])]; ok {
|
||||||
|
funcChain = append(funcChain, logFunc)
|
||||||
|
// add param to the fixParts
|
||||||
|
fixParts = append(fixParts, templateB[index+1:currentPos])
|
||||||
|
} else {
|
||||||
|
return nil, nil, errors.New("No parameter found in \"" + utils.UnsafeString(templateB[:currentPos]) + "\"")
|
||||||
|
}
|
||||||
|
} else if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:currentPos])]; ok {
|
||||||
|
// add functions without parameter
|
||||||
|
funcChain = append(funcChain, logFunc)
|
||||||
|
fixParts = append(fixParts, nil)
|
||||||
|
}
|
||||||
|
// ## function block end ##
|
||||||
|
|
||||||
|
// reduce the template string
|
||||||
|
templateB = templateB[currentPos+len(endTagB):]
|
||||||
|
}
|
||||||
|
// set the rest
|
||||||
|
funcChain = append(funcChain, nil)
|
||||||
|
fixParts = append(fixParts, templateB)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue