-
Notifications
You must be signed in to change notification settings - Fork 0
Reporting
yacron2 can report a job's outcome through three reporters - Sentry, e-mail
(SMTP), and an arbitrary shell command - configured under the report block of
the onFailure, onPermanentFailure, and onSuccess hooks. This page documents
every reporter option, its type and default, the secret-resolution rules, the
jinja2 template variables, and the environment variables passed to the shell
reporter.
Each job has three reporting hooks, each containing a report block with the
same schema:
| Hook | Fires when |
|---|---|
onFailure |
A job run is detected as failed (see Failure Detection and Retries). When retries are configured, this fires on each failed attempt. |
onPermanentFailure |
A job has failed and all configured retries are exhausted. |
onSuccess |
A job run is detected as succeeded. |
All three hooks accept the identical report block (sentry, mail, shell).
The default report configuration is applied independently to each of the three
hooks (_REPORT_DEFAULTS is deep-copied into each), so configuring one hook does
not affect the others.
A run that is deliberately terminated to make way for a newer instance
(concurrencyPolicy: Replace) is not treated as a failure and is neither
reported nor retried. See Concurrency and Timeouts.
For any given hook, yacron2 always invokes all three reporters concurrently
(asyncio.gather with return_exceptions=True). A reporter that is not
configured returns early and does nothing:
- Sentry returns if no
dsnsource is set. - Mail returns if
toorfromis unset. - Shell returns if
commandis unset (None).
An exception raised by one reporter is logged at ERROR level (with traceback)
and does not prevent the other reporters from running, nor does it propagate to
the scheduler.
subject, body (mail), body and each fingerprint entry (sentry) are
jinja2 templates. Each distinct template
source is compiled once and cached for the process lifetime (lru_cache), so the
same template string is not recompiled on every report.
The following variables are available when rendering any report template:
| Variable | Type | Description |
|---|---|---|
name |
str | Job name. |
success |
bool |
True when the job is considered successful (i.e. no fail reason). |
fail_reason |
str or None | Human-readable reason the job failed, or None on success. |
stdout |
str or None | Captured standard output (None if captureStdout is off). |
stderr |
str or None | Captured standard error (None if captureStderr is off). |
exit_code |
int or None | Process exit code (retcode). |
command |
str or list | The job's command. |
shell |
str | The job's shell. |
environment |
dict or None | The subprocess environment (None when the job defines no environment). |
Templating support was added in yacron2 0.5. The README's variable list omits
fail_reason; the code provides it (it is also used by the default body
template). To capture output for inclusion in reports, enable captureStderr
(on by default) and/or captureStdout. See Output Capturing.
The mail subject and (prepended to the) sentry body use:
Cron job '{{name}}' {% if success %}completed{% else %}failed{% endif %}
The default body (DEFAULT_BODY_TEMPLATE) prints the fail reason (when set)
followed by captured stdout/stderr, or (no output was captured):
{% if fail_reason -%}
(job failed because {{fail_reason}})
{% endif %}
{% if stdout and stderr -%}
STDOUT:
---
{{stdout}}
---
STDERR:
{{stderr}}
{% elif stdout -%}
{{stdout}}
{% elif stderr -%}
{{stderr}}
{% else -%}
(no output was captured)
{% endif %}
The default sentry body is the default subject template, a newline, and the
default body template, concatenated.
Sends an e-mail via SMTP using aiosmtplib. Reporting is enabled only when both
to and from are set; otherwise the mail reporter returns without sending.
| Option | Type | Default | Description |
|---|---|---|---|
from |
str (required in block) | None |
Envelope/From header. Required for mail reporting to occur. |
to |
str (required in block) | None |
Comma-separated recipient list, used directly as the To header. Required for mail reporting to occur. |
smtpHost |
str (Opt) | None |
SMTP server hostname. |
smtpPort |
int (Opt) | 25 |
SMTP server port. |
tls |
bool (Opt) | false |
Use TLS for the connection (aiosmtplib use_tls). |
starttls |
bool (Opt) | false |
Issue STARTTLS after connecting. |
validate_certs |
bool (Opt) | true |
Validate the server's TLS certificate. See note below. |
html |
bool (Opt) | false |
Send the body as text/html (set_content subtype html) instead of plain text. |
username |
str (Opt) | None |
SMTP login username. Login is attempted only when both username and a resolved password are present. |
password |
secret block (Opt) | unset | SMTP login password; see Secrets. |
subject |
str (Opt) | default subject template | jinja2 template for the Subject header. |
body |
str (Opt) | default body template | jinja2 template for the message body. |
In the strictyaml schema, from and to are required keys when a mail block
is present (they accept an empty value, mapping to None), while the remaining
keys are optional. Behaviorally, mail reporting is skipped unless both resolve to
a non-empty value.
Notes on behavior:
-
validate_certsdefaults totrue(changed so that SMTP TLS certificate validation is on by default). This breaks connections to servers with self-signed or otherwise untrusted certificates unless you setvalidate_certs: false. - An
RFC 5322Dateheader is set (email.utils.format_datetime(datetime.now(timezone.utc))), e.g.Wed, 18 Jun 2026 12:34:56 +0000- not ISO-8601. - Empty-body success e-mails are skipped. On a success report, if the rendered body is empty after stripping whitespace, no e-mail is sent. (Failure reports are sent even with an empty body.)
- The SMTP connection is always closed, even if
STARTTLS, login, or sending raises, so a misbehaving server cannot leak one connection per report. -
html: trueusesset_content(body, subtype="html"), which sets the correct charset and transfer-encoding for non-ASCII HTML (new in yacron2 0.15).
Minimal failure-mail example:
jobs:
- name: backup
command: /usr/local/bin/backup.sh
schedule: "0 3 * * *"
captureStderr: true
onFailure:
report:
mail:
from: cron@example.com
to: ops@example.com, oncall@example.com
smtpHost: 127.0.0.1
smtpPort: 25
starttls: false
validate_certs: falseHTML success mail with login (password from environment):
jobs:
- name: report-build
command: render-report
schedule: "@reboot"
captureStdout: true
onSuccess:
report:
mail:
from: cron@example.com
to: team@example.com
smtpHost: smtp.example.com
smtpPort: 587
starttls: true
username: cron
password:
fromEnvVar: SMTP_PASSWORD
html: true
subject: "Build report for {{ name }}"
body: "{{ stdout }}"Captures a message to Sentry via sentry-sdk. Reporting is
enabled only when a dsn source resolves to a value; otherwise the reporter
returns early.
| Option | Type | Default | Description |
|---|---|---|---|
dsn |
secret block (Opt) | all sources None
|
Sentry DSN; see Secrets. No DSN means Sentry reporting is disabled. |
fingerprint |
list of str (Opt) | ["yacron2", "{{ environment.HOSTNAME }}", "{{ name }}"] |
jinja2-templated fingerprint lines controlling Sentry issue grouping. Replaces, not appends, on merge - see note. |
level |
str (Opt) | unset (effective error) |
Sentry severity level (e.g. error, warning, info). When unset, sentry_sdk.capture_message is called with level="error". |
extra |
map of str to (str/int/bool) (Opt) | unset | Additional key/value context attached to the event. Your map is merged on top of yacron2's always-attached job/exit_code/command/shell/success context. |
body |
str (Opt) | default subject + body template | jinja2 template for the captured message text. |
environment |
str (Opt) | None |
Sentry environment tag. |
maxStringLength |
int (Opt) | 8192 |
Sets sentry_sdk.utils.MAX_STRING_LENGTH (max length before Sentry truncates strings). |
Notes on behavior:
- The Sentry client is initialised once per
(dsn, environment)pair and cached; it is rebuilt only when one of those changes, not on every report. -
maxStringLengthmutates the process-globalsentry_sdk.utils.MAX_STRING_LENGTHwhen set (and truthy). - In addition to any
extrayou supply, yacron2 always attachesjob,exit_code,command,shell, andsuccessto the event's extra context. Yourextramap is merged on top of these. - Capture uses an isolated scope (
sentry_sdk.new_scope()); the configuredfingerprintand extras are applied per event. -
fingerprintwas added in yacron2 0.6;extraandlevelin 0.8;environmentand a largermaxStringLengthin 0.14.
Fingerprint merge semantics: fingerprint is a replace-not-append setting.
When a defaults block or a job supplies its own fingerprint, it overrides the
default list entirely rather than being concatenated onto the three default
entries. This is a deliberate special case in the config merge so that custom
Sentry issue grouping is possible. All other list-valued options merge by
concatenation; environment (the job env list) merges by key. See
Includes, Defaults, and Multi-File Config.
Example:
jobs:
- name: ingest
command: run-ingest
schedule: "*/5 * * * *"
captureStderr: true
onFailure:
report:
sentry:
dsn:
fromEnvVar: SENTRY_DSN
level: warning
environment: production
fingerprint:
- ingest-job
- "{{ name }}"
extra:
datacenter: dc1
shard: 3Runs a user-supplied command, passing job state through YACRON2_* environment
variables. The shell reporter was added in yacron2 0.13.
| Option | Type | Default | Description |
|---|---|---|---|
shell |
str (Opt) | /bin/sh |
Shell used when command is a string. |
command |
str or list of str (required in block) | None |
The command to run. A list is executed directly (argv); a string is run via shell -c. Required key when a shell block is present; reporting is skipped if it resolves to nothing. |
Execution model:
- If
commandis a list, it is executed directly withasyncio.create_subprocess_exec(no shell). - If
commandis a string andshellis set (the default/bin/shapplies), it is executed as[shell, "-c", command]withasyncio.create_subprocess_exec. - If
commandis a string andshellresolves to a falsy value (e.g.shell: ""), the string is passed toasyncio.create_subprocess_shell(run by the system default shell). - The reporter does not fail the job. A failure to launch the command is logged
(with traceback) and the reporter returns; a nonzero exit code from the command
is logged at
ERRORlevel (without a spurious traceback).
The shell command inherits the full environment of the yacron2 process, plus the following variables describing the job outcome:
| Variable | Value |
|---|---|
YACRON2_FAIL_REASON |
The fail reason string, or empty string on success. |
YACRON2_FAILED |
"1" if the job failed, "0" otherwise. |
YACRON2_JOB_NAME |
The job name. |
YACRON2_JOB_COMMAND |
The job command; a list command is joined with spaces. |
YACRON2_JOB_SCHEDULE |
The job's unparsed schedule string. |
YACRON2_RETCODE |
The process exit code, as a string. |
YACRON2_STDERR |
Captured stderr (possibly truncated; see below). |
YACRON2_STDOUT |
Captured stdout (possibly truncated; see below). |
YACRON2_STDERR_TRUNCATED |
"1" if YACRON2_STDERR was truncated, "0" otherwise. |
YACRON2_STDOUT_TRUNCATED |
"1" if YACRON2_STDOUT was truncated, "0" otherwise. |
Truncation: stdout and stderr can be large, and there are OS limits on
argument/environment sizes. yacron2 truncates each stream to a maximum of
16 KiB (1024 * 16) when either stream individually, or the two combined,
exceeds that limit. YACRON2_STDERR_TRUNCATED / YACRON2_STDOUT_TRUNCATED
indicate per-stream whether truncation occurred. The README lists the first eight
variables but omits the *_TRUNCATED pair; both are set by the code (long-line
truncation handling dates from yacron2 0.17).
Example:
jobs:
- name: ping-job
command: do-work
shell: /bin/bash
schedule: "* * * * *"
onFailure:
report:
shell:
shell: /bin/bash
command: echo "job $YACRON2_JOB_NAME failed with code $YACRON2_RETCODE"List form (no shell):
shell:
command:
- /usr/local/bin/notify
- --job
- "failed"The mail.password and sentry.dsn options are secret blocks with three
mutually-exclusive sources. Each is an optional key accepting a string or empty
value:
| Source | Description |
|---|---|
value |
The secret inline in the config. |
fromFile |
Path to a file whose contents (stripped of surrounding whitespace) are the secret. |
fromEnvVar |
Name of an environment variable holding the secret. |
Resolution order is value, then fromFile, then fromEnvVar - the first
non-empty source wins. If none is set, the reporter treats the secret as absent
(Sentry: disabled; mail: no login).
If fromEnvVar is set but the named environment variable is unset/empty, the
report is skipped and an error is logged - yacron2 no longer raises
KeyError in this case. For mail, the password env-var name is not echoed to
the logs (it is tied to a secret); for sentry, the DSN env-var name is logged.
sentry:
dsn:
fromFile: /etc/secrets/sentry-dsn
mail:
from: cron@example.com
to: ops@example.com
smtpHost: 127.0.0.1
username: cron
password:
fromEnvVar: SMTP_PASSWORDThis wiki documents yacron2. See the README and the changelog.
yacron2 is a fork of gjcarneiro/yacron.
- Getting Started
- Configuration
- Job Behavior
- Integrations
- Reference and Development