作者 主題: [perl]2支不同程式同時讀同1支檔案不穩定~已解決  (閱讀 2661 次)

0 會員 與 1 訪客 正在閱讀本文。

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
如題,小弟撰寫了兩支程式,兩支都會去讀取相同的log檔,一支是處理,另一支是寫入到資料庫,
兩支程式都放到crontab底下,並設定一開機就啟動(@reboot /root/XXX.pl),
然而詭異的是假如今天測都正常,明天來就不正常,所謂的不正常是指程式有在背景執行,但是未被log檔觸發,
因為當log檔寫入某關鍵訊息會去觸發程式運作,明明已寫入關鍵訊息,照理應該會去觸發但是程式卻完全沒動作,
利用ps aux | grep 程式名稱 去查看程式運作結果,得到0:00,不過當我想不出原因時,
重開機就解決了,不過總不能每次遇到問題都用這招吧,以上問題有勞各位大大隔空抓藥了,感謝!

我程式是放在一台SERVER上(安裝ESXi,內有多台虛擬機,我的程式就是放在其中一台上面,安裝centos 6.5 x64)
« 上次編輯: 2014-05-26 09:20 由 logichom »

gwstudy

  • 活潑的大學生
  • ***
  • 文章數: 205
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #1 於: 2014-05-06 14:32 »
您這樣的描述應該沒"人"看得懂吧 :)
建議把程式及該 crontab 指令列出,log 檔內容也列出來。
太多太長就列重點片斷也可以。


davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #2 於: 2014-05-06 16:09 »
log 檔是如何產生的以及兩隻 perl 程式是用什麼方式去讀取 log 也列一下  ;D ;D

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #3 於: 2014-05-06 20:37 »
抱歉,以下補充說明:

log是由設備吐出來,本機再透過rsyslog.conf設定來收log

rsyslog.conf:
引用
#syslog
:fromhost-ip, isequal, "192.168.X.X"       /var/log/device/syslog.log

crontab:
引用
@reboot root /root/user/self.pl >> /root/user/self.log
@reboot root perl /root/user/search.pl

關鍵log:
引用
May  6 18:08:24 2014 xxxxx650 authmgr[2322]: <522008> <NOTI> <xxxxx650 192.168.X.X>  User Authentication Successful: username=FF-AA-DD-BB-30-16 MAC=ff:aa:dd:bb:30:16 IP=172.16.X.X role=guest VLAN=X AP=cc:ff:ff:cc:cc:68 SSID=xxxxx-test AAA profile=test-aaa auth method=MAC auth server=device

兩支程式分別為self.log(註冊用)及search.pl(寫到資料庫用)

self.pl:
引用
#!/usr/bin/perl
use Sys::Syslog;
use lib '/root/user/xxxxxxxx-6.0040050174877';
use xxxxxxxx;
use Class::Date qw(date);
use POSIX;
use Time::HiRes;

my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);

#宣告變數省略
my $filename="/var/log/device/syslog.log";
open(LOGFILE, $filename) or die "Can't open file , path is:$filename\n";

for(;;)
{
    while(<LOGFILE>)
    {
        ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
        $dates=strftime "%H",localtime; 
        $datess=strftime "%H:%M",localtime;
                                                                                                                           
        if($_ =~ /<522008>/)       
        {           
            @array=split(' ', $_);
            if(@array[0] == "$months[$mon]" && @array[1] == "$mday" && @array[2] =~ /$dates/)
            {
                $user=@array[13];
                $user=substr $user,9;                 
                if($user =~ /[^ \r\t\n\f]/)
                {
                    print "get $user...$months[$mon] $mday $datess\n";                 
                    $mac=@array[14];
                    $mac=substr $mac,4;
                    if($mac =~ /(?:[A-Fa-f0-9]{2}[:-]){5}(?:[A-Fa-f0-9]{2})/)
                    {
                        print "get $mac...$months[$mon] $mday $datess\n";                       
                        $auth=@array[23];
                        $auth=substr $auth,7;
                        if($auth =~ /MAC/)
                        {
                            print "get $auth\n";
                            $job="register";
                        }
                        else
                        {
                           print "auth not equal MAC:$auth\n"; 
                        }                         
                    }
                    else
                    {
                        print "MAC format error:$mac\n";
                    }                                 
                }
                else
                {
                   print "username include space:$user\n";
                }             
            }
        }       
               
        if($job eq "register")
        {
            print "start register...$datess\n";                                           
            #省略register程式碼
            printf "registermac successful\n";
                   
            $expiration_time="";
            $expire_dt="";                                                                                                       
        }
                                                 
     #省略清空變數                                                                                                           
    }                     
}
close(LOGFILE);

search.pl:
引用
#!/usr/bin/perl
use DBI;
use DBD::mysql;
use POSIX qw(strftime);

@months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
#宣告變數省略
my $dsn="DBI:mysql:database=search;host=localhost";
my $userid="account";
my $password="password";
my $dbh=DBI->connect($dsn,$userid,$password) or print "Could not connect to database: $DBI::errstr";

$dbh->do('CREATE TABLE xxxxx(SN integer auto_increment primary key,Month CHAR(3),Day CHAR(2),
Time CHAR(8),MAC CHAR(17),Username VARCHAR(64),Auth CHAR(6))');
$dbh->disconnect or warn $dbh->errstr;

my $file3="/var/log/device/syslog.log";
open(LOGFILE, $file3) or die "can't open logfile";

for(;;)
{
   while(<LOGFILE>)
   {
      chomp $_;
      ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
        my $dates = strftime "%H",localtime;
       
        @array=split(' ', $_);
        if(@array[0] == "$months[$mon]" && @array[1] == "$mday" && @array[2] =~ /$dates/)
        {      
        if($_ =~ /<522008>/)
        {
         @array = split(' ', $_);
         $month=@array[0];
         $day=@array[1];
         $time=@array[2];
                 
         $mac=@array[14];
            $mac=substr $mac,4;

         $username=@array[13];
            $username=substr $username,9;
           
            $auth=@array[23];
            $auth=substr $auth,7;
                           
         if($mac =~ /(?:[A-Fa-f0-9]{2}[:-]){5}(?:[A-Fa-f0-9]{2})/)
         {
          if($username =~ /[^ \r\t\n\f]/)
             {
          $dbh=DBI->connect($dsn,$userid,$password) or print "Could not connect to database:$DBI::errstr";
          $dbh->do("set names utf8");
          my $sth=$dbh->prepare("INSERT INTO xxxxx(Month,Day,Time,MAC,Username,Auth)values(?,?,?,?,?,?)");                
          $sth->execute($month,$day,$time,$mac,$username,$auth)or print $DBI::errstr;
          $sth->finish();      
          $dbh->disconnect or warn $dbh->errstr;
          }         
         }               
        }
      }
       #清空變數省略
   }
}
close(LOGFILE);

davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #4 於: 2014-05-06 22:12 »
filehandle 的部分移進 for 迴圈裡試試

gwstudy

  • 活潑的大學生
  • ***
  • 文章數: 205
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #5 於: 2014-05-06 23:05 »
樓主查一下是不是 log 檔每天都有 rotate。
如果 log 檔有 rotate 那這程式只能作用到 rotate 前的 log 檔最後一行。
新 log 檔檔名雖然仍叫那檔名,但那其實已經是另一個檔了,
程式要 close(LOGFILE) 再重新 open 那檔案一次才行。
« 上次編輯: 2014-05-06 23:10 由 gwstudy »

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #6 於: 2014-05-07 20:40 »
感謝以上兩位前輩的指教。

個人是在/etc/logrotate.d目錄底下新增一個空白檔案
內容為:
引用
/var/log/device/syslog.log {
   daily
   notifempty
   rotate 30
   compress
   copytruncate
}

查看該log目錄底下有
syslog.log
.
.
.
syslog.log-20140507.gz
修改時間幾乎都為半夜3點多
權限為rw-rw-rw-

請問我的程式不是一直無限迴圈讀檔嗎,如果系統進行log輪替時,程式怎麼不會發生錯誤?
« 上次編輯: 2014-05-07 22:01 由 logichom »

gwstudy

  • 活潑的大學生
  • ***
  • 文章數: 205
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #7 於: 2014-05-07 23:46 »
你 open 一個檔案後,OS 給你一個 file handle (想像成一個指標,存在你的 LOGFILE 裡),file handle 指向你檔案在磁碟的位置,跟檔名已經無關了,只要你沒 close file,即使檔名改了,你仍然可以存取到檔案內容。
假如 log rotate 過後,檔名改成 syslog.1,OS 另外生一個 syslog, 這個新的 syslog 是存在 disk 另一個位置,想透過 LOGFILE 就抓不到新位置的內容,除非重新 open syslog。




logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #8 於: 2014-05-08 00:30 »
感謝 gwstudy 前輩詳細的解說。
目前根據 davidju 前輩的意思來將程式做修改
大致如下:(程式碼省略PO過的,只PO修改的地方)
引用
#以上省略...
for(;;)
{
 my $filename="/var/log/device/syslog.log";
 open(LOGFILE, $filename) or die "Can't open file , path is:$filename\n";
 
  while(<LOGFILE>)
  {
    #中間省略...
  }
  close(LOGFILE);
}
以上不知道還有哪裡需要做修正的 ???
目前只能等明天測試才知道問題有沒有解決了...

davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #9 於: 2014-05-08 09:36 »
my $filename = '[PATH]' 可以不用在for loop 裡面,因為檔案路徑是固定的

建議在 filehandle 的部分加上 eval block,可以避免error 之後導致程式 crash

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #10 於: 2014-05-08 11:10 »
似乎解決了程式執行不穩定的問題
不過這樣會造成當程式符合關鍵log時
會一直重覆執行(因為無限迴圈吧)
例如寫入到MySQL的程式
我去查看資料居然多達400多頁
而且都是重覆的資料
看來程式要再修改了... :'(

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #11 於: 2014-05-08 11:35 »
my $filename = '[PATH]' 可以不用在for loop 裡面,因為檔案路徑是固定的

建議在 filehandle 的部分加上 eval block,可以避免error 之後導致程式 crash
真是太感謝你了,不過加上eval block這部分不是太懂,
有上網google,不過看不懂它的用法,能簡單示範一下嗎?謝謝。
另外依目前修改後的讀檔方式來看,好像程式會一直開檔關檔,這樣會影響程式或是系統的效能嗎?

davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #12 於: 2014-05-08 11:59 »
你可以在後面加上 sleep (10),每10秒check 一次

代碼: [選擇]
eval {
           open(LOGFILE, $filename) or die "Can't open file , path is:$filename\n";
};
print "Error :$@" if $@;

程式不會因為檔案不存在之類的問題跳出,而錯誤訊息可以利用 print $@ 顯示出來
« 上次編輯: 2014-05-08 12:01 由 davidju »

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #13 於: 2014-05-08 12:06 »
"你可以在後面加上 sleep (10),每10秒check 一次"
這句我不太懂,是指加在close(LOGFILE) 後面嗎?

另外如果加了eval之後,or die是否可改成or print?
因為or die就是把程式中止,我是希望程式盡量能不要因為任何錯誤而死掉

« 上次編輯: 2014-05-08 13:28 由 logichom »

davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #14 於: 2014-05-08 13:50 »
就算不加上 or die ,程式遇到錯誤一樣會跳出,一般 or die 後面會接 "$!" 顯示錯誤的原因

close(LOGFILE)  應該是在 for 裡面,sleep(10) 可以接在 close() 之後
« 上次編輯: 2014-05-08 14:43 由 davidju »

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #15 於: 2014-05-08 15:02 »
又學到一招了,感謝
在close(LOGFILE);
後面加sleep 10;
是讓程式暫停10秒再繼續讀吧?
如果這中間有log產生會不會lose掉?

不過話說回來最重要的還是要怎麼讓程式在讀到關鍵log時只要讀寫一次就好
不要一直進行後續的動作
因為才一條log就造成我的資料庫寫入11974筆重覆的資料...

davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #16 於: 2014-05-08 15:13 »
重複的問題不難的  8) 8) 8)
思考一下吧

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #17 於: 2014-05-08 15:28 »
要對資料庫不寫入重覆的值我還想的到
至於程式就想不到...
拿self.pl來說
開檔
讀取log
當判斷到<522008>
抓取log對應欄位的值接著繼續後續的動作,如註冊帳號等等
闗檔

第一次讀完之後
接著重覆以上事情
雖然有用時間做判斷但是因為程式執行時間飛快
所以又判斷成功接著陷入無窮迴圈... ::)
有想過判斷時間到秒
不過這樣好像一樣會發生重覆寫入的狀況
« 上次編輯: 2014-05-08 15:34 由 logichom »

gwstudy

  • 活潑的大學生
  • ***
  • 文章數: 205
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #18 於: 2014-05-08 18:32 »
要對資料庫不寫入重覆的值我還想的到
...
不過這樣好像一樣會發生重覆寫入的狀況

我前面說明 rotate 後,除非重新 open syslog 否則讀不到新的 syslog ... 云云,
是在說明原理,不是指點你程式要一直重覆 open。檔案重新 open 後,檔案指標
會被 reset 到檔案開頭,除非你去設定指標,否則你得從第一行讀起。
這個重覆的問題不難,上網查 perl 有關 file 的 tutorial 教學應該很快就可以搞定了,加油了。

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #19 於: 2014-05-08 19:31 »
抱歉腦袋還是轉不過來...
我需要提示
關鍵字也可以
有想過加入seek(LOGFILE, 0, 1);
不過看來沒甚麼差別

目前只能用土法煉鋼方式
引用
my $filename="/var/log/device/syslog.log";
eval{ open(LOGFILE, $filename) or die "Can't open file , path is:$filename\n"; };
print "Error:$@" if $@;
for(;;)
{
  while(<LOGFILE>)
  {
    #中間省略...
  }
    $rotate=strftime "%H:%M:%S",localtime;
    if($rotate eq "03:15:00")
    {
          close(LOGFILE);
          sleep 1;
          eval{ open(LOGFILE, $file3) or die "Can't open file , $!\n"; };
          print "Error:$@" if $@;
    }
}

« 上次編輯: 2014-05-09 12:15 由 logichom »

davidju

  • 懷疑的國中生
  • **
  • 文章數: 33
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #20 於: 2014-05-09 18:26 »
LOG 如果有新的內容進來,LOG 的檔案大小會改變,這是一個判斷的方法

重複的問題,建議你可以寫一個 hash,當搜尋到關鍵字時先存進 hash 裡並給值,也就是說在寫入資料庫之前先有一個判斷的動作,若值已經存在hash 則 next

logichom

  • 懷疑的國中生
  • **
  • 文章數: 87
    • 檢視個人資料
Re: [perl]2支不同程式同時讀同1支檔案不穩定
« 回覆 #21 於: 2014-05-21 18:38 »
這個問題就在剛剛
被我找到根本解決辦法了
不過還沒測試
直接修改logrotate

http://phorum.study-area.org/index.php/topic,58421.msg298668.html#msg298668