Skip to content

Commit

Permalink
Merge pull request elastic#2083 from urso/enh/fmtstr-with-timestamps
Browse files Browse the repository at this point in the history
Add timestamp formatting to EventStringFormatter
  • Loading branch information
tsg authored Jul 25, 2016
2 parents b3ec2d9 + 33d06a6 commit c5c51dd
Show file tree
Hide file tree
Showing 12 changed files with 1,303 additions and 7 deletions.
221 changes: 221 additions & 0 deletions libbeat/common/dtfmt/builder.go
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})
}
75 changes: 75 additions & 0 deletions libbeat/common/dtfmt/ctx.go
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)
}
64 changes: 64 additions & 0 deletions libbeat/common/dtfmt/doc.go
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 (&gt;=0) number no 20
// Y year of era (&gt;=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
13 changes: 13 additions & 0 deletions libbeat/common/dtfmt/dtfmt.go
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)
}
Loading

0 comments on commit c5c51dd

Please sign in to comment.