UNPKG

logsene-js

Version:

JavaScript client for Sematext Logs

200 lines (189 loc) 5.99 kB
'use strict' var path = require('path') var fs = require('fs') var diskBufferObjectCache = {} var os = require('os') var mkpath = require('mkpath') var util = require('util') var events = require('events') function log (message) { if (process.env.DEBUG_LOGSENE_DISK_BUFFER) { console.log(new Date().toISOString() + ': ' + message) } } function DiskBuffer (options) { this.storedRequestCount = 0 this.fileId = 0 this.options = options || {tmpDir: os.tmpDir(), maxStoredRequests: 1000} this.tmpDir = options.tmpDir this.maxStoredRequests = options.maxStoredRequests || 1000 this.storedFiles = [] this.retransmitIndex = 0 var self = this this.tid = setInterval(function () { self.retransmitNext.call(self) }, options.interval || 5000) mkpath(this.tmpDir, function (err) { if (err) { log('Error: can not activate disk buffer for logsene-js: ' + err) } self.syncFileListFromDir() }) } util.inherits(DiskBuffer, events.EventEmitter) DiskBuffer.prototype.unlock = function (fileName) { try { fs.renameSync(fileName, fileName.replace('.lock', '')) } catch (err) {} } DiskBuffer.prototype.retransmitNext = function () { if (this.storedFiles.length === 0) { this.retransmitIndex = 0 return } this.retransmitIndex = this.retransmitIndex + 1 if (this.retransmitIndex >= this.storedFiles.length) { this.retransmitIndex = 0 } var index = this.retransmitIndex log('# of files: ' + this.storedFiles.length + ' current file:' + index) if (this.storedFiles.length >= index) { try { var fileName = this.storedFiles[index] if (!fileName) { log('filename not in list:' + fileName + ' ' + this.storedFiles.length) return } log('retransmitNext: ' + fileName) try { fs.statSync(fileName) } catch (fsStatErr) { // this.rmFile(fileName) return } var lockedFileName = fileName + '.lock' fs.renameSync(fileName, lockedFileName) var buffer = fs.readFileSync(lockedFileName) var self = this setImmediate(function () { self.emit('retransmit-req', { fileName: lockedFileName, buffer: buffer }) }) } catch (err) { // console.error('retransmitNext error: ' + err.message) } } } DiskBuffer.prototype.syncFileListFromDir = function () { try { this.cleanUp() this.storedFiles = fs.readdirSync(this.tmpDir) this.storedFiles = this.storedFiles.filter(function (fileName) { var rv = false if (/\.bulk$/.test(fileName)) { rv = true } else { try { var fName = path.join(this.tmpDir, fileName) var fStat = fs.statSync(fName) var now = Date.now() if (now - fStat.atime.getTime() > 1000 * 60 * 5) { // a bulk req. should no take longer than 5 min log('rename 5 min old .lock file to .bulk: ' + fName) fs.renameSync(fName, fName.substring(0, fName.length - 5)) fs.unlinkSync(fName) } } catch (fsErr) { log('syncFileListFromDir error: ' + fsErr.message) } } return rv }.bind(this)) this.storedFiles = this.storedFiles.map(function (fileName) { return path.join(this.tmpDir, fileName) }.bind(this)) this.storedRequestCount = this.storedFiles.length } catch (err) { log('error syncFileListFromDir', err) this.storedFiles = [] } log('syncFileListFromDir: ' + this.storedFiles) } DiskBuffer.prototype.cleanUp = function () { var self = this if (this.storedRequestCount > this.maxStoredRequests) { if (this.storedFiles.length > this.maxStoredRequests) { log('cleanUp DiskBuffer ' + this.tmpDir) log('stored req: ' + this.storedRequestCount + ', maxStoredReq: ' + this.maxStoredRequests) for (var i = 0; this.storedFiles.length >= this.maxStoredRequests; i++) { if (i === this.storedFiles.length) { break } log('disk buffer limit reached, drop old file:' + this.storedFiles[i]) self.rmFile.call(self, this.storedFiles[i]) } } } } DiskBuffer.prototype.addFile = function (fileName) { this.storedFiles.push(fileName) } DiskBuffer.prototype.rmFile = function (fileName) { if (!fileName) { return } var index = this.storedFiles.indexOf(fileName.replace('.lock', '')) if (index < 0) { // already done before // this.emit('removed', {fileName: fileName}) return } try { fs.unlinkSync(fileName) log('rm file:' + fileName) this.emit('removed', { fileName: fileName }) } catch (err) { log('rmFile: could not delete file:' + err.message) // ignore when file was already deleted this.emit('removed', { fileName: fileName }) } finally { if (index > -1) { this.storedFiles.splice(index, 1) this.storedRequestCount = this.storedFiles.length return true } else { return false } } } DiskBuffer.prototype.getFileName = function () { this.fileId += 1 return path.join(this.tmpDir, this.fileId + '_' + new Date().getTime() + '.bulk') } DiskBuffer.prototype.store = function (data, cb) { var self = this this.storedRequestCount++ this.checkTmpDir = true var fn = this.getFileName() log('stored req: ' + this.storedRequestCount + ', maxStoredReq: ' + this.maxStoredRequests) this.cleanUp() fs.writeFile(fn, JSON.stringify(data), function (err) { if (cb & err) { return cb(err) } self.addFile.call(self, fn) if (cb) { return cb(null, fn) } }) } function createDiskBuffer (options) { if (!diskBufferObjectCache[options.tmpDir]) { diskBufferObjectCache[options.tmpDir] = new DiskBuffer(options) diskBufferObjectCache[options.tmpDir].isCached = false } else { diskBufferObjectCache[options.tmpDir].isCached = true } return diskBufferObjectCache[options.tmpDir] } module.exports.createDiskBuffer = createDiskBuffer module.exports.DiskBuffer = DiskBuffer