Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔥 Add logger middleware time zone support #652

Merged
merged 5 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions middleware/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ type (
// Optional. Default: 15:04:05
TimeFormat string

// TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc
//
// Optional. Default: Local
TimeZone string

// Output is a writter where logs are written
//
// Default: os.Stderr
Expand Down Expand Up @@ -133,6 +138,7 @@ var LoggerConfigDefault = LoggerConfig{
Next: nil,
Format: "#${pid} - ${time} ${status} - ${latency} ${method} ${path}\n",
TimeFormat: "2006/01/02 15:04:05",
TimeZone: "Local",
Output: os.Stderr,
}

Expand All @@ -142,6 +148,7 @@ Logger allows the following config arguments in any order:
- Logger(next func(*fiber.Ctx) bool)
- Logger(output io.Writer)
- Logger(format string)
- Logger(timeZone string)
- Logger(timeFormat string)
- Logger(config LoggerConfig)
*/
Expand All @@ -157,6 +164,8 @@ func Logger(options ...interface{}) fiber.Handler {
case string:
if strings.Contains(opt, "${") {
config.Format = opt
} else if isTimeZone(opt) {
config.TimeZone = opt
} else {
config.TimeFormat = opt
}
Expand Down Expand Up @@ -184,6 +193,9 @@ func logger(config LoggerConfig) fiber.Handler {
if config.Format == "" {
config.Format = LoggerConfigDefault.Format
}
if config.TimeZone == "" {
config.TimeZone = LoggerConfigDefault.TimeZone
}
if config.TimeFormat == "" {
config.TimeFormat = LoggerConfigDefault.TimeFormat
}
Expand All @@ -203,14 +215,13 @@ func logger(config LoggerConfig) fiber.Handler {

var tmpl loggerTemplate
tmpl.new(config.Format, "${", "}")

timestamp := time.Now().Format(config.TimeFormat)
timestamp := nowTimeString(config.TimeZone, config.TimeFormat)
// Update date/time every second in a separate go routine
if strings.Contains(config.Format, "${time}") {
go func() {
for {
mutex.Lock()
timestamp = time.Now().Format(config.TimeFormat)
timestamp = nowTimeString(config.TimeZone, config.TimeFormat)
mutex.Unlock()
time.Sleep(500 * time.Millisecond)
}
Expand Down Expand Up @@ -354,6 +365,24 @@ func logger(config LoggerConfig) fiber.Handler {
}
}

func nowTimeString(timeZone, layout string) string {
// This is different from Golang's time package which returns UTC, and Local is better than it
if timeZone == "" {
timeZone = "UTC"
}
location, err := time.LoadLocation(timeZone)
if err != nil {
log.Fatalf("Logger: failed to load time zone: %e\n", err)
Fenny marked this conversation as resolved.
Show resolved Hide resolved
}
return time.Now().In(location).Format(layout)
}

// Use Golang's time package to determine whether the TimeZone is available
func isTimeZone(name string) bool {
_, err := time.LoadLocation(name)
return err == nil
}

// MIT License fasttemplate
// Copyright (c) 2015 Aliaksandr Valialkin
// https://github.com/valyala/fasttemplate/blob/master/LICENSE
Expand Down
9 changes: 9 additions & 0 deletions middleware/logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func main() {
// Pass a custom output
app.Use(middleware.Logger(os.Stdout))

// Pass a custom time zone
app.Use(middleware.Logger("UTC"))

// Pass a custom timeformat
app.Use(middleware.Logger("15:04:05"))

Expand All @@ -34,6 +37,7 @@ func main() {
app.Use(middleware.Logger(middleware.LoggerConfig{
Format: "${time} ${method} ${path}",
TimeFormat: "15:04:05",
TimeZone: "Asia/Chongqing",
Output: os.Stdout,
}))

Expand Down Expand Up @@ -87,6 +91,11 @@ type LoggerConfig struct {
// Optional. Default: 15:04:05
TimeFormat string

// TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc
//
// Optional. Default: Local
TimeZone string

// Output is a writter where logs are written
//
// Default: os.Stderr
Expand Down
52 changes: 52 additions & 0 deletions middleware/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func Test_Middleware_Logger_Options_And_WithConfig(t *testing.T) {
Logger("15:04:05"),
Logger("${time} ${method} ${path} - ${ip} - ${status} - ${latency}\n"),
Logger(LoggerConfig{Output: buf}),
Logger("UTC"),
}

for i, logger := range loggers {
Expand All @@ -128,10 +129,61 @@ func Test_Middleware_Logger_Options_And_WithConfig(t *testing.T) {
utils.AssertEqual(t, 51, len(res), fmt.Sprintf("Has length: %v, expected: %v, raw: %s", len(res), 51, res))
} else if i == 3 {
utils.AssertEqual(t, 48, len(res), fmt.Sprintf("Has length: %v, expected: %v, raw: %s", len(res), 48, res))
} else if i == 4 {
utils.AssertEqual(t, 48, len(res), fmt.Sprintf("Has length: %v, expected: %v, raw: %s", len(res), 48, res))
}
}
}

func Test_isTimeZone(t *testing.T) {
type args struct {
name string
}
tests := []struct {
name string
args args
want bool
}{
{
"Empty",
args{""},
true,
},
{
"Local",
args{name: "Local"},
true,
},
{
"UTC",
args{name: "UTC"},
true,
},
{
"America/New_York",
args{name: "America/New_York"},
true,
},
{
"Asia/Chongqing",
args{"Asia/Chongqing"},
true,
},
{
"Time format",
args{name: "2006-01-02 15:04:05"},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isTimeZone(tt.args.name); got != tt.want {
t.Errorf("isTimeZone() = %v, want %v", got, tt.want)
}
})
}
}

// go test -v -run=^$ -bench=Benchmark_Middleware_Logger -benchmem -count=4
func Benchmark_Middleware_Logger(b *testing.B) {

Expand Down