I'm an old fashioned guy, so the first thing that comes to my mind is : "syslog". We could certainly find fancier ways (yes, I'm looking at you, scala-*), but syslog will do for now.
Once again, I'll use an Ubuntu 12.04 instance in EC2, which happens to have a running rsyslogd :) Let's take a look at the main config file, /etc/rsyslogd.conf.
We'll use UDP to ship our logs. However, because of this unbelievable 2-year old bug, we can't use port 514 (the default port for syslog): we'll use 5140 instead, don't forget to open 5140/UDP in the security group of the EC2 instance!
# provides UDP syslog reception
$ModLoad imudp
$UDPServerRun 5140
Let's now look at the default rules, located in /etc/rsyslog.d/50-default.conf. Let's keep it very simple and make sure we log everything. We'll also add a new template, because the default one misses some useful information, such as priority (duh!).
$template TraditionalFormatWithPRI,"%pri-text%: %timegenerated% %HOSTNAME% %syslogtag%%msg:::drop-last-lf%\n"
*.* /var/log/messages;TraditionalFormatWithPRI
I hear my sysadmin team booing the ugliness of this setup. Yeah, I love you too, bitchez :-P
Anyway, this should be enough, now we just need to restart rsyslogd:
ubuntu@ip-10-34-245-176:~$ sudo service rsyslog restart
ubuntu@ip-10-37-166-19:~$ echo "<14>Node sez: I see you"|nc -u -v -w0 10.34.245.176 514014>
Ain2 worked right out of the box. If you're only looking for a syslog module, I would recommend this one. However, I ended up picking winston, an interesting logging framework supporting many different transports (file, http, several databases, etc) including syslog through the winston-syslog module.
In theory, at least. For the life of me, I couldn't get this thing to work, despite a lot of creative hacking and creative swearing too. Fortunately, winston-syslogUdp works: "It was created when the authors experienced challenges with winston-syslog". Yeah, tell me about it! Good job, guys.
winston requires a newer version of Node.js than the one available in the Ubuntu repo, so let's take care of this upgrade first:
ubuntu@ip-10-37-166-19:~$ sudo add-apt-repository ppa:chris-lea/node.js
- don't log to the console
- log to a local file
- log to a remote syslog server
While I was at it, I cleaned the code a little bit and also solved the MongoDB ObjectId validation issue like this. Not sure if it's the canonical way, but it works.
// Check that the ObjectId is valid
try {
oid = new require('mongodb').ObjectID.createFromHexString(query.id);
}
catch (err) {
winston.info("Invalid OID, err="+err);
response.end();
return;
}
Here's the full code.
Let's run this, hit it with some requests and look at the logs :)
ubuntu@ip-10-37-166-19:~$ node www5.js &
ubuntu@ip-10-37-166-19:~$ tail -f log.txt
{"level":"info","message":"*** NODE APP STARTED ***","timestamp":"2013-08-05T15:07:36.678Z"}
{"level":"info","message":"Web server started","timestamp":"2013-08-05T15:07:36.823Z"}
{"level":"info","message":"Connected to memcache","timestamp":"2013-08-05T15:07:36.850Z"}
{"level":"info","message":"Connected to database","timestamp":"2013-08-05T15:07:36.876Z"}
ubuntu@ip-10-34-245-176:~$ tail -f /var/log/messages
local0.info<134>: Aug 5 15:07:34 10.37.166.19 {"message":"*** NODE APP STARTED ***"}134>
local0.info<134>: Aug 5 15:07:34 10.37.166.19 {"message":"Web server started"}134>
local0.info<134>: Aug 5 15:07:34 10.37.166.19 {"message":"Connected to memcache"}134>
local0.info<134>: Aug 5 15:07:34 10.37.166.19 {"message":"Connected to database"}134>
GET http://ec2-54-247-53-161.eu-west-1.compute.amazonaws.com:8080/?id=0
local0.info<134>: Aug 5 15:11:51 10.37.166.19 {"message":"Request received, id=0"}134>
local0.info<134>: Aug 5 15:11:51 10.37.166.19 {"message":"Finding all documents"}134>
GET http://ec2-54-247-53-161.eu-west-1.compute.amazonaws.com:8080/?id=51e3ce08915082db3df32bf7
local0.info<134>: Aug 5 15:12:19 10.37.166.19 {"message":"Request received, id=51e3ce08915082db3df32bf7"}134>
local0.info<134>: Aug 5 15:12:19 10.37.166.19 {"message":"Cache miss, key 51e3ce08915082db3df32bf7. Querying..."}134>
local0.info<134>: Aug 5 15:12:19 10.37.166.19 {"message":"Item found: {\"_id\":\"51e3ce08915082db3df32bf7\",\"x\":8}"}134>
local0.info<134>: Aug 5 15:12:19 10.37.166.19 {"message":"Stored key=51e3ce08915082db3df32bf7, value=8"}134>
local0.info<134>: Aug 5 15:12:21 10.37.166.19 {"message":"Request received, id=51e3ce08915082db3df32bf7"}134>
local0.info<134>: Aug 5 15:12:21 10.37.166.19 {"message":"Cache hit, key=51e3ce08915082db3df32bf7, value=8"}134>
The same one, more than 60 seconds later :)
local0.info<134>: Aug 5 15:14:15 10.37.166.19 {"message":"Request received, id=51e3ce08915082db3df32bf7"}134>
local0.info<134>: Aug 5 15:14:15 10.37.166.19 {"message":"Cache miss, key 51e3ce08915082db3df32bf7. Querying..."}134>
local0.info<134>: Aug 5 15:14:15 10.37.166.19 {"message":"Item found: {\"_id\":\"51e3ce08915082db3df32bf7\",\"x\":8}"}134>
local0.info<134>: Aug 5 15:14:15 10.37.166.19 {"message":"Stored key=51e3ce08915082db3df32bf7, value=8"}134>
local0.info<134>: Aug 5 15:17:22 10.37.166.19 {"message":"Invalid OID, err=Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"}134>
And so now, our web app haz logz and at 131 lines of code (comments included), it's not bloated either. Pretty amazing.
That's it for today. Keep hacking :)