|
@@ -0,0 +1,435 @@
|
|
|
|
+<?php
|
|
|
|
+namespace App\Library\Logger;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Created by PhpStorm.
|
|
|
|
+ * User: lhz
|
|
|
|
+ * Date: 2016-5-31
|
|
|
|
+ * Time: 8:50
|
|
|
|
+ * 日志器
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+use Fluent\Logger\FluentLogger;
|
|
|
|
+
|
|
|
|
+class Logger {
|
|
|
|
+ //常量定义
|
|
|
|
+ const DEBUG = 0b00000001; //Debug: debug-level messages
|
|
|
|
+ const INFO = 0b00000010; //Informational: informational messages
|
|
|
|
+ const NOTICE = 0b00000100; //Notice: normal but significant condition
|
|
|
|
+ const WARNING = 0b00001000; //Warning: warning conditions
|
|
|
|
+ const ERROR = 0b00010000; //Error: error conditions
|
|
|
|
+ const FATAL = 0b00100000; //Critical: critical conditions
|
|
|
|
+ const ALERT = 0b01000000; //Alert: action must be taken immediately
|
|
|
|
+ const EMERGENCY = 0b10000000; //Emergency: system is unusable
|
|
|
|
+ const PRE_DEFINE_ALL = self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING | self::NOTICE | self::INFO | self::DEBUG;
|
|
|
|
+ const PRE_DEFINE_ERROR = self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR;
|
|
|
|
+ const PRE_DEFINE_WARN = self::PRE_DEFINE_ERROR | self::WARNING;
|
|
|
|
+ const PRE_DEFINE_INFO = self::WARNING | self::NOTICE | self::INFO | self::DEBUG;
|
|
|
|
+
|
|
|
|
+ const DEVELOP = 'development';
|
|
|
|
+ const PRODUCT = 'production';
|
|
|
|
+
|
|
|
|
+ //单例对象
|
|
|
|
+ static $_instance = NULL;
|
|
|
|
+
|
|
|
|
+ //邮件
|
|
|
|
+ static $_mailer = NULL;
|
|
|
|
+
|
|
|
|
+ //fluentd
|
|
|
|
+ private $_fluentd = NULL;
|
|
|
|
+
|
|
|
|
+ //日志句柄
|
|
|
|
+ private $_fp = NULL;
|
|
|
|
+
|
|
|
|
+ //日志级别
|
|
|
|
+ private $_level = self::FATAL;
|
|
|
|
+
|
|
|
|
+ //应用环境
|
|
|
|
+ private $_env = self::DEVELOP;
|
|
|
|
+
|
|
|
|
+ //项目ID
|
|
|
|
+ private $_project = '';
|
|
|
|
+
|
|
|
|
+ //邮件错误通知开关
|
|
|
|
+ private $_mailSwitch = 'auto';//auto表示自动,级别在error及以上发邮件;on表示全部发送邮件;off表示关闭邮件警告
|
|
|
|
+
|
|
|
|
+ //debug_backtrace开关
|
|
|
|
+ private $_debugBackTrace = true;
|
|
|
|
+
|
|
|
|
+ //日志类型枚举
|
|
|
|
+ private $_TYPES = [
|
|
|
|
+ self::DEBUG => '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);
|
|
|
|
+ }
|
|
|
|
+}
|