text_parse.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. // Copyright 2014 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package expfmt
  14. import (
  15. "bufio"
  16. "bytes"
  17. "fmt"
  18. "io"
  19. "math"
  20. "strconv"
  21. "strings"
  22. dto "github.com/prometheus/client_model/go"
  23. "github.com/golang/protobuf/proto"
  24. "github.com/prometheus/common/model"
  25. )
  26. // A stateFn is a function that represents a state in a state machine. By
  27. // executing it, the state is progressed to the next state. The stateFn returns
  28. // another stateFn, which represents the new state. The end state is represented
  29. // by nil.
  30. type stateFn func() stateFn
  31. // ParseError signals errors while parsing the simple and flat text-based
  32. // exchange format.
  33. type ParseError struct {
  34. Line int
  35. Msg string
  36. }
  37. // Error implements the error interface.
  38. func (e ParseError) Error() string {
  39. return fmt.Sprintf("text format parsing error in line %d: %s", e.Line, e.Msg)
  40. }
  41. // TextParser is used to parse the simple and flat text-based exchange format. Its
  42. // nil value is ready to use.
  43. type TextParser struct {
  44. metricFamiliesByName map[string]*dto.MetricFamily
  45. buf *bufio.Reader // Where the parsed input is read through.
  46. err error // Most recent error.
  47. lineCount int // Tracks the line count for error messages.
  48. currentByte byte // The most recent byte read.
  49. currentToken bytes.Buffer // Re-used each time a token has to be gathered from multiple bytes.
  50. currentMF *dto.MetricFamily
  51. currentMetric *dto.Metric
  52. currentLabelPair *dto.LabelPair
  53. // The remaining member variables are only used for summaries/histograms.
  54. currentLabels map[string]string // All labels including '__name__' but excluding 'quantile'/'le'
  55. // Summary specific.
  56. summaries map[uint64]*dto.Metric // Key is created with LabelsToSignature.
  57. currentQuantile float64
  58. // Histogram specific.
  59. histograms map[uint64]*dto.Metric // Key is created with LabelsToSignature.
  60. currentBucket float64
  61. // These tell us if the currently processed line ends on '_count' or
  62. // '_sum' respectively and belong to a summary/histogram, representing the sample
  63. // count and sum of that summary/histogram.
  64. currentIsSummaryCount, currentIsSummarySum bool
  65. currentIsHistogramCount, currentIsHistogramSum bool
  66. }
  67. // TextToMetricFamilies reads 'in' as the simple and flat text-based exchange
  68. // format and creates MetricFamily proto messages. It returns the MetricFamily
  69. // proto messages in a map where the metric names are the keys, along with any
  70. // error encountered.
  71. //
  72. // If the input contains duplicate metrics (i.e. lines with the same metric name
  73. // and exactly the same label set), the resulting MetricFamily will contain
  74. // duplicate Metric proto messages. Similar is true for duplicate label
  75. // names. Checks for duplicates have to be performed separately, if required.
  76. // Also note that neither the metrics within each MetricFamily are sorted nor
  77. // the label pairs within each Metric. Sorting is not required for the most
  78. // frequent use of this method, which is sample ingestion in the Prometheus
  79. // server. However, for presentation purposes, you might want to sort the
  80. // metrics, and in some cases, you must sort the labels, e.g. for consumption by
  81. // the metric family injection hook of the Prometheus registry.
  82. //
  83. // Summaries and histograms are rather special beasts. You would probably not
  84. // use them in the simple text format anyway. This method can deal with
  85. // summaries and histograms if they are presented in exactly the way the
  86. // text.Create function creates them.
  87. //
  88. // This method must not be called concurrently. If you want to parse different
  89. // input concurrently, instantiate a separate Parser for each goroutine.
  90. func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
  91. p.reset(in)
  92. for nextState := p.startOfLine; nextState != nil; nextState = nextState() {
  93. // Magic happens here...
  94. }
  95. // Get rid of empty metric families.
  96. for k, mf := range p.metricFamiliesByName {
  97. if len(mf.GetMetric()) == 0 {
  98. delete(p.metricFamiliesByName, k)
  99. }
  100. }
  101. return p.metricFamiliesByName, p.err
  102. }
  103. func (p *TextParser) reset(in io.Reader) {
  104. p.metricFamiliesByName = map[string]*dto.MetricFamily{}
  105. if p.buf == nil {
  106. p.buf = bufio.NewReader(in)
  107. } else {
  108. p.buf.Reset(in)
  109. }
  110. p.err = nil
  111. p.lineCount = 0
  112. if p.summaries == nil || len(p.summaries) > 0 {
  113. p.summaries = map[uint64]*dto.Metric{}
  114. }
  115. if p.histograms == nil || len(p.histograms) > 0 {
  116. p.histograms = map[uint64]*dto.Metric{}
  117. }
  118. p.currentQuantile = math.NaN()
  119. p.currentBucket = math.NaN()
  120. }
  121. // startOfLine represents the state where the next byte read from p.buf is the
  122. // start of a line (or whitespace leading up to it).
  123. func (p *TextParser) startOfLine() stateFn {
  124. p.lineCount++
  125. if p.skipBlankTab(); p.err != nil {
  126. // End of input reached. This is the only case where
  127. // that is not an error but a signal that we are done.
  128. p.err = nil
  129. return nil
  130. }
  131. switch p.currentByte {
  132. case '#':
  133. return p.startComment
  134. case '\n':
  135. return p.startOfLine // Empty line, start the next one.
  136. }
  137. return p.readingMetricName
  138. }
  139. // startComment represents the state where the next byte read from p.buf is the
  140. // start of a comment (or whitespace leading up to it).
  141. func (p *TextParser) startComment() stateFn {
  142. if p.skipBlankTab(); p.err != nil {
  143. return nil // Unexpected end of input.
  144. }
  145. if p.currentByte == '\n' {
  146. return p.startOfLine
  147. }
  148. if p.readTokenUntilWhitespace(); p.err != nil {
  149. return nil // Unexpected end of input.
  150. }
  151. // If we have hit the end of line already, there is nothing left
  152. // to do. This is not considered a syntax error.
  153. if p.currentByte == '\n' {
  154. return p.startOfLine
  155. }
  156. keyword := p.currentToken.String()
  157. if keyword != "HELP" && keyword != "TYPE" {
  158. // Generic comment, ignore by fast forwarding to end of line.
  159. for p.currentByte != '\n' {
  160. if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
  161. return nil // Unexpected end of input.
  162. }
  163. }
  164. return p.startOfLine
  165. }
  166. // There is something. Next has to be a metric name.
  167. if p.skipBlankTab(); p.err != nil {
  168. return nil // Unexpected end of input.
  169. }
  170. if p.readTokenAsMetricName(); p.err != nil {
  171. return nil // Unexpected end of input.
  172. }
  173. if p.currentByte == '\n' {
  174. // At the end of the line already.
  175. // Again, this is not considered a syntax error.
  176. return p.startOfLine
  177. }
  178. if !isBlankOrTab(p.currentByte) {
  179. p.parseError("invalid metric name in comment")
  180. return nil
  181. }
  182. p.setOrCreateCurrentMF()
  183. if p.skipBlankTab(); p.err != nil {
  184. return nil // Unexpected end of input.
  185. }
  186. if p.currentByte == '\n' {
  187. // At the end of the line already.
  188. // Again, this is not considered a syntax error.
  189. return p.startOfLine
  190. }
  191. switch keyword {
  192. case "HELP":
  193. return p.readingHelp
  194. case "TYPE":
  195. return p.readingType
  196. }
  197. panic(fmt.Sprintf("code error: unexpected keyword %q", keyword))
  198. }
  199. // readingMetricName represents the state where the last byte read (now in
  200. // p.currentByte) is the first byte of a metric name.
  201. func (p *TextParser) readingMetricName() stateFn {
  202. if p.readTokenAsMetricName(); p.err != nil {
  203. return nil
  204. }
  205. if p.currentToken.Len() == 0 {
  206. p.parseError("invalid metric name")
  207. return nil
  208. }
  209. p.setOrCreateCurrentMF()
  210. // Now is the time to fix the type if it hasn't happened yet.
  211. if p.currentMF.Type == nil {
  212. p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
  213. }
  214. p.currentMetric = &dto.Metric{}
  215. // Do not append the newly created currentMetric to
  216. // currentMF.Metric right now. First wait if this is a summary,
  217. // and the metric exists already, which we can only know after
  218. // having read all the labels.
  219. if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
  220. return nil // Unexpected end of input.
  221. }
  222. return p.readingLabels
  223. }
  224. // readingLabels represents the state where the last byte read (now in
  225. // p.currentByte) is either the first byte of the label set (i.e. a '{'), or the
  226. // first byte of the value (otherwise).
  227. func (p *TextParser) readingLabels() stateFn {
  228. // Summaries/histograms are special. We have to reset the
  229. // currentLabels map, currentQuantile and currentBucket before starting to
  230. // read labels.
  231. if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  232. p.currentLabels = map[string]string{}
  233. p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName()
  234. p.currentQuantile = math.NaN()
  235. p.currentBucket = math.NaN()
  236. }
  237. if p.currentByte != '{' {
  238. return p.readingValue
  239. }
  240. return p.startLabelName
  241. }
  242. // startLabelName represents the state where the next byte read from p.buf is
  243. // the start of a label name (or whitespace leading up to it).
  244. func (p *TextParser) startLabelName() stateFn {
  245. if p.skipBlankTab(); p.err != nil {
  246. return nil // Unexpected end of input.
  247. }
  248. if p.currentByte == '}' {
  249. if p.skipBlankTab(); p.err != nil {
  250. return nil // Unexpected end of input.
  251. }
  252. return p.readingValue
  253. }
  254. if p.readTokenAsLabelName(); p.err != nil {
  255. return nil // Unexpected end of input.
  256. }
  257. if p.currentToken.Len() == 0 {
  258. p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName()))
  259. return nil
  260. }
  261. p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
  262. if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
  263. p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
  264. return nil
  265. }
  266. // Special summary/histogram treatment. Don't add 'quantile' and 'le'
  267. // labels to 'real' labels.
  268. if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) &&
  269. !(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) {
  270. p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair)
  271. }
  272. if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
  273. return nil // Unexpected end of input.
  274. }
  275. if p.currentByte != '=' {
  276. p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
  277. return nil
  278. }
  279. return p.startLabelValue
  280. }
  281. // startLabelValue represents the state where the next byte read from p.buf is
  282. // the start of a (quoted) label value (or whitespace leading up to it).
  283. func (p *TextParser) startLabelValue() stateFn {
  284. if p.skipBlankTab(); p.err != nil {
  285. return nil // Unexpected end of input.
  286. }
  287. if p.currentByte != '"' {
  288. p.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", p.currentByte))
  289. return nil
  290. }
  291. if p.readTokenAsLabelValue(); p.err != nil {
  292. return nil
  293. }
  294. p.currentLabelPair.Value = proto.String(p.currentToken.String())
  295. // Special treatment of summaries:
  296. // - Quantile labels are special, will result in dto.Quantile later.
  297. // - Other labels have to be added to currentLabels for signature calculation.
  298. if p.currentMF.GetType() == dto.MetricType_SUMMARY {
  299. if p.currentLabelPair.GetName() == model.QuantileLabel {
  300. if p.currentQuantile, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
  301. // Create a more helpful error message.
  302. p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
  303. return nil
  304. }
  305. } else {
  306. p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
  307. }
  308. }
  309. // Similar special treatment of histograms.
  310. if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  311. if p.currentLabelPair.GetName() == model.BucketLabel {
  312. if p.currentBucket, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
  313. // Create a more helpful error message.
  314. p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue()))
  315. return nil
  316. }
  317. } else {
  318. p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
  319. }
  320. }
  321. if p.skipBlankTab(); p.err != nil {
  322. return nil // Unexpected end of input.
  323. }
  324. switch p.currentByte {
  325. case ',':
  326. return p.startLabelName
  327. case '}':
  328. if p.skipBlankTab(); p.err != nil {
  329. return nil // Unexpected end of input.
  330. }
  331. return p.readingValue
  332. default:
  333. p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.Value))
  334. return nil
  335. }
  336. }
  337. // readingValue represents the state where the last byte read (now in
  338. // p.currentByte) is the first byte of the sample value (i.e. a float).
  339. func (p *TextParser) readingValue() stateFn {
  340. // When we are here, we have read all the labels, so for the
  341. // special case of a summary/histogram, we can finally find out
  342. // if the metric already exists.
  343. if p.currentMF.GetType() == dto.MetricType_SUMMARY {
  344. signature := model.LabelsToSignature(p.currentLabels)
  345. if summary := p.summaries[signature]; summary != nil {
  346. p.currentMetric = summary
  347. } else {
  348. p.summaries[signature] = p.currentMetric
  349. p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
  350. }
  351. } else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  352. signature := model.LabelsToSignature(p.currentLabels)
  353. if histogram := p.histograms[signature]; histogram != nil {
  354. p.currentMetric = histogram
  355. } else {
  356. p.histograms[signature] = p.currentMetric
  357. p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
  358. }
  359. } else {
  360. p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
  361. }
  362. if p.readTokenUntilWhitespace(); p.err != nil {
  363. return nil // Unexpected end of input.
  364. }
  365. value, err := strconv.ParseFloat(p.currentToken.String(), 64)
  366. if err != nil {
  367. // Create a more helpful error message.
  368. p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String()))
  369. return nil
  370. }
  371. switch p.currentMF.GetType() {
  372. case dto.MetricType_COUNTER:
  373. p.currentMetric.Counter = &dto.Counter{Value: proto.Float64(value)}
  374. case dto.MetricType_GAUGE:
  375. p.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64(value)}
  376. case dto.MetricType_UNTYPED:
  377. p.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64(value)}
  378. case dto.MetricType_SUMMARY:
  379. // *sigh*
  380. if p.currentMetric.Summary == nil {
  381. p.currentMetric.Summary = &dto.Summary{}
  382. }
  383. switch {
  384. case p.currentIsSummaryCount:
  385. p.currentMetric.Summary.SampleCount = proto.Uint64(uint64(value))
  386. case p.currentIsSummarySum:
  387. p.currentMetric.Summary.SampleSum = proto.Float64(value)
  388. case !math.IsNaN(p.currentQuantile):
  389. p.currentMetric.Summary.Quantile = append(
  390. p.currentMetric.Summary.Quantile,
  391. &dto.Quantile{
  392. Quantile: proto.Float64(p.currentQuantile),
  393. Value: proto.Float64(value),
  394. },
  395. )
  396. }
  397. case dto.MetricType_HISTOGRAM:
  398. // *sigh*
  399. if p.currentMetric.Histogram == nil {
  400. p.currentMetric.Histogram = &dto.Histogram{}
  401. }
  402. switch {
  403. case p.currentIsHistogramCount:
  404. p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value))
  405. case p.currentIsHistogramSum:
  406. p.currentMetric.Histogram.SampleSum = proto.Float64(value)
  407. case !math.IsNaN(p.currentBucket):
  408. p.currentMetric.Histogram.Bucket = append(
  409. p.currentMetric.Histogram.Bucket,
  410. &dto.Bucket{
  411. UpperBound: proto.Float64(p.currentBucket),
  412. CumulativeCount: proto.Uint64(uint64(value)),
  413. },
  414. )
  415. }
  416. default:
  417. p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName())
  418. }
  419. if p.currentByte == '\n' {
  420. return p.startOfLine
  421. }
  422. return p.startTimestamp
  423. }
  424. // startTimestamp represents the state where the next byte read from p.buf is
  425. // the start of the timestamp (or whitespace leading up to it).
  426. func (p *TextParser) startTimestamp() stateFn {
  427. if p.skipBlankTab(); p.err != nil {
  428. return nil // Unexpected end of input.
  429. }
  430. if p.readTokenUntilWhitespace(); p.err != nil {
  431. return nil // Unexpected end of input.
  432. }
  433. timestamp, err := strconv.ParseInt(p.currentToken.String(), 10, 64)
  434. if err != nil {
  435. // Create a more helpful error message.
  436. p.parseError(fmt.Sprintf("expected integer as timestamp, got %q", p.currentToken.String()))
  437. return nil
  438. }
  439. p.currentMetric.TimestampMs = proto.Int64(timestamp)
  440. if p.readTokenUntilNewline(false); p.err != nil {
  441. return nil // Unexpected end of input.
  442. }
  443. if p.currentToken.Len() > 0 {
  444. p.parseError(fmt.Sprintf("spurious string after timestamp: %q", p.currentToken.String()))
  445. return nil
  446. }
  447. return p.startOfLine
  448. }
  449. // readingHelp represents the state where the last byte read (now in
  450. // p.currentByte) is the first byte of the docstring after 'HELP'.
  451. func (p *TextParser) readingHelp() stateFn {
  452. if p.currentMF.Help != nil {
  453. p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName()))
  454. return nil
  455. }
  456. // Rest of line is the docstring.
  457. if p.readTokenUntilNewline(true); p.err != nil {
  458. return nil // Unexpected end of input.
  459. }
  460. p.currentMF.Help = proto.String(p.currentToken.String())
  461. return p.startOfLine
  462. }
  463. // readingType represents the state where the last byte read (now in
  464. // p.currentByte) is the first byte of the type hint after 'HELP'.
  465. func (p *TextParser) readingType() stateFn {
  466. if p.currentMF.Type != nil {
  467. p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName()))
  468. return nil
  469. }
  470. // Rest of line is the type.
  471. if p.readTokenUntilNewline(false); p.err != nil {
  472. return nil // Unexpected end of input.
  473. }
  474. metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
  475. if !ok {
  476. p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
  477. return nil
  478. }
  479. p.currentMF.Type = dto.MetricType(metricType).Enum()
  480. return p.startOfLine
  481. }
  482. // parseError sets p.err to a ParseError at the current line with the given
  483. // message.
  484. func (p *TextParser) parseError(msg string) {
  485. p.err = ParseError{
  486. Line: p.lineCount,
  487. Msg: msg,
  488. }
  489. }
  490. // skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte
  491. // that is neither ' ' nor '\t'. That byte is left in p.currentByte.
  492. func (p *TextParser) skipBlankTab() {
  493. for {
  494. if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) {
  495. return
  496. }
  497. }
  498. }
  499. // skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do
  500. // anything if p.currentByte is neither ' ' nor '\t'.
  501. func (p *TextParser) skipBlankTabIfCurrentBlankTab() {
  502. if isBlankOrTab(p.currentByte) {
  503. p.skipBlankTab()
  504. }
  505. }
  506. // readTokenUntilWhitespace copies bytes from p.buf into p.currentToken. The
  507. // first byte considered is the byte already read (now in p.currentByte). The
  508. // first whitespace byte encountered is still copied into p.currentByte, but not
  509. // into p.currentToken.
  510. func (p *TextParser) readTokenUntilWhitespace() {
  511. p.currentToken.Reset()
  512. for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' {
  513. p.currentToken.WriteByte(p.currentByte)
  514. p.currentByte, p.err = p.buf.ReadByte()
  515. }
  516. }
  517. // readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first
  518. // byte considered is the byte already read (now in p.currentByte). The first
  519. // newline byte encountered is still copied into p.currentByte, but not into
  520. // p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
  521. // recognized: '\\' tranlates into '\', and '\n' into a line-feed character. All
  522. // other escape sequences are invalid and cause an error.
  523. func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
  524. p.currentToken.Reset()
  525. escaped := false
  526. for p.err == nil {
  527. if recognizeEscapeSequence && escaped {
  528. switch p.currentByte {
  529. case '\\':
  530. p.currentToken.WriteByte(p.currentByte)
  531. case 'n':
  532. p.currentToken.WriteByte('\n')
  533. default:
  534. p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
  535. return
  536. }
  537. escaped = false
  538. } else {
  539. switch p.currentByte {
  540. case '\n':
  541. return
  542. case '\\':
  543. escaped = true
  544. default:
  545. p.currentToken.WriteByte(p.currentByte)
  546. }
  547. }
  548. p.currentByte, p.err = p.buf.ReadByte()
  549. }
  550. }
  551. // readTokenAsMetricName copies a metric name from p.buf into p.currentToken.
  552. // The first byte considered is the byte already read (now in p.currentByte).
  553. // The first byte not part of a metric name is still copied into p.currentByte,
  554. // but not into p.currentToken.
  555. func (p *TextParser) readTokenAsMetricName() {
  556. p.currentToken.Reset()
  557. if !isValidMetricNameStart(p.currentByte) {
  558. return
  559. }
  560. for {
  561. p.currentToken.WriteByte(p.currentByte)
  562. p.currentByte, p.err = p.buf.ReadByte()
  563. if p.err != nil || !isValidMetricNameContinuation(p.currentByte) {
  564. return
  565. }
  566. }
  567. }
  568. // readTokenAsLabelName copies a label name from p.buf into p.currentToken.
  569. // The first byte considered is the byte already read (now in p.currentByte).
  570. // The first byte not part of a label name is still copied into p.currentByte,
  571. // but not into p.currentToken.
  572. func (p *TextParser) readTokenAsLabelName() {
  573. p.currentToken.Reset()
  574. if !isValidLabelNameStart(p.currentByte) {
  575. return
  576. }
  577. for {
  578. p.currentToken.WriteByte(p.currentByte)
  579. p.currentByte, p.err = p.buf.ReadByte()
  580. if p.err != nil || !isValidLabelNameContinuation(p.currentByte) {
  581. return
  582. }
  583. }
  584. }
  585. // readTokenAsLabelValue copies a label value from p.buf into p.currentToken.
  586. // In contrast to the other 'readTokenAs...' functions, which start with the
  587. // last read byte in p.currentByte, this method ignores p.currentByte and starts
  588. // with reading a new byte from p.buf. The first byte not part of a label value
  589. // is still copied into p.currentByte, but not into p.currentToken.
  590. func (p *TextParser) readTokenAsLabelValue() {
  591. p.currentToken.Reset()
  592. escaped := false
  593. for {
  594. if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
  595. return
  596. }
  597. if escaped {
  598. switch p.currentByte {
  599. case '"', '\\':
  600. p.currentToken.WriteByte(p.currentByte)
  601. case 'n':
  602. p.currentToken.WriteByte('\n')
  603. default:
  604. p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
  605. return
  606. }
  607. escaped = false
  608. continue
  609. }
  610. switch p.currentByte {
  611. case '"':
  612. return
  613. case '\n':
  614. p.parseError(fmt.Sprintf("label value %q contains unescaped new-line", p.currentToken.String()))
  615. return
  616. case '\\':
  617. escaped = true
  618. default:
  619. p.currentToken.WriteByte(p.currentByte)
  620. }
  621. }
  622. }
  623. func (p *TextParser) setOrCreateCurrentMF() {
  624. p.currentIsSummaryCount = false
  625. p.currentIsSummarySum = false
  626. p.currentIsHistogramCount = false
  627. p.currentIsHistogramSum = false
  628. name := p.currentToken.String()
  629. if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil {
  630. return
  631. }
  632. // Try out if this is a _sum or _count for a summary/histogram.
  633. summaryName := summaryMetricName(name)
  634. if p.currentMF = p.metricFamiliesByName[summaryName]; p.currentMF != nil {
  635. if p.currentMF.GetType() == dto.MetricType_SUMMARY {
  636. if isCount(name) {
  637. p.currentIsSummaryCount = true
  638. }
  639. if isSum(name) {
  640. p.currentIsSummarySum = true
  641. }
  642. return
  643. }
  644. }
  645. histogramName := histogramMetricName(name)
  646. if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil {
  647. if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  648. if isCount(name) {
  649. p.currentIsHistogramCount = true
  650. }
  651. if isSum(name) {
  652. p.currentIsHistogramSum = true
  653. }
  654. return
  655. }
  656. }
  657. p.currentMF = &dto.MetricFamily{Name: proto.String(name)}
  658. p.metricFamiliesByName[name] = p.currentMF
  659. }
  660. func isValidLabelNameStart(b byte) bool {
  661. return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
  662. }
  663. func isValidLabelNameContinuation(b byte) bool {
  664. return isValidLabelNameStart(b) || (b >= '0' && b <= '9')
  665. }
  666. func isValidMetricNameStart(b byte) bool {
  667. return isValidLabelNameStart(b) || b == ':'
  668. }
  669. func isValidMetricNameContinuation(b byte) bool {
  670. return isValidLabelNameContinuation(b) || b == ':'
  671. }
  672. func isBlankOrTab(b byte) bool {
  673. return b == ' ' || b == '\t'
  674. }
  675. func isCount(name string) bool {
  676. return len(name) > 6 && name[len(name)-6:] == "_count"
  677. }
  678. func isSum(name string) bool {
  679. return len(name) > 4 && name[len(name)-4:] == "_sum"
  680. }
  681. func isBucket(name string) bool {
  682. return len(name) > 7 && name[len(name)-7:] == "_bucket"
  683. }
  684. func summaryMetricName(name string) string {
  685. switch {
  686. case isCount(name):
  687. return name[:len(name)-6]
  688. case isSum(name):
  689. return name[:len(name)-4]
  690. default:
  691. return name
  692. }
  693. }
  694. func histogramMetricName(name string) string {
  695. switch {
  696. case isCount(name):
  697. return name[:len(name)-6]
  698. case isSum(name):
  699. return name[:len(name)-4]
  700. case isBucket(name):
  701. return name[:len(name)-7]
  702. default:
  703. return name
  704. }
  705. }