linux下處理即時日誌產生另一個即時日誌

來源:互聯網
上載者:User
一.背景介紹1.知識點

寫這篇blog,主要有下面幾個知識點想介紹:

curl擷取http相應內容;

shell中執行php檔案;

php中執行shell命令(通過exec函數);

php實現tail -f命令;

包含空格的參數如何作為參數傳遞(用雙引號括起來)。

2.商務程序

這篇blog的背景是讀取"/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current"這個即時日誌,產生招聘會所需的即時日誌。

商務程序如下:
(1)從http://bj.baidu.com/jobfairs/jobfairs_im_port.php?action=getIms擷取企業和IM用戶端id的關係。
響應的格式如下:
{"status":1,"ret":{"company_id":{“im_accout”:[im_id],"company_name":[]}}}
擷取到的資料如下:
{"status":1,"ret":{"2028107":{"im_account":["31669394","50000098"],"name":["baidu"]},"2028098":{"im_account":["50029298","50000098","31669376","31669394","50006271"],"name":["sogou"]}},"msg":""}

這裡碰到的第一個問題是我開發所在的環境和http://bj.baidu.com不在同一個網段內,該url服務所在的IP為10.3.255.201,此時我需要進行hosts映射,這樣當我訪問http://bj.baidu.com/jobfairs/jobfairs_im_port.php?action=getIms時,便相當於我在訪問了http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms。
但是我們一定有一個疑問,為什麼我們不直接使用http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms來進行訪問,答案是我們需要通過url擷取到使用者的城市,即http://bj.baidu.com/jobfairs/jobfairs_im_port.php?action=getIms,這裡麵包含bj.baidu.com,包含使用者的城市資訊bj。

解決方案是通過curl對url和host進行映射:

curl -H "Host: bj.ganji.com" http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms

參考連結:http://blog.csdn.net/lianxiang_biancheng/article/details/7575370  中的curl命令的使用。

(2)其次,如果這條日誌記錄中fromUserId或者toUserId包含某個企業的IM用戶端id,則說明這條訊息屬於這個企業;

(3)最後,產生所需格式的日誌,日誌的欄位格式如下:
時間 企業Id 企業名稱 企業IM的id  應聘者IM的id 誰發送的資訊(0:企業,1:應聘者)  訊息內容

二.採用了三種實現方式1.第一種:shell讀取每一行記錄傳遞給php進行匹配並輸出

(1)start.sh是開機檔案,如下:

#!/bin/sh#執行前清除所有該進程pids=`ps aux | grep jobfairs | grep -v "grep" | awk '{print $2}'`      if [ "$pids" != "" ];then     echo $pids    kill -9 $pidsfish jobfairs.sh >> /home/baidu/log/jobfairs.log

(2)jobfairs.sh是擷取http內容,讀取即時日誌並每2分鐘重新請求的實現,如下:

#!/bin/shlogfile="/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current"hours=`date +%H`start_time=`date +%s`#17點後停止運行程式while [ $hours -lt 17 ]do    res=`curl -s -H "Host: bj.baidu.com" http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms`    #echo $res        len=${#res}    if [ $len = 0 ]; then        echo "Failed! Request error!"        exit    fi    status=`echo $res | sed -e 's/.*status"://' -e 's/,.*//'`    if [ $status != 1 ]; then         echo "Failed! Request stauts:"$status        exit    fi    ret=`echo $res | sed -e 's/.*ret"://' -e 's/,"msg.*//'`    #ret='{"2028097":{"im_account":["2875001357","197823104","3032631861","197305863"],"name":["8\u811a\u732b\u521b\u65b0\u79d1\u6280\u6709\u9650\u516c\u53f8\uff08\u4e60\u5927\u7237\u6dae\u8089\u5bf9\u976210000\u7c73\u7684\u79d1\u6280\u516c\u53f8\uff09"]},"2028098":{"im_account":["3658247660","192683241","197488883","108963206","197305001"],"name":["9\u811a\u732b\u521b\u65b0\u79d1\u6280\u6709\u9650\u516c\u53f8"]}}';    tail -f $logfile | grep sendMsgOk | grep "spamReasons=\[\]" | awk -F"\t" '{        printf("%s\t%s\t%s\t%s\n",$1,$3,$4,$11);     }' | while read line    do        /usr/local/webserver/php/bin/php jobfairs.php $ret "$line"                #120s後停止組建記錄檔,重新執行http請求去擷取公司相關資訊        end_time=`date +%s`        if [ $(expr $end_time - $start_time) -ge 120 ]; then            #echo `date +%T`" "`date +%D`            #echo "120s is done!"            break        fi    done        start_time=`date +%s`    hours=`date +%H`done

這裡還涉及到一個知識點,就是如何將包含空格的字串作為參數傳遞。
這裡的情境是這樣的:由於一行記錄各個欄位是以定位字元分隔的,其中有一個欄位msgContent是訊息內容,而訊息中經常包含空格,而php接受外來參數預設是以空格分隔的,這樣如果將$line作為參數進行傳遞,就導致msgContent被分隔為了好幾個欄位。那我們如何解決這個問題呢,答案就是通過加雙引號(即將$line變為"$line"),將一行記錄作為一個整體字串傳入即可,然後php接收到這個字串後,再通過explode("\t",$line)進行分隔出各個欄位。如下所示:        
/usr/local/webserver/php/bin/php jobfairs.php $ret "$line" 

(3)jobfairs.php是對即時日誌的每一行進行匹配並輸出為IM的log格式:

<?php    $ret = $_SERVER["argv"][1];    $arr = json_decode($ret, true);//將json字串解碼成數組    foreach ($arr as $key => $value) {        $name = $value["name"][0];//企業名稱        foreach ($value["im_account"] as $v) { //企業對應的叮咚id            $userId[$v] = $key;            $compName[$v] = $name;            //echo $key ."\t" . $v ."\t" . $name ."\n";        }    }      $line = $_SERVER["argv"][2];//擷取日誌的一條記錄    $logArr = explode("\t", $line);    //echo $line . "\n";    //擷取各個欄位    $time = $logArr[0];    $fromUserId = $logArr[1];    $toUserId = $logArr[2];    $msgContent = $logArr[3];        $fuiArr = explode('=', $fromUserId);    $tuiArr = explode('=', $toUserId);    $fui = $fuiArr[1];    $tui = $tuiArr[1];    $output = $time . "\t";    if(isset($userId[$fui])) { //fromUserId是某個企業的叮咚id        //echo $line . "\n";        $output .= "companyId=$userId[$fui]\t";        $output .= "companyName=$compName[$fui]\t";        $output .= "companyDingdongId=$fui\t";        $output .= "personalDingdongId=$tui\t";        $output .= "whoSend=0\t";        $output .= $msgContent;        echo $output . "\n";    } else if(isset($userId[$tui])) { //toUserId是某個企業的叮咚id        //echo $line . "\n";        $output .= "companyId=$userId[$tui]\t";        $output .= "companyName=$compName[$tui]\t";        $output .= "companyDingdongId=$tui\t";        $output .= "personalDingdongId=$fui\t";        $output .= "whoSend=1\t";        $output .= $msgContent;        echo $output . "\n";    }?>

2.第二種:php執行shell命令並將輸出結果進行匹配

註:該方法不能產生即時日誌,因為tail -f命令是即時擷取更新命令,php無法擷取其返回結果。所以該方法僅用於讀取一段固定文本並進行處理。

這裡通過exec執行tail -n1000這個shell命令,擷取到最後1000行資料然後再進行處理。同時,這裡還調用了php中curl模組擷取http響應內容。該檔案名稱為jobfairs2.php。

<?php    //error_reporting(E_ALL & ~E_NOTICE);    $host = array("Host: bj.baidu.com");    $data = 'user=xxx&qq=xxx&id=xxx&post=xxx';    $url = 'http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms';    $res = curl_post($host, $data, $url);       $arr = json_decode($res, true);    $status = $arr["status"];    if ($status != 1) {        echo "Request Failed!";        exit;    }        //擷取返回的公司資訊    $ret = $arr["ret"];    foreach ($ret as $key => $value) {        $name = $value["name"][0];        //將IM的Id和企業id進行hash映射        foreach ($value["im_account"] as $v) {            $userId[$v] = $key;            $compName[$v] = $name;        }    }        $logfile = "/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current";        //tail -n1000擷取最後1000行記錄,並儲存到$log變數中    $shell = "tail -n 1000 $logfile | grep sendMsgOk | grep 'spamReasons=\[\]' | ";    $shell .= "awk -F'\t' '{print $1,$3,$4,$11;}'";    exec($shell, $log); //將執行的shell結果儲存到數組中       //處理每一行記錄    foreach ($log as $line) {        //通過Regex匹配出所需要的欄位        $flag = preg_match("/([0-9]+:[0-9]+:[0-9]+).*fromUserId=([0-9]+).*toUserId=([0-9]+).*msgContent=(.*)/", $line, $matches);        if( $flag == 0 ){//匹配失敗            continue;        }                //echo $line . "\n";        $time = $matches[1];        $fui  = $matches[2];        $tui = $matches[3];        $msgContent = $matches[4];                //查看fromUserId和toUserId有沒有對應的公司        $output = $time . "\t";        //通過hash判斷IM的id是否屬於某個企業        if(isset($userId[$fui])){            //echo $line . "\n";            $output .= "companyId=$userId[$fui]\t";            $output .= "companyName=$compName[$fui]\t";            $output .= "companyDingdongId=$fui\t";            $output .= "personalDingdongId=$tui\t";            $output .= "whoSend=0\t";            $output .= $msgContent;            echo $output . "\n";        }else if(isset($userId[$tui])){            //echo $line . "\n";            $output .= "companyId=$userId[$tui]\t";            $output .= "companyName=$compName[$tui]\t";            $output .= "companyDingdongId=$tui\t";            $output .= "personalDingdongId=$fui\t";            $output .= "whoSend=1\t";            $output .= $msgContent;            echo $output . "\n";        }    }    /*    * 提交請求    * @param $host array 需要配置的網域名稱 array("Host: bj.ganji.com");    * @param $data string 需要提交的資料 'user=xxx&qq=xxx&id=xxx&post=xxx'....    * @param $url string 要提交的url 'http://192.168.1.12/xxx/xxx/api/';    */    function curl_post($host,$data,$url){        $ch = curl_init();        $res= curl_setopt($ch, CURLOPT_URL,$url);        //var_dump($res);        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);        curl_setopt ($ch, CURLOPT_HEADER, 0);        curl_setopt($ch, CURLOPT_POST, 0);        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);        curl_setopt($ch,CURLOPT_HTTPHEADER,$host);        $result = curl_exec($ch);        curl_close($ch);        if ($result == NULL) {            return 0;        }        return $result;    }?>

php執行shell命令的函數參考連結:http://blog.csdn.net/a600423444/article/details/6059548
3.第三種:php自己實現tail -f命令來即時讀取記錄檔

該檔案名稱為jobfairs3.php。這裡是php通過檔案位移量實現tail -f命令的效果。但是不適用於讀取每次都動態指向不同檔案。同時,這裡還調用了php中curl模組擷取http響應內容。

<?php    //error_reporting(E_ALL & ~E_NOTICE);    //php通過curl擷取http相應的內容    $host = array("Host: bj.ganji.com");    $data = 'user=xxx&qq=xxx&id=xxx&post=xxx';    $url = 'http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms';    $res = curl_post($host, $data, $url);    //將json字串解碼成數組     $arr = json_decode($res, true);        $status = $arr["status"];    if ($status != 1) {        echo "Request Failed!";        exit;    }    //擷取返回的公司資訊    $ret = $arr["ret"];    foreach ($ret as $key => $value) {        $name = $value["name"][0];        //將IM的Id和企業id進行hash映射        foreach ($value["im_account"] as $v) {            $userId[$v] = $key;            $compName[$v] = $name;        }    }        $logfile = "/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current";    tail_f($logfile, $userId);    //通過php實現shell的tail -f命令    function tail_f($logfile, $userId) {        $size = filesize($logfile);        $ch = fopen($logfile, 'r');        $i = 0;        while (1) {            clearstatcache();            $tmp_size = filesize($logfile);            if (0 < ($len = $tmp_size - $size)) {                $i = 0;                fseek($ch, -($len -1), SEEK_END);                $content = fread($ch, $len);                $lineArr = explode("\n", $content);                foreach ($lineArr as $line) {                    //echo $line . "\n";                    if (preg_match("/sendMsgOk.*spamReasons=\[\]/", $line)) {                        matchCompany($line, $userId);                    }                }            } else {                $i++;                if ($i > 60) {                    echo PHP_EOL . 'The file in 60s without change,So exit!';                    break;                }                sleep(1);                continue;            }            $size = $tmp_size;        }        fclose($ch);    }    //對一行記錄判斷是否在公司資訊中,如果在,則輸出組合後的記錄    function matchCompany($line, $userId) {        $flag = preg_match("/([0-9]+:[0-9]+:[0-9]+).*fromUserId=([0-9]+).*toUserId=([0-9]+).*msgContent=(.*)\tchannel=.*/", $line, $matches);        if( $flag == 0 ){            return;        }        //echo $matches[0] ."\t" .$matches[1] . "\t" . $matches[2] . "\t" . $matches[3] . "\t" . $matches[4] . "\n";        $time = $matches[1];        $fromUserId  = $matches[2];        $toUserId = $matches[3];        $msgContent = $matches[4];        //查看fromUserId和toUserId有沒有對應的公司        $output = $time . "\t";        //如果IM的id屬於某個企業        if(isset($userId[$fromUserId])){            //echo $line . "\n";            $output .= "companyId=$userId[$fui]\t";            $output .= "companyName=$compName[$fui]\t";            $output .= "companyDingdongId=$fui\t";            $output .= "personalDingdongId=$tui\t";            $output .= "whoSend=0\t";            $output .= $msgContent;            echo $output . "\n";        }else if(isset($userId[$toUserId])){            //echo $line . "\n";            $output .= "companyId=$userId[$tui]\t";            $output .= "companyName=$compName[$tui]\t";            $output .= "companyDingdongId=$tui\t";            $output .= "personalDingdongId=$fui\t";            $output .= "whoSend=1\t";            $output .= $msgContent;            echo $output . "\n";        }    }    /*    * 提交請求    * @param $host array 需要配置的網域名稱 array("Host: bj.ganji.com");    * @param $data string 需要提交的資料 'user=xxx&qq=xxx&id=xxx&post=xxx'....    * @param $url string 要提交的url 'http://192.168.1.12/xxx/xxx/api/';    */    function curl_post($host,$data,$url){        $ch = curl_init();        $res= curl_setopt($ch, CURLOPT_URL,$url);        //var_dump($res);        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);        curl_setopt ($ch, CURLOPT_HEADER, 0);        curl_setopt($ch, CURLOPT_POST, 0);        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);        curl_setopt($ch,CURLOPT_HTTPHEADER,$host);        $result = curl_exec($ch);        curl_close($ch);        if ($result == NULL) {            return 0;        }        return $result;    }?>

三.總結

本文最終採取了第一種方式來實現,最後還需要將start.sh加入到crontab -e中,加入如下一條記錄:

30 9 * * * cd /home/baidu/zhaolincheung/JobFairs; sh start.sh

第二種方式不適合這裡的業務,因為需要即時讀取日誌,但是php的exec無法返回tail -f的讀取結果。
第三種方式也可以實現,僅適用讀取一個固定的動態增長的記錄檔。但是這裡的記錄檔nginx.im.imp.current_current是一個軟串連,它會動態地指向不同的檔案,如所示:


這樣自己實現tail -f的話,由於檔案會改變,導致檔案的動態位移量有可能是不同的檔案的,導致讀取的日誌不對。所以這種方法也沒有採用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.