1package statsd 2 3import ( 4 "strconv" 5 "strings" 6) 7 8var ( 9 gaugeSymbol = []byte("g") 10 countSymbol = []byte("c") 11 histogramSymbol = []byte("h") 12 distributionSymbol = []byte("d") 13 setSymbol = []byte("s") 14 timingSymbol = []byte("ms") 15 tagSeparatorSymbol = "," 16) 17 18func appendHeader(buffer []byte, namespace string, name string) []byte { 19 if namespace != "" { 20 buffer = append(buffer, namespace...) 21 } 22 buffer = append(buffer, name...) 23 buffer = append(buffer, ':') 24 return buffer 25} 26 27func appendRate(buffer []byte, rate float64) []byte { 28 if rate < 1 { 29 buffer = append(buffer, "|@"...) 30 buffer = strconv.AppendFloat(buffer, rate, 'f', -1, 64) 31 } 32 return buffer 33} 34 35func appendWithoutNewlines(buffer []byte, s string) []byte { 36 // fastpath for strings without newlines 37 if strings.IndexByte(s, '\n') == -1 { 38 return append(buffer, s...) 39 } 40 41 for _, b := range []byte(s) { 42 if b != '\n' { 43 buffer = append(buffer, b) 44 } 45 } 46 return buffer 47} 48 49func appendTags(buffer []byte, globalTags []string, tags []string) []byte { 50 if len(globalTags) == 0 && len(tags) == 0 { 51 return buffer 52 } 53 buffer = append(buffer, "|#"...) 54 firstTag := true 55 56 for _, tag := range globalTags { 57 if !firstTag { 58 buffer = append(buffer, tagSeparatorSymbol...) 59 } 60 buffer = appendWithoutNewlines(buffer, tag) 61 firstTag = false 62 } 63 for _, tag := range tags { 64 if !firstTag { 65 buffer = append(buffer, tagSeparatorSymbol...) 66 } 67 buffer = appendWithoutNewlines(buffer, tag) 68 firstTag = false 69 } 70 return buffer 71} 72 73func appendTagsAggregated(buffer []byte, globalTags []string, tags string) []byte { 74 if len(globalTags) == 0 && tags == "" { 75 return buffer 76 } 77 78 buffer = append(buffer, "|#"...) 79 firstTag := true 80 81 for _, tag := range globalTags { 82 if !firstTag { 83 buffer = append(buffer, tagSeparatorSymbol...) 84 } 85 buffer = appendWithoutNewlines(buffer, tag) 86 firstTag = false 87 } 88 if tags != "" { 89 if !firstTag { 90 buffer = append(buffer, tagSeparatorSymbol...) 91 } 92 buffer = appendWithoutNewlines(buffer, tags) 93 } 94 return buffer 95} 96 97func appendFloatMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64, precision int) []byte { 98 buffer = appendHeader(buffer, namespace, name) 99 buffer = strconv.AppendFloat(buffer, value, 'f', precision, 64) 100 buffer = append(buffer, '|') 101 buffer = append(buffer, typeSymbol...) 102 buffer = appendRate(buffer, rate) 103 buffer = appendTags(buffer, globalTags, tags) 104 return buffer 105} 106 107func appendIntegerMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte { 108 buffer = appendHeader(buffer, namespace, name) 109 buffer = strconv.AppendInt(buffer, value, 10) 110 buffer = append(buffer, '|') 111 buffer = append(buffer, typeSymbol...) 112 buffer = appendRate(buffer, rate) 113 buffer = appendTags(buffer, globalTags, tags) 114 return buffer 115} 116 117func appendStringMetric(buffer []byte, typeSymbol []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte { 118 buffer = appendHeader(buffer, namespace, name) 119 buffer = append(buffer, value...) 120 buffer = append(buffer, '|') 121 buffer = append(buffer, typeSymbol...) 122 buffer = appendRate(buffer, rate) 123 buffer = appendTags(buffer, globalTags, tags) 124 return buffer 125} 126 127func appendGauge(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { 128 return appendFloatMetric(buffer, gaugeSymbol, namespace, globalTags, name, value, tags, rate, -1) 129} 130 131func appendCount(buffer []byte, namespace string, globalTags []string, name string, value int64, tags []string, rate float64) []byte { 132 return appendIntegerMetric(buffer, countSymbol, namespace, globalTags, name, value, tags, rate) 133} 134 135func appendHistogram(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { 136 return appendFloatMetric(buffer, histogramSymbol, namespace, globalTags, name, value, tags, rate, -1) 137} 138 139func appendDistribution(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { 140 return appendFloatMetric(buffer, distributionSymbol, namespace, globalTags, name, value, tags, rate, -1) 141} 142 143func appendSet(buffer []byte, namespace string, globalTags []string, name string, value string, tags []string, rate float64) []byte { 144 return appendStringMetric(buffer, setSymbol, namespace, globalTags, name, value, tags, rate) 145} 146 147func appendTiming(buffer []byte, namespace string, globalTags []string, name string, value float64, tags []string, rate float64) []byte { 148 return appendFloatMetric(buffer, timingSymbol, namespace, globalTags, name, value, tags, rate, 6) 149} 150 151func escapedEventTextLen(text string) int { 152 return len(text) + strings.Count(text, "\n") 153} 154 155func appendEscapedEventText(buffer []byte, text string) []byte { 156 for _, b := range []byte(text) { 157 if b != '\n' { 158 buffer = append(buffer, b) 159 } else { 160 buffer = append(buffer, "\\n"...) 161 } 162 } 163 return buffer 164} 165 166func appendEvent(buffer []byte, event Event, globalTags []string) []byte { 167 escapedTextLen := escapedEventTextLen(event.Text) 168 169 buffer = append(buffer, "_e{"...) 170 buffer = strconv.AppendInt(buffer, int64(len(event.Title)), 10) 171 buffer = append(buffer, tagSeparatorSymbol...) 172 buffer = strconv.AppendInt(buffer, int64(escapedTextLen), 10) 173 buffer = append(buffer, "}:"...) 174 buffer = append(buffer, event.Title...) 175 buffer = append(buffer, '|') 176 if escapedTextLen != len(event.Text) { 177 buffer = appendEscapedEventText(buffer, event.Text) 178 } else { 179 buffer = append(buffer, event.Text...) 180 } 181 182 if !event.Timestamp.IsZero() { 183 buffer = append(buffer, "|d:"...) 184 buffer = strconv.AppendInt(buffer, int64(event.Timestamp.Unix()), 10) 185 } 186 187 if len(event.Hostname) != 0 { 188 buffer = append(buffer, "|h:"...) 189 buffer = append(buffer, event.Hostname...) 190 } 191 192 if len(event.AggregationKey) != 0 { 193 buffer = append(buffer, "|k:"...) 194 buffer = append(buffer, event.AggregationKey...) 195 } 196 197 if len(event.Priority) != 0 { 198 buffer = append(buffer, "|p:"...) 199 buffer = append(buffer, event.Priority...) 200 } 201 202 if len(event.SourceTypeName) != 0 { 203 buffer = append(buffer, "|s:"...) 204 buffer = append(buffer, event.SourceTypeName...) 205 } 206 207 if len(event.AlertType) != 0 { 208 buffer = append(buffer, "|t:"...) 209 buffer = append(buffer, string(event.AlertType)...) 210 } 211 212 buffer = appendTags(buffer, globalTags, event.Tags) 213 return buffer 214} 215 216func appendEscapedServiceCheckText(buffer []byte, text string) []byte { 217 for i := 0; i < len(text); i++ { 218 if text[i] == '\n' { 219 buffer = append(buffer, "\\n"...) 220 } else if text[i] == 'm' && i+1 < len(text) && text[i+1] == ':' { 221 buffer = append(buffer, "m\\:"...) 222 i++ 223 } else { 224 buffer = append(buffer, text[i]) 225 } 226 } 227 return buffer 228} 229 230func appendServiceCheck(buffer []byte, serviceCheck ServiceCheck, globalTags []string) []byte { 231 buffer = append(buffer, "_sc|"...) 232 buffer = append(buffer, serviceCheck.Name...) 233 buffer = append(buffer, '|') 234 buffer = strconv.AppendInt(buffer, int64(serviceCheck.Status), 10) 235 236 if !serviceCheck.Timestamp.IsZero() { 237 buffer = append(buffer, "|d:"...) 238 buffer = strconv.AppendInt(buffer, int64(serviceCheck.Timestamp.Unix()), 10) 239 } 240 241 if len(serviceCheck.Hostname) != 0 { 242 buffer = append(buffer, "|h:"...) 243 buffer = append(buffer, serviceCheck.Hostname...) 244 } 245 246 buffer = appendTags(buffer, globalTags, serviceCheck.Tags) 247 248 if len(serviceCheck.Message) != 0 { 249 buffer = append(buffer, "|m:"...) 250 buffer = appendEscapedServiceCheckText(buffer, serviceCheck.Message) 251 } 252 return buffer 253} 254 255func appendSeparator(buffer []byte) []byte { 256 return append(buffer, '\n') 257} 258