Skip to content
Open
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
2 changes: 1 addition & 1 deletion configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (config *Configuration) Flags() []cli.Flag {
cli.StringFlag{
Name: "f,format",
Value: "%message",
Usage: "(*) Message format for the entries - field names are referenced using % sign, for example '%@timestamp %message'",
Usage: "(*) Message format for the entries - field names are referenced using % sign, length with [n], for example '%@timestamp %message[55]'",
Destination: &config.QueryDefinition.Format,
},
cli.StringFlag{
Expand Down
58 changes: 51 additions & 7 deletions elktail.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@ import (
"io/ioutil"
"os"
"regexp"
"strconv"
"strings"
"time"
"golang.org/x/crypto/ssh/terminal"

"unicode/utf8"

"github.com/codegangsta/cli"
"net/url"
"errors"
Expand All @@ -41,8 +45,11 @@ func (entry *DisplayedEntry) isBefore(timeStamp string) bool {
return entry.timeStamp < timeStamp
}

// Regexp for parsing out format fields
var formatRegexp = regexp.MustCompile("%[A-Za-z0-9@_.-]+")
// FormatRegexp is used to parse out format fields from a user-supplied format string.
// Fields are prefaced with a '%' sign, and can be optionally suffixed with padding size
// in rune length (e.g. "%@timestamp %message[65]")
var FormatRegexp = regexp.MustCompile(`(%[A-Za-z0-9@_.-]+)(?:\[([1-9][0-9]*)\])?`)

const dateFormatDMY = "2006-01-02"
const dateFormatFull = "2006-01-02T15:04:05.999Z07:00"
const tailingTimeWindow = 500
Expand Down Expand Up @@ -255,23 +262,60 @@ func (t *Tail) processHit(hit *elastic.SearchHit) map[string]interface{} {
Error.Fatalln("Failed parsing ElasticSearch response.", err)
}
t.printResult(entry)
return entry;
return entry
}


// Print result according to format
func (t *Tail) printResult(entry map[string]interface{}) {
Trace.Println("Result: ", entry)
fields := formatRegexp.FindAllString(t.queryDefinition.Format, -1)

fields := FormatRegexp.FindAllStringSubmatch(t.queryDefinition.Format, -1)
// e.g. #=> [["%message[24]", "%message", "24"]...]
Trace.Println("Fields: ", fields)

result := t.queryDefinition.Format

for _, f := range fields {
value, _ := EvaluateExpression(entry, f[1:])
result = strings.Replace(result, f, value, -1)
value, _ := EvaluateExpression(entry, f[1][1:])

if s := f[2]; s != "" {
colSize, _ := strconv.Atoi(f[2]) // regexp ensures parse-able
value = fitString(value, colSize)
}
result = strings.Replace(result, f[0], value, -1)
}
fmt.Println(result)
}

// fitString strips runes from `s` (or pads with ' ') to
// create a string of length `n`
func fitString(s string, n int) string {
l := utf8.RuneCountInString(s)

if l < n {
for p := 0; p < n-l; p++ {
s += " "
}
} else if l > n {
s = s[0:runeLengthToByteLength(s, n)]
}

return s
}

// runeLengthToByteLength returns the number of bytes in the first n
// runes of the string s
func runeLengthToByteLength(s string, n int) int {
b := 0
for i := range s {
if i > n {
break
}
b = i
}
return b
}

func (t *Tail) buildSearchQuery() elastic.Query {
var query elastic.Query
if len(t.queryDefinition.Terms) > 0 {
Expand Down
26 changes: 23 additions & 3 deletions elktail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,29 @@ func TestResolveField(t *testing.T) {
testutils.AssertEqualsString(t, "", eval(model1, "bar"))
}



func eval(model interface{}, expr string) string {
result, _ := EvaluateExpression(model, expr)
return result
}
}

func TestFormatRegexp(t *testing.T) {
formatString := "%timestamp %message[25] %trace[10] %error"

match := FormatRegexp.FindAllStringSubmatch(formatString, -1)

testutils.AssertEqualsString(t, "%timestamp", match[0][0])
testutils.AssertEqualsString(t, "%timestamp", match[0][1])
testutils.AssertEqualsString(t, "", match[0][2])

testutils.AssertEqualsString(t, "%message[25]", match[1][0])
testutils.AssertEqualsString(t, "%message", match[1][1])
testutils.AssertEqualsString(t, "25", match[1][2])

testutils.AssertEqualsString(t, "%trace[10]", match[2][0])
testutils.AssertEqualsString(t, "%trace", match[2][1])
testutils.AssertEqualsString(t, "10", match[2][2])

testutils.AssertEqualsString(t, "%error", match[3][0])
testutils.AssertEqualsString(t, "%error", match[3][1])
testutils.AssertEqualsString(t, "", match[3][2])
}