@boost/log
Version:
Lightweight level based logging system.
181 lines (168 loc) • 5.6 kB
JavaScript
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import fs from 'node:fs';
import zlib from 'node:zlib';
import { Path } from '@boost/common';
import { portablePathSchema } from '@boost/common/optimal';
import { MAX_LOG_SIZE } from '../constants.mjs';
import { Transport } from '../Transport.mjs';
class FileTransport extends Transport {
constructor(options) {
super(options);
this.path = void 0;
this.stream = void 0;
this.buffer = '';
this.draining = false;
this.lastSize = 0;
this.rotating = false;
this.path = Path.resolve(this.options.path);
this.checkFolderRequirements();
}
blueprint(schemas) {
const bool = schemas.bool,
number = schemas.number;
return _objectSpread(_objectSpread({}, super.blueprint(schemas)), {}, {
gzip: bool(),
maxSize: number(MAX_LOG_SIZE).positive(),
path: portablePathSchema.required()
});
}
/**
* Close the file stream and trigger the callback when finished.
*/
close(commit) {
const onClose = () => {
commit?.();
this.stream = undefined;
};
if (this.stream) {
this.stream.once('finish', onClose).end();
} else {
onClose();
}
}
/**
* Open the file stream for writing.
*/
open() {
if (this.stream) {
return this.stream;
}
this.stream = this.createStream();
if (this.path.exists()) {
this.lastSize = fs.statSync(this.path.path()).size;
}
if (this.buffer) {
const message = this.buffer;
this.buffer = '';
this.write(message);
}
return this.stream;
}
/**
* Write a message to the file stream, and rotate files once written if necessary.
*/
write(message) {
if (this.rotating) {
this.buffer += message;
return;
}
const stream = this.open();
const written = stream.write(message, 'utf8', () => {
this.lastSize += Buffer.byteLength(message);
this.checkIfNeedsRotation();
});
// istanbul ignore next
if (!written) {
this.draining = true;
stream.once('drain', () => {
this.draining = false;
});
}
}
/**
* Check that the parent folder exists and has the correct permissions.
*/
checkFolderRequirements() {
fs.mkdirSync(this.path.parent().path(), {
recursive: true
});
}
/**
* Check if we should change and rotate files because of max size.
*/
checkIfNeedsRotation() {
// @ts-expect-error Valid is defined
if (this.options && this.lastSize > this.options.maxSize) {
this.closeStreamAndRotateFile();
}
}
/**
* Open and create a file stream for the defined path.
* Apply file size and gzip checks.
*/
createStream() {
const stream = fs.createWriteStream(this.path.path(), {
encoding: 'utf8',
flags: 'a'
});
// Apply gzip compression to the stream
if (this.options.gzip) {
const gzip = zlib.createGzip();
gzip.pipe(stream);
return gzip;
}
return stream;
}
/**
* Return the file name with extension, of the newly rotated file.
*/
getRotatedFileName() {
return this.path.name();
}
/**
* Count the number of files within path directory that matches the given file name.
*/
getNextIncrementCount(name) {
const files = fs.readdirSync(this.path.parent().path());
const pattern = new RegExp(`^${name}.\\d+$`, 'u');
let count = 0;
files.forEach(file => {
if (file.match(pattern)) {
count += 1;
}
});
return count;
}
/**
* Close the open stream and attempt to rotate the file.
*/
closeStreamAndRotateFile() {
// istanbul ignore next
if (this.draining || this.rotating) {
return;
}
this.rotating = true;
this.close(() => {
this.rotateFile();
this.rotating = false;
});
}
/**
* Rotate the current file into a new file with an incremented name.
*/
rotateFile() {
let fileName = this.getRotatedFileName();
if (this.options.gzip) {
fileName += '.gz';
}
fileName += `.${this.getNextIncrementCount(fileName)}`;
fs.renameSync(this.path.path(), this.path.parent().append(fileName).path());
this.lastSize = 0;
}
}
export { FileTransport };
//# sourceMappingURL=FileTransport.mjs.map