Skip to content

Commit d931173

Browse files
committed
feat: Add support for OPEN_NEXT_ERROR_LOG_LEVEL
OPEN_NEXT_ERROR_LOG_LEVEL is the minimal error level from which internal errors are logged. It can be set to - "0" / "debug" - "1" / "warn" (default) - "2" / "error"
1 parent 2202f36 commit d931173

File tree

4 files changed

+153
-16
lines changed

4 files changed

+153
-16
lines changed

.changeset/chilly-cycles-own.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@opennextjs/aws": minor
3+
---
4+
5+
feat: Add support for OPEN_NEXT_ERROR_LOG_LEVEL
6+
7+
OPEN_NEXT_ERROR_LOG_LEVEL is the minimal error level from which internal errors are logged.
8+
It can be set to:
9+
10+
- "0" / "debug"
11+
- "1" / "warn" (default)
12+
- "2" / "error"

packages/open-next/src/adapters/logger.ts

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { BaseOpenNextError } from "utils/error";
1+
import { type BaseOpenNextError, isOpenNextError } from "utils/error";
22

33
export function debug(...args: any[]) {
44
if (globalThis.openNextDebug) {
@@ -42,28 +42,33 @@ const isDownplayedErrorLog = (errorLog: AwsSdkClientCommandErrorLog) =>
4242

4343
export function error(...args: any[]) {
4444
// we try to catch errors from the aws-sdk client and downplay some of them
45-
if (
46-
args.some((arg: AwsSdkClientCommandErrorLog) => isDownplayedErrorLog(arg))
47-
) {
45+
if (args.some((arg) => isDownplayedErrorLog(arg))) {
4846
debug(...args);
49-
} else if (args.some((arg) => arg.__openNextInternal)) {
47+
} else if (args.some((arg) => isOpenNextError(arg))) {
5048
// In case of an internal error, we log it with the appropriate log level
51-
const error = args.find(
52-
(arg) => arg.__openNextInternal,
53-
) as BaseOpenNextError;
54-
if (error.logLevel === 0) {
55-
debug(...args);
49+
const error = args.find((arg) => isOpenNextError(arg))!;
50+
if (error.logLevel < getOpenNextErrorLogLevel()) {
5651
return;
5752
}
53+
if (error.logLevel === 0) {
54+
// Display the name and the message instead of full Open Next errors.
55+
return debug(
56+
...args.map((arg) =>
57+
isOpenNextError(arg) ? `${arg.name}: ${arg.message}` : arg,
58+
),
59+
);
60+
}
5861
if (error.logLevel === 1) {
59-
warn(...args);
60-
return;
62+
// Display the name and the message instead of full Open Next errors.
63+
return warn(
64+
...args.map((arg) =>
65+
isOpenNextError(arg) ? `${arg.name}: ${arg.message}` : arg,
66+
),
67+
);
6168
}
62-
console.error(...args);
63-
return;
64-
} else {
65-
console.error(...args);
69+
return console.error(...args);
6670
}
71+
console.error(...args);
6772
}
6873

6974
export const awsLogger = {
@@ -73,3 +78,23 @@ export const awsLogger = {
7378
warn,
7479
error,
7580
};
81+
82+
/**
83+
* Retrieves the log level for internal errors from the
84+
* OPEN_NEXT_ERROR_LOG_LEVEL environment variable.
85+
*
86+
* @returns The numerical log level 0 (debug), 1 (warn), or 2 (error)
87+
*/
88+
function getOpenNextErrorLogLevel(): number {
89+
const strLevel = process.env.OPEN_NEXT_ERROR_LOG_LEVEL ?? "1";
90+
switch (strLevel.toLowerCase()) {
91+
case "debug":
92+
case "0":
93+
return 0;
94+
case "error":
95+
case "2":
96+
return 2;
97+
default:
98+
return 1;
99+
}
100+
}

packages/open-next/src/utils/error.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,11 @@ export class FatalError extends Error implements BaseOpenNextError {
3939
this.name = "FatalError";
4040
}
4141
}
42+
43+
export function isOpenNextError(e: any): e is BaseOpenNextError & Error {
44+
try {
45+
return "__openNextInternal" in e;
46+
} catch {
47+
return false;
48+
}
49+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import * as logger from "@opennextjs/aws/adapters/logger.js";
2+
import {
3+
FatalError,
4+
IgnorableError,
5+
RecoverableError,
6+
} from "@opennextjs/aws/utils/error.js";
7+
import { vi } from "vitest";
8+
9+
describe("logger adapter", () => {
10+
describe("Open Next errors", () => {
11+
const debug = vi.spyOn(console, "log").mockImplementation(() => null);
12+
const warn = vi.spyOn(console, "warn").mockImplementation(() => null);
13+
const error = vi.spyOn(console, "error").mockImplementation(() => null);
14+
15+
beforeEach(() => {
16+
// Direct debug to console.log
17+
globalThis.openNextDebug = true;
18+
debug.mockClear();
19+
warn.mockClear();
20+
error.mockClear();
21+
});
22+
23+
afterAll(() => {
24+
globalThis.openNextDebug = false;
25+
});
26+
27+
const ignorableError = new IgnorableError("ignorable");
28+
const recoverableError = new RecoverableError("recoverable");
29+
const fatalError = new FatalError("fatal");
30+
31+
it("default to warn when OPEN_NEXT_ERROR_LOG_LEVEL is undefined", () => {
32+
delete process.env.OPEN_NEXT_ERROR_LOG_LEVEL;
33+
logger.error(ignorableError);
34+
logger.error(recoverableError);
35+
logger.error(fatalError);
36+
expect(debug).not.toHaveBeenCalled();
37+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
38+
expect(error).toHaveBeenCalledWith(fatalError);
39+
});
40+
41+
it("OPEN_NEXT_ERROR_LOG_LEVEL is 'debug'/'0'", () => {
42+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "0";
43+
logger.error(ignorableError);
44+
logger.error(recoverableError);
45+
logger.error(fatalError);
46+
expect(debug).toHaveBeenCalledWith("IgnorableError: ignorable");
47+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
48+
expect(error).toHaveBeenCalledWith(fatalError);
49+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "debug";
50+
logger.error(ignorableError);
51+
logger.error(recoverableError);
52+
logger.error(fatalError);
53+
expect(debug).toHaveBeenCalledWith("IgnorableError: ignorable");
54+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
55+
expect(error).toHaveBeenCalledWith(fatalError);
56+
});
57+
58+
it("OPEN_NEXT_ERROR_LOG_LEVEL is 'warn'/'1'", () => {
59+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "1";
60+
logger.error(ignorableError);
61+
logger.error(recoverableError);
62+
logger.error(fatalError);
63+
expect(debug).not.toHaveBeenCalled();
64+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
65+
expect(error).toHaveBeenCalledWith(fatalError);
66+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "warn";
67+
logger.error(ignorableError);
68+
logger.error(recoverableError);
69+
logger.error(fatalError);
70+
expect(debug).not.toHaveBeenCalled();
71+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
72+
expect(error).toHaveBeenCalledWith(fatalError);
73+
});
74+
75+
it("OPEN_NEXT_ERROR_LOG_LEVEL is 'error'/'2'", () => {
76+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "2";
77+
logger.error(ignorableError);
78+
logger.error(recoverableError);
79+
logger.error(fatalError);
80+
expect(debug).not.toHaveBeenCalled();
81+
expect(warn).not.toHaveBeenCalled();
82+
expect(error).toHaveBeenCalledWith(fatalError);
83+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "error";
84+
logger.error(ignorableError);
85+
logger.error(recoverableError);
86+
logger.error(fatalError);
87+
expect(debug).not.toHaveBeenCalled();
88+
expect(warn).not.toHaveBeenCalled();
89+
expect(error).toHaveBeenCalledWith(fatalError);
90+
});
91+
});
92+
});

0 commit comments

Comments
 (0)