Skip to content
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: Added query editor and builder support for new Logfmt features #74619

Merged
merged 31 commits into from
Sep 22, 2023

Conversation

matyax
Copy link
Contributor

@matyax matyax commented Sep 8, 2023

This PR adds support for new Loki LogQL features:

  • --strict flag
  • --keep-empty flag
  • Support for parameters (labels)

Additional changes:

  • Completions have been improved with better handling of trailing spaces and pipes, when inserting text
  • Situations: path has been renamed to paths to allow better grouping of multiple paths that produce the same situation.

Which issue(s) does this PR fix?:

Fixes #71121

Special notes for your reviewer:

No special steps to follow. Just install the updated dependencies, write LogQL queries, and expect to be helped by the autocomplete suggestions.

Some examples:

imagen

imagen

imagen

Exception:

imagen

In this case we cannot offer labels because the extracted log query has errors: {place="luna"} | logfmt --keep-empty float, [$__auto]. We could attempt to improve this in a follow up PR by using error nodes to clean up the query.

@grafana-delivery-bot grafana-delivery-bot bot added this to the 10.2.x milestone Sep 8, 2023
@matyax matyax marked this pull request as ready for review September 12, 2023 12:59
@matyax matyax requested a review from a team as a code owner September 12, 2023 12:59
@matyax matyax added add to changelog no-backport Skip backport of PR labels Sep 12, 2023
@matyax
Copy link
Contributor Author

matyax commented Sep 12, 2023

Opening for review. Take it for a spin and let me know how it feels. I will be submitting the query builder changes in another PR.

@@ -451,7 +507,10 @@ function resolveDurations(node: SyntaxNode, text: string, pos: number): Situatio
}

function resolveLogRange(node: SyntaxNode, text: string, pos: number): Situation | null {
return resolveLogOrLogRange(node, text, pos, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After pipe used to be hardcoded, now se use the current query to decide. This prevents duplicated pipes in metric queries.

@matyax matyax requested a review from a team as a code owner September 13, 2023 12:53
@matyax matyax requested review from ashharrison90 and removed request for a team September 13, 2023 12:53
Copy link
Member

@ivanahuckova ivanahuckova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Left couple of comments and questions bellow. 🙂

};

expect(await getCompletions(situation, completionProvider)).toMatchInlineSnapshot(`
[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it seems like we are mixing mock completion (with mock docs) and real completion (with real docs). Why is that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also these snapshot tests are quite long (100s of lines of code). What if we just checked that it has correct labels, rather than checking everything, as that's what we care about.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it seems like we are mixing mock completion (with mock docs) and real completion (with real docs). Why is that?

Because the mocks come from:

jest.mock('../../../querybuilder/operations', () => ({
  explainOperator: () => 'Operator docs',
}));

The reason is so tests doesn't break because we change the documentation. The usual "tests should be resilient to change".

Also these snapshot tests are quite long (100s of lines of code). What if we just checked that it has correct labels, rather than checking everything, as that's what we care about.

The whole way we test the completion output here is very cumbersome, to say the least. After having worked multiple times on this, I'm now more inclined to use inline snapshots, as they give you a readable output of the completion in a "similar" way of using the editor and seeing what it offers. I find that with snapshots it's easier to see that the output is good, unlike

const expected = buildAfterSelectorCompletions('logfmt', 'json', afterPipe, true);
expect(completions).toEqual(expected);
where the expectations are invisible.

We were also doing "snapshots" in the form of hardcoded json objects in places like this:

test('Returns completion options when the situation is IN_GROUPING', async () => {
const situation: Situation = { type: 'IN_GROUPING', logQuery: '' };
const completions = await getCompletions(situation, completionProvider);
expect(completions).toEqual([
{
insertText: 'extracted',
label: 'extracted',
triggerOnInsert: false,
type: 'LABEL_NAME',
},
{
insertText: 'place',
label: 'place',
triggerOnInsert: false,
type: 'LABEL_NAME',
},
{
insertText: 'source',
label: 'source',
triggerOnInsert: false,
type: 'LABEL_NAME',
},
]);
});

If I were to test this again, at the moment I would change everything to snapshots or json objects, but I'm 100% open to suggestions. Right now I can't see a way of testing completions without having to use very large strings or objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think here to check only important things. For example documentation is not that important, but label and type.

@@ -136,7 +146,7 @@ export type Situation =
};

type Resolver = {
path: NodeType[];
paths: NodeType[][];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

const LOGFMT_ARGUMENT_COMPLETIONS: Completion[] = [
{
type: 'FUNCTION',
label: 'strict',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you considered using --strict as label. I personally think it would make it more understandable and it would also make autocomplete better
image

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I would expect the autocomplete to pick up -, but it's probably generating another new path for logfmt. I'll iterate a bit on this 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done
Uploading imagen.png…

* Query builder: add params to logfmt definition

* Logfmt operation: add default params

* Query builder: update deprecated JsonExpression

* Operation utils: update logfmt renderer

* Query builder: parse LogfmtParser

* Query builder: parse LogfmtExpressionParser

* Remove console log

* Remove unused variable

* Remove extra character from render

* Update unit tests

* Fix unit tests

* Operations: remove restParams from logfmt booleans

* Parsing: group cases

* Formatting

* Formatting

* Update modifyQuery

* LogContextProvider: update with parser changes

* LogContextProvider: remove unnecessary type castings

It takes more energy to write `as unknow as LokiQuery` than to write a refId.

* Formatting
@@ -225,8 +230,9 @@ describe('LogContextProvider', () => {
10,
LogRowContextQueryDirection.Backward,
{
expr: '{bar="baz"} | logfmt | line_format = "foo"',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Realized this was incorrect in most line format expressions, so I fixed it.

line format

Also removed some type castings that were not really necessary. FYI @svennergr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, good 👀 !

@github-actions
Copy link
Contributor

Backend code coverage report for PR #74619
No changes

@github-actions
Copy link
Contributor

github-actions bot commented Sep 18, 2023

Frontend code coverage report for PR #74619

Plugin Main PR Difference
loki 85.47% 85.7% .23%

Copy link
Member

@ivanahuckova ivanahuckova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job! Left some comments, but LGTM.

};

expect(await getCompletions(situation, completionProvider)).toMatchInlineSnapshot(`
[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think here to check only important things. For example documentation is not that important, but label and type.

@matyax matyax enabled auto-merge (squash) September 22, 2023 09:12
@matyax matyax merged commit 91ed2a6 into main Sep 22, 2023
15 checks passed
@matyax matyax deleted the matyax/logfmt-with-arguments branch September 22, 2023 09:34
rwwiv pushed a commit that referenced this pull request Oct 2, 2023
* Loki autocomplete: add IN_LOGFMT situation for log queries

* Loki autocomplete: add IN_LOGFMT situation for metric queries

* Loki autocomplete: improve handling of trailing pipes and spaces

* Loki autocomplete: add logfmt arguments completion

* Loki autocomplete: add flags support to IN_LOGFMT

* Loki autocomplete: extend IN_LOGFMT situation with labels and flag

* Loki autocomplete: return logQuery in IN_LOGFMT situation

* Loki autocomplete: offer label completions when IN_LOGFMT

* Query utils: update parser detection method

* Validation: update test

* Loki autocomplete: improve IN_LOGFMT detection when in metric query

* Loki autocomplete: improve logfmt suggestions

* Loki autocomplete: improve logfmt suggestions in different scenarios

* Loki autocomplete situation: refactor resolvers to support multiple paths

* Situation: add test case

* Loki autocomplete: allow user to use 2 flags

* Situation: change flag to flags

* Remove console log

* Validation: import test parser

* Completions: better handling of trailing comma scenario

* Upgrade lezer-logql

* Revert temporary imports

* Loki Query Builder: Add support for new logfmt features (#74858)

* Query builder: add params to logfmt definition

* Logfmt operation: add default params

* Query builder: update deprecated JsonExpression

* Operation utils: update logfmt renderer

* Query builder: parse LogfmtParser

* Query builder: parse LogfmtExpressionParser

* Remove console log

* Remove unused variable

* Remove extra character from render

* Update unit tests

* Fix unit tests

* Operations: remove restParams from logfmt booleans

* Parsing: group cases

* Formatting

* Formatting

* Update modifyQuery

* LogContextProvider: update with parser changes

* LogContextProvider: remove unnecessary type castings

It takes more energy to write `as unknow as LokiQuery` than to write a refId.

* Formatting

* Situation: use charAt instead of substring with endsWith

* Situation: explain logfmt suggestions

* Logfmt: improve flag suggestions

* Remove console log

* Completions: update test
@matyax matyax changed the title Loki Query Editor: Add support for new logfmt features Loki: Added query editor and builder support for new Logfmt features Oct 19, 2023
@zerok zerok modified the milestones: 10.2.x, 10.2.0 Oct 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Loki: add support for new logfmt features
4 participants