parser.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package ottl // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl"
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "github.com/alecthomas/participle/v2"
  10. "go.opentelemetry.io/collector/component"
  11. "go.uber.org/zap"
  12. )
  13. type ErrorMode string
  14. const (
  15. IgnoreError ErrorMode = "ignore"
  16. PropagateError ErrorMode = "propagate"
  17. )
  18. func (e *ErrorMode) UnmarshalText(text []byte) error {
  19. str := ErrorMode(strings.ToLower(string(text)))
  20. switch str {
  21. case IgnoreError, PropagateError:
  22. *e = str
  23. return nil
  24. default:
  25. return fmt.Errorf("unknown error mode %v", str)
  26. }
  27. }
  28. // Statement holds a top level Statement for processing telemetry data. A Statement is a combination of a function
  29. // invocation and the boolean expression to match telemetry for invoking the function.
  30. type Statement[K any] struct {
  31. function Expr[K]
  32. condition BoolExpr[K]
  33. origText string
  34. }
  35. // Execute is a function that will execute the statement's function if the statement's condition is met.
  36. // Returns true if the function was run, returns false otherwise.
  37. // If the statement contains no condition, the function will run and true will be returned.
  38. // In addition, the functions return value is always returned.
  39. func (s *Statement[K]) Execute(ctx context.Context, tCtx K) (any, bool, error) {
  40. condition, err := s.condition.Eval(ctx, tCtx)
  41. if err != nil {
  42. return nil, false, err
  43. }
  44. var result any
  45. if condition {
  46. result, err = s.function.Eval(ctx, tCtx)
  47. if err != nil {
  48. return nil, true, err
  49. }
  50. }
  51. return result, condition, nil
  52. }
  53. // Condition holds a top level Condition. A Condition is a boolean expression to match telemetry.
  54. type Condition[K any] struct {
  55. condition BoolExpr[K]
  56. origText string
  57. }
  58. // Eval returns true if the condition was met for the given TransformContext and false otherwise.
  59. func (c *Condition[K]) Eval(ctx context.Context, tCtx K) (bool, error) {
  60. return c.condition.Eval(ctx, tCtx)
  61. }
  62. // Parser provides the means to parse OTTL Statements and Conditions given a specific set of functions,
  63. // a PathExpressionParser, and an EnumParser.
  64. type Parser[K any] struct {
  65. functions map[string]Factory[K]
  66. pathParser PathExpressionParser[K]
  67. enumParser EnumParser
  68. telemetrySettings component.TelemetrySettings
  69. }
  70. func NewParser[K any](
  71. functions map[string]Factory[K],
  72. pathParser PathExpressionParser[K],
  73. settings component.TelemetrySettings,
  74. options ...Option[K],
  75. ) (Parser[K], error) {
  76. if settings.Logger == nil {
  77. return Parser[K]{}, fmt.Errorf("logger cannot be nil")
  78. }
  79. p := Parser[K]{
  80. functions: functions,
  81. pathParser: pathParser,
  82. enumParser: func(*EnumSymbol) (*Enum, error) {
  83. return nil, fmt.Errorf("enums aren't supported for the current context: %T", new(K))
  84. },
  85. telemetrySettings: settings,
  86. }
  87. for _, opt := range options {
  88. opt(&p)
  89. }
  90. return p, nil
  91. }
  92. type Option[K any] func(*Parser[K])
  93. func WithEnumParser[K any](parser EnumParser) Option[K] {
  94. return func(p *Parser[K]) {
  95. p.enumParser = parser
  96. }
  97. }
  98. // ParseStatements parses string statements into ottl.Statement objects ready for execution.
  99. // Returns a slice of statements and a nil error on successful parsing.
  100. // If parsing fails, returns nil and a joined error containing each error per failed statement.
  101. func (p *Parser[K]) ParseStatements(statements []string) ([]*Statement[K], error) {
  102. parsedStatements := make([]*Statement[K], 0, len(statements))
  103. var parseErrs []error
  104. for _, statement := range statements {
  105. ps, err := p.ParseStatement(statement)
  106. if err != nil {
  107. parseErrs = append(parseErrs, fmt.Errorf("unable to parse OTTL statement %q: %w", statement, err))
  108. continue
  109. }
  110. parsedStatements = append(parsedStatements, ps)
  111. }
  112. if len(parseErrs) > 0 {
  113. return nil, errors.Join(parseErrs...)
  114. }
  115. return parsedStatements, nil
  116. }
  117. // ParseStatement parses a single string statement into a Statement struct ready for execution.
  118. // Returns a Statement and a nil error on successful parsing.
  119. // If parsing fails, returns nil and an error.
  120. func (p *Parser[K]) ParseStatement(statement string) (*Statement[K], error) {
  121. parsed, err := parseStatement(statement)
  122. if err != nil {
  123. return nil, err
  124. }
  125. function, err := p.newFunctionCall(parsed.Editor)
  126. if err != nil {
  127. return nil, err
  128. }
  129. expression, err := p.newBoolExpr(parsed.WhereClause)
  130. if err != nil {
  131. return nil, err
  132. }
  133. return &Statement[K]{
  134. function: function,
  135. condition: expression,
  136. origText: statement,
  137. }, nil
  138. }
  139. // ParseConditions parses string conditions into a Condition slice ready for execution.
  140. // Returns a slice of Condition and a nil error on successful parsing.
  141. // If parsing fails, returns nil and an error containing each error per failed condition.
  142. func (p *Parser[K]) ParseConditions(conditions []string) ([]*Condition[K], error) {
  143. parsedConditions := make([]*Condition[K], 0, len(conditions))
  144. var parseErrs []error
  145. for _, condition := range conditions {
  146. ps, err := p.ParseCondition(condition)
  147. if err != nil {
  148. parseErrs = append(parseErrs, fmt.Errorf("unable to parse OTTL condition %q: %w", condition, err))
  149. continue
  150. }
  151. parsedConditions = append(parsedConditions, ps)
  152. }
  153. if len(parseErrs) > 0 {
  154. return nil, errors.Join(parseErrs...)
  155. }
  156. return parsedConditions, nil
  157. }
  158. // ParseCondition parses a single string condition into a Condition objects ready for execution.
  159. // Returns an Condition and a nil error on successful parsing.
  160. // If parsing fails, returns nil and an error.
  161. func (p *Parser[K]) ParseCondition(condition string) (*Condition[K], error) {
  162. parsed, err := parseCondition(condition)
  163. if err != nil {
  164. return nil, err
  165. }
  166. expression, err := p.newBoolExpr(parsed)
  167. if err != nil {
  168. return nil, err
  169. }
  170. return &Condition[K]{
  171. condition: expression,
  172. origText: condition,
  173. }, nil
  174. }
  175. var parser = newParser[parsedStatement]()
  176. var conditionParser = newParser[booleanExpression]()
  177. func parseStatement(raw string) (*parsedStatement, error) {
  178. parsed, err := parser.ParseString("", raw)
  179. if err != nil {
  180. return nil, fmt.Errorf("statement has invalid syntax: %w", err)
  181. }
  182. err = parsed.checkForCustomError()
  183. if err != nil {
  184. return nil, err
  185. }
  186. return parsed, nil
  187. }
  188. func parseCondition(raw string) (*booleanExpression, error) {
  189. parsed, err := conditionParser.ParseString("", raw)
  190. if err != nil {
  191. return nil, fmt.Errorf("condition has invalid syntax: %w", err)
  192. }
  193. err = parsed.checkForCustomError()
  194. if err != nil {
  195. return nil, err
  196. }
  197. return parsed, nil
  198. }
  199. // newParser returns a parser that can be used to read a string into a parsedStatement. An error will be returned if the string
  200. // is not formatted for the DSL.
  201. func newParser[G any]() *participle.Parser[G] {
  202. lex := buildLexer()
  203. parser, err := participle.Build[G](
  204. participle.Lexer(lex),
  205. participle.Unquote("String"),
  206. participle.Elide("whitespace"),
  207. participle.UseLookahead(participle.MaxLookahead), // Allows negative lookahead to work properly in 'value' for 'mathExprLiteral'.
  208. )
  209. if err != nil {
  210. panic("Unable to initialize parser; this is a programming error in OTTL:" + err.Error())
  211. }
  212. return parser
  213. }
  214. // Statements represents a list of statements that will be executed sequentially for a TransformContext.
  215. type Statements[K any] struct {
  216. statements []*Statement[K]
  217. errorMode ErrorMode
  218. telemetrySettings component.TelemetrySettings
  219. }
  220. type StatementsOption[K any] func(*Statements[K])
  221. func WithErrorMode[K any](errorMode ErrorMode) StatementsOption[K] {
  222. return func(s *Statements[K]) {
  223. s.errorMode = errorMode
  224. }
  225. }
  226. func NewStatements[K any](statements []*Statement[K], telemetrySettings component.TelemetrySettings, options ...StatementsOption[K]) Statements[K] {
  227. s := Statements[K]{
  228. statements: statements,
  229. telemetrySettings: telemetrySettings,
  230. }
  231. for _, op := range options {
  232. op(&s)
  233. }
  234. return s
  235. }
  236. // Execute is a function that will execute all the statements in the Statements list.
  237. func (s *Statements[K]) Execute(ctx context.Context, tCtx K) error {
  238. for _, statement := range s.statements {
  239. _, _, err := statement.Execute(ctx, tCtx)
  240. if err != nil {
  241. if s.errorMode == PropagateError {
  242. err = fmt.Errorf("failed to execute statement: %v, %w", statement.origText, err)
  243. return err
  244. }
  245. s.telemetrySettings.Logger.Warn("failed to execute statement", zap.Error(err), zap.String("statement", statement.origText))
  246. }
  247. }
  248. return nil
  249. }
  250. // Eval returns true if any statement's condition is true and returns false otherwise.
  251. // Does not execute the statement's function.
  252. // When errorMode is `propagate`, errors cause the evaluation to be false and an error is returned.
  253. // When errorMode is `ignore`, errors cause evaluation to continue to the next statement.
  254. func (s *Statements[K]) Eval(ctx context.Context, tCtx K) (bool, error) {
  255. for _, statement := range s.statements {
  256. match, err := statement.condition.Eval(ctx, tCtx)
  257. if err != nil {
  258. if s.errorMode == PropagateError {
  259. err = fmt.Errorf("failed to eval statement: %v, %w", statement.origText, err)
  260. return false, err
  261. }
  262. s.telemetrySettings.Logger.Warn("failed to eval statement", zap.Error(err), zap.String("statement", statement.origText))
  263. continue
  264. }
  265. if match {
  266. return true, nil
  267. }
  268. }
  269. return false, nil
  270. }