فهرست منبع

Improve Loki logs render with ANSI colors (#15558)

* Improve Loki logs render with ANSI colors

* fixup! Improve Loki logs render with ANSI colors

* fixup! Improve Loki logs render with ANSI colors

* fixup! Improve Loki logs render with ANSI colors
Valentin Agachi 6 سال پیش
والد
کامیت
425636ff70

+ 4 - 2
public/app/core/logs_model.ts

@@ -22,7 +22,7 @@ export enum LogLevel {
   dbug = 'debug',
   debug = 'debug',
   trace = 'trace',
-  unkown = 'unkown',
+  unknown = 'unknown',
 }
 
 export const LogLevelColor = {
@@ -32,7 +32,7 @@ export const LogLevelColor = {
   [LogLevel.info]: colors[0],
   [LogLevel.debug]: colors[5],
   [LogLevel.trace]: colors[2],
-  [LogLevel.unkown]: getThemeColor('#8e8e8e', '#dde4ed'),
+  [LogLevel.unknown]: getThemeColor('#8e8e8e', '#dde4ed'),
 };
 
 export interface LogSearchMatch {
@@ -44,9 +44,11 @@ export interface LogSearchMatch {
 export interface LogRowModel {
   duplicates?: number;
   entry: string;
+  hasAnsi: boolean;
   key: string; // timestamp + labels
   labels: LogsStreamLabels;
   logLevel: LogLevel;
+  raw: string;
   searchWords?: string[];
   timestamp: string; // ISO with nanosec precision
   timeFromNow: string;

+ 1 - 1
public/app/features/explore/LogMessageAnsi.tsx

@@ -38,7 +38,7 @@ export class LogMessageAnsi extends PureComponent<Props, State> {
     prevValue: '',
   };
 
-  static getDerivedStateFromProps(props, state) {
+  static getDerivedStateFromProps(props: Props, state: State) {
     if (props.value === state.prevValue) {
       return null;
     }

+ 9 - 9
public/app/features/explore/LogRow.tsx

@@ -5,7 +5,7 @@ import classnames from 'classnames';
 
 import { LogRowModel, LogLabelStatsModel, LogsParser, calculateFieldStats, getParser } from 'app/core/logs_model';
 import { LogLabels } from './LogLabels';
-import { findHighlightChunksInText, hasAnsiCodes } from 'app/core/utils/text';
+import { findHighlightChunksInText } from 'app/core/utils/text';
 import { LogLabelStats } from './LogLabelStats';
 import { LogMessageAnsi } from './LogMessageAnsi';
 
@@ -130,13 +130,13 @@ export class LogRow extends PureComponent<Props, State> {
       parsedFieldHighlights,
       showFieldStats,
     } = this.state;
+    const { entry, hasAnsi, raw } = row;
     const previewHighlights = highlighterExpressions && !_.isEqual(highlighterExpressions, row.searchWords);
     const highlights = previewHighlights ? highlighterExpressions : row.searchWords;
-    const needsHighlighter = highlights && highlights.length > 0;
+    const needsHighlighter = highlights && highlights.length > 0 && highlights[0].length > 0;
     const highlightClassName = classnames('logs-row__match-highlight', {
       'logs-row__match-highlight--preview': previewHighlights,
     });
-    const containsAnsiCodes = hasAnsiCodes(row.entry);
 
     return (
       <div className="logs-row">
@@ -160,25 +160,25 @@ export class LogRow extends PureComponent<Props, State> {
           </div>
         )}
         <div className="logs-row__message" onMouseEnter={this.onMouseOverMessage} onMouseLeave={this.onMouseOutMessage}>
-          {containsAnsiCodes && <LogMessageAnsi value={row.entry} />}
-          {!containsAnsiCodes && parsed && (
+          {parsed && (
             <Highlighter
               autoEscape
               highlightTag={FieldHighlight(this.onClickHighlight)}
-              textToHighlight={row.entry}
+              textToHighlight={entry}
               searchWords={parsedFieldHighlights}
               highlightClassName="logs-row__field-highlight"
             />
           )}
-          {!containsAnsiCodes && !parsed && needsHighlighter && (
+          {!parsed && needsHighlighter && (
             <Highlighter
-              textToHighlight={row.entry}
+              textToHighlight={entry}
               searchWords={highlights}
               findChunks={findHighlightChunksInText}
               highlightClassName={highlightClassName}
             />
           )}
-          {!containsAnsiCodes && !parsed && !needsHighlighter && row.entry}
+          {hasAnsi && !parsed && !needsHighlighter && <LogMessageAnsi value={raw} />}
+          {!hasAnsi && !parsed && !needsHighlighter && entry}
           {showFieldStats && (
             <div className="logs-row__stats">
               <LogLabelStats

+ 44 - 2
public/app/plugins/datasource/loki/result_transformer.test.ts

@@ -11,11 +11,11 @@ import {
 
 describe('getLoglevel()', () => {
   it('returns no log level on empty line', () => {
-    expect(getLogLevel('')).toBe(LogLevel.unkown);
+    expect(getLogLevel('')).toBe(LogLevel.unknown);
   });
 
   it('returns no log level on when level is part of a word', () => {
-    expect(getLogLevel('this is information')).toBe(LogLevel.unkown);
+    expect(getLogLevel('this is information')).toBe(LogLevel.unknown);
   });
 
   it('returns same log level for long and short version', () => {
@@ -158,4 +158,46 @@ describe('mergeStreamsToLogs()', () => {
       },
     ]);
   });
+
+  it('detects ANSI codes', () => {
+    expect(
+      mergeStreamsToLogs([
+        {
+          labels: '{foo="bar"}',
+          entries: [
+            {
+              line: "foo: 'bar'",
+              ts: '1970-01-01T00:00:00Z',
+            },
+          ],
+        },
+        {
+          labels: '{bar="foo"}',
+          entries: [
+            {
+              line: "bar: 'foo'",
+              ts: '1970-01-01T00:00:00Z',
+            },
+          ],
+        },
+      ]).rows
+    ).toMatchObject([
+      {
+        entry: "bar: 'foo'",
+        hasAnsi: false,
+        key: 'EK1970-01-01T00:00:00Z{bar="foo"}',
+        labels: { bar: 'foo' },
+        logLevel: 'unknown',
+        raw: "bar: 'foo'",
+      },
+      {
+        entry: "foo: 'bar'",
+        hasAnsi: true,
+        key: 'EK1970-01-01T00:00:00Z{foo="bar"}',
+        labels: { foo: 'bar' },
+        logLevel: 'unknown',
+        raw: "foo: 'bar'",
+      },
+    ]);
+  });
 });

+ 8 - 3
public/app/plugins/datasource/loki/result_transformer.ts

@@ -1,3 +1,4 @@
+import ansicolor from 'ansicolor';
 import _ from 'lodash';
 import moment from 'moment';
 
@@ -11,6 +12,7 @@ import {
   LogsStreamLabels,
   LogsMetaKind,
 } from 'app/core/logs_model';
+import { hasAnsiCodes } from 'app/core/utils/text';
 import { DEFAULT_MAX_LINES } from './datasource';
 
 /**
@@ -21,7 +23,7 @@ import { DEFAULT_MAX_LINES } from './datasource';
  */
 export function getLogLevel(line: string): LogLevel {
   if (!line) {
-    return LogLevel.unkown;
+    return LogLevel.unknown;
   }
   let level: LogLevel;
   Object.keys(LogLevel).forEach(key => {
@@ -33,7 +35,7 @@ export function getLogLevel(line: string): LogLevel {
     }
   });
   if (!level) {
-    level = LogLevel.unkown;
+    level = LogLevel.unknown;
   }
   return level;
 }
@@ -125,6 +127,7 @@ export function processEntry(
   const timeFromNow = time.fromNow();
   const timeLocal = time.format('YYYY-MM-DD HH:mm:ss');
   const logLevel = getLogLevel(line);
+  const hasAnsi = hasAnsiCodes(line);
 
   return {
     key,
@@ -133,7 +136,9 @@ export function processEntry(
     timeEpochMs,
     timeLocal,
     uniqueLabels,
-    entry: line,
+    hasAnsi,
+    entry: hasAnsi ? ansicolor.strip(line) : line,
+    raw: line,
     labels: parsedLabels,
     searchWords: search ? [search] : [],
     timestamp: ts,