Skip to content

Commit 3db872a

Browse files
[v3 backport] Backport tail-based logging from #11135 and #11346 (#11138)
* Implement tail-based logging for realish preview (#11135) * Turn on tail logs by default (#11346) * Turn on tail logs by default * Isolate inspector changes to remote only * Create tricky-starfishes-tap.md * Isolate hotkeys * fix types * fix types * fix changeset --------- Co-authored-by: Somhairle MacLeòid <smacleod@cloudflare.com>
1 parent 02d2ea9 commit 3db872a

17 files changed

Lines changed: 256 additions & 97 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Implement tail-based logging for `wrangler dev` remote mode, behind the `--x-tail-tags` flag. This will become the default in the future.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
We're soon going to make backend changes that mean that `wrangler dev --remote` sessions will no longer have an associated inspector connection. In advance of these backend changes, we've enabled a new `wrangler tail`-based logging strategy for `wrangler dev --remote`. For now, you can revert to the previous logging strategy with `wrangler dev --remote --no-x-tail-logs`, but in future it will not be possible to revert.
6+
7+
The impact of this will be that logs that were previously available via devtools will now be provided directly to the Wrangler console and it will no longer be possible to interact with the remote Worker via the devtools console.

packages/wrangler/e2e/dev-with-resources.test.ts

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const inspectorPort = await getPort();
1313

1414
const RUNTIMES = [
1515
{ flags: "", runtime: "local" },
16-
{ flags: "--remote", runtime: "remote" },
16+
{ flags: "--remote --x-tail-logs", runtime: "remote" },
1717
] as const;
1818

1919
// WebAssembly module containing single `func add(i32, i32): i32` export.
@@ -47,6 +47,9 @@ describe.sequential.each(RUNTIMES)("Core: $flags", ({ runtime, flags }) => {
4747
name = "${workerName}"
4848
main = "src/index.ts"
4949
compatibility_date = "2023-01-01"
50+
# TODO: This is a workaround for an EWC bug where remote dev workers only log properly if they have bindings.
51+
# Remove the below line when MR:7727 is merged
52+
version_metadata = { binding = "METADATA" }
5053
`,
5154
"src/index.ts": dedent`
5255
export default {
@@ -79,8 +82,10 @@ describe.sequential.each(RUNTIMES)("Core: $flags", ({ runtime, flags }) => {
7982
expect(text).toContain("Error: monkey");
8083
expect(text).toContain("src/index.ts:7:10");
8184
}
82-
await worker.readUntil(/Error: monkey/, 30_000);
83-
await worker.readUntil(/src\/index\.ts:7:10/, 30_000);
85+
await worker.readUntil(/monkey/, 30_000);
86+
if (isLocal) {
87+
await worker.readUntil(/src\/index\.ts:7:10/, 30_000);
88+
}
8489
});
8590

8691
it("works with basic service worker", async () => {
@@ -89,6 +94,9 @@ describe.sequential.each(RUNTIMES)("Core: $flags", ({ runtime, flags }) => {
8994
name = "${workerName}"
9095
main = "src/index.ts"
9196
compatibility_date = "2023-01-01"
97+
# TODO: This is a workaround for an EWC bug where remote dev workers only log properly if they have bindings.
98+
# Remove the below line when MR:7727 is merged
99+
version_metadata = { binding = "METADATA" }
92100
`,
93101
"src/index.ts": dedent`
94102
addEventListener("fetch", (event) => {
@@ -118,8 +126,10 @@ describe.sequential.each(RUNTIMES)("Core: $flags", ({ runtime, flags }) => {
118126
expect(text).toContain("Error: monkey");
119127
expect(text).toContain("src/index.ts:6:9");
120128
}
121-
await worker.readUntil(/Error: monkey/, 30_000);
122-
await worker.readUntil(/src\/index\.ts:6:9/, 30_000);
129+
await worker.readUntil(/monkey/, 30_000);
130+
if (isLocal) {
131+
await worker.readUntil(/src\/index\.ts:6:9/, 30_000);
132+
}
123133
});
124134

125135
it.todo("workers with no bundle");
@@ -166,30 +176,33 @@ describe.sequential.each(RUNTIMES)("Core: $flags", ({ runtime, flags }) => {
166176
});
167177
});
168178

169-
it("starts inspector and allows debugging", async () => {
170-
await helper.seed({
171-
"wrangler.toml": dedent`
179+
it.skipIf(runtime === "remote")(
180+
"starts inspector and allows debugging",
181+
async () => {
182+
await helper.seed({
183+
"wrangler.toml": dedent`
172184
name = "${workerName}"
173185
main = "src/index.ts"
174186
compatibility_date = "2023-01-01"
175187
`,
176-
"src/index.ts": dedent`
188+
"src/index.ts": dedent`
177189
export default {
178190
fetch(request, env, ctx) { return new Response("body"); }
179191
}
180192
`,
181-
});
182-
const worker = helper.runLongLived(
183-
`wrangler dev ${flags} --port ${port} --inspector-port ${inspectorPort}`
184-
);
185-
await worker.waitForReady();
186-
const inspectorUrl = new URL(`ws://127.0.0.1:${inspectorPort}`);
187-
const ws = new WebSocket(inspectorUrl);
188-
await events.once(ws, "open");
189-
ws.close();
190-
// TODO(soon): once we have inspector proxy worker, write basic tests here,
191-
// messages currently too non-deterministic to do this reliably
192-
});
193+
});
194+
const worker = helper.runLongLived(
195+
`wrangler dev ${flags} --port ${port} --inspector-port ${inspectorPort}`
196+
);
197+
await worker.waitForReady();
198+
const inspectorUrl = new URL(`ws://127.0.0.1:${inspectorPort}`);
199+
const ws = new WebSocket(inspectorUrl);
200+
await events.once(ws, "open");
201+
ws.close();
202+
// TODO(soon): once we have inspector proxy worker, write basic tests here,
203+
// messages currently too non-deterministic to do this reliably
204+
}
205+
);
193206

194207
it("starts https server", async () => {
195208
await helper.seed({

packages/wrangler/e2e/startWorker.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ describe.each(OPTIONS)("DevEnv (remote: $remote)", ({ remote }) => {
108108
});
109109

110110
const inspectorUrl = await worker.inspectorUrl;
111+
assert(inspectorUrl, "missing inspectorUrl");
112+
111113
const res = await undici.fetch(`http://${inspectorUrl.host}/json`);
112114

113115
await expect(res.json()).resolves.toBeInstanceOf(Array);
@@ -177,6 +179,7 @@ describe.each(OPTIONS)("DevEnv (remote: $remote)", ({ remote }) => {
177179
});
178180

179181
const inspectorUrl = await worker.inspectorUrl;
182+
assert(inspectorUrl, "missing inspectorUrl");
180183

181184
let ws = new WebSocket(inspectorUrl.href, {
182185
setHost: false,
@@ -236,6 +239,8 @@ describe.each(OPTIONS)("DevEnv (remote: $remote)", ({ remote }) => {
236239
});
237240

238241
const inspectorUrl = await worker.inspectorUrl;
242+
assert(inspectorUrl, "missing inspectorUrl");
243+
239244
const ws = new WebSocket(inspectorUrl.href);
240245

241246
const consoleApiMessages: DevToolsEvent<"Runtime.consoleAPICalled">[] =

packages/wrangler/src/__tests__/api/startDevWorker/LocalRuntimeController.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ describe("LocalRuntimeController", () => {
534534
});
535535
const event = await waitForReloadComplete(controller);
536536
const url = urlFromParts(event.proxyData.userWorkerUrl);
537+
assert(event.proxyData.userWorkerInspectorUrl);
537538
const inspectorUrl = urlFromParts(event.proxyData.userWorkerInspectorUrl);
538539

539540
// Connect inspector WebSocket

packages/wrangler/src/api/dev.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ export async function unstable_dev(
224224
experimentalImagesLocalMode: imagesLocalMode ?? false,
225225
enableIpc: options?.experimental?.enableIpc,
226226
experimentalAssetsRpc: false,
227+
experimentalTailLogs: true,
227228
};
228229

229230
//outside of test mode, rebuilds work fine, but only one instance of wrangler will work at a time

packages/wrangler/src/api/startDevWorker/ConfigController.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ async function resolveConfig(
317317
metadata: input.unsafe?.metadata ?? unsafe?.metadata,
318318
},
319319
assets: assetsOptions,
320+
experimental: {
321+
tailLogs: !!input.experimental?.tailLogs,
322+
},
320323
} satisfies StartDevWorkerOptions;
321324

322325
if (resolved.legacy.legacyAssets && resolved.legacy.site) {

packages/wrangler/src/api/startDevWorker/ProxyController.ts

Lines changed: 74 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
6464

6565
const cert =
6666
this.latestConfig.dev?.server?.secure ||
67-
this.latestConfig.dev?.inspector?.secure
67+
(this.inspectorEnabled &&
68+
this.latestConfig.dev?.inspector &&
69+
this.latestConfig.dev?.inspector?.secure)
6870
? getHttpsOptions(
6971
this.latestConfig.dev.server?.httpsKeyPath,
7072
this.latestConfig.dev.server?.httpsCertPath
@@ -108,44 +110,6 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
108110
PROXY_CONTROLLER_AUTH_SECRET: this.secret,
109111
},
110112

111-
// no need to use file-system, so don't
112-
cache: false,
113-
unsafeEphemeralDurableObjects: true,
114-
},
115-
{
116-
name: "InspectorProxyWorker",
117-
compatibilityDate: "2023-12-18",
118-
compatibilityFlags: [
119-
"nodejs_compat",
120-
"increase_websocket_message_size",
121-
],
122-
modulesRoot: path.dirname(inspectorProxyWorkerPath),
123-
modules: [{ type: "ESModule", path: inspectorProxyWorkerPath }],
124-
durableObjects: {
125-
DURABLE_OBJECT: {
126-
className: "InspectorProxyWorker",
127-
unsafePreventEviction: true,
128-
},
129-
},
130-
serviceBindings: {
131-
PROXY_CONTROLLER: async (req): Promise<Response> => {
132-
const body =
133-
(await req.json()) as InspectorProxyWorkerOutgoingRequestBody;
134-
135-
return this.onInspectorProxyWorkerRequest(body);
136-
},
137-
},
138-
bindings: {
139-
PROXY_CONTROLLER_AUTH_SECRET: this.secret,
140-
},
141-
142-
unsafeDirectSockets: [
143-
{
144-
host: this.latestConfig.dev?.inspector?.hostname,
145-
port: this.latestConfig.dev?.inspector?.port ?? 0,
146-
},
147-
],
148-
149113
// no need to use file-system, so don't
150114
cache: false,
151115
unsafeEphemeralDurableObjects: true,
@@ -165,6 +129,48 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
165129
liveReload: false,
166130
};
167131

132+
if (this.inspectorEnabled) {
133+
assert(this.latestConfig.dev?.inspector);
134+
proxyWorkerOptions.workers.push({
135+
name: "InspectorProxyWorker",
136+
compatibilityDate: "2023-12-18",
137+
compatibilityFlags: [
138+
"nodejs_compat",
139+
"increase_websocket_message_size",
140+
],
141+
modulesRoot: path.dirname(inspectorProxyWorkerPath),
142+
modules: [{ type: "ESModule", path: inspectorProxyWorkerPath }],
143+
durableObjects: {
144+
DURABLE_OBJECT: {
145+
className: "InspectorProxyWorker",
146+
unsafePreventEviction: true,
147+
},
148+
},
149+
serviceBindings: {
150+
PROXY_CONTROLLER: async (req): Promise<Response> => {
151+
const body =
152+
(await req.json()) as InspectorProxyWorkerOutgoingRequestBody;
153+
154+
return this.onInspectorProxyWorkerRequest(body);
155+
},
156+
},
157+
bindings: {
158+
PROXY_CONTROLLER_AUTH_SECRET: this.secret,
159+
},
160+
161+
unsafeDirectSockets: [
162+
{
163+
host: this.latestConfig.dev?.inspector?.hostname,
164+
port: this.latestConfig.dev?.inspector?.port ?? 0,
165+
},
166+
],
167+
168+
// no need to use file-system, so don't
169+
cache: false,
170+
unsafeEphemeralDurableObjects: true,
171+
});
172+
}
173+
168174
const proxyWorkerOptionsChanged = didMiniflareOptionsChange(
169175
this.proxyWorkerOptions,
170176
proxyWorkerOptions
@@ -193,9 +199,14 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
193199
if (willInstantiateMiniflareInstance) {
194200
void Promise.all([
195201
proxyWorker.ready,
196-
proxyWorker.unsafeGetDirectURL("InspectorProxyWorker"),
202+
!this.inspectorEnabled
203+
? Promise.resolve(undefined)
204+
: proxyWorker.unsafeGetDirectURL("InspectorProxyWorker"),
197205
])
198206
.then(([url, inspectorUrl]) => {
207+
if (!this.inspectorEnabled) {
208+
return [url, undefined];
209+
}
199210
// Don't connect the inspector proxy worker until we have a valid ready Miniflare instance.
200211
// Otherwise, tearing down the ProxyController immediately after setting it up
201212
// will result in proxyWorker.ready throwing, but reconnectInspectorProxyWorker hanging for ever,
@@ -206,6 +217,7 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
206217
]);
207218
})
208219
.then(([url, inspectorUrl]) => {
220+
assert(url);
209221
this.emitReadyEvent(proxyWorker, url, inspectorUrl);
210222
})
211223
.catch((error) => {
@@ -367,6 +379,14 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
367379
}
368380
}
369381

382+
get inspectorEnabled() {
383+
if (this.latestConfig?.dev.remote) {
384+
// In `wrangler dev --remote`, only enable the inspector if the `--x-tail-logs` flag is disabled
385+
return !this.latestConfig?.experimental?.tailLogs;
386+
}
387+
return true;
388+
}
389+
370390
// ******************
371391
// Event Handlers
372392
// ******************
@@ -386,7 +406,9 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
386406
this.latestConfig = data.config;
387407

388408
void this.sendMessageToProxyWorker({ type: "pause" });
389-
void this.sendMessageToInspectorProxyWorker({ type: "reloadStart" });
409+
if (this.inspectorEnabled) {
410+
void this.sendMessageToInspectorProxyWorker({ type: "reloadStart" });
411+
}
390412
}
391413
onReloadComplete(data: ReloadCompleteEvent) {
392414
this.latestConfig = data.config;
@@ -397,10 +419,12 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
397419
proxyData: data.proxyData,
398420
});
399421

400-
void this.sendMessageToInspectorProxyWorker({
401-
type: "reloadComplete",
402-
proxyData: data.proxyData,
403-
});
422+
if (this.inspectorEnabled) {
423+
void this.sendMessageToInspectorProxyWorker({
424+
type: "reloadComplete",
425+
proxyData: data.proxyData,
426+
});
427+
}
404428
}
405429
onProxyWorkerMessage(message: ProxyWorkerOutgoingRequestBody) {
406430
switch (message.type) {
@@ -522,7 +546,11 @@ export class ProxyController extends Controller<ProxyControllerEventMap> {
522546
// Event Dispatchers
523547
// *********************
524548

525-
emitReadyEvent(proxyWorker: Miniflare, url: URL, inspectorUrl: URL) {
549+
emitReadyEvent(
550+
proxyWorker: Miniflare,
551+
url: URL,
552+
inspectorUrl: URL | undefined
553+
) {
526554
const data: ReadyEvent = {
527555
type: "ready",
528556
proxyWorker,

0 commit comments

Comments
 (0)