Realtime In-Place Logger
Useful for analysis of data that's changing in real time. Prints properties of interest in-place to the terminal, preventing the need to keep up with quickly scrolling log lines.
See also: the same code in Python.
const clearLines = (lines) => {
for (let i = 0; i < lines; i++) {
process.stdout.write('\x1b[2K') // Clear the current line
process.stdout.write('\x1b[A') // Move cursor up one line
}
process.stdout.write('\x1b[2K') // Clear the previous line
}
class Singleton {
constructor() {
if (this.constructor.instance) {
return this.constructor.instance
}
this.constructor.instance = this
}
}
class RealtimeLogger extends Singleton {
constructor(numProperties) {
super()
if (!this._data) {
this.numProperties = numProperties
this.hasPrinted = false
this._data = {}
}
}
get(key) {
return this._data[key]
}
set(key, value) {
this._data[key] = value
}
print() {
const keys = Object.keys(this._data)
if (keys.length !== this.numProperties) {
console.warn(`Warning: Set num_properties to ${keys.length}`)
}
if (this.hasPrinted) {
clearLines(this.numProperties)
}
for (let i = 0; i < this.numProperties; i++) {
const k = keys[i]
console.log(`${k}: ${this._data[k]}`)
}
this.hasPrinted = true
}
}
Example
const viz = new RealtimeLogger(2)
viz.set('my variable', 42)
viz.set('Something else', 50)
viz.print()
for (let i = 0; i < 6; i++) {
viz.set('my variable', i)
viz.print()
}
Prints the following. Notice that, despite the fact that print()
is invoked 6 times, there are only two log lines. This is because viz
only has 2 properties, and they're printed in-place upon each invocation.
my variable: 5
Something else: 50