1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PSFS\base; |
4
|
|
|
|
5
|
|
|
use Monolog\Formatter\LineFormatter; |
6
|
|
|
use Monolog\Handler\FirePHPHandler; |
7
|
|
|
use Monolog\Handler\StreamHandler; |
8
|
|
|
use Monolog\Logger as Monolog; |
9
|
|
|
use Monolog\Processor\MemoryUsageProcessor; |
10
|
|
|
use Monolog\Processor\UidProcessor; |
11
|
|
|
use PSFS\base\config\Config; |
12
|
|
|
use PSFS\base\exception\ConfigException; |
13
|
|
|
use PSFS\base\exception\GeneratorException; |
14
|
|
|
use PSFS\base\types\helpers\GeneratorHelper; |
15
|
|
|
use PSFS\base\types\helpers\Inspector; |
16
|
|
|
use PSFS\base\types\helpers\SlackHelper; |
17
|
|
|
use PSFS\base\types\traits\SingletonTrait; |
18
|
|
|
|
19
|
|
|
|
20
|
|
|
if (!defined('LOG_DIR')) { |
21
|
|
|
GeneratorHelper::createDir(BASE_DIR . DIRECTORY_SEPARATOR . 'logs'); |
22
|
|
|
define('LOG_DIR', BASE_DIR . DIRECTORY_SEPARATOR . 'logs'); |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Class Logger |
27
|
|
|
* @package PSFS\base |
28
|
|
|
* Servicio de log |
29
|
|
|
*/ |
30
|
|
|
class Logger |
31
|
|
|
{ |
32
|
|
|
const DEFAULT_NAMESPACE = 'PSFS'; |
33
|
|
|
use SingletonTrait; |
34
|
|
|
/** |
35
|
|
|
* @var \Monolog\Logger |
36
|
|
|
*/ |
37
|
|
|
protected $logger; |
38
|
|
|
/** |
39
|
|
|
* @var resource |
40
|
|
|
*/ |
41
|
|
|
private $stream; |
42
|
|
|
/** |
43
|
|
|
* @var UidProcessor |
44
|
|
|
*/ |
45
|
|
|
private $uuid; |
46
|
|
|
/** |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
protected $logLevel; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Logger constructor. |
53
|
|
|
* @throws GeneratorException |
54
|
|
|
* @throws ConfigException |
55
|
|
|
*/ |
56
|
1 |
|
public function __construct() |
57
|
|
|
{ |
58
|
1 |
|
$args = func_get_args(); |
59
|
1 |
|
list($logger, $debug, $path) = $this->setup($args); |
60
|
1 |
|
$this->stream = fopen($path . DIRECTORY_SEPARATOR . date('Ymd') . '.log', 'a+'); |
|
|
|
|
61
|
1 |
|
if (is_resource($this->stream)) { |
62
|
1 |
|
$this->addPushLogger($logger, $debug); |
63
|
|
|
} else { |
64
|
|
|
throw new ConfigException(t('Error creating logger')); |
65
|
|
|
} |
66
|
1 |
|
$this->logLevel = strtoupper(Config::getParam('log.level', 'NOTICE')); |
67
|
1 |
|
} |
68
|
|
|
|
69
|
1 |
|
public function __destruct() |
70
|
|
|
{ |
71
|
1 |
|
fclose($this->stream); |
72
|
1 |
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @param string $logger |
76
|
|
|
* @param boolean $debug |
77
|
|
|
* @throws \Exception |
78
|
|
|
*/ |
79
|
1 |
|
private function addPushLogger($logger, $debug) |
80
|
|
|
{ |
81
|
1 |
|
$this->logger = new Monolog(strtoupper($logger)); |
82
|
1 |
|
$this->logger->pushHandler($this->addDefaultStreamHandler($debug)); |
83
|
1 |
|
if ($debug) { |
84
|
1 |
|
$phpFireLog = Config::getParam('logger.phpFire'); |
85
|
1 |
|
if (!empty($phpFireLog)) { |
86
|
1 |
|
$this->logger->pushHandler(new FirePHPHandler()); |
87
|
|
|
} |
88
|
1 |
|
$memoryLog = Config::getParam('logger.memory'); |
89
|
1 |
|
if (!empty($memoryLog)) { |
90
|
1 |
|
$this->logger->pushProcessor(new MemoryUsageProcessor()); |
91
|
|
|
} |
92
|
|
|
} |
93
|
1 |
|
$this->uuid = new UidProcessor(); |
94
|
1 |
|
$this->logger->pushProcessor($this->uuid); |
95
|
1 |
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* @param array $args |
99
|
|
|
* @return array |
100
|
|
|
* @throws exception\GeneratorException |
101
|
|
|
*/ |
102
|
1 |
|
private function setup(array $args = []) |
103
|
|
|
{ |
104
|
1 |
|
$args = $args ?? []; |
105
|
1 |
|
$debug = Config::getParam('debug'); |
106
|
1 |
|
$namespace = self::DEFAULT_NAMESPACE; |
107
|
1 |
|
if (0 !== count($args)) { |
108
|
1 |
|
$namespace = $args[0][0] ?? 'PSFS'; |
109
|
1 |
|
$debug = $args[0][1] ?? true; |
110
|
|
|
} |
111
|
1 |
|
$path = $this->createLoggerPath(); |
112
|
1 |
|
return array($this->cleanLoggerName($namespace), $debug, $path); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @return string |
117
|
|
|
*/ |
118
|
1 |
|
private function setLoggerName() |
119
|
|
|
{ |
120
|
1 |
|
$logger = Config::getParam('platform.name', self::DEFAULT_NAMESPACE); |
121
|
1 |
|
$logger = $this->cleanLoggerName($logger); |
122
|
1 |
|
return $logger; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @param $logger |
127
|
|
|
* |
128
|
|
|
* @return mixed |
129
|
|
|
*/ |
130
|
1 |
|
private function cleanLoggerName($logger) |
131
|
|
|
{ |
132
|
1 |
|
$logger = str_replace(' ', '', $logger); |
133
|
1 |
|
$logger = preg_replace("/\\\\/", ".", $logger); |
134
|
1 |
|
return $logger; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* @return string |
139
|
|
|
* @throws exception\GeneratorException |
140
|
|
|
*/ |
141
|
1 |
|
private function createLoggerPath() |
142
|
|
|
{ |
143
|
1 |
|
$logger = $this->setLoggerName(); |
144
|
1 |
|
$path = LOG_DIR . DIRECTORY_SEPARATOR . $logger . DIRECTORY_SEPARATOR . date('Y') . DIRECTORY_SEPARATOR . date('m'); |
145
|
1 |
|
GeneratorHelper::createDir($path); |
146
|
1 |
|
return $path; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param string $msg |
151
|
|
|
* @param int $type |
152
|
|
|
* @param array $context |
153
|
|
|
* @param boolean $force |
154
|
|
|
* @return bool |
155
|
|
|
*/ |
156
|
22 |
|
public function addLog($msg, $type = \Monolog\Logger::NOTICE, array $context = [], $force = false) |
157
|
|
|
{ |
158
|
22 |
|
return $this->checkLogLevel($type) || $force ? $this->logger->addRecord($type, $msg, $this->addMinimalContext($context)) : true; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* @param int $level |
163
|
|
|
* @return bool |
164
|
|
|
*/ |
165
|
22 |
|
private function checkLogLevel($level = \Monolog\Logger::NOTICE) |
166
|
|
|
{ |
167
|
22 |
|
switch ($this->logLevel) { |
168
|
22 |
|
case 'DEBUG': |
169
|
|
|
$logPass = Monolog::DEBUG; |
170
|
|
|
break; |
171
|
22 |
|
case 'INFO': |
172
|
|
|
$logPass = Monolog::INFO; |
173
|
|
|
break; |
174
|
|
|
default: |
175
|
22 |
|
case 'NOTICE': |
176
|
22 |
|
$logPass = Monolog::NOTICE; |
177
|
22 |
|
break; |
178
|
|
|
case 'WARNING': |
179
|
|
|
$logPass = Monolog::WARNING; |
180
|
|
|
break; |
181
|
|
|
case 'ERROR': |
182
|
|
|
$logPass = Monolog::ERROR; |
183
|
|
|
break; |
184
|
|
|
case 'CRITICAL': |
185
|
|
|
$logPass = Monolog::CRITICAL; |
186
|
|
|
break; |
187
|
|
|
} |
188
|
22 |
|
return $logPass <= $level; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @param string $msg |
193
|
|
|
* @param int $type |
194
|
|
|
* @param array $context |
195
|
|
|
* @param bool $force |
196
|
|
|
*/ |
197
|
22 |
|
public static function log($msg, $type = LOG_DEBUG, array $context = null, $force = false) |
198
|
|
|
{ |
199
|
22 |
|
if (null === $context) { |
200
|
20 |
|
$context = []; |
201
|
|
|
} |
202
|
22 |
|
if (Config::getParam('profiling.enable') && 'DEBUG' === Config::getParam('log.level', 'NOTICE')) { |
203
|
|
|
Inspector::stats($msg, Inspector::SCOPE_DEBUG); |
204
|
|
|
} |
205
|
|
|
switch ($type) { |
206
|
22 |
|
case LOG_DEBUG: |
207
|
19 |
|
self::getInstance()->addLog($msg, \Monolog\Logger::DEBUG, $context, $force); |
208
|
19 |
|
break; |
209
|
6 |
|
case LOG_WARNING: |
210
|
2 |
|
self::getInstance()->addLog($msg, \Monolog\Logger::WARNING, $context, $force); |
211
|
2 |
|
break; |
212
|
5 |
|
case LOG_CRIT: |
213
|
1 |
|
if (Config::getParam('log.slack.hook')) { |
214
|
|
|
SlackHelper::getInstance()->trace($msg, '', '', $context, $force); |
215
|
|
|
} |
216
|
1 |
|
self::getInstance()->addLog($msg, \Monolog\Logger::CRITICAL, $context, $force); |
217
|
1 |
|
break; |
218
|
5 |
|
case LOG_ERR: |
219
|
4 |
|
self::getInstance()->addLog($msg, \Monolog\Logger::ERROR, $context, $force); |
220
|
4 |
|
break; |
221
|
2 |
|
case LOG_INFO: |
222
|
2 |
|
self::getInstance()->addLog($msg, \Monolog\Logger::INFO, $context, $force); |
223
|
2 |
|
break; |
224
|
|
|
default: |
225
|
1 |
|
self::getInstance()->addLog($msg, \Monolog\Logger::NOTICE, $context, $force); |
226
|
1 |
|
break; |
227
|
|
|
} |
228
|
22 |
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param bool $debug |
232
|
|
|
* @return StreamHandler |
233
|
|
|
* @throws \Exception |
234
|
|
|
*/ |
235
|
1 |
|
private function addDefaultStreamHandler($debug = false) |
236
|
|
|
{ |
237
|
|
|
// the default date format is "Y-m-d H:i:s" |
238
|
1 |
|
$dateFormat = 'Y-m-d H:i:s.u'; |
239
|
|
|
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n" |
240
|
1 |
|
$output = "[%datetime%] [%channel%:%level_name%]\t%message%\t%context%\t%extra%\n"; |
241
|
|
|
// finally, create a formatter |
242
|
1 |
|
$formatter = new LineFormatter($output, $dateFormat); |
243
|
1 |
|
$stream = new StreamHandler($this->stream, $debug ? Monolog::DEBUG : Monolog::WARNING); |
244
|
1 |
|
$stream->setFormatter($formatter); |
245
|
1 |
|
return $stream; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @param array $context |
250
|
|
|
* @return array |
251
|
|
|
*/ |
252
|
5 |
|
private function addMinimalContext(array $context = []) |
253
|
|
|
{ |
254
|
5 |
|
$context['uri'] = null !== $_SERVER && array_key_exists('REQUEST_URI', $_SERVER) ? $_SERVER['REQUEST_URI'] : 'Unknow'; |
255
|
5 |
|
$context['method'] = null !== $_SERVER && array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'Unknow'; |
256
|
5 |
|
if (null !== $_SERVER && array_key_exists('HTTP_X_PSFS_UID', $_SERVER)) { |
257
|
|
|
$context['uid'] = $_SERVER['HTTP_X_PSFS_UID']; |
258
|
|
|
} |
259
|
5 |
|
return $context; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* @return string |
264
|
|
|
*/ |
265
|
1 |
|
public function getLogUid() |
266
|
|
|
{ |
267
|
1 |
|
return $this->uuid->getUid(); |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* @return string |
272
|
|
|
*/ |
273
|
1 |
|
public static function getUid() |
274
|
|
|
{ |
275
|
1 |
|
return self::getInstance()->getLogUid(); |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.