| | import { hrtimeBigIntDurationToString } from '../../build/duration-to-string' |
| | import { |
| | blue, |
| | bold, |
| | gray, |
| | green, |
| | red, |
| | white, |
| | yellow, |
| | dim, |
| | } from '../../lib/picocolors' |
| | import { stripNextRscUnionQuery } from '../../lib/url' |
| | import type { FetchMetric } from '../base-http' |
| | import type { NodeNextRequest, NodeNextResponse } from '../base-http/node' |
| | import type { LoggingConfig } from '../config-shared' |
| | import { getRequestMeta } from '../request-meta' |
| |
|
| | |
| | |
| | |
| | export function ignoreLoggingIncomingRequests( |
| | request: NodeNextRequest, |
| | loggingConfig: LoggingConfig | undefined |
| | ): boolean { |
| | |
| | if (typeof loggingConfig?.incomingRequests === 'boolean') { |
| | return !loggingConfig.incomingRequests |
| | } |
| |
|
| | |
| | const ignore = loggingConfig?.incomingRequests?.ignore |
| |
|
| | |
| | if (!ignore) { |
| | return false |
| | } |
| |
|
| | |
| | return ignore.some((pattern) => pattern.test(request.url)) |
| | } |
| |
|
| | export function logRequests( |
| | request: NodeNextRequest, |
| | response: NodeNextResponse, |
| | loggingConfig: LoggingConfig, |
| | requestStartTime: bigint, |
| | requestEndTime: bigint, |
| | devRequestTimingMiddlewareStart: bigint | undefined, |
| | devRequestTimingMiddlewareEnd: bigint | undefined, |
| | devRequestTimingInternalsEnd: bigint | undefined, |
| | devGenerateStaticParamsDuration: bigint | undefined |
| | ): void { |
| | if (!ignoreLoggingIncomingRequests(request, loggingConfig)) { |
| | logIncomingRequests( |
| | request, |
| | requestStartTime, |
| | requestEndTime, |
| | response.statusCode, |
| | devRequestTimingMiddlewareStart, |
| | devRequestTimingMiddlewareEnd, |
| | devRequestTimingInternalsEnd, |
| | devGenerateStaticParamsDuration |
| | ) |
| | } |
| |
|
| | if (request.fetchMetrics) { |
| | for (const fetchMetric of request.fetchMetrics) { |
| | logFetchMetric(fetchMetric, loggingConfig) |
| | } |
| | } |
| | } |
| |
|
| | function logIncomingRequests( |
| | request: NodeNextRequest, |
| | requestStartTime: bigint, |
| | requestEndTime: bigint, |
| | statusCode: number, |
| | devRequestTimingMiddlewareStart: bigint | undefined, |
| | devRequestTimingMiddlewareEnd: bigint | undefined, |
| | devRequestTimingInternalsEnd: bigint | undefined, |
| | devGenerateStaticParamsDuration: bigint | undefined |
| | ): void { |
| | const isRSC = getRequestMeta(request, 'isRSCRequest') |
| | const url = isRSC ? stripNextRscUnionQuery(request.url) : request.url |
| |
|
| | const statusCodeColor = |
| | statusCode < 200 |
| | ? white |
| | : statusCode < 300 |
| | ? green |
| | : statusCode < 400 |
| | ? blue |
| | : statusCode < 500 |
| | ? yellow |
| | : red |
| |
|
| | const coloredStatus = statusCodeColor(statusCode.toString()) |
| |
|
| | const totalRequestTime = requestEndTime - requestStartTime |
| |
|
| | const times: Array<[label: string, time: bigint]> = [] |
| |
|
| | let middlewareTime: bigint | undefined |
| | if (devRequestTimingMiddlewareStart && devRequestTimingMiddlewareEnd) { |
| | middlewareTime = |
| | devRequestTimingMiddlewareEnd - devRequestTimingMiddlewareStart |
| | times.push(['proxy.ts', middlewareTime]) |
| | } |
| |
|
| | if (devRequestTimingInternalsEnd) { |
| | let frameworkTime = devRequestTimingInternalsEnd - requestStartTime |
| |
|
| | |
| | if (middlewareTime) { |
| | frameworkTime -= middlewareTime |
| | } |
| | |
| | times.unshift(['compile', frameworkTime]) |
| |
|
| | |
| | if (devGenerateStaticParamsDuration) { |
| | |
| | times.push(['generate-params', devGenerateStaticParamsDuration]) |
| | } |
| |
|
| | times.push(['render', requestEndTime - devRequestTimingInternalsEnd]) |
| | } |
| |
|
| | return writeLine( |
| | `${request.method} ${url} ${coloredStatus} in ${hrtimeBigIntDurationToString(totalRequestTime)}${times.length > 0 ? dim(` (${times.map(([label, time]) => `${label}: ${hrtimeBigIntDurationToString(time)}`).join(', ')})`) : ''}` |
| | ) |
| | } |
| |
|
| | function logFetchMetric( |
| | fetchMetric: FetchMetric, |
| | loggingConfig: LoggingConfig | undefined |
| | ): void { |
| | let { |
| | cacheReason, |
| | cacheStatus, |
| | cacheWarning, |
| | end, |
| | method, |
| | start, |
| | status, |
| | url, |
| | } = fetchMetric |
| |
|
| | if (cacheStatus === 'hmr' && !loggingConfig?.fetches?.hmrRefreshes) { |
| | |
| | |
| | return |
| | } |
| |
|
| | if (loggingConfig?.fetches) { |
| | if (url.length > 48 && !loggingConfig.fetches.fullUrl) { |
| | url = truncateUrl(url) |
| | } |
| |
|
| | writeLine( |
| | white( |
| | `${method} ${url} ${status} in ${Math.round(end - start)}ms ${formatCacheStatus(cacheStatus)}` |
| | ), |
| | 1 |
| | ) |
| |
|
| | if (cacheStatus === 'skip' || cacheStatus === 'miss') { |
| | writeLine( |
| | gray( |
| | `Cache ${cacheStatus === 'skip' ? 'skipped' : 'missed'} reason: (${white(cacheReason)})` |
| | ), |
| | 2 |
| | ) |
| | } |
| | } else if (cacheWarning) { |
| | |
| | |
| | writeLine(white(`${method} ${url}`), 1) |
| | } |
| |
|
| | if (cacheWarning) { |
| | writeLine(`${yellow(bold('⚠'))} ${white(cacheWarning)}`, 2) |
| | } |
| | } |
| |
|
| | function writeLine(text: string, indentationLevel = 0): void { |
| | process.stdout.write(` ${'│ '.repeat(indentationLevel)}${text}\n`) |
| | } |
| |
|
| | function truncate(text: string, maxLength: number): string { |
| | return maxLength !== undefined && text.length > maxLength |
| | ? text.substring(0, maxLength) + '..' |
| | : text |
| | } |
| |
|
| | function truncateUrl(url: string): string { |
| | const { protocol, host, pathname, search } = new URL(url) |
| |
|
| | return ( |
| | protocol + |
| | '//' + |
| | truncate(host, 16) + |
| | truncate(pathname, 24) + |
| | truncate(search, 16) |
| | ) |
| | } |
| |
|
| | function formatCacheStatus(cacheStatus: FetchMetric['cacheStatus']): string { |
| | switch (cacheStatus) { |
| | case 'hmr': |
| | return green('(HMR cache)') |
| | case 'hit': |
| | return green('(cache hit)') |
| | case 'miss': |
| | case 'skip': |
| | return yellow(`(cache ${cacheStatus})`) |
| | default: |
| | return cacheStatus satisfies never |
| | } |
| | } |
| |
|