Просмотр исходного кода

Merge pull request #14176 from grafana/davkal/explore-logging-level-filter

Explore: Filter logs by log level
David 7 лет назад
Родитель
Сommit
9afb8b64ed

+ 28 - 9
public/app/core/logs_model.ts

@@ -3,25 +3,26 @@ import { TimeSeries } from 'app/core/core';
 import colors from 'app/core/utils/colors';
 
 export enum LogLevel {
-  crit = 'crit',
-  warn = 'warn',
+  crit = 'critical',
+  critical = 'critical',
+  warn = 'warning',
+  warning = 'warning',
   err = 'error',
   error = 'error',
   info = 'info',
   debug = 'debug',
   trace = 'trace',
-  none = 'none',
+  unkown = 'unkown',
 }
 
 export const LogLevelColor = {
-  [LogLevel.crit]: colors[7],
-  [LogLevel.warn]: colors[1],
-  [LogLevel.err]: colors[4],
+  [LogLevel.critical]: colors[7],
+  [LogLevel.warning]: colors[1],
   [LogLevel.error]: colors[4],
   [LogLevel.info]: colors[0],
-  [LogLevel.debug]: colors[3],
-  [LogLevel.trace]: colors[3],
-  [LogLevel.none]: '#eee',
+  [LogLevel.debug]: colors[5],
+  [LogLevel.trace]: colors[2],
+  [LogLevel.unkown]: '#ddd',
 };
 
 export interface LogSearchMatch {
@@ -119,6 +120,24 @@ export function dedupLogRows(logs: LogsModel, strategy: LogsDedupStrategy): Logs
   };
 }
 
+export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>): LogsModel {
+  if (hiddenLogLevels.size === 0) {
+    return logs;
+  }
+
+  const filteredRows = logs.rows.reduce((result: LogRow[], row: LogRow, index, list) => {
+    if (!hiddenLogLevels.has(row.logLevel)) {
+      result.push(row);
+    }
+    return result;
+  }, []);
+
+  return {
+    ...logs,
+    rows: filteredRows,
+  };
+}
+
 export function makeSeriesForLogs(rows: LogRow[], intervalMs: number): TimeSeries[] {
   // Graph time series by log level
   const seriesByLevel = {};

+ 19 - 15
public/app/features/explore/Graph.tsx

@@ -83,6 +83,7 @@ interface GraphProps {
   size?: { width: number; height: number };
   userOptions?: any;
   onChangeTime?: (range: RawTimeRange) => void;
+  onToggleSeries?: (alias: string, hiddenSeries: Set<string>) => void;
 }
 
 interface GraphState {
@@ -178,26 +179,29 @@ export class Graph extends PureComponent<GraphProps, GraphState> {
 
   onToggleSeries = (series: TimeSeries, exclusive: boolean) => {
     this.setState((state, props) => {
-      const { data } = props;
+      const { data, onToggleSeries } = props;
       const { hiddenSeries } = state;
-      const hidden = hiddenSeries.has(series.alias);
+
       // Deduplicate series as visibility tracks the alias property
       const oneSeriesVisible = hiddenSeries.size === new Set(data.map(d => d.alias)).size - 1;
+
+      let nextHiddenSeries = new Set();
       if (exclusive) {
-        return {
-          hiddenSeries:
-            !hidden && oneSeriesVisible
-              ? new Set()
-              : new Set(data.filter(d => d.alias !== series.alias).map(d => d.alias)),
-        };
-      }
-      // Prune hidden series no longer part of those available from the most recent query
-      const availableSeries = new Set(data.map(d => d.alias));
-      const nextHiddenSeries = intersect(new Set(hiddenSeries), availableSeries);
-      if (nextHiddenSeries.has(series.alias)) {
-        nextHiddenSeries.delete(series.alias);
+        if (hiddenSeries.has(series.alias) || !oneSeriesVisible) {
+          nextHiddenSeries = new Set(data.filter(d => d.alias !== series.alias).map(d => d.alias));
+        }
       } else {
-        nextHiddenSeries.add(series.alias);
+        // Prune hidden series no longer part of those available from the most recent query
+        const availableSeries = new Set(data.map(d => d.alias));
+        nextHiddenSeries = intersect(new Set(hiddenSeries), availableSeries);
+        if (nextHiddenSeries.has(series.alias)) {
+          nextHiddenSeries.delete(series.alias);
+        } else {
+          nextHiddenSeries.add(series.alias);
+        }
+      }
+      if (onToggleSeries) {
+        onToggleSeries(series.alias, nextHiddenSeries);
       }
       return {
         hiddenSeries: nextHiddenSeries,

+ 14 - 5
public/app/features/explore/Logs.tsx

@@ -2,7 +2,7 @@ import React, { Fragment, PureComponent } from 'react';
 import Highlighter from 'react-highlight-words';
 
 import { RawTimeRange } from 'app/types/series';
-import { LogsDedupStrategy, LogsModel, dedupLogRows } from 'app/core/logs_model';
+import { LogsDedupStrategy, LogsModel, dedupLogRows, filterLogLevels, LogLevel } from 'app/core/logs_model';
 import { findHighlightChunksInText } from 'app/core/utils/text';
 import { Switch } from 'app/core/components/Switch/Switch';
 
@@ -33,6 +33,7 @@ interface LogsProps {
 
 interface LogsState {
   dedup: LogsDedupStrategy;
+  hiddenLogLevels: Set<LogLevel>;
   showLabels: boolean;
   showLocalTime: boolean;
   showUtc: boolean;
@@ -41,6 +42,7 @@ interface LogsState {
 export default class Logs extends PureComponent<LogsProps, LogsState> {
   state = {
     dedup: LogsDedupStrategy.none,
+    hiddenLogLevels: new Set(),
     showLabels: true,
     showLocalTime: true,
     showUtc: false,
@@ -76,11 +78,17 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
     });
   };
 
+  onToggleLogLevel = (rawLevel: string, hiddenRawLevels: Set<string>) => {
+    const hiddenLogLevels: Set<LogLevel> = new Set(Array.from(hiddenRawLevels).map(level => LogLevel[level]));
+    this.setState({ hiddenLogLevels });
+  };
+
   render() {
     const { className = '', data, loading = false, position, range } = this.props;
-    const { dedup, showLabels, showLocalTime, showUtc } = this.state;
+    const { dedup, hiddenLogLevels, showLabels, showLocalTime, showUtc } = this.state;
     const hasData = data && data.rows && data.rows.length > 0;
-    const dedupedData = dedupLogRows(data, dedup);
+    const filteredData = filterLogLevels(data, hiddenLogLevels);
+    const dedupedData = dedupLogRows(filteredData, dedup);
     const dedupCount = dedupedData.rows.reduce((sum, row) => sum + row.duplicates, 0);
     const meta = [...data.meta];
     if (dedup !== LogsDedupStrategy.none) {
@@ -113,6 +121,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
             range={range}
             id={`explore-logs-graph-${position}`}
             onChangeTime={this.props.onChangeTime}
+            onToggleSeries={this.onToggleLogLevel}
             userOptions={graphOptions}
           />
         </div>
@@ -163,11 +172,11 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
         <div className="logs-entries" style={logEntriesStyle}>
           {hasData &&
             dedupedData.rows.map(row => (
-              <Fragment key={row.key}>
+              <Fragment key={row.key + row.duplicates}>
                 <div className={row.logLevel ? `logs-row-level logs-row-level-${row.logLevel}` : ''}>
                   {row.duplicates > 0 && (
                     <div className="logs-row-level__duplicates" title={`${row.duplicates} duplicates`}>
-                      {Array.apply(null, { length: row.duplicates }).map(index => (
+                      {Array.apply(null, { length: row.duplicates }).map((bogus, index) => (
                         <div className="logs-row-level__duplicate" key={`${index}`} />
                       ))}
                     </div>

+ 10 - 4
public/app/plugins/datasource/logging/result_transformer.test.ts

@@ -11,11 +11,17 @@ import {
 
 describe('getLoglevel()', () => {
   it('returns no log level on empty line', () => {
-    expect(getLogLevel('')).toBe(LogLevel.none);
+    expect(getLogLevel('')).toBe(LogLevel.unkown);
   });
 
   it('returns no log level on when level is part of a word', () => {
-    expect(getLogLevel('this is a warning')).toBe(LogLevel.none);
+    expect(getLogLevel('this is information')).toBe(LogLevel.unkown);
+  });
+
+  it('returns same log level for long and short version', () => {
+    expect(getLogLevel('[Warn]')).toBe(LogLevel.warning);
+    expect(getLogLevel('[Warning]')).toBe(LogLevel.warning);
+    expect(getLogLevel('[Warn]')).toBe('warning');
   });
 
   it('returns log level on line contains a log level', () => {
@@ -102,7 +108,7 @@ describe('mergeStreamsToLogs()', () => {
         entry: 'WARN boooo',
         labels: '{foo="bar"}',
         key: 'EK1970-01-01T00:00:00Z{foo="bar"}',
-        logLevel: 'warn',
+        logLevel: 'warning',
         uniqueLabels: '',
       },
     ]);
@@ -141,7 +147,7 @@ describe('mergeStreamsToLogs()', () => {
       {
         entry: 'WARN boooo',
         labels: '{foo="bar", baz="1"}',
-        logLevel: 'warn',
+        logLevel: 'warning',
         uniqueLabels: '{baz="1"}',
       },
       {

+ 3 - 3
public/app/plugins/datasource/logging/result_transformer.ts

@@ -14,13 +14,13 @@ import { DEFAULT_LIMIT } from './datasource';
 
 /**
  * Returns the log level of a log line.
- * Parse the line for level words. If no level is found, it returns `LogLevel.none`.
+ * Parse the line for level words. If no level is found, it returns `LogLevel.unknown`.
  *
  * Example: `getLogLevel('WARN 1999-12-31 this is great') // LogLevel.warn`
  */
 export function getLogLevel(line: string): LogLevel {
   if (!line) {
-    return LogLevel.none;
+    return LogLevel.unkown;
   }
   let level: LogLevel;
   Object.keys(LogLevel).forEach(key => {
@@ -32,7 +32,7 @@ export function getLogLevel(line: string): LogLevel {
     }
   });
   if (!level) {
-    level = LogLevel.none;
+    level = LogLevel.unkown;
   }
   return level;
 }

+ 6 - 1
public/sass/pages/_explore.scss

@@ -305,6 +305,7 @@
       opacity: 0.8;
     }
 
+    .logs-row-level-critical,
     .logs-row-level-crit {
       background-color: #705da0;
     }
@@ -314,6 +315,7 @@
       background-color: #e24d42;
     }
 
+    .logs-row-level-warning,
     .logs-row-level-warn {
       background-color: #eab839;
     }
@@ -322,11 +324,14 @@
       background-color: #7eb26d;
     }
 
-    .logs-row-level-trace,
     .logs-row-level-debug {
       background-color: #1f78c1;
     }
 
+    .logs-row-level-trace {
+      background-color: #6ed0e0;
+    }
+
     .logs-row-level__duplicates {
       position: absolute;
       width: 9px;