sc-manager/api/core/loghelper.mjs

188 lines
4.5 KiB
JavaScript

import defaults from '../defaults.mjs'
import { format } from 'util'
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
// Suggested colors (some are unreadable in common cases):
// - Good: cyan, yellow (limited use), bold, green, magenta, red
// - Bad: blue (not visible on cmd.exe), grey (same color as background on
// Solarized Dark theme from <https://github.com/altercation/solarized>, see
// issue #160)
var colors = {
'bold' : [1, 22],
'italic' : [3, 23],
'underline' : [4, 24],
'inverse' : [7, 27],
'white' : [37, 39],
'grey' : [90, 39],
'black' : [30, 39],
'blue' : [34, 39],
'cyan' : [36, 39],
'green' : [32, 39],
'magenta' : [35, 39],
'red' : [31, 39],
'yellow' : [33, 39]
};
// Levels
var TRACE = 10;
var DEBUG = 20;
var INFO = 30;
var WARN = 40;
var ERROR = 50;
var FATAL = 60;
var levelFromName = {
'trace': TRACE,
'debug': DEBUG,
'info': INFO,
'warn': WARN,
'error': ERROR,
'fatal': FATAL
};
var upperPaddedNameFromLevel = {};
Object.keys(levelFromName).forEach(function (name) {
var lvl = levelFromName[name];
upperPaddedNameFromLevel[lvl] = (
name.length === 4 ? ' ' : '') + name.toUpperCase();
});
function stylize(str, color) {
if (!str)
return '';
var codes = colors[color];
if (codes) {
return '\\033[' + codes[0] + 'm' + str +
'\\033[' + codes[1] + 'm';
} else {
return str;
}
}
function indent(s) {
return ' ' + s.split(/\r?\n/).join('\n ');
}
export function formatLog(data) {
let rec = defaults(data)
delete rec.v;
// Time.
var time = '[' + rec.time.toISOString().replace('T', ' ').replace('Z', '') + ']'
time = stylize(time, 'none')
delete rec.time;
var nameStr = rec.name;
delete rec.name;
if (rec.component) {
nameStr += '/' + rec.component;
}
delete rec.component;
nameStr += '/' + rec.pid;
delete rec.pid;
var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
var colorFromLevel = {
10: 'white', // TRACE
20: 'yellow', // DEBUG
30: 'cyan', // INFO
40: 'magenta', // WARN
50: 'red', // ERROR
60: 'inverse', // FATAL
};
level = stylize(level, colorFromLevel[rec.level]);
delete rec.level;
var src = '';
if (rec.src && rec.src.file) {
var s = rec.src;
if (s.func) {
src = format(' (%s:%d in %s)', s.file, s.line, s.func);
} else {
src = format(' (%s:%d)', s.file, s.line);
}
src = stylize(src, 'green');
}
delete rec.src;
var hostname = rec.hostname;
delete rec.hostname;
var extras = [];
var details = [];
if (rec.req_id) {
extras.push('req_id=' + rec.req_id);
}
delete rec.req_id;
var onelineMsg;
if (rec.msg.indexOf('\n') !== -1) {
onelineMsg = '';
details.push(indent(stylize(rec.msg, 'cyan')));
} else {
onelineMsg = ' ' + stylize(rec.msg, 'cyan');
}
delete rec.msg;
if (rec.err && rec.err.stack) {
var err = rec.err
if (typeof (err.stack) !== 'string') {
details.push(indent(err.stack.toString()));
} else {
details.push(indent(err.stack));
}
delete err.message;
delete err.name;
delete err.stack;
// E.g. for extra 'foo' field on 'err', add 'err.foo' at
// top-level. This *does* have the potential to stomp on a
// literal 'err.foo' key.
Object.keys(err).forEach(function (k) {
rec['err.' + k] = err[k];
})
delete rec.err;
}
var leftover = Object.keys(rec);
for (var i = 0; i < leftover.length; i++) {
var key = leftover[i];
var value = rec[key];
var stringified = false;
if (typeof (value) !== 'string') {
value = JSON.stringify(value, null, 2);
if (typeof (value) !== 'string') {
value = 'null'
}
stringified = true;
}
if (value.indexOf('\n') !== -1 || value.length > 50) {
details.push(indent(key + ': ' + value));
} else if (!stringified && (value.indexOf(' ') != -1 ||
value.length === 0))
{
extras.push(key + '=' + JSON.stringify(value));
} else {
extras.push(key + '=' + value);
}
}
extras = stylize(
(extras.length ? ' (' + extras.join(', ') + ')' : ''), 'none');
details = stylize(
(details.length ? details.join('\n --\n') + '\n' : ''), 'none');
return format('%s %s: %s on %s%s:%s%s\n%s',
time,
level,
nameStr,
hostname || '<no-hostname>',
src,
onelineMsg,
extras,
details)
}