-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix wrangler dev logs being logged on the incorrect level in some cases #10004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| --- | ||
| "wrangler": patch | ||
| --- | ||
|
|
||
| fix `wrangler dev` logs being logged on the incorrect level in some cases | ||
|
|
||
| currently the way `wrangler dev` prints logs is faulty, for example the following code | ||
|
|
||
| ```js | ||
| console.error("this is an error"); | ||
| console.warn("this is a warning"); | ||
| console.debug("this is a debug"); | ||
| ``` | ||
|
|
||
| inside a worker would cause the following logs: | ||
|
|
||
| ```text | ||
| ✘ [ERROR] this is an error | ||
|
|
||
| ✘ [ERROR] this is a warning | ||
|
|
||
| this is a debug | ||
| ``` | ||
|
|
||
| (note that the warning is printed as an error and the debug log is printed even if by default it should not) | ||
|
|
||
| the changes here make sure that the logs are instead logged to their correct level, so for the code about the following will be logged instead: | ||
|
|
||
| ```text | ||
| ✘ [ERROR] this is an error | ||
|
|
||
| ▲ [WARNING] this is a warning | ||
| ``` | ||
|
|
||
| (running `wrangler dev` with the `--log-level=debug` flag will also cause the debug log to be included as well) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| "miniflare": minor | ||
| --- | ||
|
|
||
| add `structuredWorkerdLogs` option | ||
|
|
||
| add a new top-level option named `structuredWorkerdLogs` that makes workerd print to stdout structured logs (stringified jsons of the following shape: `{ timestamp: number, level: string, message: string }`) instead of printing logs to stdout and stderr |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| { | ||
| "name": "@fixture/worker-logs", | ||
| "private": true, | ||
| "scripts": { | ||
| "check:type": "tsc", | ||
| "dev": "pnpm dev.module", | ||
| "dev.module": "wrangler dev -c wrangler.module.jsonc", | ||
| "dev.service": "wrangler dev -c wrangler.service.jsonc", | ||
| "start": "pnpm dev.module", | ||
| "test:ci": "vitest run", | ||
| "test:watch": "vitest" | ||
| }, | ||
| "devDependencies": { | ||
| "@cloudflare/workers-tsconfig": "workspace:^", | ||
| "typescript": "catalog:default", | ||
| "vitest": "catalog:default", | ||
| "wrangler": "workspace:*" | ||
| }, | ||
| "volta": { | ||
| "extends": "../../package.json" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| export default { | ||
| async fetch(request, env) { | ||
| const response = new Response("Hello"); | ||
|
|
||
| const customMessage = request.headers.get("x-custom-message"); | ||
| if (customMessage) { | ||
| if (customMessage === "%__VERY_VERY_LONG_MESSAGE_%") { | ||
| // We can't simply pass a huge long message as a header thus | ||
| // why a placeholder is used here | ||
| console.log("z".repeat(2 ** 20)); | ||
| } else { | ||
| console.log(customMessage); | ||
| } | ||
| return response; | ||
| } | ||
|
|
||
| console.log("<<<<<this is a log>>>>>"); | ||
| console.warn("<<<<<this is a warning>>>>>"); | ||
| console.error("<<<<<this is an error>>>>>"); | ||
| console.debug("<<<<<this is a debug message>>>>>"); | ||
| console.info("<<<<<this is an info message>>>>>"); | ||
|
|
||
| return response; | ||
| }, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| async function handler(request) { | ||
| console.log("<<<<<this is a log>>>>>"); | ||
| console.warn("<<<<<this is a warning>>>>>"); | ||
| console.error("<<<<<this is an error>>>>>"); | ||
| console.debug("<<<<<this is a debug message>>>>>"); | ||
| console.info("<<<<<this is an info message>>>>>"); | ||
| return new Response("Hello"); | ||
| } | ||
|
|
||
| addEventListener("fetch", (event) => { | ||
| event.respondWith(handler(event.request)); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,273 @@ | ||
| import { setTimeout } from "node:timers/promises"; | ||
| import { resolve } from "path"; | ||
| import { describe, test } from "vitest"; | ||
| import { runWranglerDev } from "../../shared/src/run-wrangler-long-lived"; | ||
|
|
||
| async function getWranglerDevOutput( | ||
| type: "module" | "service", | ||
| extraArgs: string[] = [], | ||
| customMessage?: string, | ||
| requests = 1, | ||
| env = {} | ||
| ) { | ||
| const { ip, port, stop, getOutput } = await runWranglerDev( | ||
| resolve(__dirname, ".."), | ||
| [ | ||
| `-c=wrangler.${type}.jsonc`, | ||
| "--port=0", | ||
| "--inspector-port=0", | ||
| ...extraArgs, | ||
| ], | ||
| env | ||
| ); | ||
|
|
||
| const request = new Request(`http://${ip}:${port}`); | ||
| if (customMessage) { | ||
| request.headers.set("x-custom-message", customMessage); | ||
| } | ||
|
|
||
| for (let i = 0; i < requests; i++) { | ||
| const response = await fetch(request); | ||
| await response.text(); | ||
|
|
||
| // We wait for a bit for the output stream to be completely ready | ||
| // (this is a bit slow but it's generic to be used by all tests | ||
| // in this file, it also seems to make the tests very stable) | ||
| await setTimeout(500); | ||
| } | ||
|
|
||
| await stop(); | ||
|
|
||
| let output = getOutput(); | ||
|
|
||
| output = output | ||
| // Windows gets a different marker for ✘, so let's normalize it here | ||
| // so that these tests can be platform independent | ||
| .replaceAll("✘", "X") | ||
| // Let's also normalize Windows newlines | ||
| .replaceAll("\r\n", "\n"); | ||
|
|
||
| // Let's filter out lines we're not interested in | ||
| output = output | ||
| .split("\n") | ||
| .filter((line) => | ||
| logLineToIgnoreRegexps.every((regex) => !regex.test(line)) | ||
| ) | ||
| // let's also sort the logs for more stability of the tests, ideally | ||
| // we would want to test the log's ordering as well but that seems | ||
| // to cause flakes in the CI runs | ||
| .sort() | ||
| .join("\n"); | ||
|
|
||
| return output; | ||
| } | ||
|
|
||
| describe("'wrangler dev' correctly displays logs", () => { | ||
| describe("module workers", () => { | ||
| test("default behavior", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module"); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "[31mX [41;31m[[41;97mERROR[41;31m][0m [1m<<<<<this is an error>>>>>[0m | ||
| [33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1m<<<<<this is a warning>>>>>[0m | ||
| <<<<<this is a log>>>>> | ||
| <<<<<this is an info message>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=log", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", ["--log-level=log"]); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "[31mX [41;31m[[41;97mERROR[41;31m][0m [1m<<<<<this is an error>>>>>[0m | ||
| [33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1m<<<<<this is a warning>>>>>[0m | ||
| <<<<<this is a log>>>>> | ||
| <<<<<this is an info message>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=info", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", ["--log-level=info"]); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "[31mX [41;31m[[41;97mERROR[41;31m][0m [1m<<<<<this is an error>>>>>[0m | ||
| [33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1m<<<<<this is a warning>>>>>[0m | ||
| <<<<<this is an info message>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=warn", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", ["--log-level=warn"]); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "[31mX [41;31m[[41;97mERROR[41;31m][0m [1m<<<<<this is an error>>>>>[0m | ||
| [33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1m<<<<<this is a warning>>>>>[0m" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=error", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", [ | ||
| "--log-level=error", | ||
| ]); | ||
| expect(output).toMatchInlineSnapshot( | ||
| `"[31mX [41;31m[[41;97mERROR[41;31m][0m [1m<<<<<this is an error>>>>>[0m"` | ||
| ); | ||
| }); | ||
|
|
||
| test("with --log-level=debug", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput( | ||
| "module", | ||
| ["--log-level=debug"], | ||
| undefined, | ||
| // For some reason in debug mode two requests are | ||
| // needed to trigger the log here... | ||
| 2 | ||
| ); | ||
| expect(output).toContain("<<<<<this is a debug message>>>>>"); | ||
| }); | ||
|
|
||
| test('with WRANGLER_LOG="debug"', async ({ expect }) => { | ||
| const output = await getWranglerDevOutput( | ||
| "module", | ||
| [], | ||
| undefined, | ||
| // For some reason in debug mode two requests are | ||
| // needed to trigger the log here... | ||
| 2, | ||
| { WRANGLER_LOG: "debug" } | ||
| ); | ||
| expect(output).toContain("<<<<<this is a debug message>>>>>"); | ||
| }); | ||
|
|
||
| test("with --log-level=none", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", ["--log-level=none"]); | ||
| expect(output).toMatchInlineSnapshot(`""`); | ||
| }); | ||
dario-piotrowicz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // the workerd structured logs follow this structure: | ||
| // {"timestamp":<number>,"level":"<string>","message":"<string>"} | ||
| // the following tests check for edge case scenario where the following | ||
| // structure could not get detected correctly | ||
| describe("edge case scenarios", () => { | ||
| test("base case", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", [], "hello"); | ||
| expect(output).toMatchInlineSnapshot(`"hello"`); | ||
| }); | ||
| test("quotes in message", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", [], 'hel"lo'); | ||
| expect(output).toMatchInlineSnapshot(`"hel"lo"`); | ||
| }); | ||
|
|
||
| test("braces in message", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("module", [], "hel{}lo"); | ||
| expect(output).toMatchInlineSnapshot(`"hel{}lo"`); | ||
| }); | ||
|
|
||
| test("a workerd structured message in the message", async ({ | ||
| expect, | ||
| }) => { | ||
| const output = await getWranglerDevOutput( | ||
| "module", | ||
| [], | ||
| 'This is an example of a Workerd structured log: {"timestamp":1234567890,"level":"log","message":"Hello World!"}' | ||
| ); | ||
| expect(output).toMatchInlineSnapshot( | ||
| `"This is an example of a Workerd structured log: {"timestamp":1234567890,"level":"log","message":"Hello World!"}"` | ||
| ); | ||
| }); | ||
|
|
||
| test("a very very very long message (that gets split in multiple chunks)", async ({ | ||
| expect, | ||
| }) => { | ||
| const output = await getWranglerDevOutput( | ||
| "module", | ||
| [], | ||
| "%__VERY_VERY_LONG_MESSAGE_%" | ||
| ); | ||
| expect(output).toMatch(new RegExp(`^z{${2 ** 20}}$`)); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| // Note: service workers logs are handled differently from standard logs (and are built on top of | ||
| // inspector Runtime.consoleAPICalled events), they don't work as well as logs for module | ||
| // workers. Service workers are also deprecated so it's not a huge deal, the following | ||
| // tests are only here in place to make sure that the basic logging functionality of | ||
| // service workers does work | ||
| describe("service workers", () => { | ||
| test("default behavior", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service"); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "<<<<<this is a log>>>>> | ||
| <<<<<this is a warning>>>>> | ||
| <<<<<this is an error>>>>> | ||
| <<<<<this is an info message>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=log", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service", ["--log-level=log"]); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "<<<<<this is a log>>>>> | ||
| <<<<<this is a warning>>>>> | ||
| <<<<<this is an error>>>>> | ||
| <<<<<this is an info message>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=info", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service", [ | ||
| "--log-level=info", | ||
| ]); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "<<<<<this is a warning>>>>> | ||
| <<<<<this is an error>>>>> | ||
| <<<<<this is an info message>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=warn", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service", [ | ||
| "--log-level=warn", | ||
| ]); | ||
| expect(output).toMatchInlineSnapshot(` | ||
| "<<<<<this is a warning>>>>> | ||
| <<<<<this is an error>>>>>" | ||
| `); | ||
| }); | ||
|
|
||
| test("with --log-level=error", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service", [ | ||
| "--log-level=error", | ||
| ]); | ||
| expect(output).toMatchInlineSnapshot(`"<<<<<this is an error>>>>>"`); | ||
| }); | ||
|
|
||
| test("with --log-level=debug", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service", [ | ||
| "--log-level=debug", | ||
| ]); | ||
| expect(output).toContain("<<<<<this is a debug message>>>>>"); | ||
| }); | ||
|
|
||
| test("with --log-level=none", async ({ expect }) => { | ||
| const output = await getWranglerDevOutput("service", [ | ||
| "--log-level=none", | ||
| ]); | ||
| expect(output).toMatchInlineSnapshot(`""`); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| const logLineToIgnoreRegexps = [ | ||
| // let's skip empty lines | ||
| /^\s*$/, | ||
| // part of the wrangler banner | ||
| /⛅️ wrangler/, | ||
| // divisor after the wrangler banner | ||
| /^─+$/, | ||
| // wrangler logs such as ` ⎔ Starting local server...` | ||
| /^\s*⎔/, | ||
| // wrangler's ready on log | ||
| /^\[wrangler:info\] Ready on http:\/\/[^:]+:\d+$/, | ||
| // positive response to get request | ||
| /^\[wrangler:info\] GET \/ 200 OK \(\d+ms\)$/, | ||
| // let's skip the telemetry messages | ||
| /^Cloudflare collects anonymous telemetry about your usage of Wrangler\. Learn more at https:\/\/.*$/, | ||
| ]; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.