├── .travis.yml ├── src └── MonologMiddleware │ ├── Exception │ ├── MonologConfigException.php │ └── MonologHandlerNotImplementedException.php │ ├── Validator │ ├── BrowserConsoleHandlerConfigValidator.php │ ├── LogglyHanlderConfigValidator.php │ ├── StreamHandlerConfigValidator.php │ ├── RotateFileHandlerConfigValidator.php │ ├── AbstractHandlerConfigValidator.php │ ├── SlackHandlerConfigValidator.php │ ├── PushoverHandlerConfigValidator.php │ ├── RedisHandlerConfigValidator.php │ └── NativeMailHandlerConfigValidator.php │ ├── Test │ ├── Validator │ │ ├── BrowserConsoleHandlerConfigValidatorTest.php │ │ ├── AbstractHandlerConfigValidatorTest.php │ │ ├── LogglyHanlderConfigValidatorTest.php │ │ ├── StreamHandlerConfigValidatorTest.php │ │ ├── PushoverHandlerConfigValidatorTest.php │ │ ├── SlackHandlerConfigValidatorTest.php │ │ ├── NativeMailHandlerConfigValidatorTest.php │ │ └── RedisHandlerConfigValidatorTest.php │ ├── Factory │ │ └── MonologMiddlewareFactoryTest.php │ └── Extension │ │ └── MonologConfigurationExtensionTest.php │ ├── Factory │ └── MonologMiddlewareFactory.php │ ├── MonologMiddleware.php │ ├── Loggable │ └── LoggableProvider.php │ └── Extension │ └── MonologConfigurationExtension.php ├── phpunit.xml.dist ├── .gitignore ├── composer.json ├── LICENSE └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.1 4 | - 7.2 5 | 6 | before_install: 7 | - echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini 8 | before_script: 9 | - composer install 10 | 11 | script: 12 | - vendor/bin/phpunit 13 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Exception/MonologConfigException.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | src/MonologMiddleware/Test/ 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Exception/MonologHandlerNotImplementedException.php: -------------------------------------------------------------------------------- 1 | 'browser_console', 16 | 'level' => 'INFO', 17 | ]; 18 | 19 | $browserConsoleValidator = new BrowserConsoleHandlerConfigValidator($configArray); 20 | $this->assertTrue($browserConsoleValidator->validate()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Factory/MonologMiddlewareFactoryTest.php: -------------------------------------------------------------------------------- 1 | 16 | [ 17 | 'name' => 'MonologTest', 18 | 'handlers' => [ 19 | 'main' => [ 20 | 'type' => 'stream', 21 | 'path' => "test/test.log", 22 | 'level' => Logger::DEBUG, 23 | ], 24 | ], 25 | ], 26 | ]; 27 | 28 | } 29 | 30 | public function testInvoke() 31 | { 32 | 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/LogglyHanlderConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasToken()) { 21 | return true; 22 | } 23 | } 24 | 25 | /** 26 | * @return bool 27 | * @throws MonologConfigException 28 | */ 29 | public function hasToken(): bool 30 | { 31 | if (isset($this->handlerConfigArray['token'])) { 32 | return true; 33 | } 34 | 35 | throw new MonologConfigException("Missing token in Loggly config"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/StreamHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasPath()) { 23 | return true; 24 | } 25 | } 26 | 27 | /** 28 | * @return bool 29 | * @throws MonologConfigException 30 | */ 31 | public function hasPath(): bool 32 | { 33 | if (isset($this->handlerConfigArray['path'])) { 34 | return true; 35 | } 36 | 37 | throw new MonologConfigException("Missing Path in Stream handler configuration"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/RotateFileHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasFilename()) { 24 | return true; 25 | } 26 | } 27 | 28 | /** 29 | * @return bool 30 | * @throws MonologConfigException 31 | */ 32 | public function hasFilename(): bool 33 | { 34 | if (isset($this->handlerConfigArray['filename'])) { 35 | return true; 36 | } 37 | 38 | throw new MonologConfigException("Missing filename in Rotate File handler configuration"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oras/monolog-middleware", 3 | "description": "PSR-7 Monolog Middleware", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Oras Al-Kubaisi", 8 | "email": "code@oras.me" 9 | } 10 | ], 11 | "require": { 12 | "php": "^7.1 || ^7.2", 13 | "monolog/monolog": "^1.19", 14 | "psr/http-message": "~1.0.0", 15 | "symfony/http-foundation": "^3.0", 16 | "guzzlehttp/psr7": "^1.3 || ^1.4", 17 | "psr/http-server-middleware": "^1.0", 18 | "psr/container": "^1.0", 19 | "zendframework/zend-stratigility": "^3.0" 20 | }, 21 | "require-dev": { 22 | "squizlabs/php_codesniffer": "^2.3", 23 | "phpunit/phpunit": "^7.0", 24 | "roave/security-advisories": "dev-master" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "MonologMiddleware\\": "src/MonologMiddleware/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "MonologMiddleware\\": "src/MonologMiddleware/" 34 | } 35 | }, 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.1-dev" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Oras Al-Kubaisi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/AbstractHandlerConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'stream', 15 | 'level' => 'INFO', 16 | ]; 17 | 18 | $abstractValidator = new AbstractHandlerConfigValidator($configArray); 19 | $this->assertTrue($abstractValidator->validate()); 20 | } 21 | 22 | public function testHasLevel() 23 | { 24 | $configArray = [ 25 | 'type' => 'stream', 26 | 'level' => 'INFO', 27 | ]; 28 | 29 | $abstractValidator = new AbstractHandlerConfigValidator($configArray); 30 | $this->assertTrue($abstractValidator->hasLevel()); 31 | } 32 | 33 | public function testNotHasLevel() 34 | { 35 | $configArray = [ 36 | 'type' => 'stream', 37 | ]; 38 | 39 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 40 | $abstractValidator = new AbstractHandlerConfigValidator($configArray); 41 | $abstractValidator->hasLevel(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/AbstractHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | handlerConfigArray = $handlerConfigArray; 27 | } 28 | 29 | /** 30 | * @return bool 31 | * @throws MonologConfigException 32 | */ 33 | public function validate(): bool 34 | { 35 | if ($this->hasLevel()) { 36 | return true; 37 | } 38 | 39 | throw new MonologConfigException("Missing data in handler configuration"); 40 | } 41 | 42 | /** 43 | * @return bool 44 | * @throws MonologConfigException 45 | */ 46 | public function hasLevel(): bool 47 | { 48 | if (isset($this->handlerConfigArray['level'])) { 49 | return true; 50 | } 51 | 52 | throw new MonologConfigException("Monolog level is missing from config"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/SlackHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasToken() && $this->hasChannel()) { 21 | return true; 22 | } 23 | } 24 | 25 | 26 | /** 27 | * @return bool 28 | * @throws MonologConfigException 29 | */ 30 | public function hasToken(): bool 31 | { 32 | if (isset($this->handlerConfigArray['token'])) { 33 | return true; 34 | } 35 | 36 | throw new MonologConfigException("Missing token in Slack handler configuration"); 37 | } 38 | 39 | /** 40 | * @return bool 41 | * @throws MonologConfigException 42 | */ 43 | public function hasChannel(): bool 44 | { 45 | if (isset($this->handlerConfigArray['channel'])) { 46 | return true; 47 | } 48 | 49 | throw new MonologConfigException("Missing channel in Slack handler configuration"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/LogglyHanlderConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'loggly', 16 | 'level' => 'DEBUG', 17 | 'token' => '123-123-123-123', 18 | ]; 19 | 20 | $logglyValidator = new LogglyHanlderConfigValidator($configArray); 21 | $this->assertTrue($logglyValidator->validate()); 22 | } 23 | 24 | public function testHasToken() 25 | { 26 | $configArray = [ 27 | 'type' => 'loggly', 28 | 'token' => '123-123-123-123', 29 | ]; 30 | 31 | $logglyValidator = new LogglyHanlderConfigValidator($configArray); 32 | $this->assertTrue($logglyValidator->hasToken()); 33 | } 34 | 35 | public function testNotHasToken() 36 | { 37 | $configArray = [ 38 | 'type' => 'loggly', 39 | ]; 40 | 41 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 42 | $logglyValidator = new LogglyHanlderConfigValidator($configArray); 43 | $this->assertTrue($logglyValidator->hasToken()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/StreamHandlerConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'stream', 16 | 'level' => 'INFO', 17 | 'path' => '/my/path', 18 | ]; 19 | 20 | $streamValidator = new StreamHandlerConfigValidator($configArray); 21 | $this->assertTrue($streamValidator->validate()); 22 | } 23 | 24 | public function testHasPath() 25 | { 26 | $configArray = [ 27 | 'type' => 'stream', 28 | 'level' => 'INFO', 29 | 'path' => '/my/path', 30 | ]; 31 | 32 | $streamValidator = new StreamHandlerConfigValidator($configArray); 33 | $this->assertTrue($streamValidator->hasPath()); 34 | } 35 | 36 | public function testNotHasPath() 37 | { 38 | $configArray = [ 39 | 'type' => 'stream', 40 | ]; 41 | 42 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 43 | $streamValidator = new StreamHandlerConfigValidator($configArray); 44 | $streamValidator->hasPath(); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/PushoverHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasToken() && $this->hasUser()) { 24 | return true; 25 | } 26 | } 27 | 28 | /** 29 | * @return bool 30 | * @throws MonologConfigException 31 | */ 32 | public function hasToken(): bool 33 | { 34 | if (isset($this->handlerConfigArray['token'])) { 35 | return true; 36 | } 37 | 38 | throw new MonologConfigException("Missing token in Pushover handler configuration"); 39 | } 40 | 41 | /** 42 | * @return bool 43 | * @throws MonologConfigException 44 | */ 45 | public function hasUser(): bool 46 | { 47 | if (isset($this->handlerConfigArray['user'])) { 48 | return true; 49 | } 50 | 51 | throw new MonologConfigException("Missing user in Pushover handler configuration"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/RedisHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasRedisClient() && $this->hasKey()) { 20 | return true; 21 | } 22 | } 23 | 24 | /** 25 | * @return bool 26 | * @throws MonologConfigException 27 | */ 28 | public function hasRedisClient(): bool 29 | { 30 | if (isset($this->handlerConfigArray['redis_client']) && $this->handlerConfigArray['redis_client'] instanceof \Redis) { 31 | return true; 32 | } 33 | 34 | throw new MonologConfigException("Missing Redis client in Redis handler configuration"); 35 | } 36 | 37 | /** 38 | * @return bool 39 | * @throws MonologConfigException 40 | */ 41 | public function hasKey(): bool 42 | { 43 | if (isset($this->handlerConfigArray['key'])) { 44 | return true; 45 | } 46 | 47 | throw new MonologConfigException("Missing Redis key in Redis handler configuration"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Validator/NativeMailHandlerConfigValidator.php: -------------------------------------------------------------------------------- 1 | hasTo() && $this->hasSubject() && $this->hasFrom()) { 21 | return true; 22 | } 23 | 24 | throw new MonologConfigException("Missing data in handler configuration"); 25 | } 26 | 27 | /** 28 | * @return bool 29 | * @throws MonologConfigException 30 | */ 31 | public function hasTo(): bool 32 | { 33 | if (isset($this->handlerConfigArray['to_email'])) { 34 | return true; 35 | } 36 | 37 | throw new MonologConfigException("Monolog To email is missing from config"); 38 | } 39 | 40 | /** 41 | * @return bool 42 | * @throws MonologConfigException 43 | */ 44 | public function hasSubject(): bool 45 | { 46 | if (isset($this->handlerConfigArray['subject'])) { 47 | return true; 48 | } 49 | 50 | throw new MonologConfigException("Monolog email subject is missing from config"); 51 | } 52 | 53 | /** 54 | * @return bool 55 | * @throws MonologConfigException 56 | */ 57 | public function hasFrom(): bool 58 | { 59 | if (isset($this->handlerConfigArray['from_email'])) { 60 | return true; 61 | } 62 | 63 | throw new MonologConfigException("Monolog email from is missing from config"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Factory/MonologMiddlewareFactory.php: -------------------------------------------------------------------------------- 1 | get('config'); 31 | if (null === $config) { 32 | throw new MonologConfigException("Can not find monolog configuration in your config. Make sure to have monolog configuration array in your config"); 33 | } 34 | 35 | $helper = new MonologConfigurationExtension($config['monolog']); 36 | $logHandlers = $helper->getLogHandlers(); 37 | $loggerName = ($config['monolog']['logger_name'] ?? 'monolog'); 38 | $loggables = ($config['monolog']['loggables'] ?? null); 39 | 40 | $loggableProvider = new LoggableProvider($loggables); 41 | 42 | /** 43 | * @var Logger 44 | */ 45 | $monologLogger = new Logger($loggerName); 46 | $monologLogger->setHandlers($logHandlers); 47 | 48 | return new MonologMiddleware($monologLogger, $loggableProvider); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/PushoverHandlerConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'pushover', 16 | 'level' => 'DEBUG', 17 | 'token' => '123-123-123-123', 18 | 'user' => 'username', 19 | ]; 20 | 21 | $pushoverValidator = new PushoverHandlerConfigValidator($configArray); 22 | $this->assertTrue($pushoverValidator->validate()); 23 | } 24 | 25 | public function testHasToken() 26 | { 27 | $configArray = [ 28 | 'type' => 'pushover', 29 | 'token' => 'token-token-token', 30 | ]; 31 | 32 | $pushoverValidator = new PushoverHandlerConfigValidator($configArray); 33 | $this->assertTrue($pushoverValidator->hasToken()); 34 | } 35 | 36 | public function testNotHasToken() 37 | { 38 | $configArray = [ 39 | 'type' => 'pushover', 40 | 'level' => 'DEBUG', 41 | ]; 42 | 43 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 44 | $pushoverValidator = new PushoverHandlerConfigValidator($configArray); 45 | $pushoverValidator->hasToken(); 46 | 47 | } 48 | 49 | public function testHasUser() 50 | { 51 | $configArray = [ 52 | 'type' => 'pushover', 53 | 'user' => '123-123-123', 54 | ]; 55 | 56 | $pushoverValidator = new PushoverHandlerConfigValidator($configArray); 57 | $this->assertTrue($pushoverValidator->hasUser()); 58 | } 59 | 60 | public function testNotHasUser() 61 | { 62 | $configArray = [ 63 | 'type' => 'pushover', 64 | 'level' => 'DEBUG', 65 | ]; 66 | 67 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 68 | $pushoverValidator = new PushoverHandlerConfigValidator($configArray); 69 | $pushoverValidator->hasUser(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/SlackHandlerConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'slack', 16 | 'level' => 'DEBUG', 17 | 'token' => '123-123-123-123', 18 | 'channel' => '#hello_world', 19 | ]; 20 | 21 | $slackValidator = new SlackHandlerConfigValidator($configArray); 22 | $this->assertTrue($slackValidator->validate()); 23 | } 24 | 25 | public function testHasChannel() 26 | { 27 | $configArray = [ 28 | 'type' => 'slack', 29 | 'channel' => '#hello_world', 30 | ]; 31 | 32 | $slackValidator = new SlackHandlerConfigValidator($configArray); 33 | $this->assertTrue($slackValidator->hasChannel()); 34 | } 35 | 36 | public function testNotHasChannel() 37 | { 38 | $configArray = [ 39 | 'type' => 'slack', 40 | 'level' => 'DEBUG', 41 | 'token' => '123-123-123-123', 42 | ]; 43 | 44 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 45 | $slackValidator = new SlackHandlerConfigValidator($configArray); 46 | $slackValidator->hasChannel(); 47 | 48 | } 49 | 50 | public function testHasToken() 51 | { 52 | $configArray = [ 53 | 'type' => 'slack', 54 | 'token' => '123-123-123', 55 | ]; 56 | 57 | $slackValidator = new SlackHandlerConfigValidator($configArray); 58 | $this->assertTrue($slackValidator->hasToken()); 59 | } 60 | 61 | public function testNotHasToken() 62 | { 63 | $configArray = [ 64 | 'type' => 'slack', 65 | 'level' => 'DEBUG', 66 | ]; 67 | 68 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 69 | $slackValidator = new SlackHandlerConfigValidator($configArray); 70 | $slackValidator->hasToken(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/NativeMailHandlerConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'native_mailer', 16 | 'level' => 'ERROR', 17 | 'to_email' => 'someemail@somedomain.com', 18 | 'subject' => 'Error in your application', 19 | 'from_email' => 'monolog@yoursystem.com', 20 | ]; 21 | 22 | $nativeMailValidator = new NativeMailHandlerConfigValidator($configArray); 23 | $this->assertTrue($nativeMailValidator->validate()); 24 | } 25 | 26 | public function testHasTo() 27 | { 28 | $configArray = [ 29 | 'type' => 'native_mailer', 30 | 'level' => 'ERROR', 31 | 'to_email' => 'someemail@somedomain.com', 32 | ]; 33 | 34 | $nativeMailValidator = new NativeMailHandlerConfigValidator($configArray); 35 | $this->assertTrue($nativeMailValidator->hasTo()); 36 | } 37 | 38 | public function testHasFrom() 39 | { 40 | $configArray = [ 41 | 'type' => 'native_mailer', 42 | 'level' => 'ERROR', 43 | 'from_email' => 'someemail@somedomain.com', 44 | ]; 45 | 46 | $nativeMailValidator = new NativeMailHandlerConfigValidator($configArray); 47 | $this->assertTrue($nativeMailValidator->hasFrom()); 48 | } 49 | 50 | public function testHasSubject() 51 | { 52 | $configArray = [ 53 | 'type' => 'native_mailer', 54 | 'level' => 'ERROR', 55 | 'subject' => 'someemail@somedomain.com', 56 | ]; 57 | 58 | $nativeMailValidator = new NativeMailHandlerConfigValidator($configArray); 59 | $this->assertTrue($nativeMailValidator->hasSubject()); 60 | } 61 | 62 | public function testHasToAndFromButNotSubject() 63 | { 64 | $configArray = [ 65 | 'type' => 'native_mailer', 66 | 'level' => 'ERROR', 67 | 'to_email' => 'someemail@somedomain.com', 68 | 'from_email' => 'monolog@yoursystem.com', 69 | ]; 70 | 71 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 72 | $nativeMailValidator = new NativeMailHandlerConfigValidator($configArray); 73 | $this->assertTrue($nativeMailValidator->hasTo()); 74 | $this->assertTrue($nativeMailValidator->hasFrom()); 75 | $this->assertTrue($nativeMailValidator->hasSubject()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Validator/RedisHandlerConfigValidatorTest.php: -------------------------------------------------------------------------------- 1 | 'redis', 16 | 'level' => 'INFO', 17 | 'redis_client' => new \Redis(), 18 | 'key' => 'monolog' 19 | ]; 20 | 21 | $redisValidator = new RedisHandlerConfigValidator($configArray); 22 | $this->assertTrue($redisValidator->validate()); 23 | } 24 | 25 | public function testHasRedisClient() 26 | { 27 | $configArray = [ 28 | 'type' => 'redis', 29 | 'level' => 'INFO', 30 | 'redis_client' => new \Redis(), 31 | ]; 32 | 33 | $redisValidator = new RedisHandlerConfigValidator($configArray); 34 | $this->assertTrue($redisValidator->hasRedisClient()); 35 | } 36 | 37 | public function testNotHasRedisClient() 38 | { 39 | $configArray = [ 40 | 'type' => 'redis', 41 | 'level' => 'INFO', 42 | 'key' => 'monolog' 43 | ]; 44 | 45 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 46 | $redisValidator = new RedisHandlerConfigValidator($configArray); 47 | $redisValidator->hasRedisClient(); 48 | } 49 | 50 | public function testHasRedisValueButNotRedisClient() 51 | { 52 | $configArray = [ 53 | 'type' => 'redis', 54 | 'level' => 'INFO', 55 | 'redis_client' => 'REDIS', 56 | ]; 57 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 58 | $redisValidator = new RedisHandlerConfigValidator($configArray); 59 | $redisValidator->hasRedisClient(); 60 | } 61 | 62 | public function testHasKey() 63 | { 64 | $configArray = [ 65 | 'type' => 'redis', 66 | 'level' => 'INFO', 67 | 'key' => 'monolog' 68 | ]; 69 | 70 | $redisValidator = new RedisHandlerConfigValidator($configArray); 71 | $this->assertTrue($redisValidator->hasKey()); 72 | } 73 | 74 | public function testHasNoKey() 75 | { 76 | $configArray = [ 77 | 'type' => 'redis', 78 | 'level' => 'INFO', 79 | ]; 80 | 81 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 82 | $redisValidator = new RedisHandlerConfigValidator($configArray); 83 | $this->assertTrue($redisValidator->hasKey()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/MonologMiddleware/MonologMiddleware.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 37 | $this->loggableProvider = $loggableProvider; 38 | } 39 | 40 | /** 41 | * @param ServerRequestInterface $request 42 | * @param RequestHandlerInterface $handler 43 | * @return ResponseInterface 44 | */ 45 | public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface 46 | { 47 | $response = $handler->handle($request); 48 | $level = $this->getLogLevel($response->getStatusCode()); 49 | 50 | $this->log($level, $request, $response); 51 | 52 | return $response; 53 | } 54 | 55 | /** 56 | * @param int $responseCode 57 | * @return int 58 | */ 59 | public function getLogLevel($responseCode): int 60 | { 61 | // Log level will be dependant on Response Code 62 | switch ($responseCode) { 63 | case Response::HTTP_OK: 64 | case Response::HTTP_ACCEPTED: 65 | case Response::HTTP_CREATED: 66 | case Response::HTTP_FOUND: 67 | $level = Logger::INFO; 68 | break; 69 | case Response::HTTP_NOT_FOUND: 70 | case Response::HTTP_NOT_ACCEPTABLE: 71 | case Response::HTTP_BAD_REQUEST: 72 | case Response::HTTP_BAD_GATEWAY: 73 | $level = Logger::WARNING; 74 | break; 75 | case Response::HTTP_INTERNAL_SERVER_ERROR: 76 | $level = Logger::ERROR; 77 | break; 78 | default: 79 | $level = Logger::DEBUG; 80 | } 81 | 82 | return $level; 83 | } 84 | 85 | 86 | /** 87 | * @param $level 88 | * @param ServerRequestInterface $request 89 | * @param ResponseInterface $response 90 | * @return bool 91 | */ 92 | public function log($level, ServerRequestInterface $request, ResponseInterface $response): bool 93 | { 94 | return $this->logger->addRecord($level, $this->loggableProvider->format($request, $response)); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Loggable/LoggableProvider.php: -------------------------------------------------------------------------------- 1 | template = $template ?: self::CLF; 60 | } 61 | 62 | /** 63 | * Returns a formatted message string. 64 | * 65 | * @param RequestInterface $request Request that was sent 66 | * @param ResponseInterface $response Response that was received 67 | * @param \Exception $error Exception that was received 68 | * 69 | * @return string 70 | */ 71 | public function format( 72 | RequestInterface $request, 73 | ResponseInterface $response = null, 74 | \Exception $error = null 75 | ): string 76 | { 77 | $cache = []; 78 | 79 | return preg_replace_callback( 80 | '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', 81 | function (array $matches) use ($request, $response, $error, &$cache) { 82 | 83 | if (isset($cache[$matches[1]])) { 84 | return $cache[$matches[1]]; 85 | } 86 | 87 | $result = ''; 88 | switch ($matches[1]) { 89 | case 'request': 90 | $result = Psr7\str($request); 91 | break; 92 | case 'response': 93 | $result = $response ? Psr7\str($response) : ''; 94 | break; 95 | case 'req_headers': 96 | $result = trim( 97 | $request->getMethod() 98 | .' '.$request->getRequestTarget() 99 | ) 100 | .' HTTP/'.$request->getProtocolVersion()."\r\n" 101 | .$this->headers($request); 102 | break; 103 | case 'res_headers': 104 | $result = $response ? 105 | sprintf( 106 | 'HTTP/%s %d %s', 107 | $response->getProtocolVersion(), 108 | $response->getStatusCode(), 109 | $response->getReasonPhrase() 110 | )."\r\n".$this->headers($response) 111 | : 'NULL'; 112 | break; 113 | case 'req_body': 114 | $result = $request->getBody(); 115 | break; 116 | case 'res_body': 117 | $result = $response ? $response->getBody() : 'NULL'; 118 | break; 119 | case 'ts': 120 | case 'date_iso_8601': 121 | $result = gmdate('c'); 122 | break; 123 | case 'date_common_log': 124 | $result = date('d/M/Y:H:i:s O'); 125 | break; 126 | case 'method': 127 | $result = $request->getMethod(); 128 | break; 129 | case 'version': 130 | $result = $request->getProtocolVersion(); 131 | break; 132 | case 'uri': 133 | case 'url': 134 | $result = $request->getUri(); 135 | break; 136 | case 'target': 137 | $result = $request->getRequestTarget(); 138 | break; 139 | case 'req_version': 140 | $result = $request->getProtocolVersion(); 141 | break; 142 | case 'res_version': 143 | $result = $response 144 | ? $response->getProtocolVersion() 145 | : 'NULL'; 146 | break; 147 | case 'host': 148 | $result = $request->getHeaderLine('Host'); 149 | break; 150 | case 'hostname': 151 | $result = gethostname(); 152 | break; 153 | case 'code': 154 | $result = $response ? $response->getStatusCode() : 'NULL'; 155 | break; 156 | case 'phrase': 157 | $result = $response ? $response->getReasonPhrase() : 'NULL'; 158 | break; 159 | case 'error': 160 | $result = $error ? $error->getMessage() : 'NULL'; 161 | break; 162 | default: 163 | // handle prefixed dynamic headers 164 | if (strpos($matches[1], 'req_header_') === 0) { 165 | $result = $request->getHeaderLine(substr($matches[1], 11)); 166 | } elseif (strpos($matches[1], 'res_header_') === 0) { 167 | $result = $response 168 | ? $response->getHeaderLine(substr($matches[1], 11)) 169 | : 'NULL'; 170 | } 171 | } 172 | 173 | $cache[$matches[1]] = $result; 174 | 175 | return $result; 176 | }, 177 | $this->template 178 | ); 179 | } 180 | 181 | /** 182 | * @param MessageInterface $message 183 | * @return string 184 | */ 185 | private function headers(MessageInterface $message): string 186 | { 187 | $result = ''; 188 | foreach ($message->getHeaders() as $name => $values) { 189 | $result .= $name.': '.implode(', ', $values)."\r\n"; 190 | } 191 | 192 | return trim($result); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Extension/MonologConfigurationExtension.php: -------------------------------------------------------------------------------- 1 | config = $config; 42 | $this->validate(); 43 | } 44 | 45 | /** 46 | * @return bool 47 | * @throws MonologConfigException 48 | */ 49 | public function validate(): bool 50 | { 51 | return $this->hasHandlersConfig(); 52 | } 53 | 54 | /** 55 | * @return bool 56 | * @throws MonologConfigException 57 | */ 58 | public function hasHandlersConfig(): bool 59 | { 60 | if (isset($this->config['handlers'])) { 61 | return true; 62 | } 63 | 64 | throw new MonologConfigException("Can not find monolog handlers in your config. Make sure to have monolog configuration array in your config"); 65 | } 66 | 67 | /** 68 | * @return array 69 | * @throws MonologConfigException 70 | * @throws MonologHandlerNotImplementedException 71 | * @throws \Exception 72 | * @throws \Monolog\Handler\MissingExtensionException 73 | */ 74 | public function getLogHandlers(): array 75 | { 76 | $handlers = []; 77 | foreach ($this->config['handlers'] as $key => $value) { 78 | $handlers[ $key ] = $this->getHandler($key, $value); 79 | } 80 | 81 | return $handlers; 82 | } 83 | 84 | /** 85 | * @param string $name 86 | * @param array $handlerConfig 87 | * @return BrowserConsoleHandler|ChromePHPHandler|FirePHPHandler|LogglyHandler|NativeMailerHandler|NewRelicHandler|PushoverHandler|RedisHandler|SlackHandler|StreamHandler 88 | * @throws \LogicException 89 | * @throws \InvalidArgumentException 90 | * @throws MonologConfigException 91 | * @throws MonologHandlerNotImplementedException 92 | * @throws \Exception 93 | * @throws \Monolog\Handler\MissingExtensionException 94 | */ 95 | public function getHandler($name, $handlerConfig) 96 | { 97 | if (!isset($handlerConfig['type'])) { 98 | throw new MonologConfigException(sprintf("Hander %s does not have type", $name)); 99 | } 100 | 101 | $bubble = ($handlerConfig['bubble'] ?? true); 102 | 103 | switch ($handlerConfig['type']) { 104 | case 'stream': 105 | /** 106 | * @var Validator\StreamHandlerConfigValidator $streamHandlerValidator 107 | */ 108 | $streamHandlerValidator = new Validator\StreamHandlerConfigValidator($handlerConfig); 109 | $streamHandlerValidator->validate(); 110 | $filePermission = ($handlerConfig['permission'] ?? null); 111 | $useLocking = ($handlerConfig['use_locking'] ?? false); 112 | 113 | return new StreamHandler($handlerConfig['path'], $handlerConfig['level'], $bubble, $filePermission, $useLocking); 114 | break; 115 | 116 | case 'slack': 117 | /** 118 | * @var Validator\SlackHandlerConfigValidator $slackHandlerValidator 119 | */ 120 | $slackHandlerValidator = new Validator\SlackHandlerConfigValidator($handlerConfig); 121 | $slackHandlerValidator->validate(); 122 | 123 | $username = ($handlerConfig['username'] ?? 'Monolog'); 124 | $useAttachment = ($handlerConfig['use_attachment'] ?? true); 125 | $iconEmoji = ($handlerConfig['icon_emoji'] ?? null); 126 | $useShortAttachment = ($handlerConfig['useShortAttachment'] ?? false); 127 | $includeContextAndExtra = ($handlerConfig['includeContextAndExtra'] ?? false); 128 | 129 | return new SlackHandler($handlerConfig['token'], $handlerConfig['channel'], $username, $useAttachment, $iconEmoji, $handlerConfig['level'], $bubble, $useShortAttachment, $includeContextAndExtra); 130 | break; 131 | 132 | case 'loggly': 133 | /** 134 | * @var Validator\LogglyHanlderConfigValidator $logglyHandlerValidator 135 | */ 136 | $logglyHandlerValidator = new Validator\LogglyHanlderConfigValidator($handlerConfig); 137 | $logglyHandlerValidator->validate(); 138 | 139 | return new LogglyHandler($handlerConfig['token'], $handlerConfig['level'], $bubble); 140 | break; 141 | 142 | case 'native_mailer': 143 | $nativeMailHandlerValidator = new Validator\NativeMailHandlerConfigValidator($handlerConfig); 144 | $nativeMailHandlerValidator->validate(); 145 | $maxColumnWidth = ($handlerConfig['max_column_width'] ?? 70); 146 | 147 | return new NativeMailerHandler($handlerConfig['to'], $handlerConfig['subject'], $handlerConfig['from'], $handlerConfig['level'], $bubble, $maxColumnWidth); 148 | break; 149 | 150 | case 'new_relic': 151 | $newRelicHandlerValidator = new Validator\AbstractHandlerConfigValidator($handlerConfig); 152 | $newRelicHandlerValidator->validate(); 153 | 154 | $appName = ($handlerConfig['app_name'] ?? null); 155 | 156 | return new NewRelicHandler($handlerConfig['level'], $bubble, $appName); 157 | break; 158 | case 'php_console': 159 | break; 160 | case 'pushover': 161 | $pushoverHandlerValidator = new Validator\PushoverHandlerConfigValidator($handlerConfig); 162 | $pushoverHandlerValidator->validate(); 163 | $title = ($handlerConfig['title'] ?? null); 164 | 165 | return new PushoverHandler($handlerConfig['token'], $handlerConfig['user'], $title, $handlerConfig['level'], $bubble); 166 | break; 167 | case 'redis': 168 | $redisHandlerValidator = new Validator\RedisHandlerConfigValidator($handlerConfig); 169 | $redisHandlerValidator->validate(); 170 | $capSize = ($handlerConfig['cap_size'] ?? false); 171 | 172 | return new RedisHandler($handlerConfig['redis_client'], $handlerConfig['key'], $bubble, $capSize); 173 | break; 174 | case 'rotating_file': 175 | 176 | $rotatingFileHanlderValidator = new Validator\StreamHandlerConfigValidator($handlerConfig); 177 | $rotatingFileHanlderValidator->validate(); 178 | $maxFiles = ($handlerConfig['max_files'] ?? 0); 179 | $filePermission = ($handlerConfig['file_permission'] ?? null); 180 | $filenameFormat = ($handlerConfig['filename_format'] ?? '{filename}-{date}'); 181 | $dateFormat = ($handlerConfig['date_format'] ?? 'Y-m-d'); 182 | 183 | $rotatingFileHandler = new RotatingFileHandler($handlerConfig['filename'], $maxFiles, $handlerConfig['level'], $bubble, $filePermission, false); 184 | $rotatingFileHandler->setFilenameFormat($filenameFormat, $dateFormat); 185 | 186 | return $rotatingFileHandler; 187 | break; 188 | case 'firephp': 189 | $firePhpHandlerValidator = new Validator\AbstractHandlerConfigValidator($handlerConfig); 190 | $firePhpHandlerValidator->validate(); 191 | 192 | return new FirePHPHandler($handlerConfig['level'], $bubble); 193 | break; 194 | case 'chromephp': 195 | $chromePhpHandlerValidator = new Validator\AbstractHandlerConfigValidator($handlerConfig); 196 | $chromePhpHandlerValidator->validate(); 197 | 198 | return new ChromePHPHandler($handlerConfig['level'], $bubble); 199 | break; 200 | case 'browser_console': 201 | $browserConsoleHandlerValidator = new Validator\BrowserConsoleHandlerConfigValidator($handlerConfig); 202 | $browserConsoleHandlerValidator->validate(); 203 | 204 | return new BrowserConsoleHandler($handlerConfig['level'], $bubble); 205 | break; 206 | case 'dynamodb': 207 | case 'couchdb': 208 | case 'swift_mailer': 209 | case 'sys_log': 210 | case 'zend_monitor': 211 | case 'hipchat': 212 | case 'iftt': 213 | case 'mongo': 214 | break; 215 | default: 216 | throw new MonologHandlerNotImplementedException( 217 | sprintf("Handler %s does not exist or not implemented yet in the middleware", $handlerConfig['type']) 218 | ); 219 | } 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/MonologMiddleware/Test/Extension/MonologConfigurationExtensionTest.php: -------------------------------------------------------------------------------- 1 | expectException('\MonologMiddleware\Exception\MonologConfigException'); 18 | $configExtention = new MonologConfigurationExtension($config); 19 | } 20 | 21 | public function testHasHandlerConfig() 22 | { 23 | 24 | $config = [ 25 | 'name' => 'MonologTest', 26 | 'handlers' => [ 27 | 'main' => [ 28 | 'type' => 'stream', 29 | 'path' => "test/test.log", 30 | 'level' => Logger::DEBUG, 31 | ], 32 | ], 33 | ]; 34 | 35 | $configExtension = new MonologConfigurationExtension($config); 36 | $this->assertTrue($configExtension->hasHandlersConfig()); 37 | } 38 | 39 | public function testNotHasHandlerConfig() 40 | { 41 | $config = [ 42 | 'name' => 'MonologTest', 43 | 'handlers1' => [ 44 | 'main' => [ 45 | 'type' => 'stream', 46 | 'path' => "test/test.log", 47 | 'level' => Logger::DEBUG, 48 | ], 49 | ], 50 | ]; 51 | 52 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 53 | 54 | $configExtension = new MonologConfigurationExtension($config); 55 | } 56 | 57 | public function testGetHandlerWithStreamType() 58 | { 59 | $config = [ 60 | 'name' => 'MonologTest', 61 | 'handlers' => [ 62 | 'main' => [ 63 | 'type' => 'stream', 64 | 'path' => "test/test.log", 65 | 'level' => Logger::DEBUG, 66 | ], 67 | ], 68 | ]; 69 | 70 | $configHelper = new MonologConfigurationExtension($config); 71 | 72 | $handler = $configHelper->getHandler('main', $config['handlers']['main']); 73 | 74 | $this->assertInstanceOf(StreamHandler::class, $handler); 75 | } 76 | 77 | public function testGetHandlerWithLogglyType() 78 | { 79 | $config = [ 80 | 'name' => 'MonologTest', 81 | 'handlers' => [ 82 | 'loggly' => [ 83 | 'type' => 'loggly', 84 | 'token' => "123456789", 85 | 'level' => Logger::CRITICAL, 86 | ], 87 | ], 88 | ]; 89 | 90 | $conigHelper = new MonologConfigurationExtension($config); 91 | 92 | $handler = $conigHelper->getHandler('loggly', $config['handlers']['loggly']); 93 | 94 | $this->assertInstanceOf(LogglyHandler::class, $handler); 95 | } 96 | 97 | public function testGetHandlerWithLogglyTypeAndMissingToken() 98 | { 99 | $config = [ 100 | 'name' => 'MonologTest', 101 | 'handlers' => [ 102 | 'loggly' => [ 103 | 'type' => 'loggly', 104 | 'level' => Logger::CRITICAL, 105 | ], 106 | ], 107 | ]; 108 | 109 | $this->expectException('MonologMiddleware\Exception\MonologConfigException');; 110 | $conigHelper = new MonologConfigurationExtension($config); 111 | $handler = $conigHelper->getHandler('loggly', $config['handlers']['loggly']); 112 | } 113 | 114 | public function testGetHandlerWithLogglyTypeAndMissingTokenAndLevel() 115 | { 116 | $config = [ 117 | 'name' => 'MonologTest', 118 | 'handlers' => [ 119 | 'loggly' => [ 120 | 'type' => 'loggly', 121 | ], 122 | ], 123 | ]; 124 | 125 | $this->expectException('MonologMiddleware\Exception\MonologConfigException');; 126 | $conigHelper = new MonologConfigurationExtension($config); 127 | $handler = $conigHelper->getHandler('loggly', $config['handlers']['loggly']); 128 | } 129 | 130 | public function testGetHandlerWithSlackType() 131 | { 132 | $config = [ 133 | 'name' => 'MonologTest', 134 | 'handlers' => [ 135 | 'slack' => [ 136 | 'type' => 'slack', 137 | 'token' => "123456789", 138 | 'channel' => 'mychannel', 139 | 'level' => Logger::CRITICAL, 140 | ], 141 | ], 142 | ]; 143 | 144 | $conigHelper = new MonologConfigurationExtension($config); 145 | 146 | $handler = $conigHelper->getHandler('slack', $config['handlers']['slack']); 147 | 148 | $this->assertInstanceOf(SlackHandler::class, $handler); 149 | } 150 | 151 | public function testSlackTypeWithMissingChannel() 152 | { 153 | $config = [ 154 | 'name' => 'MonologTest', 155 | 'handlers' => [ 156 | 'slack' => [ 157 | 'type' => 'slack', 158 | 'token' => "123456789", 159 | 'level' => Logger::CRITICAL, 160 | ], 161 | ], 162 | ]; 163 | 164 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 165 | $conigHelper = new MonologConfigurationExtension($config); 166 | 167 | $handler = $conigHelper->getHandler('slack', $config['handlers']['slack']); 168 | } 169 | 170 | public function testSlackTypeWithMissingToken() 171 | { 172 | $config = [ 173 | 'name' => 'MonologTest', 174 | 'handlers' => [ 175 | 'slack' => [ 176 | 'type' => 'slack', 177 | 'channel' => "mychannel", 178 | 'level' => Logger::CRITICAL, 179 | ], 180 | ], 181 | ]; 182 | 183 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 184 | $conigHelper = new MonologConfigurationExtension($config); 185 | 186 | $handler = $conigHelper->getHandler('slack', $config['handlers']['slack']); 187 | } 188 | 189 | public function testSlackTypeWithMissingTokenAndChannel() 190 | { 191 | $config = [ 192 | 'name' => 'MonologTest', 193 | 'handlers' => [ 194 | 'slack' => [ 195 | 'type' => 'slack', 196 | 'level' => Logger::CRITICAL, 197 | ], 198 | ], 199 | ]; 200 | 201 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 202 | $conigHelper = new MonologConfigurationExtension($config); 203 | 204 | $handler = $conigHelper->getHandler('slack', $config['handlers']['slack']); 205 | } 206 | 207 | public function testSlackTypeWithMissingTokenAndChannelAndLevel() 208 | { 209 | $config = [ 210 | 'name' => 'MonologTest', 211 | 'handlers' => [ 212 | 'slack' => [ 213 | 'type' => 'slack', 214 | ], 215 | ], 216 | ]; 217 | 218 | $this->expectException('MonologMiddleware\Exception\MonologConfigException'); 219 | $conigHelper = new MonologConfigurationExtension($config); 220 | 221 | $handler = $conigHelper->getHandler('slack', $config['handlers']['slack']); 222 | } 223 | 224 | public function testErrorGetHandler() 225 | { 226 | $config = [ 227 | 'name' => 'MonologTest', 228 | 'handlers' => [ 229 | 'rubbishHandler' => [ 230 | 'type' => 'rubbish', 231 | 'token' => "123456789", 232 | 'level' => Logger::CRITICAL, 233 | ], 234 | ], 235 | ]; 236 | 237 | $configExtension = new MonologConfigurationExtension($config); 238 | 239 | $this->expectException('MonologMiddleware\Exception\MonologHandlerNotImplementedException'); 240 | 241 | $configExtension->getHandler('rubbish', $config['handlers']['rubbishHandler']); 242 | } 243 | 244 | public function testGetHandlers() 245 | { 246 | $config = [ 247 | 'logger_name' => 'LoggerTest', 248 | 'handlers' => 249 | [ 250 | 'slack' => 251 | [ 252 | 'type' => 'slack', 253 | 'token' => 'token-token', 254 | 'channel' => '#test-channel', 255 | 'level' => Logger::DEBUG, 256 | 'iconEmoji' => '::ghost::', // optional 257 | 'bubble' => false, // optional 258 | ], 259 | 'main' => 260 | [ 261 | 'type' => 'stream', 262 | 'path' => "data/main.log", 263 | 'level' => Logger::DEBUG, 264 | 'bubble' => false, 265 | ], 266 | ], 267 | ]; 268 | 269 | $configExtension = new MonologConfigurationExtension($config); 270 | $handlers = $configExtension->getLogHandlers(); 271 | $this->assertArrayHasKey('slack', $handlers); 272 | $this->assertArrayHasKey('main', $handlers); 273 | $this->assertInstanceOf(SlackHandler::class, $handlers['slack']); 274 | $this->assertInstanceOf(StreamHandler::class, $handlers['main']); 275 | } 276 | 277 | public function testEmptyGetHandlers() 278 | { 279 | // This should return empty array 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/orasik/monolog-middleware.svg?branch=master)](https://travis-ci.org/orasik/monolog-middleware) 2 | [![Latest Stable Version](https://poser.pugx.org/oras/monolog-middleware/v/stable)](https://packagist.org/packages/oras/monolog-middleware) 3 | [![Total Downloads](https://poser.pugx.org/oras/monolog-middleware/downloads)](https://packagist.org/packages/oras/monolog-middleware) 4 | [![License](https://poser.pugx.org/oras/monolog-middleware/license)](https://packagist.org/packages/oras/monolog-middleware) 5 | 6 | # Monolog Logger Middleware 7 | Monolog Middleware to be used with PSR-7 middleware frameworks like Zend Expressive and Slim. 8 | 9 | **Now it does support Zend Expressive `3.*`** 10 | 11 | To use with Zend Expressive `1.*` please install version `1.1.4` 12 | To use with Zend Expressive `2.*` please install version `2.0.0` 13 | 14 | `loggables` setting inspired by Guzzle Log Format. You can set any data in request/response/headers that you want to log from config file 15 | rather than in code to give more flexibility in logging more/less data based on your needs. 16 | 17 | 18 | 19 | ### Installation 20 | 21 | ##### 1) Install middleware using composer 22 | ```sh 23 | composer require oras/monolog-middleware 24 | ``` 25 | ##### 2) Add configuration 26 | Then in your Zend Expressive `config/autoload/` directory, created a new config file call it: `logger.local.php` 27 | 28 | As a starting point, you can have the following in the file: 29 | 30 | ```php 31 | use Monolog\Logger; 32 | 33 | return [ 34 | 'monolog' => 35 | [ 36 | 'logger_name' => 'MyLog', 37 | 'loggables' => '[{host}] {request}/{response}', // optional and current one is default format that will be logged 38 | 'handlers' => 39 | [ 40 | 'main' => 41 | [ 42 | 'type' => 'stream', 43 | 'path' => "data/main.log", 44 | 'level' => Logger::DEBUG, 45 | 'bubble' => true, 46 | ], 47 | ], 48 | ], 49 | ]; 50 | ``` 51 | 52 | Please refer to Loggables list at end for all possible variables. 53 | 54 | 55 | ##### 3) Add factory and middleware to `dependencies.global.php` file as follows: 56 | ```php 57 | 'factories' => [ 58 | 59 | \MonologMiddleware\MonologMiddleware::class => \MonologMiddleware\Factory\MonologMiddlewareFactory::class, 60 | ], 61 | ``` 62 | 63 | ##### 4) Now to start recording logs of request/response for a middleware, just put the following line after routing. 64 | 65 | Example: 66 | ```php 67 | 'routes' => [ 68 | [ 69 | 'name' => 'home', 70 | 'path' => '/', 71 | 'middleware' => [ 72 | App\Action\HomePageAction::class, 73 | \MonologMiddleware\MonologMiddleware::class, 74 | ], 75 | 'allowed_methods' => ['GET'], 76 | ], 77 | ]; 78 | ``` 79 | 80 | Now every time you call the route `/`, you'll get logs for request and response. 81 | 82 | **By default, MonologMiddleware will record logs in debug mode. If you want to handle different levels, just change `level` in config.** 83 | 84 | 85 | ### Requirements 86 | - PHP >= 7.1 87 | 88 | 89 | ### Configuration examples 90 | Full example of each implemented handler in Monolog Middleware. Please note that these might not be ALL handlers 91 | supported by Monolog, they are just the implemented in this middleware. 92 | 93 | All lines are required unless stated. 94 | 95 | ##### Stream 96 | ```php 97 | $streamHandler = [ 98 | 'main' => 99 | [ 100 | 'type' => 'stream', 101 | 'path' => 'data/main.log', 102 | 'level' => Logger::DEBUG, 103 | 'bubble' => true, // optional 104 | ], 105 | ]; 106 | ``` 107 | 108 | ##### Loggly 109 | ```php 110 | $logglyHandler = [ 111 | 'loggly' => 112 | [ 113 | 'type' => 'loggly', 114 | 'token' => 'your-loggly-token', 115 | 'level' => Logger::DEBUG, 116 | 'bubble' => true, //optional 117 | ], 118 | ]; 119 | ``` 120 | 121 | ##### Slack 122 | ```php 123 | $slackHandler = [ 124 | 'slack' => 125 | [ 126 | 'type' => 'slack', 127 | 'token' => 'your-slack-token', 128 | 'channel' => '#your-slack-channel', 129 | 'level' => Logger::DEBUG, 130 | 'icon_emoji' => '::ghost::', // optional 131 | 'bubble' => true, // optional 132 | ], 133 | ]; 134 | ``` 135 | 136 | ##### Pushover 137 | ```php 138 | $pushOverHandler = [ 139 | 'pushover' => 140 | [ 141 | 'type' => 'pushover', 142 | 'token' => 'your-pushover-token', 143 | 'user' => 'pushover user', 144 | 'level' => Logger::ERROR, 145 | 'title' => 'Log title', // optional 146 | 'bubble' => true, // optional 147 | ], 148 | ]; 149 | ``` 150 | 151 | ##### Native Email handler 152 | ```php 153 | $nativeEmailHandler = [ 154 | 'native_email' => 155 | [ 156 | 'type' => 'native_email', 157 | 'level' => Logger::CRITICAL, 158 | 'from_email' => 'logs@yourserver.com', 159 | 'to_email' => 'email@email.com', 160 | 'subject' => 'Email subject', // optional 161 | 'max_column_width' => 70, //optional 162 | 'bubble' => true, // optional 163 | ], 164 | ]; 165 | ``` 166 | 167 | ##### Browser Console handler 168 | ```php 169 | $browserConsoleHandler = [ 170 | 'browser_console' => 171 | [ 172 | 'type' => 'browser_console', 173 | 'level' => Logger::DEBUG, 174 | ], 175 | ]; 176 | ``` 177 | 178 | ##### Redis handler 179 | ```php 180 | $redisHandler = [ 181 | 'redis' => 182 | [ 183 | 'type' => 'redis', 184 | 'level' => Logger::DEBUG, 185 | 'redis_client' => new \Redis(), 186 | 'key' => 'monolog', 187 | ], 188 | ]; 189 | ``` 190 | 191 | ##### FirePHP handler 192 | ```php 193 | $redisHandler = [ 194 | 'firephp' => 195 | [ 196 | 'type' => 'firephp', 197 | 'level' => Logger::DEBUG, 198 | ], 199 | ]; 200 | ``` 201 | 202 | ##### NewRelic handler 203 | ```php 204 | $redisHandler = [ 205 | 'new_relic' => 206 | [ 207 | 'type' => 'new_relic', 208 | 'level' => Logger::DEBUG, 209 | 'app_name' => 'Monolog', // optional 210 | ], 211 | ]; 212 | ``` 213 | 214 | 215 | #### Loggables list 216 | 217 | To log request/response body you can use `{req_body}` and `{res_body}` respectively in `format` setting. 218 | 219 | Full list of logs variables with description: 220 | 221 | | Variable | Substitution | 222 | | --- | --- | 223 | | {request} | Full HTTP request message | 224 | | {response} | Full HTTP response message | 225 | | {ts} | Timestamp | 226 | | {host} | Host of the request | 227 | | {method} | Method of the request | 228 | | {url} | URL of the request | 229 | | {host} | Host of the request | 230 | | {protocol} | Request protocol | 231 | | {version} | Protocol version | 232 | | {resource}| Resource of the request (path + query + fragment) | 233 | | {port} | Port of the request | 234 | | {hostname} | Hostname of the machine that sent the request | 235 | | {code} | Status code of the response (if available) | 236 | | {phrase} | Reason phrase of the response (if available) | 237 | | {curl_error} | Curl error message (if available) | 238 | | {curl_code} | Curl error code (if available) | 239 | | {curl_stderr} | Curl standard error (if available) | 240 | | {connect_time} | Time in seconds it took to establish the connection (if available) | 241 | | {total_time} | Total transaction time in seconds for last transfer (if available) | 242 | | {req_header_*} | Replace * with the lowercased name of a request header to add to the message | 243 | | {res_header_*} | Replace * with the lowercased name of a response header to add to the message | 244 | | {req_body} | Request body | 245 | | {res_body} | Response body| 246 | 247 | 248 | 249 | #### Extending Middleware 250 | 251 | To extend the middleware to log your own format, or specific data like cookies, server params .. etc. You can do that easily using the following steps: 252 | 253 | 1. Create a factory class. 254 | I have named it `MyMonologMiddlewareFactory` which will call a `MyMonologMiddleware` class which will be your customised middleware to log. 255 | 256 | ```php 257 | class MyMonologMiddlewareFactory 258 | { 259 | 260 | /** 261 | * @param ContainerInterface $serviceContainer 262 | * @return MonologMiddleware 263 | * @throws MonologConfigException 264 | */ 265 | public function __invoke(ContainerInterface $serviceContainer) 266 | { 267 | $config = $serviceContainer->get('config'); 268 | if (null === $config) { 269 | throw new MonologConfigException("Can not find monolog configuration in your config. Make sure to have monolog configuration array in your config"); 270 | } 271 | 272 | $helper = new MonologConfigurationExtension($config['monolog']); 273 | $logHandlers = $helper->getLogHandlers(); 274 | $loggerName = (isset($config['monolog']['logger_name']) ? $config['monolog']['logger_name'] : 'monolog'); 275 | /** 276 | * @var Logger 277 | */ 278 | $monologLogger = new Logger($loggerName); 279 | $monologLogger->setHandlers($logHandlers); 280 | 281 | return new MyMonologMiddleware($monologLogger); 282 | } 283 | } 284 | 285 | ``` 286 | 287 | 2. Create Middleware class 288 | 289 | ```php 290 | class MonologMiddleware implements MiddlewareInterface 291 | { 292 | /** 293 | * @var Logger 294 | */ 295 | protected $logger; 296 | 297 | /** 298 | * MonologMiddleware constructor. 299 | * @param Logger $logger 300 | */ 301 | public function __construct(Logger $logger) 302 | { 303 | $this->logger = $logger; 304 | } 305 | 306 | 307 | /** 308 | * @param ServerRequestInterface $request 309 | * @param ResponseInterface $response 310 | * @param callable $next 311 | * @return mixed 312 | */ 313 | public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null) 314 | { 315 | // Here you set logger level, message or any data that you'd like from your request or response. 316 | // For example, I am going to log cookie params 317 | 318 | $this->logger->addInfo(Logger::INFO, implode(", ", $request->getCookieParams()); 319 | return $next($request, $response); 320 | } 321 | 322 | } 323 | ``` 324 | 325 | 3. Add your factory and middleware to global dependency file. Assuming you have your middleware and factory in the same directory, the config will be: 326 | 327 | ```php 328 | 'factories' => [ 329 | MyMonologMiddleware::class => MyMonologMiddlewareFactory::class, 330 | ], 331 | ``` 332 | 333 | That's it ... you're ready to use your own customised logger. 334 | 335 | 336 | > Monolog Middleware was written during my commute time. Written with passion on SouthWest Trains. **Please mind the gap!** 337 | --------------------------------------------------------------------------------