diff --git a/src/LoggerFactory.php b/src/LoggerFactory.php index e2eb599..7a9686d 100644 --- a/src/LoggerFactory.php +++ b/src/LoggerFactory.php @@ -34,6 +34,9 @@ use RuntimeException; class LoggerFactory { + use SpecialHandlersTrait; + use ProcessorsTrait; + public const SPECIAL_HANDLERS_BUFFERING = 'buffering'; public const SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY = 'logOnErrorOnly'; public const SPECIAL_HANDLERS_MAKE_UNIQUE = 'makeUnique'; @@ -156,104 +159,4 @@ class LoggerFactory return OX_BASE_PATH . '/log' . DIRECTORY_SEPARATOR . $fileName; } - - /** - * @param AbstractProcessingHandler $handler - * @param array> $specialHandlerFlags - * @return HandlerInterface - */ - public function applySpecialHandlers( - AbstractProcessingHandler $handler, - array $specialHandlerFlags = [] - ): HandlerInterface { - if (in_array(self::SPECIAL_HANDLERS_BUFFERING, $specialHandlerFlags, true)) { - $handler = $this->setBuffering($handler); - } elseif (in_array(self::SPECIAL_HANDLERS_BUFFERING, array_keys($specialHandlerFlags), true)) { - $options = $specialHandlerFlags[self::SPECIAL_HANDLERS_BUFFERING]; - $handler = $this->setBuffering( - $handler, - /** @phpstan-ignore argument.type */ - $options[self::BUFFERING_OPTION_LIMIT] ?? 0, - /** @phpstan-ignore argument.type */ - $options[self::BUFFERING_OPTION_LEVEL] ?? Logger::DEBUG - ); - } - - if (in_array(self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY, $specialHandlerFlags, true)) { - $handler = $this->setLogItemsOnErrorOnly($handler); - } elseif (in_array(self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY, array_keys($specialHandlerFlags), true)) { - $options = $specialHandlerFlags[self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY]; - $handler = $this->setLogItemsOnErrorOnly( - $handler, - /** @phpstan-ignore argument.type */ - $options[self::LOGONERRORONLY_LEVEL] ?? Logger::ERROR - ); - } - - if (in_array(self::SPECIAL_HANDLERS_MAKE_UNIQUE, $specialHandlerFlags, true)) { - /** @phpstan-ignore argument.type */ - $handler = $this->makeUnique($handler); - } elseif (in_array(self::SPECIAL_HANDLERS_MAKE_UNIQUE, array_keys($specialHandlerFlags), true)) { - $options = $specialHandlerFlags[self::SPECIAL_HANDLERS_MAKE_UNIQUE]; - $handler = $this->makeUnique( - /** @phpstan-ignore argument.type */ - $handler, - /** @phpstan-ignore argument.type */ - $options[self::MAKEUNIQUE_OPTION_LEVEL] ?? Logger::ERROR, - /** @phpstan-ignore argument.type */ - $options[self::MAKEUNIQUE_OPTION_TIME] ?? 60 - ); - } - - return $handler; - } - - public function setBuffering( - AbstractHandler $handler, - int $bufferLimit = 0, - int $loglevel = Logger::DEBUG - ): BufferHandler { - /** @phpstan-ignore argument.type */ - return new BufferHandler($handler, $bufferLimit, $loglevel); - } - - public function setLogItemsOnErrorOnly( - AbstractHandler $handler, - int $activationLevel = Logger::ERROR - ): FingersCrossedHandler { - return new FingersCrossedHandler( - $handler, - /** @phpstan-ignore argument.type */ - new ErrorLevelActivationStrategy($activationLevel) - ); - } - - public function makeUnique( - AbstractHandler $handler, - int $deduplicationLevel = Logger::ERROR, - int $time = 60 - ): DeduplicationHandler { - /** @phpstan-ignore argument.type */ - return new DeduplicationHandler($handler, null, $deduplicationLevel, $time); - } - - public function applyProcessors($logger, array $processorFlags): Logger - { - if (in_array(self::PROCESSOR_UNIQUE_ID, $processorFlags, true)) { - $logger->pushProcessor(new UidProcessor()); - } - - if (in_array(self::PROCESSOR_FILTERSENSITIVE, array_keys($processorFlags), true)) { - $options = $processorFlags[self::PROCESSOR_FILTERSENSITIVE] ?? []; - $searchList = $options[self::FILTERSENSITIVE_SECRETS] ?? []; - - if (!is_array($searchList)) { - throw new RuntimeException('sensitive list must be an array'); - } - - $logger->pushProcessor(new SensitiveFilterProcessor($searchList)); - } - - return $logger; - } } \ No newline at end of file diff --git a/src/ProcessorsTrait.php b/src/ProcessorsTrait.php new file mode 100644 index 0000000..09ffb5c --- /dev/null +++ b/src/ProcessorsTrait.php @@ -0,0 +1,64 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\LoggerFactory; + +use Monolog\Logger; +use Monolog\Processor\UidProcessor; +use RuntimeException; + +trait ProcessorsTrait +{ + public function applyProcessors($logger, array $processorFlags): Logger + { + $this->applyUidProcessor($processorFlags, $logger); + $this->applyFilterSensitiveProcessor($processorFlags, $logger); + + return $logger; + } + + /** + * @param array $processorFlags + * @param $logger + * @return void + */ + protected function applyUidProcessor(array $processorFlags, $logger): void + { + if (in_array(self::PROCESSOR_UNIQUE_ID, $processorFlags, true)) { + $logger->pushProcessor(new UidProcessor()); + } + } + + /** + * @param array $processorFlags + * @param $logger + * @return void + */ + protected function applyFilterSensitiveProcessor(array $processorFlags, $logger): void + { + if (in_array(self::PROCESSOR_FILTERSENSITIVE, array_keys($processorFlags), true)) { + $options = $processorFlags[self::PROCESSOR_FILTERSENSITIVE] ?? []; + $searchList = $options[self::FILTERSENSITIVE_SECRETS] ?? []; + + if (!is_array($searchList)) { + throw new RuntimeException('sensitive list must be an array'); + } + + $logger->pushProcessor(new SensitiveFilterProcessor($searchList)); + } + } +} \ No newline at end of file diff --git a/src/SensitiveFilterProcessor.php b/src/SensitiveFilterProcessor.php new file mode 100644 index 0000000..8c144dd --- /dev/null +++ b/src/SensitiveFilterProcessor.php @@ -0,0 +1,69 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\LoggerFactory; + +use Monolog\Processor\ProcessorInterface; + +class SensitiveFilterProcessor implements ProcessorInterface +{ + public function __construct(protected array $secrets, protected ?string $replacement = null) + { + $this->replacement ??= '*****'; + $this->convertStringsToRegex($this->secrets); + } + + protected function convertStringsToRegex(array $search = []): void + { + array_map( + function ($search) use (&$searchStrings) { + if (!$this->stringIsRegexp($search)) { + $searchStrings[] = '/'.preg_quote($search, '/').'/i'; + if (urlencode($search) !== $search) { + $searchStrings[] = '/' . preg_quote(urlencode($search), '/') . '/i'; + } + } else { + $searchStrings[] = $search; + } + }, + $search + ); + + $this->secrets = $searchStrings; + } + + protected function stringIsRegexp(string $string): bool + { + return @preg_match($string, '') !== false; + } + + public function __invoke(array $records): array + { + foreach ($records as $key => $item) { + if (is_string($item)) { + $records[$key] = preg_replace( + $this->secrets, + $this->replacement, + $item + ); + } elseif (is_array($item)) { + $records[$key] = $this($item); + } + } + return $records; + } +} \ No newline at end of file diff --git a/src/SpecialHandlersTrait.php b/src/SpecialHandlersTrait.php new file mode 100644 index 0000000..d869990 --- /dev/null +++ b/src/SpecialHandlersTrait.php @@ -0,0 +1,139 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\LoggerFactory; + +use Monolog\Handler\AbstractHandler; +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Handler\BufferHandler; +use Monolog\Handler\DeduplicationHandler; +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossedHandler; +use Monolog\Handler\HandlerInterface; +use Monolog\Logger; + +trait SpecialHandlersTrait +{ + /** + * @param AbstractProcessingHandler $handler + * @param array> $specialHandlerFlags + * @return HandlerInterface + */ + public function applySpecialHandlers( + AbstractProcessingHandler $handler, + array $specialHandlerFlags = [] + ): HandlerInterface { + $handler = $this->applyBufferHandler($specialHandlerFlags, $handler); + $handler = $this->applyLogOnErrorOnlyHandler($specialHandlerFlags, $handler); + return $this->applyMakeUniqueHandler($specialHandlerFlags, $handler); + } + + public function setBuffering( + AbstractHandler $handler, + int $bufferLimit = 0, + int $loglevel = Logger::DEBUG + ): BufferHandler { + /** @phpstan-ignore argument.type */ + return new BufferHandler($handler, $bufferLimit, $loglevel); + } + + public function setLogItemsOnErrorOnly( + AbstractHandler $handler, + int $activationLevel = Logger::ERROR + ): FingersCrossedHandler { + return new FingersCrossedHandler( + $handler, + /** @phpstan-ignore argument.type */ + new ErrorLevelActivationStrategy($activationLevel) + ); + } + + public function makeUnique( + AbstractHandler $handler, + int $deduplicationLevel = Logger::ERROR, + int $time = 60 + ): DeduplicationHandler { + /** @phpstan-ignore argument.type */ + return new DeduplicationHandler($handler, null, $deduplicationLevel, $time); + } + + /** + * @param array $specialHandlerFlags + * @param AbstractHandler $handler + * @return AbstractHandler + */ + protected function applyBufferHandler(array $specialHandlerFlags, AbstractHandler $handler): AbstractHandler + { + if (in_array(self::SPECIAL_HANDLERS_BUFFERING, $specialHandlerFlags, true)) { + $handler = $this->setBuffering($handler); + } elseif (in_array(self::SPECIAL_HANDLERS_BUFFERING, array_keys($specialHandlerFlags), true)) { + $options = $specialHandlerFlags[self::SPECIAL_HANDLERS_BUFFERING]; + $handler = $this->setBuffering( + $handler, + /** @phpstan-ignore argument.type */ + $options[self::BUFFERING_OPTION_LIMIT] ?? 0, + /** @phpstan-ignore argument.type */ + $options[self::BUFFERING_OPTION_LEVEL] ?? Logger::DEBUG + ); + } + return $handler; + } + + /** + * @param array $specialHandlerFlags + * @param AbstractHandler $handler + * @return AbstractHandler + */ + protected function applyLogOnErrorOnlyHandler(array $specialHandlerFlags, AbstractHandler $handler): AbstractHandler + { + if (in_array(self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY, $specialHandlerFlags, true)) { + $handler = $this->setLogItemsOnErrorOnly($handler); + } elseif (in_array(self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY, array_keys($specialHandlerFlags), true)) { + $options = $specialHandlerFlags[self::SPECIAL_HANDLERS_LOG_ON_ERROR_ONLY]; + $handler = $this->setLogItemsOnErrorOnly( + $handler, + /** @phpstan-ignore argument.type */ + $options[self::LOGONERRORONLY_LEVEL] ?? Logger::ERROR + ); + } + return $handler; + } + + /** + * @param array $specialHandlerFlags + * @param AbstractHandler $handler + * @return AbstractHandler + */ + protected function applyMakeUniqueHandler(array $specialHandlerFlags, AbstractHandler $handler): AbstractHandler + { + if (in_array(self::SPECIAL_HANDLERS_MAKE_UNIQUE, $specialHandlerFlags, true)) { + /** @phpstan-ignore argument.type */ + $handler = $this->makeUnique($handler); + } elseif (in_array(self::SPECIAL_HANDLERS_MAKE_UNIQUE, array_keys($specialHandlerFlags), true)) { + $options = $specialHandlerFlags[self::SPECIAL_HANDLERS_MAKE_UNIQUE]; + $handler = $this->makeUnique( + /** @phpstan-ignore argument.type */ + $handler, + /** @phpstan-ignore argument.type */ + $options[self::MAKEUNIQUE_OPTION_LEVEL] ?? Logger::ERROR, + /** @phpstan-ignore argument.type */ + $options[self::MAKEUNIQUE_OPTION_TIME] ?? 60 + ); + } + return $handler; + } +} \ No newline at end of file