Skip to content

Commit a7f9090

Browse files
authored
fix: logger disables color based on NO_COLOR / etc (#414)
1 parent 6b73f33 commit a7f9090

File tree

2 files changed

+100
-7
lines changed

2 files changed

+100
-7
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {afterAll, beforeEach, describe, expect, it, jest} from "@jest/globals"
2+
import {Logger} from "./logger"
3+
4+
describe("Logger", () => {
5+
let sink: {info: jest.Mock; warn: jest.Mock; error: jest.Mock}
6+
7+
beforeEach(() => {
8+
sink = {
9+
info: jest.fn(),
10+
warn: jest.fn(),
11+
error: jest.fn(),
12+
}
13+
})
14+
15+
afterAll(() => {
16+
jest.restoreAllMocks()
17+
})
18+
19+
it("should include color escape sequences by default (assuming TTY)", () => {
20+
const logger = new Logger(true, undefined, sink)
21+
22+
logger.warn("test message")
23+
24+
expect(sink.warn).toHaveBeenCalledWith(
25+
expect.stringContaining("\x1b[33m[warn]\x1b[0m test message"),
26+
)
27+
})
28+
29+
it("should strip color escape sequences when NO_COLOR is set", () => {
30+
jest.replaceProperty(process, "env", {...process.env, NO_COLOR: "1"})
31+
const logger = new Logger(true, undefined, sink)
32+
33+
logger.warn("test message")
34+
35+
expect(sink.warn).toHaveBeenCalledWith("[warn] test message ")
36+
})
37+
38+
it("should strip color escape sequences when NODE_DISABLE_COLORS is set", () => {
39+
jest.replaceProperty(process, "env", {
40+
...process.env,
41+
NODE_DISABLE_COLORS: "1",
42+
})
43+
const logger = new Logger(true, undefined, sink)
44+
45+
logger.warn("test message")
46+
47+
expect(sink.warn).toHaveBeenCalledWith("[warn] test message ")
48+
})
49+
50+
it("should strip color escape sequences when TERM is dumb", () => {
51+
jest.replaceProperty(process, "env", {...process.env, TERM: "dumb"})
52+
const logger = new Logger(true, undefined, sink)
53+
54+
logger.warn("test message")
55+
56+
expect(sink.warn).toHaveBeenCalledWith("[warn] test message ")
57+
})
58+
59+
it("should strip color escape sequences when not a TTY", () => {
60+
const logger = new Logger(false, undefined, sink)
61+
62+
logger.warn("test message")
63+
64+
expect(sink.warn).toHaveBeenCalledWith("[warn] test message ")
65+
})
66+
})

packages/openapi-code-generator/src/core/logger.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import util from "node:util"
22

33
export type LoggerMeta = Record<string, unknown>
44

5-
// todo: respect NO_COLOR env var, tty, TERM, etc.
65
enum Color {
76
FgRed = "\x1b[31m",
87
FgYellow = "\x1b[33m",
@@ -15,25 +14,52 @@ const ConsoleSink = {
1514
error: (it: string) => console.info(it),
1615
}
1716

17+
const shouldColor = (isTTY: boolean) => {
18+
if (process.env.NO_COLOR) {
19+
return false
20+
}
21+
22+
if (process.env.NODE_DISABLE_COLORS) {
23+
return false
24+
}
25+
26+
if (process.env.TERM === "dumb") {
27+
return false
28+
}
29+
30+
return isTTY
31+
}
32+
1833
export class Logger {
1934
private readonly startTime = this.now()
2035
private readonly times: [string, bigint, ...bigint[]][] = []
2136

2237
constructor(
38+
private readonly isTTY: boolean,
2339
private readonly format = defaultFormat,
2440
private readonly sink = ConsoleSink,
2541
) {}
2642

2743
readonly info = (message: string, meta?: LoggerMeta): void => {
28-
this.sink.info(this.format("info", message, meta))
44+
this.sink.info(this.format("info", message, meta, shouldColor(this.isTTY)))
2945
}
3046

3147
readonly warn = (message: string, meta?: LoggerMeta): void => {
32-
this.sink.warn(this.format("warn", message, meta, Color.FgYellow))
48+
this.sink.warn(
49+
this.format(
50+
"warn",
51+
message,
52+
meta,
53+
shouldColor(this.isTTY),
54+
Color.FgYellow,
55+
),
56+
)
3357
}
3458

3559
readonly error = (message: string, meta?: LoggerMeta): void => {
36-
this.sink.error(this.format("error", message, meta, Color.FgRed))
60+
this.sink.error(
61+
this.format("error", message, meta, shouldColor(this.isTTY), Color.FgRed),
62+
)
3763
}
3864

3965
readonly time = (description: string): void => {
@@ -84,15 +110,16 @@ function defaultFormat(
84110
level: string,
85111
message: string,
86112
meta?: LoggerMeta,
113+
useColor: boolean = true,
87114
color = Color.Reset,
88115
) {
89-
return `${color}[${level}]${Color.Reset} ${message} ${
90-
meta ? util.inspect(meta, false, 3, false) : ""
116+
return `${useColor ? color : ""}[${level}]${useColor ? Color.Reset : ""} ${message} ${
117+
meta ? util.inspect(meta, false, 3, useColor) : ""
91118
}`
92119
}
93120

94121
function diff(start: bigint, end: bigint) {
95122
return Number((end - start) / BigInt(1000000))
96123
}
97124

98-
export const logger = new Logger()
125+
export const logger = new Logger(process.stdout.isTTY)

0 commit comments

Comments
 (0)