|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace alkemann\h2l; |
|
4
|
|
|
|
|
5
|
|
|
use alkemann\h2l\exceptions\ConfigMissing; |
|
6
|
|
|
|
|
7
|
|
|
/** |
|
8
|
|
|
* Class Log |
|
9
|
|
|
* |
|
10
|
|
|
* Follows http://www.php-fig.org/psr/psr-3/ |
|
11
|
|
|
* See https://github.com/php-fig/log |
|
12
|
|
|
* |
|
13
|
|
|
* |
|
14
|
|
|
* 0 Emergency: system is unusable |
|
15
|
|
|
* 1 Alert: action must be taken immediately |
|
16
|
|
|
* 2 Critical: critical conditions |
|
17
|
|
|
* 3 Error: error conditions |
|
18
|
|
|
* 4 Warning: warning conditions |
|
19
|
|
|
* 5 Notice: normal but significant condition |
|
20
|
|
|
* 6 Informational: informational messages |
|
21
|
|
|
* 7 Debug: debug-level messages |
|
22
|
|
|
* |
|
23
|
|
|
* @package alkemann\h2l |
|
24
|
|
|
* @method static void debug($message, array $context = []) Log debug level |
|
25
|
|
|
* @method static void info($message, array $context = []) Log info level |
|
26
|
|
|
* @method static void notice($message, array $context = []) Log notice level |
|
27
|
|
|
* @method static void warning($message, array $context = []) Log warning level |
|
28
|
|
|
* @method static void error($message, array $context = []) Log error level |
|
29
|
|
|
* @method static void critical($message, array $context = []) Log critical level |
|
30
|
|
|
* @method static void alert($message, array $context = []) Log alert level |
|
31
|
|
|
* @method static void emergency($message, array $context = []) Log emergency level |
|
32
|
|
|
*/ |
|
33
|
|
|
class Log |
|
34
|
|
|
{ |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* Set handlers here, either a callable or an object that implements Psr\Log\LoggerInterface |
|
38
|
|
|
* @var array |
|
39
|
|
|
*/ |
|
40
|
|
|
protected static $handlers = []; |
|
41
|
|
|
|
|
42
|
|
|
/** |
|
43
|
|
|
* Add handler, it should implement Psr\Log\LoggerInterface |
|
44
|
|
|
* |
|
45
|
|
|
* @param string $name unique name for this handler |
|
46
|
|
|
* @param object|callable $handler an object that implement Psr\Log\LoggerInterface or a callable |
|
47
|
|
|
*/ |
|
48
|
|
|
public static function handler(string $name, $handler): void |
|
49
|
|
|
{ |
|
50
|
|
|
if (is_callable($handler) === false) { |
|
51
|
|
|
if (is_object($handler)) { |
|
52
|
|
|
// TODO check if object implements interface? |
|
53
|
|
|
if (!method_exists($handler, 'log')) { |
|
54
|
|
|
throw new \InvalidArgumentException( |
|
55
|
|
|
"$name is not a valid handler, it must implement the Psr\Log\LoggerInterface" |
|
56
|
|
|
); |
|
57
|
|
|
} |
|
58
|
|
|
} else { |
|
59
|
|
|
throw new \InvalidArgumentException("$name is not a valid handler"); |
|
60
|
|
|
} |
|
61
|
|
|
} |
|
62
|
|
|
static::$handlers[$name] = $handler; |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Support the log levels of debug, info, notice, warning, error, critical, alert, emergency |
|
67
|
|
|
* |
|
68
|
|
|
* @param string $method |
|
69
|
|
|
* @param array $args |
|
70
|
|
|
*/ |
|
71
|
|
|
public static function __callStatic(string $method, array $args = []): void |
|
72
|
|
|
{ |
|
73
|
|
|
/* |
|
|
|
|
|
|
74
|
|
|
if (!in_array($method, ['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency'])) { |
|
75
|
|
|
throw new \Psr\Log\InvalidArgumentException("Undefined level") |
|
76
|
|
|
} |
|
77
|
|
|
*/ |
|
78
|
|
|
array_unshift($args, $method); |
|
79
|
|
|
call_user_func_array(['alkemann\h2l\Log', 'log'], $args); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* Send $message with level $level to all handlers |
|
84
|
|
|
* |
|
85
|
|
|
* @param $level |
|
86
|
|
|
* @param $message |
|
87
|
|
|
* @param array $context |
|
88
|
|
|
*/ |
|
89
|
|
|
public static function log($level, $message, array $context = []): void |
|
|
|
|
|
|
90
|
|
|
{ |
|
91
|
|
|
foreach (static::$handlers as $handlerName => $handler) { |
|
92
|
|
|
if (is_callable($handler)) { // avoid this? |
|
93
|
|
|
$handler($level, $message, $context); |
|
94
|
|
|
} else { |
|
95
|
|
|
$handler->$level($message, $context); |
|
96
|
|
|
} |
|
97
|
|
|
} |
|
98
|
|
|
} |
|
99
|
|
|
|
|
100
|
|
|
/** |
|
101
|
|
|
* A standard output handler, INFO, DEBUG, NOTICE to `php://stdout` and rest to `php://stderr` |
|
102
|
|
|
* |
|
103
|
|
|
* You can enable it like this: `Log::handler('standard', [Log::class, 'std']);` |
|
104
|
|
|
* |
|
105
|
|
|
* @codeCoverageIgnore |
|
106
|
|
|
* @param string $level |
|
107
|
|
|
* @param string $message |
|
108
|
|
|
* @param array $context |
|
109
|
|
|
*/ |
|
110
|
|
|
private static function std(string $level, string $message, array $context = []): void |
|
111
|
|
|
{ |
|
112
|
|
|
$level = strtoupper($level); |
|
113
|
|
|
$string = "{$level}: {$message}\n"; |
|
114
|
|
|
$channel = in_array($level, ['INFO', 'DEBUG', 'NOTICE']) ? 'php://stdout' : 'php://stderr'; |
|
115
|
|
|
file_put_contents($channel, $string); |
|
116
|
|
|
} |
|
117
|
|
|
|
|
118
|
|
|
/** |
|
119
|
|
|
* A default naive file handler that can be used initially, but should be replaced for prod |
|
120
|
|
|
* |
|
121
|
|
|
* @codeCoverageIgnore |
|
122
|
|
|
* @param string $level |
|
123
|
|
|
* @param string $message |
|
124
|
|
|
* @param array $context |
|
125
|
|
|
* @throws ConfigMissing |
|
126
|
|
|
*/ |
|
127
|
|
|
private static function file(string $level, string $message, array $context = []): void |
|
128
|
|
|
{ |
|
129
|
|
|
$path = Environment::get('logs_path'); |
|
130
|
|
|
if (is_null($path)) { |
|
131
|
|
|
throw new ConfigMissing("File handler requires a `logs_path` in Environment"); |
|
132
|
|
|
} |
|
133
|
|
|
$file = $path . 'app.log'; |
|
134
|
|
|
$fileHandler = fopen($file, 'a'); |
|
135
|
|
|
$string = date('Y-m-d H:i:s') . " " . strtoupper($level) . " " . $message . PHP_EOL; |
|
136
|
|
|
fwrite($fileHandler, $string); |
|
137
|
|
|
fclose($fileHandler); |
|
138
|
|
|
} |
|
139
|
|
|
} |
|
140
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.