DateRollingFileStream.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. "use strict";
  2. var BaseRollingFileStream = require('./BaseRollingFileStream')
  3. , debug = require('debug')('streamroller:DateRollingFileStream')
  4. , format = require('date-format')
  5. , fs = require('fs')
  6. , path = require('path')
  7. , util = require('util');
  8. module.exports = DateRollingFileStream;
  9. function findTimestampFromFileIfExists(filename, now) {
  10. return fs.existsSync(filename) ? fs.statSync(filename).mtime : new Date(now());
  11. }
  12. function DateRollingFileStream(filename, pattern, options, now) {
  13. debug("Now is ", now);
  14. if (pattern && typeof(pattern) === 'object') {
  15. now = options;
  16. options = pattern;
  17. pattern = null;
  18. }
  19. this.pattern = pattern || '.yyyy-MM-dd';
  20. this.now = now || Date.now;
  21. this.lastTimeWeWroteSomething = format.asString(
  22. this.pattern,
  23. findTimestampFromFileIfExists(filename, this.now)
  24. );
  25. this.baseFilename = filename;
  26. this.alwaysIncludePattern = false;
  27. debug('options is ', options);
  28. if (options) {
  29. if (options.alwaysIncludePattern) {
  30. debug('always include pattern is true');
  31. this.alwaysIncludePattern = true;
  32. filename = this.baseFilename + this.lastTimeWeWroteSomething;
  33. debug('filename is now ', filename);
  34. }
  35. }
  36. debug("this.now is ", this.now, ", now is ", now);
  37. DateRollingFileStream.super_.call(this, filename, options);
  38. }
  39. util.inherits(DateRollingFileStream, BaseRollingFileStream);
  40. DateRollingFileStream.prototype.shouldRoll = function () {
  41. var lastTime = this.lastTimeWeWroteSomething,
  42. thisTime = format.asString(this.pattern, new Date(this.now()));
  43. debug("DateRollingFileStream.shouldRoll with now = ",
  44. this.now(), ", thisTime = ", thisTime, ", lastTime = ", lastTime);
  45. this.lastTimeWeWroteSomething = thisTime;
  46. this.previousTime = lastTime;
  47. return thisTime !== lastTime;
  48. };
  49. DateRollingFileStream.prototype.roll = function (filename, callback) {
  50. var that = this;
  51. debug("Starting roll");
  52. var filenameObj = path.parse(this.baseFilename);
  53. if (this.alwaysIncludePattern) {
  54. this.filename = this.options.keepFileExt ?
  55. path.join(
  56. filenameObj.dir,
  57. filenameObj.name + this.lastTimeWeWroteSomething + filenameObj.ext
  58. ) :
  59. this.baseFilename + this.lastTimeWeWroteSomething;
  60. this.closeTheStream(
  61. this.compressIfNeeded.bind(this, filename,
  62. this.removeOldFilesIfNeeded.bind(this,
  63. this.openTheStream.bind(this, callback))));
  64. } else {
  65. var newFilename = this.options.keepFileExt ?
  66. path.join(filenameObj.dir, filenameObj.name + this.previousTime + filenameObj.ext) :
  67. this.baseFilename + this.previousTime;
  68. this.closeTheStream(
  69. deleteAnyExistingFile.bind(null,
  70. renameTheCurrentFile.bind(null,
  71. this.compressIfNeeded.bind(this, newFilename,
  72. this.removeOldFilesIfNeeded.bind(this,
  73. this.openTheStream.bind(this, callback))))));
  74. }
  75. function deleteAnyExistingFile(cb) {
  76. //on windows, you can get a EEXIST error if you rename a file to an existing file
  77. //so, we'll try to delete the file we're renaming to first
  78. fs.unlink(newFilename, function (err) {
  79. //ignore err: if we could not delete, it's most likely that it doesn't exist
  80. cb();
  81. });
  82. }
  83. function renameTheCurrentFile(cb) {
  84. debug("Renaming the ", filename, " -> ", newFilename);
  85. fs.rename(filename, newFilename, cb);
  86. }
  87. };
  88. DateRollingFileStream.prototype.compressIfNeeded = function (filename, cb) {
  89. debug("Checking if we need to compress the old file");
  90. if (this.options.compress) {
  91. this.compress(filename, cb);
  92. } else {
  93. cb();
  94. }
  95. };
  96. DateRollingFileStream.prototype.removeOldFilesIfNeeded = function (cb) {
  97. debug("Checking if we need to delete old files");
  98. if (this.options.daysToKeep && this.options.daysToKeep > 0) {
  99. var oldestDate = new Date(this.now() - (this.options.daysToKeep * (24 * 60 * 60 * 1000)));
  100. debug("Will delete any log files modified before ", oldestDate.toString());
  101. this.removeFilesOlderThan(oldestDate);
  102. }
  103. cb();
  104. };
  105. DateRollingFileStream.prototype.removeFilesOlderThan = function (oldestDate) {
  106. // Loop through any log files and delete any whose mtime is earlier than oldestDate
  107. var dirToScan = path.dirname(this.baseFilename);
  108. var fileToMatch = path.basename(this.baseFilename);
  109. var filesToCheck = fs.readdirSync(dirToScan).filter(function (file) {
  110. return file.indexOf(fileToMatch) > -1;
  111. });
  112. for (var i = 0; i < filesToCheck.length; i++) {
  113. var fileToCheck = path.join(dirToScan, filesToCheck[i]);
  114. var fileStats = fs.statSync(fileToCheck);
  115. if (fileStats.mtime < oldestDate) {
  116. debug("Deleting old log ", filesToCheck);
  117. fs.unlinkSync(fileToCheck);
  118. }
  119. }
  120. };