Skip to content

Commit

Permalink
add TimeLocation for logger time formating (#66)
Browse files Browse the repository at this point in the history
* add TimeLocation for logger time formating

* add tests and docs for time location
  • Loading branch information
phuslu authored Apr 22, 2024
1 parent 8102d66 commit a803cd8
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 20 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var DefaultLogger = Logger{
Writer: &IOWriter{os.Stderr},
}

// A Logger represents an active logging object that generates lines of JSON output to an io.Writer.
// Logger represents an active logging object that generates lines of JSON output to an io.Writer.
type Logger struct {
// Level defines log levels.
Level Level
Expand All @@ -59,13 +59,16 @@ type Logger struct {
// If Caller is negative, adds the full /path/to/file:line of the "caller" key.
Caller int

// TimeField defines the time filed name in output. It uses "time" in if empty.
// TimeField defines the time field name in output. It uses "time" in if empty.
TimeField string

// TimeFormat specifies the time format in output. It uses RFC3339 with millisecond if empty.
// If set with `TimeFormatUnix/TimeFormatUnixMs/TimeFormatUnixWithMs`, timestamps are formated.
// TimeFormat specifies the time format in output. Uses RFC3339 with millisecond if empty.
// If set to `TimeFormatUnix/TimeFormatUnixMs`, timestamps will be formatted.
TimeFormat string

// TimeLocation specifices that the location of TimeFormat used. Uses time.Local if empty.
TimeLocation *time.Location

// Writer specifies the writer of output. It uses a wrapped os.Stderr Writer in if empty.
Writer Writer
}
Expand Down
43 changes: 27 additions & 16 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ type Logger struct {
// If set with `TimeFormatUnix`, `TimeFormatUnixMs`, times are formated as UNIX timestamp.
TimeFormat string

// TimeUTC specifices that the timestamps should be UTC instead of system local time.
TimeUTC bool
// TimeLocation specifices that the location which TimeFormat used. It uses time.Local if empty.
TimeLocation *time.Location

// Context specifies an optional context of logger.
Context Context
Expand Down Expand Up @@ -441,9 +441,6 @@ const smallsString = "00010203040506070809" +
"90919293949596979899"

var timeNow = time.Now
var timeUtcNow = func() time.Time {
return time.Now().UTC()
}
var timeOffset, timeZone = func() (int64, string) {
now := timeNow()
_, n := now.Zone()
Expand All @@ -456,14 +453,6 @@ func (l *Logger) silent(level Level) bool {
}

func (l *Logger) header(level Level) *Entry {
headerTimeFunc := timeNow
headerTimeOffset := timeOffset

if l.TimeUTC {
headerTimeFunc = timeUtcNow
headerTimeOffset = 0
}

e := epool.Get().(*Entry)
e.buf = e.buf[:0]
e.Level = level
Expand All @@ -480,12 +469,29 @@ func (l *Logger) header(level Level) *Entry {
e.buf = append(e.buf, l.TimeField...)
e.buf = append(e.buf, '"', ':')
}
offset := timeOffset
if l.TimeLocation != nil {
if l.TimeLocation == time.UTC {
offset = 0
} else if l.TimeLocation == time.Local {
offset = timeOffset
} else {
format := l.TimeFormat
if format == "" {
format = "2006-01-02T15:04:05.999Z07:00"
}
e.buf = append(e.buf, '"')
e.buf = timeNow().In(l.TimeLocation).AppendFormat(e.buf, format)
e.buf = append(e.buf, '"')
goto headerlevel
}
}
switch l.TimeFormat {
case "":
sec, nsec, _ := now()
var tmp [32]byte
var buf []byte
if headerTimeOffset == 0 {
if offset == 0 {
// "2006-01-02T15:04:05.999Z"
tmp[25] = '"'
tmp[24] = 'Z'
Expand All @@ -502,7 +508,7 @@ func (l *Logger) header(level Level) *Entry {
buf = tmp[:31]
}
// date time
sec += 9223372028715321600 + headerTimeOffset // unixToInternal + internalToAbsolute + timeOffset
sec += 9223372028715321600 + offset // unixToInternal + internalToAbsolute + timeOffset
year, month, day, _ := absDate(uint64(sec), true)
hour, minute, second := absClock(uint64(sec))
// year
Expand Down Expand Up @@ -640,9 +646,14 @@ func (l *Logger) header(level Level) *Entry {
e.buf = append(e.buf, tmp[:]...)
default:
e.buf = append(e.buf, '"')
e.buf = headerTimeFunc().AppendFormat(e.buf, l.TimeFormat)
if l.TimeLocation == time.UTC {
e.buf = timeNow().UTC().AppendFormat(e.buf, l.TimeFormat)
} else {
e.buf = timeNow().AppendFormat(e.buf, l.TimeFormat)
}
e.buf = append(e.buf, '"')
}
headerlevel:
// level
switch level {
case DebugLevel:
Expand Down
23 changes: 23 additions & 0 deletions logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,29 @@ func TestLoggerTimeFormat(t *testing.T) {
logger.Info().Int64("timestamp_ms", timeNow().UnixNano()/1000000).Msg("this is rfc3339 time log entry")
}

func TestLoggerTimeLocation(t *testing.T) {
logger := Logger{}

for _, format := range []string{"", time.RFC822} {
logger.TimeFormat = format

logger.TimeLocation = nil
logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=nil log entry", logger.TimeFormat)

logger.TimeLocation = time.Local
logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=time.Local log entry", logger.TimeFormat)

logger.TimeLocation = time.UTC
logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=time.UTC log entry", logger.TimeFormat)

logger.TimeLocation, _ = time.LoadLocation("Asia/Singapore")
logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=Asia/Singapore log entry", logger.TimeFormat)

logger.TimeLocation, _ = time.LoadLocation("America/New_York")
logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=America/New_York log entry", logger.TimeFormat)
}
}

func TestLoggerTimeOffset(t *testing.T) {
logger := Logger{}

Expand Down

0 comments on commit a803cd8

Please sign in to comment.