Browse Source

Documentation in Markdown.

pull/325/head
Pēteris Caune 5 years ago
parent
commit
50c8c153ea
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
35 changed files with 817 additions and 20 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +29
    -0
      hc/front/management/commands/render_docs.py
  3. +1
    -0
      hc/front/urls.py
  4. +24
    -2
      hc/front/views.py
  5. +1
    -1
      static/css/channels.css
  6. +19
    -2
      static/css/docs.css
  7. +1
    -1
      templates/base.html
  8. +34
    -0
      templates/docs/bash.html
  9. +39
    -0
      templates/docs/bash.md
  10. +7
    -0
      templates/docs/csharp.html
  11. +10
    -0
      templates/docs/csharp.md
  12. +12
    -0
      templates/docs/email.html
  13. +16
    -0
      templates/docs/email.md
  14. +26
    -0
      templates/docs/introduction.html
  15. +26
    -0
      templates/docs/introduction.md
  16. +13
    -0
      templates/docs/javascript.html
  17. +17
    -0
      templates/docs/javascript.md
  18. +30
    -0
      templates/docs/measuring_script_run_time.html
  19. +35
    -0
      templates/docs/measuring_script_run_time.md
  20. +109
    -0
      templates/docs/monitoring_cron_jobs.html
  21. +113
    -0
      templates/docs/monitoring_cron_jobs.md
  22. +4
    -0
      templates/docs/php.html
  23. +7
    -0
      templates/docs/php.md
  24. +23
    -0
      templates/docs/powershell.html
  25. +29
    -0
      templates/docs/powershell.md
  26. +32
    -0
      templates/docs/python.html
  27. +37
    -0
      templates/docs/python.md
  28. +7
    -0
      templates/docs/ruby.html
  29. +10
    -0
      templates/docs/ruby.md
  30. +24
    -0
      templates/docs/signalling_failures.html
  31. +28
    -0
      templates/docs/signalling_failures.md
  32. +26
    -12
      templates/front/base_docs.html
  33. +2
    -2
      templates/front/docs_cron.html
  34. +3
    -0
      templates/front/docs_nav_item.html
  35. +22
    -0
      templates/front/docs_single.html

+ 1
- 0
CHANGELOG.md View File

@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file.
- Show sub-second durations with higher precision, 2 digits after decimal point (#321) - Show sub-second durations with higher precision, 2 digits after decimal point (#321)
- Replace the gear icon with three horizontal dots icon (#322) - Replace the gear icon with three horizontal dots icon (#322)
- Add a Pause button in the checks list (#312) - Add a Pause button in the checks list (#312)
- Documentation in Markdown
### Bug Fixes ### Bug Fixes
- Increase the allowable length of Matrix room alias to 100 (#320) - Increase the allowable length of Matrix room alias to 100 (#320)


+ 29
- 0
hc/front/management/commands/render_docs.py View File

@ -0,0 +1,29 @@
import os
from django.conf import settings
from django.core.management.base import BaseCommand
import markdown
class Command(BaseCommand):
help = "Renders Markdown to HTML"
def handle(self, *args, **options):
extensions = ["fenced_code", "codehilite", "tables"]
ec = {"codehilite": {"css_class": "highlight"}}
docs_path = os.path.join(settings.BASE_DIR, "templates/docs")
for doc in os.listdir(docs_path):
if not doc.endswith(".md"):
continue
print("Rendering %s" % doc)
src_path = os.path.join(docs_path, doc)
dst_path = os.path.join(docs_path, doc[:-3] + ".html")
text = open(src_path, "r", encoding="utf-8").read()
html = markdown.markdown(text, extensions=extensions, extension_configs=ec)
with open(dst_path, "w", encoding="utf-8") as f:
f.write(html)

+ 1
- 0
hc/front/urls.py View File

@ -76,4 +76,5 @@ urlpatterns = [
path("docs/api/", views.docs_api, name="hc-docs-api"), path("docs/api/", views.docs_api, name="hc-docs-api"),
path("docs/cron/", views.docs_cron, name="hc-docs-cron"), path("docs/cron/", views.docs_cron, name="hc-docs-cron"),
path("docs/resources/", views.docs_resources, name="hc-docs-resources"), path("docs/resources/", views.docs_resources, name="hc-docs-resources"),
path("docs/<slug:doc>/", views.serve_doc, name="hc-serve-doc"),
] ]

+ 24
- 2
hc/front/views.py View File

@ -1,5 +1,6 @@
from datetime import datetime, timedelta as td from datetime import datetime, timedelta as td
import json import json
import os
from urllib.parse import urlencode from urllib.parse import urlencode
from croniter import croniter from croniter import croniter
@ -276,6 +277,28 @@ def docs(request):
return render(request, "front/docs.html", ctx) return render(request, "front/docs.html", ctx)
def serve_doc(request, doc):
path = os.path.join(settings.BASE_DIR, "templates/docs", doc + ".html")
if not os.path.exists(path):
raise Http404("not found")
content = open(path, "r", encoding="utf-8").read()
content = content.replace("SITE_NAME", settings.SITE_NAME)
content = content.replace("PING_URL", settings.PING_ENDPOINT + "your-uuid-here")
content = content.replace(
"PING_EMAIL", "your-uuid-here@%s" % settings.PING_EMAIL_DOMAIN
)
ctx = {
"page": "docs",
"section": "home",
"section": doc,
"content": content,
}
return render(request, "front/docs_single.html", ctx)
def docs_api(request): def docs_api(request):
ctx = { ctx = {
"page": "docs", "page": "docs",
@ -290,8 +313,7 @@ def docs_api(request):
def docs_cron(request): def docs_cron(request):
ctx = {"page": "docs", "section": "cron"}
return render(request, "front/docs_cron.html", ctx)
return render(request, "front/docs_cron.html", {})
def docs_resources(request): def docs_resources(request):


+ 1
- 1
static/css/channels.css View File

@ -192,7 +192,7 @@ table.channels-table > tbody > tr > th {
float: left; float: left;
font-size: 24px; font-size: 24px;
width: 48px; width: 48px;
heigth: 48px;
height: 48px;
background: #7ec1ea; background: #7ec1ea;
border-radius: 32px; border-radius: 32px;
text-align: center; text-align: center;


+ 19
- 2
static/css/docs.css View File

@ -4,9 +4,18 @@
list-style: none; list-style: none;
} }
.docs-nav li a {
.docs-nav li {
display: block; display: block;
padding-bottom: 10px;
margin-bottom: 10px;
}
.docs-nav li.nav-header {
color: #333;
font-weight: bold;
}
li + li.nav-header {
margin-top: 20px;
} }
.docs-nav li.active > a { .docs-nav li.active > a {
@ -74,3 +83,11 @@ a.section:hover {
border-radius: 4px; border-radius: 4px;
} }
.docs-content li {
line-height: 2;
}
.docs-content img {
max-width: 100%;
border: 6px solid #DDD;
}

+ 1
- 1
templates/base.html View File

@ -124,7 +124,7 @@
{% endif %} {% endif %}
<li {% if page == 'docs' %} class="active" {% endif %}> <li {% if page == 'docs' %} class="active" {% endif %}>
<a href="{% url 'hc-docs' %}">Docs</a>
<a href="{% url 'hc-serve-doc' 'introduction' %}">Docs</a>
</li> </li>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}


+ 34
- 0
templates/docs/bash.html View File

@ -0,0 +1,34 @@
<h1>Shell scripts</h1>
<p>You can easily add SITE_NAME monitoring to a shell script. All you
have to do is make a HTTP request at the end of the script. curl and wget
are two common command line HTTP clients for that.</p>
<h2>Using curl</h2>
<div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
<span class="c1"># Exit immediately if any command exits with a non-zero status:</span>
<span class="nb">set</span> -e
<span class="c1"># Do the work here</span>
<span class="nb">echo</span> <span class="s2">&quot;Pretending to to make backups...&quot;</span>
sleep <span class="m">5</span>
<span class="nb">echo</span> <span class="s2">&quot;Backup complete!&quot;</span>
<span class="c1"># As the last thing, ping SITE_NAME using curl:</span>
<span class="hll">curl --retry <span class="m">3</span> PING_URL
</span></pre></div>
<h2>Using wget</h2>
<div class="highlight"><pre><span></span><span class="ch">#!/bin/sh</span>
<span class="c1"># Exit immediately if any command exits with a non-zero status:</span>
<span class="nb">set</span> -e
<span class="c1"># Do the work here</span>
<span class="nb">echo</span> <span class="s2">&quot;Pretending to to generate reports...&quot;</span>
sleep <span class="m">5</span>
<span class="nb">echo</span> <span class="s2">&quot;Report generation complete!&quot;</span>
<span class="c1"># As the last thing, ping SITE_NAME using wget:</span>
<span class="hll">wget PING_URL -O /dev/null
</span></pre></div>

+ 39
- 0
templates/docs/bash.md View File

@ -0,0 +1,39 @@
# Shell scripts
You can easily add SITE_NAME monitoring to a shell script. All you
have to do is make a HTTP request at the end of the script. curl and wget
are two common command line HTTP clients for that.
## Using curl
```bash hl_lines="12"
#!/bin/sh
# Exit immediately if any command exits with a non-zero status:
set -e
# Do the work here
echo "Pretending to to make backups..."
sleep 5
echo "Backup complete!"
# As the last thing, ping SITE_NAME using curl:
curl --retry 3 PING_URL
```
## Using wget
```bash hl_lines="12"
#!/bin/sh
# Exit immediately if any command exits with a non-zero status:
set -e
# Do the work here
echo "Pretending to to generate reports..."
sleep 5
echo "Report generation complete!"
# As the last thing, ping SITE_NAME using wget:
wget PING_URL -O /dev/null
```

+ 7
- 0
templates/docs/csharp.html View File

@ -0,0 +1,7 @@
<h1>C</h1>
<p>Below is an example of making a HTTP request to SITE_NAME from C#.</p>
<div class="highlight"><pre><span></span><span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">Net</span><span class="p">.</span><span class="n">WebClient</span><span class="p">())</span>
<span class="p">{</span>
<span class="n">client</span><span class="p">.</span><span class="n">DownloadString</span><span class="p">(</span><span class="s">&quot;PING_URL&quot;</span><span class="p">);</span>
<span class="p">}</span>
</pre></div>

+ 10
- 0
templates/docs/csharp.md View File

@ -0,0 +1,10 @@
# C#
Below is an example of making a HTTP request to SITE_NAME from C#.
```csharp
using (var client = new System.Net.WebClient())
{
client.DownloadString("PING_URL");
}
```

+ 12
- 0
templates/docs/email.html View File

@ -0,0 +1,12 @@
<h1>Email</h1>
<p>As an alternative to HTTP/HTTPS requests, you can "ping" checks by
sending an emails to special email addresses.</p>
<h2>Use Case: Email Delivery Monitoring</h2>
<p>Consider a cron job which runs weekly and sends weekly email
reports to a list of e-mail addresses. You have already set up a check to get alerted
when your cron job fails to run. But what you ultimately want to check is if
<strong>your emails are getting sent and delivered</strong>.</p>
<p>The solution: set up another check, and add its email address to your list of
recipient email addresses. Set its Period to 1 week. As long as your weekly email
script runs correctly, and there are no email delivery issues,
SITE_NAME will regularly receive an email, and the check and will stay up.</p>

+ 16
- 0
templates/docs/email.md View File

@ -0,0 +1,16 @@
# Email
As an alternative to HTTP/HTTPS requests, you can "ping" checks by
sending an emails to special email addresses.
## Use Case: Email Delivery Monitoring
Consider a cron job which runs weekly and sends weekly email
reports to a list of e-mail addresses. You have already set up a check to get alerted
when your cron job fails to run. But what you ultimately want to check is if
**your emails are getting sent and delivered**.
The solution: set up another check, and add its email address to your list of
recipient email addresses. Set its Period to 1 week. As long as your weekly email
script runs correctly, and there are no email delivery issues,
SITE_NAME will regularly receive an email, and the check and will stay up.

+ 26
- 0
templates/docs/introduction.html View File

@ -0,0 +1,26 @@
<h2>SITE_NAME</h2>
<p>SITE_NAME is a service for monitoring cron jobs and similar periodic processes:</p>
<ul>
<li>SITE_NAME <strong>listens for pings</strong> from services being monitored.</li>
<li>It <strong>keeps silent</strong> as long as pings arrive on time.</li>
<li>It <strong>raises an alert</strong> as soon as a ping does not arrive on time.</li>
</ul>
<p>SITE_NAME works as a <a href="https://en.wikipedia.org/wiki/Dead_man%27s_switch">dead man's switch</a> for processes that need to
run continuously or on regular, known schedule:</p>
<ul>
<li>filesystem, database backups</li>
<li>task queues</li>
<li>database replication status</li>
<li>report generation scripts</li>
<li>periodic data import and sync jobs</li>
<li>periodic antivirus scans</li>
<li>DDNS updater scripts</li>
<li>SSL renewal scripts</li>
</ul>
<p>SITE_NAME is <em>not</em> the right tool for:</p>
<ul>
<li>monitoring website uptime by probing it with HTTP requests</li>
<li>collecting application performance metrics</li>
<li>error tracking</li>
<li>log aggregation</li>
</ul>

+ 26
- 0
templates/docs/introduction.md View File

@ -0,0 +1,26 @@
## SITE_NAME
SITE_NAME is a service for monitoring cron jobs and similar periodic processes:
* SITE_NAME **listens for pings** from services being monitored.
* It **keeps silent** as long as pings arrive on time.
* It **raises an alert** as soon as a ping does not arrive on time.
SITE_NAME works as a [dead man's switch](https://en.wikipedia.org/wiki/Dead_man%27s_switch) for processes that need to
run continuously or on regular, known schedule:
* filesystem, database backups
* task queues
* database replication status
* report generation scripts
* periodic data import and sync jobs
* periodic antivirus scans
* DDNS updater scripts
* SSL renewal scripts
SITE_NAME is *not* the right tool for:
* monitoring website uptime by probing it with HTTP requests
* collecting application performance metrics
* error tracking
* log aggregation

+ 13
- 0
templates/docs/javascript.html View File

@ -0,0 +1,13 @@
<h1>Javascript</h1>
<p>Below is an example of making a HTTP request to SITE_NAME from Node.js.</p>
<div class="highlight"><pre><span></span><span class="kd">var</span> <span class="nx">https</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;https&#39;</span><span class="p">);</span>
<span class="nx">https</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">&quot;PING_URL&quot;</span><span class="p">);</span>
</pre></div>
<p>You can also send pings from a browser environment. SITE_NAME sets the
<code>Access-Control-Allow-Origin:*</code> CORS header, so cross-domain AJAX requests work.</p>
<div class="highlight"><pre><span></span><span class="kd">var</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;GET&#39;</span><span class="p">,</span> <span class="s1">&#39;PING_URL&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</pre></div>

+ 17
- 0
templates/docs/javascript.md View File

@ -0,0 +1,17 @@
# Javascript
Below is an example of making a HTTP request to SITE_NAME from Node.js.
```js
var https = require('https');
https.get("PING_URL");
```
You can also send pings from a browser environment. SITE_NAME sets the
`Access-Control-Allow-Origin:*` CORS header, so cross-domain AJAX requests work.
```js
var xhr = new XMLHttpRequest();
xhr.open('GET', 'PING_URL', true);
xhr.send(null);
```

+ 30
- 0
templates/docs/measuring_script_run_time.html View File

@ -0,0 +1,30 @@
<h1>Measuring Script Run Time</h1>
<p>Append <code>/start</code> to a ping URL and use it to signal when a job starts.
After receiving a start signal, Healthchecks.io will show the check as "Started".
It will store the "start" events and display the job execution times. The job
execution times are calculated as the time gaps between adjacent "start" and
"complete" events.</p>
<p>Signalling a start kicks off a separate timer: the job now <strong>must</strong> signal a
success within its configured "Grace Time", or it will get marked as "down".</p>
<p>Below is a code example in Python:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">URL</span> <span class="o">=</span> <span class="s2">&quot;PING_URL&quot;</span>
<span class="c1"># &quot;/start&quot; kicks off a timer: if the job takes longer than</span>
<span class="c1"># the configured grace time, the check will be marked as &quot;down&quot;</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">URL</span> <span class="o">+</span> <span class="s2">&quot;/start&quot;</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
<span class="k">except</span> <span class="n">requests</span><span class="o">.</span><span class="n">exceptions</span><span class="o">.</span><span class="n">RequestException</span><span class="p">:</span>
<span class="c1"># If the network request fails for any reason, we don&#39;t want</span>
<span class="c1"># it to prevent the main job from running</span>
<span class="k">pass</span>
<span class="c1"># TODO: run the job here</span>
<span class="n">fib</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">n</span><span class="p">:</span> <span class="n">n</span> <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;</span> <span class="mi">2</span> <span class="k">else</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">&quot;F(42) = </span><span class="si">%d</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">fib</span><span class="p">(</span><span class="mi">42</span><span class="p">))</span>
<span class="c1"># Signal success:</span>
<span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">URL</span><span class="p">)</span>
</pre></div>

+ 35
- 0
templates/docs/measuring_script_run_time.md View File

@ -0,0 +1,35 @@
# Measuring Script Run Time
Append `/start` to a ping URL and use it to signal when a job starts.
After receiving a start signal, Healthchecks.io will show the check as "Started".
It will store the "start" events and display the job execution times. The job
execution times are calculated as the time gaps between adjacent "start" and
"complete" events.
Signalling a start kicks off a separate timer: the job now **must** signal a
success within its configured "Grace Time", or it will get marked as "down".
Below is a code example in Python:
```python
import requests
URL = "PING_URL"
# "/start" kicks off a timer: if the job takes longer than
# the configured grace time, the check will be marked as "down"
try:
requests.get(URL + "/start", timeout=5)
except requests.exceptions.RequestException:
# If the network request fails for any reason, we don't want
# it to prevent the main job from running
pass
# TODO: run the job here
fib = lambda n: n if n < 2 else fib(n - 1) + fib(n - 2)
print("F(42) = %d" % fib(42))
# Signal success:
requests.get(URL)
```

+ 109
- 0
templates/docs/monitoring_cron_jobs.html View File

@ -0,0 +1,109 @@
<h2>Monitoring Cron Jobs</h2>
<p>SITE_NAME is perfectly suited for monitoring cron jobs.
Let's look at an example: a machine with the following cron job:</p>
<div class="highlight"><pre><span></span>$ crontab -l
<span class="c1"># m h dom mon dow command</span>
<span class="m">8</span> <span class="m">6</span> * * * /home/user/backup.sh
</pre></div>
<p>You can use SITE_NAME to get a notification whenever the <code>backup.sh</code> script does not
complete successfully. Here is how to set that up.</p>
<ol>
<li>
<p>If you have not already, sign up for a free SITE_NAME account.</p>
</li>
<li>
<p>In your SITE_NAME account, <strong>add a new check</strong>.</p>
<p>Note: in SITE_NAME, a <strong>check</strong> represents a single service you want to
monitor. For example, a single cron job. For each additional cron job you will
create another check. SITE_NAME pricing plans are structured primarily
around how many checks you can have in the account.</p>
</li>
<li>
<p>Give the check <strong>a meaningful name</strong>. Good naming will become
increasingly important as you add more checks to your account.</p>
</li>
<li>
<p>Edit the check's <strong>schedule</strong>:</p>
<ul>
<li>change its type from "Simple" to "Cron"</li>
<li>enter <code>8 6 * * *</code> in the cron epression field</li>
<li>set the timezone to match your machine's timezone</li>
</ul>
</li>
<li>
<p>Take note of your check's unique <strong>ping URL</strong></p>
</li>
</ol>
<p>Finally, edit your crontab and append a curl or wget call after the command:</p>
<div class="highlight"><pre><span></span>$ crontab -e
<span class="c1"># m h dom mon dow command</span>
<span class="m">8</span> <span class="m">6</span> * * * /home/user/backup.sh <span class="o">&amp;&amp;</span> curl -fsS --retry <span class="m">3</span> PING_URL &gt; /dev/null
</pre></div>
<p>Now, each time your cron job runs, it will send a HTTP request to the ping URL.</p>
<p>Since SITE_NAME knows the schedule of your cron job, it can calculate
the dates and times when the job should run. As soon as your cron job doesn't
report at an expected time, SITE_NAME will send you a notification.</p>
<p>This monitoring technique takes care of various failure scenarios that could
potentially go unnoticed otherwise:</p>
<ul>
<li>The whole machine goes down (power outage, janitor stumbles on wires, VPS provider problems, etc.)</li>
<li>cron daemon is not running, or has invalid configuration</li>
<li>cron does start your task, but the task exits with non-zero exit code</li>
</ul>
<h2>Curl Options</h2>
<p>The extra options tells curl to not print anything to standard output unless
there is an error. Feel free to adjust the curl options to suit your needs.</p>
<table class="table curl-opts">
<tr>
<th>&amp;&amp;</th>
<td>Run curl only if <code>/home/user/backup.sh</code> exits with an exit code 0</td>
</tr>
<tr>
<th>
-f, --fail
</th>
<td>Makes curl treat non-200 responses as errors</td>
</tr>
<tr>
<th>-s, --silent</th>
<td>Silent or quiet mode. Don't show progress meter or error messages.</td>
</tr>
<tr>
<th>-S, --show-error</th>
<td>When used with -s it makes curl show error message if it fails.</td>
</tr>
<tr>
<th>--retry &lt;num&gt;</th>
<td>
If a transient error is returned when curl tries to perform a
transfer, it will retry this number of times before giving up.
Setting the number to 0 makes curl do no retries
(which is the default). Transient error means either: a timeout,
an FTP 4xx response code or an HTTP 5xx response code.
</td>
</tr>
<tr>
<th>&gt; /dev/null</th>
<td>
Redirect curl's stdout to /dev/null (error messages go to stderr,)
</td>
</tr>
</table>
<h2>Looking up Your Machine's Time Zone</h2>
<p>On modern GNU/Linux systems, you can look up the time zone using the
<code>timedatectl status</code> command and looking for "Time zone" in its output:</p>
<div class="highlight"><pre><span></span>$ timedatectl status
Local time: C  2020-01-23 12:35:50 EET
Universal time: C  2020-01-23 10:35:50 UTC
RTC time: C  2020-01-23 10:35:50
<span class="hll"> Time zone: Europe/Riga (EET, +0200)
</span>System clock synchronized: yes
NTP service: active
RTC in local TZ: no
</pre></div>

+ 113
- 0
templates/docs/monitoring_cron_jobs.md View File

@ -0,0 +1,113 @@
## Monitoring Cron Jobs
SITE_NAME is perfectly suited for monitoring cron jobs.
Let's look at an example: a machine with the following cron job:
```bash
$ crontab -l
# m h dom mon dow command
8 6 * * * /home/user/backup.sh
```
You can use SITE_NAME to get a notification whenever the `backup.sh` script does not
complete successfully. Here is how to set that up.
1. If you have not already, sign up for a free SITE_NAME account.
1. In your SITE_NAME account, **add a new check**.
Note: in SITE_NAME, a **check** represents a single service you want to
monitor. For example, a single cron job. For each additional cron job you will
create another check. SITE_NAME pricing plans are structured primarily
around how many checks you can have in the account.
1. Give the check **a meaningful name**. Good naming will become
increasingly important as you add more checks to your account.
1. Edit the check's **schedule**:
* change its type from "Simple" to "Cron"
* enter `8 6 * * *` in the cron epression field
* set the timezone to match your machine's timezone
1. Take note of your check's unique **ping URL**
Finally, edit your crontab and append a curl or wget call after the command:
```bash
$ crontab -e
# m h dom mon dow command
8 6 * * * /home/user/backup.sh && curl -fsS --retry 3 PING_URL > /dev/null
```
Now, each time your cron job runs, it will send a HTTP request to the ping URL.
Since SITE_NAME knows the schedule of your cron job, it can calculate
the dates and times when the job should run. As soon as your cron job doesn't
report at an expected time, SITE_NAME will send you a notification.
This monitoring technique takes care of various failure scenarios that could
potentially go unnoticed otherwise:
* The whole machine goes down (power outage, janitor stumbles on wires, VPS provider problems, etc.)
* cron daemon is not running, or has invalid configuration
* cron does start your task, but the task exits with non-zero exit code
## Curl Options
The extra options tells curl to not print anything to standard output unless
there is an error. Feel free to adjust the curl options to suit your needs.
<table class="table curl-opts">
<tr>
<th>&amp;&amp;</th>
<td>Run curl only if <code>/home/user/backup.sh</code> exits with an exit code 0</td>
</tr>
<tr>
<th>
-f, --fail
</th>
<td>Makes curl treat non-200 responses as errors</td>
</tr>
<tr>
<th>-s, --silent</th>
<td>Silent or quiet mode. Don't show progress meter or error messages.</td>
</tr>
<tr>
<th>-S, --show-error</th>
<td>When used with -s it makes curl show error message if it fails.</td>
</tr>
<tr>
<th>--retry &lt;num&gt;</th>
<td>
If a transient error is returned when curl tries to perform a
transfer, it will retry this number of times before giving up.
Setting the number to 0 makes curl do no retries
(which is the default). Transient error means either: a timeout,
an FTP 4xx response code or an HTTP 5xx response code.
</td>
</tr>
<tr>
<th>&gt; /dev/null</th>
<td>
Redirect curl's stdout to /dev/null (error messages go to stderr,)
</td>
</tr>
</table>
## Looking up Your Machine's Time Zone
On modern GNU/Linux systems, you can look up the time zone using the
`timedatectl status` command and looking for "Time zone" in its output:
```text hl_lines="6"
$ timedatectl status
Local time: C  2020-01-23 12:35:50 EET
Universal time: C  2020-01-23 10:35:50 UTC
RTC time: C  2020-01-23 10:35:50
Time zone: Europe/Riga (EET, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
```

+ 4
- 0
templates/docs/php.html View File

@ -0,0 +1,4 @@
<h1>PHP</h1>
<p>Below is an example of making a HTTP request to SITE_NAME from PHP.</p>
<div class="highlight"><pre><span></span><span class="x">file_get_contents(&#39;https://hc-ping.com/your-uuid-here&#39;);</span>
</pre></div>

+ 7
- 0
templates/docs/php.md View File

@ -0,0 +1,7 @@
# PHP
Below is an example of making a HTTP request to SITE_NAME from PHP.
```php
file_get_contents('https://hc-ping.com/your-uuid-here');
```

+ 23
- 0
templates/docs/powershell.html View File

@ -0,0 +1,23 @@
<h1>PowerShell</h1>
<p>You can use <a href="https://msdn.microsoft.com/en-us/powershell/mt173057.aspx">PowerShell</a>
and Windows Task Scheduler to automate various tasks on a Windows system.
From within a PowerShell script it is also easy to ping SITE_NAME.</p>
<p>Here is a simple PowerShell script that pings SITE_NAME. When scheduled to
run with Task Scheduler, it will essentially just send regular "I'm alive" messages.
You can of course extend it to do more things.</p>
<div class="highlight"><pre><span></span><span class="c1"># inside a PowerShell script:</span>
Invoke-RestMethod PING_URL
</pre></div>
<p>Save the above to e.g. <code>C:\Scripts\healthchecks.ps1</code>.
Then use the following command in a Scheduled Task to run the script:</p>
<div class="highlight"><pre><span></span>powershell.exe -ExecutionPolicy bypass -File C:<span class="se">\S</span>cripts<span class="se">\h</span>ealthchecks.ps1
</pre></div>
<p>In simple cases, you can also pass the script to PowerShell directly,
using the "-command" argument:</p>
<div class="highlight"><pre><span></span><span class="c1"># Without an underlying script, passing the command to PowerShell directly:</span>
powershell.exe -command <span class="p">&amp;</span><span class="o">{</span>Invoke-RestMethod PING_URL<span class="o">}</span>
</pre></div>

+ 29
- 0
templates/docs/powershell.md View File

@ -0,0 +1,29 @@
# PowerShell
You can use [PowerShell](https://msdn.microsoft.com/en-us/powershell/mt173057.aspx)
and Windows Task Scheduler to automate various tasks on a Windows system.
From within a PowerShell script it is also easy to ping SITE_NAME.
Here is a simple PowerShell script that pings SITE_NAME. When scheduled to
run with Task Scheduler, it will essentially just send regular "I'm alive" messages.
You can of course extend it to do more things.
```bash
# inside a PowerShell script:
Invoke-RestMethod PING_URL
```
Save the above to e.g. `C:\Scripts\healthchecks.ps1`.
Then use the following command in a Scheduled Task to run the script:
```bash
powershell.exe -ExecutionPolicy bypass -File C:\Scripts\healthchecks.ps1
```
In simple cases, you can also pass the script to PowerShell directly,
using the "-command" argument:
```bash
# Without an underlying script, passing the command to PowerShell directly:
powershell.exe -command &{Invoke-RestMethod PING_URL}
```

+ 32
- 0
templates/docs/python.html View File

@ -0,0 +1,32 @@
<h1>Python</h1>
<p>If you are already using the requests library, it's convenient to also use it here:</p>
<div class="highlight"><pre><span></span><span class="c1"># using requests:</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;PING_URL&quot;</span><span class="p">)</span>
</pre></div>
<p>Otherwise, you can use the urllib standard module.</p>
<div class="highlight"><pre><span></span><span class="c1"># urllib with python 3.x:</span>
<span class="kn">import</span> <span class="nn">urllib.request</span>
<span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s2">&quot;PING_URL&quot;</span><span class="p">)</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># urllib with python 2.x:</span>
<span class="kn">import</span> <span class="nn">urllib</span>
<span class="n">urllib</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s2">&quot;PING_URL&quot;</span><span class="p">)</span>
</pre></div>
<p>You can include additional diagnostic information in the in the request body (for POST requests), or in the "User-Agent" request header:</p>
<div class="highlight"><pre><span></span><span class="c1"># Passing diagnostic information in the POST body:</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="s2">&quot;PING_URL&quot;</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="s2">&quot;temperature=-7&quot;</span><span class="p">)</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># Passing diagnostic information in the User-Agent header:</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;PING_URL&quot;</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;User-Agent&quot;</span><span class="p">:</span> <span class="s2">&quot;temperature=-7&quot;</span><span class="p">})</span>
</pre></div>

+ 37
- 0
templates/docs/python.md View File

@ -0,0 +1,37 @@
# Python
If you are already using the requests library, it's convenient to also use it here:
```python
# using requests:
import requests
requests.get("PING_URL")
```
Otherwise, you can use the urllib standard module.
```python
# urllib with python 3.x:
import urllib.request
urllib.request.urlopen("PING_URL")
```
```python
# urllib with python 2.x:
import urllib
urllib.urlopen("PING_URL")
```
You can include additional diagnostic information in the in the request body (for POST requests), or in the "User-Agent" request header:
```python
# Passing diagnostic information in the POST body:
import requests
requests.post("PING_URL", data="temperature=-7")
```
```python
# Passing diagnostic information in the User-Agent header:
import requests
requests.get("PING_URL", headers={"User-Agent": "temperature=-7"})
```

+ 7
- 0
templates/docs/ruby.html View File

@ -0,0 +1,7 @@
<h1>Ruby</h1>
<p>Below is an example of making a HTTP request to SITE_NAME from Ruby.</p>
<div class="highlight"><pre><span></span><span class="nb">require</span> <span class="s1">&#39;net/http&#39;</span>
<span class="nb">require</span> <span class="s1">&#39;uri&#39;</span>
<span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">&#39;PING_URL&#39;</span><span class="p">))</span>
</pre></div>

+ 10
- 0
templates/docs/ruby.md View File

@ -0,0 +1,10 @@
# Ruby
Below is an example of making a HTTP request to SITE_NAME from Ruby.
```ruby
require 'net/http'
require 'uri'
Net::HTTP.get(URI.parse('PING_URL'))
```

+ 24
- 0
templates/docs/signalling_failures.html View File

@ -0,0 +1,24 @@
<h1>Signalling failures</h1>
<p>Append <code>/fail</code> to a ping URL and use it to actively signal a failure.
Requesting the <code>/fail</code> URL will immediately mark the check as "down".
You can use this feature to minimize the delay from your monitored service failing
to you getting a notification.</p>
<p>Below is a skeleton code example in Python which signals a failure when the
work function returns an unexpected value or throws an exception:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">requests</span>
<span class="n">URL</span> <span class="o">=</span> <span class="s2">&quot;PING_URL&quot;</span>
<span class="k">def</span> <span class="nf">do_work</span><span class="p">():</span>
<span class="c1"># Do your number crunching, backup dumping, newsletter sending work here.</span>
<span class="c1"># Return a truthy value on success.</span>
<span class="c1"># Return a falsy value or throw an exception on failure.</span>
<span class="k">return</span> <span class="bp">True</span>
<span class="n">success</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">success</span> <span class="o">=</span> <span class="n">do_work</span><span class="p">()</span>
<span class="k">finally</span><span class="p">:</span>
<span class="c1"># On success, requests PING_URL</span>
<span class="c1"># On failure, requests PING_URL/fail</span>
<span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">URL</span> <span class="k">if</span> <span class="n">success</span> <span class="k">else</span> <span class="n">URL</span> <span class="o">+</span> <span class="s2">&quot;/fail&quot;</span><span class="p">)</span>
</pre></div>

+ 28
- 0
templates/docs/signalling_failures.md View File

@ -0,0 +1,28 @@
# Signalling failures
Append `/fail` to a ping URL and use it to actively signal a failure.
Requesting the `/fail` URL will immediately mark the check as "down".
You can use this feature to minimize the delay from your monitored service failing
to you getting a notification.
Below is a skeleton code example in Python which signals a failure when the
work function returns an unexpected value or throws an exception:
```python
import requests
URL = "PING_URL"
def do_work():
# Do your number crunching, backup dumping, newsletter sending work here.
# Return a truthy value on success.
# Return a falsy value or throw an exception on failure.
return True
success = False
try:
success = do_work()
finally:
# On success, requests PING_URL
# On failure, requests PING_URL/fail
requests.get(URL if success else URL + "/fail")
```

+ 26
- 12
templates/front/base_docs.html View File

@ -4,25 +4,39 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-sm-2">
<div class="col-sm-3">
<ul class="docs-nav"> <ul class="docs-nav">
<li {% if section == "home" %} class="active" {% endif %}>
<a href="{% url 'hc-docs' %}">How {% site_name %} Works</a>
</li>
<li {% if section == "api" %} class="active" {% endif %}>
<a href="{% url 'hc-docs-api' %}">API Reference</a>
</li>
<li {% if section == "cron" %} class="active" {% endif %}>
<a href="{% url 'hc-docs-cron' %}">Cron Syntax</a>
<li class="nav-header">Monitoring</li>
{% include "front/docs_nav_item.html" with slug="introduction" title="Introduction" %}
{% include "front/docs_nav_item.html" with slug="monitoring_cron_jobs" title="Monitoring cron jobs" %}
{% include "front/docs_nav_item.html" with slug="signalling_failures" title="Signalling failures" %}
{% include "front/docs_nav_item.html" with slug="measuring_script_run_time" title="Measuring script run time" %}
<li class="nav-header">Platforms</li>
{% include "front/docs_nav_item.html" with slug="bash" title="Bash" %}
{% include "front/docs_nav_item.html" with slug="python" title="Python" %}
{% include "front/docs_nav_item.html" with slug="ruby" title="Ruby" %}
{% include "front/docs_nav_item.html" with slug="php" title="PHP" %}
{% include "front/docs_nav_item.html" with slug="csharp" title="C#" %}
{% include "front/docs_nav_item.html" with slug="javascript" title="Javascript" %}
{% include "front/docs_nav_item.html" with slug="powershell" title="PowerShell" %}
{% include "front/docs_nav_item.html" with slug="email" title="Email" %}
<li class="nav-header">Developer Tools</li>
<li{% if section == "api" %} class="active"{% endif %}>
<a href="{% url 'hc-docs-api' %}">API</a>
</li> </li>
<li {% if section == "resources" %} class="active" {% endif %}>
<a href="{% url 'hc-docs-resources' %}">Third-Party Resources</a>
<li {% if section == "resources" %} class="active"{% endif %}>
<a href="{% url 'hc-docs-resources' %}">Third-party resources</a>
</li> </li>
<li class="nav-header">Cron</li>
<li><a href="{% url 'hc-docs-cron' %}">Cron Syntax Cheatsheet</a></li>
</ul> </ul>
</div> </div>
<div class="col-sm-10">
<div class="col-sm-9">
{% block docs_content %} {% block docs_content %}
{% endblock %} {% endblock %}
</div> </div>


+ 2
- 2
templates/front/docs_cron.html View File

@ -1,4 +1,4 @@
{% extends "front/base_docs.html" %}
{% extends "base.html" %}
{% load hc_extras %} {% load hc_extras %}
{% block title %}Cron Syntax Cheatsheet - {% site_name %}{% endblock %} {% block title %}Cron Syntax Cheatsheet - {% site_name %}{% endblock %}
@ -11,7 +11,7 @@
<meta name="keywords" content="cron syntax, crontab syntax, cron howto, cron tutorial, cron jobs, cron job example, monitor cron jobs"> <meta name="keywords" content="cron syntax, crontab syntax, cron howto, cron tutorial, cron jobs, cron job example, monitor cron jobs">
{% endblock %} {% endblock %}
{% block docs_content %}
{% block content %}
<h2>Cron Syntax Cheatsheet</h2> <h2>Cron Syntax Cheatsheet</h2>


+ 3
- 0
templates/front/docs_nav_item.html View File

@ -0,0 +1,3 @@
<li{% if slug == section %} class="active"{% endif %}>
<a href="{% url 'hc-serve-doc' slug %}">{{ title }}</a>
</li>

+ 22
- 0
templates/front/docs_single.html View File

@ -0,0 +1,22 @@
{% extends "front/base_docs.html" %}
{% load compress static hc_extras %}
{% block title %}Documentation - {% site_name %}{% endblock %}
{% block description %}
<meta name="description" content="Monitor any service that can make a HTTP request or send an email: cron jobs, Bash scripts, Python, Ruby, Node, PHP, JS, ...">
{% endblock %}
{% block keywords %}
<meta name="keywords" content="healthchecks, crontab monitoring, python health check, bash health check, cron monitoring, cron tutorial, cron howto, api health check, open source">
{% endblock %}
{% block docs_content %}<div class="docs-content">{{ content|safe }}</div>{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/clipboard.min.js' %}"></script>
<script src="{% static 'js/snippet-copy.js' %}"></script>
{% endcompress %}
{% endblock %}

Loading…
Cancel
Save