@boost/log
Advanced tools
Comparing version 2.2.4 to 2.2.5
@@ -6,2 +6,14 @@ # Change Log | ||
### 2.2.5 - 2021-03-26 | ||
#### 📦 Dependencies | ||
- **[packemon]** Update to v0.14. (#145) ([9897e2a](https://github.com/milesj/boost/commit/9897e2a)), closes [#145](https://github.com/milesj/boost/issues/145) | ||
**Note:** Version bump only for package @boost/log | ||
### 2.2.4 - 2021-02-21 | ||
@@ -8,0 +20,0 @@ |
600
lib/index.js
@@ -9,597 +9,31 @@ // Generated with Packemon: https://packemon.dev | ||
var common = require('@boost/common'); | ||
var createLogger = require('./createLogger.js'); | ||
var chalk = require('chalk'); | ||
var formats = require('./formats.js'); | ||
var path = require('path'); | ||
var Logger = require('./Logger.js'); | ||
var translate = require('@boost/translate'); | ||
var Transport = require('./Transport.js'); | ||
var os = require('os'); | ||
var ConsoleTransport = require('./transports/ConsoleTransport.js'); | ||
var util = require('util'); | ||
var FileTransport = require('./transports/FileTransport.js'); | ||
var internal = require('@boost/internal'); | ||
var RotatingFileTransport = require('./transports/RotatingFileTransport.js'); | ||
var fs = require('fs'); | ||
var StreamTransport = require('./transports/StreamTransport.js'); | ||
var zlib = require('zlib'); | ||
var constants = require('./constants.js'); | ||
function _interopDefaultLegacy(e) { | ||
return e && typeof e === 'object' && 'default' in e ? e : { | ||
'default': e | ||
}; | ||
} | ||
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk); | ||
var path__default = /*#__PURE__*/_interopDefaultLegacy(path); | ||
var os__default = /*#__PURE__*/_interopDefaultLegacy(os); | ||
var util__default = /*#__PURE__*/_interopDefaultLegacy(util); | ||
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); | ||
var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib); | ||
var msg = translate.createTranslator('log', path__default['default'].join(__dirname, '../res')); // In order of priority! | ||
const LOG_LEVELS = ['log', 'trace', 'debug', 'info', 'warn', 'error']; | ||
const DEFAULT_LABELS = { | ||
debug: chalk__default['default'].gray(msg('log:levelDebug')), | ||
error: chalk__default['default'].red(msg('log:levelError')), | ||
info: chalk__default['default'].cyan(msg('log:levelInfo')), | ||
log: chalk__default['default'].yellow(msg('log:levelLog')), | ||
trace: chalk__default['default'].magenta(msg('log:levelTrace')), | ||
warn: chalk__default['default'].yellow(msg('log:levelWarn')) | ||
}; | ||
const MAX_LOG_SIZE = 10485760; | ||
var debug = internal.createInternalDebugger('log'); | ||
function formatMetadata(metadata) { | ||
const items = []; | ||
const keys = Object.keys(metadata).sort(); | ||
keys.forEach(key => { | ||
items.push(`${key}=${metadata[key]}`); | ||
}); | ||
return `(${items.join(', ')})`; | ||
} | ||
function console$1(item) { | ||
let output = item.message; | ||
if (item.level !== 'log') { | ||
output = `${item.label} ${output}`; | ||
} | ||
return output; | ||
} | ||
function debug$1(item) { | ||
return `[${item.time.toISOString()}] ${item.level.toUpperCase()} ${item.message} ${formatMetadata({ ...item.metadata, | ||
host: item.host, | ||
name: item.name, | ||
pid: item.pid | ||
})}`; | ||
} | ||
function json(item) { | ||
return JSON.stringify(item); | ||
} | ||
function message(item) { | ||
return item.message; | ||
} | ||
var formats = /*#__PURE__*/Object.freeze({ | ||
__proto__: null, | ||
console: console$1, | ||
debug: debug$1, | ||
json: json, | ||
message: message | ||
}); | ||
class Transport extends common.Contract { | ||
constructor(options) { | ||
super(options); | ||
this.levels = []; | ||
this.levels = this.options.levels; | ||
} | ||
blueprint({ | ||
array, | ||
func, | ||
string | ||
}) { | ||
return { | ||
eol: string(os__default['default'].EOL), | ||
format: func().nullable(), | ||
levels: array(string().oneOf(LOG_LEVELS)) | ||
}; | ||
} | ||
/** | ||
* Format the log item into a message string, and append a trailing newline if missing. | ||
*/ | ||
format(item) { | ||
const { | ||
eol, | ||
format | ||
} = this.options; | ||
let output = typeof format === 'function' ? format(item) : debug$1(item); | ||
if (!output.endsWith(eol)) { | ||
output += eol; | ||
} | ||
return output; | ||
} | ||
/** | ||
* Write the formatted message according to the transport. | ||
*/ | ||
} | ||
class ConsoleTransport extends Transport { | ||
constructor(options) { | ||
super({ | ||
format: console$1, | ||
levels: LOG_LEVELS, | ||
...options | ||
}); | ||
} | ||
write(message, { | ||
level | ||
}) { | ||
// eslint-disable-next-line no-console | ||
console[level](message.trim()); | ||
} | ||
} | ||
class Logger extends common.Contract { | ||
constructor(options) { | ||
super(options); | ||
this.silent = false; | ||
const defaultLevel = internal.env('LOG_DEFAULT_LEVEL'); | ||
const maxLevel = internal.env('LOG_MAX_LEVEL'); | ||
debug('New logger "%s" created: %s %s', this.options.name, defaultLevel ? `${defaultLevel} level` : 'all levels', maxLevel ? `(max ${maxLevel})` : ''); | ||
} | ||
blueprint({ | ||
array, | ||
func, | ||
instance, | ||
object, | ||
shape, | ||
string | ||
}) { | ||
return { | ||
labels: object(string()), | ||
metadata: object(), | ||
name: string().required().notEmpty(), | ||
transports: array(instance(Transport).notNullable(), [new ConsoleTransport()]) | ||
}; | ||
} | ||
disable() { | ||
debug('Logger %s disabled', this.options.name); | ||
this.silent = true; | ||
} | ||
enable() { | ||
debug('Logger %s enabled', this.options.name); | ||
this.silent = false; | ||
} | ||
isAllowed(level, maxLevel) { | ||
if (!maxLevel) { | ||
return true; | ||
} // eslint-disable-next-line no-restricted-syntax | ||
for (const currentLevel of LOG_LEVELS) { | ||
if (currentLevel === level) { | ||
return true; | ||
} | ||
if (currentLevel === maxLevel) { | ||
break; | ||
} | ||
} | ||
return false; | ||
} | ||
log({ | ||
args = [], | ||
level, | ||
message, | ||
metadata = {} | ||
}) { | ||
const logLevel = level || internal.env('LOG_DEFAULT_LEVEL') || 'log'; | ||
if (this.silent || !this.isAllowed(logLevel, internal.env('LOG_MAX_LEVEL'))) { | ||
return; | ||
} | ||
const item = { | ||
host: os__default['default'].hostname(), | ||
label: this.options.labels[logLevel] || DEFAULT_LABELS[logLevel] || '', | ||
level: logLevel, | ||
message: util__default['default'].format(message, ...args), | ||
metadata: { ...this.options.metadata, | ||
...metadata | ||
}, | ||
name: this.options.name, | ||
pid: process.pid, | ||
time: new Date() | ||
}; | ||
this.options.transports.forEach(transport => { | ||
if (transport.levels.includes(item.level)) { | ||
void transport.write(transport.format(item), item); | ||
} | ||
}); | ||
} | ||
} | ||
function pipeLog(logger, level) { | ||
return (...args) => { | ||
let metadata = {}; | ||
let message = ''; | ||
if (common.isObject(args[0])) { | ||
metadata = args.shift(); | ||
} | ||
message = args.shift(); | ||
logger.log({ | ||
args, | ||
level, | ||
message, | ||
metadata | ||
}); | ||
}; | ||
} | ||
function createLogger(options) { | ||
const logger = new Logger(options); | ||
const log = pipeLog(logger); | ||
LOG_LEVELS.forEach(level => { | ||
Object.defineProperty(log, level, { | ||
value: pipeLog(logger, level) | ||
}); | ||
}); | ||
Object.defineProperty(log, 'disable', { | ||
value: () => { | ||
logger.disable(); | ||
} | ||
}); | ||
Object.defineProperty(log, 'enable', { | ||
value: () => { | ||
logger.enable(); | ||
} | ||
}); | ||
return log; | ||
} | ||
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 = common.Path.resolve(this.options.path); | ||
this.checkFolderRequirements(); | ||
} | ||
blueprint(preds) { | ||
const { | ||
bool, | ||
instance, | ||
union, | ||
number, | ||
string | ||
} = preds; | ||
return { ...super.blueprint(preds), | ||
gzip: bool(), | ||
maxSize: number(MAX_LOG_SIZE).positive(), | ||
path: union([string(), instance(common.Path)], '').required() | ||
}; | ||
} | ||
/** | ||
* Close the file stream and trigger the callback when finished. | ||
*/ | ||
close(cb) { | ||
const onClose = () => { | ||
if (cb) { | ||
cb(); | ||
} | ||
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__default['default'].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__default['default'].mkdirSync(this.path.parent().path(), { | ||
recursive: true | ||
}); | ||
} | ||
/** | ||
* Check if we should change and rotate files because of max size. | ||
*/ | ||
checkIfNeedsRotation() { | ||
if (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__default['default'].createWriteStream(this.path.path(), { | ||
encoding: 'utf8', | ||
flags: 'a' | ||
}); // Apply gzip compression to the stream | ||
if (this.options.gzip) { | ||
const gzip = zlib__default['default'].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__default['default'].readdirSync(this.path.parent().path()); // eslint-disable-next-line security/detect-non-literal-regexp | ||
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__default['default'].renameSync(this.path.path(), this.path.parent().append(fileName).path()); | ||
this.lastSize = 0; | ||
} | ||
} | ||
const DAYS_IN_WEEK = 7; | ||
class RotatingFileTransport extends FileTransport { | ||
constructor(...args) { | ||
super(...args); | ||
this.lastTimestamp = this.formatTimestamp(Date.now()); | ||
} | ||
blueprint(preds) { | ||
const { | ||
string | ||
} = preds; | ||
return { ...super.blueprint(preds), | ||
rotation: string().oneOf(['hourly', 'daily', 'weekly', 'monthly']) | ||
}; | ||
} | ||
/** | ||
* Format a `Date` object into a format used within the log file name. | ||
*/ | ||
formatTimestamp(ms) { | ||
const { | ||
rotation | ||
} = this.options; | ||
const date = new Date(ms); | ||
let timestamp = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, '0')}`; | ||
if (rotation === 'monthly') { | ||
return timestamp; | ||
} // Special case, calculate the week manually and return, | ||
// but do not append so other rotations inherit! | ||
if (rotation === 'weekly') { | ||
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1).getDay(); | ||
const offsetDate = date.getDate() + firstDay - 1; | ||
timestamp += `.W${Math.floor(offsetDate / DAYS_IN_WEEK) + 1}`; | ||
return timestamp; | ||
} | ||
timestamp += String(date.getDate()).padStart(2, '0'); | ||
if (rotation === 'daily') { | ||
return timestamp; | ||
} | ||
timestamp += `.${String(date.getHours()).padStart(2, '0')}`; | ||
return timestamp; | ||
} | ||
/** | ||
* @inheritdoc | ||
*/ | ||
checkIfNeedsRotation() { | ||
if (this.lastSize > this.options.maxSize || this.formatTimestamp(Date.now()) !== this.lastTimestamp) { | ||
this.closeStreamAndRotateFile(); | ||
} | ||
} | ||
/** | ||
* @inheritdoc | ||
*/ | ||
getRotatedFileName() { | ||
const name = this.path.name(true); | ||
const ext = this.path.ext(true); | ||
return `${name}-${this.lastTimestamp}.${ext}`; | ||
} | ||
/** | ||
* @inheritdoc | ||
*/ | ||
rotateFile() { | ||
super.rotateFile(); // Update timestamp to the new format | ||
this.lastTimestamp = this.formatTimestamp(Date.now()); | ||
} | ||
} | ||
class StreamTransport extends Transport { | ||
constructor(options) { | ||
super(options); | ||
this.stream = void 0; | ||
this.stream = options.stream; | ||
} | ||
blueprint(preds) { | ||
const { | ||
func, | ||
shape | ||
} = preds; | ||
return { ...super.blueprint(preds), | ||
stream: shape({ | ||
write: func().required().notNullable() | ||
}) | ||
}; | ||
} | ||
write(message) { | ||
this.stream.write(message); | ||
} | ||
} | ||
exports.createLogger = createLogger; | ||
exports.formats = formats; | ||
exports.Logger = Logger; | ||
exports.Transport = Transport; | ||
exports.ConsoleTransport = ConsoleTransport; | ||
exports.DEFAULT_LABELS = DEFAULT_LABELS; | ||
exports.FileTransport = FileTransport; | ||
exports.LOG_LEVELS = LOG_LEVELS; | ||
exports.Logger = Logger; | ||
exports.MAX_LOG_SIZE = MAX_LOG_SIZE; | ||
exports.RotatingFileTransport = RotatingFileTransport; | ||
exports.StreamTransport = StreamTransport; | ||
exports.Transport = Transport; | ||
exports.createLogger = createLogger; | ||
exports.formats = formats; | ||
exports.DEFAULT_LABELS = constants.DEFAULT_LABELS; | ||
exports.LOG_LEVELS = constants.LOG_LEVELS; | ||
exports.MAX_LOG_SIZE = constants.MAX_LOG_SIZE; | ||
//# sourceMappingURL=index.js.map |
@@ -24,1 +24,2 @@ // Generated with Packemon: https://packemon.dev | ||
exports.mockLogger = mockLogger; | ||
//# sourceMappingURL=test.js.map |
{ | ||
"name": "@boost/log", | ||
"version": "2.2.4", | ||
"version": "2.2.5", | ||
"release": "1594765247526", | ||
@@ -15,2 +15,10 @@ "description": "Lightweight level based logging system.", | ||
"types": "./dts/index.d.ts", | ||
"files": [ | ||
"dts/**/*.d.ts", | ||
"lib/**/*.{js,map}", | ||
"res/", | ||
"src/**/*.{ts,tsx,json}", | ||
"test.d.ts", | ||
"test.js" | ||
], | ||
"engines": { | ||
@@ -31,5 +39,5 @@ "node": ">=10.3.0", | ||
"dependencies": { | ||
"@boost/common": "^2.7.1", | ||
"@boost/internal": "^2.2.1", | ||
"@boost/translate": "^2.2.4", | ||
"@boost/common": "^2.7.2", | ||
"@boost/internal": "^2.2.2", | ||
"@boost/translate": "^2.2.5", | ||
"chalk": "^4.1.0" | ||
@@ -48,3 +56,3 @@ }, | ||
}, | ||
"gitHead": "85109c6c9f196f4374e13519cb52f88dc4860310" | ||
"gitHead": "f9eb83de54fa41334f1a1e487216004a03caf662" | ||
} |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
74017
61
1444
1
Updated@boost/common@^2.7.2
Updated@boost/internal@^2.2.2
Updated@boost/translate@^2.2.5