2016-02-13 00:50:37 +00:00
package datadog
import (
"fmt"
"strings"
"github.com/DataDog/datadog-go/statsd"
2017-08-07 23:37:52 +00:00
"github.com/armon/go-metrics"
2016-02-13 00:50:37 +00:00
)
// DogStatsdSink provides a MetricSink that can be used
// with a dogstatsd server. It utilizes the Dogstatsd client at github.com/DataDog/datadog-go/statsd
type DogStatsdSink struct {
client * statsd . Client
hostName string
propagateHostname bool
}
// NewDogStatsdSink is used to create a new DogStatsdSink with sane defaults
func NewDogStatsdSink ( addr string , hostName string ) ( * DogStatsdSink , error ) {
client , err := statsd . New ( addr )
if err != nil {
return nil , err
}
sink := & DogStatsdSink {
client : client ,
hostName : hostName ,
propagateHostname : false ,
}
return sink , nil
}
// SetTags sets common tags on the Dogstatsd Client that will be sent
// along with all dogstatsd packets.
// Ref: http://docs.datadoghq.com/guides/dogstatsd/#tags
func ( s * DogStatsdSink ) SetTags ( tags [ ] string ) {
s . client . Tags = tags
}
// EnableHostnamePropagation forces a Dogstatsd `host` tag with the value specified by `s.HostName`
// Since the go-metrics package has its own mechanism for attaching a hostname to metrics,
// setting the `propagateHostname` flag ensures that `s.HostName` overrides the host tag naively set by the DogStatsd server
func ( s * DogStatsdSink ) EnableHostNamePropagation ( ) {
s . propagateHostname = true
}
func ( s * DogStatsdSink ) flattenKey ( parts [ ] string ) string {
joined := strings . Join ( parts , "." )
2017-08-07 23:37:52 +00:00
return strings . Map ( sanitize , joined )
}
func sanitize ( r rune ) rune {
switch r {
case ':' :
fallthrough
case ' ' :
return '_'
default :
return r
}
2016-02-13 00:50:37 +00:00
}
2017-08-07 23:37:52 +00:00
func ( s * DogStatsdSink ) parseKey ( key [ ] string ) ( [ ] string , [ ] metrics . Label ) {
2016-02-13 00:50:37 +00:00
// Since DogStatsd supports dimensionality via tags on metric keys, this sink's approach is to splice the hostname out of the key in favor of a `host` tag
// The `host` tag is either forced here, or set downstream by the DogStatsd server
2017-08-07 23:37:52 +00:00
var labels [ ] metrics . Label
2016-02-13 00:50:37 +00:00
hostName := s . hostName
2017-08-07 23:37:52 +00:00
// Splice the hostname out of the key
2016-02-13 00:50:37 +00:00
for i , el := range key {
if el == hostName {
key = append ( key [ : i ] , key [ i + 1 : ] ... )
2017-08-07 23:37:52 +00:00
break
2016-02-13 00:50:37 +00:00
}
}
if s . propagateHostname {
2017-08-07 23:37:52 +00:00
labels = append ( labels , metrics . Label { "host" , hostName } )
2016-02-13 00:50:37 +00:00
}
2017-08-07 23:37:52 +00:00
return key , labels
2016-02-13 00:50:37 +00:00
}
// Implementation of methods in the MetricSink interface
func ( s * DogStatsdSink ) SetGauge ( key [ ] string , val float32 ) {
2017-08-07 23:37:52 +00:00
s . SetGaugeWithLabels ( key , val , nil )
2016-02-13 00:50:37 +00:00
}
func ( s * DogStatsdSink ) IncrCounter ( key [ ] string , val float32 ) {
2017-08-07 23:37:52 +00:00
s . IncrCounterWithLabels ( key , val , nil )
2016-02-13 00:50:37 +00:00
}
// EmitKey is not implemented since DogStatsd does not provide a metric type that holds an
// arbitrary number of values
func ( s * DogStatsdSink ) EmitKey ( key [ ] string , val float32 ) {
}
func ( s * DogStatsdSink ) AddSample ( key [ ] string , val float32 ) {
2017-08-07 23:37:52 +00:00
s . AddSampleWithLabels ( key , val , nil )
2016-02-13 00:50:37 +00:00
}
2017-08-07 23:37:52 +00:00
// The following ...WithLabels methods correspond to Datadog's Tag extension to Statsd.
2016-02-13 00:50:37 +00:00
// http://docs.datadoghq.com/guides/dogstatsd/#tags
2017-08-07 23:37:52 +00:00
func ( s * DogStatsdSink ) SetGaugeWithLabels ( key [ ] string , val float32 , labels [ ] metrics . Label ) {
flatKey , tags := s . getFlatkeyAndCombinedLabels ( key , labels )
2016-02-13 00:50:37 +00:00
rate := 1.0
s . client . Gauge ( flatKey , float64 ( val ) , tags , rate )
}
2017-08-07 23:37:52 +00:00
func ( s * DogStatsdSink ) IncrCounterWithLabels ( key [ ] string , val float32 , labels [ ] metrics . Label ) {
flatKey , tags := s . getFlatkeyAndCombinedLabels ( key , labels )
2016-02-13 00:50:37 +00:00
rate := 1.0
s . client . Count ( flatKey , int64 ( val ) , tags , rate )
}
2017-08-07 23:37:52 +00:00
func ( s * DogStatsdSink ) AddSampleWithLabels ( key [ ] string , val float32 , labels [ ] metrics . Label ) {
flatKey , tags := s . getFlatkeyAndCombinedLabels ( key , labels )
2016-02-13 00:50:37 +00:00
rate := 1.0
s . client . TimeInMilliseconds ( flatKey , float64 ( val ) , tags , rate )
}
2017-08-07 23:37:52 +00:00
func ( s * DogStatsdSink ) getFlatkeyAndCombinedLabels ( key [ ] string , labels [ ] metrics . Label ) ( string , [ ] string ) {
key , parsedLabels := s . parseKey ( key )
2016-02-13 00:50:37 +00:00
flatKey := s . flattenKey ( key )
2017-08-07 23:37:52 +00:00
labels = append ( labels , parsedLabels ... )
var tags [ ] string
for _ , label := range labels {
label . Name = strings . Map ( sanitize , label . Name )
label . Value = strings . Map ( sanitize , label . Value )
if label . Value != "" {
tags = append ( tags , fmt . Sprintf ( "%s:%s" , label . Name , label . Value ) )
} else {
tags = append ( tags , label . Name )
}
}
2016-02-13 00:50:37 +00:00
return flatKey , tags
}