mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
- 🛠 add bunyan and prettyjson, remove morgan - ✨ add logging module - GhostLogger class that handles setup of bunyan - PrettyStream for stdout - ✨ config for logging - @TODO: testing level fatal? - ✨ log each request via GhostLogger (express middleware) - @TODO: add errors to output - 🔥 remove errors.updateActiveTheme - we can read the value from config - 🔥 remove 15 helper functions in core/server/errors/index.js - all these functions get replaced by modules: 1. logging 2. error middleware handling for html/json 3. error creation (which will be part of PR #7477) - ✨ add express error handler for html/json - one true error handler for express responses - contains still some TODO's, but they are not high priority for first implementation/integration - this middleware only takes responsibility of either rendering html responses or return json error responses - 🎨 use new express error handler in middleware/index - 404 and 500 handling - 🎨 return error instead of error message in permissions/index.js - the rule for error handling should be: if you call a unit, this unit should return a custom Ghost error - 🎨 wrap serve static module - rule: if you call a module/unit, you should always wrap this error - it's always the same rule - so the caller never has to worry about what comes back - it's always a clear error instance - in this case: we return our notfounderror if serve static does not find the resource - this avoid having checks everywhere - 🎨 replace usages of errors/index.js functions and adapt tests - use logging.error, logging.warn - make tests green - remove some usages of logging and throwing api errors -> because when a request is involved, logging happens automatically - 🐛 return errorDetails to Ghost-Admin - errorDetails is used for Theme error handling - 🎨 use 500er error for theme is missing error in theme-handler - 🎨 extend file rotation to 1w
210 lines
5.3 KiB
JavaScript
210 lines
5.3 KiB
JavaScript
var bunyan = require('bunyan'),
|
|
_ = require('lodash'),
|
|
GhostPrettyStream = require('./PrettyStream');
|
|
|
|
function GhostLogger(options) {
|
|
this.env = options.env;
|
|
this.transports = options.transports || ['stdout'];
|
|
this.level = options.level || 'info';
|
|
this.mode = options.mode || 'short';
|
|
this.path = options.path || 'ghost.log';
|
|
this.rotation = options.rotation || false;
|
|
this.loggers = {};
|
|
|
|
this.setSerializers();
|
|
this.setLoggers();
|
|
this.setStreams();
|
|
}
|
|
|
|
// @TODO: add correlation identifier
|
|
// @TODO: res.on('finish') has no access to the response body
|
|
GhostLogger.prototype.setSerializers = function setSerializers() {
|
|
var self = this;
|
|
|
|
this.serializers = {
|
|
req: function (req) {
|
|
return {
|
|
url: req.url,
|
|
method: req.method,
|
|
originalUrl: req.originalUrl,
|
|
params: req.params,
|
|
headers: self.removeSensitiveData(req.headers),
|
|
body: self.removeSensitiveData(req.body),
|
|
query: self.removeSensitiveData(req.query)
|
|
};
|
|
},
|
|
res: function (res) {
|
|
return {
|
|
_headers: self.removeSensitiveData(res._headers),
|
|
statusCode: res.statusCode
|
|
};
|
|
},
|
|
err: function (err) {
|
|
return {
|
|
name: err.errorType,
|
|
statusCode: err.statusCode,
|
|
level: err.level,
|
|
message: err.message,
|
|
context: err.context,
|
|
help: err.help,
|
|
stack: err.stack,
|
|
hideStack: err.hideStack
|
|
};
|
|
}
|
|
};
|
|
};
|
|
|
|
GhostLogger.prototype.setLoggers = function setLoggers() {
|
|
var self = this;
|
|
|
|
this.log = {
|
|
info: function (options) {
|
|
var req = options.req,
|
|
res = options.res;
|
|
|
|
_.each(self.loggers, function (logger) {
|
|
logger.log.info({
|
|
req: req,
|
|
res: res
|
|
});
|
|
});
|
|
},
|
|
debug: function (options) {
|
|
var req = options.req,
|
|
res = options.res;
|
|
|
|
_.each(self.loggers, function (logger) {
|
|
logger.log.debug({
|
|
req: req,
|
|
res: res
|
|
});
|
|
});
|
|
},
|
|
error: function (options) {
|
|
var req = options.req,
|
|
res = options.res,
|
|
err = options.err;
|
|
|
|
_.each(self.loggers, function (logger) {
|
|
logger.log.error({
|
|
req: req,
|
|
res: res,
|
|
err: err
|
|
});
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
GhostLogger.prototype.setStreams = function setStreams() {
|
|
var self = this,
|
|
streams = [],
|
|
prettyStdOut;
|
|
|
|
_.each(self.transports, function (transport) {
|
|
if (transport === 'file') {
|
|
streams.push({
|
|
name: 'file',
|
|
stream: {
|
|
path: self.path,
|
|
level: self.level
|
|
}
|
|
});
|
|
}
|
|
|
|
if (transport === 'stdout') {
|
|
prettyStdOut = new GhostPrettyStream();
|
|
prettyStdOut.pipe(process.stdout);
|
|
|
|
streams.push({
|
|
name: 'stdout',
|
|
stream: {
|
|
type: 'raw',
|
|
stream: prettyStdOut,
|
|
level: self.level
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
if (self.rotation) {
|
|
streams.push({
|
|
name: 'rotation',
|
|
stream: {
|
|
type: 'rotating-file',
|
|
path: self.path,
|
|
period: '1w',
|
|
count: 3,
|
|
level: self.level
|
|
}
|
|
});
|
|
}
|
|
|
|
// the env defines which streams are available
|
|
_.each(streams, function (stream) {
|
|
self.loggers[stream.name] = {
|
|
name: stream.name,
|
|
log: bunyan.createLogger({
|
|
name: 'Log',
|
|
streams: [stream.stream],
|
|
serializers: self.serializers
|
|
})
|
|
};
|
|
});
|
|
};
|
|
|
|
GhostLogger.prototype.removeSensitiveData = function removeSensitiveData(obj) {
|
|
var newObj = {};
|
|
|
|
_.each(obj, function (value, key) {
|
|
if (!key.match(/pin|password|authorization|cookie/gi)) {
|
|
newObj[key] = value;
|
|
}
|
|
});
|
|
|
|
return newObj;
|
|
};
|
|
|
|
GhostLogger.prototype.info = function info() {
|
|
var print = '';
|
|
|
|
_.each(arguments, function (value) {
|
|
print += value;
|
|
print += ' ';
|
|
});
|
|
|
|
this.loggers.stdout.log.info(print);
|
|
};
|
|
|
|
GhostLogger.prototype.warn = function warn() {
|
|
var print = '';
|
|
|
|
_.each(arguments, function (value) {
|
|
print += value;
|
|
print += ' ';
|
|
});
|
|
|
|
this.loggers.stdout.log.warn(print);
|
|
};
|
|
|
|
GhostLogger.prototype.debug = function debug(options) {
|
|
this.loggers.stdout.log.debug(options);
|
|
};
|
|
|
|
GhostLogger.prototype.error = function error(err) {
|
|
this.log.error({err: err});
|
|
};
|
|
|
|
GhostLogger.prototype.request = function request(options) {
|
|
var req = options.req,
|
|
res = options.res,
|
|
err = options.err;
|
|
|
|
if (err) {
|
|
this.log.error({req: req, res: res, err: err});
|
|
} else {
|
|
this.log.info({req: req, res: res});
|
|
}
|
|
};
|
|
|
|
module.exports = GhostLogger;
|