node.jsとSocketIOでなんか作ってみたい!!!
というわけでログファイルをリアルタイムっぽく表示するページを作ってみました。
※実際は一定時間ごとにログの追記分を配信しています
「開発サーバのアクセスログ、エラーログ、MySQLクエリログなどなど、わざわざターミナルでtailするのめんどくさいよ!」という人いたらぜひ><
以下、ログファイル1つだけなサンプルの構築手順です。
セットアップ
今回はCentOS6.0を使ってます。
依存パッケージのインストール
このへん入ってれば動きそうです
[root@cent6 ~]# yum groupinstall 'Base' [root@cent6 ~]# yum groupinstall 'Development tools' [root@cent6 ~]# yum install openssl-devel
5系でやる場合はtarのアップデートが必要だったと思います。
使うポートにアクセスできるようにしときましょう
やりかたは人それぞれ
[root@cent6 ~]# /etc/init.d/iptables stop
node.jsをインストール
本家から最新版ダウンロードしましょう
[root@cent6 src]# wget http://nodejs.org/dist/v0.6.5/node-v0.6.5.tar.gz [root@cent6 src]# tar xvzf node-v0.6.5.tar.gz [root@cent6 src]# cd node-v0.6.5 [root@cent6 node-v0.6.5]# ./configure [root@cent6 node-v0.6.5]# make [root@cent6 node-v0.6.5]# make install
npm(node.jsのパッケージマネージャ)をインストール
[root@cent6 ~]# curl http://npmjs.org/install.sh | sh
npmはnode.jsに統合されていました!@sugyanさんご指摘ありがとうございます><
expressとnode-devをグローバルインストール
コマンドとして使うものは-gつきでインストールしておきます
[root@cent6 ~]# npm install express -g [root@cent6 ~]# npm install node-dev -g
expressはnode.jsのWEBアプリケーションフレームワーク、
node-devはデバッグ用のnodeの代替コマンドで、ファイル更新時の再起動が必要なくなってます。
プロジェクト作成
開発用のフォルダ作成して必要なパッケージをインストールします。
ejsはexpressで使うテンプレートエンジン、
socket.ioは言わずと知れたWebSocket用のライブラリです
[mikeda@cent6 ~]$ mkdir -p work/node/ [mikeda@cent6 ~]$ cd work/node/ [mikeda@cent6 node]$ npm install express ejs socket.io [mikeda@cent6 node]$ ls node_modules/ ejs express socket.io
expressプロジェクトを作成します。
テンプレートエンジンはejs、プロジェクト名はlog_watchです。
[mikeda@cent6 node]$ express -t ejs log_watch create : log_watch create : log_watch/package.json create : log_watch/app.js create : log_watch/public create : log_watch/routes create : log_watch/routes/index.js create : log_watch/views create : log_watch/views/layout.ejs create : log_watch/views/index.ejs create : log_watch/public/javascripts create : log_watch/public/images create : log_watch/public/stylesheets create : log_watch/public/stylesheets/style.css dont forget to install dependencies: $ cd log_watch && npm install
いったん起動してみましょう
[mikeda@cent6 node]$ cd log_watch/ [mikeda@cent6 log_watch]$ node-dev app.js
プログラム修正
app.js
サーバサイドで動作するアプリケーション部分です
通常のHTTPアクセスとSocketIOを両方やってるのでわかりづらいかも。
/** * Module dependencies. */ var express = require('express') , routes = require('./routes') , socketio = require('socket.io') , fs = require('fs') var app = module.exports = express.createServer(); var io = socketio.listen(app); // 環境に合わせて設定 var server = '192.168.189.129'; var port = 3000; var log_file = '/tmp/tmp.log'; // Configuration app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function(){ app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function(){ app.use(express.errorHandler()); }); // Routes // routes/index.jsに分離するようになったみたいだけど面倒だからいったんここに //app.get('/', routes.index); app.get('/', function(req, res){ res.render('index', { title: 'Log Watch', server: server, port: port }); }); // ファイルの監視 fs.open(log_file, "r", "0666",function(err,fd){ if(err){ throw err; } //1秒ごとにファイルを見てファイルサイズに差が会ったら差分出力 fs.watchFile(log_file, {interval:1000}, function(cur, prev){ // if(+cur.mtime !== +prev.mtime){ if(cur.size !== prev.size){ // !==だとローテート時にまずいかな var buf_size = 1024; for(var pos=prev.size; pos<cur.size; pos+=buf_size){ if(err){ throw err; } var buf = new Buffer(buf_size); fs.read(fd, buf, 0, buf_size, pos, function(err, bytesRead, buffer){ var log = buffer.toString('utf8', 0, bytesRead); // changeをログの内容つけて送信する io.sockets.emit('change', log); } ); } } }); }); app.listen(port); console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
views/index.ejs
HTMLのボディです。Textareaを1つ作ります。
<h1><%= title %></h1> <textarea id="log" cols="50" rows="10" readonly></textarea>
views/layout.ejs
HTMLのレイアウトです。
socket.ioライブラリ、クライアントサイドのJSファイルを読み込むようにします。(ホントは外出ししたほうがいいと思います。)
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> <script src="/socket.io/socket.io.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script> <script> var server = "<%= server %>"; var port = <%= port %>; </script> <script src="/javascripts/client.js"></script> </head> <body> <%- body %> </body> </html>
public/javascripts/client.js
クライアントサイドのJavaScriptです。
WebSocketでサーバに接続してchangeメッセージを受け取ったら内容をTextareaに追記します。
$(document).ready(function() { var socket = io.connect('http://'+ server +':'+ port); socket.on('connect', function() { console.log('connect'); }); socket.on('disconnect', function(){ console.log('disconnect'); }); socket.on('change', function(log) { console.log('change:' + log); $('#log').append(log); }); });