/vendor/monolog/monolog/CHANGELOG.md |
@@ -0,0 +1,342 @@ |
### 1.23.0 (2017-06-19) |
|
* Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument |
* Fixed GelfHandler truncation to be per field and not per message |
* Fixed compatibility issue with PHP <5.3.6 |
* Fixed support for headless Chrome in ChromePHPHandler |
* Fixed support for latest Aws SDK in DynamoDbHandler |
* Fixed support for SwiftMailer 6.0+ in SwiftMailerHandler |
|
### 1.22.1 (2017-03-13) |
|
* Fixed lots of minor issues in the new Slack integrations |
* Fixed support for allowInlineLineBreaks in LineFormatter when formatting exception backtraces |
|
### 1.22.0 (2016-11-26) |
|
* Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily |
* Added MercurialProcessor to add mercurial revision and branch names to log records |
* Added support for AWS SDK v3 in DynamoDbHandler |
* 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 not flushing records automatically, calling close() explicitly is not necessary anymore |
* Fixed SyslogUdpHandler to avoid sending empty frames |
* Fixed a few PHP 7.0 and 7.1 compatibility issues |
|
### 1.21.0 (2016-07-29) |
|
* 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 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 $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level |
|
### 1.20.0 (2016-07-02) |
|
* Added FingersCrossedHandler::activate() to manually trigger the handler regardless of the activation policy |
* Added StreamHandler::getUrl to retrieve the stream's URL |
* Added ability to override addRow/addTitle in HtmlFormatter |
* Added the $context to context information when the ErrorHandler handles a regular php error |
* Deprecated RotatingFileHandler::setFilenameFormat to only support 3 formats: Y, Y-m and Y-m-d |
* Fixed WhatFailureGroupHandler to work with PHP7 throwables |
* Fixed a few minor bugs |
|
### 1.19.0 (2016-04-12) |
|
* Break: StreamHandler will not close streams automatically that it does not own. If you pass in a stream (not a path/url), then it will not close it for you. You can retrieve those using getStream() if needed |
* Added DeduplicationHandler to remove duplicate records from notifications across multiple requests, useful for email or other notifications on errors |
* Added ability to use `%message%` and other LineFormatter replacements in the subject line of emails sent with NativeMailHandler and SwiftMailerHandler |
* Fixed HipChatHandler handling of long messages |
|
### 1.18.2 (2016-04-02) |
|
* Fixed ElasticaFormatter to use more precise dates |
* Fixed GelfMessageFormatter sending too long messages |
|
### 1.18.1 (2016-03-13) |
|
* Fixed SlackHandler bug where slack dropped messages randomly |
* Fixed RedisHandler issue when using with the PHPRedis extension |
* Fixed AmqpHandler content-type being incorrectly set when using with the AMQP extension |
* Fixed BrowserConsoleHandler regression |
|
### 1.18.0 (2016-03-01) |
|
* Added optional reduction of timestamp precision via `Logger->useMicrosecondTimestamps(false)`, disabling it gets you a bit of performance boost but reduces the precision to the second instead of microsecond |
* Added possibility to skip some extra stack frames in IntrospectionProcessor if you have some library wrapping Monolog that is always adding frames |
* Added `Logger->withName` to clone a logger (keeping all handlers) with a new name |
* Added FluentdFormatter for the Fluentd unix socket protocol |
* Added HandlerWrapper base class to ease the creation of handler wrappers, just extend it and override as needed |
* Added support for replacing context sub-keys using `%context.*%` in LineFormatter |
* Added support for `payload` context value in RollbarHandler |
* Added setRelease to RavenHandler to describe the application version, sent with every log |
* Added support for `fingerprint` context value in RavenHandler |
* Fixed JSON encoding errors that would gobble up the whole log record, we now handle those more gracefully by dropping chars as needed |
* Fixed write timeouts in SocketHandler and derivatives, set to 10sec by default, lower it with `setWritingTimeout()` |
* Fixed PHP7 compatibility with regard to Exception/Throwable handling in a few places |
|
### 1.17.2 (2015-10-14) |
|
* Fixed ErrorHandler compatibility with non-Monolog PSR-3 loggers |
* Fixed SlackHandler handling to use slack functionalities better |
* Fixed SwiftMailerHandler bug when sending multiple emails they all had the same id |
* Fixed 5.3 compatibility regression |
|
### 1.17.1 (2015-08-31) |
|
* Fixed RollbarHandler triggering PHP notices |
|
### 1.17.0 (2015-08-30) |
|
* Added support for `checksum` and `release` context/extra values in RavenHandler |
* Added better support for exceptions in RollbarHandler |
* Added UidProcessor::getUid |
* Added support for showing the resource type in NormalizedFormatter |
* Fixed IntrospectionProcessor triggering PHP notices |
|
### 1.16.0 (2015-08-09) |
|
* Added IFTTTHandler to notify ifttt.com triggers |
* Added Logger::setHandlers() to allow setting/replacing all handlers |
* Added $capSize in RedisHandler to cap the log size |
* Fixed StreamHandler creation of directory to only trigger when the first log write happens |
* Fixed bug in the handling of curl failures |
* Fixed duplicate logging of fatal errors when both error and fatal error handlers are registered in monolog's ErrorHandler |
* Fixed missing fatal errors records with handlers that need to be closed to flush log records |
* Fixed TagProcessor::addTags support for associative arrays |
|
### 1.15.0 (2015-07-12) |
|
* Added addTags and setTags methods to change a TagProcessor |
* Added automatic creation of directories if they are missing for a StreamHandler to open a log file |
* Added retry functionality to Loggly, Cube and Mandrill handlers so they retry up to 5 times in case of network failure |
* Fixed process exit code being incorrectly reset to 0 if ErrorHandler::registerExceptionHandler was used |
* Fixed HTML/JS escaping in BrowserConsoleHandler |
* Fixed JSON encoding errors being silently suppressed (PHP 5.5+ only) |
|
### 1.14.0 (2015-06-19) |
|
* Added PHPConsoleHandler to send record to Chrome's PHP Console extension and library |
* Added support for objects implementing __toString in the NormalizerFormatter |
* Added support for HipChat's v2 API in HipChatHandler |
* Added Logger::setTimezone() to initialize the timezone monolog should use in case date.timezone isn't correct for your app |
* Added an option to send formatted message instead of the raw record on PushoverHandler via ->useFormattedMessage(true) |
* Fixed curl errors being silently suppressed |
|
### 1.13.1 (2015-03-09) |
|
* Fixed regression in HipChat requiring a new token to be created |
|
### 1.13.0 (2015-03-05) |
|
* Added Registry::hasLogger to check for the presence of a logger instance |
* Added context.user support to RavenHandler |
* Added HipChat API v2 support in the HipChatHandler |
* Added NativeMailerHandler::addParameter to pass params to the mail() process |
* Added context data to SlackHandler when $includeContextAndExtra is true |
* Added ability to customize the Swift_Message per-email in SwiftMailerHandler |
* Fixed SwiftMailerHandler to lazily create message instances if a callback is provided |
* Fixed serialization of INF and NaN values in Normalizer and LineFormatter |
|
### 1.12.0 (2014-12-29) |
|
* Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers. |
* Added PsrHandler to forward records to another PSR-3 logger |
* Added SamplingHandler to wrap around a handler and include only every Nth record |
* Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now) |
* Added exception codes in the output of most formatters |
* Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line) |
* Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data |
* Added $host to HipChatHandler for users of private instances |
* Added $transactionName to NewRelicHandler and support for a transaction_name context value |
* Fixed MandrillHandler to avoid outputing API call responses |
* Fixed some non-standard behaviors in SyslogUdpHandler |
|
### 1.11.0 (2014-09-30) |
|
* Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names |
* Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails |
* Added MandrillHandler to send emails via the Mandrillapp.com API |
* Added SlackHandler to log records to a Slack.com account |
* Added FleepHookHandler to log records to a Fleep.io account |
* Added LogglyHandler::addTag to allow adding tags to an existing handler |
* Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end |
* Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing |
* Added support for PhpAmqpLib in the AmqpHandler |
* Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs |
* Added support for adding extra fields from $_SERVER in the WebProcessor |
* Fixed support for non-string values in PrsLogMessageProcessor |
* Fixed SwiftMailer messages being sent with the wrong date in long running scripts |
* Fixed minor PHP 5.6 compatibility issues |
* Fixed BufferHandler::close being called twice |
|
### 1.10.0 (2014-06-04) |
|
* Added Logger::getHandlers() and Logger::getProcessors() methods |
* Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached |
* Added support for extra data in NewRelicHandler |
* Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines |
|
### 1.9.1 (2014-04-24) |
|
* Fixed regression in RotatingFileHandler file permissions |
* Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records |
* Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative |
|
### 1.9.0 (2014-04-20) |
|
* Added LogEntriesHandler to send logs to a LogEntries account |
* Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler |
* Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes |
* Added support for table formatting in FirePHPHandler via the table context key |
* Added a TagProcessor to add tags to records, and support for tags in RavenHandler |
* Added $appendNewline flag to the JsonFormatter to enable using it when logging to files |
* Added sound support to the PushoverHandler |
* Fixed multi-threading support in StreamHandler |
* Fixed empty headers issue when ChromePHPHandler received no records |
* Fixed default format of the ErrorLogHandler |
|
### 1.8.0 (2014-03-23) |
|
* Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them |
* Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output |
* Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler |
* Added FlowdockHandler to send logs to a Flowdock account |
* Added RollbarHandler to send logs to a Rollbar account |
* Added HtmlFormatter to send prettier log emails with colors for each log level |
* Added GitProcessor to add the current branch/commit to extra record data |
* Added a Monolog\Registry class to allow easier global access to pre-configured loggers |
* Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement |
* Added support for HHVM |
* Added support for Loggly batch uploads |
* Added support for tweaking the content type and encoding in NativeMailerHandler |
* Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor |
* Fixed batch request support in GelfHandler |
|
### 1.7.0 (2013-11-14) |
|
* Added ElasticSearchHandler to send logs to an Elastic Search server |
* Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB |
* Added SyslogUdpHandler to send logs to a remote syslogd server |
* Added LogglyHandler to send logs to a Loggly account |
* Added $level to IntrospectionProcessor so it only adds backtraces when needed |
* Added $version to LogstashFormatter to allow using the new v1 Logstash format |
* Added $appName to NewRelicHandler |
* Added configuration of Pushover notification retries/expiry |
* Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default |
* Added chainability to most setters for all handlers |
* Fixed RavenHandler batch processing so it takes the message from the record with highest priority |
* Fixed HipChatHandler batch processing so it sends all messages at once |
* Fixed issues with eAccelerator |
* Fixed and improved many small things |
|
### 1.6.0 (2013-07-29) |
|
* Added HipChatHandler to send logs to a HipChat chat room |
* Added ErrorLogHandler to send logs to PHP's error_log function |
* Added NewRelicHandler to send logs to NewRelic's service |
* Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler |
* Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel |
* Added stack traces output when normalizing exceptions (json output & co) |
* Added Monolog\Logger::API constant (currently 1) |
* Added support for ChromePHP's v4.0 extension |
* Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel |
* Added support for sending messages to multiple users at once with the PushoverHandler |
* Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler) |
* Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now |
* Fixed issue in RotatingFileHandler when an open_basedir restriction is active |
* Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0 |
* Fixed SyslogHandler issue when many were used concurrently with different facilities |
|
### 1.5.0 (2013-04-23) |
|
* Added ProcessIdProcessor to inject the PID in log records |
* Added UidProcessor to inject a unique identifier to all log records of one request/run |
* Added support for previous exceptions in the LineFormatter exception serialization |
* Added Monolog\Logger::getLevels() to get all available levels |
* Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle |
|
### 1.4.1 (2013-04-01) |
|
* Fixed exception formatting in the LineFormatter to be more minimalistic |
* Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0 |
* Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days |
* Fixed WebProcessor array access so it checks for data presence |
* Fixed Buffer, Group and FingersCrossed handlers to make use of their processors |
|
### 1.4.0 (2013-02-13) |
|
* Added RedisHandler to log to Redis via the Predis library or the phpredis extension |
* Added ZendMonitorHandler to log to the Zend Server monitor |
* Added the possibility to pass arrays of handlers and processors directly in the Logger constructor |
* Added `$useSSL` option to the PushoverHandler which is enabled by default |
* Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously |
* Fixed header injection capability in the NativeMailHandler |
|
### 1.3.1 (2013-01-11) |
|
* Fixed LogstashFormatter to be usable with stream handlers |
* Fixed GelfMessageFormatter levels on Windows |
|
### 1.3.0 (2013-01-08) |
|
* Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface` |
* Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance |
* Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash) |
* Added PushoverHandler to send mobile notifications |
* Added CouchDBHandler and DoctrineCouchDBHandler |
* Added RavenHandler to send data to Sentry servers |
* Added support for the new MongoClient class in MongoDBHandler |
* Added microsecond precision to log records' timestamps |
* Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing |
the oldest entries |
* Fixed normalization of objects with cyclic references |
|
### 1.2.1 (2012-08-29) |
|
* Added new $logopts arg to SyslogHandler to provide custom openlog options |
* Fixed fatal error in SyslogHandler |
|
### 1.2.0 (2012-08-18) |
|
* Added AmqpHandler (for use with AMQP servers) |
* Added CubeHandler |
* Added NativeMailerHandler::addHeader() to send custom headers in mails |
* Added the possibility to specify more than one recipient in NativeMailerHandler |
* Added the possibility to specify float timeouts in SocketHandler |
* Added NOTICE and EMERGENCY levels to conform with RFC 5424 |
* Fixed the log records to use the php default timezone instead of UTC |
* Fixed BufferHandler not being flushed properly on PHP fatal errors |
* Fixed normalization of exotic resource types |
* Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog |
|
### 1.1.0 (2012-04-23) |
|
* Added Monolog\Logger::isHandling() to check if a handler will |
handle the given log level |
* Added ChromePHPHandler |
* Added MongoDBHandler |
* Added GelfHandler (for use with Graylog2 servers) |
* Added SocketHandler (for use with syslog-ng for example) |
* Added NormalizerFormatter |
* Added the possibility to change the activation strategy of the FingersCrossedHandler |
* Added possibility to show microseconds in logs |
* Added `server` and `referer` to WebProcessor output |
|
### 1.0.2 (2011-10-24) |
|
* Fixed bug in IE with large response headers and FirePHPHandler |
|
### 1.0.1 (2011-08-25) |
|
* Added MemoryPeakUsageProcessor and MemoryUsageProcessor |
* Added Monolog\Logger::getName() to get a logger's channel name |
|
### 1.0.0 (2011-07-06) |
|
* Added IntrospectionProcessor to get info from where the logger was called |
* Fixed WebProcessor in CLI |
|
### 1.0.0-RC1 (2011-07-01) |
|
* Initial release |
/vendor/monolog/monolog/doc/01-usage.md |
@@ -0,0 +1,231 @@ |
# Using Monolog |
|
- [Installation](#installation) |
- [Core Concepts](#core-concepts) |
- [Log Levels](#log-levels) |
- [Configuring a logger](#configuring-a-logger) |
- [Adding extra data in the records](#adding-extra-data-in-the-records) |
- [Leveraging channels](#leveraging-channels) |
- [Customizing the log format](#customizing-the-log-format) |
|
## Installation |
|
Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog)) |
and as such installable via [Composer](http://getcomposer.org/). |
|
```bash |
composer require monolog/monolog |
``` |
|
If you do not use Composer, you can grab the code from GitHub, and use any |
PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader)) |
to load Monolog classes. |
|
## Core Concepts |
|
Every `Logger` instance has a channel (name) and a stack of handlers. Whenever |
you add a record to the logger, it traverses the handler stack. Each handler |
decides whether it fully handled the record, and if so, the propagation of the |
record ends there. |
|
This allows for flexible logging setups, for example having a `StreamHandler` at |
the bottom of the stack that will log anything to disk, and on top of that add |
a `MailHandler` that will send emails only when an error message is logged. |
Handlers also have a `$bubble` property which defines whether they block the |
record or not if they handled it. In this example, setting the `MailHandler`'s |
`$bubble` argument to false means that records handled by the `MailHandler` will |
not propagate to the `StreamHandler` anymore. |
|
You can create many `Logger`s, each defining a channel (e.g.: db, request, |
router, ..) and each of them combining various handlers, which can be shared |
or not. The channel is reflected in the logs and allows you to easily see or |
filter records. |
|
Each Handler also has a Formatter, a default one with settings that make sense |
will be created if you don't set one. The formatters normalize and format |
incoming records so that they can be used by the handlers to output useful |
information. |
|
Custom severity levels are not available. Only the eight |
[RFC 5424](http://tools.ietf.org/html/rfc5424) levels (debug, info, notice, |
warning, error, critical, alert, emergency) are present for basic filtering |
purposes, but for sorting and other use cases that would require |
flexibility, you should add Processors to the Logger that can add extra |
information (tags, user ip, ..) to the records before they are handled. |
|
## Log Levels |
|
Monolog supports the logging levels described by [RFC 5424](http://tools.ietf.org/html/rfc5424). |
|
- **DEBUG** (100): Detailed debug information. |
|
- **INFO** (200): Interesting events. Examples: User logs in, SQL logs. |
|
- **NOTICE** (250): Normal but significant events. |
|
- **WARNING** (300): Exceptional occurrences that are not errors. Examples: |
Use of deprecated APIs, poor use of an API, undesirable things that are not |
necessarily wrong. |
|
- **ERROR** (400): Runtime errors that do not require immediate action but |
should typically be logged and monitored. |
|
- **CRITICAL** (500): Critical conditions. Example: Application component |
unavailable, unexpected exception. |
|
- **ALERT** (550): Action must be taken immediately. Example: Entire website |
down, database unavailable, etc. This should trigger the SMS alerts and wake |
you up. |
|
- **EMERGENCY** (600): Emergency: system is unusable. |
|
## Configuring a logger |
|
Here is a basic setup to log to a file and to firephp on the DEBUG level: |
|
```php |
<?php |
|
use Monolog\Logger; |
use Monolog\Handler\StreamHandler; |
use Monolog\Handler\FirePHPHandler; |
|
// Create the logger |
$logger = new Logger('my_logger'); |
// Now add some handlers |
$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG)); |
$logger->pushHandler(new FirePHPHandler()); |
|
// You can now use your logger |
$logger->addInfo('My logger is now ready'); |
``` |
|
Let's explain it. The first step is to create the logger instance which will |
be used in your code. The argument is a channel name, which is useful when |
you use several loggers (see below for more details about it). |
|
The logger itself does not know how to handle a record. It delegates it to |
some handlers. The code above registers two handlers in the stack to allow |
handling records in two different ways. |
|
Note that the FirePHPHandler is called first as it is added on top of the |
stack. This allows you to temporarily add a logger with bubbling disabled if |
you want to override other configured loggers. |
|
> If you use Monolog standalone and are looking for an easy way to |
> configure many handlers, the [theorchard/monolog-cascade](https://github.com/theorchard/monolog-cascade) |
> can help you build complex logging configs via PHP arrays, yaml or json configs. |
|
## Adding extra data in the records |
|
Monolog provides two different ways to add extra informations along the simple |
textual message. |
|
### Using the logging context |
|
The first way is the context, allowing to pass an array of data along the |
record: |
|
```php |
<?php |
|
$logger->addInfo('Adding a new user', array('username' => 'Seldaek')); |
``` |
|
Simple handlers (like the StreamHandler for instance) will simply format |
the array to a string but richer handlers can take advantage of the context |
(FirePHP is able to display arrays in pretty way for instance). |
|
### Using processors |
|
The second way is to add extra data for all records by using a processor. |
Processors can be any callable. They will get the record as parameter and |
must return it after having eventually changed the `extra` part of it. Let's |
write a processor adding some dummy data in the record: |
|
```php |
<?php |
|
$logger->pushProcessor(function ($record) { |
$record['extra']['dummy'] = 'Hello world!'; |
|
return $record; |
}); |
``` |
|
Monolog provides some built-in processors that can be used in your project. |
Look at the [dedicated chapter](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#processors) for the list. |
|
> Tip: processors can also be registered on a specific handler instead of |
the logger to apply only for this handler. |
|
## Leveraging channels |
|
Channels are a great way to identify to which part of the application a record |
is related. This is useful in big applications (and is leveraged by |
MonologBundle in Symfony2). |
|
Picture two loggers sharing a handler that writes to a single log file. |
Channels would allow you to identify the logger that issued every record. |
You can easily grep through the log files filtering this or that channel. |
|
```php |
<?php |
|
use Monolog\Logger; |
use Monolog\Handler\StreamHandler; |
use Monolog\Handler\FirePHPHandler; |
|
// Create some handlers |
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG); |
$firephp = new FirePHPHandler(); |
|
// Create the main logger of the app |
$logger = new Logger('my_logger'); |
$logger->pushHandler($stream); |
$logger->pushHandler($firephp); |
|
// Create a logger for the security-related stuff with a different channel |
$securityLogger = new Logger('security'); |
$securityLogger->pushHandler($stream); |
$securityLogger->pushHandler($firephp); |
|
// Or clone the first one to only change the channel |
$securityLogger = $logger->withName('security'); |
``` |
|
## Customizing the log format |
|
In Monolog it's easy to customize the format of the logs written into files, |
sockets, mails, databases and other handlers. Most of the handlers use the |
|
```php |
$record['formatted'] |
``` |
|
value to be automatically put into the log device. This value depends on the |
formatter settings. You can choose between predefined formatter classes or |
write your own (e.g. a multiline text file for human-readable output). |
|
To configure a predefined formatter class, just set it as the handler's field: |
|
```php |
// the default date format is "Y-m-d H:i:s" |
$dateFormat = "Y n j, g:i a"; |
// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n" |
$output = "%datetime% > %level_name% > %message% %context% %extra%\n"; |
// finally, create a formatter |
$formatter = new LineFormatter($output, $dateFormat); |
|
// Create a handler |
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG); |
$stream->setFormatter($formatter); |
// bind it to a logger object |
$securityLogger = new Logger('security'); |
$securityLogger->pushHandler($stream); |
``` |
|
You may also reuse the same formatter between multiple handlers and share those |
handlers between multiple loggers. |
|
[Handlers, Formatters and Processors](02-handlers-formatters-processors.md) → |
/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md |
@@ -0,0 +1,157 @@ |
# Handlers, Formatters and Processors |
|
- [Handlers](#handlers) |
- [Log to files and syslog](#log-to-files-and-syslog) |
- [Send alerts and emails](#send-alerts-and-emails) |
- [Log specific servers and networked logging](#log-specific-servers-and-networked-logging) |
- [Logging in development](#logging-in-development) |
- [Log to databases](#log-to-databases) |
- [Wrappers / Special Handlers](#wrappers--special-handlers) |
- [Formatters](#formatters) |
- [Processors](#processors) |
- [Third Party Packages](#third-party-packages) |
|
## Handlers |
|
### Log to files and syslog |
|
- _StreamHandler_: Logs records into any PHP stream, use this for log files. |
- _RotatingFileHandler_: Logs records to a file and creates one logfile per day. |
It will also delete files older than `$maxFiles`. You should use |
[logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile |
setups though, this is just meant as a quick and dirty solution. |
- _SyslogHandler_: Logs records to the syslog. |
- _ErrorLogHandler_: Logs records to PHP's |
[`error_log()`](http://docs.php.net/manual/en/function.error-log.php) function. |
|
### Send alerts and emails |
|
- _NativeMailerHandler_: Sends emails using PHP's |
[`mail()`](http://php.net/manual/en/function.mail.php) function. |
- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance. |
- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API. |
- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API. |
- _FlowdockHandler_: Logs records to a [Flowdock](https://www.flowdock.com/) account. |
- _SlackHandler_: Logs records to a [Slack](https://www.slack.com/) account using the Slack API. |
- _SlackbotHandler_: Logs records to a [Slack](https://www.slack.com/) account using the Slackbot incoming hook. |
- _SlackWebhookHandler_: Logs records to a [Slack](https://www.slack.com/) account using Slack Webhooks. |
- _MandrillHandler_: Sends emails via the Mandrill API using a [`Swift_Message`](http://swiftmailer.org/) instance. |
- _FleepHookHandler_: Logs records to a [Fleep](https://fleep.io/) conversation using Webhooks. |
- _IFTTTHandler_: Notifies an [IFTTT](https://ifttt.com/maker) trigger with the log channel, level name and message. |
|
### Log specific servers and networked logging |
|
- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this |
for UNIX and TCP sockets. See an [example](sockets.md). |
- _AmqpHandler_: Logs records to an [amqp](http://www.amqp.org/) compatible |
server. Requires the [php-amqp](http://pecl.php.net/package/amqp) extension (1.0+). |
- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server. |
- _CubeHandler_: Logs records to a [Cube](http://square.github.com/cube/) server. |
- _RavenHandler_: Logs records to a [Sentry](http://getsentry.com/) server using |
[raven](https://packagist.org/packages/raven/raven). |
- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server. |
- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application. |
- _LogglyHandler_: Logs records to a [Loggly](http://www.loggly.com/) account. |
- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account. |
- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. |
- _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account. |
|
### Logging in development |
|
- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing |
inline `console` messages within [FireBug](http://getfirebug.com/). |
- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing |
inline `console` messages within Chrome. |
- _BrowserConsoleHandler_: Handler to send logs to browser's Javascript `console` with |
no browser extension required. Most browsers supporting `console` API are supported. |
- _PHPConsoleHandler_: Handler for [PHP Console](https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef), providing |
inline `console` and notification popup messages within Chrome. |
|
### Log to databases |
|
- _RedisHandler_: Logs records to a [redis](http://redis.io) server. |
- _MongoDBHandler_: Handler to write records in MongoDB via a |
[Mongo](http://pecl.php.net/package/mongo) extension connection. |
- _CouchDBHandler_: Logs records to a CouchDB server. |
- _DoctrineCouchDBHandler_: Logs records to a CouchDB server via the Doctrine CouchDB ODM. |
- _ElasticSearchHandler_: Logs records to an Elastic Search server. |
- _DynamoDbHandler_: Logs records to a DynamoDB table with the [AWS SDK](https://github.com/aws/aws-sdk-php). |
|
### Wrappers / Special Handlers |
|
- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as |
parameter and will accumulate log records of all levels until a record |
exceeds the defined severity level. At which point it delivers all records, |
including those of lower severity, to the handler it wraps. This means that |
until an error actually happens you will not see anything in your logs, but |
when it happens you will have the full information, including debug and info |
records. This provides you with all the information you need, but only when |
you need it. |
- _DeduplicationHandler_: Useful if you are sending notifications or emails |
when critical errors occur. It takes a logger as parameter and will |
accumulate log records of all levels until the end of the request (or |
`flush()` is called). At that point it delivers all records to the handler |
it wraps, but only if the records are unique over a given time period |
(60seconds by default). If the records are duplicates they are simply |
discarded. The main use of this is in case of critical failure like if your |
database is unreachable for example all your requests will fail and that |
can result in a lot of notifications being sent. Adding this handler reduces |
the amount of notifications to a manageable level. |
- _WhatFailureGroupHandler_: This handler extends the _GroupHandler_ ignoring |
exceptions raised by each child handler. This allows you to ignore issues |
where a remote tcp connection may have died but you do not want your entire |
application to crash and may wish to continue to log to other handlers. |
- _BufferHandler_: This handler will buffer all the log records it receives |
until `close()` is called at which point it will call `handleBatch()` on the |
handler it wraps with all the log messages at once. This is very useful to |
send an email with all records at once for example instead of having one mail |
for every log record. |
- _GroupHandler_: This handler groups other handlers. Every record received is |
sent to all the handlers it is configured with. |
- _FilterHandler_: This handler only lets records of the given levels through |
to the wrapped handler. |
- _SamplingHandler_: Wraps around another handler and lets you sample records |
if you only want to store some of them. |
- _NullHandler_: Any record it can handle will be thrown away. This can be used |
to put on top of an existing handler stack to disable it temporarily. |
- _PsrHandler_: Can be used to forward log records to an existing PSR-3 logger |
- _TestHandler_: Used for testing, it records everything that is sent to it and |
has accessors to read out the information. |
- _HandlerWrapper_: A simple handler wrapper you can inherit from to create |
your own wrappers easily. |
|
## Formatters |
|
- _LineFormatter_: Formats a log record into a one-line string. |
- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails. |
- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. |
- _ScalarFormatter_: Used to format log records into an associative array of scalar values. |
- _JsonFormatter_: Encodes a log record into json. |
- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler. |
- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler. |
- _GelfMessageFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler. |
- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/latest). |
- _ElasticaFormatter_: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler. |
- _LogglyFormatter_: Used to format log records into Loggly messages, only useful for the LogglyHandler. |
- _FlowdockFormatter_: Used to format log records into Flowdock messages, only useful for the FlowdockHandler. |
- _MongoDBFormatter_: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler. |
|
## Processors |
|
- _PsrLogMessageProcessor_: Processes a log record's message according to PSR-3 rules, replacing `{foo}` with the value from `$context['foo']`. |
- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated. |
- _WebProcessor_: Adds the current request URI, request method and client IP to a log record. |
- _MemoryUsageProcessor_: Adds the current memory usage to a log record. |
- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record. |
- _ProcessIdProcessor_: Adds the process id to a log record. |
- _UidProcessor_: Adds a unique identifier to a log record. |
- _GitProcessor_: Adds the current git branch and commit to a log record. |
- _TagProcessor_: Adds an array of predefined tags to a log record. |
|
## Third Party Packages |
|
Third party handlers, formatters and processors are |
[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You |
can also add your own there if you publish one. |
|
← [Usage](01-usage.md) | [Utility classes](03-utilities.md) → |
/vendor/monolog/monolog/src/Monolog/ErrorHandler.php |
@@ -0,0 +1,230 @@ |
<?php |
|
/* |
* 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 Psr\Log\LoggerInterface; |
use Psr\Log\LogLevel; |
use Monolog\Handler\AbstractHandler; |
|
/** |
* Monolog error handler |
* |
* A facility to enable logging of runtime errors, exceptions and fatal errors. |
* |
* Quick setup: <code>ErrorHandler::register($logger);</code> |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class ErrorHandler |
{ |
private $logger; |
|
private $previousExceptionHandler; |
private $uncaughtExceptionLevel; |
|
private $previousErrorHandler; |
private $errorLevelMap; |
private $handleOnlyReportedErrors; |
|
private $hasFatalErrorHandler; |
private $fatalLevel; |
private $reservedMemory; |
private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); |
|
public function __construct(LoggerInterface $logger) |
{ |
$this->logger = $logger; |
} |
|
/** |
* Registers a new ErrorHandler for a given Logger |
* |
* By default it will handle errors, exceptions and fatal errors |
* |
* @param LoggerInterface $logger |
* @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling |
* @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling |
* @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling |
* @return ErrorHandler |
*/ |
public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null) |
{ |
//Forces the autoloader to run for LogLevel. Fixes an autoload issue at compile-time on PHP5.3. See https://github.com/Seldaek/monolog/pull/929 |
class_exists('\\Psr\\Log\\LogLevel', true); |
|
$handler = new static($logger); |
if ($errorLevelMap !== false) { |
$handler->registerErrorHandler($errorLevelMap); |
} |
if ($exceptionLevel !== false) { |
$handler->registerExceptionHandler($exceptionLevel); |
} |
if ($fatalLevel !== false) { |
$handler->registerFatalHandler($fatalLevel); |
} |
|
return $handler; |
} |
|
public function registerExceptionHandler($level = null, $callPrevious = true) |
{ |
$prev = set_exception_handler(array($this, 'handleException')); |
$this->uncaughtExceptionLevel = $level; |
if ($callPrevious && $prev) { |
$this->previousExceptionHandler = $prev; |
} |
} |
|
public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true) |
{ |
$prev = set_error_handler(array($this, 'handleError'), $errorTypes); |
$this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); |
if ($callPrevious) { |
$this->previousErrorHandler = $prev ?: true; |
} |
|
$this->handleOnlyReportedErrors = $handleOnlyReportedErrors; |
} |
|
public function registerFatalHandler($level = null, $reservedMemorySize = 20) |
{ |
register_shutdown_function(array($this, 'handleFatalError')); |
|
$this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); |
$this->fatalLevel = $level; |
$this->hasFatalErrorHandler = true; |
} |
|
protected function defaultErrorLevelMap() |
{ |
return array( |
E_ERROR => LogLevel::CRITICAL, |
E_WARNING => LogLevel::WARNING, |
E_PARSE => LogLevel::ALERT, |
E_NOTICE => LogLevel::NOTICE, |
E_CORE_ERROR => LogLevel::CRITICAL, |
E_CORE_WARNING => LogLevel::WARNING, |
E_COMPILE_ERROR => LogLevel::ALERT, |
E_COMPILE_WARNING => LogLevel::WARNING, |
E_USER_ERROR => LogLevel::ERROR, |
E_USER_WARNING => LogLevel::WARNING, |
E_USER_NOTICE => LogLevel::NOTICE, |
E_STRICT => LogLevel::NOTICE, |
E_RECOVERABLE_ERROR => LogLevel::ERROR, |
E_DEPRECATED => LogLevel::NOTICE, |
E_USER_DEPRECATED => LogLevel::NOTICE, |
); |
} |
|
/** |
* @private |
*/ |
public function handleException($e) |
{ |
$this->logger->log( |
$this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, |
sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), |
array('exception' => $e) |
); |
|
if ($this->previousExceptionHandler) { |
call_user_func($this->previousExceptionHandler, $e); |
} |
|
exit(255); |
} |
|
/** |
* @private |
*/ |
public function handleError($code, $message, $file = '', $line = 0, $context = array()) |
{ |
if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) { |
return; |
} |
|
// fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries |
if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { |
$level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; |
$this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); |
} |
|
if ($this->previousErrorHandler === true) { |
return false; |
} elseif ($this->previousErrorHandler) { |
return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context); |
} |
} |
|
/** |
* @private |
*/ |
public function handleFatalError() |
{ |
$this->reservedMemory = null; |
|
$lastError = error_get_last(); |
if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) { |
$this->logger->log( |
$this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, |
'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], |
array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) |
); |
|
if ($this->logger instanceof Logger) { |
foreach ($this->logger->getHandlers() as $handler) { |
if ($handler instanceof AbstractHandler) { |
$handler->close(); |
} |
} |
} |
} |
} |
|
private static function codeToString($code) |
{ |
switch ($code) { |
case E_ERROR: |
return 'E_ERROR'; |
case E_WARNING: |
return 'E_WARNING'; |
case E_PARSE: |
return 'E_PARSE'; |
case E_NOTICE: |
return 'E_NOTICE'; |
case E_CORE_ERROR: |
return 'E_CORE_ERROR'; |
case E_CORE_WARNING: |
return 'E_CORE_WARNING'; |
case E_COMPILE_ERROR: |
return 'E_COMPILE_ERROR'; |
case E_COMPILE_WARNING: |
return 'E_COMPILE_WARNING'; |
case E_USER_ERROR: |
return 'E_USER_ERROR'; |
case E_USER_WARNING: |
return 'E_USER_WARNING'; |
case E_USER_NOTICE: |
return 'E_USER_NOTICE'; |
case E_STRICT: |
return 'E_STRICT'; |
case E_RECOVERABLE_ERROR: |
return 'E_RECOVERABLE_ERROR'; |
case E_DEPRECATED: |
return 'E_DEPRECATED'; |
case E_USER_DEPRECATED: |
return 'E_USER_DEPRECATED'; |
} |
|
return 'Unknown PHP error'; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php |
@@ -0,0 +1,138 @@ |
<?php |
|
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
use Gelf\Message; |
|
/** |
* Serializes a log message to GELF |
* @see http://www.graylog2.org/about/gelf |
* |
* @author Matt Lehner <mlehner@gmail.com> |
*/ |
class GelfMessageFormatter extends NormalizerFormatter |
{ |
const DEFAULT_MAX_LENGTH = 32766; |
|
/** |
* @var string the name of the system for the Gelf log message |
*/ |
protected $systemName; |
|
/** |
* @var string a prefix for 'extra' fields from the Monolog record (optional) |
*/ |
protected $extraPrefix; |
|
/** |
* @var string a prefix for 'context' fields from the Monolog record (optional) |
*/ |
protected $contextPrefix; |
|
/** |
* @var int max length per field |
*/ |
protected $maxLength; |
|
/** |
* Translates Monolog log levels to Graylog2 log priorities. |
*/ |
private $logLevels = array( |
Logger::DEBUG => 7, |
Logger::INFO => 6, |
Logger::NOTICE => 5, |
Logger::WARNING => 4, |
Logger::ERROR => 3, |
Logger::CRITICAL => 2, |
Logger::ALERT => 1, |
Logger::EMERGENCY => 0, |
); |
|
public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $maxLength = null) |
{ |
parent::__construct('U.u'); |
|
$this->systemName = $systemName ?: gethostname(); |
|
$this->extraPrefix = $extraPrefix; |
$this->contextPrefix = $contextPrefix; |
$this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function format(array $record) |
{ |
$record = parent::format($record); |
|
if (!isset($record['datetime'], $record['message'], $record['level'])) { |
throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); |
} |
|
$message = new Message(); |
$message |
->setTimestamp($record['datetime']) |
->setShortMessage((string) $record['message']) |
->setHost($this->systemName) |
->setLevel($this->logLevels[$record['level']]); |
|
// message length + system name length + 200 for padding / metadata |
$len = 200 + strlen((string) $record['message']) + strlen($this->systemName); |
|
if ($len > $this->maxLength) { |
$message->setShortMessage(substr($record['message'], 0, $this->maxLength)); |
} |
|
if (isset($record['channel'])) { |
$message->setFacility($record['channel']); |
} |
if (isset($record['extra']['line'])) { |
$message->setLine($record['extra']['line']); |
unset($record['extra']['line']); |
} |
if (isset($record['extra']['file'])) { |
$message->setFile($record['extra']['file']); |
unset($record['extra']['file']); |
} |
|
foreach ($record['extra'] as $key => $val) { |
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val); |
$len = strlen($this->extraPrefix . $key . $val); |
if ($len > $this->maxLength) { |
$message->setAdditional($this->extraPrefix . $key, substr($val, 0, $this->maxLength)); |
break; |
} |
$message->setAdditional($this->extraPrefix . $key, $val); |
} |
|
foreach ($record['context'] as $key => $val) { |
$val = is_scalar($val) || null === $val ? $val : $this->toJson($val); |
$len = strlen($this->contextPrefix . $key . $val); |
if ($len > $this->maxLength) { |
$message->setAdditional($this->contextPrefix . $key, substr($val, 0, $this->maxLength)); |
break; |
} |
$message->setAdditional($this->contextPrefix . $key, $val); |
} |
|
if (null === $message->getFile() && isset($record['context']['exception']['file'])) { |
if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) { |
$message->setFile($matches[1]); |
$message->setLine($matches[2]); |
} |
} |
|
return $message; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php |
@@ -0,0 +1,141 @@ |
<?php |
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
|
/** |
* Formats incoming records into an HTML table |
* |
* This is especially useful for html email logging |
* |
* @author Tiago Brito <tlfbrito@gmail.com> |
*/ |
class HtmlFormatter extends NormalizerFormatter |
{ |
/** |
* Translates Monolog log levels to html color priorities. |
*/ |
protected $logLevels = array( |
Logger::DEBUG => '#cccccc', |
Logger::INFO => '#468847', |
Logger::NOTICE => '#3a87ad', |
Logger::WARNING => '#c09853', |
Logger::ERROR => '#f0ad4e', |
Logger::CRITICAL => '#FF7708', |
Logger::ALERT => '#C12A19', |
Logger::EMERGENCY => '#000000', |
); |
|
/** |
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format |
*/ |
public function __construct($dateFormat = null) |
{ |
parent::__construct($dateFormat); |
} |
|
/** |
* Creates an HTML table row |
* |
* @param string $th Row header content |
* @param string $td Row standard cell content |
* @param bool $escapeTd false if td content must not be html escaped |
* @return string |
*/ |
protected function addRow($th, $td = ' ', $escapeTd = true) |
{ |
$th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); |
if ($escapeTd) { |
$td = '<pre>'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'</pre>'; |
} |
|
return "<tr style=\"padding: 4px;spacing: 0;text-align: left;\">\n<th style=\"background: #cccccc\" width=\"100px\">$th:</th>\n<td style=\"padding: 4px;spacing: 0;text-align: left;background: #eeeeee\">".$td."</td>\n</tr>"; |
} |
|
/** |
* Create a HTML h1 tag |
* |
* @param string $title Text to be in the h1 |
* @param int $level Error level |
* @return string |
*/ |
protected function addTitle($title, $level) |
{ |
$title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); |
|
return '<h1 style="background: '.$this->logLevels[$level].';color: #ffffff;padding: 5px;" class="monolog-output">'.$title.'</h1>'; |
} |
|
/** |
* Formats a log record. |
* |
* @param array $record A record to format |
* @return mixed The formatted record |
*/ |
public function format(array $record) |
{ |
$output = $this->addTitle($record['level_name'], $record['level']); |
$output .= '<table cellspacing="1" width="100%" class="monolog-output">'; |
|
$output .= $this->addRow('Message', (string) $record['message']); |
$output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat)); |
$output .= $this->addRow('Channel', $record['channel']); |
if ($record['context']) { |
$embeddedTable = '<table cellspacing="1" width="100%">'; |
foreach ($record['context'] as $key => $value) { |
$embeddedTable .= $this->addRow($key, $this->convertToString($value)); |
} |
$embeddedTable .= '</table>'; |
$output .= $this->addRow('Context', $embeddedTable, false); |
} |
if ($record['extra']) { |
$embeddedTable = '<table cellspacing="1" width="100%">'; |
foreach ($record['extra'] as $key => $value) { |
$embeddedTable .= $this->addRow($key, $this->convertToString($value)); |
} |
$embeddedTable .= '</table>'; |
$output .= $this->addRow('Extra', $embeddedTable, false); |
} |
|
return $output.'</table>'; |
} |
|
/** |
* Formats a set of log records. |
* |
* @param array $records A set of records to format |
* @return mixed The formatted set of records |
*/ |
public function formatBatch(array $records) |
{ |
$message = ''; |
foreach ($records as $record) { |
$message .= $this->format($record); |
} |
|
return $message; |
} |
|
protected function convertToString($data) |
{ |
if (null === $data || is_scalar($data)) { |
return (string) $data; |
} |
|
$data = $this->normalize($data); |
if (version_compare(PHP_VERSION, '5.4.0', '>=')) { |
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); |
} |
|
return str_replace('\\/', '/', json_encode($data)); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php |
@@ -0,0 +1,208 @@ |
<?php |
|
/* |
* 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 Exception; |
use Throwable; |
|
/** |
* Encodes whatever record data is passed to it as json |
* |
* This can be useful to log to databases or remote APIs |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class JsonFormatter extends NormalizerFormatter |
{ |
const BATCH_MODE_JSON = 1; |
const BATCH_MODE_NEWLINES = 2; |
|
protected $batchMode; |
protected $appendNewline; |
|
/** |
* @var bool |
*/ |
protected $includeStacktraces = false; |
|
/** |
* @param int $batchMode |
* @param bool $appendNewline |
*/ |
public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true) |
{ |
$this->batchMode = $batchMode; |
$this->appendNewline = $appendNewline; |
} |
|
/** |
* The batch mode option configures the formatting style for |
* multiple records. By default, multiple records will be |
* formatted as a JSON-encoded array. However, for |
* compatibility with some API endpoints, alternative styles |
* are available. |
* |
* @return int |
*/ |
public function getBatchMode() |
{ |
return $this->batchMode; |
} |
|
/** |
* True if newlines are appended to every formatted record |
* |
* @return bool |
*/ |
public function isAppendingNewlines() |
{ |
return $this->appendNewline; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function format(array $record) |
{ |
return $this->toJson($this->normalize($record), true) . ($this->appendNewline ? "\n" : ''); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function formatBatch(array $records) |
{ |
switch ($this->batchMode) { |
case static::BATCH_MODE_NEWLINES: |
return $this->formatBatchNewlines($records); |
|
case static::BATCH_MODE_JSON: |
default: |
return $this->formatBatchJson($records); |
} |
} |
|
/** |
* @param bool $include |
*/ |
public function includeStacktraces($include = true) |
{ |
$this->includeStacktraces = $include; |
} |
|
/** |
* Return a JSON-encoded array of records. |
* |
* @param array $records |
* @return string |
*/ |
protected function formatBatchJson(array $records) |
{ |
return $this->toJson($this->normalize($records), true); |
} |
|
/** |
* Use new lines to separate records instead of a |
* JSON-encoded array. |
* |
* @param array $records |
* @return string |
*/ |
protected function formatBatchNewlines(array $records) |
{ |
$instance = $this; |
|
$oldNewline = $this->appendNewline; |
$this->appendNewline = false; |
array_walk($records, function (&$value, $key) use ($instance) { |
$value = $instance->format($value); |
}); |
$this->appendNewline = $oldNewline; |
|
return implode("\n", $records); |
} |
|
/** |
* Normalizes given $data. |
* |
* @param mixed $data |
* |
* @return mixed |
*/ |
protected function normalize($data) |
{ |
if (is_array($data) || $data instanceof \Traversable) { |
$normalized = array(); |
|
$count = 1; |
foreach ($data as $key => $value) { |
if ($count++ >= 1000) { |
$normalized['...'] = 'Over 1000 items, aborting normalization'; |
break; |
} |
$normalized[$key] = $this->normalize($value); |
} |
|
return $normalized; |
} |
|
if ($data instanceof Exception || $data instanceof Throwable) { |
return $this->normalizeException($data); |
} |
|
return $data; |
} |
|
/** |
* Normalizes given exception with or without its own stack trace based on |
* `includeStacktraces` property. |
* |
* @param Exception|Throwable $e |
* |
* @return array |
*/ |
protected function normalizeException($e) |
{ |
// TODO 2.0 only check for Throwable |
if (!$e instanceof Exception && !$e instanceof Throwable) { |
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); |
} |
|
$data = array( |
'class' => get_class($e), |
'message' => $e->getMessage(), |
'code' => $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']; |
} elseif (isset($frame['function']) && $frame['function'] === '{closure}') { |
// We should again normalize the frames, because it might contain invalid items |
$data['trace'][] = $frame['function']; |
} else { |
// We should again normalize the frames, because it might contain invalid items |
$data['trace'][] = $this->normalize($frame); |
} |
} |
} |
|
if ($previous = $e->getPrevious()) { |
$data['previous'] = $this->normalizeException($previous); |
} |
|
return $data; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php |
@@ -0,0 +1,179 @@ |
<?php |
|
/* |
* 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; |
|
/** |
* Formats incoming records into a one-line string |
* |
* This is especially useful for logging to files |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
* @author Christophe Coevoet <stof@notk.org> |
*/ |
class LineFormatter extends NormalizerFormatter |
{ |
const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; |
|
protected $format; |
protected $allowInlineLineBreaks; |
protected $ignoreEmptyContextAndExtra; |
protected $includeStacktraces; |
|
/** |
* @param string $format The format of the message |
* @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 $ignoreEmptyContextAndExtra |
*/ |
public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false) |
{ |
$this->format = $format ?: static::SIMPLE_FORMAT; |
$this->allowInlineLineBreaks = $allowInlineLineBreaks; |
$this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; |
parent::__construct($dateFormat); |
} |
|
public function includeStacktraces($include = true) |
{ |
$this->includeStacktraces = $include; |
if ($this->includeStacktraces) { |
$this->allowInlineLineBreaks = true; |
} |
} |
|
public function allowInlineLineBreaks($allow = true) |
{ |
$this->allowInlineLineBreaks = $allow; |
} |
|
public function ignoreEmptyContextAndExtra($ignore = true) |
{ |
$this->ignoreEmptyContextAndExtra = $ignore; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function format(array $record) |
{ |
$vars = parent::format($record); |
|
$output = $this->format; |
|
foreach ($vars['extra'] as $var => $val) { |
if (false !== strpos($output, '%extra.'.$var.'%')) { |
$output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); |
unset($vars['extra'][$var]); |
} |
} |
|
|
foreach ($vars['context'] as $var => $val) { |
if (false !== strpos($output, '%context.'.$var.'%')) { |
$output = str_replace('%context.'.$var.'%', $this->stringify($val), $output); |
unset($vars['context'][$var]); |
} |
} |
|
if ($this->ignoreEmptyContextAndExtra) { |
if (empty($vars['context'])) { |
unset($vars['context']); |
$output = str_replace('%context%', '', $output); |
} |
|
if (empty($vars['extra'])) { |
unset($vars['extra']); |
$output = str_replace('%extra%', '', $output); |
} |
} |
|
foreach ($vars as $var => $val) { |
if (false !== strpos($output, '%'.$var.'%')) { |
$output = str_replace('%'.$var.'%', $this->stringify($val), $output); |
} |
} |
|
// remove leftover %extra.xxx% and %context.xxx% if any |
if (false !== strpos($output, '%')) { |
$output = preg_replace('/%(?:extra|context)\..+?%/', '', $output); |
} |
|
return $output; |
} |
|
public function formatBatch(array $records) |
{ |
$message = ''; |
foreach ($records as $record) { |
$message .= $this->format($record); |
} |
|
return $message; |
} |
|
public function stringify($value) |
{ |
return $this->replaceNewlines($this->convertToString($value)); |
} |
|
protected function normalizeException($e) |
{ |
// TODO 2.0 only check for Throwable |
if (!$e instanceof \Exception && !$e instanceof \Throwable) { |
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); |
} |
|
$previousText = ''; |
if ($previous = $e->getPrevious()) { |
do { |
$previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); |
} while ($previous = $previous->getPrevious()); |
} |
|
$str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')'; |
if ($this->includeStacktraces) { |
$str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n"; |
} |
|
return $str; |
} |
|
protected function convertToString($data) |
{ |
if (null === $data || is_bool($data)) { |
return var_export($data, true); |
} |
|
if (is_scalar($data)) { |
return (string) $data; |
} |
|
if (version_compare(PHP_VERSION, '5.4.0', '>=')) { |
return $this->toJson($data, true); |
} |
|
return str_replace('\\/', '/', @json_encode($data)); |
} |
|
protected function replaceNewlines($str) |
{ |
if ($this->allowInlineLineBreaks) { |
if (0 === strpos($str, '{')) { |
return str_replace(array('\r', '\n'), array("\r", "\n"), $str); |
} |
|
return $str; |
} |
|
return str_replace(array("\r\n", "\r", "\n"), ' ', $str); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php |
@@ -0,0 +1,166 @@ |
<?php |
|
/* |
* 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; |
|
/** |
* Serializes a log message to Logstash Event Format |
* |
* @see http://logstash.net/ |
* @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb |
* |
* @author Tim Mower <timothy.mower@gmail.com> |
*/ |
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 |
*/ |
protected $systemName; |
|
/** |
* @var string an application name for the Logstash log message, used to fill the @type field |
*/ |
protected $applicationName; |
|
/** |
* @var string a prefix for 'extra' fields from the Monolog record (optional) |
*/ |
protected $extraPrefix; |
|
/** |
* @var string a prefix for 'context' fields from the Monolog record (optional) |
*/ |
protected $contextPrefix; |
|
/** |
* @var int logstash format version to use |
*/ |
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. |
parent::__construct('Y-m-d\TH:i:s.uP'); |
|
$this->systemName = $systemName ?: gethostname(); |
$this->applicationName = $applicationName; |
$this->extraPrefix = $extraPrefix; |
$this->contextPrefix = $contextPrefix; |
$this->version = $version; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function format(array $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'])) { |
$record['datetime'] = gmdate('c'); |
} |
$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'], |
'@version' => 1, |
'host' => $this->systemName, |
); |
if (isset($record['message'])) { |
$message['message'] = $record['message']; |
} |
if (isset($record['channel'])) { |
$message['type'] = $record['channel']; |
$message['channel'] = $record['channel']; |
} |
if (isset($record['level_name'])) { |
$message['level'] = $record['level_name']; |
} |
if ($this->applicationName) { |
$message['type'] = $this->applicationName; |
} |
if (!empty($record['extra'])) { |
foreach ($record['extra'] as $key => $val) { |
$message[$this->extraPrefix . $key] = $val; |
} |
} |
if (!empty($record['context'])) { |
foreach ($record['context'] as $key => $val) { |
$message[$this->contextPrefix . $key] = $val; |
} |
} |
|
return $message; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php |
@@ -0,0 +1,297 @@ |
<?php |
|
/* |
* 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 Exception; |
|
/** |
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class NormalizerFormatter implements FormatterInterface |
{ |
const SIMPLE_DATE = "Y-m-d H:i:s"; |
|
protected $dateFormat; |
|
/** |
* @param string $dateFormat The format of the timestamp: one supported by DateTime::format |
*/ |
public function __construct($dateFormat = null) |
{ |
$this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; |
if (!function_exists('json_encode')) { |
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function format(array $record) |
{ |
return $this->normalize($record); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function formatBatch(array $records) |
{ |
foreach ($records as $key => $record) { |
$records[$key] = $this->format($record); |
} |
|
return $records; |
} |
|
protected function normalize($data) |
{ |
if (null === $data || is_scalar($data)) { |
if (is_float($data)) { |
if (is_infinite($data)) { |
return ($data > 0 ? '' : '-') . 'INF'; |
} |
if (is_nan($data)) { |
return 'NaN'; |
} |
} |
|
return $data; |
} |
|
if (is_array($data)) { |
$normalized = array(); |
|
$count = 1; |
foreach ($data as $key => $value) { |
if ($count++ >= 1000) { |
$normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; |
break; |
} |
$normalized[$key] = $this->normalize($value); |
} |
|
return $normalized; |
} |
|
if ($data instanceof \DateTime) { |
return $data->format($this->dateFormat); |
} |
|
if (is_object($data)) { |
// TODO 2.0 only check for Throwable |
if ($data instanceof Exception || (PHP_VERSION_ID > 70000 && $data instanceof \Throwable)) { |
return $this->normalizeException($data); |
} |
|
// non-serializable objects that implement __toString stringified |
if (method_exists($data, '__toString') && !$data instanceof \JsonSerializable) { |
$value = $data->__toString(); |
} else { |
// the rest is json-serialized in some way |
$value = $this->toJson($data, true); |
} |
|
return sprintf("[object] (%s: %s)", get_class($data), $value); |
} |
|
if (is_resource($data)) { |
return sprintf('[resource] (%s)', get_resource_type($data)); |
} |
|
return '[unknown('.gettype($data).')]'; |
} |
|
protected function normalizeException($e) |
{ |
// TODO 2.0 only check for Throwable |
if (!$e instanceof Exception && !$e instanceof \Throwable) { |
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); |
} |
|
$data = array( |
'class' => get_class($e), |
'message' => $e->getMessage(), |
'code' => $e->getCode(), |
'file' => $e->getFile().':'.$e->getLine(), |
); |
|
if ($e instanceof \SoapFault) { |
if (isset($e->faultcode)) { |
$data['faultcode'] = $e->faultcode; |
} |
|
if (isset($e->faultactor)) { |
$data['faultactor'] = $e->faultactor; |
} |
|
if (isset($e->detail)) { |
$data['detail'] = $e->detail; |
} |
} |
|
$trace = $e->getTrace(); |
foreach ($trace as $frame) { |
if (isset($frame['file'])) { |
$data['trace'][] = $frame['file'].':'.$frame['line']; |
} elseif (isset($frame['function']) && $frame['function'] === '{closure}') { |
// We should again normalize the frames, because it might contain invalid items |
$data['trace'][] = $frame['function']; |
} else { |
// We should again normalize the frames, because it might contain invalid items |
$data['trace'][] = $this->toJson($this->normalize($frame), true); |
} |
} |
|
if ($previous = $e->getPrevious()) { |
$data['previous'] = $this->normalizeException($previous); |
} |
|
return $data; |
} |
|
/** |
* Return the JSON representation of a value |
* |
* @param mixed $data |
* @param bool $ignoreErrors |
* @throws \RuntimeException if encoding fails and errors are not ignored |
* @return string |
*/ |
protected function toJson($data, $ignoreErrors = false) |
{ |
// suppress json_encode errors since it's twitchy with some inputs |
if ($ignoreErrors) { |
return @$this->jsonEncode($data); |
} |
|
$json = $this->jsonEncode($data); |
|
if ($json === false) { |
$json = $this->handleJsonError(json_last_error(), $data); |
} |
|
return $json; |
} |
|
/** |
* @param mixed $data |
* @return string JSON encoded data or null on failure |
*/ |
private function jsonEncode($data) |
{ |
if (version_compare(PHP_VERSION, '5.4.0', '>=')) { |
return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); |
} |
|
return json_encode($data); |
} |
|
/** |
* Handle a json_encode failure. |
* |
* If the failure is due to invalid string encoding, try to clean the |
* input and encode again. If the second encoding attempt fails, the |
* inital error is not encoding related or the input can't be cleaned then |
* raise a descriptive exception. |
* |
* @param int $code return code of json_last_error function |
* @param mixed $data data that was meant to be encoded |
* @throws \RuntimeException if failure can't be corrected |
* @return string JSON encoded data after error correction |
*/ |
private function handleJsonError($code, $data) |
{ |
if ($code !== JSON_ERROR_UTF8) { |
$this->throwEncodeError($code, $data); |
} |
|
if (is_string($data)) { |
$this->detectAndCleanUtf8($data); |
} elseif (is_array($data)) { |
array_walk_recursive($data, array($this, 'detectAndCleanUtf8')); |
} else { |
$this->throwEncodeError($code, $data); |
} |
|
$json = $this->jsonEncode($data); |
|
if ($json === false) { |
$this->throwEncodeError(json_last_error(), $data); |
} |
|
return $json; |
} |
|
/** |
* Throws an exception according to a given code with a customized message |
* |
* @param int $code return code of json_last_error function |
* @param mixed $data data that was meant to be encoded |
* @throws \RuntimeException |
*/ |
private function throwEncodeError($code, $data) |
{ |
switch ($code) { |
case JSON_ERROR_DEPTH: |
$msg = 'Maximum stack depth exceeded'; |
break; |
case JSON_ERROR_STATE_MISMATCH: |
$msg = 'Underflow or the modes mismatch'; |
break; |
case JSON_ERROR_CTRL_CHAR: |
$msg = 'Unexpected control character found'; |
break; |
case JSON_ERROR_UTF8: |
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; |
break; |
default: |
$msg = 'Unknown error'; |
} |
|
throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); |
} |
|
/** |
* Detect invalid UTF-8 string characters and convert to valid UTF-8. |
* |
* Valid UTF-8 input will be left unmodified, but strings containing |
* invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed |
* original encoding of ISO-8859-15. This conversion may result in |
* incorrect output if the actual encoding was not ISO-8859-15, but it |
* will be clean UTF-8 output and will not rely on expensive and fragile |
* detection algorithms. |
* |
* Function converts the input in place in the passed variable so that it |
* can be used as a callback for array_walk_recursive. |
* |
* @param mixed &$data Input to check and convert if needed |
* @private |
*/ |
public function detectAndCleanUtf8(&$data) |
{ |
if (is_string($data) && !preg_match('//u', $data)) { |
$data = preg_replace_callback( |
'/[\x80-\xFF]+/', |
function ($m) { return utf8_encode($m[0]); }, |
$data |
); |
$data = str_replace( |
array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'), |
array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'), |
$data |
); |
} |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php |
@@ -0,0 +1,230 @@ |
<?php |
|
/* |
* 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\LineFormatter; |
|
/** |
* Handler sending logs to browser's javascript console with no browser extension required |
* |
* @author Olivier Poitrey <rs@dailymotion.com> |
*/ |
class BrowserConsoleHandler extends AbstractProcessingHandler |
{ |
protected static $initialized = false; |
protected static $records = array(); |
|
/** |
* {@inheritDoc} |
* |
* Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. |
* |
* Example of formatted string: |
* |
* You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
// Accumulate records |
self::$records[] = $record; |
|
// Register shutdown handler if not already done |
if (!self::$initialized) { |
self::$initialized = true; |
$this->registerShutdownFunction(); |
} |
} |
|
/** |
* 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. |
*/ |
public static function send() |
{ |
$format = self::getResponseFormat(); |
if ($format === 'unknown') { |
return; |
} |
|
if (count(self::$records)) { |
if ($format === 'html') { |
self::writeOutput('<script>' . self::generateScript() . '</script>'); |
} elseif ($format === 'js') { |
self::writeOutput(self::generateScript()); |
} |
self::reset(); |
} |
} |
|
/** |
* Forget all logged records |
*/ |
public static function reset() |
{ |
self::$records = array(); |
} |
|
/** |
* Wrapper for register_shutdown_function to allow overriding |
*/ |
protected function registerShutdownFunction() |
{ |
if (PHP_SAPI !== 'cli') { |
register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send')); |
} |
} |
|
/** |
* Wrapper for echo to allow overriding |
* |
* @param string $str |
*/ |
protected static function writeOutput($str) |
{ |
echo $str; |
} |
|
/** |
* Checks the format of the response |
* |
* If Content-Type is set to application/javascript or text/javascript -> js |
* If Content-Type is set to text/html, or is unset -> html |
* If Content-Type is anything else -> unknown |
* |
* @return string One of 'js', 'html' or 'unknown' |
*/ |
protected static function getResponseFormat() |
{ |
// Check content type |
foreach (headers_list() as $header) { |
if (stripos($header, 'content-type:') === 0) { |
// This handler only works with HTML and javascript outputs |
// text/javascript is obsolete in favour of application/javascript, but still used |
if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) { |
return 'js'; |
} |
if (stripos($header, 'text/html') === false) { |
return 'unknown'; |
} |
break; |
} |
} |
|
return 'html'; |
} |
|
private static function generateScript() |
{ |
$script = array(); |
foreach (self::$records as $record) { |
$context = self::dump('Context', $record['context']); |
$extra = self::dump('Extra', $record['extra']); |
|
if (empty($context) && empty($extra)) { |
$script[] = self::call_array('log', self::handleStyles($record['formatted'])); |
} else { |
$script = array_merge($script, |
array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), |
$context, |
$extra, |
array(self::call('groupEnd')) |
); |
} |
} |
|
return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; |
} |
|
private static function handleStyles($formatted) |
{ |
$args = array(self::quote('font-weight: normal')); |
$format = '%c' . $formatted; |
preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); |
|
foreach (array_reverse($matches) as $match) { |
$args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); |
$args[] = '"font-weight: normal"'; |
|
$pos = $match[0][1]; |
$format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); |
} |
|
array_unshift($args, self::quote($format)); |
|
return $args; |
} |
|
private static function handleCustomStyles($style, $string) |
{ |
static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'); |
static $labels = array(); |
|
return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) { |
if (trim($m[1]) === 'autolabel') { |
// Format the string as a label with consistent auto assigned background color |
if (!isset($labels[$string])) { |
$labels[$string] = $colors[count($labels) % count($colors)]; |
} |
$color = $labels[$string]; |
|
return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; |
} |
|
return $m[1]; |
}, $style); |
} |
|
private static function dump($title, array $dict) |
{ |
$script = array(); |
$dict = array_filter($dict); |
if (empty($dict)) { |
return $script; |
} |
$script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); |
foreach ($dict as $key => $value) { |
$value = json_encode($value); |
if (empty($value)) { |
$value = self::quote(''); |
} |
$script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); |
} |
|
return $script; |
} |
|
private static function quote($arg) |
{ |
return '"' . addcslashes($arg, "\"\n\\") . '"'; |
} |
|
private static function call() |
{ |
$args = func_get_args(); |
$method = array_shift($args); |
|
return self::call_array($method, $args); |
} |
|
private static function call_array($method, array $args) |
{ |
return 'c.' . $method . '(' . implode(', ', $args) . ');'; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php |
@@ -0,0 +1,211 @@ |
<?php |
|
/* |
* 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\ChromePHPFormatter; |
use Monolog\Logger; |
|
/** |
* Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) |
* |
* This also works out of the box with Firefox 43+ |
* |
* @author Christophe Coevoet <stof@notk.org> |
*/ |
class ChromePHPHandler extends AbstractProcessingHandler |
{ |
/** |
* Version of the extension |
*/ |
const VERSION = '4.0'; |
|
/** |
* Header name |
*/ |
const HEADER_NAME = 'X-ChromeLogger-Data'; |
|
/** |
* Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) |
*/ |
const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; |
|
protected static $initialized = false; |
|
/** |
* Tracks whether we sent too much data |
* |
* Chrome limits the headers to 256KB, so when we sent 240KB we stop sending |
* |
* @var Boolean |
*/ |
protected static $overflowed = false; |
|
protected static $json = array( |
'version' => self::VERSION, |
'columns' => array('label', 'log', 'backtrace', 'type'), |
'rows' => array(), |
); |
|
protected static $sendHeaders = true; |
|
/** |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
if (!function_exists('json_encode')) { |
throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
$messages = array(); |
|
foreach ($records as $record) { |
if ($record['level'] < $this->level) { |
continue; |
} |
$messages[] = $this->processRecord($record); |
} |
|
if (!empty($messages)) { |
$messages = $this->getFormatter()->formatBatch($messages); |
self::$json['rows'] = array_merge(self::$json['rows'], $messages); |
$this->send(); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new ChromePHPFormatter(); |
} |
|
/** |
* Creates & sends header for a record |
* |
* @see sendHeader() |
* @see send() |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
self::$json['rows'][] = $record['formatted']; |
|
$this->send(); |
} |
|
/** |
* Sends the log header |
* |
* @see sendHeader() |
*/ |
protected function send() |
{ |
if (self::$overflowed || !self::$sendHeaders) { |
return; |
} |
|
if (!self::$initialized) { |
self::$initialized = true; |
|
self::$sendHeaders = $this->headersAccepted(); |
if (!self::$sendHeaders) { |
return; |
} |
|
self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; |
} |
|
$json = @json_encode(self::$json); |
$data = base64_encode(utf8_encode($json)); |
if (strlen($data) > 240 * 1024) { |
self::$overflowed = true; |
|
$record = array( |
'message' => 'Incomplete logs, chrome header size limit reached', |
'context' => array(), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'monolog', |
'datetime' => new \DateTime(), |
'extra' => array(), |
); |
self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); |
$json = @json_encode(self::$json); |
$data = base64_encode(utf8_encode($json)); |
} |
|
if (trim($data) !== '') { |
$this->sendHeader(self::HEADER_NAME, $data); |
} |
} |
|
/** |
* Send header string to the client |
* |
* @param string $header |
* @param string $content |
*/ |
protected function sendHeader($header, $content) |
{ |
if (!headers_sent() && self::$sendHeaders) { |
header(sprintf('%s: %s', $header, $content)); |
} |
} |
|
/** |
* Verifies if the headers are accepted by the current user agent |
* |
* @return Boolean |
*/ |
protected function headersAccepted() |
{ |
if (empty($_SERVER['HTTP_USER_AGENT'])) { |
return false; |
} |
|
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; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php |
@@ -0,0 +1,151 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Logs to Cube. |
* |
* @link http://square.github.com/cube/ |
* @author Wan Chen <kami@kamisama.me> |
*/ |
class CubeHandler extends AbstractProcessingHandler |
{ |
private $udpConnection; |
private $httpConnection; |
private $scheme; |
private $host; |
private $port; |
private $acceptedSchemes = array('http', 'udp'); |
|
/** |
* Create a Cube handler |
* |
* @throws \UnexpectedValueException when given url is not a valid url. |
* A valid url must consist of three parts : protocol://host:port |
* Only valid protocols used by Cube are http and udp |
*/ |
public function __construct($url, $level = Logger::DEBUG, $bubble = true) |
{ |
$urlInfo = parse_url($url); |
|
if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { |
throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); |
} |
|
if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { |
throw new \UnexpectedValueException( |
'Invalid protocol (' . $urlInfo['scheme'] . ').' |
. ' Valid options are ' . implode(', ', $this->acceptedSchemes)); |
} |
|
$this->scheme = $urlInfo['scheme']; |
$this->host = $urlInfo['host']; |
$this->port = $urlInfo['port']; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* Establish a connection to an UDP socket |
* |
* @throws \LogicException when unable to connect to the socket |
* @throws MissingExtensionException when there is no socket extension |
*/ |
protected function connectUdp() |
{ |
if (!extension_loaded('sockets')) { |
throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); |
} |
|
$this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); |
if (!$this->udpConnection) { |
throw new \LogicException('Unable to create a socket'); |
} |
|
if (!socket_connect($this->udpConnection, $this->host, $this->port)) { |
throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); |
} |
} |
|
/** |
* Establish a connection to a http server |
* @throws \LogicException when no curl extension |
*/ |
protected function connectHttp() |
{ |
if (!extension_loaded('curl')) { |
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'); |
|
if (!$this->httpConnection) { |
throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); |
} |
|
curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); |
curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$date = $record['datetime']; |
|
$data = array('time' => $date->format('Y-m-d\TH:i:s.uO')); |
unset($record['datetime']); |
|
if (isset($record['context']['type'])) { |
$data['type'] = $record['context']['type']; |
unset($record['context']['type']); |
} else { |
$data['type'] = $record['channel']; |
} |
|
$data['data'] = $record['context']; |
$data['data']['level'] = $record['level']; |
|
if ($this->scheme === 'http') { |
$this->writeHttp(json_encode($data)); |
} else { |
$this->writeUdp(json_encode($data)); |
} |
} |
|
private function writeUdp($data) |
{ |
if (!$this->udpConnection) { |
$this->connectUdp(); |
} |
|
socket_send($this->udpConnection, $data, strlen($data), 0); |
} |
|
private function writeHttp($data) |
{ |
if (!$this->httpConnection) { |
$this->connectHttp(); |
} |
|
curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); |
curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array( |
'Content-Type: application/json', |
'Content-Length: ' . strlen('['.$data.']'), |
)); |
|
Curl\Util::execute($this->httpConnection, 5, false); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php |
@@ -0,0 +1,169 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Simple handler wrapper that deduplicates log records across multiple requests |
* |
* It also includes the BufferHandler functionality and will buffer |
* all messages until the end of the request or flush() is called. |
* |
* This works by storing all log records' messages above $deduplicationLevel |
* to the file specified by $deduplicationStore. When further logs come in at the end of the |
* request (or when flush() is called), all those above $deduplicationLevel are checked |
* against the existing stored logs. If they match and the timestamps in the stored log is |
* not older than $time seconds, the new log record is discarded. If no log record is new, the |
* whole data set is discarded. |
* |
* This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers |
* that send messages to people, to avoid spamming with the same message over and over in case of |
* a major component failure like a database server being down which makes all requests fail in the |
* same way. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class DeduplicationHandler extends BufferHandler |
{ |
/** |
* @var string |
*/ |
protected $deduplicationStore; |
|
/** |
* @var int |
*/ |
protected $deduplicationLevel; |
|
/** |
* @var int |
*/ |
protected $time; |
|
/** |
* @var bool |
*/ |
private $gc = false; |
|
/** |
* @param HandlerInterface $handler Handler. |
* @param string $deduplicationStore The file/path where the deduplication log should be kept |
* @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 Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true) |
{ |
parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); |
|
$this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; |
$this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); |
$this->time = $time; |
} |
|
public function flush() |
{ |
if ($this->bufferSize === 0) { |
return; |
} |
|
$passthru = null; |
|
foreach ($this->buffer as $record) { |
if ($record['level'] >= $this->deduplicationLevel) { |
|
$passthru = $passthru || !$this->isDuplicate($record); |
if ($passthru) { |
$this->appendRecord($record); |
} |
} |
} |
|
// default of null is valid as well as if no record matches duplicationLevel we just pass through |
if ($passthru === true || $passthru === null) { |
$this->handler->handleBatch($this->buffer); |
} |
|
$this->clear(); |
|
if ($this->gc) { |
$this->collectLogs(); |
} |
} |
|
private function isDuplicate(array $record) |
{ |
if (!file_exists($this->deduplicationStore)) { |
return false; |
} |
|
$store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
if (!is_array($store)) { |
return false; |
} |
|
$yesterday = time() - 86400; |
$timestampValidity = $record['datetime']->getTimestamp() - $this->time; |
$expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']); |
|
for ($i = count($store) - 1; $i >= 0; $i--) { |
list($timestamp, $level, $message) = explode(':', $store[$i], 3); |
|
if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { |
return true; |
} |
|
if ($timestamp < $yesterday) { |
$this->gc = true; |
} |
} |
|
return false; |
} |
|
private function collectLogs() |
{ |
if (!file_exists($this->deduplicationStore)) { |
return false; |
} |
|
$handle = fopen($this->deduplicationStore, 'rw+'); |
flock($handle, LOCK_EX); |
$validLogs = array(); |
|
$timestampValidity = time() - $this->time; |
|
while (!feof($handle)) { |
$log = fgets($handle); |
if (substr($log, 0, 10) >= $timestampValidity) { |
$validLogs[] = $log; |
} |
} |
|
ftruncate($handle, 0); |
rewind($handle); |
foreach ($validLogs as $log) { |
fwrite($handle, $log); |
} |
|
flock($handle, LOCK_UN); |
fclose($handle); |
|
$this->gc = false; |
} |
|
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); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php |
@@ -0,0 +1,163 @@ |
<?php |
|
/* |
* 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\Handler\FingersCrossed\ErrorLevelActivationStrategy; |
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; |
use Monolog\Logger; |
|
/** |
* Buffers all records until a certain level is reached |
* |
* The advantage of this approach is that you don't get any clutter in your log files. |
* 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. |
* |
* You can find the various activation strategies in the |
* Monolog\Handler\FingersCrossed\ namespace. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class FingersCrossedHandler extends AbstractHandler |
{ |
protected $handler; |
protected $activationStrategy; |
protected $buffering = true; |
protected $bufferSize; |
protected $buffer = array(); |
protected $stopBuffering; |
protected $passthruLevel; |
|
/** |
* @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). |
* @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 Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) |
* @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered |
*/ |
public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) |
{ |
if (null === $activationStrategy) { |
$activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); |
} |
|
// convert simple int activationStrategy to an object |
if (!$activationStrategy instanceof ActivationStrategyInterface) { |
$activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); |
} |
|
$this->handler = $handler; |
$this->activationStrategy = $activationStrategy; |
$this->bufferSize = $bufferSize; |
$this->bubble = $bubble; |
$this->stopBuffering = $stopBuffering; |
|
if ($passthruLevel !== null) { |
$this->passthruLevel = Logger::toMonologLevel($passthruLevel); |
} |
|
if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { |
throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function isHandling(array $record) |
{ |
return true; |
} |
|
/** |
* Manually activate this logger regardless of the activation strategy |
*/ |
public function activate() |
{ |
if ($this->stopBuffering) { |
$this->buffering = false; |
} |
if (!$this->handler instanceof HandlerInterface) { |
$record = end($this->buffer) ?: null; |
|
$this->handler = call_user_func($this->handler, $record, $this); |
if (!$this->handler instanceof HandlerInterface) { |
throw new \RuntimeException("The factory callable should return a HandlerInterface"); |
} |
} |
$this->handler->handleBatch($this->buffer); |
$this->buffer = array(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
if ($this->processors) { |
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
} |
|
if ($this->buffering) { |
$this->buffer[] = $record; |
if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { |
array_shift($this->buffer); |
} |
if ($this->activationStrategy->isHandlerActivated($record)) { |
$this->activate(); |
} |
} else { |
$this->handler->handle($record); |
} |
|
return false === $this->bubble; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
if (null !== $this->passthruLevel) { |
$level = $this->passthruLevel; |
$this->buffer = array_filter($this->buffer, function ($record) use ($level) { |
return $record['level'] >= $level; |
}); |
if (count($this->buffer) > 0) { |
$this->handler->handleBatch($this->buffer); |
$this->buffer = array(); |
} |
} |
} |
|
/** |
* Resets the state of the handler. Stops forwarding records to the wrapped handler. |
*/ |
public function reset() |
{ |
$this->buffering = true; |
} |
|
/** |
* Clears the buffer without flushing any messages down to the wrapped handler. |
* |
* It also resets the handler to its initial buffering state. |
*/ |
public function clear() |
{ |
$this->buffer = array(); |
$this->reset(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php |
@@ -0,0 +1,195 @@ |
<?php |
|
/* |
* 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\WildfireFormatter; |
|
/** |
* Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. |
* |
* @author Eric Clemmons (@ericclemmons) <eric@uxdriven.com> |
*/ |
class FirePHPHandler extends AbstractProcessingHandler |
{ |
/** |
* WildFire JSON header message format |
*/ |
const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; |
|
/** |
* FirePHP structure for parsing messages & their presentation |
*/ |
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 |
*/ |
const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; |
|
/** |
* Header prefix for Wildfire to recognize & parse headers |
*/ |
const HEADER_PREFIX = 'X-Wf'; |
|
/** |
* Whether or not Wildfire vendor-specific headers have been generated & sent yet |
*/ |
protected static $initialized = false; |
|
/** |
* Shared static message index between potentially multiple handlers |
* @var int |
*/ |
protected static $messageIndex = 1; |
|
protected static $sendHeaders = true; |
|
/** |
* Base header creation function used by init headers & record headers |
* |
* @param array $meta Wildfire Plugin, Protocol & Structure Indexes |
* @param string $message Log message |
* @return array Complete header string ready for the client as key and message as value |
*/ |
protected function createHeader(array $meta, $message) |
{ |
$header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); |
|
return array($header => $message); |
} |
|
/** |
* Creates message header from record |
* |
* @see createHeader() |
* @param array $record |
* @return string |
*/ |
protected function createRecordHeader(array $record) |
{ |
// 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. |
return $this->createHeader( |
array(1, 1, 1, self::$messageIndex++), |
$record['formatted'] |
); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new WildfireFormatter(); |
} |
|
/** |
* Wildfire initialization headers to enable message parsing |
* |
* @see createHeader() |
* @see sendHeader() |
* @return array |
*/ |
protected function getInitHeaders() |
{ |
// Initial payload consists of required headers for Wildfire |
return array_merge( |
$this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), |
$this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), |
$this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) |
); |
} |
|
/** |
* Send header string to the client |
* |
* @param string $header |
* @param string $content |
*/ |
protected function sendHeader($header, $content) |
{ |
if (!headers_sent() && self::$sendHeaders) { |
header(sprintf('%s: %s', $header, $content)); |
} |
} |
|
/** |
* Creates & sends header for a record, ensuring init headers have been sent prior |
* |
* @see sendHeader() |
* @see sendInitHeaders() |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
if (!self::$sendHeaders) { |
return; |
} |
|
// WildFire-specific headers must be sent prior to any messages |
if (!self::$initialized) { |
self::$initialized = true; |
|
self::$sendHeaders = $this->headersAccepted(); |
if (!self::$sendHeaders) { |
return; |
} |
|
foreach ($this->getInitHeaders() as $header => $content) { |
$this->sendHeader($header, $content); |
} |
} |
|
$header = $this->createRecordHeader($record); |
if (trim(current($header)) !== '') { |
$this->sendHeader(key($header), current($header)); |
} |
} |
|
/** |
* Verifies if the headers are accepted by the current user agent |
* |
* @return Boolean |
*/ |
protected function headersAccepted() |
{ |
if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { |
return true; |
} |
|
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; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php |
@@ -0,0 +1,350 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Sends notifications through the hipchat api to a hipchat room |
* |
* Notes: |
* API token - HipChat API token |
* Room - HipChat Room Id or name, where messages are sent |
* Name - Name used to send the message (from) |
* notify - Should the message trigger a notification in the clients |
* version - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2) |
* |
* @author Rafael Dohms <rafael@doh.ms> |
* @see https://www.hipchat.com/docs/api |
*/ |
class HipChatHandler extends SocketHandler |
{ |
/** |
* Use API version 1 |
*/ |
const API_V1 = 'v1'; |
|
/** |
* Use API version v2 |
*/ |
const API_V2 = 'v2'; |
|
/** |
* The maximum allowed length for the name used in the "from" field. |
*/ |
const MAXIMUM_NAME_LENGTH = 15; |
|
/** |
* The maximum allowed length for the message. |
*/ |
const MAXIMUM_MESSAGE_LENGTH = 9500; |
|
/** |
* @var string |
*/ |
private $token; |
|
/** |
* @var string |
*/ |
private $room; |
|
/** |
* @var string |
*/ |
private $name; |
|
/** |
* @var bool |
*/ |
private $notify; |
|
/** |
* @var string |
*/ |
private $format; |
|
/** |
* @var string |
*/ |
private $host; |
|
/** |
* @var string |
*/ |
private $version; |
|
/** |
* @param string $token HipChat API Token |
* @param string $room The room that should be alerted of the message (Id or Name) |
* @param string $name Name used in the "from" field. |
* @param bool $notify Trigger a notification in clients or not |
* @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 $useSSL Whether to connect via SSL. |
* @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) |
* @param string $host The HipChat server hostname. |
* @param string $version The HipChat API version (default HipChatHandler::API_V1) |
*/ |
public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) |
{ |
if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { |
throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); |
} |
|
$connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80'; |
parent::__construct($connectionString, $level, $bubble); |
|
$this->token = $token; |
$this->name = $name; |
$this->notify = $notify; |
$this->room = $room; |
$this->format = $format; |
$this->host = $host; |
$this->version = $version; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
* @return string |
*/ |
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
/** |
* Builds the body of API call |
* |
* @param array $record |
* @return string |
*/ |
private function buildContent($record) |
{ |
$dataArray = array( |
'notify' => $this->version == self::API_V1 ? |
($this->notify ? 1 : 0) : |
($this->notify ? 'true' : 'false'), |
'message' => $record['formatted'], |
'message_format' => $this->format, |
'color' => $this->getAlertColor($record['level']), |
); |
|
if (!$this->validateStringLength($dataArray['message'], static::MAXIMUM_MESSAGE_LENGTH)) { |
if (function_exists('mb_substr')) { |
$dataArray['message'] = mb_substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]'; |
} else { |
$dataArray['message'] = substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]'; |
} |
} |
|
// if we are using the legacy API then we need to send some additional information |
if ($this->version == self::API_V1) { |
$dataArray['room_id'] = $this->room; |
} |
|
// append the sender name if it is set |
// always append it if we use the v1 api (it is required in v1) |
if ($this->version == self::API_V1 || $this->name !== null) { |
$dataArray['from'] = (string) $this->name; |
} |
|
return http_build_query($dataArray); |
} |
|
/** |
* Builds the header of the API Call |
* |
* @param string $content |
* @return string |
*/ |
private function buildHeader($content) |
{ |
if ($this->version == self::API_V1) { |
$header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n"; |
} else { |
// needed for rooms with special (spaces, etc) characters in the name |
$room = rawurlencode($this->room); |
$header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n"; |
} |
|
$header .= "Host: {$this->host}\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
/** |
* Assigns a color to each level of log records. |
* |
* @param int $level |
* @return string |
*/ |
protected function getAlertColor($level) |
{ |
switch (true) { |
case $level >= Logger::ERROR: |
return 'red'; |
case $level >= Logger::WARNING: |
return 'yellow'; |
case $level >= Logger::INFO: |
return 'green'; |
case $level == Logger::DEBUG: |
return 'gray'; |
default: |
return 'yellow'; |
} |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
parent::write($record); |
$this->closeSocket(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
if (count($records) == 0) { |
return true; |
} |
|
$batchRecords = $this->combineRecords($records); |
|
$handled = false; |
foreach ($batchRecords as $batchRecord) { |
if ($this->isHandling($batchRecord)) { |
$this->write($batchRecord); |
$handled = true; |
} |
} |
|
if (!$handled) { |
return false; |
} |
|
return false === $this->bubble; |
} |
|
/** |
* Combines multiple records into one. Error level of the combined record |
* will be the highest level from the given records. Datetime will be taken |
* from the first record. |
* |
* @param $records |
* @return array |
*/ |
private function combineRecords($records) |
{ |
$batchRecord = null; |
$batchRecords = array(); |
$messages = array(); |
$formattedMessages = array(); |
$level = 0; |
$levelName = null; |
$datetime = null; |
|
foreach ($records as $record) { |
$record = $this->processRecord($record); |
|
if ($record['level'] > $level) { |
$level = $record['level']; |
$levelName = $record['level_name']; |
} |
|
if (null === $datetime) { |
$datetime = $record['datetime']; |
} |
|
$messages[] = $record['message']; |
$messageStr = implode(PHP_EOL, $messages); |
$formattedMessages[] = $this->getFormatter()->format($record); |
$formattedMessageStr = implode('', $formattedMessages); |
|
$batchRecord = array( |
'message' => $messageStr, |
'formatted' => $formattedMessageStr, |
'context' => array(), |
'extra' => array(), |
); |
|
if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) { |
// Pop the last message and implode the remaining messages |
$lastMessage = array_pop($messages); |
$lastFormattedMessage = array_pop($formattedMessages); |
$batchRecord['message'] = implode(PHP_EOL, $messages); |
$batchRecord['formatted'] = implode('', $formattedMessages); |
|
$batchRecords[] = $batchRecord; |
$messages = array($lastMessage); |
$formattedMessages = array($lastFormattedMessage); |
|
$batchRecord = null; |
} |
} |
|
if (null !== $batchRecord) { |
$batchRecords[] = $batchRecord; |
} |
|
// Set the max level and datetime for all records |
foreach ($batchRecords as &$batchRecord) { |
$batchRecord = array_merge( |
$batchRecord, |
array( |
'level' => $level, |
'level_name' => $levelName, |
'datetime' => $datetime, |
) |
); |
} |
|
return $batchRecords; |
} |
|
/** |
* Validates the length of a string. |
* |
* If the `mb_strlen()` function is available, it will use that, as HipChat |
* allows UTF-8 characters. Otherwise, it will fall back to `strlen()`. |
* |
* Note that this might cause false failures in the specific case of using |
* a valid name with less than 16 characters, but 16 or more bytes, on a |
* system where `mb_strlen()` is unavailable. |
* |
* @param string $str |
* @param int $length |
* |
* @return bool |
*/ |
private function validateStringLength($str, $length) |
{ |
if (function_exists('mb_strlen')) { |
return (mb_strlen($str) <= $length); |
} |
|
return (strlen($str) <= $length); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php |
@@ -0,0 +1,185 @@ |
<?php |
|
/* |
* 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\Logger; |
use Monolog\Formatter\LineFormatter; |
|
/** |
* NativeMailerHandler uses the mail() function to send the emails |
* |
* @author Christophe Coevoet <stof@notk.org> |
* @author Mark Garrett <mark@moderndeveloperllc.com> |
*/ |
class NativeMailerHandler extends MailHandler |
{ |
/** |
* The email addresses to which the message will be sent |
* @var array |
*/ |
protected $to; |
|
/** |
* The subject of the email |
* @var string |
*/ |
protected $subject; |
|
/** |
* Optional headers for the message |
* @var array |
*/ |
protected $headers = array(); |
|
/** |
* Optional parameters for the message |
* @var array |
*/ |
protected $parameters = array(); |
|
/** |
* The wordwrap length for the message |
* @var int |
*/ |
protected $maxColumnWidth; |
|
/** |
* The Content-type for the message |
* @var string |
*/ |
protected $contentType = 'text/plain'; |
|
/** |
* The encoding for the message |
* @var string |
*/ |
protected $encoding = 'utf-8'; |
|
/** |
* @param string|array $to The receiver of the mail |
* @param string $subject The subject of the mail |
* @param string $from The sender of the mail |
* @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 int $maxColumnWidth The maximum column width that the message lines will have |
*/ |
public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true, $maxColumnWidth = 70) |
{ |
parent::__construct($level, $bubble); |
$this->to = is_array($to) ? $to : array($to); |
$this->subject = $subject; |
$this->addHeader(sprintf('From: %s', $from)); |
$this->maxColumnWidth = $maxColumnWidth; |
} |
|
/** |
* Add headers to the message |
* |
* @param string|array $headers Custom added headers |
* @return self |
*/ |
public function addHeader($headers) |
{ |
foreach ((array) $headers as $header) { |
if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { |
throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); |
} |
$this->headers[] = $header; |
} |
|
return $this; |
} |
|
/** |
* Add parameters to the message |
* |
* @param string|array $parameters Custom added parameters |
* @return self |
*/ |
public function addParameter($parameters) |
{ |
$this->parameters = array_merge($this->parameters, (array) $parameters); |
|
return $this; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function send($content, array $records) |
{ |
$content = wordwrap($content, $this->maxColumnWidth); |
$headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); |
$headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n"; |
if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) { |
$headers .= 'MIME-Version: 1.0' . "\r\n"; |
} |
|
$subject = $this->subject; |
if ($records) { |
$subjectFormatter = new LineFormatter($this->subject); |
$subject = $subjectFormatter->format($this->getHighestRecord($records)); |
} |
|
$parameters = implode(' ', $this->parameters); |
foreach ($this->to as $to) { |
mail($to, $subject, $content, $headers, $parameters); |
} |
} |
|
/** |
* @return string $contentType |
*/ |
public function getContentType() |
{ |
return $this->contentType; |
} |
|
/** |
* @return string $encoding |
*/ |
public function getEncoding() |
{ |
return $this->encoding; |
} |
|
/** |
* @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML |
* messages. |
* @return self |
*/ |
public function setContentType($contentType) |
{ |
if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { |
throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); |
} |
|
$this->contentType = $contentType; |
|
return $this; |
} |
|
/** |
* @param string $encoding |
* @return self |
*/ |
public function setEncoding($encoding) |
{ |
if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { |
throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); |
} |
|
$this->encoding = $encoding; |
|
return $this; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php |
@@ -0,0 +1,202 @@ |
<?php |
|
/* |
* 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\Logger; |
use Monolog\Formatter\NormalizerFormatter; |
|
/** |
* Class to record a log on a NewRelic application. |
* Enabling New Relic High Security mode may prevent capture of useful information. |
* |
* @see https://docs.newrelic.com/docs/agents/php-agent |
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security |
*/ |
class NewRelicHandler extends AbstractProcessingHandler |
{ |
/** |
* Name of the New Relic application that will receive logs from this handler. |
* |
* @var string |
*/ |
protected $appName; |
|
/** |
* Name of the current transaction |
* |
* @var string |
*/ |
protected $transactionName; |
|
/** |
* Some context and extra data is passed into the handler as arrays of values. Do we send them as is |
* (useful if we are using the API), or explode them for display on the NewRelic RPM website? |
* |
* @var bool |
*/ |
protected $explodeArrays; |
|
/** |
* {@inheritDoc} |
* |
* @param string $appName |
* @param bool $explodeArrays |
* @param string $transactionName |
*/ |
public function __construct( |
$level = Logger::ERROR, |
$bubble = true, |
$appName = null, |
$explodeArrays = false, |
$transactionName = null |
) { |
parent::__construct($level, $bubble); |
|
$this->appName = $appName; |
$this->explodeArrays = $explodeArrays; |
$this->transactionName = $transactionName; |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function write(array $record) |
{ |
if (!$this->isNewRelicEnabled()) { |
throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); |
} |
|
if ($appName = $this->getAppName($record['context'])) { |
$this->setNewRelicAppName($appName); |
} |
|
if ($transactionName = $this->getTransactionName($record['context'])) { |
$this->setNewRelicTransactionName($transactionName); |
unset($record['formatted']['context']['transaction_name']); |
} |
|
if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { |
newrelic_notice_error($record['message'], $record['context']['exception']); |
unset($record['formatted']['context']['exception']); |
} else { |
newrelic_notice_error($record['message']); |
} |
|
if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) { |
foreach ($record['formatted']['context'] as $key => $parameter) { |
if (is_array($parameter) && $this->explodeArrays) { |
foreach ($parameter as $paramKey => $paramValue) { |
$this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); |
} |
} else { |
$this->setNewRelicParameter('context_' . $key, $parameter); |
} |
} |
} |
|
if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) { |
foreach ($record['formatted']['extra'] as $key => $parameter) { |
if (is_array($parameter) && $this->explodeArrays) { |
foreach ($parameter as $paramKey => $paramValue) { |
$this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); |
} |
} else { |
$this->setNewRelicParameter('extra_' . $key, $parameter); |
} |
} |
} |
} |
|
/** |
* Checks whether the NewRelic extension is enabled in the system. |
* |
* @return bool |
*/ |
protected function isNewRelicEnabled() |
{ |
return extension_loaded('newrelic'); |
} |
|
/** |
* Returns the appname where this log should be sent. Each log can override the default appname, set in this |
* handler's constructor, by providing the appname in it's context. |
* |
* @param array $context |
* @return null|string |
*/ |
protected function getAppName(array $context) |
{ |
if (isset($context['appname'])) { |
return $context['appname']; |
} |
|
return $this->appName; |
} |
|
/** |
* Returns the name of the current transaction. Each log can override the default transaction name, set in this |
* handler's constructor, by providing the transaction_name in it's context |
* |
* @param array $context |
* |
* @return null|string |
*/ |
protected function getTransactionName(array $context) |
{ |
if (isset($context['transaction_name'])) { |
return $context['transaction_name']; |
} |
|
return $this->transactionName; |
} |
|
/** |
* Sets the NewRelic application that should receive this log. |
* |
* @param string $appName |
*/ |
protected function setNewRelicAppName($appName) |
{ |
newrelic_set_appname($appName); |
} |
|
/** |
* Overwrites the name of the current transaction |
* |
* @param string $transactionName |
*/ |
protected function setNewRelicTransactionName($transactionName) |
{ |
newrelic_name_transaction($transactionName); |
} |
|
/** |
* @param string $key |
* @param mixed $value |
*/ |
protected function setNewRelicParameter($key, $value) |
{ |
if (null === $value || is_scalar($value)) { |
newrelic_add_custom_parameter($key, $value); |
} else { |
newrelic_add_custom_parameter($key, @json_encode($value)); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new NormalizerFormatter(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php |
@@ -0,0 +1,242 @@ |
<?php |
|
/* |
* 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 Exception; |
use Monolog\Formatter\LineFormatter; |
use Monolog\Logger; |
use PhpConsole\Connector; |
use PhpConsole\Handler; |
use PhpConsole\Helper; |
|
/** |
* Monolog handler for Google Chrome extension "PHP Console" |
* |
* Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely |
* |
* Usage: |
* 1. Install Google Chrome extension https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef |
* 2. See overview https://github.com/barbushin/php-console#overview |
* 3. Install PHP Console library https://github.com/barbushin/php-console#installation |
* 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) |
* |
* $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); |
* \Monolog\ErrorHandler::register($logger); |
* echo $undefinedVar; |
* $logger->addDebug('SELECT * FROM users', array('db', 'time' => 0.012)); |
* PC::debug($_SERVER); // PHP Console debugger for any type of vars |
* |
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin |
*/ |
class PHPConsoleHandler extends AbstractProcessingHandler |
{ |
private $options = array( |
'enabled' => true, // bool Is PHP Console server enabled |
'classesPartialsTraceIgnore' => array('Monolog\\'), // array Hide calls of classes started with... |
'debugTagsKeysInContext' => array(0, 'tag'), // bool Is PHP Console server enabled |
'useOwnErrorsHandler' => false, // bool Enable errors handling |
'useOwnExceptionsHandler' => false, // bool Enable exceptions handling |
'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths |
'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') |
'serverEncoding' => null, // string|null Server internal encoding |
'headersLimit' => null, // int|null Set headers size limit for your web-server |
'password' => null, // string|null Protect PHP Console connection by password |
'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed |
'ipMasks' => array(), // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') |
'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) |
'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings |
'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level |
'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number |
'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item |
'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON |
'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug |
'dataStorage' => null, // PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) |
); |
|
/** @var Connector */ |
private $connector; |
|
/** |
* @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details |
* @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) |
* @param int $level |
* @param bool $bubble |
* @throws Exception |
*/ |
public function __construct(array $options = array(), Connector $connector = null, $level = Logger::DEBUG, $bubble = true) |
{ |
if (!class_exists('PhpConsole\Connector')) { |
throw new Exception('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); |
} |
parent::__construct($level, $bubble); |
$this->options = $this->initOptions($options); |
$this->connector = $this->initConnector($connector); |
} |
|
private function initOptions(array $options) |
{ |
$wrongOptions = array_diff(array_keys($options), array_keys($this->options)); |
if ($wrongOptions) { |
throw new Exception('Unknown options: ' . implode(', ', $wrongOptions)); |
} |
|
return array_replace($this->options, $options); |
} |
|
private function initConnector(Connector $connector = null) |
{ |
if (!$connector) { |
if ($this->options['dataStorage']) { |
Connector::setPostponeStorage($this->options['dataStorage']); |
} |
$connector = Connector::getInstance(); |
} |
|
if ($this->options['registerHelper'] && !Helper::isRegistered()) { |
Helper::register(); |
} |
|
if ($this->options['enabled'] && $connector->isActiveClient()) { |
if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { |
$handler = Handler::getInstance(); |
$handler->setHandleErrors($this->options['useOwnErrorsHandler']); |
$handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); |
$handler->start(); |
} |
if ($this->options['sourcesBasePath']) { |
$connector->setSourcesBasePath($this->options['sourcesBasePath']); |
} |
if ($this->options['serverEncoding']) { |
$connector->setServerEncoding($this->options['serverEncoding']); |
} |
if ($this->options['password']) { |
$connector->setPassword($this->options['password']); |
} |
if ($this->options['enableSslOnlyMode']) { |
$connector->enableSslOnlyMode(); |
} |
if ($this->options['ipMasks']) { |
$connector->setAllowedIpMasks($this->options['ipMasks']); |
} |
if ($this->options['headersLimit']) { |
$connector->setHeadersLimit($this->options['headersLimit']); |
} |
if ($this->options['detectDumpTraceAndSource']) { |
$connector->getDebugDispatcher()->detectTraceAndSource = true; |
} |
$dumper = $connector->getDumper(); |
$dumper->levelLimit = $this->options['dumperLevelLimit']; |
$dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; |
$dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; |
$dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; |
$dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; |
if ($this->options['enableEvalListener']) { |
$connector->startEvalRequestsListener(); |
} |
} |
|
return $connector; |
} |
|
public function getConnector() |
{ |
return $this->connector; |
} |
|
public function getOptions() |
{ |
return $this->options; |
} |
|
public function handle(array $record) |
{ |
if ($this->options['enabled'] && $this->connector->isActiveClient()) { |
return parent::handle($record); |
} |
|
return !$this->bubble; |
} |
|
/** |
* Writes the record down to the log of the implementing handler |
* |
* @param array $record |
* @return void |
*/ |
protected function write(array $record) |
{ |
if ($record['level'] < Logger::NOTICE) { |
$this->handleDebugRecord($record); |
} elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) { |
$this->handleExceptionRecord($record); |
} else { |
$this->handleErrorRecord($record); |
} |
} |
|
private function handleDebugRecord(array $record) |
{ |
$tags = $this->getRecordTags($record); |
$message = $record['message']; |
if ($record['context']) { |
$message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context']))); |
} |
$this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); |
} |
|
private function handleExceptionRecord(array $record) |
{ |
$this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); |
} |
|
private function handleErrorRecord(array $record) |
{ |
$context = $record['context']; |
|
$this->connector->getErrorsDispatcher()->dispatchError( |
isset($context['code']) ? $context['code'] : null, |
isset($context['message']) ? $context['message'] : $record['message'], |
isset($context['file']) ? $context['file'] : null, |
isset($context['line']) ? $context['line'] : null, |
$this->options['classesPartialsTraceIgnore'] |
); |
} |
|
private function getRecordTags(array &$record) |
{ |
$tags = null; |
if (!empty($record['context'])) { |
$context = & $record['context']; |
foreach ($this->options['debugTagsKeysInContext'] as $key) { |
if (!empty($context[$key])) { |
$tags = $context[$key]; |
if ($key === 0) { |
array_shift($context); |
} else { |
unset($context[$key]); |
} |
break; |
} |
} |
} |
|
return $tags ?: strtolower($record['level_name']); |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('%message%'); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php |
@@ -0,0 +1,185 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Sends notifications through the pushover api to mobile phones |
* |
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com> |
* @see https://www.pushover.net/api |
*/ |
class PushoverHandler extends SocketHandler |
{ |
private $token; |
private $users; |
private $title; |
private $user; |
private $retry; |
private $expire; |
|
private $highPriorityLevel; |
private $emergencyLevel; |
private $useFormattedMessage = false; |
|
/** |
* All parameters that can be sent to Pushover |
* @see https://pushover.net/api |
* @var array |
*/ |
private $parameterNames = array( |
'token' => true, |
'user' => true, |
'message' => true, |
'device' => true, |
'title' => true, |
'url' => true, |
'url_title' => true, |
'priority' => true, |
'timestamp' => true, |
'sound' => true, |
'retry' => true, |
'expire' => true, |
'callback' => true, |
); |
|
/** |
* Sounds the api supports by default |
* @see https://pushover.net/api#sounds |
* @var array |
*/ |
private $sounds = array( |
'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', |
'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', |
'persistent', 'echo', 'updown', 'none', |
); |
|
/** |
* @param string $token Pushover api token |
* @param string|array $users Pushover user id or array of ids the message will be sent to |
* @param string $title Title sent to the Pushover API |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not |
* the pushover.net app owner. OpenSSL is required for this option. |
* @param int $highPriorityLevel The minimum logging level at which this handler will start |
* sending "high priority" requests to the Pushover API |
* @param int $emergencyLevel The minimum logging level at which this handler will start |
* sending "emergency" requests to the Pushover API |
* @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user. |
* @param int $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds). |
*/ |
public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200) |
{ |
$connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; |
parent::__construct($connectionString, $level, $bubble); |
|
$this->token = $token; |
$this->users = (array) $users; |
$this->title = $title ?: gethostname(); |
$this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); |
$this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); |
$this->retry = $retry; |
$this->expire = $expire; |
} |
|
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
private function buildContent($record) |
{ |
// Pushover has a limit of 512 characters on title and message combined. |
$maxMessageLength = 512 - strlen($this->title); |
|
$message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; |
$message = substr($message, 0, $maxMessageLength); |
|
$timestamp = $record['datetime']->getTimestamp(); |
|
$dataArray = array( |
'token' => $this->token, |
'user' => $this->user, |
'message' => $message, |
'title' => $this->title, |
'timestamp' => $timestamp, |
); |
|
if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { |
$dataArray['priority'] = 2; |
$dataArray['retry'] = $this->retry; |
$dataArray['expire'] = $this->expire; |
} elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { |
$dataArray['priority'] = 1; |
} |
|
// First determine the available parameters |
$context = array_intersect_key($record['context'], $this->parameterNames); |
$extra = array_intersect_key($record['extra'], $this->parameterNames); |
|
// Least important info should be merged with subsequent info |
$dataArray = array_merge($extra, $context, $dataArray); |
|
// Only pass sounds that are supported by the API |
if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { |
unset($dataArray['sound']); |
} |
|
return http_build_query($dataArray); |
} |
|
private function buildHeader($content) |
{ |
$header = "POST /1/messages.json HTTP/1.1\r\n"; |
$header .= "Host: api.pushover.net\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
protected function write(array $record) |
{ |
foreach ($this->users as $user) { |
$this->user = $user; |
|
parent::write($record); |
$this->closeSocket(); |
} |
|
$this->user = null; |
} |
|
public function setHighPriorityLevel($value) |
{ |
$this->highPriorityLevel = $value; |
} |
|
public function setEmergencyLevel($value) |
{ |
$this->emergencyLevel = $value; |
} |
|
/** |
* Use the formatted message? |
* @param bool $value |
*/ |
public function useFormattedMessage($value) |
{ |
$this->useFormattedMessage = (boolean) $value; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php |
@@ -0,0 +1,232 @@ |
<?php |
|
/* |
* 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\LineFormatter; |
use Monolog\Formatter\FormatterInterface; |
use Monolog\Logger; |
use Raven_Client; |
|
/** |
* Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server |
* using raven-php (https://github.com/getsentry/raven-php) |
* |
* @author Marc Abramowitz <marc@marc-abramowitz.com> |
*/ |
class RavenHandler extends AbstractProcessingHandler |
{ |
/** |
* Translates Monolog log levels to Raven log levels. |
*/ |
private $logLevels = array( |
Logger::DEBUG => Raven_Client::DEBUG, |
Logger::INFO => Raven_Client::INFO, |
Logger::NOTICE => Raven_Client::INFO, |
Logger::WARNING => Raven_Client::WARNING, |
Logger::ERROR => Raven_Client::ERROR, |
Logger::CRITICAL => Raven_Client::FATAL, |
Logger::ALERT => Raven_Client::FATAL, |
Logger::EMERGENCY => Raven_Client::FATAL, |
); |
|
/** |
* @var string should represent the current version of the calling |
* software. Can be any string (git commit, version number) |
*/ |
private $release; |
|
/** |
* @var Raven_Client the client object that sends the message to the server |
*/ |
protected $ravenClient; |
|
/** |
* @var LineFormatter The formatter to use for the logs generated via handleBatch() |
*/ |
protected $batchFormatter; |
|
/** |
* @param Raven_Client $ravenClient |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
|
$this->ravenClient = $ravenClient; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function handleBatch(array $records) |
{ |
$level = $this->level; |
|
// filter records based on their level |
$records = array_filter($records, function ($record) use ($level) { |
return $record['level'] >= $level; |
}); |
|
if (!$records) { |
return; |
} |
|
// the record with the highest severity is the "main" one |
$record = array_reduce($records, function ($highest, $record) { |
if ($record['level'] > $highest['level']) { |
return $record; |
} |
|
return $highest; |
}); |
|
// the other ones are added as a context item |
$logs = array(); |
foreach ($records as $r) { |
$logs[] = $this->processRecord($r); |
} |
|
if ($logs) { |
$record['context']['logs'] = (string) $this->getBatchFormatter()->formatBatch($logs); |
} |
|
$this->handle($record); |
} |
|
/** |
* Sets the formatter for the logs generated by handleBatch(). |
* |
* @param FormatterInterface $formatter |
*/ |
public function setBatchFormatter(FormatterInterface $formatter) |
{ |
$this->batchFormatter = $formatter; |
} |
|
/** |
* Gets the formatter for the logs generated by handleBatch(). |
* |
* @return FormatterInterface |
*/ |
public function getBatchFormatter() |
{ |
if (!$this->batchFormatter) { |
$this->batchFormatter = $this->getDefaultBatchFormatter(); |
} |
|
return $this->batchFormatter; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$previousUserContext = false; |
$options = array(); |
$options['level'] = $this->logLevels[$record['level']]; |
$options['tags'] = array(); |
if (!empty($record['extra']['tags'])) { |
$options['tags'] = array_merge($options['tags'], $record['extra']['tags']); |
unset($record['extra']['tags']); |
} |
if (!empty($record['context']['tags'])) { |
$options['tags'] = array_merge($options['tags'], $record['context']['tags']); |
unset($record['context']['tags']); |
} |
if (!empty($record['context']['fingerprint'])) { |
$options['fingerprint'] = $record['context']['fingerprint']; |
unset($record['context']['fingerprint']); |
} |
if (!empty($record['context']['logger'])) { |
$options['logger'] = $record['context']['logger']; |
unset($record['context']['logger']); |
} else { |
$options['logger'] = $record['channel']; |
} |
foreach ($this->getExtraParameters() as $key) { |
foreach (array('extra', 'context') as $source) { |
if (!empty($record[$source][$key])) { |
$options[$key] = $record[$source][$key]; |
unset($record[$source][$key]); |
} |
} |
} |
if (!empty($record['context'])) { |
$options['extra']['context'] = $record['context']; |
if (!empty($record['context']['user'])) { |
$previousUserContext = $this->ravenClient->context->user; |
$this->ravenClient->user_context($record['context']['user']); |
unset($options['extra']['context']['user']); |
} |
} |
if (!empty($record['extra'])) { |
$options['extra']['extra'] = $record['extra']; |
} |
|
if (!empty($this->release) && !isset($options['release'])) { |
$options['release'] = $this->release; |
} |
|
if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { |
$options['extra']['message'] = $record['formatted']; |
$this->ravenClient->captureException($record['context']['exception'], $options); |
} else { |
$this->ravenClient->captureMessage($record['formatted'], array(), $options); |
} |
|
if ($previousUserContext !== false) { |
$this->ravenClient->user_context($previousUserContext); |
} |
} |
|
/** |
* {@inheritDoc} |
*/ |
protected function getDefaultFormatter() |
{ |
return new LineFormatter('[%channel%] %message%'); |
} |
|
/** |
* Gets the default formatter for the logs generated by handleBatch(). |
* |
* @return FormatterInterface |
*/ |
protected function getDefaultBatchFormatter() |
{ |
return new LineFormatter(); |
} |
|
/** |
* Gets extra parameters supported by Raven that can be found in "extra" and "context" |
* |
* @return array |
*/ |
protected function getExtraParameters() |
{ |
return array('checksum', 'release', 'event_id'); |
} |
|
/** |
* @param string $value |
* @return self |
*/ |
public function setRelease($value) |
{ |
$this->release = $value; |
|
return $this; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php |
@@ -0,0 +1,132 @@ |
<?php |
|
/* |
* 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 RollbarNotifier; |
use Exception; |
use Monolog\Logger; |
|
/** |
* Sends errors to Rollbar |
* |
* If the context data contains a `payload` key, that is used as an array |
* of payload options to RollbarNotifier's report_message/report_exception methods. |
* |
* Rollbar's context info will contain the context + extra keys from the log record |
* merged, and then on top of that a few keys: |
* |
* - level (rollbar level name) |
* - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) |
* - channel |
* - datetime (unix timestamp) |
* |
* @author Paul Statezny <paulstatezny@gmail.com> |
*/ |
class RollbarHandler extends AbstractProcessingHandler |
{ |
/** |
* Rollbar notifier |
* |
* @var RollbarNotifier |
*/ |
protected $rollbarNotifier; |
|
protected $levelMap = array( |
Logger::DEBUG => 'debug', |
Logger::INFO => 'info', |
Logger::NOTICE => 'info', |
Logger::WARNING => 'warning', |
Logger::ERROR => 'error', |
Logger::CRITICAL => 'critical', |
Logger::ALERT => 'critical', |
Logger::EMERGENCY => 'critical', |
); |
|
/** |
* Records whether any log records have been added since the last flush of the rollbar notifier |
* |
* @var bool |
*/ |
private $hasRecords = false; |
|
protected $initialized = false; |
|
/** |
* @param RollbarNotifier $rollbarNotifier RollbarNotifier object constructed with valid token |
* @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(RollbarNotifier $rollbarNotifier, $level = Logger::ERROR, $bubble = true) |
{ |
$this->rollbarNotifier = $rollbarNotifier; |
|
parent::__construct($level, $bubble); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
if (!$this->initialized) { |
// __destructor() doesn't get called on Fatal errors |
register_shutdown_function(array($this, 'close')); |
$this->initialized = true; |
} |
|
$context = $record['context']; |
$payload = array(); |
if (isset($context['payload'])) { |
$payload = $context['payload']; |
unset($context['payload']); |
} |
$context = array_merge($context, $record['extra'], array( |
'level' => $this->levelMap[$record['level']], |
'monolog_level' => $record['level_name'], |
'channel' => $record['channel'], |
'datetime' => $record['datetime']->format('U'), |
)); |
|
if (isset($context['exception']) && $context['exception'] instanceof Exception) { |
$payload['level'] = $context['level']; |
$exception = $context['exception']; |
unset($context['exception']); |
|
$this->rollbarNotifier->report_exception($exception, $context, $payload); |
} else { |
$this->rollbarNotifier->report_message( |
$record['message'], |
$context['level'], |
$context, |
$payload |
); |
} |
|
$this->hasRecords = true; |
} |
|
public function flush() |
{ |
if ($this->hasRecords) { |
$this->rollbarNotifier->flush(); |
$this->hasRecords = false; |
} |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
$this->flush(); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php |
@@ -0,0 +1,178 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Stores logs to files that are rotated every day and a limited number of files are kept. |
* |
* This rotation is only intended to be used as a workaround. Using logrotate to |
* handle the rotation is strongly encouraged when you can use it. |
* |
* @author Christophe Coevoet <stof@notk.org> |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class RotatingFileHandler extends StreamHandler |
{ |
const FILE_PER_DAY = 'Y-m-d'; |
const FILE_PER_MONTH = 'Y-m'; |
const FILE_PER_YEAR = 'Y'; |
|
protected $filename; |
protected $maxFiles; |
protected $mustRotate; |
protected $nextRotation; |
protected $filenameFormat; |
protected $dateFormat; |
|
/** |
* @param string $filename |
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited) |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) |
* @param Boolean $useLocking Try to lock log file before doing any writes |
*/ |
public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) |
{ |
$this->filename = $filename; |
$this->maxFiles = (int) $maxFiles; |
$this->nextRotation = new \DateTime('tomorrow'); |
$this->filenameFormat = '{filename}-{date}'; |
$this->dateFormat = 'Y-m-d'; |
|
parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
parent::close(); |
|
if (true === $this->mustRotate) { |
$this->rotate(); |
} |
} |
|
public function setFilenameFormat($filenameFormat, $dateFormat) |
{ |
if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { |
trigger_error( |
'Invalid date format - format must be one of '. |
'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. |
'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '. |
'date formats using slashes, underscores and/or dots instead of dashes.', |
E_USER_DEPRECATED |
); |
} |
if (substr_count($filenameFormat, '{date}') === 0) { |
trigger_error( |
'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.', |
E_USER_DEPRECATED |
); |
} |
$this->filenameFormat = $filenameFormat; |
$this->dateFormat = $dateFormat; |
$this->url = $this->getTimedFilename(); |
$this->close(); |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
// on the first record written, if the log is new, we should rotate (once per day) |
if (null === $this->mustRotate) { |
$this->mustRotate = !file_exists($this->url); |
} |
|
if ($this->nextRotation < $record['datetime']) { |
$this->mustRotate = true; |
$this->close(); |
} |
|
parent::write($record); |
} |
|
/** |
* Rotates the files. |
*/ |
protected function rotate() |
{ |
// update filename |
$this->url = $this->getTimedFilename(); |
$this->nextRotation = new \DateTime('tomorrow'); |
|
// skip GC of old logs if files are unlimited |
if (0 === $this->maxFiles) { |
return; |
} |
|
$logFiles = glob($this->getGlobPattern()); |
if ($this->maxFiles >= count($logFiles)) { |
// no files to remove |
return; |
} |
|
// Sorting the files by name to remove the older ones |
usort($logFiles, function ($a, $b) { |
return strcmp($b, $a); |
}); |
|
foreach (array_slice($logFiles, $this->maxFiles) as $file) { |
if (is_writable($file)) { |
// suppress errors here as unlink() might fail if two processes |
// are cleaning up/rotating at the same time |
set_error_handler(function ($errno, $errstr, $errfile, $errline) {}); |
unlink($file); |
restore_error_handler(); |
} |
} |
|
$this->mustRotate = false; |
} |
|
protected function getTimedFilename() |
{ |
$fileInfo = pathinfo($this->filename); |
$timedFilename = str_replace( |
array('{filename}', '{date}'), |
array($fileInfo['filename'], date($this->dateFormat)), |
$fileInfo['dirname'] . '/' . $this->filenameFormat |
); |
|
if (!empty($fileInfo['extension'])) { |
$timedFilename .= '.'.$fileInfo['extension']; |
} |
|
return $timedFilename; |
} |
|
protected function getGlobPattern() |
{ |
$fileInfo = pathinfo($this->filename); |
$glob = str_replace( |
array('{filename}', '{date}'), |
array($fileInfo['filename'], '*'), |
$fileInfo['dirname'] . '/' . $this->filenameFormat |
); |
if (!empty($fileInfo['extension'])) { |
$glob .= '.'.$fileInfo['extension']; |
} |
|
return $glob; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php |
@@ -0,0 +1,294 @@ |
<?php |
|
/* |
* 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\Slack; |
|
use Monolog\Logger; |
use Monolog\Formatter\NormalizerFormatter; |
use Monolog\Formatter\FormatterInterface; |
|
/** |
* Slack record utility helping to log to Slack webhooks or API. |
* |
* @author Greg Kedzierski <greg@gregkedzierski.com> |
* @author Haralan Dobrev <hkdobrev@gmail.com> |
* @see https://api.slack.com/incoming-webhooks |
* @see https://api.slack.com/docs/message-attachments |
*/ |
class SlackRecord |
{ |
const COLOR_DANGER = 'danger'; |
|
const COLOR_WARNING = 'warning'; |
|
const COLOR_GOOD = 'good'; |
|
const COLOR_DEFAULT = '#e3e4e6'; |
|
/** |
* Slack channel (encoded ID or name) |
* @var string|null |
*/ |
private $channel; |
|
/** |
* Name of a bot |
* @var string|null |
*/ |
private $username; |
|
/** |
* User icon e.g. 'ghost', 'http://example.com/user.png' |
* @var string |
*/ |
private $userIcon; |
|
/** |
* Whether the message should be added to Slack as attachment (plain text otherwise) |
* @var bool |
*/ |
private $useAttachment; |
|
/** |
* Whether the the context/extra messages added to Slack as attachments are in a short style |
* @var bool |
*/ |
private $useShortAttachment; |
|
/** |
* Whether the attachment should include context and extra data |
* @var bool |
*/ |
private $includeContextAndExtra; |
|
/** |
* Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] |
* @var array |
*/ |
private $excludeFields; |
|
/** |
* @var FormatterInterface |
*/ |
private $formatter; |
|
/** |
* @var NormalizerFormatter |
*/ |
private $normalizerFormatter; |
|
public function __construct($channel = null, $username = null, $useAttachment = true, $userIcon = null, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array(), FormatterInterface $formatter = null) |
{ |
$this->channel = $channel; |
$this->username = $username; |
$this->userIcon = trim($userIcon, ':'); |
$this->useAttachment = $useAttachment; |
$this->useShortAttachment = $useShortAttachment; |
$this->includeContextAndExtra = $includeContextAndExtra; |
$this->excludeFields = $excludeFields; |
$this->formatter = $formatter; |
|
if ($this->includeContextAndExtra) { |
$this->normalizerFormatter = new NormalizerFormatter(); |
} |
} |
|
public function getSlackData(array $record) |
{ |
$dataArray = array(); |
$record = $this->excludeFields($record); |
|
if ($this->username) { |
$dataArray['username'] = $this->username; |
} |
|
if ($this->channel) { |
$dataArray['channel'] = $this->channel; |
} |
|
if ($this->formatter && !$this->useAttachment) { |
$message = $this->formatter->format($record); |
} else { |
$message = $record['message']; |
} |
|
if ($this->useAttachment) { |
$attachment = array( |
'fallback' => $message, |
'text' => $message, |
'color' => $this->getAttachmentColor($record['level']), |
'fields' => array(), |
'mrkdwn_in' => array('fields'), |
'ts' => $record['datetime']->getTimestamp() |
); |
|
if ($this->useShortAttachment) { |
$attachment['title'] = $record['level_name']; |
} else { |
$attachment['title'] = 'Message'; |
$attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); |
} |
|
|
if ($this->includeContextAndExtra) { |
foreach (array('extra', 'context') as $key) { |
if (empty($record[$key])) { |
continue; |
} |
|
if ($this->useShortAttachment) { |
$attachment['fields'][] = $this->generateAttachmentField( |
ucfirst($key), |
$record[$key] |
); |
} else { |
// Add all extra fields as individual fields in attachment |
$attachment['fields'] = array_merge( |
$attachment['fields'], |
$this->generateAttachmentFields($record[$key]) |
); |
} |
} |
} |
|
$dataArray['attachments'] = array($attachment); |
} else { |
$dataArray['text'] = $message; |
} |
|
if ($this->userIcon) { |
if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) { |
$dataArray['icon_url'] = $this->userIcon; |
} else { |
$dataArray['icon_emoji'] = ":{$this->userIcon}:"; |
} |
} |
|
return $dataArray; |
} |
|
/** |
* Returned a Slack message attachment color associated with |
* provided level. |
* |
* @param int $level |
* @return string |
*/ |
public function getAttachmentColor($level) |
{ |
switch (true) { |
case $level >= Logger::ERROR: |
return self::COLOR_DANGER; |
case $level >= Logger::WARNING: |
return self::COLOR_WARNING; |
case $level >= Logger::INFO: |
return self::COLOR_GOOD; |
default: |
return self::COLOR_DEFAULT; |
} |
} |
|
/** |
* Stringifies an array of key/value pairs to be used in attachment fields |
* |
* @param array $fields |
* |
* @return string |
*/ |
public function stringify($fields) |
{ |
$normalized = $this->normalizerFormatter->format($fields); |
$prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128; |
|
$hasSecondDimension = count(array_filter($normalized, 'is_array')); |
$hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric')); |
|
return $hasSecondDimension || $hasNonNumericKeys |
? json_encode($normalized, $prettyPrintFlag) |
: json_encode($normalized); |
} |
|
/** |
* Sets the formatter |
* |
* @param FormatterInterface $formatter |
*/ |
public function setFormatter(FormatterInterface $formatter) |
{ |
$this->formatter = $formatter; |
} |
|
/** |
* Generates attachment field |
* |
* @param string $title |
* @param string|array $value\ |
* |
* @return array |
*/ |
private function generateAttachmentField($title, $value) |
{ |
$value = is_array($value) |
? sprintf('```%s```', $this->stringify($value)) |
: $value; |
|
return array( |
'title' => $title, |
'value' => $value, |
'short' => false |
); |
} |
|
/** |
* Generates a collection of attachment fields from array |
* |
* @param array $data |
* |
* @return array |
*/ |
private function generateAttachmentFields(array $data) |
{ |
$fields = array(); |
foreach ($data as $key => $value) { |
$fields[] = $this->generateAttachmentField($key, $value); |
} |
|
return $fields; |
} |
|
/** |
* Get a copy of record with fields excluded according to $this->excludeFields |
* |
* @param array $record |
* |
* @return array |
*/ |
private function excludeFields(array $record) |
{ |
foreach ($this->excludeFields as $field) { |
$keys = explode('.', $field); |
$node = &$record; |
$lastKey = end($keys); |
foreach ($keys as $key) { |
if (!isset($node[$key])) { |
break; |
} |
if ($lastKey === $key) { |
unset($node[$key]); |
break; |
} |
$node = &$node[$key]; |
} |
} |
|
return $record; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php |
@@ -0,0 +1,215 @@ |
<?php |
|
/* |
* 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\Logger; |
use Monolog\Handler\Slack\SlackRecord; |
|
/** |
* Sends notifications through Slack API |
* |
* @author Greg Kedzierski <greg@gregkedzierski.com> |
* @see https://api.slack.com/ |
*/ |
class SlackHandler extends SocketHandler |
{ |
/** |
* Slack API token |
* @var string |
*/ |
private $token; |
|
/** |
* Instance of the SlackRecord util class preparing data for Slack API. |
* @var SlackRecord |
*/ |
private $slackRecord; |
|
/** |
* @param string $token Slack API token |
* @param string $channel Slack channel (encoded ID or name) |
* @param string|null $username Name of a bot |
* @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) |
* @param string|null $iconEmoji The emoji name to use (or null) |
* @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 $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style |
* @param bool $includeContextAndExtra Whether the attachment should include context and extra data |
* @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] |
* @throws MissingExtensionException If no OpenSSL PHP extension configured |
*/ |
public function __construct($token, $channel, $username = null, $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array()) |
{ |
if (!extension_loaded('openssl')) { |
throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); |
} |
|
parent::__construct('ssl://slack.com:443', $level, $bubble); |
|
$this->slackRecord = new SlackRecord( |
$channel, |
$username, |
$useAttachment, |
$iconEmoji, |
$useShortAttachment, |
$includeContextAndExtra, |
$excludeFields, |
$this->formatter |
); |
|
$this->token = $token; |
} |
|
public function getSlackRecord() |
{ |
return $this->slackRecord; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
* @return string |
*/ |
protected function generateDataStream($record) |
{ |
$content = $this->buildContent($record); |
|
return $this->buildHeader($content) . $content; |
} |
|
/** |
* Builds the body of API call |
* |
* @param array $record |
* @return string |
*/ |
private function buildContent($record) |
{ |
$dataArray = $this->prepareContentData($record); |
|
return http_build_query($dataArray); |
} |
|
/** |
* Prepares content data |
* |
* @param array $record |
* @return array |
*/ |
protected function prepareContentData($record) |
{ |
$dataArray = $this->slackRecord->getSlackData($record); |
$dataArray['token'] = $this->token; |
|
if (!empty($dataArray['attachments'])) { |
$dataArray['attachments'] = json_encode($dataArray['attachments']); |
} |
|
return $dataArray; |
} |
|
/** |
* Builds the header of the API Call |
* |
* @param string $content |
* @return string |
*/ |
private function buildHeader($content) |
{ |
$header = "POST /api/chat.postMessage HTTP/1.1\r\n"; |
$header .= "Host: slack.com\r\n"; |
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; |
$header .= "Content-Length: " . strlen($content) . "\r\n"; |
$header .= "\r\n"; |
|
return $header; |
} |
|
/** |
* {@inheritdoc} |
* |
* @param array $record |
*/ |
protected function write(array $record) |
{ |
parent::write($record); |
$this->finalizeWrite(); |
} |
|
/** |
* Finalizes the request by reading some bytes and then closing the socket |
* |
* If we do not read some but close the socket too early, slack sometimes |
* drops the request entirely. |
*/ |
protected function finalizeWrite() |
{ |
$res = $this->getResource(); |
if (is_resource($res)) { |
@fread($res, 2048); |
} |
$this->closeSocket(); |
} |
|
/** |
* Returned a Slack message attachment color associated with |
* provided level. |
* |
* @param int $level |
* @return string |
* @deprecated Use underlying SlackRecord instead |
*/ |
protected function getAttachmentColor($level) |
{ |
trigger_error( |
'SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.', |
E_USER_DEPRECATED |
); |
|
return $this->slackRecord->getAttachmentColor($level); |
} |
|
/** |
* Stringifies an array of key/value pairs to be used in attachment fields |
* |
* @param array $fields |
* @return string |
* @deprecated Use underlying SlackRecord instead |
*/ |
protected function stringify($fields) |
{ |
trigger_error( |
'SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.', |
E_USER_DEPRECATED |
); |
|
return $this->slackRecord->stringify($fields); |
} |
|
public function setFormatter(FormatterInterface $formatter) |
{ |
parent::setFormatter($formatter); |
$this->slackRecord->setFormatter($formatter); |
|
return $this; |
} |
|
public function getFormatter() |
{ |
$formatter = parent::getFormatter(); |
$this->slackRecord->setFormatter($formatter); |
|
return $formatter; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php |
@@ -0,0 +1,346 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Stores to any socket - uses fsockopen() or pfsockopen(). |
* |
* @author Pablo de Leon Belloc <pablolb@gmail.com> |
* @see http://php.net/manual/en/function.fsockopen.php |
*/ |
class SocketHandler extends AbstractProcessingHandler |
{ |
private $connectionString; |
private $connectionTimeout; |
private $resource; |
private $timeout = 0; |
private $writingTimeout = 10; |
private $lastSentBytes = null; |
private $persistent = false; |
private $errno; |
private $errstr; |
private $lastWritingAt; |
|
/** |
* @param string $connectionString Socket connection string |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
*/ |
public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) |
{ |
parent::__construct($level, $bubble); |
$this->connectionString = $connectionString; |
$this->connectionTimeout = (float) ini_get('default_socket_timeout'); |
} |
|
/** |
* Connect (if necessary) and write to the socket |
* |
* @param array $record |
* |
* @throws \UnexpectedValueException |
* @throws \RuntimeException |
*/ |
protected function write(array $record) |
{ |
$this->connectIfNotConnected(); |
$data = $this->generateDataStream($record); |
$this->writeToSocket($data); |
} |
|
/** |
* We will not close a PersistentSocket instance so it can be reused in other requests. |
*/ |
public function close() |
{ |
if (!$this->isPersistent()) { |
$this->closeSocket(); |
} |
} |
|
/** |
* Close socket, if open |
*/ |
public function closeSocket() |
{ |
if (is_resource($this->resource)) { |
fclose($this->resource); |
$this->resource = null; |
} |
} |
|
/** |
* Set socket connection to nbe persistent. It only has effect before the connection is initiated. |
* |
* @param bool $persistent |
*/ |
public function setPersistent($persistent) |
{ |
$this->persistent = (boolean) $persistent; |
} |
|
/** |
* Set connection timeout. Only has effect before we connect. |
* |
* @param float $seconds |
* |
* @see http://php.net/manual/en/function.fsockopen.php |
*/ |
public function setConnectionTimeout($seconds) |
{ |
$this->validateTimeout($seconds); |
$this->connectionTimeout = (float) $seconds; |
} |
|
/** |
* Set write timeout. Only has effect before we connect. |
* |
* @param float $seconds |
* |
* @see http://php.net/manual/en/function.stream-set-timeout.php |
*/ |
public function setTimeout($seconds) |
{ |
$this->validateTimeout($seconds); |
$this->timeout = (float) $seconds; |
} |
|
/** |
* Set writing timeout. Only has effect during connection in the writing cycle. |
* |
* @param float $seconds 0 for no timeout |
*/ |
public function setWritingTimeout($seconds) |
{ |
$this->validateTimeout($seconds); |
$this->writingTimeout = (float) $seconds; |
} |
|
/** |
* Get current connection string |
* |
* @return string |
*/ |
public function getConnectionString() |
{ |
return $this->connectionString; |
} |
|
/** |
* Get persistent setting |
* |
* @return bool |
*/ |
public function isPersistent() |
{ |
return $this->persistent; |
} |
|
/** |
* Get current connection timeout setting |
* |
* @return float |
*/ |
public function getConnectionTimeout() |
{ |
return $this->connectionTimeout; |
} |
|
/** |
* Get current in-transfer timeout |
* |
* @return float |
*/ |
public function getTimeout() |
{ |
return $this->timeout; |
} |
|
/** |
* Get current local writing timeout |
* |
* @return float |
*/ |
public function getWritingTimeout() |
{ |
return $this->writingTimeout; |
} |
|
/** |
* Check to see if the socket is currently available. |
* |
* UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. |
* |
* @return bool |
*/ |
public function isConnected() |
{ |
return is_resource($this->resource) |
&& !feof($this->resource); // on TCP - other party can close connection. |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function pfsockopen() |
{ |
return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function fsockopen() |
{ |
return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); |
} |
|
/** |
* Wrapper to allow mocking |
* |
* @see http://php.net/manual/en/function.stream-set-timeout.php |
*/ |
protected function streamSetTimeout() |
{ |
$seconds = floor($this->timeout); |
$microseconds = round(($this->timeout - $seconds) * 1e6); |
|
return stream_set_timeout($this->resource, $seconds, $microseconds); |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function fwrite($data) |
{ |
return @fwrite($this->resource, $data); |
} |
|
/** |
* Wrapper to allow mocking |
*/ |
protected function streamGetMetadata() |
{ |
return stream_get_meta_data($this->resource); |
} |
|
private function validateTimeout($value) |
{ |
$ok = filter_var($value, FILTER_VALIDATE_FLOAT); |
if ($ok === false || $value < 0) { |
throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); |
} |
} |
|
private function connectIfNotConnected() |
{ |
if ($this->isConnected()) { |
return; |
} |
$this->connect(); |
} |
|
protected function generateDataStream($record) |
{ |
return (string) $record['formatted']; |
} |
|
/** |
* @return resource|null |
*/ |
protected function getResource() |
{ |
return $this->resource; |
} |
|
private function connect() |
{ |
$this->createSocketResource(); |
$this->setSocketTimeout(); |
} |
|
private function createSocketResource() |
{ |
if ($this->isPersistent()) { |
$resource = $this->pfsockopen(); |
} else { |
$resource = $this->fsockopen(); |
} |
if (!$resource) { |
throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); |
} |
$this->resource = $resource; |
} |
|
private function setSocketTimeout() |
{ |
if (!$this->streamSetTimeout()) { |
throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); |
} |
} |
|
private function writeToSocket($data) |
{ |
$length = strlen($data); |
$sent = 0; |
$this->lastSentBytes = $sent; |
while ($this->isConnected() && $sent < $length) { |
if (0 == $sent) { |
$chunk = $this->fwrite($data); |
} else { |
$chunk = $this->fwrite(substr($data, $sent)); |
} |
if ($chunk === false) { |
throw new \RuntimeException("Could not write to socket"); |
} |
$sent += $chunk; |
$socketInfo = $this->streamGetMetadata(); |
if ($socketInfo['timed_out']) { |
throw new \RuntimeException("Write timed-out"); |
} |
|
if ($this->writingIsTimedOut($sent)) { |
throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)"); |
} |
} |
if (!$this->isConnected() && $sent < $length) { |
throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); |
} |
} |
|
private function writingIsTimedOut($sent) |
{ |
$writingTimeout = (int) floor($this->writingTimeout); |
if (0 === $writingTimeout) { |
return false; |
} |
|
if ($sent !== $this->lastSentBytes) { |
$this->lastWritingAt = time(); |
$this->lastSentBytes = $sent; |
|
return false; |
} else { |
usleep(100); |
} |
|
if ((time() - $this->lastWritingAt) >= $writingTimeout) { |
$this->closeSocket(); |
|
return true; |
} |
|
return false; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php |
@@ -0,0 +1,176 @@ |
<?php |
|
/* |
* 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\Logger; |
|
/** |
* Stores to any stream resource |
* |
* Can be used to store into php://stderr, remote and local files, etc. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class StreamHandler extends AbstractProcessingHandler |
{ |
protected $stream; |
protected $url; |
private $errorMessage; |
protected $filePermission; |
protected $useLocking; |
private $dirCreated; |
|
/** |
* @param resource|string $stream |
* @param int $level The minimum logging level at which this handler will be triggered |
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not |
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) |
* @param Boolean $useLocking Try to lock log file before doing any writes |
* |
* @throws \Exception If a missing directory is not buildable |
* @throws \InvalidArgumentException If stream is not a resource or string |
*/ |
public function __construct($stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) |
{ |
parent::__construct($level, $bubble); |
if (is_resource($stream)) { |
$this->stream = $stream; |
} elseif (is_string($stream)) { |
$this->url = $stream; |
} else { |
throw new \InvalidArgumentException('A stream must either be a resource or a string.'); |
} |
|
$this->filePermission = $filePermission; |
$this->useLocking = $useLocking; |
} |
|
/** |
* {@inheritdoc} |
*/ |
public function close() |
{ |
if ($this->url && is_resource($this->stream)) { |
fclose($this->stream); |
} |
$this->stream = null; |
} |
|
/** |
* Return the currently active stream if it is open |
* |
* @return resource|null |
*/ |
public function getStream() |
{ |
return $this->stream; |
} |
|
/** |
* Return the stream URL if it was configured with a URL and not an active resource |
* |
* @return string|null |
*/ |
public function getUrl() |
{ |
return $this->url; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
if (!is_resource($this->stream)) { |
if (null === $this->url || '' === $this->url) { |
throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); |
} |
$this->createDir(); |
$this->errorMessage = null; |
set_error_handler(array($this, 'customErrorHandler')); |
$this->stream = fopen($this->url, 'a'); |
if ($this->filePermission !== null) { |
@chmod($this->url, $this->filePermission); |
} |
restore_error_handler(); |
if (!is_resource($this->stream)) { |
$this->stream = null; |
throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url)); |
} |
} |
|
if ($this->useLocking) { |
// ignoring errors here, there's not much we can do about them |
flock($this->stream, LOCK_EX); |
} |
|
$this->streamWrite($this->stream, $record); |
|
if ($this->useLocking) { |
flock($this->stream, LOCK_UN); |
} |
} |
|
/** |
* Write to stream |
* @param resource $stream |
* @param array $record |
*/ |
protected function streamWrite($stream, array $record) |
{ |
fwrite($stream, (string) $record['formatted']); |
} |
|
private function customErrorHandler($code, $msg) |
{ |
$this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); |
} |
|
/** |
* @param string $stream |
* |
* @return null|string |
*/ |
private function getDirFromStream($stream) |
{ |
$pos = strpos($stream, '://'); |
if ($pos === false) { |
return dirname($stream); |
} |
|
if ('file://' === substr($stream, 0, 7)) { |
return dirname(substr($stream, 7)); |
} |
|
return; |
} |
|
private function createDir() |
{ |
// Do not try to create dir if it has already been tried. |
if ($this->dirCreated) { |
return; |
} |
|
$dir = $this->getDirFromStream($this->url); |
if (null !== $dir && !is_dir($dir)) { |
$this->errorMessage = null; |
set_error_handler(array($this, 'customErrorHandler')); |
$status = mkdir($dir, 0777, true); |
restore_error_handler(); |
if (false === $status) { |
throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); |
} |
} |
$this->dirCreated = true; |
} |
} |
/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php |
@@ -0,0 +1,154 @@ |
<?php |
|
/* |
* 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; |
|
/** |
* Used for testing purposes. |
* |
* It records all records and gives you access to them for verification. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
* |
* @method bool hasEmergency($record) |
* @method bool hasAlert($record) |
* @method bool hasCritical($record) |
* @method bool hasError($record) |
* @method bool hasWarning($record) |
* @method bool hasNotice($record) |
* @method bool hasInfo($record) |
* @method bool hasDebug($record) |
* |
* @method bool hasEmergencyRecords() |
* @method bool hasAlertRecords() |
* @method bool hasCriticalRecords() |
* @method bool hasErrorRecords() |
* @method bool hasWarningRecords() |
* @method bool hasNoticeRecords() |
* @method bool hasInfoRecords() |
* @method bool hasDebugRecords() |
* |
* @method bool hasEmergencyThatContains($message) |
* @method bool hasAlertThatContains($message) |
* @method bool hasCriticalThatContains($message) |
* @method bool hasErrorThatContains($message) |
* @method bool hasWarningThatContains($message) |
* @method bool hasNoticeThatContains($message) |
* @method bool hasInfoThatContains($message) |
* @method bool hasDebugThatContains($message) |
* |
* @method bool hasEmergencyThatMatches($message) |
* @method bool hasAlertThatMatches($message) |
* @method bool hasCriticalThatMatches($message) |
* @method bool hasErrorThatMatches($message) |
* @method bool hasWarningThatMatches($message) |
* @method bool hasNoticeThatMatches($message) |
* @method bool hasInfoThatMatches($message) |
* @method bool hasDebugThatMatches($message) |
* |
* @method bool hasEmergencyThatPasses($message) |
* @method bool hasAlertThatPasses($message) |
* @method bool hasCriticalThatPasses($message) |
* @method bool hasErrorThatPasses($message) |
* @method bool hasWarningThatPasses($message) |
* @method bool hasNoticeThatPasses($message) |
* @method bool hasInfoThatPasses($message) |
* @method bool hasDebugThatPasses($message) |
*/ |
class TestHandler extends AbstractProcessingHandler |
{ |
protected $records = array(); |
protected $recordsByLevel = array(); |
|
public function getRecords() |
{ |
return $this->records; |
} |
|
public function clear() |
{ |
$this->records = array(); |
$this->recordsByLevel = array(); |
} |
|
public function hasRecords($level) |
{ |
return isset($this->recordsByLevel[$level]); |
} |
|
public function hasRecord($record, $level) |
{ |
if (is_array($record)) { |
$record = $record['message']; |
} |
|
return $this->hasRecordThatPasses(function ($rec) use ($record) { |
return $rec['message'] === $record; |
}, $level); |
} |
|
public function hasRecordThatContains($message, $level) |
{ |
return $this->hasRecordThatPasses(function ($rec) use ($message) { |
return strpos($rec['message'], $message) !== false; |
}, $level); |
} |
|
public function hasRecordThatMatches($regex, $level) |
{ |
return $this->hasRecordThatPasses(function ($rec) use ($regex) { |
return preg_match($regex, $rec['message']) > 0; |
}, $level); |
} |
|
public function hasRecordThatPasses($predicate, $level) |
{ |
if (!is_callable($predicate)) { |
throw new \InvalidArgumentException("Expected a callable for hasRecordThatSucceeds"); |
} |
|
if (!isset($this->recordsByLevel[$level])) { |
return false; |
} |
|
foreach ($this->recordsByLevel[$level] as $i => $rec) { |
if (call_user_func($predicate, $rec, $i)) { |
return true; |
} |
} |
|
return false; |
} |
|
/** |
* {@inheritdoc} |
*/ |
protected function write(array $record) |
{ |
$this->recordsByLevel[$record['level']][] = $record; |
$this->records[] = $record; |
} |
|
public function __call($method, $args) |
{ |
if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { |
$genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; |
$level = constant('Monolog\Logger::' . strtoupper($matches[2])); |
if (method_exists($this, $genericMethod)) { |
$args[] = $level; |
|
return call_user_func_array(array($this, $genericMethod), $args); |
} |
} |
|
throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); |
} |
} |
/vendor/monolog/monolog/src/Monolog/Logger.php |
@@ -0,0 +1,700 @@ |
<?php |
|
/* |
* 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 Monolog\Handler\HandlerInterface; |
use Monolog\Handler\StreamHandler; |
use Psr\Log\LoggerInterface; |
use Psr\Log\InvalidArgumentException; |
|
/** |
* Monolog log channel |
* |
* It contains a stack of Handlers and a stack of Processors, |
* and uses them to store records that are added to it. |
* |
* @author Jordi Boggiano <j.boggiano@seld.be> |
*/ |
class Logger implements LoggerInterface |
{ |
/** |
* Detailed debug information |
*/ |
const DEBUG = 100; |
|
/** |
* Interesting events |
* |
* Examples: User logs in, SQL logs. |
*/ |
const INFO = 200; |
|
/** |
* Uncommon events |
*/ |
const NOTICE = 250; |
|
/** |
* Exceptional occurrences that are not errors |
* |
* Examples: Use of deprecated APIs, poor use of an API, |
* undesirable things that are not necessarily wrong. |
*/ |
const WARNING = 300; |
|
/** |
* Runtime errors |
*/ |
const ERROR = 400; |
|
/** |
* Critical conditions |
* |
* Example: Application component unavailable, unexpected exception. |
*/ |
const CRITICAL = 500; |
|
/** |
* Action must be taken immediately |
* |
* Example: Entire website down, database unavailable, etc. |
* This should trigger the SMS alerts and wake you up. |
*/ |
const ALERT = 550; |
|
/** |
* Urgent alert. |
*/ |
const EMERGENCY = 600; |
|
/** |
* Monolog API version |
* |
* This is only bumped when API breaks are done and should |
* follow the major version of the library |
* |
* @var int |
*/ |
const API = 1; |
|
/** |
* Logging levels from syslog protocol defined in RFC 5424 |
* |
* @var array $levels Logging levels |
*/ |
protected static $levels = array( |
self::DEBUG => 'DEBUG', |
self::INFO => 'INFO', |
self::NOTICE => 'NOTICE', |
self::WARNING => 'WARNING', |
self::ERROR => 'ERROR', |
self::CRITICAL => 'CRITICAL', |
self::ALERT => 'ALERT', |
self::EMERGENCY => 'EMERGENCY', |
); |
|
/** |
* @var \DateTimeZone |
*/ |
protected static $timezone; |
|
/** |
* @var string |
*/ |
protected $name; |
|
/** |
* The handler stack |
* |
* @var HandlerInterface[] |
*/ |
protected $handlers; |
|
/** |
* Processors that will process all log records |
* |
* To process records of a single handler instead, add the processor on that specific handler |
* |
* @var callable[] |
*/ |
protected $processors; |
|
/** |
* @var bool |
*/ |
protected $microsecondTimestamps = true; |
|
/** |
* @param string $name The logging channel |
* @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. |
* @param callable[] $processors Optional array of processors |
*/ |
public function __construct($name, array $handlers = array(), array $processors = array()) |
{ |
$this->name = $name; |
$this->handlers = $handlers; |
$this->processors = $processors; |
} |
|
/** |
* @return string |
*/ |
public function getName() |
{ |
return $this->name; |
} |
|
/** |
* Return a new cloned instance with the name changed |
* |
* @return static |
*/ |
public function withName($name) |
{ |
$new = clone $this; |
$new->name = $name; |
|
return $new; |
} |
|
/** |
* Pushes a handler on to the stack. |
* |
* @param HandlerInterface $handler |
* @return $this |
*/ |
public function pushHandler(HandlerInterface $handler) |
{ |
array_unshift($this->handlers, $handler); |
|
return $this; |
} |
|
/** |
* Pops a handler from the stack |
* |
* @return HandlerInterface |
*/ |
public function popHandler() |
{ |
if (!$this->handlers) { |
throw new \LogicException('You tried to pop from an empty handler stack.'); |
} |
|
return array_shift($this->handlers); |
} |
|
/** |
* Set handlers, replacing all existing ones. |
* |
* If a map is passed, keys will be ignored. |
* |
* @param HandlerInterface[] $handlers |
* @return $this |
*/ |
public function setHandlers(array $handlers) |
{ |
$this->handlers = array(); |
foreach (array_reverse($handlers) as $handler) { |
$this->pushHandler($handler); |
} |
|
return $this; |
} |
|
/** |
* @return HandlerInterface[] |
*/ |
public function getHandlers() |
{ |
return $this->handlers; |
} |
|
/** |
* Adds a processor on to the stack. |
* |
* @param callable $callback |
* @return $this |
*/ |
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; |
} |
|
/** |
* Removes the processor on top of the stack and returns it. |
* |
* @return callable |
*/ |
public function popProcessor() |
{ |
if (!$this->processors) { |
throw new \LogicException('You tried to pop from an empty processor stack.'); |
} |
|
return array_shift($this->processors); |
} |
|
/** |
* @return callable[] |
*/ |
public function getProcessors() |
{ |
return $this->processors; |
} |
|
/** |
* Control the use of microsecond resolution timestamps in the 'datetime' |
* member of new records. |
* |
* Generating microsecond resolution timestamps by calling |
* microtime(true), formatting the result via sprintf() and then parsing |
* the resulting string via \DateTime::createFromFormat() can incur |
* a measurable runtime overhead vs simple usage of DateTime to capture |
* a second resolution timestamp in systems which generate a large number |
* of log events. |
* |
* @param bool $micro True to use microtime() to create timestamps |
*/ |
public function useMicrosecondTimestamps($micro) |
{ |
$this->microsecondTimestamps = (bool) $micro; |
} |
|
/** |
* Adds a log record. |
* |
* @param int $level The logging level |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addRecord($level, $message, array $context = array()) |
{ |
if (!$this->handlers) { |
$this->pushHandler(new StreamHandler('php://stderr', static::DEBUG)); |
} |
|
$levelName = static::getLevelName($level); |
|
// check if any handler will handle this message so we can return early and save cycles |
$handlerKey = null; |
reset($this->handlers); |
while ($handler = current($this->handlers)) { |
if ($handler->isHandling(array('level' => $level))) { |
$handlerKey = key($this->handlers); |
break; |
} |
|
next($this->handlers); |
} |
|
if (null === $handlerKey) { |
return false; |
} |
|
if (!static::$timezone) { |
static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); |
} |
|
// php7.1+ always has microseconds enabled, so we do not need this hack |
if ($this->microsecondTimestamps && PHP_VERSION_ID < 70100) { |
$ts = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone); |
} else { |
$ts = new \DateTime(null, static::$timezone); |
} |
$ts->setTimezone(static::$timezone); |
|
$record = array( |
'message' => (string) $message, |
'context' => $context, |
'level' => $level, |
'level_name' => $levelName, |
'channel' => $this->name, |
'datetime' => $ts, |
'extra' => array(), |
); |
|
foreach ($this->processors as $processor) { |
$record = call_user_func($processor, $record); |
} |
|
while ($handler = current($this->handlers)) { |
if (true === $handler->handle($record)) { |
break; |
} |
|
next($this->handlers); |
} |
|
return true; |
} |
|
/** |
* Adds a log record at the DEBUG level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addDebug($message, array $context = array()) |
{ |
return $this->addRecord(static::DEBUG, $message, $context); |
} |
|
/** |
* Adds a log record at the INFO level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addInfo($message, array $context = array()) |
{ |
return $this->addRecord(static::INFO, $message, $context); |
} |
|
/** |
* Adds a log record at the NOTICE level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addNotice($message, array $context = array()) |
{ |
return $this->addRecord(static::NOTICE, $message, $context); |
} |
|
/** |
* Adds a log record at the WARNING level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addWarning($message, array $context = array()) |
{ |
return $this->addRecord(static::WARNING, $message, $context); |
} |
|
/** |
* Adds a log record at the ERROR level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addError($message, array $context = array()) |
{ |
return $this->addRecord(static::ERROR, $message, $context); |
} |
|
/** |
* Adds a log record at the CRITICAL level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addCritical($message, array $context = array()) |
{ |
return $this->addRecord(static::CRITICAL, $message, $context); |
} |
|
/** |
* Adds a log record at the ALERT level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addAlert($message, array $context = array()) |
{ |
return $this->addRecord(static::ALERT, $message, $context); |
} |
|
/** |
* Adds a log record at the EMERGENCY level. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function addEmergency($message, array $context = array()) |
{ |
return $this->addRecord(static::EMERGENCY, $message, $context); |
} |
|
/** |
* Gets all supported logging levels. |
* |
* @return array Assoc array with human-readable level names => level codes. |
*/ |
public static function getLevels() |
{ |
return array_flip(static::$levels); |
} |
|
/** |
* Gets the name of the logging level. |
* |
* @param int $level |
* @return string |
*/ |
public static function getLevelName($level) |
{ |
if (!isset(static::$levels[$level])) { |
throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels))); |
} |
|
return static::$levels[$level]; |
} |
|
/** |
* Converts PSR-3 levels to Monolog ones if necessary |
* |
* @param string|int Level number (monolog) or name (PSR-3) |
* @return int |
*/ |
public static function toMonologLevel($level) |
{ |
if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) { |
return constant(__CLASS__.'::'.strtoupper($level)); |
} |
|
return $level; |
} |
|
/** |
* Checks whether the Logger has a handler that listens on the given level |
* |
* @param int $level |
* @return Boolean |
*/ |
public function isHandling($level) |
{ |
$record = array( |
'level' => $level, |
); |
|
foreach ($this->handlers as $handler) { |
if ($handler->isHandling($record)) { |
return true; |
} |
} |
|
return false; |
} |
|
/** |
* Adds a log record at an arbitrary level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param mixed $level The log level |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function log($level, $message, array $context = array()) |
{ |
$level = static::toMonologLevel($level); |
|
return $this->addRecord($level, $message, $context); |
} |
|
/** |
* Adds a log record at the DEBUG level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function debug($message, array $context = array()) |
{ |
return $this->addRecord(static::DEBUG, $message, $context); |
} |
|
/** |
* Adds a log record at the INFO level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function info($message, array $context = array()) |
{ |
return $this->addRecord(static::INFO, $message, $context); |
} |
|
/** |
* Adds a log record at the NOTICE level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function notice($message, array $context = array()) |
{ |
return $this->addRecord(static::NOTICE, $message, $context); |
} |
|
/** |
* Adds a log record at the WARNING level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function warn($message, array $context = array()) |
{ |
return $this->addRecord(static::WARNING, $message, $context); |
} |
|
/** |
* Adds a log record at the WARNING level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function warning($message, array $context = array()) |
{ |
return $this->addRecord(static::WARNING, $message, $context); |
} |
|
/** |
* Adds a log record at the ERROR level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function err($message, array $context = array()) |
{ |
return $this->addRecord(static::ERROR, $message, $context); |
} |
|
/** |
* Adds a log record at the ERROR level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function error($message, array $context = array()) |
{ |
return $this->addRecord(static::ERROR, $message, $context); |
} |
|
/** |
* Adds a log record at the CRITICAL level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function crit($message, array $context = array()) |
{ |
return $this->addRecord(static::CRITICAL, $message, $context); |
} |
|
/** |
* Adds a log record at the CRITICAL level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function critical($message, array $context = array()) |
{ |
return $this->addRecord(static::CRITICAL, $message, $context); |
} |
|
/** |
* Adds a log record at the ALERT level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function alert($message, array $context = array()) |
{ |
return $this->addRecord(static::ALERT, $message, $context); |
} |
|
/** |
* Adds a log record at the EMERGENCY level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function emerg($message, array $context = array()) |
{ |
return $this->addRecord(static::EMERGENCY, $message, $context); |
} |
|
/** |
* Adds a log record at the EMERGENCY level. |
* |
* This method allows for compatibility with common interfaces. |
* |
* @param string $message The log message |
* @param array $context The log context |
* @return Boolean Whether the record has been processed |
*/ |
public function emergency($message, array $context = array()) |
{ |
return $this->addRecord(static::EMERGENCY, $message, $context); |
} |
|
/** |
* Set the timezone to be used for the timestamp of log records. |
* |
* This is stored globally for all Logger instances |
* |
* @param \DateTimeZone $tz Timezone object |
*/ |
public static function setTimezone(\DateTimeZone $tz) |
{ |
self::$timezone = $tz; |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.php |
@@ -0,0 +1,258 @@ |
<?php |
|
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
|
class GelfMessageFormatterTest extends \PHPUnit_Framework_TestCase |
{ |
public function setUp() |
{ |
if (!class_exists('\Gelf\Message')) { |
$this->markTestSkipped("graylog2/gelf-php or mlehner/gelf-php is not installed"); |
} |
} |
|
/** |
* @covers Monolog\Formatter\GelfMessageFormatter::format |
*/ |
public function testDefaultFormatter() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
$this->assertEquals(0, $message->getTimestamp()); |
$this->assertEquals('log', $message->getShortMessage()); |
$this->assertEquals('meh', $message->getFacility()); |
$this->assertEquals(null, $message->getLine()); |
$this->assertEquals(null, $message->getFile()); |
$this->assertEquals($this->isLegacy() ? 3 : 'error', $message->getLevel()); |
$this->assertNotEmpty($message->getHost()); |
|
$formatter = new GelfMessageFormatter('mysystem'); |
|
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
$this->assertEquals('mysystem', $message->getHost()); |
} |
|
/** |
* @covers Monolog\Formatter\GelfMessageFormatter::format |
*/ |
public function testFormatWithFileAndLine() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('file' => 'test', 'line' => 14), |
'message' => 'log', |
); |
|
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
$this->assertEquals('test', $message->getFile()); |
$this->assertEquals(14, $message->getLine()); |
} |
|
/** |
* @covers Monolog\Formatter\GelfMessageFormatter::format |
* @expectedException InvalidArgumentException |
*/ |
public function testFormatInvalidFails() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
); |
|
$formatter->format($record); |
} |
|
/** |
* @covers Monolog\Formatter\GelfMessageFormatter::format |
*/ |
public function testFormatWithContext() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
|
$message_array = $message->toArray(); |
|
$this->assertArrayHasKey('_ctxt_from', $message_array); |
$this->assertEquals('logger', $message_array['_ctxt_from']); |
|
// Test with extraPrefix |
$formatter = new GelfMessageFormatter(null, null, 'CTX'); |
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
|
$message_array = $message->toArray(); |
|
$this->assertArrayHasKey('_CTXfrom', $message_array); |
$this->assertEquals('logger', $message_array['_CTXfrom']); |
} |
|
/** |
* @covers Monolog\Formatter\GelfMessageFormatter::format |
*/ |
public function testFormatWithContextContainingException() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger', 'exception' => array( |
'class' => '\Exception', |
'file' => '/some/file/in/dir.php:56', |
'trace' => array('/some/file/1.php:23', '/some/file/2.php:3'), |
)), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
|
$this->assertEquals("/some/file/in/dir.php", $message->getFile()); |
$this->assertEquals("56", $message->getLine()); |
} |
|
/** |
* @covers Monolog\Formatter\GelfMessageFormatter::format |
*/ |
public function testFormatWithExtra() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
|
$message_array = $message->toArray(); |
|
$this->assertArrayHasKey('_key', $message_array); |
$this->assertEquals('pair', $message_array['_key']); |
|
// Test with extraPrefix |
$formatter = new GelfMessageFormatter(null, 'EXT'); |
$message = $formatter->format($record); |
|
$this->assertInstanceOf('Gelf\Message', $message); |
|
$message_array = $message->toArray(); |
|
$this->assertArrayHasKey('_EXTkey', $message_array); |
$this->assertEquals('pair', $message_array['_EXTkey']); |
} |
|
public function testFormatWithLargeData() |
{ |
$formatter = new GelfMessageFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('exception' => str_repeat(' ', 32767)), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => str_repeat(' ', 32767)), |
'message' => 'log' |
); |
$message = $formatter->format($record); |
$messageArray = $message->toArray(); |
|
// 200 for padding + metadata |
$length = 200; |
|
foreach ($messageArray as $key => $value) { |
if (!in_array($key, array('level', 'timestamp'))) { |
$length += strlen($value); |
} |
} |
|
$this->assertLessThanOrEqual(65792, $length, 'The message length is no longer than the maximum allowed length'); |
} |
|
public function testFormatWithUnlimitedLength() |
{ |
$formatter = new GelfMessageFormatter('LONG_SYSTEM_NAME', null, 'ctxt_', PHP_INT_MAX); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('exception' => str_repeat(' ', 32767 * 2)), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => str_repeat(' ', 32767 * 2)), |
'message' => 'log' |
); |
$message = $formatter->format($record); |
$messageArray = $message->toArray(); |
|
// 200 for padding + metadata |
$length = 200; |
|
foreach ($messageArray as $key => $value) { |
if (!in_array($key, array('level', 'timestamp'))) { |
$length += strlen($value); |
} |
} |
|
$this->assertGreaterThanOrEqual(131289, $length, 'The message should not be truncated'); |
} |
|
private function isLegacy() |
{ |
return interface_exists('\Gelf\IMessagePublisher'); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php |
@@ -0,0 +1,183 @@ |
<?php |
|
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
use Monolog\TestCase; |
|
class JsonFormatterTest extends TestCase |
{ |
/** |
* @covers Monolog\Formatter\JsonFormatter::__construct |
* @covers Monolog\Formatter\JsonFormatter::getBatchMode |
* @covers Monolog\Formatter\JsonFormatter::isAppendingNewlines |
*/ |
public function testConstruct() |
{ |
$formatter = new JsonFormatter(); |
$this->assertEquals(JsonFormatter::BATCH_MODE_JSON, $formatter->getBatchMode()); |
$this->assertEquals(true, $formatter->isAppendingNewlines()); |
$formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_NEWLINES, false); |
$this->assertEquals(JsonFormatter::BATCH_MODE_NEWLINES, $formatter->getBatchMode()); |
$this->assertEquals(false, $formatter->isAppendingNewlines()); |
} |
|
/** |
* @covers Monolog\Formatter\JsonFormatter::format |
*/ |
public function testFormat() |
{ |
$formatter = new JsonFormatter(); |
$record = $this->getRecord(); |
$this->assertEquals(json_encode($record)."\n", $formatter->format($record)); |
|
$formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); |
$record = $this->getRecord(); |
$this->assertEquals(json_encode($record), $formatter->format($record)); |
} |
|
/** |
* @covers Monolog\Formatter\JsonFormatter::formatBatch |
* @covers Monolog\Formatter\JsonFormatter::formatBatchJson |
*/ |
public function testFormatBatch() |
{ |
$formatter = new JsonFormatter(); |
$records = array( |
$this->getRecord(Logger::WARNING), |
$this->getRecord(Logger::DEBUG), |
); |
$this->assertEquals(json_encode($records), $formatter->formatBatch($records)); |
} |
|
/** |
* @covers Monolog\Formatter\JsonFormatter::formatBatch |
* @covers Monolog\Formatter\JsonFormatter::formatBatchNewlines |
*/ |
public function testFormatBatchNewlines() |
{ |
$formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_NEWLINES); |
$records = $expected = array( |
$this->getRecord(Logger::WARNING), |
$this->getRecord(Logger::DEBUG), |
); |
array_walk($expected, function (&$value, $key) { |
$value = json_encode($value); |
}); |
$this->assertEquals(implode("\n", $expected), $formatter->formatBatch($records)); |
} |
|
public function testDefFormatWithException() |
{ |
$formatter = new JsonFormatter(); |
$exception = new \RuntimeException('Foo'); |
$formattedException = $this->formatException($exception); |
|
$message = $this->formatRecordWithExceptionInContext($formatter, $exception); |
|
$this->assertContextContainsFormattedException($formattedException, $message); |
} |
|
public function testDefFormatWithPreviousException() |
{ |
$formatter = new JsonFormatter(); |
$exception = new \RuntimeException('Foo', 0, new \LogicException('Wut?')); |
$formattedPrevException = $this->formatException($exception->getPrevious()); |
$formattedException = $this->formatException($exception, $formattedPrevException); |
|
$message = $this->formatRecordWithExceptionInContext($formatter, $exception); |
|
$this->assertContextContainsFormattedException($formattedException, $message); |
} |
|
public function testDefFormatWithThrowable() |
{ |
if (!class_exists('Error') || !is_subclass_of('Error', 'Throwable')) { |
$this->markTestSkipped('Requires PHP >=7'); |
} |
|
$formatter = new JsonFormatter(); |
$throwable = new \Error('Foo'); |
$formattedThrowable = $this->formatException($throwable); |
|
$message = $this->formatRecordWithExceptionInContext($formatter, $throwable); |
|
$this->assertContextContainsFormattedException($formattedThrowable, $message); |
} |
|
/** |
* @param string $expected |
* @param string $actual |
* |
* @internal param string $exception |
*/ |
private function assertContextContainsFormattedException($expected, $actual) |
{ |
$this->assertEquals( |
'{"level_name":"CRITICAL","channel":"core","context":{"exception":'.$expected.'},"datetime":null,"extra":[],"message":"foobar"}'."\n", |
$actual |
); |
} |
|
/** |
* @param JsonFormatter $formatter |
* @param \Exception|\Throwable $exception |
* |
* @return string |
*/ |
private function formatRecordWithExceptionInContext(JsonFormatter $formatter, $exception) |
{ |
$message = $formatter->format(array( |
'level_name' => 'CRITICAL', |
'channel' => 'core', |
'context' => array('exception' => $exception), |
'datetime' => null, |
'extra' => array(), |
'message' => 'foobar', |
)); |
return $message; |
} |
|
/** |
* @param \Exception|\Throwable $exception |
* |
* @return string |
*/ |
private function formatExceptionFilePathWithLine($exception) |
{ |
$options = 0; |
if (version_compare(PHP_VERSION, '5.4.0', '>=')) { |
$options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; |
} |
$path = substr(json_encode($exception->getFile(), $options), 1, -1); |
return $path . ':' . $exception->getLine(); |
} |
|
/** |
* @param \Exception|\Throwable $exception |
* |
* @param null|string $previous |
* |
* @return string |
*/ |
private function formatException($exception, $previous = null) |
{ |
$formattedException = |
'{"class":"' . get_class($exception) . |
'","message":"' . $exception->getMessage() . |
'","code":' . $exception->getCode() . |
',"file":"' . $this->formatExceptionFilePathWithLine($exception) . |
($previous ? '","previous":' . $previous : '"') . |
'}'; |
return $formattedException; |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php |
@@ -0,0 +1,222 @@ |
<?php |
|
/* |
* 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; |
|
/** |
* @covers Monolog\Formatter\LineFormatter |
*/ |
class LineFormatterTest extends \PHPUnit_Framework_TestCase |
{ |
public function testDefFormatWithString() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->format(array( |
'level_name' => 'WARNING', |
'channel' => 'log', |
'context' => array(), |
'message' => 'foo', |
'datetime' => new \DateTime, |
'extra' => array(), |
)); |
$this->assertEquals('['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message); |
} |
|
public function testDefFormatWithArrayContext() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'message' => 'foo', |
'datetime' => new \DateTime, |
'extra' => array(), |
'context' => array( |
'foo' => 'bar', |
'baz' => 'qux', |
'bool' => false, |
'null' => null, |
), |
)); |
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foo {"foo":"bar","baz":"qux","bool":false,"null":null} []'."\n", $message); |
} |
|
public function testDefFormatExtras() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array('ip' => '127.0.0.1'), |
'message' => 'log', |
)); |
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log [] {"ip":"127.0.0.1"}'."\n", $message); |
} |
|
public function testFormatExtras() |
{ |
$formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context% %extra.file% %extra%\n", 'Y-m-d'); |
$message = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array('ip' => '127.0.0.1', 'file' => 'test'), |
'message' => 'log', |
)); |
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log [] test {"ip":"127.0.0.1"}'."\n", $message); |
} |
|
public function testContextAndExtraOptionallyNotShownIfEmpty() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d', false, true); |
$message = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array(), |
'message' => 'log', |
)); |
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log '."\n", $message); |
} |
|
public function testContextAndExtraReplacement() |
{ |
$formatter = new LineFormatter('%context.foo% => %extra.foo%'); |
$message = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('foo' => 'bar'), |
'datetime' => new \DateTime, |
'extra' => array('foo' => 'xbar'), |
'message' => 'log', |
)); |
$this->assertEquals('bar => xbar', $message); |
} |
|
public function testDefFormatWithObject() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array('foo' => new TestFoo, 'bar' => new TestBar, 'baz' => array(), 'res' => fopen('php://memory', 'rb')), |
'message' => 'foobar', |
)); |
|
$this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foobar [] {"foo":"[object] (Monolog\\\\Formatter\\\\TestFoo: {\\"foo\\":\\"foo\\"})","bar":"[object] (Monolog\\\\Formatter\\\\TestBar: bar)","baz":[],"res":"[resource] (stream)"}'."\n", $message); |
} |
|
public function testDefFormatWithException() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->format(array( |
'level_name' => 'CRITICAL', |
'channel' => 'core', |
'context' => array('exception' => new \RuntimeException('Foo')), |
'datetime' => new \DateTime, |
'extra' => array(), |
'message' => 'foobar', |
)); |
|
$path = str_replace('\\/', '/', json_encode(__FILE__)); |
|
$this->assertEquals('['.date('Y-m-d').'] core.CRITICAL: foobar {"exception":"[object] (RuntimeException(code: 0): Foo at '.substr($path, 1, -1).':'.(__LINE__ - 8).')"} []'."\n", $message); |
} |
|
public function testDefFormatWithPreviousException() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$previous = new \LogicException('Wut?'); |
$message = $formatter->format(array( |
'level_name' => 'CRITICAL', |
'channel' => 'core', |
'context' => array('exception' => new \RuntimeException('Foo', 0, $previous)), |
'datetime' => new \DateTime, |
'extra' => array(), |
'message' => 'foobar', |
)); |
|
$path = str_replace('\\/', '/', json_encode(__FILE__)); |
|
$this->assertEquals('['.date('Y-m-d').'] core.CRITICAL: foobar {"exception":"[object] (RuntimeException(code: 0): Foo at '.substr($path, 1, -1).':'.(__LINE__ - 8).', LogicException(code: 0): Wut? at '.substr($path, 1, -1).':'.(__LINE__ - 12).')"} []'."\n", $message); |
} |
|
public function testBatchFormat() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->formatBatch(array( |
array( |
'level_name' => 'CRITICAL', |
'channel' => 'test', |
'message' => 'bar', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array(), |
), |
array( |
'level_name' => 'WARNING', |
'channel' => 'log', |
'message' => 'foo', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array(), |
), |
)); |
$this->assertEquals('['.date('Y-m-d').'] test.CRITICAL: bar [] []'."\n".'['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message); |
} |
|
public function testFormatShouldStripInlineLineBreaks() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d'); |
$message = $formatter->format( |
array( |
'message' => "foo\nbar", |
'context' => array(), |
'extra' => array(), |
) |
); |
|
$this->assertRegExp('/foo bar/', $message); |
} |
|
public function testFormatShouldNotStripInlineLineBreaksWhenFlagIsSet() |
{ |
$formatter = new LineFormatter(null, 'Y-m-d', true); |
$message = $formatter->format( |
array( |
'message' => "foo\nbar", |
'context' => array(), |
'extra' => array(), |
) |
); |
|
$this->assertRegExp('/foo\nbar/', $message); |
} |
} |
|
class TestFoo |
{ |
public $foo = 'foo'; |
} |
|
class TestBar |
{ |
public function __toString() |
{ |
return 'bar'; |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.php |
@@ -0,0 +1,333 @@ |
<?php |
|
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
|
class LogstashFormatterTest extends \PHPUnit_Framework_TestCase |
{ |
public function tearDown() |
{ |
\PHPUnit_Framework_Error_Warning::$enabled = true; |
|
return parent::tearDown(); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testDefaultFormatter() |
{ |
$formatter = new LogstashFormatter('test', 'hostname'); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']); |
$this->assertEquals('log', $message['@message']); |
$this->assertEquals('meh', $message['@fields']['channel']); |
$this->assertContains('meh', $message['@tags']); |
$this->assertEquals(Logger::ERROR, $message['@fields']['level']); |
$this->assertEquals('test', $message['@type']); |
$this->assertEquals('hostname', $message['@source']); |
|
$formatter = new LogstashFormatter('mysystem'); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals('mysystem', $message['@type']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testFormatWithFileAndLine() |
{ |
$formatter = new LogstashFormatter('test'); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('file' => 'test', 'line' => 14), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals('test', $message['@fields']['file']); |
$this->assertEquals(14, $message['@fields']['line']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testFormatWithContext() |
{ |
$formatter = new LogstashFormatter('test'); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$message_array = $message['@fields']; |
|
$this->assertArrayHasKey('ctxt_from', $message_array); |
$this->assertEquals('logger', $message_array['ctxt_from']); |
|
// Test with extraPrefix |
$formatter = new LogstashFormatter('test', null, null, 'CTX'); |
$message = json_decode($formatter->format($record), true); |
|
$message_array = $message['@fields']; |
|
$this->assertArrayHasKey('CTXfrom', $message_array); |
$this->assertEquals('logger', $message_array['CTXfrom']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testFormatWithExtra() |
{ |
$formatter = new LogstashFormatter('test'); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$message_array = $message['@fields']; |
|
$this->assertArrayHasKey('key', $message_array); |
$this->assertEquals('pair', $message_array['key']); |
|
// Test with extraPrefix |
$formatter = new LogstashFormatter('test', null, 'EXT'); |
$message = json_decode($formatter->format($record), true); |
|
$message_array = $message['@fields']; |
|
$this->assertArrayHasKey('EXTkey', $message_array); |
$this->assertEquals('pair', $message_array['EXTkey']); |
} |
|
public function testFormatWithApplicationName() |
{ |
$formatter = new LogstashFormatter('app', 'test'); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertArrayHasKey('@type', $message); |
$this->assertEquals('app', $message['@type']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testDefaultFormatterV1() |
{ |
$formatter = new LogstashFormatter('test', 'hostname', null, 'ctxt_', LogstashFormatter::V1); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']); |
$this->assertEquals("1", $message['@version']); |
$this->assertEquals('log', $message['message']); |
$this->assertEquals('meh', $message['channel']); |
$this->assertEquals('ERROR', $message['level']); |
$this->assertEquals('test', $message['type']); |
$this->assertEquals('hostname', $message['host']); |
|
$formatter = new LogstashFormatter('mysystem', null, null, 'ctxt_', LogstashFormatter::V1); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals('mysystem', $message['type']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testFormatWithFileAndLineV1() |
{ |
$formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('file' => 'test', 'line' => 14), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals('test', $message['file']); |
$this->assertEquals(14, $message['line']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testFormatWithContextV1() |
{ |
$formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertArrayHasKey('ctxt_from', $message); |
$this->assertEquals('logger', $message['ctxt_from']); |
|
// Test with extraPrefix |
$formatter = new LogstashFormatter('test', null, null, 'CTX', LogstashFormatter::V1); |
$message = json_decode($formatter->format($record), true); |
|
$this->assertArrayHasKey('CTXfrom', $message); |
$this->assertEquals('logger', $message['CTXfrom']); |
} |
|
/** |
* @covers Monolog\Formatter\LogstashFormatter::format |
*/ |
public function testFormatWithExtraV1() |
{ |
$formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertArrayHasKey('key', $message); |
$this->assertEquals('pair', $message['key']); |
|
// Test with extraPrefix |
$formatter = new LogstashFormatter('test', null, 'EXT', 'ctxt_', LogstashFormatter::V1); |
$message = json_decode($formatter->format($record), true); |
|
$this->assertArrayHasKey('EXTkey', $message); |
$this->assertEquals('pair', $message['EXTkey']); |
} |
|
public function testFormatWithApplicationNameV1() |
{ |
$formatter = new LogstashFormatter('app', 'test', null, 'ctxt_', LogstashFormatter::V1); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('key' => 'pair'), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertArrayHasKey('type', $message); |
$this->assertEquals('app', $message['type']); |
} |
|
public function testFormatWithLatin9Data() |
{ |
if (version_compare(PHP_VERSION, '5.5.0', '<')) { |
// Ignore the warning that will be emitted by PHP <5.5.0 |
\PHPUnit_Framework_Error_Warning::$enabled = false; |
} |
$formatter = new LogstashFormatter('test', 'hostname'); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => '¯\_(ツ)_/¯', |
'context' => array(), |
'datetime' => new \DateTime("@0"), |
'extra' => array( |
'user_agent' => "\xD6WN; FBCR/OrangeEspa\xF1a; Vers\xE3o/4.0; F\xE4rist", |
), |
'message' => 'log', |
); |
|
$message = json_decode($formatter->format($record), true); |
|
$this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']); |
$this->assertEquals('log', $message['@message']); |
$this->assertEquals('¯\_(ツ)_/¯', $message['@fields']['channel']); |
$this->assertContains('¯\_(ツ)_/¯', $message['@tags']); |
$this->assertEquals(Logger::ERROR, $message['@fields']['level']); |
$this->assertEquals('test', $message['@type']); |
$this->assertEquals('hostname', $message['@source']); |
if (version_compare(PHP_VERSION, '5.5.0', '>=')) { |
$this->assertEquals('ÖWN; FBCR/OrangeEspaña; Versão/4.0; Färist', $message['@fields']['user_agent']); |
} else { |
// PHP <5.5 does not return false for an element encoding failure, |
// instead it emits a warning (possibly) and nulls the value. |
$this->assertEquals(null, $message['@fields']['user_agent']); |
} |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/MongoDBFormatterTest.php |
@@ -0,0 +1,262 @@ |
<?php |
|
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
|
/** |
* @author Florian Plattner <me@florianplattner.de> |
*/ |
class MongoDBFormatterTest extends \PHPUnit_Framework_TestCase |
{ |
public function setUp() |
{ |
if (!class_exists('MongoDate')) { |
$this->markTestSkipped('mongo extension not installed'); |
} |
} |
|
public function constructArgumentProvider() |
{ |
return array( |
array(1, true, 1, true), |
array(0, false, 0, false), |
); |
} |
|
/** |
* @param $traceDepth |
* @param $traceAsString |
* @param $expectedTraceDepth |
* @param $expectedTraceAsString |
* |
* @dataProvider constructArgumentProvider |
*/ |
public function testConstruct($traceDepth, $traceAsString, $expectedTraceDepth, $expectedTraceAsString) |
{ |
$formatter = new MongoDBFormatter($traceDepth, $traceAsString); |
|
$reflTrace = new \ReflectionProperty($formatter, 'exceptionTraceAsString'); |
$reflTrace->setAccessible(true); |
$this->assertEquals($expectedTraceAsString, $reflTrace->getValue($formatter)); |
|
$reflDepth = new\ReflectionProperty($formatter, 'maxNestingLevel'); |
$reflDepth->setAccessible(true); |
$this->assertEquals($expectedTraceDepth, $reflDepth->getValue($formatter)); |
} |
|
public function testSimpleFormat() |
{ |
$record = array( |
'message' => 'some log message', |
'context' => array(), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'test', |
'datetime' => new \DateTime('2014-02-01 00:00:00'), |
'extra' => array(), |
); |
|
$formatter = new MongoDBFormatter(); |
$formattedRecord = $formatter->format($record); |
|
$this->assertCount(7, $formattedRecord); |
$this->assertEquals('some log message', $formattedRecord['message']); |
$this->assertEquals(array(), $formattedRecord['context']); |
$this->assertEquals(Logger::WARNING, $formattedRecord['level']); |
$this->assertEquals(Logger::getLevelName(Logger::WARNING), $formattedRecord['level_name']); |
$this->assertEquals('test', $formattedRecord['channel']); |
$this->assertInstanceOf('\MongoDate', $formattedRecord['datetime']); |
$this->assertEquals('0.00000000 1391212800', $formattedRecord['datetime']->__toString()); |
$this->assertEquals(array(), $formattedRecord['extra']); |
} |
|
public function testRecursiveFormat() |
{ |
$someObject = new \stdClass(); |
$someObject->foo = 'something'; |
$someObject->bar = 'stuff'; |
|
$record = array( |
'message' => 'some log message', |
'context' => array( |
'stuff' => new \DateTime('2014-02-01 02:31:33'), |
'some_object' => $someObject, |
'context_string' => 'some string', |
'context_int' => 123456, |
'except' => new \Exception('exception message', 987), |
), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'test', |
'datetime' => new \DateTime('2014-02-01 00:00:00'), |
'extra' => array(), |
); |
|
$formatter = new MongoDBFormatter(); |
$formattedRecord = $formatter->format($record); |
|
$this->assertCount(5, $formattedRecord['context']); |
$this->assertInstanceOf('\MongoDate', $formattedRecord['context']['stuff']); |
$this->assertEquals('0.00000000 1391221893', $formattedRecord['context']['stuff']->__toString()); |
$this->assertEquals( |
array( |
'foo' => 'something', |
'bar' => 'stuff', |
'class' => 'stdClass', |
), |
$formattedRecord['context']['some_object'] |
); |
$this->assertEquals('some string', $formattedRecord['context']['context_string']); |
$this->assertEquals(123456, $formattedRecord['context']['context_int']); |
|
$this->assertCount(5, $formattedRecord['context']['except']); |
$this->assertEquals('exception message', $formattedRecord['context']['except']['message']); |
$this->assertEquals(987, $formattedRecord['context']['except']['code']); |
$this->assertInternalType('string', $formattedRecord['context']['except']['file']); |
$this->assertInternalType('integer', $formattedRecord['context']['except']['code']); |
$this->assertInternalType('string', $formattedRecord['context']['except']['trace']); |
$this->assertEquals('Exception', $formattedRecord['context']['except']['class']); |
} |
|
public function testFormatDepthArray() |
{ |
$record = array( |
'message' => 'some log message', |
'context' => array( |
'nest2' => array( |
'property' => 'anything', |
'nest3' => array( |
'nest4' => 'value', |
'property' => 'nothing', |
), |
), |
), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'test', |
'datetime' => new \DateTime('2014-02-01 00:00:00'), |
'extra' => array(), |
); |
|
$formatter = new MongoDBFormatter(2); |
$formattedResult = $formatter->format($record); |
|
$this->assertEquals( |
array( |
'nest2' => array( |
'property' => 'anything', |
'nest3' => '[...]', |
), |
), |
$formattedResult['context'] |
); |
} |
|
public function testFormatDepthArrayInfiniteNesting() |
{ |
$record = array( |
'message' => 'some log message', |
'context' => array( |
'nest2' => array( |
'property' => 'something', |
'nest3' => array( |
'property' => 'anything', |
'nest4' => array( |
'property' => 'nothing', |
), |
), |
), |
), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'test', |
'datetime' => new \DateTime('2014-02-01 00:00:00'), |
'extra' => array(), |
); |
|
$formatter = new MongoDBFormatter(0); |
$formattedResult = $formatter->format($record); |
|
$this->assertEquals( |
array( |
'nest2' => array( |
'property' => 'something', |
'nest3' => array( |
'property' => 'anything', |
'nest4' => array( |
'property' => 'nothing', |
), |
), |
), |
), |
$formattedResult['context'] |
); |
} |
|
public function testFormatDepthObjects() |
{ |
$someObject = new \stdClass(); |
$someObject->property = 'anything'; |
$someObject->nest3 = new \stdClass(); |
$someObject->nest3->property = 'nothing'; |
$someObject->nest3->nest4 = 'invisible'; |
|
$record = array( |
'message' => 'some log message', |
'context' => array( |
'nest2' => $someObject, |
), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'test', |
'datetime' => new \DateTime('2014-02-01 00:00:00'), |
'extra' => array(), |
); |
|
$formatter = new MongoDBFormatter(2, true); |
$formattedResult = $formatter->format($record); |
|
$this->assertEquals( |
array( |
'nest2' => array( |
'property' => 'anything', |
'nest3' => '[...]', |
'class' => 'stdClass', |
), |
), |
$formattedResult['context'] |
); |
} |
|
public function testFormatDepthException() |
{ |
$record = array( |
'message' => 'some log message', |
'context' => array( |
'nest2' => new \Exception('exception message', 987), |
), |
'level' => Logger::WARNING, |
'level_name' => Logger::getLevelName(Logger::WARNING), |
'channel' => 'test', |
'datetime' => new \DateTime('2014-02-01 00:00:00'), |
'extra' => array(), |
); |
|
$formatter = new MongoDBFormatter(2, false); |
$formattedRecord = $formatter->format($record); |
|
$this->assertEquals('exception message', $formattedRecord['context']['nest2']['message']); |
$this->assertEquals(987, $formattedRecord['context']['nest2']['code']); |
$this->assertEquals('[...]', $formattedRecord['context']['nest2']['trace']); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php |
@@ -0,0 +1,423 @@ |
<?php |
|
/* |
* 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; |
|
/** |
* @covers Monolog\Formatter\NormalizerFormatter |
*/ |
class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase |
{ |
public function tearDown() |
{ |
\PHPUnit_Framework_Error_Warning::$enabled = true; |
|
return parent::tearDown(); |
} |
|
public function testFormat() |
{ |
$formatter = new NormalizerFormatter('Y-m-d'); |
$formatted = $formatter->format(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'message' => 'foo', |
'datetime' => new \DateTime, |
'extra' => array('foo' => new TestFooNorm, 'bar' => new TestBarNorm, 'baz' => array(), 'res' => fopen('php://memory', 'rb')), |
'context' => array( |
'foo' => 'bar', |
'baz' => 'qux', |
'inf' => INF, |
'-inf' => -INF, |
'nan' => acos(4), |
), |
)); |
|
$this->assertEquals(array( |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'message' => 'foo', |
'datetime' => date('Y-m-d'), |
'extra' => array( |
'foo' => '[object] (Monolog\\Formatter\\TestFooNorm: {"foo":"foo"})', |
'bar' => '[object] (Monolog\\Formatter\\TestBarNorm: bar)', |
'baz' => array(), |
'res' => '[resource] (stream)', |
), |
'context' => array( |
'foo' => 'bar', |
'baz' => 'qux', |
'inf' => 'INF', |
'-inf' => '-INF', |
'nan' => 'NaN', |
), |
), $formatted); |
} |
|
public function testFormatExceptions() |
{ |
$formatter = new NormalizerFormatter('Y-m-d'); |
$e = new \LogicException('bar'); |
$e2 = new \RuntimeException('foo', 0, $e); |
$formatted = $formatter->format(array( |
'exception' => $e2, |
)); |
|
$this->assertGreaterThan(5, count($formatted['exception']['trace'])); |
$this->assertTrue(isset($formatted['exception']['previous'])); |
unset($formatted['exception']['trace'], $formatted['exception']['previous']); |
|
$this->assertEquals(array( |
'exception' => array( |
'class' => get_class($e2), |
'message' => $e2->getMessage(), |
'code' => $e2->getCode(), |
'file' => $e2->getFile().':'.$e2->getLine(), |
), |
), $formatted); |
} |
|
public function testFormatSoapFaultException() |
{ |
if (!class_exists('SoapFault')) { |
$this->markTestSkipped('Requires the soap extension'); |
} |
|
$formatter = new NormalizerFormatter('Y-m-d'); |
$e = new \SoapFault('foo', 'bar', 'hello', 'world'); |
$formatted = $formatter->format(array( |
'exception' => $e, |
)); |
|
unset($formatted['exception']['trace']); |
|
$this->assertEquals(array( |
'exception' => array( |
'class' => 'SoapFault', |
'message' => 'bar', |
'code' => 0, |
'file' => $e->getFile().':'.$e->getLine(), |
'faultcode' => 'foo', |
'faultactor' => 'hello', |
'detail' => 'world', |
), |
), $formatted); |
} |
|
public function testFormatToStringExceptionHandle() |
{ |
$formatter = new NormalizerFormatter('Y-m-d'); |
$this->setExpectedException('RuntimeException', 'Could not convert to string'); |
$formatter->format(array( |
'myObject' => new TestToStringError(), |
)); |
} |
|
public function testBatchFormat() |
{ |
$formatter = new NormalizerFormatter('Y-m-d'); |
$formatted = $formatter->formatBatch(array( |
array( |
'level_name' => 'CRITICAL', |
'channel' => 'test', |
'message' => 'bar', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array(), |
), |
array( |
'level_name' => 'WARNING', |
'channel' => 'log', |
'message' => 'foo', |
'context' => array(), |
'datetime' => new \DateTime, |
'extra' => array(), |
), |
)); |
$this->assertEquals(array( |
array( |
'level_name' => 'CRITICAL', |
'channel' => 'test', |
'message' => 'bar', |
'context' => array(), |
'datetime' => date('Y-m-d'), |
'extra' => array(), |
), |
array( |
'level_name' => 'WARNING', |
'channel' => 'log', |
'message' => 'foo', |
'context' => array(), |
'datetime' => date('Y-m-d'), |
'extra' => array(), |
), |
), $formatted); |
} |
|
/** |
* Test issue #137 |
*/ |
public function testIgnoresRecursiveObjectReferences() |
{ |
// set up the recursion |
$foo = new \stdClass(); |
$bar = new \stdClass(); |
|
$foo->bar = $bar; |
$bar->foo = $foo; |
|
// set an error handler to assert that the error is not raised anymore |
$that = $this; |
set_error_handler(function ($level, $message, $file, $line, $context) use ($that) { |
if (error_reporting() & $level) { |
restore_error_handler(); |
$that->fail("$message should not be raised"); |
} |
}); |
|
$formatter = new NormalizerFormatter(); |
$reflMethod = new \ReflectionMethod($formatter, 'toJson'); |
$reflMethod->setAccessible(true); |
$res = $reflMethod->invoke($formatter, array($foo, $bar), true); |
|
restore_error_handler(); |
|
$this->assertEquals(@json_encode(array($foo, $bar)), $res); |
} |
|
public function testIgnoresInvalidTypes() |
{ |
// set up the recursion |
$resource = fopen(__FILE__, 'r'); |
|
// set an error handler to assert that the error is not raised anymore |
$that = $this; |
set_error_handler(function ($level, $message, $file, $line, $context) use ($that) { |
if (error_reporting() & $level) { |
restore_error_handler(); |
$that->fail("$message should not be raised"); |
} |
}); |
|
$formatter = new NormalizerFormatter(); |
$reflMethod = new \ReflectionMethod($formatter, 'toJson'); |
$reflMethod->setAccessible(true); |
$res = $reflMethod->invoke($formatter, array($resource), true); |
|
restore_error_handler(); |
|
$this->assertEquals(@json_encode(array($resource)), $res); |
} |
|
public function testNormalizeHandleLargeArrays() |
{ |
$formatter = new NormalizerFormatter(); |
$largeArray = range(1, 2000); |
|
$res = $formatter->format(array( |
'level_name' => 'CRITICAL', |
'channel' => 'test', |
'message' => 'bar', |
'context' => array($largeArray), |
'datetime' => new \DateTime, |
'extra' => array(), |
)); |
|
$this->assertCount(1000, $res['context'][0]); |
$this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); |
} |
|
/** |
* @expectedException RuntimeException |
*/ |
public function testThrowsOnInvalidEncoding() |
{ |
if (version_compare(PHP_VERSION, '5.5.0', '<')) { |
// Ignore the warning that will be emitted by PHP <5.5.0 |
\PHPUnit_Framework_Error_Warning::$enabled = false; |
} |
$formatter = new NormalizerFormatter(); |
$reflMethod = new \ReflectionMethod($formatter, 'toJson'); |
$reflMethod->setAccessible(true); |
|
// send an invalid unicode sequence as a object that can't be cleaned |
$record = new \stdClass; |
$record->message = "\xB1\x31"; |
$res = $reflMethod->invoke($formatter, $record); |
if (PHP_VERSION_ID < 50500 && $res === '{"message":null}') { |
throw new \RuntimeException('PHP 5.3/5.4 throw a warning and null the value instead of returning false entirely'); |
} |
} |
|
public function testConvertsInvalidEncodingAsLatin9() |
{ |
if (version_compare(PHP_VERSION, '5.5.0', '<')) { |
// Ignore the warning that will be emitted by PHP <5.5.0 |
\PHPUnit_Framework_Error_Warning::$enabled = false; |
} |
$formatter = new NormalizerFormatter(); |
$reflMethod = new \ReflectionMethod($formatter, 'toJson'); |
$reflMethod->setAccessible(true); |
|
$res = $reflMethod->invoke($formatter, array('message' => "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE")); |
|
if (version_compare(PHP_VERSION, '5.5.0', '>=')) { |
$this->assertSame('{"message":"€ŠšŽžŒœŸ"}', $res); |
} else { |
// PHP <5.5 does not return false for an element encoding failure, |
// instead it emits a warning (possibly) and nulls the value. |
$this->assertSame('{"message":null}', $res); |
} |
} |
|
/** |
* @param mixed $in Input |
* @param mixed $expect Expected output |
* @covers Monolog\Formatter\NormalizerFormatter::detectAndCleanUtf8 |
* @dataProvider providesDetectAndCleanUtf8 |
*/ |
public function testDetectAndCleanUtf8($in, $expect) |
{ |
$formatter = new NormalizerFormatter(); |
$formatter->detectAndCleanUtf8($in); |
$this->assertSame($expect, $in); |
} |
|
public function providesDetectAndCleanUtf8() |
{ |
$obj = new \stdClass; |
|
return array( |
'null' => array(null, null), |
'int' => array(123, 123), |
'float' => array(123.45, 123.45), |
'bool false' => array(false, false), |
'bool true' => array(true, true), |
'ascii string' => array('abcdef', 'abcdef'), |
'latin9 string' => array("\xB1\x31\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xFF", '±1€ŠšŽžŒœŸÿ'), |
'unicode string' => array('¤¦¨´¸¼½¾€ŠšŽžŒœŸ', '¤¦¨´¸¼½¾€ŠšŽžŒœŸ'), |
'empty array' => array(array(), array()), |
'array' => array(array('abcdef'), array('abcdef')), |
'object' => array($obj, $obj), |
); |
} |
|
/** |
* @param int $code |
* @param string $msg |
* @dataProvider providesHandleJsonErrorFailure |
*/ |
public function testHandleJsonErrorFailure($code, $msg) |
{ |
$formatter = new NormalizerFormatter(); |
$reflMethod = new \ReflectionMethod($formatter, 'handleJsonError'); |
$reflMethod->setAccessible(true); |
|
$this->setExpectedException('RuntimeException', $msg); |
$reflMethod->invoke($formatter, $code, 'faked'); |
} |
|
public function providesHandleJsonErrorFailure() |
{ |
return array( |
'depth' => array(JSON_ERROR_DEPTH, 'Maximum stack depth exceeded'), |
'state' => array(JSON_ERROR_STATE_MISMATCH, 'Underflow or the modes mismatch'), |
'ctrl' => array(JSON_ERROR_CTRL_CHAR, 'Unexpected control character found'), |
'default' => array(-1, 'Unknown error'), |
); |
} |
|
public function testExceptionTraceWithArgs() |
{ |
if (defined('HHVM_VERSION')) { |
$this->markTestSkipped('Not supported in HHVM since it detects errors differently'); |
} |
|
// This happens i.e. in React promises or Guzzle streams where stream wrappers are registered |
// and no file or line are included in the trace because it's treated as internal function |
set_error_handler(function ($errno, $errstr, $errfile, $errline) { |
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); |
}); |
|
try { |
// This will contain $resource and $wrappedResource as arguments in the trace item |
$resource = fopen('php://memory', 'rw+'); |
fwrite($resource, 'test_resource'); |
$wrappedResource = new TestFooNorm; |
$wrappedResource->foo = $resource; |
// Just do something stupid with a resource/wrapped resource as argument |
array_keys($wrappedResource); |
} catch (\Exception $e) { |
restore_error_handler(); |
} |
|
$formatter = new NormalizerFormatter(); |
$record = array('context' => array('exception' => $e)); |
$result = $formatter->format($record); |
|
$this->assertRegExp( |
'%"resource":"\[resource\] \(stream\)"%', |
$result['context']['exception']['trace'][0] |
); |
|
if (version_compare(PHP_VERSION, '5.5.0', '>=')) { |
$pattern = '%"wrappedResource":"\[object\] \(Monolog\\\\\\\\Formatter\\\\\\\\TestFooNorm: \)"%'; |
} else { |
$pattern = '%\\\\"foo\\\\":null%'; |
} |
|
// Tests that the wrapped resource is ignored while encoding, only works for PHP <= 5.4 |
$this->assertRegExp( |
$pattern, |
$result['context']['exception']['trace'][0] |
); |
} |
} |
|
class TestFooNorm |
{ |
public $foo = 'foo'; |
} |
|
class TestBarNorm |
{ |
public function __toString() |
{ |
return 'bar'; |
} |
} |
|
class TestStreamFoo |
{ |
public $foo; |
public $resource; |
|
public function __construct($resource) |
{ |
$this->resource = $resource; |
$this->foo = 'BAR'; |
} |
|
public function __toString() |
{ |
fseek($this->resource, 0); |
|
return $this->foo . ' - ' . (string) stream_get_contents($this->resource); |
} |
} |
|
class TestToStringError |
{ |
public function __toString() |
{ |
throw new \RuntimeException('Could not convert to string'); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.php |
@@ -0,0 +1,142 @@ |
<?php |
|
/* |
* This file is part of the Monolog package. |
* |
* (c) Jordi Boggiano <j.boggiano@seld.be> |
* |
* For the full copyright and license information, please view the LICENSE |
* file that was distributed with this source code. |
*/ |
|
namespace Monolog\Formatter; |
|
use Monolog\Logger; |
|
class WildfireFormatterTest extends \PHPUnit_Framework_TestCase |
{ |
/** |
* @covers Monolog\Formatter\WildfireFormatter::format |
*/ |
public function testDefaultFormat() |
{ |
$wildfire = new WildfireFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('ip' => '127.0.0.1'), |
'message' => 'log', |
); |
|
$message = $wildfire->format($record); |
|
$this->assertEquals( |
'125|[{"Type":"ERROR","File":"","Line":"","Label":"meh"},' |
.'{"message":"log","context":{"from":"logger"},"extra":{"ip":"127.0.0.1"}}]|', |
$message |
); |
} |
|
/** |
* @covers Monolog\Formatter\WildfireFormatter::format |
*/ |
public function testFormatWithFileAndLine() |
{ |
$wildfire = new WildfireFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('from' => 'logger'), |
'datetime' => new \DateTime("@0"), |
'extra' => array('ip' => '127.0.0.1', 'file' => 'test', 'line' => 14), |
'message' => 'log', |
); |
|
$message = $wildfire->format($record); |
|
$this->assertEquals( |
'129|[{"Type":"ERROR","File":"test","Line":14,"Label":"meh"},' |
.'{"message":"log","context":{"from":"logger"},"extra":{"ip":"127.0.0.1"}}]|', |
$message |
); |
} |
|
/** |
* @covers Monolog\Formatter\WildfireFormatter::format |
*/ |
public function testFormatWithoutContext() |
{ |
$wildfire = new WildfireFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$message = $wildfire->format($record); |
|
$this->assertEquals( |
'58|[{"Type":"ERROR","File":"","Line":"","Label":"meh"},"log"]|', |
$message |
); |
} |
|
/** |
* @covers Monolog\Formatter\WildfireFormatter::formatBatch |
* @expectedException BadMethodCallException |
*/ |
public function testBatchFormatThrowException() |
{ |
$wildfire = new WildfireFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array(), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$wildfire->formatBatch(array($record)); |
} |
|
/** |
* @covers Monolog\Formatter\WildfireFormatter::format |
*/ |
public function testTableFormat() |
{ |
$wildfire = new WildfireFormatter(); |
$record = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'table-channel', |
'context' => array( |
WildfireFormatter::TABLE => array( |
array('col1', 'col2', 'col3'), |
array('val1', 'val2', 'val3'), |
array('foo1', 'foo2', 'foo3'), |
array('bar1', 'bar2', 'bar3'), |
), |
), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'table-message', |
); |
|
$message = $wildfire->format($record); |
|
$this->assertEquals( |
'171|[{"Type":"TABLE","File":"","Line":"","Label":"table-channel: table-message"},[["col1","col2","col3"],["val1","val2","val3"],["foo1","foo2","foo3"],["bar1","bar2","bar3"]]]|', |
$message |
); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.php |
@@ -0,0 +1,115 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
use Monolog\Processor\WebProcessor; |
|
class AbstractHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\AbstractHandler::__construct |
* @covers Monolog\Handler\AbstractHandler::getLevel |
* @covers Monolog\Handler\AbstractHandler::setLevel |
* @covers Monolog\Handler\AbstractHandler::getBubble |
* @covers Monolog\Handler\AbstractHandler::setBubble |
* @covers Monolog\Handler\AbstractHandler::getFormatter |
* @covers Monolog\Handler\AbstractHandler::setFormatter |
*/ |
public function testConstructAndGetSet() |
{ |
$handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler', array(Logger::WARNING, false)); |
$this->assertEquals(Logger::WARNING, $handler->getLevel()); |
$this->assertEquals(false, $handler->getBubble()); |
|
$handler->setLevel(Logger::ERROR); |
$handler->setBubble(true); |
$handler->setFormatter($formatter = new LineFormatter); |
$this->assertEquals(Logger::ERROR, $handler->getLevel()); |
$this->assertEquals(true, $handler->getBubble()); |
$this->assertSame($formatter, $handler->getFormatter()); |
} |
|
/** |
* @covers Monolog\Handler\AbstractHandler::handleBatch |
*/ |
public function testHandleBatch() |
{ |
$handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); |
$handler->expects($this->exactly(2)) |
->method('handle'); |
$handler->handleBatch(array($this->getRecord(), $this->getRecord())); |
} |
|
/** |
* @covers Monolog\Handler\AbstractHandler::isHandling |
*/ |
public function testIsHandling() |
{ |
$handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler', array(Logger::WARNING, false)); |
$this->assertTrue($handler->isHandling($this->getRecord())); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); |
} |
|
/** |
* @covers Monolog\Handler\AbstractHandler::__construct |
*/ |
public function testHandlesPsrStyleLevels() |
{ |
$handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler', array('warning', false)); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); |
$handler->setLevel('debug'); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::DEBUG))); |
} |
|
/** |
* @covers Monolog\Handler\AbstractHandler::getFormatter |
* @covers Monolog\Handler\AbstractHandler::getDefaultFormatter |
*/ |
public function testGetFormatterInitializesDefault() |
{ |
$handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); |
$this->assertInstanceOf('Monolog\Formatter\LineFormatter', $handler->getFormatter()); |
} |
|
/** |
* @covers Monolog\Handler\AbstractHandler::pushProcessor |
* @covers Monolog\Handler\AbstractHandler::popProcessor |
* @expectedException LogicException |
*/ |
public function testPushPopProcessor() |
{ |
$logger = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); |
$processor1 = new WebProcessor; |
$processor2 = new WebProcessor; |
|
$logger->pushProcessor($processor1); |
$logger->pushProcessor($processor2); |
|
$this->assertEquals($processor2, $logger->popProcessor()); |
$this->assertEquals($processor1, $logger->popProcessor()); |
$logger->popProcessor(); |
} |
|
/** |
* @covers Monolog\Handler\AbstractHandler::pushProcessor |
* @expectedException InvalidArgumentException |
*/ |
public function testPushProcessorWithNonCallable() |
{ |
$handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); |
|
$handler->pushProcessor(new \stdClass()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.php |
@@ -0,0 +1,136 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use PhpAmqpLib\Message\AMQPMessage; |
use PhpAmqpLib\Connection\AMQPConnection; |
|
/** |
* @covers Monolog\Handler\RotatingFileHandler |
*/ |
class AmqpHandlerTest extends TestCase |
{ |
public function testHandleAmqpExt() |
{ |
if (!class_exists('AMQPConnection') || !class_exists('AMQPExchange')) { |
$this->markTestSkipped("amqp-php not installed"); |
} |
|
if (!class_exists('AMQPChannel')) { |
$this->markTestSkipped("Please update AMQP to version >= 1.0"); |
} |
|
$messages = array(); |
|
$exchange = $this->getMock('AMQPExchange', array('publish', 'setName'), array(), '', false); |
$exchange->expects($this->once()) |
->method('setName') |
->with('log') |
; |
$exchange->expects($this->any()) |
->method('publish') |
->will($this->returnCallback(function ($message, $routing_key, $flags = 0, $attributes = array()) use (&$messages) { |
$messages[] = array($message, $routing_key, $flags, $attributes); |
})) |
; |
|
$handler = new AmqpHandler($exchange, 'log'); |
|
$record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); |
|
$expected = array( |
array( |
'message' => 'test', |
'context' => array( |
'data' => array(), |
'foo' => 34, |
), |
'level' => 300, |
'level_name' => 'WARNING', |
'channel' => 'test', |
'extra' => array(), |
), |
'warn.test', |
0, |
array( |
'delivery_mode' => 2, |
'content_type' => 'application/json', |
), |
); |
|
$handler->handle($record); |
|
$this->assertCount(1, $messages); |
$messages[0][0] = json_decode($messages[0][0], true); |
unset($messages[0][0]['datetime']); |
$this->assertEquals($expected, $messages[0]); |
} |
|
public function testHandlePhpAmqpLib() |
{ |
if (!class_exists('PhpAmqpLib\Connection\AMQPConnection')) { |
$this->markTestSkipped("php-amqplib not installed"); |
} |
|
$messages = array(); |
|
$exchange = $this->getMock('PhpAmqpLib\Channel\AMQPChannel', array('basic_publish', '__destruct'), array(), '', false); |
|
$exchange->expects($this->any()) |
->method('basic_publish') |
->will($this->returnCallback(function (AMQPMessage $msg, $exchange = "", $routing_key = "", $mandatory = false, $immediate = false, $ticket = null) use (&$messages) { |
$messages[] = array($msg, $exchange, $routing_key, $mandatory, $immediate, $ticket); |
})) |
; |
|
$handler = new AmqpHandler($exchange, 'log'); |
|
$record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); |
|
$expected = array( |
array( |
'message' => 'test', |
'context' => array( |
'data' => array(), |
'foo' => 34, |
), |
'level' => 300, |
'level_name' => 'WARNING', |
'channel' => 'test', |
'extra' => array(), |
), |
'log', |
'warn.test', |
false, |
false, |
null, |
array( |
'delivery_mode' => 2, |
'content_type' => 'application/json', |
), |
); |
|
$handler->handle($record); |
|
$this->assertCount(1, $messages); |
|
/* @var $msg AMQPMessage */ |
$msg = $messages[0][0]; |
$messages[0][0] = json_decode($msg->body, true); |
$messages[0][] = $msg->get_properties(); |
unset($messages[0][0]['datetime']); |
|
$this->assertEquals($expected, $messages[0]); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php |
@@ -0,0 +1,130 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
/** |
* @covers Monolog\Handler\BrowserConsoleHandlerTest |
*/ |
class BrowserConsoleHandlerTest extends TestCase |
{ |
protected function setUp() |
{ |
BrowserConsoleHandler::reset(); |
} |
|
protected function generateScript() |
{ |
$reflMethod = new \ReflectionMethod('Monolog\Handler\BrowserConsoleHandler', 'generateScript'); |
$reflMethod->setAccessible(true); |
|
return $reflMethod->invoke(null); |
} |
|
public function testStyling() |
{ |
$handler = new BrowserConsoleHandler(); |
$handler->setFormatter($this->getIdentityFormatter()); |
|
$handler->handle($this->getRecord(Logger::DEBUG, 'foo[[bar]]{color: red}')); |
|
$expected = <<<EOF |
(function (c) {if (c && c.groupCollapsed) { |
c.log("%cfoo%cbar%c", "font-weight: normal", "color: red", "font-weight: normal"); |
}})(console); |
EOF; |
|
$this->assertEquals($expected, $this->generateScript()); |
} |
|
public function testEscaping() |
{ |
$handler = new BrowserConsoleHandler(); |
$handler->setFormatter($this->getIdentityFormatter()); |
|
$handler->handle($this->getRecord(Logger::DEBUG, "[foo] [[\"bar\n[baz]\"]]{color: red}")); |
|
$expected = <<<EOF |
(function (c) {if (c && c.groupCollapsed) { |
c.log("%c[foo] %c\"bar\\n[baz]\"%c", "font-weight: normal", "color: red", "font-weight: normal"); |
}})(console); |
EOF; |
|
$this->assertEquals($expected, $this->generateScript()); |
} |
|
public function testAutolabel() |
{ |
$handler = new BrowserConsoleHandler(); |
$handler->setFormatter($this->getIdentityFormatter()); |
|
$handler->handle($this->getRecord(Logger::DEBUG, '[[foo]]{macro: autolabel}')); |
$handler->handle($this->getRecord(Logger::DEBUG, '[[bar]]{macro: autolabel}')); |
$handler->handle($this->getRecord(Logger::DEBUG, '[[foo]]{macro: autolabel}')); |
|
$expected = <<<EOF |
(function (c) {if (c && c.groupCollapsed) { |
c.log("%c%cfoo%c", "font-weight: normal", "background-color: blue; color: white; border-radius: 3px; padding: 0 2px 0 2px", "font-weight: normal"); |
c.log("%c%cbar%c", "font-weight: normal", "background-color: green; color: white; border-radius: 3px; padding: 0 2px 0 2px", "font-weight: normal"); |
c.log("%c%cfoo%c", "font-weight: normal", "background-color: blue; color: white; border-radius: 3px; padding: 0 2px 0 2px", "font-weight: normal"); |
}})(console); |
EOF; |
|
$this->assertEquals($expected, $this->generateScript()); |
} |
|
public function testContext() |
{ |
$handler = new BrowserConsoleHandler(); |
$handler->setFormatter($this->getIdentityFormatter()); |
|
$handler->handle($this->getRecord(Logger::DEBUG, 'test', array('foo' => 'bar'))); |
|
$expected = <<<EOF |
(function (c) {if (c && c.groupCollapsed) { |
c.groupCollapsed("%ctest", "font-weight: normal"); |
c.log("%c%s", "font-weight: bold", "Context"); |
c.log("%s: %o", "foo", "bar"); |
c.groupEnd(); |
}})(console); |
EOF; |
|
$this->assertEquals($expected, $this->generateScript()); |
} |
|
public function testConcurrentHandlers() |
{ |
$handler1 = new BrowserConsoleHandler(); |
$handler1->setFormatter($this->getIdentityFormatter()); |
|
$handler2 = new BrowserConsoleHandler(); |
$handler2->setFormatter($this->getIdentityFormatter()); |
|
$handler1->handle($this->getRecord(Logger::DEBUG, 'test1')); |
$handler2->handle($this->getRecord(Logger::DEBUG, 'test2')); |
$handler1->handle($this->getRecord(Logger::DEBUG, 'test3')); |
$handler2->handle($this->getRecord(Logger::DEBUG, 'test4')); |
|
$expected = <<<EOF |
(function (c) {if (c && c.groupCollapsed) { |
c.log("%ctest1", "font-weight: normal"); |
c.log("%ctest2", "font-weight: normal"); |
c.log("%ctest3", "font-weight: normal"); |
c.log("%ctest4", "font-weight: normal"); |
}})(console); |
EOF; |
|
$this->assertEquals($expected, $this->generateScript()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php |
@@ -0,0 +1,158 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
class BufferHandlerTest extends TestCase |
{ |
private $shutdownCheckHandler; |
|
/** |
* @covers Monolog\Handler\BufferHandler::__construct |
* @covers Monolog\Handler\BufferHandler::handle |
* @covers Monolog\Handler\BufferHandler::close |
*/ |
public function testHandleBuffers() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertFalse($test->hasInfoRecords()); |
$handler->close(); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 2); |
} |
|
/** |
* @covers Monolog\Handler\BufferHandler::close |
* @covers Monolog\Handler\BufferHandler::flush |
*/ |
public function testPropagatesRecordsAtEndOfRequest() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->shutdownCheckHandler = $test; |
register_shutdown_function(array($this, 'checkPropagation')); |
} |
|
public function checkPropagation() |
{ |
if (!$this->shutdownCheckHandler->hasWarningRecords() || !$this->shutdownCheckHandler->hasDebugRecords()) { |
echo '!!! BufferHandlerTest::testPropagatesRecordsAtEndOfRequest failed to verify that the messages have been propagated' . PHP_EOL; |
exit(1); |
} |
} |
|
/** |
* @covers Monolog\Handler\BufferHandler::handle |
*/ |
public function testHandleBufferLimit() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test, 2); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->close(); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertFalse($test->hasDebugRecords()); |
} |
|
/** |
* @covers Monolog\Handler\BufferHandler::handle |
*/ |
public function testHandleBufferLimitWithFlushOnOverflow() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test, 3, Logger::DEBUG, true, true); |
|
// send two records |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertCount(0, $test->getRecords()); |
|
// overflow |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertCount(3, $test->getRecords()); |
|
// should buffer again |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertCount(3, $test->getRecords()); |
|
$handler->close(); |
$this->assertCount(5, $test->getRecords()); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
} |
|
/** |
* @covers Monolog\Handler\BufferHandler::handle |
*/ |
public function testHandleLevel() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test, 0, Logger::INFO); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->close(); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertFalse($test->hasDebugRecords()); |
} |
|
/** |
* @covers Monolog\Handler\BufferHandler::flush |
*/ |
public function testFlush() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test, 0); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->flush(); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\BufferHandler::handle |
*/ |
public function testHandleUsesProcessors() |
{ |
$test = new TestHandler(); |
$handler = new BufferHandler($test); |
$handler->pushProcessor(function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
}); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->flush(); |
$this->assertTrue($test->hasWarningRecords()); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php |
@@ -0,0 +1,156 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
/** |
* @covers Monolog\Handler\ChromePHPHandler |
*/ |
class ChromePHPHandlerTest extends TestCase |
{ |
protected function setUp() |
{ |
TestChromePHPHandler::reset(); |
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0'; |
} |
|
/** |
* @dataProvider agentsProvider |
*/ |
public function testHeaders($agent) |
{ |
$_SERVER['HTTP_USER_AGENT'] = $agent; |
|
$handler = new TestChromePHPHandler(); |
$handler->setFormatter($this->getIdentityFormatter()); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::WARNING)); |
|
$expected = array( |
'X-ChromeLogger-Data' => base64_encode(utf8_encode(json_encode(array( |
'version' => ChromePHPHandler::VERSION, |
'columns' => array('label', 'log', 'backtrace', 'type'), |
'rows' => array( |
'test', |
'test', |
), |
'request_uri' => '', |
)))), |
); |
|
$this->assertEquals($expected, $handler->getHeaders()); |
} |
|
public static function agentsProvider() |
{ |
return array( |
array('Monolog Test; Chrome/1.0'), |
array('Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0'), |
array('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/56.0.2924.76 Chrome/56.0.2924.76 Safari/537.36'), |
array('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome Safari/537.36'), |
); |
} |
|
public function testHeadersOverflow() |
{ |
$handler = new TestChromePHPHandler(); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::WARNING, str_repeat('a', 150 * 1024))); |
|
// overflow chrome headers limit |
$handler->handle($this->getRecord(Logger::WARNING, str_repeat('a', 100 * 1024))); |
|
$expected = array( |
'X-ChromeLogger-Data' => base64_encode(utf8_encode(json_encode(array( |
'version' => ChromePHPHandler::VERSION, |
'columns' => array('label', 'log', 'backtrace', 'type'), |
'rows' => array( |
array( |
'test', |
'test', |
'unknown', |
'log', |
), |
array( |
'test', |
str_repeat('a', 150 * 1024), |
'unknown', |
'warn', |
), |
array( |
'monolog', |
'Incomplete logs, chrome header size limit reached', |
'unknown', |
'warn', |
), |
), |
'request_uri' => '', |
)))), |
); |
|
$this->assertEquals($expected, $handler->getHeaders()); |
} |
|
public function testConcurrentHandlers() |
{ |
$handler = new TestChromePHPHandler(); |
$handler->setFormatter($this->getIdentityFormatter()); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::WARNING)); |
|
$handler2 = new TestChromePHPHandler(); |
$handler2->setFormatter($this->getIdentityFormatter()); |
$handler2->handle($this->getRecord(Logger::DEBUG)); |
$handler2->handle($this->getRecord(Logger::WARNING)); |
|
$expected = array( |
'X-ChromeLogger-Data' => base64_encode(utf8_encode(json_encode(array( |
'version' => ChromePHPHandler::VERSION, |
'columns' => array('label', 'log', 'backtrace', 'type'), |
'rows' => array( |
'test', |
'test', |
'test', |
'test', |
), |
'request_uri' => '', |
)))), |
); |
|
$this->assertEquals($expected, $handler2->getHeaders()); |
} |
} |
|
class TestChromePHPHandler extends ChromePHPHandler |
{ |
protected $headers = array(); |
|
public static function reset() |
{ |
self::$initialized = false; |
self::$overflowed = false; |
self::$sendHeaders = true; |
self::$json['rows'] = array(); |
} |
|
protected function sendHeader($header, $content) |
{ |
$this->headers[$header] = $content; |
} |
|
public function getHeaders() |
{ |
return $this->headers; |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/DeduplicationHandlerTest.php |
@@ -0,0 +1,165 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
class DeduplicationHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\DeduplicationHandler::flush |
*/ |
public function testFlushPassthruIfAllRecordsUnderTrigger() |
{ |
$test = new TestHandler(); |
@unlink(sys_get_temp_dir().'/monolog_dedup.log'); |
$handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); |
|
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
|
$handler->flush(); |
|
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\DeduplicationHandler::flush |
* @covers Monolog\Handler\DeduplicationHandler::appendRecord |
*/ |
public function testFlushPassthruIfEmptyLog() |
{ |
$test = new TestHandler(); |
@unlink(sys_get_temp_dir().'/monolog_dedup.log'); |
$handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); |
|
$handler->handle($this->getRecord(Logger::ERROR, 'Foo:bar')); |
$handler->handle($this->getRecord(Logger::CRITICAL, "Foo\nbar")); |
|
$handler->flush(); |
|
$this->assertTrue($test->hasErrorRecords()); |
$this->assertTrue($test->hasCriticalRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\DeduplicationHandler::flush |
* @covers Monolog\Handler\DeduplicationHandler::appendRecord |
* @covers Monolog\Handler\DeduplicationHandler::isDuplicate |
* @depends testFlushPassthruIfEmptyLog |
*/ |
public function testFlushSkipsIfLogExists() |
{ |
$test = new TestHandler(); |
$handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); |
|
$handler->handle($this->getRecord(Logger::ERROR, 'Foo:bar')); |
$handler->handle($this->getRecord(Logger::CRITICAL, "Foo\nbar")); |
|
$handler->flush(); |
|
$this->assertFalse($test->hasErrorRecords()); |
$this->assertFalse($test->hasCriticalRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\DeduplicationHandler::flush |
* @covers Monolog\Handler\DeduplicationHandler::appendRecord |
* @covers Monolog\Handler\DeduplicationHandler::isDuplicate |
* @depends testFlushPassthruIfEmptyLog |
*/ |
public function testFlushPassthruIfLogTooOld() |
{ |
$test = new TestHandler(); |
$handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); |
|
$record = $this->getRecord(Logger::ERROR); |
$record['datetime']->modify('+62seconds'); |
$handler->handle($record); |
$record = $this->getRecord(Logger::CRITICAL); |
$record['datetime']->modify('+62seconds'); |
$handler->handle($record); |
|
$handler->flush(); |
|
$this->assertTrue($test->hasErrorRecords()); |
$this->assertTrue($test->hasCriticalRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\DeduplicationHandler::flush |
* @covers Monolog\Handler\DeduplicationHandler::appendRecord |
* @covers Monolog\Handler\DeduplicationHandler::isDuplicate |
* @covers Monolog\Handler\DeduplicationHandler::collectLogs |
*/ |
public function testGcOldLogs() |
{ |
$test = new TestHandler(); |
@unlink(sys_get_temp_dir().'/monolog_dedup.log'); |
$handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); |
|
// handle two records from yesterday, and one recent |
$record = $this->getRecord(Logger::ERROR); |
$record['datetime']->modify('-1day -10seconds'); |
$handler->handle($record); |
$record2 = $this->getRecord(Logger::CRITICAL); |
$record2['datetime']->modify('-1day -10seconds'); |
$handler->handle($record2); |
$record3 = $this->getRecord(Logger::CRITICAL); |
$record3['datetime']->modify('-30seconds'); |
$handler->handle($record3); |
|
// log is written as none of them are duplicate |
$handler->flush(); |
$this->assertSame( |
$record['datetime']->getTimestamp() . ":ERROR:test\n" . |
$record2['datetime']->getTimestamp() . ":CRITICAL:test\n" . |
$record3['datetime']->getTimestamp() . ":CRITICAL:test\n", |
file_get_contents(sys_get_temp_dir() . '/monolog_dedup.log') |
); |
$this->assertTrue($test->hasErrorRecords()); |
$this->assertTrue($test->hasCriticalRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
|
// clear test handler |
$test->clear(); |
$this->assertFalse($test->hasErrorRecords()); |
$this->assertFalse($test->hasCriticalRecords()); |
|
// log new records, duplicate log gets GC'd at the end of this flush call |
$handler->handle($record = $this->getRecord(Logger::ERROR)); |
$handler->handle($record2 = $this->getRecord(Logger::CRITICAL)); |
$handler->flush(); |
|
// log should now contain the new errors and the previous one that was recent enough |
$this->assertSame( |
$record3['datetime']->getTimestamp() . ":CRITICAL:test\n" . |
$record['datetime']->getTimestamp() . ":ERROR:test\n" . |
$record2['datetime']->getTimestamp() . ":CRITICAL:test\n", |
file_get_contents(sys_get_temp_dir() . '/monolog_dedup.log') |
); |
$this->assertTrue($test->hasErrorRecords()); |
$this->assertTrue($test->hasCriticalRecords()); |
$this->assertFalse($test->hasWarningRecords()); |
} |
|
public static function tearDownAfterClass() |
{ |
@unlink(sys_get_temp_dir().'/monolog_dedup.log'); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/ElasticSearchHandlerTest.php |
@@ -0,0 +1,239 @@ |
<?php |
|
/* |
* 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\ElasticaFormatter; |
use Monolog\Formatter\NormalizerFormatter; |
use Monolog\TestCase; |
use Monolog\Logger; |
use Elastica\Client; |
use Elastica\Request; |
use Elastica\Response; |
|
class ElasticSearchHandlerTest extends TestCase |
{ |
/** |
* @var Client mock |
*/ |
protected $client; |
|
/** |
* @var array Default handler options |
*/ |
protected $options = array( |
'index' => 'my_index', |
'type' => 'doc_type', |
); |
|
public function setUp() |
{ |
// Elastica lib required |
if (!class_exists("Elastica\Client")) { |
$this->markTestSkipped("ruflin/elastica not installed"); |
} |
|
// base mock Elastica Client object |
$this->client = $this->getMockBuilder('Elastica\Client') |
->setMethods(array('addDocuments')) |
->disableOriginalConstructor() |
->getMock(); |
} |
|
/** |
* @covers Monolog\Handler\ElasticSearchHandler::write |
* @covers Monolog\Handler\ElasticSearchHandler::handleBatch |
* @covers Monolog\Handler\ElasticSearchHandler::bulkSend |
* @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter |
*/ |
public function testHandle() |
{ |
// log message |
$msg = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('foo' => 7, 'bar', 'class' => new \stdClass), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
// format expected result |
$formatter = new ElasticaFormatter($this->options['index'], $this->options['type']); |
$expected = array($formatter->format($msg)); |
|
// setup ES client mock |
$this->client->expects($this->any()) |
->method('addDocuments') |
->with($expected); |
|
// perform tests |
$handler = new ElasticSearchHandler($this->client, $this->options); |
$handler->handle($msg); |
$handler->handleBatch(array($msg)); |
} |
|
/** |
* @covers Monolog\Handler\ElasticSearchHandler::setFormatter |
*/ |
public function testSetFormatter() |
{ |
$handler = new ElasticSearchHandler($this->client); |
$formatter = new ElasticaFormatter('index_new', 'type_new'); |
$handler->setFormatter($formatter); |
$this->assertInstanceOf('Monolog\Formatter\ElasticaFormatter', $handler->getFormatter()); |
$this->assertEquals('index_new', $handler->getFormatter()->getIndex()); |
$this->assertEquals('type_new', $handler->getFormatter()->getType()); |
} |
|
/** |
* @covers Monolog\Handler\ElasticSearchHandler::setFormatter |
* @expectedException InvalidArgumentException |
* @expectedExceptionMessage ElasticSearchHandler is only compatible with ElasticaFormatter |
*/ |
public function testSetFormatterInvalid() |
{ |
$handler = new ElasticSearchHandler($this->client); |
$formatter = new NormalizerFormatter(); |
$handler->setFormatter($formatter); |
} |
|
/** |
* @covers Monolog\Handler\ElasticSearchHandler::__construct |
* @covers Monolog\Handler\ElasticSearchHandler::getOptions |
*/ |
public function testOptions() |
{ |
$expected = array( |
'index' => $this->options['index'], |
'type' => $this->options['type'], |
'ignore_error' => false, |
); |
$handler = new ElasticSearchHandler($this->client, $this->options); |
$this->assertEquals($expected, $handler->getOptions()); |
} |
|
/** |
* @covers Monolog\Handler\ElasticSearchHandler::bulkSend |
* @dataProvider providerTestConnectionErrors |
*/ |
public function testConnectionErrors($ignore, $expectedError) |
{ |
$clientOpts = array('host' => '127.0.0.1', 'port' => 1); |
$client = new Client($clientOpts); |
$handlerOpts = array('ignore_error' => $ignore); |
$handler = new ElasticSearchHandler($client, $handlerOpts); |
|
if ($expectedError) { |
$this->setExpectedException($expectedError[0], $expectedError[1]); |
$handler->handle($this->getRecord()); |
} else { |
$this->assertFalse($handler->handle($this->getRecord())); |
} |
} |
|
/** |
* @return array |
*/ |
public function providerTestConnectionErrors() |
{ |
return array( |
array(false, array('RuntimeException', 'Error sending messages to Elasticsearch')), |
array(true, false), |
); |
} |
|
/** |
* Integration test using localhost Elastic Search server |
* |
* @covers Monolog\Handler\ElasticSearchHandler::__construct |
* @covers Monolog\Handler\ElasticSearchHandler::handleBatch |
* @covers Monolog\Handler\ElasticSearchHandler::bulkSend |
* @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter |
*/ |
public function testHandleIntegration() |
{ |
$msg = array( |
'level' => Logger::ERROR, |
'level_name' => 'ERROR', |
'channel' => 'meh', |
'context' => array('foo' => 7, 'bar', 'class' => new \stdClass), |
'datetime' => new \DateTime("@0"), |
'extra' => array(), |
'message' => 'log', |
); |
|
$expected = $msg; |
$expected['datetime'] = $msg['datetime']->format(\DateTime::ISO8601); |
$expected['context'] = array( |
'class' => '[object] (stdClass: {})', |
'foo' => 7, |
0 => 'bar', |
); |
|
$client = new Client(); |
$handler = new ElasticSearchHandler($client, $this->options); |
try { |
$handler->handleBatch(array($msg)); |
} catch (\RuntimeException $e) { |
$this->markTestSkipped("Cannot connect to Elastic Search server on localhost"); |
} |
|
// check document id from ES server response |
$documentId = $this->getCreatedDocId($client->getLastResponse()); |
$this->assertNotEmpty($documentId, 'No elastic document id received'); |
|
// retrieve document source from ES and validate |
$document = $this->getDocSourceFromElastic( |
$client, |
$this->options['index'], |
$this->options['type'], |
$documentId |
); |
$this->assertEquals($expected, $document); |
|
// remove test index from ES |
$client->request("/{$this->options['index']}", Request::DELETE); |
} |
|
/** |
* Return last created document id from ES response |
* @param Response $response Elastica Response object |
* @return string|null |
*/ |
protected function getCreatedDocId(Response $response) |
{ |
$data = $response->getData(); |
if (!empty($data['items'][0]['create']['_id'])) { |
return $data['items'][0]['create']['_id']; |
} |
} |
|
/** |
* Retrieve document by id from Elasticsearch |
* @param Client $client Elastica client |
* @param string $index |
* @param string $type |
* @param string $documentId |
* @return array |
*/ |
protected function getDocSourceFromElastic(Client $client, $index, $type, $documentId) |
{ |
$resp = $client->request("/{$index}/{$type}/{$documentId}", Request::GET); |
$data = $resp->getData(); |
if (!empty($data['_source'])) { |
return $data['_source']; |
} |
|
return array(); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/FilterHandlerTest.php |
@@ -0,0 +1,170 @@ |
<?php |
|
/* |
* 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\Logger; |
use Monolog\TestCase; |
|
class FilterHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\FilterHandler::isHandling |
*/ |
public function testIsHandling() |
{ |
$test = new TestHandler(); |
$handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::INFO))); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::NOTICE))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::WARNING))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::ERROR))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::CRITICAL))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::ALERT))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::EMERGENCY))); |
} |
|
/** |
* @covers Monolog\Handler\FilterHandler::handle |
* @covers Monolog\Handler\FilterHandler::setAcceptedLevels |
* @covers Monolog\Handler\FilterHandler::isHandling |
*/ |
public function testHandleProcessOnlyNeededLevels() |
{ |
$test = new TestHandler(); |
$handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE); |
|
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->assertFalse($test->hasDebugRecords()); |
|
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertTrue($test->hasInfoRecords()); |
$handler->handle($this->getRecord(Logger::NOTICE)); |
$this->assertTrue($test->hasNoticeRecords()); |
|
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertFalse($test->hasWarningRecords()); |
$handler->handle($this->getRecord(Logger::ERROR)); |
$this->assertFalse($test->hasErrorRecords()); |
$handler->handle($this->getRecord(Logger::CRITICAL)); |
$this->assertFalse($test->hasCriticalRecords()); |
$handler->handle($this->getRecord(Logger::ALERT)); |
$this->assertFalse($test->hasAlertRecords()); |
$handler->handle($this->getRecord(Logger::EMERGENCY)); |
$this->assertFalse($test->hasEmergencyRecords()); |
|
$test = new TestHandler(); |
$handler = new FilterHandler($test, array(Logger::INFO, Logger::ERROR)); |
|
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->assertFalse($test->hasDebugRecords()); |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertTrue($test->hasInfoRecords()); |
$handler->handle($this->getRecord(Logger::NOTICE)); |
$this->assertFalse($test->hasNoticeRecords()); |
$handler->handle($this->getRecord(Logger::ERROR)); |
$this->assertTrue($test->hasErrorRecords()); |
$handler->handle($this->getRecord(Logger::CRITICAL)); |
$this->assertFalse($test->hasCriticalRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FilterHandler::setAcceptedLevels |
* @covers Monolog\Handler\FilterHandler::getAcceptedLevels |
*/ |
public function testAcceptedLevelApi() |
{ |
$test = new TestHandler(); |
$handler = new FilterHandler($test); |
|
$levels = array(Logger::INFO, Logger::ERROR); |
$handler->setAcceptedLevels($levels); |
$this->assertSame($levels, $handler->getAcceptedLevels()); |
|
$handler->setAcceptedLevels(array('info', 'error')); |
$this->assertSame($levels, $handler->getAcceptedLevels()); |
|
$levels = array(Logger::CRITICAL, Logger::ALERT, Logger::EMERGENCY); |
$handler->setAcceptedLevels(Logger::CRITICAL, Logger::EMERGENCY); |
$this->assertSame($levels, $handler->getAcceptedLevels()); |
|
$handler->setAcceptedLevels('critical', 'emergency'); |
$this->assertSame($levels, $handler->getAcceptedLevels()); |
} |
|
/** |
* @covers Monolog\Handler\FilterHandler::handle |
*/ |
public function testHandleUsesProcessors() |
{ |
$test = new TestHandler(); |
$handler = new FilterHandler($test, Logger::DEBUG, Logger::EMERGENCY); |
$handler->pushProcessor( |
function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
} |
); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasWarningRecords()); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
} |
|
/** |
* @covers Monolog\Handler\FilterHandler::handle |
*/ |
public function testHandleRespectsBubble() |
{ |
$test = new TestHandler(); |
|
$handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE, false); |
$this->assertTrue($handler->handle($this->getRecord(Logger::INFO))); |
$this->assertFalse($handler->handle($this->getRecord(Logger::WARNING))); |
|
$handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE, true); |
$this->assertFalse($handler->handle($this->getRecord(Logger::INFO))); |
$this->assertFalse($handler->handle($this->getRecord(Logger::WARNING))); |
} |
|
/** |
* @covers Monolog\Handler\FilterHandler::handle |
*/ |
public function testHandleWithCallback() |
{ |
$test = new TestHandler(); |
$handler = new FilterHandler( |
function ($record, $handler) use ($test) { |
return $test; |
}, Logger::INFO, Logger::NOTICE, false |
); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FilterHandler::handle |
* @expectedException \RuntimeException |
*/ |
public function testHandleWithBadCallbackThrowsException() |
{ |
$handler = new FilterHandler( |
function ($record, $handler) { |
return 'foo'; |
} |
); |
$handler->handle($this->getRecord(Logger::WARNING)); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php |
@@ -0,0 +1,279 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; |
use Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy; |
use Psr\Log\LogLevel; |
|
class FingersCrossedHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\FingersCrossedHandler::__construct |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testHandleBuffers() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertFalse($test->hasInfoRecords()); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->close(); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 3); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testHandleStopsBufferingAfterTrigger() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->close(); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasDebugRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
* @covers Monolog\Handler\FingersCrossedHandler::reset |
*/ |
public function testHandleRestartBufferingAfterReset() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->reset(); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->close(); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertFalse($test->hasInfoRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testHandleRestartBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->close(); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertFalse($test->hasInfoRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testHandleBufferLimit() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, Logger::WARNING, 2); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasWarningRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertFalse($test->hasDebugRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testHandleWithCallback() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler(function ($record, $handler) use ($test) { |
return $test; |
}); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertFalse($test->hasInfoRecords()); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 3); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
* @expectedException RuntimeException |
*/ |
public function testHandleWithBadCallbackThrowsException() |
{ |
$handler = new FingersCrossedHandler(function ($record, $handler) { |
return 'foo'; |
}); |
$handler->handle($this->getRecord(Logger::WARNING)); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::isHandling |
*/ |
public function testIsHandlingAlways() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, Logger::ERROR); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::DEBUG))); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::__construct |
* @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::__construct |
* @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::isHandlerActivated |
*/ |
public function testErrorLevelActivationStrategy() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING)); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->assertFalse($test->hasDebugRecords()); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::__construct |
* @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::__construct |
* @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::isHandlerActivated |
*/ |
public function testErrorLevelActivationStrategyWithPsrLevel() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy('warning')); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->assertFalse($test->hasDebugRecords()); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::__construct |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testOverrideActivationStrategy() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy('warning')); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$this->assertFalse($test->hasDebugRecords()); |
$handler->activate(); |
$this->assertTrue($test->hasDebugRecords()); |
$handler->handle($this->getRecord(Logger::INFO)); |
$this->assertTrue($test->hasInfoRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::__construct |
* @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::isHandlerActivated |
*/ |
public function testChannelLevelActivationStrategy() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ChannelLevelActivationStrategy(Logger::ERROR, array('othertest' => Logger::DEBUG))); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertFalse($test->hasWarningRecords()); |
$record = $this->getRecord(Logger::DEBUG); |
$record['channel'] = 'othertest'; |
$handler->handle($record); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::__construct |
* @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::isHandlerActivated |
*/ |
public function testChannelLevelActivationStrategyWithPsrLevels() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ChannelLevelActivationStrategy('error', array('othertest' => 'debug'))); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertFalse($test->hasWarningRecords()); |
$record = $this->getRecord(Logger::DEBUG); |
$record['channel'] = 'othertest'; |
$handler->handle($record); |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasWarningRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::handle |
* @covers Monolog\Handler\FingersCrossedHandler::activate |
*/ |
public function testHandleUsesProcessors() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, Logger::INFO); |
$handler->pushProcessor(function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
}); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasWarningRecords()); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::close |
*/ |
public function testPassthruOnClose() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING), 0, true, true, Logger::INFO); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->close(); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
} |
|
/** |
* @covers Monolog\Handler\FingersCrossedHandler::close |
*/ |
public function testPsrLevelPassthruOnClose() |
{ |
$test = new TestHandler(); |
$handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING), 0, true, true, LogLevel::INFO); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
$handler->close(); |
$this->assertFalse($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.php |
@@ -0,0 +1,112 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
class GroupHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\GroupHandler::__construct |
* @expectedException InvalidArgumentException |
*/ |
public function testConstructorOnlyTakesHandler() |
{ |
new GroupHandler(array(new TestHandler(), "foo")); |
} |
|
/** |
* @covers Monolog\Handler\GroupHandler::__construct |
* @covers Monolog\Handler\GroupHandler::handle |
*/ |
public function testHandle() |
{ |
$testHandlers = array(new TestHandler(), new TestHandler()); |
$handler = new GroupHandler($testHandlers); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
foreach ($testHandlers as $test) { |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 2); |
} |
} |
|
/** |
* @covers Monolog\Handler\GroupHandler::handleBatch |
*/ |
public function testHandleBatch() |
{ |
$testHandlers = array(new TestHandler(), new TestHandler()); |
$handler = new GroupHandler($testHandlers); |
$handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); |
foreach ($testHandlers as $test) { |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 2); |
} |
} |
|
/** |
* @covers Monolog\Handler\GroupHandler::isHandling |
*/ |
public function testIsHandling() |
{ |
$testHandlers = array(new TestHandler(Logger::ERROR), new TestHandler(Logger::WARNING)); |
$handler = new GroupHandler($testHandlers); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::ERROR))); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::WARNING))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); |
} |
|
/** |
* @covers Monolog\Handler\GroupHandler::handle |
*/ |
public function testHandleUsesProcessors() |
{ |
$test = new TestHandler(); |
$handler = new GroupHandler(array($test)); |
$handler->pushProcessor(function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
}); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasWarningRecords()); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
} |
|
/** |
* @covers Monolog\Handler\GroupHandler::handle |
*/ |
public function testHandleBatchUsesProcessors() |
{ |
$testHandlers = array(new TestHandler(), new TestHandler()); |
$handler = new GroupHandler($testHandlers); |
$handler->pushProcessor(function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
}); |
$handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); |
foreach ($testHandlers as $test) { |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 2); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
$this->assertTrue($records[1]['extra']['foo']); |
} |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/HipChatHandlerTest.php |
@@ -0,0 +1,279 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
/** |
* @author Rafael Dohms <rafael@doh.ms> |
* @see https://www.hipchat.com/docs/api |
*/ |
class HipChatHandlerTest extends TestCase |
{ |
private $res; |
/** @var HipChatHandler */ |
private $handler; |
|
public function testWriteHeader() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/v1\/rooms\/message\?format=json&auth_token=.* HTTP\/1.1\\r\\nHost: api.hipchat.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
|
return $content; |
} |
|
public function testWriteCustomHostHeader() |
{ |
$this->createHandler('myToken', 'room1', 'Monolog', true, 'hipchat.foo.bar'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/v1\/rooms\/message\?format=json&auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
|
return $content; |
} |
|
public function testWriteV2() |
{ |
$this->createHandler('myToken', 'room1', 'Monolog', false, 'hipchat.foo.bar', 'v2'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
|
return $content; |
} |
|
public function testWriteV2Notify() |
{ |
$this->createHandler('myToken', 'room1', 'Monolog', true, 'hipchat.foo.bar', 'v2'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
|
return $content; |
} |
|
public function testRoomSpaces() |
{ |
$this->createHandler('myToken', 'room name', 'Monolog', false, 'hipchat.foo.bar', 'v2'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/v2\/room\/room%20name\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
|
return $content; |
} |
|
/** |
* @depends testWriteHeader |
*/ |
public function testWriteContent($content) |
{ |
$this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); |
} |
|
public function testWriteContentV1WithoutName() |
{ |
$this->createHandler('myToken', 'room1', null, false, 'hipchat.foo.bar', 'v1'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=$/', $content); |
|
return $content; |
} |
|
/** |
* @depends testWriteCustomHostHeader |
*/ |
public function testWriteContentNotify($content) |
{ |
$this->assertRegexp('/notify=1&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); |
} |
|
/** |
* @depends testWriteV2 |
*/ |
public function testWriteContentV2($content) |
{ |
$this->assertRegexp('/notify=false&message=test1&message_format=text&color=red&from=Monolog$/', $content); |
} |
|
/** |
* @depends testWriteV2Notify |
*/ |
public function testWriteContentV2Notify($content) |
{ |
$this->assertRegexp('/notify=true&message=test1&message_format=text&color=red&from=Monolog$/', $content); |
} |
|
public function testWriteContentV2WithoutName() |
{ |
$this->createHandler('myToken', 'room1', null, false, 'hipchat.foo.bar', 'v2'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/notify=false&message=test1&message_format=text&color=red$/', $content); |
|
return $content; |
} |
|
public function testWriteWithComplexMessage() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'Backup of database "example" finished in 16 minutes.')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/message=Backup\+of\+database\+%22example%22\+finished\+in\+16\+minutes\./', $content); |
} |
|
public function testWriteTruncatesLongMessage() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, str_repeat('abcde', 2000))); |
fseek($this->res, 0); |
$content = fread($this->res, 12000); |
|
$this->assertRegexp('/message='.str_repeat('abcde', 1900).'\+%5Btruncated%5D/', $content); |
} |
|
/** |
* @dataProvider provideLevelColors |
*/ |
public function testWriteWithErrorLevelsAndColors($level, $expectedColor) |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord($level, 'Backup of database "example" finished in 16 minutes.')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/color='.$expectedColor.'/', $content); |
} |
|
public function provideLevelColors() |
{ |
return array( |
array(Logger::DEBUG, 'gray'), |
array(Logger::INFO, 'green'), |
array(Logger::WARNING, 'yellow'), |
array(Logger::ERROR, 'red'), |
array(Logger::CRITICAL, 'red'), |
array(Logger::ALERT, 'red'), |
array(Logger::EMERGENCY,'red'), |
array(Logger::NOTICE, 'green'), |
); |
} |
|
/** |
* @dataProvider provideBatchRecords |
*/ |
public function testHandleBatch($records, $expectedColor) |
{ |
$this->createHandler(); |
|
$this->handler->handleBatch($records); |
|
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/color='.$expectedColor.'/', $content); |
} |
|
public function provideBatchRecords() |
{ |
return array( |
array( |
array( |
array('level' => Logger::WARNING, 'message' => 'Oh bugger!', 'level_name' => 'warning', 'datetime' => new \DateTime()), |
array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()), |
array('level' => Logger::CRITICAL, 'message' => 'Everything is broken!', 'level_name' => 'critical', 'datetime' => new \DateTime()), |
), |
'red', |
), |
array( |
array( |
array('level' => Logger::WARNING, 'message' => 'Oh bugger!', 'level_name' => 'warning', 'datetime' => new \DateTime()), |
array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()), |
), |
'yellow', |
), |
array( |
array( |
array('level' => Logger::DEBUG, 'message' => 'Just debugging.', 'level_name' => 'debug', 'datetime' => new \DateTime()), |
array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()), |
), |
'green', |
), |
array( |
array( |
array('level' => Logger::DEBUG, 'message' => 'Just debugging.', 'level_name' => 'debug', 'datetime' => new \DateTime()), |
), |
'gray', |
), |
); |
} |
|
private function createHandler($token = 'myToken', $room = 'room1', $name = 'Monolog', $notify = false, $host = 'api.hipchat.com', $version = 'v1') |
{ |
$constructorArgs = array($token, $room, $name, $notify, Logger::DEBUG, true, true, 'text', $host, $version); |
$this->res = fopen('php://memory', 'a'); |
$this->handler = $this->getMock( |
'\Monolog\Handler\HipChatHandler', |
array('fsockopen', 'streamSetTimeout', 'closeSocket'), |
$constructorArgs |
); |
|
$reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); |
$reflectionProperty->setAccessible(true); |
$reflectionProperty->setValue($this->handler, 'localhost:1234'); |
|
$this->handler->expects($this->any()) |
->method('fsockopen') |
->will($this->returnValue($this->res)); |
$this->handler->expects($this->any()) |
->method('streamSetTimeout') |
->will($this->returnValue(true)); |
$this->handler->expects($this->any()) |
->method('closeSocket') |
->will($this->returnValue(true)); |
|
$this->handler->setFormatter($this->getIdentityFormatter()); |
} |
|
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testCreateWithTooLongName() |
{ |
$hipChatHandler = new HipChatHandler('token', 'room', 'SixteenCharsHere'); |
} |
|
public function testCreateWithTooLongNameV2() |
{ |
// creating a handler with too long of a name but using the v2 api doesn't matter. |
$hipChatHandler = new HipChatHandler('token', 'room', 'SixteenCharsHere', false, Logger::CRITICAL, true, true, 'test', 'api.hipchat.com', 'v2'); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.php |
@@ -0,0 +1,111 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use InvalidArgumentException; |
|
function mail($to, $subject, $message, $additional_headers = null, $additional_parameters = null) |
{ |
$GLOBALS['mail'][] = func_get_args(); |
} |
|
class NativeMailerHandlerTest extends TestCase |
{ |
protected function setUp() |
{ |
$GLOBALS['mail'] = array(); |
} |
|
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testConstructorHeaderInjection() |
{ |
$mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', "receiver@example.org\r\nFrom: faked@attacker.org"); |
} |
|
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testSetterHeaderInjection() |
{ |
$mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); |
$mailer->addHeader("Content-Type: text/html\r\nFrom: faked@attacker.org"); |
} |
|
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testSetterArrayHeaderInjection() |
{ |
$mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); |
$mailer->addHeader(array("Content-Type: text/html\r\nFrom: faked@attacker.org")); |
} |
|
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testSetterContentTypeInjection() |
{ |
$mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); |
$mailer->setContentType("text/html\r\nFrom: faked@attacker.org"); |
} |
|
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testSetterEncodingInjection() |
{ |
$mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); |
$mailer->setEncoding("utf-8\r\nFrom: faked@attacker.org"); |
} |
|
public function testSend() |
{ |
$to = 'spammer@example.org'; |
$subject = 'dear victim'; |
$from = 'receiver@example.org'; |
|
$mailer = new NativeMailerHandler($to, $subject, $from); |
$mailer->handleBatch(array()); |
|
// batch is empty, nothing sent |
$this->assertEmpty($GLOBALS['mail']); |
|
// non-empty batch |
$mailer->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz")); |
$this->assertNotEmpty($GLOBALS['mail']); |
$this->assertInternalType('array', $GLOBALS['mail']); |
$this->assertArrayHasKey('0', $GLOBALS['mail']); |
$params = $GLOBALS['mail'][0]; |
$this->assertCount(5, $params); |
$this->assertSame($to, $params[0]); |
$this->assertSame($subject, $params[1]); |
$this->assertStringEndsWith(" test.ERROR: Foo Bar Baz [] []\n", $params[2]); |
$this->assertSame("From: $from\r\nContent-type: text/plain; charset=utf-8\r\n", $params[3]); |
$this->assertSame('', $params[4]); |
} |
|
public function testMessageSubjectFormatting() |
{ |
$mailer = new NativeMailerHandler('to@example.org', 'Alert: %level_name% %message%', 'from@example.org'); |
$mailer->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz")); |
$this->assertNotEmpty($GLOBALS['mail']); |
$this->assertInternalType('array', $GLOBALS['mail']); |
$this->assertArrayHasKey('0', $GLOBALS['mail']); |
$params = $GLOBALS['mail'][0]; |
$this->assertCount(5, $params); |
$this->assertSame('Alert: ERROR Foo Bar Baz', $params[1]); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/NewRelicHandlerTest.php |
@@ -0,0 +1,200 @@ |
<?php |
|
/* |
* 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\LineFormatter; |
use Monolog\TestCase; |
use Monolog\Logger; |
|
class NewRelicHandlerTest extends TestCase |
{ |
public static $appname; |
public static $customParameters; |
public static $transactionName; |
|
public function setUp() |
{ |
self::$appname = null; |
self::$customParameters = array(); |
self::$transactionName = null; |
} |
|
/** |
* @expectedException Monolog\Handler\MissingExtensionException |
*/ |
public function testThehandlerThrowsAnExceptionIfTheNRExtensionIsNotLoaded() |
{ |
$handler = new StubNewRelicHandlerWithoutExtension(); |
$handler->handle($this->getRecord(Logger::ERROR)); |
} |
|
public function testThehandlerCanHandleTheRecord() |
{ |
$handler = new StubNewRelicHandler(); |
$handler->handle($this->getRecord(Logger::ERROR)); |
} |
|
public function testThehandlerCanAddContextParamsToTheNewRelicTrace() |
{ |
$handler = new StubNewRelicHandler(); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message', array('a' => 'b'))); |
$this->assertEquals(array('context_a' => 'b'), self::$customParameters); |
} |
|
public function testThehandlerCanAddExplodedContextParamsToTheNewRelicTrace() |
{ |
$handler = new StubNewRelicHandler(Logger::ERROR, true, self::$appname, true); |
$handler->handle($this->getRecord( |
Logger::ERROR, |
'log message', |
array('a' => array('key1' => 'value1', 'key2' => 'value2')) |
)); |
$this->assertEquals( |
array('context_a_key1' => 'value1', 'context_a_key2' => 'value2'), |
self::$customParameters |
); |
} |
|
public function testThehandlerCanAddExtraParamsToTheNewRelicTrace() |
{ |
$record = $this->getRecord(Logger::ERROR, 'log message'); |
$record['extra'] = array('c' => 'd'); |
|
$handler = new StubNewRelicHandler(); |
$handler->handle($record); |
|
$this->assertEquals(array('extra_c' => 'd'), self::$customParameters); |
} |
|
public function testThehandlerCanAddExplodedExtraParamsToTheNewRelicTrace() |
{ |
$record = $this->getRecord(Logger::ERROR, 'log message'); |
$record['extra'] = array('c' => array('key1' => 'value1', 'key2' => 'value2')); |
|
$handler = new StubNewRelicHandler(Logger::ERROR, true, self::$appname, true); |
$handler->handle($record); |
|
$this->assertEquals( |
array('extra_c_key1' => 'value1', 'extra_c_key2' => 'value2'), |
self::$customParameters |
); |
} |
|
public function testThehandlerCanAddExtraContextAndParamsToTheNewRelicTrace() |
{ |
$record = $this->getRecord(Logger::ERROR, 'log message', array('a' => 'b')); |
$record['extra'] = array('c' => 'd'); |
|
$handler = new StubNewRelicHandler(); |
$handler->handle($record); |
|
$expected = array( |
'context_a' => 'b', |
'extra_c' => 'd', |
); |
|
$this->assertEquals($expected, self::$customParameters); |
} |
|
public function testThehandlerCanHandleTheRecordsFormattedUsingTheLineFormatter() |
{ |
$handler = new StubNewRelicHandler(); |
$handler->setFormatter(new LineFormatter()); |
$handler->handle($this->getRecord(Logger::ERROR)); |
} |
|
public function testTheAppNameIsNullByDefault() |
{ |
$handler = new StubNewRelicHandler(); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message')); |
|
$this->assertEquals(null, self::$appname); |
} |
|
public function testTheAppNameCanBeInjectedFromtheConstructor() |
{ |
$handler = new StubNewRelicHandler(Logger::DEBUG, false, 'myAppName'); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message')); |
|
$this->assertEquals('myAppName', self::$appname); |
} |
|
public function testTheAppNameCanBeOverriddenFromEachLog() |
{ |
$handler = new StubNewRelicHandler(Logger::DEBUG, false, 'myAppName'); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message', array('appname' => 'logAppName'))); |
|
$this->assertEquals('logAppName', self::$appname); |
} |
|
public function testTheTransactionNameIsNullByDefault() |
{ |
$handler = new StubNewRelicHandler(); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message')); |
|
$this->assertEquals(null, self::$transactionName); |
} |
|
public function testTheTransactionNameCanBeInjectedFromTheConstructor() |
{ |
$handler = new StubNewRelicHandler(Logger::DEBUG, false, null, false, 'myTransaction'); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message')); |
|
$this->assertEquals('myTransaction', self::$transactionName); |
} |
|
public function testTheTransactionNameCanBeOverriddenFromEachLog() |
{ |
$handler = new StubNewRelicHandler(Logger::DEBUG, false, null, false, 'myTransaction'); |
$handler->handle($this->getRecord(Logger::ERROR, 'log message', array('transaction_name' => 'logTransactName'))); |
|
$this->assertEquals('logTransactName', self::$transactionName); |
} |
} |
|
class StubNewRelicHandlerWithoutExtension extends NewRelicHandler |
{ |
protected function isNewRelicEnabled() |
{ |
return false; |
} |
} |
|
class StubNewRelicHandler extends NewRelicHandler |
{ |
protected function isNewRelicEnabled() |
{ |
return true; |
} |
} |
|
function newrelic_notice_error() |
{ |
return true; |
} |
|
function newrelic_set_appname($appname) |
{ |
return NewRelicHandlerTest::$appname = $appname; |
} |
|
function newrelic_name_transaction($transactionName) |
{ |
return NewRelicHandlerTest::$transactionName = $transactionName; |
} |
|
function newrelic_add_custom_parameter($key, $value) |
{ |
NewRelicHandlerTest::$customParameters[$key] = $value; |
|
return true; |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/PHPConsoleHandlerTest.php |
@@ -0,0 +1,273 @@ |
<?php |
|
/* |
* 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 Exception; |
use Monolog\ErrorHandler; |
use Monolog\Logger; |
use Monolog\TestCase; |
use PhpConsole\Connector; |
use PhpConsole\Dispatcher\Debug as DebugDispatcher; |
use PhpConsole\Dispatcher\Errors as ErrorDispatcher; |
use PhpConsole\Handler; |
use PHPUnit_Framework_MockObject_MockObject; |
|
/** |
* @covers Monolog\Handler\PHPConsoleHandler |
* @author Sergey Barbushin https://www.linkedin.com/in/barbushin |
*/ |
class PHPConsoleHandlerTest extends TestCase |
{ |
/** @var Connector|PHPUnit_Framework_MockObject_MockObject */ |
protected $connector; |
/** @var DebugDispatcher|PHPUnit_Framework_MockObject_MockObject */ |
protected $debugDispatcher; |
/** @var ErrorDispatcher|PHPUnit_Framework_MockObject_MockObject */ |
protected $errorDispatcher; |
|
protected function setUp() |
{ |
if (!class_exists('PhpConsole\Connector')) { |
$this->markTestSkipped('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); |
} |
$this->connector = $this->initConnectorMock(); |
|
$this->debugDispatcher = $this->initDebugDispatcherMock($this->connector); |
$this->connector->setDebugDispatcher($this->debugDispatcher); |
|
$this->errorDispatcher = $this->initErrorDispatcherMock($this->connector); |
$this->connector->setErrorsDispatcher($this->errorDispatcher); |
} |
|
protected function initDebugDispatcherMock(Connector $connector) |
{ |
return $this->getMockBuilder('PhpConsole\Dispatcher\Debug') |
->disableOriginalConstructor() |
->setMethods(array('dispatchDebug')) |
->setConstructorArgs(array($connector, $connector->getDumper())) |
->getMock(); |
} |
|
protected function initErrorDispatcherMock(Connector $connector) |
{ |
return $this->getMockBuilder('PhpConsole\Dispatcher\Errors') |
->disableOriginalConstructor() |
->setMethods(array('dispatchError', 'dispatchException')) |
->setConstructorArgs(array($connector, $connector->getDumper())) |
->getMock(); |
} |
|
protected function initConnectorMock() |
{ |
$connector = $this->getMockBuilder('PhpConsole\Connector') |
->disableOriginalConstructor() |
->setMethods(array( |
'sendMessage', |
'onShutDown', |
'isActiveClient', |
'setSourcesBasePath', |
'setServerEncoding', |
'setPassword', |
'enableSslOnlyMode', |
'setAllowedIpMasks', |
'setHeadersLimit', |
'startEvalRequestsListener', |
)) |
->getMock(); |
|
$connector->expects($this->any()) |
->method('isActiveClient') |
->will($this->returnValue(true)); |
|
return $connector; |
} |
|
protected function getHandlerDefaultOption($name) |
{ |
$handler = new PHPConsoleHandler(array(), $this->connector); |
$options = $handler->getOptions(); |
|
return $options[$name]; |
} |
|
protected function initLogger($handlerOptions = array(), $level = Logger::DEBUG) |
{ |
return new Logger('test', array( |
new PHPConsoleHandler($handlerOptions, $this->connector, $level), |
)); |
} |
|
public function testInitWithDefaultConnector() |
{ |
$handler = new PHPConsoleHandler(); |
$this->assertEquals(spl_object_hash(Connector::getInstance()), spl_object_hash($handler->getConnector())); |
} |
|
public function testInitWithCustomConnector() |
{ |
$handler = new PHPConsoleHandler(array(), $this->connector); |
$this->assertEquals(spl_object_hash($this->connector), spl_object_hash($handler->getConnector())); |
} |
|
public function testDebug() |
{ |
$this->debugDispatcher->expects($this->once())->method('dispatchDebug')->with($this->equalTo('test')); |
$this->initLogger()->addDebug('test'); |
} |
|
public function testDebugContextInMessage() |
{ |
$message = 'test'; |
$tag = 'tag'; |
$context = array($tag, 'custom' => mt_rand()); |
$expectedMessage = $message . ' ' . json_encode(array_slice($context, 1)); |
$this->debugDispatcher->expects($this->once())->method('dispatchDebug')->with( |
$this->equalTo($expectedMessage), |
$this->equalTo($tag) |
); |
$this->initLogger()->addDebug($message, $context); |
} |
|
public function testDebugTags($tagsContextKeys = null) |
{ |
$expectedTags = mt_rand(); |
$logger = $this->initLogger($tagsContextKeys ? array('debugTagsKeysInContext' => $tagsContextKeys) : array()); |
if (!$tagsContextKeys) { |
$tagsContextKeys = $this->getHandlerDefaultOption('debugTagsKeysInContext'); |
} |
foreach ($tagsContextKeys as $key) { |
$debugDispatcher = $this->initDebugDispatcherMock($this->connector); |
$debugDispatcher->expects($this->once())->method('dispatchDebug')->with( |
$this->anything(), |
$this->equalTo($expectedTags) |
); |
$this->connector->setDebugDispatcher($debugDispatcher); |
$logger->addDebug('test', array($key => $expectedTags)); |
} |
} |
|
public function testError($classesPartialsTraceIgnore = null) |
{ |
$code = E_USER_NOTICE; |
$message = 'message'; |
$file = __FILE__; |
$line = __LINE__; |
$this->errorDispatcher->expects($this->once())->method('dispatchError')->with( |
$this->equalTo($code), |
$this->equalTo($message), |
$this->equalTo($file), |
$this->equalTo($line), |
$classesPartialsTraceIgnore ?: $this->equalTo($this->getHandlerDefaultOption('classesPartialsTraceIgnore')) |
); |
$errorHandler = ErrorHandler::register($this->initLogger($classesPartialsTraceIgnore ? array('classesPartialsTraceIgnore' => $classesPartialsTraceIgnore) : array()), false); |
$errorHandler->registerErrorHandler(array(), false, E_USER_WARNING); |
$errorHandler->handleError($code, $message, $file, $line); |
} |
|
public function testException() |
{ |
$e = new Exception(); |
$this->errorDispatcher->expects($this->once())->method('dispatchException')->with( |
$this->equalTo($e) |
); |
$handler = $this->initLogger(); |
$handler->log( |
\Psr\Log\LogLevel::ERROR, |
sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), |
array('exception' => $e) |
); |
} |
|
/** |
* @expectedException Exception |
*/ |
public function testWrongOptionsThrowsException() |
{ |
new PHPConsoleHandler(array('xxx' => 1)); |
} |
|
public function testOptionEnabled() |
{ |
$this->debugDispatcher->expects($this->never())->method('dispatchDebug'); |
$this->initLogger(array('enabled' => false))->addDebug('test'); |
} |
|
public function testOptionClassesPartialsTraceIgnore() |
{ |
$this->testError(array('Class', 'Namespace\\')); |
} |
|
public function testOptionDebugTagsKeysInContext() |
{ |
$this->testDebugTags(array('key1', 'key2')); |
} |
|
public function testOptionUseOwnErrorsAndExceptionsHandler() |
{ |
$this->initLogger(array('useOwnErrorsHandler' => true, 'useOwnExceptionsHandler' => true)); |
$this->assertEquals(array(Handler::getInstance(), 'handleError'), set_error_handler(function () { |
})); |
$this->assertEquals(array(Handler::getInstance(), 'handleException'), set_exception_handler(function () { |
})); |
} |
|
public static function provideConnectorMethodsOptionsSets() |
{ |
return array( |
array('sourcesBasePath', 'setSourcesBasePath', __DIR__), |
array('serverEncoding', 'setServerEncoding', 'cp1251'), |
array('password', 'setPassword', '******'), |
array('enableSslOnlyMode', 'enableSslOnlyMode', true, false), |
array('ipMasks', 'setAllowedIpMasks', array('127.0.0.*')), |
array('headersLimit', 'setHeadersLimit', 2500), |
array('enableEvalListener', 'startEvalRequestsListener', true, false), |
); |
} |
|
/** |
* @dataProvider provideConnectorMethodsOptionsSets |
*/ |
public function testOptionCallsConnectorMethod($option, $method, $value, $isArgument = true) |
{ |
$expectCall = $this->connector->expects($this->once())->method($method); |
if ($isArgument) { |
$expectCall->with($value); |
} |
new PHPConsoleHandler(array($option => $value), $this->connector); |
} |
|
public function testOptionDetectDumpTraceAndSource() |
{ |
new PHPConsoleHandler(array('detectDumpTraceAndSource' => true), $this->connector); |
$this->assertTrue($this->connector->getDebugDispatcher()->detectTraceAndSource); |
} |
|
public static function provideDumperOptionsValues() |
{ |
return array( |
array('dumperLevelLimit', 'levelLimit', 1001), |
array('dumperItemsCountLimit', 'itemsCountLimit', 1002), |
array('dumperItemSizeLimit', 'itemSizeLimit', 1003), |
array('dumperDumpSizeLimit', 'dumpSizeLimit', 1004), |
array('dumperDetectCallbacks', 'detectCallbacks', true), |
); |
} |
|
/** |
* @dataProvider provideDumperOptionsValues |
*/ |
public function testDumperOptions($option, $dumperProperty, $value) |
{ |
new PHPConsoleHandler(array($option => $value), $this->connector); |
$this->assertEquals($value, $this->connector->getDumper()->$dumperProperty); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.php |
@@ -0,0 +1,141 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
/** |
* Almost all examples (expected header, titles, messages) taken from |
* https://www.pushover.net/api |
* @author Sebastian Göttschkes <sebastian.goettschkes@googlemail.com> |
* @see https://www.pushover.net/api |
*/ |
class PushoverHandlerTest extends TestCase |
{ |
private $res; |
private $handler; |
|
public function testWriteHeader() |
{ |
$this->createHandler(); |
$this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/1\/messages.json HTTP\/1.1\\r\\nHost: api.pushover.net\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
|
return $content; |
} |
|
/** |
* @depends testWriteHeader |
*/ |
public function testWriteContent($content) |
{ |
$this->assertRegexp('/token=myToken&user=myUser&message=test1&title=Monolog×tamp=\d{10}$/', $content); |
} |
|
public function testWriteWithComplexTitle() |
{ |
$this->createHandler('myToken', 'myUser', 'Backup finished - SQL1'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/title=Backup\+finished\+-\+SQL1/', $content); |
} |
|
public function testWriteWithComplexMessage() |
{ |
$this->createHandler(); |
$this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'Backup of database "example" finished in 16 minutes.')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/message=Backup\+of\+database\+%22example%22\+finished\+in\+16\+minutes\./', $content); |
} |
|
public function testWriteWithTooLongMessage() |
{ |
$message = str_pad('test', 520, 'a'); |
$this->createHandler(); |
$this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications |
$this->handler->handle($this->getRecord(Logger::CRITICAL, $message)); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$expectedMessage = substr($message, 0, 505); |
|
$this->assertRegexp('/message=' . $expectedMessage . '&title/', $content); |
} |
|
public function testWriteWithHighPriority() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/token=myToken&user=myUser&message=test1&title=Monolog×tamp=\d{10}&priority=1$/', $content); |
} |
|
public function testWriteWithEmergencyPriority() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::EMERGENCY, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/token=myToken&user=myUser&message=test1&title=Monolog×tamp=\d{10}&priority=2&retry=30&expire=25200$/', $content); |
} |
|
public function testWriteToMultipleUsers() |
{ |
$this->createHandler('myToken', array('userA', 'userB')); |
$this->handler->handle($this->getRecord(Logger::EMERGENCY, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/token=myToken&user=userA&message=test1&title=Monolog×tamp=\d{10}&priority=2&retry=30&expire=25200POST/', $content); |
$this->assertRegexp('/token=myToken&user=userB&message=test1&title=Monolog×tamp=\d{10}&priority=2&retry=30&expire=25200$/', $content); |
} |
|
private function createHandler($token = 'myToken', $user = 'myUser', $title = 'Monolog') |
{ |
$constructorArgs = array($token, $user, $title); |
$this->res = fopen('php://memory', 'a'); |
$this->handler = $this->getMock( |
'\Monolog\Handler\PushoverHandler', |
array('fsockopen', 'streamSetTimeout', 'closeSocket'), |
$constructorArgs |
); |
|
$reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); |
$reflectionProperty->setAccessible(true); |
$reflectionProperty->setValue($this->handler, 'localhost:1234'); |
|
$this->handler->expects($this->any()) |
->method('fsockopen') |
->will($this->returnValue($this->res)); |
$this->handler->expects($this->any()) |
->method('streamSetTimeout') |
->will($this->returnValue(true)); |
$this->handler->expects($this->any()) |
->method('closeSocket') |
->will($this->returnValue(true)); |
|
$this->handler->setFormatter($this->getIdentityFormatter()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.php |
@@ -0,0 +1,255 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
|
class RavenHandlerTest extends TestCase |
{ |
public function setUp() |
{ |
if (!class_exists('Raven_Client')) { |
$this->markTestSkipped('raven/raven not installed'); |
} |
|
require_once __DIR__ . '/MockRavenClient.php'; |
} |
|
/** |
* @covers Monolog\Handler\RavenHandler::__construct |
*/ |
public function testConstruct() |
{ |
$handler = new RavenHandler($this->getRavenClient()); |
$this->assertInstanceOf('Monolog\Handler\RavenHandler', $handler); |
} |
|
protected function getHandler($ravenClient) |
{ |
$handler = new RavenHandler($ravenClient); |
|
return $handler; |
} |
|
protected function getRavenClient() |
{ |
$dsn = 'http://43f6017361224d098402974103bfc53d:a6a0538fc2934ba2bed32e08741b2cd3@marca.python.live.cheggnet.com:9000/1'; |
|
return new MockRavenClient($dsn); |
} |
|
public function testDebug() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$record = $this->getRecord(Logger::DEBUG, 'A test debug message'); |
$handler->handle($record); |
|
$this->assertEquals($ravenClient::DEBUG, $ravenClient->lastData['level']); |
$this->assertContains($record['message'], $ravenClient->lastData['message']); |
} |
|
public function testWarning() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$record = $this->getRecord(Logger::WARNING, 'A test warning message'); |
$handler->handle($record); |
|
$this->assertEquals($ravenClient::WARNING, $ravenClient->lastData['level']); |
$this->assertContains($record['message'], $ravenClient->lastData['message']); |
} |
|
public function testTag() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$tags = array(1, 2, 'foo'); |
$record = $this->getRecord(Logger::INFO, 'test', array('tags' => $tags)); |
$handler->handle($record); |
|
$this->assertEquals($tags, $ravenClient->lastData['tags']); |
} |
|
public function testExtraParameters() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$checksum = '098f6bcd4621d373cade4e832627b4f6'; |
$release = '05a671c66aefea124cc08b76ea6d30bb'; |
$eventId = '31423'; |
$record = $this->getRecord(Logger::INFO, 'test', array('checksum' => $checksum, 'release' => $release, 'event_id' => $eventId)); |
$handler->handle($record); |
|
$this->assertEquals($checksum, $ravenClient->lastData['checksum']); |
$this->assertEquals($release, $ravenClient->lastData['release']); |
$this->assertEquals($eventId, $ravenClient->lastData['event_id']); |
} |
|
public function testFingerprint() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$fingerprint = array('{{ default }}', 'other value'); |
$record = $this->getRecord(Logger::INFO, 'test', array('fingerprint' => $fingerprint)); |
$handler->handle($record); |
|
$this->assertEquals($fingerprint, $ravenClient->lastData['fingerprint']); |
} |
|
public function testUserContext() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$recordWithNoContext = $this->getRecord(Logger::INFO, 'test with default user context'); |
// set user context 'externally' |
|
$user = array( |
'id' => '123', |
'email' => 'test@test.com', |
); |
|
$recordWithContext = $this->getRecord(Logger::INFO, 'test', array('user' => $user)); |
|
$ravenClient->user_context(array('id' => 'test_user_id')); |
// handle context |
$handler->handle($recordWithContext); |
$this->assertEquals($user, $ravenClient->lastData['user']); |
|
// check to see if its reset |
$handler->handle($recordWithNoContext); |
$this->assertInternalType('array', $ravenClient->context->user); |
$this->assertSame('test_user_id', $ravenClient->context->user['id']); |
|
// handle with null context |
$ravenClient->user_context(null); |
$handler->handle($recordWithContext); |
$this->assertEquals($user, $ravenClient->lastData['user']); |
|
// check to see if its reset |
$handler->handle($recordWithNoContext); |
$this->assertNull($ravenClient->context->user); |
} |
|
public function testException() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
try { |
$this->methodThatThrowsAnException(); |
} catch (\Exception $e) { |
$record = $this->getRecord(Logger::ERROR, $e->getMessage(), array('exception' => $e)); |
$handler->handle($record); |
} |
|
$this->assertEquals($record['message'], $ravenClient->lastData['message']); |
} |
|
public function testHandleBatch() |
{ |
$records = $this->getMultipleRecords(); |
$records[] = $this->getRecord(Logger::WARNING, 'warning'); |
$records[] = $this->getRecord(Logger::WARNING, 'warning'); |
|
$logFormatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); |
$logFormatter->expects($this->once())->method('formatBatch'); |
|
$formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); |
$formatter->expects($this->once())->method('format')->with($this->callback(function ($record) { |
return $record['level'] == 400; |
})); |
|
$handler = $this->getHandler($this->getRavenClient()); |
$handler->setBatchFormatter($logFormatter); |
$handler->setFormatter($formatter); |
$handler->handleBatch($records); |
} |
|
public function testHandleBatchDoNothingIfRecordsAreBelowLevel() |
{ |
$records = array( |
$this->getRecord(Logger::DEBUG, 'debug message 1'), |
$this->getRecord(Logger::DEBUG, 'debug message 2'), |
$this->getRecord(Logger::INFO, 'information'), |
); |
|
$handler = $this->getMock('Monolog\Handler\RavenHandler', null, array($this->getRavenClient())); |
$handler->expects($this->never())->method('handle'); |
$handler->setLevel(Logger::ERROR); |
$handler->handleBatch($records); |
} |
|
public function testHandleBatchPicksProperMessage() |
{ |
$records = array( |
$this->getRecord(Logger::DEBUG, 'debug message 1'), |
$this->getRecord(Logger::DEBUG, 'debug message 2'), |
$this->getRecord(Logger::INFO, 'information 1'), |
$this->getRecord(Logger::ERROR, 'error 1'), |
$this->getRecord(Logger::WARNING, 'warning'), |
$this->getRecord(Logger::ERROR, 'error 2'), |
$this->getRecord(Logger::INFO, 'information 2'), |
); |
|
$logFormatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); |
$logFormatter->expects($this->once())->method('formatBatch'); |
|
$formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); |
$formatter->expects($this->once())->method('format')->with($this->callback(function ($record) use ($records) { |
return $record['message'] == 'error 1'; |
})); |
|
$handler = $this->getHandler($this->getRavenClient()); |
$handler->setBatchFormatter($logFormatter); |
$handler->setFormatter($formatter); |
$handler->handleBatch($records); |
} |
|
public function testGetSetBatchFormatter() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
|
$handler->setBatchFormatter($formatter = new LineFormatter()); |
$this->assertSame($formatter, $handler->getBatchFormatter()); |
} |
|
public function testRelease() |
{ |
$ravenClient = $this->getRavenClient(); |
$handler = $this->getHandler($ravenClient); |
$release = 'v42.42.42'; |
$handler->setRelease($release); |
$record = $this->getRecord(Logger::INFO, 'test'); |
$handler->handle($record); |
$this->assertEquals($release, $ravenClient->lastData['release']); |
|
$localRelease = 'v41.41.41'; |
$record = $this->getRecord(Logger::INFO, 'test', array('release' => $localRelease)); |
$handler->handle($record); |
$this->assertEquals($localRelease, $ravenClient->lastData['release']); |
} |
|
private function methodThatThrowsAnException() |
{ |
throw new \Exception('This is an exception'); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.php |
@@ -0,0 +1,127 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
|
class RedisHandlerTest extends TestCase |
{ |
/** |
* @expectedException InvalidArgumentException |
*/ |
public function testConstructorShouldThrowExceptionForInvalidRedis() |
{ |
new RedisHandler(new \stdClass(), 'key'); |
} |
|
public function testConstructorShouldWorkWithPredis() |
{ |
$redis = $this->getMock('Predis\Client'); |
$this->assertInstanceof('Monolog\Handler\RedisHandler', new RedisHandler($redis, 'key')); |
} |
|
public function testConstructorShouldWorkWithRedis() |
{ |
$redis = $this->getMock('Redis'); |
$this->assertInstanceof('Monolog\Handler\RedisHandler', new RedisHandler($redis, 'key')); |
} |
|
public function testPredisHandle() |
{ |
$redis = $this->getMock('Predis\Client', array('rpush')); |
|
// Predis\Client uses rpush |
$redis->expects($this->once()) |
->method('rpush') |
->with('key', 'test'); |
|
$record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); |
|
$handler = new RedisHandler($redis, 'key'); |
$handler->setFormatter(new LineFormatter("%message%")); |
$handler->handle($record); |
} |
|
public function testRedisHandle() |
{ |
$redis = $this->getMock('Redis', array('rpush')); |
|
// Redis uses rPush |
$redis->expects($this->once()) |
->method('rPush') |
->with('key', 'test'); |
|
$record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); |
|
$handler = new RedisHandler($redis, 'key'); |
$handler->setFormatter(new LineFormatter("%message%")); |
$handler->handle($record); |
} |
|
public function testRedisHandleCapped() |
{ |
$redis = $this->getMock('Redis', array('multi', 'rpush', 'ltrim', 'exec')); |
|
// Redis uses multi |
$redis->expects($this->once()) |
->method('multi') |
->will($this->returnSelf()); |
|
$redis->expects($this->once()) |
->method('rpush') |
->will($this->returnSelf()); |
|
$redis->expects($this->once()) |
->method('ltrim') |
->will($this->returnSelf()); |
|
$redis->expects($this->once()) |
->method('exec') |
->will($this->returnSelf()); |
|
$record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); |
|
$handler = new RedisHandler($redis, 'key', Logger::DEBUG, true, 10); |
$handler->setFormatter(new LineFormatter("%message%")); |
$handler->handle($record); |
} |
|
public function testPredisHandleCapped() |
{ |
$redis = $this->getMock('Predis\Client', array('transaction')); |
|
$redisTransaction = $this->getMock('Predis\Client', array('rpush', 'ltrim')); |
|
$redisTransaction->expects($this->once()) |
->method('rpush') |
->will($this->returnSelf()); |
|
$redisTransaction->expects($this->once()) |
->method('ltrim') |
->will($this->returnSelf()); |
|
// Redis uses multi |
$redis->expects($this->once()) |
->method('transaction') |
->will($this->returnCallback(function ($cb) use ($redisTransaction) { |
$cb($redisTransaction); |
})); |
|
$record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); |
|
$handler = new RedisHandler($redis, 'key', Logger::DEBUG, true, 10); |
$handler->setFormatter(new LineFormatter("%message%")); |
$handler->handle($record); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php |
@@ -0,0 +1,211 @@ |
<?php |
|
/* |
* 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\TestCase; |
use PHPUnit_Framework_Error_Deprecated; |
|
/** |
* @covers Monolog\Handler\RotatingFileHandler |
*/ |
class RotatingFileHandlerTest extends TestCase |
{ |
/** |
* This var should be private but then the anonymous function |
* in the `setUp` method won't be able to set it. `$this` cant't |
* be used in the anonymous function in `setUp` because PHP 5.3 |
* does not support it. |
*/ |
public $lastError; |
|
public function setUp() |
{ |
$dir = __DIR__.'/Fixtures'; |
chmod($dir, 0777); |
if (!is_writable($dir)) { |
$this->markTestSkipped($dir.' must be writable to test the RotatingFileHandler.'); |
} |
$this->lastError = null; |
$self = $this; |
// workaround with &$self used for PHP 5.3 |
set_error_handler(function($code, $message) use (&$self) { |
$self->lastError = array( |
'code' => $code, |
'message' => $message, |
); |
}); |
} |
|
private function assertErrorWasTriggered($code, $message) |
{ |
if (empty($this->lastError)) { |
$this->fail( |
sprintf( |
'Failed asserting that error with code `%d` and message `%s` was triggered', |
$code, |
$message |
) |
); |
} |
$this->assertEquals($code, $this->lastError['code'], sprintf('Expected an error with code %d to be triggered, got `%s` instead', $code, $this->lastError['code'])); |
$this->assertEquals($message, $this->lastError['message'], sprintf('Expected an error with message `%d` to be triggered, got `%s` instead', $message, $this->lastError['message'])); |
} |
|
public function testRotationCreatesNewFile() |
{ |
touch(__DIR__.'/Fixtures/foo-'.date('Y-m-d', time() - 86400).'.rot'); |
|
$handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot'); |
$handler->setFormatter($this->getIdentityFormatter()); |
$handler->handle($this->getRecord()); |
|
$log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; |
$this->assertTrue(file_exists($log)); |
$this->assertEquals('test', file_get_contents($log)); |
} |
|
/** |
* @dataProvider rotationTests |
*/ |
public function testRotation($createFile, $dateFormat, $timeCallback) |
{ |
touch($old1 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-1)).'.rot'); |
touch($old2 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-2)).'.rot'); |
touch($old3 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-3)).'.rot'); |
touch($old4 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-4)).'.rot'); |
|
$log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot'; |
|
if ($createFile) { |
touch($log); |
} |
|
$handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); |
$handler->setFormatter($this->getIdentityFormatter()); |
$handler->setFilenameFormat('{filename}-{date}', $dateFormat); |
$handler->handle($this->getRecord()); |
|
$handler->close(); |
|
$this->assertTrue(file_exists($log)); |
$this->assertTrue(file_exists($old1)); |
$this->assertEquals($createFile, file_exists($old2)); |
$this->assertEquals($createFile, file_exists($old3)); |
$this->assertEquals($createFile, file_exists($old4)); |
$this->assertEquals('test', file_get_contents($log)); |
} |
|
public function rotationTests() |
{ |
$now = time(); |
$dayCallback = function($ago) use ($now) { |
return $now + 86400 * $ago; |
}; |
$monthCallback = function($ago) { |
return gmmktime(0, 0, 0, date('n') + $ago, 1, date('Y')); |
}; |
$yearCallback = function($ago) { |
return gmmktime(0, 0, 0, 1, 1, date('Y') + $ago); |
}; |
|
return array( |
'Rotation is triggered when the file of the current day is not present' |
=> array(true, RotatingFileHandler::FILE_PER_DAY, $dayCallback), |
'Rotation is not triggered when the file of the current day is already present' |
=> array(false, RotatingFileHandler::FILE_PER_DAY, $dayCallback), |
|
'Rotation is triggered when the file of the current month is not present' |
=> array(true, RotatingFileHandler::FILE_PER_MONTH, $monthCallback), |
'Rotation is not triggered when the file of the current month is already present' |
=> array(false, RotatingFileHandler::FILE_PER_MONTH, $monthCallback), |
|
'Rotation is triggered when the file of the current year is not present' |
=> array(true, RotatingFileHandler::FILE_PER_YEAR, $yearCallback), |
'Rotation is not triggered when the file of the current year is already present' |
=> array(false, RotatingFileHandler::FILE_PER_YEAR, $yearCallback), |
); |
} |
|
/** |
* @dataProvider dateFormatProvider |
*/ |
public function testAllowOnlyFixedDefinedDateFormats($dateFormat, $valid) |
{ |
$handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); |
$handler->setFilenameFormat('{filename}-{date}', $dateFormat); |
if (!$valid) { |
$this->assertErrorWasTriggered( |
E_USER_DEPRECATED, |
'Invalid date format - format must be one of RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), '. |
'RotatingFileHandler::FILE_PER_MONTH ("Y-m") or RotatingFileHandler::FILE_PER_YEAR ("Y"), '. |
'or you can set one of the date formats using slashes, underscores and/or dots instead of dashes.' |
); |
} |
} |
|
public function dateFormatProvider() |
{ |
return array( |
array(RotatingFileHandler::FILE_PER_DAY, true), |
array(RotatingFileHandler::FILE_PER_MONTH, true), |
array(RotatingFileHandler::FILE_PER_YEAR, true), |
array('m-d-Y', false), |
array('Y-m-d-h-i', false) |
); |
} |
|
/** |
* @dataProvider filenameFormatProvider |
*/ |
public function testDisallowFilenameFormatsWithoutDate($filenameFormat, $valid) |
{ |
$handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); |
$handler->setFilenameFormat($filenameFormat, RotatingFileHandler::FILE_PER_DAY); |
if (!$valid) { |
$this->assertErrorWasTriggered( |
E_USER_DEPRECATED, |
'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.' |
); |
} |
} |
|
public function filenameFormatProvider() |
{ |
return array( |
array('{filename}', false), |
array('{filename}-{date}', true), |
array('{date}', true), |
array('foobar-{date}', true), |
array('foo-{date}-bar', true), |
array('{date}-foobar', true), |
array('foobar', false), |
); |
} |
|
public function testReuseCurrentFile() |
{ |
$log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; |
file_put_contents($log, "foo"); |
$handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot'); |
$handler->setFormatter($this->getIdentityFormatter()); |
$handler->handle($this->getRecord()); |
$this->assertEquals('footest', file_get_contents($log)); |
} |
|
public function tearDown() |
{ |
foreach (glob(__DIR__.'/Fixtures/*.rot') as $file) { |
unlink($file); |
} |
restore_error_handler(); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php |
@@ -0,0 +1,387 @@ |
<?php |
|
/* |
* 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\Slack; |
|
use Monolog\Logger; |
use Monolog\TestCase; |
|
/** |
* @coversDefaultClass Monolog\Handler\Slack\SlackRecord |
*/ |
class SlackRecordTest extends TestCase |
{ |
private $jsonPrettyPrintFlag; |
|
protected function setUp() |
{ |
$this->jsonPrettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128; |
} |
|
public function dataGetAttachmentColor() |
{ |
return array( |
array(Logger::DEBUG, SlackRecord::COLOR_DEFAULT), |
array(Logger::INFO, SlackRecord::COLOR_GOOD), |
array(Logger::NOTICE, SlackRecord::COLOR_GOOD), |
array(Logger::WARNING, SlackRecord::COLOR_WARNING), |
array(Logger::ERROR, SlackRecord::COLOR_DANGER), |
array(Logger::CRITICAL, SlackRecord::COLOR_DANGER), |
array(Logger::ALERT, SlackRecord::COLOR_DANGER), |
array(Logger::EMERGENCY, SlackRecord::COLOR_DANGER), |
); |
} |
|
/** |
* @dataProvider dataGetAttachmentColor |
* @param int $logLevel |
* @param string $expectedColour RGB hex color or name of Slack color |
* @covers ::getAttachmentColor |
*/ |
public function testGetAttachmentColor($logLevel, $expectedColour) |
{ |
$slackRecord = new SlackRecord(); |
$this->assertSame( |
$expectedColour, |
$slackRecord->getAttachmentColor($logLevel) |
); |
} |
|
public function testAddsChannel() |
{ |
$channel = '#test'; |
$record = new SlackRecord($channel); |
$data = $record->getSlackData($this->getRecord()); |
|
$this->assertArrayHasKey('channel', $data); |
$this->assertSame($channel, $data['channel']); |
} |
|
public function testNoUsernameByDefault() |
{ |
$record = new SlackRecord(); |
$data = $record->getSlackData($this->getRecord()); |
|
$this->assertArrayNotHasKey('username', $data); |
} |
|
/** |
* @return array |
*/ |
public function dataStringify() |
{ |
$jsonPrettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128; |
|
$multipleDimensions = array(array(1, 2)); |
$numericKeys = array('library' => 'monolog'); |
$singleDimension = array(1, 'Hello', 'Jordi'); |
|
return array( |
array(array(), '[]'), |
array($multipleDimensions, json_encode($multipleDimensions, $jsonPrettyPrintFlag)), |
array($numericKeys, json_encode($numericKeys, $jsonPrettyPrintFlag)), |
array($singleDimension, json_encode($singleDimension)) |
); |
} |
|
/** |
* @dataProvider dataStringify |
*/ |
public function testStringify($fields, $expectedResult) |
{ |
$slackRecord = new SlackRecord( |
'#test', |
'test', |
true, |
null, |
true, |
true |
); |
|
$this->assertSame($expectedResult, $slackRecord->stringify($fields)); |
} |
|
public function testAddsCustomUsername() |
{ |
$username = 'Monolog bot'; |
$record = new SlackRecord(null, $username); |
$data = $record->getSlackData($this->getRecord()); |
|
$this->assertArrayHasKey('username', $data); |
$this->assertSame($username, $data['username']); |
} |
|
public function testNoIcon() |
{ |
$record = new SlackRecord(); |
$data = $record->getSlackData($this->getRecord()); |
|
$this->assertArrayNotHasKey('icon_emoji', $data); |
} |
|
public function testAddsIcon() |
{ |
$record = $this->getRecord(); |
$slackRecord = new SlackRecord(null, null, false, 'ghost'); |
$data = $slackRecord->getSlackData($record); |
|
$slackRecord2 = new SlackRecord(null, null, false, 'http://github.com/Seldaek/monolog'); |
$data2 = $slackRecord2->getSlackData($record); |
|
$this->assertArrayHasKey('icon_emoji', $data); |
$this->assertSame(':ghost:', $data['icon_emoji']); |
$this->assertArrayHasKey('icon_url', $data2); |
$this->assertSame('http://github.com/Seldaek/monolog', $data2['icon_url']); |
} |
|
public function testAttachmentsNotPresentIfNoAttachment() |
{ |
$record = new SlackRecord(null, null, false); |
$data = $record->getSlackData($this->getRecord()); |
|
$this->assertArrayNotHasKey('attachments', $data); |
} |
|
public function testAddsOneAttachment() |
{ |
$record = new SlackRecord(); |
$data = $record->getSlackData($this->getRecord()); |
|
$this->assertArrayHasKey('attachments', $data); |
$this->assertArrayHasKey(0, $data['attachments']); |
$this->assertInternalType('array', $data['attachments'][0]); |
} |
|
public function testTextEqualsMessageIfNoAttachment() |
{ |
$message = 'Test message'; |
$record = new SlackRecord(null, null, false); |
$data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); |
|
$this->assertArrayHasKey('text', $data); |
$this->assertSame($message, $data['text']); |
} |
|
public function testTextEqualsFormatterOutput() |
{ |
$formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); |
$formatter |
->expects($this->any()) |
->method('format') |
->will($this->returnCallback(function ($record) { return $record['message'] . 'test'; })); |
|
$formatter2 = $this->getMock('Monolog\\Formatter\\FormatterInterface'); |
$formatter2 |
->expects($this->any()) |
->method('format') |
->will($this->returnCallback(function ($record) { return $record['message'] . 'test1'; })); |
|
$message = 'Test message'; |
$record = new SlackRecord(null, null, false, null, false, false, array(), $formatter); |
$data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); |
|
$this->assertArrayHasKey('text', $data); |
$this->assertSame($message . 'test', $data['text']); |
|
$record->setFormatter($formatter2); |
$data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); |
|
$this->assertArrayHasKey('text', $data); |
$this->assertSame($message . 'test1', $data['text']); |
} |
|
public function testAddsFallbackAndTextToAttachment() |
{ |
$message = 'Test message'; |
$record = new SlackRecord(null); |
$data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); |
|
$this->assertSame($message, $data['attachments'][0]['text']); |
$this->assertSame($message, $data['attachments'][0]['fallback']); |
} |
|
public function testMapsLevelToColorAttachmentColor() |
{ |
$record = new SlackRecord(null); |
$errorLoggerRecord = $this->getRecord(Logger::ERROR); |
$emergencyLoggerRecord = $this->getRecord(Logger::EMERGENCY); |
$warningLoggerRecord = $this->getRecord(Logger::WARNING); |
$infoLoggerRecord = $this->getRecord(Logger::INFO); |
$debugLoggerRecord = $this->getRecord(Logger::DEBUG); |
|
$data = $record->getSlackData($errorLoggerRecord); |
$this->assertSame(SlackRecord::COLOR_DANGER, $data['attachments'][0]['color']); |
|
$data = $record->getSlackData($emergencyLoggerRecord); |
$this->assertSame(SlackRecord::COLOR_DANGER, $data['attachments'][0]['color']); |
|
$data = $record->getSlackData($warningLoggerRecord); |
$this->assertSame(SlackRecord::COLOR_WARNING, $data['attachments'][0]['color']); |
|
$data = $record->getSlackData($infoLoggerRecord); |
$this->assertSame(SlackRecord::COLOR_GOOD, $data['attachments'][0]['color']); |
|
$data = $record->getSlackData($debugLoggerRecord); |
$this->assertSame(SlackRecord::COLOR_DEFAULT, $data['attachments'][0]['color']); |
} |
|
public function testAddsShortAttachmentWithoutContextAndExtra() |
{ |
$level = Logger::ERROR; |
$levelName = Logger::getLevelName($level); |
$record = new SlackRecord(null, null, true, null, true); |
$data = $record->getSlackData($this->getRecord($level, 'test', array('test' => 1))); |
|
$attachment = $data['attachments'][0]; |
$this->assertArrayHasKey('title', $attachment); |
$this->assertArrayHasKey('fields', $attachment); |
$this->assertSame($levelName, $attachment['title']); |
$this->assertSame(array(), $attachment['fields']); |
} |
|
public function testAddsShortAttachmentWithContextAndExtra() |
{ |
$level = Logger::ERROR; |
$levelName = Logger::getLevelName($level); |
$context = array('test' => 1); |
$extra = array('tags' => array('web')); |
$record = new SlackRecord(null, null, true, null, true, true); |
$loggerRecord = $this->getRecord($level, 'test', $context); |
$loggerRecord['extra'] = $extra; |
$data = $record->getSlackData($loggerRecord); |
|
$attachment = $data['attachments'][0]; |
$this->assertArrayHasKey('title', $attachment); |
$this->assertArrayHasKey('fields', $attachment); |
$this->assertCount(2, $attachment['fields']); |
$this->assertSame($levelName, $attachment['title']); |
$this->assertSame( |
array( |
array( |
'title' => 'Extra', |
'value' => sprintf('```%s```', json_encode($extra, $this->jsonPrettyPrintFlag)), |
'short' => false |
), |
array( |
'title' => 'Context', |
'value' => sprintf('```%s```', json_encode($context, $this->jsonPrettyPrintFlag)), |
'short' => false |
) |
), |
$attachment['fields'] |
); |
} |
|
public function testAddsLongAttachmentWithoutContextAndExtra() |
{ |
$level = Logger::ERROR; |
$levelName = Logger::getLevelName($level); |
$record = new SlackRecord(null, null, true, null); |
$data = $record->getSlackData($this->getRecord($level, 'test', array('test' => 1))); |
|
$attachment = $data['attachments'][0]; |
$this->assertArrayHasKey('title', $attachment); |
$this->assertArrayHasKey('fields', $attachment); |
$this->assertCount(1, $attachment['fields']); |
$this->assertSame('Message', $attachment['title']); |
$this->assertSame( |
array(array( |
'title' => 'Level', |
'value' => $levelName, |
'short' => false |
)), |
$attachment['fields'] |
); |
} |
|
public function testAddsLongAttachmentWithContextAndExtra() |
{ |
$level = Logger::ERROR; |
$levelName = Logger::getLevelName($level); |
$context = array('test' => 1); |
$extra = array('tags' => array('web')); |
$record = new SlackRecord(null, null, true, null, false, true); |
$loggerRecord = $this->getRecord($level, 'test', $context); |
$loggerRecord['extra'] = $extra; |
$data = $record->getSlackData($loggerRecord); |
|
$expectedFields = array( |
array( |
'title' => 'Level', |
'value' => $levelName, |
'short' => false, |
), |
array( |
'title' => 'tags', |
'value' => sprintf('```%s```', json_encode($extra['tags'])), |
'short' => false |
), |
array( |
'title' => 'test', |
'value' => $context['test'], |
'short' => false |
) |
); |
|
$attachment = $data['attachments'][0]; |
$this->assertArrayHasKey('title', $attachment); |
$this->assertArrayHasKey('fields', $attachment); |
$this->assertCount(3, $attachment['fields']); |
$this->assertSame('Message', $attachment['title']); |
$this->assertSame( |
$expectedFields, |
$attachment['fields'] |
); |
} |
|
public function testAddsTimestampToAttachment() |
{ |
$record = $this->getRecord(); |
$slackRecord = new SlackRecord(); |
$data = $slackRecord->getSlackData($this->getRecord()); |
|
$attachment = $data['attachments'][0]; |
$this->assertArrayHasKey('ts', $attachment); |
$this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']); |
} |
|
public function testExcludeExtraAndContextFields() |
{ |
$record = $this->getRecord( |
Logger::WARNING, |
'test', |
array('info' => array('library' => 'monolog', 'author' => 'Jordi')) |
); |
$record['extra'] = array('tags' => array('web', 'cli')); |
|
$slackRecord = new SlackRecord(null, null, true, null, false, true, array('context.info.library', 'extra.tags.1')); |
$data = $slackRecord->getSlackData($record); |
$attachment = $data['attachments'][0]; |
|
$expected = array( |
array( |
'title' => 'info', |
'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)), |
'short' => false |
), |
array( |
'title' => 'tags', |
'value' => sprintf('```%s```', json_encode(array('web'))), |
'short' => false |
), |
); |
|
foreach ($expected as $field) { |
$this->assertNotFalse(array_search($field, $attachment['fields'])); |
break; |
} |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/SlackHandlerTest.php |
@@ -0,0 +1,155 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
use Monolog\Formatter\LineFormatter; |
use Monolog\Handler\Slack\SlackRecord; |
|
/** |
* @author Greg Kedzierski <greg@gregkedzierski.com> |
* @see https://api.slack.com/ |
*/ |
class SlackHandlerTest extends TestCase |
{ |
/** |
* @var resource |
*/ |
private $res; |
|
/** |
* @var SlackHandler |
*/ |
private $handler; |
|
public function setUp() |
{ |
if (!extension_loaded('openssl')) { |
$this->markTestSkipped('This test requires openssl to run'); |
} |
} |
|
public function testWriteHeader() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/POST \/api\/chat.postMessage HTTP\/1.1\\r\\nHost: slack.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); |
} |
|
public function testWriteContent() |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegExp('/username=Monolog/', $content); |
$this->assertRegExp('/channel=channel1/', $content); |
$this->assertRegExp('/token=myToken/', $content); |
$this->assertRegExp('/attachments/', $content); |
} |
|
public function testWriteContentUsesFormatterIfProvided() |
{ |
$this->createHandler('myToken', 'channel1', 'Monolog', false); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->createHandler('myToken', 'channel1', 'Monolog', false); |
$this->handler->setFormatter(new LineFormatter('foo--%message%')); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test2')); |
fseek($this->res, 0); |
$content2 = fread($this->res, 1024); |
|
$this->assertRegexp('/text=test1/', $content); |
$this->assertRegexp('/text=foo--test2/', $content2); |
} |
|
public function testWriteContentWithEmoji() |
{ |
$this->createHandler('myToken', 'channel1', 'Monolog', true, 'alien'); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/icon_emoji=%3Aalien%3A/', $content); |
} |
|
/** |
* @dataProvider provideLevelColors |
*/ |
public function testWriteContentWithColors($level, $expectedColor) |
{ |
$this->createHandler(); |
$this->handler->handle($this->getRecord($level, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/%22color%22%3A%22'.$expectedColor.'/', $content); |
} |
|
public function testWriteContentWithPlainTextMessage() |
{ |
$this->createHandler('myToken', 'channel1', 'Monolog', false); |
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); |
fseek($this->res, 0); |
$content = fread($this->res, 1024); |
|
$this->assertRegexp('/text=test1/', $content); |
} |
|
public function provideLevelColors() |
{ |
return array( |
array(Logger::DEBUG, urlencode(SlackRecord::COLOR_DEFAULT)), |
array(Logger::INFO, SlackRecord::COLOR_GOOD), |
array(Logger::NOTICE, SlackRecord::COLOR_GOOD), |
array(Logger::WARNING, SlackRecord::COLOR_WARNING), |
array(Logger::ERROR, SlackRecord::COLOR_DANGER), |
array(Logger::CRITICAL, SlackRecord::COLOR_DANGER), |
array(Logger::ALERT, SlackRecord::COLOR_DANGER), |
array(Logger::EMERGENCY,SlackRecord::COLOR_DANGER), |
); |
} |
|
private function createHandler($token = 'myToken', $channel = 'channel1', $username = 'Monolog', $useAttachment = true, $iconEmoji = null, $useShortAttachment = false, $includeExtra = false) |
{ |
$constructorArgs = array($token, $channel, $username, $useAttachment, $iconEmoji, Logger::DEBUG, true, $useShortAttachment, $includeExtra); |
$this->res = fopen('php://memory', 'a'); |
$this->handler = $this->getMock( |
'\Monolog\Handler\SlackHandler', |
array('fsockopen', 'streamSetTimeout', 'closeSocket'), |
$constructorArgs |
); |
|
$reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); |
$reflectionProperty->setAccessible(true); |
$reflectionProperty->setValue($this->handler, 'localhost:1234'); |
|
$this->handler->expects($this->any()) |
->method('fsockopen') |
->will($this->returnValue($this->res)); |
$this->handler->expects($this->any()) |
->method('streamSetTimeout') |
->will($this->returnValue(true)); |
$this->handler->expects($this->any()) |
->method('closeSocket') |
->will($this->returnValue(true)); |
|
$this->handler->setFormatter($this->getIdentityFormatter()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php |
@@ -0,0 +1,309 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
/** |
* @author Pablo de Leon Belloc <pablolb@gmail.com> |
*/ |
class SocketHandlerTest extends TestCase |
{ |
/** |
* @var Monolog\Handler\SocketHandler |
*/ |
private $handler; |
|
/** |
* @var resource |
*/ |
private $res; |
|
/** |
* @expectedException UnexpectedValueException |
*/ |
public function testInvalidHostname() |
{ |
$this->createHandler('garbage://here'); |
$this->writeRecord('data'); |
} |
|
/** |
* @expectedException \InvalidArgumentException |
*/ |
public function testBadConnectionTimeout() |
{ |
$this->createHandler('localhost:1234'); |
$this->handler->setConnectionTimeout(-1); |
} |
|
public function testSetConnectionTimeout() |
{ |
$this->createHandler('localhost:1234'); |
$this->handler->setConnectionTimeout(10.1); |
$this->assertEquals(10.1, $this->handler->getConnectionTimeout()); |
} |
|
/** |
* @expectedException \InvalidArgumentException |
*/ |
public function testBadTimeout() |
{ |
$this->createHandler('localhost:1234'); |
$this->handler->setTimeout(-1); |
} |
|
public function testSetTimeout() |
{ |
$this->createHandler('localhost:1234'); |
$this->handler->setTimeout(10.25); |
$this->assertEquals(10.25, $this->handler->getTimeout()); |
} |
|
public function testSetWritingTimeout() |
{ |
$this->createHandler('localhost:1234'); |
$this->handler->setWritingTimeout(10.25); |
$this->assertEquals(10.25, $this->handler->getWritingTimeout()); |
} |
|
public function testSetConnectionString() |
{ |
$this->createHandler('tcp://localhost:9090'); |
$this->assertEquals('tcp://localhost:9090', $this->handler->getConnectionString()); |
} |
|
/** |
* @expectedException UnexpectedValueException |
*/ |
public function testExceptionIsThrownOnFsockopenError() |
{ |
$this->setMockHandler(array('fsockopen')); |
$this->handler->expects($this->once()) |
->method('fsockopen') |
->will($this->returnValue(false)); |
$this->writeRecord('Hello world'); |
} |
|
/** |
* @expectedException UnexpectedValueException |
*/ |
public function testExceptionIsThrownOnPfsockopenError() |
{ |
$this->setMockHandler(array('pfsockopen')); |
$this->handler->expects($this->once()) |
->method('pfsockopen') |
->will($this->returnValue(false)); |
$this->handler->setPersistent(true); |
$this->writeRecord('Hello world'); |
} |
|
/** |
* @expectedException UnexpectedValueException |
*/ |
public function testExceptionIsThrownIfCannotSetTimeout() |
{ |
$this->setMockHandler(array('streamSetTimeout')); |
$this->handler->expects($this->once()) |
->method('streamSetTimeout') |
->will($this->returnValue(false)); |
$this->writeRecord('Hello world'); |
} |
|
/** |
* @expectedException RuntimeException |
*/ |
public function testWriteFailsOnIfFwriteReturnsFalse() |
{ |
$this->setMockHandler(array('fwrite')); |
|
$callback = function ($arg) { |
$map = array( |
'Hello world' => 6, |
'world' => false, |
); |
|
return $map[$arg]; |
}; |
|
$this->handler->expects($this->exactly(2)) |
->method('fwrite') |
->will($this->returnCallback($callback)); |
|
$this->writeRecord('Hello world'); |
} |
|
/** |
* @expectedException RuntimeException |
*/ |
public function testWriteFailsIfStreamTimesOut() |
{ |
$this->setMockHandler(array('fwrite', 'streamGetMetadata')); |
|
$callback = function ($arg) { |
$map = array( |
'Hello world' => 6, |
'world' => 5, |
); |
|
return $map[$arg]; |
}; |
|
$this->handler->expects($this->exactly(1)) |
->method('fwrite') |
->will($this->returnCallback($callback)); |
$this->handler->expects($this->exactly(1)) |
->method('streamGetMetadata') |
->will($this->returnValue(array('timed_out' => true))); |
|
$this->writeRecord('Hello world'); |
} |
|
/** |
* @expectedException RuntimeException |
*/ |
public function testWriteFailsOnIncompleteWrite() |
{ |
$this->setMockHandler(array('fwrite', 'streamGetMetadata')); |
|
$res = $this->res; |
$callback = function ($string) use ($res) { |
fclose($res); |
|
return strlen('Hello'); |
}; |
|
$this->handler->expects($this->exactly(1)) |
->method('fwrite') |
->will($this->returnCallback($callback)); |
$this->handler->expects($this->exactly(1)) |
->method('streamGetMetadata') |
->will($this->returnValue(array('timed_out' => false))); |
|
$this->writeRecord('Hello world'); |
} |
|
public function testWriteWithMemoryFile() |
{ |
$this->setMockHandler(); |
$this->writeRecord('test1'); |
$this->writeRecord('test2'); |
$this->writeRecord('test3'); |
fseek($this->res, 0); |
$this->assertEquals('test1test2test3', fread($this->res, 1024)); |
} |
|
public function testWriteWithMock() |
{ |
$this->setMockHandler(array('fwrite')); |
|
$callback = function ($arg) { |
$map = array( |
'Hello world' => 6, |
'world' => 5, |
); |
|
return $map[$arg]; |
}; |
|
$this->handler->expects($this->exactly(2)) |
->method('fwrite') |
->will($this->returnCallback($callback)); |
|
$this->writeRecord('Hello world'); |
} |
|
public function testClose() |
{ |
$this->setMockHandler(); |
$this->writeRecord('Hello world'); |
$this->assertInternalType('resource', $this->res); |
$this->handler->close(); |
$this->assertFalse(is_resource($this->res), "Expected resource to be closed after closing handler"); |
} |
|
public function testCloseDoesNotClosePersistentSocket() |
{ |
$this->setMockHandler(); |
$this->handler->setPersistent(true); |
$this->writeRecord('Hello world'); |
$this->assertTrue(is_resource($this->res)); |
$this->handler->close(); |
$this->assertTrue(is_resource($this->res)); |
} |
|
/** |
* @expectedException \RuntimeException |
*/ |
public function testAvoidInfiniteLoopWhenNoDataIsWrittenForAWritingTimeoutSeconds() |
{ |
$this->setMockHandler(array('fwrite', 'streamGetMetadata')); |
|
$this->handler->expects($this->any()) |
->method('fwrite') |
->will($this->returnValue(0)); |
|
$this->handler->expects($this->any()) |
->method('streamGetMetadata') |
->will($this->returnValue(array('timed_out' => false))); |
|
$this->handler->setWritingTimeout(1); |
|
$this->writeRecord('Hello world'); |
} |
|
private function createHandler($connectionString) |
{ |
$this->handler = new SocketHandler($connectionString); |
$this->handler->setFormatter($this->getIdentityFormatter()); |
} |
|
private function writeRecord($string) |
{ |
$this->handler->handle($this->getRecord(Logger::WARNING, $string)); |
} |
|
private function setMockHandler(array $methods = array()) |
{ |
$this->res = fopen('php://memory', 'a'); |
|
$defaultMethods = array('fsockopen', 'pfsockopen', 'streamSetTimeout'); |
$newMethods = array_diff($methods, $defaultMethods); |
|
$finalMethods = array_merge($defaultMethods, $newMethods); |
|
$this->handler = $this->getMock( |
'\Monolog\Handler\SocketHandler', $finalMethods, array('localhost:1234') |
); |
|
if (!in_array('fsockopen', $methods)) { |
$this->handler->expects($this->any()) |
->method('fsockopen') |
->will($this->returnValue($this->res)); |
} |
|
if (!in_array('pfsockopen', $methods)) { |
$this->handler->expects($this->any()) |
->method('pfsockopen') |
->will($this->returnValue($this->res)); |
} |
|
if (!in_array('streamSetTimeout', $methods)) { |
$this->handler->expects($this->any()) |
->method('streamSetTimeout') |
->will($this->returnValue(true)); |
} |
|
$this->handler->setFormatter($this->getIdentityFormatter()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php |
@@ -0,0 +1,184 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
class StreamHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWrite() |
{ |
$handle = fopen('php://memory', 'a+'); |
$handler = new StreamHandler($handle); |
$handler->setFormatter($this->getIdentityFormatter()); |
$handler->handle($this->getRecord(Logger::WARNING, 'test')); |
$handler->handle($this->getRecord(Logger::WARNING, 'test2')); |
$handler->handle($this->getRecord(Logger::WARNING, 'test3')); |
fseek($handle, 0); |
$this->assertEquals('testtest2test3', fread($handle, 100)); |
} |
|
/** |
* @covers Monolog\Handler\StreamHandler::close |
*/ |
public function testCloseKeepsExternalHandlersOpen() |
{ |
$handle = fopen('php://memory', 'a+'); |
$handler = new StreamHandler($handle); |
$this->assertTrue(is_resource($handle)); |
$handler->close(); |
$this->assertTrue(is_resource($handle)); |
} |
|
/** |
* @covers Monolog\Handler\StreamHandler::close |
*/ |
public function testClose() |
{ |
$handler = new StreamHandler('php://memory'); |
$handler->handle($this->getRecord(Logger::WARNING, 'test')); |
$streamProp = new \ReflectionProperty('Monolog\Handler\StreamHandler', 'stream'); |
$streamProp->setAccessible(true); |
$handle = $streamProp->getValue($handler); |
|
$this->assertTrue(is_resource($handle)); |
$handler->close(); |
$this->assertFalse(is_resource($handle)); |
} |
|
/** |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteCreatesTheStreamResource() |
{ |
$handler = new StreamHandler('php://memory'); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteLocking() |
{ |
$temp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'monolog_locked_log'; |
$handler = new StreamHandler($temp, Logger::DEBUG, true, null, true); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @expectedException LogicException |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteMissingResource() |
{ |
$handler = new StreamHandler(null); |
$handler->handle($this->getRecord()); |
} |
|
public function invalidArgumentProvider() |
{ |
return array( |
array(1), |
array(array()), |
array(array('bogus://url')), |
); |
} |
|
/** |
* @dataProvider invalidArgumentProvider |
* @expectedException InvalidArgumentException |
* @covers Monolog\Handler\StreamHandler::__construct |
*/ |
public function testWriteInvalidArgument($invalidArgument) |
{ |
$handler = new StreamHandler($invalidArgument); |
} |
|
/** |
* @expectedException UnexpectedValueException |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteInvalidResource() |
{ |
$handler = new StreamHandler('bogus://url'); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @expectedException UnexpectedValueException |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteNonExistingResource() |
{ |
$handler = new StreamHandler('ftp://foo/bar/baz/'.rand(0, 10000)); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteNonExistingPath() |
{ |
$handler = new StreamHandler(sys_get_temp_dir().'/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteNonExistingFileResource() |
{ |
$handler = new StreamHandler('file://'.sys_get_temp_dir().'/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @expectedException Exception |
* @expectedExceptionMessageRegExp /There is no existing directory at/ |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteNonExistingAndNotCreatablePath() |
{ |
if (defined('PHP_WINDOWS_VERSION_BUILD')) { |
$this->markTestSkipped('Permissions checks can not run on windows'); |
} |
$handler = new StreamHandler('/foo/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); |
$handler->handle($this->getRecord()); |
} |
|
/** |
* @expectedException Exception |
* @expectedExceptionMessageRegExp /There is no existing directory at/ |
* @covers Monolog\Handler\StreamHandler::__construct |
* @covers Monolog\Handler\StreamHandler::write |
*/ |
public function testWriteNonExistingAndNotCreatableFileResource() |
{ |
if (defined('PHP_WINDOWS_VERSION_BUILD')) { |
$this->markTestSkipped('Permissions checks can not run on windows'); |
} |
$handler = new StreamHandler('file:///foo/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); |
$handler->handle($this->getRecord()); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php |
@@ -0,0 +1,121 @@ |
<?php |
|
/* |
* 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\TestCase; |
use Monolog\Logger; |
|
class WhatFailureGroupHandlerTest extends TestCase |
{ |
/** |
* @covers Monolog\Handler\WhatFailureGroupHandler::__construct |
* @expectedException InvalidArgumentException |
*/ |
public function testConstructorOnlyTakesHandler() |
{ |
new WhatFailureGroupHandler(array(new TestHandler(), "foo")); |
} |
|
/** |
* @covers Monolog\Handler\WhatFailureGroupHandler::__construct |
* @covers Monolog\Handler\WhatFailureGroupHandler::handle |
*/ |
public function testHandle() |
{ |
$testHandlers = array(new TestHandler(), new TestHandler()); |
$handler = new WhatFailureGroupHandler($testHandlers); |
$handler->handle($this->getRecord(Logger::DEBUG)); |
$handler->handle($this->getRecord(Logger::INFO)); |
foreach ($testHandlers as $test) { |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 2); |
} |
} |
|
/** |
* @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch |
*/ |
public function testHandleBatch() |
{ |
$testHandlers = array(new TestHandler(), new TestHandler()); |
$handler = new WhatFailureGroupHandler($testHandlers); |
$handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); |
foreach ($testHandlers as $test) { |
$this->assertTrue($test->hasDebugRecords()); |
$this->assertTrue($test->hasInfoRecords()); |
$this->assertTrue(count($test->getRecords()) === 2); |
} |
} |
|
/** |
* @covers Monolog\Handler\WhatFailureGroupHandler::isHandling |
*/ |
public function testIsHandling() |
{ |
$testHandlers = array(new TestHandler(Logger::ERROR), new TestHandler(Logger::WARNING)); |
$handler = new WhatFailureGroupHandler($testHandlers); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::ERROR))); |
$this->assertTrue($handler->isHandling($this->getRecord(Logger::WARNING))); |
$this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); |
} |
|
/** |
* @covers Monolog\Handler\WhatFailureGroupHandler::handle |
*/ |
public function testHandleUsesProcessors() |
{ |
$test = new TestHandler(); |
$handler = new WhatFailureGroupHandler(array($test)); |
$handler->pushProcessor(function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
}); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasWarningRecords()); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
} |
|
/** |
* @covers Monolog\Handler\WhatFailureGroupHandler::handle |
*/ |
public function testHandleException() |
{ |
$test = new TestHandler(); |
$exception = new ExceptionTestHandler(); |
$handler = new WhatFailureGroupHandler(array($exception, $test, $exception)); |
$handler->pushProcessor(function ($record) { |
$record['extra']['foo'] = true; |
|
return $record; |
}); |
$handler->handle($this->getRecord(Logger::WARNING)); |
$this->assertTrue($test->hasWarningRecords()); |
$records = $test->getRecords(); |
$this->assertTrue($records[0]['extra']['foo']); |
} |
} |
|
class ExceptionTestHandler extends TestHandler |
{ |
/** |
* {@inheritdoc} |
*/ |
public function handle(array $record) |
{ |
parent::handle($record); |
|
throw new \Exception("ExceptionTestHandler::handle"); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/LoggerTest.php |
@@ -0,0 +1,548 @@ |
<?php |
|
/* |
* 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 Monolog\Processor\WebProcessor; |
use Monolog\Handler\TestHandler; |
|
class LoggerTest extends \PHPUnit_Framework_TestCase |
{ |
/** |
* @covers Monolog\Logger::getName |
*/ |
public function testGetName() |
{ |
$logger = new Logger('foo'); |
$this->assertEquals('foo', $logger->getName()); |
} |
|
/** |
* @covers Monolog\Logger::getLevelName |
*/ |
public function testGetLevelName() |
{ |
$this->assertEquals('ERROR', Logger::getLevelName(Logger::ERROR)); |
} |
|
/** |
* @covers Monolog\Logger::withName |
*/ |
public function testWithName() |
{ |
$first = new Logger('first', array($handler = new TestHandler())); |
$second = $first->withName('second'); |
|
$this->assertSame('first', $first->getName()); |
$this->assertSame('second', $second->getName()); |
$this->assertSame($handler, $second->popHandler()); |
} |
|
/** |
* @covers Monolog\Logger::toMonologLevel |
*/ |
public function testConvertPSR3ToMonologLevel() |
{ |
$this->assertEquals(Logger::toMonologLevel('debug'), 100); |
$this->assertEquals(Logger::toMonologLevel('info'), 200); |
$this->assertEquals(Logger::toMonologLevel('notice'), 250); |
$this->assertEquals(Logger::toMonologLevel('warning'), 300); |
$this->assertEquals(Logger::toMonologLevel('error'), 400); |
$this->assertEquals(Logger::toMonologLevel('critical'), 500); |
$this->assertEquals(Logger::toMonologLevel('alert'), 550); |
$this->assertEquals(Logger::toMonologLevel('emergency'), 600); |
} |
|
/** |
* @covers Monolog\Logger::getLevelName |
* @expectedException InvalidArgumentException |
*/ |
public function testGetLevelNameThrows() |
{ |
Logger::getLevelName(5); |
} |
|
/** |
* @covers Monolog\Logger::__construct |
*/ |
public function testChannel() |
{ |
$logger = new Logger('foo'); |
$handler = new TestHandler; |
$logger->pushHandler($handler); |
$logger->addWarning('test'); |
list($record) = $handler->getRecords(); |
$this->assertEquals('foo', $record['channel']); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testLog() |
{ |
$logger = new Logger(__METHOD__); |
|
$handler = $this->getMock('Monolog\Handler\NullHandler', array('handle')); |
$handler->expects($this->once()) |
->method('handle'); |
$logger->pushHandler($handler); |
|
$this->assertTrue($logger->addWarning('test')); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testLogNotHandled() |
{ |
$logger = new Logger(__METHOD__); |
|
$handler = $this->getMock('Monolog\Handler\NullHandler', array('handle'), array(Logger::ERROR)); |
$handler->expects($this->never()) |
->method('handle'); |
$logger->pushHandler($handler); |
|
$this->assertFalse($logger->addWarning('test')); |
} |
|
public function testHandlersInCtor() |
{ |
$handler1 = new TestHandler; |
$handler2 = new TestHandler; |
$logger = new Logger(__METHOD__, array($handler1, $handler2)); |
|
$this->assertEquals($handler1, $logger->popHandler()); |
$this->assertEquals($handler2, $logger->popHandler()); |
} |
|
public function testProcessorsInCtor() |
{ |
$processor1 = new WebProcessor; |
$processor2 = new WebProcessor; |
$logger = new Logger(__METHOD__, array(), array($processor1, $processor2)); |
|
$this->assertEquals($processor1, $logger->popProcessor()); |
$this->assertEquals($processor2, $logger->popProcessor()); |
} |
|
/** |
* @covers Monolog\Logger::pushHandler |
* @covers Monolog\Logger::popHandler |
* @expectedException LogicException |
*/ |
public function testPushPopHandler() |
{ |
$logger = new Logger(__METHOD__); |
$handler1 = new TestHandler; |
$handler2 = new TestHandler; |
|
$logger->pushHandler($handler1); |
$logger->pushHandler($handler2); |
|
$this->assertEquals($handler2, $logger->popHandler()); |
$this->assertEquals($handler1, $logger->popHandler()); |
$logger->popHandler(); |
} |
|
/** |
* @covers Monolog\Logger::setHandlers |
*/ |
public function testSetHandlers() |
{ |
$logger = new Logger(__METHOD__); |
$handler1 = new TestHandler; |
$handler2 = new TestHandler; |
|
$logger->pushHandler($handler1); |
$logger->setHandlers(array($handler2)); |
|
// handler1 has been removed |
$this->assertEquals(array($handler2), $logger->getHandlers()); |
|
$logger->setHandlers(array( |
"AMapKey" => $handler1, |
"Woop" => $handler2, |
)); |
|
// Keys have been scrubbed |
$this->assertEquals(array($handler1, $handler2), $logger->getHandlers()); |
} |
|
/** |
* @covers Monolog\Logger::pushProcessor |
* @covers Monolog\Logger::popProcessor |
* @expectedException LogicException |
*/ |
public function testPushPopProcessor() |
{ |
$logger = new Logger(__METHOD__); |
$processor1 = new WebProcessor; |
$processor2 = new WebProcessor; |
|
$logger->pushProcessor($processor1); |
$logger->pushProcessor($processor2); |
|
$this->assertEquals($processor2, $logger->popProcessor()); |
$this->assertEquals($processor1, $logger->popProcessor()); |
$logger->popProcessor(); |
} |
|
/** |
* @covers Monolog\Logger::pushProcessor |
* @expectedException InvalidArgumentException |
*/ |
public function testPushProcessorWithNonCallable() |
{ |
$logger = new Logger(__METHOD__); |
|
$logger->pushProcessor(new \stdClass()); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testProcessorsAreExecuted() |
{ |
$logger = new Logger(__METHOD__); |
$handler = new TestHandler; |
$logger->pushHandler($handler); |
$logger->pushProcessor(function ($record) { |
$record['extra']['win'] = true; |
|
return $record; |
}); |
$logger->addError('test'); |
list($record) = $handler->getRecords(); |
$this->assertTrue($record['extra']['win']); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testProcessorsAreCalledOnlyOnce() |
{ |
$logger = new Logger(__METHOD__); |
$handler = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler->expects($this->any()) |
->method('handle') |
->will($this->returnValue(true)) |
; |
$logger->pushHandler($handler); |
|
$processor = $this->getMockBuilder('Monolog\Processor\WebProcessor') |
->disableOriginalConstructor() |
->setMethods(array('__invoke')) |
->getMock() |
; |
$processor->expects($this->once()) |
->method('__invoke') |
->will($this->returnArgument(0)) |
; |
$logger->pushProcessor($processor); |
|
$logger->addError('test'); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testProcessorsNotCalledWhenNotHandled() |
{ |
$logger = new Logger(__METHOD__); |
$handler = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler->expects($this->once()) |
->method('isHandling') |
->will($this->returnValue(false)) |
; |
$logger->pushHandler($handler); |
$that = $this; |
$logger->pushProcessor(function ($record) use ($that) { |
$that->fail('The processor should not be called'); |
}); |
$logger->addAlert('test'); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testHandlersNotCalledBeforeFirstHandling() |
{ |
$logger = new Logger(__METHOD__); |
|
$handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler1->expects($this->never()) |
->method('isHandling') |
->will($this->returnValue(false)) |
; |
$handler1->expects($this->once()) |
->method('handle') |
->will($this->returnValue(false)) |
; |
$logger->pushHandler($handler1); |
|
$handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler2->expects($this->once()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler2->expects($this->once()) |
->method('handle') |
->will($this->returnValue(false)) |
; |
$logger->pushHandler($handler2); |
|
$handler3 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler3->expects($this->once()) |
->method('isHandling') |
->will($this->returnValue(false)) |
; |
$handler3->expects($this->never()) |
->method('handle') |
; |
$logger->pushHandler($handler3); |
|
$logger->debug('test'); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testHandlersNotCalledBeforeFirstHandlingWithAssocArray() |
{ |
$handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler1->expects($this->never()) |
->method('isHandling') |
->will($this->returnValue(false)) |
; |
$handler1->expects($this->once()) |
->method('handle') |
->will($this->returnValue(false)) |
; |
|
$handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler2->expects($this->once()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler2->expects($this->once()) |
->method('handle') |
->will($this->returnValue(false)) |
; |
|
$handler3 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler3->expects($this->once()) |
->method('isHandling') |
->will($this->returnValue(false)) |
; |
$handler3->expects($this->never()) |
->method('handle') |
; |
|
$logger = new Logger(__METHOD__, array('last' => $handler3, 'second' => $handler2, 'first' => $handler1)); |
|
$logger->debug('test'); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testBubblingWhenTheHandlerReturnsFalse() |
{ |
$logger = new Logger(__METHOD__); |
|
$handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler1->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler1->expects($this->once()) |
->method('handle') |
->will($this->returnValue(false)) |
; |
$logger->pushHandler($handler1); |
|
$handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler2->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler2->expects($this->once()) |
->method('handle') |
->will($this->returnValue(false)) |
; |
$logger->pushHandler($handler2); |
|
$logger->debug('test'); |
} |
|
/** |
* @covers Monolog\Logger::addRecord |
*/ |
public function testNotBubblingWhenTheHandlerReturnsTrue() |
{ |
$logger = new Logger(__METHOD__); |
|
$handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler1->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler1->expects($this->never()) |
->method('handle') |
; |
$logger->pushHandler($handler1); |
|
$handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler2->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
$handler2->expects($this->once()) |
->method('handle') |
->will($this->returnValue(true)) |
; |
$logger->pushHandler($handler2); |
|
$logger->debug('test'); |
} |
|
/** |
* @covers Monolog\Logger::isHandling |
*/ |
public function testIsHandling() |
{ |
$logger = new Logger(__METHOD__); |
|
$handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler1->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(false)) |
; |
|
$logger->pushHandler($handler1); |
$this->assertFalse($logger->isHandling(Logger::DEBUG)); |
|
$handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); |
$handler2->expects($this->any()) |
->method('isHandling') |
->will($this->returnValue(true)) |
; |
|
$logger->pushHandler($handler2); |
$this->assertTrue($logger->isHandling(Logger::DEBUG)); |
} |
|
/** |
* @dataProvider logMethodProvider |
* @covers Monolog\Logger::addDebug |
* @covers Monolog\Logger::addInfo |
* @covers Monolog\Logger::addNotice |
* @covers Monolog\Logger::addWarning |
* @covers Monolog\Logger::addError |
* @covers Monolog\Logger::addCritical |
* @covers Monolog\Logger::addAlert |
* @covers Monolog\Logger::addEmergency |
* @covers Monolog\Logger::debug |
* @covers Monolog\Logger::info |
* @covers Monolog\Logger::notice |
* @covers Monolog\Logger::warn |
* @covers Monolog\Logger::err |
* @covers Monolog\Logger::crit |
* @covers Monolog\Logger::alert |
* @covers Monolog\Logger::emerg |
*/ |
public function testLogMethods($method, $expectedLevel) |
{ |
$logger = new Logger('foo'); |
$handler = new TestHandler; |
$logger->pushHandler($handler); |
$logger->{$method}('test'); |
list($record) = $handler->getRecords(); |
$this->assertEquals($expectedLevel, $record['level']); |
} |
|
public function logMethodProvider() |
{ |
return array( |
// monolog methods |
array('addDebug', Logger::DEBUG), |
array('addInfo', Logger::INFO), |
array('addNotice', Logger::NOTICE), |
array('addWarning', Logger::WARNING), |
array('addError', Logger::ERROR), |
array('addCritical', Logger::CRITICAL), |
array('addAlert', Logger::ALERT), |
array('addEmergency', Logger::EMERGENCY), |
|
// ZF/Sf2 compat methods |
array('debug', Logger::DEBUG), |
array('info', Logger::INFO), |
array('notice', Logger::NOTICE), |
array('warn', Logger::WARNING), |
array('err', Logger::ERROR), |
array('crit', Logger::CRITICAL), |
array('alert', Logger::ALERT), |
array('emerg', Logger::EMERGENCY), |
); |
} |
|
/** |
* @dataProvider setTimezoneProvider |
* @covers Monolog\Logger::setTimezone |
*/ |
public function testSetTimezone($tz) |
{ |
Logger::setTimezone($tz); |
$logger = new Logger('foo'); |
$handler = new TestHandler; |
$logger->pushHandler($handler); |
$logger->info('test'); |
list($record) = $handler->getRecords(); |
$this->assertEquals($tz, $record['datetime']->getTimezone()); |
} |
|
public function setTimezoneProvider() |
{ |
return array_map( |
function ($tz) { return array(new \DateTimeZone($tz)); }, |
\DateTimeZone::listIdentifiers() |
); |
} |
|
/** |
* @dataProvider useMicrosecondTimestampsProvider |
* @covers Monolog\Logger::useMicrosecondTimestamps |
* @covers Monolog\Logger::addRecord |
*/ |
public function testUseMicrosecondTimestamps($micro, $assert) |
{ |
$logger = new Logger('foo'); |
$logger->useMicrosecondTimestamps($micro); |
$handler = new TestHandler; |
$logger->pushHandler($handler); |
$logger->info('test'); |
list($record) = $handler->getRecords(); |
$this->{$assert}('000000', $record['datetime']->format('u')); |
} |
|
public function useMicrosecondTimestampsProvider() |
{ |
return array( |
// this has a very small chance of a false negative (1/10^6) |
'with microseconds' => array(true, 'assertNotSame'), |
'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'), |
); |
} |
} |
/vendor/monolog/monolog/tests/Monolog/RegistryTest.php |
@@ -0,0 +1,153 @@ |
<?php |
|
/* |
* 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; |
|
class RegistryTest extends \PHPUnit_Framework_TestCase |
{ |
protected function setUp() |
{ |
Registry::clear(); |
} |
|
/** |
* @dataProvider hasLoggerProvider |
* @covers Monolog\Registry::hasLogger |
*/ |
public function testHasLogger(array $loggersToAdd, array $loggersToCheck, array $expectedResult) |
{ |
foreach ($loggersToAdd as $loggerToAdd) { |
Registry::addLogger($loggerToAdd); |
} |
foreach ($loggersToCheck as $index => $loggerToCheck) { |
$this->assertSame($expectedResult[$index], Registry::hasLogger($loggerToCheck)); |
} |
} |
|
public function hasLoggerProvider() |
{ |
$logger1 = new Logger('test1'); |
$logger2 = new Logger('test2'); |
$logger3 = new Logger('test3'); |
|
return array( |
// only instances |
array( |
array($logger1), |
array($logger1, $logger2), |
array(true, false), |
), |
// only names |
array( |
array($logger1), |
array('test1', 'test2'), |
array(true, false), |
), |
// mixed case |
array( |
array($logger1, $logger2), |
array('test1', $logger2, 'test3', $logger3), |
array(true, true, false, false), |
), |
); |
} |
|
/** |
* @covers Monolog\Registry::clear |
*/ |
public function testClearClears() |
{ |
Registry::addLogger(new Logger('test1'), 'log'); |
Registry::clear(); |
|
$this->setExpectedException('\InvalidArgumentException'); |
Registry::getInstance('log'); |
} |
|
/** |
* @dataProvider removedLoggerProvider |
* @covers Monolog\Registry::addLogger |
* @covers Monolog\Registry::removeLogger |
*/ |
public function testRemovesLogger($loggerToAdd, $remove) |
{ |
Registry::addLogger($loggerToAdd); |
Registry::removeLogger($remove); |
|
$this->setExpectedException('\InvalidArgumentException'); |
Registry::getInstance($loggerToAdd->getName()); |
} |
|
public function removedLoggerProvider() |
{ |
$logger1 = new Logger('test1'); |
|
return array( |
array($logger1, $logger1), |
array($logger1, 'test1'), |
); |
} |
|
/** |
* @covers Monolog\Registry::addLogger |
* @covers Monolog\Registry::getInstance |
* @covers Monolog\Registry::__callStatic |
*/ |
public function testGetsSameLogger() |
{ |
$logger1 = new Logger('test1'); |
$logger2 = new Logger('test2'); |
|
Registry::addLogger($logger1, 'test1'); |
Registry::addLogger($logger2); |
|
$this->assertSame($logger1, Registry::getInstance('test1')); |
$this->assertSame($logger2, Registry::test2()); |
} |
|
/** |
* @expectedException \InvalidArgumentException |
* @covers Monolog\Registry::getInstance |
*/ |
public function testFailsOnNonExistantLogger() |
{ |
Registry::getInstance('test1'); |
} |
|
/** |
* @covers Monolog\Registry::addLogger |
*/ |
public function testReplacesLogger() |
{ |
$log1 = new Logger('test1'); |
$log2 = new Logger('test2'); |
|
Registry::addLogger($log1, 'log'); |
|
Registry::addLogger($log2, 'log', true); |
|
$this->assertSame($log2, Registry::getInstance('log')); |
} |
|
/** |
* @expectedException \InvalidArgumentException |
* @covers Monolog\Registry::addLogger |
*/ |
public function testFailsOnUnspecifiedReplacement() |
{ |
$log1 = new Logger('test1'); |
$log2 = new Logger('test2'); |
|
Registry::addLogger($log1, 'log'); |
|
Registry::addLogger($log2, 'log'); |
} |
} |