mirror of
https://github.com/portainer/portainer.git
synced 2025-07-30 18:59:41 +02:00
feat(app/logs): format Zerolog in logs viewer [EE-4226] (#7685)
* feat(app/logs): format Zerolog in logs viewer * fix(app/logs): trim caller to only last 2 segments
This commit is contained in:
parent
6063f368ea
commit
1b0db4971f
8 changed files with 120 additions and 9 deletions
|
@ -1,5 +1,7 @@
|
|||
import tokenize from '@nxmix/tokenize-ansi';
|
||||
import x256 from 'x256';
|
||||
import { takeRight, without } from 'lodash';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
const FOREGROUND_COLORS_BY_ANSI = {
|
||||
black: x256.colors[0],
|
||||
|
@ -39,6 +41,8 @@ const BACKGROUND_COLORS_BY_ANSI = {
|
|||
bgBrightWhite: x256.colors[15],
|
||||
};
|
||||
|
||||
const TIMESTAMP_LENGTH = 31; // 30 for timestamp + 1 for trailing space
|
||||
|
||||
angular.module('portainer.docker').factory('LogHelper', [
|
||||
function LogHelperFactory() {
|
||||
'use strict';
|
||||
|
@ -76,8 +80,9 @@ angular.module('portainer.docker').factory('LogHelper', [
|
|||
}
|
||||
|
||||
// Return an array with each log including a line and styled spans for each entry.
|
||||
// If the skipHeaders param is specified, it will strip the 8 first characters of each line.
|
||||
helper.formatLogs = function (logs, skipHeaders) {
|
||||
// If the stripHeaders param is specified, it will strip the 8 first characters of each line.
|
||||
// withTimestamps param is needed to find the start of JSON for Zerolog logs parsing
|
||||
helper.formatLogs = function (logs, { stripHeaders: skipHeaders, withTimestamps }) {
|
||||
if (skipHeaders) {
|
||||
logs = stripHeaders(logs);
|
||||
}
|
||||
|
@ -120,9 +125,12 @@ angular.module('portainer.docker').factory('LogHelper', [
|
|||
}
|
||||
|
||||
const text = stripEscapeCodes(tokenLines[i]);
|
||||
|
||||
line += text;
|
||||
spans.push({ foregroundColor, backgroundColor, text });
|
||||
if ((!withTimestamps && text.startsWith('{')) || (withTimestamps && text.substring(TIMESTAMP_LENGTH).startsWith('{'))) {
|
||||
line += JSONToFormattedLine(text, spans, withTimestamps);
|
||||
} else {
|
||||
spans.push({ foregroundColor, backgroundColor, text });
|
||||
line += text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,3 +145,79 @@ angular.module('portainer.docker').factory('LogHelper', [
|
|||
return helper;
|
||||
},
|
||||
]);
|
||||
|
||||
const JSONColors = {
|
||||
Grey: 'var(--text-log-viewer-color-json-grey)',
|
||||
Magenta: 'var(--text-log-viewer-color-json-magenta)',
|
||||
Yellow: 'var(--text-log-viewer-color-json-yellow)',
|
||||
Green: 'var(--text-log-viewer-color-json-green)',
|
||||
Red: 'var(--text-log-viewer-color-json-red)',
|
||||
Blue: 'var(--text-log-viewer-color-json-blue)',
|
||||
};
|
||||
|
||||
const spaceSpan = { text: ' ' };
|
||||
|
||||
function logLevelToSpan(level) {
|
||||
switch (level) {
|
||||
case 'debug':
|
||||
return { foregroundColor: JSONColors.Grey, text: 'DBG', fontWeight: 'bold' };
|
||||
case 'info':
|
||||
return { foregroundColor: JSONColors.Green, text: 'INF', fontWeight: 'bold' };
|
||||
case 'warn':
|
||||
return { foregroundColor: JSONColors.Yellow, text: 'WRN', fontWeight: 'bold' };
|
||||
case 'error':
|
||||
return { foregroundColor: JSONColors.Red, text: 'ERR', fontWeight: 'bold' };
|
||||
default:
|
||||
return { text: level };
|
||||
}
|
||||
}
|
||||
|
||||
function JSONToFormattedLine(rawText, spans, withTimestamps) {
|
||||
const text = withTimestamps ? rawText.substring(TIMESTAMP_LENGTH) : rawText;
|
||||
const json = JSON.parse(text);
|
||||
const { level, caller, message, time } = json;
|
||||
let line = '';
|
||||
|
||||
if (withTimestamps) {
|
||||
const timestamp = rawText.substring(0, TIMESTAMP_LENGTH);
|
||||
spans.push({ text: timestamp });
|
||||
line += `${timestamp}`;
|
||||
}
|
||||
if (time) {
|
||||
const date = format(new Date(time * 1000), 'Y/MM/dd hh:mmaa');
|
||||
spans.push({ foregroundColor: JSONColors.Grey, text: date }, spaceSpan);
|
||||
line += `${date} `;
|
||||
}
|
||||
if (level) {
|
||||
const levelSpan = logLevelToSpan(level);
|
||||
spans.push(levelSpan, spaceSpan);
|
||||
line += `${levelSpan.text} `;
|
||||
}
|
||||
if (caller) {
|
||||
const trimmedCaller = takeRight(caller.split('/'), 2).join('/');
|
||||
spans.push({ foregroundColor: JSONColors.Magenta, text: trimmedCaller, fontWeight: 'bold' }, spaceSpan);
|
||||
spans.push({ foregroundColor: JSONColors.Blue, text: '>' }, spaceSpan);
|
||||
line += `${trimmedCaller} > `;
|
||||
}
|
||||
|
||||
const keys = without(Object.keys(json), 'time', 'level', 'caller', 'message');
|
||||
if (message) {
|
||||
spans.push({ foregroundColor: JSONColors.Magenta, text: `${message}` }, spaceSpan);
|
||||
line += `${message} `;
|
||||
|
||||
if (keys.length) {
|
||||
spans.push({ foregroundColor: JSONColors.Magenta, text: `|` }, spaceSpan);
|
||||
line += '| ';
|
||||
}
|
||||
}
|
||||
|
||||
keys.forEach((key) => {
|
||||
const value = json[key];
|
||||
spans.push({ foregroundColor: JSONColors.Blue, text: `${key}=` });
|
||||
spans.push({ foregroundColor: key === 'error' ? JSONColors.Red : JSONColors.Magenta, text: value });
|
||||
spans.push(spaceSpan);
|
||||
line += `${key}=${value} `;
|
||||
});
|
||||
|
||||
return line;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue