Commit c4f649cb by 冷斌

fix bug

parent b6f17675
...@@ -94,11 +94,11 @@ class Api_Charge extends PhalApi_Api { ...@@ -94,11 +94,11 @@ class Api_Charge extends PhalApi_Api {
return $rs; return $rs;
} }
$wx_config = [ $wx_config = [
'appid' => $configpri['wx_appid'], 'appid' => 'wxa6bcebff7f4c5a3f',
'appSecret' => $configpri['wx_appsecret'], 'appSecret' => '4b7d6cd67fc566659fd1d9bd399dbcba',
'mch_id' => $configpri['wx_mchid'], 'mch_id' => '1582052881',
'key' => $configpri['wx_key'], 'key' => 'XCthxXszMdXiybUZnJBxyVgyVnV4zPuQ',
'notify_url' => $configpub['site'].'/index.php?g=Appapi&m=pay&a=notify_wx', 'notify_url' => 'http://www.seals-live.com/index.php?g=Appapi&m=pay&a=notify_wx',
'log' => [ // optional 'log' => [ // optional
'file' => dirname(dirname(dirname(__DIR__))) . '/wechat.log', 'file' => dirname(dirname(dirname(__DIR__))) . '/wechat.log',
'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug
...@@ -107,7 +107,7 @@ class Api_Charge extends PhalApi_Api { ...@@ -107,7 +107,7 @@ class Api_Charge extends PhalApi_Api {
], ],
]; ];
require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php'; // require dirname(dirname(dirname(__DIR__))) . '/vendor/autoload.php';
$wx = \Yansongda\Pay\Pay::wechat($wx_config); $wx = \Yansongda\Pay\Pay::wechat($wx_config);
var_dump($wx_config, $wx); var_dump($wx_config, $wx);
die; die;
......
{ {
"require": { "require": {
"yansongda/pay": "^2.9" "yansongda/pay": "2.7.8"
} }
} }
...@@ -17,7 +17,6 @@ return array( ...@@ -17,7 +17,6 @@ return array(
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
......
...@@ -36,7 +36,6 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55 ...@@ -36,7 +36,6 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55
array ( array (
'Psr\\Log\\' => 8, 'Psr\\Log\\' => 8,
'Psr\\Http\\Message\\' => 17, 'Psr\\Http\\Message\\' => 17,
'Psr\\EventDispatcher\\' => 20,
), ),
'M' => 'M' =>
array ( array (
...@@ -95,10 +94,6 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55 ...@@ -95,10 +94,6 @@ class ComposerStaticInit6ca204ee4b311fba144444fd09f9bb55
array ( array (
0 => __DIR__ . '/..' . '/psr/http-message/src', 0 => __DIR__ . '/..' . '/psr/http-message/src',
), ),
'Psr\\EventDispatcher\\' =>
array (
0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
),
'Monolog\\' => 'Monolog\\' =>
array ( array (
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog', 0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
......
### 2.0.2 (2019-12-20)
* Fixed ElasticsearchHandler swallowing exceptions details when failing to index log records
* Fixed normalization of SoapFault objects containing non-strings as "detail" in LineFormatter
* Fixed formatting of resources in JsonFormatter
* Fixed RedisHandler failing to use MULTI properly when passed a proxied Redis instance (e.g. in Symfony with lazy services)
* Fixed FilterHandler triggering a notice when handleBatch was filtering all records passed to it
* Fixed Turkish locale messing up the conversion of level names to their constant values
### 2.0.1 (2019-11-13)
* Fixed normalization of Traversables to avoid traversing them as not all of them are rewindable
* Fixed setFormatter/getFormatter to forward to the nested handler in FilterHandler, FingersCrossedHandler, BufferHandler, OverflowHandler and SamplingHandler
* Fixed BrowserConsoleHandler formatting when using multiple styles
* Fixed normalization of exception codes to be always integers even for PDOException which have them as numeric strings
* Fixed normalization of SoapFault objects containing non-strings as "detail"
* Fixed json encoding across all handlers to always attempt recovery of non-UTF-8 strings instead of failing the whole encoding
* Fixed ChromePHPHandler to avoid sending more data than latest Chrome versions allow in headers (4KB down from 256KB).
* Fixed type error in BrowserConsoleHandler when the context array of log records was not associative.
### 2.0.0 (2019-08-30)
* BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release
* BC Break: Logger methods log/debug/info/notice/warning/error/critical/alert/emergency now have explicit void return types
* Added FallbackGroupHandler which works like the WhatFailureGroupHandler but stops dispatching log records as soon as one handler accepted it
* Fixed support for UTF-8 when cutting strings to avoid cutting a multibyte-character in half
* Fixed normalizers handling of exception backtraces to avoid serializing arguments in some cases
* Fixed date timezone handling in SyslogUdpHandler
### 2.0.0-beta2 (2019-07-06)
* BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release
* BC Break: PHP 7.2 is now the minimum required PHP version.
* BC Break: Removed SlackbotHandler, RavenHandler and HipChatHandler, see [UPGRADE.md](UPGRADE.md) for details
* Added OverflowHandler which will only flush log records to its nested handler when reaching a certain amount of logs (i.e. only pass through when things go really bad)
* Added TelegramBotHandler to log records to a [Telegram](https://core.telegram.org/bots/api) bot account
* Added support for JsonSerializable when normalizing exceptions
* Added support for RFC3164 (outdated BSD syslog protocol) to SyslogUdpHandler
* Added SoapFault details to formatted exceptions
* Fixed DeduplicationHandler silently failing to start when file could not be opened
* Fixed issue in GroupHandler and WhatFailureGroupHandler where setting multiple processors would duplicate records
* Fixed GelfFormatter losing some data when one attachment was too long
* Fixed issue in SignalHandler restarting syscalls functionality
* Improved performance of LogglyHandler when sending multiple logs in a single request
### 2.0.0-beta1 (2018-12-08)
* BC Break: This is a major release, see [UPGRADE.md](UPGRADE.md) for details if you are coming from a 1.x release
* BC Break: PHP 7.1 is now the minimum required PHP version.
* BC Break: Quite a few interface changes, only relevant if you implemented your own handlers/processors/formatters
* BC Break: Removed non-PSR-3 methods to add records, all the `add*` (e.g. `addWarning`) methods as well as `emerg`, `crit`, `err` and `warn`
* BC Break: The record timezone is now set per Logger instance and not statically anymore
* BC Break: There is no more default handler configured on empty Logger instances
* BC Break: ElasticSearchHandler renamed to ElasticaHandler
* BC Break: Various handler-specific breaks, see [UPGRADE.md](UPGRADE.md) for details
* Added scalar type hints and return hints in all the places it was possible. Switched strict_types on for more reliability.
* Added DateTimeImmutable support, all record datetime are now immutable, and will toString/json serialize with the correct date format, including microseconds (unless disabled)
* Added timezone and microseconds to the default date format
* Added SendGridHandler to use the SendGrid API to send emails
* Added LogmaticHandler to use the Logmatic.io API to store log records
* Added SqsHandler to send log records to an AWS SQS queue
* Added ElasticsearchHandler to send records via the official ES library. Elastica users should now use ElasticaHandler instead of ElasticSearchHandler
* Added NoopHandler which is similar to the NullHandle but does not prevent the bubbling of log records to handlers further down the configuration, useful for temporarily disabling a handler in configuration files
* Added ProcessHandler to write log output to the STDIN of a given process
* Added HostnameProcessor that adds the machine's hostname to log records
* Added a `$dateFormat` option to the PsrLogMessageProcessor which lets you format DateTime instances nicely
* Added support for the PHP 7.x `mongodb` extension in the MongoDBHandler
* Fixed many minor issues in various handlers, and probably added a few regressions too
### 1.25.3 (2019-12-20) ### 1.25.3 (2019-12-20)
* Fixed formatting of resources in JsonFormatter * Fixed formatting of resources in JsonFormatter
...@@ -108,7 +39,7 @@ ...@@ -108,7 +39,7 @@
* Added a way to log signals being received using Monolog\SignalHandler * Added a way to log signals being received using Monolog\SignalHandler
* Added ability to customize error handling at the Logger level using Logger::setExceptionHandler * Added ability to customize error handling at the Logger level using Logger::setExceptionHandler
* Added InsightOpsHandler to migrate users of the LogEntriesHandler * Added InsightOpsHandler to migrate users of the LogEntriesHandler
* Added protection to NormalizerFormatter against circular and very deep structures, it now stops normalizing at a depth of 9 * Added protection to NormalizerHandler against circular and very deep structures, it now stops normalizing at a depth of 9
* Added capture of stack traces to ErrorHandler when logging PHP errors * Added capture of stack traces to ErrorHandler when logging PHP errors
* Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts * Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts
* Added forwarding of context info to FluentdFormatter * Added forwarding of context info to FluentdFormatter
...@@ -148,7 +79,7 @@ ...@@ -148,7 +79,7 @@
* Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily * Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily
* Added MercurialProcessor to add mercurial revision and branch names to log records * Added MercurialProcessor to add mercurial revision and branch names to log records
* Added support for AWS SDK v3 in DynamoDbHandler * Added support for AWS SDK v3 in DynamoDbHandler
* Fixed fatal errors occurring when normalizing generators that have been fully consumed * Fixed fatal errors occuring when normalizing generators that have been fully consumed
* Fixed RollbarHandler to include a level (rollbar level), monolog_level (original name), channel and datetime (unix) * Fixed RollbarHandler to include a level (rollbar level), monolog_level (original name), channel and datetime (unix)
* Fixed RollbarHandler not flushing records automatically, calling close() explicitly is not necessary anymore * Fixed RollbarHandler not flushing records automatically, calling close() explicitly is not necessary anymore
* Fixed SyslogUdpHandler to avoid sending empty frames * Fixed SyslogUdpHandler to avoid sending empty frames
...@@ -158,7 +89,7 @@ ...@@ -158,7 +89,7 @@
* Break: Reverted the addition of $context when the ErrorHandler handles regular php errors from 1.20.0 as it was causing issues * Break: Reverted the addition of $context when the ErrorHandler handles regular php errors from 1.20.0 as it was causing issues
* Added support for more formats in RotatingFileHandler::setFilenameFormat as long as they have Y, m and d in order * Added support for more formats in RotatingFileHandler::setFilenameFormat as long as they have Y, m and d in order
* Added ability to format the main line of text the SlackHandler sends by explicitly setting a formatter on the handler * Added ability to format the main line of text the SlackHandler sends by explictly setting a formatter on the handler
* Added information about SoapFault instances in NormalizerFormatter * Added information about SoapFault instances in NormalizerFormatter
* Added $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level * Added $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level
...@@ -280,7 +211,7 @@ ...@@ -280,7 +211,7 @@
* Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data * Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data
* Added $host to HipChatHandler for users of private instances * Added $host to HipChatHandler for users of private instances
* Added $transactionName to NewRelicHandler and support for a transaction_name context value * Added $transactionName to NewRelicHandler and support for a transaction_name context value
* Fixed MandrillHandler to avoid outputting API call responses * Fixed MandrillHandler to avoid outputing API call responses
* Fixed some non-standard behaviors in SyslogUdpHandler * Fixed some non-standard behaviors in SyslogUdpHandler
### 1.11.0 (2014-09-30) ### 1.11.0 (2014-09-30)
......
Copyright (c) 2011-2019 Jordi Boggiano Copyright (c) 2011-2016 Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
......
# Monolog - Logging for PHP [![Build Status](https://travis-ci.org/Seldaek/monolog.svg?branch=master)](https://travis-ci.org/Seldaek/monolog) # Monolog - Logging for PHP [![Build Status](https://img.shields.io/travis/Seldaek/monolog.svg)](https://travis-ci.org/Seldaek/monolog)
[![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)
...@@ -36,23 +36,16 @@ $log = new Logger('name'); ...@@ -36,23 +36,16 @@ $log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::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->addWarning('Foo');
$log->error('Bar'); $log->addError('Bar');
``` ```
## Documentation ## Documentation
- [Usage Instructions](doc/01-usage.md) - [Usage Instructions](doc/01-usage.md)
- [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md) - [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md)
- [Utility Classes](doc/03-utilities.md) - [Utility classes](doc/03-utilities.md)
- [Extending Monolog](doc/04-extending.md) - [Extending Monolog](doc/04-extending.md)
- [Log Record Structure](doc/message-structure.md)
## Support Monolog Financially
Get supported Monolog and help fund the project with the [Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-monolog-monolog?utm_source=packagist-monolog-monolog&utm_medium=referral&utm_campaign=enterprise) or via [GitHub sponsorship](https://github.com/sponsors/Seldaek).
Tidelift delivers commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
## Third Party Packages ## Third Party Packages
...@@ -64,7 +57,7 @@ can also add your own there if you publish one. ...@@ -64,7 +57,7 @@ can also add your own there if you publish one.
### Requirements ### Requirements
- Monolog 2.x works with PHP 7.2 or above, use Monolog `^1.0` for PHP 5.3+ support. - Monolog works with PHP 5.3 or above, and is also tested to work with HHVM.
### Submitting bugs and feature requests ### Submitting bugs and feature requests
...@@ -74,26 +67,22 @@ Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/mono ...@@ -74,26 +67,22 @@ Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/mono
- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) - Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
can be used very easily with Monolog since it implements the interface. can be used very easily with Monolog since it implements the interface.
- [Symfony](http://symfony.com) comes out of the box with Monolog. - [Symfony2](http://symfony.com) comes out of the box with Monolog.
- [Laravel](http://laravel.com/) comes out of the box with Monolog. - [Silex](http://silex.sensiolabs.org/) comes out of the box with Monolog.
- [Laravel 4 & 5](http://laravel.com/) come out of the box with Monolog.
- [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog. - [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog.
- [PPI](https://github.com/ppi/framework) comes out of the box with Monolog. - [PPI](http://www.ppi.io/) comes out of the box with Monolog.
- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin. - [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin.
- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer. - [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer.
- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog. - [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog.
- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog. - [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog.
- [Nette Framework](http://nette.org/en/) can be used with Monolog via [Kdyby/Monolog](https://github.com/Kdyby/Monolog) extension. - [Nette Framework](http://nette.org/en/) can be used with Monolog via [Kdyby/Monolog](https://github.com/Kdyby/Monolog) extension.
- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog. - [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog.
- [FuelPHP](http://fuelphp.com/) comes out of the box with Monolog.
- [Equip Framework](https://github.com/equip/framework) comes out of the box with Monolog.
- [Yii 2](http://www.yiiframework.com/) is usable with Monolog via the [yii2-monolog](https://github.com/merorafael/yii2-monolog) or [yii2-psr-log-target](https://github.com/samdark/yii2-psr-log-target) plugins.
- [Hawkbit Micro Framework](https://github.com/HawkBitPhp/hawkbit) comes out of the box with Monolog.
- [SilverStripe 4](https://www.silverstripe.org/) comes out of the box with Monolog.
### Author ### Author
Jordi Boggiano - <j.boggiano@seld.be> - <http://twitter.com/seldaek><br /> Jordi Boggiano - <j.boggiano@seld.be> - <http://twitter.com/seldaek><br />
See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) who participated in this project. See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project.
### License ### License
......
### 2.0.0
- `Monolog\Logger::API` can be used to distinguish between a Monolog `1` and `2`
install of Monolog when writing integration code.
- Removed non-PSR-3 methods to add records, all the `add*` (e.g. `addWarning`)
methods as well as `emerg`, `crit`, `err` and `warn`.
- DateTime are now formatted with a timezone and microseconds (unless disabled).
Various formatters and log output might be affected, which may mess with log parsing
in some cases.
- The `datetime` in every record array is now a DateTimeImmutable, not that you
should have been modifying these anyway.
- The timezone is now set per Logger instance and not statically, either
via ->setTimezone or passed in the constructor. Calls to Logger::setTimezone
should be converted.
- `HandlerInterface` has been split off and two new interfaces now exist for
more granular controls: `ProcessableHandlerInterface` and
`FormattableHandlerInterface`. Handlers not extending `AbstractHandler`
should make sure to implement the relevant interfaces.
- `HandlerInterface` now requires the `close` method to be implemented. This
only impacts you if you implement the interface yourself, but you can extend
the new `Monolog\Handler\Handler` base class too.
- There is no more default handler configured on empty Logger instances, if
you were relying on that you will not get any output anymore, make sure to
configure the handler you need.
#### LogglyFormatter
- The records' `datetime` is not sent anymore. Only `timestamp` is sent to Loggly.
#### AmqpHandler
- Log levels are not shortened to 4 characters anymore. e.g. a warning record
will be sent using the `warning.channel` routing key instead of `warn.channel`
as in 1.x.
- The exchange name does not default to 'log' anymore, and it is completely ignored
now for the AMQP extension users. Only PHPAmqpLib uses it if provided.
#### RotatingFileHandler
- The file name format must now contain `{date}` and the date format must be set
to one of the predefined FILE_PER_* constants to avoid issues with file rotation.
See `setFilenameFormat`.
#### LogstashFormatter
- Removed Logstash V0 support
- Context/extra prefix has been removed in favor of letting users configure the exact key being sent
- Context/extra data are now sent as an object instead of single keys
#### HipChatHandler
- Removed deprecated HipChat handler, migrate to Slack and use SlackWebhookHandler or SlackHandler instead
#### SlackbotHandler
- Removed deprecated SlackbotHandler handler, use SlackWebhookHandler or SlackHandler instead
#### RavenHandler
- Removed deprecated RavenHandler handler, use sentry/sentry 2.x and their Sentry\Monolog\Handler instead
#### ElasticSearchHandler
- As support for the official Elasticsearch library was added, the former ElasticSearchHandler has been
renamed to ElasticaHandler and the new one added as ElasticsearchHandler.
...@@ -13,37 +13,35 @@ ...@@ -13,37 +13,35 @@
} }
], ],
"require": { "require": {
"php": "^7.2", "php": ">=5.3.0",
"psr/log": "^1.0.1" "psr/log": "~1.0"
}, },
"require-dev": { "require-dev": {
"aws/aws-sdk-php": "^2.4.9 || ^3.0", "phpunit/phpunit": "~4.5",
"graylog2/gelf-php": "~1.0",
"sentry/sentry": "^0.13",
"ruflin/elastica": ">=0.90 <3.0",
"doctrine/couchdb": "~1.0@dev", "doctrine/couchdb": "~1.0@dev",
"elasticsearch/elasticsearch": "^6.0", "aws/aws-sdk-php": "^2.4.9 || ^3.0",
"graylog2/gelf-php": "^1.4.2",
"jakub-onderka/php-parallel-lint": "^0.9",
"php-amqplib/php-amqplib": "~2.4", "php-amqplib/php-amqplib": "~2.4",
"swiftmailer/swiftmailer": "^5.3|^6.0",
"php-console/php-console": "^3.1.3", "php-console/php-console": "^3.1.3",
"phpspec/prophecy": "^1.6.1", "phpunit/phpunit-mock-objects": "2.3.0",
"phpunit/phpunit": "^8.3", "jakub-onderka/php-parallel-lint": "0.9"
"predis/predis": "^1.1",
"rollbar/rollbar": "^1.3",
"ruflin/elastica": ">=0.90 <3.0",
"swiftmailer/swiftmailer": "^5.3|^6.0"
}, },
"_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - needs hhvm 3.8+ on travis",
"suggest": { "suggest": {
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
"sentry/sentry": "Allow sending log messages to a Sentry server",
"doctrine/couchdb": "Allow sending log messages to a CouchDB server", "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server", "ruflin/elastica": "Allow sending log messages to an Elastic Search server",
"elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
"php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", "ext-mongo": "Allow sending log messages to a MongoDB server",
"mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver",
"aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
"rollbar/rollbar": "Allow sending log messages to Rollbar", "rollbar/rollbar": "Allow sending log messages to Rollbar",
"php-console/php-console": "Allow sending log messages to Google Chrome", "php-console/php-console": "Allow sending log messages to Google Chrome"
"ext-mbstring": "Allow to work properly with unicode symbols"
}, },
"autoload": { "autoload": {
"psr-4": {"Monolog\\": "src/Monolog"} "psr-4": {"Monolog\\": "src/Monolog"}
...@@ -56,16 +54,13 @@ ...@@ -56,16 +54,13 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.x-dev" "dev-master": "2.0.x-dev"
} }
}, },
"scripts": { "scripts": {
"test": [ "test": [
"parallel-lint . --exclude vendor", "parallel-lint . --exclude vendor --exclude src/Monolog/Handler/FormattableHandlerInterface.php --exclude src/Monolog/Handler/FormattableHandlerTrait.php --exclude src/Monolog/Handler/ProcessableHandlerInterface.php --exclude src/Monolog/Handler/ProcessableHandlerTrait.php",
"phpunit" "phpunit"
] ]
},
"config": {
"sort-packages": true
} }
} }
<?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;
use DateTimeZone;
/**
* Overrides default json encoding of date time objects
*
* @author Menno Holtkamp
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class DateTimeImmutable extends \DateTimeImmutable implements \JsonSerializable
{
/**
* @var bool
*/
private $useMicroseconds;
public function __construct(bool $useMicroseconds, ?DateTimeZone $timezone = null)
{
$this->useMicroseconds = $useMicroseconds;
parent::__construct('now', $timezone);
}
public function jsonSerialize(): string
{
if ($this->useMicroseconds) {
return $this->format('Y-m-d\TH:i:s.uP');
}
return $this->format('Y-m-d\TH:i:sP');
}
public function __toString(): string
{
return $this->jsonSerialize();
}
}
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -23,7 +23,7 @@ class ChromePHPFormatter implements FormatterInterface ...@@ -23,7 +23,7 @@ class ChromePHPFormatter implements FormatterInterface
/** /**
* Translates Monolog log levels to Wildfire levels. * Translates Monolog log levels to Wildfire levels.
*/ */
private $logLevels = [ private $logLevels = array(
Logger::DEBUG => 'log', Logger::DEBUG => 'log',
Logger::INFO => 'info', Logger::INFO => 'info',
Logger::NOTICE => 'info', Logger::NOTICE => 'info',
...@@ -32,7 +32,7 @@ class ChromePHPFormatter implements FormatterInterface ...@@ -32,7 +32,7 @@ class ChromePHPFormatter implements FormatterInterface
Logger::CRITICAL => 'error', Logger::CRITICAL => 'error',
Logger::ALERT => 'error', Logger::ALERT => 'error',
Logger::EMERGENCY => 'error', Logger::EMERGENCY => 'error',
]; );
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -46,7 +46,7 @@ class ChromePHPFormatter implements FormatterInterface ...@@ -46,7 +46,7 @@ class ChromePHPFormatter implements FormatterInterface
unset($record['extra']['file'], $record['extra']['line']); unset($record['extra']['file'], $record['extra']['line']);
} }
$message = ['message' => $record['message']]; $message = array('message' => $record['message']);
if ($record['context']) { if ($record['context']) {
$message['context'] = $record['context']; $message['context'] = $record['context'];
} }
...@@ -57,20 +57,17 @@ class ChromePHPFormatter implements FormatterInterface ...@@ -57,20 +57,17 @@ class ChromePHPFormatter implements FormatterInterface
$message = reset($message); $message = reset($message);
} }
return [ return array(
$record['channel'], $record['channel'],
$message, $message,
$backtrace, $backtrace,
$this->logLevels[$record['level']], $this->logLevels[$record['level']],
]; );
} }
/**
* {@inheritdoc}
*/
public function formatBatch(array $records) public function formatBatch(array $records)
{ {
$formatted = []; $formatted = array();
foreach ($records as $record) { foreach ($records as $record) {
$formatted[] = $this->format($record); $formatted[] = $this->format($record);
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -34,7 +34,7 @@ class ElasticaFormatter extends NormalizerFormatter ...@@ -34,7 +34,7 @@ class ElasticaFormatter extends NormalizerFormatter
* @param string $index Elastic Search index name * @param string $index Elastic Search index name
* @param string $type Elastic Search document type * @param string $type Elastic Search document type
*/ */
public function __construct(string $index, string $type) public function __construct($index, $type)
{ {
// elasticsearch requires a ISO 8601 format date with optional millisecond precision. // elasticsearch requires a ISO 8601 format date with optional millisecond precision.
parent::__construct('Y-m-d\TH:i:s.uP'); parent::__construct('Y-m-d\TH:i:s.uP');
...@@ -53,22 +53,31 @@ class ElasticaFormatter extends NormalizerFormatter ...@@ -53,22 +53,31 @@ class ElasticaFormatter extends NormalizerFormatter
return $this->getDocument($record); return $this->getDocument($record);
} }
public function getIndex(): string /**
* Getter index
* @return string
*/
public function getIndex()
{ {
return $this->index; return $this->index;
} }
public function getType(): string /**
* Getter type
* @return string
*/
public function getType()
{ {
return $this->type; return $this->type;
} }
/** /**
* Convert a log message into an Elastica Document * Convert a log message into an Elastica Document
* @param array $record *
* @param array $record Log message
* @return Document * @return Document
*/ */
protected function getDocument(array $record): Document protected function getDocument($record)
{ {
$document = new Document(); $document = new Document();
$document->setData($record); $document->setData($record);
......
<?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 DateTime;
/**
* Format a log message into an Elasticsearch record
*
* @author Avtandil Kikabidze <akalongman@gmail.com>
*/
class ElasticsearchFormatter extends NormalizerFormatter
{
/**
* @var string Elasticsearch index name
*/
protected $index;
/**
* @var string Elasticsearch record type
*/
protected $type;
/**
* @param string $index Elasticsearch index name
* @param string $type Elasticsearch record type
*/
public function __construct(string $index, string $type)
{
// Elasticsearch requires an ISO 8601 format date with optional millisecond precision.
parent::__construct(DateTime::ISO8601);
$this->index = $index;
$this->type = $type;
}
/**
* {@inheritdoc}
*/
public function format(array $record)
{
$record = parent::format($record);
return $this->getDocument($record);
}
/**
* Getter index
*
* @return string
*/
public function getIndex(): string
{
return $this->index;
}
/**
* Getter type
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Convert a log message into an Elasticsearch record
*
* @param array $record Log message
* @return array
*/
protected function getDocument(array $record): array
{
$record['_index'] = $this->index;
$record['_type'] = $this->type;
return $record;
}
}
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -28,7 +28,11 @@ class FlowdockFormatter implements FormatterInterface ...@@ -28,7 +28,11 @@ class FlowdockFormatter implements FormatterInterface
*/ */
private $sourceEmail; private $sourceEmail;
public function __construct(string $source, string $sourceEmail) /**
* @param string $source
* @param string $sourceEmail
*/
public function __construct($source, $sourceEmail)
{ {
$this->source = $source; $this->source = $source;
$this->sourceEmail = $sourceEmail; $this->sourceEmail = $sourceEmail;
...@@ -37,13 +41,13 @@ class FlowdockFormatter implements FormatterInterface ...@@ -37,13 +41,13 @@ class FlowdockFormatter implements FormatterInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function format(array $record): array public function format(array $record)
{ {
$tags = [ $tags = array(
'#logs', '#logs',
'#' . strtolower($record['level_name']), '#' . strtolower($record['level_name']),
'#' . $record['channel'], '#' . $record['channel'],
]; );
foreach ($record['extra'] as $value) { foreach ($record['extra'] as $value) {
$tags[] = '#' . $value; $tags[] = '#' . $value;
...@@ -56,14 +60,14 @@ class FlowdockFormatter implements FormatterInterface ...@@ -56,14 +60,14 @@ class FlowdockFormatter implements FormatterInterface
$this->getShortMessage($record['message']) $this->getShortMessage($record['message'])
); );
$record['flowdock'] = [ $record['flowdock'] = array(
'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; return $record;
} }
...@@ -71,9 +75,9 @@ class FlowdockFormatter implements FormatterInterface ...@@ -71,9 +75,9 @@ class FlowdockFormatter implements FormatterInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function formatBatch(array $records): array public function formatBatch(array $records)
{ {
$formatted = []; $formatted = array();
foreach ($records as $record) { foreach ($records as $record) {
$formatted[] = $this->format($record); $formatted[] = $this->format($record);
...@@ -82,7 +86,12 @@ class FlowdockFormatter implements FormatterInterface ...@@ -82,7 +86,12 @@ class FlowdockFormatter implements FormatterInterface
return $formatted; return $formatted;
} }
public function getShortMessage(string $message): string /**
* @param string $message
*
* @return string
*/
public function getShortMessage($message)
{ {
static $hasMbString; static $hasMbString;
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -41,42 +41,42 @@ class FluentdFormatter implements FormatterInterface ...@@ -41,42 +41,42 @@ class FluentdFormatter implements FormatterInterface
*/ */
protected $levelTag = false; protected $levelTag = false;
public function __construct(bool $levelTag = false) public function __construct($levelTag = false)
{ {
if (!function_exists('json_encode')) { if (!function_exists('json_encode')) {
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter'); throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter');
} }
$this->levelTag = $levelTag; $this->levelTag = (bool) $levelTag;
} }
public function isUsingLevelsInTag(): bool public function isUsingLevelsInTag()
{ {
return $this->levelTag; return $this->levelTag;
} }
public function format(array $record): string public function format(array $record)
{ {
$tag = $record['channel']; $tag = $record['channel'];
if ($this->levelTag) { if ($this->levelTag) {
$tag .= '.' . strtolower($record['level_name']); $tag .= '.' . strtolower($record['level_name']);
} }
$message = [ $message = array(
'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']; $message['level'] = $record['level'];
$message['level_name'] = $record['level_name']; $message['level_name'] = $record['level_name'];
} }
return Utils::jsonEncode([$tag, $record['datetime']->getTimestamp(), $message]); return Utils::jsonEncode(array($tag, $record['datetime']->getTimestamp(), $message));
} }
public function formatBatch(array $records): string public function formatBatch(array $records)
{ {
$message = ''; $message = '';
foreach ($records as $record) { foreach ($records as $record) {
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -13,17 +13,16 @@ namespace Monolog\Formatter; ...@@ -13,17 +13,16 @@ namespace Monolog\Formatter;
use Monolog\Logger; use Monolog\Logger;
use Gelf\Message; use Gelf\Message;
use Monolog\Utils;
/** /**
* Serializes a log message to GELF * Serializes a log message to GELF
* @see http://docs.graylog.org/en/latest/pages/gelf.html * @see http://www.graylog2.org/about/gelf
* *
* @author Matt Lehner <mlehner@gmail.com> * @author Matt Lehner <mlehner@gmail.com>
*/ */
class GelfMessageFormatter extends NormalizerFormatter class GelfMessageFormatter extends NormalizerFormatter
{ {
protected const DEFAULT_MAX_LENGTH = 32766; const DEFAULT_MAX_LENGTH = 32766;
/** /**
* @var string the name of the system for the Gelf log message * @var string the name of the system for the Gelf log message
...@@ -48,7 +47,7 @@ class GelfMessageFormatter extends NormalizerFormatter ...@@ -48,7 +47,7 @@ class GelfMessageFormatter extends NormalizerFormatter
/** /**
* Translates Monolog log levels to Graylog2 log priorities. * Translates Monolog log levels to Graylog2 log priorities.
*/ */
private $logLevels = [ private $logLevels = array(
Logger::DEBUG => 7, Logger::DEBUG => 7,
Logger::INFO => 6, Logger::INFO => 6,
Logger::NOTICE => 5, Logger::NOTICE => 5,
...@@ -57,15 +56,15 @@ class GelfMessageFormatter extends NormalizerFormatter ...@@ -57,15 +56,15 @@ class GelfMessageFormatter extends NormalizerFormatter
Logger::CRITICAL => 2, Logger::CRITICAL => 2,
Logger::ALERT => 1, Logger::ALERT => 1,
Logger::EMERGENCY => 0, Logger::EMERGENCY => 0,
]; );
public function __construct(?string $systemName = null, ?string $extraPrefix = null, string $contextPrefix = 'ctxt_', ?int $maxLength = null) public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $maxLength = null)
{ {
parent::__construct('U.u'); parent::__construct('U.u');
$this->systemName = (is_null($systemName) || $systemName === '') ? gethostname() : $systemName; $this->systemName = $systemName ?: gethostname();
$this->extraPrefix = is_null($extraPrefix) ? '' : $extraPrefix; $this->extraPrefix = $extraPrefix;
$this->contextPrefix = $contextPrefix; $this->contextPrefix = $contextPrefix;
$this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength;
} }
...@@ -73,14 +72,9 @@ class GelfMessageFormatter extends NormalizerFormatter ...@@ -73,14 +72,9 @@ class GelfMessageFormatter extends NormalizerFormatter
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function format(array $record): Message public function format(array $record)
{ {
if (isset($record['context'])) { $record = parent::format($record);
$record['context'] = parent::format($record['context']);
}
if (isset($record['extra'])) {
$record['extra'] = parent::format($record['extra']);
}
if (!isset($record['datetime'], $record['message'], $record['level'])) { 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'); throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given');
...@@ -93,11 +87,11 @@ class GelfMessageFormatter extends NormalizerFormatter ...@@ -93,11 +87,11 @@ class GelfMessageFormatter extends NormalizerFormatter
->setHost($this->systemName) ->setHost($this->systemName)
->setLevel($this->logLevels[$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((string) $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(substr($record['message'], 0, $this->maxLength));
} }
if (isset($record['channel'])) { if (isset($record['channel'])) {
...@@ -116,9 +110,8 @@ class GelfMessageFormatter extends NormalizerFormatter ...@@ -116,9 +110,8 @@ class GelfMessageFormatter extends NormalizerFormatter
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val); $val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
$len = strlen($this->extraPrefix . $key . $val); $len = strlen($this->extraPrefix . $key . $val);
if ($len > $this->maxLength) { if ($len > $this->maxLength) {
$message->setAdditional($this->extraPrefix . $key, Utils::substr($val, 0, $this->maxLength)); $message->setAdditional($this->extraPrefix . $key, substr($val, 0, $this->maxLength));
break;
continue;
} }
$message->setAdditional($this->extraPrefix . $key, $val); $message->setAdditional($this->extraPrefix . $key, $val);
} }
...@@ -127,9 +120,8 @@ class GelfMessageFormatter extends NormalizerFormatter ...@@ -127,9 +120,8 @@ class GelfMessageFormatter extends NormalizerFormatter
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val); $val = is_scalar($val) || null === $val ? $val : $this->toJson($val);
$len = strlen($this->contextPrefix . $key . $val); $len = strlen($this->contextPrefix . $key . $val);
if ($len > $this->maxLength) { if ($len > $this->maxLength) {
$message->setAdditional($this->contextPrefix . $key, Utils::substr($val, 0, $this->maxLength)); $message->setAdditional($this->contextPrefix . $key, substr($val, 0, $this->maxLength));
break;
continue;
} }
$message->setAdditional($this->contextPrefix . $key, $val); $message->setAdditional($this->contextPrefix . $key, $val);
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
* *
...@@ -26,7 +25,7 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -26,7 +25,7 @@ class HtmlFormatter extends NormalizerFormatter
/** /**
* Translates Monolog log levels to html color priorities. * Translates Monolog log levels to html color priorities.
*/ */
protected $logLevels = [ protected $logLevels = array(
Logger::DEBUG => '#cccccc', Logger::DEBUG => '#cccccc',
Logger::INFO => '#468847', Logger::INFO => '#468847',
Logger::NOTICE => '#3a87ad', Logger::NOTICE => '#3a87ad',
...@@ -35,12 +34,12 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -35,12 +34,12 @@ class HtmlFormatter extends NormalizerFormatter
Logger::CRITICAL => '#FF7708', Logger::CRITICAL => '#FF7708',
Logger::ALERT => '#C12A19', Logger::ALERT => '#C12A19',
Logger::EMERGENCY => '#000000', Logger::EMERGENCY => '#000000',
]; );
/** /**
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
*/ */
public function __construct(?string $dateFormat = null) public function __construct($dateFormat = null)
{ {
parent::__construct($dateFormat); parent::__construct($dateFormat);
} }
...@@ -48,11 +47,12 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -48,11 +47,12 @@ class HtmlFormatter extends NormalizerFormatter
/** /**
* Creates an HTML table row * Creates an HTML table row
* *
* @param string $th Row header content * @param string $th Row header content
* @param string $td Row standard cell content * @param string $td Row standard cell content
* @param bool $escapeTd false if td content must not be html escaped * @param bool $escapeTd false if td content must not be html escaped
* @return string
*/ */
protected function addRow(string $th, string $td = ' ', bool $escapeTd = true): string protected function addRow($th, $td = ' ', $escapeTd = true)
{ {
$th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8');
if ($escapeTd) { if ($escapeTd) {
...@@ -69,7 +69,7 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -69,7 +69,7 @@ class HtmlFormatter extends NormalizerFormatter
* @param int $level Error level * @param int $level Error level
* @return string * @return string
*/ */
protected function addTitle(string $title, int $level): string protected function addTitle($title, $level)
{ {
$title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8');
...@@ -79,21 +79,21 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -79,21 +79,21 @@ class HtmlFormatter extends NormalizerFormatter
/** /**
* Formats a log record. * Formats a log record.
* *
* @param array $record A record to format * @param array $record A record to format
* @return string The formatted record * @return mixed The formatted record
*/ */
public function format(array $record): string public function format(array $record)
{ {
$output = $this->addTitle($record['level_name'], $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', (string) $record['message']); $output .= $this->addRow('Message', (string) $record['message']);
$output .= $this->addRow('Time', $this->formatDate($record['datetime'])); $output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat));
$output .= $this->addRow('Channel', $record['channel']); $output .= $this->addRow('Channel', $record['channel']);
if ($record['context']) { 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($key, $this->convertToString($value));
} }
$embeddedTable .= '</table>'; $embeddedTable .= '</table>';
$output .= $this->addRow('Context', $embeddedTable, false); $output .= $this->addRow('Context', $embeddedTable, false);
...@@ -101,7 +101,7 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -101,7 +101,7 @@ class HtmlFormatter extends NormalizerFormatter
if ($record['extra']) { 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($key, $this->convertToString($value));
} }
$embeddedTable .= '</table>'; $embeddedTable .= '</table>';
$output .= $this->addRow('Extra', $embeddedTable, false); $output .= $this->addRow('Extra', $embeddedTable, false);
...@@ -113,10 +113,10 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -113,10 +113,10 @@ class HtmlFormatter extends NormalizerFormatter
/** /**
* Formats a set of log records. * Formats a set of log records.
* *
* @param array $records A set of records to format * @param array $records A set of records to format
* @return string The formatted set of records * @return mixed The formatted set of records
*/ */
public function formatBatch(array $records): string public function formatBatch(array $records)
{ {
$message = ''; $message = '';
foreach ($records as $record) { foreach ($records as $record) {
...@@ -126,14 +126,17 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -126,14 +126,17 @@ class HtmlFormatter extends NormalizerFormatter
return $message; return $message;
} }
protected function convertToString($data): string protected function convertToString($data)
{ {
if (null === $data || is_scalar($data)) { if (null === $data || is_scalar($data)) {
return (string) $data; return (string) $data;
} }
$data = $this->normalize($data); $data = $this->normalize($data);
if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return Utils::jsonEncode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE, true);
}
return Utils::jsonEncode($data, JSON_PRETTY_PRINT | Utils::DEFAULT_JSON_FLAGS, true); return str_replace('\\/', '/', Utils::jsonEncode($data, null, true));
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Exception;
use Monolog\Utils; use Monolog\Utils;
use Throwable; use Throwable;
...@@ -23,8 +24,8 @@ use Throwable; ...@@ -23,8 +24,8 @@ use Throwable;
*/ */
class JsonFormatter extends NormalizerFormatter class JsonFormatter extends NormalizerFormatter
{ {
public const BATCH_MODE_JSON = 1; const BATCH_MODE_JSON = 1;
public const BATCH_MODE_NEWLINES = 2; const BATCH_MODE_NEWLINES = 2;
protected $batchMode; protected $batchMode;
protected $appendNewline; protected $appendNewline;
...@@ -34,7 +35,11 @@ class JsonFormatter extends NormalizerFormatter ...@@ -34,7 +35,11 @@ class JsonFormatter extends NormalizerFormatter
*/ */
protected $includeStacktraces = false; protected $includeStacktraces = false;
public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true) /**
* @param int $batchMode
* @param bool $appendNewline
*/
public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true)
{ {
$this->batchMode = $batchMode; $this->batchMode = $batchMode;
$this->appendNewline = $appendNewline; $this->appendNewline = $appendNewline;
...@@ -46,42 +51,36 @@ class JsonFormatter extends NormalizerFormatter ...@@ -46,42 +51,36 @@ class JsonFormatter extends NormalizerFormatter
* formatted as a JSON-encoded array. However, for * formatted as a JSON-encoded array. However, for
* compatibility with some API endpoints, alternative styles * compatibility with some API endpoints, alternative styles
* are available. * are available.
*
* @return int
*/ */
public function getBatchMode(): int public function getBatchMode()
{ {
return $this->batchMode; return $this->batchMode;
} }
/** /**
* True if newlines are appended to every formatted record * True if newlines are appended to every formatted record
*
* @return bool
*/ */
public function isAppendingNewlines(): bool public function isAppendingNewlines()
{ {
return $this->appendNewline; return $this->appendNewline;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*
* @suppress PhanTypeComparisonToArray
*/ */
public function format(array $record): string public function format(array $record)
{ {
$normalized = $this->normalize($record); return $this->toJson($this->normalize($record), true) . ($this->appendNewline ? "\n" : '');
if (isset($normalized['context']) && $normalized['context'] === []) {
$normalized['context'] = new \stdClass;
}
if (isset($normalized['extra']) && $normalized['extra'] === []) {
$normalized['extra'] = new \stdClass;
}
return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : '');
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function formatBatch(array $records): string public function formatBatch(array $records)
{ {
switch ($this->batchMode) { switch ($this->batchMode) {
case static::BATCH_MODE_NEWLINES: case static::BATCH_MODE_NEWLINES:
...@@ -93,15 +92,21 @@ class JsonFormatter extends NormalizerFormatter ...@@ -93,15 +92,21 @@ class JsonFormatter extends NormalizerFormatter
} }
} }
public function includeStacktraces(bool $include = true) /**
* @param bool $include
*/
public function includeStacktraces($include = true)
{ {
$this->includeStacktraces = $include; $this->includeStacktraces = $include;
} }
/** /**
* Return a JSON-encoded array of records. * Return a JSON-encoded array of records.
*
* @param array $records
* @return string
*/ */
protected function formatBatchJson(array $records): string protected function formatBatchJson(array $records)
{ {
return $this->toJson($this->normalize($records), true); return $this->toJson($this->normalize($records), true);
} }
...@@ -109,8 +114,11 @@ class JsonFormatter extends NormalizerFormatter ...@@ -109,8 +114,11 @@ 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.
*
* @param array $records
* @return string
*/ */
protected function formatBatchNewlines(array $records): string protected function formatBatchNewlines(array $records)
{ {
$instance = $this; $instance = $this;
...@@ -131,30 +139,30 @@ class JsonFormatter extends NormalizerFormatter ...@@ -131,30 +139,30 @@ class JsonFormatter extends NormalizerFormatter
* *
* @return mixed * @return mixed
*/ */
protected function normalize($data, int $depth = 0) protected function normalize($data, $depth = 0)
{ {
if ($depth > $this->maxNormalizeDepth) { if ($depth > 9) {
return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization'; return 'Over 9 levels deep, aborting normalization';
} }
if (is_array($data)) { if (is_array($data)) {
$normalized = []; $normalized = array();
$count = 1; $count = 1;
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
if ($count++ > $this->maxNormalizeItemCount) { if ($count++ > 1000) {
$normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.count($data).' total), aborting normalization'; $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
break; break;
} }
$normalized[$key] = $this->normalize($value, $depth + 1); $normalized[$key] = $this->normalize($value, $depth+1);
} }
return $normalized; return $normalized;
} }
if ($data instanceof Throwable) { if ($data instanceof Exception || $data instanceof Throwable) {
return $this->normalizeException($data, $depth); return $this->normalizeException($data);
} }
if (is_resource($data)) { if (is_resource($data)) {
...@@ -167,12 +175,36 @@ class JsonFormatter extends NormalizerFormatter ...@@ -167,12 +175,36 @@ 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.
*
* @param Exception|Throwable $e
*
* @return array
*/ */
protected function normalizeException(Throwable $e, int $depth = 0): array protected function normalizeException($e)
{ {
$data = parent::normalizeException($e, $depth); // TODO 2.0 only check for Throwable
if (!$this->includeStacktraces) { if (!$e instanceof Exception && !$e instanceof Throwable) {
unset($data['trace']); throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
}
$data = array(
'class' => Utils::getClass($e),
'message' => $e->getMessage(),
'code' => (int) $e->getCode(),
'file' => $e->getFile().':'.$e->getLine(),
);
if ($this->includeStacktraces) {
$trace = $e->getTrace();
foreach ($trace as $frame) {
if (isset($frame['file'])) {
$data['trace'][] = $frame['file'].':'.$frame['line'];
}
}
}
if ($previous = $e->getPrevious()) {
$data['previous'] = $this->normalizeException($previous);
} }
return $data; return $data;
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -23,7 +23,7 @@ use Monolog\Utils; ...@@ -23,7 +23,7 @@ use Monolog\Utils;
*/ */
class LineFormatter extends NormalizerFormatter class LineFormatter extends NormalizerFormatter
{ {
public const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
protected $format; protected $format;
protected $allowInlineLineBreaks; protected $allowInlineLineBreaks;
...@@ -31,20 +31,20 @@ class LineFormatter extends NormalizerFormatter ...@@ -31,20 +31,20 @@ class LineFormatter extends NormalizerFormatter
protected $includeStacktraces; protected $includeStacktraces;
/** /**
* @param string|null $format The format of the message * @param string $format The format of the message
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string $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 * @param bool $ignoreEmptyContextAndExtra
*/ */
public function __construct(?string $format = null, ?string $dateFormat = null, bool $allowInlineLineBreaks = false, bool $ignoreEmptyContextAndExtra = false) public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false)
{ {
$this->format = $format === null ? static::SIMPLE_FORMAT : $format; $this->format = $format ?: static::SIMPLE_FORMAT;
$this->allowInlineLineBreaks = $allowInlineLineBreaks; $this->allowInlineLineBreaks = $allowInlineLineBreaks;
$this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
parent::__construct($dateFormat); parent::__construct($dateFormat);
} }
public function includeStacktraces(bool $include = true) public function includeStacktraces($include = true)
{ {
$this->includeStacktraces = $include; $this->includeStacktraces = $include;
if ($this->includeStacktraces) { if ($this->includeStacktraces) {
...@@ -52,12 +52,12 @@ class LineFormatter extends NormalizerFormatter ...@@ -52,12 +52,12 @@ class LineFormatter extends NormalizerFormatter
} }
} }
public function allowInlineLineBreaks(bool $allow = true) public function allowInlineLineBreaks($allow = true)
{ {
$this->allowInlineLineBreaks = $allow; $this->allowInlineLineBreaks = $allow;
} }
public function ignoreEmptyContextAndExtra(bool $ignore = true) public function ignoreEmptyContextAndExtra($ignore = true)
{ {
$this->ignoreEmptyContextAndExtra = $ignore; $this->ignoreEmptyContextAndExtra = $ignore;
} }
...@@ -65,7 +65,7 @@ class LineFormatter extends NormalizerFormatter ...@@ -65,7 +65,7 @@ class LineFormatter extends NormalizerFormatter
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function format(array $record): string public function format(array $record)
{ {
$vars = parent::format($record); $vars = parent::format($record);
...@@ -78,6 +78,7 @@ class LineFormatter extends NormalizerFormatter ...@@ -78,6 +78,7 @@ class LineFormatter extends NormalizerFormatter
} }
} }
foreach ($vars['context'] as $var => $val) { foreach ($vars['context'] as $var => $val) {
if (false !== strpos($output, '%context.'.$var.'%')) { if (false !== strpos($output, '%context.'.$var.'%')) {
$output = str_replace('%context.'.$var.'%', $this->stringify($val), $output); $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output);
...@@ -111,7 +112,7 @@ class LineFormatter extends NormalizerFormatter ...@@ -111,7 +112,7 @@ class LineFormatter extends NormalizerFormatter
return $output; return $output;
} }
public function formatBatch(array $records): string public function formatBatch(array $records)
{ {
$message = ''; $message = '';
foreach ($records as $record) { foreach ($records as $record) {
...@@ -121,28 +122,34 @@ class LineFormatter extends NormalizerFormatter ...@@ -121,28 +122,34 @@ class LineFormatter extends NormalizerFormatter
return $message; return $message;
} }
public function stringify($value): string public function stringify($value)
{ {
return $this->replaceNewlines($this->convertToString($value)); return $this->replaceNewlines($this->convertToString($value));
} }
/** protected function normalizeException($e)
* @suppress PhanParamSignatureMismatch
*/
protected function normalizeException(\Throwable $e, int $depth = 0): string
{ {
$str = $this->formatException($e); // TODO 2.0 only check for Throwable
if (!$e instanceof \Exception && !$e instanceof \Throwable) {
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
}
$previousText = '';
if ($previous = $e->getPrevious()) { if ($previous = $e->getPrevious()) {
do { do {
$str .= "\n[previous exception] " . $this->formatException($previous); $previousText .= ', '.Utils::getClass($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
} while ($previous = $previous->getPrevious()); } while ($previous = $previous->getPrevious());
} }
$str = '[object] ('.Utils::getClass($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
if ($this->includeStacktraces) {
$str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n";
}
return $str; return $str;
} }
protected function convertToString($data): string protected function convertToString($data)
{ {
if (null === $data || is_bool($data)) { if (null === $data || is_bool($data)) {
return var_export($data, true); return var_export($data, true);
...@@ -152,10 +159,14 @@ class LineFormatter extends NormalizerFormatter ...@@ -152,10 +159,14 @@ class LineFormatter extends NormalizerFormatter
return (string) $data; return (string) $data;
} }
return $this->toJson($data, true); if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
return $this->toJson($data, true);
}
return str_replace('\\/', '/', $this->toJson($data, true));
} }
protected function replaceNewlines(string $str): string protected function replaceNewlines($str)
{ {
if ($this->allowInlineLineBreaks) { if ($this->allowInlineLineBreaks) {
if (0 === strpos($str, '{')) { if (0 === strpos($str, '{')) {
...@@ -165,31 +176,6 @@ class LineFormatter extends NormalizerFormatter ...@@ -165,31 +176,6 @@ class LineFormatter extends NormalizerFormatter
return $str; return $str;
} }
return str_replace(["\r\n", "\r", "\n"], ' ', $str); return str_replace(array("\r\n", "\r", "\n"), ' ', $str);
}
private function formatException(\Throwable $e): string
{
$str = '[object] (' . Utils::getClass($e) . '(code: ' . $e->getCode();
if ($e instanceof \SoapFault) {
if (isset($e->faultcode)) {
$str .= ' faultcode: ' . $e->faultcode;
}
if (isset($e->faultactor)) {
$str .= ' faultactor: ' . $e->faultactor;
}
if (isset($e->detail) && (is_string($e->detail) || is_object($e->detail) || is_array($e->detail))) {
$str .= ' detail: ' . (is_string($e->detail) ? $e->detail : reset($e->detail));
}
}
$str .= '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . ')';
if ($this->includeStacktraces) {
$str .= "\n[stacktrace]\n" . $e->getTraceAsString() . "\n";
}
return $str;
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -21,8 +21,10 @@ class LogglyFormatter extends JsonFormatter ...@@ -21,8 +21,10 @@ class LogglyFormatter extends JsonFormatter
/** /**
* Overrides the default batch mode to new lines for compatibility with the * Overrides the default batch mode to new lines for compatibility with the
* Loggly bulk API. * Loggly bulk API.
*
* @param int $batchMode
*/ */
public function __construct(int $batchMode = self::BATCH_MODE_NEWLINES, bool $appendNewline = false) public function __construct($batchMode = self::BATCH_MODE_NEWLINES, $appendNewline = false)
{ {
parent::__construct($batchMode, $appendNewline); parent::__construct($batchMode, $appendNewline);
} }
...@@ -33,11 +35,11 @@ class LogglyFormatter extends JsonFormatter ...@@ -33,11 +35,11 @@ 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()
*/ */
public function format(array $record): string public function format(array $record)
{ {
if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTimeInterface)) { if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTime)) {
$record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO"); $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO");
unset($record["datetime"]); // TODO 2.0 unset the 'datetime' parameter, retained for BC
} }
return parent::format($record); return parent::format($record);
......
<?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;
/**
* Encodes message information into JSON in a format compatible with Logmatic.
*
* @author Julien Breux <julien.breux@gmail.com>
*/
class LogmaticFormatter extends JsonFormatter
{
protected const MARKERS = ["sourcecode", "php"];
/**
* @var string
*/
protected $hostname = '';
/**
* @var string
*/
protected $appname = '';
public function setHostname(string $hostname): self
{
$this->hostname = $hostname;
return $this;
}
public function setAppname(string $appname): self
{
$this->appname = $appname;
return $this;
}
/**
* Appends the 'hostname' and 'appname' parameter for indexing by Logmatic.
*
* @see http://doc.logmatic.io/docs/basics-to-send-data
* @see \Monolog\Formatter\JsonFormatter::format()
*/
public function format(array $record): string
{
if (!empty($this->hostname)) {
$record["hostname"] = $this->hostname;
}
if (!empty($this->appname)) {
$record["appname"] = $this->appname;
}
$record["@marker"] = static::MARKERS;
return parent::format($record);
}
}
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -14,13 +14,16 @@ namespace Monolog\Formatter; ...@@ -14,13 +14,16 @@ namespace Monolog\Formatter;
/** /**
* Serializes a log message to Logstash Event Format * Serializes a log message to Logstash Event Format
* *
* @see https://www.elastic.co/products/logstash * @see http://logstash.net/
* @see https://github.com/elastic/logstash/blob/master/logstash-core/src/main/java/org/logstash/Event.java * @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb
* *
* @author Tim Mower <timothy.mower@gmail.com> * @author Tim Mower <timothy.mower@gmail.com>
*/ */
class LogstashFormatter extends NormalizerFormatter class LogstashFormatter extends NormalizerFormatter
{ {
const V0 = 0;
const V1 = 1;
/** /**
* @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
*/ */
...@@ -32,47 +35,108 @@ class LogstashFormatter extends NormalizerFormatter ...@@ -32,47 +35,108 @@ class LogstashFormatter extends NormalizerFormatter
protected $applicationName; protected $applicationName;
/** /**
* @var string the key for 'extra' fields from the Monolog record * @var string a prefix for 'extra' fields from the Monolog record (optional)
*/ */
protected $extraKey; protected $extraPrefix;
/** /**
* @var string the key for 'context' fields from the Monolog record * @var string a prefix for 'context' fields from the Monolog record (optional)
*/ */
protected $contextKey; protected $contextPrefix;
/** /**
* @param string $applicationName The application that sends the data, used as the "type" field of logstash * @var int logstash format version to use
* @param string|null $systemName The system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
* @param string $extraKey The key for extra keys inside logstash "fields", defaults to extra
* @param string $contextKey The key for context keys inside logstash "fields", defaults to context
*/ */
public function __construct(string $applicationName, ?string $systemName = null, string $extraKey = 'extra', string $contextKey = 'context') protected $version;
/**
* @param string $applicationName the application that sends the data, used as the "type" field of logstash
* @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine
* @param string $extraPrefix prefix for extra keys inside logstash "fields"
* @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_
* @param int $version the logstash format version to use, defaults to 0
*/
public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $version = self::V0)
{ {
// logstash requires a ISO 8601 format date with optional millisecond precision. // logstash requires a ISO 8601 format date with optional millisecond precision.
parent::__construct('Y-m-d\TH:i:s.uP'); parent::__construct('Y-m-d\TH:i:s.uP');
$this->systemName = $systemName === null ? gethostname() : $systemName; $this->systemName = $systemName ?: gethostname();
$this->applicationName = $applicationName; $this->applicationName = $applicationName;
$this->extraKey = $extraKey; $this->extraPrefix = $extraPrefix;
$this->contextKey = $contextKey; $this->contextPrefix = $contextPrefix;
$this->version = $version;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function format(array $record): string public function format(array $record)
{ {
$record = parent::format($record); $record = parent::format($record);
if ($this->version === self::V1) {
$message = $this->formatV1($record);
} else {
$message = $this->formatV0($record);
}
return $this->toJson($message) . "\n";
}
protected function formatV0(array $record)
{
if (empty($record['datetime'])) { if (empty($record['datetime'])) {
$record['datetime'] = gmdate('c'); $record['datetime'] = gmdate('c');
} }
$message = [ $message = array(
'@timestamp' => $record['datetime'],
'@source' => $this->systemName,
'@fields' => array(),
);
if (isset($record['message'])) {
$message['@message'] = $record['message'];
}
if (isset($record['channel'])) {
$message['@tags'] = array($record['channel']);
$message['@fields']['channel'] = $record['channel'];
}
if (isset($record['level'])) {
$message['@fields']['level'] = $record['level'];
}
if ($this->applicationName) {
$message['@type'] = $this->applicationName;
}
if (isset($record['extra']['server'])) {
$message['@source_host'] = $record['extra']['server'];
}
if (isset($record['extra']['url'])) {
$message['@source_path'] = $record['extra']['url'];
}
if (!empty($record['extra'])) {
foreach ($record['extra'] as $key => $val) {
$message['@fields'][$this->extraPrefix . $key] = $val;
}
}
if (!empty($record['context'])) {
foreach ($record['context'] as $key => $val) {
$message['@fields'][$this->contextPrefix . $key] = $val;
}
}
return $message;
}
protected function formatV1(array $record)
{
if (empty($record['datetime'])) {
$record['datetime'] = gmdate('c');
}
$message = array(
'@timestamp' => $record['datetime'], '@timestamp' => $record['datetime'],
'@version' => 1, '@version' => 1,
'host' => $this->systemName, 'host' => $this->systemName,
]; );
if (isset($record['message'])) { if (isset($record['message'])) {
$message['message'] = $record['message']; $message['message'] = $record['message'];
} }
...@@ -83,19 +147,20 @@ class LogstashFormatter extends NormalizerFormatter ...@@ -83,19 +147,20 @@ class LogstashFormatter extends NormalizerFormatter
if (isset($record['level_name'])) { if (isset($record['level_name'])) {
$message['level'] = $record['level_name']; $message['level'] = $record['level_name'];
} }
if (isset($record['level'])) {
$message['monolog_level'] = $record['level'];
}
if ($this->applicationName) { if ($this->applicationName) {
$message['type'] = $this->applicationName; $message['type'] = $this->applicationName;
} }
if (!empty($record['extra'])) { if (!empty($record['extra'])) {
$message[$this->extraKey] = $record['extra']; foreach ($record['extra'] as $key => $val) {
$message[$this->extraPrefix . $key] = $val;
}
} }
if (!empty($record['context'])) { if (!empty($record['context'])) {
$message[$this->contextKey] = $record['context']; foreach ($record['context'] as $key => $val) {
$message[$this->contextPrefix . $key] = $val;
}
} }
return $this->toJson($message) . "\n"; return $message;
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use MongoDB\BSON\UTCDateTime;
use Monolog\Utils; use Monolog\Utils;
/** /**
...@@ -23,24 +22,21 @@ class MongoDBFormatter implements FormatterInterface ...@@ -23,24 +22,21 @@ class MongoDBFormatter implements FormatterInterface
{ {
private $exceptionTraceAsString; private $exceptionTraceAsString;
private $maxNestingLevel; private $maxNestingLevel;
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($maxNestingLevel = 3, $exceptionTraceAsString = true)
{ {
$this->maxNestingLevel = max($maxNestingLevel, 0); $this->maxNestingLevel = max($maxNestingLevel, 0);
$this->exceptionTraceAsString = $exceptionTraceAsString; $this->exceptionTraceAsString = (bool) $exceptionTraceAsString;
$this->isLegacyMongoExt = version_compare(phpversion('mongodb'), '1.1.9', '<=');
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function format(array $record): array public function format(array $record)
{ {
return $this->formatArray($record); return $this->formatArray($record);
} }
...@@ -48,7 +44,7 @@ class MongoDBFormatter implements FormatterInterface ...@@ -48,7 +44,7 @@ class MongoDBFormatter implements FormatterInterface
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function formatBatch(array $records): array public function formatBatch(array $records)
{ {
foreach ($records as $key => $record) { foreach ($records as $key => $record) {
$records[$key] = $this->format($record); $records[$key] = $this->format($record);
...@@ -57,16 +53,13 @@ class MongoDBFormatter implements FormatterInterface ...@@ -57,16 +53,13 @@ class MongoDBFormatter implements FormatterInterface
return $records; return $records;
} }
/** protected function formatArray(array $record, $nestingLevel = 0)
* @return array|string Array except when max nesting level is reached then a string "[...]"
*/
protected function formatArray(array $record, int $nestingLevel = 0)
{ {
if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) { if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
foreach ($record as $name => $value) { foreach ($record as $name => $value) {
if ($value instanceof \DateTimeInterface) { if ($value instanceof \DateTime) {
$record[$name] = $this->formatDate($value, $nestingLevel + 1); $record[$name] = $this->formatDate($value, $nestingLevel + 1);
} elseif ($value instanceof \Throwable) { } elseif ($value instanceof \Exception) {
$record[$name] = $this->formatException($value, $nestingLevel + 1); $record[$name] = $this->formatException($value, $nestingLevel + 1);
} elseif (is_array($value)) { } elseif (is_array($value)) {
$record[$name] = $this->formatArray($value, $nestingLevel + 1); $record[$name] = $this->formatArray($value, $nestingLevel + 1);
...@@ -81,7 +74,7 @@ class MongoDBFormatter implements FormatterInterface ...@@ -81,7 +74,7 @@ class MongoDBFormatter implements FormatterInterface
return $record; return $record;
} }
protected function formatObject($value, int $nestingLevel) protected function formatObject($value, $nestingLevel)
{ {
$objectVars = get_object_vars($value); $objectVars = get_object_vars($value);
$objectVars['class'] = Utils::getClass($value); $objectVars['class'] = Utils::getClass($value);
...@@ -89,14 +82,14 @@ class MongoDBFormatter implements FormatterInterface ...@@ -89,14 +82,14 @@ class MongoDBFormatter implements FormatterInterface
return $this->formatArray($objectVars, $nestingLevel); return $this->formatArray($objectVars, $nestingLevel);
} }
protected function formatException(\Throwable $exception, int $nestingLevel) protected function formatException(\Exception $exception, $nestingLevel)
{ {
$formattedException = [ $formattedException = array(
'class' => Utils::getClass($exception), 'class' => Utils::getClass($exception),
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'code' => (int) $exception->getCode(), 'code' => (int) $exception->getCode(),
'file' => $exception->getFile() . ':' . $exception->getLine(), 'file' => $exception->getFile() . ':' . $exception->getLine(),
]; );
if ($this->exceptionTraceAsString === true) { if ($this->exceptionTraceAsString === true) {
$formattedException['trace'] = $exception->getTraceAsString(); $formattedException['trace'] = $exception->getTraceAsString();
...@@ -107,35 +100,8 @@ class MongoDBFormatter implements FormatterInterface ...@@ -107,35 +100,8 @@ class MongoDBFormatter implements FormatterInterface
return $this->formatArray($formattedException, $nestingLevel); return $this->formatArray($formattedException, $nestingLevel);
} }
protected function formatDate(\DateTimeInterface $value, int $nestingLevel): UTCDateTime protected function formatDate(\DateTime $value, $nestingLevel)
{
if ($this->isLegacyMongoExt) {
return $this->legacyGetMongoDbDateTime($value);
}
return $this->getMongoDbDateTime($value);
}
private function getMongoDbDateTime(\DateTimeInterface $value): UTCDateTime
{ {
return new UTCDateTime((int) (string) floor($value->format('U.u') * 1000)); return new \MongoDate($value->getTimestamp());
}
/**
* This is needed to support MongoDB Driver v1.19 and below
*
* See https://github.com/mongodb/mongo-php-driver/issues/426
*
* It can probably be removed in 2.1 or later once MongoDB's 1.2 is released and widely adopted
*/
private function legacyGetMongoDbDateTime(\DateTimeInterface $value): UTCDateTime
{
$milliseconds = floor($value->format('U.u') * 1000);
$milliseconds = (PHP_INT_SIZE == 8) //64-bit OS?
? (int) $milliseconds
: (string) $milliseconds;
return new UTCDateTime($milliseconds);
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,9 +11,8 @@ ...@@ -11,9 +11,8 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\DateTimeImmutable; use Exception;
use Monolog\Utils; use Monolog\Utils;
use Throwable;
/** /**
* 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
...@@ -22,20 +21,16 @@ use Throwable; ...@@ -22,20 +21,16 @@ use Throwable;
*/ */
class NormalizerFormatter implements FormatterInterface class NormalizerFormatter implements FormatterInterface
{ {
public const SIMPLE_DATE = "Y-m-d\TH:i:sP"; const SIMPLE_DATE = "Y-m-d H:i:s";
protected $dateFormat; protected $dateFormat;
protected $maxNormalizeDepth = 9;
protected $maxNormalizeItemCount = 1000;
private $jsonEncodeOptions = Utils::DEFAULT_JSON_FLAGS;
/** /**
* @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
*/ */
public function __construct(?string $dateFormat = null) public function __construct($dateFormat = null)
{ {
$this->dateFormat = null === $dateFormat ? static::SIMPLE_DATE : $dateFormat; $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE;
if (!function_exists('json_encode')) { if (!function_exists('json_encode')) {
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter');
} }
...@@ -61,58 +56,10 @@ class NormalizerFormatter implements FormatterInterface ...@@ -61,58 +56,10 @@ class NormalizerFormatter implements FormatterInterface
return $records; return $records;
} }
/** protected function normalize($data, $depth = 0)
* The maximum number of normalization levels to go through
*/
public function getMaxNormalizeDepth(): int
{
return $this->maxNormalizeDepth;
}
public function setMaxNormalizeDepth(int $maxNormalizeDepth): self
{
$this->maxNormalizeDepth = $maxNormalizeDepth;
return $this;
}
/**
* The maximum number of items to normalize per level
*/
public function getMaxNormalizeItemCount(): int
{
return $this->maxNormalizeItemCount;
}
public function setMaxNormalizeItemCount(int $maxNormalizeItemCount): self
{
$this->maxNormalizeItemCount = $maxNormalizeItemCount;
return $this;
}
/**
* Enables `json_encode` pretty print.
*/
public function setJsonPrettyPrint(bool $enable): self
{
if ($enable) {
$this->jsonEncodeOptions |= JSON_PRETTY_PRINT;
} else {
$this->jsonEncodeOptions ^= JSON_PRETTY_PRINT;
}
return $this;
}
/**
* @param mixed $data
* @return int|bool|string|null|array
*/
protected function normalize($data, int $depth = 0)
{ {
if ($depth > $this->maxNormalizeDepth) { if ($depth > 9) {
return 'Over ' . $this->maxNormalizeDepth . ' levels deep, aborting normalization'; return 'Over 9 levels deep, aborting normalization';
} }
if (null === $data || is_scalar($data)) { if (null === $data || is_scalar($data)) {
...@@ -129,69 +76,62 @@ class NormalizerFormatter implements FormatterInterface ...@@ -129,69 +76,62 @@ class NormalizerFormatter implements FormatterInterface
} }
if (is_array($data)) { if (is_array($data)) {
$normalized = []; $normalized = array();
$count = 1; $count = 1;
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
if ($count++ > $this->maxNormalizeItemCount) { if ($count++ > 1000) {
$normalized['...'] = 'Over ' . $this->maxNormalizeItemCount . ' items ('.count($data).' total), aborting normalization'; $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
break; break;
} }
$normalized[$key] = $this->normalize($value, $depth + 1); $normalized[$key] = $this->normalize($value, $depth+1);
} }
return $normalized; return $normalized;
} }
if ($data instanceof \DateTimeInterface) { if ($data instanceof \DateTime) {
return $this->formatDate($data); return $data->format($this->dateFormat);
} }
if (is_object($data)) { if (is_object($data)) {
if ($data instanceof Throwable) { // TODO 2.0 only check for Throwable
return $this->normalizeException($data, $depth); if ($data instanceof Exception || (PHP_VERSION_ID > 70000 && $data instanceof \Throwable)) {
return $this->normalizeException($data);
} }
if ($data instanceof \JsonSerializable) { // non-serializable objects that implement __toString stringified
$value = $data->jsonSerialize(); if (method_exists($data, '__toString') && !$data instanceof \JsonSerializable) {
} elseif (method_exists($data, '__toString')) {
$value = $data->__toString(); $value = $data->__toString();
} else { } else {
// the rest is normalized by json encoding and decoding it // the rest is json-serialized in some way
$encoded = $this->toJson($data, true); $value = $this->toJson($data, true);
if ($encoded === false) {
$value = 'JSON_ERROR';
} else {
$value = json_decode($encoded, true);
}
} }
return [Utils::getClass($data) => $value]; return sprintf("[object] (%s: %s)", Utils::getClass($data), $value);
} }
if (is_resource($data)) { if (is_resource($data)) {
return sprintf('[resource(%s)]', get_resource_type($data)); return sprintf('[resource] (%s)', get_resource_type($data));
} }
return '[unknown('.gettype($data).')]'; return '[unknown('.gettype($data).')]';
} }
/** protected function normalizeException($e)
* @return array
*/
protected function normalizeException(Throwable $e, int $depth = 0)
{ {
if ($e instanceof \JsonSerializable) { // TODO 2.0 only check for Throwable
return (array) $e->jsonSerialize(); if (!$e instanceof Exception && !$e instanceof \Throwable) {
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
} }
$data = [ $data = array(
'class' => Utils::getClass($e), 'class' => Utils::getClass($e),
'message' => $e->getMessage(), 'message' => $e->getMessage(),
'code' => (int) $e->getCode(), 'code' => (int) $e->getCode(),
'file' => $e->getFile().':'.$e->getLine(), 'file' => $e->getFile().':'.$e->getLine(),
]; );
if ($e instanceof \SoapFault) { if ($e instanceof \SoapFault) {
if (isset($e->faultcode)) { if (isset($e->faultcode)) {
...@@ -215,7 +155,7 @@ class NormalizerFormatter implements FormatterInterface ...@@ -215,7 +155,7 @@ class NormalizerFormatter implements FormatterInterface
} }
if ($previous = $e->getPrevious()) { if ($previous = $e->getPrevious()) {
$data['previous'] = $this->normalizeException($previous, $depth + 1); $data['previous'] = $this->normalizeException($previous);
} }
return $data; return $data;
...@@ -225,32 +165,12 @@ class NormalizerFormatter implements FormatterInterface ...@@ -225,32 +165,12 @@ class NormalizerFormatter implements FormatterInterface
* Return the JSON representation of a value * Return the JSON representation of a value
* *
* @param mixed $data * @param mixed $data
* @param bool $ignoreErrors
* @throws \RuntimeException if encoding fails and errors are not ignored * @throws \RuntimeException if encoding fails and errors are not ignored
* @return string if encoding fails and ignoreErrors is true 'null' is returned * @return string
*/ */
protected function toJson($data, bool $ignoreErrors = false): string protected function toJson($data, $ignoreErrors = false)
{
return Utils::jsonEncode($data, $this->jsonEncodeOptions, $ignoreErrors);
}
protected function formatDate(\DateTimeInterface $date)
{
// 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
if ($this->dateFormat === self::SIMPLE_DATE && $date instanceof DateTimeImmutable) {
return (string) $date;
}
return $date->format($this->dateFormat);
}
protected function addJsonEncodeOption($option)
{
$this->jsonEncodeOptions |= $option;
}
protected function removeJsonEncodeOption($option)
{ {
$this->jsonEncodeOptions ^= $option; return Utils::jsonEncode($data, null, $ignoreErrors);
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -22,7 +22,7 @@ class ScalarFormatter extends NormalizerFormatter ...@@ -22,7 +22,7 @@ class ScalarFormatter extends NormalizerFormatter
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function format(array $record): array public function format(array $record)
{ {
foreach ($record as $key => $value) { foreach ($record as $key => $value) {
$record[$key] = $this->normalizeValue($value); $record[$key] = $this->normalizeValue($value);
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -22,10 +22,12 @@ use Monolog\Logger; ...@@ -22,10 +22,12 @@ use Monolog\Logger;
*/ */
class WildfireFormatter extends NormalizerFormatter class WildfireFormatter extends NormalizerFormatter
{ {
const TABLE = 'table';
/** /**
* Translates Monolog log levels to Wildfire levels. * Translates Monolog log levels to Wildfire levels.
*/ */
private $logLevels = [ private $logLevels = array(
Logger::DEBUG => 'LOG', Logger::DEBUG => 'LOG',
Logger::INFO => 'INFO', Logger::INFO => 'INFO',
Logger::NOTICE => 'INFO', Logger::NOTICE => 'INFO',
...@@ -34,12 +36,12 @@ class WildfireFormatter extends NormalizerFormatter ...@@ -34,12 +36,12 @@ class WildfireFormatter extends NormalizerFormatter
Logger::CRITICAL => 'ERROR', Logger::CRITICAL => 'ERROR',
Logger::ALERT => 'ERROR', Logger::ALERT => 'ERROR',
Logger::EMERGENCY => 'ERROR', Logger::EMERGENCY => 'ERROR',
]; );
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function format(array $record): string 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
$file = $line = ''; $file = $line = '';
...@@ -53,7 +55,7 @@ class WildfireFormatter extends NormalizerFormatter ...@@ -53,7 +55,7 @@ class WildfireFormatter extends NormalizerFormatter
} }
$record = $this->normalize($record); $record = $this->normalize($record);
$message = ['message' => $record['message']]; $message = array('message' => $record['message']);
$handleError = false; $handleError = false;
if ($record['context']) { if ($record['context']) {
$message['context'] = $record['context']; $message['context'] = $record['context'];
...@@ -67,49 +69,42 @@ class WildfireFormatter extends NormalizerFormatter ...@@ -67,49 +69,42 @@ class WildfireFormatter extends NormalizerFormatter
$message = reset($message); $message = reset($message);
} }
if (isset($record['context']['table'])) { if (isset($record['context'][self::TABLE])) {
$type = 'TABLE'; $type = 'TABLE';
$label = $record['channel'] .': '. $record['message']; $label = $record['channel'] .': '. $record['message'];
$message = $record['context']['table']; $message = $record['context'][self::TABLE];
} else { } else {
$type = $this->logLevels[$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
$json = $this->toJson([ $json = $this->toJson(array(
[ array(
'Type' => $type, 'Type' => $type,
'File' => $file, 'File' => $file,
'Line' => $line, 'Line' => $line,
'Label' => $label, 'Label' => $label,
], ),
$message, $message,
], $handleError); ), $handleError);
// The message itself is a serialization of the above JSON object + it's length // The message itself is a serialization of the above JSON object + it's length
return sprintf( return sprintf(
'%d|%s|', '%s|%s|',
strlen($json), strlen($json),
$json $json
); );
} }
/**
* {@inheritdoc}
*/
public function formatBatch(array $records) public function formatBatch(array $records)
{ {
throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter');
} }
/** protected function normalize($data, $depth = 0)
* {@inheritdoc}
* @suppress PhanTypeMismatchReturn
*/
protected function normalize($data, int $depth = 0)
{ {
if (is_object($data) && !$data instanceof \DateTimeInterface) { if (is_object($data) && !$data instanceof \DateTime) {
return $data; return $data;
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,24 +11,32 @@ ...@@ -11,24 +11,32 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface; use Monolog\ResettableInterface;
/** /**
* Base Handler class providing basic level/bubble support * Base Handler class providing the Handler structure
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
abstract class AbstractHandler extends Handler implements ResettableInterface abstract class AbstractHandler implements HandlerInterface, ResettableInterface
{ {
protected $level = Logger::DEBUG; protected $level = Logger::DEBUG;
protected $bubble = true; protected $bubble = true;
/** /**
* @param int|string $level The minimum logging level at which this handler will be triggered * @var FormatterInterface
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($level = Logger::DEBUG, bool $bubble = true) protected $formatter;
protected $processors = array();
/**
* @param int $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
*/
public function __construct($level = Logger::DEBUG, $bubble = true)
{ {
$this->setLevel($level); $this->setLevel($level);
$this->bubble = $bubble; $this->bubble = $bubble;
...@@ -37,18 +45,84 @@ abstract class AbstractHandler extends Handler implements ResettableInterface ...@@ -37,18 +45,84 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function isHandling(array $record): bool public function isHandling(array $record)
{ {
return $record['level'] >= $this->level; return $record['level'] >= $this->level;
} }
/** /**
* {@inheritdoc}
*/
public function handleBatch(array $records)
{
foreach ($records as $record) {
$this->handle($record);
}
}
/**
* Closes the handler.
*
* This will be called automatically when the object is destroyed
*/
public function close()
{
}
/**
* {@inheritdoc}
*/
public function pushProcessor($callback)
{
if (!is_callable($callback)) {
throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
}
array_unshift($this->processors, $callback);
return $this;
}
/**
* {@inheritdoc}
*/
public function popProcessor()
{
if (!$this->processors) {
throw new \LogicException('You tried to pop from an empty processor stack.');
}
return array_shift($this->processors);
}
/**
* {@inheritdoc}
*/
public function setFormatter(FormatterInterface $formatter)
{
$this->formatter = $formatter;
return $this;
}
/**
* {@inheritdoc}
*/
public function getFormatter()
{
if (!$this->formatter) {
$this->formatter = $this->getDefaultFormatter();
}
return $this->formatter;
}
/**
* Sets minimum logging level at which this handler will be triggered. * Sets minimum logging level at which this handler will be triggered.
* *
* @param int|string $level Level or level name * @param int|string $level Level or level name
* @return self * @return self
*/ */
public function setLevel($level): self public function setLevel($level)
{ {
$this->level = Logger::toMonologLevel($level); $this->level = Logger::toMonologLevel($level);
...@@ -60,7 +134,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface ...@@ -60,7 +134,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
* *
* @return int * @return int
*/ */
public function getLevel(): int public function getLevel()
{ {
return $this->level; return $this->level;
} }
...@@ -72,7 +146,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface ...@@ -72,7 +146,7 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
* false means that bubbling is not permitted. * false means that bubbling is not permitted.
* @return self * @return self
*/ */
public function setBubble(bool $bubble): self public function setBubble($bubble)
{ {
$this->bubble = $bubble; $this->bubble = $bubble;
...@@ -85,12 +159,38 @@ abstract class AbstractHandler extends Handler implements ResettableInterface ...@@ -85,12 +159,38 @@ abstract class AbstractHandler extends Handler implements ResettableInterface
* @return bool true means that this handler allows bubbling. * @return bool true means that this handler allows bubbling.
* false means that bubbling is not permitted. * false means that bubbling is not permitted.
*/ */
public function getBubble(): bool public function getBubble()
{ {
return $this->bubble; return $this->bubble;
} }
public function __destruct()
{
try {
$this->close();
} catch (\Exception $e) {
// do nothing
} catch (\Throwable $e) {
// do nothing
}
}
public function reset() public function reset()
{ {
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
/**
* Gets the default formatter.
*
* @return FormatterInterface
*/
protected function getDefaultFormatter()
{
return new LineFormatter();
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,31 +11,28 @@ ...@@ -11,31 +11,28 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\ResettableInterface;
/** /**
* Base Handler class providing the Handler structure, including processors and formatters * Base Handler class providing the Handler structure
* *
* Classes extending it should (in most cases) only implement write($record) * Classes extending it should (in most cases) only implement write($record)
* *
* @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>
*/ */
abstract class AbstractProcessingHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface abstract class AbstractProcessingHandler extends AbstractHandler
{ {
use ProcessableHandlerTrait;
use FormattableHandlerTrait;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle(array $record): bool public function handle(array $record)
{ {
if (!$this->isHandling($record)) { if (!$this->isHandling($record)) {
return false; return false;
} }
if ($this->processors) { $record = $this->processRecord($record);
$record = $this->processRecord($record);
}
$record['formatted'] = $this->getFormatter()->format($record); $record['formatted'] = $this->getFormatter()->format($record);
...@@ -46,13 +43,26 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc ...@@ -46,13 +43,26 @@ abstract class AbstractProcessingHandler extends AbstractHandler implements Proc
/** /**
* Writes the record down to the log of the implementing handler * Writes the record down to the log of the implementing handler
*
* @param array $record
* @return void
*/ */
abstract protected function write(array $record): void; abstract protected function write(array $record);
public function reset() /**
* Processes a record.
*
* @param array $record
* @return array
*/
protected function processRecord(array $record)
{ {
parent::reset(); if ($this->processors) {
foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
}
$this->resetProcessors(); return $record;
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
/** /**
...@@ -25,7 +24,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ...@@ -25,7 +24,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
/** /**
* Translates Monolog log levels to syslog log priorities. * Translates Monolog log levels to syslog log priorities.
*/ */
protected $logLevels = [ protected $logLevels = array(
Logger::DEBUG => LOG_DEBUG, Logger::DEBUG => LOG_DEBUG,
Logger::INFO => LOG_INFO, Logger::INFO => LOG_INFO,
Logger::NOTICE => LOG_NOTICE, Logger::NOTICE => LOG_NOTICE,
...@@ -34,12 +33,12 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ...@@ -34,12 +33,12 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
Logger::CRITICAL => LOG_CRIT, Logger::CRITICAL => LOG_CRIT,
Logger::ALERT => LOG_ALERT, Logger::ALERT => LOG_ALERT,
Logger::EMERGENCY => LOG_EMERG, Logger::EMERGENCY => LOG_EMERG,
]; );
/** /**
* List of valid log facility names. * List of valid log facility names.
*/ */
protected $facilities = [ protected $facilities = array(
'auth' => LOG_AUTH, 'auth' => LOG_AUTH,
'authpriv' => LOG_AUTHPRIV, 'authpriv' => LOG_AUTHPRIV,
'cron' => LOG_CRON, 'cron' => LOG_CRON,
...@@ -51,14 +50,14 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ...@@ -51,14 +50,14 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
'syslog' => LOG_SYSLOG, 'syslog' => LOG_SYSLOG,
'user' => LOG_USER, 'user' => LOG_USER,
'uucp' => LOG_UUCP, 'uucp' => LOG_UUCP,
]; );
/** /**
* @param string|int $facility Either one of the names of the keys in $this->facilities, or a LOG_* facility constant * @param mixed $facility
* @param string|int $level The minimum logging level at which this handler will be triggered * @param int $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
*/ */
public function __construct($facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true) public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
...@@ -83,7 +82,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ...@@ -83,7 +82,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
} }
// convert textual description of facility to syslog constant // convert textual description of facility to syslog constant
if (is_string($facility) && array_key_exists(strtolower($facility), $this->facilities)) { if (array_key_exists(strtolower($facility), $this->facilities)) {
$facility = $this->facilities[strtolower($facility)]; $facility = $this->facilities[strtolower($facility)];
} elseif (!in_array($facility, array_values($this->facilities), true)) { } elseif (!in_array($facility, array_values($this->facilities), true)) {
throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given');
...@@ -95,7 +94,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ...@@ -95,7 +94,7 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%');
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
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;
...@@ -32,18 +31,18 @@ class AmqpHandler extends AbstractProcessingHandler ...@@ -32,18 +31,18 @@ class AmqpHandler extends AbstractProcessingHandler
/** /**
* @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use * @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 * @param string $exchangeName
* @param string|int $level The minimum logging level at which this handler will be triggered * @param int $level
* @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
*/ */
public function __construct($exchange, ?string $exchangeName = null, $level = Logger::DEBUG, bool $bubble = true) public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true)
{ {
if ($exchange instanceof AMQPChannel) { if ($exchange instanceof AMQPExchange) {
$this->exchangeName = (string) $exchangeName; $exchange->setName($exchangeName);
} elseif (!$exchange instanceof AMQPExchange) { } elseif ($exchange instanceof AMQPChannel) {
$this->exchangeName = $exchangeName;
} else {
throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required'); 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; $this->exchange = $exchange;
...@@ -53,7 +52,7 @@ class AmqpHandler extends AbstractProcessingHandler ...@@ -53,7 +52,7 @@ class AmqpHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
$data = $record["formatted"]; $data = $record["formatted"];
$routingKey = $this->getRoutingKey($record); $routingKey = $this->getRoutingKey($record);
...@@ -63,10 +62,10 @@ class AmqpHandler extends AbstractProcessingHandler ...@@ -63,10 +62,10 @@ class AmqpHandler extends AbstractProcessingHandler
$data, $data,
$routingKey, $routingKey,
0, 0,
[ array(
'delivery_mode' => 2, 'delivery_mode' => 2,
'content_type' => 'application/json', 'content_type' => 'application/json',
] )
); );
} else { } else {
$this->exchange->basic_publish( $this->exchange->basic_publish(
...@@ -80,7 +79,7 @@ class AmqpHandler extends AbstractProcessingHandler ...@@ -80,7 +79,7 @@ class AmqpHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records)
{ {
if ($this->exchange instanceof AMQPExchange) { if ($this->exchange instanceof AMQPExchange) {
parent::handleBatch($records); parent::handleBatch($records);
...@@ -108,29 +107,41 @@ class AmqpHandler extends AbstractProcessingHandler ...@@ -108,29 +107,41 @@ class AmqpHandler extends AbstractProcessingHandler
/** /**
* Gets the routing key for the AMQP exchange * Gets the routing key for the AMQP exchange
*
* @param array $record
* @return string
*/ */
protected function getRoutingKey(array $record): string protected function getRoutingKey(array $record)
{ {
$routingKey = sprintf('%s.%s', $record['level_name'], $record['channel']); $routingKey = sprintf(
'%s.%s',
// TODO 2.0 remove substr call
substr($record['level_name'], 0, 4),
$record['channel']
);
return strtolower($routingKey); return strtolower($routingKey);
} }
private function createAmqpMessage(string $data): AMQPMessage /**
* @param string $data
* @return AMQPMessage
*/
private function createAmqpMessage($data)
{ {
return new AMQPMessage( return new AMQPMessage(
$data, (string) $data,
[ array(
'delivery_mode' => 2, 'delivery_mode' => 2,
'content_type' => 'application/json', 'content_type' => 'application/json',
] )
); );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,8 +12,6 @@ ...@@ -12,8 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Utils;
/** /**
* 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
...@@ -23,7 +21,7 @@ use Monolog\Utils; ...@@ -23,7 +21,7 @@ use Monolog\Utils;
class BrowserConsoleHandler extends AbstractProcessingHandler class BrowserConsoleHandler extends AbstractProcessingHandler
{ {
protected static $initialized = false; protected static $initialized = false;
protected static $records = []; protected static $records = array();
/** /**
* {@inheritDoc} * {@inheritDoc}
...@@ -34,7 +32,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -34,7 +32,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
* *
* You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} * You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%');
} }
...@@ -42,7 +40,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -42,7 +40,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
// Accumulate records // Accumulate records
static::$records[] = $record; static::$records[] = $record;
...@@ -58,7 +56,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -58,7 +56,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
* Convert records to javascript console commands and send it to the browser. * Convert records to javascript console commands and send it to the browser.
* This method is automatically called on PHP shutdown if output is HTML or Javascript. * This method is automatically called on PHP shutdown if output is HTML or Javascript.
*/ */
public static function send(): void public static function send()
{ {
$format = static::getResponseFormat(); $format = static::getResponseFormat();
if ($format === 'unknown') { if ($format === 'unknown') {
...@@ -75,40 +73,40 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -75,40 +73,40 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
} }
} }
public function close(): void public function close()
{ {
self::resetStatic(); self::resetStatic();
} }
public function reset() public function reset()
{ {
parent::reset();
self::resetStatic(); self::resetStatic();
} }
/** /**
* Forget all logged records * Forget all logged records
*/ */
public static function resetStatic(): void public static function resetStatic()
{ {
static::$records = []; static::$records = array();
} }
/** /**
* Wrapper for register_shutdown_function to allow overriding * Wrapper for register_shutdown_function to allow overriding
*/ */
protected function registerShutdownFunction(): void protected function registerShutdownFunction()
{ {
if (PHP_SAPI !== 'cli') { if (PHP_SAPI !== 'cli') {
register_shutdown_function(['Monolog\Handler\BrowserConsoleHandler', 'send']); register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send'));
} }
} }
/** /**
* Wrapper for echo to allow overriding * Wrapper for echo to allow overriding
*
* @param string $str
*/ */
protected static function writeOutput(string $str): void protected static function writeOutput($str)
{ {
echo $str; echo $str;
} }
...@@ -122,7 +120,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -122,7 +120,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
* *
* @return string One of 'js', 'html' or 'unknown' * @return string One of 'js', 'html' or 'unknown'
*/ */
protected static function getResponseFormat(): string protected static function getResponseFormat()
{ {
// Check content type // Check content type
foreach (headers_list() as $header) { foreach (headers_list() as $header) {
...@@ -142,9 +140,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -142,9 +140,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return 'html'; return 'html';
} }
private static function generateScript(): string private static function generateScript()
{ {
$script = []; $script = array();
foreach (static::$records as $record) { foreach (static::$records as $record) {
$context = static::dump('Context', $record['context']); $context = static::dump('Context', $record['context']);
$extra = static::dump('Extra', $record['extra']); $extra = static::dump('Extra', $record['extra']);
...@@ -152,12 +150,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -152,12 +150,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
if (empty($context) && empty($extra)) { if (empty($context) && empty($extra)) {
$script[] = static::call_array('log', static::handleStyles($record['formatted'])); $script[] = static::call_array('log', static::handleStyles($record['formatted']));
} else { } else {
$script = array_merge( $script = array_merge($script,
$script, array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))),
[static::call_array('groupCollapsed', static::handleStyles($record['formatted']))],
$context, $context,
$extra, $extra,
[static::call('groupEnd')] array(static::call('groupEnd'))
); );
} }
} }
...@@ -165,9 +162,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -165,9 +162,9 @@ 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 handleStyles(string $formatted): array private static function handleStyles($formatted)
{ {
$args = []; $args = array();
$format = '%c' . $formatted; $format = '%c' . $formatted;
preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
...@@ -176,7 +173,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -176,7 +173,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
$args[] = static::quote(static::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 = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
} }
$args[] = static::quote('font-weight: normal'); $args[] = static::quote('font-weight: normal');
...@@ -185,12 +182,12 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -185,12 +182,12 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
return array_reverse($args); return array_reverse($args);
} }
private static function handleCustomStyles(string $style, string $string): string private static function handleCustomStyles($style, $string)
{ {
static $colors = ['blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey']; static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey');
static $labels = []; static $labels = array();
return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function (array $m) use ($string, &$colors, &$labels) { return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) {
if (trim($m[1]) === 'autolabel') { if (trim($m[1]) === 'autolabel') {
// Format the string as a label with consistent auto assigned background color // Format the string as a label with consistent auto assigned background color
if (!isset($labels[$string])) { if (!isset($labels[$string])) {
...@@ -205,9 +202,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -205,9 +202,9 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
}, $style); }, $style);
} }
private static function dump(string $title, array $dict): array private static function dump($title, array $dict)
{ {
$script = []; $script = array();
$dict = array_filter($dict); $dict = array_filter($dict);
if (empty($dict)) { if (empty($dict)) {
return $script; return $script;
...@@ -218,25 +215,26 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -218,25 +215,26 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
if (empty($value)) { if (empty($value)) {
$value = static::quote(''); $value = static::quote('');
} }
$script[] = static::call('log', static::quote('%s: %o'), static::quote((string) $key), $value); $script[] = static::call('log', static::quote('%s: %o'), static::quote($key), $value);
} }
return $script; return $script;
} }
private static function quote(string $arg): string private static function quote($arg)
{ {
return '"' . addcslashes($arg, "\"\n\\") . '"'; return '"' . addcslashes($arg, "\"\n\\") . '"';
} }
private static function call(...$args): string private static function call()
{ {
$args = func_get_args();
$method = array_shift($args); $method = array_shift($args);
return static::call_array($method, $args); return static::call_array($method, $args);
} }
private static function call_array(string $method, array $args): string private static function call_array($method, array $args)
{ {
return 'c.' . $method . '(' . implode(', ', $args) . ');'; return 'c.' . $method . '(' . implode(', ', $args) . ');';
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -23,36 +23,34 @@ use Monolog\Formatter\FormatterInterface; ...@@ -23,36 +23,34 @@ use Monolog\Formatter\FormatterInterface;
* *
* @author Christophe Coevoet <stof@notk.org> * @author Christophe Coevoet <stof@notk.org>
*/ */
class BufferHandler extends AbstractHandler implements ProcessableHandlerInterface, FormattableHandlerInterface class BufferHandler extends AbstractHandler
{ {
use ProcessableHandlerTrait;
protected $handler; protected $handler;
protected $bufferSize = 0; protected $bufferSize = 0;
protected $bufferLimit; protected $bufferLimit;
protected $flushOnOverflow; protected $flushOnOverflow;
protected $buffer = []; protected $buffer = array();
protected $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 string|int $level The minimum logging level at which this handler will be triggered * @param int $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
* @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, $level = Logger::DEBUG, bool $bubble = true, bool $flushOnOverflow = false) public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->handler = $handler; $this->handler = $handler;
$this->bufferLimit = $bufferLimit; $this->bufferLimit = (int) $bufferLimit;
$this->flushOnOverflow = $flushOnOverflow; $this->flushOnOverflow = $flushOnOverflow;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle(array $record): bool public function handle(array $record)
{ {
if ($record['level'] < $this->level) { if ($record['level'] < $this->level) {
return false; return false;
...@@ -60,7 +58,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -60,7 +58,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
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;
} }
...@@ -74,7 +72,9 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -74,7 +72,9 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
} }
if ($this->processors) { if ($this->processors) {
$record = $this->processRecord($record); foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
} }
$this->buffer[] = $record; $this->buffer[] = $record;
...@@ -83,7 +83,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -83,7 +83,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
return false === $this->bubble; return false === $this->bubble;
} }
public function flush(): void public function flush()
{ {
if ($this->bufferSize === 0) { if ($this->bufferSize === 0) {
return; return;
...@@ -103,20 +103,18 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -103,20 +103,18 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function close(): void public function close()
{ {
$this->flush(); $this->flush();
$this->handler->close();
} }
/** /**
* Clears the buffer without flushing any messages down to the wrapped handler. * Clears the buffer without flushing any messages down to the wrapped handler.
*/ */
public function clear(): void public function clear()
{ {
$this->bufferSize = 0; $this->bufferSize = 0;
$this->buffer = []; $this->buffer = array();
} }
public function reset() public function reset()
...@@ -125,8 +123,6 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -125,8 +123,6 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
parent::reset(); parent::reset();
$this->resetProcessors();
if ($this->handler instanceof ResettableInterface) { if ($this->handler instanceof ResettableInterface) {
$this->handler->reset(); $this->handler->reset();
} }
...@@ -135,7 +131,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -135,7 +131,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter)
{ {
$this->handler->setFormatter($formatter); $this->handler->setFormatter($formatter);
...@@ -145,7 +141,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa ...@@ -145,7 +141,7 @@ class BufferHandler extends AbstractHandler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter()
{ {
return $this->handler->getFormatter(); return $this->handler->getFormatter();
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\ChromePHPFormatter; use Monolog\Formatter\ChromePHPFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Utils; use Monolog\Utils;
...@@ -25,22 +24,20 @@ use Monolog\Utils; ...@@ -25,22 +24,20 @@ use Monolog\Utils;
*/ */
class ChromePHPHandler extends AbstractProcessingHandler class ChromePHPHandler extends AbstractProcessingHandler
{ {
use WebRequestRecognizerTrait;
/** /**
* Version of the extension * Version of the extension
*/ */
protected const VERSION = '4.0'; const VERSION = '4.0';
/** /**
* Header name * Header name
*/ */
protected const HEADER_NAME = 'X-ChromeLogger-Data'; const HEADER_NAME = 'X-ChromeLogger-Data';
/** /**
* Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+)
*/ */
protected const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}';
protected static $initialized = false; protected static $initialized = false;
...@@ -53,19 +50,19 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -53,19 +50,19 @@ class ChromePHPHandler extends AbstractProcessingHandler
*/ */
protected static $overflowed = false; protected static $overflowed = false;
protected static $json = [ protected static $json = array(
'version' => self::VERSION, 'version' => self::VERSION,
'columns' => ['label', 'log', 'backtrace', 'type'], 'columns' => array('label', 'log', 'backtrace', 'type'),
'rows' => [], 'rows' => array(),
]; );
protected static $sendHeaders = true; protected static $sendHeaders = true;
/** /**
* @param string|int $level The minimum logging level at which this handler will be triggered * @param int $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
*/ */
public function __construct($level = Logger::DEBUG, bool $bubble = true) public function __construct($level = Logger::DEBUG, $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
if (!function_exists('json_encode')) { if (!function_exists('json_encode')) {
...@@ -76,13 +73,9 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -76,13 +73,9 @@ class ChromePHPHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records)
{ {
if (!$this->isWebRequest()) { $messages = array();
return;
}
$messages = [];
foreach ($records as $record) { foreach ($records as $record) {
if ($record['level'] < $this->level) { if ($record['level'] < $this->level) {
...@@ -101,7 +94,7 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -101,7 +94,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new ChromePHPFormatter(); return new ChromePHPFormatter();
} }
...@@ -111,13 +104,10 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -111,13 +104,10 @@ class ChromePHPHandler extends AbstractProcessingHandler
* *
* @see sendHeader() * @see sendHeader()
* @see send() * @see send()
* @param array $record
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
if (!$this->isWebRequest()) {
return;
}
self::$json['rows'][] = $record['formatted']; self::$json['rows'][] = $record['formatted'];
$this->send(); $this->send();
...@@ -128,7 +118,7 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -128,7 +118,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
* *
* @see sendHeader() * @see sendHeader()
*/ */
protected function send(): void protected function send()
{ {
if (self::$overflowed || !self::$sendHeaders) { if (self::$overflowed || !self::$sendHeaders) {
return; return;
...@@ -142,7 +132,7 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -142,7 +132,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
return; return;
} }
self::$json['request_uri'] = $_SERVER['REQUEST_URI'] ?? ''; self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
} }
$json = Utils::jsonEncode(self::$json, null, true); $json = Utils::jsonEncode(self::$json, null, true);
...@@ -150,29 +140,32 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -150,29 +140,32 @@ class ChromePHPHandler extends AbstractProcessingHandler
if (strlen($data) > 3 * 1024) { if (strlen($data) > 3 * 1024) {
self::$overflowed = true; self::$overflowed = true;
$record = [ $record = array(
'message' => 'Incomplete logs, chrome header size limit reached', 'message' => 'Incomplete logs, chrome header size limit reached',
'context' => [], 'context' => array(),
'level' => Logger::WARNING, 'level' => Logger::WARNING,
'level_name' => Logger::getLevelName(Logger::WARNING), 'level_name' => Logger::getLevelName(Logger::WARNING),
'channel' => 'monolog', 'channel' => 'monolog',
'datetime' => new \DateTimeImmutable(), 'datetime' => new \DateTime(),
'extra' => [], 'extra' => array(),
]; );
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, null, true); $json = Utils::jsonEncode(self::$json, null, true);
$data = base64_encode(utf8_encode($json)); $data = base64_encode(utf8_encode($json));
} }
if (trim($data) !== '') { if (trim($data) !== '') {
$this->sendHeader(static::HEADER_NAME, $data); $this->sendHeader(self::HEADER_NAME, $data);
} }
} }
/** /**
* Send header string to the client * Send header string to the client
*
* @param string $header
* @param string $content
*/ */
protected function sendHeader(string $header, string $content): void protected function sendHeader($header, $content)
{ {
if (!headers_sent() && self::$sendHeaders) { if (!headers_sent() && self::$sendHeaders) {
header(sprintf('%s: %s', $header, $content)); header(sprintf('%s: %s', $header, $content));
...@@ -181,13 +174,39 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -181,13 +174,39 @@ class ChromePHPHandler extends AbstractProcessingHandler
/** /**
* Verifies if the headers are accepted by the current user agent * Verifies if the headers are accepted by the current user agent
*
* @return bool
*/ */
protected function headersAccepted(): bool protected function headersAccepted()
{ {
if (empty($_SERVER['HTTP_USER_AGENT'])) { if (empty($_SERVER['HTTP_USER_AGENT'])) {
return false; return false;
} }
return preg_match(static::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']) === 1; return preg_match(self::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']);
}
/**
* BC getter for the sendHeaders property that has been made static
*/
public function __get($property)
{
if ('sendHeaders' !== $property) {
throw new \InvalidArgumentException('Undefined property '.$property);
}
return static::$sendHeaders;
}
/**
* BC setter for the sendHeaders property that has been made static
*/
public function __set($property, $value)
{
if ('sendHeaders' !== $property) {
throw new \InvalidArgumentException('Undefined property '.$property);
}
static::$sendHeaders = $value;
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\JsonFormatter; use Monolog\Formatter\JsonFormatter;
use Monolog\Logger; use Monolog\Logger;
...@@ -24,15 +23,15 @@ class CouchDBHandler extends AbstractProcessingHandler ...@@ -24,15 +23,15 @@ class CouchDBHandler extends AbstractProcessingHandler
{ {
private $options; private $options;
public function __construct(array $options = [], $level = Logger::DEBUG, bool $bubble = true) public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true)
{ {
$this->options = array_merge([ $this->options = array_merge(array(
'host' => 'localhost', 'host' => 'localhost',
'port' => 5984, 'port' => 5984,
'dbname' => 'logger', 'dbname' => 'logger',
'username' => null, 'username' => null,
'password' => null, 'password' => null,
], $options); ), $options);
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
} }
...@@ -40,7 +39,7 @@ class CouchDBHandler extends AbstractProcessingHandler ...@@ -40,7 +39,7 @@ class CouchDBHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
$basicAuth = null; $basicAuth = null;
if ($this->options['username']) { if ($this->options['username']) {
...@@ -48,17 +47,17 @@ class CouchDBHandler extends AbstractProcessingHandler ...@@ -48,17 +47,17 @@ class CouchDBHandler extends AbstractProcessingHandler
} }
$url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname'];
$context = stream_context_create([ $context = stream_context_create(array(
'http' => [ 'http' => array(
'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',
], ),
]); ));
if (false === @file_get_contents($url, false, $context)) { if (false === @file_get_contents($url, null, $context)) {
throw new \RuntimeException(sprintf('Could not connect to %s', $url)); throw new \RuntimeException(sprintf('Could not connect to %s', $url));
} }
} }
...@@ -66,7 +65,7 @@ class CouchDBHandler extends AbstractProcessingHandler ...@@ -66,7 +65,7 @@ class CouchDBHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false);
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -27,7 +27,7 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -27,7 +27,7 @@ class CubeHandler extends AbstractProcessingHandler
private $scheme; private $scheme;
private $host; private $host;
private $port; private $port;
private $acceptedSchemes = ['http', 'udp']; private $acceptedSchemes = array('http', 'udp');
/** /**
* Create a Cube handler * Create a Cube handler
...@@ -36,7 +36,7 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -36,7 +36,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, $level = Logger::DEBUG, bool $bubble = true) public function __construct($url, $level = Logger::DEBUG, $bubble = true)
{ {
$urlInfo = parse_url($url); $urlInfo = parse_url($url);
...@@ -47,8 +47,7 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -47,8 +47,7 @@ class CubeHandler extends AbstractProcessingHandler
if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { 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));
);
} }
$this->scheme = $urlInfo['scheme']; $this->scheme = $urlInfo['scheme'];
...@@ -64,7 +63,7 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -64,7 +63,7 @@ class CubeHandler extends AbstractProcessingHandler
* @throws \LogicException when unable to connect to the socket * @throws \LogicException when unable to connect to the socket
* @throws MissingExtensionException when there is no socket extension * @throws MissingExtensionException when there is no socket extension
*/ */
protected function connectUdp(): void protected function connectUdp()
{ {
if (!extension_loaded('sockets')) { if (!extension_loaded('sockets')) {
throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler');
...@@ -81,15 +80,13 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -81,15 +80,13 @@ class CubeHandler extends AbstractProcessingHandler
} }
/** /**
* Establish a connection to an http server * Establish a connection to a http server
* * @throws \LogicException when no curl extension
* @throws \LogicException when unable to connect to the socket
* @throws MissingExtensionException when no curl extension
*/ */
protected function connectHttp(): void protected function connectHttp()
{ {
if (!extension_loaded('curl')) { if (!extension_loaded('curl')) {
throw new MissingExtensionException('The curl extension is required to use http URLs with the CubeHandler'); throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler');
} }
$this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); $this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put');
...@@ -105,11 +102,11 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -105,11 +102,11 @@ class CubeHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
$date = $record['datetime']; $date = $record['datetime'];
$data = ['time' => $date->format('Y-m-d\TH:i:s.uO')]; $data = array('time' => $date->format('Y-m-d\TH:i:s.uO'));
unset($record['datetime']); unset($record['datetime']);
if (isset($record['context']['type'])) { if (isset($record['context']['type'])) {
...@@ -129,7 +126,7 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -129,7 +126,7 @@ class CubeHandler extends AbstractProcessingHandler
} }
} }
private function writeUdp(string $data): void private function writeUdp($data)
{ {
if (!$this->udpConnection) { if (!$this->udpConnection) {
$this->connectUdp(); $this->connectUdp();
...@@ -138,17 +135,17 @@ class CubeHandler extends AbstractProcessingHandler ...@@ -138,17 +135,17 @@ class CubeHandler extends AbstractProcessingHandler
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($data)
{ {
if (!$this->httpConnection) { if (!$this->httpConnection) {
$this->connectHttp(); $this->connectHttp();
} }
curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']');
curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, [ curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json', 'Content-Type: application/json',
'Content-Length: ' . strlen('['.$data.']'), 'Content-Length: ' . strlen('['.$data.']'),
]); ));
Curl\Util::execute($this->httpConnection, 5, false); Curl\Util::execute($this->httpConnection, 5, false);
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,14 +11,9 @@ ...@@ -11,14 +11,9 @@
namespace Monolog\Handler\Curl; namespace Monolog\Handler\Curl;
/** class Util
* This class is marked as internal and it is not under the BC promise of the package.
*
* @internal
*/
final class Util
{ {
private static $retriableErrorCodes = [ private static $retriableErrorCodes = array(
CURLE_COULDNT_RESOLVE_HOST, CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_CONNECT, CURLE_COULDNT_CONNECT,
CURLE_HTTP_NOT_FOUND, CURLE_HTTP_NOT_FOUND,
...@@ -26,21 +21,18 @@ final class Util ...@@ -26,21 +21,18 @@ final class Util
CURLE_OPERATION_TIMEOUTED, CURLE_OPERATION_TIMEOUTED,
CURLE_HTTP_POST_ERROR, CURLE_HTTP_POST_ERROR,
CURLE_SSL_CONNECT_ERROR, CURLE_SSL_CONNECT_ERROR,
]; );
/** /**
* Executes a CURL request with optional retries and exception on failure * Executes a CURL request with optional retries and exception on failure
* *
* @param resource $ch curl handler * @param resource $ch curl handler
* @param int $retries * @throws \RuntimeException
* @param bool $closeAfterDone
* @return bool|string @see curl_exec
*/ */
public static function execute($ch, int $retries = 5, bool $closeAfterDone = true) public static function execute($ch, $retries = 5, $closeAfterDone = true)
{ {
while ($retries--) { while ($retries--) {
$curlResponse = curl_exec($ch); if (curl_exec($ch) === false) {
if ($curlResponse === false) {
$curlErrno = curl_errno($ch); $curlErrno = curl_errno($ch);
if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) {
...@@ -50,7 +42,7 @@ final class Util ...@@ -50,7 +42,7 @@ final class Util
curl_close($ch); curl_close($ch);
} }
throw new \RuntimeException(sprintf('Curl error (code %d): %s', $curlErrno, $curlError)); throw new \RuntimeException(sprintf('Curl error (code %s): %s', $curlErrno, $curlError));
} }
continue; continue;
...@@ -59,10 +51,7 @@ final class Util ...@@ -59,10 +51,7 @@ final class Util
if ($closeAfterDone) { if ($closeAfterDone) {
curl_close($ch); curl_close($ch);
} }
break;
return $curlResponse;
} }
return false;
} }
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -58,11 +58,11 @@ class DeduplicationHandler extends BufferHandler ...@@ -58,11 +58,11 @@ class DeduplicationHandler extends BufferHandler
/** /**
* @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 string|int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes * @param 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
*/ */
public function __construct(HandlerInterface $handler, ?string $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, int $time = 60, bool $bubble = true) public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true)
{ {
parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); parent::__construct($handler, 0, Logger::DEBUG, $bubble, false);
...@@ -71,7 +71,7 @@ class DeduplicationHandler extends BufferHandler ...@@ -71,7 +71,7 @@ class DeduplicationHandler extends BufferHandler
$this->time = $time; $this->time = $time;
} }
public function flush(): void public function flush()
{ {
if ($this->bufferSize === 0) { if ($this->bufferSize === 0) {
return; return;
...@@ -81,6 +81,7 @@ class DeduplicationHandler extends BufferHandler ...@@ -81,6 +81,7 @@ class DeduplicationHandler extends BufferHandler
foreach ($this->buffer as $record) { foreach ($this->buffer as $record) {
if ($record['level'] >= $this->deduplicationLevel) { if ($record['level'] >= $this->deduplicationLevel) {
$passthru = $passthru || !$this->isDuplicate($record); $passthru = $passthru || !$this->isDuplicate($record);
if ($passthru) { if ($passthru) {
$this->appendRecord($record); $this->appendRecord($record);
...@@ -100,7 +101,7 @@ class DeduplicationHandler extends BufferHandler ...@@ -100,7 +101,7 @@ class DeduplicationHandler extends BufferHandler
} }
} }
private function isDuplicate(array $record): bool private function isDuplicate(array $record)
{ {
if (!file_exists($this->deduplicationStore)) { if (!file_exists($this->deduplicationStore)) {
return false; return false;
...@@ -130,26 +131,21 @@ class DeduplicationHandler extends BufferHandler ...@@ -130,26 +131,21 @@ class DeduplicationHandler extends BufferHandler
return false; return false;
} }
private function collectLogs(): void private function collectLogs()
{ {
if (!file_exists($this->deduplicationStore)) { if (!file_exists($this->deduplicationStore)) {
return; return false;
} }
$handle = fopen($this->deduplicationStore, 'rw+'); $handle = fopen($this->deduplicationStore, 'rw+');
if (!$handle) {
throw new \RuntimeException('Failed to open file for reading and writing: ' . $this->deduplicationStore);
}
flock($handle, LOCK_EX); flock($handle, LOCK_EX);
$validLogs = []; $validLogs = array();
$timestampValidity = time() - $this->time; $timestampValidity = time() - $this->time;
while (!feof($handle)) { while (!feof($handle)) {
$log = fgets($handle); $log = fgets($handle);
if ($log && substr($log, 0, 10) >= $timestampValidity) { if (substr($log, 0, 10) >= $timestampValidity) {
$validLogs[] = $log; $validLogs[] = $log;
} }
} }
...@@ -166,7 +162,7 @@ class DeduplicationHandler extends BufferHandler ...@@ -166,7 +162,7 @@ class DeduplicationHandler extends BufferHandler
$this->gc = false; $this->gc = false;
} }
private function appendRecord(array $record): void private function appendRecord(array $record)
{ {
file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . 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);
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -13,7 +13,6 @@ namespace Monolog\Handler; ...@@ -13,7 +13,6 @@ namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Formatter\NormalizerFormatter; use Monolog\Formatter\NormalizerFormatter;
use Monolog\Formatter\FormatterInterface;
use Doctrine\CouchDB\CouchDBClient; use Doctrine\CouchDB\CouchDBClient;
/** /**
...@@ -25,7 +24,7 @@ class DoctrineCouchDBHandler extends AbstractProcessingHandler ...@@ -25,7 +24,7 @@ class DoctrineCouchDBHandler extends AbstractProcessingHandler
{ {
private $client; private $client;
public function __construct(CouchDBClient $client, $level = Logger::DEBUG, bool $bubble = true) public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = true)
{ {
$this->client = $client; $this->client = $client;
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
...@@ -34,12 +33,12 @@ class DoctrineCouchDBHandler extends AbstractProcessingHandler ...@@ -34,12 +33,12 @@ class DoctrineCouchDBHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
$this->client->postDocument($record['formatted']); $this->client->postDocument($record['formatted']);
} }
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new NormalizerFormatter; return new NormalizerFormatter;
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -13,7 +13,6 @@ namespace Monolog\Handler; ...@@ -13,7 +13,6 @@ namespace Monolog\Handler;
use Aws\Sdk; use Aws\Sdk;
use Aws\DynamoDb\DynamoDbClient; use Aws\DynamoDb\DynamoDbClient;
use Monolog\Formatter\FormatterInterface;
use Aws\DynamoDb\Marshaler; use Aws\DynamoDb\Marshaler;
use Monolog\Formatter\ScalarFormatter; use Monolog\Formatter\ScalarFormatter;
use Monolog\Logger; use Monolog\Logger;
...@@ -26,7 +25,7 @@ use Monolog\Logger; ...@@ -26,7 +25,7 @@ use Monolog\Logger;
*/ */
class DynamoDbHandler extends AbstractProcessingHandler class DynamoDbHandler extends AbstractProcessingHandler
{ {
public const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; const DATE_FORMAT = 'Y-m-d\TH:i:s.uO';
/** /**
* @var DynamoDbClient * @var DynamoDbClient
...@@ -49,9 +48,12 @@ class DynamoDbHandler extends AbstractProcessingHandler ...@@ -49,9 +48,12 @@ class DynamoDbHandler extends AbstractProcessingHandler
protected $marshaler; protected $marshaler;
/** /**
* @param int|string $level * @param DynamoDbClient $client
* @param string $table
* @param int $level
* @param bool $bubble
*/ */
public function __construct(DynamoDbClient $client, string $table, $level = Logger::DEBUG, bool $bubble = true) public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = true)
{ {
if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) { if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) {
$this->version = 3; $this->version = 3;
...@@ -69,7 +71,7 @@ class DynamoDbHandler extends AbstractProcessingHandler ...@@ -69,7 +71,7 @@ class DynamoDbHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
$filtered = $this->filterEmptyFields($record['formatted']); $filtered = $this->filterEmptyFields($record['formatted']);
if ($this->version === 3) { if ($this->version === 3) {
...@@ -78,13 +80,17 @@ class DynamoDbHandler extends AbstractProcessingHandler ...@@ -78,13 +80,17 @@ class DynamoDbHandler extends AbstractProcessingHandler
$formatted = $this->client->formatAttributes($filtered); $formatted = $this->client->formatAttributes($filtered);
} }
$this->client->putItem([ $this->client->putItem(array(
'TableName' => $this->table, 'TableName' => $this->table,
'Item' => $formatted, 'Item' => $formatted,
]); ));
} }
protected function filterEmptyFields(array $record): array /**
* @param array $record
* @return array
*/
protected function filterEmptyFields(array $record)
{ {
return array_filter($record, function ($value) { return array_filter($record, function ($value) {
return !empty($value) || false === $value || 0 === $value; return !empty($value) || false === $value || 0 === $value;
...@@ -94,7 +100,7 @@ class DynamoDbHandler extends AbstractProcessingHandler ...@@ -94,7 +100,7 @@ class DynamoDbHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new ScalarFormatter(self::DATE_FORMAT); return new ScalarFormatter(self::DATE_FORMAT);
} }
......
<?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\Handler;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticaFormatter;
use Monolog\Logger;
use Elastica\Client;
use Elastica\Exception\ExceptionInterface;
/**
* Elastic Search handler
*
* Usage example:
*
* $client = new \Elastica\Client();
* $options = array(
* 'index' => 'elastic_index_name',
* 'type' => 'elastic_doc_type',
* );
* $handler = new ElasticaHandler($client, $options);
* $log = new Logger('application');
* $log->pushHandler($handler);
*
* @author Jelle Vink <jelle.vink@gmail.com>
*/
class ElasticaHandler extends AbstractProcessingHandler
{
/**
* @var Client
*/
protected $client;
/**
* @var array Handler config options
*/
protected $options = [];
/**
* @param Client $client Elastica Client object
* @param array $options Handler configuration
* @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
*/
public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true)
{
parent::__construct($level, $bubble);
$this->client = $client;
$this->options = array_merge(
[
'index' => 'monolog', // Elastic index name
'type' => 'record', // Elastic document type
'ignore_error' => false, // Suppress Elastica exceptions
],
$options
);
}
/**
* {@inheritDoc}
*/
protected function write(array $record): void
{
$this->bulkSend([$record['formatted']]);
}
/**
* {@inheritdoc}
*/
public function setFormatter(FormatterInterface $formatter): HandlerInterface
{
if ($formatter instanceof ElasticaFormatter) {
return parent::setFormatter($formatter);
}
throw new \InvalidArgumentException('ElasticaHandler is only compatible with ElasticaFormatter');
}
public function getOptions(): array
{
return $this->options;
}
/**
* {@inheritDoc}
*/
protected function getDefaultFormatter(): FormatterInterface
{
return new ElasticaFormatter($this->options['index'], $this->options['type']);
}
/**
* {@inheritdoc}
*/
public function handleBatch(array $records): void
{
$documents = $this->getFormatter()->formatBatch($records);
$this->bulkSend($documents);
}
/**
* Use Elasticsearch bulk API to send list of documents
* @throws \RuntimeException
*/
protected function bulkSend(array $documents): void
{
try {
$this->client->addDocuments($documents);
} catch (ExceptionInterface $e) {
if (!$this->options['ignore_error']) {
throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e);
}
}
}
}
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -11,37 +11,29 @@ ...@@ -11,37 +11,29 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Throwable;
use RuntimeException;
use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\ElasticsearchFormatter; use Monolog\Formatter\ElasticaFormatter;
use InvalidArgumentException; use Monolog\Logger;
use Elasticsearch\Common\Exceptions\RuntimeException as ElasticsearchRuntimeException; use Elastica\Client;
use Elasticsearch\Client; use Elastica\Exception\ExceptionInterface;
/** /**
* Elasticsearch handler * Elastic Search handler
*
* @link https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html
*
* Simple usage example:
* *
* $client = \Elasticsearch\ClientBuilder::create() * Usage example:
* ->setHosts($hosts)
* ->build();
* *
* $client = new \Elastica\Client();
* $options = array( * $options = array(
* 'index' => 'elastic_index_name', * 'index' => 'elastic_index_name',
* 'type' => 'elastic_doc_type', * 'type' => 'elastic_doc_type',
* ); * );
* $handler = new ElasticsearchHandler($client, $options); * $handler = new ElasticSearchHandler($client, $options);
* $log = new Logger('application'); * $log = new Logger('application');
* $log->pushHandler($handler); * $log->pushHandler($handler);
* *
* @author Avtandil Kikabidze <akalongman@gmail.com> * @author Jelle Vink <jelle.vink@gmail.com>
*/ */
class ElasticsearchHandler extends AbstractProcessingHandler class ElasticSearchHandler extends AbstractProcessingHandler
{ {
/** /**
* @var Client * @var Client
...@@ -51,24 +43,24 @@ class ElasticsearchHandler extends AbstractProcessingHandler ...@@ -51,24 +43,24 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/** /**
* @var array Handler config options * @var array Handler config options
*/ */
protected $options = []; protected $options = array();
/** /**
* @param Client $client Elasticsearch Client object * @param Client $client Elastica Client object
* @param array $options Handler configuration * @param array $options Handler configuration
* @param string|int $level The minimum logging level at which this handler will be triggered * @param int $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
*/ */
public function __construct(Client $client, array $options = [], $level = Logger::DEBUG, bool $bubble = true) public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
$this->client = $client; $this->client = $client;
$this->options = array_merge( $this->options = array_merge(
[ array(
'index' => 'monolog', // Elastic index name 'index' => 'monolog', // Elastic index name
'type' => '_doc', // Elastic document type 'type' => 'record', // Elastic document type
'ignore_error' => false, // Suppress Elasticsearch exceptions 'ignore_error' => false, // Suppress Elastica exceptions
], ),
$options $options
); );
} }
...@@ -76,29 +68,27 @@ class ElasticsearchHandler extends AbstractProcessingHandler ...@@ -76,29 +68,27 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
$this->bulkSend([$record['formatted']]); $this->bulkSend(array($record['formatted']));
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter)
{ {
if ($formatter instanceof ElasticsearchFormatter) { if ($formatter instanceof ElasticaFormatter) {
return parent::setFormatter($formatter); return parent::setFormatter($formatter);
} }
throw new \InvalidArgumentException('ElasticSearchHandler is only compatible with ElasticaFormatter');
throw new InvalidArgumentException('ElasticsearchHandler is only compatible with ElasticsearchFormatter');
} }
/** /**
* Getter options * Getter options
*
* @return array * @return array
*/ */
public function getOptions(): array public function getOptions()
{ {
return $this->options; return $this->options;
} }
...@@ -106,15 +96,15 @@ class ElasticsearchHandler extends AbstractProcessingHandler ...@@ -106,15 +96,15 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new ElasticsearchFormatter($this->options['index'], $this->options['type']); return new ElasticaFormatter($this->options['index'], $this->options['type']);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records)
{ {
$documents = $this->getFormatter()->formatBatch($records); $documents = $this->getFormatter()->formatBatch($records);
$this->bulkSend($documents); $this->bulkSend($documents);
...@@ -122,68 +112,17 @@ class ElasticsearchHandler extends AbstractProcessingHandler ...@@ -122,68 +112,17 @@ class ElasticsearchHandler extends AbstractProcessingHandler
/** /**
* Use Elasticsearch bulk API to send list of documents * Use Elasticsearch bulk API to send list of documents
* * @param array $documents
* @param array $records
* @throws \RuntimeException * @throws \RuntimeException
*/ */
protected function bulkSend(array $records): void protected function bulkSend(array $documents)
{ {
try { try {
$params = [ $this->client->addDocuments($documents);
'body' => [], } catch (ExceptionInterface $e) {
]; if (!$this->options['ignore_error']) {
throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e);
foreach ($records as $record) {
$params['body'][] = [
'index' => [
'_index' => $record['_index'],
'_type' => $record['_type'],
],
];
unset($record['_index'], $record['_type']);
$params['body'][] = $record;
}
$responses = $this->client->bulk($params);
if ($responses['errors'] === true) {
throw $this->createExceptionFromResponses($responses);
}
} catch (Throwable $e) {
if (! $this->options['ignore_error']) {
throw new RuntimeException('Error sending messages to Elasticsearch', 0, $e);
} }
} }
} }
/**
* Creates elasticsearch exception from responses array
*
* Only the first error is converted into an exception.
*
* @param array $responses returned by $this->client->bulk()
*/
protected function createExceptionFromResponses(array $responses): ElasticsearchRuntimeException
{
foreach ($responses['items'] ?? [] as $item) {
if (isset($item['index']['error'])) {
return $this->createExceptionFromError($item['index']['error']);
}
}
return new ElasticsearchRuntimeException('Elasticsearch failed to index one or more records.');
}
/**
* Creates elasticsearch exception from error array
*
* @param array $error
*/
protected function createExceptionFromError(array $error): ElasticsearchRuntimeException
{
$previous = isset($error['caused_by']) ? $this->createExceptionFromError($error['caused_by']) : null;
return new ElasticsearchRuntimeException($error['type'] . ': ' . $error['reason'], 0, $previous);
}
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Logger; use Monolog\Logger;
/** /**
...@@ -22,25 +21,24 @@ use Monolog\Logger; ...@@ -22,25 +21,24 @@ use Monolog\Logger;
*/ */
class ErrorLogHandler extends AbstractProcessingHandler class ErrorLogHandler extends AbstractProcessingHandler
{ {
public const OPERATING_SYSTEM = 0; const OPERATING_SYSTEM = 0;
public const SAPI = 4; const SAPI = 4;
protected $messageType; protected $messageType;
protected $expandNewlines; protected $expandNewlines;
/** /**
* @param int $messageType Says where the error should go. * @param int $messageType Says where the error should go.
* @param int|string $level The minimum logging level at which this handler will be triggered * @param int $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
* @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, $level = Logger::DEBUG, bool $bubble = true, bool $expandNewlines = false) public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
if (false === in_array($messageType, self::getAvailableTypes(), true)) { if (false === in_array($messageType, self::getAvailableTypes())) {
$message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true));
throw new \InvalidArgumentException($message); throw new \InvalidArgumentException($message);
} }
...@@ -51,18 +49,18 @@ class ErrorLogHandler extends AbstractProcessingHandler ...@@ -51,18 +49,18 @@ class ErrorLogHandler extends AbstractProcessingHandler
/** /**
* @return array With all available types * @return array With all available types
*/ */
public static function getAvailableTypes(): array public static function getAvailableTypes()
{ {
return [ return array(
self::OPERATING_SYSTEM, self::OPERATING_SYSTEM,
self::SAPI, self::SAPI,
]; );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%');
} }
...@@ -70,17 +68,15 @@ class ErrorLogHandler extends AbstractProcessingHandler ...@@ -70,17 +68,15 @@ class ErrorLogHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
if (!$this->expandNewlines) { if ($this->expandNewlines) {
$lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
foreach ($lines as $line) {
error_log($line, $this->messageType);
}
} else {
error_log((string) $record['formatted'], $this->messageType); error_log((string) $record['formatted'], $this->messageType);
return;
}
$lines = preg_split('{[\r\n]+}', (string) $record['formatted']);
foreach ($lines as $line) {
error_log($line, $this->messageType);
} }
} }
} }
<?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\Handler;
use Throwable;
class FallbackGroupHandler extends GroupHandler
{
/**
* {@inheritdoc}
*/
public function handle(array $record): bool
{
if ($this->processors) {
$record = $this->processRecord($record);
}
foreach ($this->handlers as $handler) {
try {
$handler->handle($record);
break;
} catch (Throwable $e) {
// What throwable?
}
}
return false === $this->bubble;
}
/**
* {@inheritdoc}
*/
public function handleBatch(array $records): void
{
if ($this->processors) {
$processed = [];
foreach ($records as $record) {
$processed[] = $this->processRecord($record);
}
$records = $processed;
}
foreach ($this->handlers as $handler) {
try {
$handler->handleBatch($records);
break;
} catch (Throwable $e) {
// What throwable?
}
}
}
}
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
/** /**
...@@ -23,10 +22,8 @@ use Monolog\Formatter\FormatterInterface; ...@@ -23,10 +22,8 @@ use Monolog\Formatter\FormatterInterface;
* @author Hennadiy Verkh * @author Hennadiy Verkh
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class FilterHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface class FilterHandler extends AbstractHandler
{ {
use ProcessableHandlerTrait;
/** /**
* Handler or factory callable($record, $this) * Handler or factory callable($record, $this)
* *
...@@ -51,10 +48,10 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -51,10 +48,10 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/** /**
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler).
* @param int|array $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 $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array * @param int $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
*/ */
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, bool $bubble = true) public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true)
{ {
$this->handler = $handler; $this->handler = $handler;
$this->bubble = $bubble; $this->bubble = $bubble;
...@@ -65,7 +62,10 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -65,7 +62,10 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
} }
} }
public function getAcceptedLevels(): array /**
* @return array
*/
public function getAcceptedLevels()
{ {
return array_flip($this->acceptedLevels); return array_flip($this->acceptedLevels);
} }
...@@ -74,7 +74,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -74,7 +74,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
* @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|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided
* @param int|string $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
*/ */
public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY): self public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY)
{ {
if (is_array($minLevelOrList)) { if (is_array($minLevelOrList)) {
$acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList);
...@@ -86,14 +86,12 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -86,14 +86,12 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
})); }));
} }
$this->acceptedLevels = array_flip($acceptedLevels); $this->acceptedLevels = array_flip($acceptedLevels);
return $this;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function isHandling(array $record): bool public function isHandling(array $record)
{ {
return isset($this->acceptedLevels[$record['level']]); return isset($this->acceptedLevels[$record['level']]);
} }
...@@ -101,14 +99,16 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -101,14 +99,16 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle(array $record): bool public function handle(array $record)
{ {
if (!$this->isHandling($record)) { if (!$this->isHandling($record)) {
return false; return false;
} }
if ($this->processors) { if ($this->processors) {
$record = $this->processRecord($record); foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
} }
$this->getHandler($record)->handle($record); $this->getHandler($record)->handle($record);
...@@ -119,9 +119,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -119,9 +119,9 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handleBatch(array $records): void public function handleBatch(array $records)
{ {
$filtered = []; $filtered = array();
foreach ($records as $record) { foreach ($records as $record) {
if ($this->isHandling($record)) { if ($this->isHandling($record)) {
$filtered[] = $record; $filtered[] = $record;
...@@ -155,7 +155,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -155,7 +155,7 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter)
{ {
$this->getHandler()->setFormatter($formatter); $this->getHandler()->setFormatter($formatter);
...@@ -165,13 +165,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese ...@@ -165,13 +165,8 @@ class FilterHandler extends Handler implements ProcessableHandlerInterface, Rese
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter()
{ {
return $this->getHandler()->getFormatter(); return $this->getHandler()->getFormatter();
} }
public function reset()
{
$this->resetProcessors();
}
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -20,6 +20,9 @@ interface ActivationStrategyInterface ...@@ -20,6 +20,9 @@ interface ActivationStrategyInterface
{ {
/** /**
* Returns whether the given record activates the handler. * Returns whether the given record activates the handler.
*
* @param array $record
* @return bool
*/ */
public function isHandlerActivated(array $record): bool; public function isHandlerActivated(array $record);
} }
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -35,27 +35,20 @@ use Monolog\Logger; ...@@ -35,27 +35,20 @@ use Monolog\Logger;
*/ */
class ChannelLevelActivationStrategy implements ActivationStrategyInterface class ChannelLevelActivationStrategy implements ActivationStrategyInterface
{ {
/**
* @var int
*/
private $defaultActionLevel; private $defaultActionLevel;
/**
* @var array
*/
private $channelToActionLevel; private $channelToActionLevel;
/** /**
* @param int|string $defaultActionLevel The default action level to be used if the record's category doesn't match any * @param int $defaultActionLevel The default action level to be used if the record's category doesn't match any
* @param array $channelToActionLevel An array that maps channel names to action levels. * @param array $channelToActionLevel An array that maps channel names to action levels.
*/ */
public function __construct($defaultActionLevel, array $channelToActionLevel = []) public function __construct($defaultActionLevel, $channelToActionLevel = array())
{ {
$this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel);
$this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel);
} }
public function isHandlerActivated(array $record): bool public function isHandlerActivated(array $record)
{ {
if (isset($this->channelToActionLevel[$record['channel']])) { if (isset($this->channelToActionLevel[$record['channel']])) {
return $record['level'] >= $this->channelToActionLevel[$record['channel']]; return $record['level'] >= $this->channelToActionLevel[$record['channel']];
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -20,20 +20,14 @@ use Monolog\Logger; ...@@ -20,20 +20,14 @@ use Monolog\Logger;
*/ */
class ErrorLevelActivationStrategy implements ActivationStrategyInterface class ErrorLevelActivationStrategy implements ActivationStrategyInterface
{ {
/**
* @var int
*/
private $actionLevel; private $actionLevel;
/**
* @param int|string $actionLevel Level or name or value
*/
public function __construct($actionLevel) public function __construct($actionLevel)
{ {
$this->actionLevel = Logger::toMonologLevel($actionLevel); $this->actionLevel = Logger::toMonologLevel($actionLevel);
} }
public function isHandlerActivated(array $record): bool public function isHandlerActivated(array $record)
{ {
return $record['level'] >= $this->actionLevel; return $record['level'] >= $this->actionLevel;
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -24,37 +24,30 @@ use Monolog\Formatter\FormatterInterface; ...@@ -24,37 +24,30 @@ use Monolog\Formatter\FormatterInterface;
* Only requests which actually trigger an error (or whatever your actionLevel is) will be * Only requests which actually trigger an error (or whatever your actionLevel is) will be
* in the logs, but they will contain all records, not only those above the level threshold. * in the logs, but they will contain all records, not only those above the level threshold.
* *
* You can then have a passthruLevel as well which means that at the end of the request,
* even if it did not get activated, it will still send through log records of e.g. at least a
* warning level.
*
* You can find the various activation strategies in the * You can find the various activation strategies in the
* Monolog\Handler\FingersCrossed\ namespace. * Monolog\Handler\FingersCrossed\ namespace.
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class FingersCrossedHandler extends Handler implements ProcessableHandlerInterface, ResettableInterface, FormattableHandlerInterface class FingersCrossedHandler extends AbstractHandler
{ {
use ProcessableHandlerTrait;
protected $handler; protected $handler;
protected $activationStrategy; protected $activationStrategy;
protected $buffering = true; protected $buffering = true;
protected $bufferSize; protected $bufferSize;
protected $buffer = []; protected $buffer = array();
protected $stopBuffering; protected $stopBuffering;
protected $passthruLevel; protected $passthruLevel;
protected $bubble;
/** /**
* @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler).
* @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|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action
* @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 $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
*/ */
public function __construct($handler, $activationStrategy = null, int $bufferSize = 0, bool $bubble = true, bool $stopBuffering = true, $passthruLevel = null) public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null)
{ {
if (null === $activationStrategy) { if (null === $activationStrategy) {
$activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING);
...@@ -83,7 +76,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -83,7 +76,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function isHandling(array $record): bool public function isHandling(array $record)
{ {
return true; return true;
} }
...@@ -91,23 +84,24 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -91,23 +84,24 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
/** /**
* Manually activate this logger regardless of the activation strategy * Manually activate this logger regardless of the activation strategy
*/ */
public function activate(): void public function activate()
{ {
if ($this->stopBuffering) { if ($this->stopBuffering) {
$this->buffering = false; $this->buffering = false;
} }
$this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer); $this->getHandler(end($this->buffer) ?: null)->handleBatch($this->buffer);
$this->buffer = []; $this->buffer = array();
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle(array $record): bool public function handle(array $record)
{ {
if ($this->processors) { if ($this->processors) {
$record = $this->processRecord($record); foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
} }
if ($this->buffering) { if ($this->buffering) {
...@@ -128,18 +122,16 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -128,18 +122,16 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function close(): void public function close()
{ {
$this->flushBuffer(); $this->flushBuffer();
$this->handler->close();
} }
public function reset() public function reset()
{ {
$this->flushBuffer(); $this->flushBuffer();
$this->resetProcessors(); parent::reset();
if ($this->getHandler() instanceof ResettableInterface) { if ($this->getHandler() instanceof ResettableInterface) {
$this->getHandler()->reset(); $this->getHandler()->reset();
...@@ -151,16 +143,16 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -151,16 +143,16 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
* *
* It also resets the handler to its initial buffering state. * It also resets the handler to its initial buffering state.
*/ */
public function clear(): void public function clear()
{ {
$this->buffer = []; $this->buffer = array();
$this->reset(); $this->reset();
} }
/** /**
* Resets the state of the handler. Stops forwarding records to the wrapped handler. * Resets the state of the handler. Stops forwarding records to the wrapped handler.
*/ */
private function flushBuffer(): void private function flushBuffer()
{ {
if (null !== $this->passthruLevel) { if (null !== $this->passthruLevel) {
$level = $this->passthruLevel; $level = $this->passthruLevel;
...@@ -172,7 +164,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -172,7 +164,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
} }
} }
$this->buffer = []; $this->buffer = array();
$this->buffering = true; $this->buffering = true;
} }
...@@ -198,7 +190,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -198,7 +190,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setFormatter(FormatterInterface $formatter): HandlerInterface public function setFormatter(FormatterInterface $formatter)
{ {
$this->getHandler()->setFormatter($formatter); $this->getHandler()->setFormatter($formatter);
...@@ -208,7 +200,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa ...@@ -208,7 +200,7 @@ class FingersCrossedHandler extends Handler implements ProcessableHandlerInterfa
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getFormatter(): FormatterInterface public function getFormatter()
{ {
return $this->getHandler()->getFormatter(); return $this->getHandler()->getFormatter();
} }
......
<?php declare(strict_types=1); <?php
/* /*
* This file is part of the Monolog package. * This file is part of the Monolog package.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\WildfireFormatter; use Monolog\Formatter\WildfireFormatter;
use Monolog\Formatter\FormatterInterface;
/** /**
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol.
...@@ -21,27 +20,25 @@ use Monolog\Formatter\FormatterInterface; ...@@ -21,27 +20,25 @@ use Monolog\Formatter\FormatterInterface;
*/ */
class FirePHPHandler extends AbstractProcessingHandler class FirePHPHandler extends AbstractProcessingHandler
{ {
use WebRequestRecognizerTrait;
/** /**
* WildFire JSON header message format * WildFire JSON header message format
*/ */
protected const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2';
/** /**
* FirePHP structure for parsing messages & their presentation * FirePHP structure for parsing messages & their presentation
*/ */
protected const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1';
/** /**
* Must reference a "known" plugin, otherwise headers won't display in FirePHP * Must reference a "known" plugin, otherwise headers won't display in FirePHP
*/ */
protected const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3';
/** /**
* Header prefix for Wildfire to recognize & parse headers * Header prefix for Wildfire to recognize & parse headers
*/ */
protected const HEADER_PREFIX = 'X-Wf'; const HEADER_PREFIX = 'X-Wf';
/** /**
* Whether or not Wildfire vendor-specific headers have been generated & sent yet * Whether or not Wildfire vendor-specific headers have been generated & sent yet
...@@ -63,24 +60,26 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -63,24 +60,26 @@ class FirePHPHandler extends AbstractProcessingHandler
* @param string $message Log message * @param string $message Log message
* @return array Complete header string ready for the client as key and message as value * @return array Complete header string ready for the client as key and message as value
*/ */
protected function createHeader(array $meta, string $message): array protected function createHeader(array $meta, $message)
{ {
$header = sprintf('%s-%s', static::HEADER_PREFIX, join('-', $meta)); $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta));
return [$header => $message]; return array($header => $message);
} }
/** /**
* Creates message header from record * Creates message header from record
* *
* @see createHeader() * @see createHeader()
* @param array $record
* @return string
*/ */
protected function createRecordHeader(array $record): array protected function createRecordHeader(array $record)
{ {
// 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++], array(1, 1, 1, self::$messageIndex++),
$record['formatted'] $record['formatted']
); );
} }
...@@ -88,7 +87,7 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -88,7 +87,7 @@ class FirePHPHandler extends AbstractProcessingHandler
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
protected function getDefaultFormatter(): FormatterInterface protected function getDefaultFormatter()
{ {
return new WildfireFormatter(); return new WildfireFormatter();
} }
...@@ -98,21 +97,25 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -98,21 +97,25 @@ class FirePHPHandler extends AbstractProcessingHandler
* *
* @see createHeader() * @see createHeader()
* @see sendHeader() * @see sendHeader()
* @return array
*/ */
protected function getInitHeaders(): array protected function getInitHeaders()
{ {
// Initial payload consists of required headers for Wildfire // Initial payload consists of required headers for Wildfire
return array_merge( return array_merge(
$this->createHeader(['Protocol', 1], static::PROTOCOL_URI), $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI),
$this->createHeader([1, 'Structure', 1], static::STRUCTURE_URI), $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI),
$this->createHeader([1, 'Plugin', 1], static::PLUGIN_URI) $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI)
); );
} }
/** /**
* Send header string to the client * Send header string to the client
*
* @param string $header
* @param string $content
*/ */
protected function sendHeader(string $header, string $content): void protected function sendHeader($header, $content)
{ {
if (!headers_sent() && self::$sendHeaders) { if (!headers_sent() && self::$sendHeaders) {
header(sprintf('%s: %s', $header, $content)); header(sprintf('%s: %s', $header, $content));
...@@ -126,9 +129,9 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -126,9 +129,9 @@ class FirePHPHandler extends AbstractProcessingHandler
* @see sendInitHeaders() * @see sendInitHeaders()
* @param array $record * @param array $record
*/ */
protected function write(array $record): void protected function write(array $record)
{ {
if (!self::$sendHeaders || !$this->isWebRequest()) { if (!self::$sendHeaders) {
return; return;
} }
...@@ -154,8 +157,10 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -154,8 +157,10 @@ class FirePHPHandler extends AbstractProcessingHandler
/** /**
* Verifies if the headers are accepted by the current user agent * Verifies if the headers are accepted by the current user agent
*
* @return bool
*/ */
protected function headersAccepted(): bool protected function headersAccepted()
{ {
if (!empty($_SERVER['HTTP_USER_AGENT']) && 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;
...@@ -163,4 +168,28 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -163,4 +168,28 @@ class FirePHPHandler extends AbstractProcessingHandler
return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); return isset($_SERVER['HTTP_X_FIREPHP_VERSION']);
} }
/**
* BC getter for the sendHeaders property that has been made static
*/
public function __get($property)
{
if ('sendHeaders' !== $property) {
throw new \InvalidArgumentException('Undefined property '.$property);
}
return static::$sendHeaders;
}
/**
* BC setter for the sendHeaders property that has been made static
*/
public function __set($property, $value)
{
if ('sendHeaders' !== $property) {
throw new \InvalidArgumentException('Undefined property '.$property);
}
static::$sendHeaders = $value;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment