Comparing version 1.0.2 to 1.0.3
300
metalog.js
'use strict'; | ||
const fs = require('fs'); | ||
const util = require('util'); | ||
const events = require('events'); | ||
@@ -48,4 +47,9 @@ const common = require('@metarhia/common'); | ||
const normalizeStack = stack => stack.replace(/\s+at\s+/g, '\n\t'); | ||
const lineStack = stack => stack.replace(/[\n\r]\s*/g, '; '); | ||
const pad = (s, len, char = ' ') => s + char.repeat(len - s.length); | ||
// Logger wrapper to bind it to certain application | ||
class ApplicationLogger { | ||
@@ -65,3 +69,3 @@ | ||
fatal(message) { | ||
const msg = Logger.normalizeStack(message); | ||
const msg = normalizeStack(message); | ||
this.logger.write('fatal', msg, this.application); | ||
@@ -71,3 +75,3 @@ } | ||
error(message) { | ||
const msg = Logger.normalizeStack(message); | ||
const msg = normalizeStack(message); | ||
this.logger.write('error', msg, this.application); | ||
@@ -85,3 +89,3 @@ } | ||
debug(message) { | ||
const msg = Logger.normalizeStack(message); | ||
const msg = normalizeStack(message); | ||
this.logger.write('debug', msg, this.application); | ||
@@ -100,163 +104,157 @@ } | ||
// Logger constructor | ||
// path <string> log directory | ||
// node <string> nodeId | ||
// writeInterval <number> flush log to disk interval | ||
// writeBuffer <number> buffer size 64kb | ||
// keepDays <number> delete files after N days, 0 to disable | ||
// toFile <string[]> write log types to file | ||
// toStdout <string[]> write log types to stdout | ||
function Logger(options) { | ||
const { path, node } = options; | ||
const { writeInterval, writeBuffer, keepDays } = options; | ||
const { toFile, toStdout } = options; | ||
this.active = false; | ||
this.path = path; | ||
this.node = node; | ||
this.writeInterval = writeInterval || 3000; | ||
this.writeBuffer = writeBuffer || 64 * 1024; | ||
this.keepDays = keepDays || 0; | ||
this.options = { flags: 'a', highWaterMark: this.writeBuffer }; | ||
this.stream = null; | ||
this.reopenTimer = null; | ||
this.flushTimer = null; | ||
this.lock = false; | ||
this.buffer = []; | ||
this.file = ''; | ||
this.toFile = logTypes(toFile); | ||
this.toStdout = logTypes(toStdout); | ||
this.open(); | ||
} | ||
class Logger extends events.EventEmitter { | ||
util.inherits(Logger, events.EventEmitter); | ||
// path <string> log directory | ||
// node <string> nodeId | ||
// writeInterval <number> flush log to disk interval | ||
// writeBuffer <number> buffer size 64kb | ||
// keepDays <number> delete files after N days, 0 to disable | ||
// toFile <string[]> write log types to file | ||
// toStdout <string[]> write log types to stdout | ||
constructor(options) { | ||
super(); | ||
const { path, node } = options; | ||
const { writeInterval, writeBuffer, keepDays } = options; | ||
const { toFile, toStdout } = options; | ||
this.active = false; | ||
this.path = path; | ||
this.node = node; | ||
this.writeInterval = writeInterval || 3000; | ||
this.writeBuffer = writeBuffer || 64 * 1024; | ||
this.keepDays = keepDays || 0; | ||
this.options = { flags: 'a', highWaterMark: this.writeBuffer }; | ||
this.stream = null; | ||
this.reopenTimer = null; | ||
this.flushTimer = null; | ||
this.lock = false; | ||
this.buffer = []; | ||
this.file = ''; | ||
this.toFile = logTypes(toFile); | ||
this.toStdout = logTypes(toStdout); | ||
this.open(); | ||
} | ||
Logger.prototype.open = function() { | ||
if (this.active) return; | ||
this.active = true; | ||
const date = common.nowDate(); | ||
this.file = this.path + '/' + date + '-' + this.node + '.log'; | ||
const now = new Date(); | ||
const nextDate = new Date(); | ||
nextDate.setUTCHours(0, 0, 0, 0); | ||
const nextReopen = nextDate - now + DAY_MILLISECONDS; | ||
this.reopenTimer = setTimeout(() => { | ||
this.once('close', () => { | ||
this.open(); | ||
open() { | ||
if (this.active) return; | ||
this.active = true; | ||
const date = common.nowDate(); | ||
this.file = `${this.path}/${date}-${this.node}.log`; | ||
const now = new Date(); | ||
const nextDate = new Date(); | ||
nextDate.setUTCHours(0, 0, 0, 0); | ||
const nextReopen = nextDate - now + DAY_MILLISECONDS; | ||
this.reopenTimer = setTimeout(() => { | ||
this.once('close', () => { | ||
this.open(); | ||
}); | ||
this.close(); | ||
}, nextReopen); | ||
if (this.keepDays) this.rotate(); | ||
this.stream = fs.createWriteStream(this.file, this.options); | ||
this.flushTimer = setInterval(() => { | ||
this.flush(); | ||
}, this.writeInterval); | ||
this.stream.on('open', () => { | ||
this.emit('open'); | ||
}); | ||
this.close(); | ||
}, nextReopen); | ||
if (this.keepDays) this.rotate(); | ||
this.stream = fs.createWriteStream(this.file, this.options); | ||
this.flushTimer = setInterval(() => { | ||
this.flush(); | ||
}, this.writeInterval); | ||
this.stream.on('open', () => { | ||
this.emit('open'); | ||
}); | ||
this.stream.on('error', () => { | ||
this.emit('error', new Error('Can\'t open log file:' + this.file)); | ||
}); | ||
}; | ||
this.stream.on('error', () => { | ||
this.emit('error', new Error(`Can't open log file: ${this.file}`)); | ||
}); | ||
} | ||
Logger.prototype.close = function() { | ||
if (!this.active) return; | ||
const stream = this.stream; | ||
if (!stream || stream.destroyed || stream.closed) return; | ||
this.flush((err) => { | ||
if (err) return; | ||
this.active = false; | ||
this.stream.end(() => { | ||
clearInterval(this.flushTimer); | ||
clearTimeout(this.reopenTimer); | ||
this.flushTimer = null; | ||
this.reopenTimer = null; | ||
const fileName = this.file; | ||
this.emit('close'); | ||
fs.stat(fileName, (err, stats) => { | ||
if (err) { | ||
console.log(err); | ||
return; | ||
} | ||
if (stats.size > 0) return; | ||
fs.unlink(this.file, (err) => { | ||
console.log(err); | ||
close() { | ||
if (!this.active) return; | ||
const stream = this.stream; | ||
if (!stream || stream.destroyed || stream.closed) return; | ||
this.flush(err => { | ||
if (err) return; | ||
this.active = false; | ||
this.stream.end(() => { | ||
clearInterval(this.flushTimer); | ||
clearTimeout(this.reopenTimer); | ||
this.flushTimer = null; | ||
this.reopenTimer = null; | ||
const fileName = this.file; | ||
this.emit('close'); | ||
fs.stat(fileName, (err, stats) => { | ||
if (err) { | ||
process.stdout.write(`${err}\n`); | ||
return; | ||
} | ||
if (stats.size > 0) return; | ||
fs.unlink(this.file, err => { | ||
process.stdout.write(`${err}\n`); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}; | ||
} | ||
Logger.prototype.rotate = function() { | ||
if (!this.keepDays) return; | ||
fs.readdir(this.path, (err, files) => { | ||
if (err) { | ||
console.log(err); | ||
return; | ||
} | ||
const now = new Date(); | ||
const date = new Date( | ||
now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0, 0 | ||
); | ||
const time = date.getTime(); | ||
let i, fileName, fileTime, fileAge; | ||
for (i in files) { | ||
fileName = files[i]; | ||
fileTime = new Date(fileName.substring(0, 10)).getTime(); | ||
fileAge = Math.floor((time - fileTime) / DAY_MILLISECONDS); | ||
if (fileAge > 1 && fileAge > this.keepDays - 1) { | ||
fs.unlink(this.path + '/' + fileName, (err) => { | ||
console.log(err); | ||
}); | ||
rotate() { | ||
if (!this.keepDays) return; | ||
fs.readdir(this.path, (err, files) => { | ||
if (err) { | ||
process.stdout.write(`${err}\n`); | ||
return; | ||
} | ||
const now = new Date(); | ||
const date = new Date( | ||
now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0, 0 | ||
); | ||
const time = date.getTime(); | ||
let i, fileName, fileTime, fileAge; | ||
for (i in files) { | ||
fileName = files[i]; | ||
fileTime = new Date(fileName.substring(0, 10)).getTime(); | ||
fileAge = Math.floor((time - fileTime) / DAY_MILLISECONDS); | ||
if (fileAge > 1 && fileAge > this.keepDays - 1) { | ||
fs.unlink(this.path + '/' + fileName, err => { | ||
process.stdout.write(`${err}\n`); | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
write(type, message, application = 'default') { | ||
const date = new Date(); | ||
if (this.toStdout[type]) { | ||
const normalColor = textColor[type]; | ||
const markColor = typeColor[type]; | ||
const time = normalColor(date.toTimeString().substring(0, 8)); | ||
const mark = markColor(' ' + pad(type, 7)); | ||
const msg = normalColor(`${this.node}/${application} ${message}`); | ||
const line = `${time} ${mark} ${msg}`; | ||
process.stdout.write(`${line}\n`); | ||
} | ||
}); | ||
}; | ||
if (this.toFile[type]) { | ||
const time = date.toISOString(); | ||
const multiline = (/[\n\r]/g).test(message); | ||
const line = multiline ? lineStack(message) : message; | ||
const data = `${time} [${type}] ${this.node}/${application} ${line}\n`; | ||
const buffer = Buffer.from(data); | ||
this.buffer.push(buffer); | ||
} | ||
} | ||
Logger.normalizeStack = (stack) => stack.replace(/\s+at\s+/g, '\n\t'); | ||
Logger.lineStack = (stack) => stack.replace(/[\n\r]\s*/g, '; '); | ||
Logger.formatStack = (stack) => stack.replace(/; /g, '\n\t'); | ||
const pad = (s, len, char = ' ') => s + char.repeat(len - s.length); | ||
Logger.prototype.write = function(type, message, application = 'default') { | ||
const date = new Date(); | ||
if (this.toStdout[type]) { | ||
const normalColor = textColor[type]; | ||
const markColor = typeColor[type]; | ||
const time = normalColor(date.toTimeString().substring(0, 8)); | ||
const mark = markColor(' ' + pad(type, 7)); | ||
const msg = normalColor(`${this.node}/${application} ${message}`); | ||
const line = `${time} ${mark} ${msg}`; | ||
console.log(line); | ||
flush(callback) { | ||
if (!this.active || this.lock || !this.buffer.length) { | ||
if (callback) callback(new Error('Can\'t flush log buffer')); | ||
return; | ||
} | ||
this.lock = true; | ||
const buffer = Buffer.concat(this.buffer); | ||
this.buffer.length = 0; | ||
this.stream.write(buffer, err => { | ||
this.lock = false; | ||
if (callback) callback(err); | ||
}); | ||
} | ||
if (this.toFile[type]) { | ||
const time = date.toISOString(); | ||
const multiline = (/[\n\r]/g).test(message); | ||
const line = multiline ? Logger.lineStack(message) : message; | ||
const data = `${time} [${type}] ${this.node}/${application} ${line}\n`; | ||
const buffer = Buffer.from(data); | ||
this.buffer.push(buffer); | ||
} | ||
}; | ||
Logger.prototype.flush = function(callback) { | ||
if (!this.active || this.lock || !this.buffer.length) { | ||
if (callback) callback(new Error('Can\'t flush log buffer')); | ||
return; | ||
bind(application) { | ||
return new ApplicationLogger(this, application); | ||
} | ||
this.lock = true; | ||
const buffer = Buffer.concat(this.buffer); | ||
this.buffer.length = 0; | ||
this.stream.write(buffer, (err) => { | ||
this.lock = false; | ||
if (callback) callback(err); | ||
}); | ||
}; | ||
Logger.prototype.bind = function(application) { | ||
return new ApplicationLogger(this, application); | ||
}; | ||
} | ||
module.exports = (args) => new Logger(args); | ||
module.exports = args => new Logger(args); |
{ | ||
"name": "metalog", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>", | ||
@@ -23,4 +23,3 @@ "description": "Meta Logger for Metarhia", | ||
"test": "eslint . && metatests test/", | ||
"lint": "eslint .", | ||
"docs": "metaschema ./metalog.js" | ||
"lint": "eslint ." | ||
}, | ||
@@ -35,6 +34,5 @@ "engines": { | ||
"devDependencies": { | ||
"eslint": "^5.11.1", | ||
"metaschema": "0.0.29", | ||
"eslint": "^5.12.0", | ||
"metatests": "^0.4.0" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2
15684
345