LokiCheatSheet.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { shuffle } from 'lodash';
  2. import React, { PureComponent } from 'react';
  3. import { QueryEditorHelpProps } from '@grafana/data';
  4. import { reportInteraction } from '@grafana/runtime';
  5. import LokiLanguageProvider from '../language_provider';
  6. import { LokiQuery } from '../types';
  7. const DEFAULT_EXAMPLES = ['{job="default/prometheus"}'];
  8. const PREFERRED_LABELS = ['job', 'app', 'k8s_app'];
  9. const EXAMPLES_LIMIT = 5;
  10. const LOGQL_EXAMPLES = [
  11. {
  12. title: 'Log pipeline',
  13. expression: '{job="mysql"} |= "metrics" | logfmt | duration > 10s',
  14. label:
  15. 'This query targets the MySQL job, filters out logs that don’t contain the word "metrics" and parses each log line to extract more labels and filters with them.',
  16. },
  17. {
  18. title: 'Count over time',
  19. expression: 'count_over_time({job="mysql"}[5m])',
  20. label: 'This query counts all the log lines within the last five minutes for the MySQL job.',
  21. },
  22. {
  23. title: 'Rate',
  24. expression: 'rate(({job="mysql"} |= "error" != "timeout")[10s])',
  25. label:
  26. 'This query gets the per-second rate of all non-timeout errors within the last ten seconds for the MySQL job.',
  27. },
  28. {
  29. title: 'Aggregate, count, and group',
  30. expression: 'sum(count_over_time({job="mysql"}[5m])) by (level)',
  31. label: 'Get the count of logs during the last five minutes, grouping by level.',
  32. },
  33. ];
  34. export default class LokiCheatSheet extends PureComponent<QueryEditorHelpProps<LokiQuery>, { userExamples: string[] }> {
  35. declare userLabelTimer: NodeJS.Timeout;
  36. state = {
  37. userExamples: [],
  38. };
  39. componentDidMount() {
  40. this.scheduleUserLabelChecking();
  41. reportInteraction('grafana_loki_cheatsheet_opened', {});
  42. }
  43. componentWillUnmount() {
  44. clearTimeout(this.userLabelTimer);
  45. }
  46. scheduleUserLabelChecking() {
  47. this.userLabelTimer = setTimeout(this.checkUserLabels, 1000);
  48. }
  49. checkUserLabels = async () => {
  50. // Set example from user labels
  51. const provider: LokiLanguageProvider = this.props.datasource?.languageProvider;
  52. if (provider.started) {
  53. const labels = provider.getLabelKeys() || [];
  54. const preferredLabel = PREFERRED_LABELS.find((l) => labels.includes(l));
  55. if (preferredLabel) {
  56. const values = await provider.getLabelValues(preferredLabel);
  57. const userExamples = shuffle(values)
  58. .slice(0, EXAMPLES_LIMIT)
  59. .map((value) => `{${preferredLabel}="${value}"}`);
  60. this.setState({ userExamples });
  61. }
  62. } else {
  63. this.scheduleUserLabelChecking();
  64. }
  65. };
  66. renderExpression(expr: string) {
  67. const { onClickExample } = this.props;
  68. const onClick = (query: LokiQuery) => {
  69. onClickExample(query);
  70. reportInteraction('grafana_loki_cheatsheet_example_clicked', {});
  71. };
  72. return (
  73. <div className="cheat-sheet-item__example" key={expr} onClick={(e) => onClick({ refId: 'A', expr })}>
  74. <code>{expr}</code>
  75. </div>
  76. );
  77. }
  78. render() {
  79. const { userExamples } = this.state;
  80. const hasUserExamples = userExamples.length > 0;
  81. return (
  82. <div>
  83. <h2>Loki Cheat Sheet</h2>
  84. <div className="cheat-sheet-item">
  85. <div className="cheat-sheet-item__title">See your logs</div>
  86. <div className="cheat-sheet-item__label">
  87. Start by selecting a log stream from the Log browser, or alternatively you can write a stream selector into
  88. the query field.
  89. </div>
  90. {hasUserExamples ? (
  91. <div>
  92. <div className="cheat-sheet-item__label">Here are some example streams from your logs:</div>
  93. {userExamples.map((example) => this.renderExpression(example))}
  94. </div>
  95. ) : (
  96. <div>
  97. <div className="cheat-sheet-item__label">Here is an example of a log stream:</div>
  98. {this.renderExpression(DEFAULT_EXAMPLES[0])}
  99. </div>
  100. )}
  101. </div>
  102. <div className="cheat-sheet-item">
  103. <div className="cheat-sheet-item__title">Combine stream selectors</div>
  104. {this.renderExpression('{app="cassandra",namespace="prod"}')}
  105. <div className="cheat-sheet-item__label">Returns all log lines from streams that have both labels.</div>
  106. </div>
  107. <div className="cheat-sheet-item">
  108. <div className="cheat-sheet-item__title">Filtering for search terms.</div>
  109. {this.renderExpression('{app="cassandra"} |~ "(duration|latency)s*(=|is|of)s*[d.]+"')}
  110. {this.renderExpression('{app="cassandra"} |= "exact match"')}
  111. {this.renderExpression('{app="cassandra"} != "do not match"')}
  112. <div className="cheat-sheet-item__label">
  113. <a href="https://grafana.com/docs/loki/latest/logql/#log-pipeline" target="logql">
  114. LogQL
  115. </a>{' '}
  116. supports exact and regular expression filters.
  117. </div>
  118. </div>
  119. {LOGQL_EXAMPLES.map((item) => (
  120. <div className="cheat-sheet-item" key={item.expression}>
  121. <div className="cheat-sheet-item__title">{item.title}</div>
  122. {this.renderExpression(item.expression)}
  123. <div className="cheat-sheet-item__label">{item.label}</div>
  124. </div>
  125. ))}
  126. </div>
  127. );
  128. }
  129. }