From a3c99f48711f39b13e4334bef03d8fbe5e774a35 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Jan 2020 13:53:31 +0100 Subject: [PATCH] Logs: Fix parsing for logfmt fields that have parens (#21407) * Logs: Fix parsing for logfmt fields that have parens - added `()`, `[]`, and `{}` to be allowed in logfmt field names * Fix matcher --- packages/grafana-data/src/utils/logs.test.ts | 13 ++++++++++++- packages/grafana-data/src/utils/logs.ts | 6 +++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/grafana-data/src/utils/logs.test.ts b/packages/grafana-data/src/utils/logs.test.ts index 0f5c4e6e2a2..9525b0e858e 100644 --- a/packages/grafana-data/src/utils/logs.test.ts +++ b/packages/grafana-data/src/utils/logs.test.ts @@ -98,21 +98,25 @@ describe('LogsParsers', () => { test('should return parsed fields', () => { expect( parser.getFields( - 'foo=bar baz="42 + 1" msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1"' + 'foo=bar baz="42 + 1" msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1" time(ms)=50 label{foo}=bar' ) ).toEqual([ 'foo=bar', 'baz="42 + 1"', 'msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1"', + 'time(ms)=50', + 'label{foo}=bar', ]); }); test('should return label for field', () => { expect(parser.getLabelFromField('foo=bar')).toBe('foo'); + expect(parser.getLabelFromField('time(ms)=50')).toBe('time(ms)'); }); test('should return value for field', () => { expect(parser.getValueFromField('foo=bar')).toBe('bar'); + expect(parser.getValueFromField('time(ms)=50')).toBe('50'); expect( parser.getValueFromField( 'msg="[resolver] received A record \\"127.0.0.1\\" for \\"localhost.\\" from udp:192.168.65.1"' @@ -126,6 +130,13 @@ describe('LogsParsers', () => { expect(match).toBeDefined(); expect(match![1]).toBe('bar'); }); + + test('should build a valid complex value matcher', () => { + const matcher = parser.buildMatcher('time(ms)'); + const match = 'time(ms)=50'.match(matcher); + expect(match).toBeDefined(); + expect(match![1]).toBe('50'); + }); }); describe('JSON', () => { diff --git a/packages/grafana-data/src/utils/logs.ts b/packages/grafana-data/src/utils/logs.ts index 3047ead11e8..03c00342f62 100644 --- a/packages/grafana-data/src/utils/logs.ts +++ b/packages/grafana-data/src/utils/logs.ts @@ -1,4 +1,4 @@ -import { countBy, chain } from 'lodash'; +import { countBy, chain, escapeRegExp } from 'lodash'; import { LogLevel, LogRowModel, LogLabelStatsModel, LogsParser } from '../types/logs'; import { DataFrame, FieldType } from '../types/index'; @@ -8,7 +8,7 @@ import { ArrayVector } from '../vector/ArrayVector'; // first a label from start of the string or first white space, then any word chars until "=" // second either an empty quotes, or anything that starts with quote and ends with unescaped quote, // or any non whitespace chars that do not start with qoute -const LOGFMT_REGEXP = /(?:^|\s)(\w+)=(""|(?:".*?[^\\]"|[^"\s]\S*))/; +const LOGFMT_REGEXP = /(?:^|\s)([\w\(\)\[\]\{\}]+)=(""|(?:".*?[^\\]"|[^"\s]\S*))/; /** * Returns the log level of a log line. @@ -85,7 +85,7 @@ export const LogsParsers: { [name: string]: LogsParser } = { }, logfmt: { - buildMatcher: label => new RegExp(`(?:^|\\s)${label}=("[^"]*"|\\S+)`), + buildMatcher: label => new RegExp(`(?:^|\\s)${escapeRegExp(label)}=("[^"]*"|\\S+)`), getFields: line => { const fields: string[] = []; line.replace(new RegExp(LOGFMT_REGEXP, 'g'), substring => {