From 1985601aa280309e7a2728481688ecd817d6db0a Mon Sep 17 00:00:00 2001 From: Sasha <64744993+r1tsuu@users.noreply.github.com> Date: Thu, 2 Jan 2025 20:46:26 +0200 Subject: [PATCH] fix: ensure `loggingLevels` is respected --- packages/next/src/routes/graphql/handler.ts | 3 ++- packages/next/src/routes/rest/routeError.ts | 9 +++---- .../src/views/Document/getDocumentData.ts | 7 +++--- .../views/Document/getDocumentPermissions.tsx | 10 ++++---- packages/next/src/views/Document/index.tsx | 4 ++- .../src/views/Versions/getLatestVersion.ts | 5 ++-- packages/next/src/views/Versions/index.tsx | 16 +++++++----- packages/payload/src/index.ts | 19 +++++++------- packages/payload/src/utilities/logError.ts | 25 +++++++++++++++++++ .../src/versions/deleteCollectionVersions.ts | 3 ++- 10 files changed, 66 insertions(+), 35 deletions(-) create mode 100644 packages/payload/src/utilities/logError.ts diff --git a/packages/next/src/routes/graphql/handler.ts b/packages/next/src/routes/graphql/handler.ts index 0e93f12a05b..c053b62458e 100644 --- a/packages/next/src/routes/graphql/handler.ts +++ b/packages/next/src/routes/graphql/handler.ts @@ -4,6 +4,7 @@ import type { APIError, Payload, PayloadRequest, SanitizedConfig } from 'payload import { configToSchema } from '@payloadcms/graphql' import { createHandler } from 'graphql-http/lib/use/fetch' import httpStatus from 'http-status' +import { logError } from 'payload' import { addDataAndFileToRequest } from '../../utilities/addDataAndFileToRequest.js' import { addLocalesToRequestFromData } from '../../utilities/addLocalesToRequest.js' @@ -22,7 +23,7 @@ const handleError = async ({ }): Promise => { const status = (err.originalError as APIError).status || httpStatus.INTERNAL_SERVER_ERROR let errorMessage = err.message - payload.logger.error(err.stack) + logError({ err, payload }) // Internal server errors can contain anything, including potentially sensitive data. // Therefore, error details will be hidden from the response unless `config.debug` is `true` diff --git a/packages/next/src/routes/rest/routeError.ts b/packages/next/src/routes/rest/routeError.ts index 9fb05a49965..85cc70d83a9 100644 --- a/packages/next/src/routes/rest/routeError.ts +++ b/packages/next/src/routes/rest/routeError.ts @@ -1,7 +1,7 @@ import type { Collection, ErrorResult, PayloadRequest, SanitizedConfig } from 'payload' import httpStatus from 'http-status' -import { APIError, formatErrors, getPayload } from 'payload' +import { APIError, formatErrors, getPayload, logError } from 'payload' import { headersWithCors } from '../../utilities/headersWithCors.js' import { mergeHeaders } from '../../utilities/mergeHeaders.js' @@ -40,16 +40,13 @@ export const routeError = async ({ req, }) - const { config, logger } = payload + const { config } = payload let response = formatErrors(err) let status = err.status || httpStatus.INTERNAL_SERVER_ERROR - const level = payload.config.loggingLevels[err.name] ?? 'error' - if (level) { - logger[level](level === 'info' ? { msg: err.message } : { err }) - } + logError({ err, payload }) // Internal server errors can contain anything, including potentially sensitive data. // Therefore, error details will be hidden from the response unless `config.debug` is `true` diff --git a/packages/next/src/views/Document/getDocumentData.ts b/packages/next/src/views/Document/getDocumentData.ts index b1cdb8357b6..4b9a86abc07 100644 --- a/packages/next/src/views/Document/getDocumentData.ts +++ b/packages/next/src/views/Document/getDocumentData.ts @@ -1,6 +1,5 @@ -import type { Locale, Payload, TypedUser, TypeWithID } from 'payload' - import { sanitizeID } from '@payloadcms/ui/shared' +import { type Locale, logError, type Payload, type TypedUser, type TypeWithID } from 'payload' type Args = { collectionSlug?: string @@ -47,8 +46,8 @@ export const getDocumentData = async ({ user, }) } - } catch (_err) { - payload.logger.error(_err) + } catch (err) { + logError({ err, payload }) } return resolvedData diff --git a/packages/next/src/views/Document/getDocumentPermissions.tsx b/packages/next/src/views/Document/getDocumentPermissions.tsx index 75a006e1356..5291c69e1c1 100644 --- a/packages/next/src/views/Document/getDocumentPermissions.tsx +++ b/packages/next/src/views/Document/getDocumentPermissions.tsx @@ -10,7 +10,7 @@ import { hasSavePermission as getHasSavePermission, isEditing as getIsEditing, } from '@payloadcms/ui/shared' -import { docAccessOperation, docAccessOperationGlobal } from 'payload' +import { docAccessOperation, docAccessOperationGlobal, logError } from 'payload' export const getDocumentPermissions = async (args: { collectionConfig?: SanitizedCollectionConfig @@ -59,8 +59,8 @@ export const getDocumentPermissions = async (args: { }, }).then((permissions) => permissions.update) } - } catch (error) { - req.payload.logger.error(error) + } catch (err) { + logError({ err, payload: req.payload }) } } @@ -86,8 +86,8 @@ export const getDocumentPermissions = async (args: { }, }).then((permissions) => permissions.update) } - } catch (error) { - req.payload.logger.error(error) + } catch (err) { + logError({ err, payload: req.payload }) } } diff --git a/packages/next/src/views/Document/index.tsx b/packages/next/src/views/Document/index.tsx index 987ce4357f3..c882328d972 100644 --- a/packages/next/src/views/Document/index.tsx +++ b/packages/next/src/views/Document/index.tsx @@ -5,6 +5,7 @@ import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerCompo import { formatAdminURL, isEditing as getIsEditing } from '@payloadcms/ui/shared' import { buildFormState } from '@payloadcms/ui/utilities/buildFormState' import { notFound, redirect } from 'next/navigation.js' +import { logError } from 'payload' import React from 'react' import type { GenerateEditViewMetadata } from './getMetaBySegment.js' @@ -384,7 +385,8 @@ export const Document: React.FC = async (args) => { if (error?.message === 'NEXT_REDIRECT') { throw error } - args.initPageResult.req.payload.logger.error(error) + + logError({ err: error, payload: args.initPageResult.req.payload }) if (error.message === 'not-found') { notFound() diff --git a/packages/next/src/views/Versions/getLatestVersion.ts b/packages/next/src/views/Versions/getLatestVersion.ts index 0ad8bf5268b..14a400beea9 100644 --- a/packages/next/src/views/Versions/getLatestVersion.ts +++ b/packages/next/src/views/Versions/getLatestVersion.ts @@ -60,8 +60,9 @@ export async function getLatestVersion(args: Args): Promise { id: response.docs[0].id, updatedAt: response.docs[0].updatedAt, } - } catch (e) { - payload.logger.error(e) + } catch (err) { + logError({ err, payload }) + return null } } diff --git a/packages/next/src/views/Versions/index.tsx b/packages/next/src/views/Versions/index.tsx index 8f6860f0c7e..6d2df6e5d0b 100644 --- a/packages/next/src/views/Versions/index.tsx +++ b/packages/next/src/views/Versions/index.tsx @@ -1,7 +1,11 @@ -import type { EditViewComponent, PaginatedDocs, PayloadServerReactComponent } from 'payload' - import { Gutter, ListQueryProvider, SetDocumentStepNav } from '@payloadcms/ui' import { notFound } from 'next/navigation.js' +import { + type EditViewComponent, + logError, + type PaginatedDocs, + type PayloadServerReactComponent, +} from 'payload' import { isNumber } from 'payload/shared' import React from 'react' @@ -94,8 +98,8 @@ export const VersionsView: PayloadServerReactComponent = asyn status: 'published', }) } - } catch (error) { - payload.logger.error(error) + } catch (err) { + logError({ err, payload }) } } @@ -137,8 +141,8 @@ export const VersionsView: PayloadServerReactComponent = asyn status: 'published', }) } - } catch (error) { - payload.logger.error(error) + } catch (err) { + logError({ err, payload }) } if (!versionsData) { diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index 3dd328ec930..f05f559d9fa 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -62,6 +62,7 @@ import type { TransformGlobalWithSelect, } from './types/index.js' import type { TraverseFieldsCallback } from './utilities/traverseFields.js' +export type { FieldState } from './admin/forms/Form.js' import type { TypeWithVersion } from './versions/types.js' import { decrypt, encrypt } from './auth/crypto.js' @@ -78,8 +79,8 @@ import { getLogger } from './utilities/logger.js' import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit.js' import { traverseFields } from './utilities/traverseFields.js' -export type { FieldState } from './admin/forms/Form.js' export type * from './admin/types.js' +export { default as executeAccess } from './auth/executeAccess.js' export interface GeneratedTypes { authUntyped: { @@ -881,7 +882,6 @@ interface RequestContext { // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface DatabaseAdapter extends BaseDatabaseAdapter {} export type { Payload, RequestContext } -export { default as executeAccess } from './auth/executeAccess.js' export { executeAuthStrategies } from './auth/executeAuthStrategies.js' export { getAccessResults } from './auth/getAccessResults.js' export { getFieldsToSign } from './auth/getFieldsToSign.js' @@ -898,7 +898,6 @@ export { registerFirstUserOperation } from './auth/operations/registerFirstUser. export { resetPasswordOperation } from './auth/operations/resetPassword.js' export { unlockOperation } from './auth/operations/unlock.js' export { verifyEmailOperation } from './auth/operations/verifyEmail.js' - export type { AuthStrategyFunction, AuthStrategyFunctionArgs, @@ -919,8 +918,8 @@ export type { } from './auth/types.js' export { generateImportMap } from './bin/generateImportMap/index.js' -export type { ImportMap } from './bin/generateImportMap/index.js' +export type { ImportMap } from './bin/generateImportMap/index.js' export { genImportMapIterateFields } from './bin/generateImportMap/iterateFields.js' export { @@ -967,6 +966,7 @@ export type { TypeWithID, TypeWithTimestamps, } from './collections/config/types.js' + export { createDataloaderCacheKey, getDataLoader } from './collections/dataloader.js' export { countOperation } from './collections/operations/count.js' export { createOperation } from './collections/operations/create.js' @@ -988,8 +988,8 @@ export { serverOnlyAdminConfigProperties, serverOnlyConfigProperties, } from './config/client.js' - export { defaults } from './config/defaults.js' + export { sanitizeConfig } from './config/sanitize.js' export type * from './config/types.js' export { combineQueries } from './database/combineQueries.js' @@ -1098,8 +1098,8 @@ export { ValidationErrorName, } from './errors/index.js' export type { ValidationFieldError } from './errors/index.js' - export { baseBlockFields } from './fields/baseFields/baseBlockFields.js' + export { baseIDField } from './fields/baseFields/baseIDField.js' export { createClientField, @@ -1209,16 +1209,16 @@ export type { ValidateOptions, ValueWithRelation, } from './fields/config/types.js' - export { getDefaultValue } from './fields/getDefaultValue.js' + export { traverseFields as afterChangeTraverseFields } from './fields/hooks/afterChange/traverseFields.js' export { promise as afterReadPromise } from './fields/hooks/afterRead/promise.js' export { traverseFields as afterReadTraverseFields } from './fields/hooks/afterRead/traverseFields.js' export { traverseFields as beforeChangeTraverseFields } from './fields/hooks/beforeChange/traverseFields.js' export { traverseFields as beforeValidateTraverseFields } from './fields/hooks/beforeValidate/traverseFields.js' export { default as sortableFieldTypes } from './fields/sortableFieldTypes.js' - export { validations } from './fields/validations.js' + export type { ArrayFieldValidation, BlocksFieldValidation, @@ -1250,7 +1250,6 @@ export type { UploadFieldValidation, UsernameFieldValidation, } from './fields/validations.js' - export { type ClientGlobalConfig, createClientGlobalConfig, @@ -1272,6 +1271,7 @@ export type { } from './globals/config/types.js' export { docAccessOperation as docAccessOperationGlobal } from './globals/operations/docAccess.js' + export { findOneOperation } from './globals/operations/findOne.js' export { findVersionByIDOperation as findVersionByIDOperationGlobal } from './globals/operations/findVersionByID.js' export { findVersionsOperation as findVersionsOperationGlobal } from './globals/operations/findVersions.js' @@ -1357,6 +1357,7 @@ export { default as isolateObjectProperty } from './utilities/isolateObjectPrope export { isPlainObject } from './utilities/isPlainObject.js' export { isValidID } from './utilities/isValidID.js' export { killTransaction } from './utilities/killTransaction.js' +export { logError } from './utilities/logError.js' export { defaultLoggerOptions } from './utilities/logger.js' export { mapAsync } from './utilities/mapAsync.js' export { sanitizeFallbackLocale } from './utilities/sanitizeFallbackLocale.js' diff --git a/packages/payload/src/utilities/logError.ts b/packages/payload/src/utilities/logError.ts new file mode 100644 index 00000000000..83160756fda --- /dev/null +++ b/packages/payload/src/utilities/logError.ts @@ -0,0 +1,25 @@ +import type pino from 'pino' + +import type { Payload } from '../types/index.js' + +export const logError = ({ err, payload }: { err: unknown; payload: Payload }): void => { + let level: pino.Level = 'error' + + if ( + err && + typeof err === 'object' && + 'name' in err && + typeof err.name === 'string' && + payload.config.loggingLevels[err.name] + ) { + level = payload.config.loggingLevels[err.name] + } + + if (level) { + payload.logger[level]( + level === 'info' + ? { msg: typeof err === 'object' && 'message' in err ? err.message : 'Error' } + : { err }, + ) + } +} diff --git a/packages/payload/src/versions/deleteCollectionVersions.ts b/packages/payload/src/versions/deleteCollectionVersions.ts index 6cfa7153382..fc4906d26ce 100644 --- a/packages/payload/src/versions/deleteCollectionVersions.ts +++ b/packages/payload/src/versions/deleteCollectionVersions.ts @@ -1,6 +1,7 @@ -import type { Payload } from '../index.js' import type { PayloadRequest } from '../types/index.js' +import { type Payload } from '../index.js' + type Args = { id?: number | string payload: Payload