Commit b392bba7 by David Committed by GitHub

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
parent c2bd36f5
......@@ -299,6 +299,7 @@ export interface ExploreQueryFieldProps<
}
export interface ExploreStartPageProps {
datasource?: DataSourceApi;
onClickExample: (query: DataQuery) => void;
}
......
......@@ -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 && (
......
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>
);
);
}
}
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>
);
}
}
......@@ -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
......
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,
};
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>
......
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,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,
};
......@@ -345,7 +345,7 @@
font-size: $font-size-h3;
}
.cheat-sheet-item__expression {
.cheat-sheet-item__example {
margin: $space-xs 0;
cursor: pointer;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment