Browse Source

Loki: Updated cheat sheet with new filter syntax (#18947)

* Loki: Updated cheat sheet with new filter syntax

- updated cheat sheet with new filter syntax
- added user-specific examples from the user's label set
- added link to LogQL docs
- separated styles for examples (clickable) and expressions

* Review feedback
David 6 năm trước cách đây
mục cha
commit
b392bba745

+ 1 - 0
packages/grafana-ui/src/types/datasource.ts

@@ -299,6 +299,7 @@ export interface ExploreQueryFieldProps<
 }
 
 export interface ExploreStartPageProps {
+  datasource?: DataSourceApi;
   onClickExample: (query: DataQuery) => void;
 }
 

+ 5 - 1
public/app/features/explore/Explore.tsx

@@ -286,7 +286,11 @@ export class Explore extends React.PureComponent<ExploreProps> {
                 return (
                   <main className="m-t-2" style={{ width }}>
                     <ErrorBoundaryAlert>
-                      {showingStartPage && <StartPage onClickExample={this.onClickExample} />}
+                      {showingStartPage && (
+                        <div className="grafana-info-box grafana-info-box--max-lg">
+                          <StartPage onClickExample={this.onClickExample} datasource={datasourceInstance} />
+                        </div>
+                      )}
                       {!showingStartPage && (
                         <>
                           {mode === ExploreMode.Metrics && (

+ 99 - 41
public/app/plugins/datasource/loki/components/LokiCheatSheet.tsx

@@ -1,43 +1,101 @@
-import React from 'react';
-
-const CHEAT_SHEET_ITEMS = [
-  {
-    title: 'See your logs',
-    label: 'Start by selecting a log stream from the Log labels selector.',
-  },
-  {
-    title: 'Logs from a "job"',
-    expression: '{job="default/prometheus"}',
-    label: 'Returns all log lines emitted by instances of this job.',
-  },
-  {
-    title: 'Combine stream selectors',
-    expression: '{app="cassandra",namespace="prod"}',
-    label: 'Returns all log lines from streams that have both labels.',
-  },
-  {
-    title: 'Search for text',
-    expression: '{app="cassandra"} (duration|latency)\\s*(=|is|of)\\s*[\\d\\.]+',
-    label: 'Add a regular expression after the selector to filter for.',
-  },
-];
-
-export default (props: any) => (
-  <div>
-    <h2>Loki Cheat Sheet</h2>
-    {CHEAT_SHEET_ITEMS.map(item => (
-      <div className="cheat-sheet-item" key={item.title}>
-        <div className="cheat-sheet-item__title">{item.title}</div>
-        {item.expression && (
-          <div
-            className="cheat-sheet-item__expression"
-            onClick={e => props.onClickExample({ refId: 'A', expr: item.expression })}
-          >
-            <code>{item.expression}</code>
+import React, { PureComponent } from 'react';
+import { shuffle } from 'lodash';
+import { ExploreStartPageProps, DataQuery } from '@grafana/ui';
+import LokiLanguageProvider from '../language_provider';
+
+const DEFAULT_EXAMPLES = ['{job="default/prometheus"}'];
+const PREFERRED_LABELS = ['job', 'app', 'k8s_app'];
+const EXAMPLES_LIMIT = 5;
+
+export default class LokiCheatSheet extends PureComponent<ExploreStartPageProps, { userExamples: string[] }> {
+  userLabelTimer: NodeJS.Timeout;
+  state = {
+    userExamples: DEFAULT_EXAMPLES,
+  };
+
+  componentDidMount() {
+    this.scheduleUserLabelChecking();
+  }
+
+  componentWillUnmount() {
+    clearTimeout(this.userLabelTimer);
+  }
+
+  scheduleUserLabelChecking() {
+    this.userLabelTimer = setTimeout(this.checkUserLabels, 1000);
+  }
+
+  checkUserLabels = async () => {
+    // Set example from user labels
+    const provider: LokiLanguageProvider = this.props.datasource.languageProvider;
+    if (provider.started) {
+      const labels = provider.getLabelKeys() || [];
+      const preferredLabel = PREFERRED_LABELS.find(l => labels.includes(l));
+      if (preferredLabel) {
+        const values = await provider.getLabelValues(preferredLabel);
+        const userExamples = shuffle(values)
+          .slice(0, EXAMPLES_LIMIT)
+          .map(value => `{${preferredLabel}="${value}"}`);
+        this.setState({ userExamples });
+      }
+    } else {
+      this.scheduleUserLabelChecking();
+    }
+  };
+
+  renderExpression(expr: string) {
+    const { onClickExample } = this.props;
+
+    return (
+      <div
+        className="cheat-sheet-item__example"
+        key={expr}
+        onClick={e => onClickExample({ refId: 'A', expr } as DataQuery)}
+      >
+        <code>{expr}</code>
+      </div>
+    );
+  }
+
+  render() {
+    const { userExamples } = this.state;
+
+    return (
+      <div>
+        <h2>Loki Cheat Sheet</h2>
+        <div className="cheat-sheet-item">
+          <div className="cheat-sheet-item__title">See your logs</div>
+          <div className="cheat-sheet-item__label">Start by selecting a log stream from the Log labels selector.</div>
+          <div className="cheat-sheet-item__label">
+            Alternatively, you can write a stream selector into the query field:
+          </div>
+          {this.renderExpression('{job="default/prometheus"}')}
+          {userExamples !== DEFAULT_EXAMPLES && userExamples.length > 0 ? (
+            <div>
+              <div className="cheat-sheet-item__label">Here are some example streams from your logs:</div>
+              {userExamples.map(example => this.renderExpression(example))}
+            </div>
+          ) : null}
+        </div>
+        <div className="cheat-sheet-item">
+          <div className="cheat-sheet-item__title">Combine stream selectors</div>
+          {this.renderExpression('{app="cassandra",namespace="prod"}')}
+          <div className="cheat-sheet-item__label">Returns all log lines from streams that have both labels.</div>
+        </div>
+
+        <div className="cheat-sheet-item">
+          <div className="cheat-sheet-item__title">Filtering for search terms.</div>
+          {this.renderExpression('{app="cassandra"} |~ "(duration|latency)s*(=|is|of)s*[d.]+"')}
+          {this.renderExpression('{app="cassandra"} |= "exact match"')}
+          {this.renderExpression('{app="cassandra"} != "do not match"')}
+          <div className="cheat-sheet-item__label">
+            <a href="https://github.com/grafana/loki/blob/master/docs/usage.md#filter-expression" target="logql">
+              LogQL
+            </a>{' '}
+            supports exact and regular expression filters.
           </div>
-        )}
-        <div className="cheat-sheet-item__label">{item.label}</div>
+        </div>
       </div>
-    ))}
-  </div>
-);
+    );
+  }
+}

+ 0 - 13
public/app/plugins/datasource/loki/components/LokiStartPage.tsx

@@ -1,13 +0,0 @@
-import React, { PureComponent } from 'react';
-import LokiCheatSheet from './LokiCheatSheet';
-import { ExploreStartPageProps } from '@grafana/ui';
-
-export default class LokiStartPage extends PureComponent<ExploreStartPageProps> {
-  render() {
-    return (
-      <div className="grafana-info-box grafana-info-box--max-lg">
-        <LokiCheatSheet onClickExample={this.props.onClickExample} />
-      </div>
-    );
-  }
-}

+ 13 - 1
public/app/plugins/datasource/loki/language_provider.ts

@@ -86,11 +86,23 @@ export default class LokiLanguageProvider extends LanguageProvider {
    */
   start = () => {
     if (!this.startTask) {
-      this.startTask = this.fetchLogLabels(this.initialRange);
+      this.startTask = this.fetchLogLabels(this.initialRange).then(() => {
+        this.started = true;
+        return [];
+      });
     }
     return this.startTask;
   };
 
+  getLabelKeys(): string[] {
+    return this.labelKeys[EMPTY_SELECTOR];
+  }
+
+  async getLabelValues(key: string): Promise<string[]> {
+    await this.fetchLabelValues(key, this.initialRange);
+    return this.labelValues[EMPTY_SELECTOR][key];
+  }
+
   /**
    * Return suggestions based on input that can be then plugged into a typeahead dropdown.
    * Keep this DOM-free for testing

+ 2 - 2
public/app/plugins/datasource/loki/module.ts

@@ -1,6 +1,6 @@
 import Datasource from './datasource';
 
-import LokiStartPage from './components/LokiStartPage';
+import LokiCheatSheet from './components/LokiCheatSheet';
 import LokiQueryField from './components/LokiQueryField';
 import LokiQueryEditor from './components/LokiQueryEditor';
 import { LokiAnnotationsQueryCtrl } from './LokiAnnotationsQueryCtrl';
@@ -14,6 +14,6 @@ export {
   LokiQueryEditor as QueryEditor,
   LokiConfigCtrl as ConfigCtrl,
   LokiQueryField as ExploreQueryField,
-  LokiStartPage as ExploreStartPage,
+  LokiCheatSheet as ExploreStartPage,
   LokiAnnotationsQueryCtrl as AnnotationsQueryCtrl,
 };

+ 4 - 3
public/app/plugins/datasource/prometheus/components/PromCheatSheet.tsx

@@ -1,4 +1,5 @@
 import React from 'react';
+import { ExploreStartPageProps, DataQuery } from '@grafana/ui';
 
 const CHEAT_SHEET_ITEMS = [
   {
@@ -19,15 +20,15 @@ const CHEAT_SHEET_ITEMS = [
   },
 ];
 
-export default (props: any) => (
+export default (props: ExploreStartPageProps) => (
   <div>
     <h2>PromQL Cheat Sheet</h2>
     {CHEAT_SHEET_ITEMS.map(item => (
       <div className="cheat-sheet-item" key={item.expression}>
         <div className="cheat-sheet-item__title">{item.title}</div>
         <div
-          className="cheat-sheet-item__expression"
-          onClick={e => props.onClickExample({ refId: 'A', expr: item.expression })}
+          className="cheat-sheet-item__example"
+          onClick={e => props.onClickExample({ refId: 'A', expr: item.expression } as DataQuery)}
         >
           <code>{item.expression}</code>
         </div>

+ 0 - 13
public/app/plugins/datasource/prometheus/components/PromStart.tsx

@@ -1,13 +0,0 @@
-import React, { PureComponent } from 'react';
-import PromCheatSheet from './PromCheatSheet';
-import { ExploreStartPageProps } from '@grafana/ui';
-
-export default class PromStart extends PureComponent<ExploreStartPageProps> {
-  render() {
-    return (
-      <div className="grafana-info-box grafana-info-box--max-lg">
-        <PromCheatSheet onClickExample={this.props.onClickExample} />
-      </div>
-    );
-  }
-}

+ 2 - 2
public/app/plugins/datasource/prometheus/module.ts

@@ -2,7 +2,7 @@ import { PrometheusDatasource } from './datasource';
 import { PromQueryEditor } from './components/PromQueryEditor';
 import { PrometheusConfigCtrl } from './config_ctrl';
 
-import PrometheusStartPage from './components/PromStart';
+import PromCheatSheet from './components/PromCheatSheet';
 import PromQueryField from './components/PromQueryField';
 
 class PrometheusAnnotationsQueryCtrl {
@@ -15,5 +15,5 @@ export {
   PrometheusConfigCtrl as ConfigCtrl,
   PrometheusAnnotationsQueryCtrl as AnnotationsQueryCtrl,
   PromQueryField as ExploreQueryField,
-  PrometheusStartPage as ExploreStartPage,
+  PromCheatSheet as ExploreStartPage,
 };

+ 1 - 1
public/sass/pages/_explore.scss

@@ -345,7 +345,7 @@
   font-size: $font-size-h3;
 }
 
-.cheat-sheet-item__expression {
+.cheat-sheet-item__example {
   margin: $space-xs 0;
   cursor: pointer;
 }