Skip to content

Commit

Permalink
Link extensions: Support multiple include filters (#1103)
Browse files Browse the repository at this point in the history
* feat: link extensions - support multiple include filters

---------

Co-authored-by: Matias Chomicki <matyax@gmail.com>
  • Loading branch information
gtk-grafana and matyax authored Feb 26, 2025
1 parent 12281df commit 2e6d9d5
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 32 deletions.
12 changes: 8 additions & 4 deletions src/services/extensions/links.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ describe('contextToLink', () => {
}),
});
});
it('should ignore "or" expressions', () => {
it('should support multiple field inclusion expressions', () => {
const target = getTestTarget({
expr: `{cluster="eu-west-1"} | pod!=\`mimir-ingester-xjntw\` | logfmt | duration <= 10s or duration > 10.2s `,
});
Expand All @@ -753,9 +753,13 @@ describe('contextToLink', () => {
const expectedMetadataString = `&var-metadata=${encodeFilter(
`pod|!=|${addCustomInputPrefixAndValueLabels('mimir-ingester-xjntw')}`
)}`;
const expectedLineFiltersUrlString = `&var-fields=${encodeFilter(
`duration|<=|${addAdHocFilterUserInputPrefix('{"value":"10s"__gfc__"parser":"logfmt"}')},10s`
)}`;
const expectedLineFiltersUrlString =
`&var-fields=${encodeFilter(
`duration|<=|${addAdHocFilterUserInputPrefix('{"value":"10s"__gfc__"parser":"logfmt"}')},10s`
)}` +
`&var-fields=${encodeFilter(
`duration|>|${addAdHocFilterUserInputPrefix('{"value":"10.2s"__gfc__"parser":"logfmt"}')},10.2s`
)}`;

expect(config).toEqual({
path: getPath({
Expand Down
35 changes: 7 additions & 28 deletions src/services/logqlMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,6 @@ export function getNodesFromQuery(query: string, nodeTypes?: number[]): SyntaxNo
return nodes;
}

/**
* Returns the leaf nodes on the left-hand-side matching nodeTypes
* @param query
* @param nodeTypes
*/
export function getLHSLeafNodesFromQuery(query: string, nodeTypes: number[]): SyntaxNode[] {
const nodes: SyntaxNode[] = [];
const tree: Tree = parser.parse(query);

tree.iterate({
enter: (node): false | void => {
if (nodeTypes.includes(node.type.id)) {
let leftChild: SyntaxNode | null;
while ((leftChild = node.node.firstChild) !== null) {
if (!nodeTypes.includes(leftChild.node.type.id)) {
nodes.push(node.node);
return false;
}
node = leftChild;
}
}
},
});
return nodes;
}

function getAllPositionsInNodeByType(node: SyntaxNode, type: number): NodePosition[] {
if (node.type.id === type) {
return [NodePosition.fromNode(node)];
Expand Down Expand Up @@ -287,11 +261,16 @@ function getStringFieldOperator(matcher: SyntaxNode) {
function parseFields(query: string, fields: FieldFilter[], context: PluginExtensionPanelContext, lokiQuery: LokiQuery) {
const dataFrame = context.data?.series.find((frame) => frame.refId === lokiQuery.refId);
// We do not currently support "or" in Grafana Logs Drilldown, so grab the left hand side LabelFilter leaf nodes as this will be the first filter expression in a given pipeline stage
const allFields = getLHSLeafNodesFromQuery(query, [LabelFilter]);

const allFields = getNodesFromQuery(query, [LabelFilter]);
for (const matcher of allFields) {
const position = NodePosition.fromNode(matcher);
const expression = position.getExpression(query);
const isParentNode = matcher.getChild(LabelFilter);

// If the Label filter contains other Label Filter nodes, we want to skip this node so we only add the leaf LabelFilter nodes
if (isParentNode) {
continue;
}

// Skip error expression, it will get added automatically when Grafana Logs Drilldown adds a parser
if (expression.substring(0, 9) === `__error__`) {
Expand Down

0 comments on commit 2e6d9d5

Please sign in to comment.