github.com/hashicorp/go-metrics@v0.5.3/datadog/dogstatsd.go (about)

     1  package datadog
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/DataDog/datadog-go/statsd"
     8  	"github.com/hashicorp/go-metrics"
     9  )
    10  
    11  // DogStatsdSink provides a MetricSink that can be used
    12  // with a dogstatsd server. It utilizes the Dogstatsd client at github.com/DataDog/datadog-go/statsd
    13  type DogStatsdSink struct {
    14  	client            *statsd.Client
    15  	hostName          string
    16  	propagateHostname bool
    17  }
    18  
    19  // NewDogStatsdSink is used to create a new DogStatsdSink with sane defaults
    20  func NewDogStatsdSink(addr string, hostName string) (*DogStatsdSink, error) {
    21  	client, err := statsd.New(addr)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  	sink := &DogStatsdSink{
    26  		client:            client,
    27  		hostName:          hostName,
    28  		propagateHostname: false,
    29  	}
    30  	return sink, nil
    31  }
    32  
    33  // SetTags sets common tags on the Dogstatsd Client that will be sent
    34  // along with all dogstatsd packets.
    35  // Ref: http://docs.datadoghq.com/guides/dogstatsd/#tags
    36  func (s *DogStatsdSink) SetTags(tags []string) {
    37  	s.client.Tags = tags
    38  }
    39  
    40  // EnableHostnamePropagation forces a Dogstatsd `host` tag with the value specified by `s.HostName`
    41  // Since the go-metrics package has its own mechanism for attaching a hostname to metrics,
    42  // setting the `propagateHostname` flag ensures that `s.HostName` overrides the host tag naively set by the DogStatsd server
    43  func (s *DogStatsdSink) EnableHostNamePropagation() {
    44  	s.propagateHostname = true
    45  }
    46  
    47  func (s *DogStatsdSink) flattenKey(parts []string) string {
    48  	joined := strings.Join(parts, ".")
    49  	return strings.Map(sanitize, joined)
    50  }
    51  
    52  func sanitize(r rune) rune {
    53  	switch r {
    54  	case ':':
    55  		fallthrough
    56  	case ' ':
    57  		return '_'
    58  	default:
    59  		return r
    60  	}
    61  }
    62  
    63  func (s *DogStatsdSink) parseKey(key []string) ([]string, []metrics.Label) {
    64  	// 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
    65  	// The `host` tag is either forced here, or set downstream by the DogStatsd server
    66  
    67  	var labels []metrics.Label
    68  	hostName := s.hostName
    69  
    70  	// Splice the hostname out of the key
    71  	for i, el := range key {
    72  		if el == hostName {
    73  			// We need an intermediate key to prevent clobbering the
    74  			// original backing array that other sinks might be consuming.
    75  			tempKey := append([]string{}, key[:i]...)
    76  			key = append(tempKey, key[i+1:]...)
    77  			break
    78  		}
    79  	}
    80  
    81  	if s.propagateHostname {
    82  		labels = append(labels, metrics.Label{"host", hostName})
    83  	}
    84  	return key, labels
    85  }
    86  
    87  // Implementation of methods in the MetricSink interface
    88  
    89  func (s *DogStatsdSink) SetGauge(key []string, val float32) {
    90  	s.SetGaugeWithLabels(key, val, nil)
    91  }
    92  
    93  func (s *DogStatsdSink) SetPrecisionGauge(key []string, val float64) {
    94  	s.SetPrecisionGaugeWithLabels(key, val, nil)
    95  }
    96  
    97  func (s *DogStatsdSink) IncrCounter(key []string, val float32) {
    98  	s.IncrCounterWithLabels(key, val, nil)
    99  }
   100  
   101  // EmitKey is not implemented since DogStatsd does not provide a metric type that holds an
   102  // arbitrary number of values
   103  func (s *DogStatsdSink) EmitKey(key []string, val float32) {
   104  }
   105  
   106  func (s *DogStatsdSink) AddSample(key []string, val float32) {
   107  	s.AddSampleWithLabels(key, val, nil)
   108  }
   109  
   110  // The following ...WithLabels methods correspond to Datadog's Tag extension to Statsd.
   111  // http://docs.datadoghq.com/guides/dogstatsd/#tags
   112  func (s *DogStatsdSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {
   113  	flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
   114  	rate := 1.0
   115  	s.client.Gauge(flatKey, float64(val), tags, rate)
   116  }
   117  
   118  // The following ...WithLabels methods correspond to Datadog's Tag extension to Statsd.
   119  // http://docs.datadoghq.com/guides/dogstatsd/#tags
   120  func (s *DogStatsdSink) SetPrecisionGaugeWithLabels(key []string, val float64, labels []metrics.Label) {
   121  	flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
   122  	rate := 1.0
   123  	s.client.Gauge(flatKey, val, tags, rate)
   124  }
   125  
   126  func (s *DogStatsdSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {
   127  	flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
   128  	rate := 1.0
   129  	s.client.Count(flatKey, int64(val), tags, rate)
   130  }
   131  
   132  func (s *DogStatsdSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {
   133  	flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
   134  	rate := 1.0
   135  	s.client.TimeInMilliseconds(flatKey, float64(val), tags, rate)
   136  }
   137  
   138  // Shutdown disables further metric collection, blocks to flush data, and tears down the sink.
   139  func (s *DogStatsdSink) Shutdown() {
   140  	s.client.Close()
   141  }
   142  
   143  func (s *DogStatsdSink) getFlatkeyAndCombinedLabels(key []string, labels []metrics.Label) (string, []string) {
   144  	key, parsedLabels := s.parseKey(key)
   145  	flatKey := s.flattenKey(key)
   146  	labels = append(labels, parsedLabels...)
   147  
   148  	var tags []string
   149  	for _, label := range labels {
   150  		label.Name = strings.Map(sanitize, label.Name)
   151  		label.Value = strings.Map(sanitize, label.Value)
   152  		if label.Value != "" {
   153  			tags = append(tags, fmt.Sprintf("%s:%s", label.Name, label.Value))
   154  		} else {
   155  			tags = append(tags, label.Name)
   156  		}
   157  	}
   158  
   159  	return flatKey, tags
   160  }