Skip to content

Commit 42fa17e

Browse files
committed
fix: better handling of streams not from logger
1 parent c795235 commit 42fa17e

File tree

3 files changed

+52
-28
lines changed

3 files changed

+52
-28
lines changed

src/logger/renderers.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as logSymbols from "log-symbols"
1010
import * as nodeEmoji from "node-emoji"
1111
import * as yaml from "js-yaml"
1212
import chalk from "chalk"
13-
import { curryRight, flow, padEnd, padStart } from "lodash"
13+
import { curryRight, flow, isArray, padEnd, padStart } from "lodash"
1414
import hasAnsi = require("has-ansi")
1515

1616
import { duration } from "./util"
@@ -30,7 +30,7 @@ const truncate = (s: string) => s.length > sectionPrefixWidth
3030
: s
3131
const sectionStyle = (s: string) => chalk.cyan.italic(padEnd(truncate(s), sectionPrefixWidth))
3232
const msgStyle = (s: string) => hasAnsi(s) ? s : chalk.gray(s)
33-
const errorStyle = (s: string) => hasAnsi(s) ? s : chalk.red(s)
33+
const errorStyle = chalk.red
3434

3535
/*** RENDER HELPERS ***/
3636
function insertVal(out: string[], idx: number, toRender: Function | string, renderArgs: any[]): string[] {
@@ -88,9 +88,14 @@ export function renderSymbol(entry: LogEntry): string {
8888
}
8989

9090
export function renderMsg(entry: LogEntry): string {
91-
const { entryStyle, msg } = entry.opts
91+
const { entryStyle, msg, notOriginatedFromLogger } = entry.opts
92+
93+
if (notOriginatedFromLogger) {
94+
return isArray(msg) ? msg.join(" ") : msg || ""
95+
}
96+
9297
const styleFn = entryStyle === EntryStyle.error ? errorStyle : msgStyle
93-
if (msg && msg instanceof Array) {
98+
if (isArray(msg)) {
9499
return msg.map(styleFn).join(chalk.gray(" → "))
95100
}
96101
return msg ? styleFn(msg) : ""

src/logger/util.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,14 @@ interface StreamWriteExtraParam {
6565
noIntercept?: boolean
6666
}
6767

68-
// Intercepts the write method of a WriteableStream and calls the provided callback on the
69-
// string to write (or optionally applies the string to the write method)
70-
// Returns a function which sets the write back to default.
71-
//
72-
// Used e.g. by FancyLogger so that writes from other sources can be intercepted
73-
// and pushed to the log stack.
68+
/**
69+
* Intercepts the write method of a WriteableStream and calls the provided callback on the
70+
* string to write (or optionally applies the string to the write method)
71+
* Returns a function which sets the write back to default.
72+
*
73+
* Used e.g. by FancyLogger so that writes from other sources can be intercepted
74+
* and pushed to the log stack.
75+
*/
7476
export function interceptStream(stream: NodeJS.WritableStream, callback: Function) {
7577
const prevWrite = stream.write
7678

src/logger/writers.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export class FancyConsoleWriter extends Writer {
165165
private formattedEntries: string[]
166166
private logUpdate: any
167167
private intervalID: number | null
168+
public persistedAtIdx: number
168169

169170
public level: LogLevel
170171

@@ -173,6 +174,7 @@ export class FancyConsoleWriter extends Writer {
173174
this.intervalID = null
174175
this.formattedEntries = [] // Entries are cached on format
175176
this.spinners = [] // Each entry has it's own spinner
177+
this.persistedAtIdx = 0
176178
}
177179

178180
private initLogUpdate(rootLogNode: RootLogNode): any {
@@ -182,18 +184,17 @@ export class FancyConsoleWriter extends Writer {
182184
write: (str, enc, cb) => (<any>process.stdout.write)(str, enc, cb, { noIntercept: true }),
183185
}
184186
const makeOpts = msg => ({
185-
// Remove trailing new line from console writes since Logger already handles it
186-
msg: typeof msg === "string" ? msg.replace(/\n$/, "") : msg,
187+
msg,
187188
notOriginatedFromLogger: true,
188189
})
189-
/*
190-
NOTE: On every write, log-update library calls the cli-cursor library to hide the cursor
191-
which the cli-cursor library does via stderr write. This causes an infinite loop as
192-
the stderr writes are intercepted and funneled back to the Logger.
193-
Therefore we manually toggle the cursor using the custom stream from above.
194-
195-
log-update types are missing the `opts?: {showCursor?: boolean}` parameter
196-
*/
190+
/**
191+
* NOTE: On every write, log-update library calls the cli-cursor library to hide the cursor
192+
* which the cli-cursor library does via stderr write. This causes an infinite loop as
193+
* the stderr writes are intercepted and funneled back to the Logger.
194+
* Therefore we manually toggle the cursor using the custom stream from above.
195+
*
196+
* log-update types are missing the `opts?: {showCursor?: boolean}` parameter
197+
*/
197198
const customLogUpdate = (<any>logUpdate.create)(<any>stream, { showCursor: true })
198199
cliCursor.hide(stream)
199200

@@ -241,7 +242,7 @@ export class FancyConsoleWriter extends Writer {
241242
private updateStream(rootLogNode: RootLogNode): void {
242243
const out = this.render(rootLogNode)
243244
if (out) {
244-
this.logUpdate(out.join("\n"))
245+
this.logUpdate(out.join(""))
245246
}
246247
}
247248

@@ -254,13 +255,19 @@ export class FancyConsoleWriter extends Writer {
254255
const level = this.level || rootLogNode.level
255256
const entries = <any>getChildNodes(rootLogNode)
256257

257-
/*
258-
This is a bit ugly for performance sake.
259-
Rather than just creating a new string with an updated spinner frame in each render cycle
260-
we instead cache the formatted string and splice the updated frame into it.
261-
*/
262-
const out = entries.reduce((acc: string[], entry: LogEntry, idx: number): string[] => {
258+
/**
259+
* This is a bit ugly for performance sake.
260+
* Rather than just creating a new string with an updated spinner frame in each render cycle
261+
* we instead cache the formatted string and splice the updated frame into it.
262+
*/
263+
const out = entries.slice(this.persistedAtIdx).reduce((acc: string[], entry: LogEntry, idx: number): string[] => {
263264
let spinnerFrame = ""
265+
266+
if (entry.notOriginatedFromLogger()) {
267+
acc.push(renderMsg(entry))
268+
return acc
269+
}
270+
264271
if (entry.status === EntryStatus.ACTIVE) {
265272
hasActiveEntries = true
266273
spinnerFrame = this.readOrSetSpinner(idx)
@@ -271,7 +278,7 @@ export class FancyConsoleWriter extends Writer {
271278
const withSpinner = spinnerFrame
272279
? `${formatted.slice(0, startPos)}${spinnerStyle(spinnerFrame)} ${formatted.slice(startPos)}`
273280
: formatted
274-
acc.push(withSpinner)
281+
acc.push(withSpinner + "\n")
275282
}
276283
return acc
277284
}, [])
@@ -300,6 +307,16 @@ export class FancyConsoleWriter extends Writer {
300307
public stop(): void {
301308
this.stopLoop()
302309
this.logUpdate && this.logUpdate.cleanUp()
310+
this.logUpdate = null
311+
}
312+
313+
/**
314+
* Escape hatch for reclaiming the stream, e.g. when reading stdin.
315+
* Logger will then continue afterwards but won't be able to update the previous content
316+
*/
317+
public stopAndPersist(rootLogNode: RootLogNode): void {
318+
this.stop()
319+
this.persistedAtIdx = rootLogNode.children.length
303320
}
304321

305322
}

0 commit comments

Comments
 (0)