Graphing DSL network connectivity with Google Charts API
I’m suffering from poor DSL internet stability (first world problems), meaning a couple of outages per day. The breaks are short, but any established connections will drop and it’s pretty annoying when that happens while in the middle of something. Since I’m not sure if it’s getting worse, I decided to start monitoring connectivity. First I had a look at some existing tools to do network monitoring. They are mostly focused on throughput, and it’s not what I’m interested in, so why not script something together and let the raspberry pi handle it.
For testing connectivity we can use the venerable ping
command. Sending out
one ping message every 10 seconds is good enough for me.
$ ping -i 10 8.8.4.4
PING 8.8.4.4 (8.8.4.4): 56 data bytes
64 bytes from 8.8.4.4: icmp_seq=0 ttl=49 time=11.398 ms
^C
The IP address shown here is only an example. In reality I use an address at
the ISP.
Parse the output to determine whether the line was up or down. Store the
result in a time-series database and we’re almost done. What I don’t want
however, is having to run a heavy software stack on the raspi.
Note that since the link state only changes up/down and down/up, I only
store state changes and not each and every measurement. This saves space
and doing lots of IOs for no reason. Rather than using a database, I simply
write to a log:
2016-04-03 00:00:00 up
2016-04-03 02:23:57 down
2016-04-03 02:25:27 up
2016-04-03 11:33:29 down
2016-04-03 11:35:09 up
Parsing ping
output isn’t very hard. Now to graph the collected data in a
chart. For this I chose to use Google Charts. It’s a JavaScript library for
graphing data. It can draw many kinds of charts, but for our connectivity
data the timeline
chart is ideal.
Google’s examples show HTML snippets with hard-coded JavaScript code with
hard-coded data table rows, which is fine as an example, but let’s make it a
little more dynamic. We can have the automatic pinger write out a JSON
data file and load it into the google chart. The expected JSON is made up
of a definition of columns (what fields are there, and what are their types)
and a number of data rows. The rows have “c
” and “v
” fields to define
values. The timeline chart expects a start and end time for every bar.
There is a fishy thing about this that needs mentioning; google charts JSON
uses a date format that is different from JavaScript’s Date.toJSON()
method.
The upside is that it looks more intuitive as a JavaScript Date
object,
but the downside is that you can’t mix it easily with real JavaScript
Date
objects because it isn’t one. Confusing? Yup.
You can set the date to zeroes and only pass the timestamp, as shown:
{
"cols": [{"type": "string", "id": "date"},
{"type": "string", "id": "state"},
{"type": "date", "id": "start"},
{"type": "date", "id": "end"}],
"rows": [{"c":[{"v": "2016-04-03 up"},{"v": "up"},
{"v": "Date(0, 0, 0, 0, 0, 0)"},
{"v": "Date(0, 0, 0, 2, 23, 57)"}]},
{"c":[{"v": "2016-04-03 down"},{"v": "down"},
{"v": "Date(0, 0, 0, 2, 23, 57)"},
{"v": "Date(0, 0, 0, 2, 25, 27)"}]},
...
OK, now that we’ve gotten the right JSON format (mind ye, JSON does not
permit a trailing comma after the last row), let’s load it up into the page.
It uses jQuery, so remember to include a <script>
tag for jquery.min.js
in the top of the HTML page.
google.charts.load('current', {'packages':['timeline'], callback: drawChart});
function drawChart() {
var jsonData = $.ajax({
url: 'pinger.json',
dataType: 'json',
async: false
}).responseText;
var data = new google.visualization.DataTable(jsonData);
var chart = new google.visualization.Timeline(document.getElementById('chart_div'));
chart.draw(data);
}
Note that this code is outside $(document).ready()
.
I added some options to change the colors and some small code to add the
current time as a final data row—otherwise the timeline would end at the
last state change, rather than at what is ‘now’. Anyway, the result looks
pretty nice. You can also plot ‘up’ and ‘down’ in the same bar, but I decided
to have the ‘link down’ moments stand out more:
As happy as I am with how this project turned out, the bad connectivity is a bit of a downer. I tried fiddling with modem settings, but the situation only got much worse. I live far away from the telco equipment and there is no option for cable or fiber here, so that’s just the way it is for now.