go.temporal.io/server@v1.23.0/common/metrics/tally/statsd/reporter.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package statsd 26 27 import ( 28 "sort" 29 "strings" 30 "time" 31 32 "github.com/cactus/go-statsd-client/v5/statsd" 33 "github.com/uber-go/tally/v4" 34 tallystatsdreporter "github.com/uber-go/tally/v4/statsd" 35 ) 36 37 type temporalTallyStatsdReporter struct { 38 //Wrapper on top of "github.com/uber-go/tally/statsd" 39 tallystatsd tally.StatsReporter 40 41 tagSeparator string 42 } 43 44 // Options allows configuration of Temporal-specific statsd reporter options in addition to Tally's statsd reporter options. 45 type Options struct { 46 TallyOptions tallystatsdreporter.Options 47 48 TagSeparator string 49 } 50 51 func (r *temporalTallyStatsdReporter) metricNameWithTags(originalName string, tags map[string]string) string { 52 if r.tagSeparator != "" { 53 return appendSeparatedTags(originalName, r.tagSeparator, tags) 54 } 55 return embedTags(originalName, tags) 56 } 57 58 // NewReporter is a wrapper on top of "github.com/uber-go/tally/statsd" 59 // The purpose is to support tagging. 60 // The implementation will append tags as metric name suffixes by default or with a separator if one is specified. 61 func NewReporter(statsd statsd.Statter, opts Options) tally.StatsReporter { 62 return &temporalTallyStatsdReporter{ 63 tallystatsd: tallystatsdreporter.NewReporter(statsd, opts.TallyOptions), 64 tagSeparator: opts.TagSeparator, 65 } 66 } 67 68 func (r *temporalTallyStatsdReporter) ReportCounter(name string, tags map[string]string, value int64) { 69 newName := r.metricNameWithTags(name, tags) 70 r.tallystatsd.ReportCounter(newName, map[string]string{}, value) 71 } 72 73 func (r *temporalTallyStatsdReporter) ReportGauge(name string, tags map[string]string, value float64) { 74 newName := r.metricNameWithTags(name, tags) 75 r.tallystatsd.ReportGauge(newName, map[string]string{}, value) 76 } 77 78 func (r *temporalTallyStatsdReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { 79 newName := r.metricNameWithTags(name, tags) 80 r.tallystatsd.ReportTimer(newName, map[string]string{}, interval) 81 } 82 83 func (r *temporalTallyStatsdReporter) ReportHistogramValueSamples( 84 name string, 85 tags map[string]string, 86 buckets tally.Buckets, 87 bucketLowerBound, 88 bucketUpperBound float64, 89 samples int64, 90 ) { 91 newName := r.metricNameWithTags(name, tags) 92 r.tallystatsd.ReportHistogramValueSamples(newName, map[string]string{}, buckets, bucketLowerBound, bucketUpperBound, samples) 93 } 94 95 func (r *temporalTallyStatsdReporter) ReportHistogramDurationSamples( 96 name string, 97 tags map[string]string, 98 buckets tally.Buckets, 99 bucketLowerBound, 100 bucketUpperBound time.Duration, 101 samples int64, 102 ) { 103 newName := r.metricNameWithTags(name, tags) 104 r.tallystatsd.ReportHistogramDurationSamples(newName, map[string]string{}, buckets, bucketLowerBound, bucketUpperBound, samples) 105 } 106 107 func (r *temporalTallyStatsdReporter) Capabilities() tally.Capabilities { 108 return r.tallystatsd.Capabilities() 109 } 110 111 func (r *temporalTallyStatsdReporter) Flush() { 112 r.tallystatsd.Flush() 113 } 114 115 // embedTags adds the sorted list of tags directly in the stat name. 116 // For example, if the stat is `hello.world` and the tags are `{universe: milkyWay, planet: earth}`, 117 // the stat will be emitted as `hello.world.planet.earth.universe.milkyWay`. 118 func embedTags(name string, tags map[string]string) string { 119 // Sort tags so they are in a consistent order when emitted. 120 var keys []string 121 for k := range tags { 122 keys = append(keys, k) 123 } 124 sort.Strings(keys) 125 126 var buffer strings.Builder 127 buffer.WriteString(name) 128 for _, tk := range keys { 129 // adding "." as delimiter so that it will show as different parts in Graphite/Grafana 130 buffer.WriteString("." + tk + "." + tags[tk]) 131 } 132 133 return buffer.String() 134 } 135 136 // appendSeparatedTags adds the sorted list of tags using the DogStatsd/InfluxDB supported tagging protocol. 137 // For example, if the stat is `hello.world` and the tags are `{universe: milkyWay, planet: earth}` and the separator is `,`, 138 // the stat will be emitted as `hello.world,planet=earth,universe=milkyWay`. 139 // 140 // For more details on the protocol see: 141 // - Datadog: https://docs.datadoghq.com/developers/dogstatsd/datagram_shell 142 // - InfluxDB: https://github.com/influxdata/telegraf/blob/ce9411343076b56dabd77fc8845cc58872d4b2e6/plugins/inputs/statsd/README.md#influx-statsd 143 func appendSeparatedTags(name string, separator string, tags map[string]string) string { 144 var buffer strings.Builder 145 buffer.WriteString(name) 146 for k, v := range tags { 147 buffer.WriteString(separator + k + "=" + v) 148 } 149 return buffer.String() 150 }