diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 190b4da..c0fbc53 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -15,9 +15,10 @@ jobs: - ubuntu-latest php: - - "8.1" - "8.2" - "8.3" + - "8.4" + - "8.5" steps: - name: Checkout diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml deleted file mode 100644 index 1a7aa24..0000000 --- a/.github/workflows/docs-build.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: docs-build - -on: - release: - types: [published] - workflow_dispatch: - -jobs: - build-deploy: - runs-on: ubuntu-latest - steps: - - name: Build Docs - uses: dotkernel/documentation-theme/github-actions/docs@main - env: - DEPLOY_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..9976515 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,50 @@ +on: + - push + +name: Run PHPStan checks + +jobs: + mutation: + name: PHPStan ${{ matrix.php }}-${{ matrix.os }} + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: + - ubuntu-latest + + php: + - "8.2" + - "8.3" + - "8.4" + - "8.5" + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: pcov + ini-values: assert.exception=1, zend.assertions=1, error_reporting=-1, log_errors_max_len=0, display_errors=On + tools: composer:v2, cs2pr + + - name: Determine composer cache directory + run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV + + - name: Cache dependencies installed with composer + uses: actions/cache@v4 + with: + path: ${{ env.COMPOSER_CACHE_DIR }} + key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: | + php${{ matrix.php }}-composer- + + - name: Install dependencies with composer + run: composer install --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run static analysis with PHPStan + run: vendor/bin/phpstan analyse diff --git a/.laminas-ci.json b/.laminas-ci.json new file mode 100644 index 0000000..6d191ca --- /dev/null +++ b/.laminas-ci.json @@ -0,0 +1,5 @@ +{ + "ignore_php_platform_requirements": { + "8.5": true + } +} diff --git a/README.md b/README.md index 2b29e05..8ce9fbe 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # dot-log ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-log) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-log/4.0.0) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-log/4.0.5) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-log)](https://github.com/dotkernel/dot-log/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-log)](https://github.com/dotkernel/dot-log/network) @@ -10,8 +10,7 @@ [![Build Static](https://github.com/dotkernel/dot-log/actions/workflows/continuous-integration.yml/badge.svg?branch=4.0)](https://github.com/dotkernel/dot-log/actions/workflows/continuous-integration.yml) [![codecov](https://codecov.io/gh/dotkernel/dot-log/graph/badge.svg?token=JX19KTBRCZ)](https://codecov.io/gh/dotkernel/dot-log) - -[![SymfonyInsight](https://insight.symfony.com/projects/287e81e8-b4fb-4452-bd8f-4f12c0ab1f76/big.svg)](https://insight.symfony.com/projects/287e81e8-b4fb-4452-bd8f-4f12c0ab1f76) +[![PHPStan](https://github.com/dotkernel/dot-log/actions/workflows/static-analysis.yml/badge.svg?branch=4.0)](https://github.com/dotkernel/dot-log/actions/workflows/static-analysis.yml) ## Adding The Config Provider @@ -75,7 +74,7 @@ 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). +The full list of format specifiers is available [on php.net in the `DateTimeInterface::format` documentation](https://www.php.net/manual/en/datetime.format.php). ## Filtering log messages diff --git a/SECURITY.md b/SECURITY.md index 8446926..b91807b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,10 +3,10 @@ ## Supported Versions -| Version | Supported | PHP Version | -|---------|--------------------|----------------------------------------------------------------------------------------------------| -| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-log/3.4.7) | -| <= 2.x | :x: | | +| Version | Supported | PHP Version | +|---------|--------------------|---------------------------------------------------------------------------------------------------------| +| 4.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-log/4.0.5) | +| <= 3.x | :x: | | ## Reporting Potential Security Issues diff --git a/composer.json b/composer.json index 200ae39..155e8ac 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "dotkernel/dot-log", "type": "library", - "description": "DotKernel log component extending and customizing laminas-log", + "description": "Dotkernel log component extending and customizing laminas-log", "license": "MIT", "homepage": "https://github.com/dotkernel/dot-log", "keywords": [ @@ -14,19 +14,20 @@ ], "authors": [ { - "name": "DotKernel Team", + "name": "Dotkernel Team", "email": "team@dotkernel.com" } ], "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "laminas/laminas-servicemanager": "^3.22", "laminas/laminas-validator": "^2.64" }, "require-dev": { - "phpunit/phpunit": "^10.2", - "laminas/laminas-coding-standard": "^2.5", - "vimeo/psalm": "^5.13" + "laminas/laminas-coding-standard": "^3.0", + "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpunit/phpunit": "^10.2" }, "autoload": { "psr-4": { @@ -48,12 +49,12 @@ "scripts": { "check": [ "@cs-check", - "@test" + "@test", + "@static-analysis" ], "cs-check": "phpcs", "cs-fix": "phpcbf", - "test": "phpunit --colors=always", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", - "static-analysis": "psalm --shepherd --stats" + "static-analysis": "phpstan analyse --memory-limit 1G", + "test": "phpunit --colors=always" } } diff --git a/docs/book/index.md b/docs/book/index.md deleted file mode 120000 index fe84005..0000000 --- a/docs/book/index.md +++ /dev/null @@ -1 +0,0 @@ -../../README.md \ No newline at end of file diff --git a/docs/book/v3/adding-config-provider.md b/docs/book/v3/adding-config-provider.md deleted file mode 100644 index e591844..0000000 --- a/docs/book/v3/adding-config-provider.md +++ /dev/null @@ -1,10 +0,0 @@ -# Adding The Config Provider - -* In `config/config.php` add an entry for the config provider `\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. -* Add the logger configuration in an autoload config file, e.g. you can create `config/autoload/logger.global.php`. Follow the `Configuring the writer(s)` chapter for a simple working example. - -Note: `Dot\Log\ConfigProvider` has an abstract factory `LoggerAbstractServiceFactory::class` which corresponds to the alias, not the class name. Instead of requesting `Laminas\Log\Logger::class` from the container, use `dot-log.my_logger` (or just `my_logger` if using laminas-log). diff --git a/docs/book/v3/configuring-writer.md b/docs/book/v3/configuring-writer.md deleted file mode 100644 index a9ed716..0000000 --- a/docs/book/v3/configuring-writer.md +++ /dev/null @@ -1,46 +0,0 @@ -# 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) - -## Writing to a file (stream) - -You can 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 `/log/dk.log` - -```php -return [ - 'dot_log' => [ - 'loggers' => [ - 'my_logger' => [ - 'writers' => [ - 'FileWriter' => [ - 'name' => 'FileWriter', - 'priority' => \Laminas\Log\Logger::ALERT, // this is equal to 1 - 'options' => [ - 'stream' => __DIR__ . '/../../log/dk.log', - ], - ], - ], - ] - ], - ], -]; -``` - -* The `FileWriter` key is optional, otherwise the writers array would be enumerative instead of associative. -* The `name` key is a developer-provided name for that writer, the writer name key is **mandatory**. - -The `priority` key does not affect the errors that are written. It is a way to organize writers, for example: - -* 1 - FILE -* 2 - SQL -* 3 - E-mail - -The most important things to write in the file, the sql or e-mail are usually fails because the servers can be external and offline, but the file is on the same server. - -The `priority` key is optional. - -The key `stream` is required only if writing into streams/files. diff --git a/docs/book/v3/example-with-formatter.md b/docs/book/v3/example-with-formatter.md deleted file mode 100644 index ed8b278..0000000 --- a/docs/book/v3/example-with-formatter.md +++ /dev/null @@ -1,41 +0,0 @@ -# Example with formatter - -* The log is used through `dot-log` -* The logger name is `my_logger` -* It writes to file: `log/dk.log` -* It is configured to explicitly write all messages -* The messages are formatted as JSON - -```php - [ - 'loggers' => [ - 'my_logger' => [ - 'writers' => [ - 'FileWriter' => [ - 'name' => 'FileWriter', - 'priority' => \Laminas\Log\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/dk.log', - // explicitly log all messages - 'filters' => [ - 'allMessages' => [ - 'name' => 'priority', - 'options' => [ - 'operator' => '>=', - 'priority' => \Laminas\Log\Logger::EMERG, - ], - ], - ], - 'formatter' => [ - 'name' => \Laminas\Log\Formatter\Json::class, - ], - ], - ], - ], - ], - ], - ], -]; -``` diff --git a/docs/book/v3/filtering-log-messages.md b/docs/book/v3/filtering-log-messages.md deleted file mode 100644 index 32c185f..0000000 --- a/docs/book/v3/filtering-log-messages.md +++ /dev/null @@ -1,97 +0,0 @@ -# Filtering log messages - -The following conforms to the `PSR-3: Logger Interface` document. - -The log levels are in order of priority/importance: - -* emergency (0) -* alert (1) -* critical (2) -* error (3) -* warning (4) -* notice (5) -* info (6) -* debug (7) - -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 `/log/dk.log` -* Second Example: `OnlyWarningsWriter` - Only warnings are logged in `/log/warnings.log` -* Third Example: `WarningOrHigherWriter` - All important messages (`warnings` or critical) are logged in `/log/important_messages.log` - -```php - [ - 'loggers' => [ - 'my_logger' => [ - 'writers' => [ - 'FileWriter' => [ - 'name' => 'FileWriter', - 'priority' => \Laminas\Log\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/dk.log', - 'filters' => [ - 'allMessages' => [ - 'name' => 'priority', - 'options' => [ - 'operator' => '>=', - 'priority' => \Laminas\Log\Logger::EMERG, - ] - ], - ], - ], - ], - // Only warnings - 'OnlyWarningsWriter' => [ - 'name' => 'stream', - 'priority' => \Laminas\Log\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/warnings_only.log', - 'filters' => [ - 'warningOnly' => [ - 'name' => 'priority', - 'options' => [ - 'operator' => '==', - 'priority' => \Laminas\Log\Logger::WARN, - ], - ], - ], - ], - ], - // Warnings and more important messages - 'WarningOrHigherWriter' => [ - 'name' => 'stream', - 'priority' => \Laminas\Log\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/important_messages.log', - 'filters' => [ - 'importantMessages' => [ - 'name' => 'priority', - 'options' => [ - // note, the smaller the priority, the more important is the message - // 0 - emergency, 1 - alert, 2- error, 3 - warn etc. - 'operator' => '<=', - 'priority' => \Laminas\Log\Logger::WARN, - ], - ], - ], - ], - ], - ], - ], - ], - ], -]; -``` - -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. - -The filter added on the first writer is equivalent to not setting a filter, but it was added to illustrate the usage of the operator to explicitly allow all messages. - -More examples on filters: https://docs.laminas.dev/laminas-log/filters/ diff --git a/docs/book/v3/formatting-messages.md b/docs/book/v3/formatting-messages.md deleted file mode 100644 index c0b028a..0000000 --- a/docs/book/v3/formatting-messages.md +++ /dev/null @@ -1,14 +0,0 @@ -# Formatting Messages - -When using `dot-log` or `laminas-log`, the logged value is not limited to a string. Arrays can be logged as well. For better readability, these arrays can be serialized. Laminas Log provides String, XML, JSON and FirePHP formatting. - -The formatter accepts following parameters: - -* name - the formatter class (it must implement `Laminas\Log\Formatter\FormatterInterface`) -* options - passed to the formatter constructor if required - -The following snippet formats the message as JSON data: - - 'formatter' => [ - 'name' => \Laminas\Log\Formatter\Json::class, - ], diff --git a/docs/book/v3/grouping-log-files-by-date.md b/docs/book/v3/grouping-log-files-by-date.md deleted file mode 100644 index 7437b4e..0000000 --- a/docs/book/v3/grouping-log-files-by-date.md +++ /dev/null @@ -1,12 +0,0 @@ -# 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 to automatically group your logs by day, week, month, year etc. - -Examples: - -* `log/dk-{Y}-{m}-{d}.log` will create a new log file each day (eg: log/dk-2021-01-01.log) -* `log/dk-{Y}-{W}.log` will create a new log file each week (eg: log/dk-2021-10.log) - -The full list of format specifiers is available [here](https://www.php.net/manual/en/datetime.format.php). diff --git a/docs/book/v3/overview.md b/docs/book/v3/overview.md deleted file mode 100644 index 975a06d..0000000 --- a/docs/book/v3/overview.md +++ /dev/null @@ -1,5 +0,0 @@ -# Overview - -> dot-log is a wrapper on top of [laminas-log](https://github.com/laminas/laminas-log) -> -> ![OSS Lifecycle](https://img.shields.io/osslifecycle/laminas/laminas-log) diff --git a/docs/book/v3/usage.md b/docs/book/v3/usage.md deleted file mode 100644 index fea781e..0000000 --- a/docs/book/v3/usage.md +++ /dev/null @@ -1,26 +0,0 @@ -# Usage - -Basic usage of the logger is illustrated below. - -The messages are written to see which logs are written and which are not written. - -```php -use Laminas\Log\Logger; -``` - -... - -```php -$logger = $container->get('dot-log.my_logger'); - -/** @var Logger $logger */ -$logger->emerg('0 EMERG'); -$logger->alert('1 ALERT'); -$logger->crit('2 CRITICAL'); -$logger->err('3 ERR'); -$logger->warn('4 WARN'); -$logger->notice('5 NOTICE'); -$logger->info('6 INF'); -$logger->debug('7 debug'); -$logger->log(Logger::NOTICE, 'NOTICE from log()'); -``` diff --git a/docs/book/v4/adding-config-provider.md b/docs/book/v4/adding-config-provider.md deleted file mode 100644 index 2e3ef2b..0000000 --- a/docs/book/v4/adding-config-provider.md +++ /dev/null @@ -1,10 +0,0 @@ -# Adding The Config Provider - -* In `config/config.php` add an entry for the config provider `\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. -* Add the logger configuration in an autoload config file, e.g. you can create `config/autoload/logger.global.php`. Follow the `Configuring the writer(s)` chapter for a simple working example. - -Note: `Dot\Log\ConfigProvider` has an abstract factory `LoggerAbstractServiceFactory::class` which corresponds to the alias, not the class name. Instead of requesting `Dot\Log\Logger::class` from the container, use `dot-log.my_logger`. diff --git a/docs/book/v4/configuring-writer.md b/docs/book/v4/configuring-writer.md deleted file mode 100644 index cf4b9a3..0000000 --- a/docs/book/v4/configuring-writer.md +++ /dev/null @@ -1,40 +0,0 @@ -# Configuring the writer(s) - -Loggers must have at least one writer. - -A writer is an object that inherits from `Dot\Log\Writer\AbstractWriter`. A writer's responsibility is to record log data to a storage backend. - -## Writing to a file (stream) - -You can 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 `/log/dk.log` - -```php -return [ - 'dot_log' => [ - 'loggers' => [ - 'my_logger' => [ - 'writers' => [ - 'FileWriter' => [ - 'name' => 'FileWriter', - 'priority' => \Dot\Log\Manager\Logger::ALERT, // this is equal to 1 - 'options' => [ - 'stream' => __DIR__ . '/../../log/dk.log', - ], - ], - ], - ] - ], - ], -]; -``` - -* The `FileWriter` key is optional, otherwise the writers array would be enumerative instead of associative. -* The `name` key is a developer-provided name for that writer, the writer name key is **mandatory**. - -The `priority` key does not affect the errors that are written. It is a way to organize writers. - -The `priority` key is optional. - -The key `stream` is required only if writing into streams/files. diff --git a/docs/book/v4/example-with-formatter.md b/docs/book/v4/example-with-formatter.md deleted file mode 100644 index de6460b..0000000 --- a/docs/book/v4/example-with-formatter.md +++ /dev/null @@ -1,41 +0,0 @@ -# Example with formatter - -* The log is used through `dot-log` -* The logger name is `my_logger` -* It writes to file: `log/dk.log` -* It is configured to explicitly write all messages -* The messages are formatted as JSON - -```php - [ - 'loggers' => [ - 'my_logger' => [ - 'writers' => [ - 'FileWriter' => [ - 'name' => 'FileWriter', - 'priority' => \Dot\Log\Manager\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/dk.log', - // explicitly log all messages - 'filters' => [ - 'allMessages' => [ - 'name' => 'priority', - 'options' => [ - 'operator' => '>=', - 'priority' => \Dot\Log\Manager\Logger::EMERG, - ], - ], - ], - 'formatter' => [ - 'name' => \Dot\Log\Manager\Formatter\Json::class, - ], - ], - ], - ], - ], - ], - ], -]; -``` diff --git a/docs/book/v4/filtering-log-messages.md b/docs/book/v4/filtering-log-messages.md deleted file mode 100644 index 914edf4..0000000 --- a/docs/book/v4/filtering-log-messages.md +++ /dev/null @@ -1,95 +0,0 @@ -# Filtering log messages - -The following conforms to the `PSR-3: Logger Interface` document. - -The log levels are in order of priority/importance: - -* emergency (0) -* alert (1) -* critical (2) -* error (3) -* warning (4) -* notice (5) -* info (6) -* debug (7) - -Although the plain Logger in `dot-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 `/log/dk.log` -* Second Example: `OnlyWarningsWriter` - Only warnings are logged in `/log/warnings.log` -* Third Example: `WarningOrHigherWriter` - All important messages (`warnings` or critical) are logged in `/log/important_messages.log` - -```php - [ - 'loggers' => [ - 'my_logger' => [ - 'writers' => [ - 'FileWriter' => [ - 'name' => 'FileWriter', - 'priority' => \Dot\Log\Manager\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/dk.log', - 'filters' => [ - 'allMessages' => [ - 'name' => 'priority', - 'options' => [ - 'operator' => '>=', - 'priority' => \Dot\Log\Manager\Logger::EMERG, - ] - ], - ], - ], - ], - // Only warnings - 'OnlyWarningsWriter' => [ - 'name' => 'stream', - 'priority' => \Dot\Log\Manager\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/warnings_only.log', - 'filters' => [ - 'warningOnly' => [ - 'name' => 'priority', - 'options' => [ - 'operator' => '==', - 'priority' => \Dot\Log\Manager\Logger::WARN, - ], - ], - ], - ], - ], - // Warnings and more important messages - 'WarningOrHigherWriter' => [ - 'name' => 'stream', - 'priority' => \Dot\Log\Manager\Logger::ALERT, - 'options' => [ - 'stream' => __DIR__ . '/../../log/important_messages.log', - 'filters' => [ - 'importantMessages' => [ - 'name' => 'priority', - 'options' => [ - // note, the smaller the priority, the more important is the message - // 0 - emergency, 1 - alert, 2- error, 3 - warn etc. - 'operator' => '<=', - 'priority' => \Dot\Log\Manager\Logger::WARN, - ], - ], - ], - ], - ], - ], - ], - ], - ], -]; -``` - -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. - -The filter added on the first writer is equivalent to not setting a filter, but it was added to illustrate the usage of the operator to explicitly allow all messages. diff --git a/docs/book/v4/formatting-messages.md b/docs/book/v4/formatting-messages.md deleted file mode 100644 index 4604825..0000000 --- a/docs/book/v4/formatting-messages.md +++ /dev/null @@ -1,14 +0,0 @@ -# Formatting Messages - -When using `dot-log`, the logged value is not limited to a string. Arrays can be logged as well. For better readability, these arrays can be serialized. Dot Log provides String and JSON formatting. - -The formatter accepts following parameters: - -* name - the formatter class (it must implement `Dot\Log\Formatter\FormatterInterface`) -* options - passed to the formatter constructor if required - -The following snippet formats the message as JSON data: - - 'formatter' => [ - 'name' => \Dot\Log\Formatter\Json::class, - ], diff --git a/docs/book/v4/grouping-log-files-by-date.md b/docs/book/v4/grouping-log-files-by-date.md deleted file mode 100644 index 7437b4e..0000000 --- a/docs/book/v4/grouping-log-files-by-date.md +++ /dev/null @@ -1,12 +0,0 @@ -# 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 to automatically group your logs by day, week, month, year etc. - -Examples: - -* `log/dk-{Y}-{m}-{d}.log` will create a new log file each day (eg: log/dk-2021-01-01.log) -* `log/dk-{Y}-{W}.log` will create a new log file each week (eg: log/dk-2021-10.log) - -The full list of format specifiers is available [here](https://www.php.net/manual/en/datetime.format.php). diff --git a/docs/book/v4/overview.md b/docs/book/v4/overview.md deleted file mode 100644 index 89f6888..0000000 --- a/docs/book/v4/overview.md +++ /dev/null @@ -1,3 +0,0 @@ -# Overview - -Robust, composite logger with filtering, formatting, and PSR-3 support. diff --git a/docs/book/v4/usage.md b/docs/book/v4/usage.md deleted file mode 100644 index e479b9c..0000000 --- a/docs/book/v4/usage.md +++ /dev/null @@ -1,26 +0,0 @@ -# Usage - -Basic usage of the logger is illustrated below. - -The messages are written to see which logs are written and which are not written. - -```php -use Dot\Log\Manager\Logger; -``` - -... - -```php -$logger = $container->get('dot-log.my_logger'); - -/** @var Logger $logger */ -$logger->emerg('0 EMERG'); -$logger->alert('1 ALERT'); -$logger->crit('2 CRITICAL'); -$logger->err('3 ERR'); -$logger->warn('4 WARN'); -$logger->notice('5 NOTICE'); -$logger->info('6 INF'); -$logger->debug('7 debug'); -$logger->log(Logger::NOTICE, 'NOTICE from log()'); -``` diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..dc25f26 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,18 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon +parameters: + level: 5 + paths: + - src + - test + treatPhpDocTypesAsCertain: false + ignoreErrors: + - + message: '/Undefined variable: \$test/' + path: test/LoggerTest.php + - + message: '/Instanceof between Dot\\Log\\Formatter\\FormatterInterface and Dot\\Log\\Formatter\\FormatterInterface will always evaluate to true./' + path: src/Writer/AbstractWriter.php + - + message: '/Property Dot\\Log\\Factory\\WriterFactory::\$creationOptions is never read, only written./' + path: src/Factory/WriterFactory.php diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 7272b57..0000000 --- a/psalm.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - diff --git a/src/Factory/WriterFactory.php b/src/Factory/WriterFactory.php index a3421e3..4f11cfa 100644 --- a/src/Factory/WriterFactory.php +++ b/src/Factory/WriterFactory.php @@ -13,14 +13,14 @@ class WriterFactory implements FactoryInterface { /** - * Options to pass to the constructor if any. + * Options to pass to the constructor, if any. */ - private null|array $creationOptions; + private ?array $creationOptions = null; - public function __construct(?array $creationOptions = null) + public function __construct(?array $options = null) { - if (is_array($creationOptions)) { - $this->setCreationOptions($creationOptions); + if (is_array($options)) { + $this->setCreationOptions($options); } } diff --git a/src/Formatter/Json.php b/src/Formatter/Json.php index 8afb4b1..9713567 100644 --- a/src/Formatter/Json.php +++ b/src/Formatter/Json.php @@ -33,7 +33,7 @@ public function format(iterable $event): string $event['timestamp'] = $event['timestamp']->format($this->getDateTimeFormat()); } - return json_encode( + return (string) json_encode( $event, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION ); diff --git a/src/Logger.php b/src/Logger.php index 59cc991..f490074 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -13,7 +13,6 @@ use Dot\Log\Writer\WriterInterface; use ErrorException; use Exception; -use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\ServiceManager; use Laminas\Stdlib\ArrayUtils; use Laminas\Stdlib\SplPriorityQueue; @@ -45,7 +44,6 @@ use const E_NOTICE; use const E_PARSE; use const E_RECOVERABLE_ERROR; -use const E_STRICT; use const E_USER_DEPRECATED; use const E_USER_ERROR; use const E_USER_NOTICE; @@ -84,7 +82,6 @@ class Logger implements LoggerInterface E_PARSE => self::ERR, E_COMPILE_ERROR => self::ERR, E_COMPILE_WARNING => self::ERR, - E_STRICT => self::DEBUG, E_DEPRECATED => self::DEBUG, E_USER_DEPRECATED => self::DEBUG, ]; @@ -150,7 +147,7 @@ public function __construct(?iterable $options = null) // Inject writer plugin manager, if available if ( isset($options['writer_plugin_manager']) - && $options['writer_plugin_manager'] instanceof AbstractPluginManager + && $options['writer_plugin_manager'] instanceof WriterPluginManager ) { $this->setWriterPluginManager($options['writer_plugin_manager']); } @@ -158,7 +155,10 @@ public function __construct(?iterable $options = null) // Inject processor plugin manager, if available if ( isset($options['processor_plugin_manager']) - && $options['processor_plugin_manager'] instanceof AbstractPluginManager + && ( + $options['processor_plugin_manager'] instanceof ProcessorPluginManager + || is_string($options['processor_plugin_manager']) + ) ) { $this->setProcessorPluginManager($options['processor_plugin_manager']); } @@ -246,12 +246,6 @@ public function addWriter(WriterInterface|string $writer, int $priority = 1, ?ar { if (is_string($writer)) { $writer = $this->writerPlugin($writer, $options); - } elseif (! $writer instanceof Writer\WriterInterface) { - throw new InvalidArgumentException(sprintf( - 'Writer must implement %s\Writer\WriterInterface; received "%s"', - __NAMESPACE__, - $writer::class - )); } $this->writers->insert($writer, $priority); @@ -289,13 +283,6 @@ public function setProcessorPluginManager(string|ProcessorPluginManager $plugins if (is_string($plugins)) { $plugins = new $plugins(); } - if (! $plugins instanceof ProcessorPluginManager) { - throw new InvalidArgumentException(sprintf( - 'processor plugin manager must extend %s\ProcessorPluginManager; received %s', - __NAMESPACE__, - $plugins::class - )); - } $this->processorPlugins = $plugins; return $this; @@ -316,11 +303,6 @@ public function addProcessor( ): static { if (is_string($processor)) { $processor = $this->processorPlugin($processor, $options); - } elseif (! $processor instanceof Processor\ProcessorInterface) { - throw new InvalidArgumentException(sprintf( - 'Processor must implement Laminas\Log\ProcessorInterface; received "%s"', - $processor::class - )); } $this->processors->insert($processor, $priority); @@ -347,12 +329,8 @@ public function log(int $priority, mixed $message, iterable $extra = []): static ); } - if (! is_array($extra) && ! $extra instanceof Traversable) { - throw new InvalidArgumentException( - '$extra must be an array or implement Traversable' - ); - } elseif ($extra instanceof Traversable) { - $extra = ArrayUtils::iteratorToArray($extra); + if ($extra instanceof Traversable) { + $extra = ArrayUtils::iteratorToArray((array) $extra); } if ($this->writers->count() === 0) { diff --git a/src/Manager/FilterPluginManager.php b/src/Manager/FilterPluginManager.php index 27c90fd..1f8d937 100644 --- a/src/Manager/FilterPluginManager.php +++ b/src/Manager/FilterPluginManager.php @@ -18,7 +18,7 @@ use function sprintf; /** - * @template F of FilterPluginManager + * @template F of FilterInterface * @extends AbstractPluginManager */ class FilterPluginManager extends AbstractPluginManager @@ -40,7 +40,7 @@ class FilterPluginManager extends AbstractPluginManager Validator::class => InvokableFactory::class, ]; - /** @var ?string */ + /** @var class-string */ protected $instanceOf = FilterInterface::class; /** diff --git a/src/Manager/FormatterPluginManager.php b/src/Manager/FormatterPluginManager.php index f94a795..034bdc7 100644 --- a/src/Manager/FormatterPluginManager.php +++ b/src/Manager/FormatterPluginManager.php @@ -15,7 +15,7 @@ use function sprintf; /** - * @template F of FormatterPluginManager + * @template F of FormatterInterface * @extends AbstractPluginManager */ class FormatterPluginManager extends AbstractPluginManager @@ -30,7 +30,7 @@ class FormatterPluginManager extends AbstractPluginManager Simple::class => InvokableFactory::class, ]; - /** @var ?string */ + /** @var class-string */ protected $instanceOf = FormatterInterface::class; /** diff --git a/src/Manager/ProcessorPluginManager.php b/src/Manager/ProcessorPluginManager.php index 0eab386..30ed334 100644 --- a/src/Manager/ProcessorPluginManager.php +++ b/src/Manager/ProcessorPluginManager.php @@ -18,7 +18,7 @@ use function sprintf; /** - * @template P of ProcessorPluginManager + * @template P of ProcessorInterface * @extends AbstractPluginManager

*/ class ProcessorPluginManager extends AbstractPluginManager @@ -39,7 +39,7 @@ class ProcessorPluginManager extends AbstractPluginManager RequestId::class => InvokableFactory::class, ]; - /** @var ?string */ + /** @var class-string

*/ protected $instanceOf = ProcessorInterface::class; /** diff --git a/src/Manager/WriterPluginManager.php b/src/Manager/WriterPluginManager.php index adeb236..0b2127d 100644 --- a/src/Manager/WriterPluginManager.php +++ b/src/Manager/WriterPluginManager.php @@ -16,7 +16,7 @@ use function sprintf; /** - * @template W of WriterPluginManager + * @template W of WriterInterface * @extends AbstractPluginManager */ class WriterPluginManager extends AbstractPluginManager @@ -38,7 +38,7 @@ class WriterPluginManager extends AbstractPluginManager Stream::class => WriterFactory::class, ]; - /** @var ?string */ + /** @var class-string */ protected $instanceOf = WriterInterface::class; /** diff --git a/src/Writer/Stream.php b/src/Writer/Stream.php index 2b29fa2..da15896 100644 --- a/src/Writer/Stream.php +++ b/src/Writer/Stream.php @@ -73,6 +73,7 @@ public function __construct( )); } + $error = null; if (is_resource($streamOrUrl)) { if ('stream' !== get_resource_type($streamOrUrl)) { throw new InvalidArgumentException(sprintf( diff --git a/test/Factory/FilterPluginManagerFactoryTest.php b/test/Factory/FilterPluginManagerFactoryTest.php index c153ca9..8ce562c 100644 --- a/test/Factory/FilterPluginManagerFactoryTest.php +++ b/test/Factory/FilterPluginManagerFactoryTest.php @@ -96,6 +96,6 @@ public function testWillInstantiate(): void $factory = (new FilterPluginManagerFactory())($this->container); - $this->assertInstanceOf(FilterPluginManager::class, $factory); + $this->assertContainsOnlyInstancesOf(FilterPluginManager::class, [$factory]); } } diff --git a/test/Factory/FormatterPluginManagerFactoryTest.php b/test/Factory/FormatterPluginManagerFactoryTest.php index e66445b..18b9e40 100644 --- a/test/Factory/FormatterPluginManagerFactoryTest.php +++ b/test/Factory/FormatterPluginManagerFactoryTest.php @@ -96,6 +96,6 @@ public function testWillInstantiate(): void $factory = (new FormatterPluginManagerFactory())($this->container); - $this->assertInstanceOf(FormatterPluginManager::class, $factory); + $this->assertContainsOnlyInstancesOf(FormatterPluginManager::class, [$factory]); } } diff --git a/test/Factory/LoggerAbstractServiceFactoryTest.php b/test/Factory/LoggerAbstractServiceFactoryTest.php index c34affc..b8539e9 100644 --- a/test/Factory/LoggerAbstractServiceFactoryTest.php +++ b/test/Factory/LoggerAbstractServiceFactoryTest.php @@ -51,7 +51,7 @@ public function testWillInstantiate(): void $factory = (new LoggerAbstractServiceFactory())($this->container, 'dot-log.test-log'); - $this->assertInstanceOf(Logger::class, $factory); + $this->assertContainsOnlyInstancesOf(Logger::class, [$factory]); } /** diff --git a/test/Factory/ProcessorPluginManagerFactoryTest.php b/test/Factory/ProcessorPluginManagerFactoryTest.php index ee1aeb4..745cee8 100644 --- a/test/Factory/ProcessorPluginManagerFactoryTest.php +++ b/test/Factory/ProcessorPluginManagerFactoryTest.php @@ -96,6 +96,6 @@ public function testWillInstantiate(): void $factory = (new ProcessorPluginManagerFactory())($this->container); - $this->assertInstanceOf(ProcessorPluginManager::class, $factory); + $this->assertContainsOnlyInstancesOf(ProcessorPluginManager::class, [$factory]); } } diff --git a/test/Factory/WriterFactoryTest.php b/test/Factory/WriterFactoryTest.php index d769f0f..afb8dd8 100644 --- a/test/Factory/WriterFactoryTest.php +++ b/test/Factory/WriterFactoryTest.php @@ -40,8 +40,7 @@ public function testWillInstantiate(): void public function testSetCreationOptions(): void { - $input = []; - - $this->assertNull($this->subject->setCreationOptions($input)); + $this->expectNotToPerformAssertions(); + $this->subject->setCreationOptions([]); } } diff --git a/test/Factory/WriterPluginManagerFactoryTest.php b/test/Factory/WriterPluginManagerFactoryTest.php index f830bbb..56cca3a 100644 --- a/test/Factory/WriterPluginManagerFactoryTest.php +++ b/test/Factory/WriterPluginManagerFactoryTest.php @@ -96,6 +96,6 @@ public function testWillInstantiate(): void $factory = (new WriterPluginManagerFactory())($this->container); - $this->assertInstanceOf(WriterPluginManager::class, $factory); + $this->assertContainsOnlyInstancesOf(WriterPluginManager::class, [$factory]); } } diff --git a/test/Filter/PriorityTest.php b/test/Filter/PriorityTest.php index c80417d..4ea29df 100644 --- a/test/Filter/PriorityTest.php +++ b/test/Filter/PriorityTest.php @@ -19,7 +19,7 @@ public function setUp(): void public function testWillInstantiateWithInt(): void { - $this->assertInstanceOf(Priority::class, $this->subject); + $this->assertContainsOnlyInstancesOf(Priority::class, [$this->subject]); } public function testWillInstantiateWithArray(): void @@ -28,7 +28,7 @@ public function testWillInstantiateWithArray(): void $result = new Priority($input); - $this->assertInstanceOf(Priority::class, $result); + $this->assertContainsOnlyInstancesOf(Priority::class, [$result]); } public function testWillNotInstantiateWithEmptyArray(): void diff --git a/test/Filter/RegexTest.php b/test/Filter/RegexTest.php index 83a76d0..ba835f4 100644 --- a/test/Filter/RegexTest.php +++ b/test/Filter/RegexTest.php @@ -23,7 +23,7 @@ public function setUp(): void public function testWillInstantiate(): void { - $this->assertInstanceOf(Regex::class, $this->subject); + $this->assertContainsOnlyInstancesOf(Regex::class, [$this->subject]); } /** diff --git a/test/Filter/SuppressFilterTest.php b/test/Filter/SuppressFilterTest.php index b056262..83484b7 100644 --- a/test/Filter/SuppressFilterTest.php +++ b/test/Filter/SuppressFilterTest.php @@ -19,7 +19,7 @@ public function setUp(): void public function testWillInstantiate(): void { - $this->assertInstanceOf(SuppressFilter::class, $this->subject); + $this->assertContainsOnlyInstancesOf(SuppressFilter::class, [$this->subject]); } public function testWillNotInstantiate(): void @@ -40,6 +40,7 @@ public function testFilter(): void public function testSuppress(): void { - $this->assertNull($this->subject->suppress(true)); + $this->expectNotToPerformAssertions(); + $this->subject->suppress(true); } } diff --git a/test/Filter/ValidatorTest.php b/test/Filter/ValidatorTest.php index 8a29a9a..2a70e0c 100644 --- a/test/Filter/ValidatorTest.php +++ b/test/Filter/ValidatorTest.php @@ -20,7 +20,7 @@ protected function setUp(): void public function testWillInstantiate(): void { - $this->assertInstanceOf(Validator::class, $this->subject); + $this->assertContainsOnlyInstancesOf(Validator::class, [$this->subject]); } public function testWillNotInstantiateWithEmptyArray(): void diff --git a/test/Formatter/BaseTest.php b/test/Formatter/BaseTest.php index 216c7c6..13958aa 100644 --- a/test/Formatter/BaseTest.php +++ b/test/Formatter/BaseTest.php @@ -19,7 +19,7 @@ protected function setUp(): void public function testWillInstantiate(): void { - $this->assertInstanceOf(Base::class, $this->subject); + $this->assertContainsOnlyInstancesOf(Base::class, [$this->subject]); } public function testFormat(): void @@ -38,6 +38,6 @@ public function testGetDatetimeFormat(): void public function testSetDatetimeFormat(): void { $result = $this->subject->setDateTimeFormat('Y-m-d H:i:s'); - $this->assertInstanceOf(Base::class, $result); + $this->assertContainsOnlyInstancesOf(Base::class, [$result]); } } diff --git a/test/Formatter/JsonTest.php b/test/Formatter/JsonTest.php index 7926c5e..8c0b9bc 100644 --- a/test/Formatter/JsonTest.php +++ b/test/Formatter/JsonTest.php @@ -34,6 +34,6 @@ public function testGetDatetimeFormat(): void public function testSetDatetimeFormat(): void { $result = $this->subject->setDateTimeFormat('Y-m-d H:i:s'); - $this->assertInstanceOf(Json::class, $result); + $this->assertContainsOnlyInstancesOf(Json::class, [$result]); } } diff --git a/test/Formatter/SimpleTest.php b/test/Formatter/SimpleTest.php index 7cee499..a2906bc 100644 --- a/test/Formatter/SimpleTest.php +++ b/test/Formatter/SimpleTest.php @@ -20,7 +20,7 @@ protected function setUp(): void public function testWillInstantiate(): void { - $this->assertInstanceOf(Simple::class, $this->subject); + $this->assertContainsOnlyInstancesOf(Simple::class, [$this->subject]); } public function testWillNotInstantiate(): void diff --git a/test/LoggerTest.php b/test/LoggerTest.php index 408b2c8..4243a58 100644 --- a/test/LoggerTest.php +++ b/test/LoggerTest.php @@ -62,7 +62,7 @@ public function testSetWriters(): void $this->subject->setWriters($writers); $writers = $this->subject->getWriters(); - $this->assertInstanceOf(SplPriorityQueue::class, $writers); + $this->assertContainsOnlyInstancesOf(SplPriorityQueue::class, [$writers]); $writer = $writers->extract(); $this->assertInstanceOf(Noop::class, $writer); } @@ -73,7 +73,7 @@ public function testAddWriterWithPriority(): void $this->subject->addWriter($writer, 3); $writers = $this->subject->getWriters(); - $this->assertInstanceOf(SplPriorityQueue::class, $writers); + $this->assertContainsOnlyInstancesOf(SplPriorityQueue::class, [$writers]); $writer = $writers->extract(); $this->assertInstanceOf(Noop::class, $writer); } @@ -86,7 +86,7 @@ public function testAddWithSamePriority(): void $this->subject->addWriter($writer2, 1); $writers = $this->subject->getWriters(); - $this->assertInstanceOf(SplPriorityQueue::class, $writers); + $this->assertContainsOnlyInstancesOf(SplPriorityQueue::class, [$writers]); $writer = $writers->extract(); $this->assertInstanceOf(Noop::class, $writer); $writer = $writers->extract(); diff --git a/test/Manager/FilterPluginManagerTest.php b/test/Manager/FilterPluginManagerTest.php index ffd09f5..1ee59ce 100644 --- a/test/Manager/FilterPluginManagerTest.php +++ b/test/Manager/FilterPluginManagerTest.php @@ -27,7 +27,8 @@ protected function setUp(): void public function testValidate(): void { - $this->assertNull($this->subject->validate(new Priority(47))); + $this->expectNotToPerformAssertions(); + $this->subject->validate(new Priority(47)); } public function testWillNotValidate(): void diff --git a/test/Manager/FormatterPluginManagerTest.php b/test/Manager/FormatterPluginManagerTest.php index 376219e..47ca4ea 100644 --- a/test/Manager/FormatterPluginManagerTest.php +++ b/test/Manager/FormatterPluginManagerTest.php @@ -27,7 +27,8 @@ protected function setUp(): void public function testValidate(): void { - $this->assertNull($this->subject->validate(new Json())); + $this->expectNotToPerformAssertions(); + $this->subject->validate(new Json()); } public function testWillNotValidate(): void diff --git a/test/Manager/ProcessorPluginManagerTest.php b/test/Manager/ProcessorPluginManagerTest.php index bf9b6c8..faa3f61 100644 --- a/test/Manager/ProcessorPluginManagerTest.php +++ b/test/Manager/ProcessorPluginManagerTest.php @@ -27,7 +27,8 @@ protected function setUp(): void public function testValidate(): void { - $this->assertNull($this->subject->validate(new Backtrace())); + $this->expectNotToPerformAssertions(); + $this->subject->validate(new Backtrace()); } public function testWillNotValidate(): void diff --git a/test/Manager/WriterPluginManagerTest.php b/test/Manager/WriterPluginManagerTest.php index 7645078..117d155 100644 --- a/test/Manager/WriterPluginManagerTest.php +++ b/test/Manager/WriterPluginManagerTest.php @@ -27,7 +27,8 @@ protected function setUp(): void public function testValidate(): void { - $this->assertNull($this->subject->validate(new Noop())); + $this->expectNotToPerformAssertions(); + $this->subject->validate(new Noop()); } public function testWillNotValidate(): void diff --git a/test/Processor/BackTraceTest.php b/test/Processor/BackTraceTest.php index 49dcfca..04f5da0 100644 --- a/test/Processor/BackTraceTest.php +++ b/test/Processor/BackTraceTest.php @@ -18,7 +18,7 @@ protected function setUp(): void public function testWillInstantiate(): void { - $this->assertInstanceOf(Backtrace::class, $this->subject); + $this->assertContainsOnlyInstancesOf(Backtrace::class, [$this->subject]); } public function testProcess(): void diff --git a/test/Writer/StreamTest.php b/test/Writer/StreamTest.php index b503458..6219294 100644 --- a/test/Writer/StreamTest.php +++ b/test/Writer/StreamTest.php @@ -92,6 +92,7 @@ public function testGetLogSeparator(): void public function testShutDown(): void { - $this->assertNull($this->subject->shutdown()); + $this->expectNotToPerformAssertions(); + $this->subject->shutdown(); } }