Skip to content

Commit a73bed3

Browse files
committed
BREAKING: Logging via mail
This refactor a few classes around, change of names, and add support to log via emails, while ignoring some specific error for emails via the marker interface `NoMailLogging`.
1 parent 78c38bc commit a73bed3

File tree

18 files changed

+614
-25
lines changed

18 files changed

+614
-25
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Api;
6+
7+
use Ecodev\Felix\Log\Filter\NoMailLogging;
8+
9+
/**
10+
* Exception that will show its message to end-user even on production server, but
11+
* will not be sent to developers as emails
12+
*/
13+
class ExceptionWithoutMailLogging extends Exception implements NoMailLogging
14+
{
15+
}

src/Api/Server.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function sendCli(ExecutionResult $result): void
100100
private function handleError(Throwable $exception, callable $formatter): array
101101
{
102102
// Always log exception in DB (and by email)
103-
_log()->err($exception->__toString());
103+
_log()->err($exception->__toString(), ['exception' => $exception]);
104104

105105
// If we are absolutely certain that the error comes from one of our trigger with a custom message for end-user,
106106
// then wrap the exception to make it showable to the end-user

src/Log/DbWriter.php renamed to src/Log/EventCompleter.php

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,24 @@
55
namespace Ecodev\Felix\Log;
66

77
use Ecodev\Felix\Model\CurrentUser;
8-
use Ecodev\Felix\Repository\LogRepository;
9-
use Laminas\Log\Writer\AbstractWriter;
8+
use Laminas\Log\Processor\ProcessorInterface;
109

11-
class DbWriter extends AbstractWriter
10+
class EventCompleter implements ProcessorInterface
1211
{
13-
/**
14-
* @var LogRepository
15-
*/
16-
private $logRepository;
17-
1812
/**
1913
* @var string
2014
*/
2115
private $baseUrl;
2216

23-
public function __construct(LogRepository $logRepository, string $baseUrl, $options = null)
17+
public function __construct(string $baseUrl)
2418
{
25-
parent::__construct($options);
26-
$this->logRepository = $logRepository;
2719
$this->baseUrl = $baseUrl;
2820
}
2921

3022
/**
31-
* Write a message to the log
32-
*
33-
* @param array $event log data event
23+
* Complete a log event with extra data, including stacktrace and any global stuff relevant to the app
3424
*/
35-
final protected function doWrite(array $event): void
36-
{
37-
$completedEvent = $this->completeEvent($event);
38-
$this->logRepository->log($completedEvent);
39-
}
40-
41-
protected function completeEvent(array $event): array
25+
public function process(array $event): array
4226
{
4327
$envData = $this->getEnvData();
4428
$event = array_merge($event, $envData);
@@ -78,6 +62,7 @@ private function getEnvData(): array
7862

7963
$envData = [
8064
'creator_id' => $user ? $user->getId() : null,
65+
'login' => $user ? $user->getLogin() : null,
8166
'url' => $url,
8267
'referer' => $referer,
8368
'request' => json_encode($request, JSON_PRETTY_PRINT),

src/Log/EventCompleterFactory.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Log;
6+
7+
use Interop\Container\ContainerInterface;
8+
use Laminas\ServiceManager\Factory\FactoryInterface;
9+
10+
class EventCompleterFactory implements FactoryInterface
11+
{
12+
/**
13+
* @param string $requestedName
14+
*/
15+
public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): EventCompleter
16+
{
17+
$config = $container->get('config');
18+
$baseUrl = 'https://' . $config['hostname'];
19+
20+
return new EventCompleter($baseUrl);
21+
}
22+
}

src/Log/Filter/NoMail.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Log\Filter;
6+
7+
use GraphQL\Error\Error;
8+
use Laminas\Log\Filter\FilterInterface;
9+
10+
final class NoMail implements FilterInterface
11+
{
12+
/**
13+
* Ignore exception that are explicitly marked as ignored
14+
*/
15+
public function filter(array $event): bool
16+
{
17+
$exception = $event['extra']['exception'] ?? null;
18+
19+
if ($exception instanceof NoMailLogging || ($exception instanceof Error && $exception->getPrevious() instanceof NoMailLogging)) {
20+
return false;
21+
}
22+
23+
return true;
24+
}
25+
}

src/Log/Filter/NoMailLogging.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Log\Filter;
6+
7+
/**
8+
* Marker interface to be used on exception that should be logged via emails
9+
*/
10+
interface NoMailLogging
11+
{
12+
}

src/Log/Formatter/Extras.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Log\Formatter;
6+
7+
use Laminas\Log\Formatter\Simple;
8+
9+
/**
10+
* Simple formatter that show the Felix extras fields
11+
*/
12+
final class Extras extends Simple
13+
{
14+
/**
15+
* Class constructor
16+
*/
17+
public function __construct()
18+
{
19+
$format = '
20+
Time : %timestamp%
21+
Priority: %priorityName% (%priority%)
22+
Login : %login%
23+
URL : %url%
24+
REFERER : %referer%
25+
IP : %ip%
26+
27+
REQUEST:
28+
%request%
29+
30+
MESSAGE:
31+
%message%
32+
33+
';
34+
parent::__construct($format);
35+
}
36+
}

src/Log/LoggerFactory.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Ecodev\Felix\Log;
66

7+
use Ecodev\Felix\Log\Writer\Db;
8+
use Ecodev\Felix\Log\Writer\Mail;
79
use Interop\Container\ContainerInterface;
810
use Laminas\Log\Logger;
911
use Laminas\Log\Writer\Stream;
@@ -22,16 +24,23 @@ final class LoggerFactory implements FactoryInterface
2224
public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): Logger
2325
{
2426
if (!$this->logger) {
25-
// Log to file
2627
$this->logger = new Logger();
28+
29+
// Log to file
2730
$fileWriter = new Stream('logs/all.log');
2831
$this->logger->addWriter($fileWriter);
2932

3033
// Log to DB
31-
$dbWriter = $container->get(DbWriter::class);
34+
$dbWriter = $container->get(Db::class);
3235
$dbWriter->addFilter(Logger::INFO);
3336
$this->logger->addWriter($dbWriter);
3437

38+
// Maybe log to emails
39+
$mailWriter = $container->get(Mail::class);
40+
if ($mailWriter) {
41+
$this->logger->addWriter($mailWriter);
42+
}
43+
3544
// Register to log all kind of PHP errors
3645
Logger::registerErrorHandler($this->logger, true);
3746
Logger::registerFatalErrorShutdownFunction($this->logger);

src/Log/Writer/Db.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Log\Writer;
6+
7+
use Ecodev\Felix\Log\EventCompleter;
8+
use Ecodev\Felix\Repository\LogRepository;
9+
use Laminas\Log\Writer\AbstractWriter;
10+
11+
class Db extends AbstractWriter
12+
{
13+
/**
14+
* @var LogRepository
15+
*/
16+
private $logRepository;
17+
18+
/**
19+
* @var EventCompleter
20+
*/
21+
private $eventCompleter;
22+
23+
public function __construct(LogRepository $logRepository, EventCompleter $extrasCompleter, $options = null)
24+
{
25+
parent::__construct($options);
26+
$this->logRepository = $logRepository;
27+
$this->eventCompleter = $extrasCompleter;
28+
}
29+
30+
/**
31+
* Write a message to the log
32+
*
33+
* @param array $event log data event
34+
*/
35+
final protected function doWrite(array $event): void
36+
{
37+
$completedEvent = $this->eventCompleter->process($event);
38+
$this->logRepository->log($completedEvent);
39+
}
40+
}

src/Log/Writer/Mail.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Ecodev\Felix\Log\Writer;
6+
7+
use Ecodev\Felix\Log\EventCompleter;
8+
use Laminas\Mail\Message;
9+
use Laminas\Mail\Transport\TransportInterface;
10+
11+
class Mail extends \Laminas\Log\Writer\Mail
12+
{
13+
/**
14+
* @var EventCompleter
15+
*/
16+
private $eventCompleter;
17+
18+
public function __construct(Message $mail, TransportInterface $transport, EventCompleter $extrasCompleter)
19+
{
20+
parent::__construct($mail, $transport);
21+
$this->eventCompleter = $extrasCompleter;
22+
}
23+
24+
/**
25+
* Write a message to the log
26+
*
27+
* @param array $event log data event
28+
*/
29+
final protected function doWrite(array $event): void
30+
{
31+
$completedEvent = $this->eventCompleter->process($event);
32+
parent::doWrite($completedEvent);
33+
}
34+
}

0 commit comments

Comments
 (0)