PageRenderTime 25ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/components/Logger.php

https://gitlab.com/nitm/yii2-module
PHP | 395 lines | 299 code | 39 blank | 57 comment | 26 complexity | 6a2de07261c07edd925ef9d6423e77e2 MD5 | raw file
  1. <?php
  2. namespace nitm\components;
  3. use yii\base\Model;
  4. use yii\helpers\ArrayHelper;
  5. use nitm\helpers\Session;
  6. use nitm\helpers\Network;
  7. class Logger extends \yii\log\Logger
  8. {
  9. use \nitm\traits\EventTraits;
  10. //constant data
  11. const LT_FILE = 'file';
  12. const LT_DB = 'db';
  13. const LT_MONGO = 'mongo';
  14. //public data
  15. public $db;
  16. public $targets;
  17. public $level = 'notice';
  18. public $traceLevel = 1;
  19. public $dbName;
  20. public $collectionName = 'no-log-selected';
  21. public $categories = [];
  22. public $oldCollectionName;
  23. public $type;
  24. public $currentUser;
  25. //For Logger events
  26. const EVENT_START = 'nitm.logger.prepare';
  27. const EVENT_PROCESS = 'nitm.logger.process';
  28. const EVENT_END = 'nitm.logger.end';
  29. public function init()
  30. {
  31. parent::init();
  32. $this->currentUser = (\Yii::$app->hasProperty('user') && \Yii::$app->user->getId()) ? \Yii::$app->user->getIdentity() : new \nitm\models\User(['username' => (php_sapi_name() == 'cli' ? 'console' : 'web')]);
  33. if(!$this->dispatcher instanceof \yii\log\Dispatcher) {
  34. $this->initDispatcher();
  35. }
  36. $this->attachToEvents([
  37. self::EVENT_START => [$this, 'prepare'],
  38. self::EVENT_PROCESS => [$this, 'process'],
  39. self::EVENT_END => [$this, 'flush']
  40. ]);
  41. }
  42. public function initTarget($refresh=false)
  43. {
  44. switch($this->type)
  45. {
  46. case self::LT_MONGO:
  47. $class = log\MongoTarget::className();
  48. break;
  49. case self::LT_DB:
  50. $class = log\DbTarget::className();
  51. break;
  52. default:
  53. $class = log\FileTarget::className();
  54. break;
  55. }
  56. $options = [
  57. 'class' => $class,
  58. 'levels' => 0,
  59. 'enabled' => true,
  60. 'categories' => $this->categories
  61. ];
  62. if($this->db && !empty($this->db))
  63. $options += ArrayHelper::toArray($this->db);
  64. return \Yii::createObject($options);
  65. }
  66. public function initDispatcher()
  67. {
  68. if(\Yii::$app->get('log') && is_array($this->dispatcher) && isset($this->dispatcher['targets'])) {
  69. $targets = array_map(function ($config) {
  70. return \Yii::createObject($config);
  71. }, $this->dispatcher['targets']);
  72. $this->dispatcher = \Yii::$app->log;
  73. $this->dispatcher->targets = $targets;
  74. \Yii::$app->log->targets = array_merge(\Yii::$app->log->targets, $targets);
  75. } else {
  76. if(is_array($this->dispatcher))
  77. {
  78. $this->dispatcher = \Yii::createObject(array_merge([
  79. 'class' => '\yii\log\Dispatcher'
  80. ], ArrayHelper::toArray($this->dispatcher)));
  81. }
  82. else {
  83. $this->dispatcher = \Yii::$app->log;
  84. }
  85. $thisTarget = $this->initTarget();
  86. $this->targets = [
  87. 'nitm-'.$this->type.'-log' => $thisTarget
  88. ];
  89. foreach($this->dispatcher->targets as $target) {
  90. $target->except = array_merge((array)$target->except, $thisTarget->categories);
  91. }
  92. $this->dispatcher->targets = array_merge(\Yii::$app->log->targets, $this->targets);
  93. }
  94. return $this;
  95. }
  96. /**
  97. * Temporarily change the collection name
  98. * @param string $newName
  99. */
  100. public function changeCollectionName($newName=null)
  101. {
  102. if(is_null($newName)) {
  103. $this->collectionName = $this->oldCollectionName;
  104. $this->oldCollectionName = null;
  105. }
  106. else {
  107. $this->oldCollectionName = $this->collectionName;
  108. $this->collectionName = $newName;
  109. }
  110. foreach($this->dispatcher->targets as $target)
  111. $target->logTable = $this->collectionName;
  112. }
  113. //- end write
  114. /**
  115. * Prepare a log event
  116. */
  117. public function prepare($event) {
  118. return false;
  119. }
  120. /**
  121. * Proces a log event
  122. * @param \yii\base\Event $event
  123. * @return boolean
  124. */
  125. public function process($event)
  126. {
  127. if($event->hasProperty('result')) {
  128. $event->data = $event->result;
  129. }
  130. $action = $event->sender->getScenario();
  131. if(!$this->canLog(ArrayHelper::getValue($event->data, 'level', null)))
  132. return;
  133. try {
  134. $changedAttributes = $event->sender->changedAttributes;
  135. } catch(\Exception $e) {
  136. $changedAttributes = '';
  137. }
  138. $event->data = array_merge([
  139. 'internal_category' => $this->getCategoryText(ArrayHelper::getValue($event->data, 'internal_category', null), $event->sender->getScenario()),
  140. 'level' => $this->getlevel($event->sender->getScenario()),
  141. 'category' => $this->getCategory(ArrayHelper::getValue($event->data, 'category', null), $event->sender->getScenario()),
  142. 'action' => \Yii::$app->getModule('nitm')->getAlert()->store()->getStoredAction($event),
  143. 'timestamp' => microtime(true),
  144. 'table_name' => $event->sender->isWhat(),
  145. 'message' => implode(' ', [
  146. "Succesfully {$action}d",
  147. $event->sender->isWhat(),
  148. ':'.$event->sender->title()."[".$event->sender->getId()."]\n\nChanged values: \n".json_encode($changedAttributes, JSON_PRETTY_PRINT)
  149. ])
  150. ], (array)$event->data);
  151. $event->handled = $this->log($event->data, $event->data['level'], $event->data['category'], ArrayHelper::remove($event->data, 'collectionName', null));
  152. return $event->handled;
  153. }
  154. public function getLevels() {
  155. return [
  156. 'log',
  157. 'debug',
  158. 'info',
  159. 'notice',
  160. 'warning',
  161. 'error',
  162. 'critical',
  163. 'alert',
  164. 'emergency',
  165. ];
  166. }
  167. public function levelName($level) {
  168. return ArrayHelper::getValue($this->levels, $this->getLevel($level), 'log');
  169. }
  170. public function getLevel($level) {
  171. if(is_numeric($level)) {
  172. return $level >= 0 && $level < count($this->levels) ? $level : 0;
  173. } else {
  174. return array_search(strtolower($level), $this->levels);
  175. }
  176. }
  177. /**
  178. * Base on the provided scenario determine a log level
  179. * @param string $scenario The model scenario
  180. * levels
  181. * 0: log,
  182. * 1: debug,
  183. * 2: info,
  184. * 3: notice,
  185. * 4: warning,
  186. * 5: error,
  187. * 6: critical,
  188. * 7: alert,
  189. * 8: emergency,
  190. * @return int The log level based on scneario
  191. */
  192. protected function getScenarioLevel($scenario)
  193. {
  194. $ret_val = 0;
  195. switch($scenario)
  196. {
  197. case 'create':
  198. case 'delete':
  199. $ret_val = 3;
  200. break;
  201. case 'disable':
  202. case 'resolve':
  203. case 'close':
  204. case 'complete':
  205. case 'approve':
  206. case 'update':
  207. $ret_val = 0;
  208. break;
  209. case 'view':
  210. $ret_val = 2;
  211. break;
  212. }
  213. return $ret_val;
  214. }
  215. protected function getCategoryText($category=null, $scenario=null)
  216. {
  217. switch($scenario)
  218. {
  219. case 'view':
  220. $ret_val = 'User Activity';
  221. break;
  222. default:
  223. $ret_val = is_null($category) ? 'User Action' : $category;
  224. break;
  225. }
  226. return $ret_val;
  227. }
  228. /**
  229. * @param string $category
  230. * @param string $scenario
  231. * @return string
  232. */
  233. protected function getCategory($category=null, $scenario=null)
  234. {
  235. switch($scenario)
  236. {
  237. case 'view':
  238. $ret_val = 'user-ctivity';
  239. break;
  240. default:
  241. $ret_val = is_null($category) ? 'user-action' : $category;
  242. break;
  243. }
  244. return $ret_val;
  245. }
  246. protected function getAction($action=null)
  247. {
  248. $ret_val = $action;
  249. return $ret_val;
  250. }
  251. /**
  252. * Log data
  253. * @param array $array
  254. * @param string $collectionName
  255. * @return Logger $this
  256. */
  257. public function log($array, $level, $category='application', $collectionName=null)
  258. {
  259. if(is_array($array) && $array != [])
  260. {
  261. $array['category'] = ArrayHelper::getValue($array, 'category', $category);
  262. $keys = array_flip([
  263. 'message', 'level', 'category', 'timestamp'
  264. ]);
  265. $coreInfo = array_values(array_replace($keys, array_intersect_key((array)$array, $keys)));
  266. $coreInfo = array_merge($coreInfo, [$this->traces, memory_get_usage()]);
  267. $array = array_merge($coreInfo,
  268. $this->getBaseInfo(),
  269. (array)array_diff_key((array)$array, $keys),
  270. $this->getExtraInfo());
  271. $array[1] = $this->getLevel($array[1]);
  272. $array['action'] = $this->getAction($array['action']);
  273. if(is_string($collectionName))
  274. $this->messages[$collectionName.':'.uniqid()] = $array;
  275. else
  276. $this->messages[] = $array;
  277. }
  278. return $this;
  279. }
  280. protected function getTraces() {
  281. $traces = [];
  282. if ($this->traceLevel > 0) {
  283. $count = 0;
  284. $ts = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  285. array_pop($ts); // remove the last trace since it would be the entry script, not very useful
  286. foreach ($ts as $trace) {
  287. if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII2_PATH) !== 0) {
  288. unset($trace['object'], $trace['args']);
  289. $traces[] = $trace;
  290. if (++$count >= $this->traceLevel) {
  291. break;
  292. }
  293. }
  294. }
  295. }
  296. return $traces;
  297. }
  298. //end fucntion
  299. /**
  300. * Determine whether this level is loggable
  301. * @param int $level
  302. * @return boolean the data can be stored for logging
  303. */
  304. public function canLog($level=null)
  305. {
  306. return $level <= $this->threshold;
  307. }
  308. protected function getThreshold() {
  309. return $this->getLevel($this->level);
  310. }
  311. /**
  312. * Return some general log info
  313. * @return array
  314. */
  315. protected function getBaseInfo()
  316. {
  317. return [
  318. 'action' => 'log',
  319. 'db_name' => \nitm\models\DB::getDefaultDbName(),
  320. 'table_name' => 'logger',
  321. 'user' => $this->currentUser->username,
  322. 'user_id' => (int)$this->currentUser->getId(),
  323. 'prefix' => $this->collectionName,
  324. 'log_time' => strtotime('now'),
  325. 'response_status_code' => \Yii::$app->response->statusCode,
  326. 'response_status_text' => \Yii::$app->response->statusText,
  327. 'response_headers' => json_encode(\Yii::$app->response->headers->toArray()),
  328. 'response_data' => json_encode(\Yii::$app->response->data),
  329. 'request_headers' => json_encode(\Yii::$app->request->headers->toArray()),
  330. 'request_data' => json_encode(\Yii::$app->request->bodyParams),
  331. 'error_level' => 0,
  332. ];
  333. }
  334. /**
  335. * Return some general log info
  336. * @return array
  337. */
  338. protected function getExtraInfo()
  339. {
  340. $parser = (\UAParser\Parser::create());
  341. $r = $parser->parse(!\Yii::$app->request->userAgent ? $_SERVER['SERVER_SOFTWARE'] : \Yii::$app->request->userAgent);
  342. return [
  343. 'ua_family' => $r->ua->family,
  344. 'ua_version' => implode('.', array_filter([$r->ua->major, $r->ua->minor])).($r->ua->patch ? '-'.$r->ua->patch : ''),
  345. 'os_family' => $r->os->family,
  346. 'os_version' => implode('.', array_filter([$r->os->major, $r->os->minor])).($r->os->patch ? '-'.$r->os->patch : ''),
  347. 'device_family' => $r->device->family,
  348. 'request_method' => \Yii::$app->request->method,
  349. 'user_agent' => \Yii::$app->request->userAgent,
  350. 'ip_addr' => !\Yii::$app->request->userIp ? 'localhost' : \Yii::$app->request->userIp,
  351. 'host' => !\Yii::$app->request->userHost ? 'localhost' : \Yii::$app->request->userHost,
  352. ];
  353. }
  354. }
  355. // end log class
  356. ?>