forked from friendica/friendica-addons
Fix Monolog
- Use new hook-loading
This commit is contained in:
parent
f328dc2b83
commit
745f9c1e5f
|
@ -11,8 +11,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.0",
|
"php": ">=7.3",
|
||||||
"monolog/monolog": "^3.2"
|
"monolog/monolog": "^2.9"
|
||||||
},
|
},
|
||||||
"license": "3-clause BSD license",
|
"license": "3-clause BSD license",
|
||||||
"config": {
|
"config": {
|
||||||
|
|
53
monolog/composer.lock
generated
53
monolog/composer.lock
generated
|
@ -4,45 +4,46 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "e812bcd051a73d1c9b19c91ec88a6a21",
|
"content-hash": "6fd294bd163b37ac6cc400e0f8785222",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.2.0",
|
"version": "2.9.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Seldaek/monolog.git",
|
"url": "https://github.com/Seldaek/monolog.git",
|
||||||
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81"
|
"reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/305444bc6fb6c89e490f4b34fa6e979584d7fa81",
|
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
|
||||||
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81",
|
"reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=7.2",
|
||||||
"psr/log": "^2.0 || ^3.0"
|
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/log-implementation": "3.0.0"
|
"psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"aws/aws-sdk-php": "^3.0",
|
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||||
"doctrine/couchdb": "~1.0@dev",
|
"doctrine/couchdb": "~1.0@dev",
|
||||||
"elasticsearch/elasticsearch": "^7 || ^8",
|
"elasticsearch/elasticsearch": "^7 || ^8",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"graylog2/gelf-php": "^1.4.2",
|
"graylog2/gelf-php": "^1.4.2 || ^2@dev",
|
||||||
"guzzlehttp/guzzle": "^7.4",
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
"guzzlehttp/psr7": "^2.2",
|
"guzzlehttp/psr7": "^2.2",
|
||||||
"mongodb/mongodb": "^1.8",
|
"mongodb/mongodb": "^1.8",
|
||||||
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
||||||
"phpstan/phpstan": "^1.4",
|
"phpspec/prophecy": "^1.15",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
"phpstan/phpstan": "^0.12.91",
|
||||||
"phpstan/phpstan-strict-rules": "^1.1",
|
"phpunit/phpunit": "^8.5.14",
|
||||||
"phpunit/phpunit": "^9.5.16",
|
"predis/predis": "^1.1 || ^2.0",
|
||||||
"predis/predis": "^1.1",
|
"rollbar/rollbar": "^1.3 || ^2 || ^3",
|
||||||
"ruflin/elastica": "^7",
|
"ruflin/elastica": "^7",
|
||||||
|
"swiftmailer/swiftmailer": "^5.3|^6.0",
|
||||||
"symfony/mailer": "^5.4 || ^6",
|
"symfony/mailer": "^5.4 || ^6",
|
||||||
"symfony/mime": "^5.4 || ^6"
|
"symfony/mime": "^5.4 || ^6"
|
||||||
},
|
},
|
||||||
|
@ -65,7 +66,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "3.x-dev"
|
"dev-main": "2.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -101,34 +102,34 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-07-24T12:00:55+00:00"
|
"time": "2023-02-06T13:44:46+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/log",
|
"name": "psr/log",
|
||||||
"version": "3.0.0",
|
"version": "1.1.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/php-fig/log.git",
|
"url": "https://github.com/php-fig/log.git",
|
||||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
|
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0.0"
|
"php": ">=5.3.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.x-dev"
|
"dev-master": "1.1.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Psr\\Log\\": "src"
|
"Psr\\Log\\": "Psr/Log/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
@ -148,7 +149,7 @@
|
||||||
"psr",
|
"psr",
|
||||||
"psr-3"
|
"psr-3"
|
||||||
],
|
],
|
||||||
"time": "2021-07-14T16:46:02+00:00"
|
"time": "2021-05-03T11:20:27+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [],
|
"packages-dev": [],
|
||||||
|
@ -158,7 +159,7 @@
|
||||||
"prefer-stable": false,
|
"prefer-stable": false,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": ">=7.0"
|
"php": ">=7.3"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": [],
|
||||||
"plugin-api-version": "1.1.0"
|
"plugin-api-version": "1.1.0"
|
||||||
|
|
|
@ -5,62 +5,3 @@
|
||||||
* Version: 1.0
|
* Version: 1.0
|
||||||
* Author: Philipp Holzer
|
* Author: Philipp Holzer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Friendica\App;
|
|
||||||
use Friendica\Core\Hook;
|
|
||||||
use Friendica\Addon\monolog\src\IntrospectionProcessor;
|
|
||||||
use Friendica\DI;
|
|
||||||
use Psr\Log\LogLevel;
|
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
function monolog_install()
|
|
||||||
{
|
|
||||||
Hook::register('logger_instance' , __FILE__, 'monolog_instance');
|
|
||||||
}
|
|
||||||
|
|
||||||
function monolog_uninstall()
|
|
||||||
{
|
|
||||||
Hook::unregister('logger_instance', __FILE__, 'monolog_instance');
|
|
||||||
}
|
|
||||||
|
|
||||||
function monolog_instance(array &$data)
|
|
||||||
{
|
|
||||||
if ($data['name'] !== 'monolog') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$loggerTimeZone = new \DateTimeZone('UTC');
|
|
||||||
|
|
||||||
$logger = new Monolog\Logger($data['channel']);
|
|
||||||
$logger->setTimezone($loggerTimeZone);
|
|
||||||
$logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
|
|
||||||
$logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
|
|
||||||
$logger->pushProcessor(new Monolog\Processor\UidProcessor());
|
|
||||||
$logger->pushProcessor(new IntrospectionProcessor($data['introspection'], LogLevel::DEBUG));
|
|
||||||
|
|
||||||
$stream = DI::config()->get('system', 'logfile');
|
|
||||||
|
|
||||||
// just add a stream in case it's either writable or not file
|
|
||||||
if (!is_file($stream) || is_writable($stream)) {
|
|
||||||
try {
|
|
||||||
$loglevel = Monolog\Logger::toMonologLevel($data['loglevel']);
|
|
||||||
|
|
||||||
// fallback to notice if an invalid loglevel is set
|
|
||||||
if (!is_int($loglevel)) {
|
|
||||||
$loglevel = LogLevel::NOTICE;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
|
|
||||||
|
|
||||||
$formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
|
|
||||||
$fileHandler->setFormatter($formatter);
|
|
||||||
|
|
||||||
$logger->pushHandler($fileHandler);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data['storage'] = $logger;
|
|
||||||
}
|
|
||||||
|
|
54
monolog/src/Factory/Monolog.php
Normal file
54
monolog/src/Factory/Monolog.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Friendica\Addon\monolog\src\Factory;
|
||||||
|
|
||||||
|
use Friendica\Addon\monolog\src\Monolog\IntrospectionProcessor;
|
||||||
|
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||||
|
use Friendica\Core\Logger\Factory\AbstractLoggerTypeFactory;
|
||||||
|
use Monolog\Formatter\LineFormatter;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Monolog\Processor\ProcessIdProcessor;
|
||||||
|
use Monolog\Processor\PsrLogMessageProcessor;
|
||||||
|
use Monolog\Processor\UidProcessor;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Psr\Log\LogLevel;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
|
||||||
|
class Monolog extends AbstractLoggerTypeFactory
|
||||||
|
{
|
||||||
|
public function create(IManageConfigValues $config, string $loglevel = null): LoggerInterface
|
||||||
|
{
|
||||||
|
$loggerTimeZone = new \DateTimeZone('UTC');
|
||||||
|
|
||||||
|
$logger = new Logger($this->channel);
|
||||||
|
$logger->setTimezone($loggerTimeZone);
|
||||||
|
$logger->pushProcessor(new PsrLogMessageProcessor());
|
||||||
|
$logger->pushProcessor(new ProcessIdProcessor());
|
||||||
|
$logger->pushProcessor(new UidProcessor());
|
||||||
|
$logger->pushProcessor(new IntrospectionProcessor($this->introspection, LogLevel::DEBUG));
|
||||||
|
|
||||||
|
$logfile = $config->get('system', 'logfile');
|
||||||
|
|
||||||
|
// just add a stream in case it's either writable or not file
|
||||||
|
if (is_writable($logfile)) {
|
||||||
|
$loglevel = $loglevel ?? static::mapLegacyConfigDebugLevel($config->get('system', 'loglevel'));
|
||||||
|
$loglevel = Logger::toMonologLevel($loglevel);
|
||||||
|
|
||||||
|
// fallback to notice if an invalid loglevel is set
|
||||||
|
if (!is_int($loglevel)) {
|
||||||
|
$loglevel = LogLevel::NOTICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileHandler = new StreamHandler($logfile, $loglevel);
|
||||||
|
|
||||||
|
$formatter = new LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
|
||||||
|
$fileHandler->setFormatter($formatter);
|
||||||
|
|
||||||
|
$logger->pushHandler($fileHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Friendica\Addon\monolog\src;
|
namespace Friendica\Addon\monolog\src\Monolog;
|
||||||
|
|
||||||
use Friendica\App\Request;
|
use Friendica\App\Request;
|
||||||
use Monolog\Handler;
|
use Monolog\Handler;
|
|
@ -19,11 +19,10 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace Friendica\Addon\monolog\src;
|
namespace Friendica\Addon\monolog\src\Monolog;
|
||||||
|
|
||||||
use Friendica\Core\Logger\Util\Introspection;
|
use Friendica\Core\Logger\Util\Introspection;
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
use Monolog\Processor\ProcessorInterface;
|
use Monolog\Processor\ProcessorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,19 +41,19 @@ class IntrospectionProcessor implements ProcessorInterface
|
||||||
public function __construct(Introspection $introspection, $level = Logger::DEBUG)
|
public function __construct(Introspection $introspection, $level = Logger::DEBUG)
|
||||||
{
|
{
|
||||||
$this->level = Logger::toMonologLevel($level);
|
$this->level = Logger::toMonologLevel($level);
|
||||||
$introspection->addClasses(['Monolog\\']);
|
$introspection->addClasses(['Monolog\\', static::class]);
|
||||||
$this->introspection = $introspection;
|
$this->introspection = $introspection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(LogRecord $record): LogRecord
|
public function __invoke(array $record): array
|
||||||
{
|
{
|
||||||
// return if the level is not high enough
|
// return if the level is not high enough
|
||||||
if ($record->level < $this->level) {
|
if ($record['level'] < $this->level) {
|
||||||
return $record;
|
return $record;
|
||||||
}
|
}
|
||||||
// we should have the call source now
|
// we should have the call source now
|
||||||
$record->extra = array_merge(
|
$record['extra'] = array_merge(
|
||||||
$record->extra,
|
$record['extra'],
|
||||||
$this->introspection->getRecord()
|
$this->introspection->getRecord()
|
||||||
);
|
);
|
||||||
|
|
30
monolog/static/dependencies.config.php
Normal file
30
monolog/static/dependencies.config.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
return [
|
||||||
|
\Monolog\Logger::class => [
|
||||||
|
'instanceOf' => \Friendica\Addon\monolog\src\Factory\Monolog::class,
|
||||||
|
'call' => [
|
||||||
|
['create', [], \Dice\Dice::CHAIN_CALL],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
30
monolog/static/hooks.config.php
Normal file
30
monolog/static/hooks.config.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Friendica\Core\Hooks\Capabilities\HookType as H;
|
||||||
|
|
||||||
|
return [
|
||||||
|
H::STRATEGY => [
|
||||||
|
\Psr\Log\LoggerInterface::class => [
|
||||||
|
\Monolog\Logger::class => ['monolog'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
21
monolog/vendor/composer/autoload_classmap.php
vendored
21
monolog/vendor/composer/autoload_classmap.php
vendored
|
@ -26,7 +26,6 @@ return array(
|
||||||
'Monolog\\Formatter\\MongoDBFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
|
'Monolog\\Formatter\\MongoDBFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
|
||||||
'Monolog\\Formatter\\NormalizerFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
|
'Monolog\\Formatter\\NormalizerFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
|
||||||
'Monolog\\Formatter\\ScalarFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
|
'Monolog\\Formatter\\ScalarFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
|
||||||
'Monolog\\Formatter\\SyslogFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php',
|
|
||||||
'Monolog\\Formatter\\WildfireFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
|
'Monolog\\Formatter\\WildfireFormatter' => $vendorDir . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
|
||||||
'Monolog\\Handler\\AbstractHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
|
'Monolog\\Handler\\AbstractHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
|
||||||
'Monolog\\Handler\\AbstractProcessingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
|
'Monolog\\Handler\\AbstractProcessingHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
|
||||||
|
@ -92,6 +91,7 @@ return array(
|
||||||
'Monolog\\Handler\\SocketHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
|
'Monolog\\Handler\\SocketHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
|
||||||
'Monolog\\Handler\\SqsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
|
'Monolog\\Handler\\SqsHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
|
||||||
'Monolog\\Handler\\StreamHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
|
'Monolog\\Handler\\StreamHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
|
||||||
|
'Monolog\\Handler\\SwiftMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php',
|
||||||
'Monolog\\Handler\\SymfonyMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
|
'Monolog\\Handler\\SymfonyMailerHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
|
||||||
'Monolog\\Handler\\SyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
|
'Monolog\\Handler\\SyslogHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
|
||||||
'Monolog\\Handler\\SyslogUdpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
|
'Monolog\\Handler\\SyslogUdpHandler' => $vendorDir . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
|
||||||
|
@ -121,12 +121,15 @@ return array(
|
||||||
'Monolog\\SignalHandler' => $vendorDir . '/monolog/monolog/src/Monolog/SignalHandler.php',
|
'Monolog\\SignalHandler' => $vendorDir . '/monolog/monolog/src/Monolog/SignalHandler.php',
|
||||||
'Monolog\\Test\\TestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/TestCase.php',
|
'Monolog\\Test\\TestCase' => $vendorDir . '/monolog/monolog/src/Monolog/Test/TestCase.php',
|
||||||
'Monolog\\Utils' => $vendorDir . '/monolog/monolog/src/Monolog/Utils.php',
|
'Monolog\\Utils' => $vendorDir . '/monolog/monolog/src/Monolog/Utils.php',
|
||||||
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php',
|
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php',
|
||||||
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php',
|
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php',
|
||||||
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php',
|
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php',
|
||||||
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php',
|
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php',
|
||||||
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php',
|
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php',
|
||||||
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php',
|
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
|
||||||
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php',
|
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
|
||||||
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php',
|
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
|
||||||
|
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php',
|
||||||
|
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
|
||||||
|
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
|
||||||
);
|
);
|
||||||
|
|
2
monolog/vendor/composer/autoload_psr4.php
vendored
2
monolog/vendor/composer/autoload_psr4.php
vendored
|
@ -6,6 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||||
$baseDir = dirname($vendorDir);
|
$baseDir = dirname($vendorDir);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
|
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
|
||||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||||
);
|
);
|
||||||
|
|
23
monolog/vendor/composer/autoload_static.php
vendored
23
monolog/vendor/composer/autoload_static.php
vendored
|
@ -20,7 +20,7 @@ class ComposerStaticInitMonologAddon
|
||||||
public static $prefixDirsPsr4 = array (
|
public static $prefixDirsPsr4 = array (
|
||||||
'Psr\\Log\\' =>
|
'Psr\\Log\\' =>
|
||||||
array (
|
array (
|
||||||
0 => __DIR__ . '/..' . '/psr/log/src',
|
0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
|
||||||
),
|
),
|
||||||
'Monolog\\' =>
|
'Monolog\\' =>
|
||||||
array (
|
array (
|
||||||
|
@ -49,7 +49,6 @@ class ComposerStaticInitMonologAddon
|
||||||
'Monolog\\Formatter\\MongoDBFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
|
'Monolog\\Formatter\\MongoDBFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php',
|
||||||
'Monolog\\Formatter\\NormalizerFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
|
'Monolog\\Formatter\\NormalizerFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php',
|
||||||
'Monolog\\Formatter\\ScalarFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
|
'Monolog\\Formatter\\ScalarFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php',
|
||||||
'Monolog\\Formatter\\SyslogFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/SyslogFormatter.php',
|
|
||||||
'Monolog\\Formatter\\WildfireFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
|
'Monolog\\Formatter\\WildfireFormatter' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php',
|
||||||
'Monolog\\Handler\\AbstractHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
|
'Monolog\\Handler\\AbstractHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractHandler.php',
|
||||||
'Monolog\\Handler\\AbstractProcessingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
|
'Monolog\\Handler\\AbstractProcessingHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php',
|
||||||
|
@ -115,6 +114,7 @@ class ComposerStaticInitMonologAddon
|
||||||
'Monolog\\Handler\\SocketHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
|
'Monolog\\Handler\\SocketHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SocketHandler.php',
|
||||||
'Monolog\\Handler\\SqsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
|
'Monolog\\Handler\\SqsHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SqsHandler.php',
|
||||||
'Monolog\\Handler\\StreamHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
|
'Monolog\\Handler\\StreamHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/StreamHandler.php',
|
||||||
|
'Monolog\\Handler\\SwiftMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php',
|
||||||
'Monolog\\Handler\\SymfonyMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
|
'Monolog\\Handler\\SymfonyMailerHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SymfonyMailerHandler.php',
|
||||||
'Monolog\\Handler\\SyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
|
'Monolog\\Handler\\SyslogHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogHandler.php',
|
||||||
'Monolog\\Handler\\SyslogUdpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
|
'Monolog\\Handler\\SyslogUdpHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php',
|
||||||
|
@ -144,14 +144,17 @@ class ComposerStaticInitMonologAddon
|
||||||
'Monolog\\SignalHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/SignalHandler.php',
|
'Monolog\\SignalHandler' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/SignalHandler.php',
|
||||||
'Monolog\\Test\\TestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/TestCase.php',
|
'Monolog\\Test\\TestCase' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Test/TestCase.php',
|
||||||
'Monolog\\Utils' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Utils.php',
|
'Monolog\\Utils' => __DIR__ . '/..' . '/monolog/monolog/src/Monolog/Utils.php',
|
||||||
'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/src/AbstractLogger.php',
|
'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php',
|
||||||
'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/src/InvalidArgumentException.php',
|
'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php',
|
||||||
'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/src/LogLevel.php',
|
'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php',
|
||||||
'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareInterface.php',
|
'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php',
|
||||||
'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerAwareTrait.php',
|
'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php',
|
||||||
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/src/LoggerInterface.php',
|
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
|
||||||
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/src/LoggerTrait.php',
|
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
|
||||||
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/src/NullLogger.php',
|
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
|
||||||
|
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php',
|
||||||
|
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
|
||||||
|
'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php',
|
||||||
);
|
);
|
||||||
|
|
||||||
public static function getInitializer(ClassLoader $loader)
|
public static function getInitializer(ClassLoader $loader)
|
||||||
|
|
53
monolog/vendor/composer/installed.json
vendored
53
monolog/vendor/composer/installed.json
vendored
|
@ -1,42 +1,43 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "monolog/monolog",
|
"name": "monolog/monolog",
|
||||||
"version": "3.2.0",
|
"version": "2.9.1",
|
||||||
"version_normalized": "3.2.0.0",
|
"version_normalized": "2.9.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Seldaek/monolog.git",
|
"url": "https://github.com/Seldaek/monolog.git",
|
||||||
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81"
|
"reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/305444bc6fb6c89e490f4b34fa6e979584d7fa81",
|
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
|
||||||
"reference": "305444bc6fb6c89e490f4b34fa6e979584d7fa81",
|
"reference": "f259e2b15fb95494c83f52d3caad003bbf5ffaa1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=7.2",
|
||||||
"psr/log": "^2.0 || ^3.0"
|
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/log-implementation": "3.0.0"
|
"psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"aws/aws-sdk-php": "^3.0",
|
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||||
"doctrine/couchdb": "~1.0@dev",
|
"doctrine/couchdb": "~1.0@dev",
|
||||||
"elasticsearch/elasticsearch": "^7 || ^8",
|
"elasticsearch/elasticsearch": "^7 || ^8",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"graylog2/gelf-php": "^1.4.2",
|
"graylog2/gelf-php": "^1.4.2 || ^2@dev",
|
||||||
"guzzlehttp/guzzle": "^7.4",
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
"guzzlehttp/psr7": "^2.2",
|
"guzzlehttp/psr7": "^2.2",
|
||||||
"mongodb/mongodb": "^1.8",
|
"mongodb/mongodb": "^1.8",
|
||||||
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
||||||
"phpstan/phpstan": "^1.4",
|
"phpspec/prophecy": "^1.15",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
"phpstan/phpstan": "^0.12.91",
|
||||||
"phpstan/phpstan-strict-rules": "^1.1",
|
"phpunit/phpunit": "^8.5.14",
|
||||||
"phpunit/phpunit": "^9.5.16",
|
"predis/predis": "^1.1 || ^2.0",
|
||||||
"predis/predis": "^1.1",
|
"rollbar/rollbar": "^1.3 || ^2 || ^3",
|
||||||
"ruflin/elastica": "^7",
|
"ruflin/elastica": "^7",
|
||||||
|
"swiftmailer/swiftmailer": "^5.3|^6.0",
|
||||||
"symfony/mailer": "^5.4 || ^6",
|
"symfony/mailer": "^5.4 || ^6",
|
||||||
"symfony/mime": "^5.4 || ^6"
|
"symfony/mime": "^5.4 || ^6"
|
||||||
},
|
},
|
||||||
|
@ -56,11 +57,11 @@
|
||||||
"rollbar/rollbar": "Allow sending log messages to Rollbar",
|
"rollbar/rollbar": "Allow sending log messages to Rollbar",
|
||||||
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
|
"ruflin/elastica": "Allow sending log messages to an Elastic Search server"
|
||||||
},
|
},
|
||||||
"time": "2022-07-24T12:00:55+00:00",
|
"time": "2023-02-06T13:44:46+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "3.x-dev"
|
"dev-main": "2.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
|
@ -100,33 +101,33 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/log",
|
"name": "psr/log",
|
||||||
"version": "3.0.0",
|
"version": "1.1.4",
|
||||||
"version_normalized": "3.0.0.0",
|
"version_normalized": "1.1.4.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/php-fig/log.git",
|
"url": "https://github.com/php-fig/log.git",
|
||||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
|
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||||
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
|
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0.0"
|
"php": ">=5.3.0"
|
||||||
},
|
},
|
||||||
"time": "2021-07-14T16:46:02+00:00",
|
"time": "2021-05-03T11:20:27+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.x-dev"
|
"dev-master": "1.1.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Psr\\Log\\": "src"
|
"Psr\\Log\\": "Psr/Log/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
|
75
monolog/vendor/monolog/monolog/CHANGELOG.md
vendored
75
monolog/vendor/monolog/monolog/CHANGELOG.md
vendored
|
@ -1,70 +1,17 @@
|
||||||
### 3.2.0 (2022-07-24)
|
### 2.9.1 (2023-02-06)
|
||||||
|
|
||||||
* Deprecated `CubeHandler` and `PHPConsoleHandler` as both projects are abandoned and those should not be used anymore (#1734)
|
* Fixed Logger not being serializable anymore (#1792)
|
||||||
* Marked `Logger` `@final` as it should not be extended, prefer composition or talk to us if you are missing something
|
|
||||||
* Added RFC 5424 level (`7` to `0`) support to `Logger::log` and `Logger::addRecord` to increase interoperability (#1723)
|
|
||||||
* Added `SyslogFormatter` to output syslog-like files which can be consumed by tools like [lnav](https://lnav.org/) (#1689)
|
|
||||||
* Added support for `__toString` for objects which are not json serializable in `JsonFormatter` (#1733)
|
|
||||||
* Added `GoogleCloudLoggingFormatter` (#1719)
|
|
||||||
* Added support for Predis 2.x (#1732)
|
|
||||||
* Added `AmqpHandler->setExtraAttributes` to allow configuring attributes when using an AMQPExchange (#1724)
|
|
||||||
* Fixed serialization/unserialization of handlers to make sure private properties are included (#1727)
|
|
||||||
* Fixed allowInlineLineBreaks in LineFormatter causing issues with windows paths containing `\n` or `\r` sequences (#1720)
|
|
||||||
* Fixed max normalization depth not being taken into account when formatting exceptions with a deep chain of previous exceptions (#1726)
|
|
||||||
* Fixed PHP 8.2 deprecation warnings (#1722)
|
|
||||||
* Fixed rare race condition or filesystem issue where StreamHandler is unable to create the directory the log should go into yet it exists already (#1678)
|
|
||||||
|
|
||||||
### 3.1.0 (2022-06-09)
|
### 2.9.0 (2023-02-05)
|
||||||
|
|
||||||
* Added `$datetime` parameter to `Logger::addRecord` as low level API to allow logging into the past or future (#1682)
|
* Deprecated FlowdockHandler & Formatter as the flowdock service was shutdown (#1748)
|
||||||
* Added `Logger::useLoggingLoopDetection` to allow disabling cyclic logging detection in concurrent frameworks (#1681)
|
* Added support for enum context values in PsrLogMessageProcessor (#1773)
|
||||||
* Fixed handling of fatal errors if callPrevious is disabled in ErrorHandler (#1670)
|
* Added graylog2/gelf-php 2.x support (#1747)
|
||||||
* Fixed interop issue by removing the need for a return type in ProcessorInterface (#1680)
|
* Improved `BrowserConsoleHandler` logging to use more appropriate methods than just console.log in the browser (#1739)
|
||||||
* Marked the reusable `Monolog\Test\TestCase` class as `@internal` to make sure PHPStorm does not show it above PHPUnit, you may still use it to test your own handlers/etc though (#1677)
|
* Fixed `WhatFailureGroupHandler` not catching errors happening inside `close()` (#1791)
|
||||||
* Fixed RotatingFileHandler issue when the date format contained slashes (#1671)
|
* Fixed datetime field in `GoogleCloudLoggingFormatter` (#1758)
|
||||||
|
* Fixed infinite loop detection within Fibers (#1753)
|
||||||
### 3.0.0 (2022-05-10)
|
* Fixed `AmqpHandler->setExtraAttributes` not working with buffering handler wrappers (#1781)
|
||||||
|
|
||||||
Changes from RC1
|
|
||||||
|
|
||||||
- The `Monolog\LevelName` enum does not exist anymore, use `Monolog\Level->getName()` instead.
|
|
||||||
|
|
||||||
### 3.0.0-RC1 (2022-05-08)
|
|
||||||
|
|
||||||
This is mostly a cleanup release offering stronger type guarantees for integrators with the
|
|
||||||
array->object/enum changes, but there is no big new feature for end users.
|
|
||||||
|
|
||||||
See [UPGRADE notes](UPGRADE.md#300) for details on all breaking changes especially if you are extending/implementing Monolog classes/interfaces.
|
|
||||||
|
|
||||||
Noteworthy BC Breaks:
|
|
||||||
|
|
||||||
- The minimum supported PHP version is now `8.1.0`.
|
|
||||||
- Log records have been converted from an array to a [`Monolog\LogRecord` object](src/Monolog/LogRecord.php)
|
|
||||||
with public (and mostly readonly) properties. e.g. instead of doing
|
|
||||||
`$record['context']` use `$record->context`.
|
|
||||||
In formatters or handlers if you rather need an array to work with you can use `$record->toArray()`
|
|
||||||
to get back a Monolog 1/2 style record array. This will contain the enum values instead of enum cases
|
|
||||||
in the `level` and `level_name` keys to be more backwards compatible and use simpler data types.
|
|
||||||
- `FormatterInterface`, `HandlerInterface`, `ProcessorInterface`, etc. changed to contain `LogRecord $record`
|
|
||||||
instead of `array $record` parameter types. If you want to support multiple Monolog versions this should
|
|
||||||
be possible by type-hinting nothing, or `array|LogRecord` if you support PHP 8.0+. You can then code
|
|
||||||
against the $record using Monolog 2 style as LogRecord implements ArrayAccess for BC.
|
|
||||||
The interfaces do not require a `LogRecord` return type even where it would be applicable, but if you only
|
|
||||||
support Monolog 3 in integration code I would recommend you use `LogRecord` return types wherever fitting
|
|
||||||
to ensure forward compatibility as it may be added in Monolog 4.
|
|
||||||
- Log levels are now enums [`Monolog\Level`](src/Monolog/Level.php) and [`Monolog\LevelName`](src/Monolog/LevelName.php)
|
|
||||||
- Removed deprecated SwiftMailerHandler, migrate to SymfonyMailerHandler instead.
|
|
||||||
- `ResettableInterface::reset()` now requires a void return type.
|
|
||||||
- All properties have had types added, which may require you to do so as well if you extended
|
|
||||||
a Monolog class and declared the same property.
|
|
||||||
|
|
||||||
New deprecations:
|
|
||||||
|
|
||||||
- `Logger::DEBUG`, `Logger::ERROR`, etc. are now deprecated in favor of the `Monolog\Level` enum.
|
|
||||||
e.g. instead of `Logger::WARNING` use `Level::Warning` if you need to pass the enum case
|
|
||||||
to Monolog or one of its handlers, or `Level::Warning->value` if you need the integer
|
|
||||||
value equal to what `Logger::WARNING` was giving you.
|
|
||||||
- `Logger::getLevelName()` is now deprecated.
|
|
||||||
|
|
||||||
### 2.8.0 (2022-07-24)
|
### 2.8.0 (2022-07-24)
|
||||||
|
|
||||||
|
|
11
monolog/vendor/monolog/monolog/README.md
vendored
11
monolog/vendor/monolog/monolog/README.md
vendored
|
@ -3,8 +3,6 @@
|
||||||
[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
|
[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
|
||||||
[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
|
[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
|
||||||
|
|
||||||
> ⚠ This is the **documentation for Monolog 3.x**, if you are using older releases
|
|
||||||
> see the documentation for [Monolog 2.x](https://github.com/Seldaek/monolog/blob/2.x/README.md) or [Monolog 1.x](https://github.com/Seldaek/monolog/blob/1.x/README.md) ⚠
|
|
||||||
|
|
||||||
Monolog sends your logs to files, sockets, inboxes, databases and various
|
Monolog sends your logs to files, sockets, inboxes, databases and various
|
||||||
web services. See the complete list of handlers below. Special handlers
|
web services. See the complete list of handlers below. Special handlers
|
||||||
|
@ -30,13 +28,12 @@ $ composer require monolog/monolog
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\Handler\StreamHandler;
|
use Monolog\Handler\StreamHandler;
|
||||||
|
|
||||||
// create a log channel
|
// create a log channel
|
||||||
$log = new Logger('name');
|
$log = new Logger('name');
|
||||||
$log->pushHandler(new StreamHandler('path/to/your.log', Level::Warning));
|
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
|
||||||
|
|
||||||
// add records to the log
|
// add records to the log
|
||||||
$log->warning('Foo');
|
$log->warning('Foo');
|
||||||
|
@ -67,13 +64,11 @@ can also add your own there if you publish one.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- Monolog `^3.0` works with PHP 8.1 or above.
|
- Monolog `^2.0` works with PHP 7.2 or above, use Monolog `^1.25` for PHP 5.3+ support.
|
||||||
- Monolog `^2.5` works with PHP 7.2 or above.
|
|
||||||
- Monolog `^1.25` works with PHP 5.3 up to 8.1, but is not very maintained anymore and will not receive PHP support fixes anymore.
|
|
||||||
|
|
||||||
### Support
|
### Support
|
||||||
|
|
||||||
Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 or 3 where possible to benefit from all the latest features and fixes.
|
Monolog 1.x support is somewhat limited at this point and only important fixes will be done. You should migrate to Monolog 2 where possible to benefit from all the latest features and fixes.
|
||||||
|
|
||||||
### Submitting bugs and feature requests
|
### Submitting bugs and feature requests
|
||||||
|
|
||||||
|
|
28
monolog/vendor/monolog/monolog/composer.json
vendored
28
monolog/vendor/monolog/monolog/composer.json
vendored
|
@ -13,25 +13,26 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=7.2",
|
||||||
"psr/log": "^2.0 || ^3.0"
|
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"aws/aws-sdk-php": "^3.0",
|
"aws/aws-sdk-php": "^2.4.9 || ^3.0",
|
||||||
"doctrine/couchdb": "~1.0@dev",
|
"doctrine/couchdb": "~1.0@dev",
|
||||||
"elasticsearch/elasticsearch": "^7 || ^8",
|
"elasticsearch/elasticsearch": "^7 || ^8",
|
||||||
"graylog2/gelf-php": "^1.4.2",
|
"graylog2/gelf-php": "^1.4.2 || ^2@dev",
|
||||||
"guzzlehttp/guzzle": "^7.4",
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
"guzzlehttp/psr7": "^2.2",
|
"guzzlehttp/psr7": "^2.2",
|
||||||
"mongodb/mongodb": "^1.8",
|
"mongodb/mongodb": "^1.8",
|
||||||
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
"php-amqplib/php-amqplib": "~2.4 || ^3",
|
||||||
"phpstan/phpstan": "^1.4",
|
"phpspec/prophecy": "^1.15",
|
||||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
"phpstan/phpstan": "^0.12.91",
|
||||||
"phpstan/phpstan-strict-rules": "^1.1",
|
"phpunit/phpunit": "^8.5.14",
|
||||||
"phpunit/phpunit": "^9.5.16",
|
"predis/predis": "^1.1 || ^2.0",
|
||||||
"predis/predis": "^1.1",
|
"rollbar/rollbar": "^1.3 || ^2 || ^3",
|
||||||
"ruflin/elastica": "^7",
|
"ruflin/elastica": "^7",
|
||||||
|
"swiftmailer/swiftmailer": "^5.3|^6.0",
|
||||||
"symfony/mailer": "^5.4 || ^6",
|
"symfony/mailer": "^5.4 || ^6",
|
||||||
"symfony/mime": "^5.4 || ^6"
|
"symfony/mime": "^5.4 || ^6"
|
||||||
},
|
},
|
||||||
|
@ -58,11 +59,11 @@
|
||||||
"psr-4": {"Monolog\\": "tests/Monolog"}
|
"psr-4": {"Monolog\\": "tests/Monolog"}
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
"psr/log-implementation": "3.0.0"
|
"psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "3.x-dev"
|
"dev-main": "2.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -72,6 +73,9 @@
|
||||||
"config": {
|
"config": {
|
||||||
"lock": false,
|
"lock": false,
|
||||||
"sort-packages": true,
|
"sort-packages": true,
|
||||||
"platform-check": false
|
"platform-check": false,
|
||||||
|
"allow-plugins": {
|
||||||
|
"composer/package-versions-deprecated": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,15 +22,25 @@ namespace Monolog\Attribute;
|
||||||
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||||
class AsMonologProcessor
|
class AsMonologProcessor
|
||||||
{
|
{
|
||||||
|
/** @var string|null */
|
||||||
|
public $channel = null;
|
||||||
|
/** @var string|null */
|
||||||
|
public $handler = null;
|
||||||
|
/** @var string|null */
|
||||||
|
public $method = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $channel The logging channel the processor should be pushed to.
|
* @param string|null $channel The logging channel the processor should be pushed to.
|
||||||
* @param string|null $handler The handler the processor should be pushed to.
|
* @param string|null $handler The handler the processor should be pushed to.
|
||||||
* @param string|null $method The method that processes the records (if the attribute is used at the class level).
|
* @param string|null $method The method that processes the records (if the attribute is used at the class level).
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public readonly ?string $channel = null,
|
?string $channel = null,
|
||||||
public readonly ?string $handler = null,
|
?string $handler = null,
|
||||||
public readonly ?string $method = null
|
?string $method = null
|
||||||
) {
|
) {
|
||||||
|
$this->channel = $channel;
|
||||||
|
$this->handler = $handler;
|
||||||
|
$this->method = $method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,10 @@ use DateTimeZone;
|
||||||
*/
|
*/
|
||||||
class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable
|
class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable
|
||||||
{
|
{
|
||||||
private bool $useMicroseconds;
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $useMicroseconds;
|
||||||
|
|
||||||
public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null)
|
public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog;
|
namespace Monolog;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
|
|
||||||
|
@ -26,33 +25,35 @@ use Psr\Log\LogLevel;
|
||||||
*/
|
*/
|
||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
private Closure|null $previousExceptionHandler = null;
|
/** @var LoggerInterface */
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
/** @var ?callable */
|
||||||
|
private $previousExceptionHandler = null;
|
||||||
/** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
|
/** @var array<class-string, LogLevel::*> an array of class name to LogLevel::* constant mapping */
|
||||||
private array $uncaughtExceptionLevelMap = [];
|
private $uncaughtExceptionLevelMap = [];
|
||||||
|
|
||||||
/** @var Closure|true|null */
|
|
||||||
private Closure|bool|null $previousErrorHandler = null;
|
|
||||||
|
|
||||||
|
/** @var callable|true|null */
|
||||||
|
private $previousErrorHandler = null;
|
||||||
/** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
|
/** @var array<int, LogLevel::*> an array of E_* constant to LogLevel::* constant mapping */
|
||||||
private array $errorLevelMap = [];
|
private $errorLevelMap = [];
|
||||||
|
/** @var bool */
|
||||||
private bool $handleOnlyReportedErrors = true;
|
private $handleOnlyReportedErrors = true;
|
||||||
|
|
||||||
private bool $hasFatalErrorHandler = false;
|
|
||||||
|
|
||||||
private string $fatalLevel = LogLevel::ALERT;
|
|
||||||
|
|
||||||
private string|null $reservedMemory = null;
|
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $hasFatalErrorHandler = false;
|
||||||
|
/** @var LogLevel::* */
|
||||||
|
private $fatalLevel = LogLevel::ALERT;
|
||||||
|
/** @var ?string */
|
||||||
|
private $reservedMemory = null;
|
||||||
/** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */
|
/** @var ?array{type: int, message: string, file: string, line: int, trace: mixed} */
|
||||||
private array|null $lastFatalData = null;
|
private $lastFatalData = null;
|
||||||
|
/** @var int[] */
|
||||||
|
private static $fatalErrors = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
|
||||||
|
|
||||||
private const FATAL_ERRORS = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR];
|
public function __construct(LoggerInterface $logger)
|
||||||
|
{
|
||||||
public function __construct(
|
$this->logger = $logger;
|
||||||
private LoggerInterface $logger
|
|
||||||
) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,6 +61,7 @@ class ErrorHandler
|
||||||
*
|
*
|
||||||
* By default it will handle errors, exceptions and fatal errors
|
* By default it will handle errors, exceptions and fatal errors
|
||||||
*
|
*
|
||||||
|
* @param LoggerInterface $logger
|
||||||
* @param array<int, LogLevel::*>|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
|
* @param array<int, LogLevel::*>|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling
|
||||||
* @param array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
|
* @param array<class-string, LogLevel::*>|false $exceptionLevelMap an array of class name to LogLevel::* constant mapping, or false to disable exception handling
|
||||||
* @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
|
* @param LogLevel::*|null|false $fatalLevel a LogLevel::* constant, null to use the default LogLevel::ALERT or false to disable fatal error handling
|
||||||
|
@ -97,8 +99,8 @@ class ErrorHandler
|
||||||
$this->uncaughtExceptionLevelMap[$class] = $level;
|
$this->uncaughtExceptionLevelMap[$class] = $level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($callPrevious && null !== $prev) {
|
if ($callPrevious && $prev) {
|
||||||
$this->previousExceptionHandler = $prev(...);
|
$this->previousExceptionHandler = $prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -110,10 +112,10 @@ class ErrorHandler
|
||||||
*/
|
*/
|
||||||
public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
|
public function registerErrorHandler(array $levelMap = [], bool $callPrevious = true, int $errorTypes = -1, bool $handleOnlyReportedErrors = true): self
|
||||||
{
|
{
|
||||||
$prev = set_error_handler($this->handleError(...), $errorTypes);
|
$prev = set_error_handler([$this, 'handleError'], $errorTypes);
|
||||||
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
|
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
|
||||||
if ($callPrevious) {
|
if ($callPrevious) {
|
||||||
$this->previousErrorHandler = $prev !== null ? $prev(...) : true;
|
$this->previousErrorHandler = $prev ?: true;
|
||||||
} else {
|
} else {
|
||||||
$this->previousErrorHandler = null;
|
$this->previousErrorHandler = null;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +131,7 @@ class ErrorHandler
|
||||||
*/
|
*/
|
||||||
public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
|
public function registerFatalHandler($level = null, int $reservedMemorySize = 20): self
|
||||||
{
|
{
|
||||||
register_shutdown_function($this->handleFatalError(...));
|
register_shutdown_function([$this, 'handleFatalError']);
|
||||||
|
|
||||||
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
|
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
|
||||||
$this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
|
$this->fatalLevel = null === $level ? LogLevel::ALERT : $level;
|
||||||
|
@ -173,7 +175,10 @@ class ErrorHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleException(\Throwable $e): never
|
/**
|
||||||
|
* @phpstan-return never
|
||||||
|
*/
|
||||||
|
private function handleException(\Throwable $e): void
|
||||||
{
|
{
|
||||||
$level = LogLevel::ERROR;
|
$level = LogLevel::ERROR;
|
||||||
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
|
foreach ($this->uncaughtExceptionLevelMap as $class => $candidate) {
|
||||||
|
@ -189,25 +194,30 @@ class ErrorHandler
|
||||||
['exception' => $e]
|
['exception' => $e]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (null !== $this->previousExceptionHandler) {
|
if ($this->previousExceptionHandler) {
|
||||||
($this->previousExceptionHandler)($e);
|
($this->previousExceptionHandler)($e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!headers_sent() && !(bool) ini_get('display_errors')) {
|
if (!headers_sent() && !ini_get('display_errors')) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(255);
|
exit(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleError(int $code, string $message, string $file = '', int $line = 0): bool
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @param mixed[] $context
|
||||||
|
*/
|
||||||
|
public function handleError(int $code, string $message, string $file = '', int $line = 0, ?array $context = []): bool
|
||||||
{
|
{
|
||||||
if ($this->handleOnlyReportedErrors && 0 === (error_reporting() & $code)) {
|
if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
|
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries
|
||||||
if (!$this->hasFatalErrorHandler || !in_array($code, self::FATAL_ERRORS, true)) {
|
if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) {
|
||||||
$level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL;
|
$level = $this->errorLevelMap[$code] ?? LogLevel::CRITICAL;
|
||||||
$this->logger->log($level, self::codeToString($code).': '.$message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]);
|
$this->logger->log($level, self::codeToString($code).': '.$message, ['code' => $code, 'message' => $message, 'file' => $file, 'line' => $line]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,9 +228,8 @@ class ErrorHandler
|
||||||
|
|
||||||
if ($this->previousErrorHandler === true) {
|
if ($this->previousErrorHandler === true) {
|
||||||
return false;
|
return false;
|
||||||
}
|
} elseif ($this->previousErrorHandler) {
|
||||||
if ($this->previousErrorHandler instanceof Closure) {
|
return (bool) ($this->previousErrorHandler)($code, $message, $file, $line, $context);
|
||||||
return (bool) ($this->previousErrorHandler)($code, $message, $file, $line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -238,7 +247,8 @@ class ErrorHandler
|
||||||
} else {
|
} else {
|
||||||
$lastError = error_get_last();
|
$lastError = error_get_last();
|
||||||
}
|
}
|
||||||
if (is_array($lastError) && in_array($lastError['type'], self::FATAL_ERRORS, true)) {
|
|
||||||
|
if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) {
|
||||||
$trace = $lastError['trace'] ?? null;
|
$trace = $lastError['trace'] ?? null;
|
||||||
$this->logger->log(
|
$this->logger->log(
|
||||||
$this->fatalLevel,
|
$this->fatalLevel,
|
||||||
|
@ -254,25 +264,44 @@ class ErrorHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function codeToString(int $code): string
|
/**
|
||||||
|
* @param int $code
|
||||||
|
*/
|
||||||
|
private static function codeToString($code): string
|
||||||
{
|
{
|
||||||
return match ($code) {
|
switch ($code) {
|
||||||
E_ERROR => 'E_ERROR',
|
case E_ERROR:
|
||||||
E_WARNING => 'E_WARNING',
|
return 'E_ERROR';
|
||||||
E_PARSE => 'E_PARSE',
|
case E_WARNING:
|
||||||
E_NOTICE => 'E_NOTICE',
|
return 'E_WARNING';
|
||||||
E_CORE_ERROR => 'E_CORE_ERROR',
|
case E_PARSE:
|
||||||
E_CORE_WARNING => 'E_CORE_WARNING',
|
return 'E_PARSE';
|
||||||
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
|
case E_NOTICE:
|
||||||
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
|
return 'E_NOTICE';
|
||||||
E_USER_ERROR => 'E_USER_ERROR',
|
case E_CORE_ERROR:
|
||||||
E_USER_WARNING => 'E_USER_WARNING',
|
return 'E_CORE_ERROR';
|
||||||
E_USER_NOTICE => 'E_USER_NOTICE',
|
case E_CORE_WARNING:
|
||||||
E_STRICT => 'E_STRICT',
|
return 'E_CORE_WARNING';
|
||||||
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
|
case E_COMPILE_ERROR:
|
||||||
E_DEPRECATED => 'E_DEPRECATED',
|
return 'E_COMPILE_ERROR';
|
||||||
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
|
case E_COMPILE_WARNING:
|
||||||
default => 'Unknown PHP error',
|
return 'E_COMPILE_WARNING';
|
||||||
};
|
case E_USER_ERROR:
|
||||||
|
return 'E_USER_ERROR';
|
||||||
|
case E_USER_WARNING:
|
||||||
|
return 'E_USER_WARNING';
|
||||||
|
case E_USER_NOTICE:
|
||||||
|
return 'E_USER_NOTICE';
|
||||||
|
case E_STRICT:
|
||||||
|
return 'E_STRICT';
|
||||||
|
case E_RECOVERABLE_ERROR:
|
||||||
|
return 'E_RECOVERABLE_ERROR';
|
||||||
|
case E_DEPRECATED:
|
||||||
|
return 'E_DEPRECATED';
|
||||||
|
case E_USER_DEPRECATED:
|
||||||
|
return 'E_USER_DEPRECATED';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Unknown PHP error';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a log message according to the ChromePHP array format
|
* Formats a log message according to the ChromePHP array format
|
||||||
|
@ -24,55 +23,52 @@ class ChromePHPFormatter implements FormatterInterface
|
||||||
/**
|
/**
|
||||||
* Translates Monolog log levels to Wildfire levels.
|
* Translates Monolog log levels to Wildfire levels.
|
||||||
*
|
*
|
||||||
* @return 'log'|'info'|'warn'|'error'
|
* @var array<int, 'log'|'info'|'warn'|'error'>
|
||||||
*/
|
*/
|
||||||
private function toWildfireLevel(Level $level): string
|
private $logLevels = [
|
||||||
{
|
Logger::DEBUG => 'log',
|
||||||
return match ($level) {
|
Logger::INFO => 'info',
|
||||||
Level::Debug => 'log',
|
Logger::NOTICE => 'info',
|
||||||
Level::Info => 'info',
|
Logger::WARNING => 'warn',
|
||||||
Level::Notice => 'info',
|
Logger::ERROR => 'error',
|
||||||
Level::Warning => 'warn',
|
Logger::CRITICAL => 'error',
|
||||||
Level::Error => 'error',
|
Logger::ALERT => 'error',
|
||||||
Level::Critical => 'error',
|
Logger::EMERGENCY => 'error',
|
||||||
Level::Alert => 'error',
|
];
|
||||||
Level::Emergency => 'error',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record)
|
public function format(array $record)
|
||||||
{
|
{
|
||||||
// Retrieve the line and file if set and remove them from the formatted extra
|
// Retrieve the line and file if set and remove them from the formatted extra
|
||||||
$backtrace = 'unknown';
|
$backtrace = 'unknown';
|
||||||
if (isset($record->extra['file'], $record->extra['line'])) {
|
if (isset($record['extra']['file'], $record['extra']['line'])) {
|
||||||
$backtrace = $record->extra['file'].' : '.$record->extra['line'];
|
$backtrace = $record['extra']['file'].' : '.$record['extra']['line'];
|
||||||
unset($record->extra['file'], $record->extra['line']);
|
unset($record['extra']['file'], $record['extra']['line']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = ['message' => $record->message];
|
$message = ['message' => $record['message']];
|
||||||
if (\count($record->context) > 0) {
|
if ($record['context']) {
|
||||||
$message['context'] = $record->context;
|
$message['context'] = $record['context'];
|
||||||
}
|
}
|
||||||
if (\count($record->extra) > 0) {
|
if ($record['extra']) {
|
||||||
$message['extra'] = $record->extra;
|
$message['extra'] = $record['extra'];
|
||||||
}
|
}
|
||||||
if (count($message) === 1) {
|
if (count($message) === 1) {
|
||||||
$message = reset($message);
|
$message = reset($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
$record->channel,
|
$record['channel'],
|
||||||
$message,
|
$message,
|
||||||
$backtrace,
|
$backtrace,
|
||||||
$this->toWildfireLevel($record->level),
|
$this->logLevels[$record['level']],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function formatBatch(array $records)
|
public function formatBatch(array $records)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,24 +12,25 @@
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Elastica\Document;
|
use Elastica\Document;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a log message into an Elastica Document
|
* Format a log message into an Elastica Document
|
||||||
*
|
*
|
||||||
* @author Jelle Vink <jelle.vink@gmail.com>
|
* @author Jelle Vink <jelle.vink@gmail.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class ElasticaFormatter extends NormalizerFormatter
|
class ElasticaFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string Elastic search index name
|
* @var string Elastic search index name
|
||||||
*/
|
*/
|
||||||
protected string $index;
|
protected $index;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string|null Elastic search document type
|
* @var ?string Elastic search document type
|
||||||
*/
|
*/
|
||||||
protected string|null $type;
|
protected $type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $index Elastic Search index name
|
* @param string $index Elastic Search index name
|
||||||
|
@ -45,9 +46,9 @@ class ElasticaFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record)
|
public function format(array $record)
|
||||||
{
|
{
|
||||||
$record = parent::format($record);
|
$record = parent::format($record);
|
||||||
|
|
||||||
|
@ -71,13 +72,14 @@ class ElasticaFormatter extends NormalizerFormatter
|
||||||
/**
|
/**
|
||||||
* Convert a log message into an Elastica Document
|
* Convert a log message into an Elastica Document
|
||||||
*
|
*
|
||||||
* @param mixed[] $record
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
protected function getDocument(array $record): Document
|
protected function getDocument(array $record): Document
|
||||||
{
|
{
|
||||||
$document = new Document();
|
$document = new Document();
|
||||||
$document->setData($record);
|
$document->setData($record);
|
||||||
if (method_exists($document, 'setType')) {
|
if (method_exists($document, 'setType')) {
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
$document->setType($this->type);
|
$document->setType($this->type);
|
||||||
}
|
}
|
||||||
$document->setIndex($this->index);
|
$document->setIndex($this->index);
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a log message into an Elasticsearch record
|
* Format a log message into an Elasticsearch record
|
||||||
|
@ -24,12 +23,12 @@ class ElasticsearchFormatter extends NormalizerFormatter
|
||||||
/**
|
/**
|
||||||
* @var string Elasticsearch index name
|
* @var string Elasticsearch index name
|
||||||
*/
|
*/
|
||||||
protected string $index;
|
protected $index;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string Elasticsearch record type
|
* @var string Elasticsearch record type
|
||||||
*/
|
*/
|
||||||
protected string $type;
|
protected $type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $index Elasticsearch index name
|
* @param string $index Elasticsearch index name
|
||||||
|
@ -45,9 +44,9 @@ class ElasticsearchFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record)
|
public function format(array $record)
|
||||||
{
|
{
|
||||||
$record = parent::format($record);
|
$record = parent::format($record);
|
||||||
|
|
||||||
|
@ -56,6 +55,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter index
|
* Getter index
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getIndex(): string
|
public function getIndex(): string
|
||||||
{
|
{
|
||||||
|
@ -64,6 +65,8 @@ class ElasticsearchFormatter extends NormalizerFormatter
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter type
|
* Getter type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getType(): string
|
public function getType(): string
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,18 +11,23 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* formats the record to be used in the FlowdockHandler
|
* formats the record to be used in the FlowdockHandler
|
||||||
*
|
*
|
||||||
* @author Dominik Liebler <liebler.dominik@gmail.com>
|
* @author Dominik Liebler <liebler.dominik@gmail.com>
|
||||||
|
* @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4
|
||||||
*/
|
*/
|
||||||
class FlowdockFormatter implements FormatterInterface
|
class FlowdockFormatter implements FormatterInterface
|
||||||
{
|
{
|
||||||
private string $source;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $source;
|
||||||
|
|
||||||
private string $sourceEmail;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $sourceEmail;
|
||||||
|
|
||||||
public function __construct(string $source, string $sourceEmail)
|
public function __construct(string $source, string $sourceEmail)
|
||||||
{
|
{
|
||||||
|
@ -31,41 +36,43 @@ class FlowdockFormatter implements FormatterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): array
|
public function format(array $record): array
|
||||||
{
|
{
|
||||||
$tags = [
|
$tags = [
|
||||||
'#logs',
|
'#logs',
|
||||||
'#' . $record->level->toPsrLogLevel(),
|
'#' . strtolower($record['level_name']),
|
||||||
'#' . $record->channel,
|
'#' . $record['channel'],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($record->extra as $value) {
|
foreach ($record['extra'] as $value) {
|
||||||
$tags[] = '#' . $value;
|
$tags[] = '#' . $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
$subject = sprintf(
|
$subject = sprintf(
|
||||||
'in %s: %s - %s',
|
'in %s: %s - %s',
|
||||||
$this->source,
|
$this->source,
|
||||||
$record->level->getName(),
|
$record['level_name'],
|
||||||
$this->getShortMessage($record->message)
|
$this->getShortMessage($record['message'])
|
||||||
);
|
);
|
||||||
|
|
||||||
return [
|
$record['flowdock'] = [
|
||||||
'source' => $this->source,
|
'source' => $this->source,
|
||||||
'from_address' => $this->sourceEmail,
|
'from_address' => $this->sourceEmail,
|
||||||
'subject' => $subject,
|
'subject' => $subject,
|
||||||
'content' => $record->message,
|
'content' => $record['message'],
|
||||||
'tags' => $tags,
|
'tags' => $tags,
|
||||||
'project' => $this->source,
|
'project' => $this->source,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @return mixed[][]
|
* @return mixed[][]
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FluentdFormatter
|
* Class FluentdFormatter
|
||||||
|
@ -40,7 +39,7 @@ class FluentdFormatter implements FormatterInterface
|
||||||
/**
|
/**
|
||||||
* @var bool $levelTag should message level be a part of the fluentd tag
|
* @var bool $levelTag should message level be a part of the fluentd tag
|
||||||
*/
|
*/
|
||||||
protected bool $levelTag = false;
|
protected $levelTag = false;
|
||||||
|
|
||||||
public function __construct(bool $levelTag = false)
|
public function __construct(bool $levelTag = false)
|
||||||
{
|
{
|
||||||
|
@ -56,25 +55,25 @@ class FluentdFormatter implements FormatterInterface
|
||||||
return $this->levelTag;
|
return $this->levelTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function format(LogRecord $record): string
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$tag = $record->channel;
|
$tag = $record['channel'];
|
||||||
if ($this->levelTag) {
|
if ($this->levelTag) {
|
||||||
$tag .= '.' . $record->level->toPsrLogLevel();
|
$tag .= '.' . strtolower($record['level_name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = [
|
$message = [
|
||||||
'message' => $record->message,
|
'message' => $record['message'],
|
||||||
'context' => $record->context,
|
'context' => $record['context'],
|
||||||
'extra' => $record->extra,
|
'extra' => $record['extra'],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!$this->levelTag) {
|
if (!$this->levelTag) {
|
||||||
$message['level'] = $record->level->value;
|
$message['level'] = $record['level'];
|
||||||
$message['level_name'] = $record->level->getName();
|
$message['level_name'] = $record['level_name'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils::jsonEncode([$tag, $record->datetime->getTimestamp(), $message]);
|
return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function formatBatch(array $records): string
|
public function formatBatch(array $records): string
|
||||||
|
|
|
@ -11,28 +11,32 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for formatters
|
* Interface for formatters
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
interface FormatterInterface
|
interface FormatterInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Formats a log record.
|
* Formats a log record.
|
||||||
*
|
*
|
||||||
* @param LogRecord $record A record to format
|
* @param array $record A record to format
|
||||||
* @return mixed The formatted record
|
* @return mixed The formatted record
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record);
|
public function format(array $record);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a set of log records.
|
* Formats a set of log records.
|
||||||
*
|
*
|
||||||
* @param array<LogRecord> $records A set of records to format
|
* @param array $records A set of records to format
|
||||||
* @return mixed The formatted set of records
|
* @return mixed The formatted set of records
|
||||||
|
*
|
||||||
|
* @phpstan-param Record[] $records
|
||||||
*/
|
*/
|
||||||
public function formatBatch(array $records);
|
public function formatBatch(array $records);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,17 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Gelf\Message;
|
use Gelf\Message;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes a log message to GELF
|
* Serializes a log message to GELF
|
||||||
* @see http://docs.graylog.org/en/latest/pages/gelf.html
|
* @see http://docs.graylog.org/en/latest/pages/gelf.html
|
||||||
*
|
*
|
||||||
* @author Matt Lehner <mlehner@gmail.com>
|
* @author Matt Lehner <mlehner@gmail.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class GelfMessageFormatter extends NormalizerFormatter
|
class GelfMessageFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
|
@ -29,39 +30,45 @@ class GelfMessageFormatter extends NormalizerFormatter
|
||||||
/**
|
/**
|
||||||
* @var string the name of the system for the Gelf log message
|
* @var string the name of the system for the Gelf log message
|
||||||
*/
|
*/
|
||||||
protected string $systemName;
|
protected $systemName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string a prefix for 'extra' fields from the Monolog record (optional)
|
* @var string a prefix for 'extra' fields from the Monolog record (optional)
|
||||||
*/
|
*/
|
||||||
protected string $extraPrefix;
|
protected $extraPrefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string a prefix for 'context' fields from the Monolog record (optional)
|
* @var string a prefix for 'context' fields from the Monolog record (optional)
|
||||||
*/
|
*/
|
||||||
protected string $contextPrefix;
|
protected $contextPrefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int max length per field
|
* @var int max length per field
|
||||||
*/
|
*/
|
||||||
protected int $maxLength;
|
protected $maxLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $gelfVersion = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates Monolog log levels to Graylog2 log priorities.
|
* Translates Monolog log levels to Graylog2 log priorities.
|
||||||
|
*
|
||||||
|
* @var array<int, int>
|
||||||
|
*
|
||||||
|
* @phpstan-var array<Level, int>
|
||||||
*/
|
*/
|
||||||
private function getGraylog2Priority(Level $level): int
|
private $logLevels = [
|
||||||
{
|
Logger::DEBUG => 7,
|
||||||
return match ($level) {
|
Logger::INFO => 6,
|
||||||
Level::Debug => 7,
|
Logger::NOTICE => 5,
|
||||||
Level::Info => 6,
|
Logger::WARNING => 4,
|
||||||
Level::Notice => 5,
|
Logger::ERROR => 3,
|
||||||
Level::Warning => 4,
|
Logger::CRITICAL => 2,
|
||||||
Level::Error => 3,
|
Logger::ALERT => 1,
|
||||||
Level::Critical => 2,
|
Logger::EMERGENCY => 0,
|
||||||
Level::Alert => 1,
|
];
|
||||||
Level::Emergency => 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null)
|
public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null)
|
||||||
{
|
{
|
||||||
|
@ -71,53 +78,65 @@ class GelfMessageFormatter extends NormalizerFormatter
|
||||||
|
|
||||||
parent::__construct('U.u');
|
parent::__construct('U.u');
|
||||||
|
|
||||||
$this->systemName = (null === $systemName || $systemName === '') ? (string) gethostname() : $systemName;
|
$this->systemName = (is_null($systemName) || $systemName === '') ? (string) gethostname() : $systemName;
|
||||||
|
|
||||||
$this->extraPrefix = null === $extraPrefix ? '' : $extraPrefix;
|
$this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix;
|
||||||
$this->contextPrefix = $contextPrefix;
|
$this->contextPrefix = $contextPrefix;
|
||||||
$this->maxLength = null === $maxLength ? self::DEFAULT_MAX_LENGTH : $maxLength;
|
$this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength;
|
||||||
|
|
||||||
|
if (method_exists(Message::class, 'setFacility')) {
|
||||||
|
$this->gelfVersion = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): Message
|
public function format(array $record): Message
|
||||||
{
|
{
|
||||||
$context = $extra = [];
|
$context = $extra = [];
|
||||||
if (isset($record->context)) {
|
if (isset($record['context'])) {
|
||||||
/** @var mixed[] $context */
|
/** @var mixed[] $context */
|
||||||
$context = parent::normalize($record->context);
|
$context = parent::normalize($record['context']);
|
||||||
}
|
}
|
||||||
if (isset($record->extra)) {
|
if (isset($record['extra'])) {
|
||||||
/** @var mixed[] $extra */
|
/** @var mixed[] $extra */
|
||||||
$extra = parent::normalize($record->extra);
|
$extra = parent::normalize($record['extra']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($record['datetime'], $record['message'], $record['level'])) {
|
||||||
|
throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given');
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = new Message();
|
$message = new Message();
|
||||||
$message
|
$message
|
||||||
->setTimestamp($record->datetime)
|
->setTimestamp($record['datetime'])
|
||||||
->setShortMessage($record->message)
|
->setShortMessage((string) $record['message'])
|
||||||
->setHost($this->systemName)
|
->setHost($this->systemName)
|
||||||
->setLevel($this->getGraylog2Priority($record->level));
|
->setLevel($this->logLevels[$record['level']]);
|
||||||
|
|
||||||
// message length + system name length + 200 for padding / metadata
|
// message length + system name length + 200 for padding / metadata
|
||||||
$len = 200 + strlen($record->message) + strlen($this->systemName);
|
$len = 200 + strlen((string) $record['message']) + strlen($this->systemName);
|
||||||
|
|
||||||
if ($len > $this->maxLength) {
|
if ($len > $this->maxLength) {
|
||||||
$message->setShortMessage(Utils::substr($record->message, 0, $this->maxLength));
|
$message->setShortMessage(Utils::substr($record['message'], 0, $this->maxLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($record->channel)) {
|
if ($this->gelfVersion === 1) {
|
||||||
$message->setAdditional('facility', $record->channel);
|
if (isset($record['channel'])) {
|
||||||
|
$message->setFacility($record['channel']);
|
||||||
}
|
}
|
||||||
if (isset($extra['line'])) {
|
if (isset($extra['line'])) {
|
||||||
$message->setAdditional('line', $extra['line']);
|
$message->setLine($extra['line']);
|
||||||
unset($extra['line']);
|
unset($extra['line']);
|
||||||
}
|
}
|
||||||
if (isset($extra['file'])) {
|
if (isset($extra['file'])) {
|
||||||
$message->setAdditional('file', $extra['file']);
|
$message->setFile($extra['file']);
|
||||||
unset($extra['file']);
|
unset($extra['file']);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$message->setAdditional('facility', $record['channel']);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($extra as $key => $val) {
|
foreach ($extra as $key => $val) {
|
||||||
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
|
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
|
||||||
|
@ -141,10 +160,13 @@ class GelfMessageFormatter extends NormalizerFormatter
|
||||||
$message->setAdditional($this->contextPrefix . $key, $val);
|
$message->setAdditional($this->contextPrefix . $key, $val);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$message->hasAdditional('file') && isset($context['exception']['file'])) {
|
if ($this->gelfVersion === 1) {
|
||||||
if (1 === preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) {
|
/** @phpstan-ignore-next-line */
|
||||||
$message->setAdditional('file', $matches[1]);
|
if (null === $message->getFile() && isset($context['exception']['file'])) {
|
||||||
$message->setAdditional('line', $matches[2]);
|
if (preg_match("/^(.+):([0-9]+)$/", $context['exception']['file'], $matches)) {
|
||||||
|
$message->setFile($matches[1]);
|
||||||
|
$message->setLine($matches[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,23 +17,24 @@ use Monolog\LogRecord;
|
||||||
/**
|
/**
|
||||||
* Encodes message information into JSON in a format compatible with Cloud logging.
|
* Encodes message information into JSON in a format compatible with Cloud logging.
|
||||||
*
|
*
|
||||||
|
* @see https://cloud.google.com/logging/docs/structured-logging
|
||||||
* @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
|
* @see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
|
||||||
*
|
*
|
||||||
* @author Luís Cobucci <lcobucci@gmail.com>
|
* @author Luís Cobucci <lcobucci@gmail.com>
|
||||||
*/
|
*/
|
||||||
final class GoogleCloudLoggingFormatter extends JsonFormatter
|
final class GoogleCloudLoggingFormatter extends JsonFormatter
|
||||||
{
|
{
|
||||||
protected function normalizeRecord(LogRecord $record): array
|
/** {@inheritdoc} **/
|
||||||
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$normalized = parent::normalizeRecord($record);
|
|
||||||
|
|
||||||
// Re-key level for GCP logging
|
// Re-key level for GCP logging
|
||||||
$normalized['severity'] = $normalized['level_name'];
|
$record['severity'] = $record['level_name'];
|
||||||
$normalized['timestamp'] = $record->datetime->format(DateTimeInterface::RFC3339_EXTENDED);
|
$record['time'] = $record['datetime']->format(DateTimeInterface::RFC3339_EXTENDED);
|
||||||
|
|
||||||
// Remove keys that are not used by GCP
|
// Remove keys that are not used by GCP
|
||||||
unset($normalized['level'], $normalized['level_name'], $normalized['datetime']);
|
unset($record['level'], $record['level_name'], $record['datetime']);
|
||||||
|
|
||||||
return $normalized;
|
return parent::format($record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats incoming records into an HTML table
|
* Formats incoming records into an HTML table
|
||||||
|
@ -26,20 +25,19 @@ class HtmlFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Translates Monolog log levels to html color priorities.
|
* Translates Monolog log levels to html color priorities.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
*/
|
*/
|
||||||
protected function getLevelColor(Level $level): string
|
protected $logLevels = [
|
||||||
{
|
Logger::DEBUG => '#CCCCCC',
|
||||||
return match ($level) {
|
Logger::INFO => '#28A745',
|
||||||
Level::Debug => '#CCCCCC',
|
Logger::NOTICE => '#17A2B8',
|
||||||
Level::Info => '#28A745',
|
Logger::WARNING => '#FFC107',
|
||||||
Level::Notice => '#17A2B8',
|
Logger::ERROR => '#FD7E14',
|
||||||
Level::Warning => '#FFC107',
|
Logger::CRITICAL => '#DC3545',
|
||||||
Level::Error => '#FD7E14',
|
Logger::ALERT => '#821722',
|
||||||
Level::Critical => '#DC3545',
|
Logger::EMERGENCY => '#000000',
|
||||||
Level::Alert => '#821722',
|
];
|
||||||
Level::Emergency => '#000000',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
||||||
|
@ -70,12 +68,14 @@ class HtmlFormatter extends NormalizerFormatter
|
||||||
* Create a HTML h1 tag
|
* Create a HTML h1 tag
|
||||||
*
|
*
|
||||||
* @param string $title Text to be in the h1
|
* @param string $title Text to be in the h1
|
||||||
|
* @param int $level Error level
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function addTitle(string $title, Level $level): string
|
protected function addTitle(string $title, int $level): string
|
||||||
{
|
{
|
||||||
$title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');
|
$title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');
|
||||||
|
|
||||||
return '<h1 style="background: '.$this->getLevelColor($level).';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>';
|
return '<h1 style="background: '.$this->logLevels[$level].';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,25 +83,25 @@ class HtmlFormatter extends NormalizerFormatter
|
||||||
*
|
*
|
||||||
* @return string The formatted record
|
* @return string The formatted record
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): string
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$output = $this->addTitle($record->level->getName(), $record->level);
|
$output = $this->addTitle($record['level_name'], $record['level']);
|
||||||
$output .= '<table cellspacing="1" width="100%" class="monolog-output">';
|
$output .= '<table cellspacing="1" width="100%" class="monolog-output">';
|
||||||
|
|
||||||
$output .= $this->addRow('Message', $record->message);
|
$output .= $this->addRow('Message', (string) $record['message']);
|
||||||
$output .= $this->addRow('Time', $this->formatDate($record->datetime));
|
$output .= $this->addRow('Time', $this->formatDate($record['datetime']));
|
||||||
$output .= $this->addRow('Channel', $record->channel);
|
$output .= $this->addRow('Channel', $record['channel']);
|
||||||
if (\count($record->context) > 0) {
|
if ($record['context']) {
|
||||||
$embeddedTable = '<table cellspacing="1" width="100%">';
|
$embeddedTable = '<table cellspacing="1" width="100%">';
|
||||||
foreach ($record->context as $key => $value) {
|
foreach ($record['context'] as $key => $value) {
|
||||||
$embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
|
$embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
|
||||||
}
|
}
|
||||||
$embeddedTable .= '</table>';
|
$embeddedTable .= '</table>';
|
||||||
$output .= $this->addRow('Context', $embeddedTable, false);
|
$output .= $this->addRow('Context', $embeddedTable, false);
|
||||||
}
|
}
|
||||||
if (\count($record->extra) > 0) {
|
if ($record['extra']) {
|
||||||
$embeddedTable = '<table cellspacing="1" width="100%">';
|
$embeddedTable = '<table cellspacing="1" width="100%">';
|
||||||
foreach ($record->extra as $key => $value) {
|
foreach ($record['extra'] as $key => $value) {
|
||||||
$embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
|
$embeddedTable .= $this->addRow((string) $key, $this->convertToString($value));
|
||||||
}
|
}
|
||||||
$embeddedTable .= '</table>';
|
$embeddedTable .= '</table>';
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Stringable;
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes whatever record data is passed to it as json
|
* Encodes whatever record data is passed to it as json
|
||||||
|
@ -21,6 +19,8 @@ use Monolog\LogRecord;
|
||||||
* This can be useful to log to databases or remote APIs
|
* This can be useful to log to databases or remote APIs
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class JsonFormatter extends NormalizerFormatter
|
class JsonFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
|
@ -28,13 +28,13 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
public const BATCH_MODE_NEWLINES = 2;
|
public const BATCH_MODE_NEWLINES = 2;
|
||||||
|
|
||||||
/** @var self::BATCH_MODE_* */
|
/** @var self::BATCH_MODE_* */
|
||||||
protected int $batchMode;
|
protected $batchMode;
|
||||||
|
/** @var bool */
|
||||||
protected bool $appendNewline;
|
protected $appendNewline;
|
||||||
|
/** @var bool */
|
||||||
protected bool $ignoreEmptyContextAndExtra;
|
protected $ignoreEmptyContextAndExtra;
|
||||||
|
/** @var bool */
|
||||||
protected bool $includeStacktraces = false;
|
protected $includeStacktraces = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param self::BATCH_MODE_* $batchMode
|
* @param self::BATCH_MODE_* $batchMode
|
||||||
|
@ -70,11 +70,11 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): string
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$normalized = parent::format($record);
|
$normalized = $this->normalize($record);
|
||||||
|
|
||||||
if (isset($normalized['context']) && $normalized['context'] === []) {
|
if (isset($normalized['context']) && $normalized['context'] === []) {
|
||||||
if ($this->ignoreEmptyContextAndExtra) {
|
if ($this->ignoreEmptyContextAndExtra) {
|
||||||
|
@ -95,16 +95,23 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function formatBatch(array $records): string
|
public function formatBatch(array $records): string
|
||||||
{
|
{
|
||||||
return match ($this->batchMode) {
|
switch ($this->batchMode) {
|
||||||
static::BATCH_MODE_NEWLINES => $this->formatBatchNewlines($records),
|
case static::BATCH_MODE_NEWLINES:
|
||||||
default => $this->formatBatchJson($records),
|
return $this->formatBatchNewlines($records);
|
||||||
};
|
|
||||||
|
case static::BATCH_MODE_JSON:
|
||||||
|
default:
|
||||||
|
return $this->formatBatchJson($records);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
public function includeStacktraces(bool $include = true): self
|
public function includeStacktraces(bool $include = true): self
|
||||||
{
|
{
|
||||||
$this->includeStacktraces = $include;
|
$this->includeStacktraces = $include;
|
||||||
|
@ -115,7 +122,7 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
/**
|
/**
|
||||||
* Return a JSON-encoded array of records.
|
* Return a JSON-encoded array of records.
|
||||||
*
|
*
|
||||||
* @phpstan-param LogRecord[] $records
|
* @phpstan-param Record[] $records
|
||||||
*/
|
*/
|
||||||
protected function formatBatchJson(array $records): string
|
protected function formatBatchJson(array $records): string
|
||||||
{
|
{
|
||||||
|
@ -126,24 +133,30 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
* Use new lines to separate records instead of a
|
* Use new lines to separate records instead of a
|
||||||
* JSON-encoded array.
|
* JSON-encoded array.
|
||||||
*
|
*
|
||||||
* @phpstan-param LogRecord[] $records
|
* @phpstan-param Record[] $records
|
||||||
*/
|
*/
|
||||||
protected function formatBatchNewlines(array $records): string
|
protected function formatBatchNewlines(array $records): string
|
||||||
{
|
{
|
||||||
|
$instance = $this;
|
||||||
|
|
||||||
$oldNewline = $this->appendNewline;
|
$oldNewline = $this->appendNewline;
|
||||||
$this->appendNewline = false;
|
$this->appendNewline = false;
|
||||||
$formatted = array_map(fn (LogRecord $record) => $this->format($record), $records);
|
array_walk($records, function (&$value, $key) use ($instance) {
|
||||||
|
$value = $instance->format($value);
|
||||||
|
});
|
||||||
$this->appendNewline = $oldNewline;
|
$this->appendNewline = $oldNewline;
|
||||||
|
|
||||||
return implode("\n", $formatted);
|
return implode("\n", $records);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalizes given $data.
|
* Normalizes given $data.
|
||||||
*
|
*
|
||||||
* @return null|scalar|array<mixed[]|scalar|null|object>|object
|
* @param mixed $data
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
protected function normalize(mixed $data, int $depth = 0): mixed
|
protected function normalize($data, int $depth = 0)
|
||||||
{
|
{
|
||||||
if ($depth > $this->maxNormalizeDepth) {
|
if ($depth > $this->maxNormalizeDepth) {
|
||||||
return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization';
|
return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization';
|
||||||
|
@ -179,7 +192,7 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($data instanceof Stringable) {
|
if (method_exists($data, '__toString')) {
|
||||||
return $data->__toString();
|
return $data->__toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +210,7 @@ class JsonFormatter extends NormalizerFormatter
|
||||||
* Normalizes given exception with or without its own stack trace based on
|
* Normalizes given exception with or without its own stack trace based on
|
||||||
* `includeStacktraces` property.
|
* `includeStacktraces` property.
|
||||||
*
|
*
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function normalizeException(Throwable $e, int $depth = 0): array
|
protected function normalizeException(Throwable $e, int $depth = 0): array
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats incoming records into a one-line string
|
* Formats incoming records into a one-line string
|
||||||
|
@ -27,16 +25,22 @@ class LineFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
|
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
|
||||||
|
|
||||||
protected string $format;
|
/** @var string */
|
||||||
protected bool $allowInlineLineBreaks;
|
protected $format;
|
||||||
protected bool $ignoreEmptyContextAndExtra;
|
/** @var bool */
|
||||||
protected bool $includeStacktraces;
|
protected $allowInlineLineBreaks;
|
||||||
protected Closure|null $stacktracesParser = null;
|
/** @var bool */
|
||||||
|
protected $ignoreEmptyContextAndExtra;
|
||||||
|
/** @var bool */
|
||||||
|
protected $includeStacktraces;
|
||||||
|
/** @var ?callable */
|
||||||
|
protected $stacktracesParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $format The format of the message
|
* @param string|null $format The format of the message
|
||||||
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
||||||
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
|
* @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
|
||||||
|
* @param bool $ignoreEmptyContextAndExtra
|
||||||
*/
|
*/
|
||||||
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
|
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false, bool $includeStacktraces = false)
|
||||||
{
|
{
|
||||||
|
@ -47,7 +51,7 @@ class LineFormatter extends NormalizerFormatter
|
||||||
parent::__construct($dateFormat);
|
parent::__construct($dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeStacktraces(bool $include = true, ?Closure $parser = null): self
|
public function includeStacktraces(bool $include = true, ?callable $parser = null): self
|
||||||
{
|
{
|
||||||
$this->includeStacktraces = $include;
|
$this->includeStacktraces = $include;
|
||||||
if ($this->includeStacktraces) {
|
if ($this->includeStacktraces) {
|
||||||
|
@ -73,13 +77,14 @@ class LineFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): string
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$vars = parent::format($record);
|
$vars = parent::format($record);
|
||||||
|
|
||||||
$output = $this->format;
|
$output = $this->format;
|
||||||
|
|
||||||
foreach ($vars['extra'] as $var => $val) {
|
foreach ($vars['extra'] as $var => $val) {
|
||||||
if (false !== strpos($output, '%extra.'.$var.'%')) {
|
if (false !== strpos($output, '%extra.'.$var.'%')) {
|
||||||
$output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
|
$output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
|
||||||
|
@ -95,12 +100,12 @@ class LineFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->ignoreEmptyContextAndExtra) {
|
if ($this->ignoreEmptyContextAndExtra) {
|
||||||
if (\count($vars['context']) === 0) {
|
if (empty($vars['context'])) {
|
||||||
unset($vars['context']);
|
unset($vars['context']);
|
||||||
$output = str_replace('%context%', '', $output);
|
$output = str_replace('%context%', '', $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($vars['extra']) === 0) {
|
if (empty($vars['extra'])) {
|
||||||
unset($vars['extra']);
|
unset($vars['extra']);
|
||||||
$output = str_replace('%extra%', '', $output);
|
$output = str_replace('%extra%', '', $output);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +122,6 @@ class LineFormatter extends NormalizerFormatter
|
||||||
$output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
|
$output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
|
||||||
if (null === $output) {
|
if (null === $output) {
|
||||||
$pcreErrorCode = preg_last_error();
|
$pcreErrorCode = preg_last_error();
|
||||||
|
|
||||||
throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
|
throw new \RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +151,7 @@ class LineFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
$str = $this->formatException($e);
|
$str = $this->formatException($e);
|
||||||
|
|
||||||
if (($previous = $e->getPrevious()) instanceof \Throwable) {
|
if ($previous = $e->getPrevious()) {
|
||||||
do {
|
do {
|
||||||
$depth++;
|
$depth++;
|
||||||
if ($depth > $this->maxNormalizeDepth) {
|
if ($depth > $this->maxNormalizeDepth) {
|
||||||
|
@ -228,7 +232,7 @@ class LineFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
$trace = $e->getTraceAsString();
|
$trace = $e->getTraceAsString();
|
||||||
|
|
||||||
if ($this->stacktracesParser !== null) {
|
if ($this->stacktracesParser) {
|
||||||
$trace = $this->stacktracesParserCustom($trace);
|
$trace = $this->stacktracesParserCustom($trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes message information into JSON in a format compatible with Loggly.
|
* Encodes message information into JSON in a format compatible with Loggly.
|
||||||
*
|
*
|
||||||
|
@ -35,13 +33,13 @@ class LogglyFormatter extends JsonFormatter
|
||||||
* @see https://www.loggly.com/docs/automated-parsing/#json
|
* @see https://www.loggly.com/docs/automated-parsing/#json
|
||||||
* @see \Monolog\Formatter\JsonFormatter::format()
|
* @see \Monolog\Formatter\JsonFormatter::format()
|
||||||
*/
|
*/
|
||||||
protected function normalizeRecord(LogRecord $record): array
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$recordData = parent::normalizeRecord($record);
|
if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTimeInterface)) {
|
||||||
|
$record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO");
|
||||||
|
unset($record["datetime"]);
|
||||||
|
}
|
||||||
|
|
||||||
$recordData["timestamp"] = $record->datetime->format("Y-m-d\TH:i:s.uO");
|
return parent::format($record);
|
||||||
unset($recordData["datetime"]);
|
|
||||||
|
|
||||||
return $recordData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes message information into JSON in a format compatible with Logmatic.
|
* Encodes message information into JSON in a format compatible with Logmatic.
|
||||||
*
|
*
|
||||||
|
@ -22,9 +20,15 @@ class LogmaticFormatter extends JsonFormatter
|
||||||
{
|
{
|
||||||
protected const MARKERS = ["sourcecode", "php"];
|
protected const MARKERS = ["sourcecode", "php"];
|
||||||
|
|
||||||
protected string $hostname = '';
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $hostname = '';
|
||||||
|
|
||||||
protected string $appName = '';
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $appname = '';
|
||||||
|
|
||||||
public function setHostname(string $hostname): self
|
public function setHostname(string $hostname): self
|
||||||
{
|
{
|
||||||
|
@ -33,9 +37,9 @@ class LogmaticFormatter extends JsonFormatter
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setAppName(string $appName): self
|
public function setAppname(string $appname): self
|
||||||
{
|
{
|
||||||
$this->appName = $appName;
|
$this->appname = $appname;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -46,19 +50,17 @@ class LogmaticFormatter extends JsonFormatter
|
||||||
* @see http://doc.logmatic.io/docs/basics-to-send-data
|
* @see http://doc.logmatic.io/docs/basics-to-send-data
|
||||||
* @see \Monolog\Formatter\JsonFormatter::format()
|
* @see \Monolog\Formatter\JsonFormatter::format()
|
||||||
*/
|
*/
|
||||||
public function normalizeRecord(LogRecord $record): array
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$record = parent::normalizeRecord($record);
|
if (!empty($this->hostname)) {
|
||||||
|
|
||||||
if ($this->hostname !== '') {
|
|
||||||
$record["hostname"] = $this->hostname;
|
$record["hostname"] = $this->hostname;
|
||||||
}
|
}
|
||||||
if ($this->appName !== '') {
|
if (!empty($this->appname)) {
|
||||||
$record["appname"] = $this->appName;
|
$record["appname"] = $this->appname;
|
||||||
}
|
}
|
||||||
|
|
||||||
$record["@marker"] = static::MARKERS;
|
$record["@marker"] = static::MARKERS;
|
||||||
|
|
||||||
return $record;
|
return parent::format($record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes a log message to Logstash Event Format
|
* Serializes a log message to Logstash Event Format
|
||||||
*
|
*
|
||||||
|
@ -26,22 +24,22 @@ class LogstashFormatter extends NormalizerFormatter
|
||||||
/**
|
/**
|
||||||
* @var string the name of the system for the Logstash log message, used to fill the @source field
|
* @var string the name of the system for the Logstash log message, used to fill the @source field
|
||||||
*/
|
*/
|
||||||
protected string $systemName;
|
protected $systemName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string an application name for the Logstash log message, used to fill the @type field
|
* @var string an application name for the Logstash log message, used to fill the @type field
|
||||||
*/
|
*/
|
||||||
protected string $applicationName;
|
protected $applicationName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string the key for 'extra' fields from the Monolog record
|
* @var string the key for 'extra' fields from the Monolog record
|
||||||
*/
|
*/
|
||||||
protected string $extraKey;
|
protected $extraKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string the key for 'context' fields from the Monolog record
|
* @var string the key for 'context' fields from the Monolog record
|
||||||
*/
|
*/
|
||||||
protected string $contextKey;
|
protected $contextKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $applicationName The application that sends the data, used as the "type" field of logstash
|
* @param string $applicationName The application that sends the data, used as the "type" field of logstash
|
||||||
|
@ -61,38 +59,41 @@ class LogstashFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): string
|
public function format(array $record): string
|
||||||
{
|
{
|
||||||
$recordData = parent::format($record);
|
$record = parent::format($record);
|
||||||
|
|
||||||
|
if (empty($record['datetime'])) {
|
||||||
|
$record['datetime'] = gmdate('c');
|
||||||
|
}
|
||||||
$message = [
|
$message = [
|
||||||
'@timestamp' => $recordData['datetime'],
|
'@timestamp' => $record['datetime'],
|
||||||
'@version' => 1,
|
'@version' => 1,
|
||||||
'host' => $this->systemName,
|
'host' => $this->systemName,
|
||||||
];
|
];
|
||||||
if (isset($recordData['message'])) {
|
if (isset($record['message'])) {
|
||||||
$message['message'] = $recordData['message'];
|
$message['message'] = $record['message'];
|
||||||
}
|
}
|
||||||
if (isset($recordData['channel'])) {
|
if (isset($record['channel'])) {
|
||||||
$message['type'] = $recordData['channel'];
|
$message['type'] = $record['channel'];
|
||||||
$message['channel'] = $recordData['channel'];
|
$message['channel'] = $record['channel'];
|
||||||
}
|
}
|
||||||
if (isset($recordData['level_name'])) {
|
if (isset($record['level_name'])) {
|
||||||
$message['level'] = $recordData['level_name'];
|
$message['level'] = $record['level_name'];
|
||||||
}
|
}
|
||||||
if (isset($recordData['level'])) {
|
if (isset($record['level'])) {
|
||||||
$message['monolog_level'] = $recordData['level'];
|
$message['monolog_level'] = $record['level'];
|
||||||
}
|
}
|
||||||
if ('' !== $this->applicationName) {
|
if ($this->applicationName) {
|
||||||
$message['type'] = $this->applicationName;
|
$message['type'] = $this->applicationName;
|
||||||
}
|
}
|
||||||
if (\count($recordData['extra']) > 0) {
|
if (!empty($record['extra'])) {
|
||||||
$message[$this->extraKey] = $recordData['extra'];
|
$message[$this->extraKey] = $record['extra'];
|
||||||
}
|
}
|
||||||
if (\count($recordData['context']) > 0) {
|
if (!empty($record['context'])) {
|
||||||
$message[$this->contextKey] = $recordData['context'];
|
$message[$this->contextKey] = $record['context'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->toJson($message) . "\n";
|
return $this->toJson($message) . "\n";
|
||||||
|
|
|
@ -14,7 +14,6 @@ namespace Monolog\Formatter;
|
||||||
use MongoDB\BSON\Type;
|
use MongoDB\BSON\Type;
|
||||||
use MongoDB\BSON\UTCDateTime;
|
use MongoDB\BSON\UTCDateTime;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a record for use with the MongoDBHandler.
|
* Formats a record for use with the MongoDBHandler.
|
||||||
|
@ -23,12 +22,15 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class MongoDBFormatter implements FormatterInterface
|
class MongoDBFormatter implements FormatterInterface
|
||||||
{
|
{
|
||||||
private bool $exceptionTraceAsString;
|
/** @var bool */
|
||||||
private int $maxNestingLevel;
|
private $exceptionTraceAsString;
|
||||||
private bool $isLegacyMongoExt;
|
/** @var int */
|
||||||
|
private $maxNestingLevel;
|
||||||
|
/** @var bool */
|
||||||
|
private $isLegacyMongoExt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record->context is 2
|
* @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
|
||||||
* @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
|
* @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
|
||||||
*/
|
*/
|
||||||
public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true)
|
public function __construct(int $maxNestingLevel = 3, bool $exceptionTraceAsString = true)
|
||||||
|
@ -40,20 +42,20 @@ class MongoDBFormatter implements FormatterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): array
|
public function format(array $record): array
|
||||||
{
|
{
|
||||||
/** @var mixed[] $res */
|
/** @var mixed[] $res */
|
||||||
$res = $this->formatArray($record->toArray());
|
$res = $this->formatArray($record);
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @return array<mixed[]>
|
* @return array<mixed[]>
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,7 +14,6 @@ namespace Monolog\Formatter;
|
||||||
use Monolog\DateTimeImmutable;
|
use Monolog\DateTimeImmutable;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
|
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
|
||||||
|
@ -25,11 +24,15 @@ class NormalizerFormatter implements FormatterInterface
|
||||||
{
|
{
|
||||||
public const SIMPLE_DATE = "Y-m-d\TH:i:sP";
|
public const SIMPLE_DATE = "Y-m-d\TH:i:sP";
|
||||||
|
|
||||||
protected string $dateFormat;
|
/** @var string */
|
||||||
protected int $maxNormalizeDepth = 9;
|
protected $dateFormat;
|
||||||
protected int $maxNormalizeItemCount = 1000;
|
/** @var int */
|
||||||
|
protected $maxNormalizeDepth = 9;
|
||||||
|
/** @var int */
|
||||||
|
protected $maxNormalizeItemCount = 1000;
|
||||||
|
|
||||||
private int $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
|
/** @var int */
|
||||||
|
private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
||||||
|
@ -43,25 +46,17 @@ class NormalizerFormatter implements FormatterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
|
||||||
public function format(LogRecord $record)
|
|
||||||
{
|
|
||||||
return $this->normalizeRecord($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize an arbitrary value to a scalar|array|null
|
|
||||||
*
|
*
|
||||||
* @return null|scalar|array<mixed[]|scalar|null>
|
* @param mixed[] $record
|
||||||
*/
|
*/
|
||||||
public function normalizeValue(mixed $data): mixed
|
public function format(array $record)
|
||||||
{
|
{
|
||||||
return $this->normalize($data);
|
return $this->normalize($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function formatBatch(array $records)
|
public function formatBatch(array $records)
|
||||||
{
|
{
|
||||||
|
@ -129,25 +124,10 @@ class NormalizerFormatter implements FormatterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provided as extension point
|
* @param mixed $data
|
||||||
*
|
* @return null|scalar|array<array|scalar|null>
|
||||||
* Because normalize is called with sub-values of context data etc, normalizeRecord can be
|
|
||||||
* extended when data needs to be appended on the record array but not to other normalized data.
|
|
||||||
*
|
|
||||||
* @return array<mixed[]|scalar|null>
|
|
||||||
*/
|
*/
|
||||||
protected function normalizeRecord(LogRecord $record): array
|
protected function normalize($data, int $depth = 0)
|
||||||
{
|
|
||||||
/** @var array<mixed> $normalized */
|
|
||||||
$normalized = $this->normalize($record->toArray());
|
|
||||||
|
|
||||||
return $normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return null|scalar|array<mixed[]|scalar|null>
|
|
||||||
*/
|
|
||||||
protected function normalize(mixed $data, int $depth = 0): mixed
|
|
||||||
{
|
{
|
||||||
if ($depth > $this->maxNormalizeDepth) {
|
if ($depth > $this->maxNormalizeDepth) {
|
||||||
return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
|
return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization';
|
||||||
|
@ -192,14 +172,14 @@ class NormalizerFormatter implements FormatterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($data instanceof \JsonSerializable) {
|
if ($data instanceof \JsonSerializable) {
|
||||||
/** @var null|scalar|array<mixed[]|scalar|null> $value */
|
/** @var null|scalar|array<array|scalar|null> $value */
|
||||||
$value = $data->jsonSerialize();
|
$value = $data->jsonSerialize();
|
||||||
} elseif (method_exists($data, '__toString')) {
|
} elseif (method_exists($data, '__toString')) {
|
||||||
/** @var string $value */
|
/** @var string $value */
|
||||||
$value = $data->__toString();
|
$value = $data->__toString();
|
||||||
} else {
|
} else {
|
||||||
// the rest is normalized by json encoding and decoding it
|
// the rest is normalized by json encoding and decoding it
|
||||||
/** @var null|scalar|array<mixed[]|scalar|null> $value */
|
/** @var null|scalar|array<array|scalar|null> $value */
|
||||||
$value = json_decode($this->toJson($data, true), true);
|
$value = json_decode($this->toJson($data, true), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,12 +233,12 @@ class NormalizerFormatter implements FormatterInterface
|
||||||
|
|
||||||
$trace = $e->getTrace();
|
$trace = $e->getTrace();
|
||||||
foreach ($trace as $frame) {
|
foreach ($trace as $frame) {
|
||||||
if (isset($frame['file'], $frame['line'])) {
|
if (isset($frame['file'])) {
|
||||||
$data['trace'][] = $frame['file'].':'.$frame['line'];
|
$data['trace'][] = $frame['file'].':'.$frame['line'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($previous = $e->getPrevious()) instanceof \Throwable) {
|
if ($previous = $e->getPrevious()) {
|
||||||
$data['previous'] = $this->normalizeException($previous, $depth + 1);
|
$data['previous'] = $this->normalizeException($previous, $depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +257,10 @@ class NormalizerFormatter implements FormatterInterface
|
||||||
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
|
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatDate(\DateTimeInterface $date): string
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function formatDate(\DateTimeInterface $date)
|
||||||
{
|
{
|
||||||
// in case the date format isn't custom then we defer to the custom DateTimeImmutable
|
// in case the date format isn't custom then we defer to the custom DateTimeImmutable
|
||||||
// formatting logic, which will pick the right format based on whether useMicroseconds is on
|
// formatting logic, which will pick the right format based on whether useMicroseconds is on
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats data into an associative array of scalar (+ null) values.
|
* Formats data into an associative array of scalar values.
|
||||||
* Objects and arrays will be JSON encoded.
|
* Objects and arrays will be JSON encoded.
|
||||||
*
|
*
|
||||||
* @author Andrew Lawson <adlawson@gmail.com>
|
* @author Andrew Lawson <adlawson@gmail.com>
|
||||||
|
@ -22,21 +20,25 @@ use Monolog\LogRecord;
|
||||||
class ScalarFormatter extends NormalizerFormatter
|
class ScalarFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @phpstan-return array<string, scalar|null> $record
|
* @phpstan-return array<string, scalar|null> $record
|
||||||
*/
|
*/
|
||||||
public function format(LogRecord $record): array
|
public function format(array $record): array
|
||||||
{
|
{
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($record->toArray() as $key => $value) {
|
foreach ($record as $key => $value) {
|
||||||
$result[$key] = $this->toScalar($value);
|
$result[$key] = $this->normalizeValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function toScalar(mixed $value): string|int|float|bool|null
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
* @return scalar|null
|
||||||
|
*/
|
||||||
|
protected function normalizeValue($value)
|
||||||
{
|
{
|
||||||
$normalized = $this->normalize($value);
|
$normalized = $this->normalize($value);
|
||||||
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?php declare(strict_types=1);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of the Monolog package.
|
|
||||||
*
|
|
||||||
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes a log message according to RFC 5424
|
|
||||||
*
|
|
||||||
* @author Dalibor Karlović <dalibor.karlovic@sigwin.hr>
|
|
||||||
* @author Renat Gabdullin <renatobyj@gmail.com>
|
|
||||||
*/
|
|
||||||
class SyslogFormatter extends LineFormatter
|
|
||||||
{
|
|
||||||
private const SYSLOG_FACILITY_USER = 1;
|
|
||||||
private const FORMAT = "<%extra.priority%>1 %datetime% %extra.hostname% %extra.app-name% %extra.procid% %channel% %extra.structured-data% %level_name%: %message% %context% %extra%\n";
|
|
||||||
private const NILVALUE = '-';
|
|
||||||
|
|
||||||
private string $hostname;
|
|
||||||
private int $procid;
|
|
||||||
|
|
||||||
public function __construct(private string $applicationName = self::NILVALUE)
|
|
||||||
{
|
|
||||||
parent::__construct(self::FORMAT, 'Y-m-d\TH:i:s.uP', true, true);
|
|
||||||
$this->hostname = (string) gethostname();
|
|
||||||
$this->procid = (int) getmypid();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function format(LogRecord $record): string
|
|
||||||
{
|
|
||||||
$record->extra = $this->formatExtra($record);
|
|
||||||
|
|
||||||
return parent::format($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param LogRecord $record
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
private function formatExtra(LogRecord $record): array
|
|
||||||
{
|
|
||||||
$extra = $record->extra;
|
|
||||||
$extra['app-name'] = $this->applicationName;
|
|
||||||
$extra['hostname'] = $this->hostname;
|
|
||||||
$extra['procid'] = $this->procid;
|
|
||||||
$extra['priority'] = self::calculatePriority($record->level);
|
|
||||||
$extra['structured-data'] = self::NILVALUE;
|
|
||||||
|
|
||||||
return $extra;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function calculatePriority(Level $level): int
|
|
||||||
{
|
|
||||||
return (self::SYSLOG_FACILITY_USER * 8) + $level->toRFC5424Level();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,8 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Formatter;
|
namespace Monolog\Formatter;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes a log message according to Wildfire's header requirements
|
* Serializes a log message according to Wildfire's header requirements
|
||||||
|
@ -20,9 +19,27 @@ use Monolog\LogRecord;
|
||||||
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
|
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
|
||||||
* @author Christophe Coevoet <stof@notk.org>
|
* @author Christophe Coevoet <stof@notk.org>
|
||||||
* @author Kirill chEbba Chebunin <iam@chebba.org>
|
* @author Kirill chEbba Chebunin <iam@chebba.org>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class WildfireFormatter extends NormalizerFormatter
|
class WildfireFormatter extends NormalizerFormatter
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Translates Monolog log levels to Wildfire levels.
|
||||||
|
*
|
||||||
|
* @var array<Level, string>
|
||||||
|
*/
|
||||||
|
private $logLevels = [
|
||||||
|
Logger::DEBUG => 'LOG',
|
||||||
|
Logger::INFO => 'INFO',
|
||||||
|
Logger::NOTICE => 'INFO',
|
||||||
|
Logger::WARNING => 'WARN',
|
||||||
|
Logger::ERROR => 'ERROR',
|
||||||
|
Logger::CRITICAL => 'ERROR',
|
||||||
|
Logger::ALERT => 'ERROR',
|
||||||
|
Logger::EMERGENCY => 'ERROR',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
|
||||||
*/
|
*/
|
||||||
|
@ -35,61 +52,46 @@ class WildfireFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates Monolog log levels to Wildfire levels.
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @return 'LOG'|'INFO'|'WARN'|'ERROR'
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function toWildfireLevel(Level $level): string
|
public function format(array $record): string
|
||||||
{
|
|
||||||
return match ($level) {
|
|
||||||
Level::Debug => 'LOG',
|
|
||||||
Level::Info => 'INFO',
|
|
||||||
Level::Notice => 'INFO',
|
|
||||||
Level::Warning => 'WARN',
|
|
||||||
Level::Error => 'ERROR',
|
|
||||||
Level::Critical => 'ERROR',
|
|
||||||
Level::Alert => 'ERROR',
|
|
||||||
Level::Emergency => 'ERROR',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function format(LogRecord $record): string
|
|
||||||
{
|
{
|
||||||
// Retrieve the line and file if set and remove them from the formatted extra
|
// Retrieve the line and file if set and remove them from the formatted extra
|
||||||
$file = $line = '';
|
$file = $line = '';
|
||||||
if (isset($record->extra['file'])) {
|
if (isset($record['extra']['file'])) {
|
||||||
$file = $record->extra['file'];
|
$file = $record['extra']['file'];
|
||||||
unset($record->extra['file']);
|
unset($record['extra']['file']);
|
||||||
}
|
}
|
||||||
if (isset($record->extra['line'])) {
|
if (isset($record['extra']['line'])) {
|
||||||
$line = $record->extra['line'];
|
$line = $record['extra']['line'];
|
||||||
unset($record->extra['line']);
|
unset($record['extra']['line']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = ['message' => $record->message];
|
/** @var mixed[] $record */
|
||||||
|
$record = $this->normalize($record);
|
||||||
|
$message = ['message' => $record['message']];
|
||||||
$handleError = false;
|
$handleError = false;
|
||||||
if (count($record->context) > 0) {
|
if ($record['context']) {
|
||||||
$message['context'] = $this->normalize($record->context);
|
$message['context'] = $record['context'];
|
||||||
$handleError = true;
|
$handleError = true;
|
||||||
}
|
}
|
||||||
if (count($record->extra) > 0) {
|
if ($record['extra']) {
|
||||||
$message['extra'] = $this->normalize($record->extra);
|
$message['extra'] = $record['extra'];
|
||||||
$handleError = true;
|
$handleError = true;
|
||||||
}
|
}
|
||||||
if (count($message) === 1) {
|
if (count($message) === 1) {
|
||||||
$message = reset($message);
|
$message = reset($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($message) && isset($message['context']['table'])) {
|
if (isset($record['context']['table'])) {
|
||||||
$type = 'TABLE';
|
$type = 'TABLE';
|
||||||
$label = $record->channel .': '. $record->message;
|
$label = $record['channel'] .': '. $record['message'];
|
||||||
$message = $message['context']['table'];
|
$message = $record['context']['table'];
|
||||||
} else {
|
} else {
|
||||||
$type = $this->toWildfireLevel($record->level);
|
$type = $this->logLevels[$record['level']];
|
||||||
$label = $record->channel;
|
$label = $record['channel'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create JSON object describing the appearance of the message in the console
|
// Create JSON object describing the appearance of the message in the console
|
||||||
|
@ -112,7 +114,7 @@ class WildfireFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @phpstan-return never
|
* @phpstan-return never
|
||||||
*/
|
*/
|
||||||
|
@ -122,11 +124,11 @@ class WildfireFormatter extends NormalizerFormatter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @return null|scalar|array<mixed[]|scalar|null>|object
|
* @return null|scalar|array<array|scalar|null>|object
|
||||||
*/
|
*/
|
||||||
protected function normalize(mixed $data, int $depth = 0): mixed
|
protected function normalize($data, int $depth = 0)
|
||||||
{
|
{
|
||||||
if (is_object($data) && !$data instanceof \DateTimeInterface) {
|
if (is_object($data) && !$data instanceof \DateTimeInterface) {
|
||||||
return $data;
|
return $data;
|
||||||
|
|
|
@ -11,50 +11,55 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Handler class providing basic level/bubble support
|
* Base Handler class providing basic level/bubble support
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
abstract class AbstractHandler extends Handler implements ResettableInterface
|
abstract class AbstractHandler extends Handler implements ResettableInterface
|
||||||
{
|
{
|
||||||
protected Level $level = Level::Debug;
|
/**
|
||||||
protected bool $bubble = true;
|
* @var int
|
||||||
|
* @phpstan-var Level
|
||||||
|
*/
|
||||||
|
protected $level = Logger::DEBUG;
|
||||||
|
/** @var bool */
|
||||||
|
protected $bubble = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|string|Level|LogLevel::* $level The minimum logging level at which this handler will be triggered
|
* @param int|string $level The minimum logging level at which this handler will be triggered
|
||||||
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
|
* @phpstan-param Level|LevelName|LogLevel::* $level
|
||||||
*/
|
*/
|
||||||
public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct($level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$this->setLevel($level);
|
$this->setLevel($level);
|
||||||
$this->bubble = $bubble;
|
$this->bubble = $bubble;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return $record->level->value >= $this->level->value;
|
return $record['level'] >= $this->level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets minimum logging level at which this handler will be triggered.
|
* Sets minimum logging level at which this handler will be triggered.
|
||||||
*
|
*
|
||||||
* @param Level|LogLevel::* $level Level or level name
|
* @param Level|LevelName|LogLevel::* $level Level or level name
|
||||||
*
|
* @return self
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
|
|
||||||
*/
|
*/
|
||||||
public function setLevel(int|string|Level $level): self
|
public function setLevel($level): self
|
||||||
{
|
{
|
||||||
$this->level = Logger::toMonologLevel($level);
|
$this->level = Logger::toMonologLevel($level);
|
||||||
|
|
||||||
|
@ -63,8 +68,12 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets minimum logging level at which this handler will be triggered.
|
* Gets minimum logging level at which this handler will be triggered.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*
|
||||||
|
* @phpstan-return Level
|
||||||
*/
|
*/
|
||||||
public function getLevel(): Level
|
public function getLevel(): int
|
||||||
{
|
{
|
||||||
return $this->level;
|
return $this->level;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +83,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
|
||||||
*
|
*
|
||||||
* @param bool $bubble true means that this handler allows bubbling.
|
* @param bool $bubble true means that this handler allows bubbling.
|
||||||
* false means that bubbling is not permitted.
|
* false means that bubbling is not permitted.
|
||||||
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function setBubble(bool $bubble): self
|
public function setBubble(bool $bubble): self
|
||||||
{
|
{
|
||||||
|
@ -94,9 +104,9 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base Handler class providing the Handler structure, including processors and formatters
|
* Base Handler class providing the Handler structure, including processors and formatters
|
||||||
*
|
*
|
||||||
|
@ -20,6 +18,11 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
* @author Christophe Coevoet <stof@notk.org>
|
* @author Christophe Coevoet <stof@notk.org>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-type FormattedRecord array{message: string, context: mixed[], level: Level, level_name: LevelName, channel: string, datetime: \DateTimeImmutable, extra: mixed[], formatted: mixed}
|
||||||
*/
|
*/
|
||||||
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -27,19 +30,20 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
|
||||||
use FormattableHandlerTrait;
|
use FormattableHandlerTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if (!$this->isHandling($record)) {
|
if (!$this->isHandling($record)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
$record->formatted = $this->getFormatter()->format($record);
|
$record['formatted'] = $this->getFormatter()->format($record);
|
||||||
|
|
||||||
$this->write($record);
|
$this->write($record);
|
||||||
|
|
||||||
|
@ -47,11 +51,16 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the (already formatted) record down to the log of the implementing handler
|
* Writes the record down to the log of the implementing handler
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
abstract protected function write(LogRecord $record): void;
|
abstract protected function write(array $record): void;
|
||||||
|
|
||||||
public function reset(): void
|
/**
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function reset()
|
||||||
{
|
{
|
||||||
parent::reset();
|
parent::reset();
|
||||||
|
|
||||||
|
|
|
@ -11,59 +11,70 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common syslog functionality
|
* Common syslog functionality
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
|
abstract class AbstractSyslogHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
protected int $facility;
|
/** @var int */
|
||||||
|
protected $facility;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates Monolog log levels to syslog log priorities.
|
||||||
|
* @var array
|
||||||
|
* @phpstan-var array<Level, int>
|
||||||
|
*/
|
||||||
|
protected $logLevels = [
|
||||||
|
Logger::DEBUG => LOG_DEBUG,
|
||||||
|
Logger::INFO => LOG_INFO,
|
||||||
|
Logger::NOTICE => LOG_NOTICE,
|
||||||
|
Logger::WARNING => LOG_WARNING,
|
||||||
|
Logger::ERROR => LOG_ERR,
|
||||||
|
Logger::CRITICAL => LOG_CRIT,
|
||||||
|
Logger::ALERT => LOG_ALERT,
|
||||||
|
Logger::EMERGENCY => LOG_EMERG,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of valid log facility names.
|
* List of valid log facility names.
|
||||||
* @var array<string, int>
|
* @var array<string, int>
|
||||||
*/
|
*/
|
||||||
protected array $facilities = [
|
protected $facilities = [
|
||||||
'auth' => \LOG_AUTH,
|
'auth' => LOG_AUTH,
|
||||||
'authpriv' => \LOG_AUTHPRIV,
|
'authpriv' => LOG_AUTHPRIV,
|
||||||
'cron' => \LOG_CRON,
|
'cron' => LOG_CRON,
|
||||||
'daemon' => \LOG_DAEMON,
|
'daemon' => LOG_DAEMON,
|
||||||
'kern' => \LOG_KERN,
|
'kern' => LOG_KERN,
|
||||||
'lpr' => \LOG_LPR,
|
'lpr' => LOG_LPR,
|
||||||
'mail' => \LOG_MAIL,
|
'mail' => LOG_MAIL,
|
||||||
'news' => \LOG_NEWS,
|
'news' => LOG_NEWS,
|
||||||
'syslog' => \LOG_SYSLOG,
|
'syslog' => LOG_SYSLOG,
|
||||||
'user' => \LOG_USER,
|
'user' => LOG_USER,
|
||||||
'uucp' => \LOG_UUCP,
|
'uucp' => LOG_UUCP,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates Monolog log levels to syslog log priorities.
|
|
||||||
*/
|
|
||||||
protected function toSyslogPriority(Level $level): int
|
|
||||||
{
|
|
||||||
return $level->toRFC5424Level();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
|
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant
|
||||||
*/
|
*/
|
||||||
public function __construct(string|int $facility = \LOG_USER, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct($facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||||
$this->facilities['local0'] = \LOG_LOCAL0;
|
$this->facilities['local0'] = LOG_LOCAL0;
|
||||||
$this->facilities['local1'] = \LOG_LOCAL1;
|
$this->facilities['local1'] = LOG_LOCAL1;
|
||||||
$this->facilities['local2'] = \LOG_LOCAL2;
|
$this->facilities['local2'] = LOG_LOCAL2;
|
||||||
$this->facilities['local3'] = \LOG_LOCAL3;
|
$this->facilities['local3'] = LOG_LOCAL3;
|
||||||
$this->facilities['local4'] = \LOG_LOCAL4;
|
$this->facilities['local4'] = LOG_LOCAL4;
|
||||||
$this->facilities['local5'] = \LOG_LOCAL5;
|
$this->facilities['local5'] = LOG_LOCAL5;
|
||||||
$this->facilities['local6'] = \LOG_LOCAL6;
|
$this->facilities['local6'] = LOG_LOCAL6;
|
||||||
$this->facilities['local7'] = \LOG_LOCAL7;
|
$this->facilities['local7'] = LOG_LOCAL7;
|
||||||
} else {
|
} else {
|
||||||
$this->facilities['local0'] = 128; // LOG_LOCAL0
|
$this->facilities['local0'] = 128; // LOG_LOCAL0
|
||||||
$this->facilities['local1'] = 136; // LOG_LOCAL1
|
$this->facilities['local1'] = 136; // LOG_LOCAL1
|
||||||
|
@ -86,7 +97,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,38 +11,24 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\JsonFormatter;
|
use Monolog\Formatter\JsonFormatter;
|
||||||
use PhpAmqpLib\Message\AMQPMessage;
|
use PhpAmqpLib\Message\AMQPMessage;
|
||||||
use PhpAmqpLib\Channel\AMQPChannel;
|
use PhpAmqpLib\Channel\AMQPChannel;
|
||||||
use AMQPExchange;
|
use AMQPExchange;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
class AmqpHandler extends AbstractProcessingHandler
|
|
||||||
{
|
|
||||||
protected AMQPExchange|AMQPChannel $exchange;
|
|
||||||
|
|
||||||
/** @var array<string, mixed> */
|
|
||||||
private array $extraAttributes = [];
|
|
||||||
|
|
||||||
protected string $exchangeName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
* @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only
|
|
||||||
*/
|
*/
|
||||||
public function __construct(AMQPExchange|AMQPChannel $exchange, ?string $exchangeName = null, int|string|Level $level = Level::Debug, bool $bubble = true)
|
class AmqpHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
if ($exchange instanceof AMQPChannel) {
|
/**
|
||||||
$this->exchangeName = (string) $exchangeName;
|
* @var AMQPExchange|AMQPChannel $exchange
|
||||||
} elseif ($exchangeName !== null) {
|
*/
|
||||||
@trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED);
|
protected $exchange;
|
||||||
}
|
/** @var array<string, mixed> */
|
||||||
$this->exchange = $exchange;
|
private $extraAttributes = [];
|
||||||
|
|
||||||
parent::__construct($level, $bubble);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
|
@ -59,7 +45,7 @@ class AmqpHandler extends AbstractProcessingHandler
|
||||||
* message_id, user_id, app_id, delivery_mode,
|
* message_id, user_id, app_id, delivery_mode,
|
||||||
* priority, timestamp, expiration, type
|
* priority, timestamp, expiration, type
|
||||||
* or reply_to, headers.
|
* or reply_to, headers.
|
||||||
* @return $this
|
* @return AmqpHandler
|
||||||
*/
|
*/
|
||||||
public function setExtraAttributes(array $extraAttributes): self
|
public function setExtraAttributes(array $extraAttributes): self
|
||||||
{
|
{
|
||||||
|
@ -68,11 +54,34 @@ class AmqpHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected $exchangeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use
|
||||||
|
* @param string|null $exchangeName Optional exchange name, for AMQPChannel (PhpAmqpLib) only
|
||||||
|
*/
|
||||||
|
public function __construct($exchange, ?string $exchangeName = null, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$data = $record->formatted;
|
if ($exchange instanceof AMQPChannel) {
|
||||||
|
$this->exchangeName = (string) $exchangeName;
|
||||||
|
} elseif (!$exchange instanceof AMQPExchange) {
|
||||||
|
throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required');
|
||||||
|
} elseif ($exchangeName) {
|
||||||
|
@trigger_error('The $exchangeName parameter can only be passed when using PhpAmqpLib, if using an AMQPExchange instance configure it beforehand', E_USER_DEPRECATED);
|
||||||
|
}
|
||||||
|
$this->exchange = $exchange;
|
||||||
|
|
||||||
|
parent::__construct($level, $bubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected function write(array $record): void
|
||||||
|
{
|
||||||
|
$data = $record["formatted"];
|
||||||
$routingKey = $this->getRoutingKey($record);
|
$routingKey = $this->getRoutingKey($record);
|
||||||
|
|
||||||
if ($this->exchange instanceof AMQPExchange) {
|
if ($this->exchange instanceof AMQPExchange) {
|
||||||
|
@ -80,7 +89,7 @@ class AmqpHandler extends AbstractProcessingHandler
|
||||||
'delivery_mode' => 2,
|
'delivery_mode' => 2,
|
||||||
'content_type' => 'application/json',
|
'content_type' => 'application/json',
|
||||||
];
|
];
|
||||||
if (\count($this->extraAttributes) > 0) {
|
if ($this->extraAttributes) {
|
||||||
$attributes = array_merge($attributes, $this->extraAttributes);
|
$attributes = array_merge($attributes, $this->extraAttributes);
|
||||||
}
|
}
|
||||||
$this->exchange->publish(
|
$this->exchange->publish(
|
||||||
|
@ -99,7 +108,7 @@ class AmqpHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
@ -114,6 +123,7 @@ class AmqpHandler extends AbstractProcessingHandler
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
$data = $this->getFormatter()->format($record);
|
$data = $this->getFormatter()->format($record);
|
||||||
|
|
||||||
|
@ -129,27 +139,30 @@ class AmqpHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the routing key for the AMQP exchange
|
* Gets the routing key for the AMQP exchange
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
protected function getRoutingKey(LogRecord $record): string
|
protected function getRoutingKey(array $record): string
|
||||||
{
|
{
|
||||||
$routingKey = sprintf('%s.%s', $record->level->name, $record->channel);
|
$routingKey = sprintf('%s.%s', $record['level_name'], $record['channel']);
|
||||||
|
|
||||||
return strtolower($routingKey);
|
return strtolower($routingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createAmqpMessage(string $data): AMQPMessage
|
private function createAmqpMessage(string $data): AMQPMessage
|
||||||
{
|
{
|
||||||
return new AMQPMessage(
|
$attributes = [
|
||||||
$data,
|
|
||||||
[
|
|
||||||
'delivery_mode' => 2,
|
'delivery_mode' => 2,
|
||||||
'content_type' => 'application/json',
|
'content_type' => 'application/json',
|
||||||
]
|
];
|
||||||
);
|
if ($this->extraAttributes) {
|
||||||
|
$attributes = array_merge($attributes, $this->extraAttributes);
|
||||||
|
}
|
||||||
|
return new AMQPMessage($data, $attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,30 +14,35 @@ namespace Monolog\Handler;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
use Monolog\Logger;
|
||||||
|
|
||||||
use function count;
|
use function count;
|
||||||
use function headers_list;
|
use function headers_list;
|
||||||
use function stripos;
|
use function stripos;
|
||||||
|
use function trigger_error;
|
||||||
|
|
||||||
|
use const E_USER_DEPRECATED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler sending logs to browser's javascript console with no browser extension required
|
* Handler sending logs to browser's javascript console with no browser extension required
|
||||||
*
|
*
|
||||||
* @author Olivier Poitrey <rs@dailymotion.com>
|
* @author Olivier Poitrey <rs@dailymotion.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class BrowserConsoleHandler extends AbstractProcessingHandler
|
class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
protected static bool $initialized = false;
|
/** @var bool */
|
||||||
|
protected static $initialized = false;
|
||||||
/** @var LogRecord[] */
|
/** @var FormattedRecord[] */
|
||||||
protected static array $records = [];
|
protected static $records = [];
|
||||||
|
|
||||||
protected const FORMAT_HTML = 'html';
|
protected const FORMAT_HTML = 'html';
|
||||||
protected const FORMAT_JS = 'js';
|
protected const FORMAT_JS = 'js';
|
||||||
protected const FORMAT_UNKNOWN = 'unknown';
|
protected const FORMAT_UNKNOWN = 'unknown';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
|
* Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format.
|
||||||
*
|
*
|
||||||
|
@ -51,9 +56,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
// Accumulate records
|
// Accumulate records
|
||||||
static::$records[] = $record;
|
static::$records[] = $record;
|
||||||
|
@ -76,11 +81,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count(static::$records) > 0) {
|
if (count(static::$records)) {
|
||||||
if ($format === self::FORMAT_HTML) {
|
if ($format === self::FORMAT_HTML) {
|
||||||
static::writeOutput('<script>' . self::generateScript() . '</script>');
|
static::writeOutput('<script>' . static::generateScript() . '</script>');
|
||||||
} else { // js format
|
} elseif ($format === self::FORMAT_JS) {
|
||||||
static::writeOutput(self::generateScript());
|
static::writeOutput(static::generateScript());
|
||||||
}
|
}
|
||||||
static::resetStatic();
|
static::resetStatic();
|
||||||
}
|
}
|
||||||
|
@ -91,7 +96,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
self::resetStatic();
|
self::resetStatic();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
parent::reset();
|
parent::reset();
|
||||||
|
|
||||||
|
@ -169,18 +174,18 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
$script = [];
|
$script = [];
|
||||||
foreach (static::$records as $record) {
|
foreach (static::$records as $record) {
|
||||||
$context = self::dump('Context', $record->context);
|
$context = static::dump('Context', $record['context']);
|
||||||
$extra = self::dump('Extra', $record->extra);
|
$extra = static::dump('Extra', $record['extra']);
|
||||||
|
|
||||||
if (\count($context) === 0 && \count($extra) === 0) {
|
if (empty($context) && empty($extra)) {
|
||||||
$script[] = self::call_array('log', self::handleStyles($record->formatted));
|
$script[] = static::call_array(static::getConsoleMethodForLevel($record['level']), static::handleStyles($record['formatted']));
|
||||||
} else {
|
} else {
|
||||||
$script = array_merge(
|
$script = array_merge(
|
||||||
$script,
|
$script,
|
||||||
[self::call_array('groupCollapsed', self::handleStyles($record->formatted))],
|
[static::call_array('groupCollapsed', static::handleStyles($record['formatted']))],
|
||||||
$context,
|
$context,
|
||||||
$extra,
|
$extra,
|
||||||
[self::call('groupEnd')]
|
[static::call('groupEnd')]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,6 +193,20 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
|
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function getConsoleMethodForLevel(int $level): string
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Logger::DEBUG => 'debug',
|
||||||
|
Logger::INFO => 'info',
|
||||||
|
Logger::NOTICE => 'info',
|
||||||
|
Logger::WARNING => 'warn',
|
||||||
|
Logger::ERROR => 'error',
|
||||||
|
Logger::CRITICAL => 'error',
|
||||||
|
Logger::ALERT => 'error',
|
||||||
|
Logger::EMERGENCY => 'error',
|
||||||
|
][$level] ?? 'log';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
|
@ -199,14 +218,14 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
foreach (array_reverse($matches) as $match) {
|
foreach (array_reverse($matches) as $match) {
|
||||||
$args[] = '"font-weight: normal"';
|
$args[] = '"font-weight: normal"';
|
||||||
$args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0]));
|
$args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
|
||||||
|
|
||||||
$pos = $match[0][1];
|
$pos = $match[0][1];
|
||||||
$format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0]));
|
$format = Utils::substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . Utils::substr($format, $pos + strlen($match[0][0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$args[] = self::quote('font-weight: normal');
|
$args[] = static::quote('font-weight: normal');
|
||||||
$args[] = self::quote($format);
|
$args[] = static::quote($format);
|
||||||
|
|
||||||
return array_reverse($args);
|
return array_reverse($args);
|
||||||
}
|
}
|
||||||
|
@ -232,7 +251,6 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
if (null === $style) {
|
if (null === $style) {
|
||||||
$pcreErrorCode = preg_last_error();
|
$pcreErrorCode = preg_last_error();
|
||||||
|
|
||||||
throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
|
throw new \RuntimeException('Failed to run preg_replace_callback: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,16 +265,16 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
$script = [];
|
$script = [];
|
||||||
$dict = array_filter($dict);
|
$dict = array_filter($dict);
|
||||||
if (\count($dict) === 0) {
|
if (empty($dict)) {
|
||||||
return $script;
|
return $script;
|
||||||
}
|
}
|
||||||
$script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title));
|
$script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title));
|
||||||
foreach ($dict as $key => $value) {
|
foreach ($dict as $key => $value) {
|
||||||
$value = json_encode($value);
|
$value = json_encode($value);
|
||||||
if (empty($value)) {
|
if (empty($value)) {
|
||||||
$value = self::quote('');
|
$value = static::quote('');
|
||||||
}
|
}
|
||||||
$script[] = self::call('log', self::quote('%s: %o'), self::quote((string) $key), $value);
|
$script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $script;
|
return $script;
|
||||||
|
@ -277,7 +295,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
|
||||||
throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
|
throw new \UnexpectedValueException('Expected the first arg to be a string, got: '.var_export($method, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::call_array($method, $args);
|
return static::call_array($method, $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffers all records until closing the handler and then pass them as batch.
|
* Buffers all records until closing the handler and then pass them as batch.
|
||||||
|
@ -23,30 +22,32 @@ use Monolog\LogRecord;
|
||||||
* sending one per log message.
|
* sending one per log message.
|
||||||
*
|
*
|
||||||
* @author Christophe Coevoet <stof@notk.org>
|
* @author Christophe Coevoet <stof@notk.org>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
use ProcessableHandlerTrait;
|
use ProcessableHandlerTrait;
|
||||||
|
|
||||||
protected HandlerInterface $handler;
|
/** @var HandlerInterface */
|
||||||
|
protected $handler;
|
||||||
protected int $bufferSize = 0;
|
/** @var int */
|
||||||
|
protected $bufferSize = 0;
|
||||||
protected int $bufferLimit;
|
/** @var int */
|
||||||
|
protected $bufferLimit;
|
||||||
protected bool $flushOnOverflow;
|
/** @var bool */
|
||||||
|
protected $flushOnOverflow;
|
||||||
/** @var LogRecord[] */
|
/** @var Record[] */
|
||||||
protected array $buffer = [];
|
protected $buffer = [];
|
||||||
|
/** @var bool */
|
||||||
protected bool $initialized = false;
|
protected $initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HandlerInterface $handler Handler.
|
* @param HandlerInterface $handler Handler.
|
||||||
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
|
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
|
||||||
* @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
|
* @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
|
||||||
*/
|
*/
|
||||||
public function __construct(HandlerInterface $handler, int $bufferLimit = 0, int|string|Level $level = Level::Debug, bool $bubble = true, bool $flushOnOverflow = false)
|
public function __construct(HandlerInterface $handler, int $bufferLimit = 0, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
$this->handler = $handler;
|
$this->handler = $handler;
|
||||||
|
@ -55,11 +56,11 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if ($record->level->isLowerThan($this->level)) {
|
if ($record['level'] < $this->level) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,8 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +108,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
@ -124,7 +126,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||||
$this->buffer = [];
|
$this->buffer = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
$this->flush();
|
$this->flush();
|
||||||
|
|
||||||
|
@ -138,7 +140,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -152,7 +154,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,10 +13,8 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\ChromePHPFormatter;
|
use Monolog\Formatter\ChromePHPFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
use Monolog\DateTimeImmutable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
|
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/)
|
||||||
|
@ -24,6 +22,8 @@ use Monolog\DateTimeImmutable;
|
||||||
* This also works out of the box with Firefox 43+
|
* This also works out of the box with Firefox 43+
|
||||||
*
|
*
|
||||||
* @author Christophe Coevoet <stof@notk.org>
|
* @author Christophe Coevoet <stof@notk.org>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class ChromePHPHandler extends AbstractProcessingHandler
|
class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
|
@ -44,25 +44,29 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
|
protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
|
||||||
|
|
||||||
protected static bool $initialized = false;
|
/** @var bool */
|
||||||
|
protected static $initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks whether we sent too much data
|
* Tracks whether we sent too much data
|
||||||
*
|
*
|
||||||
* Chrome limits the headers to 4KB, so when we sent 3KB we stop sending
|
* Chrome limits the headers to 4KB, so when we sent 3KB we stop sending
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected static bool $overflowed = false;
|
protected static $overflowed = false;
|
||||||
|
|
||||||
/** @var mixed[] */
|
/** @var mixed[] */
|
||||||
protected static array $json = [
|
protected static $json = [
|
||||||
'version' => self::VERSION,
|
'version' => self::VERSION,
|
||||||
'columns' => ['label', 'log', 'backtrace', 'type'],
|
'columns' => ['label', 'log', 'backtrace', 'type'],
|
||||||
'rows' => [],
|
'rows' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
protected static bool $sendHeaders = true;
|
/** @var bool */
|
||||||
|
protected static $sendHeaders = true;
|
||||||
|
|
||||||
public function __construct(int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct($level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
if (!function_exists('json_encode')) {
|
if (!function_exists('json_encode')) {
|
||||||
|
@ -71,7 +75,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
@ -82,15 +86,15 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
$messages = [];
|
$messages = [];
|
||||||
|
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
if ($record->level < $this->level) {
|
if ($record['level'] < $this->level) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/** @var Record $message */
|
||||||
$message = $this->processRecord($record);
|
$message = $this->processRecord($record);
|
||||||
$messages[] = $message;
|
$messages[] = $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($messages) > 0) {
|
if (!empty($messages)) {
|
||||||
$messages = $this->getFormatter()->formatBatch($messages);
|
$messages = $this->getFormatter()->formatBatch($messages);
|
||||||
self::$json['rows'] = array_merge(self::$json['rows'], $messages);
|
self::$json['rows'] = array_merge(self::$json['rows'], $messages);
|
||||||
$this->send();
|
$this->send();
|
||||||
|
@ -98,7 +102,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -111,13 +115,13 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
* @see sendHeader()
|
* @see sendHeader()
|
||||||
* @see send()
|
* @see send()
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (!$this->isWebRequest()) {
|
if (!$this->isWebRequest()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$json['rows'][] = $record->formatted;
|
self::$json['rows'][] = $record['formatted'];
|
||||||
|
|
||||||
$this->send();
|
$this->send();
|
||||||
}
|
}
|
||||||
|
@ -149,12 +153,15 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
if (strlen($data) > 3 * 1024) {
|
if (strlen($data) > 3 * 1024) {
|
||||||
self::$overflowed = true;
|
self::$overflowed = true;
|
||||||
|
|
||||||
$record = new LogRecord(
|
$record = [
|
||||||
message: 'Incomplete logs, chrome header size limit reached',
|
'message' => 'Incomplete logs, chrome header size limit reached',
|
||||||
level: Level::Warning,
|
'context' => [],
|
||||||
channel: 'monolog',
|
'level' => Logger::WARNING,
|
||||||
datetime: new DateTimeImmutable(true),
|
'level_name' => Logger::getLevelName(Logger::WARNING),
|
||||||
);
|
'channel' => 'monolog',
|
||||||
|
'datetime' => new \DateTimeImmutable(),
|
||||||
|
'extra' => [],
|
||||||
|
];
|
||||||
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
|
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record);
|
||||||
$json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true);
|
$json = Utils::jsonEncode(self::$json, Utils::DEFAULT_JSON_FLAGS & ~JSON_UNESCAPED_UNICODE, true);
|
||||||
$data = base64_encode($json);
|
$data = base64_encode($json);
|
||||||
|
@ -180,7 +187,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
protected function headersAccepted(): bool
|
protected function headersAccepted(): bool
|
||||||
{
|
{
|
||||||
if (!isset($_SERVER['HTTP_USER_AGENT'])) {
|
if (empty($_SERVER['HTTP_USER_AGENT'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,42 +13,22 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\JsonFormatter;
|
use Monolog\Formatter\JsonFormatter;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CouchDB handler
|
* CouchDB handler
|
||||||
*
|
*
|
||||||
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
* @author Markus Bachmann <markus.bachmann@bachi.biz>
|
||||||
* @phpstan-type Options array{
|
|
||||||
* host: string,
|
|
||||||
* port: int,
|
|
||||||
* dbname: string,
|
|
||||||
* username: string|null,
|
|
||||||
* password: string|null
|
|
||||||
* }
|
|
||||||
* @phpstan-type InputOptions array{
|
|
||||||
* host?: string,
|
|
||||||
* port?: int,
|
|
||||||
* dbname?: string,
|
|
||||||
* username?: string|null,
|
|
||||||
* password?: string|null
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
class CouchDBHandler extends AbstractProcessingHandler
|
class CouchDBHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/**
|
/** @var mixed[] */
|
||||||
* @var mixed[]
|
private $options;
|
||||||
* @phpstan-var Options
|
|
||||||
*/
|
|
||||||
private array $options;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed[] $options
|
* @param mixed[] $options
|
||||||
*
|
|
||||||
* @phpstan-param InputOptions $options
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$this->options = array_merge([
|
$this->options = array_merge([
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
|
@ -62,12 +42,12 @@ class CouchDBHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$basicAuth = null;
|
$basicAuth = null;
|
||||||
if (null !== $this->options['username'] && null !== $this->options['password']) {
|
if ($this->options['username']) {
|
||||||
$basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
|
$basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +55,7 @@ class CouchDBHandler extends AbstractProcessingHandler
|
||||||
$context = stream_context_create([
|
$context = stream_context_create([
|
||||||
'http' => [
|
'http' => [
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'content' => $record->formatted,
|
'content' => $record['formatted'],
|
||||||
'ignore_errors' => true,
|
'ignore_errors' => true,
|
||||||
'max_redirects' => 0,
|
'max_redirects' => 0,
|
||||||
'header' => 'Content-type: application/json',
|
'header' => 'Content-type: application/json',
|
||||||
|
@ -88,7 +68,7 @@ class CouchDBHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs to Cube.
|
* Logs to Cube.
|
||||||
|
@ -24,13 +23,18 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class CubeHandler extends AbstractProcessingHandler
|
class CubeHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
private ?\Socket $udpConnection = null;
|
/** @var resource|\Socket|null */
|
||||||
private ?\CurlHandle $httpConnection = null;
|
private $udpConnection = null;
|
||||||
private string $scheme;
|
/** @var resource|\CurlHandle|null */
|
||||||
private string $host;
|
private $httpConnection = null;
|
||||||
private int $port;
|
/** @var string */
|
||||||
|
private $scheme;
|
||||||
|
/** @var string */
|
||||||
|
private $host;
|
||||||
|
/** @var int */
|
||||||
|
private $port;
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
private array $acceptedSchemes = ['http', 'udp'];
|
private $acceptedSchemes = ['http', 'udp'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Cube handler
|
* Create a Cube handler
|
||||||
|
@ -39,7 +43,7 @@ class CubeHandler extends AbstractProcessingHandler
|
||||||
* A valid url must consist of three parts : protocol://host:port
|
* A valid url must consist of three parts : protocol://host:port
|
||||||
* Only valid protocols used by Cube are http and udp
|
* Only valid protocols used by Cube are http and udp
|
||||||
*/
|
*/
|
||||||
public function __construct(string $url, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(string $url, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$urlInfo = parse_url($url);
|
$urlInfo = parse_url($url);
|
||||||
|
|
||||||
|
@ -47,7 +51,7 @@ class CubeHandler extends AbstractProcessingHandler
|
||||||
throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
|
throw new \UnexpectedValueException('URL "'.$url.'" is not valid');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($urlInfo['scheme'], $this->acceptedSchemes, true)) {
|
if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) {
|
||||||
throw new \UnexpectedValueException(
|
throw new \UnexpectedValueException(
|
||||||
'Invalid protocol (' . $urlInfo['scheme'] . ').'
|
'Invalid protocol (' . $urlInfo['scheme'] . ').'
|
||||||
. ' Valid options are ' . implode(', ', $this->acceptedSchemes)
|
. ' Valid options are ' . implode(', ', $this->acceptedSchemes)
|
||||||
|
@ -56,7 +60,7 @@ class CubeHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
$this->scheme = $urlInfo['scheme'];
|
$this->scheme = $urlInfo['scheme'];
|
||||||
$this->host = $urlInfo['host'];
|
$this->host = $urlInfo['host'];
|
||||||
$this->port = $urlInfo['port'];
|
$this->port = (int) $urlInfo['port'];
|
||||||
|
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
}
|
}
|
||||||
|
@ -107,24 +111,24 @@ class CubeHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$date = $record->datetime;
|
$date = $record['datetime'];
|
||||||
|
|
||||||
$data = ['time' => $date->format('Y-m-d\TH:i:s.uO')];
|
$data = ['time' => $date->format('Y-m-d\TH:i:s.uO')];
|
||||||
$context = $record->context;
|
unset($record['datetime']);
|
||||||
|
|
||||||
if (isset($context['type'])) {
|
if (isset($record['context']['type'])) {
|
||||||
$data['type'] = $context['type'];
|
$data['type'] = $record['context']['type'];
|
||||||
unset($context['type']);
|
unset($record['context']['type']);
|
||||||
} else {
|
} else {
|
||||||
$data['type'] = $record->channel;
|
$data['type'] = $record['channel'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$data['data'] = $context;
|
$data['data'] = $record['context'];
|
||||||
$data['data']['level'] = $record->level;
|
$data['data']['level'] = $record['level'];
|
||||||
|
|
||||||
if ($this->scheme === 'http') {
|
if ($this->scheme === 'http') {
|
||||||
$this->writeHttp(Utils::jsonEncode($data));
|
$this->writeHttp(Utils::jsonEncode($data));
|
||||||
|
@ -135,20 +139,16 @@ class CubeHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
private function writeUdp(string $data): void
|
private function writeUdp(string $data): void
|
||||||
{
|
{
|
||||||
if (null === $this->udpConnection) {
|
if (!$this->udpConnection) {
|
||||||
$this->connectUdp();
|
$this->connectUdp();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->udpConnection) {
|
|
||||||
throw new \LogicException('No UDP socket could be opened');
|
|
||||||
}
|
|
||||||
|
|
||||||
socket_send($this->udpConnection, $data, strlen($data), 0);
|
socket_send($this->udpConnection, $data, strlen($data), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function writeHttp(string $data): void
|
private function writeHttp(string $data): void
|
||||||
{
|
{
|
||||||
if (null === $this->httpConnection) {
|
if (!$this->httpConnection) {
|
||||||
$this->connectHttp();
|
$this->connectHttp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ use CurlHandle;
|
||||||
final class Util
|
final class Util
|
||||||
{
|
{
|
||||||
/** @var array<int> */
|
/** @var array<int> */
|
||||||
private static array $retriableErrorCodes = [
|
private static $retriableErrorCodes = [
|
||||||
CURLE_COULDNT_RESOLVE_HOST,
|
CURLE_COULDNT_RESOLVE_HOST,
|
||||||
CURLE_COULDNT_CONNECT,
|
CURLE_COULDNT_CONNECT,
|
||||||
CURLE_HTTP_NOT_FOUND,
|
CURLE_HTTP_NOT_FOUND,
|
||||||
|
@ -34,17 +34,19 @@ final class Util
|
||||||
/**
|
/**
|
||||||
* Executes a CURL request with optional retries and exception on failure
|
* Executes a CURL request with optional retries and exception on failure
|
||||||
*
|
*
|
||||||
* @param CurlHandle $ch curl handler
|
* @param resource|CurlHandle $ch curl handler
|
||||||
|
* @param int $retries
|
||||||
|
* @param bool $closeAfterDone
|
||||||
* @return bool|string @see curl_exec
|
* @return bool|string @see curl_exec
|
||||||
*/
|
*/
|
||||||
public static function execute(CurlHandle $ch, int $retries = 5, bool $closeAfterDone = true)
|
public static function execute($ch, int $retries = 5, bool $closeAfterDone = true)
|
||||||
{
|
{
|
||||||
while ($retries--) {
|
while ($retries--) {
|
||||||
$curlResponse = curl_exec($ch);
|
$curlResponse = curl_exec($ch);
|
||||||
if ($curlResponse === false) {
|
if ($curlResponse === false) {
|
||||||
$curlErrno = curl_errno($ch);
|
$curlErrno = curl_errno($ch);
|
||||||
|
|
||||||
if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || $retries === 0) {
|
if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) {
|
||||||
$curlError = curl_error($ch);
|
$curlError = curl_error($ch);
|
||||||
|
|
||||||
if ($closeAfterDone) {
|
if ($closeAfterDone) {
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple handler wrapper that deduplicates log records across multiple requests
|
* Simple handler wrapper that deduplicates log records across multiple requests
|
||||||
|
@ -35,29 +33,45 @@ use Monolog\LogRecord;
|
||||||
* same way.
|
* same way.
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class DeduplicationHandler extends BufferHandler
|
class DeduplicationHandler extends BufferHandler
|
||||||
{
|
{
|
||||||
protected string $deduplicationStore;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $deduplicationStore;
|
||||||
|
|
||||||
protected Level $deduplicationLevel;
|
/**
|
||||||
|
* @var Level
|
||||||
|
*/
|
||||||
|
protected $deduplicationLevel;
|
||||||
|
|
||||||
protected int $time;
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $time;
|
||||||
|
|
||||||
private bool $gc = false;
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $gc = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HandlerInterface $handler Handler.
|
* @param HandlerInterface $handler Handler.
|
||||||
* @param string $deduplicationStore The file/path where the deduplication log should be kept
|
* @param string $deduplicationStore The file/path where the deduplication log should be kept
|
||||||
* @param int|string|Level|LogLevel::* $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
|
* @param string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
|
||||||
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
|
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
|
||||||
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $deduplicationLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $deduplicationLevel
|
||||||
*/
|
*/
|
||||||
public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, int|string|Level $deduplicationLevel = Level::Error, int $time = 60, bool $bubble = true)
|
public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($handler, 0, Level::Debug, $bubble, false);
|
parent::__construct($handler, 0, Logger::DEBUG, $bubble, false);
|
||||||
|
|
||||||
$this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore;
|
$this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore;
|
||||||
$this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel);
|
$this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel);
|
||||||
|
@ -73,8 +87,8 @@ class DeduplicationHandler extends BufferHandler
|
||||||
$passthru = null;
|
$passthru = null;
|
||||||
|
|
||||||
foreach ($this->buffer as $record) {
|
foreach ($this->buffer as $record) {
|
||||||
if ($record->level->value >= $this->deduplicationLevel->value) {
|
if ($record['level'] >= $this->deduplicationLevel) {
|
||||||
$passthru = $passthru === true || !$this->isDuplicate($record);
|
$passthru = $passthru || !$this->isDuplicate($record);
|
||||||
if ($passthru) {
|
if ($passthru) {
|
||||||
$this->appendRecord($record);
|
$this->appendRecord($record);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +107,10 @@ class DeduplicationHandler extends BufferHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isDuplicate(LogRecord $record): bool
|
/**
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
*/
|
||||||
|
private function isDuplicate(array $record): bool
|
||||||
{
|
{
|
||||||
if (!file_exists($this->deduplicationStore)) {
|
if (!file_exists($this->deduplicationStore)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -105,13 +122,13 @@ class DeduplicationHandler extends BufferHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
$yesterday = time() - 86400;
|
$yesterday = time() - 86400;
|
||||||
$timestampValidity = $record->datetime->getTimestamp() - $this->time;
|
$timestampValidity = $record['datetime']->getTimestamp() - $this->time;
|
||||||
$expectedMessage = preg_replace('{[\r\n].*}', '', $record->message);
|
$expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']);
|
||||||
|
|
||||||
for ($i = count($store) - 1; $i >= 0; $i--) {
|
for ($i = count($store) - 1; $i >= 0; $i--) {
|
||||||
list($timestamp, $level, $message) = explode(':', $store[$i], 3);
|
list($timestamp, $level, $message) = explode(':', $store[$i], 3);
|
||||||
|
|
||||||
if ($level === $record->level->getName() && $message === $expectedMessage && $timestamp > $timestampValidity) {
|
if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +148,7 @@ class DeduplicationHandler extends BufferHandler
|
||||||
|
|
||||||
$handle = fopen($this->deduplicationStore, 'rw+');
|
$handle = fopen($this->deduplicationStore, 'rw+');
|
||||||
|
|
||||||
if (false === $handle) {
|
if (!$handle) {
|
||||||
throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore);
|
throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +159,7 @@ class DeduplicationHandler extends BufferHandler
|
||||||
|
|
||||||
while (!feof($handle)) {
|
while (!feof($handle)) {
|
||||||
$log = fgets($handle);
|
$log = fgets($handle);
|
||||||
if (is_string($log) && '' !== $log && substr($log, 0, 10) >= $timestampValidity) {
|
if ($log && substr($log, 0, 10) >= $timestampValidity) {
|
||||||
$validLogs[] = $log;
|
$validLogs[] = $log;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,8 +176,11 @@ class DeduplicationHandler extends BufferHandler
|
||||||
$this->gc = false;
|
$this->gc = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function appendRecord(LogRecord $record): void
|
/**
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
*/
|
||||||
|
private function appendRecord(array $record): void
|
||||||
{
|
{
|
||||||
file_put_contents($this->deduplicationStore, $record->datetime->getTimestamp() . ':' . $record->level->getName() . ':' . preg_replace('{[\r\n].*}', '', $record->message) . "\n", FILE_APPEND);
|
file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\NormalizerFormatter;
|
use Monolog\Formatter\NormalizerFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Doctrine\CouchDB\CouchDBClient;
|
use Doctrine\CouchDB\CouchDBClient;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CouchDB handler for Doctrine CouchDB ODM
|
* CouchDB handler for Doctrine CouchDB ODM
|
||||||
|
@ -24,20 +23,21 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class DoctrineCouchDBHandler extends AbstractProcessingHandler
|
class DoctrineCouchDBHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
private CouchDBClient $client;
|
/** @var CouchDBClient */
|
||||||
|
private $client;
|
||||||
|
|
||||||
public function __construct(CouchDBClient $client, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->client->postDocument($record->formatted);
|
$this->client->postDocument($record['formatted']);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
|
|
|
@ -16,8 +16,7 @@ use Aws\DynamoDb\DynamoDbClient;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Aws\DynamoDb\Marshaler;
|
use Aws\DynamoDb\Marshaler;
|
||||||
use Monolog\Formatter\ScalarFormatter;
|
use Monolog\Formatter\ScalarFormatter;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/)
|
* Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/)
|
||||||
|
@ -29,15 +28,35 @@ class DynamoDbHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO';
|
public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO';
|
||||||
|
|
||||||
protected DynamoDbClient $client;
|
/**
|
||||||
|
* @var DynamoDbClient
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
protected string $table;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $table;
|
||||||
|
|
||||||
protected Marshaler $marshaler;
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $version;
|
||||||
|
|
||||||
public function __construct(DynamoDbClient $client, string $table, int|string|Level $level = Level::Debug, bool $bubble = true)
|
/**
|
||||||
|
* @var Marshaler
|
||||||
|
*/
|
||||||
|
protected $marshaler;
|
||||||
|
|
||||||
|
public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
|
if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) {
|
||||||
|
$this->version = 3;
|
||||||
$this->marshaler = new Marshaler;
|
$this->marshaler = new Marshaler;
|
||||||
|
} else {
|
||||||
|
$this->version = 2;
|
||||||
|
}
|
||||||
|
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->table = $table;
|
$this->table = $table;
|
||||||
|
@ -46,12 +65,17 @@ class DynamoDbHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$filtered = $this->filterEmptyFields($record->formatted);
|
$filtered = $this->filterEmptyFields($record['formatted']);
|
||||||
|
if ($this->version === 3) {
|
||||||
$formatted = $this->marshaler->marshalItem($filtered);
|
$formatted = $this->marshaler->marshalItem($filtered);
|
||||||
|
} else {
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
|
$formatted = $this->client->formatAttributes($filtered);
|
||||||
|
}
|
||||||
|
|
||||||
$this->client->putItem([
|
$this->client->putItem([
|
||||||
'TableName' => $this->table,
|
'TableName' => $this->table,
|
||||||
|
@ -66,12 +90,12 @@ class DynamoDbHandler extends AbstractProcessingHandler
|
||||||
protected function filterEmptyFields(array $record): array
|
protected function filterEmptyFields(array $record): array
|
||||||
{
|
{
|
||||||
return array_filter($record, function ($value) {
|
return array_filter($record, function ($value) {
|
||||||
return [] !== $value;
|
return !empty($value) || false === $value || 0 === $value;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,10 +14,9 @@ namespace Monolog\Handler;
|
||||||
use Elastica\Document;
|
use Elastica\Document;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\ElasticaFormatter;
|
use Monolog\Formatter\ElasticaFormatter;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Elastica\Client;
|
use Elastica\Client;
|
||||||
use Elastica\Exception\ExceptionInterface;
|
use Elastica\Exception\ExceptionInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Elastic Search handler
|
* Elastic Search handler
|
||||||
|
@ -34,34 +33,24 @@ use Monolog\LogRecord;
|
||||||
* $log->pushHandler($handler);
|
* $log->pushHandler($handler);
|
||||||
*
|
*
|
||||||
* @author Jelle Vink <jelle.vink@gmail.com>
|
* @author Jelle Vink <jelle.vink@gmail.com>
|
||||||
* @phpstan-type Options array{
|
|
||||||
* index: string,
|
|
||||||
* type: string,
|
|
||||||
* ignore_error: bool
|
|
||||||
* }
|
|
||||||
* @phpstan-type InputOptions array{
|
|
||||||
* index?: string,
|
|
||||||
* type?: string,
|
|
||||||
* ignore_error?: bool
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
class ElasticaHandler extends AbstractProcessingHandler
|
class ElasticaHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
protected Client $client;
|
/**
|
||||||
|
* @var Client
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[] Handler config options
|
* @var mixed[] Handler config options
|
||||||
* @phpstan-var Options
|
|
||||||
*/
|
*/
|
||||||
protected array $options;
|
protected $options = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Client $client Elastica Client object
|
* @param Client $client Elastica Client object
|
||||||
* @param mixed[] $options Handler configuration
|
* @param mixed[] $options Handler configuration
|
||||||
*
|
|
||||||
* @phpstan-param InputOptions $options
|
|
||||||
*/
|
*/
|
||||||
public function __construct(Client $client, array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
|
@ -76,15 +65,15 @@ class ElasticaHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->bulkSend([$record->formatted]);
|
$this->bulkSend([$record['formatted']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -97,8 +86,6 @@ class ElasticaHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*
|
|
||||||
* @phpstan-return Options
|
|
||||||
*/
|
*/
|
||||||
public function getOptions(): array
|
public function getOptions(): array
|
||||||
{
|
{
|
||||||
|
@ -106,7 +93,7 @@ class ElasticaHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -114,7 +101,7 @@ class ElasticaHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,13 +14,12 @@ namespace Monolog\Handler;
|
||||||
use Elastic\Elasticsearch\Response\Elasticsearch;
|
use Elastic\Elasticsearch\Response\Elasticsearch;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\ElasticsearchFormatter;
|
use Monolog\Formatter\ElasticsearchFormatter;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException;
|
use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException;
|
||||||
use Elasticsearch\Client;
|
use Elasticsearch\Client;
|
||||||
use Monolog\LogRecord;
|
|
||||||
use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException;
|
use Elastic\Elasticsearch\Exception\InvalidArgumentException as ElasticInvalidArgumentException;
|
||||||
use Elastic\Elasticsearch\Client as Client8;
|
use Elastic\Elasticsearch\Client as Client8;
|
||||||
|
|
||||||
|
@ -44,26 +43,18 @@ use Elastic\Elasticsearch\Client as Client8;
|
||||||
* $log->pushHandler($handler);
|
* $log->pushHandler($handler);
|
||||||
*
|
*
|
||||||
* @author Avtandil Kikabidze <akalongman@gmail.com>
|
* @author Avtandil Kikabidze <akalongman@gmail.com>
|
||||||
* @phpstan-type Options array{
|
|
||||||
* index: string,
|
|
||||||
* type: string,
|
|
||||||
* ignore_error: bool
|
|
||||||
* }
|
|
||||||
* @phpstan-type InputOptions array{
|
|
||||||
* index?: string,
|
|
||||||
* type?: string,
|
|
||||||
* ignore_error?: bool
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
class ElasticsearchHandler extends AbstractProcessingHandler
|
class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
protected Client|Client8 $client;
|
/**
|
||||||
|
* @var Client|Client8
|
||||||
|
*/
|
||||||
|
protected $client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[] Handler config options
|
* @var mixed[] Handler config options
|
||||||
* @phpstan-var Options
|
|
||||||
*/
|
*/
|
||||||
protected array $options;
|
protected $options = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
|
@ -73,11 +64,13 @@ class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* @param Client|Client8 $client Elasticsearch Client object
|
* @param Client|Client8 $client Elasticsearch Client object
|
||||||
* @param mixed[] $options Handler configuration
|
* @param mixed[] $options Handler configuration
|
||||||
*
|
|
||||||
* @phpstan-param InputOptions $options
|
|
||||||
*/
|
*/
|
||||||
public function __construct(Client|Client8 $client, array $options = [], int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct($client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
|
if (!$client instanceof Client && !$client instanceof Client8) {
|
||||||
|
throw new \TypeError('Elasticsearch\Client or Elastic\Elasticsearch\Client instance required');
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->options = array_merge(
|
$this->options = array_merge(
|
||||||
|
@ -99,15 +92,15 @@ class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->bulkSend([$record->formatted]);
|
$this->bulkSend([$record['formatted']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -122,8 +115,6 @@ class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
* Getter options
|
* Getter options
|
||||||
*
|
*
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*
|
|
||||||
* @phpstan-return Options
|
|
||||||
*/
|
*/
|
||||||
public function getOptions(): array
|
public function getOptions(): array
|
||||||
{
|
{
|
||||||
|
@ -131,7 +122,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -139,7 +130,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
@ -150,7 +141,7 @@ class ElasticsearchHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* Use Elasticsearch bulk API to send list of documents
|
* Use Elasticsearch bulk API to send list of documents
|
||||||
*
|
*
|
||||||
* @param array<array<mixed>> $records Records + _index/_type keys
|
* @param array[] $records Records + _index/_type keys
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
*/
|
*/
|
||||||
protected function bulkSend(array $records): void
|
protected function bulkSend(array $records): void
|
||||||
|
|
|
@ -13,9 +13,8 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores to PHP error_log() handler.
|
* Stores to PHP error_log() handler.
|
||||||
|
@ -27,14 +26,16 @@ class ErrorLogHandler extends AbstractProcessingHandler
|
||||||
public const OPERATING_SYSTEM = 0;
|
public const OPERATING_SYSTEM = 0;
|
||||||
public const SAPI = 4;
|
public const SAPI = 4;
|
||||||
|
|
||||||
protected int $messageType;
|
/** @var int */
|
||||||
protected bool $expandNewlines;
|
protected $messageType;
|
||||||
|
/** @var bool */
|
||||||
|
protected $expandNewlines;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $messageType Says where the error should go.
|
* @param int $messageType Says where the error should go.
|
||||||
* @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
|
* @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
|
||||||
*/
|
*/
|
||||||
public function __construct(int $messageType = self::OPERATING_SYSTEM, int|string|Level $level = Level::Debug, bool $bubble = true, bool $expandNewlines = false)
|
public function __construct(int $messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ class ErrorLogHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -68,20 +69,19 @@ class ErrorLogHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (!$this->expandNewlines) {
|
if (!$this->expandNewlines) {
|
||||||
error_log((string) $record->formatted, $this->messageType);
|
error_log((string) $record['formatted'], $this->messageType);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lines = preg_split('{[\r\n]+}', (string) $record->formatted);
|
$lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
|
||||||
if ($lines === false) {
|
if ($lines === false) {
|
||||||
$pcreErrorCode = preg_last_error();
|
$pcreErrorCode = preg_last_error();
|
||||||
|
|
||||||
throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode));
|
throw new \RuntimeException('Failed to preg_split formatted string: ' . $pcreErrorCode . ' / '. Utils::pcreLastErrorMessage($pcreErrorCode));
|
||||||
}
|
}
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forwards records to at most one handler
|
* Forwards records to at most one handler
|
||||||
|
@ -20,15 +19,18 @@ use Monolog\LogRecord;
|
||||||
* If a handler fails, the exception is suppressed and the record is forwarded to the next handler.
|
* If a handler fails, the exception is suppressed and the record is forwarded to the next handler.
|
||||||
*
|
*
|
||||||
* As soon as one handler handles a record successfully, the handling stops there.
|
* As soon as one handler handles a record successfully, the handling stops there.
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class FallbackGroupHandler extends GroupHandler
|
class FallbackGroupHandler extends GroupHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
foreach ($this->handlers as $handler) {
|
foreach ($this->handlers as $handler) {
|
||||||
|
@ -44,15 +46,16 @@ class FallbackGroupHandler extends GroupHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
$processed = [];
|
$processed = [];
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
$processed[] = $this->processRecord($record);
|
$processed[] = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
/** @var Record[] $records */
|
||||||
$records = $processed;
|
$records = $processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,10 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple handler wrapper that filters records based on a list of levels
|
* Simple handler wrapper that filters records based on a list of levels
|
||||||
|
@ -26,99 +23,110 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* @author Hennadiy Verkh
|
* @author Hennadiy Verkh
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
|
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
use ProcessableHandlerTrait;
|
use ProcessableHandlerTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler or factory Closure($record, $this)
|
* Handler or factory callable($record, $this)
|
||||||
*
|
*
|
||||||
* @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
|
* @var callable|HandlerInterface
|
||||||
|
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
|
||||||
*/
|
*/
|
||||||
protected Closure|HandlerInterface $handler;
|
protected $handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum level for logs that are passed to handler
|
* Minimum level for logs that are passed to handler
|
||||||
*
|
*
|
||||||
* @var bool[] Map of Level value => true
|
* @var int[]
|
||||||
* @phpstan-var array<value-of<Level::VALUES>, true>
|
* @phpstan-var array<Level, int>
|
||||||
*/
|
*/
|
||||||
protected array $acceptedLevels;
|
protected $acceptedLevels;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the messages that are handled can bubble up the stack or not
|
* Whether the messages that are handled can bubble up the stack or not
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected bool $bubble;
|
protected $bubble;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
|
* @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
|
||||||
*
|
*
|
||||||
* @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $filterHandler).
|
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler).
|
||||||
* @param int|string|Level|array<int|string|Level|LogLevel::*> $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
|
* @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
|
||||||
* @param int|string|Level|LogLevel::* $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
|
* @param int|string $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
|
||||||
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|array<value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $minLevelOrList
|
* @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $maxLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $maxLevel
|
||||||
*/
|
*/
|
||||||
public function __construct(Closure|HandlerInterface $handler, int|string|Level|array $minLevelOrList = Level::Debug, int|string|Level $maxLevel = Level::Emergency, bool $bubble = true)
|
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$this->handler = $handler;
|
$this->handler = $handler;
|
||||||
$this->bubble = $bubble;
|
$this->bubble = $bubble;
|
||||||
$this->setAcceptedLevels($minLevelOrList, $maxLevel);
|
$this->setAcceptedLevels($minLevelOrList, $maxLevel);
|
||||||
|
|
||||||
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
|
||||||
|
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-return list<Level> List of levels
|
* @phpstan-return array<int, Level>
|
||||||
*/
|
*/
|
||||||
public function getAcceptedLevels(): array
|
public function getAcceptedLevels(): array
|
||||||
{
|
{
|
||||||
return array_map(fn (int $level) => Level::from($level), array_keys($this->acceptedLevels));
|
return array_flip($this->acceptedLevels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|string|Level|LogLevel::*|array<int|string|Level|LogLevel::*> $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
|
* @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
|
||||||
* @param int|string|Level|LogLevel::* $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array
|
* @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|array<value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $minLevelOrList
|
* @phpstan-param Level|LevelName|LogLevel::*|array<Level|LevelName|LogLevel::*> $minLevelOrList
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $maxLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $maxLevel
|
||||||
*/
|
*/
|
||||||
public function setAcceptedLevels(int|string|Level|array $minLevelOrList = Level::Debug, int|string|Level $maxLevel = Level::Emergency): self
|
public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self
|
||||||
{
|
{
|
||||||
if (is_array($minLevelOrList)) {
|
if (is_array($minLevelOrList)) {
|
||||||
$acceptedLevels = array_map(Logger::toMonologLevel(...), $minLevelOrList);
|
$acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList);
|
||||||
} else {
|
} else {
|
||||||
$minLevelOrList = Logger::toMonologLevel($minLevelOrList);
|
$minLevelOrList = Logger::toMonologLevel($minLevelOrList);
|
||||||
$maxLevel = Logger::toMonologLevel($maxLevel);
|
$maxLevel = Logger::toMonologLevel($maxLevel);
|
||||||
$acceptedLevels = array_values(array_filter(Level::cases(), fn (Level $level) => $level->value >= $minLevelOrList->value && $level->value <= $maxLevel->value));
|
$acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) {
|
||||||
}
|
return $level >= $minLevelOrList && $level <= $maxLevel;
|
||||||
$this->acceptedLevels = [];
|
}));
|
||||||
foreach ($acceptedLevels as $level) {
|
|
||||||
$this->acceptedLevels[$level->value] = true;
|
|
||||||
}
|
}
|
||||||
|
$this->acceptedLevels = array_flip($acceptedLevels);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return isset($this->acceptedLevels[$record->level->value]);
|
return isset($this->acceptedLevels[$record['level']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if (!$this->isHandling($record)) {
|
if (!$this->isHandling($record)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +136,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
@ -147,23 +155,26 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
|
||||||
/**
|
/**
|
||||||
* Return the nested handler
|
* Return the nested handler
|
||||||
*
|
*
|
||||||
* If the handler was provided as a factory, this will trigger the handler's instantiation.
|
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
|
||||||
|
*
|
||||||
|
* @return HandlerInterface
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
public function getHandler(LogRecord $record = null): HandlerInterface
|
public function getHandler(array $record = null)
|
||||||
{
|
{
|
||||||
if (!$this->handler instanceof HandlerInterface) {
|
if (!$this->handler instanceof HandlerInterface) {
|
||||||
$handler = ($this->handler)($record, $this);
|
$this->handler = ($this->handler)($record, $this);
|
||||||
if (!$handler instanceof HandlerInterface) {
|
if (!$this->handler instanceof HandlerInterface) {
|
||||||
throw new \RuntimeException("The factory Closure should return a HandlerInterface");
|
throw new \RuntimeException("The factory callable should return a HandlerInterface");
|
||||||
}
|
}
|
||||||
$this->handler = $handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->handler;
|
return $this->handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -178,7 +189,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -190,7 +201,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
|
||||||
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
|
throw new \UnexpectedValueException('The nested handler of type '.get_class($handler).' does not support formatters.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
$this->resetProcessors();
|
$this->resetProcessors();
|
||||||
|
|
||||||
|
|
|
@ -11,17 +11,19 @@
|
||||||
|
|
||||||
namespace Monolog\Handler\FingersCrossed;
|
namespace Monolog\Handler\FingersCrossed;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for activation strategies for the FingersCrossedHandler.
|
* Interface for activation strategies for the FingersCrossedHandler.
|
||||||
*
|
*
|
||||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
interface ActivationStrategyInterface
|
interface ActivationStrategyInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns whether the given record activates the handler.
|
* Returns whether the given record activates the handler.
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
public function isHandlerActivated(LogRecord $record): bool;
|
public function isHandlerActivated(array $record): bool;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler\FingersCrossed;
|
namespace Monolog\Handler\FingersCrossed;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Channel and Error level based monolog activation strategy. Allows to trigger activation
|
* Channel and Error level based monolog activation strategy. Allows to trigger activation
|
||||||
|
@ -25,45 +23,55 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* <code>
|
* <code>
|
||||||
* $activationStrategy = new ChannelLevelActivationStrategy(
|
* $activationStrategy = new ChannelLevelActivationStrategy(
|
||||||
* Level::Critical,
|
* Logger::CRITICAL,
|
||||||
* array(
|
* array(
|
||||||
* 'request' => Level::Alert,
|
* 'request' => Logger::ALERT,
|
||||||
* 'sensitive' => Level::Error,
|
* 'sensitive' => Logger::ERROR,
|
||||||
* )
|
* )
|
||||||
* );
|
* );
|
||||||
* $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy);
|
* $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy);
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @author Mike Meessen <netmikey@gmail.com>
|
* @author Mike Meessen <netmikey@gmail.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
|
class ChannelLevelActivationStrategy implements ActivationStrategyInterface
|
||||||
{
|
{
|
||||||
private Level $defaultActionLevel;
|
/**
|
||||||
|
* @var Level
|
||||||
|
*/
|
||||||
|
private $defaultActionLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<string, Level>
|
* @var array<string, Level>
|
||||||
*/
|
*/
|
||||||
private array $channelToActionLevel;
|
private $channelToActionLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|string|Level|LogLevel::* $defaultActionLevel The default action level to be used if the record's category doesn't match any
|
* @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any
|
||||||
* @param array<string, int|string|Level|LogLevel::*> $channelToActionLevel An array that maps channel names to action levels.
|
* @param array<string, int> $channelToActionLevel An array that maps channel names to action levels.
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $defaultActionLevel
|
* @phpstan-param array<string, Level> $channelToActionLevel
|
||||||
* @phpstan-param array<string, value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*> $channelToActionLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $defaultActionLevel
|
||||||
*/
|
*/
|
||||||
public function __construct(int|string|Level $defaultActionLevel, array $channelToActionLevel = [])
|
public function __construct($defaultActionLevel, array $channelToActionLevel = [])
|
||||||
{
|
{
|
||||||
$this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel);
|
$this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel);
|
||||||
$this->channelToActionLevel = array_map(Logger::toMonologLevel(...), $channelToActionLevel);
|
$this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isHandlerActivated(LogRecord $record): bool
|
/**
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
*/
|
||||||
|
public function isHandlerActivated(array $record): bool
|
||||||
{
|
{
|
||||||
if (isset($this->channelToActionLevel[$record->channel])) {
|
if (isset($this->channelToActionLevel[$record['channel']])) {
|
||||||
return $record->level->value >= $this->channelToActionLevel[$record->channel]->value;
|
return $record['level'] >= $this->channelToActionLevel[$record['channel']];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $record->level->value >= $this->defaultActionLevel->value;
|
return $record['level'] >= $this->defaultActionLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog\Handler\FingersCrossed;
|
namespace Monolog\Handler\FingersCrossed;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
|
|
||||||
|
@ -20,23 +18,29 @@ use Psr\Log\LogLevel;
|
||||||
* Error level based activation strategy.
|
* Error level based activation strategy.
|
||||||
*
|
*
|
||||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class ErrorLevelActivationStrategy implements ActivationStrategyInterface
|
class ErrorLevelActivationStrategy implements ActivationStrategyInterface
|
||||||
{
|
{
|
||||||
private Level $actionLevel;
|
/**
|
||||||
|
* @var Level
|
||||||
|
*/
|
||||||
|
private $actionLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|string|Level $actionLevel Level or name or value
|
* @param int|string $actionLevel Level or name or value
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $actionLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $actionLevel
|
||||||
*/
|
*/
|
||||||
public function __construct(int|string|Level $actionLevel)
|
public function __construct($actionLevel)
|
||||||
{
|
{
|
||||||
$this->actionLevel = Logger::toMonologLevel($actionLevel);
|
$this->actionLevel = Logger::toMonologLevel($actionLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isHandlerActivated(LogRecord $record): bool
|
public function isHandlerActivated(array $record): bool
|
||||||
{
|
{
|
||||||
return $record->level->value >= $this->actionLevel->value;
|
return $record['level'] >= $this->actionLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,12 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
|
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
|
||||||
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
|
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffers all records until a certain level is reached
|
* Buffers all records until a certain level is reached
|
||||||
|
@ -36,50 +33,55 @@ use Monolog\LogRecord;
|
||||||
* Monolog\Handler\FingersCrossed\ namespace.
|
* Monolog\Handler\FingersCrossed\ namespace.
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
|
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
use ProcessableHandlerTrait;
|
use ProcessableHandlerTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler or factory Closure($record, $this)
|
* @var callable|HandlerInterface
|
||||||
*
|
* @phpstan-var callable(?Record, HandlerInterface): HandlerInterface|HandlerInterface
|
||||||
* @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
|
|
||||||
*/
|
*/
|
||||||
protected Closure|HandlerInterface $handler;
|
protected $handler;
|
||||||
|
/** @var ActivationStrategyInterface */
|
||||||
protected ActivationStrategyInterface $activationStrategy;
|
protected $activationStrategy;
|
||||||
|
/** @var bool */
|
||||||
protected bool $buffering = true;
|
protected $buffering = true;
|
||||||
|
/** @var int */
|
||||||
protected int $bufferSize;
|
protected $bufferSize;
|
||||||
|
/** @var Record[] */
|
||||||
/** @var LogRecord[] */
|
protected $buffer = [];
|
||||||
protected array $buffer = [];
|
/** @var bool */
|
||||||
|
protected $stopBuffering;
|
||||||
protected bool $stopBuffering;
|
/**
|
||||||
|
* @var ?int
|
||||||
protected Level|null $passthruLevel = null;
|
* @phpstan-var ?Level
|
||||||
|
*/
|
||||||
protected bool $bubble;
|
protected $passthruLevel;
|
||||||
|
/** @var bool */
|
||||||
|
protected $bubble;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
|
* @psalm-param HandlerInterface|callable(?Record, HandlerInterface): HandlerInterface $handler
|
||||||
*
|
*
|
||||||
* @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $fingersCrossedHandler).
|
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler).
|
||||||
* @param int|string|Level|LogLevel::* $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
|
* @param int|string|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action, or a level name/value at which the handler is activated
|
||||||
* @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
|
* @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
|
||||||
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
|
||||||
* @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true)
|
* @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true)
|
||||||
* @param int|string|Level|LogLevel::*|null $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
|
* @param int|string $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::*|ActivationStrategyInterface $activationStrategy
|
* @phpstan-param Level|LevelName|LogLevel::* $passthruLevel
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $passthruLevel
|
* @phpstan-param Level|LevelName|LogLevel::*|ActivationStrategyInterface $activationStrategy
|
||||||
*/
|
*/
|
||||||
public function __construct(Closure|HandlerInterface $handler, int|string|Level|ActivationStrategyInterface $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, int|string|Level|null $passthruLevel = null)
|
public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null)
|
||||||
{
|
{
|
||||||
if (null === $activationStrategy) {
|
if (null === $activationStrategy) {
|
||||||
$activationStrategy = new ErrorLevelActivationStrategy(Level::Warning);
|
$activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert simple int activationStrategy to an object
|
// convert simple int activationStrategy to an object
|
||||||
|
@ -96,12 +98,16 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
if ($passthruLevel !== null) {
|
if ($passthruLevel !== null) {
|
||||||
$this->passthruLevel = Logger::toMonologLevel($passthruLevel);
|
$this->passthruLevel = Logger::toMonologLevel($passthruLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
|
||||||
|
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -120,11 +126,12 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +151,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
@ -153,7 +160,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
$this->getHandler()->close();
|
$this->getHandler()->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
$this->flushBuffer();
|
$this->flushBuffer();
|
||||||
|
|
||||||
|
@ -183,7 +190,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
if (null !== $this->passthruLevel) {
|
if (null !== $this->passthruLevel) {
|
||||||
$level = $this->passthruLevel;
|
$level = $this->passthruLevel;
|
||||||
$this->buffer = array_filter($this->buffer, function ($record) use ($level) {
|
$this->buffer = array_filter($this->buffer, function ($record) use ($level) {
|
||||||
return $record->level >= $level;
|
return $record['level'] >= $level;
|
||||||
});
|
});
|
||||||
if (count($this->buffer) > 0) {
|
if (count($this->buffer) > 0) {
|
||||||
$this->getHandler(end($this->buffer))->handleBatch($this->buffer);
|
$this->getHandler(end($this->buffer))->handleBatch($this->buffer);
|
||||||
|
@ -197,23 +204,26 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
/**
|
/**
|
||||||
* Return the nested handler
|
* Return the nested handler
|
||||||
*
|
*
|
||||||
* If the handler was provided as a factory, this will trigger the handler's instantiation.
|
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
|
||||||
|
*
|
||||||
|
* @return HandlerInterface
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
public function getHandler(LogRecord $record = null): HandlerInterface
|
public function getHandler(array $record = null)
|
||||||
{
|
{
|
||||||
if (!$this->handler instanceof HandlerInterface) {
|
if (!$this->handler instanceof HandlerInterface) {
|
||||||
$handler = ($this->handler)($record, $this);
|
$this->handler = ($this->handler)($record, $this);
|
||||||
if (!$handler instanceof HandlerInterface) {
|
if (!$this->handler instanceof HandlerInterface) {
|
||||||
throw new \RuntimeException("The factory Closure should return a HandlerInterface");
|
throw new \RuntimeException("The factory callable should return a HandlerInterface");
|
||||||
}
|
}
|
||||||
$this->handler = $handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->handler;
|
return $this->handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -228,7 +238,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,12 +13,13 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\WildfireFormatter;
|
use Monolog\Formatter\WildfireFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
|
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
|
||||||
*
|
*
|
||||||
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
|
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class FirePHPHandler extends AbstractProcessingHandler
|
class FirePHPHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
|
@ -46,15 +47,18 @@ class FirePHPHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not Wildfire vendor-specific headers have been generated & sent yet
|
* Whether or not Wildfire vendor-specific headers have been generated & sent yet
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected static bool $initialized = false;
|
protected static $initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared static message index between potentially multiple handlers
|
* Shared static message index between potentially multiple handlers
|
||||||
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected static int $messageIndex = 1;
|
protected static $messageIndex = 1;
|
||||||
|
|
||||||
protected static bool $sendHeaders = true;
|
/** @var bool */
|
||||||
|
protected static $sendHeaders = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base header creation function used by init headers & record headers
|
* Base header creation function used by init headers & record headers
|
||||||
|
@ -81,19 +85,21 @@ class FirePHPHandler extends AbstractProcessingHandler
|
||||||
* @phpstan-return non-empty-array<string, string>
|
* @phpstan-return non-empty-array<string, string>
|
||||||
*
|
*
|
||||||
* @see createHeader()
|
* @see createHeader()
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
protected function createRecordHeader(LogRecord $record): array
|
protected function createRecordHeader(array $record): array
|
||||||
{
|
{
|
||||||
// Wildfire is extensible to support multiple protocols & plugins in a single request,
|
// Wildfire is extensible to support multiple protocols & plugins in a single request,
|
||||||
// but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
|
// but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake.
|
||||||
return $this->createHeader(
|
return $this->createHeader(
|
||||||
[1, 1, 1, self::$messageIndex++],
|
[1, 1, 1, self::$messageIndex++],
|
||||||
$record->formatted
|
$record['formatted']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -134,7 +140,7 @@ class FirePHPHandler extends AbstractProcessingHandler
|
||||||
* @see sendHeader()
|
* @see sendHeader()
|
||||||
* @see sendInitHeaders()
|
* @see sendInitHeaders()
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (!self::$sendHeaders || !$this->isWebRequest()) {
|
if (!self::$sendHeaders || !$this->isWebRequest()) {
|
||||||
return;
|
return;
|
||||||
|
@ -165,7 +171,7 @@ class FirePHPHandler extends AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
protected function headersAccepted(): bool
|
protected function headersAccepted(): bool
|
||||||
{
|
{
|
||||||
if (isset($_SERVER['HTTP_USER_AGENT']) && 1 === preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) {
|
if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,7 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends logs to Fleep.io using Webhook integrations
|
* Sends logs to Fleep.io using Webhook integrations
|
||||||
|
@ -23,6 +22,8 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
|
* @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
|
||||||
* @author Ando Roots <ando@sqroot.eu>
|
* @author Ando Roots <ando@sqroot.eu>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class FleepHookHandler extends SocketHandler
|
class FleepHookHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
|
@ -33,7 +34,7 @@ class FleepHookHandler extends SocketHandler
|
||||||
/**
|
/**
|
||||||
* @var string Webhook token (specifies the conversation where logs are sent)
|
* @var string Webhook token (specifies the conversation where logs are sent)
|
||||||
*/
|
*/
|
||||||
protected string $token;
|
protected $token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new Fleep.io Handler.
|
* Construct a new Fleep.io Handler.
|
||||||
|
@ -46,7 +47,7 @@ class FleepHookHandler extends SocketHandler
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $token,
|
string $token,
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
float $timeout = 0.0,
|
float $timeout = 0.0,
|
||||||
|
@ -88,16 +89,16 @@ class FleepHookHandler extends SocketHandler
|
||||||
/**
|
/**
|
||||||
* Handles a log record
|
* Handles a log record
|
||||||
*/
|
*/
|
||||||
public function write(LogRecord $record): void
|
public function write(array $record): void
|
||||||
{
|
{
|
||||||
parent::write($record);
|
parent::write($record);
|
||||||
$this->closeSocket();
|
$this->closeSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
$content = $this->buildContent($record);
|
$content = $this->buildContent($record);
|
||||||
|
|
||||||
|
@ -120,11 +121,13 @@ class FleepHookHandler extends SocketHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the body of API call
|
* Builds the body of API call
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
private function buildContent(LogRecord $record): string
|
private function buildContent(array $record): string
|
||||||
{
|
{
|
||||||
$dataArray = [
|
$dataArray = [
|
||||||
'message' => $record->formatted,
|
'message' => $record['formatted'],
|
||||||
];
|
];
|
||||||
|
|
||||||
return http_build_query($dataArray);
|
return http_build_query($dataArray);
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\Formatter\FlowdockFormatter;
|
use Monolog\Formatter\FlowdockFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends notifications through the Flowdock push API
|
* Sends notifications through the Flowdock push API
|
||||||
|
@ -27,17 +26,23 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* @author Dominik Liebler <liebler.dominik@gmail.com>
|
* @author Dominik Liebler <liebler.dominik@gmail.com>
|
||||||
* @see https://www.flowdock.com/api/push
|
* @see https://www.flowdock.com/api/push
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
|
* @deprecated Since 2.9.0 and 3.3.0, Flowdock was shutdown we will thus drop this handler in Monolog 4
|
||||||
*/
|
*/
|
||||||
class FlowdockHandler extends SocketHandler
|
class FlowdockHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
protected string $apiToken;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $apiToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws MissingExtensionException if OpenSSL is missing
|
* @throws MissingExtensionException if OpenSSL is missing
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $apiToken,
|
string $apiToken,
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
float $timeout = 0.0,
|
float $timeout = 0.0,
|
||||||
|
@ -63,7 +68,7 @@ class FlowdockHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -83,9 +88,9 @@ class FlowdockHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
parent::write($record);
|
parent::write($record);
|
||||||
|
|
||||||
|
@ -93,9 +98,9 @@ class FlowdockHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
$content = $this->buildContent($record);
|
$content = $this->buildContent($record);
|
||||||
|
|
||||||
|
@ -104,10 +109,12 @@ class FlowdockHandler extends SocketHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the body of API call
|
* Builds the body of API call
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
private function buildContent(LogRecord $record): string
|
private function buildContent(array $record): string
|
||||||
{
|
{
|
||||||
return Utils::jsonEncode($record->formatted);
|
return Utils::jsonEncode($record['formatted']['flowdock']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,12 +23,15 @@ interface FormattableHandlerInterface
|
||||||
/**
|
/**
|
||||||
* Sets the formatter.
|
* Sets the formatter.
|
||||||
*
|
*
|
||||||
|
* @param FormatterInterface $formatter
|
||||||
* @return HandlerInterface self
|
* @return HandlerInterface self
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface;
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the formatter.
|
* Gets the formatter.
|
||||||
|
*
|
||||||
|
* @return FormatterInterface
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface;
|
public function getFormatter(): FormatterInterface;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,13 @@ use Monolog\Formatter\LineFormatter;
|
||||||
*/
|
*/
|
||||||
trait FormattableHandlerTrait
|
trait FormattableHandlerTrait
|
||||||
{
|
{
|
||||||
protected FormatterInterface|null $formatter = null;
|
/**
|
||||||
|
* @var ?FormatterInterface
|
||||||
|
*/
|
||||||
|
protected $formatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -34,11 +37,11 @@ trait FormattableHandlerTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
if (null === $this->formatter) {
|
if (!$this->formatter) {
|
||||||
$this->formatter = $this->getDefaultFormatter();
|
$this->formatter = $this->getDefaultFormatter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,9 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Gelf\PublisherInterface;
|
use Gelf\PublisherInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\GelfMessageFormatter;
|
use Monolog\Formatter\GelfMessageFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to send messages to a Graylog2 (http://www.graylog2.org) server
|
* Handler to send messages to a Graylog2 (http://www.graylog2.org) server
|
||||||
|
@ -28,12 +27,12 @@ class GelfHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* @var PublisherInterface the publisher object that sends the message to the server
|
* @var PublisherInterface the publisher object that sends the message to the server
|
||||||
*/
|
*/
|
||||||
protected PublisherInterface $publisher;
|
protected $publisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param PublisherInterface $publisher a gelf publisher object
|
* @param PublisherInterface $publisher a gelf publisher object
|
||||||
*/
|
*/
|
||||||
public function __construct(PublisherInterface $publisher, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(PublisherInterface $publisher, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
|
@ -41,15 +40,15 @@ class GelfHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->publisher->publish($record->formatted);
|
$this->publisher->publish($record['formatted']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,20 +13,22 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forwards records to multiple handlers
|
* Forwards records to multiple handlers
|
||||||
*
|
*
|
||||||
* @author Lenar Lõhmus <lenar@city.ee>
|
* @author Lenar Lõhmus <lenar@city.ee>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
|
class GroupHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface
|
||||||
{
|
{
|
||||||
use ProcessableHandlerTrait;
|
use ProcessableHandlerTrait;
|
||||||
|
|
||||||
/** @var HandlerInterface[] */
|
/** @var HandlerInterface[] */
|
||||||
protected array $handlers;
|
protected $handlers;
|
||||||
protected bool $bubble;
|
/** @var bool */
|
||||||
|
protected $bubble;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HandlerInterface[] $handlers Array of Handlers.
|
* @param HandlerInterface[] $handlers Array of Handlers.
|
||||||
|
@ -45,9 +47,9 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
foreach ($this->handlers as $handler) {
|
foreach ($this->handlers as $handler) {
|
||||||
if ($handler->isHandling($record)) {
|
if ($handler->isHandling($record)) {
|
||||||
|
@ -59,11 +61,12 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,15 +78,16 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
$processed = [];
|
$processed = [];
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
$processed[] = $this->processRecord($record);
|
$processed[] = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
/** @var Record[] $records */
|
||||||
$records = $processed;
|
$records = $processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +96,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
$this->resetProcessors();
|
$this->resetProcessors();
|
||||||
|
|
||||||
|
@ -113,7 +117,7 @@ class GroupHandler extends Handler implements ProcessableHandlerInterface, Reset
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace Monolog\Handler;
|
||||||
abstract class Handler implements HandlerInterface
|
abstract class Handler implements HandlerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ abstract class Handler implements HandlerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,12 +11,13 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that all Monolog Handlers must implement
|
* Interface that all Monolog Handlers must implement
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
interface HandlerInterface
|
interface HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -29,9 +30,13 @@ interface HandlerInterface
|
||||||
* is no guarantee that handle() will not be called, and isHandling() might not be called
|
* is no guarantee that handle() will not be called, and isHandling() might not be called
|
||||||
* for a given record.
|
* for a given record.
|
||||||
*
|
*
|
||||||
* @param LogRecord $record Partial log record having only a level initialized
|
* @param array $record Partial log record containing only a level key
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @phpstan-param array{level: Level} $record
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool;
|
public function isHandling(array $record): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a record.
|
* Handles a record.
|
||||||
|
@ -43,16 +48,20 @@ interface HandlerInterface
|
||||||
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
|
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
|
||||||
* calling further handlers in the stack with a given log record.
|
* calling further handlers in the stack with a given log record.
|
||||||
*
|
*
|
||||||
* @param LogRecord $record The record to handle
|
* @param array $record The record to handle
|
||||||
* @return bool true means that this handler handled the record, and that bubbling is not permitted.
|
* @return bool true means that this handler handled the record, and that bubbling is not permitted.
|
||||||
* false means the record was either not processed or that this handler allows bubbling.
|
* false means the record was either not processed or that this handler allows bubbling.
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool;
|
public function handle(array $record): bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a set of records at once.
|
* Handles a set of records at once.
|
||||||
*
|
*
|
||||||
* @param array<LogRecord> $records The records to handle
|
* @param array $records The records to handle (an array of record arrays)
|
||||||
|
*
|
||||||
|
* @phpstan-param Record[] $records
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void;
|
public function handleBatch(array $records): void;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This simple wrapper class can be used to extend handlers functionality.
|
* This simple wrapper class can be used to extend handlers functionality.
|
||||||
|
@ -22,7 +21,7 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* Inherit from this class and override handle() like this:
|
* Inherit from this class and override handle() like this:
|
||||||
*
|
*
|
||||||
* public function handle(LogRecord $record)
|
* public function handle(array $record)
|
||||||
* {
|
* {
|
||||||
* if ($record meets certain conditions) {
|
* if ($record meets certain conditions) {
|
||||||
* return false;
|
* return false;
|
||||||
|
@ -34,7 +33,10 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface
|
class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, FormattableHandlerInterface, ResettableInterface
|
||||||
{
|
{
|
||||||
protected HandlerInterface $handler;
|
/**
|
||||||
|
* @var HandlerInterface
|
||||||
|
*/
|
||||||
|
protected $handler;
|
||||||
|
|
||||||
public function __construct(HandlerInterface $handler)
|
public function __construct(HandlerInterface $handler)
|
||||||
{
|
{
|
||||||
|
@ -42,23 +44,23 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return $this->handler->isHandling($record);
|
return $this->handler->isHandling($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
return $this->handler->handle($record);
|
return $this->handler->handle($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
|
@ -66,7 +68,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
@ -74,7 +76,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function pushProcessor(callable $callback): HandlerInterface
|
public function pushProcessor(callable $callback): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -88,7 +90,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function popProcessor(): callable
|
public function popProcessor(): callable
|
||||||
{
|
{
|
||||||
|
@ -100,7 +102,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -114,7 +116,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
@ -125,7 +127,7 @@ class HandlerWrapper implements HandlerInterface, ProcessableHandlerInterface, F
|
||||||
throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
|
throw new \LogicException('The wrapped handler does not implement ' . FormattableHandlerInterface::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
if ($this->handler instanceof ResettableInterface) {
|
if ($this->handler instanceof ResettableInterface) {
|
||||||
$this->handler->reset();
|
$this->handler->reset();
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IFTTTHandler uses cURL to trigger IFTTT Maker actions
|
* IFTTTHandler uses cURL to trigger IFTTT Maker actions
|
||||||
|
@ -28,14 +27,16 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class IFTTTHandler extends AbstractProcessingHandler
|
class IFTTTHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
private string $eventName;
|
/** @var string */
|
||||||
private string $secretKey;
|
private $eventName;
|
||||||
|
/** @var string */
|
||||||
|
private $secretKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $eventName The name of the IFTTT Maker event that should be triggered
|
* @param string $eventName The name of the IFTTT Maker event that should be triggered
|
||||||
* @param string $secretKey A valid IFTTT secret key
|
* @param string $secretKey A valid IFTTT secret key
|
||||||
*/
|
*/
|
||||||
public function __construct(string $eventName, string $secretKey, int|string|Level $level = Level::Error, bool $bubble = true)
|
public function __construct(string $eventName, string $secretKey, $level = Logger::ERROR, bool $bubble = true)
|
||||||
{
|
{
|
||||||
if (!extension_loaded('curl')) {
|
if (!extension_loaded('curl')) {
|
||||||
throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler');
|
throw new MissingExtensionException('The curl extension is needed to use the IFTTTHandler');
|
||||||
|
@ -48,14 +49,14 @@ class IFTTTHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function write(LogRecord $record): void
|
public function write(array $record): void
|
||||||
{
|
{
|
||||||
$postData = [
|
$postData = [
|
||||||
"value1" => $record->channel,
|
"value1" => $record["channel"],
|
||||||
"value2" => $record["level_name"],
|
"value2" => $record["level_name"],
|
||||||
"value3" => $record->message,
|
"value3" => $record["message"],
|
||||||
];
|
];
|
||||||
$postString = Utils::jsonEncode($postData);
|
$postString = Utils::jsonEncode($postData);
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inspired on LogEntriesHandler.
|
* Inspired on LogEntriesHandler.
|
||||||
|
@ -22,7 +21,10 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class InsightOpsHandler extends SocketHandler
|
class InsightOpsHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
protected string $logToken;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $logToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $token Log token supplied by InsightOps
|
* @param string $token Log token supplied by InsightOps
|
||||||
|
@ -35,7 +37,7 @@ class InsightOpsHandler extends SocketHandler
|
||||||
string $token,
|
string $token,
|
||||||
string $region = 'us',
|
string $region = 'us',
|
||||||
bool $useSSL = true,
|
bool $useSSL = true,
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
float $timeout = 0.0,
|
float $timeout = 0.0,
|
||||||
|
@ -65,10 +67,10 @@ class InsightOpsHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
return $this->logToken . ' ' . $record->formatted;
|
return $this->logToken . ' ' . $record['formatted'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,17 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Robert Kaufmann III <rok3@rok3.me>
|
* @author Robert Kaufmann III <rok3@rok3.me>
|
||||||
*/
|
*/
|
||||||
class LogEntriesHandler extends SocketHandler
|
class LogEntriesHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
protected string $logToken;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $logToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $token Log token supplied by LogEntries
|
* @param string $token Log token supplied by LogEntries
|
||||||
|
@ -31,7 +33,7 @@ class LogEntriesHandler extends SocketHandler
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $token,
|
string $token,
|
||||||
bool $useSSL = true,
|
bool $useSSL = true,
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
string $host = 'data.logentries.com',
|
string $host = 'data.logentries.com',
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
|
@ -59,10 +61,10 @@ class LogEntriesHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
return $this->logToken . ' ' . $record->formatted;
|
return $this->logToken . ' ' . $record['formatted'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,11 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\LogglyFormatter;
|
use Monolog\Formatter\LogglyFormatter;
|
||||||
use function array_key_exists;
|
use function array_key_exists;
|
||||||
use CurlHandle;
|
use CurlHandle;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends errors to Loggly.
|
* Sends errors to Loggly.
|
||||||
|
@ -34,21 +33,22 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* Caches the curl handlers for every given endpoint.
|
* Caches the curl handlers for every given endpoint.
|
||||||
*
|
*
|
||||||
* @var CurlHandle[]
|
* @var resource[]|CurlHandle[]
|
||||||
*/
|
*/
|
||||||
protected array $curlHandlers = [];
|
protected $curlHandlers = [];
|
||||||
|
|
||||||
protected string $token;
|
/** @var string */
|
||||||
|
protected $token;
|
||||||
|
|
||||||
/** @var string[] */
|
/** @var string[] */
|
||||||
protected array $tag = [];
|
protected $tag = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $token API token supplied by Loggly
|
* @param string $token API token supplied by Loggly
|
||||||
*
|
*
|
||||||
* @throws MissingExtensionException If the curl extension is missing
|
* @throws MissingExtensionException If the curl extension is missing
|
||||||
*/
|
*/
|
||||||
public function __construct(string $token, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(string $token, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
if (!extension_loaded('curl')) {
|
if (!extension_loaded('curl')) {
|
||||||
throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler');
|
throw new MissingExtensionException('The curl extension is needed to use the LogglyHandler');
|
||||||
|
@ -61,8 +61,12 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads and returns the shared curl handler for the given endpoint.
|
* Loads and returns the shared curl handler for the given endpoint.
|
||||||
|
*
|
||||||
|
* @param string $endpoint
|
||||||
|
*
|
||||||
|
* @return resource|CurlHandle
|
||||||
*/
|
*/
|
||||||
protected function getCurlHandler(string $endpoint): CurlHandle
|
protected function getCurlHandler(string $endpoint)
|
||||||
{
|
{
|
||||||
if (!array_key_exists($endpoint, $this->curlHandlers)) {
|
if (!array_key_exists($endpoint, $this->curlHandlers)) {
|
||||||
$this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint);
|
$this->curlHandlers[$endpoint] = $this->loadCurlHandle($endpoint);
|
||||||
|
@ -73,8 +77,12 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a fresh curl session for the given endpoint and returns its handler.
|
* Starts a fresh curl session for the given endpoint and returns its handler.
|
||||||
|
*
|
||||||
|
* @param string $endpoint
|
||||||
|
*
|
||||||
|
* @return resource|CurlHandle
|
||||||
*/
|
*/
|
||||||
private function loadCurlHandle(string $endpoint): CurlHandle
|
private function loadCurlHandle(string $endpoint)
|
||||||
{
|
{
|
||||||
$url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token);
|
$url = sprintf("https://%s/%s/%s/", static::HOST, $endpoint, $this->token);
|
||||||
|
|
||||||
|
@ -90,13 +98,10 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* @param string[]|string $tag
|
* @param string[]|string $tag
|
||||||
*/
|
*/
|
||||||
public function setTag(string|array $tag): self
|
public function setTag($tag): self
|
||||||
{
|
{
|
||||||
if ('' === $tag || [] === $tag) {
|
$tag = !empty($tag) ? $tag : [];
|
||||||
$this->tag = [];
|
|
||||||
} else {
|
|
||||||
$this->tag = is_array($tag) ? $tag : [$tag];
|
$this->tag = is_array($tag) ? $tag : [$tag];
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -104,9 +109,9 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* @param string[]|string $tag
|
* @param string[]|string $tag
|
||||||
*/
|
*/
|
||||||
public function addTag(string|array $tag): self
|
public function addTag($tag): self
|
||||||
{
|
{
|
||||||
if ('' !== $tag) {
|
if (!empty($tag)) {
|
||||||
$tag = is_array($tag) ? $tag : [$tag];
|
$tag = is_array($tag) ? $tag : [$tag];
|
||||||
$this->tag = array_unique(array_merge($this->tag, $tag));
|
$this->tag = array_unique(array_merge($this->tag, $tag));
|
||||||
}
|
}
|
||||||
|
@ -114,9 +119,9 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->send($record->formatted, static::ENDPOINT_SINGLE);
|
$this->send($record["formatted"], static::ENDPOINT_SINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
|
@ -124,10 +129,10 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
$level = $this->level;
|
$level = $this->level;
|
||||||
|
|
||||||
$records = array_filter($records, function ($record) use ($level) {
|
$records = array_filter($records, function ($record) use ($level) {
|
||||||
return ($record->level >= $level);
|
return ($record['level'] >= $level);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (\count($records) > 0) {
|
if ($records) {
|
||||||
$this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH);
|
$this->send($this->getFormatter()->formatBatch($records), static::ENDPOINT_BATCH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +143,7 @@ class LogglyHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
$headers = ['Content-Type: application/json'];
|
$headers = ['Content-Type: application/json'];
|
||||||
|
|
||||||
if (\count($this->tag) > 0) {
|
if (!empty($this->tag)) {
|
||||||
$headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag);
|
$headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,26 +11,34 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\LogmaticFormatter;
|
use Monolog\Formatter\LogmaticFormatter;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Julien Breux <julien.breux@gmail.com>
|
* @author Julien Breux <julien.breux@gmail.com>
|
||||||
*/
|
*/
|
||||||
class LogmaticHandler extends SocketHandler
|
class LogmaticHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
private string $logToken;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $logToken;
|
||||||
|
|
||||||
private string $hostname;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $hostname;
|
||||||
|
|
||||||
private string $appName;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $appname;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $token Log token supplied by Logmatic.
|
* @param string $token Log token supplied by Logmatic.
|
||||||
* @param string $hostname Host name supplied by Logmatic.
|
* @param string $hostname Host name supplied by Logmatic.
|
||||||
* @param string $appName Application name supplied by Logmatic.
|
* @param string $appname Application name supplied by Logmatic.
|
||||||
* @param bool $useSSL Whether or not SSL encryption should be used.
|
* @param bool $useSSL Whether or not SSL encryption should be used.
|
||||||
*
|
*
|
||||||
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
|
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
|
||||||
|
@ -38,9 +46,9 @@ class LogmaticHandler extends SocketHandler
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $token,
|
string $token,
|
||||||
string $hostname = '',
|
string $hostname = '',
|
||||||
string $appName = '',
|
string $appname = '',
|
||||||
bool $useSSL = true,
|
bool $useSSL = true,
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
float $timeout = 0.0,
|
float $timeout = 0.0,
|
||||||
|
@ -68,29 +76,29 @@ class LogmaticHandler extends SocketHandler
|
||||||
|
|
||||||
$this->logToken = $token;
|
$this->logToken = $token;
|
||||||
$this->hostname = $hostname;
|
$this->hostname = $hostname;
|
||||||
$this->appName = $appName;
|
$this->appname = $appname;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
return $this->logToken . ' ' . $record->formatted;
|
return $this->logToken . ' ' . $record['formatted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
$formatter = new LogmaticFormatter();
|
$formatter = new LogmaticFormatter();
|
||||||
|
|
||||||
if ($this->hostname !== '') {
|
if (!empty($this->hostname)) {
|
||||||
$formatter->setHostname($this->hostname);
|
$formatter->setHostname($this->hostname);
|
||||||
}
|
}
|
||||||
if ($this->appName !== '') {
|
if (!empty($this->appname)) {
|
||||||
$formatter->setAppName($this->appName);
|
$formatter->setAppname($this->appname);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $formatter;
|
return $formatter;
|
||||||
|
|
|
@ -13,32 +13,33 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\HtmlFormatter;
|
use Monolog\Formatter\HtmlFormatter;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all mail handlers
|
* Base class for all mail handlers
|
||||||
*
|
*
|
||||||
* @author Gyula Sallai
|
* @author Gyula Sallai
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
abstract class MailHandler extends AbstractProcessingHandler
|
abstract class MailHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handleBatch(array $records): void
|
public function handleBatch(array $records): void
|
||||||
{
|
{
|
||||||
$messages = [];
|
$messages = [];
|
||||||
|
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
if ($record->level->isLowerThan($this->level)) {
|
if ($record['level'] < $this->level) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/** @var Record $message */
|
||||||
$message = $this->processRecord($record);
|
$message = $this->processRecord($record);
|
||||||
$messages[] = $message;
|
$messages[] = $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\count($messages) > 0) {
|
if (!empty($messages)) {
|
||||||
$this->send((string) $this->getFormatter()->formatBatch($messages), $messages);
|
$this->send((string) $this->getFormatter()->formatBatch($messages), $messages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,26 +50,27 @@ abstract class MailHandler extends AbstractProcessingHandler
|
||||||
* @param string $content formatted email body to be sent
|
* @param string $content formatted email body to be sent
|
||||||
* @param array $records the array of log records that formed this content
|
* @param array $records the array of log records that formed this content
|
||||||
*
|
*
|
||||||
* @phpstan-param non-empty-array<LogRecord> $records
|
* @phpstan-param Record[] $records
|
||||||
*/
|
*/
|
||||||
abstract protected function send(string $content, array $records): void;
|
abstract protected function send(string $content, array $records): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->send((string) $record->formatted, [$record]);
|
$this->send((string) $record['formatted'], [$record]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-param non-empty-array<LogRecord> $records
|
* @phpstan-param non-empty-array<Record> $records
|
||||||
|
* @phpstan-return Record
|
||||||
*/
|
*/
|
||||||
protected function getHighestRecord(array $records): LogRecord
|
protected function getHighestRecord(array $records): array
|
||||||
{
|
{
|
||||||
$highestRecord = null;
|
$highestRecord = null;
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
if ($highestRecord === null || $record->level->isHigherThan($highestRecord->level)) {
|
if ($highestRecord === null || $highestRecord['level'] < $record['level']) {
|
||||||
$highestRecord = $record;
|
$highestRecord = $record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +85,8 @@ abstract class MailHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default formatter.
|
* Gets the default formatter.
|
||||||
|
*
|
||||||
|
* @return FormatterInterface
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Swift;
|
use Swift;
|
||||||
use Swift_Message;
|
use Swift_Message;
|
||||||
|
|
||||||
|
@ -22,20 +22,22 @@ use Swift_Message;
|
||||||
*/
|
*/
|
||||||
class MandrillHandler extends MailHandler
|
class MandrillHandler extends MailHandler
|
||||||
{
|
{
|
||||||
protected Swift_Message $message;
|
/** @var Swift_Message */
|
||||||
protected string $apiKey;
|
protected $message;
|
||||||
|
/** @var string */
|
||||||
|
protected $apiKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-param (Swift_Message|callable(): Swift_Message) $message
|
* @psalm-param Swift_Message|callable(): Swift_Message $message
|
||||||
*
|
*
|
||||||
* @param string $apiKey A valid Mandrill API key
|
* @param string $apiKey A valid Mandrill API key
|
||||||
* @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
|
* @param callable|Swift_Message $message An example message for real messages, only the body will be replaced
|
||||||
*/
|
*/
|
||||||
public function __construct(string $apiKey, callable|Swift_Message $message, int|string|Level $level = Level::Error, bool $bubble = true)
|
public function __construct(string $apiKey, $message, $level = Logger::ERROR, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
if (!$message instanceof Swift_Message) {
|
if (!$message instanceof Swift_Message && is_callable($message)) {
|
||||||
$message = $message();
|
$message = $message();
|
||||||
}
|
}
|
||||||
if (!$message instanceof Swift_Message) {
|
if (!$message instanceof Swift_Message) {
|
||||||
|
@ -46,7 +48,7 @@ class MandrillHandler extends MailHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function send(string $content, array $records): void
|
protected function send(string $content, array $records): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,10 +14,9 @@ namespace Monolog\Handler;
|
||||||
use MongoDB\Driver\BulkWrite;
|
use MongoDB\Driver\BulkWrite;
|
||||||
use MongoDB\Driver\Manager;
|
use MongoDB\Driver\Manager;
|
||||||
use MongoDB\Client;
|
use MongoDB\Client;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Formatter\MongoDBFormatter;
|
use Monolog\Formatter\MongoDBFormatter;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs to a MongoDB database.
|
* Logs to a MongoDB database.
|
||||||
|
@ -34,11 +33,12 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class MongoDBHandler extends AbstractProcessingHandler
|
class MongoDBHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
private \MongoDB\Collection $collection;
|
/** @var \MongoDB\Collection */
|
||||||
|
private $collection;
|
||||||
private Client|Manager $manager;
|
/** @var Client|Manager */
|
||||||
|
private $manager;
|
||||||
private string|null $namespace = null;
|
/** @var string */
|
||||||
|
private $namespace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -47,8 +47,12 @@ class MongoDBHandler extends AbstractProcessingHandler
|
||||||
* @param string $database Database name
|
* @param string $database Database name
|
||||||
* @param string $collection Collection name
|
* @param string $collection Collection name
|
||||||
*/
|
*/
|
||||||
public function __construct(Client|Manager $mongodb, string $database, string $collection, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct($mongodb, string $database, string $collection, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
|
if (!($mongodb instanceof Client || $mongodb instanceof Manager)) {
|
||||||
|
throw new \InvalidArgumentException('MongoDB\Client or MongoDB\Driver\Manager instance required');
|
||||||
|
}
|
||||||
|
|
||||||
if ($mongodb instanceof Client) {
|
if ($mongodb instanceof Client) {
|
||||||
$this->collection = $mongodb->selectCollection($database, $collection);
|
$this->collection = $mongodb->selectCollection($database, $collection);
|
||||||
} else {
|
} else {
|
||||||
|
@ -59,21 +63,21 @@ class MongoDBHandler extends AbstractProcessingHandler
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (isset($this->collection)) {
|
if (isset($this->collection)) {
|
||||||
$this->collection->insertOne($record->formatted);
|
$this->collection->insertOne($record['formatted']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->manager, $this->namespace)) {
|
if (isset($this->manager, $this->namespace)) {
|
||||||
$bulk = new BulkWrite;
|
$bulk = new BulkWrite;
|
||||||
$bulk->insert($record->formatted);
|
$bulk->insert($record["formatted"]);
|
||||||
$this->manager->executeBulkWrite($this->namespace, $bulk);
|
$this->manager->executeBulkWrite($this->namespace, $bulk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,39 +26,43 @@ class NativeMailerHandler extends MailHandler
|
||||||
* The email addresses to which the message will be sent
|
* The email addresses to which the message will be sent
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
protected array $to;
|
protected $to;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The subject of the email
|
* The subject of the email
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected string $subject;
|
protected $subject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional headers for the message
|
* Optional headers for the message
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
protected array $headers = [];
|
protected $headers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional parameters for the message
|
* Optional parameters for the message
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
protected array $parameters = [];
|
protected $parameters = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The wordwrap length for the message
|
* The wordwrap length for the message
|
||||||
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected int $maxColumnWidth;
|
protected $maxColumnWidth;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Content-type for the message
|
* The Content-type for the message
|
||||||
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
protected string|null $contentType = null;
|
protected $contentType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The encoding for the message
|
* The encoding for the message
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected string $encoding = 'utf-8';
|
protected $encoding = 'utf-8';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|string[] $to The receiver of the mail
|
* @param string|string[] $to The receiver of the mail
|
||||||
|
@ -66,7 +70,7 @@ class NativeMailerHandler extends MailHandler
|
||||||
* @param string $from The sender of the mail
|
* @param string $from The sender of the mail
|
||||||
* @param int $maxColumnWidth The maximum column width that the message lines will have
|
* @param int $maxColumnWidth The maximum column width that the message lines will have
|
||||||
*/
|
*/
|
||||||
public function __construct(string|array $to, string $subject, string $from, int|string|Level $level = Level::Error, bool $bubble = true, int $maxColumnWidth = 70)
|
public function __construct($to, string $subject, string $from, $level = Logger::ERROR, bool $bubble = true, int $maxColumnWidth = 70)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
$this->to = (array) $to;
|
$this->to = (array) $to;
|
||||||
|
@ -105,11 +109,11 @@ class NativeMailerHandler extends MailHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function send(string $content, array $records): void
|
protected function send(string $content, array $records): void
|
||||||
{
|
{
|
||||||
$contentType = $this->getContentType() ?? ($this->isHtmlBody($content) ? 'text/html' : 'text/plain');
|
$contentType = $this->getContentType() ?: ($this->isHtmlBody($content) ? 'text/html' : 'text/plain');
|
||||||
|
|
||||||
if ($contentType !== 'text/html') {
|
if ($contentType !== 'text/html') {
|
||||||
$content = wordwrap($content, $this->maxColumnWidth);
|
$content = wordwrap($content, $this->maxColumnWidth);
|
||||||
|
@ -121,8 +125,11 @@ class NativeMailerHandler extends MailHandler
|
||||||
$headers .= 'MIME-Version: 1.0' . "\r\n";
|
$headers .= 'MIME-Version: 1.0' . "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$subject = $this->subject;
|
||||||
|
if ($records) {
|
||||||
$subjectFormatter = new LineFormatter($this->subject);
|
$subjectFormatter = new LineFormatter($this->subject);
|
||||||
$subject = $subjectFormatter->format($this->getHighestRecord($records));
|
$subject = $subjectFormatter->format($this->getHighestRecord($records));
|
||||||
|
}
|
||||||
|
|
||||||
$parameters = implode(' ', $this->parameters);
|
$parameters = implode(' ', $this->parameters);
|
||||||
foreach ($this->to as $to) {
|
foreach ($this->to as $to) {
|
||||||
|
|
|
@ -11,17 +11,16 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\Formatter\NormalizerFormatter;
|
use Monolog\Formatter\NormalizerFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to record a log on a NewRelic application.
|
* Class to record a log on a NewRelic application.
|
||||||
* Enabling New Relic High Security mode may prevent capture of useful information.
|
* Enabling New Relic High Security mode may prevent capture of useful information.
|
||||||
*
|
*
|
||||||
* This handler requires a NormalizerFormatter to function and expects an array in $record->formatted
|
* This handler requires a NormalizerFormatter to function and expects an array in $record['formatted']
|
||||||
*
|
*
|
||||||
* @see https://docs.newrelic.com/docs/agents/php-agent
|
* @see https://docs.newrelic.com/docs/agents/php-agent
|
||||||
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
|
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
|
||||||
|
@ -29,58 +28,75 @@ use Monolog\LogRecord;
|
||||||
class NewRelicHandler extends AbstractProcessingHandler
|
class NewRelicHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* Name of the New Relic application that will receive logs from this handler.
|
||||||
|
*
|
||||||
|
* @var ?string
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
protected $appName;
|
||||||
int|string|Level $level = Level::Error,
|
|
||||||
bool $bubble = true,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the New Relic application that will receive logs from this handler.
|
* Name of the current transaction
|
||||||
|
*
|
||||||
|
* @var ?string
|
||||||
*/
|
*/
|
||||||
protected string|null $appName = null,
|
protected $transactionName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some context and extra data is passed into the handler as arrays of values. Do we send them as is
|
* Some context and extra data is passed into the handler as arrays of values. Do we send them as is
|
||||||
* (useful if we are using the API), or explode them for display on the NewRelic RPM website?
|
* (useful if we are using the API), or explode them for display on the NewRelic RPM website?
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected bool $explodeArrays = false,
|
protected $explodeArrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the current transaction
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* @param string|null $appName
|
||||||
|
* @param bool $explodeArrays
|
||||||
|
* @param string|null $transactionName
|
||||||
*/
|
*/
|
||||||
protected string|null $transactionName = null
|
public function __construct(
|
||||||
|
$level = Logger::ERROR,
|
||||||
|
bool $bubble = true,
|
||||||
|
?string $appName = null,
|
||||||
|
bool $explodeArrays = false,
|
||||||
|
?string $transactionName = null
|
||||||
) {
|
) {
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
|
$this->appName = $appName;
|
||||||
|
$this->explodeArrays = $explodeArrays;
|
||||||
|
$this->transactionName = $transactionName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (!$this->isNewRelicEnabled()) {
|
if (!$this->isNewRelicEnabled()) {
|
||||||
throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler');
|
throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== ($appName = $this->getAppName($record->context))) {
|
if ($appName = $this->getAppName($record['context'])) {
|
||||||
$this->setNewRelicAppName($appName);
|
$this->setNewRelicAppName($appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== ($transactionName = $this->getTransactionName($record->context))) {
|
if ($transactionName = $this->getTransactionName($record['context'])) {
|
||||||
$this->setNewRelicTransactionName($transactionName);
|
$this->setNewRelicTransactionName($transactionName);
|
||||||
unset($record->formatted['context']['transaction_name']);
|
unset($record['formatted']['context']['transaction_name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) {
|
if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
|
||||||
newrelic_notice_error($record->message, $record->context['exception']);
|
newrelic_notice_error($record['message'], $record['context']['exception']);
|
||||||
unset($record->formatted['context']['exception']);
|
unset($record['formatted']['context']['exception']);
|
||||||
} else {
|
} else {
|
||||||
newrelic_notice_error($record->message);
|
newrelic_notice_error($record['message']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($record->formatted['context']) && is_array($record->formatted['context'])) {
|
if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) {
|
||||||
foreach ($record->formatted['context'] as $key => $parameter) {
|
foreach ($record['formatted']['context'] as $key => $parameter) {
|
||||||
if (is_array($parameter) && $this->explodeArrays) {
|
if (is_array($parameter) && $this->explodeArrays) {
|
||||||
foreach ($parameter as $paramKey => $paramValue) {
|
foreach ($parameter as $paramKey => $paramValue) {
|
||||||
$this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue);
|
$this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue);
|
||||||
|
@ -91,8 +107,8 @@ class NewRelicHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($record->formatted['extra']) && is_array($record->formatted['extra'])) {
|
if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) {
|
||||||
foreach ($record->formatted['extra'] as $key => $parameter) {
|
foreach ($record['formatted']['extra'] as $key => $parameter) {
|
||||||
if (is_array($parameter) && $this->explodeArrays) {
|
if (is_array($parameter) && $this->explodeArrays) {
|
||||||
foreach ($parameter as $paramKey => $paramValue) {
|
foreach ($parameter as $paramKey => $paramValue) {
|
||||||
$this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue);
|
$this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue);
|
||||||
|
@ -106,6 +122,8 @@ class NewRelicHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the NewRelic extension is enabled in the system.
|
* Checks whether the NewRelic extension is enabled in the system.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function isNewRelicEnabled(): bool
|
protected function isNewRelicEnabled(): bool
|
||||||
{
|
{
|
||||||
|
@ -159,6 +177,7 @@ class NewRelicHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param string $key
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
*/
|
*/
|
||||||
protected function setNewRelicParameter(string $key, $value): void
|
protected function setNewRelicParameter(string $key, $value): void
|
||||||
|
@ -171,7 +190,7 @@ class NewRelicHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No-op
|
* No-op
|
||||||
*
|
*
|
||||||
|
@ -25,17 +23,17 @@ use Monolog\LogRecord;
|
||||||
class NoopHandler extends Handler
|
class NoopHandler extends Handler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Psr\Log\LogLevel;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
use Psr\Log\LogLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blackhole
|
* Blackhole
|
||||||
|
@ -23,34 +21,40 @@ use Monolog\LogRecord;
|
||||||
* to put on top of an existing stack to override it temporarily.
|
* to put on top of an existing stack to override it temporarily.
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class NullHandler extends Handler
|
class NullHandler extends Handler
|
||||||
{
|
{
|
||||||
private Level $level;
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|int|Level $level The minimum logging level at which this handler will be triggered
|
* @param string|int $level The minimum logging level at which this handler will be triggered
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
|
* @phpstan-param Level|LevelName|LogLevel::* $level
|
||||||
*/
|
*/
|
||||||
public function __construct(string|int|Level $level = Level::Debug)
|
public function __construct($level = Logger::DEBUG)
|
||||||
{
|
{
|
||||||
$this->level = Logger::toMonologLevel($level);
|
$this->level = Logger::toMonologLevel($level);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return $record->level->value >= $this->level->value;
|
return $record['level'] >= $this->level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
return $record->level->value >= $this->level->value;
|
return $record['level'] >= $this->level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to only pass log messages when a certain threshold of number of messages is reached.
|
* Handler to only pass log messages when a certain threshold of number of messages is reached.
|
||||||
|
@ -28,7 +27,7 @@ use Monolog\LogRecord;
|
||||||
* $handler = new SomeHandler(...)
|
* $handler = new SomeHandler(...)
|
||||||
*
|
*
|
||||||
* // Pass all warnings to the handler when more than 10 & all error messages when more then 5
|
* // Pass all warnings to the handler when more than 10 & all error messages when more then 5
|
||||||
* $overflow = new OverflowHandler($handler, [Level::Warning->value => 10, Level::Error->value => 5]);
|
* $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]);
|
||||||
*
|
*
|
||||||
* $log->pushHandler($overflow);
|
* $log->pushHandler($overflow);
|
||||||
*```
|
*```
|
||||||
|
@ -37,25 +36,36 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface
|
class OverflowHandler extends AbstractHandler implements FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
private HandlerInterface $handler;
|
/** @var HandlerInterface */
|
||||||
|
private $handler;
|
||||||
|
|
||||||
/** @var array<int, int> */
|
/** @var int[] */
|
||||||
private array $thresholdMap = [];
|
private $thresholdMap = [
|
||||||
|
Logger::DEBUG => 0,
|
||||||
|
Logger::INFO => 0,
|
||||||
|
Logger::NOTICE => 0,
|
||||||
|
Logger::WARNING => 0,
|
||||||
|
Logger::ERROR => 0,
|
||||||
|
Logger::CRITICAL => 0,
|
||||||
|
Logger::ALERT => 0,
|
||||||
|
Logger::EMERGENCY => 0,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffer of all messages passed to the handler before the threshold was reached
|
* Buffer of all messages passed to the handler before the threshold was reached
|
||||||
*
|
*
|
||||||
* @var mixed[][]
|
* @var mixed[][]
|
||||||
*/
|
*/
|
||||||
private array $buffer = [];
|
private $buffer = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int, int> $thresholdMap Dictionary of log level value => threshold
|
* @param HandlerInterface $handler
|
||||||
|
* @param int[] $thresholdMap Dictionary of logger level => threshold
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
HandlerInterface $handler,
|
HandlerInterface $handler,
|
||||||
array $thresholdMap = [],
|
array $thresholdMap = [],
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true
|
bool $bubble = true
|
||||||
) {
|
) {
|
||||||
$this->handler = $handler;
|
$this->handler = $handler;
|
||||||
|
@ -75,15 +85,15 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
|
||||||
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
|
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
|
||||||
* calling further handlers in the stack with a given log record.
|
* calling further handlers in the stack with a given log record.
|
||||||
*
|
*
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if ($record->level->isLowerThan($this->level)) {
|
if ($record['level'] < $this->level) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$level = $record->level->value;
|
$level = $record['level'];
|
||||||
|
|
||||||
if (!isset($this->thresholdMap[$level])) {
|
if (!isset($this->thresholdMap[$level])) {
|
||||||
$this->thresholdMap[$level] = 0;
|
$this->thresholdMap[$level] = 0;
|
||||||
|
@ -112,7 +122,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -126,7 +136,7 @@ class OverflowHandler extends AbstractHandler implements FormattableHandlerInter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,13 +13,11 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use PhpConsole\Connector;
|
use PhpConsole\Connector;
|
||||||
use PhpConsole\Handler as VendorPhpConsoleHandler;
|
use PhpConsole\Handler as VendorPhpConsoleHandler;
|
||||||
use PhpConsole\Helper;
|
use PhpConsole\Helper;
|
||||||
use Monolog\LogRecord;
|
|
||||||
use PhpConsole\Storage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monolog handler for Google Chrome extension "PHP Console"
|
* Monolog handler for Google Chrome extension "PHP Console"
|
||||||
|
@ -39,59 +37,14 @@ use PhpConsole\Storage;
|
||||||
* PC::debug($_SERVER); // PHP Console debugger for any type of vars
|
* PC::debug($_SERVER); // PHP Console debugger for any type of vars
|
||||||
*
|
*
|
||||||
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin
|
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin
|
||||||
* @phpstan-type Options array{
|
|
||||||
* enabled: bool,
|
|
||||||
* classesPartialsTraceIgnore: string[],
|
|
||||||
* debugTagsKeysInContext: array<int|string>,
|
|
||||||
* useOwnErrorsHandler: bool,
|
|
||||||
* useOwnExceptionsHandler: bool,
|
|
||||||
* sourcesBasePath: string|null,
|
|
||||||
* registerHelper: bool,
|
|
||||||
* serverEncoding: string|null,
|
|
||||||
* headersLimit: int|null,
|
|
||||||
* password: string|null,
|
|
||||||
* enableSslOnlyMode: bool,
|
|
||||||
* ipMasks: string[],
|
|
||||||
* enableEvalListener: bool,
|
|
||||||
* dumperDetectCallbacks: bool,
|
|
||||||
* dumperLevelLimit: int,
|
|
||||||
* dumperItemsCountLimit: int,
|
|
||||||
* dumperItemSizeLimit: int,
|
|
||||||
* dumperDumpSizeLimit: int,
|
|
||||||
* detectDumpTraceAndSource: bool,
|
|
||||||
* dataStorage: Storage|null
|
|
||||||
* }
|
|
||||||
* @phpstan-type InputOptions array{
|
|
||||||
* enabled?: bool,
|
|
||||||
* classesPartialsTraceIgnore?: string[],
|
|
||||||
* debugTagsKeysInContext?: array<int|string>,
|
|
||||||
* useOwnErrorsHandler?: bool,
|
|
||||||
* useOwnExceptionsHandler?: bool,
|
|
||||||
* sourcesBasePath?: string|null,
|
|
||||||
* registerHelper?: bool,
|
|
||||||
* serverEncoding?: string|null,
|
|
||||||
* headersLimit?: int|null,
|
|
||||||
* password?: string|null,
|
|
||||||
* enableSslOnlyMode?: bool,
|
|
||||||
* ipMasks?: string[],
|
|
||||||
* enableEvalListener?: bool,
|
|
||||||
* dumperDetectCallbacks?: bool,
|
|
||||||
* dumperLevelLimit?: int,
|
|
||||||
* dumperItemsCountLimit?: int,
|
|
||||||
* dumperItemSizeLimit?: int,
|
|
||||||
* dumperDumpSizeLimit?: int,
|
|
||||||
* detectDumpTraceAndSource?: bool,
|
|
||||||
* dataStorage?: Storage|null
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
* @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4
|
* @deprecated Since 2.8.0 and 3.2.0, PHPConsole is abandoned and thus we will drop this handler in Monolog 4
|
||||||
*/
|
*/
|
||||||
class PHPConsoleHandler extends AbstractProcessingHandler
|
class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/**
|
/** @var array<string, mixed> */
|
||||||
* @phpstan-var Options
|
private $options = [
|
||||||
*/
|
|
||||||
private array $options = [
|
|
||||||
'enabled' => true, // bool Is PHP Console server enabled
|
'enabled' => true, // bool Is PHP Console server enabled
|
||||||
'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
|
'classesPartialsTraceIgnore' => ['Monolog\\'], // array Hide calls of classes started with...
|
||||||
'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled
|
'debugTagsKeysInContext' => [0, 'tag'], // bool Is PHP Console server enabled
|
||||||
|
@ -114,15 +67,15 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ)
|
'dataStorage' => null, // \PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ)
|
||||||
];
|
];
|
||||||
|
|
||||||
private Connector $connector;
|
/** @var Connector */
|
||||||
|
private $connector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
|
* @param array<string, mixed> $options See \Monolog\Handler\PHPConsoleHandler::$options for more details
|
||||||
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
|
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional)
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
* @phpstan-param InputOptions $options
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = [], ?Connector $connector = null, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(array $options = [], ?Connector $connector = null, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
if (!class_exists('PhpConsole\Connector')) {
|
if (!class_exists('PhpConsole\Connector')) {
|
||||||
throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
|
throw new \RuntimeException('PHP Console library not found. See https://github.com/barbushin/php-console#installation');
|
||||||
|
@ -134,15 +87,13 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $options
|
* @param array<string, mixed> $options
|
||||||
* @return array<string, mixed>
|
|
||||||
*
|
*
|
||||||
* @phpstan-param InputOptions $options
|
* @return array<string, mixed>
|
||||||
* @phpstan-return Options
|
|
||||||
*/
|
*/
|
||||||
private function initOptions(array $options): array
|
private function initOptions(array $options): array
|
||||||
{
|
{
|
||||||
$wrongOptions = array_diff(array_keys($options), array_keys($this->options));
|
$wrongOptions = array_diff(array_keys($options), array_keys($this->options));
|
||||||
if (\count($wrongOptions) > 0) {
|
if ($wrongOptions) {
|
||||||
throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions));
|
throw new \RuntimeException('Unknown options: ' . implode(', ', $wrongOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,8 +102,8 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
private function initConnector(?Connector $connector = null): Connector
|
private function initConnector(?Connector $connector = null): Connector
|
||||||
{
|
{
|
||||||
if (null === $connector) {
|
if (!$connector) {
|
||||||
if ($this->options['dataStorage'] instanceof Storage) {
|
if ($this->options['dataStorage']) {
|
||||||
Connector::setPostponeStorage($this->options['dataStorage']);
|
Connector::setPostponeStorage($this->options['dataStorage']);
|
||||||
}
|
}
|
||||||
$connector = Connector::getInstance();
|
$connector = Connector::getInstance();
|
||||||
|
@ -169,22 +120,22 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
$handler->setHandleExceptions($this->options['useOwnExceptionsHandler']);
|
$handler->setHandleExceptions($this->options['useOwnExceptionsHandler']);
|
||||||
$handler->start();
|
$handler->start();
|
||||||
}
|
}
|
||||||
if (null !== $this->options['sourcesBasePath']) {
|
if ($this->options['sourcesBasePath']) {
|
||||||
$connector->setSourcesBasePath($this->options['sourcesBasePath']);
|
$connector->setSourcesBasePath($this->options['sourcesBasePath']);
|
||||||
}
|
}
|
||||||
if (null !== $this->options['serverEncoding']) {
|
if ($this->options['serverEncoding']) {
|
||||||
$connector->setServerEncoding($this->options['serverEncoding']);
|
$connector->setServerEncoding($this->options['serverEncoding']);
|
||||||
}
|
}
|
||||||
if (null !== $this->options['password']) {
|
if ($this->options['password']) {
|
||||||
$connector->setPassword($this->options['password']);
|
$connector->setPassword($this->options['password']);
|
||||||
}
|
}
|
||||||
if ($this->options['enableSslOnlyMode']) {
|
if ($this->options['enableSslOnlyMode']) {
|
||||||
$connector->enableSslOnlyMode();
|
$connector->enableSslOnlyMode();
|
||||||
}
|
}
|
||||||
if (\count($this->options['ipMasks']) > 0) {
|
if ($this->options['ipMasks']) {
|
||||||
$connector->setAllowedIpMasks($this->options['ipMasks']);
|
$connector->setAllowedIpMasks($this->options['ipMasks']);
|
||||||
}
|
}
|
||||||
if (null !== $this->options['headersLimit'] && $this->options['headersLimit'] > 0) {
|
if ($this->options['headersLimit']) {
|
||||||
$connector->setHeadersLimit($this->options['headersLimit']);
|
$connector->setHeadersLimit($this->options['headersLimit']);
|
||||||
}
|
}
|
||||||
if ($this->options['detectDumpTraceAndSource']) {
|
if ($this->options['detectDumpTraceAndSource']) {
|
||||||
|
@ -217,7 +168,7 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
return $this->options;
|
return $this->options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if ($this->options['enabled'] && $this->connector->isActiveClient()) {
|
if ($this->options['enabled'] && $this->connector->isActiveClient()) {
|
||||||
return parent::handle($record);
|
return parent::handle($record);
|
||||||
|
@ -229,39 +180,48 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* Writes the record down to the log of the implementing handler
|
* Writes the record down to the log of the implementing handler
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if ($record->level->isLowerThan(Level::Notice)) {
|
if ($record['level'] < Logger::NOTICE) {
|
||||||
$this->handleDebugRecord($record);
|
$this->handleDebugRecord($record);
|
||||||
} elseif (isset($record->context['exception']) && $record->context['exception'] instanceof \Throwable) {
|
} elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Throwable) {
|
||||||
$this->handleExceptionRecord($record);
|
$this->handleExceptionRecord($record);
|
||||||
} else {
|
} else {
|
||||||
$this->handleErrorRecord($record);
|
$this->handleErrorRecord($record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleDebugRecord(LogRecord $record): void
|
/**
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
*/
|
||||||
|
private function handleDebugRecord(array $record): void
|
||||||
{
|
{
|
||||||
[$tags, $filteredContext] = $this->getRecordTags($record);
|
$tags = $this->getRecordTags($record);
|
||||||
$message = $record->message;
|
$message = $record['message'];
|
||||||
if (\count($filteredContext) > 0) {
|
if ($record['context']) {
|
||||||
$message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($filteredContext)), null, true);
|
$message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(array_filter($record['context'])), null, true);
|
||||||
}
|
}
|
||||||
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
|
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleExceptionRecord(LogRecord $record): void
|
/**
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
*/
|
||||||
|
private function handleExceptionRecord(array $record): void
|
||||||
{
|
{
|
||||||
$this->connector->getErrorsDispatcher()->dispatchException($record->context['exception']);
|
$this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleErrorRecord(LogRecord $record): void
|
/**
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
*/
|
||||||
|
private function handleErrorRecord(array $record): void
|
||||||
{
|
{
|
||||||
$context = $record->context;
|
$context = $record['context'];
|
||||||
|
|
||||||
$this->connector->getErrorsDispatcher()->dispatchError(
|
$this->connector->getErrorsDispatcher()->dispatchError(
|
||||||
$context['code'] ?? null,
|
$context['code'] ?? null,
|
||||||
$context['message'] ?? $record->message,
|
$context['message'] ?? $record['message'],
|
||||||
$context['file'] ?? null,
|
$context['file'] ?? null,
|
||||||
$context['line'] ?? null,
|
$context['line'] ?? null,
|
||||||
$this->options['classesPartialsTraceIgnore']
|
$this->options['classesPartialsTraceIgnore']
|
||||||
|
@ -269,32 +229,32 @@ class PHPConsoleHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{string, mixed[]}
|
* @phpstan-param Record $record
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function getRecordTags(LogRecord $record): array
|
private function getRecordTags(array &$record)
|
||||||
{
|
{
|
||||||
$tags = null;
|
$tags = null;
|
||||||
$filteredContext = [];
|
if (!empty($record['context'])) {
|
||||||
if ($record->context !== []) {
|
$context = & $record['context'];
|
||||||
$filteredContext = $record->context;
|
|
||||||
foreach ($this->options['debugTagsKeysInContext'] as $key) {
|
foreach ($this->options['debugTagsKeysInContext'] as $key) {
|
||||||
if (isset($filteredContext[$key])) {
|
if (!empty($context[$key])) {
|
||||||
$tags = $filteredContext[$key];
|
$tags = $context[$key];
|
||||||
if ($key === 0) {
|
if ($key === 0) {
|
||||||
array_shift($filteredContext);
|
array_shift($context);
|
||||||
} else {
|
} else {
|
||||||
unset($filteredContext[$key]);
|
unset($context[$key]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$tags ?? $record->level->toPsrLogLevel(), $filteredContext];
|
return $tags ?: strtolower($record['level_name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores to STDIN of any process, specified by a command.
|
* Stores to STDIN of any process, specified by a command.
|
||||||
|
@ -34,14 +33,20 @@ class ProcessHandler extends AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
private $process;
|
private $process;
|
||||||
|
|
||||||
private string $command;
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $command;
|
||||||
|
|
||||||
private ?string $cwd;
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $cwd;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var resource[]
|
* @var resource[]
|
||||||
*/
|
*/
|
||||||
private array $pipes = [];
|
private $pipes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array<int, string[]>
|
* @var array<int, string[]>
|
||||||
|
@ -58,7 +63,7 @@ class ProcessHandler extends AbstractProcessingHandler
|
||||||
* @param string|null $cwd "Current working directory" (CWD) for the process to be executed in.
|
* @param string|null $cwd "Current working directory" (CWD) for the process to be executed in.
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public function __construct(string $command, int|string|Level $level = Level::Debug, bool $bubble = true, ?string $cwd = null)
|
public function __construct(string $command, $level = Logger::DEBUG, bool $bubble = true, ?string $cwd = null)
|
||||||
{
|
{
|
||||||
if ($command === '') {
|
if ($command === '') {
|
||||||
throw new \InvalidArgumentException('The command argument must be a non-empty string.');
|
throw new \InvalidArgumentException('The command argument must be a non-empty string.');
|
||||||
|
@ -78,14 +83,14 @@ class ProcessHandler extends AbstractProcessingHandler
|
||||||
*
|
*
|
||||||
* @throws \UnexpectedValueException
|
* @throws \UnexpectedValueException
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->ensureProcessIsStarted();
|
$this->ensureProcessIsStarted();
|
||||||
|
|
||||||
$this->writeProcessInput($record->formatted);
|
$this->writeProcessInput($record['formatted']);
|
||||||
|
|
||||||
$errors = $this->readProcessErrors();
|
$errors = $this->readProcessErrors();
|
||||||
if ($errors !== '') {
|
if (empty($errors) === false) {
|
||||||
throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors));
|
throw new \UnexpectedValueException(sprintf('Errors while writing to process: %s', $errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +134,7 @@ class ProcessHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
$errors = $this->readProcessErrors();
|
$errors = $this->readProcessErrors();
|
||||||
|
|
||||||
if (is_resource($this->process) === false || $errors !== '') {
|
if (is_resource($this->process) === false || empty($errors) === false) {
|
||||||
throw new \UnexpectedValueException(
|
throw new \UnexpectedValueException(
|
||||||
sprintf('The process "%s" could not be opened: ' . $errors, $this->command)
|
sprintf('The process "%s" could not be opened: ' . $errors, $this->command)
|
||||||
);
|
);
|
||||||
|
@ -171,7 +176,7 @@ class ProcessHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,19 +12,20 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Processor\ProcessorInterface;
|
use Monolog\Processor\ProcessorInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface to describe loggers that have processors
|
* Interface to describe loggers that have processors
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
interface ProcessableHandlerInterface
|
interface ProcessableHandlerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Adds a processor in the stack.
|
* Adds a processor in the stack.
|
||||||
*
|
*
|
||||||
* @phpstan-param ProcessorInterface|(callable(LogRecord): LogRecord) $callback
|
* @psalm-param ProcessorInterface|callable(Record): Record $callback
|
||||||
*
|
*
|
||||||
* @param ProcessorInterface|callable $callback
|
* @param ProcessorInterface|callable $callback
|
||||||
* @return HandlerInterface self
|
* @return HandlerInterface self
|
||||||
|
@ -34,7 +35,7 @@ interface ProcessableHandlerInterface
|
||||||
/**
|
/**
|
||||||
* Removes the processor on top of the stack and returns it.
|
* Removes the processor on top of the stack and returns it.
|
||||||
*
|
*
|
||||||
* @phpstan-return ProcessorInterface|(callable(LogRecord): LogRecord) $callback
|
* @psalm-return ProcessorInterface|callable(Record): Record $callback
|
||||||
*
|
*
|
||||||
* @throws \LogicException In case the processor stack is empty
|
* @throws \LogicException In case the processor stack is empty
|
||||||
* @return callable|ProcessorInterface
|
* @return callable|ProcessorInterface
|
||||||
|
|
|
@ -13,23 +13,24 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\ResettableInterface;
|
use Monolog\ResettableInterface;
|
||||||
use Monolog\Processor\ProcessorInterface;
|
use Monolog\Processor\ProcessorInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper trait for implementing ProcessableInterface
|
* Helper trait for implementing ProcessableInterface
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
trait ProcessableHandlerTrait
|
trait ProcessableHandlerTrait
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var callable[]
|
* @var callable[]
|
||||||
* @phpstan-var array<(callable(LogRecord): LogRecord)|ProcessorInterface>
|
* @phpstan-var array<ProcessorInterface|callable(Record): Record>
|
||||||
*/
|
*/
|
||||||
protected array $processors = [];
|
protected $processors = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function pushProcessor(callable $callback): HandlerInterface
|
public function pushProcessor(callable $callback): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -39,18 +40,24 @@ trait ProcessableHandlerTrait
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function popProcessor(): callable
|
public function popProcessor(): callable
|
||||||
{
|
{
|
||||||
if (\count($this->processors) === 0) {
|
if (!$this->processors) {
|
||||||
throw new \LogicException('You tried to pop from an empty processor stack.');
|
throw new \LogicException('You tried to pop from an empty processor stack.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_shift($this->processors);
|
return array_shift($this->processors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function processRecord(LogRecord $record): LogRecord
|
/**
|
||||||
|
* Processes a record.
|
||||||
|
*
|
||||||
|
* @phpstan-param Record $record
|
||||||
|
* @phpstan-return Record
|
||||||
|
*/
|
||||||
|
protected function processRecord(array $record): array
|
||||||
{
|
{
|
||||||
foreach ($this->processors as $processor) {
|
foreach ($this->processors as $processor) {
|
||||||
$record = $processor($record);
|
$record = $processor($record);
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proxies log messages to an existing PSR-3 compliant logger.
|
* Proxies log messages to an existing PSR-3 compliant logger.
|
||||||
|
@ -29,15 +28,20 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* PSR-3 compliant logger
|
* PSR-3 compliant logger
|
||||||
|
*
|
||||||
|
* @var LoggerInterface
|
||||||
*/
|
*/
|
||||||
protected LoggerInterface $logger;
|
protected $logger;
|
||||||
|
|
||||||
protected FormatterInterface|null $formatter = null;
|
/**
|
||||||
|
* @var FormatterInterface|null
|
||||||
|
*/
|
||||||
|
protected $formatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied
|
* @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied
|
||||||
*/
|
*/
|
||||||
public function __construct(LoggerInterface $logger, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
|
@ -45,19 +49,19 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if (!$this->isHandling($record)) {
|
if (!$this->isHandling($record)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->formatter !== null) {
|
if ($this->formatter) {
|
||||||
$formatted = $this->formatter->format($record);
|
$formatted = $this->formatter->format($record);
|
||||||
$this->logger->log($record->level->toPsrLogLevel(), (string) $formatted, $record->context);
|
$this->logger->log(strtolower($record['level_name']), (string) $formatted, $record['context']);
|
||||||
} else {
|
} else {
|
||||||
$this->logger->log($record->level->toPsrLogLevel(), $record->message, $record->context);
|
$this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false === $this->bubble;
|
return false === $this->bubble;
|
||||||
|
@ -65,6 +69,8 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the formatter.
|
* Sets the formatter.
|
||||||
|
*
|
||||||
|
* @param FormatterInterface $formatter
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -75,10 +81,12 @@ class PsrHandler extends AbstractHandler implements FormattableHandlerInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the formatter.
|
* Gets the formatter.
|
||||||
|
*
|
||||||
|
* @return FormatterInterface
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
if ($this->formatter === null) {
|
if (!$this->formatter) {
|
||||||
throw new \LogicException('No formatter has been set and this handler does not have a default formatter');
|
throw new \LogicException('No formatter has been set and this handler does not have a default formatter');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,45 +11,48 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Monolog\Logger;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Psr\Log\LogLevel;
|
use Psr\Log\LogLevel;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends notifications through the pushover api to mobile phones
|
* Sends notifications through the pushover api to mobile phones
|
||||||
*
|
*
|
||||||
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
|
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com>
|
||||||
* @see https://www.pushover.net/api
|
* @see https://www.pushover.net/api
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
|
* @phpstan-import-type LevelName from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class PushoverHandler extends SocketHandler
|
class PushoverHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
private string $token;
|
/** @var string */
|
||||||
|
private $token;
|
||||||
/** @var array<int|string> */
|
/** @var array<int|string> */
|
||||||
private array $users;
|
private $users;
|
||||||
|
/** @var string */
|
||||||
|
private $title;
|
||||||
|
/** @var string|int|null */
|
||||||
|
private $user = null;
|
||||||
|
/** @var int */
|
||||||
|
private $retry;
|
||||||
|
/** @var int */
|
||||||
|
private $expire;
|
||||||
|
|
||||||
private string $title;
|
/** @var int */
|
||||||
|
private $highPriorityLevel;
|
||||||
private string|int|null $user = null;
|
/** @var int */
|
||||||
|
private $emergencyLevel;
|
||||||
private int $retry;
|
/** @var bool */
|
||||||
|
private $useFormattedMessage = false;
|
||||||
private int $expire;
|
|
||||||
|
|
||||||
private Level $highPriorityLevel;
|
|
||||||
|
|
||||||
private Level $emergencyLevel;
|
|
||||||
|
|
||||||
private bool $useFormattedMessage = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All parameters that can be sent to Pushover
|
* All parameters that can be sent to Pushover
|
||||||
* @see https://pushover.net/api
|
* @see https://pushover.net/api
|
||||||
* @var array<string, bool>
|
* @var array<string, bool>
|
||||||
*/
|
*/
|
||||||
private array $parameterNames = [
|
private $parameterNames = [
|
||||||
'token' => true,
|
'token' => true,
|
||||||
'user' => true,
|
'user' => true,
|
||||||
'message' => true,
|
'message' => true,
|
||||||
|
@ -70,7 +73,7 @@ class PushoverHandler extends SocketHandler
|
||||||
* @see https://pushover.net/api#sounds
|
* @see https://pushover.net/api#sounds
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
private array $sounds = [
|
private $sounds = [
|
||||||
'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
|
'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming',
|
||||||
'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb',
|
'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb',
|
||||||
'persistent', 'echo', 'updown', 'none',
|
'persistent', 'echo', 'updown', 'none',
|
||||||
|
@ -82,30 +85,28 @@ class PushoverHandler extends SocketHandler
|
||||||
* @param string|null $title Title sent to the Pushover API
|
* @param string|null $title Title sent to the Pushover API
|
||||||
* @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
|
* @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
|
||||||
* the pushover.net app owner. OpenSSL is required for this option.
|
* the pushover.net app owner. OpenSSL is required for this option.
|
||||||
|
* @param string|int $highPriorityLevel The minimum logging level at which this handler will start
|
||||||
|
* sending "high priority" requests to the Pushover API
|
||||||
|
* @param string|int $emergencyLevel The minimum logging level at which this handler will start
|
||||||
|
* sending "emergency" requests to the Pushover API
|
||||||
* @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will
|
* @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will
|
||||||
* send the same notification to the user.
|
* send the same notification to the user.
|
||||||
* @param int $expire The expire parameter specifies how many seconds your notification will continue
|
* @param int $expire The expire parameter specifies how many seconds your notification will continue
|
||||||
* to be retried for (every retry seconds).
|
* to be retried for (every retry seconds).
|
||||||
*
|
*
|
||||||
* @param int|string|Level|LogLevel::* $highPriorityLevel The minimum logging level at which this handler will start
|
|
||||||
* sending "high priority" requests to the Pushover API
|
|
||||||
* @param int|string|Level|LogLevel::* $emergencyLevel The minimum logging level at which this handler will start
|
|
||||||
* sending "emergency" requests to the Pushover API
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @phpstan-param string|array<int|string> $users
|
* @phpstan-param string|array<int|string> $users
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $highPriorityLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $highPriorityLevel
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $emergencyLevel
|
* @phpstan-param Level|LevelName|LogLevel::* $emergencyLevel
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $token,
|
string $token,
|
||||||
$users,
|
$users,
|
||||||
?string $title = null,
|
?string $title = null,
|
||||||
int|string|Level $level = Level::Critical,
|
$level = Logger::CRITICAL,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $useSSL = true,
|
bool $useSSL = true,
|
||||||
int|string|Level $highPriorityLevel = Level::Critical,
|
$highPriorityLevel = Logger::CRITICAL,
|
||||||
int|string|Level $emergencyLevel = Level::Emergency,
|
$emergencyLevel = Logger::EMERGENCY,
|
||||||
int $retry = 30,
|
int $retry = 30,
|
||||||
int $expire = 25200,
|
int $expire = 25200,
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
|
@ -128,29 +129,32 @@ class PushoverHandler extends SocketHandler
|
||||||
|
|
||||||
$this->token = $token;
|
$this->token = $token;
|
||||||
$this->users = (array) $users;
|
$this->users = (array) $users;
|
||||||
$this->title = $title ?? (string) gethostname();
|
$this->title = $title ?: (string) gethostname();
|
||||||
$this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
|
$this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel);
|
||||||
$this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
|
$this->emergencyLevel = Logger::toMonologLevel($emergencyLevel);
|
||||||
$this->retry = $retry;
|
$this->retry = $retry;
|
||||||
$this->expire = $expire;
|
$this->expire = $expire;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
$content = $this->buildContent($record);
|
$content = $this->buildContent($record);
|
||||||
|
|
||||||
return $this->buildHeader($content) . $content;
|
return $this->buildHeader($content) . $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildContent(LogRecord $record): string
|
/**
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
|
*/
|
||||||
|
private function buildContent(array $record): string
|
||||||
{
|
{
|
||||||
// Pushover has a limit of 512 characters on title and message combined.
|
// Pushover has a limit of 512 characters on title and message combined.
|
||||||
$maxMessageLength = 512 - strlen($this->title);
|
$maxMessageLength = 512 - strlen($this->title);
|
||||||
|
|
||||||
$message = ($this->useFormattedMessage) ? $record->formatted : $record->message;
|
$message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message'];
|
||||||
$message = Utils::substr($message, 0, $maxMessageLength);
|
$message = Utils::substr($message, 0, $maxMessageLength);
|
||||||
|
|
||||||
$timestamp = $record->datetime->getTimestamp();
|
$timestamp = $record['datetime']->getTimestamp();
|
||||||
|
|
||||||
$dataArray = [
|
$dataArray = [
|
||||||
'token' => $this->token,
|
'token' => $this->token,
|
||||||
|
@ -160,23 +164,23 @@ class PushoverHandler extends SocketHandler
|
||||||
'timestamp' => $timestamp,
|
'timestamp' => $timestamp,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($record->level->value >= $this->emergencyLevel->value) {
|
if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) {
|
||||||
$dataArray['priority'] = 2;
|
$dataArray['priority'] = 2;
|
||||||
$dataArray['retry'] = $this->retry;
|
$dataArray['retry'] = $this->retry;
|
||||||
$dataArray['expire'] = $this->expire;
|
$dataArray['expire'] = $this->expire;
|
||||||
} elseif ($record->level->value >= $this->highPriorityLevel->value) {
|
} elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) {
|
||||||
$dataArray['priority'] = 1;
|
$dataArray['priority'] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First determine the available parameters
|
// First determine the available parameters
|
||||||
$context = array_intersect_key($record->context, $this->parameterNames);
|
$context = array_intersect_key($record['context'], $this->parameterNames);
|
||||||
$extra = array_intersect_key($record->extra, $this->parameterNames);
|
$extra = array_intersect_key($record['extra'], $this->parameterNames);
|
||||||
|
|
||||||
// Least important info should be merged with subsequent info
|
// Least important info should be merged with subsequent info
|
||||||
$dataArray = array_merge($extra, $context, $dataArray);
|
$dataArray = array_merge($extra, $context, $dataArray);
|
||||||
|
|
||||||
// Only pass sounds that are supported by the API
|
// Only pass sounds that are supported by the API
|
||||||
if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds, true)) {
|
if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) {
|
||||||
unset($dataArray['sound']);
|
unset($dataArray['sound']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +198,7 @@ class PushoverHandler extends SocketHandler
|
||||||
return $header;
|
return $header;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
foreach ($this->users as $user) {
|
foreach ($this->users as $user) {
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
@ -207,25 +211,25 @@ class PushoverHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|string|Level|LogLevel::* $level
|
* @param int|string $value
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
|
* @phpstan-param Level|LevelName|LogLevel::* $value
|
||||||
*/
|
*/
|
||||||
public function setHighPriorityLevel(int|string|Level $level): self
|
public function setHighPriorityLevel($value): self
|
||||||
{
|
{
|
||||||
$this->highPriorityLevel = Logger::toMonologLevel($level);
|
$this->highPriorityLevel = Logger::toMonologLevel($value);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|string|Level|LogLevel::* $level
|
* @param int|string $value
|
||||||
*
|
*
|
||||||
* @phpstan-param value-of<Level::VALUES>|value-of<Level::NAMES>|Level|LogLevel::* $level
|
* @phpstan-param Level|LevelName|LogLevel::* $value
|
||||||
*/
|
*/
|
||||||
public function setEmergencyLevel(int|string|Level $level): self
|
public function setEmergencyLevel($value): self
|
||||||
{
|
{
|
||||||
$this->emergencyLevel = Logger::toMonologLevel($level);
|
$this->emergencyLevel = Logger::toMonologLevel($value);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -233,9 +237,9 @@ class PushoverHandler extends SocketHandler
|
||||||
/**
|
/**
|
||||||
* Use the formatted message?
|
* Use the formatted message?
|
||||||
*/
|
*/
|
||||||
public function useFormattedMessage(bool $useFormattedMessage): self
|
public function useFormattedMessage(bool $value): self
|
||||||
{
|
{
|
||||||
$this->useFormattedMessage = $useFormattedMessage;
|
$this->useFormattedMessage = $value;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,7 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
use Predis\Client as Predis;
|
|
||||||
use Redis;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs to a Redis key using rpush
|
* Logs to a Redis key using rpush
|
||||||
|
@ -28,21 +25,29 @@ use Redis;
|
||||||
* $log->pushHandler($redis);
|
* $log->pushHandler($redis);
|
||||||
*
|
*
|
||||||
* @author Thomas Tourlourat <thomas@tourlourat.com>
|
* @author Thomas Tourlourat <thomas@tourlourat.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class RedisHandler extends AbstractProcessingHandler
|
class RedisHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/** @var Predis<Predis>|Redis */
|
/** @var \Predis\Client<\Predis\Client>|\Redis */
|
||||||
private Predis|Redis $redisClient;
|
private $redisClient;
|
||||||
private string $redisKey;
|
/** @var string */
|
||||||
protected int $capSize;
|
private $redisKey;
|
||||||
|
/** @var int */
|
||||||
|
protected $capSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Predis<Predis>|Redis $redis The redis instance
|
* @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance
|
||||||
* @param string $key The key name to push records to
|
* @param string $key The key name to push records to
|
||||||
* @param int $capSize Number of entries to limit list size to, 0 = unlimited
|
* @param int $capSize Number of entries to limit list size to, 0 = unlimited
|
||||||
*/
|
*/
|
||||||
public function __construct(Predis|Redis $redis, string $key, int|string|Level $level = Level::Debug, bool $bubble = true, int $capSize = 0)
|
public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true, int $capSize = 0)
|
||||||
{
|
{
|
||||||
|
if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) {
|
||||||
|
throw new \InvalidArgumentException('Predis\Client or Redis instance required');
|
||||||
|
}
|
||||||
|
|
||||||
$this->redisClient = $redis;
|
$this->redisClient = $redis;
|
||||||
$this->redisKey = $key;
|
$this->redisKey = $key;
|
||||||
$this->capSize = $capSize;
|
$this->capSize = $capSize;
|
||||||
|
@ -51,41 +56,43 @@ class RedisHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if ($this->capSize > 0) {
|
if ($this->capSize) {
|
||||||
$this->writeCapped($record);
|
$this->writeCapped($record);
|
||||||
} else {
|
} else {
|
||||||
$this->redisClient->rpush($this->redisKey, $record->formatted);
|
$this->redisClient->rpush($this->redisKey, $record["formatted"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write and cap the collection
|
* Write and cap the collection
|
||||||
* Writes the record to the redis list and caps its
|
* Writes the record to the redis list and caps its
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
protected function writeCapped(LogRecord $record): void
|
protected function writeCapped(array $record): void
|
||||||
{
|
{
|
||||||
if ($this->redisClient instanceof Redis) {
|
if ($this->redisClient instanceof \Redis) {
|
||||||
$mode = defined('Redis::MULTI') ? Redis::MULTI : 1;
|
$mode = defined('\Redis::MULTI') ? \Redis::MULTI : 1;
|
||||||
$this->redisClient->multi($mode)
|
$this->redisClient->multi($mode)
|
||||||
->rPush($this->redisKey, $record->formatted)
|
->rpush($this->redisKey, $record["formatted"])
|
||||||
->ltrim($this->redisKey, -$this->capSize, -1)
|
->ltrim($this->redisKey, -$this->capSize, -1)
|
||||||
->exec();
|
->exec();
|
||||||
} else {
|
} else {
|
||||||
$redisKey = $this->redisKey;
|
$redisKey = $this->redisKey;
|
||||||
$capSize = $this->capSize;
|
$capSize = $this->capSize;
|
||||||
$this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) {
|
$this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) {
|
||||||
$tx->rpush($redisKey, $record->formatted);
|
$tx->rpush($redisKey, $record["formatted"]);
|
||||||
$tx->ltrim($redisKey, -$capSize, -1);
|
$tx->ltrim($redisKey, -$capSize, -1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,10 +13,7 @@ namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\LineFormatter;
|
use Monolog\Formatter\LineFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
use Predis\Client as Predis;
|
|
||||||
use Redis;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the message to a Redis Pub/Sub channel using PUBLISH
|
* Sends the message to a Redis Pub/Sub channel using PUBLISH
|
||||||
|
@ -24,23 +21,28 @@ use Redis;
|
||||||
* usage example:
|
* usage example:
|
||||||
*
|
*
|
||||||
* $log = new Logger('application');
|
* $log = new Logger('application');
|
||||||
* $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Level::Warning);
|
* $redis = new RedisPubSubHandler(new Predis\Client("tcp://localhost:6379"), "logs", Logger::WARNING);
|
||||||
* $log->pushHandler($redis);
|
* $log->pushHandler($redis);
|
||||||
*
|
*
|
||||||
* @author Gaëtan Faugère <gaetan@fauge.re>
|
* @author Gaëtan Faugère <gaetan@fauge.re>
|
||||||
*/
|
*/
|
||||||
class RedisPubSubHandler extends AbstractProcessingHandler
|
class RedisPubSubHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/** @var Predis<Predis>|Redis */
|
/** @var \Predis\Client<\Predis\Client>|\Redis */
|
||||||
private Predis|Redis $redisClient;
|
private $redisClient;
|
||||||
private string $channelKey;
|
/** @var string */
|
||||||
|
private $channelKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Predis<Predis>|Redis $redis The redis instance
|
* @param \Predis\Client<\Predis\Client>|\Redis $redis The redis instance
|
||||||
* @param string $key The channel key to publish records to
|
* @param string $key The channel key to publish records to
|
||||||
*/
|
*/
|
||||||
public function __construct(Predis|Redis $redis, string $key, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct($redis, string $key, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
|
if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) {
|
||||||
|
throw new \InvalidArgumentException('Predis\Client or Redis instance required');
|
||||||
|
}
|
||||||
|
|
||||||
$this->redisClient = $redis;
|
$this->redisClient = $redis;
|
||||||
$this->channelKey = $key;
|
$this->channelKey = $key;
|
||||||
|
|
||||||
|
@ -48,15 +50,15 @@ class RedisPubSubHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->redisClient->publish($this->channelKey, $record->formatted);
|
$this->redisClient->publish($this->channelKey, $record["formatted"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function getDefaultFormatter(): FormatterInterface
|
protected function getDefaultFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
|
||||||
use Rollbar\RollbarLogger;
|
use Rollbar\RollbarLogger;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Monolog\LogRecord;
|
use Monolog\Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends errors to Rollbar
|
* Sends errors to Rollbar
|
||||||
|
@ -34,19 +33,37 @@ use Monolog\LogRecord;
|
||||||
*/
|
*/
|
||||||
class RollbarHandler extends AbstractProcessingHandler
|
class RollbarHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
protected RollbarLogger $rollbarLogger;
|
/**
|
||||||
|
* @var RollbarLogger
|
||||||
|
*/
|
||||||
|
protected $rollbarLogger;
|
||||||
|
|
||||||
|
/** @var string[] */
|
||||||
|
protected $levelMap = [
|
||||||
|
Logger::DEBUG => 'debug',
|
||||||
|
Logger::INFO => 'info',
|
||||||
|
Logger::NOTICE => 'info',
|
||||||
|
Logger::WARNING => 'warning',
|
||||||
|
Logger::ERROR => 'error',
|
||||||
|
Logger::CRITICAL => 'critical',
|
||||||
|
Logger::ALERT => 'critical',
|
||||||
|
Logger::EMERGENCY => 'critical',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records whether any log records have been added since the last flush of the rollbar notifier
|
* Records whether any log records have been added since the last flush of the rollbar notifier
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private bool $hasRecords = false;
|
private $hasRecords = false;
|
||||||
|
|
||||||
protected bool $initialized = false;
|
/** @var bool */
|
||||||
|
protected $initialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token
|
* @param RollbarLogger $rollbarLogger RollbarLogger object constructed with valid token
|
||||||
*/
|
*/
|
||||||
public function __construct(RollbarLogger $rollbarLogger, int|string|Level $level = Level::Error, bool $bubble = true)
|
public function __construct(RollbarLogger $rollbarLogger, $level = Logger::ERROR, bool $bubble = true)
|
||||||
{
|
{
|
||||||
$this->rollbarLogger = $rollbarLogger;
|
$this->rollbarLogger = $rollbarLogger;
|
||||||
|
|
||||||
|
@ -54,41 +71,22 @@ class RollbarHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates Monolog log levels to Rollbar levels.
|
* {@inheritDoc}
|
||||||
*
|
|
||||||
* @return 'debug'|'info'|'warning'|'error'|'critical'
|
|
||||||
*/
|
*/
|
||||||
protected function toRollbarLevel(Level $level): string
|
protected function write(array $record): void
|
||||||
{
|
|
||||||
return match ($level) {
|
|
||||||
Level::Debug => 'debug',
|
|
||||||
Level::Info => 'info',
|
|
||||||
Level::Notice => 'info',
|
|
||||||
Level::Warning => 'warning',
|
|
||||||
Level::Error => 'error',
|
|
||||||
Level::Critical => 'critical',
|
|
||||||
Level::Alert => 'critical',
|
|
||||||
Level::Emergency => 'critical',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
protected function write(LogRecord $record): void
|
|
||||||
{
|
{
|
||||||
if (!$this->initialized) {
|
if (!$this->initialized) {
|
||||||
// __destructor() doesn't get called on Fatal errors
|
// __destructor() doesn't get called on Fatal errors
|
||||||
register_shutdown_function([$this, 'close']);
|
register_shutdown_function(array($this, 'close'));
|
||||||
$this->initialized = true;
|
$this->initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$context = $record->context;
|
$context = $record['context'];
|
||||||
$context = array_merge($context, $record->extra, [
|
$context = array_merge($context, $record['extra'], [
|
||||||
'level' => $this->toRollbarLevel($record->level),
|
'level' => $this->levelMap[$record['level']],
|
||||||
'monolog_level' => $record->level->getName(),
|
'monolog_level' => $record['level_name'],
|
||||||
'channel' => $record->channel,
|
'channel' => $record['channel'],
|
||||||
'datetime' => $record->datetime->format('U'),
|
'datetime' => $record['datetime']->format('U'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isset($context['exception']) && $context['exception'] instanceof Throwable) {
|
if (isset($context['exception']) && $context['exception'] instanceof Throwable) {
|
||||||
|
@ -96,7 +94,7 @@ class RollbarHandler extends AbstractProcessingHandler
|
||||||
unset($context['exception']);
|
unset($context['exception']);
|
||||||
$toLog = $exception;
|
$toLog = $exception;
|
||||||
} else {
|
} else {
|
||||||
$toLog = $record->message;
|
$toLog = $record['message'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// @phpstan-ignore-next-line
|
// @phpstan-ignore-next-line
|
||||||
|
@ -114,7 +112,7 @@ class RollbarHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
@ -122,9 +120,9 @@ class RollbarHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
$this->flush();
|
$this->flush();
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,8 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores logs to files that are rotated every day and a limited number of files are kept.
|
* Stores logs to files that are rotated every day and a limited number of files are kept.
|
||||||
|
@ -31,19 +30,26 @@ class RotatingFileHandler extends StreamHandler
|
||||||
public const FILE_PER_MONTH = 'Y-m';
|
public const FILE_PER_MONTH = 'Y-m';
|
||||||
public const FILE_PER_YEAR = 'Y';
|
public const FILE_PER_YEAR = 'Y';
|
||||||
|
|
||||||
protected string $filename;
|
/** @var string */
|
||||||
protected int $maxFiles;
|
protected $filename;
|
||||||
protected bool|null $mustRotate = null;
|
/** @var int */
|
||||||
protected \DateTimeImmutable $nextRotation;
|
protected $maxFiles;
|
||||||
protected string $filenameFormat;
|
/** @var bool */
|
||||||
protected string $dateFormat;
|
protected $mustRotate;
|
||||||
|
/** @var \DateTimeImmutable */
|
||||||
|
protected $nextRotation;
|
||||||
|
/** @var string */
|
||||||
|
protected $filenameFormat;
|
||||||
|
/** @var string */
|
||||||
|
protected $dateFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param string $filename
|
||||||
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
|
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
|
||||||
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
|
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
|
||||||
* @param bool $useLocking Try to lock log file before doing any writes
|
* @param bool $useLocking Try to lock log file before doing any writes
|
||||||
*/
|
*/
|
||||||
public function __construct(string $filename, int $maxFiles = 0, int|string|Level $level = Level::Debug, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
|
public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
|
||||||
{
|
{
|
||||||
$this->filename = Utils::canonicalizePath($filename);
|
$this->filename = Utils::canonicalizePath($filename);
|
||||||
$this->maxFiles = $maxFiles;
|
$this->maxFiles = $maxFiles;
|
||||||
|
@ -55,7 +61,7 @@ class RotatingFileHandler extends StreamHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
|
@ -67,9 +73,9 @@ class RotatingFileHandler extends StreamHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function reset(): void
|
public function reset()
|
||||||
{
|
{
|
||||||
parent::reset();
|
parent::reset();
|
||||||
|
|
||||||
|
@ -80,7 +86,7 @@ class RotatingFileHandler extends StreamHandler
|
||||||
|
|
||||||
public function setFilenameFormat(string $filenameFormat, string $dateFormat): self
|
public function setFilenameFormat(string $filenameFormat, string $dateFormat): self
|
||||||
{
|
{
|
||||||
if (0 === preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
|
if (!preg_match('{^[Yy](([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
|
||||||
throw new InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
'Invalid date format - format must be one of '.
|
'Invalid date format - format must be one of '.
|
||||||
'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '.
|
'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '.
|
||||||
|
@ -102,16 +108,16 @@ class RotatingFileHandler extends StreamHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
// on the first record written, if the log is new, we should rotate (once per day)
|
// on the first record written, if the log is new, we should rotate (once per day)
|
||||||
if (null === $this->mustRotate) {
|
if (null === $this->mustRotate) {
|
||||||
$this->mustRotate = null === $this->url || !file_exists($this->url);
|
$this->mustRotate = null === $this->url || !file_exists($this->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->nextRotation <= $record->datetime) {
|
if ($this->nextRotation <= $record['datetime']) {
|
||||||
$this->mustRotate = true;
|
$this->mustRotate = true;
|
||||||
$this->close();
|
$this->close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sampling handler
|
* Sampling handler
|
||||||
|
@ -28,42 +26,52 @@ use Monolog\LogRecord;
|
||||||
*
|
*
|
||||||
* @author Bryan Davis <bd808@wikimedia.org>
|
* @author Bryan Davis <bd808@wikimedia.org>
|
||||||
* @author Kunal Mehta <legoktm@gmail.com>
|
* @author Kunal Mehta <legoktm@gmail.com>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type Level from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
class SamplingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface
|
||||||
{
|
{
|
||||||
use ProcessableHandlerTrait;
|
use ProcessableHandlerTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler or factory Closure($record, $this)
|
* @var HandlerInterface|callable
|
||||||
*
|
* @phpstan-var HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface
|
||||||
* @phpstan-var (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface
|
|
||||||
*/
|
*/
|
||||||
protected Closure|HandlerInterface $handler;
|
protected $handler;
|
||||||
|
|
||||||
protected int $factor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-param (Closure(LogRecord|null, HandlerInterface): HandlerInterface)|HandlerInterface $handler
|
* @var int $factor
|
||||||
|
*/
|
||||||
|
protected $factor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @psalm-param HandlerInterface|callable(Record|array{level: Level}|null, HandlerInterface): HandlerInterface $handler
|
||||||
*
|
*
|
||||||
* @param Closure|HandlerInterface $handler Handler or factory Closure($record|null, $samplingHandler).
|
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler).
|
||||||
* @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled)
|
* @param int $factor Sample factor (e.g. 10 means every ~10th record is sampled)
|
||||||
*/
|
*/
|
||||||
public function __construct(Closure|HandlerInterface $handler, int $factor)
|
public function __construct($handler, int $factor)
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->handler = $handler;
|
$this->handler = $handler;
|
||||||
$this->factor = $factor;
|
$this->factor = $factor;
|
||||||
|
|
||||||
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) {
|
||||||
|
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isHandling(LogRecord $record): bool
|
public function isHandling(array $record): bool
|
||||||
{
|
{
|
||||||
return $this->getHandler($record)->isHandling($record);
|
return $this->getHandler($record)->isHandling($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(LogRecord $record): bool
|
public function handle(array $record): bool
|
||||||
{
|
{
|
||||||
if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
|
if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) {
|
||||||
if (\count($this->processors) > 0) {
|
if ($this->processors) {
|
||||||
|
/** @var Record $record */
|
||||||
$record = $this->processRecord($record);
|
$record = $this->processRecord($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,23 +84,26 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
|
||||||
/**
|
/**
|
||||||
* Return the nested handler
|
* Return the nested handler
|
||||||
*
|
*
|
||||||
* If the handler was provided as a factory, this will trigger the handler's instantiation.
|
* If the handler was provided as a factory callable, this will trigger the handler's instantiation.
|
||||||
|
*
|
||||||
|
* @phpstan-param Record|array{level: Level}|null $record
|
||||||
|
*
|
||||||
|
* @return HandlerInterface
|
||||||
*/
|
*/
|
||||||
public function getHandler(LogRecord $record = null): HandlerInterface
|
public function getHandler(array $record = null)
|
||||||
{
|
{
|
||||||
if (!$this->handler instanceof HandlerInterface) {
|
if (!$this->handler instanceof HandlerInterface) {
|
||||||
$handler = ($this->handler)($record, $this);
|
$this->handler = ($this->handler)($record, $this);
|
||||||
if (!$handler instanceof HandlerInterface) {
|
if (!$this->handler instanceof HandlerInterface) {
|
||||||
throw new \RuntimeException("The factory Closure should return a HandlerInterface");
|
throw new \RuntimeException("The factory callable should return a HandlerInterface");
|
||||||
}
|
}
|
||||||
$this->handler = $handler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->handler;
|
return $this->handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
public function setFormatter(FormatterInterface $formatter): HandlerInterface
|
||||||
{
|
{
|
||||||
|
@ -107,7 +118,7 @@ class SamplingHandler extends AbstractHandler implements ProcessableHandlerInter
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function getFormatter(): FormatterInterface
|
public function getFormatter(): FormatterInterface
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html
|
* SendGridrHandler uses the SendGrid API v2 function to send Log emails, more information in https://sendgrid.com/docs/API_Reference/Web_API/mail.html
|
||||||
|
@ -22,29 +22,33 @@ class SendGridHandler extends MailHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The SendGrid API User
|
* The SendGrid API User
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected string $apiUser;
|
protected $apiUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SendGrid API Key
|
* The SendGrid API Key
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected string $apiKey;
|
protected $apiKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The email addresses to which the message will be sent
|
* The email addresses to which the message will be sent
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected string $from;
|
protected $from;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The email addresses to which the message will be sent
|
* The email addresses to which the message will be sent
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
protected array $to;
|
protected $to;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The subject of the email
|
* The subject of the email
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected string $subject;
|
protected $subject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $apiUser The SendGrid API User
|
* @param string $apiUser The SendGrid API User
|
||||||
|
@ -53,7 +57,7 @@ class SendGridHandler extends MailHandler
|
||||||
* @param string|string[] $to The recipients of the email
|
* @param string|string[] $to The recipients of the email
|
||||||
* @param string $subject The subject of the mail
|
* @param string $subject The subject of the mail
|
||||||
*/
|
*/
|
||||||
public function __construct(string $apiUser, string $apiKey, string $from, string|array $to, string $subject, int|string|Level $level = Level::Error, bool $bubble = true)
|
public function __construct(string $apiUser, string $apiKey, string $from, $to, string $subject, $level = Logger::ERROR, bool $bubble = true)
|
||||||
{
|
{
|
||||||
if (!extension_loaded('curl')) {
|
if (!extension_loaded('curl')) {
|
||||||
throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler');
|
throw new MissingExtensionException('The curl extension is needed to use the SendGridHandler');
|
||||||
|
@ -68,7 +72,7 @@ class SendGridHandler extends MailHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function send(string $content, array $records): void
|
protected function send(string $content, array $records): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
|
|
||||||
namespace Monolog\Handler\Slack;
|
namespace Monolog\Handler\Slack;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\Formatter\NormalizerFormatter;
|
use Monolog\Formatter\NormalizerFormatter;
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slack record utility helping to log to Slack webhooks or API.
|
* Slack record utility helping to log to Slack webhooks or API.
|
||||||
|
@ -24,6 +23,9 @@ use Monolog\LogRecord;
|
||||||
* @author Haralan Dobrev <hkdobrev@gmail.com>
|
* @author Haralan Dobrev <hkdobrev@gmail.com>
|
||||||
* @see https://api.slack.com/incoming-webhooks
|
* @see https://api.slack.com/incoming-webhooks
|
||||||
* @see https://api.slack.com/docs/message-attachments
|
* @see https://api.slack.com/docs/message-attachments
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from \Monolog\Handler\AbstractProcessingHandler
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
*/
|
*/
|
||||||
class SlackRecord
|
class SlackRecord
|
||||||
{
|
{
|
||||||
|
@ -37,43 +39,55 @@ class SlackRecord
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slack channel (encoded ID or name)
|
* Slack channel (encoded ID or name)
|
||||||
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
private string|null $channel;
|
private $channel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of a bot
|
* Name of a bot
|
||||||
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
private string|null $username;
|
private $username;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User icon e.g. 'ghost', 'http://example.com/user.png'
|
* User icon e.g. 'ghost', 'http://example.com/user.png'
|
||||||
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
private string|null $userIcon;
|
private $userIcon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the message should be added to Slack as attachment (plain text otherwise)
|
* Whether the message should be added to Slack as attachment (plain text otherwise)
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private bool $useAttachment;
|
private $useAttachment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the the context/extra messages added to Slack as attachments are in a short style
|
* Whether the the context/extra messages added to Slack as attachments are in a short style
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private bool $useShortAttachment;
|
private $useShortAttachment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the attachment should include context and extra data
|
* Whether the attachment should include context and extra data
|
||||||
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private bool $includeContextAndExtra;
|
private $includeContextAndExtra;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
|
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
|
||||||
* @var string[]
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
private array $excludeFields;
|
private $excludeFields;
|
||||||
|
|
||||||
private FormatterInterface|null $formatter;
|
/**
|
||||||
|
* @var ?FormatterInterface
|
||||||
|
*/
|
||||||
|
private $formatter;
|
||||||
|
|
||||||
private NormalizerFormatter $normalizerFormatter;
|
/**
|
||||||
|
* @var NormalizerFormatter
|
||||||
|
*/
|
||||||
|
private $normalizerFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $excludeFields
|
* @param string[] $excludeFields
|
||||||
|
@ -85,7 +99,7 @@ class SlackRecord
|
||||||
?string $userIcon = null,
|
?string $userIcon = null,
|
||||||
bool $useShortAttachment = false,
|
bool $useShortAttachment = false,
|
||||||
bool $includeContextAndExtra = false,
|
bool $includeContextAndExtra = false,
|
||||||
array $excludeFields = [],
|
array $excludeFields = array(),
|
||||||
FormatterInterface $formatter = null
|
FormatterInterface $formatter = null
|
||||||
) {
|
) {
|
||||||
$this
|
$this
|
||||||
|
@ -107,76 +121,77 @@ class SlackRecord
|
||||||
* Returns required data in format that Slack
|
* Returns required data in format that Slack
|
||||||
* is expecting.
|
* is expecting.
|
||||||
*
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
* @phpstan-return mixed[]
|
* @phpstan-return mixed[]
|
||||||
*/
|
*/
|
||||||
public function getSlackData(LogRecord $record): array
|
public function getSlackData(array $record): array
|
||||||
{
|
{
|
||||||
$dataArray = [];
|
$dataArray = array();
|
||||||
|
$record = $this->removeExcludedFields($record);
|
||||||
|
|
||||||
if ($this->username !== null) {
|
if ($this->username) {
|
||||||
$dataArray['username'] = $this->username;
|
$dataArray['username'] = $this->username;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->channel !== null) {
|
if ($this->channel) {
|
||||||
$dataArray['channel'] = $this->channel;
|
$dataArray['channel'] = $this->channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->formatter !== null && !$this->useAttachment) {
|
if ($this->formatter && !$this->useAttachment) {
|
||||||
|
/** @phpstan-ignore-next-line */
|
||||||
$message = $this->formatter->format($record);
|
$message = $this->formatter->format($record);
|
||||||
} else {
|
} else {
|
||||||
$message = $record->message;
|
$message = $record['message'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$recordData = $this->removeExcludedFields($record);
|
|
||||||
|
|
||||||
if ($this->useAttachment) {
|
if ($this->useAttachment) {
|
||||||
$attachment = [
|
$attachment = array(
|
||||||
'fallback' => $message,
|
'fallback' => $message,
|
||||||
'text' => $message,
|
'text' => $message,
|
||||||
'color' => $this->getAttachmentColor($record->level),
|
'color' => $this->getAttachmentColor($record['level']),
|
||||||
'fields' => [],
|
'fields' => array(),
|
||||||
'mrkdwn_in' => ['fields'],
|
'mrkdwn_in' => array('fields'),
|
||||||
'ts' => $recordData['datetime']->getTimestamp(),
|
'ts' => $record['datetime']->getTimestamp(),
|
||||||
'footer' => $this->username,
|
'footer' => $this->username,
|
||||||
'footer_icon' => $this->userIcon,
|
'footer_icon' => $this->userIcon,
|
||||||
];
|
);
|
||||||
|
|
||||||
if ($this->useShortAttachment) {
|
if ($this->useShortAttachment) {
|
||||||
$attachment['title'] = $recordData['level_name'];
|
$attachment['title'] = $record['level_name'];
|
||||||
} else {
|
} else {
|
||||||
$attachment['title'] = 'Message';
|
$attachment['title'] = 'Message';
|
||||||
$attachment['fields'][] = $this->generateAttachmentField('Level', $recordData['level_name']);
|
$attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->includeContextAndExtra) {
|
if ($this->includeContextAndExtra) {
|
||||||
foreach (['extra', 'context'] as $key) {
|
foreach (array('extra', 'context') as $key) {
|
||||||
if (!isset($recordData[$key]) || \count($recordData[$key]) === 0) {
|
if (empty($record[$key])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->useShortAttachment) {
|
if ($this->useShortAttachment) {
|
||||||
$attachment['fields'][] = $this->generateAttachmentField(
|
$attachment['fields'][] = $this->generateAttachmentField(
|
||||||
$key,
|
(string) $key,
|
||||||
$recordData[$key]
|
$record[$key]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Add all extra fields as individual fields in attachment
|
// Add all extra fields as individual fields in attachment
|
||||||
$attachment['fields'] = array_merge(
|
$attachment['fields'] = array_merge(
|
||||||
$attachment['fields'],
|
$attachment['fields'],
|
||||||
$this->generateAttachmentFields($recordData[$key])
|
$this->generateAttachmentFields($record[$key])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataArray['attachments'] = [$attachment];
|
$dataArray['attachments'] = array($attachment);
|
||||||
} else {
|
} else {
|
||||||
$dataArray['text'] = $message;
|
$dataArray['text'] = $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->userIcon !== null) {
|
if ($this->userIcon) {
|
||||||
if (false !== ($iconUrl = filter_var($this->userIcon, FILTER_VALIDATE_URL))) {
|
if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
|
||||||
$dataArray['icon_url'] = $iconUrl;
|
$dataArray['icon_url'] = $this->userIcon;
|
||||||
} else {
|
} else {
|
||||||
$dataArray['icon_emoji'] = ":{$this->userIcon}:";
|
$dataArray['icon_emoji'] = ":{$this->userIcon}:";
|
||||||
}
|
}
|
||||||
|
@ -189,14 +204,18 @@ class SlackRecord
|
||||||
* Returns a Slack message attachment color associated with
|
* Returns a Slack message attachment color associated with
|
||||||
* provided level.
|
* provided level.
|
||||||
*/
|
*/
|
||||||
public function getAttachmentColor(Level $level): string
|
public function getAttachmentColor(int $level): string
|
||||||
{
|
{
|
||||||
return match ($level) {
|
switch (true) {
|
||||||
Level::Error, Level::Critical, Level::Alert, Level::Emergency => static::COLOR_DANGER,
|
case $level >= Logger::ERROR:
|
||||||
Level::Warning => static::COLOR_WARNING,
|
return static::COLOR_DANGER;
|
||||||
Level::Info, Level::Notice => static::COLOR_GOOD,
|
case $level >= Logger::WARNING:
|
||||||
Level::Debug => static::COLOR_DEFAULT
|
return static::COLOR_WARNING;
|
||||||
};
|
case $level >= Logger::INFO:
|
||||||
|
return static::COLOR_GOOD;
|
||||||
|
default:
|
||||||
|
return static::COLOR_DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,13 +225,13 @@ class SlackRecord
|
||||||
*/
|
*/
|
||||||
public function stringify(array $fields): string
|
public function stringify(array $fields): string
|
||||||
{
|
{
|
||||||
/** @var array<mixed> $normalized */
|
/** @var Record $fields */
|
||||||
$normalized = $this->normalizerFormatter->normalizeValue($fields);
|
$normalized = $this->normalizerFormatter->format($fields);
|
||||||
|
|
||||||
$hasSecondDimension = \count(array_filter($normalized, 'is_array')) > 0;
|
$hasSecondDimension = count(array_filter($normalized, 'is_array'));
|
||||||
$hasOnlyNonNumericKeys = \count(array_filter(array_keys($normalized), 'is_numeric')) === 0;
|
$hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
|
||||||
|
|
||||||
return $hasSecondDimension || $hasOnlyNonNumericKeys
|
return $hasSecondDimension || $hasNonNumericKeys
|
||||||
? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS)
|
? Utils::jsonEncode($normalized, JSON_PRETTY_PRINT|Utils::DEFAULT_JSON_FLAGS)
|
||||||
: Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS);
|
: Utils::jsonEncode($normalized, Utils::DEFAULT_JSON_FLAGS);
|
||||||
}
|
}
|
||||||
|
@ -311,11 +330,11 @@ class SlackRecord
|
||||||
? sprintf('```%s```', substr($this->stringify($value), 0, 1990))
|
? sprintf('```%s```', substr($this->stringify($value), 0, 1990))
|
||||||
: $value;
|
: $value;
|
||||||
|
|
||||||
return [
|
return array(
|
||||||
'title' => ucfirst($title),
|
'title' => ucfirst($title),
|
||||||
'value' => $value,
|
'value' => $value,
|
||||||
'short' => false,
|
'short' => false,
|
||||||
];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -327,10 +346,10 @@ class SlackRecord
|
||||||
*/
|
*/
|
||||||
private function generateAttachmentFields(array $data): array
|
private function generateAttachmentFields(array $data): array
|
||||||
{
|
{
|
||||||
/** @var array<mixed> $normalized */
|
/** @var Record $data */
|
||||||
$normalized = $this->normalizerFormatter->normalizeValue($data);
|
$normalized = $this->normalizerFormatter->format($data);
|
||||||
|
|
||||||
$fields = [];
|
$fields = array();
|
||||||
foreach ($normalized as $key => $value) {
|
foreach ($normalized as $key => $value) {
|
||||||
$fields[] = $this->generateAttachmentField((string) $key, $value);
|
$fields[] = $this->generateAttachmentField((string) $key, $value);
|
||||||
}
|
}
|
||||||
|
@ -341,14 +360,15 @@ class SlackRecord
|
||||||
/**
|
/**
|
||||||
* Get a copy of record with fields excluded according to $this->excludeFields
|
* Get a copy of record with fields excluded according to $this->excludeFields
|
||||||
*
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
|
*
|
||||||
* @return mixed[]
|
* @return mixed[]
|
||||||
*/
|
*/
|
||||||
private function removeExcludedFields(LogRecord $record): array
|
private function removeExcludedFields(array $record): array
|
||||||
{
|
{
|
||||||
$recordData = $record->toArray();
|
|
||||||
foreach ($this->excludeFields as $field) {
|
foreach ($this->excludeFields as $field) {
|
||||||
$keys = explode('.', $field);
|
$keys = explode('.', $field);
|
||||||
$node = &$recordData;
|
$node = &$record;
|
||||||
$lastKey = end($keys);
|
$lastKey = end($keys);
|
||||||
foreach ($keys as $key) {
|
foreach ($keys as $key) {
|
||||||
if (!isset($node[$key])) {
|
if (!isset($node[$key])) {
|
||||||
|
@ -362,6 +382,6 @@ class SlackRecord
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $recordData;
|
return $record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,28 +12,31 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\Handler\Slack\SlackRecord;
|
use Monolog\Handler\Slack\SlackRecord;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends notifications through Slack API
|
* Sends notifications through Slack API
|
||||||
*
|
*
|
||||||
* @author Greg Kedzierski <greg@gregkedzierski.com>
|
* @author Greg Kedzierski <greg@gregkedzierski.com>
|
||||||
* @see https://api.slack.com/
|
* @see https://api.slack.com/
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class SlackHandler extends SocketHandler
|
class SlackHandler extends SocketHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Slack API token
|
* Slack API token
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
private string $token;
|
private $token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of the SlackRecord util class preparing data for Slack API.
|
* Instance of the SlackRecord util class preparing data for Slack API.
|
||||||
|
* @var SlackRecord
|
||||||
*/
|
*/
|
||||||
private SlackRecord $slackRecord;
|
private $slackRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $token Slack API token
|
* @param string $token Slack API token
|
||||||
|
@ -52,11 +55,11 @@ class SlackHandler extends SocketHandler
|
||||||
?string $username = null,
|
?string $username = null,
|
||||||
bool $useAttachment = true,
|
bool $useAttachment = true,
|
||||||
?string $iconEmoji = null,
|
?string $iconEmoji = null,
|
||||||
$level = Level::Critical,
|
$level = Logger::CRITICAL,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $useShortAttachment = false,
|
bool $useShortAttachment = false,
|
||||||
bool $includeContextAndExtra = false,
|
bool $includeContextAndExtra = false,
|
||||||
array $excludeFields = [],
|
array $excludeFields = array(),
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
float $timeout = 0.0,
|
float $timeout = 0.0,
|
||||||
float $writingTimeout = 10.0,
|
float $writingTimeout = 10.0,
|
||||||
|
@ -102,9 +105,9 @@ class SlackHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function generateDataStream(LogRecord $record): string
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
$content = $this->buildContent($record);
|
$content = $this->buildContent($record);
|
||||||
|
|
||||||
|
@ -113,8 +116,10 @@ class SlackHandler extends SocketHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the body of API call
|
* Builds the body of API call
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
private function buildContent(LogRecord $record): string
|
private function buildContent(array $record): string
|
||||||
{
|
{
|
||||||
$dataArray = $this->prepareContentData($record);
|
$dataArray = $this->prepareContentData($record);
|
||||||
|
|
||||||
|
@ -122,14 +127,15 @@ class SlackHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
protected function prepareContentData(LogRecord $record): array
|
protected function prepareContentData(array $record): array
|
||||||
{
|
{
|
||||||
$dataArray = $this->slackRecord->getSlackData($record);
|
$dataArray = $this->slackRecord->getSlackData($record);
|
||||||
$dataArray['token'] = $this->token;
|
$dataArray['token'] = $this->token;
|
||||||
|
|
||||||
if (isset($dataArray['attachments']) && is_array($dataArray['attachments']) && \count($dataArray['attachments']) > 0) {
|
if (!empty($dataArray['attachments'])) {
|
||||||
$dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']);
|
$dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,9 +157,9 @@ class SlackHandler extends SocketHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
parent::write($record);
|
parent::write($record);
|
||||||
$this->finalizeWrite();
|
$this->finalizeWrite();
|
||||||
|
|
|
@ -12,10 +12,9 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Formatter\FormatterInterface;
|
use Monolog\Formatter\FormatterInterface;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\Handler\Slack\SlackRecord;
|
use Monolog\Handler\Slack\SlackRecord;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends notifications through Slack Webhooks
|
* Sends notifications through Slack Webhooks
|
||||||
|
@ -27,13 +26,15 @@ class SlackWebhookHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Slack Webhook token
|
* Slack Webhook token
|
||||||
|
* @var string
|
||||||
*/
|
*/
|
||||||
private string $webhookUrl;
|
private $webhookUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of the SlackRecord util class preparing data for Slack API.
|
* Instance of the SlackRecord util class preparing data for Slack API.
|
||||||
|
* @var SlackRecord
|
||||||
*/
|
*/
|
||||||
private SlackRecord $slackRecord;
|
private $slackRecord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $webhookUrl Slack Webhook URL
|
* @param string $webhookUrl Slack Webhook URL
|
||||||
|
@ -53,9 +54,9 @@ class SlackWebhookHandler extends AbstractProcessingHandler
|
||||||
?string $iconEmoji = null,
|
?string $iconEmoji = null,
|
||||||
bool $useShortAttachment = false,
|
bool $useShortAttachment = false,
|
||||||
bool $includeContextAndExtra = false,
|
bool $includeContextAndExtra = false,
|
||||||
$level = Level::Critical,
|
$level = Logger::CRITICAL,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
array $excludeFields = []
|
array $excludeFields = array()
|
||||||
) {
|
) {
|
||||||
if (!extension_loaded('curl')) {
|
if (!extension_loaded('curl')) {
|
||||||
throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler');
|
throw new MissingExtensionException('The curl extension is needed to use the SlackWebhookHandler');
|
||||||
|
@ -87,21 +88,21 @@ class SlackWebhookHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$postData = $this->slackRecord->getSlackData($record);
|
$postData = $this->slackRecord->getSlackData($record);
|
||||||
$postString = Utils::jsonEncode($postData);
|
$postString = Utils::jsonEncode($postData);
|
||||||
|
|
||||||
$ch = curl_init();
|
$ch = curl_init();
|
||||||
$options = [
|
$options = array(
|
||||||
CURLOPT_URL => $this->webhookUrl,
|
CURLOPT_URL => $this->webhookUrl,
|
||||||
CURLOPT_POST => true,
|
CURLOPT_POST => true,
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
CURLOPT_HTTPHEADER => ['Content-type: application/json'],
|
CURLOPT_HTTPHEADER => array('Content-type: application/json'),
|
||||||
CURLOPT_POSTFIELDS => $postString,
|
CURLOPT_POSTFIELDS => $postString,
|
||||||
];
|
);
|
||||||
if (defined('CURLOPT_SAFE_UPLOAD')) {
|
if (defined('CURLOPT_SAFE_UPLOAD')) {
|
||||||
$options[CURLOPT_SAFE_UPLOAD] = true;
|
$options[CURLOPT_SAFE_UPLOAD] = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,29 +11,41 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores to any socket - uses fsockopen() or pfsockopen().
|
* Stores to any socket - uses fsockopen() or pfsockopen().
|
||||||
*
|
*
|
||||||
* @author Pablo de Leon Belloc <pablolb@gmail.com>
|
* @author Pablo de Leon Belloc <pablolb@gmail.com>
|
||||||
* @see http://php.net/manual/en/function.fsockopen.php
|
* @see http://php.net/manual/en/function.fsockopen.php
|
||||||
|
*
|
||||||
|
* @phpstan-import-type Record from \Monolog\Logger
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class SocketHandler extends AbstractProcessingHandler
|
class SocketHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
private string $connectionString;
|
/** @var string */
|
||||||
private float $connectionTimeout;
|
private $connectionString;
|
||||||
|
/** @var float */
|
||||||
|
private $connectionTimeout;
|
||||||
/** @var resource|null */
|
/** @var resource|null */
|
||||||
private $resource;
|
private $resource;
|
||||||
private float $timeout;
|
/** @var float */
|
||||||
private float $writingTimeout;
|
private $timeout;
|
||||||
private int|null $lastSentBytes = null;
|
/** @var float */
|
||||||
private int|null $chunkSize;
|
private $writingTimeout;
|
||||||
private bool $persistent;
|
/** @var ?int */
|
||||||
private int|null $errno = null;
|
private $lastSentBytes = null;
|
||||||
private string|null $errstr = null;
|
/** @var ?int */
|
||||||
private float|null $lastWritingAt = null;
|
private $chunkSize;
|
||||||
|
/** @var bool */
|
||||||
|
private $persistent;
|
||||||
|
/** @var ?int */
|
||||||
|
private $errno = null;
|
||||||
|
/** @var ?string */
|
||||||
|
private $errstr = null;
|
||||||
|
/** @var ?float */
|
||||||
|
private $lastWritingAt = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $connectionString Socket connection string
|
* @param string $connectionString Socket connection string
|
||||||
|
@ -48,7 +60,7 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $connectionString,
|
string $connectionString,
|
||||||
$level = Level::Debug,
|
$level = Logger::DEBUG,
|
||||||
bool $bubble = true,
|
bool $bubble = true,
|
||||||
bool $persistent = false,
|
bool $persistent = false,
|
||||||
float $timeout = 0.0,
|
float $timeout = 0.0,
|
||||||
|
@ -75,12 +87,12 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* Connect (if necessary) and write to the socket
|
* Connect (if necessary) and write to the socket
|
||||||
*
|
*
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @throws \UnexpectedValueException
|
* @throws \UnexpectedValueException
|
||||||
* @throws \RuntimeException
|
* @throws \RuntimeException
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
$this->connectIfNotConnected();
|
$this->connectIfNotConnected();
|
||||||
$data = $this->generateDataStream($record);
|
$data = $this->generateDataStream($record);
|
||||||
|
@ -201,6 +213,8 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current local writing timeout
|
* Get current local writing timeout
|
||||||
|
*
|
||||||
|
* @return float
|
||||||
*/
|
*/
|
||||||
public function getWritingTimeout(): float
|
public function getWritingTimeout(): float
|
||||||
{
|
{
|
||||||
|
@ -250,8 +264,10 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
* Wrapper to allow mocking
|
* Wrapper to allow mocking
|
||||||
*
|
*
|
||||||
* @see http://php.net/manual/en/function.stream-set-timeout.php
|
* @see http://php.net/manual/en/function.stream-set-timeout.php
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function streamSetTimeout(): bool
|
protected function streamSetTimeout()
|
||||||
{
|
{
|
||||||
$seconds = floor($this->timeout);
|
$seconds = floor($this->timeout);
|
||||||
$microseconds = round(($this->timeout - $seconds) * 1e6);
|
$microseconds = round(($this->timeout - $seconds) * 1e6);
|
||||||
|
@ -268,9 +284,9 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
*
|
*
|
||||||
* @see http://php.net/manual/en/function.stream-set-chunk-size.php
|
* @see http://php.net/manual/en/function.stream-set-chunk-size.php
|
||||||
*
|
*
|
||||||
* @return int|false
|
* @return int|bool
|
||||||
*/
|
*/
|
||||||
protected function streamSetChunkSize(): int|bool
|
protected function streamSetChunkSize()
|
||||||
{
|
{
|
||||||
if (!is_resource($this->resource)) {
|
if (!is_resource($this->resource)) {
|
||||||
throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource');
|
throw new \LogicException('streamSetChunkSize called but $this->resource is not a resource');
|
||||||
|
@ -286,9 +302,9 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* Wrapper to allow mocking
|
* Wrapper to allow mocking
|
||||||
*
|
*
|
||||||
* @return int|false
|
* @return int|bool
|
||||||
*/
|
*/
|
||||||
protected function fwrite(string $data): int|bool
|
protected function fwrite(string $data)
|
||||||
{
|
{
|
||||||
if (!is_resource($this->resource)) {
|
if (!is_resource($this->resource)) {
|
||||||
throw new \LogicException('fwrite called but $this->resource is not a resource');
|
throw new \LogicException('fwrite called but $this->resource is not a resource');
|
||||||
|
@ -302,7 +318,7 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
*
|
*
|
||||||
* @return mixed[]|bool
|
* @return mixed[]|bool
|
||||||
*/
|
*/
|
||||||
protected function streamGetMetadata(): array|bool
|
protected function streamGetMetadata()
|
||||||
{
|
{
|
||||||
if (!is_resource($this->resource)) {
|
if (!is_resource($this->resource)) {
|
||||||
throw new \LogicException('streamGetMetadata called but $this->resource is not a resource');
|
throw new \LogicException('streamGetMetadata called but $this->resource is not a resource');
|
||||||
|
@ -326,9 +342,12 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
$this->connect();
|
$this->connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function generateDataStream(LogRecord $record): string
|
/**
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
|
*/
|
||||||
|
protected function generateDataStream(array $record): string
|
||||||
{
|
{
|
||||||
return (string) $record->formatted;
|
return (string) $record['formatted'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -368,7 +387,7 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
private function setStreamChunkSize(): void
|
private function setStreamChunkSize(): void
|
||||||
{
|
{
|
||||||
if (null !== $this->chunkSize && false === $this->streamSetChunkSize()) {
|
if ($this->chunkSize && !$this->streamSetChunkSize()) {
|
||||||
throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
|
throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,7 +408,7 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
$sent += $chunk;
|
$sent += $chunk;
|
||||||
$socketInfo = $this->streamGetMetadata();
|
$socketInfo = $this->streamGetMetadata();
|
||||||
if (is_array($socketInfo) && (bool) $socketInfo['timed_out']) {
|
if (is_array($socketInfo) && $socketInfo['timed_out']) {
|
||||||
throw new \RuntimeException("Write timed-out");
|
throw new \RuntimeException("Write timed-out");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,7 +437,7 @@ class SocketHandler extends AbstractProcessingHandler
|
||||||
usleep(100);
|
usleep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((microtime(true) - (float) $this->lastWritingAt) >= $this->writingTimeout) {
|
if ((microtime(true) - $this->lastWritingAt) >= $this->writingTimeout) {
|
||||||
$this->closeSocket();
|
$this->closeSocket();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -12,9 +12,8 @@
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Aws\Sqs\SqsClient;
|
use Aws\Sqs\SqsClient;
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes to any sqs queue.
|
* Writes to any sqs queue.
|
||||||
|
@ -28,10 +27,12 @@ class SqsHandler extends AbstractProcessingHandler
|
||||||
/** 100 KB in bytes - head message size for new error log */
|
/** 100 KB in bytes - head message size for new error log */
|
||||||
protected const HEAD_MESSAGE_SIZE = 102400;
|
protected const HEAD_MESSAGE_SIZE = 102400;
|
||||||
|
|
||||||
private SqsClient $client;
|
/** @var SqsClient */
|
||||||
private string $queueUrl;
|
private $client;
|
||||||
|
/** @var string */
|
||||||
|
private $queueUrl;
|
||||||
|
|
||||||
public function __construct(SqsClient $sqsClient, string $queueUrl, int|string|Level $level = Level::Debug, bool $bubble = true)
|
public function __construct(SqsClient $sqsClient, string $queueUrl, $level = Logger::DEBUG, bool $bubble = true)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
|
@ -40,15 +41,15 @@ class SqsHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (!isset($record->formatted) || 'string' !== gettype($record->formatted)) {
|
if (!isset($record['formatted']) || 'string' !== gettype($record['formatted'])) {
|
||||||
throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record));
|
throw new \InvalidArgumentException('SqsHandler accepts only formatted records as a string' . Utils::getRecordMessageForException($record));
|
||||||
}
|
}
|
||||||
|
|
||||||
$messageBody = $record->formatted;
|
$messageBody = $record['formatted'];
|
||||||
if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) {
|
if (strlen($messageBody) >= static::MAX_MESSAGE_SIZE) {
|
||||||
$messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE);
|
$messageBody = Utils::substr($messageBody, 0, static::HEAD_MESSAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
namespace Monolog\Handler;
|
namespace Monolog\Handler;
|
||||||
|
|
||||||
use Monolog\Level;
|
use Monolog\Logger;
|
||||||
use Monolog\Utils;
|
use Monolog\Utils;
|
||||||
use Monolog\LogRecord;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores to any stream resource
|
* Stores to any stream resource
|
||||||
|
@ -21,21 +20,29 @@ use Monolog\LogRecord;
|
||||||
* Can be used to store into php://stderr, remote and local files, etc.
|
* Can be used to store into php://stderr, remote and local files, etc.
|
||||||
*
|
*
|
||||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* @phpstan-import-type FormattedRecord from AbstractProcessingHandler
|
||||||
*/
|
*/
|
||||||
class StreamHandler extends AbstractProcessingHandler
|
class StreamHandler extends AbstractProcessingHandler
|
||||||
{
|
{
|
||||||
|
/** @const int */
|
||||||
protected const MAX_CHUNK_SIZE = 2147483647;
|
protected const MAX_CHUNK_SIZE = 2147483647;
|
||||||
/** 10MB */
|
/** @const int 10MB */
|
||||||
protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024;
|
protected const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024;
|
||||||
protected int $streamChunkSize;
|
/** @var int */
|
||||||
|
protected $streamChunkSize;
|
||||||
/** @var resource|null */
|
/** @var resource|null */
|
||||||
protected $stream;
|
protected $stream;
|
||||||
protected string|null $url = null;
|
/** @var ?string */
|
||||||
private string|null $errorMessage = null;
|
protected $url = null;
|
||||||
protected int|null $filePermission;
|
/** @var ?string */
|
||||||
protected bool $useLocking;
|
private $errorMessage = null;
|
||||||
|
/** @var ?int */
|
||||||
|
protected $filePermission;
|
||||||
|
/** @var bool */
|
||||||
|
protected $useLocking;
|
||||||
/** @var true|null */
|
/** @var true|null */
|
||||||
private bool|null $dirCreated = null;
|
private $dirCreated = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write
|
* @param resource|string $stream If a missing path can't be created, an UnexpectedValueException will be thrown on first write
|
||||||
|
@ -44,7 +51,7 @@ class StreamHandler extends AbstractProcessingHandler
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException If stream is not a resource or string
|
* @throws \InvalidArgumentException If stream is not a resource or string
|
||||||
*/
|
*/
|
||||||
public function __construct($stream, int|string|Level $level = Level::Debug, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
|
public function __construct($stream, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
|
||||||
{
|
{
|
||||||
parent::__construct($level, $bubble);
|
parent::__construct($level, $bubble);
|
||||||
|
|
||||||
|
@ -76,11 +83,11 @@ class StreamHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public function close(): void
|
public function close(): void
|
||||||
{
|
{
|
||||||
if (null !== $this->url && is_resource($this->stream)) {
|
if ($this->url && is_resource($this->stream)) {
|
||||||
fclose($this->stream);
|
fclose($this->stream);
|
||||||
}
|
}
|
||||||
$this->stream = null;
|
$this->stream = null;
|
||||||
|
@ -99,21 +106,26 @@ class StreamHandler extends AbstractProcessingHandler
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the stream URL if it was configured with a URL and not an active resource
|
* Return the stream URL if it was configured with a URL and not an active resource
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getUrl(): ?string
|
public function getUrl(): ?string
|
||||||
{
|
{
|
||||||
return $this->url;
|
return $this->url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
public function getStreamChunkSize(): int
|
public function getStreamChunkSize(): int
|
||||||
{
|
{
|
||||||
return $this->streamChunkSize;
|
return $this->streamChunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
protected function write(LogRecord $record): void
|
protected function write(array $record): void
|
||||||
{
|
{
|
||||||
if (!is_resource($this->stream)) {
|
if (!is_resource($this->stream)) {
|
||||||
$url = $this->url;
|
$url = $this->url;
|
||||||
|
@ -138,6 +150,10 @@ class StreamHandler extends AbstractProcessingHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
$stream = $this->stream;
|
$stream = $this->stream;
|
||||||
|
if (!is_resource($stream)) {
|
||||||
|
throw new \LogicException('No stream was opened yet' . Utils::getRecordMessageForException($record));
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->useLocking) {
|
if ($this->useLocking) {
|
||||||
// ignoring errors here, there's not much we can do about them
|
// ignoring errors here, there's not much we can do about them
|
||||||
flock($stream, LOCK_EX);
|
flock($stream, LOCK_EX);
|
||||||
|
@ -153,10 +169,13 @@ class StreamHandler extends AbstractProcessingHandler
|
||||||
/**
|
/**
|
||||||
* Write to stream
|
* Write to stream
|
||||||
* @param resource $stream
|
* @param resource $stream
|
||||||
|
* @param array $record
|
||||||
|
*
|
||||||
|
* @phpstan-param FormattedRecord $record
|
||||||
*/
|
*/
|
||||||
protected function streamWrite($stream, LogRecord $record): void
|
protected function streamWrite($stream, array $record): void
|
||||||
{
|
{
|
||||||
fwrite($stream, (string) $record->formatted);
|
fwrite($stream, (string) $record['formatted']);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function customErrorHandler(int $code, string $msg): bool
|
private function customErrorHandler(int $code, string $msg): bool
|
||||||
|
@ -183,7 +202,7 @@ class StreamHandler extends AbstractProcessingHandler
|
||||||
private function createDir(string $url): void
|
private function createDir(string $url): void
|
||||||
{
|
{
|
||||||
// Do not try to create dir if it has already been tried.
|
// Do not try to create dir if it has already been tried.
|
||||||
if (true === $this->dirCreated) {
|
if ($this->dirCreated) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue