Cronping

Attaching Logs

Send job output alongside your pings so you can inspect what happened directly in the dashboard.

Overview

All Cronping ping endpoints accept an optional request body when you use POST. The body is stored alongside the ping event (up to 100 KB) and is visible in the ping history in the dashboard.

This is useful for:

  • Attaching the output of a backup or maintenance script
  • Capturing error messages when a job fails
  • Recording metrics or summaries without a separate logging pipeline

Capturing command output in a shell script

Capture stdout and stderr into a variable, then POST it as the request body:

#!/bin/sh

m=$(/usr/bin/backup.sh 2>&1)
curl -fsS -m 10 --data-raw "$m" https://ping.cronping.com/<token>

Signal success or failure based on the exit code

#!/bin/sh

m=$(/usr/bin/backup.sh 2>&1)
curl -fsS -m 10 --data-raw "$m" https://ping.cronping.com/<token>/$?

Exit code 0 → success ping. Any other value → failure ping. The captured output is attached either way.

Output is too large for --data-raw

If the command produces a lot of output you may get an "Argument list too long" error. Use a temp file instead:

#!/bin/sh

/usr/bin/backup.sh > /tmp/backup.log 2>&1
curl -fsS -m 10 --data-binary @/tmp/backup.log https://ping.cronping.com/<token>/$?

Sending logs without changing status

Use the /log endpoint to attach a log entry without altering the heartbeat's current status. Cronping records the event but leaves the heartbeat state unchanged.

curl -fsS -m 10 \
  --data-raw "Processed 1,423 records in 8.3s" \
  https://ping.cronping.com/<token>/log

This is useful for attaching progress updates or mid-job diagnostics.


Language examples

Python

import urllib.request
import subprocess

result = subprocess.run(
    ["/usr/bin/backup.sh"],
    capture_output=True,
    text=True,
)

output = (result.stdout + result.stderr).encode()
action = str(result.returncode)  # "0" = success, anything else = failure

req = urllib.request.Request(
    f"https://ping.cronping.com/<token>/{action}",
    data=output[:100_000],
    method="POST",
)
try:
    urllib.request.urlopen(req, timeout=10)
except Exception as e:
    print(f"Cronping ping failed: {e}")

Node.js

const { execSync } = require("child_process");

let output = "";
let exitCode = 0;

try {
  output = execSync("/usr/bin/backup.sh", { stdio: "pipe" }).toString();
} catch (err) {
  output = err.stdout?.toString() + err.stderr?.toString();
  exitCode = err.status ?? 1;
}

try {
  await fetch(`https://ping.cronping.com/<token>/${exitCode}`, {
    method: "POST",
    body: output.slice(0, 100_000),
    signal: AbortSignal.timeout(10_000),
  });
} catch (err) {
  console.error("Cronping ping failed:", err.message);
}

Go

package main

import (
	"bytes"
	"fmt"
	"net/http"
	"os/exec"
	"strconv"
	"time"
)

func main() {
	out, err := exec.Command("/usr/bin/backup.sh").CombinedOutput()

	exitCode := 0
	if err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			exitCode = exitErr.ExitCode()
		} else {
			exitCode = 1
		}
	}

	body := out
	if len(body) > 100_000 {
		body = body[:100_000]
	}

	client := &http.Client{Timeout: 10 * time.Second}
	url := "https://ping.cronping.com/<token>/" + strconv.Itoa(exitCode)
	resp, pingErr := client.Post(url, "text/plain", bytes.NewReader(body))
	if pingErr != nil {
		fmt.Printf("Cronping ping failed: %v\n", pingErr)
		return
	}
	defer resp.Body.Close()
}

C#

using System;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

static readonly HttpClient _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };

static async Task RunJobAndPingAsync(string token)
{
    var process = new Process
    {
        StartInfo = new ProcessStartInfo("/usr/bin/backup.sh")
        {
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
        }
    };

    process.Start();
    string output = await process.StandardOutput.ReadToEndAsync()
                  + await process.StandardError.ReadToEndAsync();
    await process.WaitForExitAsync();

    int exitCode = process.ExitCode;

    try
    {
        var content = new StringContent(
            output.Length > 100_000 ? output[..100_000] : output,
            Encoding.UTF8,
            "text/plain"
        );
        using var _ = await _httpClient.PostAsync(
            $"https://ping.cronping.com/{token}/{exitCode}",
            content
        );
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine($"Cronping ping failed: {ex.Message}");
    }
}

Size limit

Cronping stores up to 100 KB per ping. If your job output is larger:

  • Trim verbose output and send only a summary (row counts, durations, errors).
  • Send the last 100 KB, which usually contains the most relevant output:
m=$(/usr/bin/backup.sh 2>&1 | tail -c 100000)
curl -fsS -m 10 --data-raw "$m" https://ping.cronping.com/<token>/$?
  • For full log retention, pipe output to a dedicated log service (Loki, Datadog, etc.) and use Cronping only for status signals.

On this page