'DEBUG', self::INFO => 'INFO', self::NOTICE => 'NOTICE', self::WARNING => 'WARNING', self::ERROR => 'ERROR', self::FATAL => 'FATAL', self::ALERT => 'ALERT', self::EMERGENCY => 'EMERGENCY', ]; //日志类型分类 private $_CATEGORIES = [ self::DEBUG => 'INFO', self::INFO => 'INFO', self::NOTICE => 'INFO', self::WARNING => 'INFO', self::ERROR => 'ERROR', self::FATAL => 'ERROR', self::ALERT => 'ERROR', self::EMERGENCY => 'ERROR', ]; public function __construct($path = NULL) { $this->setPath($path); $this->setLevel(self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING); } private function __clone() { } private function _formatMsg($msg, $type, $file = null, $line = null) { $extra = ''; if ($this->_debugBackTrace && $file === null && $line === null) { $traceLevel = 10; $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $traceLevel); $cnt = count($trace); if ($cnt > 0) { foreach ($trace as $l) { if (isset($l['file']) && isset($l['line'])) { $extra .= sprintf("%s(%d)\n", $l['file'], $l['line']); } } $file = $trace[$cnt-1]['file']; $line = $trace[$cnt-1]['line']; } } if ($file === null) { $file = __FILE__; } if ($line === null) { $line = __LINE__; } return sprintf("%s [%-5s] [%-25s :%5d] %s\n%s\n", date('Y-m-d H:i:s'), $this->_TYPES[$type], substr($file, -24), $line, $msg, $extra); } private function _sendFluentd($data, $type, $errFile = null, $errLine = null) { $tag = sprintf("%s.%s.%s", $this->_project, $this->_CATEGORIES[$type], $this->_TYPES[$type]); $subject = sprintf("[%s][%s]%s", $this->_project, $this->_TYPES[$type], substr($data, 0, 40)); $reqMicro = explode('.', $_SERVER['REQUEST_TIME_FLOAT']); $reqTime = new \DateTime(date('Y-m-d H:i:s.' . $reqMicro[1], $_SERVER['REQUEST_TIME'])); $curMicro = microtime(true); $tMicro = sprintf("%06d", ($curMicro - floor($curMicro)) * 1000000); $curTime = new \DateTime(date('Y-m-d H:i:s.' . $tMicro, $curMicro)); $msg = [ 'title' => $subject, 'server_name' => $_SERVER['SERVER_NAME'], 'server_ip' => $_SERVER['SERVER_ADDR'], 'server_port' => $_SERVER['SERVER_PORT'], 'client_ip' => $_SERVER['REMOTE_ADDR'], 'request_uri' => $_SERVER['REQUEST_URI'], 'request_time' => $reqTime->format('Y-m-d H:i:s.u'), 'current_time' => $curTime->format('Y-m-d H:i:s.u'), 'server_info' => print_r($_SERVER, true), 'get_info' => print_r($_GET, true), 'post_info' => print_r($_POST, true), 'data' => $data, 'trace' => $this->_formatMsg($data, $type, $errFile, $errLine), ]; $this->_fluentd->post($tag, $msg); } private function _write($msg, $type, $errFile = null, $errLine = null) { if ($type & $this->_level) { if (is_object($msg) || is_array($msg)) { $data = print_r($msg, true); } else { $data = strval($msg); } //发送fluentd if ($this->_fluentd !== null) { $this->_sendFluentd($data, $type, $errFile, $errLine); } $data = $this->_formatMsg($data, $type, $errFile, $errLine); //记录日志 fwrite($this->_fp, $data); //发送邮件 // if ($this->_mailSwitch == 'on' || // ($this->_mailSwitch == 'auto' && // ($type & self::ERROR || $type & self::FATAL))) { // if (self::$_mailer !== null) { // $subject = sprintf("[ERROR_MAIL][%s][%s]Error info from logger", $this->_TYPES[$type], $this->_project); // self::$_mailer->subject($subject); // self::$_mailer->msgHTML($data); // if (!self::$_mailer->send()) {//邮件发送失败,写日志 // $err = self::$_mailer->ErrorInfo(); // fwrite($this->_fp, $this->_formatMsg($err, self::ERROR)); // } // } // } } } private function parseArgs($args) { $ret = ""; foreach ($args as $arg) { if (is_object($arg) || is_array($arg)) { $data = print_r($arg, true); } else { $data = strval($arg); } $ret .= $data . " "; } return $ret; } /** * @param string $path * @return Logger|null * 单例对象,参数为日志路径 */ public static function getInstance($path = '') { if (!(self::$_instance instanceof self)) { self::$_instance = new self($path); } return self::$_instance; } /** * @param string $path * @throws Exception * 设置log路径,默认标准输出 */ public function setPath($path = '') { if (empty($path)) { $this->_fp = fopen('php://stdout', 'w'); } else { if (is_dir($path)) { throw new \Exception('Cannot be a path, must be a file ' . $path); } $dn = dirname($path); if (!file_exists($dn)) { @mkdir($dn, 0777, true); } if (!file_exists($path)) { @touch($path); } if (!is_writable($path)) { throw new \Exception('Log file cannot be written! ' . $path); } $this->_fp = fopen($path, 'a'); } } /** * @param string $host * @param int $port * @param array $options * 配置fluentd */ public function setFluentd($host = '127.0.0.1', $port = 24224, $options = []) { $this->_fluentd = new FluentLogger($host, $port, $options); } /** * @param $val [development | production] * 设置应用环境,默认development * 关联日志级别 */ public function setEnv($val) { if ($val == self::DEVELOP) { $this->_env = $val; $this->setLevel(self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING | self::NOTICE | self::INFO | self::DEBUG); } elseif ($val == self::PRODUCT) { $this->_env = $val; $this->setLevel(self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING); } } /** * @param $val * 设置日志级别,默认self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING */ public function setLevel($val) { $this->_level = $val; } /** * @param $val true/false * debug back trace开关,默认打开 */ public function setDebugBackTrace($val) { $this->_debugBackTrace = $val; } public function setMailer(Mailer $mailer) { self::$_mailer = $mailer; } /** * @param $mail array * key as mail address * value as mail name */ public function addMailTo($mail) { foreach ($mail as $addr => $name) { if (strpos($addr, '@') !== false) { self::$_mailer->addAddress($addr, $name); } elseif (strpos($name, '@') !== false) { self::$_mailer->addAddress($name); } } } public function setProjectId($projectId) { $this->_project = $projectId; } /** * @param null $msg * 调试输出 */ public function Debug() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::DEBUG); } /** * @param null $msg * 信息输出 */ public function Info() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::INFO); } /** * @param null $msg * 注意输出 */ public function Notice() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::NOTICE); } /** * @param null $msg * 警告输出 */ public function Warning() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::WARNING); } /** * @param null $msg * 错误输出 */ public function Error() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::ERROR); } /** * @param null $msg * 致命错误输出 */ public function Fatal() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::FATAL); } /** * @param null $msg * 警报错误输出 */ public function Alert() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::ALERT); } /** * @param null $msg * 紧急错误输出 */ public function Emergency() { if (func_num_args() == 0) { return; } $args = func_get_args(); $msg = $this->parseArgs($args); $this->_write($msg, self::EMERGENCY); } /** * @param $errno * @param $errstr * @param $errfile * @param $errline * 系统错误捕获器 */ public function cbErrorHandler($errno, $errstr, $errfile, $errline) { switch ($errno) { case E_ERROR: case E_USER_ERROR: case E_CORE_ERROR: $errType = self::FATAL; break; case E_WARNING: case E_USER_WARNING: case E_CORE_WARNING: $errType = self::ERROR; break; default: $errType = self::WARNING; } $this->_write($errstr, $errType, $errfile, $errline); } }