Skip to content

Commit

Permalink
Added different log level for FailedToSendCommand when response lengt…
Browse files Browse the repository at this point in the history
…h is too high
  • Loading branch information
asgrim committed Dec 15, 2020
1 parent d387a49 commit 992febd
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/Agent.php
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public function send() : bool

return false;
} catch (FailedToSendCommand $failedToSendCommand) {
$this->logger->error($failedToSendCommand->getMessage());
$this->logger->log($failedToSendCommand->logLevel(), $failedToSendCommand->getMessage());

return false;
}
Expand Down
138 changes: 93 additions & 45 deletions src/Connector/Exception/FailedToSendCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,74 +4,116 @@

namespace Scoutapm\Connector\Exception;

use Psr\Log\LogLevel;
use RuntimeException;
use Scoutapm\Connector\Command;
use Scoutapm\Connector\ConnectionAddress;
use Throwable;
use function get_class;
use function socket_last_error;
use function socket_strerror;
use function sprintf;

/**
* @psalm-type ValidLogLevel = LogLevel::*
*/
final class FailedToSendCommand extends RuntimeException
{
/**
* @var string
* @psalm-var ValidLogLevel
*/
private $logLevel;

/**
* @psalm-param ValidLogLevel $logLevel
*/
public function __construct(string $logLevel, string $message, int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);

$this->logLevel = $logLevel;
}

/** @psalm-return ValidLogLevel */
public function logLevel() : string
{
return $this->logLevel;
}

/** @param resource $socketResource */
public static function writingMessageSizeToSocket(Command $attemptedCommand, $socketResource, ConnectionAddress $connectionAddress) : self
{
$socketErrorNumber = socket_last_error($socketResource);

return new self(sprintf(
'Failed to write message size for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
));
return new self(
LogLevel::ERROR,
sprintf(
'Failed to write message size for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
)
);
}

/** @param resource $socketResource */
public static function writingMessageContentToSocket(Command $attemptedCommand, $socketResource, ConnectionAddress $connectionAddress) : self
{
$socketErrorNumber = socket_last_error($socketResource);

return new self(sprintf(
'Failed to write message content for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
));
return new self(
LogLevel::ERROR,
sprintf(
'Failed to write message content for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
)
);
}

/** @param resource $socketResource */
public static function readingResponseSizeFromSocket(Command $attemptedCommand, $socketResource, ConnectionAddress $connectionAddress) : self
{
$socketErrorNumber = socket_last_error($socketResource);

return new self(sprintf(
'Failed to read response size for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
));
return new self(
LogLevel::ERROR,
sprintf(
'Failed to read response size for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
)
);
}

public static function fromEmptyResponseSize(Command $attemptedCommand, ConnectionAddress $connectionAddress) : self
{
return new self(sprintf(
'Response size was not returned for %s (empty string). Address was: %s',
get_class($attemptedCommand),
$connectionAddress->toString()
));
return new self(
LogLevel::ERROR,
sprintf(
'Response size was not returned for %s (empty string). Address was: %s',
get_class($attemptedCommand),
$connectionAddress->toString()
)
);
}

public static function fromFailedResponseUnpack(Command $attemptedCommand, ConnectionAddress $connectionAddress) : self
{
return new self(sprintf(
'Response length could not be unpacked for %s (maybe invalid format?). Address was: %s',
get_class($attemptedCommand),
$connectionAddress->toString()
));
return new self(
LogLevel::ERROR,
sprintf(
'Response length could not be unpacked for %s (maybe invalid format?). Address was: %s',
get_class($attemptedCommand),
$connectionAddress->toString()
)
);
}

public static function fromTooLargeResponseLength(
Expand All @@ -80,26 +122,32 @@ public static function fromTooLargeResponseLength(
Command $attemptedCommand,
ConnectionAddress $connectionAddress
) : self {
return new self(sprintf(
'Response length returned (%d) exceeded our limit for reading (%d) for %s. Address was: %s',
$responseLengthReturned,
$responseLengthLimit,
get_class($attemptedCommand),
$connectionAddress->toString()
));
return new self(
LogLevel::NOTICE,
sprintf(
'Response length returned (%d) exceeded our limit for reading (%d) for %s. Address was: %s',
$responseLengthReturned,
$responseLengthLimit,
get_class($attemptedCommand),
$connectionAddress->toString()
)
);
}

/** @param resource $socketResource */
public static function readingResponseContentFromSocket(Command $attemptedCommand, $socketResource, ConnectionAddress $connectionAddress) : self
{
$socketErrorNumber = socket_last_error($socketResource);

return new self(sprintf(
'Failed to read response content for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
));
return new self(
LogLevel::ERROR,
sprintf(
'Failed to read response content for %s - error %d (%s). Address was: %s',
get_class($attemptedCommand),
$socketErrorNumber,
socket_strerror($socketErrorNumber),
$connectionAddress->toString()
)
);
}
}
23 changes: 22 additions & 1 deletion tests/Unit/AgentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -752,13 +752,34 @@ public function testFailureToSendCommandExceptionIsCaughtWhilstSending() : void

$this->connector->expects(self::once())
->method('sendCommand')
->willThrowException(new FailedToSendCommand('Splines did not reticulate to send the message'));
->willThrowException(new FailedToSendCommand(LogLevel::ERROR, 'Splines did not reticulate to send the message'));

self::assertFalse($agent->send());

self::assertTrue($this->logger->hasErrorThatContains('Splines did not reticulate to send the message'));
}

public function testFailureToSendCommandExceptionIsCaughtWhilstSendingWithNoticeLogLevel() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
]);

$this->connector->expects(self::once())
->method('connected')
->willReturn(true);

$this->connector->expects(self::once())
->method('sendCommand')
->willThrowException(new FailedToSendCommand(LogLevel::NOTICE, 'Splines did not reticulate to send the message'));

self::assertFalse($agent->send());

self::assertTrue($this->logger->hasNoticeThatContains('Splines did not reticulate to send the message'));
}

public function testOlderVersionsOfExtensionIsNotedInLogs() : void
{
$agent = $this->agentFromConfigArray([
Expand Down
99 changes: 99 additions & 0 deletions tests/Unit/Connector/Exception/FailedToSendCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

namespace Scoutapm\UnitTests\Connector\Exception;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
use Scoutapm\Config;
use Scoutapm\Connector\Command;
use Scoutapm\Connector\ConnectionAddress;
use Scoutapm\Connector\Exception\FailedToSendCommand;
use Webmozart\Assert\Assert;
use const AF_INET;
use const SOCK_STREAM;
use function socket_create;

/** @covers \Scoutapm\Connector\Exception\FailedToSendCommand */
final class FailedToSendCommandTest extends TestCase
{
private const CONNECTION_ADDRESS = 'tcp://127.0.0.01:1234';

/** @var Command&MockObject */
private $command;
/** @var ConnectionAddress */
private $connectionAddress;
/** @var resource */
private $socketResource;

public function setUp() : void
{
$this->command = $this->createMock(Command::class);

$config = new Config();
$config->set(Config\ConfigKey::CORE_AGENT_SOCKET_PATH, self::CONNECTION_ADDRESS);
$this->connectionAddress = ConnectionAddress::fromConfig($config);

$socketResource = socket_create(AF_INET, SOCK_STREAM, 0);
Assert::resource($socketResource);
$this->socketResource = $socketResource;
}

public function testWritingMessageSizeToSocket() : void
{
$exception = FailedToSendCommand::writingMessageSizeToSocket($this->command, $this->socketResource, $this->connectionAddress);

self::assertStringContainsString('Failed to write message size', $exception->getMessage());
self::assertSame(LogLevel::ERROR, $exception->logLevel());
}

public function testWritingMessageContentToSocket() : void
{
$exception = FailedToSendCommand::writingMessageContentToSocket($this->command, $this->socketResource, $this->connectionAddress);

self::assertStringContainsString('Failed to write message content', $exception->getMessage());
self::assertSame(LogLevel::ERROR, $exception->logLevel());
}

public function testReadingResponseSizeFromSocket() : void
{
$exception = FailedToSendCommand::readingResponseSizeFromSocket($this->command, $this->socketResource, $this->connectionAddress);

self::assertStringContainsString('Failed to read response size', $exception->getMessage());
self::assertSame(LogLevel::ERROR, $exception->logLevel());
}

public function testFromEmptyResponseSize() : void
{
$exception = FailedToSendCommand::fromEmptyResponseSize($this->command, $this->connectionAddress);

self::assertStringContainsString('Response size was not returned', $exception->getMessage());
self::assertSame(LogLevel::ERROR, $exception->logLevel());
}

public function testFromFailedResponseUnpack() : void
{
$exception = FailedToSendCommand::fromFailedResponseUnpack($this->command, $this->connectionAddress);

self::assertStringContainsString('Response length could not be unpacked', $exception->getMessage());
self::assertSame(LogLevel::ERROR, $exception->logLevel());
}

public function testFromTooLargeResponseLength() : void
{
$exception = FailedToSendCommand::fromTooLargeResponseLength(2000, 1000, $this->command, $this->connectionAddress);

self::assertStringContainsString('Response length returned (2000) exceeded our limit for reading (1000)', $exception->getMessage());
self::assertSame(LogLevel::NOTICE, $exception->logLevel());
}

public function testReadingResponseContentFromSocket() : void
{
$exception = FailedToSendCommand::readingResponseContentFromSocket($this->command, $this->socketResource, $this->connectionAddress);

self::assertStringContainsString('Failed to read response content', $exception->getMessage());
self::assertSame(LogLevel::ERROR, $exception->logLevel());
}
}

0 comments on commit 992febd

Please sign in to comment.