mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Rework FPM tests logging for better debugging
This commit is contained in:
parent
3477499d26
commit
1e8fa6607d
11 changed files with 1360 additions and 702 deletions
|
@ -50,17 +50,11 @@ for ($interval = 0; $interval < $max_interval; $interval += $step) {
|
||||||
usleep($interval);
|
usleep($interval);
|
||||||
}
|
}
|
||||||
echo "Reached interval $interval us with $step us steps\n";
|
echo "Reached interval $interval us with $step us steps\n";
|
||||||
$tester->expectLogNotice('Reloading in progress ...');
|
$tester->readAllLogNotices('Reloading in progress ...');
|
||||||
/* Consume mix of 'Reloading in progress ...' and 'reloading: .*' */
|
|
||||||
$tester->getLogLines(2000);
|
|
||||||
|
|
||||||
$tester->signal('USR2');
|
$tester->reload();
|
||||||
$tester->expectLogNotice('Reloading in progress ...');
|
$tester->expectLogReloadingNotices();
|
||||||
$tester->expectLogNotice('reloading: .*');
|
|
||||||
$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
|
|
||||||
$tester->expectLogStartNotices();
|
|
||||||
$tester->ping('{{ADDR}}');
|
$tester->ping('{{ADDR}}');
|
||||||
|
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
$tester->expectLogTerminatingNotices();
|
$tester->expectLogTerminatingNotices();
|
||||||
$tester->close();
|
$tester->close();
|
||||||
|
|
|
@ -63,18 +63,10 @@ for ($interval = 0; $interval < $max_interval; $interval += $step) {
|
||||||
echo "Reached interval $interval us with $step us steps\n";
|
echo "Reached interval $interval us with $step us steps\n";
|
||||||
$tester->expectLogNotice('Reloading in progress ...');
|
$tester->expectLogNotice('Reloading in progress ...');
|
||||||
/* Consume mix of 'Reloading in progress ...' and 'reloading: .*' */
|
/* Consume mix of 'Reloading in progress ...' and 'reloading: .*' */
|
||||||
$skipped = $tester->getLogLines(2000);
|
$tester->readAllLogNotices('Reloading in progress ...');
|
||||||
|
|
||||||
$tester->signal('USR2');
|
$tester->reload();
|
||||||
$tester->expectLogNotice('Reloading in progress ...');
|
$tester->expectLogReloadingNotices();
|
||||||
/* When a child ignores SIGQUIT, the following expectation fails due to timeout. */
|
|
||||||
if (!$tester->expectLogNotice('reloading: .*')) {
|
|
||||||
/* for troubleshooting */
|
|
||||||
echo "Skipped messages\n";
|
|
||||||
echo implode('', $skipped);
|
|
||||||
}
|
|
||||||
$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
|
|
||||||
$tester->expectLogStartNotices();
|
|
||||||
|
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
$tester->expectLogTerminatingNotices();
|
$tester->expectLogTerminatingNotices();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--TEST--
|
--TEST--
|
||||||
FPM: GH-8885 - access.log with stderr begins to write logs to error_log after daemon reload
|
FPM: GH-8885 - access.log with stderr begins to write logs to error_log after reloading logs
|
||||||
--SKIPIF--
|
--SKIPIF--
|
||||||
<?php
|
<?php
|
||||||
include "skipif.inc";
|
include "skipif.inc";
|
||||||
|
@ -26,60 +26,25 @@ pm.min_spare_servers = 1
|
||||||
pm.max_spare_servers = 3
|
pm.max_spare_servers = 3
|
||||||
EOT;
|
EOT;
|
||||||
|
|
||||||
// php-fpm must not be launched with --force-stderr option
|
$tester = new FPM\Tester($cfg);
|
||||||
$tester = new FPM\Tester($cfg, '', [FPM\Tester::PHP_FPM_DISABLE_FORCE_STDERR => true]);
|
$tester->start(forceStderr: false);
|
||||||
// getPrefixedFile('err.log') is the same path that returns processTemplate('{{FILE:LOG}}')
|
|
||||||
$errorLogFile = $tester->getPrefixedFile('err.log');
|
|
||||||
|
|
||||||
$tester->start();
|
|
||||||
$tester->expectNoLogMessages();
|
$tester->expectNoLogMessages();
|
||||||
|
$tester->switchLogSource('{{FILE:LOG}}');
|
||||||
$content = file_get_contents($errorLogFile);
|
$tester->expectLogStartNotices();
|
||||||
assert($content !== false && strlen($content) > 0, 'File must not be empty');
|
$tester->ping();
|
||||||
|
$tester->switchLogSource('{{MASTER:OUT}}');
|
||||||
$errorLogLines = explode("\n", $content);
|
$tester->expectLogPattern('/127.0.0.1 .* "GET \/ping" 200/');
|
||||||
array_pop($errorLogLines);
|
$tester->reloadLogs();
|
||||||
|
$tester->switchLogSource('{{FILE:LOG}}');
|
||||||
assert(count($errorLogLines) === 2, 'Expected 2 records in the error_log file');
|
$tester->expectLogReloadingLogsNotices();
|
||||||
assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid'));
|
$tester->ping();
|
||||||
assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections'));
|
$tester->switchLogSource('{{MASTER:OUT}}');
|
||||||
|
$tester->expectLogPattern('/127.0.0.1 .* "GET \/ping" 200/');
|
||||||
$tester->ping('{{ADDR}}');
|
$tester->switchLogSource('{{FILE:LOG}}');
|
||||||
$stderrLines = $tester->getLogLines(-1);
|
|
||||||
assert(count($stderrLines) === 1, 'Expected 1 record in the stderr output (access.log)');
|
|
||||||
$stderrLine = $stderrLines[0];
|
|
||||||
assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLine), 'Incorrect format of access.log record');
|
|
||||||
|
|
||||||
$tester->signal('USR1');
|
|
||||||
$tester->expectNoLogMessages();
|
|
||||||
|
|
||||||
$content = file_get_contents($errorLogFile);
|
|
||||||
assert($content !== false && strlen($content) > 0, 'File must not be empty');
|
|
||||||
$errorLogLines = explode("\n", $content);
|
|
||||||
array_pop($errorLogLines);
|
|
||||||
|
|
||||||
assert(count($errorLogLines) >= 4, 'Expected at least 4 records in the error_log file');
|
|
||||||
assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid'));
|
|
||||||
assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections'));
|
|
||||||
assert(strpos($errorLogLines[2], 'NOTICE: error log file re-opened'));
|
|
||||||
assert(strpos($errorLogLines[3], 'NOTICE: access log file re-opened'));
|
|
||||||
|
|
||||||
|
|
||||||
$tester->ping('{{ADDR}}');
|
|
||||||
$stderrLines = $tester->getLogLines(-1);
|
|
||||||
assert(count($stderrLines) === 1, 'Must be only 1 record in the access.log');
|
|
||||||
assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLines[0]), 'Incorrect format of access.log record');
|
|
||||||
|
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
$stderrLines = $tester->expectNoLogMessages();
|
$tester->expectLogTerminatingNotices();
|
||||||
|
$tester->switchLogSource('{{MASTER:OUT}}');
|
||||||
$content = file_get_contents($errorLogFile);
|
$tester->expectNoLogMessages();
|
||||||
assert($content !== false && strlen($content) > 0, 'File must not be empty');
|
|
||||||
$errorLogLines = explode("\n", $content);
|
|
||||||
array_pop($errorLogLines);
|
|
||||||
$errorLogLastLine = array_pop($errorLogLines);
|
|
||||||
assert(strpos($errorLogLastLine, 'NOTICE: exiting, bye-bye'));
|
|
||||||
|
|
||||||
$tester->close();
|
$tester->close();
|
||||||
?>
|
?>
|
||||||
Done
|
Done
|
||||||
|
|
|
@ -26,60 +26,25 @@ pm.min_spare_servers = 1
|
||||||
pm.max_spare_servers = 3
|
pm.max_spare_servers = 3
|
||||||
EOT;
|
EOT;
|
||||||
|
|
||||||
// php-fpm must not be launched with --force-stderr option
|
$tester = new FPM\Tester($cfg);
|
||||||
$tester = new FPM\Tester($cfg, '', [FPM\Tester::PHP_FPM_DISABLE_FORCE_STDERR => true]);
|
$tester->start(forceStderr: false);
|
||||||
// getPrefixedFile('err.log') is the same path that returns processTemplate('{{FILE:LOG}}')
|
|
||||||
$errorLogFile = $tester->getPrefixedFile('err.log');
|
|
||||||
|
|
||||||
$tester->start();
|
|
||||||
$tester->expectNoLogMessages();
|
$tester->expectNoLogMessages();
|
||||||
|
$tester->switchLogSource('{{FILE:LOG}}');
|
||||||
$content = file_get_contents($errorLogFile);
|
$tester->expectLogStartNotices();
|
||||||
assert($content !== false && strlen($content) > 0, 'File must not be empty');
|
$tester->ping();
|
||||||
|
$tester->switchLogSource('{{MASTER:OUT}}');
|
||||||
$errorLogLines = explode("\n", $content);
|
$tester->expectLogPattern('/127.0.0.1 .* "GET \/ping" 200/');
|
||||||
array_pop($errorLogLines);
|
$tester->reload();
|
||||||
|
$tester->switchLogSource('{{FILE:LOG}}');
|
||||||
assert(count($errorLogLines) === 2, 'Expected 2 records in the error_log file');
|
$tester->expectLogReloadingNotices();
|
||||||
assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid'));
|
$tester->ping();
|
||||||
assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections'));
|
$tester->switchLogSource('{{MASTER:OUT}}');
|
||||||
|
$tester->expectLogPattern('/127.0.0.1 .* "GET \/ping" 200/');
|
||||||
$tester->ping('{{ADDR}}');
|
$tester->switchLogSource('{{FILE:LOG}}');
|
||||||
$stderrLines = $tester->getLogLines(-1);
|
|
||||||
assert(count($stderrLines) === 1, 'Expected 1 record in the stderr output (access.log)');
|
|
||||||
$stderrLine = $stderrLines[0];
|
|
||||||
assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLine), 'Incorrect format of access.log record');
|
|
||||||
|
|
||||||
$tester->signal('USR2');
|
|
||||||
$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
|
|
||||||
|
|
||||||
$content = file_get_contents($errorLogFile);
|
|
||||||
assert($content !== false && strlen($content) > 0, 'File must not be empty');
|
|
||||||
$errorLogLines = explode("\n", $content);
|
|
||||||
array_pop($errorLogLines);
|
|
||||||
|
|
||||||
assert(count($errorLogLines) >= 5, 'Expected at least 5 records in the error_log file');
|
|
||||||
assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid'));
|
|
||||||
assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections'));
|
|
||||||
assert(strpos($errorLogLines[2], 'NOTICE: Reloading in progress'));
|
|
||||||
assert(strpos($errorLogLines[3], 'NOTICE: reloading: execvp'));
|
|
||||||
assert(strpos($errorLogLines[4], 'NOTICE: using inherited socket'));
|
|
||||||
|
|
||||||
$tester->ping('{{ADDR}}');
|
|
||||||
$stderrLines = $tester->getLogLines(-1);
|
|
||||||
assert(count($stderrLines) === 1, 'Must be only 1 record in the access.log');
|
|
||||||
assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLines[0]), 'Incorrect format of access.log record');
|
|
||||||
|
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
$stderrLines = $tester->expectNoLogMessages();
|
$tester->expectLogTerminatingNotices();
|
||||||
|
$tester->switchLogSource('{{MASTER:OUT}}');
|
||||||
$content = file_get_contents($errorLogFile);
|
$tester->expectNoLogMessages();
|
||||||
assert($content !== false && strlen($content) > 0, 'File must not be empty');
|
|
||||||
$errorLogLines = explode("\n", $content);
|
|
||||||
array_pop($errorLogLines);
|
|
||||||
$errorLogLastLine = array_pop($errorLogLines);
|
|
||||||
assert(strpos($errorLogLastLine, 'NOTICE: exiting, bye-bye'));
|
|
||||||
|
|
||||||
$tester->close();
|
$tester->close();
|
||||||
?>
|
?>
|
||||||
Done
|
Done
|
||||||
|
|
|
@ -30,9 +30,8 @@ $tester = new FPM\Tester($cfg, $code);
|
||||||
$tester->start();
|
$tester->start();
|
||||||
$tester->expectLogStartNotices();
|
$tester->expectLogStartNotices();
|
||||||
$tester->request()->expectEmptyBody();
|
$tester->request()->expectEmptyBody();
|
||||||
$lines = $tester->getLogLines(2);
|
$tester->expectLogLine(str_repeat('a', 1021) . "\0f", decorated: false);
|
||||||
var_dump($lines[0] === str_repeat('a', 1021) . "\0f\n");
|
$tester->expectLogLine("abc", decorated: false);
|
||||||
var_dump($lines[1] === "abc\n");
|
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
$tester->expectLogTerminatingNotices();
|
$tester->expectLogTerminatingNotices();
|
||||||
$tester->close();
|
$tester->close();
|
||||||
|
@ -40,8 +39,6 @@ $tester->close();
|
||||||
?>
|
?>
|
||||||
Done
|
Done
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
bool(true)
|
|
||||||
bool(true)
|
|
||||||
Done
|
Done
|
||||||
--CLEAN--
|
--CLEAN--
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -31,13 +31,12 @@ $tester->start();
|
||||||
$tester->expectLogStartNotices();
|
$tester->expectLogStartNotices();
|
||||||
$tester->request()->expectEmptyBody();
|
$tester->request()->expectEmptyBody();
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
var_dump($tester->getLastLogLine() === str_repeat('a', 1022) . "\n");
|
$tester->expectLogLine(str_repeat('a', 1022), decorated: false);
|
||||||
$tester->close();
|
$tester->close();
|
||||||
|
|
||||||
?>
|
?>
|
||||||
Done
|
Done
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
bool(true)
|
|
||||||
Done
|
Done
|
||||||
--CLEAN--
|
--CLEAN--
|
||||||
<?php
|
<?php
|
||||||
|
|
|
@ -33,17 +33,14 @@ $tester->start();
|
||||||
$tester->expectLogStartNotices();
|
$tester->expectLogStartNotices();
|
||||||
$tester->request()->expectEmptyBody();
|
$tester->request()->expectEmptyBody();
|
||||||
$tester->terminate();
|
$tester->terminate();
|
||||||
var_dump($tester->getLastLogLine() === str_repeat('a', 100) . str_repeat('b', 923) . "\n");
|
$tester->expectLogLine(str_repeat('a', 100) . str_repeat('b', 923), decorated: false);
|
||||||
var_dump($tester->getLastLogLine() === str_repeat('b', 1023) . "\n");
|
$tester->expectLogLine(str_repeat('b', 1023), decorated: false);
|
||||||
var_dump($tester->getLastLogLine() === str_repeat('b', 554) . "\n");
|
$tester->expectLogLine(str_repeat('b', 554), decorated: false);
|
||||||
$tester->close();
|
$tester->close();
|
||||||
|
|
||||||
?>
|
?>
|
||||||
Done
|
Done
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
bool(true)
|
|
||||||
bool(true)
|
|
||||||
bool(true)
|
|
||||||
Done
|
Done
|
||||||
--CLEAN--
|
--CLEAN--
|
||||||
<?php
|
<?php
|
||||||
|
|
336
sapi/fpm/tests/logreader.inc
Normal file
336
sapi/fpm/tests/logreader.inc
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FPM;
|
||||||
|
|
||||||
|
class LogReader
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Log debugging.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private bool $debug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log descriptor.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private ?string $currentSourceName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log descriptors.
|
||||||
|
*
|
||||||
|
* @var LogSource[]
|
||||||
|
*/
|
||||||
|
private array $sources = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log reader constructor.
|
||||||
|
*
|
||||||
|
* @param bool $debug
|
||||||
|
*/
|
||||||
|
public function __construct(bool $debug = false)
|
||||||
|
{
|
||||||
|
$this->debug = $debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns log descriptor source.
|
||||||
|
*
|
||||||
|
* @return LogSource
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function getSource(): LogSource
|
||||||
|
{
|
||||||
|
if ( ! $this->currentSourceName) {
|
||||||
|
throw new \Exception('Log descriptor is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->sources[$this->currentSourceName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set current stream source and create it if it does not exist.
|
||||||
|
*
|
||||||
|
* @param string $name Stream name.
|
||||||
|
* @param resource $stream The actual stream.
|
||||||
|
*/
|
||||||
|
public function setStreamSource(string $name, $stream)
|
||||||
|
{
|
||||||
|
$this->currentSourceName = $name;
|
||||||
|
if ( ! isset($this->sources[$name])) {
|
||||||
|
$this->sources[$name] = new LogStreamSource($stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set file source as current and create it if it does not exist.
|
||||||
|
*
|
||||||
|
* @param string $name Source name.
|
||||||
|
* @param string $filePath Source file path.s
|
||||||
|
*/
|
||||||
|
public function setFileSource(string $name, string $filePath)
|
||||||
|
{
|
||||||
|
$this->currentSourceName = $name;
|
||||||
|
if ( ! isset($this->sources[$name])) {
|
||||||
|
$this->sources[$name] = new LogFileSource($filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single log line.
|
||||||
|
*
|
||||||
|
* @param int $timeoutSeconds
|
||||||
|
* @param int $timeoutMicroseconds
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getLine(int $timeoutSeconds = 3, int $timeoutMicroseconds = 0): ?string
|
||||||
|
{
|
||||||
|
$line = $this->getSource()->getLine($timeoutSeconds, $timeoutMicroseconds);
|
||||||
|
$this->trace(is_null($line) ? "LINE - null" : "LINE: $line");
|
||||||
|
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print separation line.
|
||||||
|
*/
|
||||||
|
public function printSeparator(): void
|
||||||
|
{
|
||||||
|
echo str_repeat('-', 68) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print all logs.
|
||||||
|
*/
|
||||||
|
public function printLogs(): void
|
||||||
|
{
|
||||||
|
$hasMultipleDescriptors = count($this->sources) > 1;
|
||||||
|
echo "LOGS:\n";
|
||||||
|
foreach ($this->sources as $name => $source) {
|
||||||
|
if ($hasMultipleDescriptors) {
|
||||||
|
echo ">>> source: $name\n";
|
||||||
|
}
|
||||||
|
$this->printSeparator();
|
||||||
|
foreach ($source->getAllLines() as $line) {
|
||||||
|
echo $line;
|
||||||
|
}
|
||||||
|
$this->printSeparator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print error and logs.
|
||||||
|
*
|
||||||
|
* @param string|null $errorMessage Error message to print before the logs.
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
private function printError(?string $errorMessage): bool
|
||||||
|
{
|
||||||
|
if (is_null($errorMessage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
echo "ERROR: " . $errorMessage . "\n\n";
|
||||||
|
$this->printLogs();
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read log until matcher matches the log message or there are no more logs.
|
||||||
|
*
|
||||||
|
* @param callable $matcher Callback to identify a match
|
||||||
|
* @param string|null $notFoundMessage Error message if matcher does not succeed.
|
||||||
|
* @param bool $checkAllLogs Whether to also check past logs.
|
||||||
|
* @param int $timeoutSeconds Timeout in seconds for reading of all messages.
|
||||||
|
* @param int $timeoutMicroseconds Additional timeout in microseconds for reading of all messages.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function readUntil(
|
||||||
|
callable $matcher,
|
||||||
|
string $notFoundMessage = null,
|
||||||
|
bool $checkAllLogs = false,
|
||||||
|
int $timeoutSeconds = 3,
|
||||||
|
int $timeoutMicroseconds = 0
|
||||||
|
): bool {
|
||||||
|
$startTime = microtime(true);
|
||||||
|
$endTime = $startTime + $timeoutSeconds + ($timeoutMicroseconds / 1_000_000);
|
||||||
|
if ($checkAllLogs) {
|
||||||
|
foreach ($this->getSource()->getAllLines() as $line) {
|
||||||
|
if ($matcher($line)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (microtime(true) > $endTime) {
|
||||||
|
return $this->printError($notFoundMessage);
|
||||||
|
}
|
||||||
|
$line = $this->getLine($timeoutSeconds, $timeoutMicroseconds);
|
||||||
|
if ($line === null || microtime(true) > $endTime) {
|
||||||
|
return $this->printError($notFoundMessage);
|
||||||
|
}
|
||||||
|
} while ( ! $matcher($line));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print tracing message - only in debug .
|
||||||
|
*
|
||||||
|
* @param string $msg Message to print.
|
||||||
|
*/
|
||||||
|
private function trace(string $msg): void
|
||||||
|
{
|
||||||
|
if ($this->debug) {
|
||||||
|
print "LogReader - $msg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class LogSource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get single line from the source.
|
||||||
|
*
|
||||||
|
* @param int $timeoutSeconds Read timeout in seconds
|
||||||
|
* @param int $timeoutMicroseconds Read timeout in microseconds
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public abstract function getLine(int $timeoutSeconds, int $timeoutMicroseconds): ?string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all lines that has been returned by getLine() method.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public abstract function getAllLines(): array;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogStreamSource extends LogSource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var resource
|
||||||
|
*/
|
||||||
|
private $stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $lines = [];
|
||||||
|
|
||||||
|
public function __construct($stream)
|
||||||
|
{
|
||||||
|
$this->stream = $stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get single line from the stream.
|
||||||
|
*
|
||||||
|
* @param int $timeoutSeconds Read timeout in seconds
|
||||||
|
* @param int $timeoutMicroseconds Read timeout in microseconds
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getLine(int $timeoutSeconds, int $timeoutMicroseconds): ?string
|
||||||
|
{
|
||||||
|
if (feof($this->stream)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$read = [$this->stream];
|
||||||
|
$write = null;
|
||||||
|
$except = null;
|
||||||
|
if (stream_select($read, $write, $except, $timeoutSeconds, $timeoutMicroseconds)) {
|
||||||
|
$line = fgets($this->stream);
|
||||||
|
$this->lines[] = $line;
|
||||||
|
|
||||||
|
return $line;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all stream read lines.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getAllLines(): array
|
||||||
|
{
|
||||||
|
return $this->lines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogFileSource extends LogSource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private string $filePath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private int $position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private array $lines = [];
|
||||||
|
|
||||||
|
public function __construct(string $filePath)
|
||||||
|
{
|
||||||
|
$this->filePath = $filePath;
|
||||||
|
$this->position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get single line from the file.
|
||||||
|
*
|
||||||
|
* @param int $timeoutSeconds Read timeout in seconds
|
||||||
|
* @param int $timeoutMicroseconds Read timeout in microseconds
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getLine(int $timeoutSeconds, int $timeoutMicroseconds): ?string
|
||||||
|
{
|
||||||
|
$endTime = microtime(true) + $timeoutSeconds + ($timeoutMicroseconds / 1_000_000);
|
||||||
|
while ($this->position >= count($this->lines)) {
|
||||||
|
if (is_file($this->filePath)) {
|
||||||
|
$lines = file($this->filePath);
|
||||||
|
if ($lines === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$this->lines = $lines;
|
||||||
|
if ($this->position < count($lines)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usleep(50_000);
|
||||||
|
if (microtime(true) > $endTime) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->lines[$this->position++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all returned lines from the file.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getAllLines(): array
|
||||||
|
{
|
||||||
|
return array_slice($this->lines, 0, $this->position);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,8 @@ namespace FPM;
|
||||||
|
|
||||||
class LogTool
|
class LogTool
|
||||||
{
|
{
|
||||||
const P_TIME = '\[\d\d-\w\w\w-\d{4} \d\d:\d\d:\d\d\]';
|
const P_TIME = '\[\d\d-\w\w\w-\d{4} \d\d:\d\d:\d\d(?:\.\d+)?\]';
|
||||||
|
const P_DEBUG = '(?:pid \d+, (?:\w+|\(null\))\(\), line \d+: )?';
|
||||||
const P_PREFIX = '\[pool unconfined\] child \d+ said into stderr: ';
|
const P_PREFIX = '\[pool unconfined\] child \d+ said into stderr: ';
|
||||||
const P_PREFIX_STDOUT = '\[pool unconfined\] child \d+ said into stdout: ';
|
const P_PREFIX_STDOUT = '\[pool unconfined\] child \d+ said into stdout: ';
|
||||||
const FINAL_SUFFIX = ', pipe is closed';
|
const FINAL_SUFFIX = ', pipe is closed';
|
||||||
|
@ -16,46 +17,70 @@ class LogTool
|
||||||
const ALERT = 'ALERT';
|
const ALERT = 'ALERT';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $message;
|
private bool $debug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var LogReader
|
||||||
|
*/
|
||||||
|
private LogReader $logReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private $level;
|
private string $message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private ?string $level = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $position;
|
private int $position;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $suffixPosition;
|
private int $suffixPosition = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $limit;
|
private int $limit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
private $pattern;
|
private ?string $pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
private $error;
|
private ?string $error = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $pipeClosed = false;
|
private bool $pipeClosed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Log tool constructor.
|
||||||
|
*
|
||||||
|
* @param LogReader $logReader
|
||||||
|
* @param bool $debug
|
||||||
|
*/
|
||||||
|
public function __construct(LogReader $logReader, bool $debug = false)
|
||||||
|
{
|
||||||
|
$this->logReader = $logReader;
|
||||||
|
$this->debug = $debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set expected message for output logging.
|
||||||
|
*
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param int $limit
|
* @param int $limit
|
||||||
* @param int $repeat
|
* @param int $repeat
|
||||||
|
@ -68,15 +93,20 @@ class LogTool
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Set the expected logging level.
|
||||||
|
*
|
||||||
* @param string $level
|
* @param string $level
|
||||||
* @return int
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function setExpectedLevel(string $level)
|
public function setExpectedLevel(string $level): string
|
||||||
{
|
{
|
||||||
return $this->level = $level;
|
return $this->level = $level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the expected logging level.
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getExpectedLevel(): string
|
public function getExpectedLevel(): string
|
||||||
|
@ -85,22 +115,53 @@ class LogTool
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Set whether pipe closed error is shown.
|
||||||
|
*
|
||||||
* @param bool $pipeClosed
|
* @param bool $pipeClosed
|
||||||
*/
|
*/
|
||||||
public function setPipeClosed(bool $pipeClosed)
|
public function setPipeClosed(bool $pipeClosed): void
|
||||||
{
|
{
|
||||||
$this->pipeClosed = $pipeClosed;
|
$this->pipeClosed = $pipeClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Match the matcher checking the log lines using the callback.
|
||||||
|
*
|
||||||
|
* @param callable $matcher Callback checking whether the log line matches the expected message.
|
||||||
|
* @param string $notFoundMessage Error message to show if the message is not found.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function checkTruncatedMessage(string $line)
|
private function match(callable $matcher, string $notFoundMessage): bool
|
||||||
|
{
|
||||||
|
if ($this->getError()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->logReader->readUntil($matcher, $notFoundMessage)) {
|
||||||
|
$this->popError();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
echo $this->popError();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $line Log line to check against.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function checkTruncatedMessage(string $line = null): bool
|
||||||
{
|
{
|
||||||
if ($this->message === null) {
|
if ($this->message === null) {
|
||||||
throw new \LogicException('The message has not been set');
|
throw new \LogicException('The message has not been set');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$matcher = function (string $line) {
|
||||||
$lineLen = strlen($line);
|
$lineLen = strlen($line);
|
||||||
if ( ! $this->checkLineLength($line)) {
|
if ( ! $this->checkLineLength($line)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -129,44 +190,62 @@ class LogTool
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($line !== null) {
|
||||||
|
return $matcher($line);
|
||||||
|
} else {
|
||||||
|
return $this->match($matcher, 'Truncated message not found');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $lines
|
* Check wrapped message.
|
||||||
* @param bool $terminated
|
*
|
||||||
* @param bool $decorated
|
* @param bool $terminated Whether to check termination lines.
|
||||||
|
* @param bool $decorated Whether the output is decorated with prefix and suffix.
|
||||||
|
* @param bool $isStdErr Whether the message is written to stderr.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function checkWrappedMessage(array $lines, bool $terminated = true, bool $decorated = true, bool $is_stderr = true)
|
public function checkWrappedMessage(
|
||||||
{
|
bool $terminated = true,
|
||||||
|
bool $decorated = true,
|
||||||
|
bool $isStdErr = true,
|
||||||
|
): bool {
|
||||||
if ($this->message === null) {
|
if ($this->message === null) {
|
||||||
throw new \LogicException('The message has not been set');
|
throw new \LogicException('The message has not been set');
|
||||||
}
|
}
|
||||||
if ($decorated) {
|
if ($decorated) {
|
||||||
$this->pattern = sprintf(
|
$this->pattern = sprintf(
|
||||||
'/^(%s %s: %s)"([^"]*)"(.*)?$/',
|
'/^(%s %s: %s%s)"([^"]*)"(.*)?$/',
|
||||||
self::P_TIME,
|
self::P_TIME,
|
||||||
$this->getExpectedLevel(),
|
$this->getExpectedLevel(),
|
||||||
$is_stderr ? self::P_PREFIX : self::P_PREFIX_STDOUT
|
self::P_DEBUG,
|
||||||
|
$isStdErr ? self::P_PREFIX : self::P_PREFIX_STDOUT
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$this->pattern = null;
|
$this->pattern = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$idx = 0;
|
$matcher = fn(string $line) => $this->checkLine($line);
|
||||||
foreach ($lines as $idx => $line) {
|
|
||||||
if (!$this->checkLine($line)) {
|
while (strlen($this->message) !== $this->position) {
|
||||||
break;
|
if ( ! $this->match($matcher, 'Output message not found')) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->suffixPosition > 0) {
|
if ($this->suffixPosition > 0) {
|
||||||
|
$suffixMatcher = function ($line) use ($isStdErr) {
|
||||||
$suffixPattern = sprintf(
|
$suffixPattern = sprintf(
|
||||||
'/^%s %s: %s(.*)$/',
|
'/^%s %s: %s%s(.*)$/',
|
||||||
self::P_TIME, $this->getExpectedLevel(),
|
self::P_TIME,
|
||||||
$is_stderr ? self::P_PREFIX : self::P_PREFIX_STDOUT
|
$this->getExpectedLevel(),
|
||||||
|
self::P_DEBUG,
|
||||||
|
$isStdErr ? self::P_PREFIX : self::P_PREFIX_STDOUT
|
||||||
);
|
);
|
||||||
$line = $lines[++$idx];
|
|
||||||
if (preg_match($suffixPattern, $line, $matches) === 0) {
|
if (preg_match($suffixPattern, $line, $matches) === 0) {
|
||||||
return $this->error("Unexpected line: $line");
|
return $this->error("Unexpected line: $line");
|
||||||
}
|
}
|
||||||
|
@ -175,30 +254,41 @@ class LogTool
|
||||||
"The suffix has not been finished from position $this->suffixPosition in line: $line"
|
"The suffix has not been finished from position $this->suffixPosition in line: $line"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if ( ! $this->match($suffixMatcher, 'Suffix message not found')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$this->suffixPosition = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($terminated) {
|
if ($terminated) {
|
||||||
return $this->expectTerminatorLines($lines, $idx);
|
return $this->expectTerminatorLines();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Check workers output line.
|
||||||
|
*
|
||||||
|
* @param string $line Log output line.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function checkLine(string $line)
|
private function checkLine(string $line): bool
|
||||||
{
|
{
|
||||||
if ($this->pattern === null) {
|
$useLine = $this->pattern === null;
|
||||||
|
if ($useLine) {
|
||||||
// plain (not decorated) output
|
// plain (not decorated) output
|
||||||
$out = rtrim($line);
|
$out = rtrim($line);
|
||||||
$finalSuffix = null;
|
$finalSuffix = null;
|
||||||
} elseif (($res = preg_match($this->pattern, $line, $matches)) > 0) {
|
} elseif (preg_match($this->pattern, $line, $matches) > 0) {
|
||||||
$out = $matches[2];
|
$out = $matches[2];
|
||||||
$finalSuffix = $matches[3] ?? false;
|
$finalSuffix = $matches[3] ?? false;
|
||||||
} else {
|
} else {
|
||||||
return $this->error("Unexpected line: $line");
|
return $this->error("Unexpected line: $line", $line);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rem = strlen($this->message) - $this->position;
|
$rem = strlen($this->message) - $this->position;
|
||||||
|
@ -206,51 +296,58 @@ class LogTool
|
||||||
if ( ! $this->checkLineLength($line, $lineLen)) {
|
if ( ! $this->checkLineLength($line, $lineLen)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!$this->checkMessage($out, $this->position)) {
|
if ( ! $this->checkMessage($out, $this->position, $useLine)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$outLen = strlen($out);
|
$outLen = strlen($out);
|
||||||
|
$this->position += $outLen;
|
||||||
if ($rem > $outLen) { // continuous line
|
if ($rem > $outLen) { // continuous line
|
||||||
if ($lineLen !== $this->limit) {
|
if ($lineLen !== $this->limit) {
|
||||||
if ($lineLen + ($rem - $outLen) < $this->limit) {
|
if ($lineLen + ($rem - $outLen) < $this->limit) {
|
||||||
return $this->error("Printed less than the message len");
|
return $this->error("Printed less than the message len");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->error(
|
return $this->error(
|
||||||
"The continuous line length is $lineLen but it should equal to limit $this->limit"
|
"The continuous line length is $lineLen but it should equal to limit $this->limit"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->position += $outLen;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ($rem !== $outLen) {
|
if ($rem !== $outLen) {
|
||||||
return $this->error("Printed more than the message len");
|
return $this->error("Printed more than the message len");
|
||||||
}
|
}
|
||||||
if ( ! $this->pipeClosed || $finalSuffix === null) {
|
if ( ! $this->pipeClosed || $finalSuffix === null) {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
if ($finalSuffix === false) {
|
if ($finalSuffix === false) {
|
||||||
return $this->error("No final suffix");
|
return $this->error("No final suffix");
|
||||||
}
|
}
|
||||||
if (empty($finalSuffix) || strpos(self::FINAL_SUFFIX, $finalSuffix) === false) {
|
if (empty($finalSuffix) || ! str_contains(self::FINAL_SUFFIX, $finalSuffix)) {
|
||||||
return $this->error("The final suffix has to be equal to ', pipe is closed'");
|
return $this->error("The final suffix has to be equal to ', pipe is closed'");
|
||||||
}
|
}
|
||||||
if (self::FINAL_SUFFIX !== $finalSuffix) {
|
if (self::FINAL_SUFFIX !== $finalSuffix) {
|
||||||
$this->suffixPosition = strlen($finalSuffix);
|
$this->suffixPosition = strlen($finalSuffix);
|
||||||
}
|
}
|
||||||
// complete final suffix printed
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Check the message line length - specifically if it's behind the limit.
|
||||||
* @param int $lineLen
|
*
|
||||||
|
* @param string $line Log output line.
|
||||||
|
* @param int|null $lineLen Line length.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function checkLineLength(string $line, $lineLen = null) {
|
private function checkLineLength(string $line, int $lineLen = null): bool
|
||||||
|
{
|
||||||
$lineLen = $lineLen ?: strlen($line);
|
$lineLen = $lineLen ?: strlen($line);
|
||||||
if ($lineLen > $this->limit) {
|
if ($lineLen > $this->limit) {
|
||||||
return $this->error(
|
return $this->error(
|
||||||
"The line length is $lineLen which is higher than limit $this->limit"
|
"The line length is $lineLen which is higher than limit $this->limit",
|
||||||
|
$line
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,12 +355,19 @@ class LogTool
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $matchedMessage
|
* Check whether matched message part matches the expected message.
|
||||||
* @param int $expectedMessageStart
|
*
|
||||||
|
* @param string $matchedMessage The output message or part of it (match).
|
||||||
|
* @param int $expectedMessageStart Message position.
|
||||||
|
* @param bool $isLine Whether the whole log line is provided as a matched message.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
private function checkMessage(string $matchedMessage, int $expectedMessageStart = 0)
|
private function checkMessage(
|
||||||
{
|
string $matchedMessage,
|
||||||
|
int $expectedMessageStart = 0,
|
||||||
|
bool $isLine = false
|
||||||
|
): bool {
|
||||||
if ($expectedMessageStart < 0) {
|
if ($expectedMessageStart < 0) {
|
||||||
$expectedMessage = $this->message;
|
$expectedMessage = $this->message;
|
||||||
} else {
|
} else {
|
||||||
|
@ -271,247 +375,430 @@ class LogTool
|
||||||
}
|
}
|
||||||
if ($expectedMessage !== $matchedMessage) {
|
if ($expectedMessage !== $matchedMessage) {
|
||||||
return $this->error(
|
return $this->error(
|
||||||
|
$this->getMatchDebugMessage(
|
||||||
sprintf(
|
sprintf(
|
||||||
"The actual string(%d) does not match expected string(%d):\n",
|
"The actual string(%d) does not match expected string(%d):\n",
|
||||||
strlen($matchedMessage),
|
strlen($matchedMessage),
|
||||||
strlen($expectedMessage)
|
strlen($expectedMessage)
|
||||||
) .
|
),
|
||||||
"- EXPECT: '$expectedMessage'\n" .
|
expectedMessage: $expectedMessage,
|
||||||
"- ACTUAL: '$matchedMessage'"
|
actualMessage: "'$matchedMessage'",
|
||||||
|
),
|
||||||
|
$isLine ? $matchedMessage : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->traceMatch(
|
||||||
|
"Message matched",
|
||||||
|
expectedMessage: $expectedMessage,
|
||||||
|
actualMessage: "'$matchedMessage'",
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $lines
|
* Expect log entries for daemon reloading.
|
||||||
|
*
|
||||||
|
* @param int $expectedNumberOfSockets
|
||||||
|
* @param bool $expectInitialProgressMessage
|
||||||
|
* @param bool $expectReloadingMessage
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectReloadingLines(array $lines)
|
public function expectReloadingLines(
|
||||||
{
|
int $expectedNumberOfSockets,
|
||||||
if (
|
bool $expectInitialProgressMessage = true,
|
||||||
!$this->expectNotice($lines[0], 'Reloading in progress ...') ||
|
bool $expectReloadingMessage = true
|
||||||
!$this->expectNotice($lines[1], 'reloading: .*')
|
): bool {
|
||||||
) {
|
if ($expectInitialProgressMessage && ! $this->expectNotice('Reloading in progress ...')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($expectReloadingMessage && ! $this->expectNotice('reloading: .*')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 2; $i < count($lines) - 2; $i++) {
|
for ($i = 0; $i < $expectedNumberOfSockets; $i++) {
|
||||||
if (!$this->expectNotice($lines[$i], 'using inherited socket fd=\d+, "[^"]+"')) {
|
if ( ! $this->expectNotice('using inherited socket fd=\d+, "[^"]+"')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->expectStartingLines(array_splice($lines, $i));
|
return $this->expectStartingLines();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $lines
|
* Expect log entries for reloading logs.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectStartingLines(array $lines)
|
public function expectReloadingLogsLines(): bool {
|
||||||
{
|
|
||||||
if ($this->getError()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($lines) < 2) {
|
|
||||||
return $this->error("No starting lines");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
$this->expectNotice($lines[0], 'fpm is running, pid \d+') &&
|
$this->expectNotice('error log file re-opened') &&
|
||||||
$this->expectNotice($lines[1], 'ready to handle connections')
|
$this->expectNotice('access log file re-opened')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $lines
|
* Expect starting lines when FPM starts.
|
||||||
* @param int $idx
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectTerminatorLines(array $lines, int $idx = -1)
|
public function expectStartingLines(): bool
|
||||||
{
|
{
|
||||||
if ($this->getError()) {
|
if ($this->getError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($lines) - $idx < 3) {
|
|
||||||
return $this->error("No terminating lines");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
$this->expectNotice($lines[++$idx], 'Terminating ...') &&
|
$this->expectNotice('fpm is running, pid \d+') &&
|
||||||
$this->expectNotice($lines[++$idx], 'exiting, bye-bye!')
|
$this->expectNotice('ready to handle connections')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $type
|
* Expect termination lines when FPM terminates.
|
||||||
* @param string $line
|
*
|
||||||
* @param string $expectedMessage
|
|
||||||
* @param string|null $pool
|
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectEntry(string $type, string $line, string $expectedMessage, $pool = null)
|
public function expectTerminatorLines(): bool
|
||||||
{
|
{
|
||||||
if ($this->getError()) {
|
if ($this->getError()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
$this->expectNotice('Terminating ...') &&
|
||||||
|
$this->expectNotice('exiting, bye-bye!')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get log entry matcher.
|
||||||
|
*
|
||||||
|
* @param string $type Entry type like NOTICE, WARNING, DEBUG and so on.
|
||||||
|
* @param string $expectedMessage Message to search for
|
||||||
|
* @param string $pool Pool that is used and prefixes the message.
|
||||||
|
* @param string $ignoreErrorFor Ignore error for supplied string in the message.
|
||||||
|
*
|
||||||
|
* @return callable
|
||||||
|
*/
|
||||||
|
private function getEntryMatcher(
|
||||||
|
string $type,
|
||||||
|
string $expectedMessage,
|
||||||
|
?string $pool,
|
||||||
|
string $ignoreErrorFor
|
||||||
|
): callable {
|
||||||
if ($pool !== null) {
|
if ($pool !== null) {
|
||||||
$expectedMessage = '\[pool ' . $pool . '\] ' . $expectedMessage;
|
$expectedMessage = '\[pool ' . $pool . '\] ' . $expectedMessage;
|
||||||
}
|
}
|
||||||
|
$this->trace("Matching EXPECTED: $expectedMessage");
|
||||||
|
|
||||||
|
$pattern = sprintf('/^(?:%s )?%s: %s(%s)$/', self::P_TIME, $type, self::P_DEBUG, $expectedMessage);
|
||||||
|
$this->trace("PATTERN: $pattern");
|
||||||
|
|
||||||
|
return function ($line) use ($expectedMessage, $pattern, $type, $ignoreErrorFor) {
|
||||||
$line = rtrim($line);
|
$line = rtrim($line);
|
||||||
$pattern = sprintf('/^(%s )?%s: %s$/', self::P_TIME, $type, $expectedMessage);
|
|
||||||
|
|
||||||
if (preg_match($pattern, $line, $matches) === 0) {
|
if (preg_match($pattern, $line, $matches) === 0) {
|
||||||
|
if ($this->getError()) { // quick bail out to save some CPU
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get actual message
|
||||||
|
$types = implode('|', [self::NOTICE, self::WARNING, self::ERROR, self::ALERT]);
|
||||||
|
$errorPattern = sprintf('/^(?:%s )?(%s): %s(.*)$/', self::P_TIME, $types, self::P_DEBUG);
|
||||||
|
if (preg_match($errorPattern, $line, $matches) === 0) {
|
||||||
|
$actualMessage = null;
|
||||||
|
} else {
|
||||||
|
$expectedMessage = $type . ' - ' . $expectedMessage;
|
||||||
|
$actualMessage = $matches[1] . ' - ' . $matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
return $this->error(
|
return $this->error(
|
||||||
"The $type does not match expected message:\n" .
|
$this->getMatchDebugMessage(
|
||||||
"- PATTERN: $pattern\n" .
|
'Most likely invalid match for entry',
|
||||||
"- MESSAGE: $line\n" .
|
$pattern,
|
||||||
"- EXPECT: '$expectedMessage'\n" .
|
$line,
|
||||||
"- ACTUAL: '" . substr($line, strpos($line, $type) + strlen($type) + 2) . "'"
|
$expectedMessage,
|
||||||
|
$actualMessage
|
||||||
|
),
|
||||||
|
$line,
|
||||||
|
$ignoreErrorFor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
$this->trace("Matched ACTUAL: " . $matches[1]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all log entries until timeout.
|
||||||
|
*
|
||||||
|
* @param string $type Entry type like NOTICE, WARNING, DEBUG and so on.
|
||||||
|
* @param string $expectedMessage Message to search for
|
||||||
|
* @param string|null $pool Pool that is used and prefixes the message.
|
||||||
|
* @param string $ignoreErrorFor Ignore error for supplied string in the message.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function readAllEntries(
|
||||||
|
string $type,
|
||||||
|
string $expectedMessage,
|
||||||
|
string $pool = null,
|
||||||
|
string $ignoreErrorFor = self::DEBUG
|
||||||
|
): bool {
|
||||||
|
if ($this->getError()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matcher = $this->getEntryMatcher($type, $expectedMessage, $pool, $ignoreErrorFor);
|
||||||
|
|
||||||
|
while ($this->logReader->readUntil($matcher)) {
|
||||||
|
$this->popError();
|
||||||
|
}
|
||||||
|
$this->popError();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Expect log entry.
|
||||||
* @param string $expectedMessage
|
*
|
||||||
* @param string|null $pool
|
* @param string $type Entry type like NOTICE, WARNING, DEBUG and so on.
|
||||||
|
* @param string $expectedMessage Message to search for
|
||||||
|
* @param string|null $pool Pool that is used and prefixes the message.
|
||||||
|
* @param string $ignoreErrorFor Ignore error for supplied string in the message.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectDebug(string $line, string $expectedMessage, $pool = null)
|
public function expectEntry(
|
||||||
{
|
string $type,
|
||||||
return $this->expectEntry(self::DEBUG, $line, $expectedMessage, $pool);
|
string $expectedMessage,
|
||||||
|
string $pool = null,
|
||||||
|
string $ignoreErrorFor = self::DEBUG
|
||||||
|
): bool {
|
||||||
|
if ($this->getError()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->match(
|
||||||
|
$this->getEntryMatcher($type, $expectedMessage, $pool, $ignoreErrorFor),
|
||||||
|
"The $type does not match expected message"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Expect debug log entry.
|
||||||
|
*
|
||||||
* @param string $expectedMessage
|
* @param string $expectedMessage
|
||||||
* @param string|null $pool
|
* @param string|null $pool
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectNotice(string $line, string $expectedMessage, $pool = null)
|
public function expectDebug(string $expectedMessage, string $pool = null): bool
|
||||||
{
|
{
|
||||||
return $this->expectEntry(self::NOTICE, $line, $expectedMessage, $pool);
|
return $this->expectEntry(self::DEBUG, $expectedMessage, $pool, self::ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Expect notice log entry.
|
||||||
|
*
|
||||||
* @param string $expectedMessage
|
* @param string $expectedMessage
|
||||||
* @param string|null $pool
|
* @param string|null $pool
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectWarning(string $line, string $expectedMessage, $pool = null)
|
public function expectNotice(string $expectedMessage, string $pool = null): bool
|
||||||
{
|
{
|
||||||
return $this->expectEntry(self::WARNING, $line, $expectedMessage, $pool);
|
return $this->expectEntry(self::NOTICE, $expectedMessage, $pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Expect warning log entry.
|
||||||
|
*
|
||||||
* @param string $expectedMessage
|
* @param string $expectedMessage
|
||||||
* @param string|null $pool
|
* @param string|null $pool
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectError(string $line, string $expectedMessage, $pool = null)
|
public function expectWarning(string $expectedMessage, string $pool = null): bool
|
||||||
{
|
{
|
||||||
return $this->expectEntry(self::ERROR, $line, $expectedMessage, $pool);
|
return $this->expectEntry(self::WARNING, $expectedMessage, $pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $line
|
* Expect error log entry.
|
||||||
|
*
|
||||||
* @param string $expectedMessage
|
* @param string $expectedMessage
|
||||||
* @param string|null $pool
|
* @param string|null $pool
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function expectAlert(string $line, string $expectedMessage, $pool = null)
|
public function expectError(string $expectedMessage, string $pool = null): bool
|
||||||
{
|
{
|
||||||
return $this->expectEntry(self::ALERT, $line, $expectedMessage, $pool);
|
return $this->expectEntry(self::ERROR, $expectedMessage, $pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Expect alert log entry.
|
||||||
|
*
|
||||||
|
* @param string $expectedMessage
|
||||||
|
* @param string|null $pool
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function expectAlert(string $expectedMessage, string $pool = null): bool
|
||||||
|
{
|
||||||
|
return $this->expectEntry(self::ALERT, $expectedMessage, $pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expect pattern in the log line.
|
||||||
|
*
|
||||||
|
* @param string $pattern
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function expectPattern(string $pattern): bool
|
||||||
|
{
|
||||||
|
return $this->match(
|
||||||
|
function ($line) use ($pattern) {
|
||||||
|
if (preg_match($pattern, $line) === 1) {
|
||||||
|
$this->traceMatch("Pattern expectation", $pattern, $line);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
'The search pattern not found'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get match debug message.
|
||||||
|
*
|
||||||
|
* @param string $title
|
||||||
|
* @param string|null $pattern
|
||||||
|
* @param string|null $line
|
||||||
|
* @param string|null $expectedMessage
|
||||||
|
* @param string|null $actualMessage
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getMatchDebugMessage(
|
||||||
|
string $title,
|
||||||
|
string $pattern = null,
|
||||||
|
string $line = null,
|
||||||
|
string $expectedMessage = null,
|
||||||
|
string $actualMessage = null
|
||||||
|
): string {
|
||||||
|
$msg = "$title:\n";
|
||||||
|
if ($pattern !== null) {
|
||||||
|
$msg .= "- PATTERN: $pattern\n";
|
||||||
|
}
|
||||||
|
if ($line !== null) {
|
||||||
|
$msg .= "- LINE: $line\n";
|
||||||
|
}
|
||||||
|
if ($expectedMessage !== null) {
|
||||||
|
$msg .= "- EXPECTED: $expectedMessage\n";
|
||||||
|
}
|
||||||
|
if ($actualMessage !== null) {
|
||||||
|
$msg .= "- ACTUAL: $actualMessage\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print tracing of the match.
|
||||||
|
*
|
||||||
|
* @param string $title
|
||||||
|
* @param string|null $pattern
|
||||||
|
* @param string|null $line
|
||||||
|
* @param string|null $expectedMessage
|
||||||
|
* @param string|null $actualMessage
|
||||||
|
*/
|
||||||
|
private function traceMatch(
|
||||||
|
string $title,
|
||||||
|
string $pattern = null,
|
||||||
|
string $line = null,
|
||||||
|
string $expectedMessage = null,
|
||||||
|
string $actualMessage = null
|
||||||
|
): void {
|
||||||
|
if ($this->debug) {
|
||||||
|
echo "LogTool - " . $this->getMatchDebugMessage($title, $pattern, $line, $expectedMessage, $actualMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print tracing message - only in debug .
|
||||||
|
*
|
||||||
|
* @param string $msg Message to print.
|
||||||
|
*/
|
||||||
|
private function trace(string $msg): void
|
||||||
|
{
|
||||||
|
if ($this->debug) {
|
||||||
|
print "LogTool - $msg\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save error message if the line does not contain ignored string.
|
||||||
|
*
|
||||||
* @param string $msg
|
* @param string $msg
|
||||||
* @return bool
|
* @param string|null $line
|
||||||
|
* @param string $ignoreFor
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
*/
|
*/
|
||||||
private function error(string $msg)
|
private function error(string $msg, string $line = null, string $ignoreFor = self::DEBUG): bool
|
||||||
{
|
{
|
||||||
|
if ($this->error === null && ($line === null || ! str_contains($line, $ignoreFor))) {
|
||||||
|
$this->trace("Setting error: $msg");
|
||||||
$this->error = $msg;
|
$this->error = $msg;
|
||||||
echo "ERROR: $msg\n";
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* Get saved error.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function getError()
|
public function getError(): ?string
|
||||||
{
|
{
|
||||||
return $this->error;
|
return $this->error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get saved error and clear it.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function popError(): ?string
|
||||||
|
{
|
||||||
|
$error = $this->error;
|
||||||
|
$this->error = null;
|
||||||
|
if ($error !== null) {
|
||||||
|
$this->trace("Clearing error: $error");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($argv[1]) && $argv[1] === 'logtool-selftest') {
|
return $error;
|
||||||
$cases = [
|
|
||||||
[
|
|
||||||
'limit' => 1050,
|
|
||||||
'lines' => [
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
|
|
||||||
str_repeat('a', 968) . '"',
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
|
|
||||||
str_repeat('a', 968) . '"',
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
|
|
||||||
str_repeat('a', 112) . '", pipe is closed',
|
|
||||||
'[08-Oct-2017 19:53:55] NOTICE: Terminating ...',
|
|
||||||
'[08-Oct-2017 19:53:55] NOTICE: exiting, bye-bye!',
|
|
||||||
],
|
|
||||||
'message' => str_repeat('a', 2048),
|
|
||||||
'type' => 'stdio',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'limit' => 1050,
|
|
||||||
'lines' => [
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
|
|
||||||
str_repeat('a', 968) . '"',
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
|
|
||||||
str_repeat('a', 968) . '"',
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: "' .
|
|
||||||
str_repeat('a', 964) . '", pi',
|
|
||||||
'[08-Oct-2017 19:53:50] WARNING: [pool unconfined] child 23183 said into stderr: pe is closed',
|
|
||||||
'[08-Oct-2017 19:53:55] NOTICE: Terminating ...',
|
|
||||||
'[08-Oct-2017 19:53:55] NOTICE: exiting, bye-bye!',
|
|
||||||
],
|
|
||||||
'message' => str_repeat('a', 2900),
|
|
||||||
'type' => 'stdio',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'limit' => 1024,
|
|
||||||
'line' => '[08-Oct-2017 19:53:50] WARNING: ' . str_repeat('a',989) . '...',
|
|
||||||
'message' => str_repeat('a', 2900),
|
|
||||||
'type' => 'message',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'limit' => 1024,
|
|
||||||
'line' => '[08-Oct-2017 19:53:50] WARNING: ' . str_repeat('a',20),
|
|
||||||
'message' => str_repeat('a', 20),
|
|
||||||
'type' => 'message',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
foreach ($cases as $case) {
|
|
||||||
printf("Test message with len %d and limit %d: ", strlen($case['message']), $case['limit']);
|
|
||||||
$logTool = new LogTool();
|
|
||||||
$logTool->setExpectedMessage($case['message'], $case['limit']);
|
|
||||||
if ($case['type'] === 'stdio') {
|
|
||||||
$logTool->checkWrappedMessage($case['lines']);
|
|
||||||
} else {
|
|
||||||
$logTool->checkTruncatedMessage($case['line']);
|
|
||||||
}
|
|
||||||
if (!$logTool->getError()) {
|
|
||||||
echo "OK\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo "Done\n";
|
|
||||||
}
|
|
||||||
|
|
|
@ -170,11 +170,11 @@ class Response
|
||||||
*/
|
*/
|
||||||
public function debugOutput()
|
public function debugOutput()
|
||||||
{
|
{
|
||||||
echo "-------------- RESPONSE: --------------\n";
|
echo ">>> Response\n";
|
||||||
echo "OUT:\n";
|
echo "----------------- OUT -----------------\n";
|
||||||
echo $this->data['out_response'];
|
echo $this->data['out_response'] . "\n";
|
||||||
echo "ERR:\n";
|
echo "----------------- ERR -----------------\n";
|
||||||
echo $this->data['err_response'];
|
echo $this->data['err_response'] . "\n";
|
||||||
echo "---------------------------------------\n\n";
|
echo "---------------------------------------\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue