2014-12-31 21:37:58 +00:00
|
|
|
package graphite
|
2014-03-04 01:52:57 +00:00
|
|
|
|
|
|
|
import (
|
2014-10-22 05:32:19 +00:00
|
|
|
"errors"
|
2014-12-30 22:46:33 +00:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2014-05-16 07:52:07 +00:00
|
|
|
"strings"
|
2014-12-30 22:46:33 +00:00
|
|
|
"time"
|
2014-03-04 01:52:57 +00:00
|
|
|
)
|
|
|
|
|
2014-10-22 05:32:19 +00:00
|
|
|
var (
|
2014-12-31 21:37:58 +00:00
|
|
|
// ErrBindAddressRequired is returned when starting the Server
|
2014-10-22 05:32:19 +00:00
|
|
|
// without a TCP or UDP listening address.
|
|
|
|
ErrBindAddressRequired = errors.New("bind address required")
|
2014-03-04 01:52:57 +00:00
|
|
|
|
2014-12-31 21:37:58 +00:00
|
|
|
// ErrServerClosed return when closing an already closed graphite server.
|
|
|
|
ErrServerClosed = errors.New("server already closed")
|
|
|
|
|
|
|
|
// ErrDatabaseNotSpecified retuned when no database was specified in the config file
|
|
|
|
ErrDatabaseNotSpecified = errors.New("database was not specified in config")
|
2014-12-30 22:46:33 +00:00
|
|
|
|
2014-12-31 21:37:58 +00:00
|
|
|
// ErrServerNotSpecified returned when Server is not specified.
|
|
|
|
ErrServerNotSpecified = errors.New("server not present")
|
2014-10-23 04:21:48 +00:00
|
|
|
)
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2015-01-07 08:10:46 +00:00
|
|
|
// SeriesWriter defines the interface for the destination of the data.
|
2015-01-07 07:18:14 +00:00
|
|
|
type SeriesWriter interface {
|
2015-01-06 03:14:43 +00:00
|
|
|
WriteSeries(database, retentionPolicy, name string, tags map[string]string, timestamp time.Time, values map[string]interface{}) error
|
|
|
|
}
|
|
|
|
|
2015-01-07 08:10:46 +00:00
|
|
|
// Parser encapulates a Graphite Parser.
|
2015-01-07 07:18:14 +00:00
|
|
|
type Parser struct {
|
|
|
|
Separator string
|
|
|
|
LastEnabled bool
|
2014-10-22 05:32:19 +00:00
|
|
|
}
|
|
|
|
|
2015-01-08 00:09:03 +00:00
|
|
|
// metric represents a Metric as processed by the Graphite parser.
|
|
|
|
type Metric struct {
|
|
|
|
Name string
|
|
|
|
Tags map[string]string
|
|
|
|
Value interface{}
|
|
|
|
Timestamp time.Time
|
|
|
|
}
|
|
|
|
|
2015-01-07 08:10:46 +00:00
|
|
|
// NewParser returns a GraphiteParser instance.
|
2015-01-07 07:18:14 +00:00
|
|
|
func NewParser() *Parser {
|
|
|
|
p := Parser{}
|
2015-01-06 23:38:35 +00:00
|
|
|
return &p
|
|
|
|
}
|
|
|
|
|
2015-01-07 08:10:46 +00:00
|
|
|
// Parse performs Graphite parsing of a single line.
|
2015-01-08 00:09:03 +00:00
|
|
|
func (p *Parser) Parse(line string) (*Metric, error) {
|
2014-12-30 22:46:33 +00:00
|
|
|
// Break into 3 fields (name, value, timestamp).
|
2015-01-07 07:18:14 +00:00
|
|
|
fields := strings.Fields(line)
|
2014-12-30 22:46:33 +00:00
|
|
|
if len(fields) != 3 {
|
2015-01-07 07:18:14 +00:00
|
|
|
return nil, fmt.Errorf("received %q which doesn't have three fields", line)
|
2014-12-30 22:46:33 +00:00
|
|
|
}
|
2014-10-22 00:20:43 +00:00
|
|
|
|
2015-01-08 00:09:03 +00:00
|
|
|
m := new(Metric)
|
2014-12-30 22:46:33 +00:00
|
|
|
// decode the name and tags
|
2015-01-07 07:36:56 +00:00
|
|
|
name, tags, err := p.DecodeNameAndTags(fields[0])
|
2014-12-31 21:37:58 +00:00
|
|
|
if err != nil {
|
2014-12-30 22:46:33 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2014-12-31 21:37:58 +00:00
|
|
|
m.Name = name
|
|
|
|
m.Tags = tags
|
2014-11-18 00:23:21 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
// Parse value.
|
|
|
|
v, err := strconv.ParseFloat(fields[1], 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-11-18 00:23:21 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
// Determine if value is a float or an int.
|
|
|
|
if i := int64(v); float64(i) == v {
|
2015-01-06 03:14:43 +00:00
|
|
|
m.Value = int64(v)
|
2014-12-30 22:46:33 +00:00
|
|
|
} else {
|
2015-01-06 03:14:43 +00:00
|
|
|
m.Value = v
|
2014-10-22 00:20:43 +00:00
|
|
|
}
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
// Parse timestamp.
|
|
|
|
unixTime, err := strconv.ParseInt(fields[2], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-10-22 00:20:43 +00:00
|
|
|
}
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2014-12-31 21:37:58 +00:00
|
|
|
m.Timestamp = time.Unix(0, unixTime*int64(time.Millisecond))
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
return m, nil
|
|
|
|
}
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2015-01-07 08:10:46 +00:00
|
|
|
// DecodeNameAndTags parses the name and tags of a single field of a Graphite datum.
|
2015-01-07 07:36:56 +00:00
|
|
|
func (p *Parser) DecodeNameAndTags(field string) (string, map[string]string, error) {
|
2014-12-30 22:46:33 +00:00
|
|
|
var (
|
|
|
|
name string
|
|
|
|
tags = make(map[string]string)
|
|
|
|
)
|
|
|
|
|
|
|
|
// decode the name and tags
|
2015-01-07 07:18:14 +00:00
|
|
|
values := strings.Split(field, p.Separator)
|
2014-12-30 22:46:33 +00:00
|
|
|
if len(values)%2 != 1 {
|
2014-12-31 21:37:58 +00:00
|
|
|
// There should always be an odd number of fields to map a metric name and tags
|
|
|
|
// ex: region.us-west.hostname.server01.cpu -> tags -> region: us-west, hostname: server01, metric name -> cpu
|
2014-12-30 22:46:33 +00:00
|
|
|
return name, tags, fmt.Errorf("received %q which doesn't conform to format of key.value.key.value.metric or metric", field)
|
|
|
|
}
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2015-01-07 07:18:14 +00:00
|
|
|
if p.LastEnabled {
|
2015-01-06 03:14:43 +00:00
|
|
|
name = values[len(values)-1]
|
|
|
|
values = values[0 : len(values)-1]
|
2015-01-07 07:18:14 +00:00
|
|
|
} else {
|
2015-01-06 03:14:43 +00:00
|
|
|
name = values[0]
|
|
|
|
values = values[1:len(values)]
|
|
|
|
}
|
2015-01-07 07:18:14 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
if name == "" {
|
|
|
|
return name, tags, fmt.Errorf("no name specified for metric. %q", field)
|
|
|
|
}
|
2014-11-18 00:23:21 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
// Grab the pairs and throw them in the map
|
|
|
|
for i := 0; i < len(values); i += 2 {
|
|
|
|
k := values[i]
|
|
|
|
v := values[i+1]
|
|
|
|
tags[k] = v
|
|
|
|
}
|
2014-10-22 05:32:19 +00:00
|
|
|
|
2014-12-30 22:46:33 +00:00
|
|
|
return name, tags, nil
|
2014-10-22 00:20:43 +00:00
|
|
|
}
|