The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/core/logs_model.ts

177 lines
4.2 KiB

import _ from 'lodash';
import { TimeSeries } from 'app/core/core';
import colors from 'app/core/utils/colors';
export enum LogLevel {
crit = 'critical',
critical = 'critical',
warn = 'warning',
warning = 'warning',
err = 'error',
error = 'error',
info = 'info',
debug = 'debug',
trace = 'trace',
unkown = 'unkown',
}
export const LogLevelColor = {
[LogLevel.critical]: colors[7],
[LogLevel.warning]: colors[1],
[LogLevel.error]: colors[4],
[LogLevel.info]: colors[0],
[LogLevel.debug]: colors[5],
[LogLevel.trace]: colors[2],
[LogLevel.unkown]: '#ddd',
};
export interface LogSearchMatch {
start: number;
length: number;
text: string;
}
export interface LogRow {
duplicates?: number;
entry: string;
key: string; // timestamp + labels
labels: LogsStreamLabels;
logLevel: LogLevel;
searchWords?: string[];
timestamp: string; // ISO with nanosec precision
timeFromNow: string;
timeEpochMs: number;
timeLocal: string;
uniqueLabels?: LogsStreamLabels;
}
export enum LogsMetaKind {
Number,
String,
LabelsMap,
}
export interface LogsMetaItem {
label: string;
value: string | number | LogsStreamLabels;
kind: LogsMetaKind;
}
export interface LogsModel {
meta?: LogsMetaItem[];
rows: LogRow[];
series?: TimeSeries[];
}
export interface LogsStream {
labels: string;
entries: LogsStreamEntry[];
search?: string;
parsedLabels?: LogsStreamLabels;
uniqueLabels?: LogsStreamLabels;
}
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 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 = {};
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;
}, []);
}