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