Skip to content

Commit 968b65f

Browse files
committed
ref(logger): read SENTRY_LOG_LEVEL lazily, not at module load time
Follow the same pattern as getSentryUrl() in oauth.ts — env vars are read at call time so tests can set them after import. - Replace getInitialLogLevel() (private, called at module load) with getEnvLogLevel() (exported, called lazily by bin.ts) - Logger always starts at DEFAULT_LOG_LEVEL (info=3); bin.ts applies env var first, then CLI flags override - Add getEnvLogLevel tests covering set/unset/empty env var paths - logger.ts now at 100% patch coverage
1 parent d0128b5 commit 968b65f

File tree

3 files changed

+67
-11
lines changed

3 files changed

+67
-11
lines changed

src/bin.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { buildContext } from "./context.js";
55
import { AuthError, formatError, getExitCode } from "./lib/errors.js";
66
import { error } from "./lib/formatters/colors.js";
77
import { runInteractiveLogin } from "./lib/interactive-login.js";
8-
import { extractLogLevelFromArgs, setLogLevel } from "./lib/logger.js";
8+
import {
9+
extractLogLevelFromArgs,
10+
getEnvLogLevel,
11+
setLogLevel,
12+
} from "./lib/logger.js";
913
import { withTelemetry } from "./lib/telemetry.js";
1014
import { startCleanupOldBinary } from "./lib/upgrade.js";
1115
import {
@@ -90,9 +94,17 @@ async function main(): Promise<void> {
9094

9195
const args = process.argv.slice(2);
9296

97+
// Apply SENTRY_LOG_LEVEL env var first (lazy read, not at module load time).
98+
// CLI flags below override this if present.
99+
const envLogLevel = getEnvLogLevel();
100+
if (envLogLevel !== null) {
101+
setLogLevel(envLogLevel);
102+
}
103+
93104
// Extract global log-level flags before Stricli parses args.
94105
// --log-level is consumed (removed); --verbose is read but left in place
95106
// because some commands (e.g., `api`) define their own --verbose flag.
107+
// CLI flags take priority over SENTRY_LOG_LEVEL env var.
96108
const logLevel = extractLogLevelFromArgs(args);
97109
if (logLevel !== null) {
98110
setLogLevel(logLevel);

src/lib/logger.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,19 @@ export function parseLogLevel(name: string): number {
103103
}
104104

105105
/**
106-
* Read the initial log level from the `SENTRY_LOG_LEVEL` environment variable.
106+
* Read the log level from `SENTRY_LOG_LEVEL`, called lazily by {@link setLogLevel}.
107107
*
108-
* @returns consola numeric level (defaults to 3 / info)
108+
* Following the same pattern as `getSentryUrl()` in oauth.ts — env vars are read
109+
* at call time (not module load time) so tests can set them after import.
110+
*
111+
* @returns consola numeric level, or null if not set
109112
*/
110-
function getInitialLogLevel(): number {
113+
export function getEnvLogLevel(): number | null {
111114
const envLevel = process.env[LOG_LEVEL_ENV_VAR];
112115
if (envLevel) {
113116
return parseLogLevel(envLevel);
114117
}
115-
return DEFAULT_LOG_LEVEL;
118+
return null;
116119
}
117120

118121
/**
@@ -124,9 +127,13 @@ function getInitialLogLevel(): number {
124127
*
125128
* The Sentry reporter is added lazily via {@link attachSentryReporter} after
126129
* Sentry.init() completes, since the reporter needs an active Sentry client.
130+
*
131+
* The initial level defaults to info (3). `SENTRY_LOG_LEVEL` is applied lazily
132+
* by bin.ts calling `setLogLevel(getEnvLogLevel())` — not at module load time —
133+
* so tests can override the env var after import.
127134
*/
128135
export const logger = createConsola({
129-
level: getInitialLogLevel(),
136+
level: DEFAULT_LOG_LEVEL,
130137
// stderr is the correct stream for diagnostic/log output in CLIs —
131138
// stdout is reserved for command output (data, JSON, tables).
132139
stderr: process.stderr,

test/lib/logger.test.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/**
22
* Unit tests for the logger module.
33
*
4-
* Tests parseLogLevel, extractLogLevelFromArgs, setLogLevel,
4+
* Tests parseLogLevel, getEnvLogLevel, extractLogLevelFromArgs, setLogLevel,
55
* attachSentryReporter, and the logger instance configuration.
66
*/
77

88
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
99
import {
1010
attachSentryReporter,
1111
extractLogLevelFromArgs,
12+
getEnvLogLevel,
1213
LOG_LEVEL_ENV_VAR,
1314
LOG_LEVEL_NAMES,
1415
logger,
@@ -73,6 +74,44 @@ describe("LOG_LEVEL_ENV_VAR", () => {
7374
});
7475
});
7576

77+
describe("getEnvLogLevel", () => {
78+
let savedEnv: string | undefined;
79+
80+
beforeEach(() => {
81+
savedEnv = process.env[LOG_LEVEL_ENV_VAR];
82+
});
83+
84+
afterEach(() => {
85+
if (savedEnv === undefined) {
86+
delete process.env[LOG_LEVEL_ENV_VAR];
87+
} else {
88+
process.env[LOG_LEVEL_ENV_VAR] = savedEnv;
89+
}
90+
});
91+
92+
test("returns null when env var is not set", () => {
93+
delete process.env[LOG_LEVEL_ENV_VAR];
94+
expect(getEnvLogLevel()).toBeNull();
95+
});
96+
97+
test("returns parsed level when env var is set", () => {
98+
process.env[LOG_LEVEL_ENV_VAR] = "debug";
99+
expect(getEnvLogLevel()).toBe(4);
100+
});
101+
102+
test("returns parsed level for each valid name", () => {
103+
for (const [idx, name] of LOG_LEVEL_NAMES.entries()) {
104+
process.env[LOG_LEVEL_ENV_VAR] = name;
105+
expect(getEnvLogLevel()).toBe(idx);
106+
}
107+
});
108+
109+
test("returns null for empty string", () => {
110+
process.env[LOG_LEVEL_ENV_VAR] = "";
111+
expect(getEnvLogLevel()).toBeNull();
112+
});
113+
});
114+
76115
describe("setLogLevel", () => {
77116
let originalLevel: number;
78117

@@ -209,10 +248,8 @@ describe("logger instance", () => {
209248
});
210249

211250
test("default level is info (3)", () => {
212-
// Unless SENTRY_LOG_LEVEL was set in the environment
213-
if (!process.env.SENTRY_LOG_LEVEL) {
214-
expect(logger.level).toBe(3);
215-
}
251+
// Logger always starts at info (3) — env var is applied lazily by bin.ts
252+
expect(logger.level).toBe(3);
216253
});
217254
});
218255

0 commit comments

Comments
 (0)