Compare commits

...

30 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
José Tobias de Freitas Neto 77f1a84a99 Merge remote-tracking branch 'origin/production' into production 2 years ago
José Tobias de Freitas Neto 974a855f34 Aprimorando entradas de log com dados de contexto adicionais
O commit atualizou o mecanismo de registro dentro do LogTrait. Agora inclui dados de contexto específicos: 'custom_data' que usa o customData existente, 'current_url' que captura a URL completa atual, e 'current_user' que registra o usuário autenticado. Essas adições fornecem uma visão mais abrangente das condições durante cada entrada de log.
2 years ago
José Tobias de Freitas Neto 3782796c4d Update 'README.md' 2 years ago
José Tobias de Freitas Neto 83203e8f9a CustomExceptionHandler deve utlizar Throwable 2 years ago
José Tobias de Freitas Neto 7ea7fc6746 Adicionando compatibilidade com pusher-php-server e guzzle superiores 2 years ago
José Tobias de Freitas Neto 43890cc3ed Adicionando compatibilidade com data-transfer-object superior ao 2.8 2 years ago
José Tobias de Freitas Neto ff0e5fa7d7 Adicionando compatibilidade com hashids superior ao 4.1 2 years ago
José Tobias de Freitas Neto 9c9141b579 Adicionando compatibilidade com laravel superior ao 5.8 2 years ago
José Tobias de Freitas Neto bd2d6ee15d Adicionando compatibilidade com laravel superior ao 5.8 2 years ago
José Tobias de Freitas Neto 68daee53c1 Adicionando compatibilidade com php superiores ao 7.4 2 years ago
José Tobias de Freitas Neto 1fd4d7060b Atualizando retorno do método render 2 years ago
José Tobias de Freitas Neto a74e98164e Atualizando nome da váriável de ambiente 2 years ago
José Tobias de Freitas Neto e7efe707a6 Adicionando um CustomHandler para ser utilizado nas aplicações
Esse custom handler permite capturar corretamente exceções não tratadas no código.
2 years ago
José Tobias de Freitas Neto e12ca5942d Mudando estabilidade para stable 2 years ago

@ -44,6 +44,7 @@ Redirecione logs para a classe LogstashLogger. Configuração em `config/logging
'environments' => env('LOGSTASH_ENVIRONMENTS', 'production'),
'bubble' => true,
'level' => Logger::DEBUG
// 'level' => \Monolog\Level::Debug //Para versões mais recentes
],
],
```
@ -95,7 +96,7 @@ Redirecione logs para a classe EmailLogger. Configuração em `config/logging.ph
'subject' => env('APP_NAME') . ' - Error Log',
'host' => env('MAIL_HOST'),
'port' => env('MAIL_PORT'),
'from' => env('MAIL_FROM'),
'from' => env('MAIL_FROM_ADDRESS'),
'to' => env('EMAIL_LOG_CHANNEL_TO'),
'email' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
@ -114,7 +115,7 @@ Variáveis de Ambiente necessárias:
APP_NAME=nome_aplicacao
MAIL_HOST=host_email
MAIL_PORT=porta_email
MAIL_FROM=remetente_email
MAIL_FROM_ADDRESS=remetente_email
MAIL_USERNAME=usuario_email
MAIL_PASSWORD=senha_email
MAIL_ENCRYPTION=criptografia_email

@ -4,13 +4,14 @@
"type": "project",
"license": "MIT",
"require": {
"php": "^7.4",
"laravel/framework": "5.8.*",
"hashids/hashids": "^4.1",
"php": ">=7.4",
"laravel/framework": ">=5.8",
"hashids/hashids": ">=4.1",
"ext-json": "*",
"spatie/data-transfer-object": "^2.8",
"pusher/pusher-php-server": "~4.0",
"guzzlehttp/guzzle": "^7.0"
"spatie/data-transfer-object": ">=2.8",
"pusher/pusher-php-server": ">=4.0",
"guzzlehttp/guzzle": ">=7.0",
"php-amqplib/php-amqplib": "*"
},
"autoload": {
"psr-4": {
@ -23,7 +24,7 @@
"name": "José Tobias de Freitas Neto"
}
],
"minimum-stability": "dev",
"minimum-stability": "stable",
"extra": {
"laravel": {
"providers": [

952
composer.lock generated

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;
}
}

@ -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);
@ -146,4 +163,4 @@ class DiscordHandler extends AbstractProcessingHandler
{
time_sleep_until($timestamp);
}
}
}

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

@ -3,8 +3,10 @@
namespace Ae3\LaravelLogsLayer\app\Providers;
use Ae3\LaravelLogsLayer\app\Containers\LogDataContainer;
use Ae3\LaravelLogsLayer\app\Exceptions\CustomExceptionHandler;
use Ae3\LaravelLogsLayer\app\Observers\LogCaptureObserver;
use Ae3\LaravelLogsLayer\app\Services\AbstractLogService;
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
use Illuminate\Support\ServiceProvider;
class LogsLayerServiceProvider extends ServiceProvider
@ -24,6 +26,8 @@ class LogsLayerServiceProvider extends ServiceProvider
});
LogCaptureObserver::registerListeners();
$this->app->singleton(ExceptionHandlerContract::class, CustomExceptionHandler::class);
}
/**

@ -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,8 +69,15 @@ trait LogTrait
{
$this->initializeLogServices();
$customData['server_ip'] = config('laravel-logs-layer.server_ip');
foreach ($this->logServices as $logService) {
$logService->$method($message, $customData);
$logService->$method($message, [
'custom_data' => $customData,
'current_url' => request()->fullUrl(),
'current_user' => auth()->user(),
'tags' => [],
]);
}
}
@ -137,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