Skip to content

Commit

Permalink
Allow disabling HTTP request logs
Browse files Browse the repository at this point in the history
  • Loading branch information
mohsen1 committed Dec 28, 2024
1 parent b2c20c9 commit bc8aed5
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 6 deletions.
16 changes: 16 additions & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ export interface ReactCompilerOptions {
panicThreshold?: 'ALL_ERRORS' | 'CRITICAL_ERRORS' | 'NONE'
}

export interface IncomingRequestLoggingConfig {
/**
* A regular expression to match incoming requests that should not be logged.
* You can specify multiple patterns to match incoming requests that should not be logged.
* @default []
*/
ignorePattern?: RegExp[] | RegExp
}

export interface LoggingConfig {
fetches?: {
fullUrl?: boolean
Expand All @@ -232,6 +241,13 @@ export interface LoggingConfig {
*/
hmrRefreshes?: boolean
}

/**
* If set to false, incoming request logging is disabled.
* You can specify a pattern to match incoming requests that should not be logged.
* @default true
*/
incomingRequest?: boolean | IncomingRequestLoggingConfig
}

export interface ExperimentalConfig {
Expand Down
65 changes: 65 additions & 0 deletions packages/next/src/server/dev/log-requests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { shouldLogIncomingRequest } from './log-requests'
import type { NodeNextRequest } from '../base-http/node'
import type { LoggingConfig } from '../config-shared'

describe('shouldLogIncomingRequest', () => {
const createMockRequest = (url: string): NodeNextRequest => {
return { url } as NodeNextRequest
}

it('should return true when logging config is undefined', () => {
const req = createMockRequest('/api/test')
expect(shouldLogIncomingRequest(req, undefined)).toBe(true)
})

it('should return true when incomingRequest is true', () => {
const req = createMockRequest('/api/test')
const config: LoggingConfig = { incomingRequest: true }
expect(shouldLogIncomingRequest(req, config)).toBe(true)
})

it('should return false when incomingRequest is false', () => {
const req = createMockRequest('/api/test')
const config: LoggingConfig = { incomingRequest: false }
expect(shouldLogIncomingRequest(req, config)).toBe(false)
})

it('should return true when ignorePattern is empty', () => {
const req = createMockRequest('/api/test')
const config: LoggingConfig = {
incomingRequest: { ignorePattern: [] },
}
expect(shouldLogIncomingRequest(req, config)).toBe(true)
})

it('should filter requests matching ignorePattern', () => {
const req = createMockRequest('/api/health')
const config: LoggingConfig = {
incomingRequest: {
ignorePattern: [/^\/api\/health/],
},
}
expect(shouldLogIncomingRequest(req, config)).toBe(false)
})

it('should allow requests not matching ignorePattern', () => {
const req = createMockRequest('/api/users')
const config: LoggingConfig = {
incomingRequest: {
ignorePattern: [/^\/api\/health/, /^\/metrics/],
},
}
expect(shouldLogIncomingRequest(req, config)).toBe(true)
})

it('should return true when ignorePattern contains invalid patterns', () => {
const req = createMockRequest('/api/test')
const config: LoggingConfig = {
incomingRequest: {
// @ts-expect-error testing invalid pattern
ignorePattern: ['not-a-regex'],
},
}
expect(shouldLogIncomingRequest(req, config)).toBe(true)
})
})
50 changes: 44 additions & 6 deletions packages/next/src/server/dev/log-requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
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 type {
IncomingRequestLoggingConfig,
LoggingConfig,
} from '../config-shared'
import { getRequestMeta } from '../request-meta'

export interface RequestLoggingOptions {
Expand All @@ -20,14 +23,49 @@ export interface RequestLoggingOptions {
readonly requestDurationInMs: number
}

/**
* Returns true if the incoming request should be logged.
* @returns True if the incoming request should be logged, false otherwise.
*/
export function shouldLogIncomingRequest(
request: NodeNextRequest,
loggingConfig: LoggingConfig | undefined
): boolean {
if (typeof loggingConfig?.incomingRequest === 'boolean') {
return loggingConfig.incomingRequest
}

const ignorePattern = (
loggingConfig?.incomingRequest as IncomingRequestLoggingConfig
)?.ignorePattern

if (
!ignorePattern ||
!Array.isArray(ignorePattern) ||
ignorePattern.length === 0
) {
return true
}

// This warning should rarely happen due to the type checking
if (ignorePattern.some((pattern) => !(pattern instanceof RegExp))) {
writeLine(yellow('Warning: Invalid incomingRequest.ignorePattern!'))
return true
}

return !ignorePattern.some((pattern) => pattern.test(request.url))
}

export function logRequests(options: RequestLoggingOptions): void {
const { request, response, loggingConfig, requestDurationInMs } = options

logIncomingRequest({
request,
requestDurationInMs,
statusCode: response.statusCode,
})
if (shouldLogIncomingRequest(request, loggingConfig)) {
logIncomingRequest({
request,
requestDurationInMs,
statusCode: response.statusCode,
})
}

if (request.fetchMetrics) {
for (const fetchMetric of request.fetchMetrics) {
Expand Down

0 comments on commit bc8aed5

Please sign in to comment.