diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | app/class/Logger.php | 94 | ||||
-rw-r--r-- | index.php | 4 | ||||
-rw-r--r-- | tests/LoggerTest.php | 228 |
4 files changed, 327 insertions, 0 deletions
@@ -1,5 +1,6 @@ .vscode/* .env +*.log *.bundle.js *.bundle.js.map assets/render/* diff --git a/app/class/Logger.php b/app/class/Logger.php new file mode 100644 index 0000000..939dbf8 --- /dev/null +++ b/app/class/Logger.php @@ -0,0 +1,94 @@ +<?php + +namespace Wcms; + +use Throwable; + +/** + * Class used to log messages. + * It must be init once at the very beginning of the application. + */ +class Logger +{ + private static $file = null; + private static $verbosity = 4; + + /** + * Initialize the logger by openning the file and setting the log level. + * + * @param string $path the logfile's path + * @param int $verbosity 0: no log, 1: errors only, 2: add warn, 3: add info, 4: add debug. + */ + public static function init(string $path, int $verbosity = 4) + { + self::$file = fopen($path, "a") or die("Unable to open log file!"); + self::$verbosity = $verbosity; + } + + protected static function write(string $level, string $msg, array $args = []) + { + $caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]; + $pwd = getcwd() . DIRECTORY_SEPARATOR; + $args = array_merge([ + "[ $level ]", + str_replace($pwd, '', $caller['file']), + $caller['line'] + ], $args); + vfprintf(self::$file, date('c') . " %-9s %s(%d) $msg\n", $args); + } + + /** + * Log an error message using printf format. + */ + public static function error(string $msg, ...$args) + { + if (self::$verbosity > 0) { + self::write('ERROR', $msg, $args); + } + } + + /** + * Log a xarning message using printf format. + */ + public static function warning(string $msg, ...$args) + { + if (self::$verbosity > 1) { + self::write('WARN', $msg, $args); + } + } + + /** + * Log an info message using printf format. + */ + public static function info(string $msg, ...$args) + { + if (self::$verbosity > 2) { + self::write('INFO', $msg, $args); + } + } + + /** + * Log a debug message using printf format. + */ + public static function debug(string $msg, ...$args) + { + if (self::$verbosity > 3) { + self::write('DEBUG', $msg, $args); + } + } + + /** + * Log an exception. + */ + public static function exception(Throwable $e, bool $withtrace = false) + { + if (self::$verbosity > 0) { + $msg = $e->getMessage(); + if ($withtrace) { + // TODO: Maybe print a more beautiful stack trace. + $msg .= PHP_EOL . $e->getTraceAsString(); + } + self::write('ERROR', $msg); + } + } +} @@ -1,10 +1,13 @@ <?php +use Wcms\Logger; + session_start(); require('./vendor/autoload.php'); +Logger::init('w_error.log', 2); $app = new Wcms\Application(); $app->wakeup(); @@ -30,5 +33,6 @@ try { if (isreportingerrors()) { Sentry\captureException($e); } + Logger::exception($e, true); echo '<h1>⚠ Woops ! There is a little problem : </h1>', $e->getMessage(), "\n"; } diff --git a/tests/LoggerTest.php b/tests/LoggerTest.php new file mode 100644 index 0000000..18dcb02 --- /dev/null +++ b/tests/LoggerTest.php @@ -0,0 +1,228 @@ +<?php + +namespace Wcms\Tests; + +use Exception; +use PHPUnit\Framework\TestCase; +use Throwable; +use Wcms\Logger; + +class LoggerTest extends TestCase +{ + protected $logfile = 'build/w_error.log'; + + protected function setUp(): void + { + parent::setUp(); + if (file_exists($this->logfile)) { + unlink($this->logfile); + } + } + + /** + * @test + */ + public function initTest(): void + { + Logger::init($this->logfile); + $this->assertFileExists($this->logfile, 'Log file has not been created.'); + $this->assertEmpty(file_get_contents($this->logfile)); + } + + /** + * @test + * @dataProvider errorNotLoggedProvider + */ + public function errorNotLoggedTest(int $verbosity): void + { + Logger::init($this->logfile, $verbosity); + Logger::error('Error.'); + $this->assertEmpty(file_get_contents($this->logfile)); + } + + public function errorNotLoggedProvider(): array + { + return [[0]]; + } + + /** + * @test + * @dataProvider errorLoggedProvider + */ + public function errorLoggedTest(int $verbosity, string $msg, array $args, string $expected): void + { + Logger::init($this->logfile, $verbosity); + Logger::error($msg, ...$args); + $expected = " [ ERROR ] tests/LoggerTest.php(55) $expected\n"; + $this->assertEquals($expected, substr(file_get_contents($this->logfile), 25)); + } + + public function errorLoggedProvider(): array + { + return [ + [1, 'Error %s.', ['test'], 'Error test.'], + [2, 'Error %s %d.', ['test', 2], 'Error test 2.'], + [3, 'Error.', [], 'Error.'], + [4, 'Error.', [], 'Error.'], + ]; + } + + /** + * @test + * @dataProvider warningNotLoggedProvider + */ + public function warningNotLoggedTest(int $verbosity): void + { + Logger::init($this->logfile, $verbosity); + Logger::warning('Error.'); + $this->assertEmpty(file_get_contents($this->logfile)); + } + + public function warningNotLoggedProvider(): array + { + return [[0], [1]]; + } + + /** + * @test + * @dataProvider warningLoggedProvider + */ + public function warningLoggedTest(int $verbosity, string $msg, array $args, string $expected): void + { + Logger::init($this->logfile, $verbosity); + Logger::warning($msg, ...$args); + $expected = " [ WARN ] tests/LoggerTest.php(93) $expected\n"; + $this->assertEquals($expected, substr(file_get_contents($this->logfile), 25)); + } + + public function warningLoggedProvider(): array + { + return [ + [2, 'Error %s.', ['test'], 'Error test.'], + [3, 'Error.', [], 'Error.'], + [4, 'Error.', [], 'Error.'], + ]; + } + + /** + * @test + * @dataProvider infoNotLoggedProvider + */ + public function infoNotLoggedTest(int $verbosity): void + { + Logger::init($this->logfile, $verbosity); + Logger::info('Error.'); + $this->assertEmpty(file_get_contents($this->logfile)); + } + + public function infoNotLoggedProvider(): array + { + return [[0], [1], [2]]; + } + + /** + * @test + * @dataProvider infoLoggedProvider + */ + public function infoLoggedTest(int $verbosity, string $msg, array $args, string $expected): void + { + Logger::init($this->logfile, $verbosity); + Logger::info($msg, ...$args); + $expected = " [ INFO ] tests/LoggerTest.php(130) $expected\n"; + $this->assertEquals($expected, substr(file_get_contents($this->logfile), 25)); + } + + public function infoLoggedProvider(): array + { + return [ + [3, 'Error %s.', ['test'], 'Error test.'], + [4, 'Error.', [], 'Error.'], + ]; + } + + /** + * @test + * @dataProvider debugNotLoggedProvider + */ + public function debugNotLoggedTest(int $verbosity): void + { + Logger::init($this->logfile, $verbosity); + Logger::debug('Error.'); + $this->assertEmpty(file_get_contents($this->logfile)); + } + + public function debugNotLoggedProvider(): array + { + return [[0], [1], [2], [3]]; + } + + /** + * @test + * @dataProvider debugLoggedProvider + */ + public function debugLoggedTest(int $verbosity, string $msg, array $args, string $expected): void + { + Logger::init($this->logfile, $verbosity); + Logger::debug($msg, ...$args); + $expected = " [ DEBUG ] tests/LoggerTest.php(166) $expected\n"; + $this->assertEquals($expected, substr(file_get_contents($this->logfile), 25)); + } + + public function debugLoggedProvider(): array + { + return [ + [4, 'Error %s.', ['test'], 'Error test.'], + ]; + } + + /** + * @test + * @dataProvider exceptionNotLoggedProvider + */ + public function exceptionNotLoggedTest(int $verbosity): void + { + Logger::init($this->logfile, $verbosity); + Logger::exception(new Exception('Error')); + $this->assertEmpty(file_get_contents($this->logfile)); + } + + public function exceptionNotLoggedProvider(): array + { + return [[0]]; + } + + /** + * @test + * @dataProvider exceptionLoggedProvider + */ + public function exceptionLoggedTest(int $verbosity, Throwable $e, string $expected) + { + Logger::init($this->logfile, $verbosity); + Logger::exception($e); + $expected = " [ ERROR ] tests/LoggerTest.php(201) $expected\n"; + $this->assertEquals($expected, substr(file_get_contents($this->logfile), 25)); + } + + public function exceptionLoggedProvider(): array + { + return [ + [1, new Exception('Test 1'), 'Test 1'], + [2, new Exception('Test 2'), 'Test 2'], + [3, new Exception('Test 3'), 'Test 3'], + [4, new Exception('Test 4'), 'Test 4'], + ]; + } + + /** + * @test + */ + public function exceptionBacktraceTest(): void + { + Logger::init($this->logfile, 1); + Logger::exception(new Exception('Error'), true); + $content = file_get_contents($this->logfile); + $expected = " [ ERROR ] tests/LoggerTest.php(222) Error\n"; + $this->assertEquals($expected, substr($content, 25, 43)); + $this->assertRegExp('/(#\d+ [\w\/\.]*\(\d+\): .*\)\n)+#\d+ \{main\}\n/U', substr($content, 68)); + } +} |