index.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*!
  2. * mini-logger - index.js
  3. * Copyright(c) 2014 dead_horse <dead_horse@qq.com>
  4. * MIT Licensed
  5. */
  6. 'use strict';
  7. /**
  8. * Module dependencies.
  9. */
  10. var EventEmitter = require('events').EventEmitter;
  11. var logfilestream = require('logfilestream');
  12. var formater = require('error-formater');
  13. var copy = require('copy-to');
  14. var util = require('util');
  15. var ms = require('ms');
  16. var os = require('os');
  17. var defer = typeof setImediate === 'function'
  18. ? setImediate
  19. : process.nextTick;
  20. var SEPERATOR = os.EOL + os.EOL;
  21. /**
  22. * Expose `Logger`
  23. */
  24. module.exports = Logger;
  25. var defaultOptions = {
  26. categories: [],
  27. format: '[{category}.]YYYY-MM-DD[.log]',
  28. stdout: false,
  29. file: true,
  30. errorFormater: formater,
  31. seperator: SEPERATOR
  32. }
  33. function Logger(options) {
  34. if (!(this instanceof Logger)) return new Logger(options);
  35. if (!options || !options.dir) throw new Error('options.dir required');
  36. this._options = {};
  37. copy(options).and(defaultOptions).to(this._options);
  38. options = this._options;
  39. if (!Array.isArray(options.categories)) options.categories = [ options.categories ];
  40. options.categories.push('error');
  41. options.categories = uniq(options.categories);
  42. options.duration = typeof options.duration === 'string'
  43. ? ms(options.duration)
  44. : options.duration;
  45. options.flushInterval = typeof options.flushInterval === 'string'
  46. ? ms(options.flushInterval)
  47. : options.flushInterval;
  48. options.encoding = (options.encoding || 'utf-8').toLowerCase();
  49. if (options.encoding === 'utf8') options.encoding = 'utf-8';
  50. this._init();
  51. }
  52. util.inherits(Logger, EventEmitter);
  53. Logger.prototype._init = function() {
  54. var ctx = this;
  55. // create log functions
  56. this._options.categories.forEach(function (category) {
  57. ctx[category] = function (msg) {
  58. msg = (msg instanceof Error)
  59. ? msg = ctx._options.errorFormater(msg)
  60. : typeof msg === 'object'
  61. ? JSON.stringify(msg)
  62. : util.format.apply(util, arguments);
  63. msg += ctx._options.seperator;
  64. ctx._write(category, msg);
  65. };
  66. });
  67. this._streams = {};
  68. if (!this._options.file) return;
  69. // create log file streams
  70. this._options.categories.forEach(function (category) {
  71. var format = ctx._options.format.replace(/\{category\}/g, category);
  72. var stream = logfilestream({
  73. logdir: ctx._options.dir,
  74. duration: ctx._options.duration,
  75. nameformat: format,
  76. mkdir: ctx._options.mkdir,
  77. buffer: ctx._options.flushInterval,
  78. mode: ctx._options.mode,
  79. encoding: ctx._options.encoding
  80. });
  81. stream.on('error', ctx.emit.bind(ctx, 'error'));
  82. ctx._streams[category] = stream;
  83. });
  84. defer(function () {
  85. if (!ctx.listeners('error').length) ctx.on('error', onerror);
  86. });
  87. };
  88. Logger.prototype._write = function (category, msg) {
  89. // write to file
  90. if (this._options.file && this._streams[category]) this._streams[category].write(msg);
  91. // write to stdout
  92. if (this._options.stdout) {
  93. msg = '[' + category + '] ' + msg;
  94. if (this._options.encoding !== 'utf-8') {
  95. msg = require('iconv-lite').encode(msg, this._options.encoding);
  96. }
  97. category === 'error'
  98. ? process.stderr.write(msg)
  99. : process.stdout.write(msg);
  100. }
  101. };
  102. /**
  103. * flush logs into file immediate
  104. */
  105. Logger.prototype.flush = function(category) {
  106. if (category) return this._streams[category].flush();
  107. for (var category in this._streams) {
  108. this._streams[category].flush();
  109. }
  110. };
  111. Logger.prototype.getPath = function(category) {
  112. if (!category) return;
  113. if (!this._streams[category]) return;
  114. if (!this._streams[category].stream) return;
  115. return this._streams[category].stream.path;
  116. };
  117. Logger.prototype.destroy = function (category) {
  118. if (category) return this._destory(category);
  119. this._options.categories.forEach(this._destory.bind(this));
  120. };
  121. Logger.prototype._destory = function (category) {
  122. delete this[category];
  123. if (!this._streams[category]) return;
  124. this._streams[category].end();
  125. this._streams[category].removeAllListeners();
  126. this._streams[category] = null;
  127. };
  128. function onerror(err) {
  129. console.error(err.stack);
  130. }
  131. function uniq(categories) {
  132. var res = {};
  133. categories.forEach(function (c) {
  134. res[c] = 1;
  135. });
  136. return Object.keys(res);
  137. }