format.go 7.6 KB

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