parser.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package parser
  2. import (
  3. "fmt"
  4. "io"
  5. "strconv"
  6. "strings"
  7. "time"
  8. )
  9. // MetricRecord is the definition of a single metric instance in time.
  10. type MetricRecord struct {
  11. Name string
  12. Labels map[string]string
  13. Value float64
  14. Timestamp *time.Time
  15. }
  16. // Parse reads the input reader containing the raw metric format, and returns a slice of MetricRecord instances
  17. // containing the data parsed from the input.
  18. func Parse(reader io.Reader) ([]*MetricRecord, error) {
  19. return newParser(reader).parse()
  20. }
  21. // Parses Metrics from raw metric format.
  22. //
  23. // metric_name ["{" label_name "=" `"` label_value `"` { "," label_name"=" `"` label_value `"` } [ "," ] "}"] value [ timestamp ]
  24. //
  25. // In the sample syntax:
  26. // - metric_name and label_name carry the usual Prometheus expression language
  27. // restrictions.
  28. // - label_value can be any sequence of UTF-8 characters, but the backslash
  29. // (\), double-quote ("), and line feed (\n) characters have to be escaped as
  30. // \\, \", and \n, respectively.
  31. // - value is a float represented as required by Go's ParseFloat() function. In
  32. // addition to standard numerical values, NaN, +Inf, and -Inf are valid
  33. // values representing not a number, positive infinity, and negative
  34. // infinity, respectively.
  35. // - The timestamp is an int64 (milliseconds since epoch, i.e. 1970-01-01
  36. // 00:00:00 UTC, excluding leap seconds), represented as required by Go's
  37. // ParseInt() function.
  38. type parser struct {
  39. lex *lexer
  40. current token
  41. }
  42. // creates a new parser, which is meant to be used once and discarded
  43. func newParser(r io.Reader) *parser {
  44. return &parser{
  45. lex: newLexer(r),
  46. }
  47. }
  48. func (p *parser) advance() token {
  49. p.current = p.lex.next()
  50. return p.current
  51. }
  52. func (p *parser) parse() ([]*MetricRecord, error) {
  53. var metrics []*MetricRecord
  54. p.advance()
  55. for {
  56. if p.current.Type == Eof {
  57. break
  58. }
  59. if p.current.Type == Comment {
  60. p.advance()
  61. continue
  62. }
  63. metric, err := p.parseMetric()
  64. if err != nil {
  65. return nil, err
  66. }
  67. metrics = append(metrics, metric)
  68. }
  69. if len(p.lex.errors) > 0 {
  70. var sb strings.Builder
  71. for _, err := range p.lex.errors {
  72. sb.WriteString(" * ")
  73. sb.WriteString(err.Error())
  74. sb.WriteRune('\n')
  75. }
  76. return nil, fmt.Errorf("lexical errors: \n%s", sb.String())
  77. }
  78. return metrics, nil
  79. }
  80. func (p *parser) parseMetric() (*MetricRecord, error) {
  81. // expected to be advanced to current token
  82. if p.current.Type != Literal {
  83. return nil, fmt.Errorf("[metric parse error] expected literal, got unexpected token %v", p.current)
  84. }
  85. metric := &MetricRecord{
  86. Name: p.current.Value,
  87. }
  88. p.advance()
  89. // No Bracket: Parse Value/Timestamp
  90. if p.current.Type != OpenBracket {
  91. v, ts, err := p.parseValueAndTimestamp()
  92. if err != nil {
  93. return nil, err
  94. }
  95. metric.Value = v
  96. metric.Timestamp = ts
  97. return metric, nil
  98. }
  99. // Parse Label Pairs
  100. p.advance()
  101. for {
  102. if p.current.Type == Comma {
  103. p.advance()
  104. continue
  105. }
  106. if p.current.Type == CloseBracket {
  107. break
  108. }
  109. labelName, labelValue, err := p.parseLabelValuePair()
  110. if err != nil {
  111. return nil, err
  112. }
  113. if metric.Labels == nil {
  114. metric.Labels = make(map[string]string)
  115. }
  116. metric.Labels[labelName] = labelValue
  117. }
  118. p.advance()
  119. // Value and Timestamp
  120. v, ts, err := p.parseValueAndTimestamp()
  121. if err != nil {
  122. return nil, err
  123. }
  124. metric.Value = v
  125. metric.Timestamp = ts
  126. return metric, nil
  127. }
  128. func (p *parser) parseValueAndTimestamp() (float64, *time.Time, error) {
  129. var ts *time.Time
  130. if p.current.Type != Value {
  131. return 0.0, nil, fmt.Errorf("[value and time parse error] expected value, got unexpected token %v", p.current)
  132. }
  133. v, err := strconv.ParseFloat(p.current.Value, 64)
  134. if err != nil {
  135. return 0.0, nil, fmt.Errorf("failed to parse value %v: %v", p.current.Value, err)
  136. }
  137. p.advance()
  138. if p.current.Type == Value {
  139. t, err := strconv.ParseInt(p.current.Value, 10, 64)
  140. if err != nil {
  141. return 0.0, nil, fmt.Errorf("failed to parse timestamp %v: %v", p.current.Value, err)
  142. }
  143. epoch := time.Unix(0, t*int64(time.Millisecond))
  144. ts = &epoch
  145. p.advance()
  146. }
  147. return v, ts, nil
  148. }
  149. func (p *parser) parseLabelValuePair() (string, string, error) {
  150. if p.current.Type != Literal {
  151. return "", "", fmt.Errorf("[label parse error] expected literal, got unexpected token %v", p.current)
  152. }
  153. // start with label name literal
  154. labelName := p.current.Value
  155. p.advance()
  156. // must be '='
  157. if p.current.Type != Equal {
  158. return "", "", fmt.Errorf("[label parse error] expected '=', got unexpected token %v", p.current)
  159. }
  160. p.advance()
  161. // must be string type
  162. if p.current.Type != String {
  163. return "", "", fmt.Errorf("[label parse error] expected string, got unexpected token %v", p.current)
  164. }
  165. // label value string
  166. labelValue := p.current.Value
  167. p.advance()
  168. return labelName, labelValue, nil
  169. }