forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request elastic#2083 from urso/enh/fmtstr-with-timestamps
Add timestamp formatting to EventStringFormatter
- Loading branch information
Showing
12 changed files
with
1,303 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
package dtfmt | ||
|
||
type builder struct { | ||
elements []element | ||
} | ||
|
||
func newBuilder() *builder { | ||
return &builder{} | ||
} | ||
|
||
func (b *builder) estimateSize() int { | ||
sz := 0 | ||
for _, e := range b.elements { | ||
sz += e.estimateSize() | ||
} | ||
return sz | ||
} | ||
|
||
func (b *builder) createConfig() (ctxConfig, error) { | ||
cfg := ctxConfig{} | ||
for _, e := range b.elements { | ||
if err := e.requires(&cfg); err != nil { | ||
return ctxConfig{}, err | ||
} | ||
} | ||
return cfg, nil | ||
} | ||
|
||
func (b *builder) compile() (prog, error) { | ||
p := prog{} | ||
|
||
for _, e := range b.elements { | ||
tmp, err := e.compile() | ||
if err != nil { | ||
return prog{}, err | ||
} | ||
|
||
p.p = append(p.p, tmp.p...) | ||
} | ||
return p, nil | ||
} | ||
|
||
func (b *builder) optimize() { | ||
if len(b.elements) == 0 { | ||
return | ||
} | ||
|
||
// combine rune/string literals | ||
el := b.elements[:1] | ||
for _, e := range b.elements[1:] { | ||
last := el[len(el)-1] | ||
if r, ok := e.(runeLiteral); ok { | ||
if l, ok := last.(runeLiteral); ok { | ||
el[len(el)-1] = stringLiteral{ | ||
append(append([]byte{}, string(l.r)...), string(r.r)...), | ||
} | ||
} else if l, ok := last.(stringLiteral); ok { | ||
el[len(el)-1] = stringLiteral{append(l.s, string(r.r)...)} | ||
} else { | ||
el = append(el, e) | ||
} | ||
} else if s, ok := e.(stringLiteral); ok { | ||
if l, ok := last.(runeLiteral); ok { | ||
el[len(el)-1] = stringLiteral{ | ||
append(append([]byte{}, string(l.r)...), s.s...), | ||
} | ||
} else if l, ok := last.(stringLiteral); ok { | ||
el[len(el)-1] = stringLiteral{append(l.s, s.s...)} | ||
} else { | ||
el = append(el, e) | ||
} | ||
} else { | ||
el = append(el, e) | ||
} | ||
} | ||
b.elements = el | ||
} | ||
|
||
func (b *builder) add(e element) { | ||
b.elements = append(b.elements, e) | ||
} | ||
|
||
func (b *builder) millisOfSecond(digits int) { | ||
b.appendDecimal(ftMillisOfSecond, digits, 3) | ||
} | ||
|
||
func (b *builder) millisOfDay(digits int) { | ||
b.appendDecimal(ftMillisOfDay, digits, 8) | ||
} | ||
|
||
func (b *builder) secondOfMinute(digits int) { | ||
b.appendDecimal(ftSecondOfMinute, digits, 2) | ||
} | ||
|
||
func (b *builder) secondOfDay(digits int) { | ||
b.appendDecimal(ftSecondOfDay, digits, 5) | ||
} | ||
|
||
func (b *builder) minuteOfHour(digits int) { | ||
b.appendDecimal(ftMinuteOfHour, digits, 2) | ||
} | ||
|
||
func (b *builder) minuteOfDay(digits int) { | ||
b.appendDecimal(ftMinuteOfDay, digits, 4) | ||
} | ||
|
||
func (b *builder) hourOfDay(digits int) { | ||
b.appendDecimal(ftHourOfDay, digits, 2) | ||
} | ||
|
||
func (b *builder) clockhourOfDay(digits int) { | ||
b.appendDecimal(ftClockhourOfDay, digits, 2) | ||
} | ||
|
||
func (b *builder) hourOfHalfday(digits int) { | ||
b.appendDecimal(ftHourOfHalfday, digits, 2) | ||
} | ||
|
||
func (b *builder) clockhourOfHalfday(digits int) { | ||
b.appendDecimal(ftClockhourOfHalfday, digits, 2) | ||
} | ||
|
||
func (b *builder) dayOfWeek(digits int) { | ||
b.appendDecimal(ftDayOfWeek, digits, 1) | ||
} | ||
|
||
func (b *builder) dayOfMonth(digits int) { | ||
b.appendDecimal(ftDayOfMonth, digits, 2) | ||
} | ||
|
||
func (b *builder) dayOfYear(digits int) { | ||
b.appendDecimal(ftDayOfYear, digits, 3) | ||
} | ||
|
||
func (b *builder) weekOfWeekyear(digits int) { | ||
b.appendDecimal(ftWeekOfWeekyear, digits, 2) | ||
} | ||
|
||
func (b *builder) weekyear(minDigits, maxDigits int) { | ||
b.appendDecimal(ftWeekyear, minDigits, maxDigits) | ||
} | ||
|
||
func (b *builder) monthOfYear(digits int) { | ||
b.appendDecimal(ftMonthOfYear, digits, 2) | ||
} | ||
|
||
func (b *builder) year(minDigits, maxDigits int) { | ||
b.appendSigned(ftYear, minDigits, maxDigits) | ||
} | ||
|
||
func (b *builder) twoDigitYear() { | ||
b.add(twoDigitYear{ftYear}) | ||
} | ||
|
||
func (b *builder) twoDigitWeekYear() { | ||
b.add(twoDigitYear{ftWeekyear}) | ||
} | ||
|
||
func (b *builder) halfdayOfDayText() { | ||
b.appendText(ftHalfdayOfDay) | ||
} | ||
|
||
func (b *builder) dayOfWeekText() { | ||
b.appendText(ftDayOfWeek) | ||
} | ||
|
||
func (b *builder) dayOfWeekShortText() { | ||
b.appendShortText(ftDayOfWeek) | ||
} | ||
|
||
func (b *builder) monthOfYearText() { | ||
b.appendText(ftMonthOfYear) | ||
} | ||
|
||
func (b *builder) monthOfYearShortText() { | ||
b.appendShortText(ftMonthOfYear) | ||
} | ||
|
||
// TODO: add timezone support | ||
|
||
func (b *builder) appendRune(r rune) { | ||
b.add(runeLiteral{r}) | ||
} | ||
|
||
func (b *builder) appendLiteral(l string) { | ||
switch len(l) { | ||
case 0: | ||
case 1: | ||
b.add(runeLiteral{rune(l[0])}) | ||
default: | ||
b.add(stringLiteral{[]byte(l)}) | ||
} | ||
} | ||
|
||
func (b *builder) appendDecimalValue(ft fieldType, minDigits, maxDigits int, signed bool) { | ||
if maxDigits < minDigits { | ||
maxDigits = minDigits | ||
} | ||
|
||
if minDigits <= 1 { | ||
b.add(unpaddedNumber{ft, maxDigits, signed}) | ||
} else { | ||
b.add(paddedNumber{ft, minDigits, maxDigits, signed}) | ||
} | ||
} | ||
|
||
func (b *builder) appendDecimal(ft fieldType, minDigits, maxDigits int) { | ||
b.appendDecimalValue(ft, minDigits, maxDigits, false) | ||
} | ||
|
||
func (b *builder) appendSigned(ft fieldType, minDigits, maxDigits int) { | ||
b.appendDecimalValue(ft, minDigits, maxDigits, true) | ||
} | ||
|
||
func (b *builder) appendText(ft fieldType) { | ||
b.add(textField{ft, false}) | ||
} | ||
|
||
func (b *builder) appendShortText(ft fieldType) { | ||
b.add(textField{ft, true}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package dtfmt | ||
|
||
import "time" | ||
|
||
// ctx stores pre-computed time fields used by the formatter. | ||
type ctx struct { | ||
year int | ||
month time.Month | ||
day int | ||
weekday time.Weekday | ||
yearday int | ||
isoWeek, isoYear int | ||
|
||
hour, min, sec int | ||
millis int | ||
|
||
buf []byte | ||
} | ||
|
||
type ctxConfig struct { | ||
date bool | ||
clock bool | ||
weekday bool | ||
yearday bool | ||
millis bool | ||
iso bool | ||
} | ||
|
||
func (c *ctx) initTime(config *ctxConfig, t time.Time) { | ||
if config.date { | ||
c.year, c.month, c.day = t.Date() | ||
} | ||
if config.clock { | ||
c.hour, c.min, c.sec = t.Clock() | ||
} | ||
if config.iso { | ||
c.isoYear, c.isoWeek = t.ISOWeek() | ||
} | ||
|
||
if config.millis { | ||
c.millis = t.Nanosecond() / 1000000 | ||
} | ||
|
||
if config.yearday { | ||
c.yearday = t.YearDay() | ||
} | ||
|
||
if config.weekday { | ||
c.weekday = t.Weekday() | ||
} | ||
} | ||
|
||
func (c *ctxConfig) enableDate() { | ||
c.date = true | ||
} | ||
|
||
func (c *ctxConfig) enableClock() { | ||
c.clock = true | ||
} | ||
|
||
func (c *ctxConfig) enableWeekday() { | ||
c.weekday = true | ||
} | ||
|
||
func (c *ctxConfig) enableYearday() { | ||
c.yearday = true | ||
} | ||
|
||
func (c *ctxConfig) enableISO() { | ||
c.iso = true | ||
} | ||
|
||
func isLeap(year int) bool { | ||
return year%4 == 0 && (year%100 != 0 || year%400 == 0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// dtfmt package provides time formatter support with pattern syntax mostly | ||
// similar to joda DateTimeFormat. The pattern syntax supported is a subset | ||
// (mostly compatible) with joda DateTimeFormat. | ||
// | ||
// | ||
// Symbol Meaning Type Supported Examples | ||
// ------ ------- ------- --------- ------- | ||
// G era text no AD | ||
// C century of era (>=0) number no 20 | ||
// Y year of era (>=0) year yes 1996 | ||
// | ||
// x weekyear year yes 1996 | ||
// w week of weekyear number yes 27 | ||
// e day of week number yes 2 | ||
// E day of week text yes Tuesday; Tue | ||
// | ||
// y year year yes 1996 | ||
// D day of year number yes 189 | ||
// M month of year month yes July; Jul; 07 | ||
// d day of month number yes 10 | ||
// | ||
// a halfday of day text yes PM | ||
// K hour of halfday (0~11) number yes 0 | ||
// h clockhour of halfday (1~12) number yes 12 | ||
// | ||
// H hour of day (0~23) number yes 0 | ||
// k clockhour of day (1~24) number yes 24 | ||
// m minute of hour number yes 30 | ||
// s second of minute number yes 55 | ||
// S fraction of second millis no 978 | ||
// | ||
// z time zone text no Pacific Standard Time; PST | ||
// Z time zone offset/id zone no -0800; -08:00; America/Los_Angeles | ||
// | ||
// ' escape for text delimiter | ||
// '' single quote literal | ||
// | ||
// The format is based on pattern letter count. Any character not in the range | ||
// [a-z][A-Z] is interpreted as literal and copied into final string as is. | ||
// Arbitrary Literals can also be written using single quotes `'` | ||
// | ||
// Types: Notes: | ||
// ------ ------ | ||
// text Use full form if number of letters is >= 4. | ||
// Otherwise a short form is used (if available). | ||
// | ||
// number Minimum number of digits depends on number of letters. | ||
// Shorter numbers are zero-padded. | ||
// | ||
// year mostly like number. If Pattern length is 2, | ||
// the year will be displayed as zero-based year | ||
// of the century (modulo 100) | ||
// | ||
// month If pattern length >= 3, formatting is according to | ||
// text type. Otherwise number type | ||
// formatting rules are applied. | ||
// | ||
// millis Not yet supported | ||
// | ||
// zone Not yet supported | ||
// | ||
// literal Literals are copied as is into formatted string | ||
// | ||
package dtfmt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package dtfmt | ||
|
||
import "time" | ||
|
||
// Format applies the format-pattern to the given timestamp. | ||
// Returns the formatted string or an error if pattern is invalid. | ||
func Format(t time.Time, pattern string) (string, error) { | ||
f, err := NewFormatter(pattern) | ||
if err != nil { | ||
return "", err | ||
} | ||
return f.Format(t) | ||
} |
Oops, something went wrong.