Skip to content

Commit 2b2a48b

Browse files
authored
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 2b2a48b

File tree

5 files changed

+149
-20
lines changed

5 files changed

+149
-20
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: 42 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,34 @@ 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+
// console.log is used so that logging does not depend on openNextDebug.
56+
return console.log(
57+
...args.map((arg) =>
58+
isOpenNextError(arg) ? `${arg.name}: ${arg.message}` : arg,
59+
),
60+
);
61+
}
5862
if (error.logLevel === 1) {
59-
warn(...args);
60-
return;
63+
// Display the name and the message instead of full Open Next errors.
64+
return warn(
65+
...args.map((arg) =>
66+
isOpenNextError(arg) ? `${arg.name}: ${arg.message}` : arg,
67+
),
68+
);
6169
}
62-
console.error(...args);
63-
return;
64-
} else {
65-
console.error(...args);
70+
return console.error(...args);
6671
}
72+
console.error(...args);
6773
}
6874

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

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+
}

packages/tests-unit/tests/adapters/cache.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ describe("S3Cache", () => {
7070
beforeEach(() => {
7171
vi.clearAllMocks();
7272

73-
cache = new S3Cache({
74-
_appDir: false,
75-
_requestHeaders: undefined as never,
76-
});
73+
cache = new S3Cache();
7774

7875
globalThis.disableIncrementalCache = false;
7976
globalThis.isNextAfter15 = false;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
debug.mockClear();
17+
warn.mockClear();
18+
error.mockClear();
19+
});
20+
21+
const ignorableError = new IgnorableError("ignorable");
22+
const recoverableError = new RecoverableError("recoverable");
23+
const fatalError = new FatalError("fatal");
24+
25+
it("default to warn when OPEN_NEXT_ERROR_LOG_LEVEL is undefined", () => {
26+
delete process.env.OPEN_NEXT_ERROR_LOG_LEVEL;
27+
logger.error(ignorableError);
28+
logger.error(recoverableError);
29+
logger.error(fatalError);
30+
expect(debug).not.toHaveBeenCalled();
31+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
32+
expect(error).toHaveBeenCalledWith(fatalError);
33+
});
34+
35+
it("OPEN_NEXT_ERROR_LOG_LEVEL is 'debug'/'0'", () => {
36+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "0";
37+
logger.error(ignorableError);
38+
logger.error(recoverableError);
39+
logger.error(fatalError);
40+
expect(debug).toHaveBeenCalledWith("IgnorableError: ignorable");
41+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
42+
expect(error).toHaveBeenCalledWith(fatalError);
43+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "debug";
44+
logger.error(ignorableError);
45+
logger.error(recoverableError);
46+
logger.error(fatalError);
47+
expect(debug).toHaveBeenCalledWith("IgnorableError: ignorable");
48+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
49+
expect(error).toHaveBeenCalledWith(fatalError);
50+
});
51+
52+
it("OPEN_NEXT_ERROR_LOG_LEVEL is 'warn'/'1'", () => {
53+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "1";
54+
logger.error(ignorableError);
55+
logger.error(recoverableError);
56+
logger.error(fatalError);
57+
expect(debug).not.toHaveBeenCalled();
58+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
59+
expect(error).toHaveBeenCalledWith(fatalError);
60+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "warn";
61+
logger.error(ignorableError);
62+
logger.error(recoverableError);
63+
logger.error(fatalError);
64+
expect(debug).not.toHaveBeenCalled();
65+
expect(warn).toHaveBeenCalledWith("RecoverableError: recoverable");
66+
expect(error).toHaveBeenCalledWith(fatalError);
67+
});
68+
69+
it("OPEN_NEXT_ERROR_LOG_LEVEL is 'error'/'2'", () => {
70+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "2";
71+
logger.error(ignorableError);
72+
logger.error(recoverableError);
73+
logger.error(fatalError);
74+
expect(debug).not.toHaveBeenCalled();
75+
expect(warn).not.toHaveBeenCalled();
76+
expect(error).toHaveBeenCalledWith(fatalError);
77+
process.env.OPEN_NEXT_ERROR_LOG_LEVEL = "error";
78+
logger.error(ignorableError);
79+
logger.error(recoverableError);
80+
logger.error(fatalError);
81+
expect(debug).not.toHaveBeenCalled();
82+
expect(warn).not.toHaveBeenCalled();
83+
expect(error).toHaveBeenCalledWith(fatalError);
84+
});
85+
});
86+
});

0 commit comments

Comments
 (0)