Log Attachment
When a job fails, you can attach its stdout/stderr output directly to the run record. The log is stored in Crontify and surfaced in the run detail view — no need to dig through your server logs or a separate logging service to understand what went wrong.
How it works
Pass a log string when calling monitor.fail() or sending a /fail HTTP ping. Crontify stores the log separately from the run record — it is never fetched in list queries and never bloats the monitor detail page. It's only loaded when you open a specific run.
SDK usage
try {
await doWork();
await monitor.success();
} catch (error) {
await monitor.fail({
message: error.message,
log: error.stack, // Full stack trace attached to the run
});
}
Automatic capture with wrap()
When using wrap(), stack traces are captured automatically on error — you don't need to pass log manually:
await monitor.wrap(async () => {
await doWork();
});
// If doWork() throws, error.stack is automatically attached as the log
Attaching richer output
For more detailed diagnostics, you can capture and attach full process output:
import { execSync } from 'child_process';
try {
const output = execSync('python3 job.py', { encoding: 'utf-8' });
await monitor.success();
} catch (error) {
await monitor.fail({
message: 'Python job failed',
log: error.stdout + error.stderr, // Both streams
});
}
Character limit
Logs are truncated to 10,000 characters server-side. This is enforced as a hard limit regardless of what you send. If your output is longer, the first 10,000 characters are kept and the rest is discarded.
The SDK also applies this limit client-side before sending.
Viewing logs in the dashboard
On the monitor detail page, click any failed run to open the run detail panel. If the run has a log attached, it appears in a collapsible code block below the error message. Logs longer than 400 characters are collapsed by default with an Expand button.
Log snippets in alerts
When a fail alert fires for a run that has a log, the first 500 characters of the log are included in the alert as a snippet. Slack and Discord show it in a code block. Webhooks include it as a logSnippet field in the JSON payload.
The full log is always available on the dashboard — the snippet is just for quick context in the notification.
HTTP usage
curl -X POST https://api.crontify.com/api/v1/ping/$MONITOR_ID/fail \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"errorMessage": "Job failed with exit code 1",
"logOutput": "Traceback (most recent call last):\n File \"job.py\", line 42, in <module>\n ..."
}'
Retrieving logs via API
GET /api/v1/monitors/:monitorId/runs/:runId/log
Authorization: Bearer <jwt>
{
"hasLog": true,
"content": "Error: Connection refused\n at ..."
}
If no log was attached, hasLog is false and content is null.