EC studio EC studio 技術ブログ

2009年10月06日投稿者:山本 正喜

jQueryとPHPでLinuxのtailコマンドを実装する

Linuxにはtailというファイルの末尾10行程度を表示する、
ログファイルのチェックなどに便利なコマンドがあります。

オプションで一定間隔で常に末尾10行の表示を
更新し続けることもでき、ログの監視などに使えます。

Linux tailコマンド

ただ、とっても便利でサーバー管理者御用達のこのコマンド、
当然ながらシェルでログインできなくては使えません。。

レンタルサーバーなどでシェル権限がない場合も多いですし、
何よりもっとカンタンにブラウザから見たい!と思いませんか?

業務上の都合もありとっても欲しくなってので、作っちゃいました。

jQueryとPHPを使えば、思った以上にサクッと作れてしまったので、
技術ブログでソースコードや解説も含めて公開します。
(jQuery、メチャクチャ便利!です)

jQuery + PHPでtailを作る!

まずは今回の完成品です。

phptail

シンプルですね。
「TAIL」ボタンを押すとtail実行、
「STOP」ボタンを押すと更新が止まります。

実際に実行するとこうなります。
※サンプルとしてこのプログラム自体をtailしました

phptail 実行

Ajaxで実装されており、2秒ごとに表示が更新されます。

では、ソースコードです。

PHP:
  1. <?php
  2.     header('Content-type:text/html; charset=UTF-8');
  3.    
  4.     //対象のファイルパス
  5.     $logpath = 'logfile.log';
  6.     //表示する末尾の行数
  7.     $lines = 30;
  8.     //更新インターバル (ミリ秒)
  9.     $interval = 2000;
  10.    
  11.     if (isset($_GET['load'])){
  12.         echo 'file:'.basename($logpath).' reload:'.date('H:i:s').'<hr size="1"/>';
  13.        
  14.         if (!file_exists($logpath)){
  15.             die($logpath.'は存在しません');
  16.         }
  17.        
  18.         foreach (read_tail($logpath,$lines) as $i => $line){
  19.             $line = rtrim($line,"\r\n");
  20.             echo strtr(htmlspecialchars($line,ENT_QUOTES),array("\t" => '&nbsp;&nbsp;&nbsp;&nbsp;'));
  21.             if ($i <($lines - 1)){
  22.                 echo '<br />';
  23.             }
  24.         }
  25.        
  26.         exit;
  27.     }
  28.    
  29. /**
  30. * ログをファイルから指定行数読み出す
  31. *
  32. * tail function by flash tekkie
  33. * http://tekkie.flashbit.net/php/tail-functionality-in-php
  34. *
  35. * @param string $file ファイルパス
  36. * @param int $lines 行数
  37. * @return array 行ごとの配列
  38. */
  39. function read_tail($file, $lines) {
  40.     //global $fsize;
  41.     $handle = fopen($file, "r");
  42.     $linecounter = $lines;
  43.     $pos = -2;
  44.     $beginning = false;
  45.     $text = array();
  46.     while ($linecounter> 0) {
  47.         $t = " ";
  48.         while ($t != "\n") {
  49.             if(fseek($handle, $pos, SEEK_END) == -1) {
  50.                 $beginning = true;
  51.                 break;
  52.             }
  53.             $t = fgetc($handle);
  54.             $pos --;
  55.         }
  56.         $linecounter --;
  57.         if ($beginning) {
  58.             rewind($handle);
  59.         }
  60.         $text[$lines-$linecounter-1] = fgets($handle);
  61.         if ($beginning) break;
  62.     }
  63.     fclose ($handle);
  64.     return array_reverse($text);
  65. }
  66. ?>
  67. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
  68. <head>
  69. <meta name="robots" content="noindex,nofollow" />
  70. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
  71. <script type="text/javascript">
  72. var timer = null;
  73. function start_tail(){
  74.     if (timer){
  75.         clearInterval(timer);
  76.     }
  77.     $('#status').html('running...');
  78.     timer = setInterval(run,<?php echo $interval ?>);
  79. }
  80.  
  81. function run(){
  82.     $('#console').load('?load=1&m=' + new Date().getTime());
  83. }
  84.  
  85. function stop(){
  86.     clearInterval(timer);
  87.     $('#status').empty();
  88. }
  89. </script>
  90. <style type="text/css">
  91. #console {
  92.     width:800px;
  93.     height:500px;
  94.     overflow:scroll;
  95.     border:1px solid #999999;
  96.     font-size:12px;
  97.     font-family:consolas;
  98. }
  99. </style>
  100. </head>
  101. <body>
  102. <input type="button" onclick="start_tail()" value="TAIL"> <input type="button" onclick="stop()" value="STOP"> <span id="status"></span><br />
  103. <pre id="console"></pre>
  104. </body>
  105. </html>

これだけ!です。
外部ファイルも不要なので、このコードを
phptail.phpなどで保存して、実行すればokです。(文字コードはUTF-8で)

たった100行ぐらいでAjaxを使ったtailを実装できちゃうところが、
jQueryとPHPの素晴らしいところですね。

では、ソースコードの解説をしてみます。

PHPの処理を作る

今回のキモはなんといってもtail部分。
ファイルを末尾から読み出すのは結構難しいです。

こういった処理を自分で作ってもいいですが、
海外サイトに良さそうなコードがあったのでお借りしましょう。

「php tail」でGoogle検索すればすぐ見つかりました。
flash tekkie » Blog Archive » tail functionality in PHP

今回はここのread_file関数を使うことにします。
read_fileだと少々わかりにくいのでread_tailに変更しました。

PHP:
  1. function read_tail($file, $lines) {
  2.     //global $fsize;
  3.     $handle = fopen($file, "r");
  4.     $linecounter = $lines;
  5.     $pos = -2;
  6.     $beginning = false;
  7.     $text = array();
  8.     while ($linecounter> 0) {
  9.         $t = " ";
  10.         while ($t != "\n") {
  11.             if(fseek($handle, $pos, SEEK_END) == -1) {
  12.                 $beginning = true;
  13.                 break;
  14.             }
  15.             $t = fgetc($handle);
  16.             $pos --;
  17.         }
  18.         $linecounter --;
  19.         if ($beginning) {
  20.             rewind($handle);
  21.         }
  22.         $text[$lines-$linecounter-1] = fgets($handle);
  23.         if ($beginning) break;
  24.     }
  25.     fclose ($handle);
  26.     return array_reverse($text);
  27. }

この関数に読み出したいファイルパスを渡して、
それを一定秒数ごとに表示してやればokですね。

Ajaxで定期的に読み込む

Ajaxというと難しいイメージがありますが、
jQueryを使えばほんとにカンタンに実装できてしまいます。

今回は
「TAIL」ボタンにstart_tail()を、
「STOP」ボタンにstop()を、
onclick属性でそれぞれ割り当てました。

JavaScriptの関数コードは

JAVASCRIPT:
  1. <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
  2. <script type="text/javascript">
  3. var timer = null;
  4. function start_tail(){
  5.     if (timer){
  6.         clearInterval(timer);
  7.     }
  8.     $('#status').html('running...');
  9.     timer = setInterval(run,<?php echo $interval ?>);
  10. }
  11.  
  12. function run(){
  13.     $('#console').load('?load=1&m=' + new Date().getTime());
  14. }
  15.  
  16. function stop(){
  17.     clearInterval(timer);
  18.     $('#status').empty();
  19. }
  20. </script>

これだけです。
はじめにajax.googleapis.comからjQueryのライブラリを読み込みます。

これはGoogleが無償で提供しているGoogle AJAX Libraries APIというもので、
Googleがホストしてくれている様々なJavaScriptライブラリを
自由に使ってかまわないというものです。

http://code.google.com/intl/ja/apis/ajaxlibs/

これのおかげで、jquery.jsなどを別途用意する必要がありません。
(Googleに感謝!)

次に、start_tailを見てみると、
setIntervalでrunという関数を定期的に実行しています。

このrunという関数が実行される度に、Ajaxで先ほどの
PHPで作ったtailの処理が実行されます。

JAVASCRIPT:
  1. $('#console').load('?load=1&amp;m=' + new Date().getTime());

ここが、Ajaxです。なんと1行!

これは、id="console"のタグの中身に対して、
'?load=1&m=タイムスタンプ' のURLの実行結果を読み込みます。

?より前が省略されているのは、自分自身(php)を実行するためです。
?でload=1というパラメータをつけることにより、
PHP側で、

PHP:
  1. if (isset($_GET['load'])){
  2.         echo 'file:'.basename($logpath).' reload:'.date('H:i:s').'<hr size="1"/>';
  3.        
  4.         if (!file_exists($logpath)){
  5.             die($logpath.'は存在しません');
  6.         }
  7.        
  8.         foreach (read_tail($logpath,$lines) as $i => $line){
  9.             $line = rtrim($line,"\r\n");
  10.             echo strtr(htmlspecialchars($line,ENT_QUOTES),array("\t" => '&nbsp;&nbsp;&nbsp;&nbsp;'));
  11.             if ($i <($lines - 1)){
  12.                 echo '<br />';
  13.             }
  14.         }
  15.        
  16.         exit;
  17.     }

このように、Ajaxから呼ばれた場合のみに
tail処理を行わせてexitさせる ということをしています。

Ajax用に別のphpを用意してもいいのですが、
簡単な処理であれば自分自身を呼び出す方が
ソースコードもまとまり便利です。

"m=タイムスタンプ"の部分は、IEの場合
Ajaxで同じURLを連続して読み出した場合に
キャッシュされてしまうため、URLを毎回変化させています。
PHP側ではこのmの値は全く使用しません。

今回のtailではブラウザ互換の都合上、
タブはスペース4つに置換されるという仕様にしています。
(まあ、問題児はもちろんIEなんですが、、)

IE8,FireFox3.5,Chrome3 で動作確認しています。

問題がある場合はソースを修正してください。

PHPで作ることの意味、応用アイデア

以上でソースコードの説明は終わりです。

とても短いソースコードなので、業務の用途にあわせて
改変していただければなと思います。

今回はとてもシンプルにただ1つのファイルを監視するだけですが、
PHPを改造すればローテートされる複数のログファイルを横断したり、
正規表現で抜き出したり、自由自在にファイルを
監視するスクリプトを作れるのではないかと思います。

ただ注意しなければいけないのが、監視対象のファイルを
GET値などで指定できるようにすると、セキュリティ上
非常に危ないのでそういった改造をする場合は
注意するようにしてください。

このソースコードは自由に使用していただいて構いませんが、
各自の自己責任での使用をお願いします! :)


関連した記事:

■ 「日本でいちばん社員満足度が高い会社の非常識な働き方」

日本でいちばん社員満足度が高い会社の非常識な働き方

この記事へのコメント (4)

PHP側で実行できるjqueryライクなオープンソースの紹介 phpQuery

PHP側でJQueryのような書き方でHTMLを簡単に加工できます。

http://code.google.com/p/phpquery/

$doc = phpQuery::newDocumentHTML(“http://www.yahoo.co.jp/”);

$html = $doc['div'] -> html();

このような簡単な書き方でHTMLを操作できる。
jQueryで実行できる関数がPHP側で実現できます。
正規表現も使わないで簡単にHTMLを加工できる。

指定したサイトのある部分だけ(CSSクラスやIDだけで)簡単に取って来れそう

jQueyrリファレンス
http://semooh.jp/jquery/

投稿者: 通りすがり | 2009/10/07 水曜日 0:05:05

ステキ。
いい記事でした

投稿者: t98907 | 2010/02/18 木曜日 17:12:05

ありがとうございます!

投稿者: 山本 正喜 | 2010/02/18 木曜日 23:58:20

とても分かりやすい記事ありがとうありがとうございます!
こんなのを探してました!

投稿者: まーちゃん | 2011/07/14 木曜日 23:04:07
コメントを投稿

取材に関するお問い合わせ


(担当:大崎)

EC studio 採用情報の詳細はこちら

投稿者
全ECスタッフ導入の
おすすめソフトウェア
人気のエントリー
カテゴリー
最近のエントリー
アーカイブ

BLOG オフィシャルブログ

社長ブログ
EC studio社長ブログ

ブログを読む

技術ブログ
技術部のブログ

ブログを読む

デザインブログ
デザイン部のブログ

ブログを読む

Copyright© EC studio, All Rights Reserved.