mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-01 13:54:35 +03:00
0e13ef8767
* 🎨 rotation config - every parameter is configureable - increase default number of files to 100 * 🎨 ghost.log location - example: content/logs/http___my_ghost_blog_com_ghost.log - user can change the path to something custom by setting logging.path * 🛠 add response-time as dependency * 🎨 readable PrettyStream - tidy up - generic handling (was important to support more use cases, for example: logging.info({ anyKey: anyValue })) - common log format - less code 🕵🏻 * 🎨 GhostLogger cleanup - remove setLoggers -> this function had too much of redundant code - instead: add smart this.log function - remove logging.request (---> GhostLogger just forwards the values, it doesn't matter if that is a request or not a request) - make .warn .debug .info .error small and smart * 🎨 app.js: add response time as middleware and remove logging.request * 🎨 setStdoutStream and setFileStream - redesign GhostLogger to add CustomLoggers very easily ----> Example CustomLogger function CustomLogger(options) { // Base iterates over defined transports // EXAMPLE: ['stdout', 'elasticsearch'] Base.call(this, options); } util.inherits(...); // OVERRIDE default stdout stream and your own!!! CustomLogger.prototype.setStdoutStream = function() {} // add a new stream // get's called automatically when transport elasticsearch is defined CustomLogger.prototype.setElasticsearchStream = function() {} * 🎨 log into multiple file by default - content/logs/domain.error.log --> contains only the errors - content/logs/domain.log --> contains everything - rotation for both files * 🔥 remove logging.debug and use npm debug only * ✨ shortcuts for mode and level * 🎨 jshint/jscs * 🎨 stdout as much as possible for an error * 🎨 fix tests * 🎨 remove req.ip from log output, remove response-time dependency * 🎨 create middleware for logging - added TODO to move logging middleware to ignition
164 lines
4.7 KiB
JavaScript
164 lines
4.7 KiB
JavaScript
// jscs:disable
|
|
var _ = require('lodash'),
|
|
moment = require('moment'),
|
|
Stream = require('stream').Stream,
|
|
util = require('util'),
|
|
format = util.format,
|
|
prettyjson = require('prettyjson'),
|
|
__private__ = {
|
|
levelFromName: {
|
|
10: 'trace',
|
|
20: 'debug',
|
|
30: 'info',
|
|
40: 'warn',
|
|
50: 'error',
|
|
60: 'fatal'
|
|
},
|
|
colorForLevel: {
|
|
10: 'grey',
|
|
20: 'grey',
|
|
30: 'cyan',
|
|
40: 'magenta',
|
|
50: 'red',
|
|
60: 'inverse'
|
|
},
|
|
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]
|
|
}
|
|
};
|
|
|
|
|
|
function PrettyStream(options) {
|
|
this.mode = options.mode || 'short';
|
|
}
|
|
util.inherits(PrettyStream, Stream);
|
|
|
|
|
|
function colorize(color, value) {
|
|
return '\x1B[' + __private__.colors[color][0] + 'm' + value + '\x1B[' + __private__.colors[color][1] + 'm';
|
|
}
|
|
|
|
|
|
PrettyStream.prototype.write = function write(data) {
|
|
if (typeof data === 'string') {
|
|
try {
|
|
data = JSON.parse(data);
|
|
} catch (err) {
|
|
this.emit('data', err);
|
|
}
|
|
}
|
|
|
|
var output = '',
|
|
time = moment(data.time).format('YYYY-MM-DD HH:mm:ss'),
|
|
logLevel = __private__.levelFromName[data.level].toUpperCase(),
|
|
codes = __private__.colors[__private__.colorForLevel[data.level]],
|
|
bodyPretty = '';
|
|
|
|
logLevel = '\x1B[' + codes[0] + 'm' + logLevel + '\x1B[' + codes[1] + 'm';
|
|
|
|
// CASE: bunyan passes each plain string/integer as `msg` attribute (logging.info('Hey!'))
|
|
if (data.msg) {
|
|
bodyPretty += data.msg;
|
|
|
|
output += format('[%s] %s %s\n',
|
|
time,
|
|
logLevel,
|
|
bodyPretty
|
|
);
|
|
}
|
|
// CASE: log objects in pretty JSON format
|
|
else {
|
|
// common log format:
|
|
// 127.0.0.1 user-identifier user-id [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
|
|
|
|
// if all values are available we log in common format
|
|
// can be extended to define from outside, but not important
|
|
try {
|
|
output += format('%s [%s] "%s %s" %s %s\n',
|
|
logLevel,
|
|
time,
|
|
data.req.method.toUpperCase(),
|
|
data.req.originalUrl,
|
|
data.res.statusCode,
|
|
data.res.responseTime
|
|
);
|
|
} catch (err) {
|
|
output += format('[%s] %s\n',
|
|
time,
|
|
logLevel
|
|
);
|
|
}
|
|
|
|
_.each(_.omit(data, ['time', 'level', 'name', 'hostname', 'pid', 'v', 'msg']), function (value, key) {
|
|
// we always output errors for now
|
|
if (_.isObject(value) && value.message && value.stack) {
|
|
var error = '\n';
|
|
error += colorize('red', value.message) + '\n';
|
|
|
|
if (value.level) {
|
|
error += colorize('white', 'level:') + colorize('white', value.level) + '\n\n';
|
|
}
|
|
|
|
if (value.context) {
|
|
error += colorize('white', value.context) + '\n';
|
|
}
|
|
|
|
if (value.help) {
|
|
error += colorize('yellow', value.help) + '\n';
|
|
}
|
|
|
|
if (value.errorDetails) {
|
|
error += prettyjson.render(value.errorDetails, {}) + '\n';
|
|
}
|
|
|
|
if (value.stack && !value.hideStack) {
|
|
error += colorize('white', value.stack) + '\n';
|
|
}
|
|
|
|
output += format('%s\n', colorize('red', error));
|
|
}
|
|
else if (_.isObject(value)) {
|
|
bodyPretty += '\n' + colorize('yellow', key.toUpperCase()) + '\n';
|
|
|
|
var sanitized = {};
|
|
|
|
_.each(value, function (innerValue, innerKey) {
|
|
if (!_.isEmpty(innerValue)) {
|
|
sanitized[innerKey] = innerValue;
|
|
}
|
|
});
|
|
|
|
bodyPretty += prettyjson.render(sanitized, {}) + '\n';
|
|
} else {
|
|
bodyPretty += prettyjson.render(value, {}) + '\n';
|
|
}
|
|
});
|
|
|
|
if (this.mode !== 'short') {
|
|
output += format('%s\n', colorize('grey', bodyPretty));
|
|
}
|
|
}
|
|
|
|
try {
|
|
this.emit('data', output);
|
|
} catch (err) {
|
|
this.emit('data', err);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
module.exports = PrettyStream;
|