Skip to content

Commit 9dcd94e

Browse files
committed
feat: add network request testing functionality to DevLogDemo components
- Implemented network request testing in DevLogDemo for various frameworks (React, Vue, Nuxt, TanStack). - Added buttons to trigger network tests, including GET, POST, and handling of 404 errors. - Enhanced logging for network requests to provide feedback on success and errors. - Updated relevant configuration files to support network logging capabilities.
1 parent 2afac55 commit 9dcd94e

17 files changed

Lines changed: 645 additions & 47 deletions

File tree

example/next-app/app/DevLogDemo.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,31 @@ export default function DevLogDemo() {
2929
}
3030
};
3131

32+
const handleNetworkTest = async () => {
33+
console.log("🌐 Testing network requests...");
34+
35+
try {
36+
// Test successful fetch
37+
await fetch("https://jsonplaceholder.typicode.com/posts/1");
38+
console.log("✅ Successful fetch completed");
39+
40+
// Test POST request
41+
await fetch("https://jsonplaceholder.typicode.com/posts", {
42+
method: "POST",
43+
headers: { "Content-Type": "application/json" },
44+
body: JSON.stringify({ title: "Test", body: "Test body" })
45+
});
46+
console.log("✅ POST request completed");
47+
48+
// Test 404 error
49+
await fetch("https://jsonplaceholder.typicode.com/posts/999999");
50+
console.log("⚠️ 404 request completed");
51+
52+
} catch (error) {
53+
console.error("🌐 Network test error:", error);
54+
}
55+
};
56+
3257
return (
3358
<div className="p-6 my-8 bg-gray-50 dark:bg-gray-900 rounded-lg">
3459
<h2 className="text-2xl font-bold mb-4">Browser Echo Demo</h2>
@@ -52,6 +77,13 @@ export default function DevLogDemo() {
5277
Test Async Error
5378
</button>
5479

80+
<button
81+
onClick={handleNetworkTest}
82+
className="px-4 py-2 bg-indigo-500 text-white rounded hover:bg-indigo-600 transition-colors"
83+
>
84+
Test Network Requests
85+
</button>
86+
5587
<button
5688
onClick={() => {
5789
console.group("📂 Grouped logs");

example/nuxt-app/app/components/DevLogDemo.vue

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<button @click="testComplexObject">Test Complex Object</button>
1313
<button @click="testMultipleArgs">Test Multiple Arguments</button>
1414
<button @click="testBatchLogging">Test Batch Logging</button>
15+
<button @click="testNetworkRequests">Test Network Requests</button>
1516
</div>
1617

1718
<div class="info">
@@ -75,6 +76,31 @@ const testBatchLogging = () => {
7576
console.log('These logs might be batched together for efficiency');
7677
};
7778
79+
const testNetworkRequests = async () => {
80+
console.log('🌐 Testing network requests...');
81+
82+
try {
83+
// Test successful fetch
84+
await fetch('https://jsonplaceholder.typicode.com/posts/1');
85+
console.log('✅ Successful fetch completed');
86+
87+
// Test POST request
88+
await fetch('https://jsonplaceholder.typicode.com/posts', {
89+
method: 'POST',
90+
headers: { 'Content-Type': 'application/json' },
91+
body: JSON.stringify({ title: 'Test', body: 'Test body' })
92+
});
93+
console.log('✅ POST request completed');
94+
95+
// Test 404 error
96+
await fetch('https://jsonplaceholder.typicode.com/posts/999999');
97+
console.log('⚠️ 404 request completed');
98+
99+
} catch (error) {
100+
console.error('🌐 Network test error:', error);
101+
}
102+
};
103+
78104
// Log on component mount
79105
console.log('DevLogDemo component mounted in Nuxt app');
80106
</script>

example/nuxt-app/components/DevLogDemo.vue

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<button @click="testComplexObject">Test Complex Object</button>
1313
<button @click="testMultipleArgs">Test Multiple Arguments</button>
1414
<button @click="testBatchLogging">Test Batch Logging</button>
15+
<button @click="testNetworkRequests">Test Network Requests</button>
1516
</div>
1617

1718
<div class="info">
@@ -75,6 +76,31 @@ const testBatchLogging = () => {
7576
console.log('These logs might be batched together for efficiency');
7677
};
7778
79+
const testNetworkRequests = async () => {
80+
console.log('🌐 Testing network requests...');
81+
82+
try {
83+
// Test successful fetch
84+
await fetch('https://jsonplaceholder.typicode.com/posts/1');
85+
console.log('✅ Successful fetch completed');
86+
87+
// Test POST request
88+
await fetch('https://jsonplaceholder.typicode.com/posts', {
89+
method: 'POST',
90+
headers: { 'Content-Type': 'application/json' },
91+
body: JSON.stringify({ title: 'Test', body: 'Test body' })
92+
});
93+
console.log('✅ POST request completed');
94+
95+
// Test 404 error
96+
await fetch('https://jsonplaceholder.typicode.com/posts/999999');
97+
console.log('⚠️ 404 request completed');
98+
99+
} catch (error) {
100+
console.error('🌐 Network test error:', error);
101+
}
102+
};
103+
78104
// Log on component mount
79105
console.log('DevLogDemo component mounted in Nuxt app');
80106
</script>

example/react-vite-app/src/DevLogDemo.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,31 @@ export default function DevLogDemo() {
2828
}
2929
}
3030

31+
const handleNetworkTest = async () => {
32+
console.log('🌐 Testing network requests...')
33+
34+
try {
35+
// Test successful fetch
36+
await fetch('https://jsonplaceholder.typicode.com/posts/1')
37+
console.log('✅ Successful fetch completed')
38+
39+
// Test POST request
40+
await fetch('https://jsonplaceholder.typicode.com/posts', {
41+
method: 'POST',
42+
headers: { 'Content-Type': 'application/json' },
43+
body: JSON.stringify({ title: 'Test', body: 'Test body' })
44+
})
45+
console.log('✅ POST request completed')
46+
47+
// Test 404 error
48+
await fetch('https://jsonplaceholder.typicode.com/posts/999999')
49+
console.log('⚠️ 404 request completed')
50+
51+
} catch (error) {
52+
console.error('🌐 Network test error:', error)
53+
}
54+
}
55+
3156
return (
3257
<div className="demo-section">
3358
<h2>Browser Echo Demo</h2>
@@ -42,6 +67,10 @@ export default function DevLogDemo() {
4267
Test Async Error
4368
</button>
4469

70+
<button onClick={handleNetworkTest}>
71+
Test Network Requests
72+
</button>
73+
4574
<button onClick={() => {
4675
console.group('📂 Grouped logs')
4776
console.log('Message 1 in group')

example/tanstack-app/src/components/DevLogDemo.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@ export function DevLogDemo() {
1919
console.error(new Error('Boom from TanStack!'))
2020
}, [])
2121

22+
const testNetworkRequests = React.useCallback(async () => {
23+
console.log('🌐 Testing network requests...')
24+
25+
try {
26+
// Test successful fetch
27+
await fetch('https://jsonplaceholder.typicode.com/posts/1')
28+
console.log('✅ Successful fetch completed')
29+
30+
// Test POST request
31+
await fetch('https://jsonplaceholder.typicode.com/posts', {
32+
method: 'POST',
33+
headers: { 'Content-Type': 'application/json' },
34+
body: JSON.stringify({ title: 'Test', body: 'Test body' })
35+
})
36+
console.log('✅ POST request completed')
37+
38+
// Test 404 error
39+
await fetch('https://jsonplaceholder.typicode.com/posts/999999')
40+
console.log('⚠️ 404 request completed')
41+
42+
} catch (error) {
43+
console.error('🌐 Network test error:', error)
44+
}
45+
}, [])
46+
2247
React.useEffect(() => {
2348
emitAll()
2449
emitError()
@@ -38,6 +63,12 @@ export function DevLogDemo() {
3863
>
3964
Emit Error
4065
</button>
66+
<button
67+
onClick={testNetworkRequests}
68+
style={{ padding: '6px 10px', border: '1px solid #999', borderRadius: 6 }}
69+
>
70+
Test Network Requests
71+
</button>
4172
</div>
4273
)
4374
}

example/vue-vite-app/src/components/DevLogDemo.vue

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
Test Async Error
1313
</button>
1414

15+
<button @click="handleNetworkTest">
16+
Test Network Requests
17+
</button>
18+
1519
<button @click="handleGroupedLogs">
1620
Test Grouped Logs
1721
</button>
@@ -61,6 +65,31 @@ const handleAsyncError = async () => {
6165
}
6266
}
6367
68+
const handleNetworkTest = async () => {
69+
console.log('🌐 Testing network requests...')
70+
71+
try {
72+
// Test successful fetch
73+
await fetch('https://jsonplaceholder.typicode.com/posts/1')
74+
console.log('✅ Successful fetch completed')
75+
76+
// Test POST request
77+
await fetch('https://jsonplaceholder.typicode.com/posts', {
78+
method: 'POST',
79+
headers: { 'Content-Type': 'application/json' },
80+
body: JSON.stringify({ title: 'Test', body: 'Test body' })
81+
})
82+
console.log('✅ POST request completed')
83+
84+
// Test 404 error
85+
await fetch('https://jsonplaceholder.typicode.com/posts/999999')
86+
console.log('⚠️ 404 request completed')
87+
88+
} catch (error) {
89+
console.error('🌐 Network test error:', error)
90+
}
91+
}
92+
6493
const handleGroupedLogs = () => {
6594
console.group('📂 Grouped logs')
6695
console.log('Message 1 in group')

example/vue-vite-app/vite.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import browserEcho from '@browser-echo/vite'
66
export default defineConfig({
77
plugins: [vue(), browserEcho(
88
{
9-
stackMode: 'condensed'
9+
stackMode: 'condensed',
10+
network: { enabled: true }
1011
},
1112
)],
1213
})

packages/core/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This package provides the `initBrowserEcho` function that patches `console.*` me
1313
## Features
1414

1515
- Drop-in client patch that wraps `console.log/info/warn/error/debug`
16+
- Network request logging (fetch/XMLHttpRequest interception)
1617
- Batched HTTP requests (uses `sendBeacon` when available)
1718
- Source hints `(file:line:col)` + stack traces
1819
- Configurable log levels and batching
@@ -42,7 +43,8 @@ initBrowserEcho({
4243
preserveConsole: true,
4344
tag: '[browser]',
4445
batch: { size: 20, interval: 300 },
45-
stackMode: 'condensed'
46+
stackMode: 'condensed',
47+
network: { enabled: true, captureFetch: true, captureXmlHttpRequest: true }
4648
});
4749
```
4850

@@ -59,6 +61,12 @@ interface BrowserEchoOptions {
5961
include?: BrowserLogLevel[]; // default: ['log','info','warn','error','debug']
6062
preserveConsole?: boolean; // default: true (also keep logging in the browser)
6163
tag?: string; // default: '[browser]'
64+
// network logging
65+
network?: {
66+
enabled?: boolean; // default: true (captures network requests)
67+
captureFetch?: boolean; // default: true (intercepts fetch())
68+
captureXmlHttpRequest?: boolean; // default: true (intercepts XMLHttpRequest)
69+
};
6270
// stacks
6371
stackMode?: 'none' | 'condensed' | 'full'; // default: 'full' (provider-specific; Vite supports all)
6472
showSource?: boolean; // default: true (when available)

packages/core/src/client.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,98 @@ export function initBrowserEcho(opts: InitBrowserEchoOptions = {}) {
3838
ORIGINAL['info']?.(`${tag} forwarding console logs to ${route} (session ${session})`);
3939
} catch {}
4040

41+
// Network interception (fetch/XHR)
42+
try {
43+
const net = opts.network || {};
44+
const enableNetwork = typeof net.enabled === 'boolean' ? net.enabled : true;
45+
if (enableNetwork) {
46+
// Intercept fetch
47+
try {
48+
const captureFetch = typeof net.captureFetch === 'boolean' ? net.captureFetch : true;
49+
if (captureFetch && typeof (w.fetch) === 'function') {
50+
const originalFetch = w.fetch.bind(w);
51+
w.fetch = (async (...args: any[]) => {
52+
let method = 'GET';
53+
let url = '';
54+
try {
55+
const input = args[0];
56+
const init = args[1] || {};
57+
if (typeof input === 'string') url = input;
58+
else if (input && typeof input.url === 'string') url = input.url;
59+
if (init && init.method) method = String(init.method).toUpperCase();
60+
else if (input && input.method) method = String(input.method).toUpperCase();
61+
} catch {}
62+
const started = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
63+
try {
64+
const res = await originalFetch(...(args as any));
65+
const finished = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
66+
const data = {
67+
kind: 'network',
68+
transport: 'fetch',
69+
method,
70+
url,
71+
status: (res && typeof res.status === 'number') ? res.status : 0,
72+
ok: !!(res && (res as any).ok),
73+
ms: Math.round(finished - started)
74+
} as const;
75+
enqueue({ level: 'info', text: `NET ${JSON.stringify(data)}`, time: Date.now(), stack: '', source: url });
76+
return res;
77+
} catch (err) {
78+
const finished = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
79+
const data = { kind: 'network', transport: 'fetch', method, url, error: safeFormat(err), ms: Math.round(finished - started) } as const;
80+
enqueue({ level: 'error', text: `NET ${JSON.stringify(data)}`, time: Date.now(), stack: '', source: url });
81+
throw err;
82+
}
83+
}) as typeof w.fetch;
84+
}
85+
} catch {}
86+
87+
// Intercept XMLHttpRequest
88+
try {
89+
const captureXHR = typeof net.captureXmlHttpRequest === 'boolean' ? net.captureXmlHttpRequest : true;
90+
if (captureXHR && typeof (w.XMLHttpRequest) !== 'undefined') {
91+
const OrigXHR = w.XMLHttpRequest as any;
92+
function WrappedXHR(this: any) {
93+
const xhr = new OrigXHR();
94+
let method = 'GET';
95+
let url = '';
96+
let start = 0;
97+
const origOpen = xhr.open;
98+
xhr.open = function(m: string, u: string, ...rest: any[]) {
99+
try { method = (m || 'GET').toUpperCase(); url = u || ''; } catch {}
100+
return origOpen.call(xhr, m, u, ...rest);
101+
};
102+
const origSend = xhr.send;
103+
xhr.send = function(...sendArgs: any[]) {
104+
start = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
105+
try {
106+
// loadend fires for success and error
107+
xhr.addEventListener('loadend', function() {
108+
try {
109+
const end = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
110+
const status = Number(xhr.status || 0);
111+
const ok = status >= 200 && status < 400;
112+
const data = { kind: 'network', transport: 'xhr', method, url, status, ok, ms: Math.round(end - start) } as const;
113+
enqueue({ level: ok ? 'info' : 'error', text: `NET ${JSON.stringify(data)}`, time: Date.now(), stack: '', source: url });
114+
} catch {}
115+
}, { once: true });
116+
} catch {}
117+
try { return origSend.apply(xhr, sendArgs as any); }
118+
catch (err) {
119+
const end = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now();
120+
const data = { kind: 'network', transport: 'xhr', method, url, error: safeFormat(err), ms: Math.round(end - start) } as const;
121+
enqueue({ level: 'error', text: `NET ${JSON.stringify(data)}`, time: Date.now(), stack: '', source: url });
122+
throw err;
123+
}
124+
};
125+
return xhr;
126+
}
127+
(w as any).XMLHttpRequest = WrappedXHR;
128+
}
129+
} catch {}
130+
}
131+
} catch {}
132+
41133
function enqueue(entry: any) {
42134
queue.push(entry);
43135
if (queue.length >= batchSize) flush();

0 commit comments

Comments
 (0)