Real-time hit log using node.js and nowjs
When looking to break into a new technology, I usually like to go in head strong, and build something that I can use in our live applications. Ever since first learning about node.js, I have wanted to build a new hitlog for our application. The hitlog is basically an activity monitor of what is going on right now, in our application. It's basically a extreme access logs, with a lot of custom data, and I stream that to the browser using AJAX. We have various logs, for various aspects of our application, and like to monitor activity from time to time, in order to see what's going on, if there are any problems, and when a problem occurs, we can easily search the logs for a particular user. Quite frankly, I just like to see live, moving things.
What I found when searching for real-time data, was that most people were creating things that are of no use to me. Such as chasing cursors around a screen. Not that I'm saying that isn't a great starting point for many projects and even I will look at the code and thing oooh, ahhh.
Getting started.
While rummaging around trying to find a place to start, I ran across nowjs. A real-time framework for server and client communications using node.js and socket.io. What I love about it is, a client can talk to all clients, or the server can talk to all clients. Clients can be added to groups, and it can detect connects and disconnects. The examples are generally for creating web based chat, but it offers much more functionality that can be used for say.... this project.
The best part of node.js and nowjs is that I managed to build the server and client in a matter of minutes.
To start, I installed node.js on my test server, and installed the nowjs module. nowjs is simple to install with:
npm install now
..and we're ready to go.
So, I created a folder for the new project, and created two files, server.js and client.html.
In server.js we want to start by including a few modules and creating the http server that will process post requests from the application, and send data to clients.
var fs = require('fs'),
sys = require('sys'),
querystring = require('querystring');
var server = require('http').createServer(function(req, response){
if (req.method === "POST"){
// Process POST data
} else {
// Handle clients
});
server.listen(8080);From here we need to setup nowjs to handle the clients and send the incoming hits to connected clients. To do this, we create the now and everyone objects for nowjs.
var now = require("now");
var everyone = now.initialize(server);With these defined, we are now ready to start the client and post data handling which makes this thing work. :-) Lets start with the client. The way nowjs works is that everything is shared between client and server. I can create a function or variable in one place, and access it in the other. Since this is a purpose built node.js server, we don't need to serve any more than 1 file. In the code below, if it's not a post, we will only serve one file, client.html.
var fs = require('fs'),
sys = require('sys'),
querystring = require('querystring');
var server = require('http').createServer(function(req, response){
if (req.method === "POST"){
// Process POST data
} else {
fs.readFile(__dirname+'/client.html', function(err,data){
response.writeHead(200, {'Content-Type':'text/html'});
response.write(data);
response.end();
});
});
server.listen(8080);
var now = require("now");
var everyone = now.initialize(server);Now as you can see, we've added the now and everyone objects, and all requests made to the server will serve client.html. Next we need to take post data from the application, and send it out to our connected clients. To do this, we will add event listeners for data and end of the post request.
var fs = require('fs'),
sys = require('sys'),
querystring = require('querystring');
var server = require('http').createServer(function(req, response){
if (req.method === "POST"){
var post = '';
req.addListener("data",function(chunk){
post += chunk;
}).addListener("end",function(){
var postData = querystring.parse(post);
console.log(sys.inspect(postData)+"\n");
everyone.now.receiveMessage("[ "+postData.i+" ] "+postData.p+" [ "+postData.u+" ] [ "+postData.t+" ] [ "+postData.s+" ] "+postData.r+"");
response.writeHead(200, {'Content-Type':'text/html'});
response.end();
});
} else {
fs.readFile(__dirname+'/client.html', function(err,data){
response.writeHead(200, {'Content-Type':'text/html'});
response.write(data);
response.end();
});
});
server.listen(8080);
var now = require("now");
var everyone = now.initialize(server);There are plenty of tutorials out there on how to handle post data, I just picked a simple method that worked. The point of interest here is the everyone.now.receiveMessage() function. This is not defined, and is not a property of everyone or now. I'll get to that in a minute. The important point to note here, is that my server is now complete. That's it, though I'm sure it needs some refining, security, etc. In a nutshell, that's 25 lines of code, and we're ready to go server side. Now lets take a look at the client.
As simple as the server was to write, you would think that the client were much more in depth. It may be when I'm done with it, but for the purpose of this tutorial, it was surprisingly simple. Basically we needed to include two dependencies, jquery and now.js.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" type="text/javascript"></script> <script src="/nowjs/now.js"></script>
Once we have that in place, all we really need to do is define now.receiveMessage() and create the messages div, as seen below.
<script>
$(document).ready(function(){
now.receiveMessage = function(message){
$("#messages").append("<br>" + message);
}
});
</script>And we add our messages div in the body.
<div id="messages"></div>
Now, I won't get into posting the data from the application to the node.js server. Basically, I add a script to the footer of each page which posts data via ajax to a script on the server which curls that data over to our node.js server. :-) Once we have the client and the server set up, all we have to do is run it.
node server.js
Once connected to the client, and pushing the app script live, we start to see hits pop up in our simple, no-db hit log. :-)
You can see the full source of both files below and I'm going to go work on my next node.js project!
Cheers!
var fs = require('fs'),
sys = require('sys'),
querystring = require('querystring');
var server = require('http').createServer(function(req, response){
if (req.method === "POST"){
var post = '';
req.addListener("data",function(chunk){
post += chunk;
}).addListener("end",function(){
var postData = querystring.parse(post);
console.log(sys.inspect(postData)+"\n");
everyone.now.receiveMessage("[ "+postData.i+" ] "+postData.p+" [ "+postData.u+" ] [ "+postData.t+" ] [ "+postData.s+" ] "+postData.r+"");
response.writeHead(200, {'Content-Type':'text/html'});
response.end();
});
} else {
fs.readFile(__dirname+'/client.html', function(err,data){
response.writeHead(200, {'Content-Type':'text/html'});
response.write(data);
response.end();
});
});
server.listen(8080);
var now = require("now");
var everyone = now.initialize(server);
client.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Real Time Test</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script src="/nowjs/now.js"></script>
<script>
$(document).ready(function(){
now.receiveMessage = function(message){
$("#messages").append("<br>" + message);
}
});
</script>
</head>
<body>
<div id="messages"></div>
</body>
</html>