Skip to content

Commit

Permalink
feat: customize log levels and downgrade common errors to info (#9156)
Browse files Browse the repository at this point in the history
### What?

Allows configuration of the log level based on the error being thrown
and also downgrades common errors to be info instead of error by
default.

### Why?

Currently all errors result in logger.error being called which can
polute the logs with junk that is normal and doesn't need attention.

### How?

Adds a config property called `loggingLevels` that is used to override
the default log levels based on the name of the error being thrown.
Sanitize config will provide the defaulted 'info' level errors which can
be overriden in the config.

Before
![Screenshot 2024-11-12
144459](https://github.com/user-attachments/assets/47318329-23b7-4627-afc4-a0bcf4dc3d58)

After

![image](https://github.com/user-attachments/assets/85b06be4-0ab8-4ca2-b237-d6a4d54add3a)
  • Loading branch information
DanRibbens authored Nov 13, 2024
1 parent f264c80 commit d628222
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/configuration/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ The following options are available:
| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |
| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |
| **`logger`** | Logger options, logger options with a destination stream, or an instantiated logger instance. [More details](https://getpino.io/#/docs/api?id=options). |
| **`loggingLevels`** | An object to override the level to use in the logger for Payload's errors. |
| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |
| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |
| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/routes/rest/routeError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ export const routeError = async ({

let status = err.status || httpStatus.INTERNAL_SERVER_ERROR

logger.error(err.stack)
const level = payload.config.loggingLevels[err.name] ?? 'error'
if (level) {
logger[level](level === 'info' ? { msg: err.message } : { err })
}

// Internal server errors can contain anything, including potentially sensitive data.
// Therefore, error details will be hidden from the response unless `config.debug` is `true`
Expand Down
10 changes: 10 additions & 0 deletions packages/payload/src/config/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ import { defaults } from './defaults.js'
const sanitizeAdminConfig = (configToSanitize: Config): Partial<SanitizedConfig> => {
const sanitizedConfig = { ...configToSanitize }

// default logging level will be 'error' if not provided
sanitizedConfig.loggingLevels = {
Forbidden: 'info',
Locked: 'info',
MissingFile: 'info',
NotFound: 'info',
ValidationError: 'info',
...(sanitizedConfig.loggingLevels || {}),
}

// add default user collection if none provided
if (!sanitizedConfig?.admin?.user) {
const firstCollectionWithAuth = sanitizedConfig.collections.find(({ auth }) => Boolean(auth))
Expand Down
25 changes: 24 additions & 1 deletion packages/payload/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { BusboyConfig } from 'busboy'
import type GraphQL from 'graphql'
import type { GraphQLFormattedError } from 'graphql'
import type { JSONSchema4 } from 'json-schema'
import type { DestinationStream, pino } from 'pino'
import type { DestinationStream, Level, pino } from 'pino'
import type React from 'react'
import type { default as sharp } from 'sharp'
import type { DeepRequired } from 'ts-essentials'
Expand All @@ -34,6 +34,7 @@ import type {
} from '../collections/config/types.js'
import type { DatabaseAdapterResult } from '../database/types.js'
import type { EmailAdapter, SendEmailOptions } from '../email/types.js'
import type { ErrorName } from '../errors/types.js'
import type { GlobalConfig, Globals, SanitizedGlobalConfig } from '../globals/config/types.js'
import type { JobsConfig, Payload, RequestContext, TypedUser } from '../index.js'
import type { PayloadRequest, Where } from '../types/index.js'
Expand Down Expand Up @@ -980,6 +981,28 @@ export type Config = {
*/
logger?: 'sync' | { destination?: DestinationStream; options: pino.LoggerOptions } | PayloadLogger

/**
* Override the log level of errors for Payload's error handler or disable logging with `false`.
* Levels can be any of the following: 'trace', 'debug', 'info', 'warn', 'error', 'fatal' or false.
*
* Default levels:
* {
`* APIError: 'error',
`* AuthenticationError: 'error',
`* ErrorDeletingFile: 'error',
`* FileRetrievalError: 'error',
`* FileUploadError: 'error',
`* Forbidden: 'info',
`* Locked: 'info',
`* LockedAuth: 'error',
`* MissingFile: 'info',
`* NotFound: 'info',
`* QueryError: 'error',
`* ValidationError: 'info',
* }
*/
loggingLevels?: Partial<Record<ErrorName, false | Level>>

/**
* The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries.
*
Expand Down
17 changes: 17 additions & 0 deletions packages/payload/src/errors/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
export * from './index.js'

/**
* Error names that can be thrown by Payload during runtime
*/
export type ErrorName =
| 'APIError'
| 'AuthenticationError'
| 'ErrorDeletingFile'
| 'FileRetrievalError'
| 'FileUploadError'
| 'Forbidden'
| 'Locked'
| 'LockedAuth'
| 'MissingFile'
| 'NotFound'
| 'QueryError'
| 'ValidationError'

0 comments on commit d628222

Please sign in to comment.