Compare commits

..

16 Commits

Author SHA1 Message Date
José Tobias de Freitas Neto ddb84917b5 Capturando server_ip e adicionando ao logException 5 months ago
José Tobias de Freitas Neto 730aa08b87 Adicionando IP do servidor ao customData nos logs
Se nenhum IP for especificado no .env, será utilizado o 127.0.0.1
6 months ago
José Tobias de Freitas Neto 00472d7ac1 Adicionando IP do servidor ao arquivo de configuração
Possibilita setar um IP para o servidor que está disparando os logs
6 months ago
José Neto 6218053a9a Constante não presente em versões antigas da lib 2 years ago
José Neto 554b985b7d Renomeando método 2 years ago
José Neto 49967bf8f7 Trabalhando tanto com Monolog 1.x quanto com Monolog 2.x 2 years ago
José Neto 834c8bc178 Vinculando a fila ao exchange
Até então a fila de processamento não era especificada, ficando a cargo do RabbitMQ a escolha da mesma. Agora forçamos a fila que deve ser utilizada. Isso permite configurar diferentes filas para diferentes fins.
2 years ago
José Neto 4e876045da Adicionando o serviço do RabbitMQ 2 years ago
José Neto eea473fdaa O parâmetro $record deve ser LogRecord 2 years ago
José Neto 663b6bd26a Adicionando integração com rabbitmq 2 years ago
José Neto c059c3e33a Removendo logs em fila temporariamente. 2 years ago
José Neto d28e404826 Merge remote-tracking branch 'origin/production' into production 2 years ago
José Neto 5d9f1465d7 Permitindo colocar os logs em fila. 2 years ago
José Tobias de Freitas Neto b2e8f4e1a8 Corrigindo parâmetros de acordo com o novo construtor do monolog 2 years ago
José Tobias de Freitas Neto b90cd58e2c Adicionando tags aos logs
Esta entrada não está sendo utilizada, mas deverá ser no futuro.
2 years ago
José Tobias de Freitas Neto dc98979f61 Melhorando entradas de log com dados de contexto adicionais
O commit atualiza o mecanismo de registro dentro do LogTrait. Agora ele inclui dados de contexto específicos: 'custom_data' utilizando o customData existente, 'current_url' capturando a URL completa atual e 'current_user' registrando o usuário autenticado. Essas adições fornecem uma visão mais abrangente das circunstâncias durante cada entrada de log.
2 years ago

@ -10,7 +10,8 @@
"ext-json": "*",
"spatie/data-transfer-object": ">=2.8",
"pusher/pusher-php-server": ">=4.0",
"guzzlehttp/guzzle": ">=7.0"
"guzzlehttp/guzzle": ">=7.0",
"php-amqplib/php-amqplib": "*"
},
"autoload": {
"psr-4": {

952
composer.lock generated

File diff suppressed because it is too large Load Diff

@ -7,9 +7,9 @@ use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response;
use Monolog\LogRecord;
class DiscordHandler extends AbstractProcessingHandler
{
@ -32,10 +32,10 @@ class DiscordHandler extends AbstractProcessingHandler
/**
* @param string $webhook
* @param int $level
* @param mixed $level
* @param bool $bubble
*/
public function __construct(string $webhook, int $level = Logger::ERROR, bool $bubble = true)
public function __construct(string $webhook, $level = 400, bool $bubble = true)
{
$this->webhook = $webhook;
$this->client = new Client();
@ -43,12 +43,29 @@ class DiscordHandler extends AbstractProcessingHandler
parent::__construct($level, $bubble);
}
/**
* @param mixed $record
* @return void
* @throws GuzzleException
*/
protected 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
* @throws GuzzleException
*/
protected function write(array $record): void
protected function recordHandler(array $record)
{
if ($this->rateLimitRemaining === 0 && $this->rateLimitReset !== null) {
$this->waitUntil($this->rateLimitReset);

@ -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]);
}
}

@ -4,6 +4,7 @@ namespace Ae3\LaravelLogsLayer\app\Services;
use Ae3\LaravelLogsLayer\app\Containers\LogDataContainer;
use Ae3\LaravelLogsLayer\app\DataTransferObjects\ExceptionContextDTO;
use Ae3\LaravelLogsLayer\app\Jobs\ProcessLog;
use Illuminate\Support\Facades\Log;
use Throwable;
@ -166,7 +167,12 @@ abstract class AbstractLogService implements Contracts\LogServiceInterface
{
return [
'message' => $message,
'custom_data' => $context,
'custom_data' => $context['custom_data'],
'current_url' => $context['current_url'],
'current_user' => $this->asPrettyJson($context['current_user']),
'tags' => $context['tags'],
'queries' => $this->asPrettyJson($this->logDataContainer->getCapturedQueries()),
'guzzle' => $this->asPrettyJson($this->logDataContainer->getCapturedHttpClientEvents()),
];
}

@ -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)),
];
}
}

@ -10,6 +10,7 @@ use Ae3\LaravelLogsLayer\app\Services\DailyLogService;
use Ae3\LaravelLogsLayer\app\Services\DiscordLogService;
use Ae3\LaravelLogsLayer\app\Services\EmailLogService;
use Ae3\LaravelLogsLayer\app\Services\LogstashLogService;
use Ae3\LaravelLogsLayer\app\Services\RabbitMQLogService;
use Hashids\Hashids;
use Illuminate\Support\Str;
use Throwable;
@ -52,6 +53,10 @@ trait LogTrait
if ($defaultChannel === 'email' || in_array('email', $stackChannels, true)) {
$this->logServices[] = app(EmailLogService::class);
}
if ($defaultChannel === 'rabbitmq' || in_array('rabbitmq', $stackChannels, true)) {
$this->logServices[] = app(RabbitMQLogService::class);
}
}
/**
@ -64,11 +69,14 @@ trait LogTrait
{
$this->initializeLogServices();
$customData['server_ip'] = config('laravel-logs-layer.server_ip');
foreach ($this->logServices as $logService) {
$logService->$method($message, [
'custom_data' => $customData,
'current_url' => request()->fullUrl(),
'current_user' => auth()->user(),
'tags' => [],
]);
}
}
@ -141,6 +149,8 @@ trait LogTrait
$this->initializeLogServices();
$customData['server_ip'] = config('laravel-logs-layer.server_ip');
foreach ($this->logServices as $logService) {
$logService->$log_level($caller, $exception, ExceptionContextDTO::fromArray([
'code' => $errorCode,

@ -9,5 +9,11 @@
*/
return [
'queue' => [
'enabled' => env('LOG_QUEUE_ENABLED', false),
'retry_until_in_minutes' => env('LOG_QUEUE_RETRY_UNTIL_IN_MINUTES', 60),
'backoff' => explode(',', env('LOG_QUEUE_BACKOFF', '15'))
],
'sensitive_data' => env('LOGS_LAYER_SENSITIVE_DATA', 'password,password_confirmation,token,api_token,api_key,access_token,refresh_token,authorization_code,client_secret'),
'server_ip' => env('SERVER_IP', '127.0.0.1')
];

Loading…
Cancel
Save