mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
- added dedup switches to logs view - strategy 'exact' matches rows that are exact (except for dates) - strategy 'numbers' strips all numbers - strategy 'signature' strips all letters and numbers to that only whitespace and punctuation remains - added duplication indicator next to log level
151 lines
3.6 KiB
TypeScript
151 lines
3.6 KiB
TypeScript
import _ from 'lodash';
|
|
import { TimeSeries } from 'app/core/core';
|
|
import colors from 'app/core/utils/colors';
|
|
|
|
export enum LogLevel {
|
|
crit = 'crit',
|
|
warn = 'warn',
|
|
err = 'error',
|
|
error = 'error',
|
|
info = 'info',
|
|
debug = 'debug',
|
|
trace = 'trace',
|
|
none = 'none',
|
|
}
|
|
|
|
export const LogLevelColor = {
|
|
[LogLevel.crit]: colors[7],
|
|
[LogLevel.warn]: colors[1],
|
|
[LogLevel.err]: colors[4],
|
|
[LogLevel.error]: colors[4],
|
|
[LogLevel.info]: colors[0],
|
|
[LogLevel.debug]: colors[3],
|
|
[LogLevel.trace]: colors[3],
|
|
[LogLevel.none]: '#eee',
|
|
};
|
|
|
|
export interface LogSearchMatch {
|
|
start: number;
|
|
length: number;
|
|
text: string;
|
|
}
|
|
|
|
export interface LogRow {
|
|
duplicates?: number;
|
|
entry: string;
|
|
key: string; // timestamp + labels
|
|
labels: string;
|
|
logLevel: LogLevel;
|
|
searchWords?: string[];
|
|
timestamp: string; // ISO with nanosec precision
|
|
timeFromNow: string;
|
|
timeEpochMs: number;
|
|
timeLocal: string;
|
|
uniqueLabels?: string;
|
|
}
|
|
|
|
export interface LogsMetaItem {
|
|
label: string;
|
|
value: string;
|
|
}
|
|
|
|
export interface LogsModel {
|
|
meta?: LogsMetaItem[];
|
|
rows: LogRow[];
|
|
series?: TimeSeries[];
|
|
}
|
|
|
|
export interface LogsStream {
|
|
labels: string;
|
|
entries: LogsStreamEntry[];
|
|
search?: string;
|
|
parsedLabels?: LogsStreamLabels;
|
|
uniqueLabels?: string;
|
|
}
|
|
|
|
export interface LogsStreamEntry {
|
|
line: string;
|
|
timestamp: string;
|
|
}
|
|
|
|
export interface LogsStreamLabels {
|
|
[key: string]: string;
|
|
}
|
|
|
|
export enum LogsDedupStrategy {
|
|
none = 'none',
|
|
exact = 'exact',
|
|
numbers = 'numbers',
|
|
signature = 'signature',
|
|
}
|
|
|
|
const isoDateRegexp = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-6]\d[,\.]\d+([+-][0-2]\d:[0-5]\d|Z)/g;
|
|
function isDuplicateRow(row: LogRow, other: LogRow, strategy: LogsDedupStrategy): boolean {
|
|
switch (strategy) {
|
|
case LogsDedupStrategy.exact:
|
|
// Exact still strips dates
|
|
return row.entry.replace(isoDateRegexp, '') === other.entry.replace(isoDateRegexp, '');
|
|
|
|
case LogsDedupStrategy.numbers:
|
|
return row.entry.replace(/\d/g, '') === other.entry.replace(/\d/g, '');
|
|
|
|
case LogsDedupStrategy.signature:
|
|
return row.entry.replace(/\w/g, '') === other.entry.replace(/\w/g, '');
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function dedupLogRows(logs: LogsModel, strategy: LogsDedupStrategy): LogsModel {
|
|
if (strategy === LogsDedupStrategy.none) {
|
|
return logs;
|
|
}
|
|
|
|
const dedupedRows = logs.rows.reduce((result: LogRow[], row: LogRow, index, list) => {
|
|
const previous = result[result.length - 1];
|
|
if (index > 0 && isDuplicateRow(row, previous, strategy)) {
|
|
previous.duplicates++;
|
|
} else {
|
|
row.duplicates = 0;
|
|
result.push(row);
|
|
}
|
|
return result;
|
|
}, []);
|
|
|
|
return {
|
|
...logs,
|
|
rows: dedupedRows,
|
|
};
|
|
}
|
|
|
|
export function makeSeriesForLogs(rows: LogRow[], intervalMs: number): TimeSeries[] {
|
|
// Graph time series by log level
|
|
const seriesByLevel = {};
|
|
rows.forEach(row => {
|
|
if (!seriesByLevel[row.logLevel]) {
|
|
seriesByLevel[row.logLevel] = { lastTs: null, datapoints: [], alias: row.logLevel };
|
|
}
|
|
const levelSeries = seriesByLevel[row.logLevel];
|
|
|
|
// Bucket to nearest minute
|
|
const time = Math.round(row.timeEpochMs / intervalMs / 10) * intervalMs * 10;
|
|
// Entry for time
|
|
if (time === levelSeries.lastTs) {
|
|
levelSeries.datapoints[levelSeries.datapoints.length - 1][0]++;
|
|
} else {
|
|
levelSeries.datapoints.push([1, time]);
|
|
levelSeries.lastTs = time;
|
|
}
|
|
});
|
|
|
|
return Object.keys(seriesByLevel).reduce((acc, level) => {
|
|
if (seriesByLevel[level]) {
|
|
const gs = new TimeSeries(seriesByLevel[level]);
|
|
gs.setColor(LogLevelColor[level]);
|
|
acc.push(gs);
|
|
}
|
|
return acc;
|
|
}, []);
|
|
}
|