From 1e64d855c8ed2c5ddbe52d487538f19aa85a3c4d Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 01:02:36 -0400 Subject: [PATCH 01/14] prep for filter improvements --- src/Helpers/FilterHelper.php | 63 +++++++++++++------ src/Helpers/ScheduledTaskHelper.php | 2 +- src/Laritor.php | 73 +++++++++++++++++++++-- src/Override/DefaultOverride.php | 50 ++++++++++++---- src/Override/LaritorOverride.php | 47 +++++++++++---- src/Override/TestOverride.php | 2 +- src/Recorders/CacheRecorder.php | 43 +++++++++---- src/Recorders/CommandRecorder.php | 4 -- src/Recorders/ExceptionRecorder.php | 5 +- src/Recorders/FeatureFlagRecorder.php | 9 +-- src/Recorders/LogRecorder.php | 23 ------- src/Recorders/MailRecorder.php | 4 -- src/Recorders/NotificationRecorder.php | 5 +- src/Recorders/OutboundRequestRecorder.php | 5 -- src/Recorders/QueryRecorder.php | 4 -- src/Recorders/QueuedJobRecorder.php | 6 +- src/Recorders/RequestRecorder.php | 6 +- src/Recorders/ScheduledTaskRecorder.php | 12 ---- 18 files changed, 226 insertions(+), 137 deletions(-) diff --git a/src/Helpers/FilterHelper.php b/src/Helpers/FilterHelper.php index dfa0173..a06a213 100644 --- a/src/Helpers/FilterHelper.php +++ b/src/Helpers/FilterHelper.php @@ -3,10 +3,24 @@ namespace BinaryBuilds\LaritorClient\Helpers; use BinaryBuilds\LaritorClient\Override\LaritorOverride; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Str; class FilterHelper { + public static $ignoredCommands = [ + 'horizon', 'pulse:', 'db:seed', 'optimize', 'schedule:work', 'schedule:run', + 'schedule:finish', 'package:discover', 'event:cache', 'view:cache', + 'config:cache', 'queue:work', 'queue:listen', 'octane:install', + 'auth:clear-resets', 'config:cache', 'horizon:snapshot', + 'horizon:status', 'horizon:supervisor', 'inertia:start-ssr', + 'invoke-serialized-closure', 'model:prune', 'nightwatch:agent', + 'nightwatch:status', 'queue:monitor', 'reverb:start', + 'schedule:list', 'laritor:sync', 'laritor:send-metrics', + 'vendor:publish' + ]; + public static function recordEvent(callable $callable, $default = true) { return rescue(function () use ($callable){ @@ -16,7 +30,8 @@ public static function recordEvent(callable $callable, $default = true) public static function recordCacheHit($cacheKey): bool { - return static::recordEvent(function () use ($cacheKey) { + return ! Str::startsWith($cacheKey, ['laritor']) && + static::recordEvent(function () use ($cacheKey) { return app(LaritorOverride::class)->recordCacheHit($cacheKey); }); } @@ -28,38 +43,41 @@ public static function recordException($exception): bool }); } - public static function recordOutboundRequest($url): bool + public static function recordOutboundRequest($url, $status_code, $duration): bool { - return static::recordEvent(function () use ($url) { - return app(LaritorOverride::class)->recordOutboundRequest($url); + return Str::doesntContain($url, 'laritor.net') && + static::recordEvent(function () use ($url, $status_code, $duration) { + return app(LaritorOverride::class)->recordOutboundRequest($url, $status_code, $duration); }); } - public static function recordQuery($query, $duration): bool + public static function recordQuery($query, $duration, $path): bool { - return static::recordEvent(function () use ($query, $duration) { - return app(LaritorOverride::class)->recordQuery($query, $duration); + return static::recordEvent(function () use ($query, $duration, $path) { + return app(LaritorOverride::class)->recordQuery($query, $duration, $path); }); } - public static function recordQueuedJob($job): bool + public static function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool { - return static::recordEvent(function () use ($job) { - return app(LaritorOverride::class)->recordQueuedJob($job); + return Str::doesntContain($job, 'QueueHealthCheck') && + static::recordEvent(function () use ($connection, $queue, $job, $status, $duration) { + return app(LaritorOverride::class)->recordQueuedJob($connection, $queue, $job, $status, $duration); }); } - public static function recordRequest($request): bool + public static function recordRequest($request, $response, int $status, int $duration): bool { - return static::recordEvent(function () use ($request) { - return app(LaritorOverride::class)->recordRequest($request); + return !$request->is('laritor/*') && static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordRequest($request, $response, $status, $duration, Auth::user()); }); } - public static function recordCommandOrScheduledTask($command): bool + public static function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool { - return static::recordEvent(function () use ($command) { - return app(LaritorOverride::class)->recordCommandOrScheduledTask($command); + return Str::doesntContain($command, self::$ignoredCommands) && + static::recordEvent(function () use ($command, $status, $duration) { + return app(LaritorOverride::class)->recordCommandOrScheduledTask($command, $status, $duration); }); } @@ -70,10 +88,10 @@ public static function recordTaskScheduler(): bool }); } - public static function recordMail($message): bool + public static function recordMail($mailable, $to, $subject): bool { - return static::recordEvent(function () use ($message) { - return app(LaritorOverride::class)->recordMail($message); + return static::recordEvent(function () use ($mailable, $to, $subject) { + return app(LaritorOverride::class)->recordMail($mailable, $to, $subject); }); } @@ -91,6 +109,13 @@ public static function recordFeatureFlag($flag, $scope): bool }); } + public static function recordLog($level, $message, array $context): bool + { + return static::recordEvent(function () use ($level, $message, $context) { + return app(LaritorOverride::class)->recordLog($level, $message, $context); + }); + } + public static function isBot($request): bool { return static::recordEvent(function () use ($request) { diff --git a/src/Helpers/ScheduledTaskHelper.php b/src/Helpers/ScheduledTaskHelper.php index 9e0e548..e82f8d3 100644 --- a/src/Helpers/ScheduledTaskHelper.php +++ b/src/Helpers/ScheduledTaskHelper.php @@ -25,7 +25,7 @@ public function getScheduledTasks() mb_strpos(Str::replace("'",'', $event->command), 'artisan') ); - if (in_array($task, ['artisan laritor:send-metrics']) || !FilterHelper::recordCommandOrScheduledTask($event->command)) { + if (in_array($task, ['artisan laritor:send-metrics'])) { continue; } diff --git a/src/Laritor.php b/src/Laritor.php index 7989bb3..b20296d 100644 --- a/src/Laritor.php +++ b/src/Laritor.php @@ -2,7 +2,19 @@ namespace BinaryBuilds\LaritorClient; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; +use BinaryBuilds\LaritorClient\Recorders\CacheRecorder; +use BinaryBuilds\LaritorClient\Recorders\CommandRecorder; +use BinaryBuilds\LaritorClient\Recorders\ExceptionRecorder; +use BinaryBuilds\LaritorClient\Recorders\FeatureFlagRecorder; use BinaryBuilds\LaritorClient\Recorders\LogRecorder; +use BinaryBuilds\LaritorClient\Recorders\MailRecorder; +use BinaryBuilds\LaritorClient\Recorders\NotificationRecorder; +use BinaryBuilds\LaritorClient\Recorders\OutboundRequestRecorder; +use BinaryBuilds\LaritorClient\Recorders\QueryRecorder; +use BinaryBuilds\LaritorClient\Recorders\QueuedJobRecorder; +use BinaryBuilds\LaritorClient\Recorders\RequestRecorder; +use BinaryBuilds\LaritorClient\Recorders\ScheduledTaskRecorder; use Carbon\Carbon; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Event; @@ -40,6 +52,24 @@ class Laritor public const CUSTOM_EVENT = 'custom'; + private $exception = null; + + /** + * @return \Throwable|null + */ + public function getException() + { + return $this->exception; + } + + /** + * @param \Throwable|null $exception + */ + public function setException($exception): void + { + $this->exception = $exception; + } + /** * @return string */ @@ -243,6 +273,7 @@ public function reset() $this->response = 0; $this->context = 'BOOT'; $this->hasCustomLogs = false; + $this->exception = null; } /** @@ -252,7 +283,7 @@ public function sendEvents() { rescue(function () { Event::fakeFor(function (){ - $this->cleanupEvents(); + $this->filterEvents(); if ($this->shouldSendEvents()) { $this->callApi(); } @@ -262,12 +293,42 @@ public function sendEvents() }, null, false); } - public function cleanupEvents() + public function filterEvents() { - if (isset($this->events['outbound_requests'])) { - $this->events['outbound_requests'] = array_values(array_filter($this->events['outbound_requests'], function ($event) { - return !empty($event['completed_at']); - })); + foreach ($this->events as $type => $events) { + $filtered = []; + foreach ($events as $event) { + $shouldAdd = match ($type){ + CacheRecorder::$eventType => FilterHelper::recordCacheHit($event['key']), + CommandRecorder::$eventType => FilterHelper::recordCommandOrScheduledTask($event['command'], $event['code'] === 0 ? 'completed' : 'failed', $event['duration'] ?? 0), + ExceptionRecorder::$eventType => FilterHelper::recordException($this->exception), + FeatureFlagRecorder::$eventType => FilterHelper::recordFeatureFlag($event['flag'], $event['feature_flag_scope']), + LogRecorder::$eventType => FilterHelper::recordLog($event['level'], $event['message'], $event['log_context']), + MailRecorder::$eventType => FilterHelper::recordMail($event['mailable'], $event['to'], $event['subject']), + NotificationRecorder::$eventType => FilterHelper::recordNotification($event['notifiable_instance'], $event['notification']), + OutboundRequestRecorder::$eventType => !empty($event['completed_at']) && FilterHelper::recordOutboundRequest($event['url'], $event['code'], $event['duration']), + QueryRecorder::$eventType => FilterHelper::recordQuery($event['query'], $event['time'], $event['path']), + QueuedJobRecorder::$eventType => FilterHelper::recordQueuedJob($event['connection'], $event['queue'], $event['job'], $event['status'], $event['duration'] ?? 0), + RequestRecorder::$eventType => FilterHelper::recordRequest($event['request_instance'], $event['response_instance'], $event['response']['status_code'], $event['request']['duration']), + ScheduledTaskRecorder::$eventType => FilterHelper::recordCommandOrScheduledTask($event['task'], $event['status'], $event['duration'] ?? 0), + default => false + }; + + if ($shouldAdd) { + unset($event['feature_flag_scope']); + unset($event['notifiable_instance']); + unset($event['request_instance']); + unset($event['response_instance']); + + $filtered[] = $event; + } + } + + if (!empty($filtered)) { + $this->events[$type] = $filtered; + } else { + unset($this->events[$type]); + } } } diff --git a/src/Override/DefaultOverride.php b/src/Override/DefaultOverride.php index 5814e86..b9caa3a 100644 --- a/src/Override/DefaultOverride.php +++ b/src/Override/DefaultOverride.php @@ -50,20 +50,23 @@ public function recordException($exception): bool } /** - * @param string $url + * @param $url + * @param $status_code + * @param $duration * @return bool */ - public function recordOutboundRequest($url): bool + public function recordOutboundRequest($url, $status_code, $duration): bool { return true; } /** - * @param string $query - * @param int $duration + * @param $query + * @param $duration + * @param $path * @return bool */ - public function recordQuery($query, $duration): bool + public function recordQuery($query, $duration, $path): bool { $ignore = [ "`".config('session.table')."`", @@ -81,19 +84,27 @@ public function recordQuery($query, $duration): bool } /** - * @param Job $job + * @param string $connection + * @param string $queue + * @param string $job + * @param string $status + * @param int $duration * @return bool */ - public function recordQueuedJob($job): bool + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool { return true; } /** - * @param Request $request + * @param $request + * @param $response + * @param $status + * @param $duration + * @param $user * @return bool */ - public function recordRequest($request): bool + public function recordRequest($request, $response, $status, $duration, $user): bool { $ignore = [ 'telescope/*'. @@ -115,9 +126,11 @@ public function recordRequest($request): bool /** * @param string $command + * @param string $status + * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask($command): bool + public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool { return true; } @@ -131,10 +144,12 @@ public function recordTaskScheduler(): bool } /** - * @param Email $message + * @param $mailable + * @param $to + * @param $subject * @return bool */ - public function recordMail($message): bool + public function recordMail($mailable, $to, $subject): bool { return true; } @@ -159,6 +174,17 @@ public function recordFeatureFlag($flag, $scope): bool return true; } + /** + * @param $level + * @param $message + * @param array $context + * @return bool + */ + public function recordLog($level, $message, array $context = []): bool + { + return true; + } + /** * @param Request $request * @return bool diff --git a/src/Override/LaritorOverride.php b/src/Override/LaritorOverride.php index acca918..5ee89a0 100644 --- a/src/Override/LaritorOverride.php +++ b/src/Override/LaritorOverride.php @@ -22,35 +22,48 @@ public function recordCacheHit($cacheKey): bool; public function recordException($exception): bool; /** - * @param string $url + * @param $url + * @param $status_code + * @param $duration * @return bool */ - public function recordOutboundRequest($url): bool; + public function recordOutboundRequest($url, $status_code, $duration): bool; /** - * @param string $query - * @param int $duration + * @param $query + * @param $duration + * @param $path * @return bool */ - public function recordQuery($query, $duration): bool; + public function recordQuery($query, $duration, $path): bool; /** - * @param Job $job + * @param string $connection + * @param string $queue + * @param string $job + * @param string $status + * @param int $duration * @return bool */ - public function recordQueuedJob($job): bool; + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool; /** - * @param Request $request + * @param $request + * @param $response + * @param $status + * @param $duration + * @param $user * @return bool */ - public function recordRequest($request): bool; + public function recordRequest($request, $response, $status, $duration, $user): bool; /** * @param string $command + * @param string $status + * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask($command): bool; + public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool; /** * @return bool @@ -58,10 +71,12 @@ public function recordCommandOrScheduledTask($command): bool; public function recordTaskScheduler(): bool; /** - * @param Email $message + * @param $mailable + * @param $to + * @param $subject * @return bool */ - public function recordMail($message): bool; + public function recordMail($mailable, $to, $subject): bool; /** * @param mixed $notifiable @@ -77,6 +92,14 @@ public function recordNotification($notifiable, $notification): bool; */ public function recordFeatureFlag($flag, $scope): bool; + /** + * @param $level + * @param $message + * @param array $context + * @return bool + */ + public function recordLog($level, $message, array $context = []): bool; + /** * @param Request $request * @return bool diff --git a/src/Override/TestOverride.php b/src/Override/TestOverride.php index 490ab3b..b057636 100644 --- a/src/Override/TestOverride.php +++ b/src/Override/TestOverride.php @@ -4,7 +4,7 @@ class TestOverride extends DefaultOverride { - public function recordRequest($request): bool + public function recordRequest($request, $response, $status, $duration, $user): bool { $ignore = [ 'laritor-job', diff --git a/src/Recorders/CacheRecorder.php b/src/Recorders/CacheRecorder.php index 86018a0..ba57186 100644 --- a/src/Recorders/CacheRecorder.php +++ b/src/Recorders/CacheRecorder.php @@ -7,6 +7,7 @@ use Illuminate\Cache\Events\CacheMissed; use Illuminate\Cache\Events\KeyForgotten; use Illuminate\Cache\Events\KeyWritten; +use Illuminate\Cache\Events\RetrievingKey; use Illuminate\Support\Str; class CacheRecorder extends Recorder @@ -32,14 +33,11 @@ class CacheRecorder extends Recorder */ public function trackEvent($event) { - if ( Str::startsWith($event->key, ['laritor']) || - !FilterHelper::recordCacheHit($event->key) - ) { - return; - } - $type = null; - if ($event instanceof CacheHit) { + if (class_exists(RetrievingKey::class) && $event instanceof RetrievingKey) { + $type = 'RETRIEVING'; + } + elseif ($event instanceof CacheHit) { $type = 'HIT'; } elseif ($event instanceof CacheMissed) { $type = 'MISS'; @@ -49,11 +47,30 @@ public function trackEvent($event) $type = 'DELETE'; } - $this->laritor->pushEvent(static::$eventType, [ - 'key' => $event->key, - 'type' => $type, - 'occurred_at' => now()->format('Y-m-d H:i:s'), - 'context' => $this->laritor->getContext() - ]); + $eventFound = false; + if ($type !== 'RETRIEVING') { + $events = collect($this->laritor->getEvents(static::$eventType)) + ->map(function ($added) use ($event, $type, &$eventFound) { + if ($added['type'] === 'RETRIEVING' && $added['key'] === $event->key) { + $eventFound = true; + $added['type'] = $type; + $added['duration'] = microtime(true) - $added['timestamp']; + } + return $added; + }); + + $this->laritor->addEvents(static::$eventType, $events); + } + + if (!$eventFound) { + $this->laritor->pushEvent(static::$eventType, [ + 'key' => $event->key, + 'type' => $type, + 'store' => $event->storeName, + 'duration' => 0, + 'occurred_at' => now()->format('Y-m-d H:i:s'), + 'context' => $this->laritor->getContext() + ]); + } } } diff --git a/src/Recorders/CommandRecorder.php b/src/Recorders/CommandRecorder.php index 75eed86..4adf9ac 100644 --- a/src/Recorders/CommandRecorder.php +++ b/src/Recorders/CommandRecorder.php @@ -33,10 +33,6 @@ class CommandRecorder extends Recorder */ public function trackEvent($event) { - if ($this->ignore($event->command) || !FilterHelper::recordCommandOrScheduledTask($event->command)) { - return; - } - if ($event instanceof CommandStarting ) { $this->start($event); } elseif ($event instanceof CommandFinished ) { diff --git a/src/Recorders/ExceptionRecorder.php b/src/Recorders/ExceptionRecorder.php index 6f18684..143f8d1 100644 --- a/src/Recorders/ExceptionRecorder.php +++ b/src/Recorders/ExceptionRecorder.php @@ -4,6 +4,7 @@ use BinaryBuilds\LaritorClient\Helpers\DataHelper; use BinaryBuilds\LaritorClient\Helpers\FilterHelper; +use BinaryBuilds\LaritorClient\Laritor; use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Support\Str; use BinaryBuilds\LaritorClient\Helpers\FileHelper; @@ -25,9 +26,7 @@ public function trackEvent($event) { $throwable = $event; - if (!FilterHelper::recordException($throwable)) { - return; - } + app(Laritor::class)->setException($throwable); $data = [ 'message' => DataHelper::redactData($throwable->getMessage()), diff --git a/src/Recorders/FeatureFlagRecorder.php b/src/Recorders/FeatureFlagRecorder.php index 83785e1..2e9bebd 100644 --- a/src/Recorders/FeatureFlagRecorder.php +++ b/src/Recorders/FeatureFlagRecorder.php @@ -19,11 +19,7 @@ class FeatureFlagRecorder extends Recorder */ public function trackEvent($event) { - if(!FilterHelper::recordFeatureFlag($event->feature, $event->scope)) { - return; - } - - self::recordFeatureCheck($event->feature, $event->value !== false); + self::recordFeatureCheck($event->feature, $event->scope, $event->value !== false); } public static function registerRecorder() @@ -33,13 +29,14 @@ public static function registerRecorder() } } - public static function recordFeatureCheck(string $feature, bool $active = true) + public static function recordFeatureCheck(string $feature, $scope = null, bool $active = true) { $laritor = app(Laritor::class); $laritor->pushEvent(self::$eventType, [ 'feature' => $feature, 'active' => $active, + 'feature_flag_scope' => $scope, 'context' => $laritor->getContext(), 'checked_at' => now()->toDateTimeString(), ]); diff --git a/src/Recorders/LogRecorder.php b/src/Recorders/LogRecorder.php index c738e9e..56000c6 100644 --- a/src/Recorders/LogRecorder.php +++ b/src/Recorders/LogRecorder.php @@ -25,10 +25,6 @@ class LogRecorder extends Recorder */ public function trackEvent($event) { - if(!$this->shouldRecordLog($event)) { - return; - } - $this->laritor->pushEvent(static::$eventType, [ 'level' => $event->level, 'message' => DataHelper::redactData($event->message), @@ -37,23 +33,4 @@ public function trackEvent($event) 'context' => $this->laritor->getContext() ]); } - - public function shouldRecordLog($event) - { - $levels = [ - 'DEBUG' => 1, - 'NOTICE' => 2, - 'INFO' => 3, - 'WARNING' => 4, - 'ERROR' => 5, - 'ALERT' => 6, - 'CRITICAL' => 7, - 'EMERGENCY' => 8 - ]; - - $minIndex = $levels[strtoupper(config('laritor.log_level'))]; - $logIndex = $levels[strtoupper($event->level)]; - - return $logIndex >= $minIndex; - } } diff --git a/src/Recorders/MailRecorder.php b/src/Recorders/MailRecorder.php index bdde587..1e8e4ff 100644 --- a/src/Recorders/MailRecorder.php +++ b/src/Recorders/MailRecorder.php @@ -31,10 +31,6 @@ class MailRecorder extends Recorder */ public function trackEvent($event) { - if (!FilterHelper::recordMail($event->message)) { - return; - } - if ($event instanceof MessageSending ) { $this->sending($event); } diff --git a/src/Recorders/NotificationRecorder.php b/src/Recorders/NotificationRecorder.php index 1cad862..82a1f1c 100644 --- a/src/Recorders/NotificationRecorder.php +++ b/src/Recorders/NotificationRecorder.php @@ -35,10 +35,6 @@ class NotificationRecorder extends Recorder */ public function trackEvent($event) { - if (!FilterHelper::recordNotification($event->notifiable, $event->notification)) { - return; - } - if ($event instanceof NotificationSending ) { $this->sending($event); } @@ -56,6 +52,7 @@ public function sending(NotificationSending $event) 'id' => $event->notification->id, 'notification' => get_class($event->notification), 'notifiable' => $this->formatNotifiable($event->notifiable), + 'notifiable_instance' => $event->notifiable, 'context' => $this->laritor->getContext(), 'started_at' => now()->format('Y-m-d H:i:s'), 'completed_at' => null diff --git a/src/Recorders/OutboundRequestRecorder.php b/src/Recorders/OutboundRequestRecorder.php index b42b71f..f9ea158 100644 --- a/src/Recorders/OutboundRequestRecorder.php +++ b/src/Recorders/OutboundRequestRecorder.php @@ -46,11 +46,6 @@ public function trackEvent($event) */ public function sending(RequestSending $event) { - if ( Str::contains($event->request->url(), 'laritor.net') || - !FilterHelper::recordOutboundRequest($event->request->url())) { - return; - } - $this->laritor->pushEvent(static::$eventType, [ 'started_at' => now(), 'completed_at' => null, diff --git a/src/Recorders/QueryRecorder.php b/src/Recorders/QueryRecorder.php index 6a6cf0e..4b48fa1 100644 --- a/src/Recorders/QueryRecorder.php +++ b/src/Recorders/QueryRecorder.php @@ -24,10 +24,6 @@ class QueryRecorder extends Recorder */ public function trackEvent($event) { - if (!FilterHelper::recordQuery($event->sql, $event->time)) { - return; - } - if($caller = $this->getCallerFromStackTrace()) { $time = $event->time; diff --git a/src/Recorders/QueuedJobRecorder.php b/src/Recorders/QueuedJobRecorder.php index 79942f0..b4c5cd2 100644 --- a/src/Recorders/QueuedJobRecorder.php +++ b/src/Recorders/QueuedJobRecorder.php @@ -10,6 +10,8 @@ use Illuminate\Queue\Events\JobProcessed; use Illuminate\Queue\Events\JobProcessing; use Illuminate\Queue\Events\JobQueued; +use Illuminate\Queue\Events\JobQueueing; +use Illuminate\Support\Facades\Queue; class QueuedJobRecorder extends Recorder { @@ -31,10 +33,6 @@ class QueuedJobRecorder extends Recorder */ public function trackEvent($event) { - if ($event->job instanceof QueueHealthCheck || !FilterHelper::recordQueuedJob($event->job)) { - return; - } - if ($event instanceof JobQueued ) { $this->queued($event); } diff --git a/src/Recorders/RequestRecorder.php b/src/Recorders/RequestRecorder.php index 1761a31..932518c 100644 --- a/src/Recorders/RequestRecorder.php +++ b/src/Recorders/RequestRecorder.php @@ -32,10 +32,6 @@ public function trackEvent($event) $request = $event->request; $response = $event->response; - if ($request->is('laritor/*') || !FilterHelper::recordRequest($request)) { - return; - } - $isBot = FilterHelper::isBot($request); $this->laritor->responseRenderCompleted(isset($event->response->exception) ? $event->response->exception : null); @@ -57,6 +53,8 @@ public function trackEvent($event) /** @phpstan-ignore-next-line */ $controller = $request->route() ? explode('@', optional($request->route())->getActionName()) : []; $this->laritor->pushEvent(static::$eventType, [ + 'request_instance' => $request, + 'response_instance' => $response, 'request' => [ 'started_at' => now()->subMilliseconds($duration)->format('Y-m-d H:i:s'), 'completed_at' => now()->format('Y-m-d H:i:s'), diff --git a/src/Recorders/ScheduledTaskRecorder.php b/src/Recorders/ScheduledTaskRecorder.php index 6301792..2236bb9 100644 --- a/src/Recorders/ScheduledTaskRecorder.php +++ b/src/Recorders/ScheduledTaskRecorder.php @@ -30,18 +30,6 @@ class ScheduledTaskRecorder extends Recorder */ public function trackEvent($event) { - $task = Str::substr( - Str::replace("'",'', $event->task->command), - mb_strpos(Str::replace("'",'', $event->task->command), 'artisan') - ); - - if ( - in_array($task, ['artisan laritor:send-metrics', 'artisan laritor:sync']) || - !FilterHelper::recordCommandOrScheduledTask($event->task->command) - ) { - return; - } - if ($event instanceof ScheduledTaskStarting ) { $this->start($event); } elseif ($event instanceof ScheduledTaskFinished ) { From c0ddf47955df4c2bc497da8f82ad4f4eda217a88 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 01:08:07 -0400 Subject: [PATCH 02/14] fix compatibility issues --- src/Helpers/FilterHelper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Helpers/FilterHelper.php b/src/Helpers/FilterHelper.php index a06a213..302e68d 100644 --- a/src/Helpers/FilterHelper.php +++ b/src/Helpers/FilterHelper.php @@ -45,7 +45,7 @@ public static function recordException($exception): bool public static function recordOutboundRequest($url, $status_code, $duration): bool { - return Str::doesntContain($url, 'laritor.net') && + return ! Str::contains($url, 'laritor.net') && static::recordEvent(function () use ($url, $status_code, $duration) { return app(LaritorOverride::class)->recordOutboundRequest($url, $status_code, $duration); }); @@ -60,7 +60,7 @@ public static function recordQuery($query, $duration, $path): bool public static function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool { - return Str::doesntContain($job, 'QueueHealthCheck') && + return ! Str::contains($job, 'QueueHealthCheck') && static::recordEvent(function () use ($connection, $queue, $job, $status, $duration) { return app(LaritorOverride::class)->recordQueuedJob($connection, $queue, $job, $status, $duration); }); @@ -75,7 +75,7 @@ public static function recordRequest($request, $response, int $status, int $dura public static function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool { - return Str::doesntContain($command, self::$ignoredCommands) && + return ! Str::contains($command, self::$ignoredCommands) && static::recordEvent(function () use ($command, $status, $duration) { return app(LaritorOverride::class)->recordCommandOrScheduledTask($command, $status, $duration); }); From ea991e9d21d14f36875a2c22d4c8d8da9f22f7af Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 01:14:46 -0400 Subject: [PATCH 03/14] fix storeName incompatibility --- src/Recorders/CacheRecorder.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Recorders/CacheRecorder.php b/src/Recorders/CacheRecorder.php index ba57186..4090253 100644 --- a/src/Recorders/CacheRecorder.php +++ b/src/Recorders/CacheRecorder.php @@ -2,13 +2,10 @@ namespace BinaryBuilds\LaritorClient\Recorders; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Cache\Events\CacheHit; use Illuminate\Cache\Events\CacheMissed; use Illuminate\Cache\Events\KeyForgotten; use Illuminate\Cache\Events\KeyWritten; -use Illuminate\Cache\Events\RetrievingKey; -use Illuminate\Support\Str; class CacheRecorder extends Recorder { @@ -34,7 +31,8 @@ class CacheRecorder extends Recorder public function trackEvent($event) { $type = null; - if (class_exists(RetrievingKey::class) && $event instanceof RetrievingKey) { + if (class_exists(\Illuminate\Cache\Events\RetrievingKey::class) && + $event instanceof \Illuminate\Cache\Events\RetrievingKey) { $type = 'RETRIEVING'; } elseif ($event instanceof CacheHit) { @@ -66,7 +64,7 @@ public function trackEvent($event) $this->laritor->pushEvent(static::$eventType, [ 'key' => $event->key, 'type' => $type, - 'store' => $event->storeName, + 'store' => property_exists($event, 'storeName') ? $event->storeName : config('cache.default'), 'duration' => 0, 'occurred_at' => now()->format('Y-m-d H:i:s'), 'context' => $this->laritor->getContext() From 180635a50de9351d2a3c616c5082110f3aaf6325 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 01:20:34 -0400 Subject: [PATCH 04/14] cleanup --- src/Recorders/CommandRecorder.php | 1 - src/Recorders/ExceptionRecorder.php | 1 - src/Recorders/FeatureFlagRecorder.php | 1 - src/Recorders/MailRecorder.php | 1 - src/Recorders/NotificationRecorder.php | 2 -- src/Recorders/OutboundRequestRecorder.php | 2 -- src/Recorders/QueryRecorder.php | 2 -- src/Recorders/QueuedJobRecorder.php | 4 ---- src/Recorders/ScheduledTaskRecorder.php | 2 -- 9 files changed, 16 deletions(-) diff --git a/src/Recorders/CommandRecorder.php b/src/Recorders/CommandRecorder.php index 4adf9ac..36b2187 100644 --- a/src/Recorders/CommandRecorder.php +++ b/src/Recorders/CommandRecorder.php @@ -4,7 +4,6 @@ use BinaryBuilds\LaritorClient\CommandOutput; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Console\Events\CommandFinished; use Illuminate\Console\Events\CommandStarting; use Illuminate\Support\Str; diff --git a/src/Recorders/ExceptionRecorder.php b/src/Recorders/ExceptionRecorder.php index 143f8d1..65a4967 100644 --- a/src/Recorders/ExceptionRecorder.php +++ b/src/Recorders/ExceptionRecorder.php @@ -3,7 +3,6 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use BinaryBuilds\LaritorClient\Laritor; use Illuminate\Contracts\Debug\ExceptionHandler; use Illuminate\Support\Str; diff --git a/src/Recorders/FeatureFlagRecorder.php b/src/Recorders/FeatureFlagRecorder.php index 2e9bebd..c04b4ae 100644 --- a/src/Recorders/FeatureFlagRecorder.php +++ b/src/Recorders/FeatureFlagRecorder.php @@ -2,7 +2,6 @@ namespace BinaryBuilds\LaritorClient\Recorders; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use BinaryBuilds\LaritorClient\Laritor; use Illuminate\Support\Facades\Event; diff --git a/src/Recorders/MailRecorder.php b/src/Recorders/MailRecorder.php index 1e8e4ff..c9d9288 100644 --- a/src/Recorders/MailRecorder.php +++ b/src/Recorders/MailRecorder.php @@ -3,7 +3,6 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Events\MessageSending; use Illuminate\Mail\Events\MessageSent; diff --git a/src/Recorders/NotificationRecorder.php b/src/Recorders/NotificationRecorder.php index 82a1f1c..b5f59cd 100644 --- a/src/Recorders/NotificationRecorder.php +++ b/src/Recorders/NotificationRecorder.php @@ -3,9 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Database\Eloquent\Model; -use Illuminate\Log\Events\MessageLogged; use Illuminate\Notifications\AnonymousNotifiable; use Illuminate\Notifications\Events\NotificationSending; use Illuminate\Notifications\Events\NotificationSent; diff --git a/src/Recorders/OutboundRequestRecorder.php b/src/Recorders/OutboundRequestRecorder.php index f9ea158..1baff83 100644 --- a/src/Recorders/OutboundRequestRecorder.php +++ b/src/Recorders/OutboundRequestRecorder.php @@ -3,13 +3,11 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Http\Client\Events\ConnectionFailed; use Illuminate\Http\Client\Events\RequestSending; use Illuminate\Http\Client\Events\ResponseReceived; use Illuminate\Http\Client\Request; use Illuminate\Http\Client\Response; -use Illuminate\Support\Str; class OutboundRequestRecorder extends Recorder diff --git a/src/Recorders/QueryRecorder.php b/src/Recorders/QueryRecorder.php index 4b48fa1..709a6c9 100644 --- a/src/Recorders/QueryRecorder.php +++ b/src/Recorders/QueryRecorder.php @@ -3,9 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Database\Events\QueryExecuted; -use Illuminate\Support\Str; use BinaryBuilds\LaritorClient\Helpers\FileHelper; class QueryRecorder extends Recorder diff --git a/src/Recorders/QueuedJobRecorder.php b/src/Recorders/QueuedJobRecorder.php index b4c5cd2..58bc38f 100644 --- a/src/Recorders/QueuedJobRecorder.php +++ b/src/Recorders/QueuedJobRecorder.php @@ -3,15 +3,11 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; -use BinaryBuilds\LaritorClient\Jobs\QueueHealthCheck; use Carbon\Carbon; use Illuminate\Queue\Events\JobExceptionOccurred; use Illuminate\Queue\Events\JobProcessed; use Illuminate\Queue\Events\JobProcessing; use Illuminate\Queue\Events\JobQueued; -use Illuminate\Queue\Events\JobQueueing; -use Illuminate\Support\Facades\Queue; class QueuedJobRecorder extends Recorder { diff --git a/src/Recorders/ScheduledTaskRecorder.php b/src/Recorders/ScheduledTaskRecorder.php index 2236bb9..5d83456 100644 --- a/src/Recorders/ScheduledTaskRecorder.php +++ b/src/Recorders/ScheduledTaskRecorder.php @@ -3,13 +3,11 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; -use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Console\Events\ScheduledTaskFailed; use Illuminate\Console\Events\ScheduledTaskFinished; use Illuminate\Console\Events\ScheduledTaskSkipped; use Illuminate\Console\Events\ScheduledTaskStarting; use Illuminate\Console\Scheduling\CallbackEvent; -use Illuminate\Console\Scheduling\Event; use Illuminate\Support\Facades\Context; use Illuminate\Support\Str; From b5aa986f0073b6eee37e27891d98cea288c0a878 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 01:33:24 -0400 Subject: [PATCH 05/14] added upgrade guide --- UPGRADING.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 UPGRADING.md diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..6da121f --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,39 @@ +# Upgrading +## Upgrading to 4.x from 3.x + +This guide covers upgrading `binarybuilds/laritor-client` from 3.x to 4.x. + +### Upgrade the package + +Update your Composer constraint, then refresh the lock file: + +```sh +composer require binarybuilds/laritor-client:^4.0 --update-with-all-dependencies +``` + +### Update custom filters + +4.x moves event filtering from the point where an event is recorded to just before the event batch is sent. This lets filters use the completed event data, such as an HTTP response status, request duration, queued-job outcome, or mail recipient. + +If your application binds a custom implementation of `BinaryBuilds\LaritorClient\Override\LaritorOverride`, update it to match the new interface. A custom class that extends `DefaultOverride` only needs to update the methods it overrides; a class that implements the interface directly must implement the new `recordLog()` method as well. + +| Filter | 3.x signature | 4.x signature | +| --- | --- | --- | +| Outbound request | `recordOutboundRequest($url)` | `recordOutboundRequest($url, $statusCode, $duration)` | +| Query | `recordQuery($query, $duration)` | `recordQuery($query, $duration, $path)` | +| Queued job | `recordQueuedJob($job)` | `recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration)` | +| Request | `recordRequest($request)` | `recordRequest($request, $response, $status, $duration, $user)` | +| Command / scheduled task | `recordCommandOrScheduledTask($command)` | `recordCommandOrScheduledTask(string $command, string $status, int $duration)` | +| Mail | `recordMail($message)` | `recordMail($mailable, $to, $subject)` | +| Log | _not available_ | `recordLog($level, $message, array $context = [])` | + +### Review filtering behavior + +`LARITOR_LOG_LEVEL` is no longer applied by `LogRecorder`. If you used it to limit logs, move that policy into a custom `recordLog()` filter, for example: + +```php +public function recordLog($level, $message, array $context = []): bool +{ + return in_array(strtolower($level), ['error', 'critical', 'alert', 'emergency'], true); +} +``` \ No newline at end of file From e85143f2f4694d61442de0c0fbb8f8e47cb9a9c6 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 03:43:51 -0400 Subject: [PATCH 06/14] update data filter stubs and generator command --- src/Commands/DataFilterMakeCommand.php | 11 +- src/Laritor.php | 99 ++++++++++++ src/Override/DefaultOverride.php | 2 - src/Recorders/CacheRecorder.php | 1 + src/Recorders/CommandRecorder.php | 8 +- src/Recorders/QueuedJobRecorder.php | 5 +- src/Recorders/RequestRecorder.php | 7 +- stubs/ExceptionsOnlyDataFilter.stub | 149 +++++++++++++++++ stubs/FullObservabilityDataFilter.stub | 148 +++++++++++++++++ stubs/IssuesOnlyDataFilter.stub | 216 +++++++++++++++++++++++++ stubs/LaritorDataFilter.stub | 122 -------------- 11 files changed, 639 insertions(+), 129 deletions(-) create mode 100644 stubs/ExceptionsOnlyDataFilter.stub create mode 100644 stubs/FullObservabilityDataFilter.stub create mode 100644 stubs/IssuesOnlyDataFilter.stub delete mode 100644 stubs/LaritorDataFilter.stub diff --git a/src/Commands/DataFilterMakeCommand.php b/src/Commands/DataFilterMakeCommand.php index 788574d..12e8b57 100644 --- a/src/Commands/DataFilterMakeCommand.php +++ b/src/Commands/DataFilterMakeCommand.php @@ -3,6 +3,7 @@ namespace BinaryBuilds\LaritorClient\Commands; use Illuminate\Console\GeneratorCommand; +use Symfony\Component\Console\Input\InputArgument; class DataFilterMakeCommand extends GeneratorCommand { @@ -34,7 +35,11 @@ class DataFilterMakeCommand extends GeneratorCommand */ protected function getStub() { - return __DIR__.'/../../stubs/LaritorDataFilter.stub'; + return match ($this->argument('type')) { + 'issues-only' => __DIR__.'/../../stubs/IssuesOnlyDataFilter.stub', + 'exceptions-only' => __DIR__.'/../../stubs/ExceptionsOnlyDataFilter.stub', + default => __DIR__.'/../../stubs/FullObservabilityDataFilter.stub' + }; } /** @@ -50,7 +55,9 @@ protected function getDefaultNamespace($rootNamespace) protected function getArguments() { - return []; + return [ + ['type', InputArgument::OPTIONAL, 'The type of the filter', 'full-observability'], + ]; } protected function getNameInput() diff --git a/src/Laritor.php b/src/Laritor.php index b20296d..b2ccd97 100644 --- a/src/Laritor.php +++ b/src/Laritor.php @@ -54,6 +54,94 @@ class Laritor private $exception = null; + private int $requestStatus = 0; + + private int $requestDuration = 0; + + private $failedJob = null; + + private $failedCommand = null; + + private $jobDuration = 0; + + private $commandDuration = 0; + + public function getJobDuration() + { + return $this->jobDuration; + } + + public function setJobDuration($jobDuration): void + { + $this->jobDuration = $jobDuration; + } + + public function getCommandDuration() + { + return $this->commandDuration; + } + + public function setCommandDuration($commandDuration): void + { + $this->commandDuration = $commandDuration; + } + + /** + * @param $failedJob + * @return void + */ + public function setFailedJob($failedJob): void + { + $this->failedJob = $failedJob; + } + + /** + * @param $failedCommand + * @return void + */ + public function setFailedCommand($failedCommand): void + { + $this->failedCommand = $failedCommand; + } + + public function hasFailedJob(): bool + { + return !is_null($this->failedJob); + } + + public function hasFailedCommand(): bool + { + return !is_null($this->failedCommand); + } + + /** + * @param int $status + * @return void + */ + public function setRequestStatus(int $status): void + { + $this->requestStatus = $status; + } + + /** + * @param int $duration + * @return void + */ + public function setRequestDuration(int $duration): void + { + $this->requestDuration = $duration; + } + + public function getRequestStatus(): int + { + return $this->requestStatus; + } + + public function getRequestDuration(): int + { + return $this->requestDuration; + } + /** * @return \Throwable|null */ @@ -62,6 +150,11 @@ public function getException() return $this->exception; } + public static function hasException(): bool + { + return !is_null(app(Laritor::class)->getException()); + } + /** * @param \Throwable|null $exception */ @@ -274,6 +367,12 @@ public function reset() $this->context = 'BOOT'; $this->hasCustomLogs = false; $this->exception = null; + $this->requestStatus = 0; + $this->requestDuration = 0; + $this->failedJob = null; + $this->failedCommand = null; + $this->jobDuration = 0; + $this->commandDuration = 0; } /** diff --git a/src/Override/DefaultOverride.php b/src/Override/DefaultOverride.php index b9caa3a..8f08b34 100644 --- a/src/Override/DefaultOverride.php +++ b/src/Override/DefaultOverride.php @@ -2,12 +2,10 @@ namespace BinaryBuilds\LaritorClient\Override; -use Illuminate\Contracts\Queue\Job; use Illuminate\Http\Request; use Illuminate\Notifications\Notification; use Illuminate\Support\Str; use Jaybizzle\CrawlerDetect\CrawlerDetect; -use Symfony\Component\Mime\Email; class DefaultOverride implements LaritorOverride { diff --git a/src/Recorders/CacheRecorder.php b/src/Recorders/CacheRecorder.php index 4090253..8318ddb 100644 --- a/src/Recorders/CacheRecorder.php +++ b/src/Recorders/CacheRecorder.php @@ -18,6 +18,7 @@ class CacheRecorder extends Recorder * @var string[] */ public static $events = [ + \Illuminate\Cache\Events\RetrievingKey::class, CacheHit::class, CacheMissed::class, KeyWritten::class, diff --git a/src/Recorders/CommandRecorder.php b/src/Recorders/CommandRecorder.php index 36b2187..f266be6 100644 --- a/src/Recorders/CommandRecorder.php +++ b/src/Recorders/CommandRecorder.php @@ -88,7 +88,13 @@ public function finish(CommandFinished $event) )->firstWhere('completed_at', '=',null); if ($command) { - $command['duration'] = $command['started_at']->diffInMilliseconds(); + $duration = $command['started_at']->diffInMilliseconds(); + $this->laritor->setCommandDuration($duration); + if ($event->exitCode > 0) { + $this->laritor->setFailedCommand($event->command); + } + + $command['duration'] = $duration; $command['completed_at'] = now()->format('Y-m-d H:i:s'); $command['started_at'] = $command['started_at']->format('Y-m-d H:i:s'); $command['code'] = $event->exitCode; diff --git a/src/Recorders/QueuedJobRecorder.php b/src/Recorders/QueuedJobRecorder.php index 58bc38f..a160b3c 100644 --- a/src/Recorders/QueuedJobRecorder.php +++ b/src/Recorders/QueuedJobRecorder.php @@ -36,6 +36,7 @@ public function trackEvent($event) $this->processing($event); } elseif ($event instanceof JobExceptionOccurred) { app(ExceptionRecorder::class)->handle($event->exception); + $this->laritor->setFailedJob($event->job); $this->complete($event); } elseif ($event instanceof JobProcessed ) { $this->complete($event); @@ -125,7 +126,9 @@ public function complete($event) foreach ($this->laritor->getEvents(static::$eventType) as $job) { if (isset($job['id']) && $job['id'] === $this->resolveJobId($event)) { $start = Carbon::parse($job['started_at']); - $job['duration'] = $start->diffInMilliseconds(); + $duration = $start->diffInMilliseconds(); + $this->laritor->setJobDuration($duration); + $job['duration'] = $duration; $job['started_at'] = $start->toDateTimeString(); $job['completed_at'] = now()->toDateTimeString(); $job['status'] = $event instanceof JobExceptionOccurred ? 'failed' : 'processed'; diff --git a/src/Recorders/RequestRecorder.php b/src/Recorders/RequestRecorder.php index 932518c..681a57c 100644 --- a/src/Recorders/RequestRecorder.php +++ b/src/Recorders/RequestRecorder.php @@ -52,6 +52,11 @@ public function trackEvent($event) /** @phpstan-ignore-next-line */ $controller = $request->route() ? explode('@', optional($request->route())->getActionName()) : []; + + $status = $this->getStatusCode($response); + $this->laritor->setRequestDuration($duration); + $this->laritor->setRequestStatus($status); + $this->laritor->pushEvent(static::$eventType, [ 'request_instance' => $request, 'response_instance' => $response, @@ -66,7 +71,7 @@ public function trackEvent($event) 'body' => $this->getRequestBody($request), ], 'response' => [ - 'status_code' => $this->getStatusCode($response), + 'status_code' => $status, 'size' => strlen($response->getContent()), 'headers' => $this->getResponseHeaders($response), 'body' => $this->getResponseBody($response), diff --git a/stubs/ExceptionsOnlyDataFilter.stub b/stubs/ExceptionsOnlyDataFilter.stub new file mode 100644 index 0000000..7c70952 --- /dev/null +++ b/stubs/ExceptionsOnlyDataFilter.stub @@ -0,0 +1,149 @@ +getRequestStatus() >= 400 || + $laritor->getRequestDuration() >= 1000; + } + + public function hasFailedJob() + { + $laritor = app(Laritor::class); + + return + $laritor->hasFailedJob() || + $laritor->getJobDuration() >= 60000; + } + + public function hasFailedCommand() + { + $laritor = app(Laritor::class); + + return + $laritor->hasFailedCommand() || + $laritor->getCommandDuration() >= 300000; + } + + /** + * @param string $cacheKey + * @return bool + */ + public function recordCacheHit($cacheKey): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException(); + } + + /** + * @param \Throwable $exception + * @return bool + */ + public function recordException($exception): bool + { + return true; + } + + /** + * @param $url + * @param $status_code + * @param $duration + * @return bool + */ + public function recordOutboundRequest($url, $status_code, $duration): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException() || + $status_code >= 300 || + $duration >= 1000; + } + + /** + * @param $query + * @param $duration + * @param $path + * @return bool + */ + public function recordQuery($query, $duration, $path): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException() || + $duration >= 200; + } + + /** + * @param string $connection + * @param string $queue + * @param string $job + * @param string $status + * @param int $duration + * @return bool + */ + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedCommand() || + Laritor::hasException() || + strtolower($status) === 'failed' || + $duration >= 60000; + } + + /** + * @param $request + * @param $response + * @param $status + * @param $duration + * @param $user + * @return bool + */ + public function recordRequest($request, $response, $status, $duration, $user): bool + { + return + Laritor::hasException() || + $status >= 400 || + $duration >= 1000; + } + + /** + * @param string $command + * @param string $status + * @param int $duration + * @return bool + */ + public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool + { + return strtolower($status) === 'failed' || $duration >= 60000; + } + + /** + * @return bool + */ + public function recordTaskScheduler(): bool + { + return parent::recordTaskScheduler(); + } + + /** + * @param $mailable + * @param $to + * @param $subject + * @return bool + */ + public function recordMail($mailable, $to, $subject): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException(); + } + + /** + * @param mixed $notifiable + * @param Notification $notification + * @return bool + */ + public function recordNotification($notifiable, $notification): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException(); + } + + /** + * @param string $flag + * @param mixed $scope + * @return bool + */ + public function recordFeatureFlag($flag, $scope): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException(); + } + + /** + * @param $level + * @param $message + * @param array $context + * @return bool + */ + public function recordLog($level, $message, array $context = []): bool + { + return + $this->hasFailedRequest() || + $this->hasFailedJob() || + $this->hasFailedCommand() || + Laritor::hasException() || + !in_array(strtoupper($level), ['INFO','DEBUG']); + } + + /** + * @param Request $request + * @return bool + */ + public function isBot($request): bool + { + return parent::isBot($request); + } +} \ No newline at end of file diff --git a/stubs/LaritorDataFilter.stub b/stubs/LaritorDataFilter.stub deleted file mode 100644 index 3f37286..0000000 --- a/stubs/LaritorDataFilter.stub +++ /dev/null @@ -1,122 +0,0 @@ - Date: Sun, 19 Jul 2026 12:33:25 -0400 Subject: [PATCH 07/14] fix cache recorder --- src/Recorders/CacheRecorder.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Recorders/CacheRecorder.php b/src/Recorders/CacheRecorder.php index 8318ddb..be01ab9 100644 --- a/src/Recorders/CacheRecorder.php +++ b/src/Recorders/CacheRecorder.php @@ -18,7 +18,6 @@ class CacheRecorder extends Recorder * @var string[] */ public static $events = [ - \Illuminate\Cache\Events\RetrievingKey::class, CacheHit::class, CacheMissed::class, KeyWritten::class, @@ -72,4 +71,16 @@ public function trackEvent($event) ]); } } + + /** + * @return void + */ + public static function registerRecorder() + { + if (class_exists(\Illuminate\Cache\Events\RetrievingKey::class)) { + self::$events[] = \Illuminate\Cache\Events\RetrievingKey::class; + } + + parent::registerRecorder(); + } } From e3f731d910faebfdc9cc2d8e03988e57ed1a3d30 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 21:59:37 -0400 Subject: [PATCH 08/14] moved filters from env to override class to allow more control over filters --- config/laritor.php | 46 +------- src/Commands/SyncCommand.php | 7 +- src/Helpers/DataHelper.php | 2 +- src/Helpers/FilterHelper.php | 124 ++++++++++++++++++++++ src/Laritor.php | 21 +--- src/Override/DefaultOverride.php | 90 ++++++++++++++++ src/Override/LaritorOverride.php | 42 ++++++++ src/Recorders/CommandRecorder.php | 3 +- src/Recorders/FetchesStackTrace.php | 3 +- src/Recorders/LogRecorder.php | 3 +- src/Recorders/OutboundRequestRecorder.php | 31 +++--- src/Recorders/QueryRecorder.php | 6 +- src/Recorders/QueuedJobRecorder.php | 14 ++- src/Recorders/RequestRecorder.php | 51 ++++----- src/Recorders/ScheduledTaskRecorder.php | 12 ++- stubs/ExceptionsOnlyDataFilter.stub | 90 ++++++++++++++++ stubs/FullObservabilityDataFilter.stub | 90 ++++++++++++++++ stubs/IssuesOnlyDataFilter.stub | 90 ++++++++++++++++ 18 files changed, 601 insertions(+), 124 deletions(-) diff --git a/config/laritor.php b/config/laritor.php index 7d77dd9..a7b6b77 100644 --- a/config/laritor.php +++ b/config/laritor.php @@ -19,51 +19,7 @@ 'server_name' => env('LARITOR_SERVER_NAME'), - 'log_level' => env('LARITOR_LOG_LEVEL', 'debug'), - 'max_events' => env('LARITOR_MAX_EVENTS_PER_OCCURRENCE', 5000), - 'context' => env('LARITOR_RECORD_CONTEXT', true), - - 'db_schema' => env('LARITOR_RECORD_DB_SCHEMA', true), - - 'query_bindings' => env('LARITOR_RECORD_QUERY_BINDINGS', true), - - 'requests' => [ - - 'query_string' => env('LARITOR_RECORD_QUERY_STRING', true), - - 'body' => env('LARITOR_RECORD_REQUEST_BODY', false), - - 'headers' => env('LARITOR_RECORD_REQUEST_HEADERS', false), - - 'response_headers' => env('LARITOR_RECORD_REQUEST_RESPONSE_HEADERS', false), - - 'response_body' => env('LARITOR_RECORD_REQUEST_RESPONSE_BODY', false), - - 'rate_limit' => [ - 'enabled' => env('LARITOR_RATE_LIMIT_REQUESTS', false), - - 'attempts' => env('LARITOR_RATE_LIMIT_REQUESTS_ATTEMPTS', 5), - ], - ], - - 'outbound_requests' => [ - - 'body' => env('LARITOR_RECORD_OUTBOUND_REQUEST_BODY', false), - - 'headers' => env('LARITOR_RECORD_OUTBOUND_REQUEST_HEADERS', false), - - 'response_headers' => env('LARITOR_RECORD_OUTBOUND_REQUEST_RESPONSE_HEADERS', false), - - 'response_body' => env('LARITOR_RECORD_OUTBOUND_REQUEST_RESPONSE_BODY', false), - ], - - 'session' => [ - 'data' => env('LARITOR_RECORD_SESSION_DATA', false), - ], - - 'whitelisted_vendors' => env('LARITOR_WHITELISTED_VENDORS', ''), - - 'ingest_events_without_occurrence' => env('LARITOR_INGEST_EVENTS_WITHOUT_OCCURRENCE', false), + 'ingest_events_without_occurrence' => env('LARITOR_INGEST_EVENTS_WITHOUT_OCCURRENCE', true), ]; \ No newline at end of file diff --git a/src/Commands/SyncCommand.php b/src/Commands/SyncCommand.php index 836a33e..62a2354 100644 --- a/src/Commands/SyncCommand.php +++ b/src/Commands/SyncCommand.php @@ -2,6 +2,7 @@ namespace BinaryBuilds\LaritorClient\Commands; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use BinaryBuilds\LaritorClient\SendOutputToLaritor; use Illuminate\Console\Command; use BinaryBuilds\LaritorClient\Helpers\DatabaseHelper; @@ -61,11 +62,7 @@ public function handle( $health_checks = $healthCheckHelper->getHealthChecks(); - $schema = []; - - if ( config('laritor.db_schema') ) { - $schema = $databaseHelper->getSchema(); - } + $schema = FilterHelper::recordDatabaseSchema() ? $databaseHelper->getSchema() : []; $response = $laritor->sync([ 'scheduled_tasks' => $scheduled_tasks, diff --git a/src/Helpers/DataHelper.php b/src/Helpers/DataHelper.php index 3fba15f..06383ad 100644 --- a/src/Helpers/DataHelper.php +++ b/src/Helpers/DataHelper.php @@ -41,7 +41,7 @@ public static function redactData($text) public static function getRedactedContext() { - if (config('laritor.context') && class_exists(\Illuminate\Support\Facades\Context::class)) { + if (class_exists(\Illuminate\Support\Facades\Context::class)) { return app(DataRedactor::class)->redactArray( \Illuminate\Support\Facades\Context::all() ); diff --git a/src/Helpers/FilterHelper.php b/src/Helpers/FilterHelper.php index 302e68d..f37f359 100644 --- a/src/Helpers/FilterHelper.php +++ b/src/Helpers/FilterHelper.php @@ -122,4 +122,128 @@ public static function isBot($request): bool return app(LaritorOverride::class)->isBot($request); }, false); } + + public static function recordCommandContext(string $command, string $status, int $duration): bool + { + return static::recordEvent(function () use ($command, $status, $duration) { + return app(LaritorOverride::class)->recordCommandContext($command, $status, $duration); + }, true); + } + + public static function recordScheduledTaskContext(string $task, string $status, int $duration): bool + { + return static::recordEvent(function () use ($task, $status, $duration) { + return app(LaritorOverride::class)->recordScheduledTaskContext($task, $status, $duration); + }, true); + } + + public static function recordRequestContext($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordRequestContext($request, $response, $status, $duration, Auth::user()); + }, true); + } + + public static function recordLogContext($level, $message): bool + { + return static::recordEvent(function () use ($level, $message) { + return app(LaritorOverride::class)->recordLogContext($level, $message); + }, true); + } + + public static function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + { + return static::recordEvent(function () use ($connection, $queue, $job, $status, $duration) { + return app(LaritorOverride::class)->recordQueuedJobContext($connection, $queue, $job, $status, $duration); + }, true); + } + + public static function recordDatabaseSchema(): bool + { + return static::recordEvent(function () { + return app(LaritorOverride::class)->recordDatabaseSchema(); + }, true); + } + + public static function recordQueryBindings($query, $duration, $path): bool + { + return static::recordEvent(function () use ($query, $duration, $path) { + return app(LaritorOverride::class)->recordQueryBindings($query, $duration, $path); + }, true); + } + + public static function recordRequestQueryParameters($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordRequestQueryParameters($request, $response, $status, $duration, Auth::user()); + }, true); + } + + public static function recordRequestHeaders($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordRequestHeaders($request, $response, $status, $duration, Auth::user()); + }, true); + } + + public static function recordRequestBody($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordRequestBody($request, $response, $status, $duration, Auth::user()); + }, false); + } + + public static function recordResponseHeaders($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordResponseHeaders($request, $response, $status, $duration, Auth::user()); + }, true); + } + + public static function recordResponseBody($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordResponseBody($request, $response, $status, $duration, Auth::user()); + }, false); + } + + public static function recordSessionData($request, $response, $status, $duration): bool + { + return static::recordEvent(function () use ($request, $response, $status, $duration) { + return app(LaritorOverride::class)->recordSessionData($request, $response, $status, $duration, Auth::user()); + }, true); + } + + public static function recordOutboundRequestHeaders($url, $status_code, $duration): bool + { + return static::recordEvent(function () use ($url, $status_code, $duration) { + return app(LaritorOverride::class)->recordOutboundRequestHeaders($url, $status_code, $duration); + }, true); + } + + public static function recordOutboundRequestBody($url, $status_code, $duration): bool + { + return static::recordEvent(function () use ($url, $status_code, $duration) { + return app(LaritorOverride::class)->recordOutboundRequestBody($url, $status_code, $duration); + }, false); + } + + public static function recordOutboundRequestResponseHeaders($url, $status_code, $duration): bool + { + return static::recordEvent(function () use ($url, $status_code, $duration) { + return app(LaritorOverride::class)->recordOutboundRequestResponseHeaders($url, $status_code, $duration); + }, true); + } + + public static function recordOutboundRequestResponseBody($url, $status_code, $duration): bool + { + return static::recordEvent(function () use ($url, $status_code, $duration) { + return app(LaritorOverride::class)->recordOutboundRequestResponseBody($url, $status_code, $duration); + }, false); + } + + public static function whitelistedVendors(): array + { + return []; + } } \ No newline at end of file diff --git a/src/Laritor.php b/src/Laritor.php index b2ccd97..a86bc21 100644 --- a/src/Laritor.php +++ b/src/Laritor.php @@ -538,25 +538,6 @@ public function shouldSendEvents() } } - if (! $hasOccurrence) { - return false; - } - - if (app()->runningInConsole() || ! $this->isRateLimiterEnabled() ) { - return true; - } - - $key = 'laritor-'.Str::slug(request()->path()); - if (! RateLimiter::tooManyAttempts($key, config('laritor.requests.rate_limit.attempts') ) ) { - RateLimiter::hit($key); - return true; - } - - return false; - } - - public function isRateLimiterEnabled() - { - return (bool)config('laritor.requests.rate_limit.enabled', false); + return $hasOccurrence; } } diff --git a/src/Override/DefaultOverride.php b/src/Override/DefaultOverride.php index 8f08b34..8b75309 100644 --- a/src/Override/DefaultOverride.php +++ b/src/Override/DefaultOverride.php @@ -193,4 +193,94 @@ public function isBot($request): bool $crawler = new CrawlerDetect(); return $crawler->isCrawler($userAgent); } + + public function recordCommandContext(string $command, string $status, int $duration): bool + { + return true; + } + + public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + { + return true; + } + + public function recordRequestContext($request, $response, $status, $duration, $user): bool + { + return true; + } + + public function recordLogContext($level, $message): bool + { + return true; + } + + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + { + return true; + } + + public function recordDatabaseSchema(): bool + { + return true; + } + + public function recordQueryBindings($query, $duration, $path): bool + { + return true; + } + + public function recordRequestQueryParameters($request, $response, $status, $duration, $user): bool + { + return true; + } + + public function recordRequestHeaders($request, $response, $status, $duration, $user): bool + { + return true; + } + + public function recordRequestBody($request, $response, $status, $duration, $user): bool + { + return false; + } + + public function recordResponseHeaders($request, $response, $status, $duration, $user): bool + { + return true; + } + + public function recordResponseBody($request, $response, $status, $duration, $user): bool + { + return false; + } + + public function recordSessionData($request, $response, $status, $duration, $user): bool + { + return true; + } + + public function recordOutboundRequestHeaders($url, $status_code, $duration): bool + { + return true; + } + + public function recordOutboundRequestBody($url, $status_code, $duration): bool + { + return false; + } + + public function recordOutboundRequestResponseHeaders($url, $status_code, $duration): bool + { + return true; + } + + public function recordOutboundRequestResponseBody($url, $status_code, $duration): bool + { + return false; + } + + public function whitelistedVendors(): array + { + return []; + } } \ No newline at end of file diff --git a/src/Override/LaritorOverride.php b/src/Override/LaritorOverride.php index 5ee89a0..2b6b80d 100644 --- a/src/Override/LaritorOverride.php +++ b/src/Override/LaritorOverride.php @@ -105,4 +105,46 @@ public function recordLog($level, $message, array $context = []): bool; * @return bool */ public function isBot($request): bool; + + public function recordCommandContext(string $command, string $status, int $duration): bool; + + public function recordScheduledTaskContext(string $task, string $status, int $duration): bool; + + public function recordRequestContext($request, $response, $status, $duration, $user): bool; + + public function recordLogContext($level, $message): bool; + + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool; + + public function recordDatabaseSchema(): bool; + + /** + * @param $query + * @param $duration + * @param $path + * @return bool + */ + public function recordQueryBindings($query, $duration, $path): bool; + + public function recordRequestQueryParameters($request, $response, $status, $duration, $user): bool; + + public function recordRequestHeaders($request, $response, $status, $duration, $user): bool; + + public function recordRequestBody($request, $response, $status, $duration, $user): bool; + + public function recordResponseHeaders($request, $response, $status, $duration, $user): bool; + + public function recordResponseBody($request, $response, $status, $duration, $user): bool; + + public function recordSessionData($request, $response, $status, $duration, $user): bool; + + public function recordOutboundRequestHeaders($url, $status_code, $duration): bool; + + public function recordOutboundRequestBody($url, $status_code, $duration): bool; + + public function recordOutboundRequestResponseHeaders($url, $status_code, $duration): bool; + + public function recordOutboundRequestResponseBody($url, $status_code, $duration): bool; + + public function whitelistedVendors(): array; } \ No newline at end of file diff --git a/src/Recorders/CommandRecorder.php b/src/Recorders/CommandRecorder.php index f266be6..aee3138 100644 --- a/src/Recorders/CommandRecorder.php +++ b/src/Recorders/CommandRecorder.php @@ -4,6 +4,7 @@ use BinaryBuilds\LaritorClient\CommandOutput; use BinaryBuilds\LaritorClient\Helpers\DataHelper; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Console\Events\CommandFinished; use Illuminate\Console\Events\CommandStarting; use Illuminate\Support\Str; @@ -98,7 +99,7 @@ public function finish(CommandFinished $event) $command['completed_at'] = now()->format('Y-m-d H:i:s'); $command['started_at'] = $command['started_at']->format('Y-m-d H:i:s'); $command['code'] = $event->exitCode; - $command['custom_context'] = DataHelper::getRedactedContext(); + $command['custom_context'] = FilterHelper::recordCommandContext($event->command, $event->exitCode > 0 ? 'failed' : 'completed', $duration) ? DataHelper::getRedactedContext() : []; $command['output'] = app(CommandOutput::class)->getLines(); app(CommandOutput::class)->resetLines(); diff --git a/src/Recorders/FetchesStackTrace.php b/src/Recorders/FetchesStackTrace.php index 26cb914..44e1f7c 100644 --- a/src/Recorders/FetchesStackTrace.php +++ b/src/Recorders/FetchesStackTrace.php @@ -2,6 +2,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Support\Str; trait FetchesStackTrace @@ -36,7 +37,7 @@ protected function getCallerFromStackTrace($forgetLines = 0) */ protected function whitelistedVendors(): array { - $whitelist = config('laritor.whitelisted_vendors', '') ? explode(',', config('laritor.whitelisted_vendors', '')) : []; + $whitelist = FilterHelper::whitelistedVendors(); return array_map(function ($path) { return 'vendor/'.$path; diff --git a/src/Recorders/LogRecorder.php b/src/Recorders/LogRecorder.php index 56000c6..5724519 100644 --- a/src/Recorders/LogRecorder.php +++ b/src/Recorders/LogRecorder.php @@ -3,6 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Log\Events\MessageLogged; class LogRecorder extends Recorder @@ -28,7 +29,7 @@ public function trackEvent($event) $this->laritor->pushEvent(static::$eventType, [ 'level' => $event->level, 'message' => DataHelper::redactData($event->message), - 'log_context' => DataHelper::redactArray($event->context), + 'log_context' => FilterHelper::recordLogContext($event->level, $event->message) ? DataHelper::redactArray($event->context) : [], 'occurred_at' => now()->format('Y-m-d H:i:s'), 'context' => $this->laritor->getContext() ]); diff --git a/src/Recorders/OutboundRequestRecorder.php b/src/Recorders/OutboundRequestRecorder.php index 1baff83..d6a03b7 100644 --- a/src/Recorders/OutboundRequestRecorder.php +++ b/src/Recorders/OutboundRequestRecorder.php @@ -3,6 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Http\Client\Events\ConnectionFailed; use Illuminate\Http\Client\Events\RequestSending; use Illuminate\Http\Client\Events\ResponseReceived; @@ -82,18 +83,20 @@ public function completeOutboundRequest($outboundRequestEvent) if ( $request['status'] === 'sent' && $request['url'] === $outboundRequestEvent->request->url() ) { $started = $request['started_at']; + $duration = $started->diffInMilliseconds(); + $status = $outboundRequestEvent instanceof ResponseReceived ? $outboundRequestEvent->response->status() : 0; $request['started_at'] = $started->format('Y-m-d H:i:s'); $request['completed_at'] = now()->format('Y-m-d H:i:s'); - $request['duration'] = $started->diffInMilliseconds(); - $request['code'] = $outboundRequestEvent instanceof ResponseReceived ? $outboundRequestEvent->response->status() : 0; + $request['duration'] = $duration; + $request['code'] = $status; $request['status'] = 'completed'; $request['request'] = [ - 'body' => $this->getRequestBody($outboundRequestEvent->request), - 'headers' => $this->getRequestHeaders($outboundRequestEvent->request), + 'body' => $this->getRequestBody($outboundRequestEvent->request, $status, $duration), + 'headers' => $this->getRequestHeaders($outboundRequestEvent->request, $status, $duration), ]; $request['response'] = [ - 'body' => $outboundRequestEvent instanceof ConnectionFailed ? false : $this->getResponseBody($outboundRequestEvent->response), - 'headers' => $outboundRequestEvent instanceof ConnectionFailed ? false : $this->getResponseHeaders($outboundRequestEvent->response), + 'body' => $outboundRequestEvent instanceof ConnectionFailed ? false : $this->getResponseBody($outboundRequestEvent->response, $outboundRequestEvent->request->url(), $status, $duration), + 'headers' => $outboundRequestEvent instanceof ConnectionFailed ? false : $this->getResponseHeaders($outboundRequestEvent->response, $outboundRequestEvent->request->url(), $status, $duration), ]; } @@ -103,9 +106,9 @@ public function completeOutboundRequest($outboundRequestEvent) $this->laritor->addEvents(static::$eventType, $outboundRequests); } - protected function getRequestBody(Request $request) + protected function getRequestBody(Request $request, $status, $duration) { - if (config('laritor.outbound_requests.body')) { + if (FilterHelper::recordOutboundRequestBody($request->url(), $status, $duration)) { return $request->isJson() ? DataHelper::redactArray(json_decode($request->body(), true)) : DataHelper::redactData($request->body()); @@ -114,18 +117,18 @@ protected function getRequestBody(Request $request) return false; } - protected function getRequestHeaders(Request $request) + protected function getRequestHeaders(Request $request, $status, $duration) { - if (config('laritor.outbound_requests.headers')) { + if (FilterHelper::recordOutboundRequestHeaders($request->url(), $status, $duration)) { return DataHelper::redactHeaders($request->headers()); } return false; } - protected function getResponseBody(Response $response) + protected function getResponseBody(Response $response, $url, $status, $duration) { - if (config('laritor.outbound_requests.response_body')) { + if (FilterHelper::recordOutboundRequestResponseBody($url, $status, $duration)) { $body = $response->json(); if (is_array($body)) { @@ -138,9 +141,9 @@ protected function getResponseBody(Response $response) return false; } - protected function getResponseHeaders(Response $response) + protected function getResponseHeaders(Response $response, $url, $status, $duration) { - if (config('laritor.outbound_requests.response_headers')) { + if (FilterHelper::recordOutboundRequestResponseHeaders($url, $status, $duration)) { return DataHelper::redactHeaders($response->headers()); } diff --git a/src/Recorders/QueryRecorder.php b/src/Recorders/QueryRecorder.php index 709a6c9..346b630 100644 --- a/src/Recorders/QueryRecorder.php +++ b/src/Recorders/QueryRecorder.php @@ -3,6 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Database\Events\QueryExecuted; use BinaryBuilds\LaritorClient\Helpers\FileHelper; @@ -24,12 +25,13 @@ public function trackEvent($event) { if($caller = $this->getCallerFromStackTrace()) { $time = $event->time; + $path = FileHelper::parseFileName($caller['file']) .'@'.$caller['line']; $query = [ 'query' => $event->sql, - 'bindings' => config('laritor.query_bindings') ? DataHelper::redactData($this->replaceBindings($event)) : null, + 'bindings' => FilterHelper::recordQueryBindings($event->sql, $time, $path) ? DataHelper::redactData($this->replaceBindings($event)) : null, 'time' => $time, - 'path' => FileHelper::parseFileName($caller['file']) .'@'.$caller['line'], + 'path' => $path, 'completed_at' => now()->format('Y-m-d H:i:s'), 'context' => $this->laritor->getContext() ]; diff --git a/src/Recorders/QueuedJobRecorder.php b/src/Recorders/QueuedJobRecorder.php index a160b3c..8cc4b94 100644 --- a/src/Recorders/QueuedJobRecorder.php +++ b/src/Recorders/QueuedJobRecorder.php @@ -3,6 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Carbon\Carbon; use Illuminate\Queue\Events\JobExceptionOccurred; use Illuminate\Queue\Events\JobProcessed; @@ -56,16 +57,18 @@ public function queued(JobQueued $event) $jobPayload = $event->payload(); } + $queue = $event->job->queue ?? config("queue.connections.{$event->connectionName}.queue", 'default'); + $jobName = isset($jobPayload['displayName']) ? $jobPayload['displayName'] : get_class($event->job); $this->laritor->pushEvent(static::$eventType, [ 'connection' => $event->connectionName, - 'queue' => $event->job->queue ?? config("queue.connections.{$event->connectionName}.queue", 'default'), - 'job' => isset($jobPayload['displayName']) ? $jobPayload['displayName'] : get_class($event->job), + 'queue' => $queue, + 'job' => $jobName, 'id' => $this->resolveJobId($event), 'delay' => isset($event->delay) ? $event->delay : ( isset($jobPayload['delay']) ? $jobPayload['delay'] : 0 ), 'queued_at' => now()->toDateTimeString(), 'status' => 'queued', 'context' => $this->laritor->getContext(), - 'custom_context' => DataHelper::getRedactedContext(), + 'custom_context' => FilterHelper::recordQueuedJobContext($event->connectionName, $queue, $jobName, 'queued', 0) ? DataHelper::getRedactedContext() : [], ]); } @@ -125,14 +128,15 @@ public function complete($event) $jobs = []; foreach ($this->laritor->getEvents(static::$eventType) as $job) { if (isset($job['id']) && $job['id'] === $this->resolveJobId($event)) { + $status = $event instanceof JobExceptionOccurred ? 'failed' : 'processed'; $start = Carbon::parse($job['started_at']); $duration = $start->diffInMilliseconds(); $this->laritor->setJobDuration($duration); $job['duration'] = $duration; $job['started_at'] = $start->toDateTimeString(); $job['completed_at'] = now()->toDateTimeString(); - $job['status'] = $event instanceof JobExceptionOccurred ? 'failed' : 'processed'; - $job['custom_context'] = DataHelper::getRedactedContext(); + $job['status'] = $status; + $job['custom_context'] = FilterHelper::recordQueuedJobContext($job['connection'], $job['queue'], $job['job'], $status, $duration) ? DataHelper::getRedactedContext() : []; } $jobs[] = $job; diff --git a/src/Recorders/RequestRecorder.php b/src/Recorders/RequestRecorder.php index 681a57c..269f337 100644 --- a/src/Recorders/RequestRecorder.php +++ b/src/Recorders/RequestRecorder.php @@ -44,19 +44,19 @@ public function trackEvent($event) 'data' => [] ]; + $status = $this->getStatusCode($response); + $this->laritor->setRequestDuration($duration); + $this->laritor->setRequestStatus($status); + if ($request->hasSession()) { $session['id'] = $request->session()->getId(); $session['name'] = $request->session()->getName(); - $session['data'] = config('laritor.session.data') ? $request->session()->all() : []; + $session['data'] = FilterHelper::recordSessionData($request, $response, $status, $duration) ? $request->session()->all() : []; } /** @phpstan-ignore-next-line */ $controller = $request->route() ? explode('@', optional($request->route())->getActionName()) : []; - $status = $this->getStatusCode($response); - $this->laritor->setRequestDuration($duration); - $this->laritor->setRequestStatus($status); - $this->laritor->pushEvent(static::$eventType, [ 'request_instance' => $request, 'response_instance' => $response, @@ -65,16 +65,16 @@ public function trackEvent($event) 'completed_at' => now()->format('Y-m-d H:i:s'), 'duration' => $duration, 'memory' => round(memory_get_peak_usage(true) / 1024 / 1024, 1), - 'url' => $this->getUrl($request), + 'url' => $this->getUrl($request, $response, $status, $duration), 'size' => strlen($request->getContent()), - 'headers' => $this->getRequestHeaders($request), - 'body' => $this->getRequestBody($request), + 'headers' => $this->getRequestHeaders($request, $response, $status, $duration), + 'body' => $this->getRequestBody($request, $response, $status, $duration), ], 'response' => [ 'status_code' => $status, 'size' => strlen($response->getContent()), - 'headers' => $this->getResponseHeaders($response), - 'body' => $this->getResponseBody($response), + 'headers' => $this->getResponseHeaders($request, $response, $status, $duration), + 'body' => $this->getResponseBody($request, $response, $status, $duration), ], 'session' => $session, 'user' => [ @@ -92,7 +92,7 @@ public function trackEvent($event) 'controller_method' => isset($controller[1]) ? $controller[1] : 'closure', 'method' => $request->method(), ], - 'custom_context' => $this->getContext($request), + 'custom_context' => $this->getContext($request, $response, $status, $duration), ]); } @@ -105,7 +105,7 @@ private function getStatusCode($response) return $response->getStatusCode(); } - private function getContext($request) + private function getContext($request, $response, $status, $duration) { $context = []; @@ -121,12 +121,15 @@ private function getContext($request) } } - return array_merge($context, DataHelper::getRedactedContext()); + return array_merge( + $context, + FilterHelper::recordRequestContext($request, $response, $status, $duration) ? DataHelper::getRedactedContext() : [] + ); } - protected function getRequestBody($request) + protected function getRequestBody($request, $response, $status, $duration) { - if (config('laritor.requests.body')) { + if (FilterHelper::recordRequestBody($request, $response, $status, $duration)) { $payload = $request->post(); return ! empty($payload) ? DataHelper::redactArray($payload) : @@ -136,18 +139,18 @@ protected function getRequestBody($request) return false; } - protected function getRequestHeaders($request) + protected function getRequestHeaders($request, $response, $status, $duration) { - if (config('laritor.requests.headers')) { + if (FilterHelper::recordRequestHeaders($request, $response, $status, $duration)) { return DataHelper::redactHeaders($request->headers->all()); } return false; } - protected function getResponseBody($response) + protected function getResponseBody($request, $response, $status, $duration) { - if (config('laritor.requests.response_body')) { + if (FilterHelper::recordResponseBody($request, $response, $status, $duration)) { $body = $response->getContent(); @@ -163,9 +166,9 @@ protected function getResponseBody($response) return false; } - protected function getResponseHeaders($response) + protected function getResponseHeaders($request, $response, $status, $duration) { - if (config('laritor.requests.response_headers')) { + if (FilterHelper::recordResponseHeaders($request, $response, $status, $duration)) { return DataHelper::redactHeaders($response->headers->all()); } @@ -193,7 +196,7 @@ private function getAuthenticatedUser() return $user; } - private function getUrl($request) + private function getUrl($request, $response, $status, $duration) { if ($this->isLivewireUpdateRequest($request)) { $url = ''; @@ -204,7 +207,7 @@ private function getUrl($request) $url = rtrim($fragments['path'], '/'); } - if (config('laritor.requests.query_string') && isset($fragments['query'])) { + if (FilterHelper::recordRequestQueryParameters($request, $response, $status, $duration) && isset($fragments['query'])) { $url .= '?' . $fragments['query']; } @@ -215,7 +218,7 @@ private function getUrl($request) } $query = ''; - if (config('laritor.requests.query_string')) { + if (FilterHelper::recordRequestQueryParameters($request, $response, $status, $duration)) { $query = $request->getQueryString(); $query = $query ? '?'.$query : ''; diff --git a/src/Recorders/ScheduledTaskRecorder.php b/src/Recorders/ScheduledTaskRecorder.php index 5d83456..769788b 100644 --- a/src/Recorders/ScheduledTaskRecorder.php +++ b/src/Recorders/ScheduledTaskRecorder.php @@ -3,6 +3,7 @@ namespace BinaryBuilds\LaritorClient\Recorders; use BinaryBuilds\LaritorClient\Helpers\DataHelper; +use BinaryBuilds\LaritorClient\Helpers\FilterHelper; use Illuminate\Console\Events\ScheduledTaskFailed; use Illuminate\Console\Events\ScheduledTaskFinished; use Illuminate\Console\Events\ScheduledTaskSkipped; @@ -88,11 +89,11 @@ public function start(ScheduledTaskStarting $event) public function skip(ScheduledTaskSkipped $event) { $event = $event->task; - + $task = $event instanceof CallbackEvent ? 'Closure' : $event->command; $payload = [ 'started_at' => now()->format('Y-m-d H:i:s'), 'duration' => 0, - 'task' => $event instanceof CallbackEvent ? 'Closure' : $event->command, + 'task' => $task, 'expression' => $event->expression, 'timezone' => $event->timezone, 'user' => $event->user, @@ -100,7 +101,7 @@ public function skip(ScheduledTaskSkipped $event) 'maintenance' => $event->evenInMaintenanceMode, 'one_server' => $event->onOneServer, 'status' => 'skipped', - 'custom_context' => DataHelper::getRedactedContext(), + 'custom_context' => FilterHelper::recordScheduledTaskContext($task, 'skipped', 0) ? DataHelper::getRedactedContext() : [], 'scheduled_at_timestamp' => microtime(true), ]; @@ -124,11 +125,12 @@ public function completeScheduledTask($event, $status) $task['task'] === ( $event instanceof CallbackEvent ? 'Closure' : $event->command) && $task['status'] === 'started' ) { + $duration = $task['started_at']->diffInMilliseconds(); $task['status'] = $status; - $task['duration'] = $task['started_at']->diffInMilliseconds(); + $task['duration'] = $duration; $task['completed_at'] = now()->format('Y-m-d H:i:s'); $task['started_at'] = $task['started_at']->format('Y-m-d H:i:s'); - $task['custom_context'] = DataHelper::getRedactedContext(); + $task['custom_context'] = FilterHelper::recordScheduledTaskContext($task, $status, $duration) ? DataHelper::getRedactedContext() : []; } return $task; diff --git a/stubs/ExceptionsOnlyDataFilter.stub b/stubs/ExceptionsOnlyDataFilter.stub index 7c70952..8defe0c 100644 --- a/stubs/ExceptionsOnlyDataFilter.stub +++ b/stubs/ExceptionsOnlyDataFilter.stub @@ -146,4 +146,94 @@ class LaritorDataFilter extends DefaultOverride { return parent::isBot($request); } + + public function recordCommandContext(string $command, string $status, int $duration): bool + { + return Laritor::hasException(); + } + + public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + { + return Laritor::hasException(); + } + + public function recordRequestContext($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordLogContext($level, $message): bool + { + return Laritor::hasException(); + } + + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + { + return Laritor::hasException(); + } + + public function recordDatabaseSchema(): bool + { + return parent::recordDatabaseSchema(); + } + + public function recordQueryBindings($query, $duration, $path): bool + { + return Laritor::hasException(); + } + + public function recordRequestQueryParameters($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordRequestHeaders($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordRequestBody($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordResponseHeaders($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordResponseBody($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordSessionData($request, $response, $status, $duration, $user): bool + { + return Laritor::hasException(); + } + + public function recordOutboundRequestHeaders($url, $status_code, $duration): bool + { + return Laritor::hasException(); + } + + public function recordOutboundRequestBody($url, $status_code, $duration): bool + { + return Laritor::hasException(); + } + + public function recordOutboundRequestResponseHeaders($url, $status_code, $duration): bool + { + return Laritor::hasException(); + } + + public function recordOutboundRequestResponseBody($url, $status_code, $duration): bool + { + return Laritor::hasException(); + } + + public function whitelistedVendors(): array + { + return Laritor::hasException(); + } } \ No newline at end of file diff --git a/stubs/FullObservabilityDataFilter.stub b/stubs/FullObservabilityDataFilter.stub index 2aa94f0..600a879 100644 --- a/stubs/FullObservabilityDataFilter.stub +++ b/stubs/FullObservabilityDataFilter.stub @@ -145,4 +145,94 @@ class LaritorDataFilter extends DefaultOverride { return parent::isBot($request); } + + public function recordCommandContext(string $command, string $status, int $duration): bool + { + return parent::recordCommandContext($command, $status, $duration); + } + + public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + { + return parent::recordScheduledTaskContext($task, $status, $duration); + } + + public function recordRequestContext($request, $response, $status, $duration, $user): bool + { + return parent::recordRequestContext($request, $response, $status, $duration, $user); + } + + public function recordLogContext($level, $message): bool + { + return parent::recordLogContext($level, $message); + } + + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + { + return parent::recordQueuedJobContext($connection, $queue, $job, $status, $duration); + } + + public function recordDatabaseSchema(): bool + { + return parent::recordDatabaseSchema(); + } + + public function recordQueryBindings($query, $duration, $path): bool + { + return parent::recordQueryBindings($query, $duration, $path); + } + + public function recordRequestQueryParameters($request, $response, $status, $duration, $user): bool + { + return parent::recordRequestQueryParameters($request, $response, $status, $duration, $user); + } + + public function recordRequestHeaders($request, $response, $status, $duration, $user): bool + { + return parent::recordRequestHeaders($request, $response, $status, $duration, $user); + } + + public function recordRequestBody($request, $response, $status, $duration, $user): bool + { + return parent::recordRequestBody($request, $response, $status, $duration, $user); + } + + public function recordResponseHeaders($request, $response, $status, $duration, $user): bool + { + return parent::recordResponseHeaders($request, $response, $status, $duration, $user); + } + + public function recordResponseBody($request, $response, $status, $duration, $user): bool + { + return parent::recordResponseBody($request, $response, $status, $duration, $user); + } + + public function recordSessionData($request, $response, $status, $duration, $user): bool + { + return parent::recordSessionData($request, $response, $status, $duration, $user); + } + + public function recordOutboundRequestHeaders($url, $status_code, $duration): bool + { + return parent::recordOutboundRequestHeaders($url, $status_code, $duration); + } + + public function recordOutboundRequestBody($url, $status_code, $duration): bool + { + return parent::recordOutboundRequestBody($url, $status_code, $duration); + } + + public function recordOutboundRequestResponseHeaders($url, $status_code, $duration): bool + { + return parent::recordOutboundRequestResponseHeaders($url, $status_code, $duration); + } + + public function recordOutboundRequestResponseBody($url, $status_code, $duration): bool + { + return parent::recordOutboundRequestResponseBody($url, $status_code, $duration); + } + + public function whitelistedVendors(): array + { + return parent::whitelistedVendors(); + } } \ No newline at end of file diff --git a/stubs/IssuesOnlyDataFilter.stub b/stubs/IssuesOnlyDataFilter.stub index 22447f5..0cfefec 100644 --- a/stubs/IssuesOnlyDataFilter.stub +++ b/stubs/IssuesOnlyDataFilter.stub @@ -213,4 +213,94 @@ class LaritorDataFilter extends DefaultOverride { return parent::isBot($request); } + + public function recordCommandContext(string $command, string $status, int $duration): bool + { + return self::recordCommandOrScheduledTask($command, $status, $duration); + } + + public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + { + return self::recordCommandOrScheduledTask($task, $status, $duration); + } + + public function recordRequestContext($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordLogContext($level, $message): bool + { + return self::recordLog($level, $message); + } + + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + { + return self::recordQueuedJob($connection, $queue, $job, $status, $duration); + } + + public function recordDatabaseSchema(): bool + { + return parent::recordDatabaseSchema(); + } + + public function recordQueryBindings($query, $duration, $path): bool + { + return self::recordQuery($query, $duration, $path); + } + + public function recordRequestQueryParameters($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordRequestHeaders($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordRequestBody($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordResponseHeaders($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordResponseBody($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordSessionData($request, $response, $status, $duration, $user): bool + { + return self::recordRequest($request, $response, $status, $duration, $user); + } + + public function recordOutboundRequestHeaders($url, $status_code, $duration): bool + { + return self::recordOutboundRequest($url, $status_code, $duration); + } + + public function recordOutboundRequestBody($url, $status_code, $duration): bool + { + return self::recordOutboundRequest($url, $status_code, $duration); + } + + public function recordOutboundRequestResponseHeaders($url, $status_code, $duration): bool + { + return self::recordOutboundRequest($url, $status_code, $duration); + } + + public function recordOutboundRequestResponseBody($url, $status_code, $duration): bool + { + return self::recordOutboundRequest($url, $status_code, $duration); + } + + public function whitelistedVendors(): array + { + return parent::whitelistedVendors(); + } } \ No newline at end of file From c0f181f13317086a28679c5783fb82d45b0473fb Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 22:04:18 -0400 Subject: [PATCH 09/14] fix tests --- src/Recorders/OutboundRequestRecorder.php | 8 ++++---- src/Recorders/RequestRecorder.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Recorders/OutboundRequestRecorder.php b/src/Recorders/OutboundRequestRecorder.php index d6a03b7..28e80e5 100644 --- a/src/Recorders/OutboundRequestRecorder.php +++ b/src/Recorders/OutboundRequestRecorder.php @@ -114,7 +114,7 @@ protected function getRequestBody(Request $request, $status, $duration) DataHelper::redactData($request->body()); } - return false; + return []; } protected function getRequestHeaders(Request $request, $status, $duration) @@ -123,7 +123,7 @@ protected function getRequestHeaders(Request $request, $status, $duration) return DataHelper::redactHeaders($request->headers()); } - return false; + return []; } protected function getResponseBody(Response $response, $url, $status, $duration) @@ -138,7 +138,7 @@ protected function getResponseBody(Response $response, $url, $status, $duration) return DataHelper::redactData($response->body()); } - return false; + return []; } protected function getResponseHeaders(Response $response, $url, $status, $duration) @@ -147,6 +147,6 @@ protected function getResponseHeaders(Response $response, $url, $status, $durati return DataHelper::redactHeaders($response->headers()); } - return false; + return []; } } diff --git a/src/Recorders/RequestRecorder.php b/src/Recorders/RequestRecorder.php index 269f337..c7a909b 100644 --- a/src/Recorders/RequestRecorder.php +++ b/src/Recorders/RequestRecorder.php @@ -136,7 +136,7 @@ protected function getRequestBody($request, $response, $status, $duration) DataHelper::redactData(trim($request->getContent())); } - return false; + return []; } protected function getRequestHeaders($request, $response, $status, $duration) @@ -145,7 +145,7 @@ protected function getRequestHeaders($request, $response, $status, $duration) return DataHelper::redactHeaders($request->headers->all()); } - return false; + return []; } protected function getResponseBody($request, $response, $status, $duration) @@ -163,7 +163,7 @@ protected function getResponseBody($request, $response, $status, $duration) return DataHelper::redactData($body); } - return false; + return []; } protected function getResponseHeaders($request, $response, $status, $duration) @@ -172,7 +172,7 @@ protected function getResponseHeaders($request, $response, $status, $duration) return DataHelper::redactHeaders($response->headers->all()); } - return false; + return []; } private function getAuthenticatedUser() From 0a699ab466559785c35aac6b70a0fa437847c95e Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 22:26:12 -0400 Subject: [PATCH 10/14] fix tests --- src/Override/TestOverride.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Override/TestOverride.php b/src/Override/TestOverride.php index b057636..7db0c23 100644 --- a/src/Override/TestOverride.php +++ b/src/Override/TestOverride.php @@ -24,4 +24,14 @@ public function recordException($exception): bool { return !request()->is('laritor-failed-job'); } + + public function recordOutboundRequestBody($url, $status_code, $duration): bool + { + return true; + } + + public function recordOutboundRequestResponseBody($url, $status_code, $duration): bool + { + return true; + } } \ No newline at end of file From dbb0faf9e466518701a03d5e4f7eb4b3d54f4bf7 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 22:27:34 -0400 Subject: [PATCH 11/14] fix tests --- src/Override/TestOverride.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Override/TestOverride.php b/src/Override/TestOverride.php index 7db0c23..3be7bae 100644 --- a/src/Override/TestOverride.php +++ b/src/Override/TestOverride.php @@ -34,4 +34,14 @@ public function recordOutboundRequestResponseBody($url, $status_code, $duration) { return true; } + + public function recordRequestBody($request, $response, $status, $duration, $user): bool + { + return true; + } + + public function recordResponseBody($request, $response, $status, $duration, $user): bool + { + return true; + } } \ No newline at end of file From 1405df9457c480be30dc97ffc86472c6b1935922 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 22:41:27 -0400 Subject: [PATCH 12/14] fix tests --- phpunit.xml | 9 --------- src/Helpers/FilterHelper.php | 12 ++++++------ src/Override/DefaultOverride.php | 10 +++++----- src/Override/LaritorOverride.php | 8 ++++---- stubs/ExceptionsOnlyDataFilter.stub | 10 +++++----- stubs/FullObservabilityDataFilter.stub | 10 +++++----- stubs/IssuesOnlyDataFilter.stub | 10 +++++----- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 9536b2c..e3a4584 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -13,15 +13,6 @@ - - - - - - - - - diff --git a/src/Helpers/FilterHelper.php b/src/Helpers/FilterHelper.php index f37f359..002f97c 100644 --- a/src/Helpers/FilterHelper.php +++ b/src/Helpers/FilterHelper.php @@ -58,7 +58,7 @@ public static function recordQuery($query, $duration, $path): bool }); } - public static function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool + public static function recordQueuedJob(string $connection, string $queue, string $job, string $status, $duration): bool { return ! Str::contains($job, 'QueueHealthCheck') && static::recordEvent(function () use ($connection, $queue, $job, $status, $duration) { @@ -66,14 +66,14 @@ public static function recordQueuedJob(string $connection, string $queue, string }); } - public static function recordRequest($request, $response, int $status, int $duration): bool + public static function recordRequest($request, $response, int $status, $duration): bool { return !$request->is('laritor/*') && static::recordEvent(function () use ($request, $response, $status, $duration) { return app(LaritorOverride::class)->recordRequest($request, $response, $status, $duration, Auth::user()); }); } - public static function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool + public static function recordCommandOrScheduledTask(string $command, string $status, $duration): bool { return ! Str::contains($command, self::$ignoredCommands) && static::recordEvent(function () use ($command, $status, $duration) { @@ -123,14 +123,14 @@ public static function isBot($request): bool }, false); } - public static function recordCommandContext(string $command, string $status, int $duration): bool + public static function recordCommandContext(string $command, string $status, $duration): bool { return static::recordEvent(function () use ($command, $status, $duration) { return app(LaritorOverride::class)->recordCommandContext($command, $status, $duration); }, true); } - public static function recordScheduledTaskContext(string $task, string $status, int $duration): bool + public static function recordScheduledTaskContext(string $task, string $status, $duration): bool { return static::recordEvent(function () use ($task, $status, $duration) { return app(LaritorOverride::class)->recordScheduledTaskContext($task, $status, $duration); @@ -151,7 +151,7 @@ public static function recordLogContext($level, $message): bool }, true); } - public static function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + public static function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, $duration): bool { return static::recordEvent(function () use ($connection, $queue, $job, $status, $duration) { return app(LaritorOverride::class)->recordQueuedJobContext($connection, $queue, $job, $status, $duration); diff --git a/src/Override/DefaultOverride.php b/src/Override/DefaultOverride.php index 8b75309..e2051a4 100644 --- a/src/Override/DefaultOverride.php +++ b/src/Override/DefaultOverride.php @@ -89,7 +89,7 @@ public function recordQuery($query, $duration, $path): bool * @param int $duration * @return bool */ - public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, $duration): bool { return true; } @@ -128,7 +128,7 @@ public function recordRequest($request, $response, $status, $duration, $user): b * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool + public function recordCommandOrScheduledTask(string $command, string $status, $duration): bool { return true; } @@ -194,12 +194,12 @@ public function isBot($request): bool return $crawler->isCrawler($userAgent); } - public function recordCommandContext(string $command, string $status, int $duration): bool + public function recordCommandContext(string $command, string $status, $duration): bool { return true; } - public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + public function recordScheduledTaskContext(string $task, string $status, $duration): bool { return true; } @@ -214,7 +214,7 @@ public function recordLogContext($level, $message): bool return true; } - public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, $duration): bool { return true; } diff --git a/src/Override/LaritorOverride.php b/src/Override/LaritorOverride.php index 2b6b80d..8455130 100644 --- a/src/Override/LaritorOverride.php +++ b/src/Override/LaritorOverride.php @@ -63,7 +63,7 @@ public function recordRequest($request, $response, $status, $duration, $user): b * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool; + public function recordCommandOrScheduledTask(string $command, string $status, $duration): bool; /** * @return bool @@ -106,15 +106,15 @@ public function recordLog($level, $message, array $context = []): bool; */ public function isBot($request): bool; - public function recordCommandContext(string $command, string $status, int $duration): bool; + public function recordCommandContext(string $command, string $status, $duration): bool; - public function recordScheduledTaskContext(string $task, string $status, int $duration): bool; + public function recordScheduledTaskContext(string $task, string $status, $duration): bool; public function recordRequestContext($request, $response, $status, $duration, $user): bool; public function recordLogContext($level, $message): bool; - public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool; + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, $duration): bool; public function recordDatabaseSchema(): bool; diff --git a/stubs/ExceptionsOnlyDataFilter.stub b/stubs/ExceptionsOnlyDataFilter.stub index 8defe0c..1250768 100644 --- a/stubs/ExceptionsOnlyDataFilter.stub +++ b/stubs/ExceptionsOnlyDataFilter.stub @@ -59,7 +59,7 @@ class LaritorDataFilter extends DefaultOverride * @param int $duration * @return bool */ - public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, $duration): bool { return Laritor::hasException(); } @@ -83,7 +83,7 @@ class LaritorDataFilter extends DefaultOverride * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool + public function recordCommandOrScheduledTask(string $command, string $status, $duration): bool { return Laritor::hasException(); } @@ -147,12 +147,12 @@ class LaritorDataFilter extends DefaultOverride return parent::isBot($request); } - public function recordCommandContext(string $command, string $status, int $duration): bool + public function recordCommandContext(string $command, string $status, $duration): bool { return Laritor::hasException(); } - public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + public function recordScheduledTaskContext(string $task, string $status, $duration): bool { return Laritor::hasException(); } @@ -167,7 +167,7 @@ class LaritorDataFilter extends DefaultOverride return Laritor::hasException(); } - public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, $duration): bool { return Laritor::hasException(); } diff --git a/stubs/FullObservabilityDataFilter.stub b/stubs/FullObservabilityDataFilter.stub index 600a879..d7a3796 100644 --- a/stubs/FullObservabilityDataFilter.stub +++ b/stubs/FullObservabilityDataFilter.stub @@ -58,7 +58,7 @@ class LaritorDataFilter extends DefaultOverride * @param int $duration * @return bool */ - public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, $duration): bool { return parent::recordQueuedJob($connection, $queue, $job, $status, $duration); } @@ -82,7 +82,7 @@ class LaritorDataFilter extends DefaultOverride * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool + public function recordCommandOrScheduledTask(string $command, string $status, $duration): bool { return parent::recordCommandOrScheduledTask($command, $status, $duration); } @@ -146,12 +146,12 @@ class LaritorDataFilter extends DefaultOverride return parent::isBot($request); } - public function recordCommandContext(string $command, string $status, int $duration): bool + public function recordCommandContext(string $command, string $status, $duration): bool { return parent::recordCommandContext($command, $status, $duration); } - public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + public function recordScheduledTaskContext(string $task, string $status, $duration): bool { return parent::recordScheduledTaskContext($task, $status, $duration); } @@ -166,7 +166,7 @@ class LaritorDataFilter extends DefaultOverride return parent::recordLogContext($level, $message); } - public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, $duration): bool { return parent::recordQueuedJobContext($connection, $queue, $job, $status, $duration); } diff --git a/stubs/IssuesOnlyDataFilter.stub b/stubs/IssuesOnlyDataFilter.stub index 0cfefec..3d96428 100644 --- a/stubs/IssuesOnlyDataFilter.stub +++ b/stubs/IssuesOnlyDataFilter.stub @@ -101,7 +101,7 @@ class LaritorDataFilter extends DefaultOverride * @param int $duration * @return bool */ - public function recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJob(string $connection, string $queue, string $job, string $status, $duration): bool { return $this->hasFailedRequest() || @@ -133,7 +133,7 @@ class LaritorDataFilter extends DefaultOverride * @param int $duration * @return bool */ - public function recordCommandOrScheduledTask(string $command, string $status, int $duration): bool + public function recordCommandOrScheduledTask(string $command, string $status, $duration): bool { return strtolower($status) === 'failed' || $duration >= 60000; } @@ -214,12 +214,12 @@ class LaritorDataFilter extends DefaultOverride return parent::isBot($request); } - public function recordCommandContext(string $command, string $status, int $duration): bool + public function recordCommandContext(string $command, string $status, $duration): bool { return self::recordCommandOrScheduledTask($command, $status, $duration); } - public function recordScheduledTaskContext(string $task, string $status, int $duration): bool + public function recordScheduledTaskContext(string $task, string $status, $duration): bool { return self::recordCommandOrScheduledTask($task, $status, $duration); } @@ -234,7 +234,7 @@ class LaritorDataFilter extends DefaultOverride return self::recordLog($level, $message); } - public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, int $duration): bool + public function recordQueuedJobContext(string $connection, string $queue, string $job, string $status, $duration): bool { return self::recordQueuedJob($connection, $queue, $job, $status, $duration); } From 3983f8dad68041a4979e7886a7da62546a7fe050 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 22:58:00 -0400 Subject: [PATCH 13/14] update upgrade guide --- UPGRADING.md | 112 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 19 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 6da121f..af2d5d2 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -1,7 +1,8 @@ -# Upgrading -## Upgrading to 4.x from 3.x +# Upgrade Guide -This guide covers upgrading `binarybuilds/laritor-client` from 3.x to 4.x. +## Upgrading to 4.x + +This guide covers upgrading `binarybuilds/laritor-client` to 4.x. ### Upgrade the package @@ -11,29 +12,102 @@ Update your Composer constraint, then refresh the lock file: composer require binarybuilds/laritor-client:^4.0 --update-with-all-dependencies ``` -### Update custom filters +### Optional: Update custom event filters + +> This step is required only if your application uses a custom Laritor override filter class. If you are unsure how to upgrade your custom override class, rename your current override class, publish the new override class by following https://laritor.com/docs/customization and make any necessary changes after publishing. -4.x moves event filtering from the point where an event is recorded to just before the event batch is sent. This lets filters use the completed event data, such as an HTTP response status, request duration, queued-job outcome, or mail recipient. +4.x moves event filtering from the point where an event is recorded to just before the event batch is sent. Filters can therefore use final status and duration values. It also replaces the recording and payload environment variables from `config/laritor.php` with methods on an override class. Keep filters side-effect free, since they run while Laritor prepares a batch for delivery. -If your application binds a custom implementation of `BinaryBuilds\LaritorClient\Override\LaritorOverride`, update it to match the new interface. A custom class that extends `DefaultOverride` only needs to update the methods it overrides; a class that implements the interface directly must implement the new `recordLog()` method as well. +Configure 4.x filters in a class extending `BinaryBuilds\LaritorClient\Override\DefaultOverride`. The override receives the request, response, status, duration, user, and other completed-event data needed to make context-aware decisions. -| Filter | 3.x signature | 4.x signature | +| Filter or setting | 3.x signature / environment variable | 4.x override method | | --- | --- | --- | -| Outbound request | `recordOutboundRequest($url)` | `recordOutboundRequest($url, $statusCode, $duration)` | -| Query | `recordQuery($query, $duration)` | `recordQuery($query, $duration, $path)` | -| Queued job | `recordQueuedJob($job)` | `recordQueuedJob(string $connection, string $queue, string $job, string $status, int $duration)` | -| Request | `recordRequest($request)` | `recordRequest($request, $response, $status, $duration, $user)` | -| Command / scheduled task | `recordCommandOrScheduledTask($command)` | `recordCommandOrScheduledTask(string $command, string $status, int $duration)` | -| Mail | `recordMail($message)` | `recordMail($mailable, $to, $subject)` | -| Log | _not available_ | `recordLog($level, $message, array $context = [])` | +| Outbound request filter | `recordOutboundRequest($url)` | `recordOutboundRequest($url, $statusCode, $duration)` | +| Query filter | `recordQuery($query, $duration)` | `recordQuery($query, $duration, $path)` | +| Queued-job filter | `recordQueuedJob($job)` | `recordQueuedJob(string $connection, string $queue, string $job, string $status, $duration)` | +| Request filter | `recordRequest($request)` | `recordRequest($request, $response, $status, $duration, $user)` | +| Command / scheduled-task filter | `recordCommandOrScheduledTask($command)` | `recordCommandOrScheduledTask(string $command, string $status, $duration)` | +| Mail filter | `recordMail($message)` | `recordMail($mailable, $to, $subject)` | +| Log filter | _Not available_ | `recordLog($level, $message, array $context = [])` | +| Log level | `LARITOR_LOG_LEVEL` | `recordLog($level, $message, array $context = [])` | +| Context | `LARITOR_RECORD_CONTEXT` | `recordRequestContext()`, `recordCommandContext()`, `recordScheduledTaskContext()`, `recordQueuedJobContext()`, `recordLogContext()` | +| Database schema | `LARITOR_RECORD_DB_SCHEMA` | `recordDatabaseSchema()` | +| Query bindings | `LARITOR_RECORD_QUERY_BINDINGS` | `recordQueryBindings($query, $duration, $path)` | +| Request query string | `LARITOR_RECORD_QUERY_STRING` | `recordRequestQueryParameters()` | +| Request headers / body | `LARITOR_RECORD_REQUEST_HEADERS` / `LARITOR_RECORD_REQUEST_BODY` | `recordRequestHeaders()` / `recordRequestBody()` | +| Response headers / body | `LARITOR_RECORD_REQUEST_RESPONSE_HEADERS` / `LARITOR_RECORD_REQUEST_RESPONSE_BODY` | `recordResponseHeaders()` / `recordResponseBody()` | +| Session data | `LARITOR_RECORD_SESSION_DATA` | `recordSessionData()` | +| Outbound-request headers / body | `LARITOR_RECORD_OUTBOUND_REQUEST_HEADERS` / `LARITOR_RECORD_OUTBOUND_REQUEST_BODY` | `recordOutboundRequestHeaders()` / `recordOutboundRequestBody()` | +| Outbound response headers / body | `LARITOR_RECORD_OUTBOUND_REQUEST_RESPONSE_HEADERS` / `LARITOR_RECORD_OUTBOUND_REQUEST_RESPONSE_BODY` | `recordOutboundRequestResponseHeaders()` / `recordOutboundRequestResponseBody()` | +| Whitelisted vendors | `LARITOR_WHITELISTED_VENDORS` | `whitelistedVendors(): array` | -### Review filtering behavior +If your application implements `LaritorOverride` directly, implement every new payload/context method in the table as well as `recordLog()`. Extending `DefaultOverride` is the recommended migration path: only update the methods you need. -`LARITOR_LOG_LEVEL` is no longer applied by `LogRecorder`. If you used it to limit logs, move that policy into a custom `recordLog()` filter, for example: +For example, this override retains the 3.x-style “only errors and above” log policy and disables request headers and session data: ```php -public function recordLog($level, $message, array $context = []): bool +namespace App\Laritor; + +use BinaryBuilds\LaritorClient\Override\DefaultOverride; + +class LaritorDataFilter extends DefaultOverride { - return in_array(strtolower($level), ['error', 'critical', 'alert', 'emergency'], true); + public function recordLog($level, $message, array $context = []): bool + { + return in_array(strtolower($level), ['error', 'critical', 'alert', 'emergency'], true); + } + + public function recordRequestHeaders($request, $response, $status, $duration, $user): bool + { + return false; + } + + public function recordSessionData($request, $response, $status, $duration, $user): bool + { + return false; + } } -``` \ No newline at end of file +``` + +Bind the override in an application service provider (typically in `register`): + +```php +use App\Laritor\LaritorDataFilter; +use BinaryBuilds\LaritorClient\Override\LaritorOverride; + +$this->app->bind(LaritorOverride::class, LaritorDataFilter::class); +``` + +Review the defaults before deploying. `DefaultOverride` records request/response headers and session data by default; request and response bodies remain disabled. Existing redaction still applies, but applications with stricter data-collection requirements should explicitly return `false` from the relevant methods. + +For example, a request filter can exclude successful health checks while retaining failures: + +```php +public function recordRequest($request, $response, $status, $duration, $user): bool +{ + return ! $request->is('health') || $status >= 400; +} +``` + +### Use a generated filter preset (optional) + +The filter generator now accepts an optional preset and creates `App\Laritor\LaritorDataFilter`: + +```sh +# Full observability (default) +php artisan make:laritor-filter + +# Capture data associated with failures and slow operations +php artisan make:laritor-filter issues-only + +# Capture only exception-related data +php artisan make:laritor-filter exceptions-only +``` + +Bind the generated class as shown above. If a file with that name already exists, review and merge its customizations rather than overwriting it. + +### Other behavior changes + +- The default for `LARITOR_INGEST_EVENTS_WITHOUT_OCCURRENCE` is now `true`. Set it explicitly to `false` if you need the former default behavior. +- Cache events now include the cache store name and a `duration` field. +- The default filters omit Laritor's own cache keys, Laritor HTTP ingestion requests and routes, `QueueHealthCheck` jobs, and Laritor/internal Artisan commands, in addition to common framework and monitoring noise. From 531a04276b633ddcc795ac296ca85d46fc5a8850 Mon Sep 17 00:00:00 2001 From: Srinath Reddy Dudi Date: Sun, 19 Jul 2026 23:17:52 -0400 Subject: [PATCH 14/14] bump version --- src/Laritor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Laritor.php b/src/Laritor.php index a86bc21..9e27f87 100644 --- a/src/Laritor.php +++ b/src/Laritor.php @@ -25,7 +25,7 @@ class Laritor { - public const VERSION = '3.0.11'; + public const VERSION = '4.0.0'; /** * @var array