问题
Is it possible to get the line number and file for each log output ?
For example:
var winston = require('winston');
winston.log('info', 'some message!'); // this is at line 4 of myfile.js
should specify in log file that 'some message' came from myFile.js line 4.
回答1:
You can pass the file name as label
and you can get the file name from callingModule.
Create logger.js
file and code like
var winston = require('winston');
var getLabel = function (callingModule) {
var parts = callingModule.filename.split('/');
return parts[parts.length - 2] + '/' + parts.pop();
};
module.exports = function (callingModule) {
return new winston.Logger({
transports: [
new winston.transports.Console({
label: getLabel(callingModule),
json: false,
timestamp: true,
depth:true,
colorize:true
})
]
});
};
Now Here your test file
var logger = require('./logger')(module);
function test() {
logger.info('test logger');
}
test();
and if you run test file than the output looks like
2017-07-08T07:15:20.671Z - info: [utils/test.js] test logger
回答2:
winston didn't have the plan to do that due to performance concerns. Please check here for detailed information.
I tried https://github.com/baryon/tracer but it isn't good, e.g. line number is incorrect from time to time.
回答3:
I found this code somewhere, yeah but It is working. Use it in a new winston.js
and then requires that in any file.
var winston = require('winston')
var path = require('path')
var PROJECT_ROOT = path.join(__dirname, '..')
var appRoot = require('app-root-path');
const options = {
file: {
level: 'info',
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxFiles: 5,
colorize: false,
timestamp: true
},
console: {
level: 'debug',
handleExceptions: true,
json: true,
colorize: true,
timestamp: true
}
};
var logger = new winston.Logger({
transports: [
new winston.transports.File(options.file),
new winston.transports.Console(options.console)
],
exitOnError: false // do not exit on handled exceptions
});
logger.stream = {
write: function (message) {
logger.info(message)
}
}
// A custom logger interface that wraps winston, making it easy to instrument
// code and still possible to replace winston in the future.
module.exports.debug = module.exports.log = function () {
logger.debug.apply(logger, formatLogArguments(arguments))
}
module.exports.info = function () {
logger.info.apply(logger, formatLogArguments(arguments))
}
module.exports.warn = function () {
logger.warn.apply(logger, formatLogArguments(arguments))
}
module.exports.error = function () {
logger.error.apply(logger, formatLogArguments(arguments))
}
module.exports.stream = logger.stream
/**
* Attempts to add file and line number info to the given log arguments.
*/
function formatLogArguments (args) {
args = Array.prototype.slice.call(args)
var stackInfo = getStackInfo(1)
if (stackInfo) {
// get file path relative to project root
var calleeStr = '(' + stackInfo.relativePath + ':' + stackInfo.line + ')'
if (typeof (args[0]) === 'string') {
args[0] = calleeStr + ' ' + args[0]
} else {
args.unshift(calleeStr)
}
}
return args
}
/**
* Parses and returns info about the call stack at the given index.
*/
function getStackInfo (stackIndex) {
// get call stack, and analyze it
// get all file, method, and line numbers
var stacklist = (new Error()).stack.split('\n').slice(3)
// stack trace format:
// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
// do not remove the regex expresses to outside of this method (due to a BUG in node.js)
var stackReg = /at\s+(.*)\s+\((.*):(\d*):(\d*)\)/gi
var stackReg2 = /at\s+()(.*):(\d*):(\d*)/gi
var s = stacklist[stackIndex] || stacklist[0]
var sp = stackReg.exec(s) || stackReg2.exec(s)
if (sp && sp.length === 5) {
return {
method: sp[1],
relativePath: path.relative(PROJECT_ROOT, sp[2]),
line: sp[3],
pos: sp[4],
file: path.basename(sp[2]),
stack: stacklist.join('\n')
}
}
}
回答4:
I'm using winston 2.x (but the same solution will work for winston 3.x) and that's the way I'm logging the filename and linenumber of the caller:
IMPORTANT: pay attention to the embedded code comments!
/**
* Use CallSite to extract filename and number
* @returns {string} filename and line number separated by a colon
*/
const getFileNameAndLineNumber = () => {
const oldStackTrace = Error.prepareStackTrace;
try {
// eslint-disable-next-line handle-callback-err
Error.prepareStackTrace = (err, structuredStackTrace) => structuredStackTrace;
Error.captureStackTrace(this);
// in this example I needed to "peel" the first 10 CallSites in order to get to the caller we're looking for, hence the magic number 11
// in your code, the number of stacks depends on the levels of abstractions you're using, which mainly depends on winston version!
// so I advise you to put a breakpoint here and see if you need to adjust the number!
return this.stack[11].getFileName() + ':' + this.stack[11].getLineNumber();
} finally {
Error.prepareStackTrace = oldStackTrace;
}
};
And (a simplified version of) the formatter function:
function humanReadableFormatter ({level, message}) {
const filename = getFileNameAndLineNumber();
return `[${level}] ${filename} ${message}`;
}
Then declare the transport to use the formatter:
new winston.transports.Console({
level: 'info',
handleExceptions: true,
humanReadableUnhandledException: true,
json: false,
colorize: 'level',
stderrLevels: ['warn', 'error', 'alert'],
formatter: humanReadableFormatter,
})
To read more about prepareStackTrace read: https://v8.dev/docs/stack-trace-api#customizing-stack-traces
来源:https://stackoverflow.com/questions/44952938/get-line-number-and-filename-for-a-log-output