Skip to content

Commit

Permalink
Log cached fetches during HMR refreshes if enabled in logging config (#…
Browse files Browse the repository at this point in the history
…68287)

In #67925 we decided not to log any fetches during HMR refreshes (i.e.
when editing server components). With this PR, we introduce a new
logging config (`logging.fetches.hmrRefreshes`) so that users can opt in
to logging of `fetch` requests that are restored from the Server
Components HMR cache.

<img width="806" alt="hmr cache logs"
src="https://github.com/user-attachments/assets/ea789520-8490-479a-a4f0-1febb42388e2">
  • Loading branch information
unstubbable authored Jul 30, 2024
1 parent c039891 commit 9d691d0
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 12 deletions.
13 changes: 13 additions & 0 deletions docs/02-app/02-api-reference/05-next-config-js/logging.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ module.exports = {
}
```

{/* TODO: Cross-reference severComponentsHmrCache page when #67839 has landed. */}
Any `fetch` requests that are restored from the Server Components HMR cache are not logged by default. However, this can be enabled by setting `logging.fetches.hmrRefreshes` to `true`.

```js filename="next.config.js"
module.exports = {
logging: {
fetches: {
hmrRefreshes: true,
},
},
}
```

In addition, you can disable the development logging by setting `logging` to `false`.

```js filename="next.config.js"
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ export const configSchema: zod.ZodType<NextConfig> = z.lazy(() =>
fetches: z
.object({
fullUrl: z.boolean().optional(),
hmrRefreshes: z.boolean().optional(),
})
.optional(),
}),
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ export interface ReactCompilerOptions {
export interface LoggingConfig {
fetches?: {
fullUrl?: boolean
/**
* If true, fetch requests that are restored from the HMR cache are logged
* during an HMR refresh request, i.e. when editing a server component.
*/
hmrRefreshes?: boolean
}
}

Expand Down
18 changes: 12 additions & 6 deletions packages/next/src/server/dev/log-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ function logFetchMetric(
url,
} = fetchMetric

if (cacheStatus === 'hmr') {
// Cache hits during HMR refreshes are intentionally not logged.
if (cacheStatus === 'hmr' && !loggingConfig?.fetches?.hmrRefreshes) {
// Cache hits during HMR refreshes are intentionally not logged, unless
// explicitly enabled in the logging config.
return
}

Expand Down Expand Up @@ -138,8 +139,13 @@ function truncateUrl(url: string): string {
)
}

function formatCacheStatus(cacheStatus: 'hit' | 'miss' | 'skip'): string {
const color = cacheStatus === 'hit' ? green : yellow

return color(`(cache ${cacheStatus})`)
function formatCacheStatus(cacheStatus: FetchMetric['cacheStatus']): string {
switch (cacheStatus) {
case 'hmr':
return green('(HMR cache)')
case 'hit':
return green('(cache hit)')
default:
return yellow(`(cache ${cacheStatus})`)
}
}
2 changes: 1 addition & 1 deletion test/e2e/app-dir/logging/app/fetch-no-store/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export default async function Page() {
}
)

return <div>Hello World!</div>
return <h1>Hello World!</h1>
}
56 changes: 51 additions & 5 deletions test/e2e/app-dir/logging/fetch-logging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,23 +227,69 @@ describe('app-dir - logging', () => {
})

it('should not log requests for HMR refreshes', async () => {
const browser = await next.browser('/default-cache')
const browser = await next.browser('/fetch-no-store')
let headline = await browser.waitForElementByCss('h1').text()
expect(headline).toBe('Default Cache')
expect(headline).toBe('Hello World!')
const outputIndex = next.cliOutput.length

await next.patchFile(
'app/default-cache/page.js',
(content) => content.replace('Default Cache', 'Hello!'),
'app/fetch-no-store/page.js',
(content) => content.replace('Hello World!', 'Hello Test!'),
async () =>
retry(async () => {
headline = await browser.waitForElementByCss('h1').text()
expect(headline).toBe('Hello!')
expect(headline).toBe('Hello Test!')
const logs = stripAnsi(next.cliOutput.slice(outputIndex))
expect(logs).toInclude(' GET /fetch-no-store')
expect(logs).not.toInclude(` │ GET `)
})
)
})

describe('when logging.fetches.hmrRefreshes is true', () => {
beforeAll(async () => {
await next.patchFile('next.config.js', (content) =>
content.replace('// hmrRefreshes: true', 'hmrRefreshes: true')
)
})

afterAll(async () => {
await next.patchFile('next.config.js', (content) =>
content.replace('hmrRefreshes: true', '// hmrRefreshes: true')
)
})

it('should log requests for HMR refreshes', async () => {
const browser = await next.browser('/fetch-no-store')
let headline = await browser.waitForElementByCss('h1').text()
expect(headline).toBe('Hello World!')
const outputIndex = next.cliOutput.length

await next.patchFile(
'app/fetch-no-store/page.js',
(content) => content.replace('Hello World!', 'Hello Test!'),
async () => {
const expectedUrl = withFullUrlFetches
? 'https://next-data-api-endpoint.vercel.app/api/random'
: 'https://next-data-api-en../api/random'

return retry(async () => {
headline = await browser.waitForElementByCss('h1').text()
expect(headline).toBe('Hello Test!')

const logs = stripAnsi(
next.cliOutput.slice(outputIndex)
).replace(/\d+ms/g, '1ms')

expect(logs).toInclude(' GET /fetch-no-store')
expect(logs).toInclude(
` │ GET ${expectedUrl}?request-input 200 in 1ms (HMR cache)`
)
})
}
)
})
})
}
} else {
// No fetches logging enabled
Expand Down
1 change: 1 addition & 0 deletions test/e2e/app-dir/logging/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module.exports = {
logging: {
fetches: {
fullUrl: true,
// hmrRefreshes: true,
},
},
}

0 comments on commit 9d691d0

Please sign in to comment.