You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					310 lines
				
				8.4 KiB
			
		
		
			
		
	
	
					310 lines
				
				8.4 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								'use strict'
							 | 
						||
| 
								 | 
							
								var Progress = require('are-we-there-yet')
							 | 
						||
| 
								 | 
							
								var Gauge = require('gauge')
							 | 
						||
| 
								 | 
							
								var EE = require('events').EventEmitter
							 | 
						||
| 
								 | 
							
								var log = exports = module.exports = new EE()
							 | 
						||
| 
								 | 
							
								var util = require('util')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var setBlocking = require('set-blocking')
							 | 
						||
| 
								 | 
							
								var consoleControl = require('console-control-strings')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								setBlocking(true)
							 | 
						||
| 
								 | 
							
								var stream = process.stderr
							 | 
						||
| 
								 | 
							
								Object.defineProperty(log, 'stream', {
							 | 
						||
| 
								 | 
							
								  set: function (newStream) {
							 | 
						||
| 
								 | 
							
								    stream = newStream
							 | 
						||
| 
								 | 
							
								    if (this.gauge) this.gauge.setWriteTo(stream, stream)
							 | 
						||
| 
								 | 
							
								  },
							 | 
						||
| 
								 | 
							
								  get: function () {
							 | 
						||
| 
								 | 
							
								    return stream
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// by default, decide based on tty-ness.
							 | 
						||
| 
								 | 
							
								var colorEnabled
							 | 
						||
| 
								 | 
							
								log.useColor = function () {
							 | 
						||
| 
								 | 
							
								  return colorEnabled != null ? colorEnabled : stream.isTTY
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.enableColor = function () {
							 | 
						||
| 
								 | 
							
								  colorEnabled = true
							 | 
						||
| 
								 | 
							
								  this.gauge.setTheme({hasColor: colorEnabled, hasUnicode: unicodeEnabled})
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								log.disableColor = function () {
							 | 
						||
| 
								 | 
							
								  colorEnabled = false
							 | 
						||
| 
								 | 
							
								  this.gauge.setTheme({hasColor: colorEnabled, hasUnicode: unicodeEnabled})
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// default level
							 | 
						||
| 
								 | 
							
								log.level = 'info'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.gauge = new Gauge(stream, {
							 | 
						||
| 
								 | 
							
								  enabled: false, // no progress bars unless asked
							 | 
						||
| 
								 | 
							
								  theme: {hasColor: log.useColor()},
							 | 
						||
| 
								 | 
							
								  template: [
							 | 
						||
| 
								 | 
							
								    {type: 'progressbar', length: 20},
							 | 
						||
| 
								 | 
							
								    {type: 'activityIndicator', kerning: 1, length: 1},
							 | 
						||
| 
								 | 
							
								    {type: 'section', default: ''},
							 | 
						||
| 
								 | 
							
								    ':',
							 | 
						||
| 
								 | 
							
								    {type: 'logline', kerning: 1, default: ''}
							 | 
						||
| 
								 | 
							
								  ]
							 | 
						||
| 
								 | 
							
								})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.tracker = new Progress.TrackerGroup()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// we track this separately as we may need to temporarily disable the
							 | 
						||
| 
								 | 
							
								// display of the status bar for our own loggy purposes.
							 | 
						||
| 
								 | 
							
								log.progressEnabled = log.gauge.isEnabled()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var unicodeEnabled
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.enableUnicode = function () {
							 | 
						||
| 
								 | 
							
								  unicodeEnabled = true
							 | 
						||
| 
								 | 
							
								  this.gauge.setTheme({hasColor: this.useColor(), hasUnicode: unicodeEnabled})
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.disableUnicode = function () {
							 | 
						||
| 
								 | 
							
								  unicodeEnabled = false
							 | 
						||
| 
								 | 
							
								  this.gauge.setTheme({hasColor: this.useColor(), hasUnicode: unicodeEnabled})
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.setGaugeThemeset = function (themes) {
							 | 
						||
| 
								 | 
							
								  this.gauge.setThemeset(themes)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.setGaugeTemplate = function (template) {
							 | 
						||
| 
								 | 
							
								  this.gauge.setTemplate(template)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.enableProgress = function () {
							 | 
						||
| 
								 | 
							
								  if (this.progressEnabled) return
							 | 
						||
| 
								 | 
							
								  this.progressEnabled = true
							 | 
						||
| 
								 | 
							
								  this.tracker.on('change', this.showProgress)
							 | 
						||
| 
								 | 
							
								  if (this._pause) return
							 | 
						||
| 
								 | 
							
								  this.gauge.enable()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.disableProgress = function () {
							 | 
						||
| 
								 | 
							
								  if (!this.progressEnabled) return
							 | 
						||
| 
								 | 
							
								  this.progressEnabled = false
							 | 
						||
| 
								 | 
							
								  this.tracker.removeListener('change', this.showProgress)
							 | 
						||
| 
								 | 
							
								  this.gauge.disable()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var trackerConstructors = ['newGroup', 'newItem', 'newStream']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var mixinLog = function (tracker) {
							 | 
						||
| 
								 | 
							
								  // mixin the public methods from log into the tracker
							 | 
						||
| 
								 | 
							
								  // (except: conflicts and one's we handle specially)
							 | 
						||
| 
								 | 
							
								  Object.keys(log).forEach(function (P) {
							 | 
						||
| 
								 | 
							
								    if (P[0] === '_') return
							 | 
						||
| 
								 | 
							
								    if (trackerConstructors.filter(function (C) { return C === P }).length) return
							 | 
						||
| 
								 | 
							
								    if (tracker[P]) return
							 | 
						||
| 
								 | 
							
								    if (typeof log[P] !== 'function') return
							 | 
						||
| 
								 | 
							
								    var func = log[P]
							 | 
						||
| 
								 | 
							
								    tracker[P] = function () {
							 | 
						||
| 
								 | 
							
								      return func.apply(log, arguments)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								  // if the new tracker is a group, make sure any subtrackers get
							 | 
						||
| 
								 | 
							
								  // mixed in too
							 | 
						||
| 
								 | 
							
								  if (tracker instanceof Progress.TrackerGroup) {
							 | 
						||
| 
								 | 
							
								    trackerConstructors.forEach(function (C) {
							 | 
						||
| 
								 | 
							
								      var func = tracker[C]
							 | 
						||
| 
								 | 
							
								      tracker[C] = function () { return mixinLog(func.apply(tracker, arguments)) }
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return tracker
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Add tracker constructors to the top level log object
							 | 
						||
| 
								 | 
							
								trackerConstructors.forEach(function (C) {
							 | 
						||
| 
								 | 
							
								  log[C] = function () { return mixinLog(this.tracker[C].apply(this.tracker, arguments)) }
							 | 
						||
| 
								 | 
							
								})
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.clearProgress = function (cb) {
							 | 
						||
| 
								 | 
							
								  if (!this.progressEnabled) return cb && process.nextTick(cb)
							 | 
						||
| 
								 | 
							
								  this.gauge.hide(cb)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.showProgress = function (name, completed) {
							 | 
						||
| 
								 | 
							
								  if (!this.progressEnabled) return
							 | 
						||
| 
								 | 
							
								  var values = {}
							 | 
						||
| 
								 | 
							
								  if (name) values.section = name
							 | 
						||
| 
								 | 
							
								  var last = log.record[log.record.length - 1]
							 | 
						||
| 
								 | 
							
								  if (last) {
							 | 
						||
| 
								 | 
							
								    values.subsection = last.prefix
							 | 
						||
| 
								 | 
							
								    var disp = log.disp[last.level] || last.level
							 | 
						||
| 
								 | 
							
								    var logline = this._format(disp, log.style[last.level])
							 | 
						||
| 
								 | 
							
								    if (last.prefix) logline += ' ' + this._format(last.prefix, this.prefixStyle)
							 | 
						||
| 
								 | 
							
								    logline += ' ' + last.message.split(/\r?\n/)[0]
							 | 
						||
| 
								 | 
							
								    values.logline = logline
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  values.completed = completed || this.tracker.completed()
							 | 
						||
| 
								 | 
							
								  this.gauge.show(values)
							 | 
						||
| 
								 | 
							
								}.bind(log) // bind for use in tracker's on-change listener
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// temporarily stop emitting, but don't drop
							 | 
						||
| 
								 | 
							
								log.pause = function () {
							 | 
						||
| 
								 | 
							
								  this._paused = true
							 | 
						||
| 
								 | 
							
								  if (this.progressEnabled) this.gauge.disable()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.resume = function () {
							 | 
						||
| 
								 | 
							
								  if (!this._paused) return
							 | 
						||
| 
								 | 
							
								  this._paused = false
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var b = this._buffer
							 | 
						||
| 
								 | 
							
								  this._buffer = []
							 | 
						||
| 
								 | 
							
								  b.forEach(function (m) {
							 | 
						||
| 
								 | 
							
								    this.emitLog(m)
							 | 
						||
| 
								 | 
							
								  }, this)
							 | 
						||
| 
								 | 
							
								  if (this.progressEnabled) this.gauge.enable()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log._buffer = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var id = 0
							 | 
						||
| 
								 | 
							
								log.record = []
							 | 
						||
| 
								 | 
							
								log.maxRecordSize = 10000
							 | 
						||
| 
								 | 
							
								log.log = function (lvl, prefix, message) {
							 | 
						||
| 
								 | 
							
								  var l = this.levels[lvl]
							 | 
						||
| 
								 | 
							
								  if (l === undefined) {
							 | 
						||
| 
								 | 
							
								    return this.emit('error', new Error(util.format(
							 | 
						||
| 
								 | 
							
								      'Undefined log level: %j', lvl)))
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var a = new Array(arguments.length - 2)
							 | 
						||
| 
								 | 
							
								  var stack = null
							 | 
						||
| 
								 | 
							
								  for (var i = 2; i < arguments.length; i++) {
							 | 
						||
| 
								 | 
							
								    var arg = a[i - 2] = arguments[i]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // resolve stack traces to a plain string.
							 | 
						||
| 
								 | 
							
								    if (typeof arg === 'object' && arg &&
							 | 
						||
| 
								 | 
							
								        (arg instanceof Error) && arg.stack) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Object.defineProperty(arg, 'stack', {
							 | 
						||
| 
								 | 
							
								        value: stack = arg.stack + '',
							 | 
						||
| 
								 | 
							
								        enumerable: true,
							 | 
						||
| 
								 | 
							
								        writable: true
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (stack) a.unshift(stack + '\n')
							 | 
						||
| 
								 | 
							
								  message = util.format.apply(util, a)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var m = { id: id++,
							 | 
						||
| 
								 | 
							
								            level: lvl,
							 | 
						||
| 
								 | 
							
								            prefix: String(prefix || ''),
							 | 
						||
| 
								 | 
							
								            message: message,
							 | 
						||
| 
								 | 
							
								            messageRaw: a }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.emit('log', m)
							 | 
						||
| 
								 | 
							
								  this.emit('log.' + lvl, m)
							 | 
						||
| 
								 | 
							
								  if (m.prefix) this.emit(m.prefix, m)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.record.push(m)
							 | 
						||
| 
								 | 
							
								  var mrs = this.maxRecordSize
							 | 
						||
| 
								 | 
							
								  var n = this.record.length - mrs
							 | 
						||
| 
								 | 
							
								  if (n > mrs / 10) {
							 | 
						||
| 
								 | 
							
								    var newSize = Math.floor(mrs * 0.9)
							 | 
						||
| 
								 | 
							
								    this.record = this.record.slice(-1 * newSize)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.emitLog(m)
							 | 
						||
| 
								 | 
							
								}.bind(log)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.emitLog = function (m) {
							 | 
						||
| 
								 | 
							
								  if (this._paused) {
							 | 
						||
| 
								 | 
							
								    this._buffer.push(m)
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (this.progressEnabled) this.gauge.pulse(m.prefix)
							 | 
						||
| 
								 | 
							
								  var l = this.levels[m.level]
							 | 
						||
| 
								 | 
							
								  if (l === undefined) return
							 | 
						||
| 
								 | 
							
								  if (l < this.levels[this.level]) return
							 | 
						||
| 
								 | 
							
								  if (l > 0 && !isFinite(l)) return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // If 'disp' is null or undefined, use the lvl as a default
							 | 
						||
| 
								 | 
							
								  // Allows: '', 0 as valid disp
							 | 
						||
| 
								 | 
							
								  var disp = log.disp[m.level] != null ? log.disp[m.level] : m.level
							 | 
						||
| 
								 | 
							
								  this.clearProgress()
							 | 
						||
| 
								 | 
							
								  m.message.split(/\r?\n/).forEach(function (line) {
							 | 
						||
| 
								 | 
							
								    if (this.heading) {
							 | 
						||
| 
								 | 
							
								      this.write(this.heading, this.headingStyle)
							 | 
						||
| 
								 | 
							
								      this.write(' ')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    this.write(disp, log.style[m.level])
							 | 
						||
| 
								 | 
							
								    var p = m.prefix || ''
							 | 
						||
| 
								 | 
							
								    if (p) this.write(' ')
							 | 
						||
| 
								 | 
							
								    this.write(p, this.prefixStyle)
							 | 
						||
| 
								 | 
							
								    this.write(' ' + line + '\n')
							 | 
						||
| 
								 | 
							
								  }, this)
							 | 
						||
| 
								 | 
							
								  this.showProgress()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log._format = function (msg, style) {
							 | 
						||
| 
								 | 
							
								  if (!stream) return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var output = ''
							 | 
						||
| 
								 | 
							
								  if (this.useColor()) {
							 | 
						||
| 
								 | 
							
								    style = style || {}
							 | 
						||
| 
								 | 
							
								    var settings = []
							 | 
						||
| 
								 | 
							
								    if (style.fg) settings.push(style.fg)
							 | 
						||
| 
								 | 
							
								    if (style.bg) settings.push('bg' + style.bg[0].toUpperCase() + style.bg.slice(1))
							 | 
						||
| 
								 | 
							
								    if (style.bold) settings.push('bold')
							 | 
						||
| 
								 | 
							
								    if (style.underline) settings.push('underline')
							 | 
						||
| 
								 | 
							
								    if (style.inverse) settings.push('inverse')
							 | 
						||
| 
								 | 
							
								    if (settings.length) output += consoleControl.color(settings)
							 | 
						||
| 
								 | 
							
								    if (style.beep) output += consoleControl.beep()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  output += msg
							 | 
						||
| 
								 | 
							
								  if (this.useColor()) {
							 | 
						||
| 
								 | 
							
								    output += consoleControl.color('reset')
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return output
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.write = function (msg, style) {
							 | 
						||
| 
								 | 
							
								  if (!stream) return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  stream.write(this._format(msg, style))
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.addLevel = function (lvl, n, style, disp) {
							 | 
						||
| 
								 | 
							
								  // If 'disp' is null or undefined, use the lvl as a default
							 | 
						||
| 
								 | 
							
								  if (disp == null) disp = lvl
							 | 
						||
| 
								 | 
							
								  this.levels[lvl] = n
							 | 
						||
| 
								 | 
							
								  this.style[lvl] = style
							 | 
						||
| 
								 | 
							
								  if (!this[lvl]) {
							 | 
						||
| 
								 | 
							
								    this[lvl] = function () {
							 | 
						||
| 
								 | 
							
								      var a = new Array(arguments.length + 1)
							 | 
						||
| 
								 | 
							
								      a[0] = lvl
							 | 
						||
| 
								 | 
							
								      for (var i = 0; i < arguments.length; i++) {
							 | 
						||
| 
								 | 
							
								        a[i + 1] = arguments[i]
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return this.log.apply(this, a)
							 | 
						||
| 
								 | 
							
								    }.bind(this)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  this.disp[lvl] = disp
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.prefixStyle = { fg: 'magenta' }
							 | 
						||
| 
								 | 
							
								log.headingStyle = { fg: 'white', bg: 'black' }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								log.style = {}
							 | 
						||
| 
								 | 
							
								log.levels = {}
							 | 
						||
| 
								 | 
							
								log.disp = {}
							 | 
						||
| 
								 | 
							
								log.addLevel('silly', -Infinity, { inverse: true }, 'sill')
							 | 
						||
| 
								 | 
							
								log.addLevel('verbose', 1000, { fg: 'blue', bg: 'black' }, 'verb')
							 | 
						||
| 
								 | 
							
								log.addLevel('info', 2000, { fg: 'green' })
							 | 
						||
| 
								 | 
							
								log.addLevel('timing', 2500, { fg: 'green', bg: 'black' })
							 | 
						||
| 
								 | 
							
								log.addLevel('http', 3000, { fg: 'green', bg: 'black' })
							 | 
						||
| 
								 | 
							
								log.addLevel('notice', 3500, { fg: 'blue', bg: 'black' })
							 | 
						||
| 
								 | 
							
								log.addLevel('warn', 4000, { fg: 'black', bg: 'yellow' }, 'WARN')
							 | 
						||
| 
								 | 
							
								log.addLevel('error', 5000, { fg: 'red', bg: 'black' }, 'ERR!')
							 | 
						||
| 
								 | 
							
								log.addLevel('silent', Infinity)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// allow 'error' prefix
							 | 
						||
| 
								 | 
							
								log.on('error', function () {})
							 |