logger实现使用winston、morgan和winston每日轮换文件



我正在尝试在node js中实现一个记录器,它将每天为日志创建一个自定义格式的新日志文件

为此,我使用了三个包

  1. winston
  2. 摩根
  3. winston每日轮换文件

因此,最终输出应该每天在logs文件夹中创建一个新的日志文件,并将所有http(morgan日志)和键入的日志(winston日志)记录到以下格式的中

日期||Filename ||statusCode||logMessage||uuid(用于跟踪)

例如:2019年1月18日星期五13:48:18 GMT+0530(IST)||[index.js]||200||呼叫新路线||287dccb0-1afa-11e9-88a0-dfb1c65be9d

为此,我编写了三个文件index.js(nodejs的根文件)logger.js(logger实现和配置)和logger.test.js(使用jest的logger的测试用例)

附加包装

  1. cors
  2. uuid
  3. http上下文
  4. 应用根路径
  5. express http上下文
  6. 玩笑

我有的问题

  1. 如果我将logger.error({message:{statusCode:200,logMsg:"服务器将在端口3000启动"})放在应用程序上的索引.js中。在console.log()之前侦听,则uuid为null
  2. 我写的测试用例是错误的,我是新手,我只是想知道如何检查所有的用例
  3. 为什么当我测试suits uuid为null时,我如何为测试用例传递uuid
  4. 我如何检查是否会创建新的文件夹,如果已经有日志文件夹,则会创建新文件之类的测试用例
  5. 我如何添加其他级别,信息,调试,警告基于环境。如何改进此代码以实现记录器功能

// index.js
const app = require('express')();
const cors = require('cors')
const morgan = require('morgan') // HTTP request logger middleware 
const logger = require('./config/logger')(module) //Logger
const uuid = require('uuid')
const httpContext = require('express-http-context')
// Use any third party middleware that does not need access to the context here
// app.use(some3rdParty.middleware);
app.use(httpContext.middleware);
// all code from here on has access to the same context for each request
// Run the context for each request.
// Assigning a unique identifier to each request
app.use((req, res, next) => {
httpContext.set('reqId', uuid.v1());
next()
})
// using morgan with winston(logger)
app.use(morgan('combined', {
stream: {
write: (message) => logger.error(message)
}
}))
app.use(cors());
app.use("/new", (req, res) => {
logger.error({
message: {
statusCode: 400,
logMsg: "hitting new route"
}
})
nextLayer(res)
})
const nextLayer = (res) => {
logger.error({
message: {
statusCode: 400,
logMsg: "hitting in nextLayer function"
}
})
res.send("OK")
}
app.listen(4000, () => {
console.log('Server running on port 4000');
})

// Logger.js
const appRoot = require('app-root-path')
const {
createLogger,
format,
transports
} = require('winston')
const {
combine,
timestamp,
label,
printf
} = format
const path = require('path')
require('winston-daily-rotate-file');
const httpContext = require('express-http-context')
/**
* @method checkMessageProp
* @param {message} can be object if developer defined, else it will be string
*                  if its a network request
* @returns a fixed format how the status code and message should show
*/
const checkMessageProp = (message) => {
switch (typeof message) {
case "object":
const {
statusCode,
logMsg
} = message
return `${statusCode ? statusCode : "Not Defined"} || ${logMsg ? logMsg : "Not Defined"}`;
case "string":
let messageSplit = message.split(`"`)
var message = messageSplit ? `${messageSplit[2].trim().split(" ")[0]} || ${messageSplit[1]}` : null
return message
default:
return message
}
}
/**
* @method customFormat
* @param {log} the log passed by the developer or based on network requests
* @returns a customFormat how it should be logged to the log files
*/
const customFormat = printf(log => {
const now = new Date();
const reqId = httpContext.get('reqId');
return `${log.timestamp ? new Date(log.timestamp) : now} || [${log.label}] || ${checkMessageProp(log.message)} || ${reqId ? reqId : null}`
});
/**
* @method getFileName
* @param {moduleObj} the module realted object passed from the require of logger file 
* @returns the file name where the logger was invoked
*/
const getFileName = moduleObj => {
if (Object.keys(moduleObj).length > 0) {
let parts = moduleObj.filename.split(path.sep)
return parts.pop()
} else {
return "Module not passed while requiring the logger"
}
}
// Custom settings for each transport 
const options = moduleObj => {
return {
dailyRotateFile: {
filename: `${appRoot}/logs/TPS-UI-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
prepend: true,
level: "error",
timestamp: new Date(),
localTime: true
}
}
}
// Instantiate a Winston Logger with the settings
let logger = moduleObj => {
return createLogger({
format: combine(
label({
label: getFileName(moduleObj)
}),
customFormat
),
transports: [
new transports.DailyRotateFile(options(moduleObj).dailyRotateFile)
],
exitOnError: false // do not exit on handled exceptions
})
}
module.exports = logger


// logger.test.js
const logger = require('./logger')
beforeEach(() => {
mockLoggerMessageObject = {
message: {
statusCode: 400,
logMsg: "Calling in test suite"
}
}
mockLoggerMessageString = `::ffff:127.0.0.1 - - [18/Jan/2019:04:50:57 +0000] 
"GET /new HTTP/1.1" 200 2 "http://localhost/" "Mozilla/5.0 
(linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0"`
mockLoggerMessageNumberFormat = 123
mockLoggerMessageArrayFormat = ["data", "test", 123]
})
describe(`Logger test cases`, () => {
test('should invoke the logger function with the mock Logger message object', () => {
expect(logger(module).error(mockLoggerMessageObject)).toBeDefined()
})
test(`should invoke the logger function with empty object`, () => {
expect(logger(module).error({})).toBeDefined()
})
test(`should invoke the logger function without any module object`, () => {
expect(logger({}).error(mockLoggerMessageObject)).toBeDefined()
})
test(`should invoke the logger function without any module and message object`, () => {
expect(logger({}).error({})).toBeDefined()
})
test(`should invoke the logger function with the http request`, () => {
expect(logger(module).error(mockLoggerMessageString)).toBeDefined()
})
test(`should invoke the logger function with the number format`, () => {
expect(logger(module).error(mockLoggerMessageNumberFormat)).toBeDefined()
})
test(`should invoke the logger function with the array format`, () => {
expect(logger(module).error(mockLoggerMessageArrayFormat)).toBeDefined()
})
})

对于winston,我使用的是timestamp(),就像这样,它会自动将timestamp属性添加到对象中

const {transports, createLogger, format} = require('winston');
const logger = createLogger({
format: format.combine(
format.timestamp(),
format.json()
),

还要检查它是否创建了文件,你可以模拟日期,比如2019-01-01,并检查它是否会创建文件2019-01-01.log而不是将日期移至2019-01-02并记录其他内容。Winston将创建新的文件夹和gzip档案,您可以检查文件是否存在,是否可以解压缩并包含信息

试着阅读温斯顿的文档。基本上我会说你可能需要使用

format.timestamp()
format.json()
colorize()

dailyRotate with zippedArchive:真正的

如果morgan不适合您的需求,您可以尝试直接登录

app.use((req, res, next) => { 
logger.silly({ message:'start', req,res}); 
return next().then(r=>logger.silly({ message:'end', req,res}; return r;);
}

最新更新