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