├── phpstan.neon ├── ruleset.xml ├── src ├── Exception │ ├── Exception.php │ └── NotSupportedException.php ├── Handler │ ├── NewRelicHandler.php │ └── FallbackNetteHandler.php ├── Processor │ ├── PriorityProcessor.php │ ├── TracyUrlProcessor.php │ └── TracyExceptionProcessor.php ├── Tracy │ ├── BlueScreenRenderer.php │ └── MonologAdapter.php ├── Logger.php ├── DI │ └── MonologExtension.php └── CustomChannel.php ├── README.md ├── composer.json └── license.md /phpstan.neon: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | src/Exception/Exception.php 7 | src/Exception/NotSupportedException.php 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/Exception/Exception.php: -------------------------------------------------------------------------------- 1 | isNewRelicEnabled()) { 24 | return; 25 | } 26 | 27 | parent::write($record); 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function isHandling(array $record): bool 34 | { 35 | if (!$this->isNewRelicEnabled()) { 36 | return FALSE; 37 | } 38 | 39 | return parent::isHandling($record); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kdyby/Monolog 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/Kdyby/Monolog.svg?branch=master)](https://travis-ci.org/Kdyby/Monolog) 5 | [![Downloads this Month](https://img.shields.io/packagist/dm/kdyby/monolog.svg)](https://packagist.org/packages/kdyby/monolog) 6 | [![Latest stable](https://img.shields.io/packagist/v/kdyby/monolog.svg)](https://packagist.org/packages/kdyby/monolog) 7 | [![Coverage Status](https://coveralls.io/repos/github/Kdyby/Monolog/badge.svg?branch=master)](https://coveralls.io/github/Kdyby/Monolog?branch=master) 8 | 9 | Integration of [Monolog](https://github.com/Seldaek/monolog) into Nette Framework 10 | 11 | Installation 12 | ------------ 13 | 14 | The best way to install Kdyby/Monolog is using [Composer](http://getcomposer.org/): 15 | 16 | ```sh 17 | $ composer require kdyby/monolog 18 | ``` 19 | 20 | Documentation 21 | ------------ 22 | 23 | Learn more in [documentation](https://github.com/Kdyby/Monolog/blob/master/docs/en/index.md). 24 | 25 | ----- 26 | 27 | Homepage [http://www.kdyby.org](http://www.kdyby.org) and repository [http://github.com/Kdyby/Monolog](http://github.com/Kdyby/Monolog). 28 | -------------------------------------------------------------------------------- /src/Processor/PriorityProcessor.php: -------------------------------------------------------------------------------- 1 | logException($exception, $file); 33 | } 34 | 35 | /** 36 | * @internal 37 | * @deprecated 38 | */ 39 | public function log($message, $priority = self::INFO): ?string 40 | { 41 | throw new \Kdyby\Monolog\Exception\NotSupportedException('This class is only for rendering exceptions'); 42 | } 43 | 44 | /** 45 | * @internal 46 | * @deprecated 47 | */ 48 | public function defaultMailer($message, string $email): void 49 | { 50 | // pass 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/Processor/TracyUrlProcessor.php: -------------------------------------------------------------------------------- 1 | baseUrl = rtrim($baseUrl, '/'); 33 | $this->blueScreenRenderer = $blueScreenRenderer; 34 | } 35 | 36 | public function __invoke(array $record) 37 | { 38 | if ($this->isHandling($record)) { 39 | $exceptionFile = $this->blueScreenRenderer->getExceptionFile($record['context']['exception']); 40 | $record['context']['tracyUrl'] = sprintf('%s/%s', $this->baseUrl, basename($exceptionFile)); 41 | } 42 | 43 | return $record; 44 | } 45 | 46 | public function isHandling(array $record): bool 47 | { 48 | return isset($record['context']['exception']) 49 | && ($record['context']['exception'] instanceof \Throwable || $record['context']['exception'] instanceof \Exception); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kdyby/monolog", 3 | "type": "library", 4 | "description": "Integration of Monolog into Nette Framework", 5 | "keywords": ["nette", "monolog", "logging", "diagnostics"], 6 | "homepage": "http://kdyby.org", 7 | "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], 8 | "authors": [ 9 | { 10 | "name": "Martin Bažík", 11 | "email": "martin@bazo.sk", 12 | "homepage": "http://bazo.sk" 13 | }, 14 | { 15 | "name": "Filip Procházka", 16 | "homepage": "http://filip-prochazka.com", 17 | "email": "filip@prochazka.su" 18 | } 19 | ], 20 | "require": { 21 | "php": "^7.1 || ^7.2 || ^7.3", 22 | "nette/di": "^3.0", 23 | "kdyby/strict-objects": "^2.0", 24 | "monolog/monolog": "^2.0", 25 | "tracy/tracy": "^2.7 || ^3.0" 26 | }, 27 | "require-dev": { 28 | "nette/application": "^3.0", 29 | "nette/bootstrap": "^3.0", 30 | "nette/caching": "^3.0", 31 | "nette/http": "^3.0", 32 | "nette/tester": "^2.2", 33 | "phpstan/phpstan-shim": "^0.11.0@dev" 34 | }, 35 | "minimum-stability": "dev", 36 | "support": { 37 | "email": "filip@prochazka.su", 38 | "issues": "https://github.com/kdyby/monolog/issues" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Kdyby\\Monolog\\": "src/" 43 | } 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "KdybyTests\\": "tests/KdybyTests/" 48 | }, 49 | "classmap": [ 50 | "tests/KdybyTests" 51 | ] 52 | }, 53 | "provide": { 54 | "psr/log-implementation": "1.0.0" 55 | }, 56 | "extra": { 57 | "branch-alias": { 58 | "dev-master": "1.3-dev" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Processor/TracyExceptionProcessor.php: -------------------------------------------------------------------------------- 1 | blueScreenRenderer = $blueScreenRenderer; 28 | } 29 | 30 | public function __invoke(array $record) 31 | { 32 | if (!$this->isHandling($record)) { 33 | return $record; 34 | } 35 | 36 | $exception = $record['context']['exception']; 37 | $filename = $this->blueScreenRenderer->getExceptionFile($exception); 38 | $record['context']['tracy_filename'] = basename($filename); 39 | 40 | if (!file_exists($filename)) { 41 | $this->blueScreenRenderer->renderToFile($exception, $filename); 42 | $record['context']['tracy_created'] = TRUE; 43 | } 44 | 45 | return $record; 46 | } 47 | 48 | public function isHandling(array $record): bool 49 | { 50 | return !isset($record['context']['tracy']) 51 | && !isset($record['context']['tracy_filename']) 52 | && isset($record['context']['exception']) 53 | && ($record['context']['exception'] instanceof \Throwable || $record['context']['exception'] instanceof \Exception); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | Good news! You may use Kdyby Framework under the terms of either 5 | the New BSD License or the GNU General Public License (GPL) version 2 or 3. 6 | 7 | The BSD License is recommended for most projects. It is easy to understand and it 8 | places almost no restrictions on what you can do with the framework. If the GPL 9 | fits better to your project, you can use the framework under this license. 10 | 11 | You don't have to notify anyone which license you are using. You can freely 12 | use Kdyby Framework in commercial projects as long as the copyright header 13 | remains intact. 14 | 15 | 16 | 17 | New BSD License 18 | --------------- 19 | 20 | Copyright (c) 2008 Filip Procházka (http://filip-prochazka.com) 21 | All rights reserved. 22 | 23 | Redistribution and use in source and binary forms, with or without modification, 24 | are permitted provided that the following conditions are met: 25 | 26 | * Redistributions of source code must retain the above copyright notice, 27 | this list of conditions and the following disclaimer. 28 | 29 | * Redistributions in binary form must reproduce the above copyright notice, 30 | this list of conditions and the following disclaimer in the documentation 31 | and/or other materials provided with the distribution. 32 | 33 | * Neither the name of "Kdyby Framework" nor the names of its contributors 34 | may be used to endorse or promote products derived from this software 35 | without specific prior written permission. 36 | 37 | This software is provided by the copyright holders and contributors "as is" and 38 | any express or implied warranties, including, but not limited to, the implied 39 | warranties of merchantability and fitness for a particular purpose are 40 | disclaimed. In no event shall the copyright owner or contributors be liable for 41 | any direct, indirect, incidental, special, exemplary, or consequential damages 42 | (including, but not limited to, procurement of substitute goods or services; 43 | loss of use, data, or profits; or business interruption) however caused and on 44 | any theory of liability, whether in contract, strict liability, or tort 45 | (including negligence or otherwise) arising in any way out of the use of this 46 | software, even if advised of the possibility of such damage. 47 | 48 | 49 | 50 | GNU General Public License 51 | -------------------------- 52 | 53 | GPL licenses are very very long, so instead of including them here we offer 54 | you URLs with full text: 55 | 56 | - [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) 57 | - [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) 58 | -------------------------------------------------------------------------------- /src/Handler/FallbackNetteHandler.php: -------------------------------------------------------------------------------- 1 | appName = $appName; 49 | $this->logDir = $logDir; 50 | 51 | $this->defaultFormatter = new LineFormatter('[%datetime%] %message% %context% %extra%'); 52 | $this->priorityFormatter = new LineFormatter('[%datetime%] %level_name%: %message% %context% %extra%'); 53 | } 54 | 55 | public function handle(array $record): bool 56 | { 57 | if ($record['channel'] === $this->appName) { 58 | $this->setFormatter($this->defaultFormatter); 59 | $record['filename'] = strtolower($record['level_name']); 60 | 61 | } else { 62 | $this->setFormatter($this->priorityFormatter); 63 | $record['filename'] = $record['channel']; 64 | } 65 | 66 | return parent::handle($record); 67 | } 68 | 69 | /** 70 | * {@inheritdoc} 71 | */ 72 | protected function write(array $record): void 73 | { 74 | if ($this->expandNewlines) { 75 | $entry = ''; 76 | /** @var string[] $lines */ 77 | $lines = preg_split('{[\r\n]+}', (string) $record['message']); 78 | foreach ($lines as $line) { 79 | $entry .= trim($this->getFormatter()->format(['message' => $line] + $record)) . PHP_EOL; 80 | } 81 | 82 | } else { 83 | $entry = preg_replace('#\s*\r?\n\s*#', ' ', trim($record['formatted'])) . PHP_EOL; 84 | } 85 | 86 | $file = $this->logDir . '/' . strtolower($record['filename'] ?: 'info') . '.log'; 87 | if (!@file_put_contents($file, $entry, FILE_APPEND | LOCK_EX)) { 88 | throw new \RuntimeException(sprintf('Unable to write to log file %s. Is directory writable?', $file)); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/Tracy/MonologAdapter.php: -------------------------------------------------------------------------------- 1 | MonologLogger::DEBUG, 32 | self::INFO => MonologLogger::INFO, 33 | self::WARNING => MonologLogger::WARNING, 34 | self::ERROR => MonologLogger::ERROR, 35 | self::EXCEPTION => MonologLogger::CRITICAL, 36 | self::CRITICAL => MonologLogger::CRITICAL, 37 | ]; 38 | 39 | /** 40 | * @var \Monolog\Logger 41 | */ 42 | private $monolog; 43 | 44 | /** 45 | * @var \Kdyby\Monolog\Tracy\BlueScreenRenderer 46 | */ 47 | private $blueScreenRenderer; 48 | 49 | /** 50 | * @var string 51 | */ 52 | private $accessPriority; 53 | 54 | public function __construct( 55 | MonologLogger $monolog, 56 | BlueScreenRenderer $blueScreenRenderer, 57 | $email = NULL, 58 | $accessPriority = self::INFO 59 | ) 60 | { 61 | parent::__construct($blueScreenRenderer->directory, $email); 62 | $this->monolog = $monolog; 63 | $this->blueScreenRenderer = $blueScreenRenderer; 64 | $this->accessPriority = $accessPriority; 65 | } 66 | 67 | /** 68 | * {@inheritdoc} 69 | */ 70 | public function getExceptionFile(\Throwable $exception, string $level = self::EXCEPTION): string 71 | { 72 | return $this->blueScreenRenderer->getExceptionFile($exception); 73 | } 74 | 75 | public function log($originalMessage, $priority = self::INFO): ?string 76 | { 77 | $message = $this->formatMessage($originalMessage); 78 | $context = [ 79 | 'priority' => $priority, 80 | 'at' => Helpers::getSource(), 81 | ]; 82 | 83 | if ($originalMessage instanceof \Throwable || $originalMessage instanceof \Exception) { 84 | $context['exception'] = $originalMessage; 85 | } 86 | 87 | $exceptionFile = ($originalMessage instanceof \Throwable || $originalMessage instanceof \Exception) 88 | ? $this->getExceptionFile($originalMessage) 89 | : NULL; 90 | 91 | if ($this->email !== NULL && $this->mailer !== NULL && in_array($priority, [self::ERROR, self::EXCEPTION, self::CRITICAL], TRUE)) { 92 | $this->sendEmail(implode(' ', [ 93 | @date('[Y-m-d H-i-s]'), 94 | $message, 95 | ' @ ' . Helpers::getSource(), 96 | ($exceptionFile !== NULL) ? ' @@ ' . basename($exceptionFile) : NULL, 97 | ])); 98 | } 99 | 100 | if ($priority === self::ACCESS) { 101 | $priority = $this->accessPriority; 102 | } 103 | 104 | $this->monolog->addRecord( 105 | $this->getLevel($priority), 106 | $message, 107 | $context 108 | ); 109 | 110 | return $exceptionFile; 111 | } 112 | 113 | /** 114 | * @param string $priority 115 | * 116 | * @return int 117 | */ 118 | protected function getLevel($priority): int 119 | { 120 | if (isset($this->priorityMap[$priority])) { 121 | return $this->priorityMap[$priority]; 122 | } 123 | 124 | $levels = MonologLogger::getLevels(); 125 | return isset($levels[$uPriority = strtoupper($priority)]) ? $levels[$uPriority] : MonologLogger::INFO; 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/Logger.php: -------------------------------------------------------------------------------- 1 | debug($message, array_merge(['channel' => $this->name], $context)); 38 | } 39 | 40 | /** 41 | * Adds a log record at the INFO level. 42 | * 43 | * This method allows for compatibility with common interfaces. 44 | * 45 | * @param string $message The log message 46 | * @param array $context The log context 47 | */ 48 | public function addInfo($message, array $context = []): void 49 | { 50 | $this->info($message, array_merge(['channel' => $this->name], $context)); 51 | } 52 | 53 | /** 54 | * Adds a log record at the NOTICE level. 55 | * 56 | * This method allows for compatibility with common interfaces. 57 | * 58 | * @param string $message The log message 59 | * @param array $context The log context 60 | */ 61 | public function addNotice($message, array $context = []): void 62 | { 63 | $this->notice($message, array_merge(['channel' => $this->name], $context)); 64 | } 65 | 66 | /** 67 | * Adds a log record at the WARNING level. 68 | * 69 | * This method allows for compatibility with common interfaces. 70 | * 71 | * @param string $message The log message 72 | * @param array $context The log context 73 | */ 74 | public function addWarning($message, array $context = []): void 75 | { 76 | $this->warning($message, array_merge(['channel' => $this->name], $context)); 77 | } 78 | 79 | /** 80 | * Adds a log record at the ERROR level. 81 | * 82 | * This method allows for compatibility with common interfaces. 83 | * 84 | * @param string $message The log message 85 | * @param array $context The log context 86 | */ 87 | public function addError($message, array $context = []): void 88 | { 89 | $this->error($message, array_merge(['channel' => $this->name], $context)); 90 | } 91 | 92 | /** 93 | * Adds a log record at the CRITICAL level. 94 | * 95 | * This method allows for compatibility with common interfaces. 96 | * 97 | * @param string $message The log message 98 | * @param array $context The log context 99 | */ 100 | public function addCritical($message, array $context = []): void 101 | { 102 | $this->critical($message, array_merge(['channel' => $this->name], $context)); 103 | } 104 | 105 | /** 106 | * Adds a log record at the ALERT level. 107 | * 108 | * This method allows for compatibility with common interfaces. 109 | * 110 | * @param string $message The log message 111 | * @param array $context The log context 112 | */ 113 | public function addAlert($message, array $context = []): void 114 | { 115 | $this->alert($message, array_merge(['channel' => $this->name], $context)); 116 | } 117 | 118 | /** 119 | * Adds a log record at the EMERGENCY level. 120 | * 121 | * This method allows for compatibility with common interfaces. 122 | * 123 | * @param string $message The log message 124 | * @param array $context The log context 125 | */ 126 | public function addEmergency($message, array $context = []): void 127 | { 128 | $this->emergency($message, array_merge(['channel' => $this->name], $context)); 129 | } 130 | 131 | /** 132 | * Adds a log record at the WARNING level. 133 | * 134 | * This method allows for compatibility with common interfaces. 135 | * 136 | * @param string $message The log message 137 | * @param array $context The log context 138 | */ 139 | public function warn($message, array $context = []): void 140 | { 141 | $this->warning($message, array_merge(['channel' => $this->name], $context)); 142 | } 143 | 144 | /** 145 | * Adds a log record at the ERROR level. 146 | * 147 | * This method allows for compatibility with common interfaces. 148 | * 149 | * @param string $message The log message 150 | * @param array $context The log context 151 | */ 152 | public function err($message, array $context = []): void 153 | { 154 | $this->error($message, array_merge(['channel' => $this->name], $context)); 155 | } 156 | 157 | /** 158 | * Adds a log record at the CRITICAL level. 159 | * 160 | * This method allows for compatibility with common interfaces. 161 | * 162 | * @param string $message The log message 163 | * @param array $context The log context 164 | */ 165 | public function crit($message, array $context = []): void 166 | { 167 | $this->critical($message, array_merge(['channel' => $this->name], $context)); 168 | } 169 | 170 | /** 171 | * Adds a log record at the EMERGENCY level. 172 | * 173 | * This method allows for compatibility with common interfaces. 174 | * 175 | * @param string $message The log message 176 | * @param array $context The log context 177 | */ 178 | public function emerg($message, array $context = []): void 179 | { 180 | $this->emergency($message, array_merge(['channel' => $this->name], $context)); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/DI/MonologExtension.php: -------------------------------------------------------------------------------- 1 | Expect::anyOf(Expect::arrayOf('Nette\DI\Definitions\Statement'), 'false')->default([]), 52 | 'processors' => Expect::anyOf(Expect::arrayOf('Nette\DI\Definitions\Statement'), 'false')->default([]), 53 | 'name' => Expect::string('app'), 54 | 'hookToTracy' => Expect::bool(TRUE), 55 | 'tracyBaseUrl' => Expect::string(), 56 | 'usePriorityProcessor' => Expect::bool(TRUE), 57 | 'accessPriority' => Expect::string(ILogger::INFO), 58 | 'logDir' => Expect::string(), 59 | ])->castTo('array'); 60 | } 61 | 62 | public function loadConfiguration(): void 63 | { 64 | $builder = $this->getContainerBuilder(); 65 | $this->config['logDir'] = self::resolveLogDir($builder->parameters); 66 | $config = $this->config; 67 | self::createDirectory($config['logDir']); 68 | 69 | if (!isset($builder->parameters[$this->name]) || (is_array($builder->parameters[$this->name]) && !isset($builder->parameters[$this->name]['name']))) { 70 | $builder->parameters[$this->name]['name'] = $config['name']; 71 | } 72 | 73 | if (!isset($builder->parameters['logDir'])) { // BC 74 | $builder->parameters['logDir'] = $config['logDir']; 75 | } 76 | 77 | $builder->addDefinition($this->prefix('logger')) 78 | ->setFactory(KdybyLogger::class, [$config['name']]); 79 | 80 | // Tracy adapter 81 | $builder->addDefinition($this->prefix('adapter')) 82 | ->setFactory(MonologAdapter::class, [ 83 | 'monolog' => $this->prefix('@logger'), 84 | 'blueScreenRenderer' => $this->prefix('@blueScreenRenderer'), 85 | 'email' => Debugger::$email, 86 | 'accessPriority' => $config['accessPriority'], 87 | ]) 88 | ->addTag('logger'); 89 | 90 | // The renderer has to be separate, to solve circural service dependencies 91 | $builder->addDefinition($this->prefix('blueScreenRenderer')) 92 | ->setFactory(BlueScreenRenderer::class, [ 93 | 'directory' => $config['logDir'], 94 | ]) 95 | ->setAutowired(FALSE) 96 | ->addTag('logger'); 97 | 98 | if ($config['hookToTracy'] === TRUE && $builder->hasDefinition('tracy.logger')) { 99 | // TracyExtension initializes the logger from DIC, if definition is changed 100 | $builder->removeDefinition($existing = 'tracy.logger'); 101 | $builder->addAlias($existing, $this->prefix('adapter')); 102 | } 103 | 104 | $this->loadHandlers($config); 105 | $this->loadProcessors($config); 106 | $this->setConfig($config); 107 | } 108 | 109 | protected function loadHandlers(array $config): void 110 | { 111 | $builder = $this->getContainerBuilder(); 112 | 113 | foreach ($config['handlers'] as $handlerName => $implementation) { 114 | 115 | $builder->addDefinition($this->prefix('handler.' . $handlerName)) 116 | ->setFactory($implementation) 117 | ->setAutowired(FALSE) 118 | ->addTag(self::TAG_HANDLER) 119 | ->addTag(self::TAG_PRIORITY, is_numeric($handlerName) ? $handlerName : 0); 120 | } 121 | } 122 | 123 | protected function loadProcessors(array $config): void 124 | { 125 | $builder = $this->getContainerBuilder(); 126 | 127 | if ($config['usePriorityProcessor'] === TRUE) { 128 | // change channel name to priority if available 129 | $builder->addDefinition($this->prefix('processor.priorityProcessor')) 130 | ->setFactory(PriorityProcessor::class) 131 | ->addTag(self::TAG_PROCESSOR) 132 | ->addTag(self::TAG_PRIORITY, 20); 133 | } 134 | 135 | $builder->addDefinition($this->prefix('processor.tracyException')) 136 | ->setFactory(TracyExceptionProcessor::class, [ 137 | 'blueScreenRenderer' => $this->prefix('@blueScreenRenderer'), 138 | ]) 139 | ->addTag(self::TAG_PROCESSOR) 140 | ->addTag(self::TAG_PRIORITY, 100); 141 | 142 | if ($config['tracyBaseUrl'] !== NULL) { 143 | $builder->addDefinition($this->prefix('processor.tracyBaseUrl')) 144 | ->setFactory(TracyUrlProcessor::class, [ 145 | 'baseUrl' => $config['tracyBaseUrl'], 146 | 'blueScreenRenderer' => $this->prefix('@blueScreenRenderer'), 147 | ]) 148 | ->addTag(self::TAG_PROCESSOR) 149 | ->addTag(self::TAG_PRIORITY, 10); 150 | } 151 | 152 | foreach ($config['processors'] as $processorName => $implementation) { 153 | 154 | $this->compiler->loadDefinitionsFromConfig([ 155 | $serviceName = $this->prefix('processor.' . $processorName) => $implementation, 156 | ]); 157 | 158 | $builder->getDefinition($serviceName) 159 | ->addTag(self::TAG_PROCESSOR) 160 | ->addTag(self::TAG_PRIORITY, is_numeric($processorName) ? $processorName : 0); 161 | } 162 | } 163 | 164 | public function beforeCompile(): void 165 | { 166 | $builder = $this->getContainerBuilder(); 167 | /** @var \Nette\DI\ServiceDefinition $logger */ 168 | $logger = $builder->getDefinition($this->prefix('logger')); 169 | 170 | foreach ($handlers = $this->findByTagSorted(self::TAG_HANDLER) as $serviceName => $meta) { 171 | $logger->addSetup('pushHandler', ['@' . $serviceName]); 172 | } 173 | 174 | foreach ($this->findByTagSorted(self::TAG_PROCESSOR) as $serviceName => $meta) { 175 | $logger->addSetup('pushProcessor', ['@' . $serviceName]); 176 | } 177 | 178 | if (empty($handlers) && !array_key_exists('registerFallback', $this->config)) { 179 | $this->config['registerFallback'] = TRUE; 180 | } 181 | 182 | if (array_key_exists('registerFallback', $this->config) && !empty($this->config['registerFallback'])) { 183 | $logger->addSetup('pushHandler', [ 184 | new Statement(FallbackNetteHandler::class, [ 185 | 'appName' => $this->config['name'], 186 | 'logDir' => $this->config['logDir'], 187 | ]), 188 | ]); 189 | } 190 | 191 | /** @var \Nette\DI\ServiceDefinition $service */ 192 | foreach ($builder->findByType(LoggerAwareInterface::class) as $service) { 193 | $service->addSetup('setLogger', ['@' . $this->prefix('logger')]); 194 | } 195 | } 196 | 197 | protected function findByTagSorted($tag): array 198 | { 199 | $builder = $this->getContainerBuilder(); 200 | 201 | $services = $builder->findByTag($tag); 202 | uksort($services, function ($nameA, $nameB) use ($builder) { 203 | $pa = $builder->getDefinition($nameA)->getTag(self::TAG_PRIORITY) ?: 0; 204 | $pb = $builder->getDefinition($nameB)->getTag(self::TAG_PRIORITY) ?: 0; 205 | return $pa > $pb ? 1 : ($pa < $pb ? -1 : 0); 206 | }); 207 | 208 | return $services; 209 | } 210 | 211 | public function afterCompile(ClassTypeGenerator $class): void 212 | { 213 | $initialize = $class->getMethod('initialize'); 214 | 215 | if (Debugger::$logDirectory === NULL && array_key_exists('logDir', $this->config)) { 216 | $initialize->addBody('?::$logDirectory = ?;', [new PhpLiteral(Debugger::class), $this->config['logDir']]); 217 | } 218 | } 219 | 220 | public static function register(Configurator $configurator): void 221 | { 222 | $configurator->onCompile[] = function ($config, Compiler $compiler) { 223 | $compiler->addExtension('monolog', new MonologExtension()); 224 | }; 225 | } 226 | 227 | /** 228 | * @return string 229 | */ 230 | private static function resolveLogDir(array $parameters): string 231 | { 232 | if (isset($parameters['logDir'])) { 233 | return DIHelpers::expand('%logDir%', $parameters); 234 | } 235 | 236 | if (Debugger::$logDirectory !== NULL) { 237 | return Debugger::$logDirectory; 238 | } 239 | 240 | return DIHelpers::expand('%appDir%/../log', $parameters); 241 | } 242 | 243 | /** 244 | * @param string $logDir 245 | */ 246 | private static function createDirectory($logDir): void 247 | { 248 | if (!@mkdir($logDir, 0777, TRUE) && !is_dir($logDir)) { 249 | throw new \RuntimeException(sprintf('Log dir %s cannot be created', $logDir)); 250 | } 251 | } 252 | 253 | } 254 | -------------------------------------------------------------------------------- /src/CustomChannel.php: -------------------------------------------------------------------------------- 1 | parentLogger = $parentLogger; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function pushHandler(HandlerInterface $handler): MonologLogger 37 | { 38 | return $this->parentLogger->pushHandler($handler); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function popHandler(): HandlerInterface 45 | { 46 | return $this->parentLogger->popHandler(); 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function getHandlers(): array 53 | { 54 | return $this->parentLogger->getHandlers(); 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function pushProcessor(callable $callback): MonologLogger 61 | { 62 | return $this->parentLogger->pushProcessor($callback); 63 | } 64 | 65 | /** 66 | * {@inheritdoc} 67 | */ 68 | public function popProcessor(): callable 69 | { 70 | return $this->parentLogger->popProcessor(); 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function getProcessors(): array 77 | { 78 | return $this->parentLogger->getProcessors(); 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | */ 84 | public function addRecord(int $level, string $message, array $context = []): bool 85 | { 86 | return $this->parentLogger->addRecord($level, $message, array_merge(['channel' => $this->name], $context)); 87 | } 88 | 89 | /** 90 | * Adds a log record at the DEBUG level. 91 | * 92 | * This method allows for compatibility with common interfaces. 93 | * 94 | * @param string $message The log message 95 | * @param array $context The log context 96 | */ 97 | public function addDebug($message, array $context = []): void 98 | { 99 | $this->parentLogger->debug($message, array_merge(['channel' => $this->name], $context)); 100 | } 101 | 102 | /** 103 | * Adds a log record at the INFO level. 104 | * 105 | * This method allows for compatibility with common interfaces. 106 | * 107 | * @param string $message The log message 108 | * @param array $context The log context 109 | */ 110 | public function addInfo($message, array $context = []): void 111 | { 112 | $this->parentLogger->info($message, array_merge(['channel' => $this->name], $context)); 113 | } 114 | 115 | /** 116 | * Adds a log record at the NOTICE level. 117 | * 118 | * This method allows for compatibility with common interfaces. 119 | * 120 | * @param string $message The log message 121 | * @param array $context The log context 122 | */ 123 | public function addNotice($message, array $context = []): void 124 | { 125 | $this->parentLogger->notice($message, array_merge(['channel' => $this->name], $context)); 126 | } 127 | 128 | /** 129 | * Adds a log record at the WARNING level. 130 | * 131 | * This method allows for compatibility with common interfaces. 132 | * 133 | * @param string $message The log message 134 | * @param array $context The log context 135 | */ 136 | public function addWarning($message, array $context = []): void 137 | { 138 | $this->parentLogger->warning($message, array_merge(['channel' => $this->name], $context)); 139 | } 140 | 141 | /** 142 | * Adds a log record at the ERROR level. 143 | * 144 | * This method allows for compatibility with common interfaces. 145 | * 146 | * @param string $message The log message 147 | * @param array $context The log context 148 | */ 149 | public function addError($message, array $context = []): void 150 | { 151 | $this->parentLogger->error($message, array_merge(['channel' => $this->name], $context)); 152 | } 153 | 154 | /** 155 | * Adds a log record at the CRITICAL level. 156 | * 157 | * This method allows for compatibility with common interfaces. 158 | * 159 | * @param string $message The log message 160 | * @param array $context The log context 161 | */ 162 | public function addCritical($message, array $context = []): void 163 | { 164 | $this->parentLogger->critical($message, array_merge(['channel' => $this->name], $context)); 165 | } 166 | 167 | /** 168 | * Adds a log record at the ALERT level. 169 | * 170 | * This method allows for compatibility with common interfaces. 171 | * 172 | * @param string $message The log message 173 | * @param array $context The log context 174 | */ 175 | public function addAlert($message, array $context = []): void 176 | { 177 | $this->parentLogger->alert($message, array_merge(['channel' => $this->name], $context)); 178 | } 179 | 180 | /** 181 | * Adds a log record at the EMERGENCY level. 182 | * 183 | * This method allows for compatibility with common interfaces. 184 | * 185 | * @param string $message The log message 186 | * @param array $context The log context 187 | */ 188 | public function addEmergency($message, array $context = []): void 189 | { 190 | $this->parentLogger->emergency($message, array_merge(['channel' => $this->name], $context)); 191 | } 192 | 193 | /** 194 | * {@inheritdoc} 195 | * 196 | * @return bool Whether the record has been processed 197 | */ 198 | public function isHandling(int $level): bool 199 | { 200 | return $this->parentLogger->isHandling($level); 201 | } 202 | 203 | /** 204 | * {@inheritdoc} 205 | */ 206 | public function log($level, $message, array $context = []): void 207 | { 208 | $this->parentLogger->log($level, $message, array_merge(['channel' => $this->name], $context)); 209 | } 210 | 211 | /** 212 | * {@inheritdoc} 213 | */ 214 | public function debug($message, array $context = []): void 215 | { 216 | $this->parentLogger->debug($message, array_merge(['channel' => $this->name], $context)); 217 | } 218 | 219 | /** 220 | * {@inheritdoc} 221 | */ 222 | public function info($message, array $context = []): void 223 | { 224 | $this->parentLogger->info($message, array_merge(['channel' => $this->name], $context)); 225 | } 226 | 227 | /** 228 | * {@inheritdoc} 229 | */ 230 | public function notice($message, array $context = []): void 231 | { 232 | $this->parentLogger->notice($message, array_merge(['channel' => $this->name], $context)); 233 | } 234 | 235 | /** 236 | * Adds a log record at the WARNING level. 237 | * 238 | * This method allows for compatibility with common interfaces. 239 | * 240 | * @param string $message The log message 241 | * @param array $context The log context 242 | */ 243 | public function warn($message, array $context = []): void 244 | { 245 | $this->parentLogger->warning($message, array_merge(['channel' => $this->name], $context)); 246 | } 247 | 248 | /** 249 | * {@inheritdoc} 250 | */ 251 | public function warning($message, array $context = []): void 252 | { 253 | $this->parentLogger->warning($message, array_merge(['channel' => $this->name], $context)); 254 | } 255 | 256 | /** 257 | * Adds a log record at the ERROR level. 258 | * 259 | * This method allows for compatibility with common interfaces. 260 | * 261 | * @param string $message The log message 262 | * @param array $context The log context 263 | */ 264 | public function err($message, array $context = []): void 265 | { 266 | $this->parentLogger->error($message, array_merge(['channel' => $this->name], $context)); 267 | } 268 | 269 | /** 270 | * {@inheritdoc} 271 | */ 272 | public function error($message, array $context = []): void 273 | { 274 | $this->parentLogger->error($message, array_merge(['channel' => $this->name], $context)); 275 | } 276 | 277 | /** 278 | * Adds a log record at the CRITICAL level. 279 | * 280 | * This method allows for compatibility with common interfaces. 281 | * 282 | * @param string $message The log message 283 | * @param array $context The log context 284 | */ 285 | public function crit($message, array $context = []): void 286 | { 287 | $this->parentLogger->critical($message, array_merge(['channel' => $this->name], $context)); 288 | } 289 | 290 | /** 291 | * {@inheritdoc} 292 | */ 293 | public function critical($message, array $context = []): void 294 | { 295 | $this->parentLogger->critical($message, array_merge(['channel' => $this->name], $context)); 296 | } 297 | 298 | /** 299 | * {@inheritdoc} 300 | */ 301 | public function alert($message, array $context = []): void 302 | { 303 | $this->parentLogger->alert($message, array_merge(['channel' => $this->name], $context)); 304 | } 305 | 306 | /** 307 | * Adds a log record at the EMERGENCY level. 308 | * 309 | * This method allows for compatibility with common interfaces. 310 | * 311 | * @param string $message The log message 312 | * @param array $context The log context 313 | */ 314 | public function emerg($message, array $context = []): void 315 | { 316 | $this->parentLogger->emergency($message, array_merge(['channel' => $this->name], $context)); 317 | } 318 | 319 | /** 320 | * {@inheritdoc} 321 | */ 322 | public function emergency($message, array $context = []): void 323 | { 324 | $this->parentLogger->emergency($message, array_merge(['channel' => $this->name], $context)); 325 | } 326 | 327 | } 328 | --------------------------------------------------------------------------------