fd22b0bf58
V2 (Laravel re-make)
328 lines
7.8 KiB
JavaScript
Vendored
328 lines
7.8 KiB
JavaScript
Vendored
import Types from './types.js'
|
|
import { isLogObj } from './utils/index.js'
|
|
|
|
let paused = false
|
|
const queue = []
|
|
|
|
class Consola {
|
|
constructor (options = {}) {
|
|
this._reporters = options.reporters || []
|
|
this._types = options.types || Types
|
|
this.level = options.level !== undefined ? options.level : 3
|
|
this._defaults = options.defaults || {}
|
|
this._async = options.async !== undefined ? options.async : undefined
|
|
this._stdout = options.stdout
|
|
this._stderr = options.stderr
|
|
this._mockFn = options.mockFn
|
|
this._throttle = options.throttle || 1000
|
|
this._throttleMin = options.throttleMin || 5
|
|
|
|
// Create logger functions for current instance
|
|
for (const type in this._types) {
|
|
const defaults = {
|
|
type,
|
|
...this._types[type],
|
|
...this._defaults
|
|
}
|
|
this[type] = this._wrapLogFn(defaults)
|
|
this[type].raw = this._wrapLogFn(defaults, true)
|
|
}
|
|
|
|
// Use _mockFn if is set
|
|
if (this._mockFn) {
|
|
this.mockTypes()
|
|
}
|
|
|
|
// Keep serialized version of last log
|
|
this._lastLogSerialized = undefined
|
|
this._lastLog = undefined
|
|
this._lastLogTime = undefined
|
|
this._lastLogCount = 0
|
|
this._throttleTimeout = undefined
|
|
}
|
|
|
|
get stdout () {
|
|
return this._stdout || console._stdout // eslint-disable-line no-console
|
|
}
|
|
|
|
get stderr () {
|
|
return this._stderr || console._stderr // eslint-disable-line no-console
|
|
}
|
|
|
|
create (options) {
|
|
return new Consola(Object.assign({
|
|
reporters: this._reporters,
|
|
level: this.level,
|
|
types: this._types,
|
|
defaults: this._defaults,
|
|
stdout: this._stdout,
|
|
stderr: this._stderr,
|
|
mockFn: this._mockFn
|
|
}, options))
|
|
}
|
|
|
|
withDefaults (defaults) {
|
|
return this.create({
|
|
defaults: Object.assign({}, this._defaults, defaults)
|
|
})
|
|
}
|
|
|
|
withTag (tag) {
|
|
return this.withDefaults({
|
|
tag: this._defaults.tag ? (this._defaults.tag + ':' + tag) : tag
|
|
})
|
|
}
|
|
|
|
addReporter (reporter) {
|
|
this._reporters.push(reporter)
|
|
return this
|
|
}
|
|
|
|
removeReporter (reporter) {
|
|
if (reporter) {
|
|
const i = this._reporters.indexOf(reporter)
|
|
if (i >= 0) {
|
|
return this._reporters.splice(i, 1)
|
|
}
|
|
} else {
|
|
this._reporters.splice(0)
|
|
}
|
|
return this
|
|
}
|
|
|
|
setReporters (reporters) {
|
|
this._reporters = Array.isArray(reporters)
|
|
? reporters
|
|
: [reporters]
|
|
return this
|
|
}
|
|
|
|
wrapAll () {
|
|
this.wrapConsole()
|
|
this.wrapStd()
|
|
}
|
|
|
|
restoreAll () {
|
|
this.restoreConsole()
|
|
this.restoreStd()
|
|
}
|
|
|
|
wrapConsole () {
|
|
for (const type in this._types) {
|
|
// Backup original value
|
|
if (!console['__' + type]) { // eslint-disable-line no-console
|
|
console['__' + type] = console[type] // eslint-disable-line no-console
|
|
}
|
|
// Override
|
|
console[type] = this[type].raw // eslint-disable-line no-console
|
|
}
|
|
}
|
|
|
|
restoreConsole () {
|
|
for (const type in this._types) {
|
|
// Restore if backup is available
|
|
if (console['__' + type]) { // eslint-disable-line no-console
|
|
console[type] = console['__' + type] // eslint-disable-line no-console
|
|
delete console['__' + type] // eslint-disable-line no-console
|
|
}
|
|
}
|
|
}
|
|
|
|
wrapStd () {
|
|
this._wrapStream(this.stdout, 'log')
|
|
this._wrapStream(this.stderr, 'log')
|
|
}
|
|
|
|
_wrapStream (stream, type) {
|
|
if (!stream) {
|
|
return
|
|
}
|
|
|
|
// Backup original value
|
|
if (!stream.__write) {
|
|
stream.__write = stream.write
|
|
}
|
|
|
|
// Override
|
|
stream.write = (data) => {
|
|
this[type].raw(String(data).trim())
|
|
}
|
|
}
|
|
|
|
restoreStd () {
|
|
this._restoreStream(this.stdout)
|
|
this._restoreStream(this.stderr)
|
|
}
|
|
|
|
_restoreStream (stream) {
|
|
if (!stream) {
|
|
return
|
|
}
|
|
|
|
if (stream.__write) {
|
|
stream.write = stream.__write
|
|
delete stream.__write
|
|
}
|
|
}
|
|
|
|
pauseLogs () {
|
|
paused = true
|
|
}
|
|
|
|
resumeLogs () {
|
|
paused = false
|
|
|
|
// Process queue
|
|
const _queue = queue.splice(0)
|
|
for (const item of _queue) {
|
|
item[0]._logFn(item[1], item[2])
|
|
}
|
|
}
|
|
|
|
mockTypes (mockFn) {
|
|
this._mockFn = mockFn || this._mockFn
|
|
|
|
if (typeof this._mockFn !== 'function') {
|
|
return
|
|
}
|
|
|
|
for (const type in this._types) {
|
|
this[type] = this._mockFn(type, this._types[type]) || this[type]
|
|
this[type].raw = this[type]
|
|
}
|
|
}
|
|
|
|
_wrapLogFn (defaults, isRaw) {
|
|
return (...args) => {
|
|
if (paused) {
|
|
queue.push([this, defaults, args, isRaw])
|
|
return
|
|
}
|
|
return this._logFn(defaults, args, isRaw)
|
|
}
|
|
}
|
|
|
|
_logFn (defaults, args, isRaw) {
|
|
if (defaults.level > this.level) {
|
|
return this._async ? Promise.resolve(false) : false
|
|
}
|
|
|
|
// Construct a new log object
|
|
const logObj = Object.assign({
|
|
date: new Date(),
|
|
args: []
|
|
}, defaults)
|
|
|
|
// Consume arguments
|
|
if (!isRaw && args.length === 1 && isLogObj(args[0])) {
|
|
Object.assign(logObj, args[0])
|
|
} else {
|
|
logObj.args = Array.from(args)
|
|
}
|
|
|
|
// Aliases
|
|
if (logObj.message) {
|
|
logObj.args.unshift(logObj.message)
|
|
delete logObj.message
|
|
}
|
|
if (logObj.additional) {
|
|
if (!Array.isArray(logObj.additional)) {
|
|
logObj.additional = logObj.additional.split('\n')
|
|
}
|
|
logObj.args.push('\n' + logObj.additional.join('\n'))
|
|
delete logObj.additional
|
|
}
|
|
|
|
// Normalize type and tag to lowercase
|
|
logObj.type = typeof logObj.type === 'string' ? logObj.type.toLowerCase() : ''
|
|
logObj.tag = typeof logObj.tag === 'string' ? logObj.tag.toLowerCase() : ''
|
|
|
|
// Resolve log
|
|
/**
|
|
* @param newLog false if the throttle expired and
|
|
* we don't want to log a duplicate
|
|
*/
|
|
const resolveLog = (newLog = false) => {
|
|
const repeated = this._lastLogCount - this._throttleMin
|
|
if (this._lastLog && repeated > 0) {
|
|
const args = [...this._lastLog.args]
|
|
if (repeated > 1) {
|
|
args.push(`(repeated ${repeated} times)`)
|
|
}
|
|
this._log({ ...this._lastLog, args })
|
|
this._lastLogCount = 1
|
|
}
|
|
|
|
// Log
|
|
if (newLog) {
|
|
this._lastLog = logObj
|
|
if (this._async) {
|
|
return this._logAsync(logObj)
|
|
} else {
|
|
this._log(logObj)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Throttle
|
|
clearTimeout(this._throttleTimeout)
|
|
const diffTime = this._lastLogTime ? logObj.date - this._lastLogTime : 0
|
|
this._lastLogTime = logObj.date
|
|
if (diffTime < this._throttle) {
|
|
try {
|
|
const serializedLog = JSON.stringify([logObj.type, logObj.tag, logObj.args])
|
|
const isSameLog = this._lastLogSerialized === serializedLog
|
|
this._lastLogSerialized = serializedLog
|
|
if (isSameLog) {
|
|
this._lastLogCount++
|
|
if (this._lastLogCount > this._throttleMin) {
|
|
// Auto-resolve when throttle is timed out
|
|
this._throttleTimeout = setTimeout(resolveLog, this._throttle)
|
|
return // SPAM!
|
|
}
|
|
}
|
|
} catch (_) {
|
|
// Circular References
|
|
}
|
|
}
|
|
|
|
resolveLog(true)
|
|
}
|
|
|
|
_log (logObj) {
|
|
for (const reporter of this._reporters) {
|
|
reporter.log(logObj, {
|
|
async: false,
|
|
stdout: this.stdout,
|
|
stderr: this.stderr
|
|
})
|
|
}
|
|
}
|
|
|
|
_logAsync (logObj) {
|
|
return Promise.all(
|
|
this._reporters.map(reporter => reporter.log(logObj, {
|
|
async: true,
|
|
stdout: this.stdout,
|
|
stderr: this.stderr
|
|
}))
|
|
)
|
|
}
|
|
}
|
|
|
|
// Legacy support
|
|
Consola.prototype.add = Consola.prototype.addReporter
|
|
|
|
Consola.prototype.remove = Consola.prototype.removeReporter
|
|
Consola.prototype.clear = Consola.prototype.removeReporter
|
|
|
|
Consola.prototype.withScope = Consola.prototype.withTag
|
|
|
|
Consola.prototype.mock = Consola.prototype.mockTypes
|
|
|
|
Consola.prototype.pause = Consola.prototype.pauseLogs
|
|
Consola.prototype.resume = Consola.prototype.resumeLogs
|
|
|
|
// Export class
|
|
export default Consola
|