generated from astrohelm/node-workspace
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
202 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict'; | ||
|
||
const DEFAULT_COLOR = '#6666ff'; | ||
const DEFAULT_METHOD = console.log; | ||
const DEFAULT_BIND = { method: DEFAULT_METHOD, color: DEFAULT_COLOR }; | ||
const { format: timeFormat } = new Intl.DateTimeFormat(); //? Bro language | ||
module.exports = Browser; | ||
|
||
// prettier-ignore | ||
const BINDINGS = { | ||
info: { method: console.info ?? DEFAULT_METHOD, color: '#659AD2' }, | ||
warn: { method: console.warn ?? DEFAULT_METHOD, color: '#F9C749' }, | ||
debug: { method: console.debug ?? DEFAULT_METHOD, color: '#9AA2AA' }, | ||
error: { method: console.error ?? DEFAULT_METHOD, color: '#EC3D47' }, | ||
trace: { method: console.trace ?? DEFAULT_METHOD, color: '#F9C749' }, | ||
fatal: { method: console.error ?? DEFAULT_METHOD, color: '#EC3D47' }, | ||
}; | ||
|
||
function Browser() {} | ||
Browser.prototype.write = function (_, { lvl, time, msg = '', ...other }) { | ||
const { color, method } = BINDINGS[lvl] ?? DEFAULT_BIND; | ||
const format = `[${timeFormat(time)}] %c${lvl.toUpperCase()}:`; | ||
method(format, color, msg, JSON.stringify(other, null, 2).slice(1, -1)); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
'use strict'; | ||
|
||
const { join } = require('path'); | ||
|
||
module.exports = { | ||
INVALID_OPTIONS: 'Invalid type of options parameter, must be an object', | ||
INVALID_DIRECTORY: 'Can not create directory: ', | ||
ROTATION_ERROR: 'Error wile rotating the file: ', | ||
STREAM_ERROR: 'Log file is not writable: ', | ||
|
||
OPTIONS: { | ||
path: join(__dirname, './logs'), | ||
bufferSize: 64 * 1_024, | ||
writeInterval: 3_000, | ||
silence: false, | ||
locale: 'af', | ||
keep: 3, | ||
}, | ||
|
||
STREAM_OPTIONS: { flags: 'a' }, | ||
DAY_IN_MS: 1000 * 60 * 60 * 24, | ||
STAB_FUNCTION: () => false, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
'use strict'; | ||
|
||
const { INVALID_OPTIONS, INVALID_DIRECTORY, STREAM_ERROR, ROTATION_ERROR } = require('./config'); | ||
const { OPTIONS, STREAM_OPTIONS, DAY_IN_MS, STAB_FUNCTION } = require('./config'); | ||
|
||
const { createWriteStream, promises: fsp } = require('node:fs'); | ||
const { EventEmitter } = require('node:events'); | ||
const { stat, mkdir, readdir, unlink } = fsp; | ||
const { join } = require('node:path'); | ||
|
||
const PROMISE_TO_BOOL = [() => true, () => false]; | ||
|
||
module.exports = class FSLogger extends EventEmitter { | ||
#buffer = { length: 0, store: [] }; | ||
#active = false; | ||
#options = null; | ||
#stream = null; | ||
#lock = null; | ||
#location = ''; | ||
#file = ''; | ||
|
||
#switchTimer = null; | ||
#flushTimer = null; | ||
|
||
//? Only this method can throw exceptions | ||
constructor(options = {}) { | ||
super(); | ||
if (options === null || typeof options !== 'object') throw new Error(INVALID_OPTIONS); | ||
this.#options = { ...OPTIONS, ...options }; | ||
if (!this.#options.silence) return; | ||
const emit = this.emit.bind(this); | ||
this.emit = (event, ...args) => { | ||
if (event === 'error') return this; | ||
return emit(event, ...args); | ||
}; | ||
} | ||
|
||
async start() { | ||
if (this.#active) return this; | ||
const path = this.#options.path; | ||
var isExist = await stat(path) | ||
.then(v => v.isDirectory()) | ||
.catch(STAB_FUNCTION); | ||
|
||
if (!isExist) isExist = await mkdir(path).then(...PROMISE_TO_BOOL); | ||
if (!isExist) throw new Error(INVALID_DIRECTORY + path); | ||
await this.#open(); | ||
return this; | ||
} | ||
|
||
async finish() { | ||
if (!(this.#active && this.#stream)) return; | ||
if (this.#stream.destroyed || this.#stream.closed) return; | ||
await this.#flush(); | ||
this.#active = false; | ||
this.#switchTimer = (clearTimeout(this.#switchTimer), null); | ||
this.#flushTimer = (clearTimeout(this.#flushTimer), null); | ||
await stat(this.#location) | ||
.then(({ size }) => !size && unlink(this.#location).catch(STAB_FUNCTION)) | ||
.catch(STAB_FUNCTION); | ||
} | ||
|
||
write(log) { | ||
this.#buffer.store.push(Buffer.from(log + '\n')); | ||
this.#buffer.length += log.length; | ||
this.#buffer.length > this.#options.bufferSize && this.flush(); | ||
} | ||
|
||
async #open() { | ||
this.#file = new Date().toLocaleDateString(this.#options.locale) + '.log'; | ||
this.#location = join(this.#options.path, this.#file); | ||
await this.#rotate(); | ||
|
||
const error = await new Promise(resolve => { | ||
const signal = () => resolve(STREAM_ERROR + this.#file); | ||
this.#stream = createWriteStream(this.#location, STREAM_OPTIONS); | ||
this.#stream.on('error', () => this.emit(STREAM_ERROR + this.#file)); | ||
this.#stream.once('open', () => { | ||
this.#stream.off('error', signal); | ||
resolve(); | ||
}); | ||
}); | ||
|
||
if (error) return void this.#stream.destroy(); | ||
const today = new Date(); | ||
const tm = -today.getTime() + today.setUTCHours(0, 0, 0, 0) + DAY_IN_MS; | ||
|
||
this.#active = true; | ||
this.#flushTimer = setInterval(() => this.#flush(), this.#options.writeInterval); | ||
this.#switchTimer = setTimeout(() => this.finish().then(() => this.#open()), tm); | ||
} | ||
|
||
async #flush() { | ||
if (this.#lock) await this.#lock; | ||
if (!this.#buffer.length) return Promise.resolve(); | ||
const buffer = Buffer.concat(this.#buffer.store); | ||
this.#buffer.store.length = this.#buffer.length = 0; | ||
this.#lock = new Promise(res => void this.#stream.write(buffer, res)); | ||
return this.#lock; | ||
} | ||
|
||
async #rotate() { | ||
if (!this.#options.keep) return; | ||
const promises = []; | ||
try { | ||
var today = new Date().getTime(); | ||
var files = await readdir(this.#options.path); | ||
for (var name of files) { | ||
if (!name.endsWith('.log')) continue; | ||
var date = new Date(name.substring(0, name.lastIndexOf('.'))).getTime(); | ||
if ((today - date) / DAY_IN_MS < this.#options.keep) continue; | ||
promises.push(unlink(join(this.#options.path, name))); | ||
} | ||
await Promise.all(promises); | ||
} catch (err) { | ||
this.emit('error', ROTATION_ERROR + err); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
'use strict'; | ||
|
||
module.exports = { | ||
FSLogger: require('./fslogger'), | ||
Console: require('./console'), | ||
'nalogy/filesystem': require('./filesystem'), | ||
'nalogy/console': require('./console'), | ||
'nalogy/browser': require('./browser'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters