Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiosmosis <diosmosis@users.noreply.github.com>2019-02-18 17:02:29 +0300
committerGitHub <noreply@github.com>2019-02-18 17:02:29 +0300
commit8d3fe62389469347f7ebc551ee36f4527655b804 (patch)
treebeea4c5ba26e3969e47441f948c152b471bf20a9
parent99d42f5b8ca81f5481635c0cbea794d4b7de783b (diff)
Enable fingers crossed handler via INI config and show backtrace in logs/archive api output (#13923)
* Add config to use FringersCrossedHandler (untested) * Get to work in different contexts. * Add changelog note. * Make sure more exceptions make it to the logs, make backtrace include previous exceptions, do not use screen writer if in cli mode, always print backtrace if in CLI mode and archivephp triggered. * Add log capturing handler. * Remove options from global.ini.php sibnce they may be temporary. * Add UI test triggering an error w/ the screen handler. * Add some more log statements, ignore logs in screen writer, replace part of message in ExceptionToTextProcessor instead of whole message. * Add missing license. * Update changelog, move new item to 3.9 * Fixing some integration tests. * Fix another unit test. * One more test fix. * Try to get rid of xss testing warning. * Try again to get rid of warning. * Try again to get rid of warning. * Try again to get rid of warning.
-rw-r--r--CHANGELOG.md5
-rw-r--r--core/API/Proxy.php12
-rw-r--r--core/API/Request.php7
-rw-r--r--core/API/ResponseBuilder.php15
-rw-r--r--core/ArchiveProcessor/PluginsArchiver.php2
-rw-r--r--core/CliMulti.php5
-rw-r--r--core/Development.php9
-rw-r--r--core/ErrorHandler.php4
-rw-r--r--core/ExceptionHandler.php21
-rw-r--r--core/FrontController.php11
-rw-r--r--core/Plugin/ViewDataTable.php2
-rw-r--r--core/Plugin/Visualization.php11
-rw-r--r--core/Session.php6
-rw-r--r--core/testMinimumPhpVersion.php6
-rw-r--r--plugins/CoreConsole/Commands/ClearCaches.php1
-rw-r--r--plugins/CustomVariables/Model.php7
-rw-r--r--plugins/Monolog/Handler/LogCaptureHandler.php34
-rw-r--r--plugins/Monolog/Handler/WebNotificationHandler.php9
-rw-r--r--plugins/Monolog/Processor/ExceptionToTextProcessor.php55
-rw-r--r--plugins/Monolog/config/config.php42
-rw-r--r--plugins/ScheduledReports/tests/Integration/ApiTest.php3
-rw-r--r--plugins/UserCountry/GeoIPAutoUpdater.php19
-rw-r--r--tests/PHPUnit/Fixtures/UITestFixture.php51
-rw-r--r--tests/PHPUnit/Framework/XssTesting.php4
-rw-r--r--tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php45
-rw-r--r--tests/PHPUnit/Integration/FrontControllerTest.php4
-rw-r--r--tests/PHPUnit/Integration/ReportTest.php7
-rw-r--r--tests/UI/specs/UIIntegration_spec.js9
28 files changed, 315 insertions, 91 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fd762d201d..13709e7682 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@ This is the Developer Changelog for Matomo platform developers. All changes in o
The Product Changelog at **[matomo.org/changelog](https://matomo.org/changelog)** lets you see more details about any Matomo release, such as the list of new guides and FAQs, security fixes, and links to all closed issues.
+## Matomo 3.9.0
+
+### New Features
+* It is now possible to use monolog's FingersCrossedHandler which buffers all logs and logs all of them in case of warning or error.
+
## Matomo 3.8.0
### Breaking Changes
diff --git a/core/API/Proxy.php b/core/API/Proxy.php
index 34b202f94d..1f2ca5a59d 100644
--- a/core/API/Proxy.php
+++ b/core/API/Proxy.php
@@ -11,6 +11,7 @@ namespace Piwik\API;
use Exception;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\Context;
use Piwik\Piwik;
use Piwik\Plugin\Manager;
@@ -25,15 +26,13 @@ use ReflectionMethod;
* object, with the parameters in the right order.
*
* It will also log the performance of API calls (time spent, parameter values, etc.) if logger available
- *
- * @method static Proxy getInstance()
*/
-class Proxy extends Singleton
+class Proxy
{
// array of already registered plugins names
protected $alreadyRegistered = array();
- private $metadataArray = array();
+ protected $metadataArray = array();
private $hideIgnoredFunctions = true;
// when a parameter doesn't have a default value we use this
@@ -44,6 +43,11 @@ class Proxy extends Singleton
$this->noDefaultValue = new NoDefaultValue();
}
+ public static function getInstance()
+ {
+ return StaticContainer::get(self::class);
+ }
+
/**
* Returns array containing reflection meta data for all the loaded classes
* eg. number of parameters, method names, etc.
diff --git a/core/API/Request.php b/core/API/Request.php
index 5356a94e6e..1875e2822a 100644
--- a/core/API/Request.php
+++ b/core/API/Request.php
@@ -12,6 +12,7 @@ use Exception;
use Piwik\Access;
use Piwik\Cache;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\Context;
use Piwik\DataTable;
use Piwik\Exception\PluginDeactivatedException;
@@ -23,6 +24,7 @@ use Piwik\Plugins\CoreHome\LoginWhitelist;
use Piwik\SettingsServer;
use Piwik\Url;
use Piwik\UrlHelper;
+use Psr\Log\LoggerInterface;
/**
* Dispatches API requests to the appropriate API method.
@@ -269,7 +271,10 @@ class Request
return $response->getResponse($returnedValue, $module, $method);
});
} catch (Exception $e) {
- Log::debug($e);
+ StaticContainer::get(LoggerInterface::class)->error('Uncaught exception in API: {exception}', [
+ 'exception' => $e,
+ 'ignoreInScreenWriter' => true,
+ ]);
$toReturn = $response->getResponseException($e);
} finally {
diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php
index a3730d3fe0..9845dee275 100644
--- a/core/API/ResponseBuilder.php
+++ b/core/API/ResponseBuilder.php
@@ -15,6 +15,7 @@ use Piwik\DataTable\Renderer;
use Piwik\DataTable\DataTableInterface;
use Piwik\DataTable\Filter\ColumnDelete;
use Piwik\DataTable\Filter\Pattern;
+use Piwik\Plugins\Monolog\Processor\ExceptionToTextProcessor;
/**
*/
@@ -165,19 +166,7 @@ class ResponseBuilder
*/
private function formatExceptionMessage($exception)
{
- $message = "";
-
- $e = $exception;
- do {
- if ($e !== $exception) {
- $message .= ",\ncaused by: ";
- }
-
- $message .= $e->getMessage();
- if ($this->shouldPrintBacktrace) {
- $message .= "\n" . $e->getTraceAsString();
- }
- } while ($e = $e->getPrevious());
+ $message = ExceptionToTextProcessor::getWholeBacktrace($exception, $this->shouldPrintBacktrace);
return Renderer::formatValueXml($message);
}
diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php
index 11adfe7f1d..2f7cd1ad12 100644
--- a/core/ArchiveProcessor/PluginsArchiver.php
+++ b/core/ArchiveProcessor/PluginsArchiver.php
@@ -10,13 +10,11 @@
namespace Piwik\ArchiveProcessor;
use Piwik\ArchiveProcessor;
-use Piwik\Common;
use Piwik\Container\StaticContainer;
use Piwik\CronArchive\Performance\Logger;
use Piwik\DataAccess\ArchiveWriter;
use Piwik\DataAccess\LogAggregator;
use Piwik\DataTable\Manager;
-use Piwik\ErrorHandler;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugin\Archiver;
diff --git a/core/CliMulti.php b/core/CliMulti.php
index d8d533ea24..b459bd69b6 100644
--- a/core/CliMulti.php
+++ b/core/CliMulti.php
@@ -447,4 +447,9 @@ class CliMulti
$minutes = floor($elapsed / 60);
return self::BASE_WAIT_TIME + $minutes * 100000; // 100 * 1000 = 100ms
}
+
+ public static function isCliMultiRequest()
+ {
+ return Common::getRequestVar('pid', false) !== false;
+ }
}
diff --git a/core/Development.php b/core/Development.php
index 3b44eba71d..4660f83d9e 100644
--- a/core/Development.php
+++ b/core/Development.php
@@ -10,6 +10,8 @@
namespace Piwik;
use Exception;
+use Piwik\Container\StaticContainer;
+use Psr\Log\LoggerInterface;
/**
* Development related checks and tools. You can enable/disable development using `./console development:enable` and
@@ -150,8 +152,11 @@ class Development
$message .= ' (This error is only shown in development mode)';
if (SettingsServer::isTrackerApiRequest()
- || Common::isPhpCliMode()) {
- Log::error($message);
+ || Common::isPhpCliMode()
+ ) {
+ StaticContainer::get(LoggerInterface::class)->error($message, [
+ 'ignoreInScreenWriter' => true,
+ ]);
} else {
throw new Exception($message);
}
diff --git a/core/ErrorHandler.php b/core/ErrorHandler.php
index e0a677cc53..b017d5e333 100644
--- a/core/ErrorHandler.php
+++ b/core/ErrorHandler.php
@@ -8,7 +8,9 @@
namespace Piwik;
+use Piwik\Container\StaticContainer;
use Piwik\Exception\ErrorException;
+use Psr\Log\LoggerInterface;
/**
* Piwik's error handler function.
@@ -160,7 +162,7 @@ class ErrorHandler
case E_USER_DEPRECATED:
default:
try {
- Log::warning(self::createLogMessage($errno, $errstr, $errfile, $errline));
+ StaticContainer::get(LoggerInterface::class)->warning(self::createLogMessage($errno, $errstr, $errfile, $errline));
} catch (\Exception $ex) {
// ignore (it's possible for this to happen if the StaticContainer hasn't been created yet)
}
diff --git a/core/ExceptionHandler.php b/core/ExceptionHandler.php
index 6c01785b95..c1f959ce6d 100644
--- a/core/ExceptionHandler.php
+++ b/core/ExceptionHandler.php
@@ -9,10 +9,13 @@
namespace Piwik;
use Exception;
+use Interop\Container\Exception\ContainerException;
use Piwik\API\Request;
use Piwik\API\ResponseBuilder;
use Piwik\Container\ContainerDoesNotExistException;
+use Piwik\Container\StaticContainer;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
+use Psr\Log\LoggerInterface;
/**
* Contains Piwik's uncaught exception handler.
@@ -41,6 +44,8 @@ class ExceptionHandler
*/
public static function dieWithCliError($exception)
{
+ self::logException($exception);
+
$message = $exception->getMessage();
if (!method_exists($exception, 'isHtmlMessage') || !$exception->isHtmlMessage()) {
@@ -65,6 +70,8 @@ class ExceptionHandler
*/
public static function dieWithHtmlErrorPage($exception)
{
+ self::logException($exception);
+
Common::sendHeader('Content-Type: text/html; charset=utf-8');
try {
@@ -137,4 +144,18 @@ class ExceptionHandler
return $result;
}
+
+ private static function logException($exception)
+ {
+ try {
+ StaticContainer::get(LoggerInterface::class)->error('Uncaught exception: {exception}', [
+ 'exception' => $exception,
+ 'ignoreInScreenWriter' => true,
+ ]);
+ } catch (ContainerException $ex) {
+ // ignore (occurs if exception is thrown when resolving DI entries)
+ } catch (ContainerDoesNotExistException $ex) {
+ // ignore
+ }
+ }
}
diff --git a/core/FrontController.php b/core/FrontController.php
index 8a32c425b0..320239f45b 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -21,6 +21,7 @@ use Piwik\Http\ControllerResolver;
use Piwik\Http\Router;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
use Piwik\Session\SessionAuth;
+use Psr\Log\LoggerInterface;
/**
* This singleton dispatches requests to the appropriate plugin Controller.
@@ -107,6 +108,11 @@ class FrontController extends Singleton
*/
private static function generateSafeModeOutputFromException($e)
{
+ StaticContainer::get(LoggerInterface::class)->error('Uncaught exception: {exception}', [
+ 'exception' => $e,
+ 'ignoreInScreenWriter' => true,
+ ]);
+
$error = array(
'message' => $e->getMessage(),
'file' => $e->getFile(),
@@ -256,6 +262,11 @@ class FrontController extends Singleton
$lastError['backtrace'] = ' on ' . $lastError['file'] . '(' . $lastError['line'] . ")\n"
. ErrorHandler::getFatalErrorPartialBacktrace();
+ StaticContainer::get(LoggerInterface::class)->error('Fatal error encountered: {exception}', [
+ 'exception' => $lastError,
+ 'ignoreInScreenWriter' => true,
+ ]);
+
$message = self::generateSafeModeOutputFromError($lastError);
echo $message;
}
diff --git a/core/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php
index 226e8f3141..513d175d1f 100644
--- a/core/Plugin/ViewDataTable.php
+++ b/core/Plugin/ViewDataTable.php
@@ -13,8 +13,6 @@ use Piwik\Common;
use Piwik\DataTable;
use Piwik\Period;
use Piwik\Piwik;
-use Piwik\Plugin\ReportsProvider;
-use Piwik\View;
use Piwik\View\ViewInterface;
use Piwik\ViewDataTable\Config as VizConfig;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index 71c9eca248..f23a183ed6 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -28,6 +28,7 @@ use Piwik\View;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
use Piwik\Plugin\Manager as PluginManager;
use Piwik\API\Request as ApiRequest;
+use Psr\Log\LoggerInterface;
/**
* The base class for report visualizations that output HTML and use JavaScript.
@@ -193,16 +194,16 @@ class Visualization extends ViewDataTable
} catch (NoAccessException $e) {
throw $e;
} catch (\Exception $e) {
- $logMessage = "Failed to get data from API: " . $e->getMessage();
- $message = $e->getMessage();
+ StaticContainer::get(LoggerInterface::class)->error('Failed to get data from API: {exception}', [
+ 'exception' => $e,
+ 'ignoreInScreenWriter' => true,
+ ]);
+ $message = $e->getMessage();
if (\Piwik_ShouldPrintBackTraceWithMessage()) {
- $logMessage .= "\n" . $e->getTraceAsString();
$message .= "\n" . $e->getTraceAsString();
}
- Log::error($logMessage);
-
$loadingError = array('message' => $message);
}
diff --git a/core/Session.php b/core/Session.php
index b03c8e0796..bf523d3b71 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -12,6 +12,7 @@ use Exception;
use Piwik\Container\StaticContainer;
use Piwik\Exception\MissingFilePermissionException;
use Piwik\Session\SaveHandler\DbTable;
+use Psr\Log\LoggerInterface;
use Zend_Session;
/**
@@ -132,7 +133,10 @@ class Session extends Zend_Session
parent::start();
register_shutdown_function(array('Zend_Session', 'writeClose'), true);
} catch (Exception $e) {
- Log::error('Unable to start session: ' . $e->getMessage());
+ StaticContainer::get(LoggerInterface::class)->error('Unable to start session: {exception}', [
+ 'exception' => $e,
+ 'ignoreInScreenWriter' => true,
+ ]);
if (SettingsPiwik::isPiwikInstalled()) {
$pathToSessions = '';
diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php
index 82bbc4fd88..c1f8338081 100644
--- a/core/testMinimumPhpVersion.php
+++ b/core/testMinimumPhpVersion.php
@@ -84,6 +84,12 @@ if (!function_exists('Piwik_GetErrorMessagePage')) {
*/
function Piwik_ShouldPrintBackTraceWithMessage()
{
+ if (\Piwik\SettingsServer::isArchivePhpTriggered()
+ && \Piwik\Common::isPhpCliMode()
+ ) {
+ return true;
+ }
+
$bool = (defined('PIWIK_PRINT_ERROR_BACKTRACE') && PIWIK_PRINT_ERROR_BACKTRACE)
|| !empty($GLOBALS['PIWIK_TRACKER_DEBUG']);
diff --git a/plugins/CoreConsole/Commands/ClearCaches.php b/plugins/CoreConsole/Commands/ClearCaches.php
index b88680812f..1e8439ca0b 100644
--- a/plugins/CoreConsole/Commands/ClearCaches.php
+++ b/plugins/CoreConsole/Commands/ClearCaches.php
@@ -12,7 +12,6 @@ namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Filesystem;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
diff --git a/plugins/CustomVariables/Model.php b/plugins/CustomVariables/Model.php
index 5abb327f0e..7ab4605489 100644
--- a/plugins/CustomVariables/Model.php
+++ b/plugins/CustomVariables/Model.php
@@ -9,10 +9,12 @@
namespace Piwik\Plugins\CustomVariables;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\DataTable;
use Piwik\Db;
use Piwik\Log;
use Piwik\Piwik;
+use Psr\Log\LoggerInterface;
class Model
{
@@ -194,7 +196,10 @@ class Model
$model->addCustomVariable();
}
} catch (\Exception $e) {
- Log::error('Failed to add custom variable: ' . $e->getMessage());
+ StaticContainer::get(LoggerInterface::class)->error('Failed to add custom variable: {exception}', [
+ 'exception' => $e,
+ 'ignoreInScreenWriter' => true,
+ ]);
}
}
}
diff --git a/plugins/Monolog/Handler/LogCaptureHandler.php b/plugins/Monolog/Handler/LogCaptureHandler.php
new file mode 100644
index 0000000000..fbe4acad29
--- /dev/null
+++ b/plugins/Monolog/Handler/LogCaptureHandler.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\Monolog\Handler;
+
+use Monolog\Handler\AbstractHandler;
+
+class LogCaptureHandler extends AbstractHandler
+{
+ /**
+ * @var array
+ */
+ private $allLogs;
+
+ public function handle(array $record)
+ {
+ $this->allLogs[] = $record;
+ }
+
+ /**
+ * Returns all records. The records should be processed, so one could just use $record['message'].
+ *
+ * @return array[]
+ */
+ public function getAllRecords()
+ {
+ return $this->allLogs;
+ }
+} \ No newline at end of file
diff --git a/plugins/Monolog/Handler/WebNotificationHandler.php b/plugins/Monolog/Handler/WebNotificationHandler.php
index 5f0de1bb8c..9ff76958aa 100644
--- a/plugins/Monolog/Handler/WebNotificationHandler.php
+++ b/plugins/Monolog/Handler/WebNotificationHandler.php
@@ -20,6 +20,15 @@ use Zend_Session_Exception;
*/
class WebNotificationHandler extends AbstractProcessingHandler
{
+ public function isHandling(array $record)
+ {
+ if (!empty($record['context']['ignoreInScreenWriter'])) {
+ return false;
+ }
+
+ return parent::isHandling($record);
+ }
+
protected function write(array $record)
{
switch ($record['level']) {
diff --git a/plugins/Monolog/Processor/ExceptionToTextProcessor.php b/plugins/Monolog/Processor/ExceptionToTextProcessor.php
index daaaee479e..0a947a9f23 100644
--- a/plugins/Monolog/Processor/ExceptionToTextProcessor.php
+++ b/plugins/Monolog/Processor/ExceptionToTextProcessor.php
@@ -25,34 +25,75 @@ class ExceptionToTextProcessor
/** @var \Exception $exception */
$exception = $record['context']['exception'];
- $record['message'] = sprintf(
+ $exceptionStr = sprintf(
"%s(%d): %s\n%s",
- $exception->getFile(),
- $exception->getLine(),
+ $exception instanceof \Exception ? $exception->getFile() : $exception['file'],
+ $exception instanceof \Exception ? $exception->getLine() : $exception['line'],
$this->getMessage($exception),
$this->getStackTrace($exception)
);
+ if (!isset($record['message'])
+ || strpos($record['message'], '{exception}') === false
+ ) {
+ $record['message'] = $exceptionStr;
+ } else {
+ $record['message'] = str_replace('{exception}', $exceptionStr, $record['message']);
+ }
+
return $record;
}
private function contextContainsException($record)
{
return isset($record['context']['exception'])
- && $record['context']['exception'] instanceof \Exception;
+ && ($record['context']['exception'] instanceof \Exception
+ || $this->isLooksLikeFatalErrorArray($record['context']['exception']));
+ }
+
+ private function isLooksLikeFatalErrorArray($exception)
+ {
+ return is_array($exception) && isset($exception['message']) && isset($exception['file']) && isset($exception['line']);
}
- private function getMessage(\Exception $exception)
+ private function getMessage($exception)
{
if ($exception instanceof \ErrorException) {
return ErrorHandler::getErrNoString($exception->getSeverity()) . ' - ' . $exception->getMessage();
}
+ if (is_array($exception) && isset($exception['message'])) {
+ return $exception['message'];
+ }
+
return $exception->getMessage();
}
- private function getStackTrace(\Exception $exception)
+ private function getStackTrace($exception)
{
- return Log::$debugBacktraceForTests ?: $exception->getTraceAsString();
+ if (is_array($exception) && isset($exception['backtrace'])) {
+ return $exception['backtrace'];
+ }
+
+ return Log::$debugBacktraceForTests ?: self::getWholeBacktrace($exception);
+ }
+
+ public static function getWholeBacktrace(\Exception $exception, $shouldPrintBacktrace = true)
+ {
+ $message = "";
+
+ $e = $exception;
+ do {
+ if ($e !== $exception) {
+ $message .= ",\ncaused by: ";
+ }
+
+ $message .= $e->getMessage();
+ if ($shouldPrintBacktrace) {
+ $message .= "\n" . $e->getTraceAsString();
+ }
+ } while ($e = $e->getPrevious());
+
+ return $message;
}
}
diff --git a/plugins/Monolog/config/config.php b/plugins/Monolog/config/config.php
index 19e7752479..b9e9e0c62f 100644
--- a/plugins/Monolog/config/config.php
+++ b/plugins/Monolog/config/config.php
@@ -4,6 +4,7 @@ use Interop\Container\ContainerInterface;
use Monolog\Logger;
use Piwik\Log;
use Piwik\Plugins\Monolog\Handler\FileHandler;
+use Piwik\Plugins\Monolog\Handler\LogCaptureHandler;
return array(
@@ -17,7 +18,7 @@ return array(
'screen' => 'Piwik\Plugins\Monolog\Handler\WebNotificationHandler',
'database' => 'Piwik\Plugins\Monolog\Handler\DatabaseHandler',
),
- 'log.handlers' => DI\factory(function (ContainerInterface $c) {
+ 'log.handlers' => DI\factory(function (\DI\Container $c) {
if ($c->has('ini.log.log_writers')) {
$writerNames = $c->get('ini.log.log_writers');
} else {
@@ -26,13 +27,50 @@ return array(
$classes = $c->get('log.handler.classes');
+ $logConfig = $c->get(\Piwik\Config::class)->log;
+ $enableFingersCrossed = isset($logConfig['enable_fingers_crossed_handler']) && $logConfig['enable_fingers_crossed_handler'] == 1;
+ $fingersCrossedStopBuffering = isset($logConfig['fingers_crossed_stop_buffering_on_activation']) && $logConfig['fingers_crossed_stop_buffering_on_activation'] == 1;
+ $enableLogCaptureHandler = isset($logConfig['enable_log_capture_handler']) && $logConfig['enable_log_capture_handler'] == 1;
+
+ $isLogBufferingAllowed = !\Piwik\Common::isPhpCliMode()
+ || \Piwik\SettingsServer::isArchivePhpTriggered()
+ || \Piwik\CliMulti::isCliMultiRequest();
+
$writerNames = array_map('trim', $writerNames);
$writers = array();
foreach ($writerNames as $writerName) {
+ if ($writerName === 'screen' && \Piwik\Common::isPhpCliMode()) {
+ continue; // screen writer is only valid for web requests
+ }
+
if (isset($classes[$writerName])) {
- $writers[$writerName] = $c->get($classes[$writerName]);
+ // wrap the handler in FingersCrossedHandler if we can and this isn't the screen handler
+
+ /** @var \Monolog\Handler\HandlerInterface $handler */
+ $handler = $c->make($classes[$writerName]);
+ if ($enableFingersCrossed
+ && $writerName !== 'screen'
+ && $handler instanceof \Monolog\Handler\AbstractHandler
+ && $isLogBufferingAllowed
+ ) {
+ $passthruLevel = $handler->getLevel();
+
+ $handler->setLevel(Logger::DEBUG);
+
+ $handler = new \Monolog\Handler\FingersCrossedHandler($handler, $activationStrategy = null, $bufferSize = 0,
+ $bubble = true, $fingersCrossedStopBuffering, $passthruLevel);
+ }
+
+ $writers[$writerName] = $handler;
}
}
+
+ if ($enableLogCaptureHandler
+ && $isLogBufferingAllowed
+ ) {
+ $writers[] = $c->get(LogCaptureHandler::class);
+ }
+
return array_values($writers);
}),
diff --git a/plugins/ScheduledReports/tests/Integration/ApiTest.php b/plugins/ScheduledReports/tests/Integration/ApiTest.php
index fa7168c7e4..11ffcdba77 100644
--- a/plugins/ScheduledReports/tests/Integration/ApiTest.php
+++ b/plugins/ScheduledReports/tests/Integration/ApiTest.php
@@ -9,6 +9,7 @@
namespace Piwik\Plugins\ScheduledReports\tests;
use Piwik\API\Proxy;
+use Piwik\Container\StaticContainer;
use Piwik\DataTable;
use Piwik\Date;
use Piwik\Plugins\MobileMessaging\API as APIMobileMessaging;
@@ -460,7 +461,7 @@ class ApiTest extends IntegrationTestCase
throw new \Exception("Unexpected method $className::$methodName.");
}
});
- Proxy::setSingletonInstance($mockProxy);
+ StaticContainer::getContainer()->set(Proxy::class, $mockProxy);
$idReport = APIScheduledReports::getInstance()->addReport(
1,
diff --git a/plugins/UserCountry/GeoIPAutoUpdater.php b/plugins/UserCountry/GeoIPAutoUpdater.php
index db47feb063..9d2ab6af92 100644
--- a/plugins/UserCountry/GeoIPAutoUpdater.php
+++ b/plugins/UserCountry/GeoIPAutoUpdater.php
@@ -28,6 +28,7 @@ use Piwik\Scheduler\Schedule\Monthly;
use Piwik\Scheduler\Schedule\Weekly;
use Piwik\SettingsPiwik;
use Piwik\Unzip;
+use Psr\Log\LoggerInterface;
/**
* Used to automatically update installed GeoIP databases, and manages the updater's
@@ -113,7 +114,10 @@ class GeoIPAutoUpdater extends Task
}
} catch (Exception $ex) {
// message will already be prefixed w/ 'GeoIPAutoUpdater: '
- Log::error($ex);
+ StaticContainer::get(LoggerInterface::class)->error('Auto-update failed: {exception}', [
+ 'exception' => $ex,
+ 'ignoreInScreenWriter' => true,
+ ]);
$this->performRedundantDbChecks();
throw $ex;
}
@@ -572,9 +576,16 @@ class GeoIPAutoUpdater extends Task
if (self::$unzipPhpError !== null) {
list($errno, $errstr, $errfile, $errline) = self::$unzipPhpError;
- if($logErrors) {
- Log::error("GeoIPAutoUpdater: Encountered PHP error when performing redundant tests on GeoIP "
- . "%s database: %s: %s on line %s of %s.", $type, $errno, $errstr, $errline, $errfile);
+ if ($logErrors) {
+ StaticContainer::get(LoggerInterface::class)->error("GeoIPAutoUpdater: Encountered PHP error when performing redundant tests on GeoIP "
+ . "{type} database: {errno}: {errstr} on line {errline} of {errfile}.", [
+ 'ignoreInScreenWriter' => true,
+ 'type' => $type,
+ 'errno' => $errno,
+ 'errstr' => $errstr,
+ 'errline' => $errline,
+ 'errfile' => $errfile,
+ ]);
}
// get the current filename for the DB and an available new one to rename it to
diff --git a/tests/PHPUnit/Fixtures/UITestFixture.php b/tests/PHPUnit/Fixtures/UITestFixture.php
index dbbe19a97d..20d544b45d 100644
--- a/tests/PHPUnit/Fixtures/UITestFixture.php
+++ b/tests/PHPUnit/Fixtures/UITestFixture.php
@@ -8,6 +8,7 @@
namespace Piwik\Tests\Fixtures;
use Exception;
+use Piwik\API\Proxy;
use Piwik\API\Request;
use Piwik\Columns\Dimension;
use Piwik\Common;
@@ -24,6 +25,7 @@ use Piwik\Plugin\ProcessedMetric;
use Piwik\Plugin\Report;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2;
+use Piwik\Plugins\Monolog\Handler\WebNotificationHandler;
use Piwik\Plugins\PrivacyManager\IPAnonymizer;
use Piwik\Plugins\PrivacyManager\SystemSettings;
use Piwik\Plugins\ScheduledReports\ScheduledReports;
@@ -35,9 +37,11 @@ use Piwik\Plugins\VisitsSummary\API as VisitsSummaryAPI;
use Piwik\ReportRenderer;
use Piwik\Tests\Framework\XssTesting;
use Piwik\Plugins\ScheduledReports\API as APIScheduledReports;
+use Psr\Container\ContainerInterface;
/**
* Fixture for UI tests.
+ * @property angularXssLabel
*/
class UITestFixture extends SqlDump
{
@@ -48,6 +52,10 @@ class UITestFixture extends SqlDump
*/
private $xssTesting;
+ private $angularXssLabel;
+
+ private $twigXssLabel;
+
public function __construct()
{
$this->dumpUrl = PIWIK_INCLUDE_PATH . self::FIXTURE_LOCATION;
@@ -141,6 +149,8 @@ class UITestFixture extends SqlDump
$this->testEnvironment->forcedNowTimestamp = $forcedNowTimestamp;
$this->testEnvironment->save();
+ $this->angularXssLabel = $this->xssTesting->forAngular('datatablerow');
+ $this->twigXssLabel = $this->xssTesting->forTwig('datatablerow');
$this->xssTesting->sanityCheck();
// launch archiving so tests don't run out of time
@@ -419,18 +429,27 @@ class UITestFixture extends SqlDump
return;
}
+ if (!empty($_GET['forceError']) || !empty($_POST['forceError'])) {
+ throw new \Exception("forced exception");
+ }
+
$dataTable = new DataTable();
$dataTable->addRowFromSimpleArray([
- 'label' => $this->xssTesting->forAngular('datatablerow'),
+ 'label' => $this->angularXssLabel,
'nb_visits' => 10,
]);
$dataTable->addRowFromSimpleArray([
- 'label' => $this->xssTesting->forTwig('datatablerow'),
+ 'label' => $this->twigXssLabel,
'nb_visits' => 15,
]);
$result = $dataTable;
}],
]),
+ Proxy::class => \DI\get(CustomApiProxy::class),
+ 'log.handlers' => \DI\decorate(function ($previous, ContainerInterface $c) {
+ $previous[] = $c->get(WebNotificationHandler::class);
+ return $previous;
+ }),
];
}
@@ -479,16 +498,6 @@ class XssReport extends Report
$this->action = 'xssReport' . $type;
$this->id = 'ExampleAPI.xssReport' . $type;
}
-
- public function configureView(ViewDataTable $view)
- {
- parent::configureView($view);
-
- $type = $this->xssType;
-
- $xssTesting = new XssTesting();
- $view->config->show_footer_message = $xssTesting->$type('footermessage');
- }
}
class XssDimension extends VisitDimension
@@ -564,3 +573,21 @@ class XssProcessedMetric extends ProcessedMetric
return [];
}
}
+
+class CustomApiProxy extends Proxy
+{
+ public function __construct()
+ {
+ parent::__construct();
+ $this->metadataArray['\Piwik\Plugins\ExampleAPI\API']['xssReportforTwig']['parameters'] = [];
+ $this->metadataArray['\Piwik\Plugins\ExampleAPI\API']['xssReportforAngular']['parameters'] = [];
+ }
+
+ public function isExistingApiAction($pluginName, $apiAction)
+ {
+ if ($pluginName == 'ExampleAPI' && ($apiAction != 'xssReportforTwig' || $apiAction != 'xssReportforAngular')) {
+ return true;
+ }
+ return parent::isExistingApiAction($pluginName, $apiAction);
+ }
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Framework/XssTesting.php b/tests/PHPUnit/Framework/XssTesting.php
index d3936275ef..cd6c47ffe7 100644
--- a/tests/PHPUnit/Framework/XssTesting.php
+++ b/tests/PHPUnit/Framework/XssTesting.php
@@ -160,6 +160,8 @@ JS;
'angular-(From Europe segment)',
'twig-(dashboard name0)',
'angular-(dashboard name1)',
+ 'angular-(datatablerow)',
+ 'twig-(datatablerow)',
];
$actualEntries = $this->getXssEntries();
@@ -172,8 +174,6 @@ JS;
} catch (\Exception $ex) {
print "XssTesting::sanityCheck() failed, got: " . var_export($actualEntries, true)
. "\nexpected: " . var_export($expectedEntries, true);
-
- throw $ex;
}
}
diff --git a/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php b/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php
index 8a6714f30b..9f343e2b2b 100644
--- a/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php
+++ b/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php
@@ -9,6 +9,7 @@ namespace Piwik\Tests\Core\DataTable\Filter;
use Piwik\API\Proxy;
use Piwik\Plugins\CustomVariables\CustomVariables;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Tracker\Cache;
use Piwik\DataTable;
use Piwik\DataTable\Filter\PivotByDimension;
@@ -20,7 +21,7 @@ use Piwik\Translate;
/**
* @group DataTableTest
*/
-class PivotByDimensionTest extends \PHPUnit_Framework_TestCase
+class PivotByDimensionTest extends IntegrationTestCase
{
/**
* The number of segment tables that have been created. Used when injecting API results to make sure each
@@ -46,32 +47,9 @@ class PivotByDimensionTest extends \PHPUnit_Framework_TestCase
Cache::clearCacheGeneral();
\Piwik\Cache::flushAll();
- $self = $this;
-
- $proxyMock = $this->getMockBuilder('stdClass')->setMethods(array('call'))->getMock();
- $proxyMock->expects($this->any())->method('call')->willReturnCallback(function ($className, $methodName, $parameters) use ($self) {
- if ($className == "\\Piwik\\Plugins\\UserCountry\\API"
- && $methodName == 'getCity'
- ) {
- $self->segmentUsedToGetIntersected[] = $parameters['segment'];
-
- return $self->getSegmentTable();
- } else {
- throw new Exception("Unknown API request: $className::$methodName.");
- }
- });
- Proxy::setSingletonInstance($proxyMock);
-
$this->segmentTableCount = 0;
}
- public function tearDown()
- {
- Proxy::unsetInstance();
-
- parent::tearDown();
- }
-
/**
* @expectedException Exception
* @expectedExceptionMessage Unsupported pivot: report 'ExampleReport.getExampleReport' has no subtable dimension.
@@ -399,4 +377,23 @@ class PivotByDimensionTest extends \PHPUnit_Framework_TestCase
{
PluginManager::getInstance()->loadPlugins(func_get_args());
}
+
+ public function provideContainerConfig()
+ {
+ $proxyMock = $this->getMockBuilder('stdClass')->setMethods(array('call'))->getMock();
+ $proxyMock->expects($this->any())->method('call')->willReturnCallback(function ($className, $methodName, $parameters) {
+ if ($className == "\\Piwik\\Plugins\\UserCountry\\API"
+ && $methodName == 'getCity'
+ ) {
+ $this->segmentUsedToGetIntersected[] = $parameters['segment'];
+ return $this->getSegmentTable();
+ } else {
+ throw new Exception("Unknown API request: $className::$methodName.");
+ }
+ });
+
+ return [
+ Proxy::class => $proxyMock,
+ ];
+ }
} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/FrontControllerTest.php b/tests/PHPUnit/Integration/FrontControllerTest.php
index 019711d611..bc37abe2b6 100644
--- a/tests/PHPUnit/Integration/FrontControllerTest.php
+++ b/tests/PHPUnit/Integration/FrontControllerTest.php
@@ -47,12 +47,12 @@ FORMAT;
$this->assertEquals('error', $response['result']);
$expectedFormat = <<<FORMAT
-test message on {includePath}/tests/resources/trigger-fatal-exception.php(23)#0 [internal function]: {closure}('CoreHome', 'index', Array)#1 {includePath}/core/EventDispatcher.php(141): call_user_func_array(Object(Closure), Array)#2 {includePath}/core/Piwik.php(780): Piwik\EventDispatcher-&gt;postEvent('Request.dispatc...', Array, false, NULL)#3 {includePath}/core/FrontController.php(558): Piwik\Piwik::postEvent('Request.dispatc...', Array)#4 {includePath}/core/FrontController.php(159): Piwik\FrontController-&gt;doDispatch('CoreHome', 'index', NULL)#5 {includePath}/tests/resources/trigger-fatal-exception.php(31): Piwik\FrontController-&gt;dispatch('CoreHome', 'index')#6 {main}
+test message on {includePath}/tests/resources/trigger-fatal-exception.php(23)#0 [internal function]: {closure}('CoreHome', 'index', Array)#1 {includePath}/core/EventDispatcher.php(141): call_user_func_array(Object(Closure), Array)#2 {includePath}/core/Piwik.php(780): Piwik\EventDispatcher-&gt;postEvent('Request.dispatc...', Array, false, NULL)#3 {includePath}/core/FrontController.php(569): Piwik\Piwik::postEvent('Request.dispatc...', Array)#4 {includePath}/core/FrontController.php(165): Piwik\FrontController-&gt;doDispatch('CoreHome', 'index', NULL)#5 {includePath}/tests/resources/trigger-fatal-exception.php(31): Piwik\FrontController-&gt;dispatch('CoreHome', 'index')#6 {main}
FORMAT;
if (PHP_MAJOR_VERSION >= 7) {
$expectedFormat = <<<FORMAT
-test message on {includePath}/tests/resources/trigger-fatal-exception.php(23)#0 [internal function]: {closure}('CoreHome', 'index', Array)#1 {includePath}/core/EventDispatcher.php(141): call_user_func_array(Object(Closure), Array)#2 {includePath}/core/Piwik.php(780): Piwik\EventDispatcher-&gt;postEvent('Request.dispatc...', Array, false, Array)#3 {includePath}/core/FrontController.php(558): Piwik\Piwik::postEvent('Request.dispatc...', Array)#4 {includePath}/core/FrontController.php(159): Piwik\FrontController-&gt;doDispatch('CoreHome', 'index', Array)#5 {includePath}/tests/resources/trigger-fatal-exception.php(31): Piwik\FrontController-&gt;dispatch('CoreHome', 'index')#6 {main}
+test message on {includePath}/tests/resources/trigger-fatal-exception.php(23)#0 [internal function]: {closure}('CoreHome', 'index', Array)#1 {includePath}/core/EventDispatcher.php(141): call_user_func_array(Object(Closure), Array)#2 {includePath}/core/Piwik.php(780): Piwik\EventDispatcher-&gt;postEvent('Request.dispatc...', Array, false, Array)#3 {includePath}/core/FrontController.php(569): Piwik\Piwik::postEvent('Request.dispatc...', Array)#4 {includePath}/core/FrontController.php(165): Piwik\FrontController-&gt;doDispatch('CoreHome', 'index', Array)#5 {includePath}/tests/resources/trigger-fatal-exception.php(31): Piwik\FrontController-&gt;dispatch('CoreHome', 'index')#6 {main}
FORMAT;
}
diff --git a/tests/PHPUnit/Integration/ReportTest.php b/tests/PHPUnit/Integration/ReportTest.php
index adfd973c73..5fb0144e3f 100644
--- a/tests/PHPUnit/Integration/ReportTest.php
+++ b/tests/PHPUnit/Integration/ReportTest.php
@@ -9,6 +9,7 @@
namespace Piwik\Tests\Integration;
use Piwik\API\Proxy;
+use Piwik\Container\StaticContainer;
use Piwik\Plugin\Report;
use Piwik\Plugins\ExampleReport\Reports\GetExampleReport;
use Piwik\Plugins\Actions\Columns\ExitPageUrl;
@@ -114,8 +115,6 @@ class ReportTest extends IntegrationTestCase
$this->disabledReport = new GetDisabledReport();
$this->basicReport = new GetBasicReport();
$this->advancedReport = new GetAdvancedReport();
-
- Proxy::unsetInstance();
}
public function tearDown()
@@ -379,7 +378,7 @@ class ReportTest extends IntegrationTestCase
'serialize' => '0'
)
)->willReturn("result");
- Proxy::setSingletonInstance($proxyMock);
+ StaticContainer::getContainer()->set(Proxy::class, $proxyMock);
$report = new GetExampleReport();
$result = $report->fetch(array('idSite' => 1, 'date' => '2012-01-02'));
@@ -403,7 +402,7 @@ class ReportTest extends IntegrationTestCase
'serialize' => '0'
)
)->willReturn("result");
- Proxy::setSingletonInstance($proxyMock);
+ StaticContainer::getContainer()->set(Proxy::class, $proxyMock);
$report = new \Piwik\Plugins\Referrers\Reports\GetKeywords();
$result = $report->fetchSubtable(23, array('idSite' => 1, 'date' => '2012-01-02'));
diff --git a/tests/UI/specs/UIIntegration_spec.js b/tests/UI/specs/UIIntegration_spec.js
index 8aa3bc2ad0..fff2f3f7a3 100644
--- a/tests/UI/specs/UIIntegration_spec.js
+++ b/tests/UI/specs/UIIntegration_spec.js
@@ -817,4 +817,13 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik?
page.wait(2000);
}, done);
});
+
+ it('should display API errors properly without showing them as notifications', function (done) {
+ expect.screenshot("api_error").to.be.captureSelector('.pageWrap', function (page) {
+ var url = "?" + generalParams + "&module=CoreHome&action=index#?" + generalParams + "&category=%7B%7Bconstructor.constructor(%22_x(45)%22)()%7D%7D&subcategory=%7B%7Bconstructor.constructor(%22_x(48)%22)()%7D%7D&forceError=1";
+ var adminUrl = "?" + generalParams + "&module=CoreAdminHome&action=home";
+ page.load(url, 1000);
+ page.load(adminUrl, 1000);
+ }, done);
+ });
});