Source File
text_parse.go
Belonging Package
github.com/prometheus/common/expfmt
package expfmt
import (
dto
)
type ParseError struct {
Line int
Msg string
}
type TextParser struct {
metricFamiliesByName map[string]*dto.MetricFamily
buf *bufio.Reader // Where the parsed input is read through.
err error // Most recent error.
lineCount int // Tracks the line count for error messages.
currentByte byte // The most recent byte read.
currentToken bytes.Buffer // Re-used each time a token has to be gathered from multiple bytes.
currentMF *dto.MetricFamily
currentMetric *dto.Metric
currentLabelPair *dto.LabelPair
histograms map[uint64]*dto.Metric // Key is created with LabelsToSignature.
func ( *TextParser) ( io.Reader) (map[string]*dto.MetricFamily, error) {
.reset()
for , := range .metricFamiliesByName {
if len(.GetMetric()) == 0 {
delete(.metricFamiliesByName, )
}
if .err == io.EOF {
.parseError("unexpected end of input stream")
}
return .metricFamiliesByName, .err
}
func ( *TextParser) ( io.Reader) {
.metricFamiliesByName = map[string]*dto.MetricFamily{}
if .buf == nil {
.buf = bufio.NewReader()
} else {
.buf.Reset()
}
.err = nil
.lineCount = 0
if .summaries == nil || len(.summaries) > 0 {
.summaries = map[uint64]*dto.Metric{}
}
if .histograms == nil || len(.histograms) > 0 {
.histograms = map[uint64]*dto.Metric{}
}
.currentQuantile = math.NaN()
.currentBucket = math.NaN()
}
func ( *TextParser) () stateFn {
.lineCount++
.err = nil
return nil
}
switch .currentByte {
case '#':
return .startComment
case '\n':
return . // Empty line, start the next one.
}
return .readingMetricName
}
func ( *TextParser) () stateFn {
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
if .currentByte == '\n' {
return .startOfLine
}
if .readTokenUntilWhitespace(); .err != nil {
return nil // Unexpected end of input.
if .currentByte == '\n' {
return .startOfLine
}
:= .currentToken.String()
for .currentByte != '\n' {
if .currentByte, .err = .buf.ReadByte(); .err != nil {
return nil // Unexpected end of input.
}
}
return .startOfLine
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
if .readTokenAsMetricName(); .err != nil {
return nil // Unexpected end of input.
}
return .startOfLine
}
if !isBlankOrTab(.currentByte) {
.parseError("invalid metric name in comment")
return nil
}
.setOrCreateCurrentMF()
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
return .startOfLine
}
switch {
case "HELP":
return .readingHelp
case "TYPE":
return .readingType
}
panic(fmt.Sprintf("code error: unexpected keyword %q", ))
}
func ( *TextParser) () stateFn {
if .readTokenAsMetricName(); .err != nil {
return nil
}
if .currentToken.Len() == 0 {
.parseError("invalid metric name")
return nil
}
if .skipBlankTabIfCurrentBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
return .readingLabels
}
if .currentMF.GetType() == dto.MetricType_SUMMARY || .currentMF.GetType() == dto.MetricType_HISTOGRAM {
.currentLabels = map[string]string{}
.currentLabels[string(model.MetricNameLabel)] = .currentMF.GetName()
.currentQuantile = math.NaN()
.currentBucket = math.NaN()
}
if .currentByte != '{' {
return .readingValue
}
return .startLabelName
}
func ( *TextParser) () stateFn {
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
if .currentByte == '}' {
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
return .readingValue
}
if .readTokenAsLabelName(); .err != nil {
return nil // Unexpected end of input.
}
if .currentToken.Len() == 0 {
.parseError(fmt.Sprintf("invalid label name for metric %q", .currentMF.GetName()))
return nil
}
.currentLabelPair = &dto.LabelPair{Name: proto.String(.currentToken.String())}
if .currentLabelPair.GetName() == string(model.MetricNameLabel) {
.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
return nil
if !(.currentMF.GetType() == dto.MetricType_SUMMARY && .currentLabelPair.GetName() == model.QuantileLabel) &&
!(.currentMF.GetType() == dto.MetricType_HISTOGRAM && .currentLabelPair.GetName() == model.BucketLabel) {
.currentMetric.Label = append(.currentMetric.Label, .currentLabelPair)
}
if .skipBlankTabIfCurrentBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
if .currentByte != '=' {
.parseError(fmt.Sprintf("expected '=' after label name, found %q", .currentByte))
return nil
}
return .startLabelValue
}
func ( *TextParser) () stateFn {
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
if .currentByte != '"' {
.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", .currentByte))
return nil
}
if .readTokenAsLabelValue(); .err != nil {
return nil
}
if !model.LabelValue(.currentToken.String()).IsValid() {
.parseError(fmt.Sprintf("invalid label value %q", .currentToken.String()))
return nil
}
if .currentMF.GetType() == dto.MetricType_SUMMARY {
if .currentLabelPair.GetName() == model.QuantileLabel {
.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", .currentLabelPair.GetValue()))
return nil
}
} else {
.currentLabels[.currentLabelPair.GetName()] = .currentLabelPair.GetValue()
}
if .currentMF.GetType() == dto.MetricType_HISTOGRAM {
if .currentLabelPair.GetName() == model.BucketLabel {
.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", .currentLabelPair.GetValue()))
return nil
}
} else {
.currentLabels[.currentLabelPair.GetName()] = .currentLabelPair.GetValue()
}
}
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
switch .currentByte {
case ',':
return .startLabelName
case '}':
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
return .readingValue
default:
.parseError(fmt.Sprintf("unexpected end of label value %q", .currentLabelPair.GetValue()))
return nil
}
}
if .currentMF.GetType() == dto.MetricType_SUMMARY {
:= model.LabelsToSignature(.currentLabels)
if := .summaries[]; != nil {
.currentMetric =
} else {
.summaries[] = .currentMetric
.currentMF.Metric = append(.currentMF.Metric, .currentMetric)
}
} else if .currentMF.GetType() == dto.MetricType_HISTOGRAM {
:= model.LabelsToSignature(.currentLabels)
if := .histograms[]; != nil {
.currentMetric =
} else {
.histograms[] = .currentMetric
.currentMF.Metric = append(.currentMF.Metric, .currentMetric)
}
} else {
.currentMF.Metric = append(.currentMF.Metric, .currentMetric)
}
if .readTokenUntilWhitespace(); .err != nil {
return nil // Unexpected end of input.
}
, := parseFloat(.currentToken.String())
.parseError(fmt.Sprintf("expected float as value, got %q", .currentToken.String()))
return nil
}
switch .currentMF.GetType() {
case dto.MetricType_COUNTER:
.currentMetric.Counter = &dto.Counter{Value: proto.Float64()}
case dto.MetricType_GAUGE:
.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64()}
case dto.MetricType_UNTYPED:
.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64()}
if .currentMetric.Summary == nil {
.currentMetric.Summary = &dto.Summary{}
}
switch {
case .currentIsSummaryCount:
.currentMetric.Summary.SampleCount = proto.Uint64(uint64())
case .currentIsSummarySum:
.currentMetric.Summary.SampleSum = proto.Float64()
case !math.IsNaN(.currentQuantile):
.currentMetric.Summary.Quantile = append(
.currentMetric.Summary.Quantile,
&dto.Quantile{
Quantile: proto.Float64(.currentQuantile),
Value: proto.Float64(),
},
)
}
if .currentMetric.Histogram == nil {
.currentMetric.Histogram = &dto.Histogram{}
}
switch {
case .currentIsHistogramCount:
.currentMetric.Histogram.SampleCount = proto.Uint64(uint64())
case .currentIsHistogramSum:
.currentMetric.Histogram.SampleSum = proto.Float64()
case !math.IsNaN(.currentBucket):
.currentMetric.Histogram.Bucket = append(
.currentMetric.Histogram.Bucket,
&dto.Bucket{
UpperBound: proto.Float64(.currentBucket),
CumulativeCount: proto.Uint64(uint64()),
},
)
}
default:
.err = fmt.Errorf("unexpected type for metric name %q", .currentMF.GetName())
}
if .currentByte == '\n' {
return .startOfLine
}
return .startTimestamp
}
func ( *TextParser) () stateFn {
if .skipBlankTab(); .err != nil {
return nil // Unexpected end of input.
}
if .readTokenUntilWhitespace(); .err != nil {
return nil // Unexpected end of input.
}
, := strconv.ParseInt(.currentToken.String(), 10, 64)
.parseError(fmt.Sprintf("expected integer as timestamp, got %q", .currentToken.String()))
return nil
}
.currentMetric.TimestampMs = proto.Int64()
if .readTokenUntilNewline(false); .err != nil {
return nil // Unexpected end of input.
}
if .currentToken.Len() > 0 {
.parseError(fmt.Sprintf("spurious string after timestamp: %q", .currentToken.String()))
return nil
}
return .startOfLine
}
if .readTokenUntilNewline(true); .err != nil {
return nil // Unexpected end of input.
}
.currentMF.Help = proto.String(.currentToken.String())
return .startOfLine
}
if .readTokenUntilNewline(false); .err != nil {
return nil // Unexpected end of input.
}
, := dto.MetricType_value[strings.ToUpper(.currentToken.String())]
if ! {
.parseError(fmt.Sprintf("unknown metric type %q", .currentToken.String()))
return nil
}
.currentMF.Type = dto.MetricType().Enum()
return .startOfLine
}
func ( *TextParser) ( string) {
.err = ParseError{
Line: .lineCount,
Msg: ,
}
}
func ( *TextParser) () {
for {
if .currentByte, .err = .buf.ReadByte(); .err != nil || !isBlankOrTab(.currentByte) {
return
}
}
}
func ( *TextParser) () {
if isBlankOrTab(.currentByte) {
.skipBlankTab()
}
}
func ( *TextParser) () {
.currentToken.Reset()
for .err == nil && !isBlankOrTab(.currentByte) && .currentByte != '\n' {
.currentToken.WriteByte(.currentByte)
.currentByte, .err = .buf.ReadByte()
}
}
func ( *TextParser) ( bool) {
.currentToken.Reset()
:= false
for .err == nil {
if && {
switch .currentByte {
case '\\':
.currentToken.WriteByte(.currentByte)
case 'n':
.currentToken.WriteByte('\n')
default:
.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", .currentByte))
return
}
= false
} else {
switch .currentByte {
case '\n':
return
case '\\':
= true
default:
.currentToken.WriteByte(.currentByte)
}
}
.currentByte, .err = .buf.ReadByte()
}
}
func ( *TextParser) () {
.currentToken.Reset()
if !isValidMetricNameStart(.currentByte) {
return
}
for {
.currentToken.WriteByte(.currentByte)
.currentByte, .err = .buf.ReadByte()
if .err != nil || !isValidMetricNameContinuation(.currentByte) {
return
}
}
}
func ( *TextParser) () {
.currentToken.Reset()
if !isValidLabelNameStart(.currentByte) {
return
}
for {
.currentToken.WriteByte(.currentByte)
.currentByte, .err = .buf.ReadByte()
if .err != nil || !isValidLabelNameContinuation(.currentByte) {
return
}
}
}
func ( *TextParser) () {
.currentToken.Reset()
:= false
for {
if .currentByte, .err = .buf.ReadByte(); .err != nil {
return
}
if {
switch .currentByte {
case '"', '\\':
.currentToken.WriteByte(.currentByte)
case 'n':
.currentToken.WriteByte('\n')
default:
.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", .currentByte))
return
}
= false
continue
}
switch .currentByte {
case '"':
return
case '\n':
.parseError(fmt.Sprintf("label value %q contains unescaped new-line", .currentToken.String()))
return
case '\\':
= true
default:
.currentToken.WriteByte(.currentByte)
}
}
}
func ( *TextParser) () {
.currentIsSummaryCount = false
.currentIsSummarySum = false
.currentIsHistogramCount = false
.currentIsHistogramSum = false
:= .currentToken.String()
if .currentMF = .metricFamiliesByName[]; .currentMF != nil {
return
:= summaryMetricName()
if .currentMF = .metricFamiliesByName[]; .currentMF != nil {
if .currentMF.GetType() == dto.MetricType_SUMMARY {
if isCount() {
.currentIsSummaryCount = true
}
if isSum() {
.currentIsSummarySum = true
}
return
}
}
:= histogramMetricName()
if .currentMF = .metricFamiliesByName[]; .currentMF != nil {
if .currentMF.GetType() == dto.MetricType_HISTOGRAM {
if isCount() {
.currentIsHistogramCount = true
}
if isSum() {
.currentIsHistogramSum = true
}
return
}
}
.currentMF = &dto.MetricFamily{Name: proto.String()}
.metricFamiliesByName[] = .currentMF
}
func ( byte) bool {
return ( >= 'a' && <= 'z') || ( >= 'A' && <= 'Z') || == '_'
}
func ( byte) bool {
return isValidLabelNameStart() || ( >= '0' && <= '9')
}
func ( byte) bool {
return isValidLabelNameStart() || == ':'
}
func ( byte) bool {
return isValidLabelNameContinuation() || == ':'
}
func ( byte) bool {
return == ' ' || == '\t'
}
func ( string) bool {
return len() > 6 && [len()-6:] == "_count"
}
func ( string) bool {
return len() > 4 && [len()-4:] == "_sum"
}
func ( string) bool {
return len() > 7 && [len()-7:] == "_bucket"
}
func ( string) string {
switch {
case isCount():
return [:len()-6]
case isSum():
return [:len()-4]
default:
return
}
}
func ( string) string {
switch {
case isCount():
return [:len()-6]
case isSum():
return [:len()-4]
case isBucket():
return [:len()-7]
default:
return
}
}
func ( string) (float64, error) {
if strings.ContainsAny(, "pP_") {
return 0, fmt.Errorf("unsupported character in float")
}
return strconv.ParseFloat(, 64)
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |