Closed sauloonze closed 10 months ago
Hey @sauloonze thank you for your kind words, it means a lot :)
This is awesome because it's actually something that @craigpotter and I have been working on ourselves - we're working on our own official Saloon request logger for Laravel! 😉 Keep it quiet though 🤫
I'll share a little bit of how we were going to do it which might help you. I won't take the credit for it, this is all Craig's idea.
I assume you're using Laravel based on the migration structure. Assuming you are and you have the saloonphp/laravel-plugin
installed, you can actually listen out for two specific events that will get fired whenever a Saloon request is sent. You can then write the request/response into your database table. I recommend listening to the SentSaloonRequest
event.
Firstly, you want to create an event listener.
After that, in your EventServiceProvider.php
you should add the following code:
<?php
use Saloon\Laravel\Events\SentSaloonRequest;
class EventServiceProvider extends ServiceProvider
{
/**
* The event to listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
SentSaloonRequest::class => [
YourListener::class,
],
];
}
After that, in your listener you'll have access to the following properties:
$event->pendingRequest
$event->response
The PendingRequest
will return everything that was sent to the API including headers, body, URL, and method. It will all be combined.
The Response
class contains the response from the API.
Looking at your table - it all looks pretty good! Saloon doesn't actually keep track of duration but I'm going to absolutely write this down because that could be really handy. You could probably work out duration by also hooking onto the SendingSaloonRequest
also provided by Saloon and then work out the difference.
Hope this helps and I'll let Craig chime in if he wants to add anything extra
I think @Sammyjo20 pretty much summed it up, I don't really have anything to add apart from Sam is far too kind giving me all the credit
Thanks for the quick reply and great work, Craigpotter and Sammyjo20!
I'm looking forward to trying out SentSaloonRequest. Right now, I'm working on the debug mode for this feature. Is there a way to add a unique identifier to the request for better logging, maybe through models? Any tips would be really helpful!
$this->debugRequest(
function (PendingRequest $pendingRequest, RequestInterface $psrRequest) {
echo '[API]['.$pendingRequest->getUrl().'] Requesting... ' . PHP_EOL;
// hide secrets from $pendingRequest->body()
ApiLogsModel::create([
'url' => $pendingRequest->getUrl(),
'method' => $pendingRequest->getMethod(),
'request_body' => $pendingRequest->body(),
'request_query' => $pendingRequest->query()->all(),
]);
}
);
$this->debugResponse(function (Response $response) {
$request = $response->getPendingRequest();
ApiLogsModel::where('url', $request->getUrl())->update([
'response_body' => strlen($response->body()),
'response_code' => $response->status(),
'finished_at' => now(),
]);
echo '[API]['.$request->getUrl().'] Response Done! - '.strlen($response->body()).' bytes' . PHP_EOL;
});
Hey @sauloonze I apologise for the late reply.
You could make use of Saloon's config to attach a temporary ID to your requests. This won't be sent to the API.
$this->debugRequest(
function (PendingRequest $pendingRequest, RequestInterface $psrRequest) {
$id = Str::random();
$pendingRequest->config()->add('internal-log-id', $id);
// ...
}
);
Then on the response, you can find that request ID:
$this->debugResponse(function (Response $response) {
$pendingRequest = $response->getPendingRequest();
$id = $pendingRequest->config()->get('internal-log-id');
// ....
});
Thanks @Sammyjo20,
With your help, I completed my task successfully! Here is the piece of code related to the api logs table.
public function __construct()
{
$this->setUpLogging();
$this->authenticateWithCachedToken();
}
protected function setUpLogging(): void
{
$this->debugRequest([$this, 'logRequest']);
$this->debugResponse([$this, 'logResponse']);
}
public function logRequest(PendingRequest $pendingRequest): void
{
Log::info('[API]['.$pendingRequest->getUrl().'] Requesting...');
$apiLog = ApiLogsModel::start($pendingRequest);
$pendingRequest->config()->add('internal-log-id', $apiLog->id);
}
public function logResponse(Response $response): void
{
$pendingRequest = $response->getPendingRequest();
$id = $pendingRequest->config()->get('internal-log-id');
$log = ApiLogsModel::where('id', $id)->first();
$log->finish($response);
Log::info('[API]['.$pendingRequest->getUrl().'] Response Done... '.strlen($response->body()).' bytes');
}
Looks good. If you want a suggestion, I would change:
$log = ApiLogsModel::where('id', $id)->first();
to
$log = ApiLogsModel::where('id', $id)->sole();
If for some reason the ApiLogsModel isn't saved or has been edited, you will get errors on the response as you will be trying to access finish($response) on null
Hello Sam and Team,
Great work on the architecture and the documentation effort. I'm working on adding an 'api_log' table to track the API lifecycle.
The table structure is as follows:
I attempted to implement this using debugRequest and debugResponse, but it seems that's not quite right. Would it be better to use middleware or use the pipeline?
Any guidance you could provide on the best approach would be much appreciated.
Thanks for the amazing work!