Commit 28f8c396 by 朱继来

监控告警

parent e6f0b724
Showing with 1289 additions and 206 deletions
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
"justinrainbow/json-schema": "~1.3", "justinrainbow/json-schema": "~1.3",
"maatwebsite/excel": "~2.0.0", "maatwebsite/excel": "~2.0.0",
"guzzlehttp/guzzle": "^6.3", "guzzlehttp/guzzle": "^6.3",
"predis/predis": "^1.1" "predis/predis": "^1.1",
"redgo/monitor-ding": "0.2"
}, },
"require-dev": { "require-dev": {
"fzaninotto/faker": "~1.4", "fzaninotto/faker": "~1.4",
......
...@@ -155,6 +155,7 @@ return [ ...@@ -155,6 +155,7 @@ return [
App\Providers\AuthServiceProvider::class, App\Providers\AuthServiceProvider::class,
App\Providers\EventServiceProvider::class, App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class, App\Providers\RouteServiceProvider::class,
Redgo\MonitorDing\MonitorDingServiceProvider::class,
], ],
/* /*
...@@ -201,6 +202,7 @@ return [ ...@@ -201,6 +202,7 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class, 'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class, 'View' => Illuminate\Support\Facades\View::class,
'Excel' => Maatwebsite\Excel\Facades\Excel::class, 'Excel' => Maatwebsite\Excel\Facades\Excel::class,
'MonitorDing' => Redgo\MonitorDing\Facades\MonitorDing::class,
], ],
......
<?php
return [
// 是否开启报错写入
'enabled' => true,
// curl证书验证, 线下环境不用开启
'curl_verify' => true,
// webhook的值
'webhook' => 'https://oapi.dingtalk.com/robot/send?access_token=a1ecbb3b7dc17660f0d8b833d79a16578b682e28b673dd024c4125ca974abadd',
];
\ No newline at end of file
...@@ -377,11 +377,11 @@ class ClassLoader ...@@ -377,11 +377,11 @@ class ClassLoader
$subPath = $class; $subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) { while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos); $subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\'; $search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) { if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) { foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) { $length = $this->prefixLengthsPsr4[$first][$search];
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file; return $file;
} }
} }
......
This diff could not be displayed because it is too large.
...@@ -11,11 +11,11 @@ return array( ...@@ -11,11 +11,11 @@ return array(
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'e7223560d890eab89cda23685e711e2c' => $vendorDir . '/psy/psysh/src/Psy/functions.php', 'e7223560d890eab89cda23685e711e2c' => $vendorDir . '/psy/psysh/src/Psy/functions.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php', 'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php', '58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
); );
...@@ -27,6 +27,7 @@ return array( ...@@ -27,6 +27,7 @@ return array(
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), 'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'), 'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'SuperClosure\\' => array($vendorDir . '/jeremeamia/SuperClosure/src'), 'SuperClosure\\' => array($vendorDir . '/jeremeamia/SuperClosure/src'),
'Redgo\\MonitorDing\\' => array($vendorDir . '/redgo/monitor-ding/src'),
'Psy\\' => array($vendorDir . '/psy/psysh/src/Psy'), 'Psy\\' => array($vendorDir . '/psy/psysh/src/Psy'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
......
This diff could not be displayed because it is too large.
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
} }
], ],
"require": { "require": {
"php": ">=5.5.9", "php": ">=5.5.9"
"ext-fileinfo": "*"
}, },
"require-dev": { "require-dev": {
"ext-fileinfo": "*",
"phpspec/phpspec": "^3.4", "phpspec/phpspec": "^3.4",
"phpunit/phpunit": "^5.7.10" "phpunit/phpunit": "^5.7.10"
}, },
......
...@@ -8,12 +8,12 @@ The `Handler` type and associated calls will be removed in version 2.0. ...@@ -8,12 +8,12 @@ The `Handler` type and associated calls will be removed in version 2.0.
### Upgrade path ### Upgrade path
You should create your own implementation for handling OOP usage, You should either create your own implementation for handling OOP usage,
but it's recommended to move away from using an OOP-style wrapper entirely. but it's recommended to move away from using an OOP-style wrapper entirely.
The reason for this is that it's too easy for implementation details (for The reason for this is that it's too easy for implementation details (for
your application this is Flysystem) to leak into the application. The most your application this is Flysystem) to leak into the application. The most
important part for Flysystem is that it improves portability and creates a important part for Flysystem is that it improved portability and creates a
solid boundary between your application core and the infrastructure you use. solid boundary between your application core and the infrastructure you use.
The OOP-style handling breaks this principle, therefore I want to stop The OOP-style handling breaks this principle, therefor I want to stop
promoting it. promoting it.
\ No newline at end of file
### 1.24.0 (2018-11-05)
* Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors
* Added a `ProcessorInterface` as an optional way to label a class as being a processor (mostly useful for autowiring dependency containers)
* Added a way to log signals being received using Monolog\SignalHandler
* Added ability to customize error handling at the Logger level using Logger::setExceptionHandler
* Added InsightOpsHandler to migrate users of the LogEntriesHandler
* Added protection to NormalizerHandler against circular and very deep structures, it now stops normalizing at a depth of 9
* Added capture of stack traces to ErrorHandler when logging PHP errors
* Added RavenHandler support for a `contexts` context or extra key to forward that to Sentry's contexts
* Added forwarding of context info to FluentdFormatter
* Added SocketHandler::setChunkSize to override the default chunk size in case you must send large log lines to rsyslog for example
* Added ability to extend/override BrowserConsoleHandler
* Added SlackWebhookHandler::getWebhookUrl and SlackHandler::getToken to enable class extensibility
* Added SwiftMailerHandler::getSubjectFormatter to enable class extensibility
* Dropped official support for HHVM in test builds
* Fixed normalization of exception traces when call_user_func is used to avoid serializing objects and the data they contain
* Fixed naming of fields in Slack handler, all field names are now capitalized in all cases
* Fixed HipChatHandler bug where slack dropped messages randomly
* Fixed normalization of objects in Slack handlers
* Fixed support for PHP7's Throwable in NewRelicHandler
* Fixed race bug when StreamHandler sometimes incorrectly reported it failed to create a directory
* Fixed table row styling issues in HtmlFormatter
* Fixed RavenHandler dropping the message when logging exception
* Fixed WhatFailureGroupHandler skipping processors when using handleBatch
and implement it where possible
* Fixed display of anonymous class names
### 1.23.0 (2017-06-19) ### 1.23.0 (2017-06-19)
* Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) [![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) [![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog)
[![Reference Status](https://www.versioneye.com/php/monolog:monolog/reference_badge.svg)](https://www.versioneye.com/php/monolog:monolog/references)
Monolog sends your logs to files, sockets, inboxes, databases and various Monolog sends your logs to files, sockets, inboxes, databases and various
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account. - _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account.
- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. - _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server.
- _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account. - _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account.
- _InsightOpsHandler_: Logs records to a [InsightOps](https://www.rapid7.com/products/insightops/) account.
### Logging in development ### Logging in development
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
help in some older codebases or for ease of use. help in some older codebases or for ease of use.
- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register - _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register
a Logger instance as an exception handler, error handler or fatal error handler. a Logger instance as an exception handler, error handler or fatal error handler.
- _SignalHandler_: The `Monolog\SignalHandler` class allows you to easily register
a Logger instance as a POSIX signal handler.
- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log - _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log
level is reached. level is reached.
- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain - _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain
......
...@@ -14,6 +14,7 @@ namespace Monolog; ...@@ -14,6 +14,7 @@ namespace Monolog;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Monolog\Handler\AbstractHandler; use Monolog\Handler\AbstractHandler;
use Monolog\Registry;
/** /**
* Monolog error handler * Monolog error handler
...@@ -38,6 +39,7 @@ class ErrorHandler ...@@ -38,6 +39,7 @@ class ErrorHandler
private $hasFatalErrorHandler; private $hasFatalErrorHandler;
private $fatalLevel; private $fatalLevel;
private $reservedMemory; private $reservedMemory;
private $lastFatalTrace;
private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR);
public function __construct(LoggerInterface $logger) public function __construct(LoggerInterface $logger)
...@@ -132,7 +134,7 @@ class ErrorHandler ...@@ -132,7 +134,7 @@ class ErrorHandler
{ {
$this->logger->log( $this->logger->log(
$this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, $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()), sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()),
array('exception' => $e) array('exception' => $e)
); );
...@@ -156,6 +158,13 @@ class ErrorHandler ...@@ -156,6 +158,13 @@ class ErrorHandler
if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) {
$level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; $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)); $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line));
} else {
// http://php.net/manual/en/function.debug-backtrace.php
// As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added.
// Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'.
$trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS);
array_shift($trace); // Exclude handleError from trace
$this->lastFatalTrace = $trace;
} }
if ($this->previousErrorHandler === true) { if ($this->previousErrorHandler === true) {
...@@ -177,7 +186,7 @@ class ErrorHandler ...@@ -177,7 +186,7 @@ class ErrorHandler
$this->logger->log( $this->logger->log(
$this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace)
); );
if ($this->logger instanceof Logger) { if ($this->logger instanceof Logger) {
......
...@@ -62,6 +62,7 @@ class FluentdFormatter implements FormatterInterface ...@@ -62,6 +62,7 @@ class FluentdFormatter implements FormatterInterface
$message = array( $message = array(
'message' => $record['message'], 'message' => $record['message'],
'context' => $record['context'],
'extra' => $record['extra'], 'extra' => $record['extra'],
); );
......
...@@ -58,7 +58,7 @@ class HtmlFormatter extends NormalizerFormatter ...@@ -58,7 +58,7 @@ class HtmlFormatter extends NormalizerFormatter
$td = '<pre>'.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'</pre>'; $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>"; return "<tr style=\"padding: 4px;text-align: left;\">\n<th style=\"vertical-align: top;background: #ccc;color: #000\" width=\"100\">$th:</th>\n<td style=\"padding: 4px;text-align: left;vertical-align: top;background: #eee;color: #000\">".$td."</td>\n</tr>";
} }
/** /**
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Exception; use Exception;
use Monolog\Utils;
use Throwable; use Throwable;
/** /**
...@@ -138,18 +139,23 @@ class JsonFormatter extends NormalizerFormatter ...@@ -138,18 +139,23 @@ class JsonFormatter extends NormalizerFormatter
* *
* @return mixed * @return mixed
*/ */
protected function normalize($data) protected function normalize($data, $depth = 0)
{ {
if ($depth > 9) {
return 'Over 9 levels deep, aborting normalization';
}
if (is_array($data) || $data instanceof \Traversable) { if (is_array($data) || $data instanceof \Traversable) {
$normalized = array(); $normalized = array();
$count = 1; $count = 1;
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
if ($count++ >= 1000) { if ($count++ > 1000) {
$normalized['...'] = 'Over 1000 items, aborting normalization'; $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
break; break;
} }
$normalized[$key] = $this->normalize($value);
$normalized[$key] = $this->normalize($value, $depth+1);
} }
return $normalized; return $normalized;
...@@ -174,11 +180,11 @@ class JsonFormatter extends NormalizerFormatter ...@@ -174,11 +180,11 @@ class JsonFormatter extends NormalizerFormatter
{ {
// TODO 2.0 only check for Throwable // TODO 2.0 only check for Throwable
if (!$e instanceof Exception && !$e instanceof Throwable) { if (!$e instanceof Exception && !$e instanceof Throwable) {
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
} }
$data = array( $data = array(
'class' => get_class($e), 'class' => Utils::getClass($e),
'message' => $e->getMessage(), 'message' => $e->getMessage(),
'code' => $e->getCode(), 'code' => $e->getCode(),
'file' => $e->getFile().':'.$e->getLine(), 'file' => $e->getFile().':'.$e->getLine(),
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Utils;
/** /**
* Formats incoming records into a one-line string * Formats incoming records into a one-line string
* *
...@@ -129,17 +131,17 @@ class LineFormatter extends NormalizerFormatter ...@@ -129,17 +131,17 @@ class LineFormatter extends NormalizerFormatter
{ {
// TODO 2.0 only check for Throwable // TODO 2.0 only check for Throwable
if (!$e instanceof \Exception && !$e instanceof \Throwable) { if (!$e instanceof \Exception && !$e instanceof \Throwable) {
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
} }
$previousText = ''; $previousText = '';
if ($previous = $e->getPrevious()) { if ($previous = $e->getPrevious()) {
do { do {
$previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); $previousText .= ', '.Utils::getClass($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
} while ($previous = $previous->getPrevious()); } while ($previous = $previous->getPrevious());
} }
$str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')'; $str = '[object] ('.Utils::getClass($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
if ($this->includeStacktraces) { if ($this->includeStacktraces) {
$str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n"; $str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n";
} }
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Monolog\Utils;
/** /**
* Formats a record for use with the MongoDBHandler. * Formats a record for use with the MongoDBHandler.
* *
...@@ -75,7 +77,7 @@ class MongoDBFormatter implements FormatterInterface ...@@ -75,7 +77,7 @@ class MongoDBFormatter implements FormatterInterface
protected function formatObject($value, $nestingLevel) protected function formatObject($value, $nestingLevel)
{ {
$objectVars = get_object_vars($value); $objectVars = get_object_vars($value);
$objectVars['class'] = get_class($value); $objectVars['class'] = Utils::getClass($value);
return $this->formatArray($objectVars, $nestingLevel); return $this->formatArray($objectVars, $nestingLevel);
} }
...@@ -83,7 +85,7 @@ class MongoDBFormatter implements FormatterInterface ...@@ -83,7 +85,7 @@ class MongoDBFormatter implements FormatterInterface
protected function formatException(\Exception $exception, $nestingLevel) protected function formatException(\Exception $exception, $nestingLevel)
{ {
$formattedException = array( $formattedException = array(
'class' => get_class($exception), 'class' => Utils::getClass($exception),
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'code' => $exception->getCode(), 'code' => $exception->getCode(),
'file' => $exception->getFile() . ':' . $exception->getLine(), 'file' => $exception->getFile() . ':' . $exception->getLine(),
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
namespace Monolog\Formatter; namespace Monolog\Formatter;
use Exception; use Exception;
use Monolog\Utils;
/** /**
* Normalizes incoming records to remove objects/resources so it's easier to dump to various targets * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets
...@@ -55,8 +56,12 @@ class NormalizerFormatter implements FormatterInterface ...@@ -55,8 +56,12 @@ class NormalizerFormatter implements FormatterInterface
return $records; return $records;
} }
protected function normalize($data) protected function normalize($data, $depth = 0)
{ {
if ($depth > 9) {
return 'Over 9 levels deep, aborting normalization';
}
if (null === $data || is_scalar($data)) { if (null === $data || is_scalar($data)) {
if (is_float($data)) { if (is_float($data)) {
if (is_infinite($data)) { if (is_infinite($data)) {
...@@ -75,11 +80,12 @@ class NormalizerFormatter implements FormatterInterface ...@@ -75,11 +80,12 @@ class NormalizerFormatter implements FormatterInterface
$count = 1; $count = 1;
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
if ($count++ >= 1000) { if ($count++ > 1000) {
$normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
break; break;
} }
$normalized[$key] = $this->normalize($value);
$normalized[$key] = $this->normalize($value, $depth+1);
} }
return $normalized; return $normalized;
...@@ -103,7 +109,7 @@ class NormalizerFormatter implements FormatterInterface ...@@ -103,7 +109,7 @@ class NormalizerFormatter implements FormatterInterface
$value = $this->toJson($data, true); $value = $this->toJson($data, true);
} }
return sprintf("[object] (%s: %s)", get_class($data), $value); return sprintf("[object] (%s: %s)", Utils::getClass($data), $value);
} }
if (is_resource($data)) { if (is_resource($data)) {
...@@ -117,11 +123,11 @@ class NormalizerFormatter implements FormatterInterface ...@@ -117,11 +123,11 @@ class NormalizerFormatter implements FormatterInterface
{ {
// TODO 2.0 only check for Throwable // TODO 2.0 only check for Throwable
if (!$e instanceof Exception && !$e instanceof \Throwable) { if (!$e instanceof Exception && !$e instanceof \Throwable) {
throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
} }
$data = array( $data = array(
'class' => get_class($e), 'class' => Utils::getClass($e),
'message' => $e->getMessage(), 'message' => $e->getMessage(),
'code' => $e->getCode(), 'code' => $e->getCode(),
'file' => $e->getFile().':'.$e->getLine(), 'file' => $e->getFile().':'.$e->getLine(),
...@@ -146,9 +152,20 @@ class NormalizerFormatter implements FormatterInterface ...@@ -146,9 +152,20 @@ class NormalizerFormatter implements FormatterInterface
if (isset($frame['file'])) { if (isset($frame['file'])) {
$data['trace'][] = $frame['file'].':'.$frame['line']; $data['trace'][] = $frame['file'].':'.$frame['line'];
} elseif (isset($frame['function']) && $frame['function'] === '{closure}') { } elseif (isset($frame['function']) && $frame['function'] === '{closure}') {
// We should again normalize the frames, because it might contain invalid items // Simplify closures handling
$data['trace'][] = $frame['function']; $data['trace'][] = $frame['function'];
} else { } else {
if (isset($frame['args'])) {
// Make sure that objects present as arguments are not serialized nicely but rather only
// as a class name to avoid any unexpected leak of sensitive information
$frame['args'] = array_map(function ($arg) {
if (is_object($arg) && !($arg instanceof \DateTime || $arg instanceof \DateTimeInterface)) {
return sprintf("[object] (%s)", Utils::getClass($arg));
}
return $arg;
}, $frame['args']);
}
// We should again normalize the frames, because it might contain invalid items // We should again normalize the frames, because it might contain invalid items
$data['trace'][] = $this->toJson($this->normalize($frame), true); $data['trace'][] = $this->toJson($this->normalize($frame), true);
} }
......
...@@ -102,12 +102,12 @@ class WildfireFormatter extends NormalizerFormatter ...@@ -102,12 +102,12 @@ class WildfireFormatter extends NormalizerFormatter
throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter');
} }
protected function normalize($data) protected function normalize($data, $depth = 0)
{ {
if (is_object($data) && !$data instanceof \DateTime) { if (is_object($data) && !$data instanceof \DateTime) {
return $data; return $data;
} }
return parent::normalize($data); return parent::normalize($data, $depth);
} }
} }
...@@ -11,16 +11,17 @@ ...@@ -11,16 +11,17 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Logger;
use Monolog\ResettableInterface;
/** /**
* Base Handler class providing the Handler structure * Base Handler class providing the Handler structure
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
abstract class AbstractHandler implements HandlerInterface abstract class AbstractHandler implements HandlerInterface, ResettableInterface
{ {
protected $level = Logger::DEBUG; protected $level = Logger::DEBUG;
protected $bubble = true; protected $bubble = true;
...@@ -32,8 +33,8 @@ abstract class AbstractHandler implements HandlerInterface ...@@ -32,8 +33,8 @@ abstract class AbstractHandler implements HandlerInterface
protected $processors = array(); protected $processors = array();
/** /**
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($level = Logger::DEBUG, $bubble = true) public function __construct($level = Logger::DEBUG, $bubble = true)
{ {
...@@ -141,8 +142,8 @@ abstract class AbstractHandler implements HandlerInterface ...@@ -141,8 +142,8 @@ abstract class AbstractHandler implements HandlerInterface
/** /**
* Sets the bubbling behavior. * Sets the bubbling behavior.
* *
* @param Boolean $bubble true means that this handler allows bubbling. * @param bool $bubble true means that this handler allows bubbling.
* false means that bubbling is not permitted. * false means that bubbling is not permitted.
* @return self * @return self
*/ */
public function setBubble($bubble) public function setBubble($bubble)
...@@ -155,8 +156,8 @@ abstract class AbstractHandler implements HandlerInterface ...@@ -155,8 +156,8 @@ abstract class AbstractHandler implements HandlerInterface
/** /**
* Gets the bubbling behavior. * Gets the bubbling behavior.
* *
* @return Boolean true means that this handler allows bubbling. * @return bool true means that this handler allows bubbling.
* false means that bubbling is not permitted. * false means that bubbling is not permitted.
*/ */
public function getBubble() public function getBubble()
{ {
...@@ -174,6 +175,15 @@ abstract class AbstractHandler implements HandlerInterface ...@@ -174,6 +175,15 @@ abstract class AbstractHandler implements HandlerInterface
} }
} }
public function reset()
{
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
/** /**
* Gets the default formatter. * Gets the default formatter.
* *
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\ResettableInterface;
/** /**
* Base Handler class providing the Handler structure * Base Handler class providing the Handler structure
* *
......
...@@ -53,9 +53,9 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler ...@@ -53,9 +53,9 @@ abstract class AbstractSyslogHandler extends AbstractProcessingHandler
); );
/** /**
* @param mixed $facility * @param mixed $facility
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true)
{ {
......
...@@ -43,11 +43,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -43,11 +43,11 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
protected function write(array $record) protected function write(array $record)
{ {
// Accumulate records // Accumulate records
self::$records[] = $record; static::$records[] = $record;
// Register shutdown handler if not already done // Register shutdown handler if not already done
if (!self::$initialized) { if (!static::$initialized) {
self::$initialized = true; static::$initialized = true;
$this->registerShutdownFunction(); $this->registerShutdownFunction();
} }
} }
...@@ -58,27 +58,37 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -58,27 +58,37 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
*/ */
public static function send() public static function send()
{ {
$format = self::getResponseFormat(); $format = static::getResponseFormat();
if ($format === 'unknown') { if ($format === 'unknown') {
return; return;
} }
if (count(self::$records)) { if (count(static::$records)) {
if ($format === 'html') { if ($format === 'html') {
self::writeOutput('<script>' . self::generateScript() . '</script>'); static::writeOutput('<script>' . static::generateScript() . '</script>');
} elseif ($format === 'js') { } elseif ($format === 'js') {
self::writeOutput(self::generateScript()); static::writeOutput(static::generateScript());
} }
self::reset(); static::resetStatic();
} }
} }
public function close()
{
self::resetStatic();
}
public function reset()
{
self::resetStatic();
}
/** /**
* Forget all logged records * Forget all logged records
*/ */
public static function reset() public static function resetStatic()
{ {
self::$records = array(); static::$records = array();
} }
/** /**
...@@ -133,18 +143,18 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -133,18 +143,18 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
private static function generateScript() private static function generateScript()
{ {
$script = array(); $script = array();
foreach (self::$records as $record) { foreach (static::$records as $record) {
$context = self::dump('Context', $record['context']); $context = static::dump('Context', $record['context']);
$extra = self::dump('Extra', $record['extra']); $extra = static::dump('Extra', $record['extra']);
if (empty($context) && empty($extra)) { if (empty($context) && empty($extra)) {
$script[] = self::call_array('log', self::handleStyles($record['formatted'])); $script[] = static::call_array('log', static::handleStyles($record['formatted']));
} else { } else {
$script = array_merge($script, $script = array_merge($script,
array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))),
$context, $context,
$extra, $extra,
array(self::call('groupEnd')) array(static::call('groupEnd'))
); );
} }
} }
...@@ -154,19 +164,19 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -154,19 +164,19 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
private static function handleStyles($formatted) private static function handleStyles($formatted)
{ {
$args = array(self::quote('font-weight: normal')); $args = array(static::quote('font-weight: normal'));
$format = '%c' . $formatted; $format = '%c' . $formatted;
preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
foreach (array_reverse($matches) as $match) { foreach (array_reverse($matches) as $match) {
$args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0]));
$args[] = '"font-weight: normal"'; $args[] = '"font-weight: normal"';
$pos = $match[0][1]; $pos = $match[0][1];
$format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0]));
} }
array_unshift($args, self::quote($format)); array_unshift($args, static::quote($format));
return $args; return $args;
} }
...@@ -198,13 +208,13 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -198,13 +208,13 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
if (empty($dict)) { if (empty($dict)) {
return $script; return $script;
} }
$script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title));
foreach ($dict as $key => $value) { foreach ($dict as $key => $value) {
$value = json_encode($value); $value = json_encode($value);
if (empty($value)) { if (empty($value)) {
$value = self::quote(''); $value = static::quote('');
} }
$script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); $script[] = static::call('log', static::quote('%s: %o'), static::quote($key), $value);
} }
return $script; return $script;
...@@ -220,7 +230,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler ...@@ -220,7 +230,7 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
$args = func_get_args(); $args = func_get_args();
$method = array_shift($args); $method = array_shift($args);
return self::call_array($method, $args); return static::call_array($method, $args);
} }
private static function call_array($method, array $args) private static function call_array($method, array $args)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface;
/** /**
* Buffers all records until closing the handler and then pass them as batch. * Buffers all records until closing the handler and then pass them as batch.
...@@ -34,8 +35,8 @@ class BufferHandler extends AbstractHandler ...@@ -34,8 +35,8 @@ class BufferHandler extends AbstractHandler
* @param HandlerInterface $handler Handler. * @param HandlerInterface $handler Handler.
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
*/ */
public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false)
{ {
...@@ -114,4 +115,15 @@ class BufferHandler extends AbstractHandler ...@@ -114,4 +115,15 @@ class BufferHandler extends AbstractHandler
$this->bufferSize = 0; $this->bufferSize = 0;
$this->buffer = array(); $this->buffer = array();
} }
public function reset()
{
$this->flush();
parent::reset();
if ($this->handler instanceof ResettableInterface) {
$this->handler->reset();
}
}
} }
...@@ -32,7 +32,7 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -32,7 +32,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
* Header name * Header name
*/ */
const HEADER_NAME = 'X-ChromeLogger-Data'; const HEADER_NAME = 'X-ChromeLogger-Data';
/** /**
* Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+)
*/ */
...@@ -45,7 +45,7 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -45,7 +45,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
* *
* Chrome limits the headers to 256KB, so when we sent 240KB we stop sending * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending
* *
* @var Boolean * @var bool
*/ */
protected static $overflowed = false; protected static $overflowed = false;
...@@ -58,8 +58,8 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -58,8 +58,8 @@ class ChromePHPHandler extends AbstractProcessingHandler
protected static $sendHeaders = true; protected static $sendHeaders = true;
/** /**
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($level = Logger::DEBUG, $bubble = true) public function __construct($level = Logger::DEBUG, $bubble = true)
{ {
...@@ -174,7 +174,7 @@ class ChromePHPHandler extends AbstractProcessingHandler ...@@ -174,7 +174,7 @@ class ChromePHPHandler extends AbstractProcessingHandler
/** /**
* Verifies if the headers are accepted by the current user agent * Verifies if the headers are accepted by the current user agent
* *
* @return Boolean * @return bool
*/ */
protected function headersAccepted() protected function headersAccepted()
{ {
......
...@@ -60,7 +60,7 @@ class DeduplicationHandler extends BufferHandler ...@@ -60,7 +60,7 @@ class DeduplicationHandler extends BufferHandler
* @param string $deduplicationStore The file/path where the deduplication log should be kept * @param string $deduplicationStore The file/path where the deduplication log should be kept
* @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes * @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes
* @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true) public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true)
{ {
......
...@@ -46,10 +46,10 @@ class ElasticSearchHandler extends AbstractProcessingHandler ...@@ -46,10 +46,10 @@ class ElasticSearchHandler extends AbstractProcessingHandler
protected $options = array(); protected $options = array();
/** /**
* @param Client $client Elastica Client object * @param Client $client Elastica Client object
* @param array $options Handler configuration * @param array $options Handler configuration
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true)
{ {
......
...@@ -28,10 +28,10 @@ class ErrorLogHandler extends AbstractProcessingHandler ...@@ -28,10 +28,10 @@ class ErrorLogHandler extends AbstractProcessingHandler
protected $expandNewlines; protected $expandNewlines;
/** /**
* @param int $messageType Says where the error should go. * @param int $messageType Says where the error should go.
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries
*/ */
public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false) public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false)
{ {
......
...@@ -40,7 +40,7 @@ class FilterHandler extends AbstractHandler ...@@ -40,7 +40,7 @@ class FilterHandler extends AbstractHandler
/** /**
* Whether the messages that are handled can bubble up the stack or not * Whether the messages that are handled can bubble up the stack or not
* *
* @var Boolean * @var bool
*/ */
protected $bubble; protected $bubble;
...@@ -48,7 +48,7 @@ class FilterHandler extends AbstractHandler ...@@ -48,7 +48,7 @@ class FilterHandler extends AbstractHandler
* @param callable|HandlerInterface $handler Handler or factory callable($record, $this). * @param callable|HandlerInterface $handler Handler or factory callable($record, $this).
* @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided
* @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true) public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true)
{ {
......
...@@ -22,7 +22,7 @@ interface ActivationStrategyInterface ...@@ -22,7 +22,7 @@ interface ActivationStrategyInterface
* Returns whether the given record activates the handler. * Returns whether the given record activates the handler.
* *
* @param array $record * @param array $record
* @return Boolean * @return bool
*/ */
public function isHandlerActivated(array $record); public function isHandlerActivated(array $record);
} }
...@@ -14,6 +14,7 @@ namespace Monolog\Handler; ...@@ -14,6 +14,7 @@ namespace Monolog\Handler;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface;
/** /**
* Buffers all records until a certain level is reached * Buffers all records until a certain level is reached
...@@ -41,8 +42,8 @@ class FingersCrossedHandler extends AbstractHandler ...@@ -41,8 +42,8 @@ class FingersCrossedHandler extends AbstractHandler
* @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler).
* @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action
* @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true)
* @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered
*/ */
public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null)
...@@ -130,24 +131,18 @@ class FingersCrossedHandler extends AbstractHandler ...@@ -130,24 +131,18 @@ class FingersCrossedHandler extends AbstractHandler
*/ */
public function close() public function close()
{ {
if (null !== $this->passthruLevel) { $this->flushBuffer();
$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() public function reset()
{ {
$this->buffering = true; $this->flushBuffer();
parent::reset();
if ($this->handler instanceof ResettableInterface) {
$this->handler->reset();
}
} }
/** /**
...@@ -160,4 +155,23 @@ class FingersCrossedHandler extends AbstractHandler ...@@ -160,4 +155,23 @@ class FingersCrossedHandler extends AbstractHandler
$this->buffer = array(); $this->buffer = array();
$this->reset(); $this->reset();
} }
/**
* Resets the state of the handler. Stops forwarding records to the wrapped handler.
*/
private function flushBuffer()
{
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();
$this->buffering = true;
}
} }
...@@ -158,7 +158,7 @@ class FirePHPHandler extends AbstractProcessingHandler ...@@ -158,7 +158,7 @@ class FirePHPHandler extends AbstractProcessingHandler
/** /**
* Verifies if the headers are accepted by the current user agent * Verifies if the headers are accepted by the current user agent
* *
* @return Boolean * @return bool
*/ */
protected function headersAccepted() protected function headersAccepted()
{ {
......
...@@ -50,14 +50,6 @@ class GelfHandler extends AbstractProcessingHandler ...@@ -50,14 +50,6 @@ class GelfHandler extends AbstractProcessingHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function close()
{
$this->publisher = null;
}
/**
* {@inheritdoc}
*/
protected function write(array $record) protected function write(array $record)
{ {
$this->publisher->publish($record['formatted']); $this->publisher->publish($record['formatted']);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\ResettableInterface;
/** /**
* Forwards records to multiple handlers * Forwards records to multiple handlers
...@@ -23,8 +24,8 @@ class GroupHandler extends AbstractHandler ...@@ -23,8 +24,8 @@ class GroupHandler extends AbstractHandler
protected $handlers; protected $handlers;
/** /**
* @param array $handlers Array of Handlers. * @param array $handlers Array of Handlers.
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct(array $handlers, $bubble = true) public function __construct(array $handlers, $bubble = true)
{ {
...@@ -90,6 +91,17 @@ class GroupHandler extends AbstractHandler ...@@ -90,6 +91,17 @@ class GroupHandler extends AbstractHandler
} }
} }
public function reset()
{
parent::reset();
foreach ($this->handlers as $handler) {
if ($handler instanceof ResettableInterface) {
$handler->reset();
}
}
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
...@@ -31,7 +31,7 @@ interface HandlerInterface ...@@ -31,7 +31,7 @@ interface HandlerInterface
* *
* @param array $record Partial log record containing only a level key * @param array $record Partial log record containing only a level key
* *
* @return Boolean * @return bool
*/ */
public function isHandling(array $record); public function isHandling(array $record);
...@@ -46,7 +46,7 @@ interface HandlerInterface ...@@ -46,7 +46,7 @@ interface HandlerInterface
* calling further handlers in the stack with a given log record. * calling further handlers in the stack with a given log record.
* *
* @param array $record The record to handle * @param array $record The record to handle
* @return Boolean true means that this handler handled the record, and that bubbling is not permitted. * @return bool true means that this handler handled the record, and that bubbling is not permitted.
* false means the record was either not processed or that this handler allows bubbling. * false means the record was either not processed or that this handler allows bubbling.
*/ */
public function handle(array $record); public function handle(array $record);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
/** /**
...@@ -30,7 +31,7 @@ use Monolog\Formatter\FormatterInterface; ...@@ -30,7 +31,7 @@ use Monolog\Formatter\FormatterInterface;
* *
* @author Alexey Karapetov <alexey@karapetov.com> * @author Alexey Karapetov <alexey@karapetov.com>
*/ */
class HandlerWrapper implements HandlerInterface class HandlerWrapper implements HandlerInterface, ResettableInterface
{ {
/** /**
* @var HandlerInterface * @var HandlerInterface
...@@ -105,4 +106,11 @@ class HandlerWrapper implements HandlerInterface ...@@ -105,4 +106,11 @@ class HandlerWrapper implements HandlerInterface
{ {
return $this->handler->getFormatter(); return $this->handler->getFormatter();
} }
public function reset()
{
if ($this->handler instanceof ResettableInterface) {
return $this->handler->reset();
}
}
} }
...@@ -219,6 +219,21 @@ class HipChatHandler extends SocketHandler ...@@ -219,6 +219,21 @@ class HipChatHandler extends SocketHandler
protected function write(array $record) protected function write(array $record)
{ {
parent::write($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, hipchat sometimes
* drops the request entirely.
*/
protected function finalizeWrite()
{
$res = $this->getResource();
if (is_resource($res)) {
@fread($res, 2048);
}
$this->closeSocket(); $this->closeSocket();
} }
......
...@@ -30,10 +30,10 @@ class IFTTTHandler extends AbstractProcessingHandler ...@@ -30,10 +30,10 @@ class IFTTTHandler extends AbstractProcessingHandler
private $secretKey; private $secretKey;
/** /**
* @param string $eventName The name of the IFTTT Maker event that should be triggered * @param string $eventName The name of the IFTTT Maker event that should be triggered
* @param string $secretKey A valid IFTTT secret key * @param string $secretKey A valid IFTTT secret key
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true) public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true)
{ {
......
<?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;
/**
* Inspired on LogEntriesHandler.
*
* @author Robert Kaufmann III <rok3@rok3.me>
* @author Gabriel Machado <gabriel.ms1@hotmail.com>
*/
class InsightOpsHandler extends SocketHandler
{
/**
* @var string
*/
protected $logToken;
/**
* @param string $token Log token supplied by InsightOps
* @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'.
* @param bool $useSSL Whether or not SSL encryption should be used
* @param int $level The minimum logging level to trigger this handler
* @param bool $bubble Whether or not messages that are handled should bubble up the stack.
*
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/
public function __construct($token, $region = 'us', $useSSL = true, $level = Logger::DEBUG, $bubble = true)
{
if ($useSSL && !extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler');
}
$endpoint = $useSSL
? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443'
: $region . '.data.logs.insight.rapid7.com:80';
parent::__construct($endpoint, $level, $bubble);
$this->logToken = $token;
}
/**
* {@inheritdoc}
*
* @param array $record
* @return string
*/
protected function generateDataStream($record)
{
return $this->logToken . ' ' . $record['formatted'];
}
}
...@@ -31,13 +31,13 @@ class LogEntriesHandler extends SocketHandler ...@@ -31,13 +31,13 @@ class LogEntriesHandler extends SocketHandler
* *
* @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing
*/ */
public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true) public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true, $host = 'data.logentries.com')
{ {
if ($useSSL && !extension_loaded('openssl')) { if ($useSSL && !extension_loaded('openssl')) {
throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler');
} }
$endpoint = $useSSL ? 'ssl://data.logentries.com:443' : 'data.logentries.com:80'; $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80';
parent::__construct($endpoint, $level, $bubble); parent::__construct($endpoint, $level, $bubble);
$this->logToken = $token; $this->logToken = $token;
} }
......
...@@ -27,7 +27,7 @@ class MandrillHandler extends MailHandler ...@@ -27,7 +27,7 @@ class MandrillHandler extends MailHandler
* @param string $apiKey A valid Mandrill API key * @param string $apiKey A valid Mandrill API key
* @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true) public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true)
{ {
......
...@@ -18,6 +18,8 @@ use Monolog\Formatter\NormalizerFormatter; ...@@ -18,6 +18,8 @@ use Monolog\Formatter\NormalizerFormatter;
* Class to record a log on a NewRelic application. * Class to record a log on a NewRelic application.
* Enabling New Relic High Security mode may prevent capture of useful information. * Enabling New Relic High Security mode may prevent capture of useful information.
* *
* This handler requires a NormalizerFormatter to function and expects an array in $record['formatted']
*
* @see https://docs.newrelic.com/docs/agents/php-agent * @see https://docs.newrelic.com/docs/agents/php-agent
* @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security
*/ */
...@@ -84,7 +86,7 @@ class NewRelicHandler extends AbstractProcessingHandler ...@@ -84,7 +86,7 @@ class NewRelicHandler extends AbstractProcessingHandler
unset($record['formatted']['context']['transaction_name']); unset($record['formatted']['context']['transaction_name']);
} }
if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) {
newrelic_notice_error($record['message'], $record['context']['exception']); newrelic_notice_error($record['message'], $record['context']['exception']);
unset($record['formatted']['context']['exception']); unset($record['formatted']['context']['exception']);
} else { } else {
......
...@@ -31,7 +31,7 @@ class PsrHandler extends AbstractHandler ...@@ -31,7 +31,7 @@ class PsrHandler extends AbstractHandler
/** /**
* @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true) public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true)
{ {
......
...@@ -69,8 +69,8 @@ class PushoverHandler extends SocketHandler ...@@ -69,8 +69,8 @@ class PushoverHandler extends SocketHandler
* @param string|array $users Pushover user id or array of ids the message will be sent to * @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 string $title Title sent to the Pushover API
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not
* the pushover.net app owner. OpenSSL is required for this option. * the pushover.net app owner. OpenSSL is required for this option.
* @param int $highPriorityLevel The minimum logging level at which this handler will start * @param int $highPriorityLevel The minimum logging level at which this handler will start
* sending "high priority" requests to the Pushover API * sending "high priority" requests to the Pushover API
...@@ -180,6 +180,6 @@ class PushoverHandler extends SocketHandler ...@@ -180,6 +180,6 @@ class PushoverHandler extends SocketHandler
*/ */
public function useFormattedMessage($value) public function useFormattedMessage($value)
{ {
$this->useFormattedMessage = (boolean) $value; $this->useFormattedMessage = (bool) $value;
} }
} }
...@@ -18,7 +18,7 @@ use Raven_Client; ...@@ -18,7 +18,7 @@ use Raven_Client;
/** /**
* Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server * Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server
* using raven-php (https://github.com/getsentry/raven-php) * using sentry-php (https://github.com/getsentry/sentry-php)
* *
* @author Marc Abramowitz <marc@marc-abramowitz.com> * @author Marc Abramowitz <marc@marc-abramowitz.com>
*/ */
...@@ -27,7 +27,7 @@ class RavenHandler extends AbstractProcessingHandler ...@@ -27,7 +27,7 @@ class RavenHandler extends AbstractProcessingHandler
/** /**
* Translates Monolog log levels to Raven log levels. * Translates Monolog log levels to Raven log levels.
*/ */
private $logLevels = array( protected $logLevels = array(
Logger::DEBUG => Raven_Client::DEBUG, Logger::DEBUG => Raven_Client::DEBUG,
Logger::INFO => Raven_Client::INFO, Logger::INFO => Raven_Client::INFO,
Logger::NOTICE => Raven_Client::INFO, Logger::NOTICE => Raven_Client::INFO,
...@@ -42,7 +42,7 @@ class RavenHandler extends AbstractProcessingHandler ...@@ -42,7 +42,7 @@ class RavenHandler extends AbstractProcessingHandler
* @var string should represent the current version of the calling * @var string should represent the current version of the calling
* software. Can be any string (git commit, version number) * software. Can be any string (git commit, version number)
*/ */
private $release; protected $release;
/** /**
* @var Raven_Client the client object that sends the message to the server * @var Raven_Client the client object that sends the message to the server
...@@ -57,7 +57,7 @@ class RavenHandler extends AbstractProcessingHandler ...@@ -57,7 +57,7 @@ class RavenHandler extends AbstractProcessingHandler
/** /**
* @param Raven_Client $ravenClient * @param Raven_Client $ravenClient
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true)
{ {
...@@ -180,7 +180,7 @@ class RavenHandler extends AbstractProcessingHandler ...@@ -180,7 +180,7 @@ class RavenHandler extends AbstractProcessingHandler
} }
if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) {
$options['extra']['message'] = $record['formatted']; $options['message'] = $record['formatted'];
$this->ravenClient->captureException($record['context']['exception'], $options); $this->ravenClient->captureException($record['context']['exception'], $options);
} else { } else {
$this->ravenClient->captureMessage($record['formatted'], array(), $options); $this->ravenClient->captureMessage($record['formatted'], array(), $options);
...@@ -216,7 +216,7 @@ class RavenHandler extends AbstractProcessingHandler ...@@ -216,7 +216,7 @@ class RavenHandler extends AbstractProcessingHandler
*/ */
protected function getExtraParameters() protected function getExtraParameters()
{ {
return array('checksum', 'release', 'event_id'); return array('contexts', 'checksum', 'release', 'event_id');
} }
/** /**
......
...@@ -129,4 +129,16 @@ class RollbarHandler extends AbstractProcessingHandler ...@@ -129,4 +129,16 @@ class RollbarHandler extends AbstractProcessingHandler
{ {
$this->flush(); $this->flush();
} }
/**
* {@inheritdoc}
*/
public function reset()
{
$this->flush();
parent::reset();
}
} }
...@@ -39,9 +39,9 @@ class RotatingFileHandler extends StreamHandler ...@@ -39,9 +39,9 @@ class RotatingFileHandler extends StreamHandler
* @param string $filename * @param string $filename
* @param int $maxFiles The maximal amount of files to keep (0 means unlimited) * @param int $maxFiles The maximal amount of files to keep (0 means unlimited)
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
* @param Boolean $useLocking Try to lock log file before doing any writes * @param bool $useLocking Try to lock log file before doing any writes
*/ */
public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false)
{ {
...@@ -66,6 +66,18 @@ class RotatingFileHandler extends StreamHandler ...@@ -66,6 +66,18 @@ class RotatingFileHandler extends StreamHandler
} }
} }
/**
* {@inheritdoc}
*/
public function reset()
{
parent::reset();
if (true === $this->mustRotate) {
$this->rotate();
}
}
public function setFilenameFormat($filenameFormat, $dateFormat) public function setFilenameFormat($filenameFormat, $dateFormat)
{ {
if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) {
...@@ -166,7 +178,7 @@ class RotatingFileHandler extends StreamHandler ...@@ -166,7 +178,7 @@ class RotatingFileHandler extends StreamHandler
$fileInfo = pathinfo($this->filename); $fileInfo = pathinfo($this->filename);
$glob = str_replace( $glob = str_replace(
array('{filename}', '{date}'), array('{filename}', '{date}'),
array($fileInfo['filename'], '*'), array($fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'),
$fileInfo['dirname'] . '/' . $this->filenameFormat $fileInfo['dirname'] . '/' . $this->filenameFormat
); );
if (!empty($fileInfo['extension'])) { if (!empty($fileInfo['extension'])) {
......
...@@ -146,7 +146,7 @@ class SlackRecord ...@@ -146,7 +146,7 @@ class SlackRecord
if ($this->useShortAttachment) { if ($this->useShortAttachment) {
$attachment['fields'][] = $this->generateAttachmentField( $attachment['fields'][] = $this->generateAttachmentField(
ucfirst($key), $key,
$record[$key] $record[$key]
); );
} else { } else {
...@@ -229,8 +229,8 @@ class SlackRecord ...@@ -229,8 +229,8 @@ class SlackRecord
/** /**
* Generates attachment field * Generates attachment field
* *
* @param string $title * @param string $title
* @param string|array $value\ * @param string|array $value
* *
* @return array * @return array
*/ */
...@@ -241,7 +241,7 @@ class SlackRecord ...@@ -241,7 +241,7 @@ class SlackRecord
: $value; : $value;
return array( return array(
'title' => $title, 'title' => ucfirst($title),
'value' => $value, 'value' => $value,
'short' => false 'short' => false
); );
...@@ -257,7 +257,7 @@ class SlackRecord ...@@ -257,7 +257,7 @@ class SlackRecord
private function generateAttachmentFields(array $data) private function generateAttachmentFields(array $data)
{ {
$fields = array(); $fields = array();
foreach ($data as $key => $value) { foreach ($this->normalizerFormatter->format($data) as $key => $value) {
$fields[] = $this->generateAttachmentField($key, $value); $fields[] = $this->generateAttachmentField($key, $value);
} }
......
...@@ -75,6 +75,11 @@ class SlackHandler extends SocketHandler ...@@ -75,6 +75,11 @@ class SlackHandler extends SocketHandler
return $this->slackRecord; return $this->slackRecord;
} }
public function getToken()
{
return $this->token;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
......
...@@ -70,6 +70,11 @@ class SlackWebhookHandler extends AbstractProcessingHandler ...@@ -70,6 +70,11 @@ class SlackWebhookHandler extends AbstractProcessingHandler
return $this->slackRecord; return $this->slackRecord;
} }
public function getWebhookUrl()
{
return $this->webhookUrl;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
......
...@@ -27,15 +27,16 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -27,15 +27,16 @@ class SocketHandler extends AbstractProcessingHandler
private $timeout = 0; private $timeout = 0;
private $writingTimeout = 10; private $writingTimeout = 10;
private $lastSentBytes = null; private $lastSentBytes = null;
private $chunkSize = null;
private $persistent = false; private $persistent = false;
private $errno; private $errno;
private $errstr; private $errstr;
private $lastWritingAt; private $lastWritingAt;
/** /**
* @param string $connectionString Socket connection string * @param string $connectionString Socket connection string
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true)
{ {
...@@ -87,7 +88,7 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -87,7 +88,7 @@ class SocketHandler extends AbstractProcessingHandler
*/ */
public function setPersistent($persistent) public function setPersistent($persistent)
{ {
$this->persistent = (boolean) $persistent; $this->persistent = (bool) $persistent;
} }
/** /**
...@@ -128,6 +129,16 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -128,6 +129,16 @@ class SocketHandler extends AbstractProcessingHandler
} }
/** /**
* Set chunk size. Only has effect during connection in the writing cycle.
*
* @param float $bytes
*/
public function setChunkSize($bytes)
{
$this->chunkSize = $bytes;
}
/**
* Get current connection string * Get current connection string
* *
* @return string * @return string
...@@ -178,6 +189,16 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -178,6 +189,16 @@ class SocketHandler extends AbstractProcessingHandler
} }
/** /**
* Get current chunk size
*
* @return float
*/
public function getChunkSize()
{
return $this->chunkSize;
}
/**
* Check to see if the socket is currently available. * 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. * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details.
...@@ -221,6 +242,16 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -221,6 +242,16 @@ class SocketHandler extends AbstractProcessingHandler
/** /**
* Wrapper to allow mocking * Wrapper to allow mocking
*
* @see http://php.net/manual/en/function.stream-set-chunk-size.php
*/
protected function streamSetChunkSize()
{
return stream_set_chunk_size($this->resource, $this->chunkSize);
}
/**
* Wrapper to allow mocking
*/ */
protected function fwrite($data) protected function fwrite($data)
{ {
...@@ -268,6 +299,7 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -268,6 +299,7 @@ class SocketHandler extends AbstractProcessingHandler
{ {
$this->createSocketResource(); $this->createSocketResource();
$this->setSocketTimeout(); $this->setSocketTimeout();
$this->setStreamChunkSize();
} }
private function createSocketResource() private function createSocketResource()
...@@ -290,6 +322,13 @@ class SocketHandler extends AbstractProcessingHandler ...@@ -290,6 +322,13 @@ class SocketHandler extends AbstractProcessingHandler
} }
} }
private function setStreamChunkSize()
{
if ($this->chunkSize && !$this->streamSetChunkSize()) {
throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()");
}
}
private function writeToSocket($data) private function writeToSocket($data)
{ {
$length = strlen($data); $length = strlen($data);
......
...@@ -32,9 +32,9 @@ class StreamHandler extends AbstractProcessingHandler ...@@ -32,9 +32,9 @@ class StreamHandler extends AbstractProcessingHandler
/** /**
* @param resource|string $stream * @param resource|string $stream
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write)
* @param Boolean $useLocking Try to lock log file before doing any writes * @param bool $useLocking Try to lock log file before doing any writes
* *
* @throws \Exception If a missing directory is not buildable * @throws \Exception If a missing directory is not buildable
* @throws \InvalidArgumentException If stream is not a resource or string * @throws \InvalidArgumentException If stream is not a resource or string
...@@ -167,7 +167,7 @@ class StreamHandler extends AbstractProcessingHandler ...@@ -167,7 +167,7 @@ class StreamHandler extends AbstractProcessingHandler
set_error_handler(array($this, 'customErrorHandler')); set_error_handler(array($this, 'customErrorHandler'));
$status = mkdir($dir, 0777, true); $status = mkdir($dir, 0777, true);
restore_error_handler(); restore_error_handler();
if (false === $status) { if (false === $status && !is_dir($dir)) {
throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir));
} }
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Swift; use Swift;
...@@ -29,7 +30,7 @@ class SwiftMailerHandler extends MailHandler ...@@ -29,7 +30,7 @@ class SwiftMailerHandler extends MailHandler
* @param \Swift_Mailer $mailer The mailer to use * @param \Swift_Mailer $mailer The mailer to use
* @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true)
{ {
...@@ -48,6 +49,17 @@ class SwiftMailerHandler extends MailHandler ...@@ -48,6 +49,17 @@ class SwiftMailerHandler extends MailHandler
} }
/** /**
* Gets the formatter for the Swift_Message subject.
*
* @param string $format The format of the subject
* @return FormatterInterface
*/
protected function getSubjectFormatter($format)
{
return new LineFormatter($format);
}
/**
* Creates instance of Swift_Message to be sent * Creates instance of Swift_Message to be sent
* *
* @param string $content formatted email body to be sent * @param string $content formatted email body to be sent
...@@ -69,7 +81,7 @@ class SwiftMailerHandler extends MailHandler ...@@ -69,7 +81,7 @@ class SwiftMailerHandler extends MailHandler
} }
if ($records) { if ($records) {
$subjectFormatter = new LineFormatter($message->getSubject()); $subjectFormatter = $this->getSubjectFormatter($message->getSubject());
$message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); $message->setSubject($subjectFormatter->format($this->getHighestRecord($records)));
} }
......
...@@ -32,11 +32,11 @@ class SyslogHandler extends AbstractSyslogHandler ...@@ -32,11 +32,11 @@ class SyslogHandler extends AbstractSyslogHandler
protected $logopts; protected $logopts;
/** /**
* @param string $ident * @param string $ident
* @param mixed $facility * @param mixed $facility
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param int $logopts Option flags for the openlog() call, defaults to LOG_PID * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID
*/ */
public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID) public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID)
{ {
......
...@@ -25,12 +25,12 @@ class SyslogUdpHandler extends AbstractSyslogHandler ...@@ -25,12 +25,12 @@ class SyslogUdpHandler extends AbstractSyslogHandler
protected $ident; protected $ident;
/** /**
* @param string $host * @param string $host
* @param int $port * @param int $port
* @param mixed $facility * @param mixed $facility
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $ident Program name or tag for each log message. * @param string $ident Program name or tag for each log message.
*/ */
public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php') public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php')
{ {
......
...@@ -84,14 +84,24 @@ class TestHandler extends AbstractProcessingHandler ...@@ -84,14 +84,24 @@ class TestHandler extends AbstractProcessingHandler
return isset($this->recordsByLevel[$level]); return isset($this->recordsByLevel[$level]);
} }
/**
* @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records
* @param int $level Logger::LEVEL constant value
*/
public function hasRecord($record, $level) public function hasRecord($record, $level)
{ {
if (is_array($record)) { if (is_string($record)) {
$record = $record['message']; $record = array('message' => $record);
} }
return $this->hasRecordThatPasses(function ($rec) use ($record) { return $this->hasRecordThatPasses(function ($rec) use ($record) {
return $rec['message'] === $record; if ($rec['message'] !== $record['message']) {
return false;
}
if (isset($record['context']) && $rec['context'] !== $record['context']) {
return false;
}
return true;
}, $level); }, $level);
} }
......
...@@ -48,6 +48,16 @@ class WhatFailureGroupHandler extends GroupHandler ...@@ -48,6 +48,16 @@ class WhatFailureGroupHandler extends GroupHandler
*/ */
public function handleBatch(array $records) public function handleBatch(array $records)
{ {
if ($this->processors) {
$processed = array();
foreach ($records as $record) {
foreach ($this->processors as $processor) {
$processed[] = call_user_func($processor, $record);
}
}
$records = $processed;
}
foreach ($this->handlers as $handler) { foreach ($this->handlers as $handler) {
try { try {
$handler->handleBatch($records); $handler->handleBatch($records);
......
...@@ -19,7 +19,7 @@ use Monolog\Logger; ...@@ -19,7 +19,7 @@ use Monolog\Logger;
* @author Nick Otter * @author Nick Otter
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class GitProcessor class GitProcessor implements ProcessorInterface
{ {
private $level; private $level;
private static $cache; private static $cache;
......
...@@ -24,7 +24,7 @@ use Monolog\Logger; ...@@ -24,7 +24,7 @@ use Monolog\Logger;
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class IntrospectionProcessor class IntrospectionProcessor implements ProcessorInterface
{ {
private $level; private $level;
......
...@@ -16,7 +16,7 @@ namespace Monolog\Processor; ...@@ -16,7 +16,7 @@ namespace Monolog\Processor;
* *
* @author Rob Jensen * @author Rob Jensen
*/ */
abstract class MemoryProcessor abstract class MemoryProcessor implements ProcessorInterface
{ {
/** /**
* @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported.
...@@ -34,8 +34,8 @@ abstract class MemoryProcessor ...@@ -34,8 +34,8 @@ abstract class MemoryProcessor
*/ */
public function __construct($realUsage = true, $useFormatting = true) public function __construct($realUsage = true, $useFormatting = true)
{ {
$this->realUsage = (boolean) $realUsage; $this->realUsage = (bool) $realUsage;
$this->useFormatting = (boolean) $useFormatting; $this->useFormatting = (bool) $useFormatting;
} }
/** /**
......
...@@ -18,7 +18,7 @@ use Monolog\Logger; ...@@ -18,7 +18,7 @@ use Monolog\Logger;
* *
* @author Jonathan A. Schweder <jonathanschweder@gmail.com> * @author Jonathan A. Schweder <jonathanschweder@gmail.com>
*/ */
class MercurialProcessor class MercurialProcessor implements ProcessorInterface
{ {
private $level; private $level;
private static $cache; private static $cache;
......
...@@ -16,7 +16,7 @@ namespace Monolog\Processor; ...@@ -16,7 +16,7 @@ namespace Monolog\Processor;
* *
* @author Andreas Hörnicke * @author Andreas Hörnicke
*/ */
class ProcessIdProcessor class ProcessIdProcessor implements ProcessorInterface
{ {
/** /**
* @param array $record * @param array $record
......
<?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\Processor;
/**
* An optional interface to allow labelling Monolog processors.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
interface ProcessorInterface
{
/**
* @return array The processed records
*/
public function __invoke(array $records);
}
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
namespace Monolog\Processor; namespace Monolog\Processor;
use Monolog\Utils;
/** /**
* Processes a record's message according to PSR-3 rules * Processes a record's message according to PSR-3 rules
* *
...@@ -18,7 +20,7 @@ namespace Monolog\Processor; ...@@ -18,7 +20,7 @@ namespace Monolog\Processor;
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class PsrLogMessageProcessor class PsrLogMessageProcessor implements ProcessorInterface
{ {
/** /**
* @param array $record * @param array $record
...@@ -35,7 +37,7 @@ class PsrLogMessageProcessor ...@@ -35,7 +37,7 @@ class PsrLogMessageProcessor
if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) {
$replacements['{'.$key.'}'] = $val; $replacements['{'.$key.'}'] = $val;
} elseif (is_object($val)) { } elseif (is_object($val)) {
$replacements['{'.$key.'}'] = '[object '.get_class($val).']'; $replacements['{'.$key.'}'] = '[object '.Utils::getClass($val).']';
} else { } else {
$replacements['{'.$key.'}'] = '['.gettype($val).']'; $replacements['{'.$key.'}'] = '['.gettype($val).']';
} }
......
...@@ -16,7 +16,7 @@ namespace Monolog\Processor; ...@@ -16,7 +16,7 @@ namespace Monolog\Processor;
* *
* @author Martijn Riemers * @author Martijn Riemers
*/ */
class TagProcessor class TagProcessor implements ProcessorInterface
{ {
private $tags; private $tags;
......
...@@ -11,12 +11,14 @@ ...@@ -11,12 +11,14 @@
namespace Monolog\Processor; namespace Monolog\Processor;
use Monolog\ResettableInterface;
/** /**
* Adds a unique identifier into records * Adds a unique identifier into records
* *
* @author Simon Mönch <sm@webfactory.de> * @author Simon Mönch <sm@webfactory.de>
*/ */
class UidProcessor class UidProcessor implements ProcessorInterface, ResettableInterface
{ {
private $uid; private $uid;
...@@ -26,7 +28,8 @@ class UidProcessor ...@@ -26,7 +28,8 @@ class UidProcessor
throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32');
} }
$this->uid = substr(hash('md5', uniqid('', true)), 0, $length);
$this->uid = $this->generateUid($length);
} }
public function __invoke(array $record) public function __invoke(array $record)
...@@ -43,4 +46,14 @@ class UidProcessor ...@@ -43,4 +46,14 @@ class UidProcessor
{ {
return $this->uid; return $this->uid;
} }
public function reset()
{
$this->uid = $this->generateUid(strlen($this->uid));
}
private function generateUid($length)
{
return substr(hash('md5', uniqid('', true)), 0, $length);
}
} }
...@@ -16,7 +16,7 @@ namespace Monolog\Processor; ...@@ -16,7 +16,7 @@ namespace Monolog\Processor;
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class WebProcessor class WebProcessor implements ProcessorInterface
{ {
/** /**
* @var array|\ArrayAccess * @var array|\ArrayAccess
......
<?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 or Processor implementing this interface will be reset when Logger::reset() is called.
*
* Resetting ends a log cycle gets them back to their initial state.
*
* Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal
* state, and getting it back to a state in which it can receive log records again.
*
* This is useful in case you want to avoid logs leaking between two requests or jobs when you
* have a long running process like a worker or an application server serving multiple requests
* in one process.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
interface ResettableInterface
{
public function reset();
}
<?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 ReflectionExtension;
/**
* Monolog POSIX signal handler
*
* @author Robert Gust-Bardon <robert@gust-bardon.org>
*/
class SignalHandler
{
private $logger;
private $previousSignalHandler = array();
private $signalLevelMap = array();
private $signalRestartSyscalls = array();
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, $callPrevious = true, $restartSyscalls = true, $async = true)
{
if (!extension_loaded('pcntl') || !function_exists('pcntl_signal')) {
return $this;
}
if ($callPrevious) {
if (function_exists('pcntl_signal_get_handler')) {
$handler = pcntl_signal_get_handler($signo);
if ($handler === false) {
return $this;
}
$this->previousSignalHandler[$signo] = $handler;
} else {
$this->previousSignalHandler[$signo] = true;
}
} else {
unset($this->previousSignalHandler[$signo]);
}
$this->signalLevelMap[$signo] = $level;
$this->signalRestartSyscalls[$signo] = $restartSyscalls;
if (function_exists('pcntl_async_signals') && $async !== null) {
pcntl_async_signals($async);
}
pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls);
return $this;
}
public function handleSignal($signo, array $siginfo = null)
{
static $signals = array();
if (!$signals && extension_loaded('pcntl')) {
$pcntl = new ReflectionExtension('pcntl');
$constants = $pcntl->getConstants();
if (!$constants) {
// HHVM 3.24.2 returns an empty array.
$constants = get_defined_constants(true);
$constants = $constants['Core'];
}
foreach ($constants as $name => $value) {
if (substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && is_int($value)) {
$signals[$value] = $name;
}
}
unset($constants);
}
$level = isset($this->signalLevelMap[$signo]) ? $this->signalLevelMap[$signo] : LogLevel::CRITICAL;
$signal = isset($signals[$signo]) ? $signals[$signo] : $signo;
$context = isset($siginfo) ? $siginfo : array();
$this->logger->log($level, sprintf('Program received signal %s', $signal), $context);
if (!isset($this->previousSignalHandler[$signo])) {
return;
}
if ($this->previousSignalHandler[$signo] === true || $this->previousSignalHandler[$signo] === SIG_DFL) {
if (extension_loaded('pcntl') && function_exists('pcntl_signal') && function_exists('pcntl_sigprocmask') && function_exists('pcntl_signal_dispatch')
&& extension_loaded('posix') && function_exists('posix_getpid') && function_exists('posix_kill')) {
$restartSyscalls = isset($this->restartSyscalls[$signo]) ? $this->restartSyscalls[$signo] : true;
pcntl_signal($signo, SIG_DFL, $restartSyscalls);
pcntl_sigprocmask(SIG_UNBLOCK, array($signo), $oldset);
posix_kill(posix_getpid(), $signo);
pcntl_signal_dispatch();
pcntl_sigprocmask(SIG_SETMASK, $oldset);
pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls);
}
} elseif (is_callable($this->previousSignalHandler[$signo])) {
if (PHP_VERSION_ID >= 70100) {
$this->previousSignalHandler[$signo]($signo, $siginfo);
} else {
$this->previousSignalHandler[$signo]($signo);
}
}
}
}
<?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 Utils
{
/**
* @internal
*/
public static function getClass($object)
{
$class = \get_class($object);
return 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
}
}
...@@ -40,7 +40,7 @@ class FluentdFormatterTest extends TestCase ...@@ -40,7 +40,7 @@ class FluentdFormatterTest extends TestCase
$formatter = new FluentdFormatter(); $formatter = new FluentdFormatter();
$this->assertEquals( $this->assertEquals(
'["test",0,{"message":"test","extra":[],"level":300,"level_name":"WARNING"}]', '["test",0,{"message":"test","context":[],"extra":[],"level":300,"level_name":"WARNING"}]',
$formatter->format($record) $formatter->format($record)
); );
} }
...@@ -55,7 +55,7 @@ class FluentdFormatterTest extends TestCase ...@@ -55,7 +55,7 @@ class FluentdFormatterTest extends TestCase
$formatter = new FluentdFormatter(true); $formatter = new FluentdFormatter(true);
$this->assertEquals( $this->assertEquals(
'["test.error",0,{"message":"test","extra":[]}]', '["test.error",0,{"message":"test","context":[],"extra":[]}]',
$formatter->format($record) $formatter->format($record)
); );
} }
......
...@@ -180,4 +180,40 @@ class JsonFormatterTest extends TestCase ...@@ -180,4 +180,40 @@ class JsonFormatterTest extends TestCase
'}'; '}';
return $formattedException; return $formattedException;
} }
public function testNormalizeHandleLargeArraysWithExactly1000Items()
{
$formatter = new NormalizerFormatter();
$largeArray = range(1, 1000);
$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->assertArrayNotHasKey('...', $res['context'][0]);
}
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(1001, $res['context'][0]);
$this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']);
}
} }
...@@ -193,6 +193,15 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase ...@@ -193,6 +193,15 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(@json_encode(array($foo, $bar)), $res); $this->assertEquals(@json_encode(array($foo, $bar)), $res);
} }
public function testCanNormalizeReferences()
{
$formatter = new NormalizerFormatter();
$x = array('foo' => 'bar');
$y = array('x' => &$x);
$x['y'] = &$y;
$formatter->format($y);
}
public function testIgnoresInvalidTypes() public function testIgnoresInvalidTypes()
{ {
// set up the recursion // set up the recursion
...@@ -217,6 +226,24 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase ...@@ -217,6 +226,24 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(@json_encode(array($resource)), $res); $this->assertEquals(@json_encode(array($resource)), $res);
} }
public function testNormalizeHandleLargeArraysWithExactly1000Items()
{
$formatter = new NormalizerFormatter();
$largeArray = range(1, 1000);
$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->assertArrayNotHasKey('...', $res['context'][0]);
}
public function testNormalizeHandleLargeArrays() public function testNormalizeHandleLargeArrays()
{ {
$formatter = new NormalizerFormatter(); $formatter = new NormalizerFormatter();
...@@ -231,7 +258,7 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase ...@@ -231,7 +258,7 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase
'extra' => array(), 'extra' => array(),
)); ));
$this->assertCount(1000, $res['context'][0]); $this->assertCount(1001, $res['context'][0]);
$this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']);
} }
...@@ -380,6 +407,29 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase ...@@ -380,6 +407,29 @@ class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase
$result['context']['exception']['trace'][0] $result['context']['exception']['trace'][0]
); );
} }
public function testExceptionTraceDoesNotLeakCallUserFuncArgs()
{
try {
$arg = new TestInfoLeak;
call_user_func(array($this, 'throwHelper'), $arg, $dt = new \DateTime());
} catch (\Exception $e) {
}
$formatter = new NormalizerFormatter();
$record = array('context' => array('exception' => $e));
$result = $formatter->format($record);
$this->assertSame(
'{"function":"throwHelper","class":"Monolog\\\\Formatter\\\\NormalizerFormatterTest","type":"->","args":["[object] (Monolog\\\\Formatter\\\\TestInfoLeak)","'.$dt->format('Y-m-d H:i:s').'"]}',
$result['context']['exception']['trace'][0]
);
}
private function throwHelper($arg)
{
throw new \RuntimeException('Thrown');
}
} }
class TestFooNorm class TestFooNorm
...@@ -421,3 +471,11 @@ class TestToStringError ...@@ -421,3 +471,11 @@ class TestToStringError
throw new \RuntimeException('Could not convert to string'); throw new \RuntimeException('Could not convert to string');
} }
} }
class TestInfoLeak
{
public function __toString()
{
return 'Sensitive information';
}
}
...@@ -21,7 +21,7 @@ class BrowserConsoleHandlerTest extends TestCase ...@@ -21,7 +21,7 @@ class BrowserConsoleHandlerTest extends TestCase
{ {
protected function setUp() protected function setUp()
{ {
BrowserConsoleHandler::reset(); BrowserConsoleHandler::resetStatic();
} }
protected function generateScript() protected function generateScript()
......
...@@ -21,7 +21,7 @@ class ChromePHPHandlerTest extends TestCase ...@@ -21,7 +21,7 @@ class ChromePHPHandlerTest extends TestCase
{ {
protected function setUp() protected function setUp()
{ {
TestChromePHPHandler::reset(); TestChromePHPHandler::resetStatic();
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0'; $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0';
} }
...@@ -136,7 +136,7 @@ class TestChromePHPHandler extends ChromePHPHandler ...@@ -136,7 +136,7 @@ class TestChromePHPHandler extends ChromePHPHandler
{ {
protected $headers = array(); protected $headers = array();
public static function reset() public static function resetStatic()
{ {
self::$initialized = false; self::$initialized = false;
self::$overflowed = false; self::$overflowed = false;
......
...@@ -58,7 +58,7 @@ class FingersCrossedHandlerTest extends TestCase ...@@ -58,7 +58,7 @@ class FingersCrossedHandlerTest extends TestCase
* @covers Monolog\Handler\FingersCrossedHandler::activate * @covers Monolog\Handler\FingersCrossedHandler::activate
* @covers Monolog\Handler\FingersCrossedHandler::reset * @covers Monolog\Handler\FingersCrossedHandler::reset
*/ */
public function testHandleRestartBufferingAfterReset() public function testHandleResetBufferingAfterReset()
{ {
$test = new TestHandler(); $test = new TestHandler();
$handler = new FingersCrossedHandler($test); $handler = new FingersCrossedHandler($test);
...@@ -76,7 +76,7 @@ class FingersCrossedHandlerTest extends TestCase ...@@ -76,7 +76,7 @@ class FingersCrossedHandlerTest extends TestCase
* @covers Monolog\Handler\FingersCrossedHandler::handle * @covers Monolog\Handler\FingersCrossedHandler::handle
* @covers Monolog\Handler\FingersCrossedHandler::activate * @covers Monolog\Handler\FingersCrossedHandler::activate
*/ */
public function testHandleRestartBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled() public function testHandleResetBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled()
{ {
$test = new TestHandler(); $test = new TestHandler();
$handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false); $handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false);
......
...@@ -21,7 +21,7 @@ class FirePHPHandlerTest extends TestCase ...@@ -21,7 +21,7 @@ class FirePHPHandlerTest extends TestCase
{ {
public function setUp() public function setUp()
{ {
TestFirePHPHandler::reset(); TestFirePHPHandler::resetStatic();
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0'; $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0';
} }
...@@ -77,7 +77,7 @@ class TestFirePHPHandler extends FirePHPHandler ...@@ -77,7 +77,7 @@ class TestFirePHPHandler extends FirePHPHandler
{ {
protected $headers = array(); protected $headers = array();
public static function reset() public static function resetStatic()
{ {
self::$initialized = false; self::$initialized = false;
self::$sendHeaders = true; self::$sendHeaders = true;
......
<?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 Robert Kaufmann III <rok3@rok3.me>
* @author Gabriel Machado <gabriel.ms1@hotmail.com>
*/
class InsightOpsHandlerTest extends TestCase
{
/**
* @var resource
*/
private $resource;
/**
* @var LogEntriesHandler
*/
private $handler;
public function testWriteContent()
{
$this->createHandler();
$this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test'));
fseek($this->resource, 0);
$content = fread($this->resource, 1024);
$this->assertRegexp('/testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] test.CRITICAL: Critical write test/', $content);
}
public function testWriteBatchContent()
{
$this->createHandler();
$this->handler->handleBatch($this->getMultipleRecords());
fseek($this->resource, 0);
$content = fread($this->resource, 1024);
$this->assertRegexp('/(testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] .* \[\] \[\]\n){3}/', $content);
}
private function createHandler()
{
$useSSL = extension_loaded('openssl');
$args = array('testToken', 'us', $useSSL, Logger::DEBUG, true);
$this->resource = fopen('php://memory', 'a');
$this->handler = $this->getMock(
'\Monolog\Handler\InsightOpsHandler',
array('fsockopen', 'streamSetTimeout', 'closeSocket'),
$args
);
$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->resource));
$this->handler->expects($this->any())
->method('streamSetTimeout')
->will($this->returnValue(true));
$this->handler->expects($this->any())
->method('closeSocket')
->will($this->returnValue(true));
}
}
...@@ -191,6 +191,40 @@ class RotatingFileHandlerTest extends TestCase ...@@ -191,6 +191,40 @@ class RotatingFileHandlerTest extends TestCase
); );
} }
/**
* @dataProvider rotationWhenSimilarFilesExistTests
*/
public function testRotationWhenSimilarFileNamesExist($dateFormat)
{
touch($old1 = __DIR__.'/Fixtures/foo-foo-'.date($dateFormat).'.rot');
touch($old2 = __DIR__.'/Fixtures/foo-bar-'.date($dateFormat).'.rot');
$log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot';
$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));
}
public function rotationWhenSimilarFilesExistTests()
{
return array(
'Rotation is triggered when the file of the current day is not present but similar exists'
=> array(RotatingFileHandler::FILE_PER_DAY),
'Rotation is triggered when the file of the current month is not present but similar exists'
=> array(RotatingFileHandler::FILE_PER_MONTH),
'Rotation is triggered when the file of the current year is not present but similar exists'
=> array(RotatingFileHandler::FILE_PER_YEAR),
);
}
public function testReuseCurrentFile() public function testReuseCurrentFile()
{ {
$log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot';
......
...@@ -320,12 +320,12 @@ class SlackRecordTest extends TestCase ...@@ -320,12 +320,12 @@ class SlackRecordTest extends TestCase
'short' => false, 'short' => false,
), ),
array( array(
'title' => 'tags', 'title' => 'Tags',
'value' => sprintf('```%s```', json_encode($extra['tags'])), 'value' => sprintf('```%s```', json_encode($extra['tags'])),
'short' => false 'short' => false
), ),
array( array(
'title' => 'test', 'title' => 'Test',
'value' => $context['test'], 'value' => $context['test'],
'short' => false 'short' => false
) )
...@@ -353,6 +353,14 @@ class SlackRecordTest extends TestCase ...@@ -353,6 +353,14 @@ class SlackRecordTest extends TestCase
$this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']); $this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']);
} }
public function testContextHasException()
{
$record = $this->getRecord(Logger::CRITICAL, 'This is a critical message.', array('exception' => new \Exception()));
$slackRecord = new SlackRecord(null, null, true, null, false, true);
$data = $slackRecord->getSlackData($record);
$this->assertInternalType('string', $data['attachments'][0]['fields'][1]['value']);
}
public function testExcludeExtraAndContextFields() public function testExcludeExtraAndContextFields()
{ {
$record = $this->getRecord( $record = $this->getRecord(
...@@ -368,12 +376,12 @@ class SlackRecordTest extends TestCase ...@@ -368,12 +376,12 @@ class SlackRecordTest extends TestCase
$expected = array( $expected = array(
array( array(
'title' => 'info', 'title' => 'Info',
'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)), 'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)),
'short' => false 'short' => false
), ),
array( array(
'title' => 'tags', 'title' => 'Tags',
'value' => sprintf('```%s```', json_encode(array('web'))), 'value' => sprintf('```%s```', json_encode(array('web'))),
'short' => false 'short' => false
), ),
......
...@@ -77,6 +77,13 @@ class SocketHandlerTest extends TestCase ...@@ -77,6 +77,13 @@ class SocketHandlerTest extends TestCase
$this->assertEquals(10.25, $this->handler->getWritingTimeout()); $this->assertEquals(10.25, $this->handler->getWritingTimeout());
} }
public function testSetChunkSize()
{
$this->createHandler('localhost:1234');
$this->handler->setChunkSize(1025);
$this->assertEquals(1025, $this->handler->getChunkSize());
}
public function testSetConnectionString() public function testSetConnectionString()
{ {
$this->createHandler('tcp://localhost:9090'); $this->createHandler('tcp://localhost:9090');
...@@ -121,6 +128,19 @@ class SocketHandlerTest extends TestCase ...@@ -121,6 +128,19 @@ class SocketHandlerTest extends TestCase
} }
/** /**
* @expectedException UnexpectedValueException
*/
public function testExceptionIsThrownIfCannotSetChunkSize()
{
$this->setMockHandler(array('streamSetChunkSize'));
$this->handler->setChunkSize(8192);
$this->handler->expects($this->once())
->method('streamSetChunkSize')
->will($this->returnValue(false));
$this->writeRecord('Hello world');
}
/**
* @expectedException RuntimeException * @expectedException RuntimeException
*/ */
public function testWriteFailsOnIfFwriteReturnsFalse() public function testWriteFailsOnIfFwriteReturnsFalse()
...@@ -304,6 +324,12 @@ class SocketHandlerTest extends TestCase ...@@ -304,6 +324,12 @@ class SocketHandlerTest extends TestCase
->will($this->returnValue(true)); ->will($this->returnValue(true));
} }
if (!in_array('streamSetChunkSize', $methods)) {
$this->handler->expects($this->any())
->method('streamSetChunkSize')
->will($this->returnValue(8192));
}
$this->handler->setFormatter($this->getIdentityFormatter()); $this->handler->setFormatter($this->getIdentityFormatter());
} }
} }
...@@ -54,6 +54,52 @@ class TestHandlerTest extends TestCase ...@@ -54,6 +54,52 @@ class TestHandlerTest extends TestCase
$this->assertEquals(array($record), $records); $this->assertEquals(array($record), $records);
} }
public function testHandlerAssertEmptyContext() {
$handler = new TestHandler;
$record = $this->getRecord(Logger::WARNING, 'test', array());
$this->assertFalse($handler->hasWarning(array(
'message' => 'test',
'context' => array(),
)));
$handler->handle($record);
$this->assertTrue($handler->hasWarning(array(
'message' => 'test',
'context' => array(),
)));
$this->assertFalse($handler->hasWarning(array(
'message' => 'test',
'context' => array(
'foo' => 'bar'
),
)));
}
public function testHandlerAssertNonEmptyContext() {
$handler = new TestHandler;
$record = $this->getRecord(Logger::WARNING, 'test', array('foo' => 'bar'));
$this->assertFalse($handler->hasWarning(array(
'message' => 'test',
'context' => array(
'foo' => 'bar'
),
)));
$handler->handle($record);
$this->assertTrue($handler->hasWarning(array(
'message' => 'test',
'context' => array(
'foo' => 'bar'
),
)));
$this->assertFalse($handler->hasWarning(array(
'message' => 'test',
'context' => array(),
)));
}
public function methodProvider() public function methodProvider()
{ {
return array( return array(
......
...@@ -88,6 +88,29 @@ class WhatFailureGroupHandlerTest extends TestCase ...@@ -88,6 +88,29 @@ class WhatFailureGroupHandlerTest extends TestCase
} }
/** /**
* @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch
*/
public function testHandleBatchUsesProcessors()
{
$testHandlers = array(new TestHandler(), new TestHandler());
$handler = new WhatFailureGroupHandler($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']);
}
}
/**
* @covers Monolog\Handler\WhatFailureGroupHandler::handle * @covers Monolog\Handler\WhatFailureGroupHandler::handle
*/ */
public function testHandleException() public function testHandleException()
......
...@@ -545,4 +545,146 @@ class LoggerTest extends \PHPUnit_Framework_TestCase ...@@ -545,4 +545,146 @@ class LoggerTest extends \PHPUnit_Framework_TestCase
'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'), 'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'),
); );
} }
/**
* @covers Monolog\Logger::setExceptionHandler
*/
public function testSetExceptionHandler()
{
$logger = new Logger(__METHOD__);
$this->assertNull($logger->getExceptionHandler());
$callback = function ($ex) {
};
$logger->setExceptionHandler($callback);
$this->assertEquals($callback, $logger->getExceptionHandler());
}
/**
* @covers Monolog\Logger::setExceptionHandler
* @expectedException InvalidArgumentException
*/
public function testBadExceptionHandlerType()
{
$logger = new Logger(__METHOD__);
$logger->setExceptionHandler(false);
}
/**
* @covers Monolog\Logger::handleException
* @expectedException Exception
*/
public function testDefaultHandleException()
{
$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->throwException(new \Exception('Some handler exception')))
;
$logger->pushHandler($handler);
$logger->info('test');
}
/**
* @covers Monolog\Logger::handleException
* @covers Monolog\Logger::addRecord
*/
public function testCustomHandleException()
{
$logger = new Logger(__METHOD__);
$that = $this;
$logger->setExceptionHandler(function ($e, $record) use ($that) {
$that->assertEquals($e->getMessage(), 'Some handler exception');
$that->assertTrue(is_array($record));
$that->assertEquals($record['message'], 'test');
});
$handler = $this->getMock('Monolog\Handler\HandlerInterface');
$handler->expects($this->any())
->method('isHandling')
->will($this->returnValue(true))
;
$handler->expects($this->any())
->method('handle')
->will($this->throwException(new \Exception('Some handler exception')))
;
$logger->pushHandler($handler);
$logger->info('test');
}
public function testReset()
{
$logger = new Logger('app');
$testHandler = new Handler\TestHandler();
$bufferHandler = new Handler\BufferHandler($testHandler);
$groupHandler = new Handler\GroupHandler(array($bufferHandler));
$fingersCrossedHandler = new Handler\FingersCrossedHandler($groupHandler);
$logger->pushHandler($fingersCrossedHandler);
$processorUid1 = new Processor\UidProcessor(10);
$uid1 = $processorUid1->getUid();
$groupHandler->pushProcessor($processorUid1);
$processorUid2 = new Processor\UidProcessor(5);
$uid2 = $processorUid2->getUid();
$logger->pushProcessor($processorUid2);
$getProperty = function ($object, $property) {
$reflectionProperty = new \ReflectionProperty(get_class($object), $property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($object);
};
$that = $this;
$assertBufferOfBufferHandlerEmpty = function () use ($getProperty, $bufferHandler, $that) {
$that->assertEmpty($getProperty($bufferHandler, 'buffer'));
};
$assertBuffersEmpty = function() use ($assertBufferOfBufferHandlerEmpty, $getProperty, $fingersCrossedHandler, $that) {
$assertBufferOfBufferHandlerEmpty();
$that->assertEmpty($getProperty($fingersCrossedHandler, 'buffer'));
};
$logger->debug('debug');
$logger->reset();
$assertBuffersEmpty();
$this->assertFalse($testHandler->hasDebugRecords());
$this->assertFalse($testHandler->hasErrorRecords());
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
$logger->debug('debug');
$logger->error('error');
$logger->reset();
$assertBuffersEmpty();
$this->assertTrue($testHandler->hasDebugRecords());
$this->assertTrue($testHandler->hasErrorRecords());
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
$logger->info('info');
$this->assertNotEmpty($getProperty($fingersCrossedHandler, 'buffer'));
$assertBufferOfBufferHandlerEmpty();
$this->assertFalse($testHandler->hasInfoRecords());
$logger->reset();
$assertBuffersEmpty();
$this->assertFalse($testHandler->hasInfoRecords());
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
$logger->notice('notice');
$logger->emergency('emergency');
$logger->reset();
$assertBuffersEmpty();
$this->assertFalse($testHandler->hasInfoRecords());
$this->assertTrue($testHandler->hasNoticeRecords());
$this->assertTrue($testHandler->hasEmergencyRecords());
$this->assertNotSame($uid1, $processorUid1->getUid());
$this->assertNotSame($uid2, $processorUid2->getUid());
}
} }
...@@ -25,9 +25,12 @@ ...@@ -25,9 +25,12 @@
"symfony/translation": "~2.6 || ~3.0 || ~4.0" "symfony/translation": "~2.6 || ~3.0 || ~4.0"
}, },
"require-dev": { "require-dev": {
"friendsofphp/php-cs-fixer": "~2",
"phpunit/phpunit": "^4.8.35 || ^5.7" "phpunit/phpunit": "^4.8.35 || ^5.7"
}, },
"suggest": {
"friendsofphp/php-cs-fixer": "Needed for the `composer phpcs` command. Allow to automatically fix code style.",
"phpstan/phpstan": "Needed for the `composer phpstan` command. Allow to detect potential errors."
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"": "src/" "": "src/"
......
...@@ -195,7 +195,7 @@ class Carbon extends DateTime implements JsonSerializable ...@@ -195,7 +195,7 @@ class Carbon extends DateTime implements JsonSerializable
't' => '(2[89]|3[01])', 't' => '(2[89]|3[01])',
'L' => '(0|1)', 'L' => '(0|1)',
'o' => '([1-9][0-9]{0,4})', 'o' => '([1-9][0-9]{0,4})',
'Y' => '([1-9][0-9]{0,4})', 'Y' => '([1-9]?[0-9]{4})',
'y' => '([0-9]{2})', 'y' => '([0-9]{2})',
'a' => '(am|pm)', 'a' => '(am|pm)',
'A' => '(AM|PM)', 'A' => '(AM|PM)',
...@@ -217,8 +217,8 @@ class Carbon extends DateTime implements JsonSerializable ...@@ -217,8 +217,8 @@ class Carbon extends DateTime implements JsonSerializable
'U' => '([0-9]*)', 'U' => '([0-9]*)',
// The formats below are combinations of the above formats. // The formats below are combinations of the above formats.
'c' => '(([1-9][0-9]{0,4})\-(1[012]|0[1-9])\-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[\+\-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP 'c' => '(([1-9]?[0-9]{4})\-(1[012]|0[1-9])\-(3[01]|[12][0-9]|0[1-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])[\+\-](1[012]|0[0-9]):([0134][05]))', // Y-m-dTH:i:sP
'r' => '(([a-zA-Z]{3}), ([123][0-9]|[1-9]) ([a-zA-Z]{3}) ([1-9][0-9]{0,4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [\+\-](1[012]|0[0-9])([0134][05]))', // D, j M Y H:i:s O 'r' => '(([a-zA-Z]{3}), ([123][0-9]|[1-9]) ([a-zA-Z]{3}) ([1-9]?[0-9]{4}) (2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]) [\+\-](1[012]|0[0-9])([0134][05]))', // D, j M Y H:i:s O
); );
/** /**
...@@ -3976,7 +3976,13 @@ class Carbon extends DateTime implements JsonSerializable ...@@ -3976,7 +3976,13 @@ class Carbon extends DateTime implements JsonSerializable
$value = $diff->days * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE + $value = $diff->days * static::HOURS_PER_DAY * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE +
$diff->h * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE + $diff->h * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE +
$diff->i * static::SECONDS_PER_MINUTE + $diff->i * static::SECONDS_PER_MINUTE +
$diff->s; $diff->s - (
version_compare(PHP_VERSION, '7.1.8-dev', '>=') &&
property_exists($diff, 'f') &&
$diff->f < 0
? 1
: 0
);
return $absolute || !$diff->invert ? $value : -$value; return $absolute || !$diff->invert ? $value : -$value;
} }
......
...@@ -498,6 +498,11 @@ class CarbonInterval extends DateInterval ...@@ -498,6 +498,11 @@ class CarbonInterval extends DateInterval
{ {
$instance = new static(static::getDateIntervalSpec($di)); $instance = new static(static::getDateIntervalSpec($di));
$instance->invert = $di->invert; $instance->invert = $di->invert;
foreach (array('y', 'm', 'd', 'h', 'i', 's') as $unit) {
if ($di->$unit < 0) {
$instance->$unit *= -1;
}
}
return $instance; return $instance;
} }
...@@ -961,15 +966,15 @@ class CarbonInterval extends DateInterval ...@@ -961,15 +966,15 @@ class CarbonInterval extends DateInterval
public static function getDateIntervalSpec(DateInterval $interval) public static function getDateIntervalSpec(DateInterval $interval)
{ {
$date = array_filter(array( $date = array_filter(array(
static::PERIOD_YEARS => $interval->y, static::PERIOD_YEARS => abs($interval->y),
static::PERIOD_MONTHS => $interval->m, static::PERIOD_MONTHS => abs($interval->m),
static::PERIOD_DAYS => $interval->d, static::PERIOD_DAYS => abs($interval->d),
)); ));
$time = array_filter(array( $time = array_filter(array(
static::PERIOD_HOURS => $interval->h, static::PERIOD_HOURS => abs($interval->h),
static::PERIOD_MINUTES => $interval->i, static::PERIOD_MINUTES => abs($interval->i),
static::PERIOD_SECONDS => $interval->s, static::PERIOD_SECONDS => abs($interval->s),
)); ));
$specString = static::PERIOD_PREFIX; $specString = static::PERIOD_PREFIX;
......
# monitorDing
Laravel 插件,用于给钉钉自定义机器人发送消息
# 引入步骤
发布视图
```
php artisan vendor:publish
```
`config/app.php` 添加
```
Redgo\MonitorDing\MonitorDingServiceProvider::class,
```
如果需要配置门面模式, 在 `config/app.php` 添加
```
'MonitorDing' => Redgo\MonitorDing\Facades\MonitorDing::class,
```
# 使用
1. 首先在配置文件 `config/monitorDing` 修改 `webhook`
{
"name": "redgo/monitor-ding",
"license": "MIT",
"description": "用于给钉钉自定义机器人发送消息",
"type": "library",
"authors": [
{
"name": "redgo",
"email": "1223858310@qq.com"
}
],
"require": {
"laravel/framework": "^5.1"
},
"autoload": {
"psr-4": {
"Redgo\\MonitorDing\\": "src/"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
<?php namespace Redgo\MonitorDing\Exceptions;
use Exception;
class SendErrorException extends Exception {
}
\ No newline at end of file
<?php namespace Redgo\MonitorDing\Facades;
use Illuminate\Support\Facades\Facade;
class MonitorDing extends Facade {
/**
* Return facade accessor
* @return string
*/
protected static function getFacadeAccessor()
{
return 'monitorDing';
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment