Skip to content

Commit

Permalink
Merge pull request #12 from dotkernel/feature/group-by-date
Browse files Browse the repository at this point in the history
Issue #11: Parse date format specifiers in FileWriter's stream option.
  • Loading branch information
arhimede committed Apr 26, 2021
2 parents 82ec910 + a069df6 commit 24bd1a2
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 44 deletions.
53 changes: 32 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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' => [
Expand All @@ -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',
],
],
],
Expand All @@ -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
Expand All @@ -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
<?php
Expand All @@ -84,7 +95,7 @@ return [
'name' => 'FileWriter',
'priority' => \Laminas\Log\Logger::ALERT,
'options' => [
'stream' => __DIR__.'/../../data/logs/dk.log',
'stream' => __DIR__ . '/../../log/dk.log',
'filters' => [
'allMessages' => [
'name' => 'priority',
Expand All @@ -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',
Expand All @@ -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',
Expand All @@ -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.

Expand All @@ -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


Expand All @@ -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

Expand All @@ -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' => [
Expand Down
70 changes: 47 additions & 23 deletions src/Factory/LoggerAbstractServiceFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
}
}

0 comments on commit 24bd1a2

Please sign in to comment.