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