Log::endProfile()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 4
nc 3
nop 2
crap 20
1
<?php
2
/**
3
 * Logger files.
4
 *
5
 * @package App
6
 *
7
 * @copyright YetiForce S.A.
8
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Mariusz Krzaczkowski <[email protected]>
10
 */
11
12
namespace App;
13
14
use yii\log\Logger;
15
16
/**
17
 * Logger class.
18
 */
19
class Log extends Logger
20
{
21
	public static $logToConsole;
22
	public static $logToFile;
23
	public static $logToProfile;
24
	public $logToLevels = 0;
25
	/**
26
	 * Column mapping by table for logs owasp.
27
	 *
28
	 * @var array
29
	 */
30
	public static $owaspColumnMapping = [
31
		'access_for_admin' => ['date', 'username', 'ip', 'module', 'url', 'agent', 'request', 'referer'],
32
		'access_for_api' => ['date', 'username', 'ip', 'url', 'agent', 'request'],
33
		'access_for_user' => ['date', 'username', 'ip', 'module', 'url', 'agent', 'request', 'referer'],
34
		'access_to_record' => ['date', 'username', 'ip', 'module', 'record', 'url', 'agent', 'request', 'referer'],
35
		'csrf' => ['date', 'username', 'ip', 'referer', 'url', 'agent'],
36
	];
37
	/**
38
	 * Column mapping by table for logs viewer.
39
	 *
40
	 * @var array
41
	 */
42
	public static $logsViewerColumnMapping = [
43
		'magento' => [
44
			'label' => 'LBL_MAGENTO',
45 6388
			'labelModule' => 'Settings:Magento',
46
			'table' => 'l_#__magento',
47 6388
			'icon' => 'yfi-magento',
48 6388
			'columns' => [
49 6388
				'time' => ['type' => 'DateTime', 'label' => 'LBL_TIME'],
50
				'category' => ['type' => 'Text', 'label' => 'LBL_CATEGORY'],
51 6388
				'message' => ['type' => 'Text', 'label' => 'LBL_MESSAGE'],
52
				'code' => ['type' => 'Text', 'label' => 'LBL_CODE'],
53
				'trace' => ['type' => 'Text', 'label' => 'LBL_BACKTRACE'],
54 6388
			],
55 6388
			'filter' => [
56 116
				'time' => 'DateTimeRange',
57
				'category' => 'Text',
58 6388
				'message' => 'Text',
59
				'code' => 'Text',
60
				'trace' => 'Text',
61
			],
62
		],
63
		'wapro' => [
64
			'label' => 'LBL_WAPRO_ERP',
65
			'labelModule' => 'Settings:Wapro',
66
			'table' => 'l_#__wapro',
67
			'icon' => 'fab fa-connectdevelop',
68 5890
			'columns' => [
69
				'time' => ['type' => 'DateTime', 'label' => 'LBL_TIME'],
70 5890
				'category' => ['type' => 'Text', 'label' => 'LBL_CATEGORY'],
71 5890
				'message' => ['type' => 'Text', 'label' => 'LBL_MESSAGE'],
72
				'error' => ['type' => 'Text', 'label' => 'LBL_CODE'],
73 5890
				'trace' => ['type' => 'Text', 'label' => 'LBL_BACKTRACE'],
74
			],
75
			'filter' => [
76
				'time' => 'DateTimeRange',
77
				'category' => 'Text',
78
				'message' => 'Text',
79
				'code' => 'Text',
80
				'trace' => 'Text',
81
			],
82
		],
83 7
		'switchUsers' => [
84
			'label' => 'LBL_SWITCH_USERS',
85 7
			'labelModule' => 'Settings:Users',
86 7
			'table' => 'l_#__switch_users',
87
			'icon' => 'yfi-users',
88 7
			'columns' => [
89
				'date' => ['type' => 'DateTime', 'label' => 'LBL_TIME'],
90
				'status' => ['type' => 'Text', 'label' => 'LBL_STATUS'],
91
				'busername' => ['type' => 'Text', 'label' => 'LBL_BASE_USER'],
92
				'dusername' => ['type' => 'Text', 'label' => 'LBL_DEST_USER'],
93
				'ip' => ['type' => 'Text', 'label' => 'LBL_IP_ADDRESS'],
94
				'agent' => ['type' => 'Text', 'label' => 'LBL_USER_AGENT'],
95
			],
96
			'filter' => [
97
				'date' => 'DateTimeRange',
98 7
				'busername' => 'Text',
99
				'dusername' => 'Text',
100 7
				'ip' => 'Text',
101 7
				'agent' => 'Text',
102
			],
103 7
		],
104
		'batchMethod' => [
105
			'label' => 'LBL_BATCH_METHODS',
106
			'labelModule' => 'Settings:CronTasks',
107
			'table' => 'l_#__batchmethod',
108
			'icon' => 'fas fa-swatchbook',
109
			'columns' => [
110
				'date' => ['type' => 'DateTime', 'label' => 'LBL_TIME'],
111
				'method' => ['type' => 'Text', 'label' => 'LBL_BATCH_NAME'],
112
				'message' => ['type' => 'Text', 'label' => 'LBL_ERROR_MASAGE'],
113 22
				'userid' => ['type' => 'Owner', 'label' => 'LBL_OWNER'],
114
				'params' => ['type' => 'Text', 'label' => 'LBL_PARAMS'],
115 22
			],
116 22
			'filter' => [
117
				'date' => 'DateTimeRange',
118
				'method' => 'Text',
119
				'message' => 'Text',
120
				'params' => 'Text',
121
			],
122
		],
123
		'mail' => [
124
			'label' => 'LBL_MAILS_NOT_SENT',
125
			'labelModule' => 'Settings:Log',
126
			'table' => 'l_#__mail',
127
			'icon' => 'adminIcon-mail-queue',
128
			'columns' => [
129
				'date' => ['type' => 'DateTime', 'label' => 'LBL_TIME'],
130
				'subject' => ['type' => 'Text', 'label' => 'LBL_SUBJECT'],
131
				'from' => ['type' => 'Text', 'label' => 'LBL_FROM'],
132
				'to' => ['type' => 'Text', 'label' => 'LBL_TO'],
133
				'owner' => ['type' => 'Owner', 'label' => 'LBL_OWNER'],
134
			],
135
			'filter' => [
136
				'date' => 'DateTimeRange',
137
				'subject' => 'Text',
138
				'from' => 'Text',
139
				'to' => 'Text',
140
			],
141
		],
142
		'profile' => [
143
			'label' => 'LBL_PROFILING',
144
			'labelModule' => 'Settings:Log',
145
			'table' => 'l_#__profile',
146
			'icon' => 'fas fa-stopwatch',
147
			'columns' => [
148
				'category' => ['type' => 'Text', 'label' => 'Category'],
149
				'info' => ['type' => 'Text', 'label' => 'LBL_PARAMS'],
150
				'log_time' => ['type' => 'Text', 'label' => 'LBL_TIME'],
151
				'trace' => ['type' => 'Text', 'label' => 'LBL_BACKTRACE'],
152
				'duration' => ['type' => 'Text', 'label' => 'LBL_DURATION'],
153
			],
154
			'filter' => [
155
				'category' => 'Text',
156
				'subinfoject' => 'Text',
157
				'log_time' => 'Text',
158
				'trace' => 'Text',
159
				'duration' => 'Text',
160
			],
161
		],
162
	];
163
	public static $levelMap = [
164
		'error' => Logger::LEVEL_ERROR,
165
		'warning' => Logger::LEVEL_WARNING,
166
		'info' => Logger::LEVEL_INFO,
167
		'trace' => Logger::LEVEL_TRACE,
168
		'profile' => Logger::LEVEL_PROFILE,
169
	];
170
171
	/**
172
	 * Initializes the logger by registering [[flush()]] as a shutdown function.
173
	 */
174
	public function init()
175
	{
176
		parent::init();
177
		if (\Config\Debug::$LOG_LEVELS) {
178
			$this->setLevels(\Config\Debug::$LOG_LEVELS);
0 ignored issues
show
Bug introduced by
Config\Debug::LOG_LEVELS of type true is incompatible with the type array|integer expected by parameter $levels of App\Log::setLevels(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
			$this->setLevels(/** @scrutinizer ignore-type */ \Config\Debug::$LOG_LEVELS);
Loading history...
179
		}
180
	}
181
182
	/**
183
	 * Sets the message levels that this target is interested in.
184
	 *
185
	 * @param array|int $levels message levels that this target is interested in.
186
	 */
187
	public function setLevels($levels)
188
	{
189
		if (\is_array($levels)) {
190
			foreach ($levels as $level) {
191
				if (isset(self::$levelMap[$level])) {
192
					$this->logToLevels |= self::$levelMap[$level];
193
				} else {
194
					throw new Exceptions\AppException("Unrecognized level: $level");
195
				}
196
			}
197
		} else {
198
			$bitmapValues = array_reduce(self::$levelMap, fn ($carry, $item) => $carry | $item);
199
			if (!($bitmapValues & $levels) && 0 !== $levels) {
200
				throw new Exceptions\AppException("Incorrect $levels value");
201
			}
202
			$this->logToLevels = $levels;
203
		}
204
	}
205
206
	/**
207
	 * Logs a message with the given type and category.
208
	 * If [[traceLevel]] is greater than 0, additional call stack information about
209
	 * the application code will be logged as well.
210
	 *
211
	 * @param array|string $message  the message to be logged. This can be a simple string or a more
212
	 *                               complex data structure that will be handled by a [[Target|log target]]
213
	 * @param int          $level    the level of the message. This must be one of the following:
214
	 *                               `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`,
215
	 *                               `Logger::LEVEL_PROFILE_BEGIN`, `Logger::LEVEL_PROFILE_END`
216
	 * @param string       $category the category of the message
217
	 */
218
	public function log($message, $level, $category = '')
219
	{
220
		if (0 !== $this->logToLevels && !($this->logToLevels & $level)) {
221
			return;
222
		}
223
		$traces = '';
224
		if ($this->traceLevel) {
225
			$traces = Debuger::getBacktrace(2, $this->traceLevel, ' - ');
226
		}
227
		if (static::$logToConsole) {
228
			Debuger::addLogs($message, self::getLevelName($level), $traces);
229
		}
230
		$this->messages[] = [$message, $level, $category, microtime(true), $traces];
231
		if ($this->flushInterval > 0 && \count($this->messages) >= $this->flushInterval) {
232
			$this->flush();
233
		}
234
	}
235
236
	/**
237
	 * Logs a trace message.
238
	 * Trace messages are logged mainly for development purpose to see
239
	 * the execution work flow of some code.
240
	 *
241
	 * @param string $message  the message to be logged
242
	 * @param string $category the category of the message
243
	 */
244
	public static function trace($message, $category = '')
245
	{
246
		if (static::$logToFile) {
247
			\Yii::getLogger()->log($message, Logger::LEVEL_TRACE, $category);
248
		}
249
	}
250
251
	/**
252
	 * Logs an informative message.
253
	 * An informative message is typically logged by an application to keep record of
254
	 * something important (e.g. an administrator logs in).
255
	 *
256
	 * @param string $message  the message to be logged
257
	 * @param string $category the category of the message
258
	 */
259
	public static function info($message, $category = '')
260
	{
261
		if (static::$logToFile) {
262
			\Yii::getLogger()->log($message, Logger::LEVEL_INFO, $category);
263
		}
264
	}
265
266
	/**
267
	 * Logs a warning message.
268
	 * A warning message is typically logged when an error occurs while the execution
269
	 * can still continue.
270
	 *
271
	 * @param string $message  the message to be logged
272
	 * @param string $category the category of the message
273
	 */
274
	public static function warning($message, $category = '')
275
	{
276
		if (static::$logToFile) {
277
			\Yii::getLogger()->log($message, Logger::LEVEL_WARNING, $category);
278
		}
279
	}
280
281
	/**
282
	 * Logs an error message.
283
	 * An error message is typically logged when an unrecoverable error occurs
284
	 * during the execution of an application.
285
	 *
286
	 * @param string $message  the message to be logged
287
	 * @param string $category the category of the message
288
	 */
289
	public static function error($message, $category = '')
290
	{
291
		if (static::$logToFile) {
292
			\Yii::getLogger()->log($message, Logger::LEVEL_ERROR, $category);
293
		}
294
	}
295
296
	/**
297
	 * Marks the beginning of a code block for profiling.
298
	 * This has to be matched with a call to [[endProfile]] with the same category name.
299
	 * The begin- and end- calls must also be properly nested. For example,.
300
	 *
301
	 * ```php
302
	 * \Yii::beginProfile('block1');
303
	 * // some code to be profiled
304
	 *     \Yii::beginProfile('block2');
305
	 *     // some other code to be profiled
306
	 *     \Yii::endProfile('block2');
307
	 * \Yii::endProfile('block1');
308
	 * ```
309
	 *
310
	 * @param string $token    token for the code block
311
	 * @param string $category the category of this log message
312
	 *
313
	 * @see endProfile()
314
	 */
315
	public static function beginProfile($token, $category = '')
316
	{
317
		if (static::$logToProfile) {
318
			$categories = \Config\Debug::$LOG_PROFILE_CATEGORIES ?? [];
319
			if ($categories && !\in_array($category, $categories)) {
320
				return;
321
			}
322
			\Yii::getLogger()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category);
323
		}
324
	}
325
326
	/**
327
	 * Marks the end of a code block for profiling.
328
	 * This has to be matched with a previous call to [[beginProfile]] with the same category name.
329
	 *
330
	 * @param string $token    token for the code block
331
	 * @param string $category the category of this log message
332
	 *
333
	 * @see beginProfile()
334
	 */
335
	public static function endProfile($token, $category = '')
336
	{
337
		if (static::$logToProfile) {
338
			$categories = \Config\Debug::$LOG_PROFILE_CATEGORIES ?? [];
339
			if ($categories && !\in_array($category, $categories)) {
340
				return;
341
			}
342
			\Yii::getLogger()->log($token, Logger::LEVEL_PROFILE_END, $category);
343
		}
344
	}
345
346
	/**
347
	 * Get user action logs.
348
	 *
349
	 * @param string $type
350
	 * @param string $mode
351
	 * @param bool   $countMode
352
	 *
353
	 * @return array
354
	 */
355
	public static function getLogs($type, $mode, $countMode = false)
356
	{
357
		$db = \App\Db::getInstance('log');
358
		$query = (new \App\Db\Query())->from('o_#__' . $type);
359
		if ('oneDay' === $mode) {
360
			$query->where(['>=', 'date', date('Y-m-d H:i:s', strtotime('-1 day'))]);
361
		} else {
362
			$query->limit(100);
363
		}
364
		if ($countMode) {
365
			return $query->count('*', $db);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->count('*', $db) returns the type integer|string which is incompatible with the documented return type array.
Loading history...
366
		}
367
		$query->orderBy(['id' => SORT_DESC]);
368
		return $query->all($db);
369
	}
370
371
	/**
372
	 * Get last logs.
373
	 *
374
	 * @param bool|string[] $types
375
	 *
376
	 * @return string
377
	 */
378
	public static function getlastLogs($types = false)
379
	{
380
		$content = '';
381
		$i = 0;
382
		foreach (\Yii::getLogger()->messages as $message) {
383
			$level = \yii\log\Logger::getLevelName($message[1]);
384
			if (false !== $types && !\in_array($level, $types)) {
385
				continue;
386
			}
387
			$content .= "#$i [$level]";
388
			$category = $message[2] ?: '';
389
			if ($category) {
390
				$content .= "[$category]";
391
			}
392
			$content .= " {$message[0]}" . PHP_EOL;
393
			++$i;
394
		}
395
		return $content;
396
	}
397
}
398