Created
January 11, 2017 10:00
-
-
Save ronalddddd/425d9972c318383c0076360db94bef8d to your computer and use it in GitHub Desktop.
Reusable logger (with cloudwatch transport and request logging middleware)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generic logger with CloudWatch transport | |
const winston = require('winston'); | |
const WinstonCloudWatch = require('winston-cloudwatch'); | |
const AWS_REGION = process.env.npm_config_logger_cloudwatch_log_region; | |
const AWS_KEY_ID = process.env.npm_config_logger_cloudwatch_key_id; | |
const AWS_SECRET = process.env.npm_config_logger_cloudwatch_key_secret; | |
const LOG_GROUP_NAME = process.env.npm_config_logger_cloudwatch_log_group || 'unknown-group'; | |
const LOG_STREAM_NAME = process.env.npm_config_logger_cloudwatch_log_stream || 'unknown-stream'; | |
const LOG_LEVEL = process.env.npm_config_logger_log_level || 'info'; | |
const SENSITIVE_KEYS = (process.env.npm_config_log_sensitive_keys) ? | |
JSON.parse(process.env.npm_config_log_sensitive_keys) : ['password']; | |
const loggers = {}; | |
// Setup the default logger | |
loggers.default = winston; | |
loggers.default.level = LOG_LEVEL; | |
if (AWS_REGION && AWS_KEY_ID && AWS_SECRET) { | |
winston.add(WinstonCloudWatch, { | |
level: LOG_LEVEL, | |
logGroupName: LOG_GROUP_NAME, | |
logStreamName: LOG_STREAM_NAME, | |
awsAccessKeyId: AWS_KEY_ID, | |
awsSecretKey: AWS_SECRET, | |
awsRegion: AWS_REGION, | |
jsonMessage: true, | |
}); | |
} | |
class Logger { | |
// Removes functions, circular-references and mask sensitive properties | |
static makeSerializable(obj, existingObjects = [], sensitiveKeys = SENSITIVE_KEYS) { | |
if (typeof obj !== 'object' || obj === null) return obj; | |
const outputObj = {}; | |
existingObjects.push(obj); | |
Object.keys(obj).forEach((key) => { | |
if (typeof obj[key] === 'object') { | |
if (obj[key] instanceof Array) { | |
outputObj[key] = obj[key].map(arrayElement => Logger.makeSerializable(arrayElement, existingObjects)); | |
} else { | |
outputObj[key] = (existingObjects.indexOf(obj[key]) > -1) ? | |
'***CIRCULAR-REF***' : Logger.makeSerializable(obj[key], existingObjects); | |
} | |
} else if (typeof obj[key] === 'function') { | |
outputObj[key] = '***FUNCTION***' | |
} else { | |
outputObj[key] = (sensitiveKeys.indexOf(key) > -1) ? '***MASKED***' : obj[key]; | |
} | |
}); | |
return outputObj; | |
} | |
static get logger() { | |
return Logger.getLogger(); | |
} | |
static getLogger(loggerName) { | |
if (!loggerName || !(AWS_REGION && AWS_KEY_ID && AWS_SECRET)) { | |
return loggers.default; | |
} else if (!loggers[loggerName]) { // TODO: do something with individual stream instances | |
const transportName = `CloudWatch_${loggerName}`; | |
winston.transports[transportName] = WinstonCloudWatch; | |
loggers[loggerName] = winston.loggers.add(loggerName, { | |
[transportName]: { | |
level: LOG_LEVEL, | |
logGroupName: LOG_GROUP_NAME, | |
logStreamName: LOG_STREAM_NAME, | |
awsAccessKeyId: AWS_KEY_ID, | |
awsSecretKey: AWS_SECRET, | |
awsRegion: AWS_REGION, | |
jsonMessage: true, | |
} | |
}); | |
} | |
return loggers[loggerName]; | |
} | |
static get middleware() { | |
const logger = Logger.logger; | |
// request logging middleware | |
function requestLogger(req, res, next) { | |
// Wrap the response.end method | |
const _end = res.end; | |
res.end = function() { | |
const logData = Logger.makeSerializable({ | |
statusCode: res.statusCode, | |
statusMessage: res.statusMessage, | |
method: req.method, | |
originalUrl: req.originalUrl, | |
path: req.path, | |
params: req.params, | |
query: req.query, | |
headers: req.headers, | |
body: req.body, | |
}); | |
if (res.statusCode >= 400) { | |
try { | |
// TODO: this is too hacky | |
logData.responseBody = JSON.parse(new Buffer(arguments[0]).toString()); | |
} catch (e) { | |
// not JSON response or something wrong with buffer | |
} | |
} | |
logger.info(`HTTP ${req.method} request`, logData); | |
_end.apply(res, arguments); | |
}; | |
next(); | |
} | |
return requestLogger; | |
} | |
} | |
module.exports = Logger; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment