diff --git a/docs/configuration/overview.mdx b/docs/configuration/overview.mdx index c1ace91dc8..6b2167e9ec 100644 --- a/docs/configuration/overview.mdx +++ b/docs/configuration/overview.mdx @@ -38,6 +38,7 @@ Payload is a *config-based*, code-first CMS and application framework. The Paylo | `rateLimit` | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks and [more](/docs/production/preventing-abuse#rate-limiting-requests). | | `hooks` | Tap into Payload-wide hooks. [More](/docs/hooks/overview) | | `plugins` | An array of Payload plugins. [More](/docs/plugins/overview) | +| `loggerOptions` | Pino that will be passed through and used on Payload's logger. See [Pino Docs](https://getpino.io/#/docs/api?id=options) for more info on what is available. #### Simple example diff --git a/src/config/load.ts b/src/config/load.ts index 51fc7e6070..a43106c96e 100644 --- a/src/config/load.ts +++ b/src/config/load.ts @@ -1,6 +1,8 @@ /* eslint-disable import/no-dynamic-require */ /* eslint-disable global-require */ import path from 'path'; +import pino from 'pino'; +import Logger from '../utilities/logger'; import { SanitizedConfig } from './types'; import findConfig from './find'; import validate from './validate'; @@ -8,7 +10,8 @@ import babelConfig from '../babel.config'; const removedExtensions = ['.scss', '.css', '.svg', '.png', '.jpg', '.eot', '.ttf', '.woff', '.woff2']; -const loadConfig = (): SanitizedConfig => { +const loadConfig = (logger?: pino.Logger): SanitizedConfig => { + const localLogger = logger ?? Logger(); const configPath = findConfig(); removedExtensions.forEach((ext) => { @@ -35,7 +38,7 @@ const loadConfig = (): SanitizedConfig => { if (config.default) config = config.default; - const validatedConfig = validate(config); + const validatedConfig = validate(config, localLogger); return { ...validatedConfig, diff --git a/src/config/types.ts b/src/config/types.ts index 6ad3fb4d71..27f5ce4f5c 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -7,6 +7,7 @@ import SMTPConnection from 'nodemailer/lib/smtp-connection'; import GraphQL from 'graphql'; import { ConnectionOptions } from 'mongoose'; import React from 'react'; +import { LoggerOptions } from 'pino'; import { Payload } from '..'; import { AfterErrorHook, CollectionConfig, SanitizedCollectionConfig } from '../collections/config/types'; import { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types'; @@ -68,6 +69,8 @@ export type InitOptions = { email?: EmailOptions; local?: boolean; onInit?: (payload: Payload) => void; + /** Pino LoggerOptions */ + loggerOptions?: LoggerOptions; }; export type AccessResult = boolean | Where; diff --git a/src/config/validate.ts b/src/config/validate.ts index 95dad17339..46d8cb86d9 100644 --- a/src/config/validate.ts +++ b/src/config/validate.ts @@ -1,7 +1,7 @@ import { ValidationResult } from 'joi'; +import { Logger } from 'pino'; import schema from './schema'; import collectionSchema from '../collections/config/schema'; -import Logger from '../utilities/logger'; import { SanitizedConfig } from './types'; import { SanitizedCollectionConfig } from '../collections/config/types'; import fieldSchema, { idField } from '../fields/config/schema'; @@ -9,8 +9,6 @@ import { SanitizedGlobalConfig } from '../globals/config/types'; import globalSchema from '../globals/config/schema'; import { fieldAffectsData } from '../fields/config/types'; -const logger = Logger(); - const validateFields = (context: string, entity: SanitizedCollectionConfig | SanitizedGlobalConfig): string[] => { const errors: string[] = []; entity.fields.forEach((field) => { @@ -65,7 +63,7 @@ const validateGlobals = (globals: SanitizedGlobalConfig[]): string[] => { return errors; }; -const validateSchema = (config: SanitizedConfig): SanitizedConfig => { +const validateSchema = (config: SanitizedConfig, logger: Logger): SanitizedConfig => { const result = schema.validate(config, { abortEarly: false, }); diff --git a/src/email/build.ts b/src/email/build.ts index f466572a80..8d482c8045 100644 --- a/src/email/build.ts +++ b/src/email/build.ts @@ -1,13 +1,11 @@ import nodemailer, { Transporter } from 'nodemailer'; +import { Logger } from 'pino'; import { EmailOptions, EmailTransport, hasTransport, hasTransportOptions } from '../config/types'; import { InvalidConfiguration } from '../errors'; import mockHandler from './mockHandler'; -import Logger from '../utilities/logger'; import { BuildEmailResult, MockEmailHandler } from './types'; -const logger = Logger(); - -async function handleTransport(transport: Transporter, email: EmailTransport): BuildEmailResult { +async function handleTransport(transport: Transporter, email: EmailTransport, logger: Logger): BuildEmailResult { try { await transport.verify(); } catch (err) { @@ -26,7 +24,7 @@ const ensureConfigHasFrom = (emailConfig) => { } }; -const handleMockAccount = async (emailConfig: EmailOptions) => { +const handleMockAccount = async (emailConfig: EmailOptions, logger: Logger) => { let mockAccount: MockEmailHandler; try { mockAccount = await mockHandler(emailConfig); @@ -38,28 +36,25 @@ const handleMockAccount = async (emailConfig: EmailOptions) => { logger.info(`Mock email account password: ${pass}`); } } catch (err) { - logger.error( - 'There was a problem setting up the mock email handler', - err, - ); + logger.error('There was a problem setting up the mock email handler', err); } return mockAccount; }; -export default async function buildEmail(emailConfig: EmailOptions): BuildEmailResult { +export default async function buildEmail(emailConfig: EmailOptions, logger: Logger): BuildEmailResult { if (hasTransport(emailConfig) && emailConfig.transport) { ensureConfigHasFrom(emailConfig); const email = { ...emailConfig }; const { transport } : {transport: Transporter} = emailConfig; - return handleTransport(transport, email); + return handleTransport(transport, email, logger); } if (hasTransportOptions(emailConfig) && emailConfig.transportOptions) { ensureConfigHasFrom(emailConfig); const email = { ...emailConfig } as EmailTransport; const transport = nodemailer.createTransport(emailConfig.transportOptions); - return handleTransport(transport, email); + return handleTransport(transport, email, logger); } - return handleMockAccount(emailConfig); + return handleMockAccount(emailConfig, logger); } diff --git a/src/express/middleware/errorHandler.spec.ts b/src/express/middleware/errorHandler.spec.ts index 822567ba34..e561abc5c9 100644 --- a/src/express/middleware/errorHandler.spec.ts +++ b/src/express/middleware/errorHandler.spec.ts @@ -5,7 +5,7 @@ import { APIError } from '../../errors'; import { PayloadRequest } from '../types'; import { SanitizedConfig } from '../../config/types'; -const logger = Logger(); +const logger = Logger('payload'); const testError = new APIError('test error', 503); diff --git a/src/index.ts b/src/index.ts index 7f37d226ee..6c5aadc354 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import express, { Express, Router } from 'express'; +import pino from 'pino'; import crypto from 'crypto'; import { TypeWithID, @@ -78,7 +79,7 @@ export class Payload { globals: Globals; - logger = Logger(); + logger: pino.Logger; express: Express @@ -117,6 +118,7 @@ export class Payload { * @param options */ init(options: InitOptions): void { + this.logger = Logger('payload', options.loggerOptions); this.logger.info('Starting Payload...'); if (!options.secret) { throw new Error( @@ -139,7 +141,7 @@ export class Payload { this.mongoURL = options.mongoURL; this.local = options.local; - this.config = loadConfig(); + this.config = loadConfig(this.logger); bindOperations(this); bindRequestHandlers(this); @@ -155,7 +157,7 @@ export class Payload { } // Configure email service - this.email = buildEmail(this.emailOptions); + this.email = buildEmail(this.emailOptions, this.logger); this.sendEmail = sendEmail.bind(this); // Initialize collections & globals @@ -165,8 +167,7 @@ export class Payload { // Connect to database - - connectMongoose(this.mongoURL, options.mongoOptions, options.local); + connectMongoose(this.mongoURL, options.mongoOptions, options.local, this.logger); // If not initializing locally, set up HTTP routing if (!this.local) { diff --git a/src/mongoose/connect.ts b/src/mongoose/connect.ts index 15bdd5a924..28f7fc4607 100644 --- a/src/mongoose/connect.ts +++ b/src/mongoose/connect.ts @@ -1,10 +1,13 @@ import mongoose, { ConnectOptions } from 'mongoose'; -import Logger from '../utilities/logger'; +import pino from 'pino'; import { connection } from './testCredentials'; -const logger = Logger(); - -const connectMongoose = async (url: string, options: ConnectOptions, local: boolean): Promise => { +const connectMongoose = async ( + url: string, + options: ConnectOptions, + local: boolean, + logger: pino.Logger, +): Promise => { let urlToConnect = url; let successfulConnectionMessage = 'Connected to Mongo server successfully!'; const connectionOptions = { diff --git a/src/utilities/logger.ts b/src/utilities/logger.ts index 382abe0cf1..841aa79f46 100644 --- a/src/utilities/logger.ts +++ b/src/utilities/logger.ts @@ -4,11 +4,17 @@ import memoize from 'micro-memoize'; export type PayloadLogger = pino.Logger; -export default memoize((name = 'payload') => pino({ - name, - enabled: falsey(process.env.DISABLE_LOGGING), - prettyPrint: { - ignore: 'pid,hostname', - translateTime: 'HH:MM:ss', - }, -}) as PayloadLogger); +export default memoize( + (name = 'payload', options?: pino.LoggerOptions) => pino({ + name, + enabled: falsey(process.env.DISABLE_LOGGING), + ...(options + ? { options } + : { + prettyPrint: { + ignore: 'pid,hostname', + translateTime: 'HH:MM:ss', + }, + }), + }) as PayloadLogger, +);