You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

780 lines
25 KiB

  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/prometheus/common/model"
  24. "google.golang.org/protobuf/proto"
  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. // zero 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. // If p.err is io.EOF now, we have run into a premature end of the input
  102. // stream. Turn this error into something nicer and more
  103. // meaningful. (io.EOF is often used as a signal for the legitimate end
  104. // of an input stream.)
  105. if p.err == io.EOF {
  106. p.parseError("unexpected end of input stream")
  107. }
  108. return p.metricFamiliesByName, p.err
  109. }
  110. func (p *TextParser) reset(in io.Reader) {
  111. p.metricFamiliesByName = map[string]*dto.MetricFamily{}
  112. if p.buf == nil {
  113. p.buf = bufio.NewReader(in)
  114. } else {
  115. p.buf.Reset(in)
  116. }
  117. p.err = nil
  118. p.lineCount = 0
  119. if p.summaries == nil || len(p.summaries) > 0 {
  120. p.summaries = map[uint64]*dto.Metric{}
  121. }
  122. if p.histograms == nil || len(p.histograms) > 0 {
  123. p.histograms = map[uint64]*dto.Metric{}
  124. }
  125. p.currentQuantile = math.NaN()
  126. p.currentBucket = math.NaN()
  127. }
  128. // startOfLine represents the state where the next byte read from p.buf is the
  129. // start of a line (or whitespace leading up to it).
  130. func (p *TextParser) startOfLine() stateFn {
  131. p.lineCount++
  132. if p.skipBlankTab(); p.err != nil {
  133. // This is the only place that we expect to see io.EOF,
  134. // which is not an error but the signal that we are done.
  135. // Any other error that happens to align with the start of
  136. // a line is still an error.
  137. if p.err == io.EOF {
  138. p.err = nil
  139. }
  140. return nil
  141. }
  142. switch p.currentByte {
  143. case '#':
  144. return p.startComment
  145. case '\n':
  146. return p.startOfLine // Empty line, start the next one.
  147. }
  148. return p.readingMetricName
  149. }
  150. // startComment represents the state where the next byte read from p.buf is the
  151. // start of a comment (or whitespace leading up to it).
  152. func (p *TextParser) startComment() stateFn {
  153. if p.skipBlankTab(); p.err != nil {
  154. return nil // Unexpected end of input.
  155. }
  156. if p.currentByte == '\n' {
  157. return p.startOfLine
  158. }
  159. if p.readTokenUntilWhitespace(); p.err != nil {
  160. return nil // Unexpected end of input.
  161. }
  162. // If we have hit the end of line already, there is nothing left
  163. // to do. This is not considered a syntax error.
  164. if p.currentByte == '\n' {
  165. return p.startOfLine
  166. }
  167. keyword := p.currentToken.String()
  168. if keyword != "HELP" && keyword != "TYPE" {
  169. // Generic comment, ignore by fast forwarding to end of line.
  170. for p.currentByte != '\n' {
  171. if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
  172. return nil // Unexpected end of input.
  173. }
  174. }
  175. return p.startOfLine
  176. }
  177. // There is something. Next has to be a metric name.
  178. if p.skipBlankTab(); p.err != nil {
  179. return nil // Unexpected end of input.
  180. }
  181. if p.readTokenAsMetricName(); p.err != nil {
  182. return nil // Unexpected end of input.
  183. }
  184. if p.currentByte == '\n' {
  185. // At the end of the line already.
  186. // Again, this is not considered a syntax error.
  187. return p.startOfLine
  188. }
  189. if !isBlankOrTab(p.currentByte) {
  190. p.parseError("invalid metric name in comment")
  191. return nil
  192. }
  193. p.setOrCreateCurrentMF()
  194. if p.skipBlankTab(); p.err != nil {
  195. return nil // Unexpected end of input.
  196. }
  197. if p.currentByte == '\n' {
  198. // At the end of the line already.
  199. // Again, this is not considered a syntax error.
  200. return p.startOfLine
  201. }
  202. switch keyword {
  203. case "HELP":
  204. return p.readingHelp
  205. case "TYPE":
  206. return p.readingType
  207. }
  208. panic(fmt.Sprintf("code error: unexpected keyword %q", keyword))
  209. }
  210. // readingMetricName represents the state where the last byte read (now in
  211. // p.currentByte) is the first byte of a metric name.
  212. func (p *TextParser) readingMetricName() stateFn {
  213. if p.readTokenAsMetricName(); p.err != nil {
  214. return nil
  215. }
  216. if p.currentToken.Len() == 0 {
  217. p.parseError("invalid metric name")
  218. return nil
  219. }
  220. p.setOrCreateCurrentMF()
  221. // Now is the time to fix the type if it hasn't happened yet.
  222. if p.currentMF.Type == nil {
  223. p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
  224. }
  225. p.currentMetric = &dto.Metric{}
  226. // Do not append the newly created currentMetric to
  227. // currentMF.Metric right now. First wait if this is a summary,
  228. // and the metric exists already, which we can only know after
  229. // having read all the labels.
  230. if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
  231. return nil // Unexpected end of input.
  232. }
  233. return p.readingLabels
  234. }
  235. // readingLabels represents the state where the last byte read (now in
  236. // p.currentByte) is either the first byte of the label set (i.e. a '{'), or the
  237. // first byte of the value (otherwise).
  238. func (p *TextParser) readingLabels() stateFn {
  239. // Summaries/histograms are special. We have to reset the
  240. // currentLabels map, currentQuantile and currentBucket before starting to
  241. // read labels.
  242. if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  243. p.currentLabels = map[string]string{}
  244. p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName()
  245. p.currentQuantile = math.NaN()
  246. p.currentBucket = math.NaN()
  247. }
  248. if p.currentByte != '{' {
  249. return p.readingValue
  250. }
  251. return p.startLabelName
  252. }
  253. // startLabelName represents the state where the next byte read from p.buf is
  254. // the start of a label name (or whitespace leading up to it).
  255. func (p *TextParser) startLabelName() stateFn {
  256. if p.skipBlankTab(); p.err != nil {
  257. return nil // Unexpected end of input.
  258. }
  259. if p.currentByte == '}' {
  260. if p.skipBlankTab(); p.err != nil {
  261. return nil // Unexpected end of input.
  262. }
  263. return p.readingValue
  264. }
  265. if p.readTokenAsLabelName(); p.err != nil {
  266. return nil // Unexpected end of input.
  267. }
  268. if p.currentToken.Len() == 0 {
  269. p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName()))
  270. return nil
  271. }
  272. p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
  273. if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
  274. p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
  275. return nil
  276. }
  277. // Special summary/histogram treatment. Don't add 'quantile' and 'le'
  278. // labels to 'real' labels.
  279. if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) &&
  280. !(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) {
  281. p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair)
  282. }
  283. if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
  284. return nil // Unexpected end of input.
  285. }
  286. if p.currentByte != '=' {
  287. p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
  288. return nil
  289. }
  290. // Check for duplicate label names.
  291. labels := make(map[string]struct{})
  292. for _, l := range p.currentMetric.Label {
  293. lName := l.GetName()
  294. if _, exists := labels[lName]; !exists {
  295. labels[lName] = struct{}{}
  296. } else {
  297. p.parseError(fmt.Sprintf("duplicate label names for metric %q", p.currentMF.GetName()))
  298. return nil
  299. }
  300. }
  301. return p.startLabelValue
  302. }
  303. // startLabelValue represents the state where the next byte read from p.buf is
  304. // the start of a (quoted) label value (or whitespace leading up to it).
  305. func (p *TextParser) startLabelValue() stateFn {
  306. if p.skipBlankTab(); p.err != nil {
  307. return nil // Unexpected end of input.
  308. }
  309. if p.currentByte != '"' {
  310. p.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", p.currentByte))
  311. return nil
  312. }
  313. if p.readTokenAsLabelValue(); p.err != nil {
  314. return nil
  315. }
  316. if !model.LabelValue(p.currentToken.String()).IsValid() {
  317. p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String()))
  318. return nil
  319. }
  320. p.currentLabelPair.Value = proto.String(p.currentToken.String())
  321. // Special treatment of summaries:
  322. // - Quantile labels are special, will result in dto.Quantile later.
  323. // - Other labels have to be added to currentLabels for signature calculation.
  324. if p.currentMF.GetType() == dto.MetricType_SUMMARY {
  325. if p.currentLabelPair.GetName() == model.QuantileLabel {
  326. if p.currentQuantile, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
  327. // Create a more helpful error message.
  328. p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
  329. return nil
  330. }
  331. } else {
  332. p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
  333. }
  334. }
  335. // Similar special treatment of histograms.
  336. if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  337. if p.currentLabelPair.GetName() == model.BucketLabel {
  338. if p.currentBucket, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
  339. // Create a more helpful error message.
  340. p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue()))
  341. return nil
  342. }
  343. } else {
  344. p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
  345. }
  346. }
  347. if p.skipBlankTab(); p.err != nil {
  348. return nil // Unexpected end of input.
  349. }
  350. switch p.currentByte {
  351. case ',':
  352. return p.startLabelName
  353. case '}':
  354. if p.skipBlankTab(); p.err != nil {
  355. return nil // Unexpected end of input.
  356. }
  357. return p.readingValue
  358. default:
  359. p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.GetValue()))
  360. return nil
  361. }
  362. }
  363. // readingValue represents the state where the last byte read (now in
  364. // p.currentByte) is the first byte of the sample value (i.e. a float).
  365. func (p *TextParser) readingValue() stateFn {
  366. // When we are here, we have read all the labels, so for the
  367. // special case of a summary/histogram, we can finally find out
  368. // if the metric already exists.
  369. if p.currentMF.GetType() == dto.MetricType_SUMMARY {
  370. signature := model.LabelsToSignature(p.currentLabels)
  371. if summary := p.summaries[signature]; summary != nil {
  372. p.currentMetric = summary
  373. } else {
  374. p.summaries[signature] = p.currentMetric
  375. p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
  376. }
  377. } else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  378. signature := model.LabelsToSignature(p.currentLabels)
  379. if histogram := p.histograms[signature]; histogram != nil {
  380. p.currentMetric = histogram
  381. } else {
  382. p.histograms[signature] = p.currentMetric
  383. p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
  384. }
  385. } else {
  386. p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
  387. }
  388. if p.readTokenUntilWhitespace(); p.err != nil {
  389. return nil // Unexpected end of input.
  390. }
  391. value, err := parseFloat(p.currentToken.String())
  392. if err != nil {
  393. // Create a more helpful error message.
  394. p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String()))
  395. return nil
  396. }
  397. switch p.currentMF.GetType() {
  398. case dto.MetricType_COUNTER:
  399. p.currentMetric.Counter = &dto.Counter{Value: proto.Float64(value)}
  400. case dto.MetricType_GAUGE:
  401. p.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64(value)}
  402. case dto.MetricType_UNTYPED:
  403. p.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64(value)}
  404. case dto.MetricType_SUMMARY:
  405. // *sigh*
  406. if p.currentMetric.Summary == nil {
  407. p.currentMetric.Summary = &dto.Summary{}
  408. }
  409. switch {
  410. case p.currentIsSummaryCount:
  411. p.currentMetric.Summary.SampleCount = proto.Uint64(uint64(value))
  412. case p.currentIsSummarySum:
  413. p.currentMetric.Summary.SampleSum = proto.Float64(value)
  414. case !math.IsNaN(p.currentQuantile):
  415. p.currentMetric.Summary.Quantile = append(
  416. p.currentMetric.Summary.Quantile,
  417. &dto.Quantile{
  418. Quantile: proto.Float64(p.currentQuantile),
  419. Value: proto.Float64(value),
  420. },
  421. )
  422. }
  423. case dto.MetricType_HISTOGRAM:
  424. // *sigh*
  425. if p.currentMetric.Histogram == nil {
  426. p.currentMetric.Histogram = &dto.Histogram{}
  427. }
  428. switch {
  429. case p.currentIsHistogramCount:
  430. p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value))
  431. case p.currentIsHistogramSum:
  432. p.currentMetric.Histogram.SampleSum = proto.Float64(value)
  433. case !math.IsNaN(p.currentBucket):
  434. p.currentMetric.Histogram.Bucket = append(
  435. p.currentMetric.Histogram.Bucket,
  436. &dto.Bucket{
  437. UpperBound: proto.Float64(p.currentBucket),
  438. CumulativeCount: proto.Uint64(uint64(value)),
  439. },
  440. )
  441. }
  442. default:
  443. p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName())
  444. }
  445. if p.currentByte == '\n' {
  446. return p.startOfLine
  447. }
  448. return p.startTimestamp
  449. }
  450. // startTimestamp represents the state where the next byte read from p.buf is
  451. // the start of the timestamp (or whitespace leading up to it).
  452. func (p *TextParser) startTimestamp() stateFn {
  453. if p.skipBlankTab(); p.err != nil {
  454. return nil // Unexpected end of input.
  455. }
  456. if p.readTokenUntilWhitespace(); p.err != nil {
  457. return nil // Unexpected end of input.
  458. }
  459. timestamp, err := strconv.ParseInt(p.currentToken.String(), 10, 64)
  460. if err != nil {
  461. // Create a more helpful error message.
  462. p.parseError(fmt.Sprintf("expected integer as timestamp, got %q", p.currentToken.String()))
  463. return nil
  464. }
  465. p.currentMetric.TimestampMs = proto.Int64(timestamp)
  466. if p.readTokenUntilNewline(false); p.err != nil {
  467. return nil // Unexpected end of input.
  468. }
  469. if p.currentToken.Len() > 0 {
  470. p.parseError(fmt.Sprintf("spurious string after timestamp: %q", p.currentToken.String()))
  471. return nil
  472. }
  473. return p.startOfLine
  474. }
  475. // readingHelp represents the state where the last byte read (now in
  476. // p.currentByte) is the first byte of the docstring after 'HELP'.
  477. func (p *TextParser) readingHelp() stateFn {
  478. if p.currentMF.Help != nil {
  479. p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName()))
  480. return nil
  481. }
  482. // Rest of line is the docstring.
  483. if p.readTokenUntilNewline(true); p.err != nil {
  484. return nil // Unexpected end of input.
  485. }
  486. p.currentMF.Help = proto.String(p.currentToken.String())
  487. return p.startOfLine
  488. }
  489. // readingType represents the state where the last byte read (now in
  490. // p.currentByte) is the first byte of the type hint after 'HELP'.
  491. func (p *TextParser) readingType() stateFn {
  492. if p.currentMF.Type != nil {
  493. p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName()))
  494. return nil
  495. }
  496. // Rest of line is the type.
  497. if p.readTokenUntilNewline(false); p.err != nil {
  498. return nil // Unexpected end of input.
  499. }
  500. metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
  501. if !ok {
  502. p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
  503. return nil
  504. }
  505. p.currentMF.Type = dto.MetricType(metricType).Enum()
  506. return p.startOfLine
  507. }
  508. // parseError sets p.err to a ParseError at the current line with the given
  509. // message.
  510. func (p *TextParser) parseError(msg string) {
  511. p.err = ParseError{
  512. Line: p.lineCount,
  513. Msg: msg,
  514. }
  515. }
  516. // skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte
  517. // that is neither ' ' nor '\t'. That byte is left in p.currentByte.
  518. func (p *TextParser) skipBlankTab() {
  519. for {
  520. if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) {
  521. return
  522. }
  523. }
  524. }
  525. // skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do
  526. // anything if p.currentByte is neither ' ' nor '\t'.
  527. func (p *TextParser) skipBlankTabIfCurrentBlankTab() {
  528. if isBlankOrTab(p.currentByte) {
  529. p.skipBlankTab()
  530. }
  531. }
  532. // readTokenUntilWhitespace copies bytes from p.buf into p.currentToken. The
  533. // first byte considered is the byte already read (now in p.currentByte). The
  534. // first whitespace byte encountered is still copied into p.currentByte, but not
  535. // into p.currentToken.
  536. func (p *TextParser) readTokenUntilWhitespace() {
  537. p.currentToken.Reset()
  538. for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' {
  539. p.currentToken.WriteByte(p.currentByte)
  540. p.currentByte, p.err = p.buf.ReadByte()
  541. }
  542. }
  543. // readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first
  544. // byte considered is the byte already read (now in p.currentByte). The first
  545. // newline byte encountered is still copied into p.currentByte, but not into
  546. // p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
  547. // recognized: '\\' translates into '\', and '\n' into a line-feed character.
  548. // All other escape sequences are invalid and cause an error.
  549. func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
  550. p.currentToken.Reset()
  551. escaped := false
  552. for p.err == nil {
  553. if recognizeEscapeSequence && escaped {
  554. switch p.currentByte {
  555. case '\\':
  556. p.currentToken.WriteByte(p.currentByte)
  557. case 'n':
  558. p.currentToken.WriteByte('\n')
  559. default:
  560. p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
  561. return
  562. }
  563. escaped = false
  564. } else {
  565. switch p.currentByte {
  566. case '\n':
  567. return
  568. case '\\':
  569. escaped = true
  570. default:
  571. p.currentToken.WriteByte(p.currentByte)
  572. }
  573. }
  574. p.currentByte, p.err = p.buf.ReadByte()
  575. }
  576. }
  577. // readTokenAsMetricName copies a metric name from p.buf into p.currentToken.
  578. // The first byte considered is the byte already read (now in p.currentByte).
  579. // The first byte not part of a metric name is still copied into p.currentByte,
  580. // but not into p.currentToken.
  581. func (p *TextParser) readTokenAsMetricName() {
  582. p.currentToken.Reset()
  583. if !isValidMetricNameStart(p.currentByte) {
  584. return
  585. }
  586. for {
  587. p.currentToken.WriteByte(p.currentByte)
  588. p.currentByte, p.err = p.buf.ReadByte()
  589. if p.err != nil || !isValidMetricNameContinuation(p.currentByte) {
  590. return
  591. }
  592. }
  593. }
  594. // readTokenAsLabelName copies a label name from p.buf into p.currentToken.
  595. // The first byte considered is the byte already read (now in p.currentByte).
  596. // The first byte not part of a label name is still copied into p.currentByte,
  597. // but not into p.currentToken.
  598. func (p *TextParser) readTokenAsLabelName() {
  599. p.currentToken.Reset()
  600. if !isValidLabelNameStart(p.currentByte) {
  601. return
  602. }
  603. for {
  604. p.currentToken.WriteByte(p.currentByte)
  605. p.currentByte, p.err = p.buf.ReadByte()
  606. if p.err != nil || !isValidLabelNameContinuation(p.currentByte) {
  607. return
  608. }
  609. }
  610. }
  611. // readTokenAsLabelValue copies a label value from p.buf into p.currentToken.
  612. // In contrast to the other 'readTokenAs...' functions, which start with the
  613. // last read byte in p.currentByte, this method ignores p.currentByte and starts
  614. // with reading a new byte from p.buf. The first byte not part of a label value
  615. // is still copied into p.currentByte, but not into p.currentToken.
  616. func (p *TextParser) readTokenAsLabelValue() {
  617. p.currentToken.Reset()
  618. escaped := false
  619. for {
  620. if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
  621. return
  622. }
  623. if escaped {
  624. switch p.currentByte {
  625. case '"', '\\':
  626. p.currentToken.WriteByte(p.currentByte)
  627. case 'n':
  628. p.currentToken.WriteByte('\n')
  629. default:
  630. p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
  631. return
  632. }
  633. escaped = false
  634. continue
  635. }
  636. switch p.currentByte {
  637. case '"':
  638. return
  639. case '\n':
  640. p.parseError(fmt.Sprintf("label value %q contains unescaped new-line", p.currentToken.String()))
  641. return
  642. case '\\':
  643. escaped = true
  644. default:
  645. p.currentToken.WriteByte(p.currentByte)
  646. }
  647. }
  648. }
  649. func (p *TextParser) setOrCreateCurrentMF() {
  650. p.currentIsSummaryCount = false
  651. p.currentIsSummarySum = false
  652. p.currentIsHistogramCount = false
  653. p.currentIsHistogramSum = false
  654. name := p.currentToken.String()
  655. if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil {
  656. return
  657. }
  658. // Try out if this is a _sum or _count for a summary/histogram.
  659. summaryName := summaryMetricName(name)
  660. if p.currentMF = p.metricFamiliesByName[summaryName]; p.currentMF != nil {
  661. if p.currentMF.GetType() == dto.MetricType_SUMMARY {
  662. if isCount(name) {
  663. p.currentIsSummaryCount = true
  664. }
  665. if isSum(name) {
  666. p.currentIsSummarySum = true
  667. }
  668. return
  669. }
  670. }
  671. histogramName := histogramMetricName(name)
  672. if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil {
  673. if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
  674. if isCount(name) {
  675. p.currentIsHistogramCount = true
  676. }
  677. if isSum(name) {
  678. p.currentIsHistogramSum = true
  679. }
  680. return
  681. }
  682. }
  683. p.currentMF = &dto.MetricFamily{Name: proto.String(name)}
  684. p.metricFamiliesByName[name] = p.currentMF
  685. }
  686. func isValidLabelNameStart(b byte) bool {
  687. return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
  688. }
  689. func isValidLabelNameContinuation(b byte) bool {
  690. return isValidLabelNameStart(b) || (b >= '0' && b <= '9')
  691. }
  692. func isValidMetricNameStart(b byte) bool {
  693. return isValidLabelNameStart(b) || b == ':'
  694. }
  695. func isValidMetricNameContinuation(b byte) bool {
  696. return isValidLabelNameContinuation(b) || b == ':'
  697. }
  698. func isBlankOrTab(b byte) bool {
  699. return b == ' ' || b == '\t'
  700. }
  701. func isCount(name string) bool {
  702. return len(name) > 6 && name[len(name)-6:] == "_count"
  703. }
  704. func isSum(name string) bool {
  705. return len(name) > 4 && name[len(name)-4:] == "_sum"
  706. }
  707. func isBucket(name string) bool {
  708. return len(name) > 7 && name[len(name)-7:] == "_bucket"
  709. }
  710. func summaryMetricName(name string) string {
  711. switch {
  712. case isCount(name):
  713. return name[:len(name)-6]
  714. case isSum(name):
  715. return name[:len(name)-4]
  716. default:
  717. return name
  718. }
  719. }
  720. func histogramMetricName(name string) string {
  721. switch {
  722. case isCount(name):
  723. return name[:len(name)-6]
  724. case isSum(name):
  725. return name[:len(name)-4]
  726. case isBucket(name):
  727. return name[:len(name)-7]
  728. default:
  729. return name
  730. }
  731. }
  732. func parseFloat(s string) (float64, error) {
  733. if strings.ContainsAny(s, "pP_") {
  734. return 0, fmt.Errorf("unsupported character in float")
  735. }
  736. return strconv.ParseFloat(s, 64)
  737. }