RollingFileStream.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. "use strict";
  2. var BaseRollingFileStream = require('./BaseRollingFileStream')
  3. , debug = require('debug')('streamroller:RollingFileStream')
  4. , util = require('util')
  5. , path = require('path')
  6. , child_process = require('child_process')
  7. , fs = require('fs');
  8. module.exports = RollingFileStream;
  9. function RollingFileStream (filename, size, backups, options) {
  10. //if you don't specify a size, this will behave like a normal file stream
  11. this.size = size || Number.MAX_SAFE_INTEGER;
  12. this.backups = backups || 1;
  13. function throwErrorIfArgumentsAreNotValid() {
  14. if (!filename || size <= 0) {
  15. throw new Error("You must specify a filename and file size");
  16. }
  17. }
  18. throwErrorIfArgumentsAreNotValid();
  19. RollingFileStream.super_.call(this, filename, options);
  20. }
  21. util.inherits(RollingFileStream, BaseRollingFileStream);
  22. RollingFileStream.prototype.shouldRoll = function() {
  23. debug("should roll with current size ", this.currentSize, " and max size ", this.size);
  24. return this.currentSize >= this.size;
  25. };
  26. RollingFileStream.prototype.roll = function(filename, callback) {
  27. var that = this;
  28. var fileNameObj = path.parse(filename);
  29. var dir = fileNameObj.dir;
  30. var name = fileNameObj.name;
  31. var ext = fileNameObj.ext === '' ? '' : fileNameObj.ext.substring(1);
  32. var nameMatcher = new RegExp('^' + name);
  33. function justTheseFiles (item) {
  34. return nameMatcher.test(item);
  35. }
  36. function getExtensions(filename_) {
  37. return filename_.substring((name + '.').length).split('.');
  38. }
  39. function index(filename_) {
  40. debug('Calculating index of '+filename_);
  41. var exts = getExtensions(filename_);
  42. if (exts[exts.length - 1] === 'gz') {
  43. exts.pop();
  44. }
  45. if (that.options.keepFileExt) {
  46. return parseInt(exts[0], 10) || 0;
  47. } else {
  48. return parseInt(exts[exts.length - 1]) || 0;
  49. }
  50. }
  51. function byIndex(a, b) {
  52. if (index(a) > index(b)) {
  53. return 1;
  54. } else if (index(a) < index(b) ) {
  55. return -1;
  56. } else {
  57. return 0;
  58. }
  59. }
  60. function increaseFileIndex (fileToRename, cb) {
  61. var idx = index(fileToRename);
  62. debug('Index of ' + fileToRename + ' is ' + idx);
  63. if (idx < that.backups) {
  64. var newIdx = (idx + 1).toString();
  65. var fileNameItems = [name];
  66. if (ext) {
  67. if (that.options.keepFileExt) {
  68. fileNameItems.push(newIdx, ext);
  69. } else {
  70. fileNameItems.push(ext, newIdx);
  71. }
  72. } else {
  73. fileNameItems.push(newIdx);
  74. }
  75. var destination = path.join(dir, fileNameItems.join('.'));
  76. if (that.options.compress && path.extname(fileToRename) === '.gz') {
  77. destination += '.gz';
  78. }
  79. //on windows, you can get a EEXIST error if you rename a file to an existing file
  80. //so, we'll try to delete the file we're renaming to first
  81. fs.unlink(destination, function (err) {
  82. //ignore err: if we could not delete, it's most likely that it doesn't exist
  83. debug('Renaming ' + fileToRename + ' -> ' + destination);
  84. fs.rename(path.join(dir, fileToRename), destination, function(err) {
  85. if (err) {
  86. cb(err);
  87. } else {
  88. if (that.options.compress && path.extname(fileToRename) !== '.gz') {
  89. that.compress(destination, cb);
  90. } else {
  91. cb();
  92. }
  93. }
  94. });
  95. });
  96. } else {
  97. cb();
  98. }
  99. }
  100. function renameTheFiles(cb) {
  101. //roll the backups (rename file.n to file.n+1, where n <= numBackups)
  102. debug("Renaming the old files");
  103. fs.readdir(path.dirname(filename), function (err, files) {
  104. if (err) {
  105. return cb(err);
  106. }
  107. var filesToProcess = files.filter(justTheseFiles).sort(byIndex);
  108. (function processOne(err) {
  109. var file = filesToProcess.pop();
  110. if (!file || err) { return cb(err); }
  111. increaseFileIndex(file, processOne);
  112. })();
  113. });
  114. }
  115. debug("Rolling, rolling, rolling");
  116. this.closeTheStream(
  117. renameTheFiles.bind(null,
  118. this.openTheStream.bind(this,
  119. callback)));
  120. };