2016-06-02 17:47:15 +00:00
package grok
import (
"testing"
"time"
"github.com/influxdata/telegraf"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var benchM telegraf . Metric
func Benchmark_ParseLine_CommonLogFormat ( b * testing . B ) {
p := & Parser {
Patterns : [ ] string { "%{COMMON_LOG_FORMAT}" } ,
}
2017-06-05 21:45:11 +00:00
_ = p . Compile ( )
2016-06-02 17:47:15 +00:00
var m telegraf . Metric
for n := 0 ; n < b . N ; n ++ {
m , _ = p . ParseLine ( ` 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ` )
}
benchM = m
}
func Benchmark_ParseLine_CombinedLogFormat ( b * testing . B ) {
p := & Parser {
Patterns : [ ] string { "%{COMBINED_LOG_FORMAT}" } ,
}
2017-06-05 21:45:11 +00:00
_ = p . Compile ( )
2016-06-02 17:47:15 +00:00
var m telegraf . Metric
for n := 0 ; n < b . N ; n ++ {
m , _ = p . ParseLine ( ` 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "-" "Mozilla" ` )
}
benchM = m
}
func Benchmark_ParseLine_CustomPattern ( b * testing . B ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatterns : `
DURATION % { NUMBER } [ nuµm ] ? s
RESPONSE_CODE % { NUMBER : response_code : tag }
RESPONSE_TIME % { DURATION : response_time : duration }
TEST_LOG_A % { NUMBER : myfloat : float } % { RESPONSE_CODE } % { IPORHOST : clientip } % { RESPONSE_TIME }
` ,
}
2017-06-05 21:45:11 +00:00
_ = p . Compile ( )
2016-06-02 17:47:15 +00:00
var m telegraf . Metric
for n := 0 ; n < b . N ; n ++ {
m , _ = p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
}
benchM = m
}
2017-02-21 18:50:10 +00:00
// Test a very simple parse pattern.
func TestSimpleParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TESTLOG}" } ,
CustomPatterns : `
TESTLOG % { NUMBER : num : int } % { WORD : client }
` ,
}
assert . NoError ( t , p . Compile ( ) )
m , err := p . ParseLine ( ` 142 bot ` )
assert . NoError ( t , err )
require . NotNil ( t , m )
assert . Equal ( t ,
map [ string ] interface { } {
"num" : int64 ( 142 ) ,
"client" : "bot" ,
} ,
m . Fields ( ) )
}
// Verify that patterns with a regex lookahead fail at compile time.
func TestParsePatternsWithLookahead ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{MYLOG}" } ,
CustomPatterns : `
NOBOT ( ( ? ! bot | crawl ) . ) *
MYLOG % { NUMBER : num : int } % { NOBOT : client }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err := p . ParseLine ( ` 1466004605359052000 bot ` )
assert . Error ( t , err )
}
2016-07-18 11:27:46 +00:00
func TestMeasurementName ( t * testing . T ) {
p := & Parser {
Measurement : "my_web_log" ,
Patterns : [ ] string { "%{COMMON_LOG_FORMAT}" } ,
}
assert . NoError ( t , p . Compile ( ) )
// Parse an influxdb POST request
m , err := p . ParseLine ( ` 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 2326 ) ,
"auth" : "frank" ,
"client_ip" : "127.0.0.1" ,
"http_version" : float64 ( 1.0 ) ,
"ident" : "user-identifier" ,
"request" : "/apache_pb.gif" ,
2016-12-20 15:57:32 +00:00
} ,
m . Fields ( ) )
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
assert . Equal ( t , "my_web_log" , m . Name ( ) )
}
func TestCLF_IPv6 ( t * testing . T ) {
p := & Parser {
Measurement : "my_web_log" ,
Patterns : [ ] string { "%{COMMON_LOG_FORMAT}" } ,
}
assert . NoError ( t , p . Compile ( ) )
m , err := p . ParseLine ( ` 2001:0db8:85a3:0000:0000:8a2e:0370:7334 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 2326 ) ,
"auth" : "frank" ,
"client_ip" : "2001:0db8:85a3:0000:0000:8a2e:0370:7334" ,
"http_version" : float64 ( 1.0 ) ,
"ident" : "user-identifier" ,
"request" : "/apache_pb.gif" ,
} ,
m . Fields ( ) )
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
assert . Equal ( t , "my_web_log" , m . Name ( ) )
m , err = p . ParseLine ( ` ::1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 2326 ) ,
"auth" : "frank" ,
"client_ip" : "::1" ,
"http_version" : float64 ( 1.0 ) ,
"ident" : "user-identifier" ,
"request" : "/apache_pb.gif" ,
2016-07-18 11:27:46 +00:00
} ,
m . Fields ( ) )
2016-07-18 11:54:33 +00:00
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
2016-07-18 11:27:46 +00:00
assert . Equal ( t , "my_web_log" , m . Name ( ) )
}
2016-08-04 15:25:35 +00:00
func TestCustomInfluxdbHttpd ( t * testing . T ) {
2016-06-02 17:47:15 +00:00
p := & Parser {
2016-08-04 15:25:35 +00:00
Patterns : [ ] string { ` \[httpd\] % { COMBINED_LOG_FORMAT} % { UUID:uuid:drop} % { NUMBER:response_time_us:int} ` } ,
2016-06-02 17:47:15 +00:00
}
assert . NoError ( t , p . Compile ( ) )
// Parse an influxdb POST request
m , err := p . ParseLine ( ` [httpd] ::1 - - [14/Jun/2016:11:33:29 +0100] "POST /write?consistency=any&db=telegraf&precision=ns&rp= HTTP/1.1" 204 0 "-" "InfluxDBClient" 6f61bc44-321b-11e6-8050-000000000000 2513 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 0 ) ,
"auth" : "-" ,
"client_ip" : "::1" ,
"http_version" : float64 ( 1.1 ) ,
"ident" : "-" ,
"referrer" : "-" ,
"request" : "/write?consistency=any&db=telegraf&precision=ns&rp=" ,
"response_time_us" : int64 ( 2513 ) ,
"agent" : "InfluxDBClient" ,
} ,
m . Fields ( ) )
2016-07-18 11:54:33 +00:00
assert . Equal ( t , map [ string ] string { "verb" : "POST" , "resp_code" : "204" } , m . Tags ( ) )
2016-06-02 17:47:15 +00:00
// Parse an influxdb GET request
m , err = p . ParseLine ( ` [httpd] ::1 - - [14/Jun/2016:12:10:02 +0100] "GET /query?db=telegraf&q=SELECT+bytes%2Cresponse_time_us+FROM+logparser_grok+WHERE+http_method+%3D+%27GET%27+AND+response_time_us+%3E+0+AND+time+%3E+now%28%29+-+1h HTTP/1.1" 200 578 "http://localhost:8083/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36" 8a3806f1-3220-11e6-8006-000000000000 988 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 578 ) ,
"auth" : "-" ,
"client_ip" : "::1" ,
"http_version" : float64 ( 1.1 ) ,
"ident" : "-" ,
"referrer" : "http://localhost:8083/" ,
"request" : "/query?db=telegraf&q=SELECT+bytes%2Cresponse_time_us+FROM+logparser_grok+WHERE+http_method+%3D+%27GET%27+AND+response_time_us+%3E+0+AND+time+%3E+now%28%29+-+1h" ,
"response_time_us" : int64 ( 988 ) ,
"agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36" ,
} ,
m . Fields ( ) )
2016-07-18 11:54:33 +00:00
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
2016-06-02 17:47:15 +00:00
}
// common log format
// 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
func TestBuiltinCommonLogFormat ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{COMMON_LOG_FORMAT}" } ,
}
assert . NoError ( t , p . Compile ( ) )
// Parse an influxdb POST request
m , err := p . ParseLine ( ` 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 2326 ) ,
"auth" : "frank" ,
"client_ip" : "127.0.0.1" ,
"http_version" : float64 ( 1.0 ) ,
"ident" : "user-identifier" ,
"request" : "/apache_pb.gif" ,
} ,
m . Fields ( ) )
2016-07-18 11:54:33 +00:00
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
2016-06-02 17:47:15 +00:00
}
2016-10-07 11:08:35 +00:00
// common log format
// 127.0.0.1 user1234 frank1234 [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
func TestBuiltinCommonLogFormatWithNumbers ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{COMMON_LOG_FORMAT}" } ,
}
assert . NoError ( t , p . Compile ( ) )
// Parse an influxdb POST request
m , err := p . ParseLine ( ` 127.0.0.1 user1234 frank1234 [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 2326 ) ,
"auth" : "frank1234" ,
"client_ip" : "127.0.0.1" ,
"http_version" : float64 ( 1.0 ) ,
"ident" : "user1234" ,
"request" : "/apache_pb.gif" ,
} ,
m . Fields ( ) )
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
}
2016-06-02 17:47:15 +00:00
// combined log format
// 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "-" "Mozilla"
func TestBuiltinCombinedLogFormat ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{COMBINED_LOG_FORMAT}" } ,
}
assert . NoError ( t , p . Compile ( ) )
// Parse an influxdb POST request
m , err := p . ParseLine ( ` 127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "-" "Mozilla" ` )
require . NotNil ( t , m )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"resp_bytes" : int64 ( 2326 ) ,
"auth" : "frank" ,
"client_ip" : "127.0.0.1" ,
"http_version" : float64 ( 1.0 ) ,
"ident" : "user-identifier" ,
"request" : "/apache_pb.gif" ,
"referrer" : "-" ,
"agent" : "Mozilla" ,
} ,
m . Fields ( ) )
2016-07-18 11:54:33 +00:00
assert . Equal ( t , map [ string ] string { "verb" : "GET" , "resp_code" : "200" } , m . Tags ( ) )
2016-06-02 17:47:15 +00:00
}
func TestCompileStringAndParse ( t * testing . T ) {
p := & Parser {
2016-07-18 13:44:25 +00:00
Patterns : [ ] string { "%{TEST_LOG_A}" } ,
2016-06-02 17:47:15 +00:00
CustomPatterns : `
DURATION % { NUMBER } [ nuµm ] ? s
RESPONSE_CODE % { NUMBER : response_code : tag }
RESPONSE_TIME % { DURATION : response_time : duration }
TEST_LOG_A % { NUMBER : myfloat : float } % { RESPONSE_CODE } % { IPORHOST : clientip } % { RESPONSE_TIME }
` ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` 1.25 200 192.168.1.1 5.432µs ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
}
2016-07-18 13:44:25 +00:00
func TestCompileErrorsOnInvalidPattern ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatterns : `
DURATION % { NUMBER } [ nuµm ] ? s
RESPONSE_CODE % { NUMBER : response_code : tag }
RESPONSE_TIME % { DURATION : response_time : duration }
TEST_LOG_A % { NUMBER : myfloat : float } % { RESPONSE_CODE } % { IPORHOST : clientip } % { RESPONSE_TIME }
` ,
}
assert . Error ( t , p . Compile ( ) )
metricA , _ := p . ParseLine ( ` 1.25 200 192.168.1.1 5.432µs ` )
require . Nil ( t , metricA )
}
func TestParsePatternsWithoutCustom ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{POSINT:ts:ts-epochnano} response_time=%{POSINT:response_time:int} mymetric=%{NUMBER:metric:float}" } ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` 1466004605359052000 response_time=20821 mymetric=10890.645 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"response_time" : int64 ( 20821 ) ,
"metric" : float64 ( 10890.645 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricA . Tags ( ) )
assert . Equal ( t , time . Unix ( 0 , 1466004605359052000 ) , metricA . Time ( ) )
}
2016-06-02 17:47:15 +00:00
func TestParseEpochNano ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{MYAPP}" } ,
CustomPatterns : `
MYAPP % { POSINT : ts : ts - epochnano } response_time = % { POSINT : response_time : int } mymetric = % { NUMBER : metric : float }
` ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` 1466004605359052000 response_time=20821 mymetric=10890.645 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"response_time" : int64 ( 20821 ) ,
"metric" : float64 ( 10890.645 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricA . Tags ( ) )
assert . Equal ( t , time . Unix ( 0 , 1466004605359052000 ) , metricA . Time ( ) )
}
func TestParseEpoch ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{MYAPP}" } ,
CustomPatterns : `
MYAPP % { POSINT : ts : ts - epoch } response_time = % { POSINT : response_time : int } mymetric = % { NUMBER : metric : float }
` ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` 1466004605 response_time=20821 mymetric=10890.645 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"response_time" : int64 ( 20821 ) ,
"metric" : float64 ( 10890.645 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricA . Tags ( ) )
assert . Equal ( t , time . Unix ( 1466004605 , 0 ) , metricA . Time ( ) )
}
2017-10-19 23:36:32 +00:00
func TestParseEpochDecimal ( t * testing . T ) {
var tests = [ ] struct {
name string
line string
noMatch bool
err error
tags map [ string ] string
fields map [ string ] interface { }
time time . Time
} {
{
name : "ns precision" ,
line : "1466004605.359052000 value=42" ,
tags : map [ string ] string { } ,
fields : map [ string ] interface { } {
"value" : int64 ( 42 ) ,
} ,
time : time . Unix ( 0 , 1466004605359052000 ) ,
} ,
{
name : "ms precision" ,
line : "1466004605.359 value=42" ,
tags : map [ string ] string { } ,
fields : map [ string ] interface { } {
"value" : int64 ( 42 ) ,
} ,
time : time . Unix ( 0 , 1466004605359000000 ) ,
} ,
{
name : "second precision" ,
line : "1466004605 value=42" ,
tags : map [ string ] string { } ,
fields : map [ string ] interface { } {
"value" : int64 ( 42 ) ,
} ,
time : time . Unix ( 0 , 1466004605000000000 ) ,
} ,
{
name : "sub ns precision" ,
line : "1466004605.123456789123 value=42" ,
tags : map [ string ] string { } ,
fields : map [ string ] interface { } {
"value" : int64 ( 42 ) ,
} ,
time : time . Unix ( 0 , 1466004605123456789 ) ,
} ,
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
parser := & Parser {
Patterns : [ ] string { "%{NUMBER:ts:ts-epoch} value=%{NUMBER:value:int}" } ,
}
assert . NoError ( t , parser . Compile ( ) )
m , err := parser . ParseLine ( tt . line )
if tt . noMatch {
require . Nil ( t , m )
require . Nil ( t , err )
return
}
require . Equal ( t , tt . err , err )
require . NotNil ( t , m )
require . Equal ( t , tt . tags , m . Tags ( ) )
require . Equal ( t , tt . fields , m . Fields ( ) )
require . Equal ( t , tt . time , m . Time ( ) )
} )
}
}
2016-06-02 17:47:15 +00:00
func TestParseEpochErrors ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{MYAPP}" } ,
CustomPatterns : `
MYAPP % { WORD : ts : ts - epoch } response_time = % { POSINT : response_time : int } mymetric = % { NUMBER : metric : float }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err := p . ParseLine ( ` foobar response_time=20821 mymetric=10890.645 ` )
assert . NoError ( t , err )
p = & Parser {
Patterns : [ ] string { "%{MYAPP}" } ,
CustomPatterns : `
MYAPP % { WORD : ts : ts - epochnano } response_time = % { POSINT : response_time : int } mymetric = % { NUMBER : metric : float }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err = p . ParseLine ( ` foobar response_time=20821 mymetric=10890.645 ` )
assert . NoError ( t , err )
}
2016-07-22 16:17:50 +00:00
func TestParseGenericTimestamp ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { ` \[% { HTTPDATE:ts:ts}\] response_time=% { POSINT:response_time:int} mymetric=% { NUMBER:metric:float} ` } ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [09/Jun/2016:03:37:03 +0000] response_time=20821 mymetric=10890.645 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"response_time" : int64 ( 20821 ) ,
"metric" : float64 ( 10890.645 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricA . Tags ( ) )
assert . Equal ( t , time . Unix ( 1465443423 , 0 ) . UTC ( ) , metricA . Time ( ) . UTC ( ) )
metricB , err := p . ParseLine ( ` [09/Jun/2016:03:37:04 +0000] response_time=20821 mymetric=10890.645 ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"response_time" : int64 ( 20821 ) ,
"metric" : float64 ( 10890.645 ) ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
assert . Equal ( t , time . Unix ( 1465443424 , 0 ) . UTC ( ) , metricB . Time ( ) . UTC ( ) )
}
func TestParseGenericTimestampNotFound ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { ` \[% { NOTSPACE:ts:ts}\] response_time=% { POSINT:response_time:int} mymetric=% { NUMBER:metric:float} ` } ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [foobar] response_time=20821 mymetric=10890.645 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"response_time" : int64 ( 20821 ) ,
"metric" : float64 ( 10890.645 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricA . Tags ( ) )
}
2016-06-02 17:47:15 +00:00
func TestCompileFileAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
"myint" : int64 ( 101 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
assert . Equal ( t ,
time . Date ( 2016 , time . June , 4 , 12 , 41 , 45 , 0 , time . FixedZone ( "foo" , 60 * 60 ) ) . Nanosecond ( ) ,
metricA . Time ( ) . Nanosecond ( ) )
metricB , err := p . ParseLine ( ` [04/06/2016--12:41:45] 1.25 mystring dropme nomodifier ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"myfloat" : 1.25 ,
"mystring" : "mystring" ,
"nomodifier" : "nomodifier" ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
assert . Equal ( t ,
time . Date ( 2016 , time . June , 4 , 12 , 41 , 45 , 0 , time . FixedZone ( "foo" , 60 * 60 ) ) . Nanosecond ( ) ,
metricB . Time ( ) . Nanosecond ( ) )
}
func TestCompileNoModifiersAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_C}" } ,
CustomPatterns : `
DURATION % { NUMBER } [ nuµm ] ? s
TEST_LOG_C % { NUMBER : myfloat } % { NUMBER } % { IPORHOST : clientip } % { DURATION : rt }
` ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` 1.25 200 192.168.1.1 5.432µs ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : "1.25" ,
"rt" : "5.432µs" ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricA . Tags ( ) )
}
func TestCompileNoNamesAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_C}" } ,
CustomPatterns : `
DURATION % { NUMBER } [ nuµm ] ? s
TEST_LOG_C % { NUMBER } % { NUMBER } % { IPORHOST } % { DURATION }
` ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` 1.25 200 192.168.1.1 5.432µs ` )
require . Nil ( t , metricA )
assert . NoError ( t , err )
}
func TestParseNoMatch ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] notnumber 200 192.168.1.1 5.432µs 101 ` )
assert . NoError ( t , err )
assert . Nil ( t , metricA )
}
func TestCompileErrors ( t * testing . T ) {
// Compile fails because there are multiple timestamps:
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatterns : `
TEST_LOG_A % { HTTPDATE : ts1 : ts - httpd } % { HTTPDATE : ts2 : ts - httpd } % { NUMBER : mynum : int }
` ,
}
assert . Error ( t , p . Compile ( ) )
// Compile fails because file doesn't exist:
p = & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "/tmp/foo/bar/baz" } ,
}
assert . Error ( t , p . Compile ( ) )
}
func TestParseErrors ( t * testing . T ) {
// Parse fails because the pattern doesn't exist
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_B}" } ,
CustomPatterns : `
TEST_LOG_A % { HTTPDATE : ts : ts - httpd } % { WORD : myword : int } % { }
` ,
}
2016-07-18 13:44:25 +00:00
assert . Error ( t , p . Compile ( ) )
2016-06-02 17:47:15 +00:00
_ , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] notnumber 200 192.168.1.1 5.432µs 101 ` )
assert . Error ( t , err )
// Parse fails because myword is not an int
p = & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" } ,
CustomPatterns : `
TEST_LOG_A % { HTTPDATE : ts : ts - httpd } % { WORD : myword : int }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err = p . ParseLine ( ` 04/Jun/2016:12:41:45 +0100 notnumber ` )
assert . Error ( t , err )
// Parse fails because myword is not a float
p = & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" } ,
CustomPatterns : `
TEST_LOG_A % { HTTPDATE : ts : ts - httpd } % { WORD : myword : float }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err = p . ParseLine ( ` 04/Jun/2016:12:41:45 +0100 notnumber ` )
assert . Error ( t , err )
// Parse fails because myword is not a duration
p = & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" } ,
CustomPatterns : `
TEST_LOG_A % { HTTPDATE : ts : ts - httpd } % { WORD : myword : duration }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err = p . ParseLine ( ` 04/Jun/2016:12:41:45 +0100 notnumber ` )
assert . Error ( t , err )
// Parse fails because the time layout is wrong.
p = & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" } ,
CustomPatterns : `
TEST_LOG_A % { HTTPDATE : ts : ts - unix } % { WORD : myword : duration }
` ,
}
assert . NoError ( t , p . Compile ( ) )
_ , err = p . ParseLine ( ` 04/Jun/2016:12:41:45 +0100 notnumber ` )
assert . Error ( t , err )
}
func TestTsModder ( t * testing . T ) {
tsm := & tsModder { }
reftime := time . Date ( 2006 , time . December , 1 , 1 , 1 , 1 , int ( time . Millisecond ) , time . UTC )
modt := tsm . tsMod ( reftime )
assert . Equal ( t , reftime , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Microsecond * 1 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Microsecond * 2 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Microsecond * 3 ) , modt )
reftime = time . Date ( 2006 , time . December , 1 , 1 , 1 , 1 , int ( time . Microsecond ) , time . UTC )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Nanosecond * 1 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Nanosecond * 2 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Nanosecond * 3 ) , modt )
reftime = time . Date ( 2006 , time . December , 1 , 1 , 1 , 1 , int ( time . Microsecond ) * 999 , time . UTC )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Nanosecond * 1 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Nanosecond * 2 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Nanosecond * 3 ) , modt )
reftime = time . Date ( 2006 , time . December , 1 , 1 , 1 , 1 , 0 , time . UTC )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Millisecond * 1 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Millisecond * 2 ) , modt )
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime . Add ( time . Millisecond * 3 ) , modt )
reftime = time . Time { }
modt = tsm . tsMod ( reftime )
assert . Equal ( t , reftime , modt )
}
func TestTsModder_Rollover ( t * testing . T ) {
tsm := & tsModder { }
reftime := time . Date ( 2006 , time . December , 1 , 1 , 1 , 1 , int ( time . Millisecond ) , time . UTC )
modt := tsm . tsMod ( reftime )
for i := 1 ; i < 1000 ; i ++ {
modt = tsm . tsMod ( reftime )
}
assert . Equal ( t , reftime . Add ( time . Microsecond * 999 + time . Nanosecond ) , modt )
reftime = time . Date ( 2006 , time . December , 1 , 1 , 1 , 1 , int ( time . Microsecond ) , time . UTC )
modt = tsm . tsMod ( reftime )
for i := 1 ; i < 1001 ; i ++ {
modt = tsm . tsMod ( reftime )
}
assert . Equal ( t , reftime . Add ( time . Nanosecond * 1000 ) , modt )
}
2017-04-13 00:10:17 +00:00
func TestShortPatternRegression ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TS_UNIX:timestamp:ts-unix} %{NUMBER:value:int}" } ,
CustomPatterns : `
TS_UNIX % { DAY } % { MONTH } % { MONTHDAY } % { HOUR } : % { MINUTE } : % { SECOND } % { TZ } % { YEAR }
` ,
}
require . NoError ( t , p . Compile ( ) )
metric , err := p . ParseLine ( ` Wed Apr 12 13:10:34 PST 2017 42 ` )
require . NoError ( t , err )
require . NotNil ( t , metric )
require . Equal ( t ,
map [ string ] interface { } {
"value" : int64 ( 42 ) ,
} ,
metric . Fields ( ) )
}
2017-06-05 21:45:11 +00:00
func TestTimezoneEmptyCompileFileAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
Timezone : "" ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
"myint" : int64 ( 101 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465040505000000000 ) , metricA . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
metricB , err := p . ParseLine ( ` [04/06/2016--12:41:45] 1.25 mystring dropme nomodifier ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"myfloat" : 1.25 ,
"mystring" : "mystring" ,
"nomodifier" : "nomodifier" ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465044105000000000 ) , metricB . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
}
func TestTimezoneMalformedCompileFileAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
Timezone : "Something/Weird" ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
"myint" : int64 ( 101 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465040505000000000 ) , metricA . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
metricB , err := p . ParseLine ( ` [04/06/2016--12:41:45] 1.25 mystring dropme nomodifier ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"myfloat" : 1.25 ,
"mystring" : "mystring" ,
"nomodifier" : "nomodifier" ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465044105000000000 ) , metricB . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
}
func TestTimezoneEuropeCompileFileAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
Timezone : "Europe/Berlin" ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
"myint" : int64 ( 101 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465040505000000000 ) , metricA . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
metricB , err := p . ParseLine ( ` [04/06/2016--12:41:45] 1.25 mystring dropme nomodifier ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"myfloat" : 1.25 ,
"mystring" : "mystring" ,
"nomodifier" : "nomodifier" ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465036905000000000 ) , metricB . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
}
func TestTimezoneAmericasCompileFileAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
Timezone : "Canada/Eastern" ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
"myint" : int64 ( 101 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465040505000000000 ) , metricA . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
metricB , err := p . ParseLine ( ` [04/06/2016--12:41:45] 1.25 mystring dropme nomodifier ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"myfloat" : 1.25 ,
"mystring" : "mystring" ,
"nomodifier" : "nomodifier" ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465058505000000000 ) , metricB . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
}
func TestTimezoneLocalCompileFileAndParse ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { "%{TEST_LOG_A}" , "%{TEST_LOG_B}" } ,
CustomPatternFiles : [ ] string { "./testdata/test-patterns" } ,
Timezone : "Local" ,
}
assert . NoError ( t , p . Compile ( ) )
metricA , err := p . ParseLine ( ` [04/Jun/2016:12:41:45 +0100] 1.25 200 192.168.1.1 5.432µs 101 ` )
require . NotNil ( t , metricA )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"clientip" : "192.168.1.1" ,
"myfloat" : float64 ( 1.25 ) ,
"response_time" : int64 ( 5432 ) ,
"myint" : int64 ( 101 ) ,
} ,
metricA . Fields ( ) )
assert . Equal ( t , map [ string ] string { "response_code" : "200" } , metricA . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , int64 ( 1465040505000000000 ) , metricA . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
metricB , err := p . ParseLine ( ` [04/06/2016--12:41:45] 1.25 mystring dropme nomodifier ` )
require . NotNil ( t , metricB )
assert . NoError ( t , err )
assert . Equal ( t ,
map [ string ] interface { } {
"myfloat" : 1.25 ,
"mystring" : "mystring" ,
"nomodifier" : "nomodifier" ,
} ,
metricB . Fields ( ) )
assert . Equal ( t , map [ string ] string { } , metricB . Tags ( ) )
2018-03-28 00:30:51 +00:00
assert . Equal ( t , time . Date ( 2016 , time . June , 4 , 12 , 41 , 45 , 0 , time . Local ) . UnixNano ( ) , metricB . Time ( ) . UnixNano ( ) )
2017-06-05 21:45:11 +00:00
}
2018-04-11 01:16:21 +00:00
func TestNewlineInPatterns ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { `
% { SYSLOGTIMESTAMP : timestamp }
` } ,
}
require . NoError ( t , p . Compile ( ) )
m , err := p . ParseLine ( "Apr 10 05:11:57" )
require . NoError ( t , err )
require . NotNil ( t , m )
}
2018-05-23 23:37:14 +00:00
func TestSyslogTimestampParser ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { ` % { SYSLOGTIMESTAMP:timestamp:ts-syslog} value=% { NUMBER:value:int} ` } ,
timeFunc : func ( ) time . Time { return time . Date ( 2018 , time . April , 1 , 0 , 0 , 0 , 0 , nil ) } ,
}
require . NoError ( t , p . Compile ( ) )
m , err := p . ParseLine ( "Sep 25 09:01:55 value=42" )
require . NoError ( t , err )
require . NotNil ( t , m )
require . Equal ( t , 2018 , m . Time ( ) . Year ( ) )
}
2018-06-22 04:15:33 +00:00
func TestFailDivideByZeroError ( t * testing . T ) {
p := & Parser {
Patterns : [ ] string { ` % { APPLE_SYSLOG_TIME_SHORT:timestamp:ts-"Jan 2 15:04:05"} % { HOSTNAME} % { APP_NAME:app_name}\[% { NUMBER:pid:int}\]% { GREEDYDATA:message} ` } ,
CustomPatterns : `
APPLE_SYSLOG_TIME_SHORT % { MONTH } + % { MONTHDAY } % { TIME }
APP_NAME [ a - zA - Z0 - 9 \ . ] +
` ,
}
require . NoError ( t , p . Compile ( ) )
m , err := p . ParseLine ( "Nov 6 13:57:03 rsavage iTunes[6504]: info> Scale factor of main display = 2.0" )
m , err = p . ParseLine ( "Nov 6 13:57:03 rsavage iTunes[6504]: objc[6504]: Class AMSupportURLConnectionDelegate is implemented in both /System/Library/PrivateFrameworks/EmbeddedOSInstall.framework/Versions/A/EmbeddedOSInstall (0x7fffa594e748) and /System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice (0x107e67008). One of the two will be used. Which one is undefined." )
require . NoError ( t , err )
require . NotNil ( t , m )
require . Equal ( t , 2018 , m . Time ( ) . Year ( ) )
}