Merge pull request #244 from acelaya/feature/psr-logs

Feature/psr logs
This commit is contained in:
Alejandro Celaya 2018-10-20 13:06:20 +02:00 committed by GitHub
commit f18f8c89ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 145 additions and 31 deletions

View file

@ -1,15 +1,19 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Monolog\Processor;
use const PHP_EOL;
return [
'logger' => [
'formatters' => [
'dashed' => [
'format' => '[%datetime%] %channel%.%level_name% - %message% %context%' . PHP_EOL,
'format' => '[%datetime%] %channel%.%level_name% - %message%' . PHP_EOL,
'include_stacktraces' => true,
],
],
@ -24,9 +28,19 @@ return [
],
],
'processors' => [
'exception_with_new_line' => [
'class' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
],
'psr3' => [
'class' => Processor\PsrLogMessageProcessor::class,
],
],
'loggers' => [
'Shlink' => [
'handlers' => ['rotating_file_handler'],
'processors' => ['exception_with_new_line', 'psr3'],
],
],
],

View file

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Common\Logger\Processor;
use const PHP_EOL;
use function str_replace;
use function strpos;
final class ExceptionWithNewLineProcessor
{
private const EXCEPTION_PLACEHOLDER = '{e}';
public function __invoke(array $record)
{
$message = $record['message'];
$messageHasExceptionPlaceholder = strpos($message, self::EXCEPTION_PLACEHOLDER) !== false;
if ($messageHasExceptionPlaceholder) {
$record['message'] = str_replace(
self::EXCEPTION_PLACEHOLDER,
PHP_EOL . self::EXCEPTION_PLACEHOLDER,
$message
);
}
return $record;
}
}

View file

@ -3,40 +3,44 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Common\Util;
use function random_int;
use function sprintf;
use function strlen;
trait StringUtilsTrait
{
protected function generateRandomString($length = 10)
private function generateRandomString($length = 10): string
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[mt_rand(0, $charactersLength - 1)];
$randomString .= $characters[random_int(0, $charactersLength - 1)];
}
return $randomString;
}
protected function generateV4Uuid()
private function generateV4Uuid(): string
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 bits for "time_low"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
random_int(0, 0xffff),
random_int(0, 0xffff),
// 16 bits for "time_mid"
mt_rand(0, 0xffff),
random_int(0, 0xffff),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
mt_rand(0, 0x0fff) | 0x4000,
random_int(0, 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
mt_rand(0, 0x3fff) | 0x8000,
random_int(0, 0x3fff) | 0x8000,
// 48 bits for "node"
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
random_int(0, 0xffff),
random_int(0, 0xffff),
random_int(0, 0xffff)
);
}
}

View file

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\Common\Logger\Processor;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Common\Logger\Processor\ExceptionWithNewLineProcessor;
use const PHP_EOL;
class ExceptionWithNewLineProcessorTest extends TestCase
{
/**
* @var ExceptionWithNewLineProcessor
*/
private $processor;
public function setUp()
{
$this->processor = new ExceptionWithNewLineProcessor();
}
/**
* @test
* @dataProvider provideNoPlaceholderRecords
*/
public function keepsRecordAsIsWhenNoPlaceholderExists(array $record)
{
$this->assertSame($record, ($this->processor)($record));
}
public function provideNoPlaceholderRecords(): array
{
return [
[['message' => 'Hello World']],
[['message' => 'Shlink']],
[['message' => 'Foo bar']],
];
}
/**
* @test
* @dataProvider providePlaceholderRecords
*/
public function properlyReplacesExceptionPlaceholderAddingNewLine(array $record, array $expected)
{
$this->assertEquals($expected, ($this->processor)($record));
}
public function providePlaceholderRecords(): array
{
return [
[
['message' => 'Hello World with placeholder {e}'],
['message' => 'Hello World with placeholder ' . PHP_EOL . '{e}'],
],
[
['message' => '{e} Shlink'],
['message' => PHP_EOL . '{e} Shlink'],
],
[
['message' => 'Foo {e} bar'],
['message' => 'Foo ' . PHP_EOL . '{e} bar'],
],
];
}
}

View file

@ -75,7 +75,7 @@ abstract class AbstractTrackingAction implements MiddlewareInterface
return $this->createResp($url->getLongUrl());
} catch (InvalidShortCodeException | EntityDoesNotExistException $e) {
$this->logger->warning('An error occurred while tracking short code.' . PHP_EOL . $e);
$this->logger->warning('An error occurred while tracking short code. {e}', ['e' => $e]);
return $this->buildErrorResponse($request, $handler);
}
}

View file

@ -63,7 +63,7 @@ class PreviewAction implements MiddlewareInterface
$imagePath = $this->previewGenerator->generatePreview($url->getLongUrl());
return $this->generateImageResponse($imagePath);
} catch (InvalidShortCodeException | EntityDoesNotExistException | PreviewGenerationException $e) {
$this->logger->warning('An error occurred while generating preview image.' . PHP_EOL . $e);
$this->logger->warning('An error occurred while generating preview image. {e}', ['e' => $e]);
return $this->buildErrorResponse($request, $handler);
}
}

View file

@ -67,7 +67,7 @@ class QrCodeAction implements MiddlewareInterface
try {
$this->urlShortener->shortCodeToUrl($shortCode);
} catch (InvalidShortCodeException | EntityDoesNotExistException $e) {
$this->logger->warning('An error occurred while creating QR code' . PHP_EOL . $e);
$this->logger->warning('An error occurred while creating QR code. {e}', ['e' => $e]);
return $this->buildErrorResponse($request, $handler);
}

View file

@ -57,7 +57,7 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
$longUrl = $shortUrlData->getLongUrl();
$customSlug = $shortUrlMeta->getCustomSlug();
} catch (InvalidArgumentException $e) {
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
$this->logger->warning('Provided data is invalid. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
'message' => $e->getMessage(),
@ -77,7 +77,7 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
return new JsonResponse($transformer->transform($shortUrl));
} catch (InvalidUrlException $e) {
$this->logger->warning('Provided Invalid URL.' . PHP_EOL . $e);
$this->logger->warning('Provided Invalid URL. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => \sprintf(
@ -86,7 +86,7 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
),
], self::STATUS_BAD_REQUEST);
} catch (NonUniqueSlugException $e) {
$this->logger->warning('Provided non-unique slug.' . PHP_EOL . $e);
$this->logger->warning('Provided non-unique slug. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => \sprintf(
@ -95,7 +95,7 @@ abstract class AbstractCreateShortUrlAction extends AbstractRestAction
),
], self::STATUS_BAD_REQUEST);
} catch (\Throwable $e) {
$this->logger->error('Unexpected error creating short url.' . PHP_EOL . $e);
$this->logger->error('Unexpected error creating short url. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::UNKNOWN_ERROR,
'message' => $this->translator->translate('Unexpected error occurred'),

View file

@ -50,14 +50,15 @@ class DeleteShortUrlAction extends AbstractRestAction
return new EmptyResponse();
} catch (Exception\InvalidShortCodeException $e) {
$this->logger->warning(
\sprintf('Provided short code %s does not belong to any URL.', $shortCode) . PHP_EOL . $e
'Provided short code {shortCode} does not belong to any URL. {e}',
['e' => $e, 'shortCode' => $shortCode]
);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => \sprintf($this->translator->translate('No URL found for short code "%s"'), $shortCode),
], self::STATUS_NOT_FOUND);
} catch (Exception\DeleteShortUrlException $e) {
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
$this->logger->warning('Provided data is invalid. {e}', ['e' => $e]);
$messagePlaceholder = $this->translator->translate(
'It is not possible to delete URL with short code "%s" because it has reached more than "%s" visits.'
);

View file

@ -60,13 +60,13 @@ class EditShortUrlAction extends AbstractRestAction
);
return new EmptyResponse();
} catch (Exception\InvalidShortCodeException $e) {
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
$this->logger->warning('Provided data is invalid. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => \sprintf($this->translator->translate('No URL found for short code "%s"'), $shortCode),
], self::STATUS_NOT_FOUND);
} catch (Exception\ValidationException $e) {
$this->logger->warning('Provided data is invalid.' . PHP_EOL . $e);
$this->logger->warning('Provided data is invalid. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => $this->translator->translate('Provided data is invalid.'),

View file

@ -60,7 +60,7 @@ class ListShortUrlsAction extends AbstractRestAction
$this->domainConfig
))]);
} catch (\Exception $e) {
$this->logger->error('Unexpected error while listing short URLs.' . PHP_EOL . $e);
$this->logger->error('Unexpected error while listing short URLs. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::UNKNOWN_ERROR,
'message' => $this->translator->translate('Unexpected error occurred'),

View file

@ -59,7 +59,7 @@ class ResolveShortUrlAction extends AbstractRestAction
$url = $this->urlShortener->shortCodeToUrl($shortCode);
return new JsonResponse($transformer->transform($url));
} catch (InvalidShortCodeException $e) {
$this->logger->warning('Provided short code with invalid format.' . PHP_EOL . $e);
$this->logger->warning('Provided short code with invalid format. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => \sprintf(
@ -68,13 +68,13 @@ class ResolveShortUrlAction extends AbstractRestAction
),
], self::STATUS_BAD_REQUEST);
} catch (EntityDoesNotExistException $e) {
$this->logger->warning('Provided short code couldn\'t be found.' . PHP_EOL . $e);
$this->logger->warning('Provided short code couldn\'t be found. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::INVALID_ARGUMENT_ERROR,
'message' => \sprintf($this->translator->translate('No URL found for short code "%s"'), $shortCode),
], self::STATUS_NOT_FOUND);
} catch (\Exception $e) {
$this->logger->error('Unexpected error while resolving the URL behind a short code.' . PHP_EOL . $e);
$this->logger->error('Unexpected error while resolving the URL behind a short code. {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::UNKNOWN_ERROR,
'message' => $this->translator->translate('Unexpected error occurred'),

View file

@ -59,7 +59,7 @@ class GetVisitsAction extends AbstractRestAction
],
]);
} catch (InvalidArgumentException $e) {
$this->logger->warning('Provided nonexistent short code' . PHP_EOL . $e);
$this->logger->warning('Provided nonexistent short code {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::getRestErrorCodeFromException($e),
'message' => sprintf(
@ -68,7 +68,7 @@ class GetVisitsAction extends AbstractRestAction
),
], self::STATUS_NOT_FOUND);
} catch (\Exception $e) {
$this->logger->error('Unexpected error while parsing short code' . PHP_EOL . $e);
$this->logger->error('Unexpected error while parsing short code {e}', ['e' => $e]);
return new JsonResponse([
'error' => RestUtils::UNKNOWN_ERROR,
'message' => $this->translator->translate('Unexpected error occurred'),

View file

@ -80,7 +80,7 @@ class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterfa
try {
$plugin = $this->requestToAuthPlugin->fromRequest($request);
} catch (ContainerExceptionInterface | NoAuthenticationException $e) {
$this->logger->warning('Invalid or no authentication provided.' . PHP_EOL . $e);
$this->logger->warning('Invalid or no authentication provided. {e}', ['e' => $e]);
return $this->createErrorResponse(sprintf($this->translator->translate(
'Expected one of the following authentication headers, but none were provided, ["%s"]'
), implode('", "', RequestToHttpAuthPlugin::SUPPORTED_AUTH_HEADERS)));
@ -91,7 +91,7 @@ class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterfa
$response = $handler->handle($request);
return $plugin->update($request, $response);
} catch (VerifyAuthenticationException $e) {
$this->logger->warning('Authentication verification failed.' . PHP_EOL . $e);
$this->logger->warning('Authentication verification failed. {e}', ['e' => $e]);
return $this->createErrorResponse($e->getPublicMessage(), $e->getErrorCode());
}
}