-
Notifications
You must be signed in to change notification settings - Fork 11.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loki: Add the ability to prettify logql queries #64337
Conversation
Backend code coverage report for PR #64337 |
Frontend code coverage report for PR #64337
|
public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work so far! I will try to come back to this with a potential approach using Lezer to replace the template variables.
Leaving this as a comment to continue later. I've been playing with using the paser to get the node that points to a variable, to be used after formatting to replace the interpolated string with the variable name. For example, I would like to keep a reference to the So I can use it later to replace it with the variable name using the parsed prettified query: To attempt to do that, I would store a path to the node (node parents until the root of the tree), for every detected variable. Datasource: async formatQuery(query: string) {
const transformedQuery = transformForFormatting(query, this.interpolateString.bind(this));
let formatted = await this.metadataRequest('format_query', { query: transformedQuery.query });
return formatted;
} Formatter.ts import { SyntaxNode } from '@lezer/common';
import { ScopedVars, TypedVariableModel } from '@grafana/data';
import { parser } from '@grafana/lezer-logql';
import { ErrorId } from 'app/plugins/datasource/prometheus/querybuilder/shared/parsingUtils';
import { placeHolderScopedVars } from './components/monaco-query-field/monaco-completion-provider/validation';
type Transformation = {
original: string;
replaced: string;
}
type TransformedQuery = {
transformations: Transformation[];
query: string;
}
interface ParseError {
text: string;
node: SyntaxNode;
}
function parseQuery(query: string) {
const parseErrors = new Map<SyntaxNode | null, ParseError[]>();
const tree = parser.parse(query);
tree.iterate({
enter: (nodeRef): false | void => {
if (nodeRef.type.id === ErrorId) {
const node = nodeRef.node;
const siblings = parseErrors.get(node.parent) || [];
parseErrors.set(node.parent, [...siblings, {
node,
text: query.substring(node.from, node.to),
}]);
}
},
});
return parseErrors;
}
export function transformForFormatting(query: string, interpolateString: (string: string, scopedVars?: ScopedVars) => string): TransformedQuery {
// We get the possible variables from the parse errors
const errors = parseQuery(query);
// Nothing to fix, the query is ready for formatting
if (!errors.size) {
return {
transformations: [],
query,
};
}
errors.forEach((parseError, parent) => {
if (parseError[0].text === '$') {
const variable = query.substring(parseError[0].node.from, parseError[parseError.length-1].node.from);
const chain = [parent?.type.name];
let lastParent = parent;
while (lastParent?.parent) {
chain.push(lastParent.parent.type.name);
lastParent = lastParent.parent;
}
console.log(variable, interpolateString(variable, placeHolderScopedVars), chain);
}
});
return {
transformations: [],
query,
};
}
export function revertTransformations(query: string, transformations: Transformation[]): string {
return '';
} Anyway, given the complexity that this could require, I wonder if we should either:
In any case, this could be useful to you as an introduction to Lezer. To be continued. |
Will follow up with something that might work. Stay tunned. |
We might try something like this: #64591 Let me know what you think. |
* Prettify query: use the parser to interpolate and recover the original query * Fix typo Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> * Fix typo Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> * fix: reverse transformation not working --------- Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Gareth Dawson <gwdawson.work@gmail.com>
@gwdawson Found the bug that is currently duplicating the output of The root cause of the problem is the function Instead of getting an array of You get 2 times those two: All ☝️ are recursive calls over the same nodes. So, you correctly format the first two From that, the following change fixes the bug: function iterateNode(node: SyntaxNode, lookingFor: number[]): SyntaxNode[] {
const nodes: SyntaxNode[] = [];
let child = node.firstChild;
while (child) {
if (lookingFor.includes(child.type.id)) {
nodes.push(child);
lookingFor.splice(lookingFor.indexOf(child.type.id), 1); // <---- this
}
nodes.push(...iterateNode(child, lookingFor));
child = child.nextSibling;
}
return nodes;
} The problem with that change is that it breaks other code that is dependent on One of the main issues with For example: function iterateNode(node: SyntaxNode, lookingFor: number[]): SyntaxNode[] {
const nodes: SyntaxNode[] = [];
let child = node.firstChild;
while (child) {
if (lookingFor.includes(child.type.id)) {
nodes.push(child);
}
// nodes.push(...iterateNode(child, lookingFor)); This should not be necessary
child = child.nextSibling; // maybe this while should move to the children if there are no more siblings?
}
return nodes;
} TL;DRAdding Other things I noticedI would suggest that you avoid recursive calls, specially calls to grafana/public/app/plugins/datasource/loki/formatter.ts Lines 329 to 331 in 610c420
There, I would change it to: const metricExpr = query.substring(node.from, node.to); // slice the MetricExpr part
const metricNode = getNodeFromQuery(metricExpr, MetricExpr); // Get the new MetricExpr node
if (metricNode) {
response += indentMultiline(formatMetricExpr(metricNode, metricExpr), 1); // Pass it to formatMetricExpr
} PD: it works really well! Great work so far 🤝 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last thing and we're done here 🚢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice work 👏 👏 👏
this change is related to the api implementation we tested. dismissing to unblock merge
* pushed to get help of a genius * fix: error response is not json * feat: make request on click * refactor: remove print statement * refactor: remove unnecessary code * feat: convert grafana variables to value for API request * use the parser to interpolate and recover the original query (#64591) * Prettify query: use the parser to interpolate and recover the original query * Fix typo Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> * Fix typo Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> * fix: reverse transformation not working --------- Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Co-authored-by: Gareth Dawson <gwdawson.work@gmail.com> * fix: bugs created from merge * refactor: move prettify code out of monaco editor * fix: variables with the same value get converted back to the incorect variable * refactor * use consistent styling with bigquery * fix: only allow text/plain and application/json * fix: only make the request if the query is valid * endpoint now returns application/json * prettify from js * WIP: not all cases are handles, code still needs cleaning up * WIP * large refactor, finished support for all pipeline expressions * add tests for all format functions * idk why these files changed * add support for range aggregation expr & refactor * add support for vector aggregation expressions * add support for bin op expression * add support for literal and vector expressions * add tests and fix some bugs * add support for distinct and decolorize * feat: update variable replace and return * fix: lezer throws an errow when using a range variable * remove api implementation * remove api implementation * remove type assertions * add feature flag * update naming * fix: bug incorrectly formatting unwrap with labelfilter * support label replace expr * remove duplicate code (after migration) * add more tests * validate query before formatting * move tests to lezer repo * add feature tracking * populate feature tracking with some data * upgrade lezer version to 0.1.7 * bump lezer to 0.1.8 * add tests --------- Co-authored-by: Matias Chomicki <matyax@gmail.com> Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
What is this feature?
adds a button giving the user an option to prettify their loki query.
Which issue(s) does this PR fix?:
Fixes #61041
how to test this pr:
in grafana/grafana
lokiFormatQuery