仕事でログを可視化したいんですけど、どうせならリアルタイムで処理したいなと思いまして、ちょろちょろ調べていると、SSE使ったら良いらしいと言うので試してみました。本当はsinatraを使いたかったのですけど、SSEと組み合わせるのが面倒そうだったので、素直にnodejsにしました。
クライアントからはEventSourceオブジェクト作ってonmessageをフックするだけなので、WebSocketでガチャガチャするのに比べたら、コードが超シンプルで良いですね。
// sse.js var http = require("http"); var fs = require("fs"); http.createServer( function(req, res) { console.log(req.url); if (req.headers.accept && req.headers.accept == 'text/event-stream') { if (req.url == '/events') { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); setInterval(function() { var d = new Date(); res.write('id: ' + d.getTime() + '\n'); res.write('data: ' + d + '\n\n'); }, 2000); } } else { res.writeHead(200, {'Content-Type':'text/html'}); res.write(fs.readFileSync('index.html')); res.end(); } } ).listen(8888);
<!-- index.html --> <!DOCTYPE html> <html> <head> <script type="text/javascript"> window.onload = function() { var source = new EventSource('/events'); source.onmessage = function(evt) { document.body.innerHTML += evt.data + '<br />'; } } </script> </head> <body> </body> </html>
ちなみに上記サンプルを走らせている間に、ngrepで中身を覗くと下記の出力が得られました。idとdataのチャンクを交互に送っているだけですね。一応ストリームの仕様はこの辺で見れます。
$ ngrep -W byline -q -d lo0 '' port 8888 ... T 127.0.0.1:8888 -> 127.0.0.1:54344 [AP] HTTP/1.1 200 OK. Content-Type: text/event-stream. Cache-Control: no-cache. Connection: keep-alive. Date: Wed, 14 May 2014 03:08:42 GMT. Transfer-Encoding: chunked. . 12. id: 1400036924400 . T 127.0.0.1:8888 -> 127.0.0.1:54344 [AP] 2f. data: Tue May 13 2014 23:08:44 GMT-0400 (EDT) . T 127.0.0.1:8888 -> 127.0.0.1:54344 [AP] 12. id: 1400036926406 . T 127.0.0.1:8888 -> 127.0.0.1:54344 [AP] 2f. data: Tue May 13 2014 23:08:46 GMT-0400 (EDT) .
何となくSSEの雰囲気がつかめました。これは色々使えそうですね。