mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-14 04:00:57 +03:00
commit
f18f8c89ec
14 changed files with 145 additions and 31 deletions
|
@ -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'],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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.'
|
||||
);
|
||||
|
|
|
@ -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.'),
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue