Logger.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <?php
  2. namespace App\Library\Logger;
  3. /**
  4. * Created by PhpStorm.
  5. * User: lhz
  6. * Date: 2016-5-31
  7. * Time: 8:50
  8. * 日志器
  9. */
  10. use Fluent\Logger\FluentLogger;
  11. class Logger {
  12. //常量定义
  13. const DEBUG = 0b00000001; //Debug: debug-level messages
  14. const INFO = 0b00000010; //Informational: informational messages
  15. const NOTICE = 0b00000100; //Notice: normal but significant condition
  16. const WARNING = 0b00001000; //Warning: warning conditions
  17. const ERROR = 0b00010000; //Error: error conditions
  18. const FATAL = 0b00100000; //Critical: critical conditions
  19. const ALERT = 0b01000000; //Alert: action must be taken immediately
  20. const EMERGENCY = 0b10000000; //Emergency: system is unusable
  21. const PRE_DEFINE_ALL = self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING | self::NOTICE | self::INFO | self::DEBUG;
  22. const PRE_DEFINE_ERROR = self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR;
  23. const PRE_DEFINE_WARN = self::PRE_DEFINE_ERROR | self::WARNING;
  24. const PRE_DEFINE_INFO = self::WARNING | self::NOTICE | self::INFO | self::DEBUG;
  25. const DEVELOP = 'development';
  26. const PRODUCT = 'production';
  27. //单例对象
  28. static $_instance = NULL;
  29. //邮件
  30. static $_mailer = NULL;
  31. //fluentd
  32. private $_fluentd = NULL;
  33. //日志句柄
  34. private $_fp = NULL;
  35. //日志级别
  36. private $_level = self::FATAL;
  37. //应用环境
  38. private $_env = self::DEVELOP;
  39. //项目ID
  40. private $_project = '';
  41. //邮件错误通知开关
  42. private $_mailSwitch = 'auto';//auto表示自动,级别在error及以上发邮件;on表示全部发送邮件;off表示关闭邮件警告
  43. //debug_backtrace开关
  44. private $_debugBackTrace = true;
  45. //日志类型枚举
  46. private $_TYPES = [
  47. self::DEBUG => 'DEBUG',
  48. self::INFO => 'INFO',
  49. self::NOTICE => 'NOTICE',
  50. self::WARNING => 'WARNING',
  51. self::ERROR => 'ERROR',
  52. self::FATAL => 'FATAL',
  53. self::ALERT => 'ALERT',
  54. self::EMERGENCY => 'EMERGENCY',
  55. ];
  56. //日志类型分类
  57. private $_CATEGORIES = [
  58. self::DEBUG => 'INFO',
  59. self::INFO => 'INFO',
  60. self::NOTICE => 'INFO',
  61. self::WARNING => 'INFO',
  62. self::ERROR => 'ERROR',
  63. self::FATAL => 'ERROR',
  64. self::ALERT => 'ERROR',
  65. self::EMERGENCY => 'ERROR',
  66. ];
  67. public function __construct($path = NULL)
  68. {
  69. $this->setPath($path);
  70. $this->setLevel(self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING);
  71. }
  72. private function __clone() {
  73. }
  74. private function _formatMsg($msg, $type, $file = null, $line = null) {
  75. $extra = '';
  76. if ($this->_debugBackTrace && $file === null && $line === null) {
  77. $traceLevel = 10;
  78. $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $traceLevel);
  79. $cnt = count($trace);
  80. if ($cnt > 0) {
  81. foreach ($trace as $l) {
  82. if (isset($l['file']) && isset($l['line'])) {
  83. $extra .= sprintf("%s(%d)\n", $l['file'], $l['line']);
  84. }
  85. }
  86. $file = $trace[$cnt-1]['file'];
  87. $line = $trace[$cnt-1]['line'];
  88. }
  89. }
  90. if ($file === null) {
  91. $file = __FILE__;
  92. }
  93. if ($line === null) {
  94. $line = __LINE__;
  95. }
  96. 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);
  97. }
  98. private function _sendFluentd($data, $type, $errFile = null, $errLine = null) {
  99. $tag = sprintf("%s.%s.%s", $this->_project, $this->_CATEGORIES[$type], $this->_TYPES[$type]);
  100. $subject = sprintf("[%s][%s]%s", $this->_project, $this->_TYPES[$type], substr($data, 0, 40));
  101. $reqMicro = explode('.', $_SERVER['REQUEST_TIME_FLOAT']);
  102. $reqTime = new \DateTime(date('Y-m-d H:i:s.' . $reqMicro[1], $_SERVER['REQUEST_TIME']));
  103. $curMicro = microtime(true);
  104. $tMicro = sprintf("%06d", ($curMicro - floor($curMicro)) * 1000000);
  105. $curTime = new \DateTime(date('Y-m-d H:i:s.' . $tMicro, $curMicro));
  106. $msg = [
  107. 'title' => $subject,
  108. 'server_name' => $_SERVER['SERVER_NAME'],
  109. 'server_ip' => $_SERVER['SERVER_ADDR'],
  110. 'server_port' => $_SERVER['SERVER_PORT'],
  111. 'client_ip' => $_SERVER['REMOTE_ADDR'],
  112. 'request_uri' => $_SERVER['REQUEST_URI'],
  113. 'request_time' => $reqTime->format('Y-m-d H:i:s.u'),
  114. 'current_time' => $curTime->format('Y-m-d H:i:s.u'),
  115. 'server_info' => print_r($_SERVER, true),
  116. 'get_info' => print_r($_GET, true),
  117. 'post_info' => print_r($_POST, true),
  118. 'data' => $data,
  119. 'trace' => $this->_formatMsg($data, $type, $errFile, $errLine),
  120. ];
  121. $this->_fluentd->post($tag, $msg);
  122. }
  123. private function _write($msg, $type, $errFile = null, $errLine = null) {
  124. if ($type & $this->_level) {
  125. if (is_object($msg) || is_array($msg)) {
  126. $data = print_r($msg, true);
  127. } else {
  128. $data = strval($msg);
  129. }
  130. //发送fluentd
  131. if ($this->_fluentd !== null) {
  132. $this->_sendFluentd($data, $type, $errFile, $errLine);
  133. }
  134. $data = $this->_formatMsg($data, $type, $errFile, $errLine);
  135. //记录日志
  136. fwrite($this->_fp, $data);
  137. //发送邮件
  138. // if ($this->_mailSwitch == 'on' ||
  139. // ($this->_mailSwitch == 'auto' &&
  140. // ($type & self::ERROR || $type & self::FATAL))) {
  141. // if (self::$_mailer !== null) {
  142. // $subject = sprintf("[ERROR_MAIL][%s][%s]Error info from logger", $this->_TYPES[$type], $this->_project);
  143. // self::$_mailer->subject($subject);
  144. // self::$_mailer->msgHTML($data);
  145. // if (!self::$_mailer->send()) {//邮件发送失败,写日志
  146. // $err = self::$_mailer->ErrorInfo();
  147. // fwrite($this->_fp, $this->_formatMsg($err, self::ERROR));
  148. // }
  149. // }
  150. // }
  151. }
  152. }
  153. private function parseArgs($args) {
  154. $ret = "";
  155. foreach ($args as $arg) {
  156. if (is_object($arg) || is_array($arg)) {
  157. $data = print_r($arg, true);
  158. } else {
  159. $data = strval($arg);
  160. }
  161. $ret .= $data . " ";
  162. }
  163. return $ret;
  164. }
  165. /**
  166. * @param string $path
  167. * @return Logger|null
  168. * 单例对象,参数为日志路径
  169. */
  170. public static function getInstance($path = '') {
  171. if (!(self::$_instance instanceof self)) {
  172. self::$_instance = new self($path);
  173. }
  174. return self::$_instance;
  175. }
  176. /**
  177. * @param string $path
  178. * @throws Exception
  179. * 设置log路径,默认标准输出
  180. */
  181. public function setPath($path = '') {
  182. if (empty($path)) {
  183. $this->_fp = fopen('php://stdout', 'w');
  184. } else {
  185. if (is_dir($path)) {
  186. throw new \Exception('Cannot be a path, must be a file ' . $path);
  187. }
  188. $dn = dirname($path);
  189. if (!file_exists($dn)) {
  190. @mkdir($dn, 0777, true);
  191. }
  192. if (!file_exists($path)) {
  193. @touch($path);
  194. }
  195. if (!is_writable($path)) {
  196. throw new \Exception('Log file cannot be written! ' . $path);
  197. }
  198. $this->_fp = fopen($path, 'a');
  199. }
  200. }
  201. /**
  202. * @param string $host
  203. * @param int $port
  204. * @param array $options
  205. * 配置fluentd
  206. */
  207. public function setFluentd($host = '127.0.0.1', $port = 24224, $options = []) {
  208. $this->_fluentd = new FluentLogger($host, $port, $options);
  209. }
  210. /**
  211. * @param $val [development | production]
  212. * 设置应用环境,默认development
  213. * 关联日志级别
  214. */
  215. public function setEnv($val) {
  216. if ($val == self::DEVELOP) {
  217. $this->_env = $val;
  218. $this->setLevel(self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING | self::NOTICE | self::INFO | self::DEBUG);
  219. } elseif ($val == self::PRODUCT) {
  220. $this->_env = $val;
  221. $this->setLevel(self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING);
  222. }
  223. }
  224. /**
  225. * @param $val
  226. * 设置日志级别,默认self::EMERGENCY | self::ALERT | self::FATAL | self::ERROR | self::WARNING
  227. */
  228. public function setLevel($val) {
  229. $this->_level = $val;
  230. }
  231. /**
  232. * @param $val true/false
  233. * debug back trace开关,默认打开
  234. */
  235. public function setDebugBackTrace($val) {
  236. $this->_debugBackTrace = $val;
  237. }
  238. public function setMailer(Mailer $mailer) {
  239. self::$_mailer = $mailer;
  240. }
  241. /**
  242. * @param $mail array
  243. * key as mail address
  244. * value as mail name
  245. */
  246. public function addMailTo($mail) {
  247. foreach ($mail as $addr => $name) {
  248. if (strpos($addr, '@') !== false) {
  249. self::$_mailer->addAddress($addr, $name);
  250. } elseif (strpos($name, '@') !== false) {
  251. self::$_mailer->addAddress($name);
  252. }
  253. }
  254. }
  255. public function setProjectId($projectId) {
  256. $this->_project = $projectId;
  257. }
  258. /**
  259. * @param null $msg
  260. * 调试输出
  261. */
  262. public function Debug() {
  263. if (func_num_args() == 0) {
  264. return;
  265. }
  266. $args = func_get_args();
  267. $msg = $this->parseArgs($args);
  268. $this->_write($msg, self::DEBUG);
  269. }
  270. /**
  271. * @param null $msg
  272. * 信息输出
  273. */
  274. public function Info() {
  275. if (func_num_args() == 0) {
  276. return;
  277. }
  278. $args = func_get_args();
  279. $msg = $this->parseArgs($args);
  280. $this->_write($msg, self::INFO);
  281. }
  282. /**
  283. * @param null $msg
  284. * 注意输出
  285. */
  286. public function Notice() {
  287. if (func_num_args() == 0) {
  288. return;
  289. }
  290. $args = func_get_args();
  291. $msg = $this->parseArgs($args);
  292. $this->_write($msg, self::NOTICE);
  293. }
  294. /**
  295. * @param null $msg
  296. * 警告输出
  297. */
  298. public function Warning() {
  299. if (func_num_args() == 0) {
  300. return;
  301. }
  302. $args = func_get_args();
  303. $msg = $this->parseArgs($args);
  304. $this->_write($msg, self::WARNING);
  305. }
  306. /**
  307. * @param null $msg
  308. * 错误输出
  309. */
  310. public function Error() {
  311. if (func_num_args() == 0) {
  312. return;
  313. }
  314. $args = func_get_args();
  315. $msg = $this->parseArgs($args);
  316. $this->_write($msg, self::ERROR);
  317. }
  318. /**
  319. * @param null $msg
  320. * 致命错误输出
  321. */
  322. public function Fatal() {
  323. if (func_num_args() == 0) {
  324. return;
  325. }
  326. $args = func_get_args();
  327. $msg = $this->parseArgs($args);
  328. $this->_write($msg, self::FATAL);
  329. }
  330. /**
  331. * @param null $msg
  332. * 警报错误输出
  333. */
  334. public function Alert() {
  335. if (func_num_args() == 0) {
  336. return;
  337. }
  338. $args = func_get_args();
  339. $msg = $this->parseArgs($args);
  340. $this->_write($msg, self::ALERT);
  341. }
  342. /**
  343. * @param null $msg
  344. * 紧急错误输出
  345. */
  346. public function Emergency() {
  347. if (func_num_args() == 0) {
  348. return;
  349. }
  350. $args = func_get_args();
  351. $msg = $this->parseArgs($args);
  352. $this->_write($msg, self::EMERGENCY);
  353. }
  354. /**
  355. * @param $errno
  356. * @param $errstr
  357. * @param $errfile
  358. * @param $errline
  359. * 系统错误捕获器
  360. */
  361. public function cbErrorHandler($errno, $errstr, $errfile, $errline) {
  362. switch ($errno) {
  363. case E_ERROR:
  364. case E_USER_ERROR:
  365. case E_CORE_ERROR:
  366. $errType = self::FATAL;
  367. break;
  368. case E_WARNING:
  369. case E_USER_WARNING:
  370. case E_CORE_WARNING:
  371. $errType = self::ERROR;
  372. break;
  373. default:
  374. $errType = self::WARNING;
  375. }
  376. $this->_write($errstr, $errType, $errfile, $errline);
  377. }
  378. }