Rippler docs

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 the start/ 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 body

Crontab

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=$$"; fi

Three 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/else means 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 $code

The 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.

On this page