file.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. const debug = require('debug')('log4js:file');
  2. const path = require('path');
  3. const streams = require('streamroller');
  4. const os = require('os');
  5. const eol = os.EOL;
  6. let mainSighupListenerStarted = false;
  7. const sighupListeners = new Set();
  8. function mainSighupHandler() {
  9. sighupListeners.forEach((app) => {
  10. app.sighupHandler();
  11. });
  12. }
  13. function openTheStream(file, fileSize, numFiles, options) {
  14. const stream = new streams.RollingFileStream(
  15. file,
  16. fileSize,
  17. numFiles,
  18. options
  19. );
  20. stream.on('error', (err) => {
  21. console.error('log4js.fileAppender - Writing to file %s, error happened ', file, err); //eslint-disable-line
  22. });
  23. stream.on('drain', () => {
  24. process.emit("log4js:pause", false);
  25. });
  26. return stream;
  27. }
  28. /**
  29. * File Appender writing the logs to a text file. Supports rolling of logs by size.
  30. *
  31. * @param file file log messages will be written to
  32. * @param layout a function that takes a logEvent and returns a string
  33. * (defaults to basicLayout).
  34. * @param logSize - the maximum size (in bytes) for a log file,
  35. * if not provided then logs won't be rotated.
  36. * @param numBackups - the number of log files to keep after logSize
  37. * has been reached (default 5)
  38. * @param options - options to be passed to the underlying stream
  39. * @param timezoneOffset - optional timezone offset in minutes (default system local)
  40. */
  41. function fileAppender(file, layout, logSize, numBackups, options, timezoneOffset) {
  42. file = path.normalize(file);
  43. numBackups = (!numBackups && numBackups !== 0) ? 5 : numBackups;
  44. debug(
  45. 'Creating file appender (',
  46. file, ', ',
  47. logSize, ', ',
  48. numBackups, ', ',
  49. options, ', ',
  50. timezoneOffset, ')'
  51. );
  52. let writer = openTheStream(file, logSize, numBackups, options);
  53. const app = function (loggingEvent) {
  54. if (!writer.writable) {
  55. return;
  56. }
  57. if (options.removeColor === true) {
  58. // eslint-disable-next-line no-control-regex
  59. const regex = /\x1b[[0-9;]*m/g;
  60. loggingEvent.data = loggingEvent.data.map(d => {
  61. if (typeof d === 'string') return d.replace(regex, '');
  62. return d;
  63. });
  64. }
  65. if (!writer.write(layout(loggingEvent, timezoneOffset) + eol, "utf8")) {
  66. process.emit('log4js:pause', true);
  67. }
  68. };
  69. app.reopen = function () {
  70. writer.end(() => { writer = openTheStream(file, logSize, numBackups, options); });
  71. };
  72. app.sighupHandler = function () {
  73. debug('SIGHUP handler called.');
  74. app.reopen();
  75. };
  76. app.shutdown = function (complete) {
  77. sighupListeners.delete(app);
  78. if (sighupListeners.size === 0 && mainSighupListenerStarted) {
  79. process.removeListener('SIGHUP', mainSighupHandler);
  80. mainSighupListenerStarted = false;
  81. }
  82. writer.end('', 'utf-8', complete);
  83. };
  84. // On SIGHUP, close and reopen all files. This allows this appender to work with
  85. // logrotate. Note that if you are using logrotate, you should not set
  86. // `logSize`.
  87. sighupListeners.add(app);
  88. if (!mainSighupListenerStarted) {
  89. process.on('SIGHUP', mainSighupHandler);
  90. mainSighupListenerStarted = true;
  91. }
  92. return app;
  93. }
  94. function configure(config, layouts) {
  95. let layout = layouts.basicLayout;
  96. if (config.layout) {
  97. layout = layouts.layout(config.layout.type, config.layout);
  98. }
  99. // security default (instead of relying on streamroller default)
  100. config.mode = config.mode || 0o600;
  101. return fileAppender(
  102. config.filename,
  103. layout,
  104. config.maxLogSize,
  105. config.backups,
  106. config,
  107. config.timezoneOffset
  108. );
  109. }
  110. module.exports.configure = configure;