1
0
Fork 0
telegraf/plugins/parsers/wavefront/element.go
Daniel Baumann 4978089aab
Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-24 07:26:29 +02:00

225 lines
4.3 KiB
Go

package wavefront
import (
"errors"
"fmt"
"io"
"strconv"
"time"
)
var (
errInvalidTimestamp = errors.New("invalid timestamp")
)
type elementParser interface {
parse(p *PointParser, pt *Point) error
}
type nameParser struct{}
type valueParser struct{}
type timestampParser struct {
optional bool
}
type whiteSpaceParser struct {
nextOptional bool
}
type tagParser struct{}
type loopedParser struct {
wrappedParser elementParser
wsParser *whiteSpaceParser
}
func (*nameParser) parse(p *PointParser, pt *Point) error {
// Valid characters are: a-z, A-Z, 0-9, hyphen ("-"), underscore ("_"), dot (".").
// Forward slash ("/") and comma (",") are allowed if metricName is enclosed in double quotes.
// Delta (U+2206) is allowed as the first character of the
// metricName
name, err := parseLiteral(p)
if err != nil {
return err
}
pt.Name = name
return nil
}
func (*valueParser) parse(p *PointParser, pt *Point) error {
tok, lit := p.scan()
if tok == EOF {
return fmt.Errorf("found %q, expected number", lit)
}
p.writeBuf.Reset()
if tok == MinusSign {
p.writeBuf.WriteString(lit)
tok, lit = p.scan()
}
for tok != EOF && (tok == Letter || tok == Number || tok == Dot || tok == MinusSign) {
p.writeBuf.WriteString(lit)
tok, lit = p.scan()
}
p.unscan()
pt.Value = p.writeBuf.String()
if _, err := strconv.ParseFloat(pt.Value, 64); err != nil {
return fmt.Errorf("invalid metric value %s", pt.Value)
}
return nil
}
func (ep *timestampParser) parse(p *PointParser, pt *Point) error {
tok, lit := p.scan()
if tok == EOF {
if ep.optional {
p.unscanTokens(2)
return setTimestamp(pt, 0, 1)
}
return fmt.Errorf("found %q, expected number", lit)
}
if tok != Number {
if ep.optional {
p.unscanTokens(2)
return setTimestamp(pt, 0, 1)
}
return errInvalidTimestamp
}
p.writeBuf.Reset()
for tok == Number {
p.writeBuf.WriteString(lit)
tok, lit = p.scan()
}
p.unscan()
tsStr := p.writeBuf.String()
ts, err := strconv.ParseInt(tsStr, 10, 64)
if err != nil {
return err
}
return setTimestamp(pt, ts, len(tsStr))
}
func setTimestamp(pt *Point, ts int64, numDigits int) error {
if numDigits == 19 {
// nanoseconds
ts = ts / 1e9
} else if numDigits == 16 {
// microseconds
ts = ts / 1e6
} else if numDigits == 13 {
// milliseconds
ts = ts / 1e3
} else if numDigits != 10 {
// must be in seconds, return error if not 0
if ts != 0 {
return errInvalidTimestamp
}
ts = getCurrentTime()
}
pt.Timestamp = ts
return nil
}
func (ep *loopedParser) parse(p *PointParser, pt *Point) error {
for {
err := ep.wrappedParser.parse(p, pt)
if err != nil {
return err
}
err = ep.wsParser.parse(p, pt)
if errors.Is(err, io.EOF) {
break
}
}
return nil
}
func (*tagParser) parse(p *PointParser, pt *Point) error {
k, err := parseLiteral(p)
if err != nil {
if k == "" {
return nil
}
return err
}
next, lit := p.scan()
if next != Equals {
return fmt.Errorf("found %q, expected equals", lit)
}
v, err := parseLiteral(p)
if err != nil {
return err
}
if len(pt.Tags) == 0 {
pt.Tags = make(map[string]string)
}
pt.Tags[k] = v
return nil
}
func (ep *whiteSpaceParser) parse(p *PointParser, _ *Point) error {
tok := Ws
for tok == Ws {
tok, _ = p.scan()
}
if tok == EOF {
if !ep.nextOptional {
return io.EOF
}
return nil
}
p.unscan()
return nil
}
func parseQuotedLiteral(p *PointParser) (string, error) {
p.writeBuf.Reset()
escaped := false
tok, lit := p.scan()
for tok != EOF && (tok != Quotes || (tok == Quotes && escaped)) {
// let everything through
escaped = tok == Backslash
p.writeBuf.WriteString(lit)
tok, lit = p.scan()
}
if tok == EOF {
return "", fmt.Errorf("found %q, expected quotes", lit)
}
return p.writeBuf.String(), nil
}
func parseLiteral(p *PointParser) (string, error) {
tok, lit := p.scan()
if tok == EOF {
return "", fmt.Errorf("found %q, expected literal", lit)
}
if tok == Quotes {
return parseQuotedLiteral(p)
}
p.writeBuf.Reset()
for tok != EOF && tok > literalBeg && tok < literalEnd {
p.writeBuf.WriteString(lit)
tok, lit = p.scan()
if tok == Delta {
return "", errors.New("found delta inside metric name")
}
}
if tok == Quotes {
return "", errors.New("found quote inside unquoted literal")
}
p.unscan()
return p.writeBuf.String(), nil
}
func getCurrentTime() int64 {
return time.Now().UnixNano() / 1e9
}