| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| # Filter language |
| |
| This language allows us to filter logs based on a set of conditions. The language is designed to |
| map directly to UI chip elements. |
| |
| ## Examples |
| |
| 1. Show logs of severity info or higher for entries where the moniker is `core/ffx-laboratory:hello` |
| or the message contains “hello”. |
| |
| ``` |
| (moniker:core/ffx-laboratory:hello | message:hello) severity:info |
| ``` |
| |
| 2. Show logs of severity error or higher, where the component url contains a package named hello, |
| where the manifest is named hello or where the message contains “hello world” but where the |
| message doesn’t contain “bye”. |
| |
| ``` |
| (package_name:hello | manifest:hello | message:"hello world") severity:error !message:"bye" |
| ``` |
| |
| 3. Show logs from both core/foo and core/bar |
| |
| ``` |
| moniker:core/foo|core/bar |
| |
| # which is the same as: |
| moniker:core/foo|moniker:core/bar |
| ``` |
| |
| 4. Show logs where any field contains foo, bar or baz either in the message, url, moniker, etc. |
| |
| ``` |
| any:foo|bar|baz |
| ``` |
| |
| ## Grammar |
| |
| ``` |
| <operation> := <filter group> | <filter group> <and> <operation> |
| |
| <filter group> := <single filter> | (<filter disjunction>) |
| |
| <filter disjunction> := <single filter> |
| | <not> <single filter> |
| | <single filter> <or> <filter disjunction> |
| |
| <single filter> := <category> <core operator> <core operator values> |
| | severity <operator> <severity> |
| | <pid tid category> <core operator> <numbers> |
| | <custom key> <operator> <custom key values> |
| | <regex> |
| | <string> |
| |
| <core operator> := <contains> | <equality> |
| <operator> := <contains> | <equality> | <comparison> |
| <contains> := ':' |
| <equality> := '=' |
| <comparison> := '>' | '<' | '>=' | '<=' |
| |
| <category> := moniker | tag | package_name | manifest | message | any |
| <pid tid category> := pid | tid |
| |
| <custom key values> := <numbers> | <boolean> | <regex> | <strings> |
| <core operator values> := <regex> | <strings> |
| |
| <severity> := trace | debug | info | warn | error | fatal |
| |
| <boolean> := true | false |
| |
| <regex> := '/' <chars with spaces> '/' |
| |
| <strings> := <string> | <string> '|' <strings> |
| <string> := <chars> | "<chars with spaces>" |
| |
| <chars> := any sequence of characters without space |
| <chars with spaces> := any sequence of characters with spaces |
| |
| <numbers> := <number> | <number> '|' <numbers> |
| <number> := any sequence of numbers, including negative, exp, etc |
| |
| <or> := '|' | or |
| <not> := not | '!' |
| <and> := and | '&' | ε # just writing a space will be interpreted as AND in the top level |
| ``` |
| |
| The severities, logical operators, categories are case insensitive. When doing a `:` (contains) |
| operation, the value being matched against will be treated as case insensitive. |
| |
| The following categories are supported: |
| |
| 1. Moniker: identifies the component that emitted the log. |
| |
| 2. Tag: a log entry might contain one or more tags in its metadata. |
| |
| 3. Package name: The name of the package present in the corresponding section of the url with |
| which the component was launched. Supported operations: |
| |
| 4. Manifest: The name of the manifest present in the corresponding section of the url with which |
| the component was launched. |
| |
| 5. Message: The log message. |
| |
| 6. Severity: The severity of the log. |
| |
| 7. Pid and Tid: the process and thread ids that emitted the log. |
| |
| 8. Custom key: A structured field contained in the payload of a structured log. |
| |
| 9. Any: Matches a value in any of the previous sections. |
| |
| All categories support `=`(equals) and `:` (contains). The `:` operator will do substring search, |
| equality when dealing with numbers of booleans or a minimum severity operation when applied to |
| `severity`. Custom keys and severities also support `>`, `<`, `>=`, `<=`, `=`. |
| */ |
| |
| Expression |
| = head:FilterGroup tail:(_ (And _)? FilterGroup)* { |
| return tail.reduce((result, element) => { |
| return result.concat([element[2]]); |
| }, [head]); |
| } |
| |
| FilterGroup |
| = "(" _ head:Filter tail:(_ Or _ Filter)* _ ")" { |
| let group = tail.reduce((result, element) => { |
| return result.concat(element[3]); |
| }, [head]) |
| return group; |
| } |
| / filter:Filter { return [filter]; } |
| |
| |
| Filter |
| = Not _ single:SingleFilter { |
| single.not = true; |
| return single; |
| } |
| / single:SingleFilter { return single; } |
| |
| SingleFilter |
| = "severity"i operator:Operator severities:Severities { |
| return { |
| category: 'severity', |
| subCategory: undefined, |
| operator: operator, |
| values: severities, |
| not: false |
| }; |
| } |
| / category:Category operator:CoreOperator strings:CoreOperatorValues { |
| return { |
| category: category, |
| subCategory: undefined, |
| operator: operator, |
| values: strings, |
| not: false, |
| }; |
| } |
| / category:PidTidCategory operator:CoreOperator numbers:Numbers { |
| return { |
| category: category, |
| subCategory: undefined, |
| operator: operator, |
| values: numbers, |
| not: false, |
| } |
| } |
| / customKey:CustomKey operator:Operator values:CustomKeyValues { |
| return { |
| category: 'custom', |
| subCategory: customKey, |
| operator: operator, |
| values: values, |
| not: false |
| } |
| } |
| / regex:Regex { |
| return { |
| category: 'any', |
| subCategory: undefined, |
| operator: 'contains', |
| values: [regex], |
| not: false, |
| } |
| } |
| / string:String { |
| return { |
| category: 'any', |
| subCategory: undefined, |
| operator: 'contains', |
| values: [string], |
| not: false, |
| } |
| }; |
| |
| // NOTE: the returned types must match the ones in the Operator type in filter.ts. |
| CoreOperator |
| = ":" { return 'contains'; } |
| / "=" { return 'equal'; } |
| |
| // NOTE: the returned types must match the ones in the Operator type in filter.ts. |
| Operator |
| = ":" { return 'contains'; } |
| / "=" { return 'equal'; } |
| / ">=" { return 'greaterEq'; } |
| / ">" { return 'greater'; } |
| / "<=" { return 'lessEq'; } |
| / "<" { return 'less'; } |
| |
| // NOTE: the returned types must match the ones in the Category type in filter.ts. |
| Category |
| = "any"i |
| / "custom"i |
| / "manifest"i |
| / "message"i |
| / "moniker"i |
| / "package-name"i |
| / "tag"i; |
| |
| PidTidCategory |
| = "pid"i |
| / "tid"i; |
| |
| CustomKeyValues |
| = Numbers |
| / b:Boolean { return [b]; } |
| / r:Regex { return [r]; } |
| / Strings |
| |
| CoreOperatorValues |
| = r:Regex { return [r]; } |
| / Strings |
| |
| Severities |
| = head:Severity tail:("|" Severities)* { |
| return tail.reduce((result, element) => { |
| return result.concat(element[1]); |
| }, [head]); |
| } |
| |
| // NOTE: the returned types must match the ones in the Severity type in filter.ts. |
| Severity |
| = "trace"i |
| / "debug"i |
| / "info"i |
| / "warning"i { return "warn"; } |
| / "warn"i |
| / "error"i |
| / "fatal"i |
| |
| Boolean |
| = "true"i { return true; } |
| / "false"i { return false; } |
| |
| Regex = "/" chars:RegexChar* "/" { |
| return new RegExp(chars.join("")); |
| } |
| |
| RegexChar |
| = [^/\\] |
| / "\\/" { return "/"; } |
| / "\\\\" { return "\\"; } |
| |
| Strings |
| = head:String tail:("|" Strings)* { |
| return tail.reduce((result, element) => { |
| return result.concat(element[1]); |
| }, [head]); |
| } |
| |
| CustomKey = [^:\\|\\(\\)><=" ]+ { return text(); } |
| |
| Numbers |
| = head:Number tail:("|" Numbers)* { |
| return tail.reduce((result, element) => { |
| return result.concat(element[1]); |
| }, [head]); |
| } |
| |
| // TODO(fxbug.dev/99486): support floating point, exponentials. |
| Number |
| = [0-9]+ { return parseInt(text(), 10); } |
| |
| String |
| = '"' chars:CharWithinQuotes* '"' { |
| return chars.join(''); |
| } |
| / chars:CharNotInQuotes+ { return chars.join(''); } |
| |
| CharWithinQuotes |
| = [^"\\] |
| / '\\"' { return '"'; } |
| / "\\\\" { return "\\"; } |
| |
| CharNotInQuotes |
| = [^"\\|\\(\\): ] |
| / "\\" char:( |
| '"' |
| / '\\' |
| / '(' |
| / ')' |
| / ':' |
| / ' ' |
| ) { |
| return char; |
| } |
| |
| Not = "!" / "not"i |
| And = "&" / "and"i |
| Or = "|" / "or"i |
| |
| // Whitespace |
| _ = [ \t\n\r]* |