Taking a Live Look: How to Observe Your Node.js Application Logs in Real-Time

Introduction

In this article, I’ll be demonstrating how you can add a “live” console web tail to your Node.js application hosted on BlueMix in a few easy steps without cluttering up your application code. We’ll be achieving this by overloading the Console object, something you find in almost every javascript program. This is a handy feature for real-time debugging as well as monitoring your running applications. It also serves as a prototype for modifying console statements or adding entirely new console types. I’ll be assuming you have at least have a basic familiarity with Node.js. The instructions vary slightly depending on if you’d like to use an Express server or an HTTP server, so I have provided a set of steps and an example for both. The inspiration for this project/hack was a blog post about overloading the console functions while preserving their functionality found here by Udi Talias.

Before we start, here are the links for the finished Express example and the finished HTTP example.

Steps for Express

We’ll start by taking a look at a sample Express application:

'use strict';<br>
// dependencies<br>
var http        =  require('http')<br>
  , express     =  require('express');<p></p>
<p>// port and host<br>
var host        = process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';<br>
var port        = process.env.VCAP_APP_PORT || process.env.PORT || 3000;</p>
<p>// create Express server<br>
var app = express();</p>
<p>// set up middleware<br>
app.use(express.bodyParser());<br>
app.use(express.logger('dev'));</p>
<p>// endpoint that creates logs to demonstrate web tail<br>
app.get('/', function(req, res){<br>
    setInterval(function() {<br>
        console.log("you would be seeing the logs of your app");<br>
    }, 3000);<br>
    setInterval(function() {<br>
        console.log("right here.");<br>
    }, 1500);<br>
    setInterval(function() {<br>
        console.log("as it runs");<br>
    }, 1500);<br>
    setInterval(function() {<br>
        console.log("isn't that exciting?");<br>
    }, 5000);<br>
    res.end("Hello world.  I'm demonstrating the functionality of /dash.html by logging test messages on an interval.");<br>
});</p>
<p>app.listen(port, host);</p>
<p></p>
Scroll to view full table

Not much more than a basic Hello World Express application, that will create some console.log chatter for us to observe in our web tail. For reference, this is the starting file structure of our project:

C:\Users\IBM_ADMIN\console tailing\&gt;tree /F<br>
Folder PATH listing<br>
Volume serial number is 1E2C-67D3<br>
C:.<br>
    app.js<br>
    manifest.yml<br>
    package.json<br>
Scroll to view full table

Now we can jump into installing our live console with the following steps.

  • Modify your package.json file in your project, and add the following dependencies if you aren’t already using them: socket.io and ansi-to-html. For example:

    "dependencies":<br>
    {<br>
        "express": "3.4.7",<br>
        "socket.io": "*",<br>
        "ansi-to-html": "0.1.1"<br>
    }<br>
    Scroll to view full table
  • Create two folders in your project folder, named lib and public. Drop console.js into lib and drop tail.html in public. Your file structure should now look something like this (feel free to ignore my stylesheets folder, it isn’t required) :

    C:\Users\IBM_ADMIN\console tailing&gt;tree /F<br>
    Folder PATH listing<br>
    Volume serial number is 1E2C-67D3<br>
    C:.<br>
    │   app.js<br>
    │   manifest.yml<br>
    │   package.json<br>
    │<br>
    ├───lib<br>
    │       console.js<br>
    │<br>
    └───public<br>
        │   tail.html<br>
        │<br>
        └───stylesheets<br>
                index.css<br>
    Scroll to view full table
  • Modify lines 13 and 16 in tail.html so that they point to the URL of your BlueMix application, for example:

    &lt;script src="https://.ng.bluemix.net/socket.io/socket.io.js"&gt;<p></p>
    <p>var socket = io.connect("https://.ng.bluemix.net");<br>
    </p>
    Scroll to view full table
  • The only thing left to do is modify our app.js slightly. What we’ll need to do is

    • add this line so that your Express server will serve static files from your public directory:

      app.use(express.static(__dirname + '/public'));<br>
      Scroll to view full table
    • replace the line of code telling your app to listen on the removed port and host variables with a line requiring console.js and passing your Express server to it as a parameter.

      // app.listen(port, host);<br>
      var custom_console = require('./lib/console.js')(app);<br>
      Scroll to view full table

    So that when you’re all done, it looks like this:

    'use strict';<br>
    // dependencies<br>
    var http        =  require('http')<br>
      , express     =  require('express');<p></p>
    <p>// port and host<br>
    var host        = process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';<br>
    var port        = process.env.VCAP_APP_PORT || process.env.PORT || 3000;</p>
    <p>// create Express server<br>
    var app = express();</p>
    <p>// set up middleware<br>
    app.use(express.bodyParser());<br>
    // line for serving static files from public directory<br>
    app.use(express.static(__dirname + '/public'));<br>
    app.use(express.logger('dev'));</p>
    <p>// endpoint that creates logs to demonstrate web tail<br>
    app.get('/', function(req, res){<br>
        setInterval(function() {<br>
            console.log("you would be seeing the logs of your app");<br>
        }, 3000);<br>
        setInterval(function() {<br>
            console.log("right here.");<br>
        }, 1500);<br>
        setInterval(function() {<br>
            console.log("as it runs");<br>
        }, 1500);<br>
        setInterval(function() {<br>
            console.log("isn't that exciting?");<br>
        }, 5000);<br>
        res.end("Hello world.  I'm demonstrating the functionality of /dash.html by logging test messages on an interval.");<br>
    });</p>
    <p>// app.listen(port, host);<br>
    // require to connect console.js by passing the express server<br>
    // Anonymous Function that prefixes timestamps to console.log<br>
    var custom_console = require('./lib/console.js')(app);<br>
    </p>
    Scroll to view full table

And we’re done! Jump down to the Result’s section to test drive it.

Steps for HTTP

We’ll start by taking a look at a sample Node.js application using an HTTP server:

'use strict';<br>
// dependencies<br>
var app = require('http').createServer(handler)<br>
  , fs = require('fs');<p></p>
<p>// port and host<br>
var host        = process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';<br>
var port        = process.env.VCAP_APP_PORT || process.env.PORT || 3000;</p>
<p>function handler (req, res) {<br>
    res.writeHead(200);<br>
    triggerConsole();<br>
    res.end("Hello world!  I am demonstrating the live web console tail.  Navigate to /tail.html and check it out.");<br>
}</p>
<p>// dummy function used to recreate standard application log "chatter"<br>
function triggerConsole() {<br>
    setInterval(function() {<br>
        console.log("you would be seing the logs of your app");<br>
    }, 3000);<br>
    setInterval(function() {<br>
        console.log("right here.");<br>
    }, 1500);<br>
    setInterval(function() {<br>
        console.log("as it runs");<br>
    }, 1500);<br>
    setInterval(function() {<br>
        console.log("isn't that exciting?");<br>
    }, 5000);<br>
}</p>
<p>app.listen(port, host);<br>
</p>
Scroll to view full table

Not much more than a basic Hello World application, that will create some console.log chatter for us to observe in our web tail. For reference, this is the starting file structure of our project:

C:\Users\IBM_ADMIN\console tailing\&gt;tree /F<br>
C:.<br>
    app.js<br>
    manifest.yml<br>
    package.json<br>
Scroll to view full table

Now we can jump into installing our live console with the following steps.

  • Modify your package.json file in your project, and add the following dependencies if you aren’t already using them: socket.io and ansi-to-html. For example:

    "dependencies":<br>
    {<br>
        "express": "3.4.7",<br>
        "socket.io": "*",<br>
        "ansi-to-html": "0.1.1"<br>
    }<br>
    Scroll to view full table
  • Create two folders in your project folder, named lib and public. Drop console.js into lib and drop tail.html in public. Your file structure should now look something like this (feel free to ignore my stylesheets folder, it isn’t required) :
C:\Users\IBM_ADMIN\console tailing&gt;tree /F<br>
C:.<br>
│   app.js<br>
│   manifest.yml<br>
│   package.json<br>
│<br>
├───lib<br>
│       console.js<br>
│<br>
└───public<br>
    │   tail.html<br>
    │<br>
    └───stylesheets<br>
            index.css<br>
Scroll to view full table
  • Modify lines 13 and 16 in tail.html so that they point to the URL of your BlueMix application, for example:
&lt;script src="https://.ng.bluemix.net/socket.io/socket.io.js"&gt;<p></p>
<p>var socket = io.connect("https://.ng.bluemix.net");<br>
</p>
Scroll to view full table
  • The only thing left to do is modify our app.js slightly. What we’ll need to do is

    • add these lines to your callback function passed when creating your HTTP server, in order to conditionally determine if you are navigating to tail.html or not. We do this so that tail.html can be served from our public directory:

      // detect if we are navigating to the tail or not<br>
      if (req.url == "/tail.html") {<br>
          fs.readFile(__dirname + '/public/tail.html',<br>
          function (err, data) {<br>
          if (err) {<br>
            res.writeHead(500);<br>
            return res.end('Error loading tail.html');<br>
          }<br>
          res.writeHead(200);<br>
          res.end(data);<br>
          });<br>
      }<br>
      Scroll to view full table
    • replace the line of code telling your app to listen on the removed port and host variables with a line requiring console.js and pass your HTTP server to it as a parameter.

      // app.listen(port, host);<br>
      var custom_console = require('./lib/console.js')(app);<br>
      Scroll to view full table

    So that when you’re all done, it looks like this:

    'use strict';<br>
    // dependencies<br>
    var app = require('http').createServer(handler)<br>
      , fs = require('fs');<p></p>
    <p>// port and host<br>
    var host        = process.env.VCAP_APP_HOST || process.env.HOST || 'localhost';<br>
    var port        = process.env.VCAP_APP_PORT || process.env.PORT || 3000;</p>
    <p>function handler (req, res) {<br>
        // detect if we are navigating to the tail or not<br>
        if (req.url == "/tail.html") {<br>
            fs.readFile(__dirname + '/public/tail.html',<br>
            function (err, data) {<br>
            if (err) {<br>
              res.writeHead(500);<br>
              return res.end('Error loading tail.html');<br>
            }<br>
            res.writeHead(200);<br>
            res.end(data);<br>
            });<br>
        } else {<br>
            // if not tail then generic endpoint<br>
            res.writeHead(200);<br>
            triggerConsole();<br>
            res.end("Hello world!  I am demonstrating the live web console tail.  Navigate to /tail.html and check it out.");<br>
        }<br>
    }</p>
    <p>// dummy function used to recreate standard application log "chatter"<br>
    function triggerConsole() {<br>
        setInterval(function() {<br>
            console.log("you would be seing the logs of your app");<br>
        }, 3000);<br>
        setInterval(function() {<br>
            console.log("right here.");<br>
        }, 1500);<br>
        setInterval(function() {<br>
            console.log("as it runs");<br>
        }, 1500);<br>
        setInterval(function() {<br>
            console.log("isn't that exciting?");<br>
        }, 5000);<br>
    }</p>
    <p>// app.listen(port, host);<br>
    // line requiring the external file lib/console.js passing it the expected parameters<br>
    var timestamp_console = require('./lib/console.js')(app);<br>
    </p>
    Scroll to view full table

And we’re done! Jump down to the Result’s section to test drive it.

Checking Out the Result

It’s time to see the fruits of our labor. Push your example of choice to BlueMix. Now navigate to your application’s route and append /tail.html to it. You should be greeted by the web tail:


You can confirm everything is working properly by opening your application’s route in another tab and hitting that endpoint to trigger some logging functions. You should see your tail.html populate itself with logs as they are called by your application. You can now debug your app in real-time or monitor it’s usage on the fly.

More from Cloud

Modernizing child support enforcement with IBM and AWS

7 min read - With 68% of child support enforcement (CSE) systems aging, most state agencies are currently modernizing them or preparing to modernize. More than 20% of families and children are supported by these systems, and with the current constituents of these systems becoming more consumer technology-centric, the use of antiquated technology systems is archaic and unsustainable. At this point, families expect state agencies to have a modern, efficient child support system. The following are some factors driving these states to pursue modernization:…

7 min read

IBM Cloud Databases for Elasticsearch End of Life and pricing changes

2 min read - As part of our partnership with Elastic, IBM is announcing the release of a new version of IBM Cloud Databases for Elasticsearch. We are excited to bring you an enhanced offering of our enterprise-ready, fully managed Elasticsearch. Our partnership with Elastic means that we will be able to offer more, richer functionality and world-class levels of support. The release of version 7.17 of our managed database service will include support for additional functionality, including things like Role Based Access Control…

2 min read

Connected products at the edge

6 min read - There are many overlapping business usage scenarios involving both the disciplines of the Internet of Things (IoT) and edge computing. But there is one very practical and promising use case that has been commonly deployed without many people thinking about it: connected products. This use case involves devices and equipment embedded with sensors, software and connectivity that exchange data with other products, operators or environments in real-time. In this blog post, we will look at the frequently overlooked phenomenon of…

6 min read

SRG Technology drives global software services with IBM Cloud VPC under the hood

4 min read - Headquartered in Ft. Lauderdale, Florida, SRG Technology LLC. (SRGT) is a software development company supporting the education, healthcare and travel industries. Their team creates data systems that deliver the right data in real time to customers around the globe. Whether those customers are medical offices and hospitals, schools or school districts, government agencies, or individual small businesses, SRGT addresses a wide spectrum of software services and technology needs with round-the-clock innovative thinking and fresh approaches to modern data problems. The…

4 min read