Compare commits
29 Commits
0.0.2
...
production
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Ae3\LaravelLogsLayer\app\Exceptions;
|
||||
|
||||
use Ae3\LaravelLogsLayer\app\Enums\LogsLevelsEnum;
|
||||
use Throwable;
|
||||
use Ae3\LaravelLogsLayer\app\Traits\LogTrait;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class CustomExceptionHandler extends ExceptionHandler
|
||||
{
|
||||
use LogTrait;
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param Throwable $exception
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function report(Throwable $exception)
|
||||
{
|
||||
$level = $this->getExceptionLevel($exception);
|
||||
$this->logException(__METHOD__, $exception, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Throwable $exception
|
||||
* @return JsonResponse|Response|\Symfony\Component\HttpFoundation\Response
|
||||
*/
|
||||
public function render($request, Throwable $exception)
|
||||
{
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable $exception
|
||||
* @return string
|
||||
*/
|
||||
protected function getExceptionLevel(Throwable $exception): string
|
||||
{
|
||||
if (method_exists($exception, 'getLevel')) {
|
||||
return mb_strtolower($exception->getLevel());
|
||||
}
|
||||
|
||||
return LogsLevelsEnum::ERROR;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Ae3\LaravelLogsLayer\app\Handlers;
|
||||
|
||||
use Exception;
|
||||
use Monolog\Handler\AbstractProcessingHandler;
|
||||
use Monolog\LogRecord;
|
||||
use PhpAmqpLib\Channel\AbstractChannel;
|
||||
use PhpAmqpLib\Channel\AMQPChannel;
|
||||
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
||||
use PhpAmqpLib\Message\AMQPMessage;
|
||||
|
||||
class RabbitMQHandler extends AbstractProcessingHandler
|
||||
{
|
||||
/**
|
||||
* @var AMQPStreamConnection
|
||||
*/
|
||||
private AMQPStreamConnection $connection;
|
||||
/**
|
||||
* @var AbstractChannel|AMQPChannel
|
||||
*/
|
||||
private $channel;
|
||||
/**
|
||||
* @var mixed|string
|
||||
*/
|
||||
private $exchange;
|
||||
/**
|
||||
* @var mixed|string
|
||||
*/
|
||||
private $routingKey;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($exchange = 'logs', $routingKey = 'log', $level = 400, $bubble = true)
|
||||
{
|
||||
parent::__construct($level, $bubble);
|
||||
|
||||
$this->connection = new AMQPStreamConnection(
|
||||
config('logging.channels.rabbitmq.host'),
|
||||
config('logging.channels.rabbitmq.port'),
|
||||
config('logging.channels.rabbitmq.username'),
|
||||
config('logging.channels.rabbitmq.password')
|
||||
);
|
||||
$this->channel = $this->connection->channel();
|
||||
|
||||
$this->exchange = $exchange;
|
||||
$this->routingKey = $routingKey;
|
||||
$this->channel->exchange_declare($exchange, 'direct', false, true, false);
|
||||
|
||||
$queueName = config('logging.channels.rabbitmq.queue', 'logstash_queue');
|
||||
$this->channel->queue_declare($queueName, false, true, false, false);
|
||||
$this->channel->queue_bind($queueName, $this->exchange, $this->routingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $record
|
||||
* @return void
|
||||
*/
|
||||
public function write($record): void
|
||||
{
|
||||
if (is_array($record)) {
|
||||
// Implementação para Monolog 1.x
|
||||
$this->recordHandler($record);
|
||||
}elseif (class_exists(LogRecord::class) && $record instanceof LogRecord) {
|
||||
// Implementação para Monolog 2.x
|
||||
$arrayRecord = $record->toArray();
|
||||
$this->recordHandler($arrayRecord);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $record
|
||||
* @return void
|
||||
*/
|
||||
protected function recordHandler(array $record)
|
||||
{
|
||||
$data = json_encode($record);
|
||||
$msg = new AMQPMessage($data, [
|
||||
'delivery_mode' => 2
|
||||
]);
|
||||
|
||||
$this->channel->basic_publish($msg, $this->exchange, $this->routingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
$this->channel->close();
|
||||
$this->connection->close();
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Ae3\LaravelLogsLayer\app\Jobs;
|
||||
|
||||
use Ae3\LaravelLogsLayer\app\Exceptions\InvalidArgumentException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ProcessLog implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $channel;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $level;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected string $message;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $data;
|
||||
|
||||
/**
|
||||
* @param string $channel
|
||||
* @param string $level
|
||||
* @param string $message
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(
|
||||
string $channel,
|
||||
string $level,
|
||||
string $message,
|
||||
array $data
|
||||
)
|
||||
{
|
||||
$this->level = $level;
|
||||
$this->message = $message;
|
||||
$this->data = $data;
|
||||
$this->channel = $channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the time at which the job should timeout.
|
||||
*/
|
||||
public function retryUntil(): \DateTime
|
||||
{
|
||||
return now()->addMinutes(config('laravel-logs-layer.queue.retry_until_in_minutes', 60));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of seconds to wait before retrying the job.
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public function backoff(): array
|
||||
{
|
||||
return array_map('intval', config('laravel-logs-layer.queue.backoff', ['5','10','20','40']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$level = $this->level;
|
||||
Log::channel($this->channel)->$level($this->message, $this->data);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Ae3\LaravelLogsLayer\app\Loggers;
|
||||
|
||||
use Ae3\LaravelLogsLayer\app\Exceptions\MissingConfigurationException;
|
||||
use Ae3\LaravelLogsLayer\app\Handlers\RabbitMQHandler;
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
|
||||
class RabbitMQLogger extends AbstractLogger
|
||||
{
|
||||
/**
|
||||
* @param array $config
|
||||
* @return void
|
||||
* @throws MissingConfigurationException
|
||||
*/
|
||||
public function validateConfig(array $config): void
|
||||
{
|
||||
$requiredKeys = ['host', 'port', 'username', 'password'];
|
||||
|
||||
foreach (array_merge($requiredKeys, $this->requiredKeys) as $key) {
|
||||
if (!array_key_exists($key, $config)) {
|
||||
throw new MissingConfigurationException("Missing configuration key: $key in rabbitmq channel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createLogger(array $config): Logger
|
||||
{
|
||||
$handler = new RabbitMQHandler(
|
||||
$config['exchange'] ?? 'logs',
|
||||
$config['routing_key'] ?? 'log',
|
||||
$config['level'] ?? Logger::DEBUG,
|
||||
$config['bubble'] ?? true
|
||||
);
|
||||
|
||||
return new Logger('rabbitmq', [$handler]);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Ae3\LaravelLogsLayer\app\Services;
|
||||
|
||||
use Ae3\LaravelLogsLayer\app\DataTransferObjects\ExceptionContextDTO;
|
||||
use Throwable;
|
||||
|
||||
class RabbitMQLogService extends AbstractLogService
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getLogChannel(): string
|
||||
{
|
||||
return 'rabbitmq';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function buildLogData(string $caller, Throwable $exception, ExceptionContextDTO $contextDto): array
|
||||
{
|
||||
return [
|
||||
'caller' => $caller,
|
||||
'status_code' => $exception->getCode(),
|
||||
'line' => $exception->getLine(),
|
||||
'file' => $exception->getFile(),
|
||||
'error_code' => $contextDto->code,
|
||||
'custom_data' => $this->asPrettyJson($contextDto->custom_data),
|
||||
'tags' => $contextDto->tags,
|
||||
'exception' => get_class($exception),
|
||||
'current_url' => $contextDto->current_url,
|
||||
'current_user' => $this->asPrettyJson($contextDto->current_user),
|
||||
'classes' => $this->asPrettyJson($this->getContextClasses($exception->getTrace(), $contextDto->root_namespace)),
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue