syntax.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. import { Grammar } from 'prismjs';
  2. import { CompletionItem } from '@grafana/ui';
  3. export const QUERY_COMMANDS: CompletionItem[] = [
  4. {
  5. label: 'fields',
  6. documentation: 'Retrieves the specified fields from log events',
  7. },
  8. { label: 'display', documentation: 'Specifies which fields to display in the query results' },
  9. {
  10. label: 'filter',
  11. documentation: 'Filters the results of a query based on one or more conditions',
  12. },
  13. {
  14. label: 'stats',
  15. documentation: 'Calculates aggregate statistics based on the values of log fields',
  16. },
  17. { label: 'sort', documentation: 'Sorts the retrieved log events' },
  18. { label: 'limit', documentation: 'Specifies the number of log events returned by the query' },
  19. {
  20. label: 'parse',
  21. documentation:
  22. 'Extracts data from a log field, creating one or more ephemeral fields that you can process further in the query',
  23. },
  24. ];
  25. export const COMPARISON_OPERATORS = ['=', '!=', '<', '<=', '>', '>='];
  26. export const ARITHMETIC_OPERATORS = ['+', '-', '*', '/', '^', '%'];
  27. export const NUMERIC_OPERATORS = [
  28. {
  29. label: 'abs',
  30. detail: 'abs(a)',
  31. documentation: 'Absolute value.',
  32. },
  33. {
  34. label: 'ceil',
  35. detail: 'ceil(a)',
  36. documentation: 'Round to ceiling (the smallest integer that is greater than the value of a).',
  37. },
  38. {
  39. label: 'floor',
  40. detail: 'floor(a)',
  41. documentation: 'Round to floor (the largest integer that is smaller than the value of a).',
  42. },
  43. {
  44. label: 'greatest',
  45. detail: 'greatest(a,b, ... z)',
  46. documentation: 'Returns the largest value.',
  47. },
  48. {
  49. label: 'least',
  50. detail: 'least(a, b, ... z)',
  51. documentation: 'Returns the smallest value.',
  52. },
  53. {
  54. label: 'log',
  55. detail: 'log(a)',
  56. documentation: 'Natural logarithm.',
  57. },
  58. {
  59. label: 'sqrt',
  60. detail: 'sqrt(a)',
  61. documentation: 'Square root.',
  62. },
  63. ];
  64. export const GENERAL_FUNCTIONS = [
  65. {
  66. label: 'ispresent',
  67. detail: 'ispresent(fieldname)',
  68. documentation: 'Returns true if the field exists.',
  69. },
  70. {
  71. label: 'coalesce',
  72. detail: 'coalesce(fieldname1, fieldname2, ... fieldnamex)',
  73. documentation: 'Returns the first non-null value from the list.',
  74. },
  75. ];
  76. export const STRING_FUNCTIONS = [
  77. {
  78. label: 'isempty',
  79. detail: 'isempty(fieldname)',
  80. documentation: 'Returns true if the field is missing or is an empty string.',
  81. },
  82. {
  83. label: 'isblank',
  84. detail: 'isblank(fieldname)',
  85. documentation: 'Returns true if the field is missing, an empty string, or contains only white space.',
  86. },
  87. {
  88. label: 'concat',
  89. detail: 'concat(string1, string2, ... stringz)',
  90. documentation: 'Concatenates the strings.',
  91. },
  92. {
  93. label: 'ltrim',
  94. detail: 'ltrim(string) or ltrim(string1, string2)',
  95. documentation:
  96. 'Remove white space from the left of the string. If the function has a second string argument, it removes the characters of string2 from the left of string1.',
  97. },
  98. {
  99. label: 'rtrim',
  100. detail: 'rtrim(string) or rtrim(string1, string2)',
  101. documentation:
  102. 'Remove white space from the right of the string. If the function has a second string argument, it removes the characters of string2 from the right of string1.',
  103. },
  104. {
  105. label: 'trim',
  106. detail: 'trim(string) or trim(string1, string2)',
  107. documentation:
  108. 'Remove white space from both ends of the string. If the function has a second string argument, it removes the characters of string2 from both sides of string1.',
  109. },
  110. {
  111. label: 'strlen',
  112. detail: 'strlen(string)',
  113. documentation: 'Returns the length of the string in Unicode code points.',
  114. },
  115. {
  116. label: 'toupper',
  117. detail: 'toupper(string)',
  118. documentation: 'Converts the string to uppercase.',
  119. },
  120. {
  121. label: 'tolower',
  122. detail: 'tolower(string)',
  123. documentation: 'Converts the string to lowercase.',
  124. },
  125. {
  126. label: 'substr',
  127. detail: 'substr(string1, x), or substr(string1, x, y)',
  128. documentation:
  129. 'Returns a substring from the index specified by the number argument to the end of the string. If the function has a second number argument, it contains the length of the substring to be retrieved.',
  130. },
  131. {
  132. label: 'replace',
  133. detail: 'replace(string1, string2, string3)',
  134. documentation: 'Replaces all instances of string2 in string1 with string3.',
  135. },
  136. {
  137. label: 'strcontains',
  138. detail: 'strcontains(string1, string2)',
  139. documentation: 'Returns 1 if string1 contains string2 and 0 otherwise.',
  140. },
  141. ];
  142. export const DATETIME_FUNCTIONS = [
  143. {
  144. label: 'bin',
  145. detail: 'bin(period)',
  146. documentation: 'Rounds the value of @timestamp to the given period and then truncates.',
  147. },
  148. {
  149. label: 'datefloor',
  150. detail: 'datefloor(a, period)',
  151. documentation: 'Truncates the timestamp to the given period.',
  152. },
  153. {
  154. label: 'dateceil',
  155. detail: 'dateceil(a, period)',
  156. documentation: 'Rounds up the timestamp to the given period and then truncates.',
  157. },
  158. {
  159. label: 'fromMillis',
  160. detail: 'fromMillis(fieldname)',
  161. documentation:
  162. 'Interprets the input field as the number of milliseconds since the Unix epoch and converts it to a timestamp.',
  163. },
  164. {
  165. label: 'toMillis',
  166. detail: 'toMillis(fieldname)',
  167. documentation:
  168. 'Converts the timestamp found in the named field into a number representing the milliseconds since the Unix epoch.',
  169. },
  170. ];
  171. export const IP_FUNCTIONS = [
  172. {
  173. label: 'isValidIp',
  174. detail: 'isValidIp(fieldname)',
  175. documentation: 'Returns true if the field is a valid v4 or v6 IP address.',
  176. },
  177. {
  178. label: 'isValidIpV4',
  179. detail: 'isValidIpV4(fieldname)',
  180. documentation: 'Returns true if the field is a valid v4 IP address.',
  181. },
  182. {
  183. label: 'isValidIpV6',
  184. detail: 'isValidIpV6(fieldname)',
  185. documentation: 'Returns true if the field is a valid v6 IP address.',
  186. },
  187. {
  188. label: 'isIpInSubnet',
  189. detail: 'isIpInSubnet(fieldname, string)',
  190. documentation: 'Returns true if the field is a valid v4 or v6 IP address within the specified v4 or v6 subnet.',
  191. },
  192. {
  193. label: 'isIpv4InSubnet',
  194. detail: 'isIpv4InSubnet(fieldname, string)',
  195. documentation: 'Returns true if the field is a valid v4 IP address within the specified v4 subnet.',
  196. },
  197. {
  198. label: 'isIpv6InSubnet',
  199. detail: 'isIpv6InSubnet(fieldname, string)',
  200. documentation: 'Returns true if the field is a valid v6 IP address within the specified v6 subnet.',
  201. },
  202. ];
  203. export const BOOLEAN_FUNCTIONS = [
  204. {
  205. label: 'ispresent',
  206. detail: 'ispresent(fieldname)',
  207. documentation: 'Returns true if the field exists.',
  208. },
  209. {
  210. label: 'isempty',
  211. detail: 'isempty(fieldname)',
  212. documentation: 'Returns true if the field is missing or is an empty string.',
  213. },
  214. {
  215. label: 'isblank',
  216. detail: 'isblank(fieldname)',
  217. documentation: 'Returns true if the field is missing, an empty string, or contains only white space.',
  218. },
  219. {
  220. label: 'strcontains',
  221. detail: 'strcontains(string1, string2)',
  222. documentation: 'Returns 1 if string1 contains string2 and 0 otherwise.',
  223. },
  224. ...IP_FUNCTIONS,
  225. ];
  226. export const AGGREGATION_FUNCTIONS_STATS = [
  227. {
  228. label: 'avg',
  229. detail: 'avg(NumericFieldname)',
  230. documentation: 'The average of the values in the specified field.',
  231. },
  232. {
  233. label: 'count',
  234. detail: 'count(fieldname) or count(*)',
  235. documentation: 'Counts the log records.',
  236. },
  237. {
  238. label: 'count_distinct',
  239. detail: 'count_distinct(fieldname)',
  240. documentation: 'Returns the number of unique values for the field.',
  241. },
  242. {
  243. label: 'max',
  244. detail: 'max(fieldname)',
  245. documentation: 'The maximum of the values for this log field in the queried logs.',
  246. },
  247. {
  248. label: 'min',
  249. detail: 'min(fieldname)',
  250. documentation: 'The minimum of the values for this log field in the queried logs.',
  251. },
  252. {
  253. label: 'pct',
  254. detail: 'pct(fieldname, value)',
  255. documentation: 'A percentile indicates the relative standing of a value in a datas.',
  256. },
  257. {
  258. label: 'stddev',
  259. detail: 'stddev(NumericFieldname)',
  260. documentation: 'The standard deviation of the values in the specified field.',
  261. },
  262. {
  263. label: 'sum',
  264. detail: 'sum(NumericFieldname)',
  265. documentation: 'The sum of the values in the specified field.',
  266. },
  267. ];
  268. export const NON_AGGREGATION_FUNCS_STATS = [
  269. {
  270. label: 'earliest',
  271. detail: 'earliest(fieldname)',
  272. documentation:
  273. 'Returns the value of fieldName from the log event that has the earliest time stamp in the queried logs.',
  274. },
  275. {
  276. label: 'latest',
  277. detail: 'latest(fieldname)',
  278. documentation:
  279. 'Returns the value of fieldName from the log event that has the latest time stamp in the queried logs.',
  280. },
  281. {
  282. label: 'sortsFirst',
  283. detail: 'sortsFirst(fieldname)',
  284. documentation: 'Returns the value of fieldName that sorts first in the queried logs.',
  285. },
  286. {
  287. label: 'sortsLast',
  288. detail: 'sortsLast(fieldname)',
  289. documentation: 'Returns the value of fieldName that sorts last in the queried logs.',
  290. },
  291. ];
  292. export const STATS_FUNCS = [...AGGREGATION_FUNCTIONS_STATS, ...NON_AGGREGATION_FUNCS_STATS];
  293. export const KEYWORDS = ['as', 'like', 'by', 'in', 'desc', 'asc'];
  294. export const FIELD_AND_FILTER_FUNCTIONS = [
  295. ...NUMERIC_OPERATORS,
  296. ...GENERAL_FUNCTIONS,
  297. ...STRING_FUNCTIONS,
  298. ...DATETIME_FUNCTIONS,
  299. ...IP_FUNCTIONS,
  300. ];
  301. export const FUNCTIONS = [...FIELD_AND_FILTER_FUNCTIONS, ...STATS_FUNCS];
  302. const tokenizer: Grammar = {
  303. comment: {
  304. pattern: /^#.*/,
  305. greedy: true,
  306. },
  307. backticks: {
  308. pattern: /`.*?`/,
  309. alias: 'string',
  310. greedy: true,
  311. },
  312. quote: {
  313. pattern: /".*?"/,
  314. alias: 'string',
  315. greedy: true,
  316. },
  317. regex: {
  318. pattern: /\/.*?\/(?=\||\s*$|,)/,
  319. greedy: true,
  320. },
  321. 'query-command': {
  322. pattern: new RegExp(`\\b(?:${QUERY_COMMANDS.map((command) => command.label).join('|')})\\b`, 'i'),
  323. alias: 'function',
  324. },
  325. function: {
  326. pattern: new RegExp(`\\b(?:${FUNCTIONS.map((f) => f.label).join('|')})\\b`, 'i'),
  327. },
  328. keyword: {
  329. pattern: new RegExp(`(\\s+)(${KEYWORDS.join('|')})(?=\\s+)`, 'i'),
  330. lookbehind: true,
  331. },
  332. // 'log-group-name': {
  333. // pattern: /[\.\-_/#A-Za-z0-9]+/,
  334. // },
  335. 'field-name': {
  336. pattern: /(@?[_a-zA-Z]+[_.0-9a-zA-Z]*)|(`((\\`)|([^`]))*?`)/,
  337. greedy: true,
  338. },
  339. number: /\b-?\d+((\.\d*)?([eE][+-]?\d+)?)?\b/,
  340. 'command-separator': {
  341. pattern: /\|/,
  342. alias: 'punctuation',
  343. },
  344. 'comparison-operator': {
  345. pattern: /([<>]=?)|(!?=)/,
  346. },
  347. punctuation: /[{}()`,.]/,
  348. whitespace: /\s+/,
  349. };
  350. export default tokenizer;