ui
This commit is contained in:
+327
@@ -0,0 +1,327 @@
|
||||
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
|
||||
Reference in New Issue
Block a user