2015-06-22 20:53:51 +00:00
package graphite
import (
"fmt"
"math"
2015-06-23 16:07:47 +00:00
"sort"
2015-06-22 20:53:51 +00:00
"strconv"
"strings"
"time"
2016-02-10 17:26:18 +00:00
"github.com/influxdata/influxdb/models"
2015-06-22 20:53:51 +00:00
)
2015-11-22 19:23:56 +00:00
// Minimum and maximum supported dates for timestamps.
2015-08-24 16:34:47 +00:00
var (
2016-12-31 01:17:59 +00:00
// The minimum graphite timestamp allowed.
2015-11-22 19:23:56 +00:00
MinDate = time . Date ( 1901 , 12 , 13 , 0 , 0 , 0 , 0 , time . UTC )
2016-12-31 01:17:59 +00:00
// The maximum graphite timestamp allowed.
2015-11-22 19:23:56 +00:00
MaxDate = time . Date ( 2038 , 1 , 19 , 0 , 0 , 0 , 0 , time . UTC )
2015-08-24 16:34:47 +00:00
)
2015-06-23 05:28:52 +00:00
2015-11-22 19:23:56 +00:00
var defaultTemplate * template
2015-06-23 05:28:52 +00:00
func init ( ) {
var err error
2015-06-24 18:08:36 +00:00
defaultTemplate , err = NewTemplate ( "measurement*" , nil , DefaultSeparator )
2015-06-23 05:28:52 +00:00
if err != nil {
panic ( err )
}
}
2015-06-25 05:33:42 +00:00
// Parser encapsulates a Graphite Parser.
2015-06-22 20:53:51 +00:00
type Parser struct {
2015-06-23 05:28:52 +00:00
matcher * matcher
2015-09-16 20:33:08 +00:00
tags models . Tags
2015-06-22 20:53:51 +00:00
}
2016-12-31 01:17:59 +00:00
// Options are configurable values that can be provided to a Parser.
2015-06-24 18:08:36 +00:00
type Options struct {
Separator string
Templates [ ] string
2015-09-16 20:33:08 +00:00
DefaultTags models . Tags
2015-06-24 18:08:36 +00:00
}
2016-12-31 01:17:59 +00:00
// NewParserWithOptions returns a graphite parser using the given options.
2015-06-24 18:08:36 +00:00
func NewParserWithOptions ( options Options ) ( * Parser , error ) {
2015-06-23 16:07:47 +00:00
2015-06-24 04:33:11 +00:00
matcher := newMatcher ( )
2015-06-23 16:07:47 +00:00
matcher . AddDefaultTemplate ( defaultTemplate )
2015-06-22 22:47:03 +00:00
2015-06-24 18:08:36 +00:00
for _ , pattern := range options . Templates {
2015-06-23 16:07:47 +00:00
template := pattern
filter := ""
2015-06-24 03:29:11 +00:00
// Format is [filter] <template> [tag1=value1,tag2=value2]
2015-06-23 21:27:05 +00:00
parts := strings . Fields ( pattern )
2015-10-06 21:25:29 +00:00
if len ( parts ) < 1 {
continue
} else if len ( parts ) >= 2 {
2015-07-06 20:43:45 +00:00
if strings . Contains ( parts [ 1 ] , "=" ) {
template = parts [ 0 ]
} else {
filter = parts [ 0 ]
template = parts [ 1 ]
}
2015-06-23 16:07:47 +00:00
}
2015-06-24 18:53:55 +00:00
// Parse out the default tags specific to this template
2016-06-30 16:49:53 +00:00
var tags models . Tags
2015-06-23 19:49:46 +00:00
if strings . Contains ( parts [ len ( parts ) - 1 ] , "=" ) {
tagStrs := strings . Split ( parts [ len ( parts ) - 1 ] , "," )
for _ , kv := range tagStrs {
parts := strings . Split ( kv , "=" )
2016-06-30 16:49:53 +00:00
tags . SetString ( parts [ 0 ] , parts [ 1 ] )
2015-06-23 19:49:46 +00:00
}
}
2015-06-24 18:08:36 +00:00
tmpl , err := NewTemplate ( template , tags , options . Separator )
2015-06-23 05:28:52 +00:00
if err != nil {
return nil , err
}
2015-06-23 16:07:47 +00:00
matcher . Add ( filter , tmpl )
2015-06-22 20:53:51 +00:00
}
2015-06-24 18:08:36 +00:00
return & Parser { matcher : matcher , tags : options . DefaultTags } , nil
}
// NewParser returns a GraphiteParser instance.
2015-09-16 20:33:08 +00:00
func NewParser ( templates [ ] string , defaultTags models . Tags ) ( * Parser , error ) {
2015-06-24 18:08:36 +00:00
return NewParserWithOptions (
Options {
Templates : templates ,
DefaultTags : defaultTags ,
Separator : DefaultSeparator ,
} )
2015-06-22 20:53:51 +00:00
}
// Parse performs Graphite parsing of a single line.
2015-09-16 20:33:08 +00:00
func ( p * Parser ) Parse ( line string ) ( models . Point , error ) {
2015-06-22 20:53:51 +00:00
// Break into 3 fields (name, value, timestamp).
fields := strings . Fields ( line )
2015-06-25 05:48:36 +00:00
if len ( fields ) != 2 && len ( fields ) != 3 {
return nil , fmt . Errorf ( "received %q which doesn't have required fields" , line )
2015-06-22 20:53:51 +00:00
}
// decode the name and tags
2015-10-06 21:25:29 +00:00
template := p . matcher . Match ( fields [ 0 ] )
2015-10-07 19:31:46 +00:00
measurement , tags , field , err := template . Apply ( fields [ 0 ] )
if err != nil {
return nil , err
}
2015-06-25 05:38:10 +00:00
// Could not extract measurement, use the raw value
if measurement == "" {
measurement = fields [ 0 ]
2015-06-22 20:53:51 +00:00
}
// Parse value.
v , err := strconv . ParseFloat ( fields [ 1 ] , 64 )
if err != nil {
2015-06-25 05:33:42 +00:00
return nil , fmt . Errorf ( ` field "%s" value: %s ` , fields [ 0 ] , err )
2015-06-22 20:53:51 +00:00
}
2015-10-27 16:21:54 +00:00
if math . IsNaN ( v ) || math . IsInf ( v , 0 ) {
2016-02-08 11:01:01 +00:00
return nil , & UnsupportedValueError { Field : fields [ 0 ] , Value : v }
2015-10-27 16:21:54 +00:00
}
2015-09-21 11:26:43 +00:00
fieldValues := map [ string ] interface { } { }
2015-09-20 20:17:50 +00:00
if field != "" {
2015-09-21 11:26:43 +00:00
fieldValues [ field ] = v
2015-09-20 20:17:50 +00:00
} else {
2015-09-21 11:26:43 +00:00
fieldValues [ "value" ] = v
2015-09-20 20:17:50 +00:00
}
2015-06-22 20:53:51 +00:00
2015-06-25 05:48:36 +00:00
// If no 3rd field, use now as timestamp
timestamp := time . Now ( ) . UTC ( )
2015-06-22 20:53:51 +00:00
2015-06-25 05:48:36 +00:00
if len ( fields ) == 3 {
// Parse timestamp.
unixTime , err := strconv . ParseFloat ( fields [ 2 ] , 64 )
if err != nil {
return nil , fmt . Errorf ( ` field "%s" time: %s ` , fields [ 0 ] , err )
}
// -1 is a special value that gets converted to current UTC time
// See https://github.com/graphite-project/carbon/issues/54
if unixTime != float64 ( - 1 ) {
// Check if we have fractional seconds
timestamp = time . Unix ( int64 ( unixTime ) , int64 ( ( unixTime - math . Floor ( unixTime ) ) * float64 ( time . Second ) ) )
2015-08-24 16:34:47 +00:00
if timestamp . Before ( MinDate ) || timestamp . After ( MaxDate ) {
return nil , fmt . Errorf ( "timestamp out of range" )
}
2015-06-25 05:48:36 +00:00
}
}
2015-06-22 20:53:51 +00:00
2015-06-24 18:53:55 +00:00
// Set the default tags on the point if they are not already set
2016-06-30 16:49:53 +00:00
for _ , t := range p . tags {
if _ , ok := tags [ string ( t . Key ) ] ; ! ok {
tags [ string ( t . Key ) ] = string ( t . Value )
2015-06-23 19:35:39 +00:00
}
}
2016-06-30 16:49:53 +00:00
return models . NewPoint ( measurement , models . NewTags ( tags ) , fieldValues , timestamp )
2015-06-22 20:53:51 +00:00
}
2015-11-23 17:39:52 +00:00
// ApplyTemplate extracts the template fields from the given line and
2015-11-22 19:23:56 +00:00
// returns the measurement name and tags.
2015-10-07 19:31:46 +00:00
func ( p * Parser ) ApplyTemplate ( line string ) ( string , map [ string ] string , string , error ) {
2015-10-06 21:25:29 +00:00
// Break line into fields (name, value, timestamp), only name is used
fields := strings . Fields ( line )
if len ( fields ) == 0 {
2015-10-07 19:31:46 +00:00
return "" , make ( map [ string ] string ) , "" , nil
2015-10-06 21:25:29 +00:00
}
// decode the name and tags
template := p . matcher . Match ( fields [ 0 ] )
2015-10-08 17:08:58 +00:00
name , tags , field , err := template . Apply ( fields [ 0 ] )
2015-10-07 21:42:45 +00:00
// Set the default tags on the point if they are not already set
2016-06-30 16:49:53 +00:00
for _ , t := range p . tags {
if _ , ok := tags [ string ( t . Key ) ] ; ! ok {
tags [ string ( t . Key ) ] = string ( t . Value )
2015-10-07 21:42:45 +00:00
}
}
2015-10-08 17:08:58 +00:00
return name , tags , field , err
2015-10-06 21:25:29 +00:00
}
2016-12-31 01:17:59 +00:00
// template represents a pattern and tags to map a graphite metric string to a influxdb Point.
2015-06-23 05:12:02 +00:00
type template struct {
2015-06-22 22:47:03 +00:00
tags [ ] string
2015-09-16 20:33:08 +00:00
defaultTags models . Tags
2015-06-22 22:47:03 +00:00
greedyMeasurement bool
2015-06-25 05:33:42 +00:00
separator string
2015-06-22 22:47:03 +00:00
}
2015-11-22 19:23:56 +00:00
// NewTemplate returns a new template ensuring it has a measurement
// specified.
2015-09-16 20:33:08 +00:00
func NewTemplate ( pattern string , defaultTags models . Tags , separator string ) ( * template , error ) {
2015-06-23 05:12:02 +00:00
tags := strings . Split ( pattern , "." )
2015-06-24 18:08:36 +00:00
hasMeasurement := false
2015-06-25 05:33:42 +00:00
template := & template { tags : tags , defaultTags : defaultTags , separator : separator }
2015-06-22 22:47:03 +00:00
2015-06-24 18:08:36 +00:00
for _ , tag := range tags {
2015-06-22 22:47:03 +00:00
if strings . HasPrefix ( tag , "measurement" ) {
2015-06-24 18:08:36 +00:00
hasMeasurement = true
2015-06-22 22:47:03 +00:00
}
if tag == "measurement*" {
2015-06-23 05:12:02 +00:00
template . greedyMeasurement = true
2015-06-22 22:47:03 +00:00
}
}
2015-06-24 18:08:36 +00:00
if ! hasMeasurement {
2015-06-23 05:12:02 +00:00
return nil , fmt . Errorf ( "no measurement specified for template. %q" , pattern )
2015-06-22 22:47:03 +00:00
}
2015-06-23 05:12:02 +00:00
return template , nil
2015-06-22 22:47:03 +00:00
}
2015-11-23 17:39:52 +00:00
// Apply extracts the template fields from the given line and returns the measurement
2016-12-31 01:17:59 +00:00
// name and tags.
2015-10-07 19:31:46 +00:00
func ( t * template ) Apply ( line string ) ( string , map [ string ] string , string , error ) {
2015-06-22 22:47:03 +00:00
fields := strings . Split ( line , "." )
2015-06-22 20:53:51 +00:00
var (
2016-02-14 22:36:02 +00:00
measurement [ ] string
tags = make ( map [ string ] [ ] string )
field string
hasFieldWildcard = false
hasMeasurementWildcard = false
2015-06-22 20:53:51 +00:00
)
2015-06-23 19:49:46 +00:00
// Set any default tags
2016-06-30 16:49:53 +00:00
for _ , t := range t . defaultTags {
tags [ string ( t . Key ) ] = append ( tags [ string ( t . Key ) ] , string ( t . Value ) )
2015-06-23 19:49:46 +00:00
}
2016-02-10 14:27:28 +00:00
// See if an invalid combination has been specified in the template:
for _ , tag := range t . tags {
if tag == "measurement*" {
2016-02-14 22:36:02 +00:00
hasMeasurementWildcard = true
2016-02-10 14:27:28 +00:00
} else if tag == "field*" {
2016-02-14 22:36:02 +00:00
hasFieldWildcard = true
2016-02-10 14:27:28 +00:00
}
}
2016-02-14 22:36:02 +00:00
if hasFieldWildcard && hasMeasurementWildcard {
2016-02-10 14:27:28 +00:00
return "" , nil , "" , fmt . Errorf ( "either 'field*' or 'measurement*' can be used in each template (but not both together): %q" , strings . Join ( t . tags , t . separator ) )
}
2015-06-23 05:12:02 +00:00
for i , tag := range t . tags {
2015-06-22 22:47:03 +00:00
if i >= len ( fields ) {
continue
2015-06-22 20:53:51 +00:00
}
2015-06-22 22:47:03 +00:00
if tag == "measurement" {
2015-06-24 18:08:36 +00:00
measurement = append ( measurement , fields [ i ] )
2015-09-20 20:17:50 +00:00
} else if tag == "field" {
2015-10-07 19:31:46 +00:00
if len ( field ) != 0 {
return "" , nil , "" , fmt . Errorf ( "'field' can only be used once in each template: %q" , line )
}
2015-11-22 19:23:56 +00:00
field = fields [ i ]
2016-02-06 13:32:28 +00:00
} else if tag == "field*" {
2016-02-10 11:46:18 +00:00
field = strings . Join ( fields [ i : ] , t . separator )
2016-02-06 13:32:28 +00:00
break
2015-06-22 22:47:03 +00:00
} else if tag == "measurement*" {
2015-06-24 18:08:36 +00:00
measurement = append ( measurement , fields [ i : ] ... )
2015-06-22 21:39:28 +00:00
break
2015-06-22 23:11:27 +00:00
} else if tag != "" {
2016-01-21 15:09:11 +00:00
tags [ tag ] = append ( tags [ tag ] , fields [ i ] )
2015-06-22 20:53:51 +00:00
}
}
2016-01-21 15:09:11 +00:00
// Convert to map of strings.
out_tags := make ( map [ string ] string )
for k , values := range tags {
out_tags [ k ] = strings . Join ( values , t . separator )
}
return strings . Join ( measurement , t . separator ) , out_tags , field , nil
2015-06-22 20:53:51 +00:00
}
2015-06-23 05:28:52 +00:00
2015-06-24 18:53:55 +00:00
// matcher determines which template should be applied to a given metric
// based on a filter tree.
2015-06-23 05:28:52 +00:00
type matcher struct {
2015-06-24 04:33:11 +00:00
root * node
2015-06-23 16:07:47 +00:00
defaultTemplate * template
}
2015-06-24 04:33:11 +00:00
func newMatcher ( ) * matcher {
return & matcher {
root : & node { } ,
}
}
2015-06-24 18:53:55 +00:00
2016-12-31 01:17:59 +00:00
// Add inserts the template in the filter tree based the given filter.
2015-06-24 18:53:55 +00:00
func ( m * matcher ) Add ( filter string , template * template ) {
if filter == "" {
2015-06-23 16:07:47 +00:00
m . AddDefaultTemplate ( template )
return
}
2015-06-24 18:53:55 +00:00
m . root . Insert ( filter , template )
2015-06-23 16:07:47 +00:00
}
func ( m * matcher ) AddDefaultTemplate ( template * template ) {
m . defaultTemplate = template
2015-06-23 05:28:52 +00:00
}
2016-12-31 01:17:59 +00:00
// Match returns the template that matches the given graphite line.
2015-06-23 05:28:52 +00:00
func ( m * matcher ) Match ( line string ) * template {
2015-06-24 04:33:11 +00:00
tmpl := m . root . Search ( line )
if tmpl != nil {
return tmpl
}
return m . defaultTemplate
}
2015-06-23 16:07:47 +00:00
2015-06-25 05:33:42 +00:00
// node is an item in a sorted k-ary tree. Each child is sorted by its value.
// The special value of "*", is always last.
2015-06-24 04:33:11 +00:00
type node struct {
value string
children nodes
template * template
}
func ( n * node ) insert ( values [ ] string , template * template ) {
2015-06-24 19:21:53 +00:00
// Add the end, set the template
2015-06-24 04:33:11 +00:00
if len ( values ) == 0 {
2015-06-24 19:21:53 +00:00
n . template = template
2015-06-24 04:33:11 +00:00
return
2015-06-23 16:07:47 +00:00
}
2015-06-24 18:53:55 +00:00
// See if the the current element already exists in the tree. If so, insert the
// into that sub-tree
2015-06-24 04:33:11 +00:00
for _ , v := range n . children {
if v . value == values [ 0 ] {
v . insert ( values [ 1 : ] , template )
return
2015-06-23 16:07:47 +00:00
}
}
2015-06-24 18:53:55 +00:00
// New element, add it to the tree and sort the children
2015-06-24 19:21:53 +00:00
newNode := & node { value : values [ 0 ] }
2015-06-24 04:33:11 +00:00
n . children = append ( n . children , newNode )
sort . Sort ( & n . children )
2015-06-24 18:53:55 +00:00
2015-12-21 07:05:37 +00:00
// Inherit template if value is wildcard
if values [ 0 ] == "*" {
newNode . template = n . template
}
2015-06-24 18:53:55 +00:00
// Now insert the rest of the tree into the new element
2015-06-24 04:33:11 +00:00
newNode . insert ( values [ 1 : ] , template )
2015-06-23 16:07:47 +00:00
}
2015-06-25 05:33:42 +00:00
// Insert inserts the given string template into the tree. The filter string is separated
2015-06-24 18:53:55 +00:00
// on "." and each part is used as the path in the tree.
2015-06-25 05:33:42 +00:00
func ( n * node ) Insert ( filter string , template * template ) {
n . insert ( strings . Split ( filter , "." ) , template )
2015-06-23 16:07:47 +00:00
}
2015-06-24 04:33:11 +00:00
func ( n * node ) search ( lineParts [ ] string ) * template {
2015-06-24 18:53:55 +00:00
// Nothing to search
2015-06-24 04:33:11 +00:00
if len ( lineParts ) == 0 || len ( n . children ) == 0 {
return n . template
}
2015-06-23 16:07:47 +00:00
2015-06-24 04:33:11 +00:00
// If last element is a wildcard, don't include in this search since it's sorted
2015-06-25 05:33:42 +00:00
// to the end but lexicographically it would not always be and sort.Search assumes
2015-06-24 04:33:11 +00:00
// the slice is sorted.
length := len ( n . children )
if n . children [ length - 1 ] . value == "*" {
2015-11-22 19:23:56 +00:00
length --
2015-06-24 04:33:11 +00:00
}
2015-06-24 18:53:55 +00:00
// Find the index of child with an exact match
2015-06-24 04:33:11 +00:00
i := sort . Search ( length , func ( i int ) bool {
2015-07-06 20:21:24 +00:00
return n . children [ i ] . value >= lineParts [ 0 ]
2015-06-24 04:33:11 +00:00
} )
2015-06-23 16:07:47 +00:00
2015-06-24 18:53:55 +00:00
// Found an exact match, so search that child sub-tree
2015-06-24 04:33:11 +00:00
if i < len ( n . children ) && n . children [ i ] . value == lineParts [ 0 ] {
return n . children [ i ] . search ( lineParts [ 1 : ] )
2015-07-09 18:24:06 +00:00
}
// Not an exact match, see if we have a wildcard child to search
if n . children [ len ( n . children ) - 1 ] . value == "*" {
return n . children [ len ( n . children ) - 1 ] . search ( lineParts [ 1 : ] )
2015-06-23 05:28:52 +00:00
}
2015-06-24 04:33:11 +00:00
return n . template
2015-06-23 05:28:52 +00:00
}
2015-06-24 04:33:11 +00:00
func ( n * node ) Search ( line string ) * template {
return n . search ( strings . Split ( line , "." ) )
}
type nodes [ ] * node
// Less returns a boolean indicating whether the filter at position j
2015-06-25 05:33:42 +00:00
// is less than the filter at position k. Filters are order by string
2015-06-24 04:33:11 +00:00
// comparison of each component parts. A wildcard value "*" is never
// less than a non-wildcard value.
//
// For example, the filters:
// "*.*"
2015-06-25 05:33:42 +00:00
// "servers.*"
// "servers.localhost"
2015-06-24 04:33:11 +00:00
// "*.localhost"
//
// Would be sorted as:
2015-06-25 05:33:42 +00:00
// "servers.localhost"
// "servers.*"
2015-06-24 04:33:11 +00:00
// "*.localhost"
// "*.*"
func ( n * nodes ) Less ( j , k int ) bool {
if ( * n ) [ j ] . value == "*" && ( * n ) [ k ] . value != "*" {
return false
}
if ( * n ) [ j ] . value != "*" && ( * n ) [ k ] . value == "*" {
return true
}
return ( * n ) [ j ] . value < ( * n ) [ k ] . value
}
func ( n * nodes ) Swap ( i , j int ) { ( * n ) [ i ] , ( * n ) [ j ] = ( * n ) [ j ] , ( * n ) [ i ] }
func ( n * nodes ) Len ( ) int { return len ( * n ) }