Skip to content

feat: add silent flag for running jobs #12931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/payload/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,7 @@ export class BasePayload {
await this.jobs.run({
limit: cronConfig.limit ?? DEFAULT_LIMIT,
queue: cronConfig.queue,
silent: cronConfig.silent,
})
})

Expand Down
16 changes: 14 additions & 2 deletions packages/payload/src/queues/config/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { CollectionConfig, Job } from '../../../index.js'
import type { Payload, PayloadRequest, Sort } from '../../../types/index.js'
import type { RunJobsSilent } from '../../localAPI.js'
import type { RunJobsArgs } from '../../operations/runJobs/index.js'
import type { JobStats } from '../global.js'
import type { TaskConfig } from './taskTypes.js'
import type { WorkflowConfig } from './workflowTypes.js'

export type CronConfig = {
export type AutorunCronConfig = {
/**
* The cron schedule for the job.
* @default '* * * * *' (every minute).
Expand Down Expand Up @@ -35,6 +36,15 @@ export type CronConfig = {
* The queue name for the job.
*/
queue?: string
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
}

export type RunJobAccessArgs = {
Expand Down Expand Up @@ -79,7 +89,9 @@ export type JobsConfig = {
*
* @remark this property should not be used on serverless platforms like Vercel
*/
autoRun?: ((payload: Payload) => CronConfig[] | Promise<CronConfig[]>) | CronConfig[]
autoRun?:
| ((payload: Payload) => AutorunCronConfig[] | Promise<AutorunCronConfig[]>)
| AutorunCronConfig[]
/**
* Determine whether or not to delete a job after it has successfully completed.
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/payload/src/queues/endpoints/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,17 @@ export const runJobsEndpoint: Endpoint = {
handleSchedules: handleSchedulesParam,
limit,
queue,
silent: silentParam,
} = req.query as {
allQueues?: 'false' | 'true'
handleSchedules?: 'false' | 'true'
limit?: number
queue?: string
silent?: string
}

const silent = silentParam === 'true'

const shouldHandleSchedules =
handleSchedulesParam &&
!(typeof handleSchedulesParam === 'string' && handleSchedulesParam === 'false')
Expand All @@ -63,6 +67,7 @@ export const runJobsEndpoint: Endpoint = {
req,
// Access is validated above, so it's safe to override here
overrideAccess: true,
silent,
}

if (typeof queue === 'string') {
Expand Down
39 changes: 27 additions & 12 deletions packages/payload/src/queues/errors/handleTaskError.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ObjectIdImport from 'bson-objectid'

import type { PayloadRequest } from '../../index.js'
import type { RunJobsSilent } from '../localAPI.js'
import type { UpdateJobFunction } from '../operations/runJobs/runJob/getUpdateJobFunction.js'
import type { TaskError } from './index.js'

Expand All @@ -13,10 +14,20 @@ const ObjectId = (ObjectIdImport.default ||
export async function handleTaskError({
error,
req,
silent = false,
updateJob,
}: {
error: TaskError
req: PayloadRequest
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
updateJob: UpdateJobFunction
}): Promise<{
hasFinalError: boolean
Expand Down Expand Up @@ -102,12 +113,14 @@ export async function handleTaskError({
waitUntil: job.waitUntil,
})

req.payload.logger.error({
err: error,
job,
msg: `Error running task ${taskID}. Attempt ${job.totalTried} - max retries reached`,
taskSlug,
})
if (!silent || (typeof silent === 'object' && !silent.error)) {
req.payload.logger.error({
err: error,
job,
msg: `Error running task ${taskID}. Attempt ${job.totalTried} - max retries reached`,
taskSlug,
})
}
return {
hasFinalError: true,
}
Expand Down Expand Up @@ -135,12 +148,14 @@ export async function handleTaskError({
retriesConfig: workflowConfig.retries,
})

req.payload.logger.error({
err: error,
job,
msg: `Error running task ${taskID}. Attempt ${job.totalTried + 1}${maxWorkflowRetries !== undefined ? '/' + (maxWorkflowRetries + 1) : ''}`,
taskSlug,
})
if (!silent || (typeof silent === 'object' && !silent.error)) {
req.payload.logger.error({
err: error,
job,
msg: `Error running task ${taskID}. Attempt ${job.totalTried + 1}${maxWorkflowRetries !== undefined ? '/' + (maxWorkflowRetries + 1) : ''}`,
taskSlug,
})
}

// Update job's waitUntil only if this waitUntil is later than the current one
if (waitUntil && (!job.waitUntil || waitUntil > new Date(job.waitUntil))) {
Expand Down
21 changes: 17 additions & 4 deletions packages/payload/src/queues/errors/handleWorkflowError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { PayloadRequest } from '../../index.js'
import type { RunJobsSilent } from '../localAPI.js'
import type { UpdateJobFunction } from '../operations/runJobs/runJob/getUpdateJobFunction.js'
import type { WorkflowError } from './index.js'

Expand All @@ -15,10 +16,20 @@ import { getWorkflowRetryBehavior } from './getWorkflowRetryBehavior.js'
export async function handleWorkflowError({
error,
req,
silent = false,
updateJob,
}: {
error: WorkflowError
req: PayloadRequest
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
updateJob: UpdateJobFunction
}): Promise<{
hasFinalError: boolean
Expand Down Expand Up @@ -55,10 +66,12 @@ export async function handleWorkflowError({

const jobLabel = job.workflowSlug || `Task: ${job.taskSlug}`

req.payload.logger.error({
err: error,
msg: `Error running job ${jobLabel} id: ${job.id} attempt ${job.totalTried + 1}${maxWorkflowRetries !== undefined ? '/' + (maxWorkflowRetries + 1) : ''}`,
})
if (!silent || (typeof silent === 'object' && !silent.error)) {
req.payload.logger.error({
err: error,
msg: `Error running job ${jobLabel} id: ${job.id} attempt ${job.totalTried + 1}${maxWorkflowRetries !== undefined ? '/' + (maxWorkflowRetries + 1) : ''}`,
})
}

// Tasks update the job if they error - but in case there is an unhandled error (e.g. in the workflow itself, not in a task)
// we need to ensure the job is updated to reflect the error
Expand Down
26 changes: 26 additions & 0 deletions packages/payload/src/queues/localAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import { handleSchedules, type HandleSchedulesResult } from './operations/handle
import { runJobs } from './operations/runJobs/index.js'
import { updateJob, updateJobs } from './utilities/updateJob.js'

export type RunJobsSilent =
| {
error?: boolean
info?: boolean
}
| boolean
export const getJobsLocalAPI = (payload: Payload) => ({
handleSchedules: async (args?: {
// By default, schedule all queues - only scheduling jobs scheduled to be added to the `default` queue would not make sense
Expand Down Expand Up @@ -156,6 +162,15 @@ export const getJobsLocalAPI = (payload: Payload) => ({
* If you want to run them in sequence, set this to true.
*/
sequential?: boolean
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
where?: Where
}): Promise<ReturnType<typeof runJobs>> => {
const newReq: PayloadRequest = args?.req ?? (await createLocalReq({}, payload))
Expand All @@ -168,6 +183,7 @@ export const getJobsLocalAPI = (payload: Payload) => ({
queue: args?.queue,
req: newReq,
sequential: args?.sequential,
silent: args?.silent,
where: args?.where,
})
},
Expand All @@ -176,13 +192,23 @@ export const getJobsLocalAPI = (payload: Payload) => ({
id: number | string
overrideAccess?: boolean
req?: PayloadRequest
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
}): Promise<ReturnType<typeof runJobs>> => {
const newReq: PayloadRequest = args.req ?? (await createLocalReq({}, payload))

return await runJobs({
id: args.id,
overrideAccess: args.overrideAccess !== false,
req: newReq,
silent: args.silent,
})
},

Expand Down
39 changes: 29 additions & 10 deletions packages/payload/src/queues/operations/runJobs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Job } from '../../../index.js'
import type { PayloadRequest, Sort, Where } from '../../../types/index.js'
import type { WorkflowJSON } from '../../config/types/workflowJSONTypes.js'
import type { WorkflowConfig, WorkflowHandler } from '../../config/types/workflowTypes.js'
import type { RunJobsSilent } from '../../localAPI.js'
import type { RunJobResult } from './runJob/index.js'

import { Forbidden } from '../../../errors/Forbidden.js'
Expand Down Expand Up @@ -53,6 +54,15 @@ export type RunJobsArgs = {
* If you want to run them in sequence, set this to true.
*/
sequential?: boolean
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
where?: Where
}

Expand Down Expand Up @@ -84,6 +94,7 @@ export const runJobs = async (args: RunJobsArgs): Promise<RunJobsResult> => {
},
},
sequential,
silent = false,
where: whereFromProps,
} = args

Expand Down Expand Up @@ -219,11 +230,13 @@ export const runJobs = async (args: RunJobsArgs): Promise<RunJobsResult> => {
}
}

payload.logger.info({
msg: `Running ${jobs.length} jobs.`,
new: newJobs?.length,
retrying: existingJobs?.length,
})
if (!silent || (typeof silent === 'object' && !silent.info)) {
payload.logger.info({
msg: `Running ${jobs.length} jobs.`,
new: newJobs?.length,
retrying: existingJobs?.length,
})
}

const successfullyCompletedJobs: (number | string)[] = []

Expand Down Expand Up @@ -277,7 +290,9 @@ export const runJobs = async (args: RunJobsArgs): Promise<RunJobsResult> => {
if (!workflowHandler) {
const jobLabel = job.workflowSlug || `Task: ${job.taskSlug}`
const errorMessage = `Can't find runner while importing with the path ${workflowConfig.handler} in job type ${jobLabel}.`
payload.logger.error(errorMessage)
if (!silent || (typeof silent === 'object' && !silent.error)) {
payload.logger.error(errorMessage)
}

await updateJob({
error: {
Expand All @@ -300,6 +315,7 @@ export const runJobs = async (args: RunJobsArgs): Promise<RunJobsResult> => {
const result = await runJob({
job,
req: jobReq,
silent,
updateJob,
workflowConfig,
workflowHandler,
Expand All @@ -314,6 +330,7 @@ export const runJobs = async (args: RunJobsArgs): Promise<RunJobsResult> => {
const result = await runJSONJob({
job,
req: jobReq,
silent,
updateJob,
workflowConfig,
workflowHandler,
Expand Down Expand Up @@ -370,10 +387,12 @@ export const runJobs = async (args: RunJobsArgs): Promise<RunJobsResult> => {
})
}
} catch (err) {
payload.logger.error({
err,
msg: `Failed to delete jobs ${successfullyCompletedJobs.join(', ')} on complete`,
})
if (!silent || (typeof silent === 'object' && !silent.error)) {
payload.logger.error({
err,
msg: `Failed to delete jobs ${successfullyCompletedJobs.join(', ')} on complete`,
})
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions packages/payload/src/queues/operations/runJobs/runJSONJob/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Job } from '../../../../index.js'
import type { PayloadRequest } from '../../../../types/index.js'
import type { WorkflowJSON, WorkflowStep } from '../../../config/types/workflowJSONTypes.js'
import type { WorkflowConfig } from '../../../config/types/workflowTypes.js'
import type { RunJobsSilent } from '../../../localAPI.js'
import type { UpdateJobFunction } from '../runJob/getUpdateJobFunction.js'
import type { JobRunStatus } from '../runJob/index.js'

Expand All @@ -12,6 +13,15 @@ import { getRunTaskFunction } from '../runJob/getRunTaskFunction.js'
type Args = {
job: Job
req: PayloadRequest
/**
* If set to true, the job system will not log any output to the console (for both info and error logs).
* Can be an option for more granular control over logging.
*
* This will not automatically affect user-configured logs (e.g. if you call `console.log` or `payload.logger.info` in your job code).
*
* @default false
*/
silent?: RunJobsSilent
updateJob: UpdateJobFunction
workflowConfig: WorkflowConfig
workflowHandler: WorkflowJSON
Expand All @@ -24,6 +34,7 @@ export type RunJSONJobResult = {
export const runJSONJob = async ({
job,
req,
silent = false,
updateJob,
workflowConfig,
workflowHandler,
Expand Down Expand Up @@ -79,6 +90,7 @@ export const runJSONJob = async ({
: 'An unhandled error occurred',
workflowConfig,
}),
silent,

req,
updateJob,
Expand Down
Loading
Loading