/** * 监控日志文件 * * @param array $logPaths * @return void * @throws ZtChart_Model_Monitor_Exception */ public function tail($logPaths) { if (($inotify = inotify_init()) === false) { throw new ZtChart_Model_Monitor_Daemon_Exception('Failed to obtain an inotify instance.'); } $watchFiles = $watchDirs = array(); foreach ($logPaths as $file) { if (($watch = inotify_add_watch($inotify, $file, IN_CREATE | IN_MODIFY)) === false) { throw new ZtChart_Model_Monitor_Daemon_Exception("Failed to watch file '{$file}'."); } if (is_file($file)) { if (false === ($fd = fopen($file, "r"))) { throw new ZtChart_Model_Monitor_Daemon_Exception("File '{$file}' is not readable."); } $this->_ffseek($fd); $watchFiles[$watch] = $fd; } else { if (is_dir($file)) { $watchFiles[$watch] = array(); $watchDirs[$watch] = $file; } } } while (($events = inotify_read($inotify)) !== false) { foreach ($events as $event) { if ($event['mask'] & IN_Q_OVERFLOW) { throw new ZtChart_Model_Monitor_Daemon_Exception("The number of inotify queued events reaches upper limit."); } if (!$this->accept($event['name'])) { continue; } if ($event['mask'] & (IN_CREATE | IN_MODIFY)) { if (!($event['mask'] & IN_ISDIR)) { if (!array_key_exists($event['name'], $watchFiles[$event['wd']])) { $fn = $fd = null; foreach ($watchFiles[$event['wd']] as $wfn => $wfd) { if (strncasecmp($wfn, $event['name'], strpos($wfn, '.')) == 0) { $fn = $wfn; $fd = $wfd; break; } } // 判断当前创建或修改的日志文件是否最新 if (strcasecmp($fn, $event['name']) < 0) { if (is_resource($fd) && fclose($fd)) { unset($watchFiles[$event['wd']][$fn]); } $filename = $watchDirs[$event['wd']] . DIRECTORY_SEPARATOR . $event['name']; if (false === ($fd = fopen($filename, "r"))) { $this->_logger->err("File '{$filename}' is not readable\n"); continue; } if ($event['mask'] & IN_MODIFY) { $this->_ffseek($fd); } $watchFiles[$event['wd']][$event['name']] = $fd; } else { // 如果不是最新的日志文件(重写的上一个小时的日志文件),则不处理。 continue; } } else { $fd = $watchFiles[$event['wd']][$event['name']]; } } else { continue; // $fd = $watchFiles[$event['wd']]; } // 读取日志并分析 $raw = ''; $lines = 0; while (true) { $raw .= $block = fread($fd, 4096); if (false !== ($pos = strpos($raw, "\n"))) { $rawline = substr($raw, 0, $pos + 1); try { $mlog = new ZtChart_Model_Monitor_Log($rawline); $line = $mlog->transform(); if (!empty($line)) { $this->_shmWrite($mlog->getShmIdentifier(), $line); $lines++; } } catch (ZtChart_Model_Monitor_Log_Exception $e) { $this->_logger->warn("日志处理错误:" . iconv('GBK', 'UTF-8', $rawline)); } $raw = substr($raw, $pos + 1); } else { if (($offset = strlen($block)) > 0) { fseek($fd, -$offset, SEEK_CUR); } break; } } if (0 != $lines) { $this->_logger->info(sprintf("%4s行有效数据已处理:%s", $lines, $watchDirs[$event['wd']] . '/' . $event['name'])); } } } } foreach ($watchFiles as $watch => $fd) { if (is_resource($fd)) { $fd = (array) $fd; } array_walk($fd, 'fclose'); inotify_rm_watch($inotify, $watch); } fclose($inotify); }