我正在用PHP构建一个日志解析器程序。日志解析器从ProFTPD创建的日志文件中读取数据,然后在检测到特定命令时运行某些操作。为了能够检测日志文件中的更改,我使用的是Inotify。如果日志文件太大,我想通过向日志解析器发送信号来完成处理当前文件然后终止日志解析器来旋转日志。然后,Logrotate会在确保正在读取的原始文件被清空后再次重新启动日志解析器。
问题在于,当我使用Inotify时,当inotify处于阻塞状态时,中断将无效。
例如:
#!/usr/bin/php -q
<?php
declare(ticks = 1);
$log_parser = new LogParser();
$log_parser->process_log();
class LogParser {
private $ftp_log_file = '/var/log/proftpd/proftpd.log';
# file descriptor for the log file
private $fd;
# inotify instance
private $inotify_inst;
# watch id for the inotifier
private $watch_id;
public function process_log() {
// Open an inotify instance
$this->inotify_inst = inotify_init();
$this->watch_id = inotify_add_watch($this->inotify_inst, $this->ftp_log_file, IN_MODIFY);
$this->fd = fopen($this->ftp_log_file, 'r');
if ($this->fd === false)
die("unable to open $this->ftp_log_file!\n");
pcntl_signal(SIGUSR1, function($signal) {
$this->sig_handler($signal);
});
while (1) {
# If the thread gets blocked here, the signals do not work
$events = inotify_read($this->inotify_inst);
while ($line = trim(fgets($this->fd))) {
// Parse the log ...
}
}
fclose($this->fd);
// stop watching our directory
inotify_rm_watch($this->inotify_inst, $this->watch_id);
// close our inotify instance
fclose($this->inotify_inst);
}
private function sig_handler($signo) {
switch ($signo) {
case SIGUSR1:
// Do some action ...
}
}
}
我知道一个解决方案可能是我启动父进程然后将信号处理程序添加到该父进程。父进程应该启动日志解析器,日志解析器会被inotify_read阻塞,但是父进程不会,但是想知道是否存在不涉及父进程的解决方案 - 如果inotify能够支持中断吗?
由于
答案 0 :(得分:0)
在此处找到解决方案:php inotify blocking but with timeout
最终代码:
#!/usr/bin/php -q
<?php
declare(ticks = 1);
$log_parser = new LogParser();
$log_parser->process_log();
class LogParser {
private $ftp_log_file = '/var/log/proftpd/proftpd.log';
# file descriptor for the log file
private $fd;
# inotify instance
private $inotify_inst;
# watch id for the inotifier
private $watch_id;
public function process_log() {
// Open an inotify instance
$this->inotify_inst = inotify_init();
stream_set_blocking($this->inotify_inst, false);
$this->watch_id = inotify_add_watch($this->inotify_inst, $this->ftp_log_file, IN_MODIFY);
$this->fd = fopen($this->ftp_log_file, 'r');
if ($this->fd === false)
die("unable to open $this->ftp_log_file!\n");
pcntl_signal(SIGUSR1, function($signal) {
$this->sig_handler($signal);
});
while (1) {
while (1) {
$r = array($this->inotify_inst);
$timeout = 60;
$w = array();
$e = array();
$time_left = stream_select($r, $w, $e, $timeout);
if ($time_left != 0) {
$events = inotify_read($this->inotify_inst);
if ($events) {
break;
}
}
}
while ($line = trim(fgets($this->fd))) {
// Parse the log ...
}
}
fclose($this->fd);
// stop watching our directory
inotify_rm_watch($this->inotify_inst, $this->watch_id);
// close our inotify instance
fclose($this->inotify_inst);
}
private function sig_handler($signo) {
switch ($signo) {
case SIGUSR1:
// Do some action ...
}
}
}
建议的解决方案不会阻止中断,它还会将线程设置为非阻塞状态。