Integrations
Report job starts, successes, failures and durations from common runtimes.
Every snippet here can be pasted as-is. Replace <pingID> with your monitor's own.
The app shows these same snippets with it already filled in.
Nothing below is required. One curl to the ping URL when a job finishes is enough.
These add two things:
- A run id (
rid), so the ping that closes a run is paired with thestart/that opened it instead of the previous event. A job that sometimes overlaps itself measures wrong without one. - A duration, timed by the job. The server can only see when the pings arrived, which includes network time and retry backoff.
The curl flags
Every shell example uses the same set:
-f treat a non-2xx response as an error
-s -S hide the progress meter, keep the errors
-m 10 time out after 10 seconds, so a hanging ping cannot block the job
--retry 5 retry on transient failures
-o /dev/null discard the response bodyCrontab
Goes in your own crontab, via crontab -e.
0 3 * * * curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/start/?rid=$$"; if /path/to/your-job.sh; then curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/?rid=$$"; else curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/fail/?rid=$$"; fiThree details matter:
- The start ping is separated from the job by
;rather than&&, so a problem reaching Rippler cannot stop your job from running. - The
if/elsemeans a failed success ping is not mistaken for a failed job. $$is the shell's process id, which works as a run id.
/etc/crontab takes a user column after the schedule. Paste this there without one and cron
reads curl as the username, rejects the file, and ignores every job in it. See
troubleshooting.
Shell
Reports a crash immediately instead of waiting for the ping to be missed, and times the run itself.
#!/bin/bash
set -o pipefail
rid=$(uuidgen 2>/dev/null || echo "$$-$(date +%s)")
started=$(date +%s)
curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/start/?rid=$rid"
/path/to/your-job.sh
code=$?
duration=$(( $(date +%s) - started ))
if [ $code -eq 0 ]; then
curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/?rid=$rid&duration=$duration"
else
curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/fail/?rid=$rid&duration=$duration"
fi
exit $codeThe exit $code at the end matters: the wrapper passes the job's own exit status
through, so anything watching the script still sees what it expects.
Python
import time, uuid, requests
PING_URL = "https://api.rippler.io/ping/<pingID>/"
rid = str(uuid.uuid4())
started = time.monotonic()
requests.get(PING_URL + "start/", params={"rid": rid}, timeout=10)
path = "fail/"
try:
do_the_work()
path = ""
finally:
requests.get(
PING_URL + path,
params={"rid": rid, "duration": round(time.monotonic() - started, 3)},
timeout=10,
)The path starts as fail/ and is only cleared once the work returns, so a raised
exception reports a failure without being caught. The finally means the ping is
sent either way.
time.monotonic() rather than time.time(), so a clock adjustment mid-run cannot
produce a negative duration.
Node.js
const PING_URL = 'https://api.rippler.io/ping/<pingID>/';
const rid = crypto.randomUUID();
const started = Date.now();
const ping = (path = '', params = {}) =>
fetch(`${PING_URL}${path}?${new URLSearchParams({ rid, ...params })}`, {
signal: AbortSignal.timeout(10_000),
}).catch(() => {});
await ping('start/');
try {
await doTheWork();
await ping('', { duration: (Date.now() - started) / 1000 });
} catch (error) {
await ping('fail/', { duration: (Date.now() - started) / 1000 });
throw error;
}The .catch(() => {}) on the ping is deliberate, for the same reason as the ; in
the crontab line: a failed ping must not become a failed job. The throw error at
the end preserves the original failure.
GitHub Actions
The first step goes at the top of the job, the other two at the end. A failing workflow reports the failure.
env:
RIPPLER_RID: ${{ github.run_id }}-${{ github.run_attempt }}
- name: Tell Rippler the job started
run: curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/start/?rid=$RIPPLER_RID"
# … your build steps …
- name: Report success to Rippler
if: success()
run: curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/?rid=$RIPPLER_RID"
- name: Report failure to Rippler
if: failure()
run: curl -fsS -m 10 --retry 5 -o /dev/null "https://api.rippler.io/ping/<pingID>/fail/?rid=$RIPPLER_RID"Including run_attempt in the run id makes a re-run a distinct run instead of a
second closing ping for the first one.
Use another language or runtime
There is no SDK to install. A ping is an HTTP request to a URL with no authentication, so any language that can make one can report a job. See Pings for the URLs and their parameters.
If your jobs already live in a crontab, rippler sync sets all of them up
at once.