Message templates¶
Added in v1.0.0-beta.2
Providers take a template block that reshapes the message they send.
Every provider has a built-in message format. A template block under a
provider replaces parts of that message with your own text, written in
Jinja syntax:
providers:
slack:
webhook_url: "${SLACK_WEBHOOK_URL}"
template:
text: |
{{ emoji }} *[{{ environment | default("no env") | upper }}] {{ title }}*
{% if message %}{{ message }}{% endif %}
{% if log_url %}<{{ log_url }}|View logs>{% endif %}
pagerduty:
routing_key: "${PD_ROUTING_KEY}"
template:
summary: "[{{ service }}] {{ title }}"
Templates are set per provider, and only the parts you set change. A provider
without a template block, or a part a template doesn't set, keeps the
built-in format, so existing configs send exactly what they did before.
Parts you can template¶
| Provider | Parts | Replaces | Extra variables |
|---|---|---|---|
console |
text |
The printed line. | |
slack |
text |
The message text. |
emoji: the severity's Slack emoji, such as :x: |
webhook |
body |
The whole JSON body. Must render to valid JSON; see Webhook bodies. | |
pagerduty |
summary |
payload.summary. Cut to PagerDuty's 1024 characters. |
|
teams |
title, text |
The card's title and message text blocks. The fact table and log button stay. | |
opsgenie |
message, description |
The alert message (cut to 130 characters) and description (cut to 15000). |
|
email |
subject, body |
The subject (after subject_prefix, which still applies) and the plain-text body. A subject is joined onto one line. |
Everything else a provider sends stays as the built-in format: PagerDuty's
dedup_key, severity and custom details, Opsgenie's alias, priority and tags,
email threading headers. That keeps deduplication and routing on the receiving
side working whatever the text says.
Variables¶
Every template sees the signal's fields by name:
| Variable | Notes |
|---|---|
title, source, event_type, severity |
Always set. severity is lowercase: info, success, warning, error, critical. |
message, environment, service, component, team, run_id, correlation_id, trace_id, log_url, dedup_key |
Undefined when the signal doesn't set them, so {% if message %} works and {{ message }} prints nothing. |
metadata |
The signal's metadata object, such as {{ metadata.dag_id }}. Loop over it with the items filter. |
id, timestamp |
The signal id, and the time it was created as RFC 3339 in UTC. |
Filters and tests from Jinja's built-in set work, such as upper, lower,
default, replace, trim, join, length and tojson.
Mistakes stop the load¶
A template is checked when the config loads, like the rest of the file. These are load-time errors, with the key that failed in the message:
- a syntax error (
{{ title) - a part the provider doesn't have (
providers.slack.template.txt) - a variable that doesn't exist (
{{ tittle }}), including another provider's extra variable (emojioutside Slack)
metadata.<key> is not checked, since metadata differs from signal to signal.
A template never costs the alert¶
If a template still fails while a signal is sent (for example, {{ metadata.count + 1 }}
on a signal whose count is text), or renders to nothing but whitespace, the
provider logs a warning and sends its built-in format instead. The alert goes
out either way.
Webhook bodies¶
The webhook body template must produce JSON. Use the tojson filter for
every value, so quotes and newlines in a title are escaped:
providers:
webhook:
url: "https://alerts.example.com/hook"
template:
body: |
{
"summary": {{ title | tojson }},
"level": {{ severity | upper | tojson }},
"labels": {"env": {{ environment | default("none") | tojson }}}
}
A body that is not valid JSON once rendered is logged, and the full signal is sent instead, as without a template.
Escaping¶
Templates don't escape anything. Slack and Teams render their own markdown, so
a * or _ in a title formats text there, as it does with the built-in
format.
Templates and layered configs¶
template belongs to its provider entry, and a later
layer replaces a provider entry whole. A
team file that wants its own Slack text restates the provider:
# teams/data-platform.yaml
extends: ../default.yaml
providers:
slack:
webhook_url: "${DATA_SLACK_WEBHOOK_URL}"
template:
text: "{{ emoji }} [data-platform] {{ title }}"
Checking a template¶
opssignal preview prints what each provider
would be sent for a signal, without sending or recording anything:
opssignal preview --config signal.yaml \
--source airflow --event-type task_failed --severity error \
--title "load failed" --environment prod --metadata '{"dag_id": "orders"}'
Python and C¶
Templates live in the config, so the Python package and the C library use them with no code changes.