From a069df6af1c57caf46e20b41581654169981bfc4 Mon Sep 17 00:00:00 2001 From: alexmerlin Date: Mon, 26 Apr 2021 09:16:04 +0300 Subject: [PATCH] Issue #11: Parse date format specifiers in FileWriter's stream option. --- README.md | 53 +++++++++------ src/Factory/LoggerAbstractServiceFactory.php | 70 +++++++++++++------- 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index ae15942..e5bca0d 100644 --- a/README.md +++ b/README.md @@ -6,24 +6,24 @@ DotKernel log component extending and customizing * Enter config/config.php * If there is no entry for the config provider below, add it: `\Dot\Log\ConfigProvider::class` -* Make sure it is added before with the Application-Specific components, eg.: \Frontend\App\ConfigProvider.php,  `\Admin\App\ConfigProvider::class`,  `MyProject\ConfigProvider::class` , etc. +* Make sure it is added before with the Application-Specific components, eg.: \Frontend\App\ConfigProvider.php, `\Admin\App\ConfigProvider::class`, `MyProject\ConfigProvider::class` , etc. * Open the `Dot\Log\ConfigProvider` -* In the dependencies section you will see an absctract factory (LoggerAbstractServiceFactory::class) +* In the dependencies section you will see an absctract factory (LoggerAbstractServiceFactory::class) * This class responds to "selectors" instead of class names - - Instead of requesting the `Laminas\Log\Logger::class` from the container, dot-log.my_logger should be requested (or just `my_logger` if using laminas-log) + - Instead of requesting the `Laminas\Log\Logger::class`from the container, dot-log.my_logger should be requested (or just `my_logger` if using laminas-log) ## Configuring the writer(s) Loggers must have at least one writer. -A writer is an object that inherits from `Laminas\Log\Writer\AbstractWriter`. A writer's responsibility is to record log data to a storage backend. (from laminas-log's writer documentation) +A writer is an object that inherits from `Laminas\Log\Writer\AbstractWriter`. A writer's responsibility is to record log data to a storage backend. (from laminas-log's writer documentation) ### Writing to a file (stream) -It is possible separate logs into multiple files using writers and filters.  -For example *warnings.log*, *errors.log*, *all_messages.log*. +It is possible separate logs into multiple files using writers and filters. +For example *warnings.log*, *errors.log*, *all_messages.log*. -The following is the simplest example to write all log messages to `/data/logs/dk.log` +The following is the simplest example to write all log messages to `/log/dk.log` ```php return [ 'dot_log' => [ @@ -34,7 +34,7 @@ return [ 'name' => 'FileWriter', 'priority' => \Laminas\Log\Logger::ALERT, // this is equal to 1 'options' => [ - 'stream' => __DIR__.'/../../data/logs/dk.log', + 'stream' => __DIR__ . '/../../log/dk.log', ], ], ], @@ -43,9 +43,10 @@ return [ ], ]; ``` -* The `FileWriter` key is optional, otherwise the writers array would be enumerative instead of associative. +* The `FileWriter` key is optional, otherwise the writers array would be enumerative instead of associative. * The writer name key is a developer-provided name for that writer, the writer name key is **mandatory**. + The writer priority key is not affecting the errors that are written, it is a way to organize writers, for example: 1 - FILE @@ -55,22 +56,32 @@ It is the most important to write in the file, the sql or e-mail are more probab The writer priority key is optional. -To write into a file the key stream must be present in the writer options array. This is required only if writing into streams/files. +To write into a file the key stream must be present in the writer options array. This is required only if writing into streams/files. + + +## Grouping log files by date +By default, logs will be written to the same file: `log/dk.log`. +Optionally, you can use date format specifiers wrapped between curly braces in your FileWriter's `stream` option, automatically grouping your logs by day, week, month, year etc. +Examples: +* `log/dk-{Y}-{m}-{d}.log` will write every day to a different file (eg: log/dk-2021-01-01.log) +* `log/dk-{Y}-{W}.log` will write every week to a different file (eg: log/dk-2021-10.log) + +The full list of format specifiers is available [here](https://www.php.net/manual/en/datetime.format.php). ## Filtering log messages As per PSR-3 document. -The log levels are: emergency (0), alert (1), critical (2), error (3), warn (4), notice (5), info (6), debug (7) (in order of priority/importance) +The log levels are: emergency (0), alert (1), critical (2), error (3), warn (4), notice (5), info (6), debug (7) (in order of priority/importance) Although the plain Logger in Laminas Log is not fully compatible with PSR-3, it provides a way to log all of these message types. The following example has three file writers using filters: -* First Example: `FileWriter` - All messages are logged in `/data/logs/dk.log` -* Second Example: `OnlyWarningsWriter` - Only warnings are logged in `/data/logs/warnings.log` -* Third Example: `WarningOrHigherWriter` - All important messages (`warnings` or more critical) are logged in `/data/logs/important_messages.log` +* First Example: `FileWriter` - All messages are logged in `/log/dk.log` +* Second Example: `OnlyWarningsWriter` - Only warnings are logged in `/log/warnings.log` +* Third Example: `WarningOrHigherWriter` - All important messages (`warnings` or more critical) are logged in `/log/important_messages.log` ```php 'FileWriter', 'priority' => \Laminas\Log\Logger::ALERT, 'options' => [ - 'stream' => __DIR__.'/../../data/logs/dk.log', + 'stream' => __DIR__ . '/../../log/dk.log', 'filters' => [ 'allMessages' => [ 'name' => 'priority', @@ -101,7 +112,7 @@ return [ 'name' => 'stream', 'priority' => \Laminas\Log\Logger::ALERT, 'options' => [ - 'stream' => __DIR__.'/../../data/logs/warnings_only.log', + 'stream' => __DIR__ . '/../../log/warnings_only.log', 'filters' => [ 'warningOnly' => [ 'name' => 'priority', @@ -118,7 +129,7 @@ return [ 'name' => 'stream', 'priority' => \Laminas\Log\Logger::ALERT, 'options' => [ - 'stream' => __DIR__.'/../../data/logs/important_messages.log', + 'stream' => __DIR__ . '/../../log/important_messages.log', 'filters' => [ 'importantMessages' => [ 'name' => 'priority', @@ -141,7 +152,7 @@ return [ As in the writer configuration, the developer can optionally use keys for associating the filters with a name. -IMPORTANT NOTE: the operator for more important messages is <=, this is because the number representation is smaller for a more important message type. +IMPORTANT NOTE: the operator for more important messages is <=, this is because the number representation is smaller for a more important message type. The filter added on the first writer is equal to not setting a filter, but it was been added to illustrate how to explicitly allow all messages. @@ -163,7 +174,7 @@ Laminas Log provides String formatting, XML, JSON and FirePHP formatting. The formatter accepts following parameters: -name - the formatter class (it must implement Laminas\Log\Formatter\FormatterInterface) +name - the formatter class (it must implement Laminas\Log\Formatter\FormatterInterface) options - options to pass to the formatter constructor if required @@ -178,7 +189,7 @@ The following formats the message as JSON data: * The log is used through dot-log * The logger name is my_logger -* Writes to file: data/logs/dk.log +* Writes to file: log/dk.log * Explicitly allows all the messages to be written * Formats the messages as JSON @@ -195,7 +206,7 @@ return [ 'name' => 'FileWriter', 'priority' => \Laminas\Log\Logger::ALERT, 'options' => [ - 'stream' => __DIR__.'/../../data/logs/dk.log', + 'stream' => __DIR__ . '/../../log/dk.log', // explicitly log all messages 'filters' => [ 'allMessages' => [ diff --git a/src/Factory/LoggerAbstractServiceFactory.php b/src/Factory/LoggerAbstractServiceFactory.php index 99d055a..33e6622 100644 --- a/src/Factory/LoggerAbstractServiceFactory.php +++ b/src/Factory/LoggerAbstractServiceFactory.php @@ -11,8 +11,17 @@ use Dot\Mail\Service\MailServiceInterface; use Interop\Container\ContainerInterface; +use Laminas\Log\Logger; use Laminas\Log\Writer\Mail; +use function count; +use function date; +use function explode; +use function is_array; +use function is_string; +use function preg_match_all; +use function str_replace; + /** * Class LoggerAbstractServiceFactory * @package Dot\Log @@ -49,7 +58,7 @@ public function canCreate(ContainerInterface $container, $requestedName) * @param ContainerInterface $container * @param string $requestedName * @param array|null $options - * @return object|\Laminas\Log\Logger + * @return object|Logger */ public function __invoke(ContainerInterface $container, $requestedName, array $options = null) { @@ -81,31 +90,46 @@ protected function getConfig(ContainerInterface $services): array */ protected function processConfig(&$config, ContainerInterface $services) { - parent::processConfig($config, $services); - - if (!isset($config['writers'])) { - return; + if (isset($config['writers'])) { + foreach ($config['writers'] as $index => $writerConfig) { + if (!empty($writerConfig['options']['stream'])) { + $config['writers'][$index]['options']['stream'] = self::parseVariables( + $writerConfig['options']['stream'] + ); + } + if (isset($writerConfig['name']) + && in_array($writerConfig['name'], ['mail', Mail::class, 'laminaslogwritermail']) + && isset($writerConfig['options']['mail_service']) + && is_string($writerConfig['options']['mail_service']) + && $services->has($writerConfig['options']['mail_service']) + ) { + /** @var MailServiceInterface $mailService */ + $mailService = $services->get($writerConfig[['options']['mail_service']]); + $mail = $mailService->getMessage(); + $transport = $mailService->getTransport(); + + $config['writers'][$index]['options']['mail'] = $mail; + $config['writers'][$index]['options']['transport'] = $transport; + } + } } - foreach ($config['writers'] as $index => $writerConfig) { - if (isset($writerConfig['name']) - && ('mail' === $writerConfig['name'] - || Mail::class === $writerConfig['name'] - || 'laminaslogwritermail' === $writerConfig['name'] - ) - && isset($writerConfig['options']['mail_service']) - && is_string($writerConfig['options']['mail_service']) - && $services->has($writerConfig['options']['mail_service']) - ) { - /** @var MailServiceInterface $mailService */ - $mailService = $services->get($writerConfig[['options']['mail_service']]); - $mail = $mailService->getMessage(); - $transport = $mailService->getTransport(); - - $config['writers'][$index]['options']['mail'] = $mail; - $config['writers'][$index]['options']['transport'] = $transport; - continue; + parent::processConfig($config, $services); + } + + /** + * @param string $stream + * @return string + */ + private static function parseVariables(string $stream): string + { + preg_match_all('/{([a-z])}/i', $stream, $matches); + if (!empty($matches[1])) { + foreach ($matches[1] as $match) { + $stream = str_replace('{' . $match . '}', date($match), $stream); } } + + return $stream; } }