diff --git a/.circleci/config.yml b/.circleci/config.yml index 834dc97..6f1c2c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,10 +34,10 @@ jobs: <<: *defaults docker: - image: php:7.3-alpine - build-phpRC: + build-php74: <<: *defaults docker: - - image: php:rc-alpine + - image: php:7.4-alpine workflows: version: 2 @@ -46,4 +46,4 @@ workflows: - build-php71 - build-php72 - build-php73 - - build-phpRC + - build-php74 diff --git a/composer.json b/composer.json index 5fe941f..a2a340c 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ } ], "require": { + "ext-json": "*", "php": ">=7.1", "psr/log": "1.1.*" }, diff --git a/src/BasicGoogleCloudLogger.php b/src/BasicGoogleCloudLogger.php new file mode 100644 index 0000000..46da42b --- /dev/null +++ b/src/BasicGoogleCloudLogger.php @@ -0,0 +1,39 @@ +_handle = fopen('php://stderr', 'wb'); + } + + public function setHandle($handle) + { + $this->_handle = $handle; + } + + protected function _writeLog($message) + { + fwrite($this->_handle, $message); + } + + protected function _formatLog($level, $message, array $context = null) + { + return json_encode(array_filter([ + 'timestamp' => (new \DateTime())->format(DATE_RFC3339_EXTENDED), + 'severity' => $level, + 'textPayload' => $message, + 'jsonPayload' => $context, + ])) . PHP_EOL; + } +} diff --git a/src/ErrorLogLogger.php b/src/ErrorLogLogger.php index 9dc2dc4..6455c97 100644 --- a/src/ErrorLogLogger.php +++ b/src/ErrorLogLogger.php @@ -33,6 +33,11 @@ public function __construct($maxLevel = LogLevel::DEBUG) $this->setMaxLogLevel($maxLevel); } + protected function _writeLog($message) + { + error_log($message); + } + /** * @param string $level One of the Psr\Log\LogLevel constants */ @@ -41,12 +46,21 @@ public function setMaxLogLevel($level) $this->_maxLevel = $this->_levelToNum($level); } - public function log($level, $message, array $context = []) + public function log($level, $message, array $context = null) { if($this->_levelToNum($level) <= $this->_maxLevel) { - error_log($message); + $this->_writeLog($this->_formatLog($level, $message, $context)); + } + } + + protected function _formatLog($level, $message, array $context = null) + { + if(!empty($context)) + { + $message .= ' ' . json_encode($context); } + return "[$level] $message"; } private function _levelToNum($level) diff --git a/src/Log.php b/src/Log.php index ff196e1..a59e871 100644 --- a/src/Log.php +++ b/src/Log.php @@ -79,11 +79,26 @@ public static function debug($message, array $context = []) public static function exception(Throwable $e) { - static::error("EXCEPTION (" . $e->getCode() . "): " . $e->getMessage()); + static::critical( + $e->getMessage(), + [ + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + ] + ); } public static function exceptionWithTrace(Throwable $e) { - static::error("EXCEPTION (" . $e->getCode() . "): " . $e->getMessage() . "\n" . $e->getTraceAsString()); + static::critical( + $e->getMessage(), + [ + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack_trace' => $e->getTraceAsString(), + ] + ); } } diff --git a/tests/AbstractLoggerTestCase.php b/tests/AbstractLoggerTestCase.php deleted file mode 100644 index 62e8573..0000000 --- a/tests/AbstractLoggerTestCase.php +++ /dev/null @@ -1,31 +0,0 @@ -_tempFile = tempnam(sys_get_temp_dir(), 'packaged-log-'); - ini_set('error_log', $this->_tempFile); - } - - public function tearDown() - { - unlink($this->_tempFile); - } - - protected function _getLogContents() - { - return file_get_contents($this->_tempFile); - } - - public function assertLastLog($test) - { - $this->assertStringEndsWith($test . PHP_EOL, $this->_getLogContents()); - } -} diff --git a/tests/BasicGoogleCloudLoggerTest.php b/tests/BasicGoogleCloudLoggerTest.php new file mode 100644 index 0000000..b54036f --- /dev/null +++ b/tests/BasicGoogleCloudLoggerTest.php @@ -0,0 +1,118 @@ +_tempFile = tempnam(sys_get_temp_dir(), 'packaged-log-'); + $this->_handler = fopen($this->_tempFile, 'wb'); + } + + public function tearDown() + { + fclose($this->_handler); + unlink($this->_tempFile); + } + + protected function _getLogContents() + { + return file_get_contents($this->_tempFile); + } + + public function assertLastLog($test) + { + self::assertStringEndsWith($test . PHP_EOL, $this->_getLogContents()); + } + + private function _getTestLogger($maxLevel = LogLevel::DEBUG) + { + $l = new BasicGoogleCloudLogger($maxLevel); + $l->setHandle($this->_handler); + return $l; + } + + public function testLogger() + { + Log::bind($this->_getTestLogger()); + + Log::debug('debug: test'); + self::assertLastLog('","severity":"debug","textPayload":"debug: test"}'); + + Log::info('info: test'); + self::assertLastLog('","severity":"info","textPayload":"info: test"}'); + + Log::notice('notice: test'); + self::assertLastLog('","severity":"notice","textPayload":"notice: test"}'); + + Log::warning('warning: test'); + self::assertLastLog('","severity":"warning","textPayload":"warning: test"}'); + + Log::error('error: test'); + self::assertLastLog('","severity":"error","textPayload":"error: test"}'); + + Log::critical('critical: test'); + self::assertLastLog('","severity":"critical","textPayload":"critical: test"}'); + + Log::alert('alert: test'); + self::assertLastLog('","severity":"alert","textPayload":"alert: test"}'); + + Log::emergency('emergency: test'); + self::assertLastLog('","severity":"emergency","textPayload":"emergency: test"}'); + } + + public function testLevelLog() + { + Log::bind($this->_getTestLogger(LogLevel::INFO)); + + Log::info('info: test'); + self::assertLastLog('","severity":"info","textPayload":"info: test"}'); + + Log::debug('debug: test'); + self::assertLastLog('","severity":"info","textPayload":"info: test"}'); + } + + public function testExceptionLog() + { + Log::bind($this->_getTestLogger()); + + $e = new Exception('exception message', 123); + Log::exception($e); + self::assertContains(',"textPayload":"exception message"', $this->_getLogContents()); + self::assertContains(',"severity":"critical"', $this->_getLogContents()); + self::assertNotContains('"stace_trace":', $this->_getLogContents()); + } + + public function testExceptionTraceLog() + { + Log::bind($this->_getTestLogger()); + + $e = new Exception('exception message', 123); + Log::exceptionWithTrace($e); + self::assertContains('"textPayload":"exception message"', $this->_getLogContents()); + self::assertContains('"severity":"critical"', $this->_getLogContents()); + self::assertContains('"code":123', $this->_getLogContents()); + self::assertContains('"line":100', $this->_getLogContents()); + self::assertContains('BasicGoogleCloudLoggerTest.php', $this->_getLogContents()); + self::assertContains('"stack_trace"', $this->_getLogContents()); + } + + public function testContextLog() + { + Log::bind($this->_getTestLogger()); + Log::debug('debug: test', ['test1' => 'value1', 'test2' => 'value2']); + self::assertLastLog( + '","severity":"debug","textPayload":"debug: test","jsonPayload":{"test1":"value1","test2":"value2"}}' + ); + } +} diff --git a/tests/ErrorLogLoggerTest.php b/tests/ErrorLogLoggerTest.php new file mode 100644 index 0000000..b4ed180 --- /dev/null +++ b/tests/ErrorLogLoggerTest.php @@ -0,0 +1,117 @@ +_tempFile = tempnam(sys_get_temp_dir(), 'packaged-log-'); + ini_set('error_log', $this->_tempFile); + } + + public function tearDown() + { + unlink($this->_tempFile); + } + + protected function _getLogContents() + { + return file_get_contents($this->_tempFile); + } + + public function assertLastLog($test) + { + self::assertStringEndsWith($test . PHP_EOL, $this->_getLogContents()); + } + + public function testDefaultLogger() + { + Log::unbind(); + + Log::info('info: test'); + self::assertLastLog('info: test'); + + Log::debug('debug: test'); + self::assertLastLog('debug: test'); + } + + public function testErrorLogLogger() + { + Log::bind(new ErrorLogLogger()); + + Log::debug('debug: test'); + self::assertLastLog('debug: test'); + + Log::info('info: test'); + self::assertLastLog('info: test'); + + Log::notice('notice: test'); + self::assertLastLog('notice: test'); + + Log::warning('warning: test'); + self::assertLastLog('warning: test'); + + Log::error('error: test'); + self::assertLastLog('error: test'); + + Log::critical('critical: test'); + self::assertLastLog('critical: test'); + + Log::alert('alert: test'); + self::assertLastLog('alert: test'); + + Log::emergency('emergency: test'); + self::assertLastLog('emergency: test'); + } + + public function testLevelLog() + { + Log::bind(new ErrorLogLogger(LogLevel::INFO)); + + Log::info('info: test'); + self::assertLastLog('info: test'); + + Log::debug('debug: test'); + self::assertLastLog('info: test'); + } + + public function testExceptionLog() + { + Log::bind(new ErrorLogLogger()); + + $e = new Exception('exception message', 123); + Log::exception($e); + self::assertContains('[critical] exception message ', $this->_getLogContents()); + self::assertContains('"code":123', $this->_getLogContents()); + self::assertContains('"line":90', $this->_getLogContents()); + self::assertContains('ErrorLogLoggerTest.php', $this->_getLogContents()); + } + + public function testExceptionTraceLog() + { + Log::bind(new ErrorLogLogger()); + + $e = new Exception('exception message', 123); + Log::exceptionWithTrace($e); + self::assertContains('[critical] exception message ', $this->_getLogContents()); + self::assertContains('"code":123', $this->_getLogContents()); + self::assertContains('"line":102', $this->_getLogContents()); + self::assertContains('ErrorLogLoggerTest.php', $this->_getLogContents()); + self::assertContains('"stack_trace"', $this->_getLogContents()); + } + + public function testContextLog() + { + Log::bind(new ErrorLogLogger()); + Log::debug('debug: test', ['test1' => 'value1', 'test2' => 'value2']); + self::assertLastLog('debug: test {"test1":"value1","test2":"value2"}'); + } +} diff --git a/tests/LoggerTest.php b/tests/LoggerTest.php deleted file mode 100644 index 56da8cb..0000000 --- a/tests/LoggerTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertLastLog('info: test'); - - Log::debug('debug: test'); - $this->assertLastLog('debug: test'); - } - - public function testErrorLogLogger() - { - Log::bind(new ErrorLogLogger()); - - Log::debug('debug: test'); - $this->assertLastLog('debug: test'); - - Log::info('info: test'); - $this->assertLastLog('info: test'); - - Log::notice('notice: test'); - $this->assertLastLog('notice: test'); - - Log::warning('warning: test'); - $this->assertLastLog('warning: test'); - - Log::error('error: test'); - $this->assertLastLog('error: test'); - - Log::critical('critical: test'); - $this->assertLastLog('critical: test'); - - Log::alert('alert: test'); - $this->assertLastLog('alert: test'); - - Log::emergency('emergency: test'); - $this->assertLastLog('emergency: test'); - } - - public function testLevelLog() - { - Log::bind(new ErrorLogLogger(LogLevel::INFO)); - - Log::info('info: test'); - $this->assertLastLog('info: test'); - - Log::debug('debug: test'); - $this->assertLastLog('info: test'); - } - - public function testExceptionLog() - { - Log::bind(new ErrorLogLogger()); - - $e = new Exception('exception message', 123); - Log::exception($e); - $this->assertLastLog('EXCEPTION (123): exception message'); - } - - public function testExceptionTraceLog() - { - Log::bind(new ErrorLogLogger()); - - $e = new Exception('exception message', 123); - Log::exceptionWithTrace($e); - $this->assertContains('EXCEPTION (123): exception message', $this->_getLogContents()); - $this->assertContains('Packaged\Log\Tests\LoggerTest->testExceptionTraceLog()', $this->_getLogContents()); - $this->assertContains('#0', $this->_getLogContents()); - $this->assertContains('{main}', $this->_getLogContents()); - } -}