6 - Log structured success entry on PollFediverseAction with url count and duration

This commit is contained in:
myrmidex 2026-04-28 18:47:04 +02:00
parent a59c086da2
commit 9cecc47b8b
2 changed files with 54 additions and 18 deletions

View file

@ -5,6 +5,7 @@
namespace Lvl0\FediDiscover\Actions;
use Carbon\CarbonImmutable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Lvl0\FediDiscover\Clients\FediverseClientFactory;
use Lvl0\FediDiscover\Events\UrlDiscovered;
@ -18,22 +19,26 @@ public function __construct(private FediverseClientFactory $factory) {}
public function execute(Instance $instance): void
{
$start = microtime(true);
$client = $this->factory->for($instance);
$posts = $client->fetchPostsSince($instance, $instance->last_seen_id);
$posts->each(function (FediversePost $post) use ($instance) {
try {
$this->processLinks($post, $instance);
} catch (Throwable $e) {
Log::warning('fedi-discover:processLinks failed', [
'instance_id' => $instance->id,
'instance_url' => $instance->url,
'post_url' => $post->selfUrl,
'exception' => $e::class,
'message' => $e->getMessage(),
]);
}
});
$urlCount = $posts
->map(function (FediversePost $post) use ($instance) {
try {
return $this->processLinks($post, $instance);
} catch (Throwable $e) {
Log::warning('fedi-discover:processLinks failed', [
'instance_id' => $instance->id,
'instance_url' => $instance->url,
'post_url' => $post->selfUrl,
'exception' => $e::class,
'message' => $e->getMessage(),
]);
}
})
->sum();
if ($posts->isNotEmpty()) {
$instance->last_seen_id = $posts->first()->cursorId;
@ -41,21 +46,27 @@ public function execute(Instance $instance): void
$instance->last_polled_at = now();
$instance->save();
Log::info('fedi-discover:poll succeeded', [
'instance_id' => $instance->id,
'url_count' => $urlCount,
'duration_ms' => (int) round((microtime(true) - $start) * 1000),
]);
}
private function processLinks(FediversePost $post, Instance $instance): void
private function processLinks(FediversePost $post, Instance $instance): int
{
if ($post->body === null) {
return;
return 0;
}
$linksFound = preg_match_all('~https?://[^\s<>"\'()\[\]]+~', $post->body, $matches);
if ($linksFound === 0) {
return;
return 0;
}
collect($matches[0])
return collect($matches[0])
->map(fn (string $u) => rtrim($u, '.,;:!?'))
->filter(fn (string $u) => filter_var($u, FILTER_VALIDATE_URL) !== false)
->filter(fn (string $u) => parse_url($u, PHP_URL_HOST) !== parse_url($instance->url, PHP_URL_HOST))
@ -66,6 +77,7 @@ private function processLinks(FediversePost $post, Instance $instance): void
discoveredAt: CarbonImmutable::now(),
postUrl: $post->selfUrl,
postBody: $post->body,
));
))
->count();
}
}

View file

@ -7,6 +7,7 @@
use Carbon\CarbonImmutable;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Lvl0\FediDiscover\Actions\PollFediverseAction;
use Lvl0\FediDiscover\Clients\FediverseClientFactory;
use Lvl0\FediDiscover\Clients\FediverseClientInterface;
@ -198,6 +199,29 @@ public function test_it_leaves_last_seen_id_unchanged_when_no_posts_are_returned
$this->assertSame('500', $instance->fresh()->last_seen_id);
}
public function test_poll_logs_a_structured_success_entry_with_url_count_and_duration(): void
{
Log::spy();
Event::fake([UrlDiscovered::class]);
$instance = $this->makeInstance();
$this->pollInstance($instance, [
new FediversePost('1', 'https://mastodon.social/@alice/1', 'See https://example.com/one and https://other.example/two'),
new FediversePost('2', 'https://mastodon.social/@bob/2', 'Also https://example.com/three'),
]);
Log::shouldHaveReceived('info')
->once()
->withArgs(function (string $message, array $context) use ($instance): bool {
return $message === 'fedi-discover:poll succeeded'
&& $context['instance_id'] === $instance->id
&& $context['url_count'] === 3
&& isset($context['duration_ms'])
&& $context['duration_ms'] >= 0;
});
}
/**
* @param array<FediversePost> $posts
*/