Widgets and dashboard with Atlasboard, Node.js and d3.js
So it’s been a while since I added something to this blog. I’ve been very busy with work and at the same time finishing up my second book on Three.js. For our company we’re looking for a new and flexible way to create dashboards. We want to use these kinds of dashboard to monitor the various development teams, provide a complete realtime overview for the IT manager and even try to monitor our complete devops process. A colleague of mine mentioned Atlasboard from atlassian. A really simple and straightforward dashboard with a very modular architecture. In this article I’ll give a quick overview on how easy it is to create your own widgets and jobs.
Installing Atlasboard
Atlasboard uses node.js as a container. So to install Atlasboard, first make sure you’ve got node.js and npm installed. Once installed you can use npm to install Atlasboard:
npm install -g atlasboard
Note that when you do this on windows you might run into some issues starting Atlasboard. See the following thread here for the solution: https://bitbucket.org/atlassian/atlasboard/issue/61/cant-create-dashboard-on-windows
And that’s it. Now you can create a dashboard that runs on Atlasboard. Go to the directory where you want to create your dashboard and do the following:
atlasboard new mydashboard
This will create a directory called mydashboard. Move to this directory and start the atlasboard server:
cd mydashboard
atlasboard start 3333
Now open your browser, point it to http://localhost:3333 and you’ll see your first dashboard:
Understanding Atlasboard
To understand atlasboard you need to understand it’s main components. The wiki from Atlasboard has some basic information on this, but its very easy to understand. The following figure from Atlasboard wiki explains pretty much everything:
Basically to work with Atlasboard you need to understand the following features:
- Jobs: A job is a simple javascript file that is executed by node.js at a scheduled interval. With this job you can pull in information from various sources which can then be displayed by a widget. This can be any type of information. For instance github statistics, jenkins statistics, build results, sonar results, twitter feeds etc. Atlasboard doesn't use a database by default to keep track of the information retrieved by its jobs, but if you'd like you could easily ad mongoDB or something else.
- Widgets: Consists out of a javascript file, an html template and a css file. When a job fires, it sends the information it retrieved to a widget, which can then display it. Atlasboard comes with a couple of standard widgets you can use, but, as you'll see in this article, creating custom widgets is very easy.
- Dashboards: Defines the size of the individual widgets and their position on screen. In this file you also define which widget and job are tied together, the interval for each of the jobs, and provides you with a convenient way to configure widgets and jobs.
In the next section we’ll show you how to create your own custom widgets and jobs. The very simple goal is to create the following dashboard, which shows a gauge and a graph which both show the same information: the free memory on my system:
Setup the dashboard
First of, lets setup the dashboard file. In the directories created by Atlasboard, you’ll find a file called myfirst_dashboard.json. If you open this file you’ll see the configuration for all the example widgets and jobs for the demo dashboard. Open this file, and change it to this:
{
"layout": {
"title": false,
"customJS" : ["jquery.peity.js"],
"widgets" : [
{"row" : 1, "col" : 1, "width" : 4, "height" : 2, "widget" : "freemem", "job" : "freemem", "config": "freemem-config"},
{"row" : 2, "col" : 1, "width" : 4, "height" : 2, "widget" : "gauge", "job" : "freemem2", "config": "gauge-config"}
]
},
"config" : {
"gauge-config" : {
"interval" : 1000
},
"freemem-config" : {
"interval" : 1000
}
}
}
In the layout part of this file we define where the widgets are positioned on screen, which widget to use, widget job to use and which config to use. So when we look at the first line, you can see that we expect the freemem widget to use the freemem job and the freemem-config. This last part can directly be seen in the config part of this file (note there is config inheritance in atlasboard, but I’ll skipt that for now).
Create the jobs
So lets create the appropriate jobs and widgets. Atlasboard provides a command line for this. So do the following in your dashboard directory:
atlasboard generate widget freemem
atlasboard generate widget gauge
atlasboard generate job freemem
atlasboard generate job freemem2
This will create the necessary files for the widgets and jobs in the packages/default directory. Lets start by looking at the job (freemem and freemem2 are the same). The following listing provides the complete content for the freemem.js job:
var os = require('os');
/**
* Job: freemem
*
* Expected configuration:
*
* { }
*/
var data = [];
var MAX_LENGTH = 100;
module.exports = function(config, dependencies, job_callback) {
// add the correct information to the data array
var length = data.push(os.freemem());
if (length > MAX_LENGTH) {
data.shift();
}
job_callback(null, {
data: data,
total: os.totalmem()
});
};
This code uses the node.js os module to read the free and total memory of my system. By using the job_callback function we send two data elements to the widget at the front. The freemem2.js file is exactly the same. It seems that atlasboard can’t use the same job for the same widgets twice. Since I wanted to share the information I just created two jobs that look the same. Not the best solution, but the best I found so far :)
Create the widgets
Now all that is left to do is create the widgets. Lets first look at the graph. For the graph I used rickshaw which is included in atlasboard. Atlasboard also provides an easier to use interface to create graphs (https://bitbucket.org/atlassian/atlasboard-charts-package), but I like the direct Rickshaw approach better. The code for a widget is very simple:
freemem.css:
.content {
font-size: 35px;
color: #454545;
font-weight: bold;
text-align: center;
}
freemem.html:
<h2>freemem</h2>
<div class="content">
<div id="graph"></div>
<div id="text"></div>
</div>
freemem.js:
widget = {
//runs when we receive data from the job
onData: function (el, data) {
//The parameters our job passed through are in the data object
//el is our widget element, so our actions should all be relative to that
if (data.title) {
$('h2', el).text(data.title);
}
var graphElement = document.querySelector("#graph");
var textElement = document.querySelector("#text");
while (graphElement.firstChild) {
graphElement.removeChild(graphElement.firstChild);
}
var dataArray = [];
var count = 0;
data.data.forEach(function(e){
dataArray.push({x: count++, y:e});
});
var graph = new Rickshaw.Graph({
element: document.querySelector("#graph"),
height: 350,
renderer: 'area',
stroke: true,
series: [
{
data: dataArray,
color: 'rgba(192,132,255,0.3)',
stroke: 'rgba(0,0,0,0.15)'
}
]
});
graph.renderer.unstack = true;
graph.render();
$(textElement).html("" + new Date());
}
};
If you look through the code of freemem.js you can see that we don’t really do anything complex. We just parse the data we receive and use Rickshaw to draw the graph. Easy right?
If you look at the sourcecode for gauge it isn’t that much more complex. I’ve used the d3.js based gauge from here: http://tomerdoron.blogspot.co.il/2011/12/google-style-gauges-using-d3js.html
And changed to code so it’ll react to the updates from the job:
widget = {
gauges: [],
//runs when we receive data from the job
onData: function(el, data) {
//The parameters our job passed through are in the data object
//el is our widget element, so our actions should all be relative to that
if (data.title) {
$('h2', el).text(data.title);
}
var gaugeContainer = document.querySelector("#memoryGaugeContainer");
// if no gauge is there yet, create one;
if (gaugeContainer.childNodes.length != 1) {
this.gauges['memory'] = createGauge("memory", "Memory");
}
var freePercentage = 100-(data.data.pop()/(data.total/100));
var gauge = this.gauges['memory'];
gauge.redraw(freePercentage);
}
};
function createGauge(name, label, min, max)
...
}
function Gauge(placeholderName, configuration)
{
...
}
the createGauge and Gauge object were taken from the previous link. I only implemented the widget code. Easy right :)
Conclusions
That’s it for this first article on Atlasboard. We’re seriously considering implementing this at work, so I’ll try to give an update in a couple of weeks. Overall I really like the approach Atlasboard takes and how easy it is to create new widgets and jobs.