123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- 'use strict';
- const os = require('os');
- const util = require('util');
- const chalk = require('chalk');
- const utility = require('utility');
- const iconv = require('iconv-lite');
- const levels = require('./level');
- const circularJSON = require('circular-json-for-egg');
- const { FrameworkBaseError, FrameworkErrorFormater } = require('egg-errors');
- const hostname = os.hostname();
- const duartionRegexp = /([0-9]+ms)/g;
- // eslint-disable-next-line no-useless-escape
- const categoryRegexp = /(\[[\w\-_.:]+\])/g;
- const httpMethodRegexp = /(GET|POST|PUT|PATH|HEAD|DELETE) /g;
- /**
- * @class LoggerUtils
- */
- module.exports = {
- normalizeLevel(level) {
- if (typeof level === 'number') {
- return level;
- }
- // 'WARN' => level.warn
- if (typeof level === 'string' && level) {
- return levels[level.toUpperCase()];
- }
- },
- // default format
- defaultFormatter(meta) {
- return meta.date + ' ' + meta.level + ' ' + meta.pid + ' ' + meta.message;
- },
- // output to Terminal format
- consoleFormatter(meta) {
- let msg = meta.date + ' ' + meta.level + ' ' + meta.pid + ' ' + meta.message;
- if (!chalk.supportsColor) {
- return msg;
- }
- if (meta.level === 'ERROR') {
- return chalk.red(msg);
- } else if (meta.level === 'WARN') {
- return chalk.yellow(msg);
- }
- msg = msg.replace(duartionRegexp, chalk.green('$1'));
- msg = msg.replace(categoryRegexp, chalk.blue('$1'));
- msg = msg.replace(httpMethodRegexp, chalk.cyan('$1 '));
- return msg;
- },
- /**
- * Get final formated log string buffer
- *
- * Invoke link: {@Link Logger#log} -> {@link Transport#log} -> LoggerUtils.format
- * @function LoggerUtils#format
- * @param {String} level - log level
- * @param {Array} args - format arguments
- * @param {Object} meta - loging behaviour meta infomation
- * - {String} level
- * - {Boolean} raw
- * - {Function} formatter
- * - {Error} error
- * - {String} message
- * - {Number} pid
- * - {String} hostname
- * - {String} date
- * @param {Object} options - {@link Transport}'s options
- * - {String} encoding
- * - {Boolean} json
- * - {Function} formatter
- * @return {String|Buffer} formatted log string buffer
- */
- format(level, args, meta, options) {
- meta = meta || {};
- let message;
- let output;
- let formatter = meta.formatter || options.formatter;
- if (meta.ctx && options.contextFormatter) formatter = options.contextFormatter;
- if (args[0] instanceof Error) {
- message = formatError(args[0]);
- } else {
- message = util.format.apply(util, args);
- }
- if (meta.raw === true) {
- output = message;
- } else if (options.json === true || formatter) {
- meta.level = level;
- meta.date = utility.logDate(',');
- meta.pid = process.pid;
- meta.hostname = hostname;
- meta.message = message;
- if (options.json === true) {
- const outputMeta = { ...meta };
- outputMeta.ctx = undefined;
- output = JSON.stringify(outputMeta);
- } else {
- output = formatter(meta);
- }
- } else {
- output = message;
- }
- if (!output) return Buffer.from('');
- output += options.eol;
- // convert string to buffer when encoding is not utf8
- return options.encoding === 'utf8' ? output : iconv.encode(output, options.encoding);
- },
- // Like `Object.assign`, but don't copy `undefined` value
- assign(target) {
- if (!target) {
- return {};
- }
- const sources = Array.prototype.slice.call(arguments, 1);
- for (let i = 0; i < sources.length; i++) {
- const source = sources[i];
- if (!source) continue;
- const keys = Object.keys(source);
- for (let j = 0; j < keys.length; j++) {
- const key = keys[j];
- if (source[key] !== undefined && source[key] !== null) {
- target[key] = source[key];
- }
- }
- }
- return target;
- },
- formatError,
- };
- function formatError(err) {
- if (FrameworkBaseError.isFrameworkError(err)) {
- return FrameworkErrorFormater.format(err);
- }
- if (err.name === 'Error' && typeof err.code === 'string') {
- err.name = err.code + err.name;
- }
- if (err.host) {
- err.message += ` (${err.host})`;
- }
- // name and stack could not be change on node 0.11+
- const errStack = err.stack || 'no_stack';
- const errProperties = Object.keys(err).map(key => inspect(key, err[key])).join('\n');
- return util.format('nodejs.%s: %s\n%s\n%s\npid: %s\nhostname: %s\n',
- err.name,
- err.message,
- errStack.substring(errStack.indexOf('\n') + 1),
- errProperties,
- process.pid,
- hostname
- );
- }
- function inspect(key, value) {
- return `${key}: ${formatObject(value)}`;
- }
- function formatString(str) {
- if (str.length > 10000) {
- return `${str.substr(0, 10000)}...(${str.length})`;
- }
- return str;
- }
- function formatBuffer(buf) {
- const tail = buf.data.length > 50 ? ` ...(${buf.data.length}) ` : '';
- const bufStr = buf.data.slice(0, 50).map(i => {
- i = i.toString(16);
- if (i.length === 1) i = `0${i}`;
- return i;
- }).join(' ');
- return `<Buffer ${bufStr}${tail}>`;
- }
- function formatObject(obj) {
- try {
- return circularJSON.stringify(obj, (key, v) => {
- if (typeof v === 'string') return formatString(v);
- if (v && v.type === 'Buffer' && Array.isArray(v.data)) {
- return formatBuffer(v);
- }
- if (v instanceof RegExp) return util.inspect(v);
- return v;
- });
- } catch (_) {
- /* istanbul ignore next */
- return String(obj);
- }
- }
|