Creating a Laravel Logger - Part 3: Integrating our Pusher logger package
For this part of the series, you will need PHP 7.13+, Laravel 5.7+ and Composer installed on your machine.
In this part, we’ll see how we can connect our app and the Laravel package so that when a log form is submitted using the dispatch button, it is triggered to Pusher Channels and if the log level is of type error, it is also published through Pusher Beams too.
In the first two parts of this series, we built a simple Laravel app with a form to collect a log message and level and we also built a Laravel package to interact with the Pusher API.
Requirements
To follow along with this series you need the following things:
- Completed previous parts of the series. Part 1, Part 2
- Laravel installed on your local machine. Installation guide.
- Knowledge of PHP and the Laravel framework.
- Composer installed on your local machine. Installation guide.
- Android Studio >= 3.x installed on your machine (If you are building for Android).
- Knowledge of Kotlin and the Android Studio IDE.
- Xcode >= 10.x installed on your machine (If you are building for iOS).
- Knowledge of the Swift programming language and the Xcode IDE.
- A Pusher application. Create one here.
- A Pusher Beams application. Create one here.
Connecting our Laravel app with the logger package
To get started, you need the code from the first part of the article, which is available in the GitHub repository. When you have the code, cd
to the directory of the code and copy the .env.example
to your.env
file if you don’t already have it and update the following keys:
# File: ./.env
# [...]
PUSHER_APP_ID="PUSHER_APP_ID"
PUSHER_APP_KEY="PUSHER_APP_KEY"
PUSHER_APP_SECRET="PUSHER_APP_SECRET"
PUSHER_APP_CLUSTER="PUSHER_APP_CLUSTER"
PUSHER_BEAMS_SECRET_KEY="PUSHER_BEAMS_SECRET_KEY"
PUSHER_BEAMS_INSTANCE_ID="PUSHER_BEAMS_INSTANCE_ID"
Update with the keys from your Pusher Channels and Pusher Beams dashboard.
Next, open the config/broadcasting.php
file and scroll until you see the connections
key. In there, you’ll have the pusher
object. Add the beams
object to it and make sure it looks like this:
// File: ./config/broadcasting.php
'connections' => [
// [...]
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'encrypted' => true,
],
'beams' => [
'secret_key' => env('PUSHER_BEAMS_SECRET_KEY'),
'instance_id' => env('PUSHER_BEAMS_INSTANCE_ID'),
],
],
],
You see that this file instructs Laravel to get the keys from the .env
file. Inside our Laravel app, we need to add a new repository definition. Open composer.json
in the root directory of the Laravel app and add this there:
// File: ./composer.json
// [...]
"repositories": [
{
"type": "path",
"url": "./packages/*",
"options": {
"symlink": true
}
}
],
// [...]
By setting the "type": "path"
, the Composer knows that you would like to reference a local repository and the url
defines the package location which can be relative or absolute to the package code.
Composer will copy the package code into the vendor
folder of our app and anytime there is an update change on the package there is a need to run composer update
. To prevent Composer from doing so you need to specify the symlink
option and composer will create a symlink to the package folder.
Run this in the root directory of the app to install the package:
$ composer require packagename/pusher-logger
If you used a different package name than this in the previous part, then use that package name.
When your package installation is complete you’ll see the “Package manifest generated successfully” message.
Now that our package is installed, let us create the logic to use the package. Open a new terminal tab, inside the root directory of the project folder and run:
$ php artisan make:controller LogController
This command will create a LogController
class inside the LogController.php
file found at the app/Http/Controllers
directory.
Open the LogController.php
and add the method below:
<?php // File: ./app/Http/Controllers/LogController.php
// [...]
use Illuminate\Http\Request;
class LogController extends Controller {
public function __invoke(Request $request)
{
// Logic Here
}
// [...]
}
The __invoke
method is a magic method in PHP
which gets called when the object is called as a function. $request
is an instance of the Illuminate\Http\Request
class via dependency injection and it returns the current HTTP request.
Now, we will introduce the Laravel package into our LogController
class, so at the top of the class after the namespace declaration add this:
use PackageNamespace\PusherLogger\PusherLogger;
If you used a different namespace, replace the namespace above with whatever you used.
Remember that when we’re building our package, we could log a message and the level with the syntax below:
PusherLogger::log('Winter is Coming', PusherLogger::LEVEL_WARNING)
->setEvent('log-event')
->setChannel('log-channel')
->setInterests(['log-interest'])
->send()
So let’s update our logic in the __invoke
method like so:
// File: ./app/Http/Controllers/LogController.php
// [...]
public function __invoke(Request $request)
{
$data = $request->validate([
'message' => 'required|string',
'level' => 'required|string'
]);
$dispatched = PusherLogger::log($data['message'], $data['level'])
->setChannel('log-channel')
->setEvent('log-event')
->setInterests(['log-interest'])
->send();
return response()->json(['status' => $dispatched]);
}
// [...]
From this snippet above, we first validate the request to make sure the message and level values are being sent to the server. Next, we set the log message, level, channel and event name. In a case where the log is an error, we set the interests to be sent to the Pusher Beam API using the setInterests
method which accepts an array of interests.
Finally, we triggered the send
method that dispatches the log to both the Pusher Channels and Pusher Beams.
Now open api.php
located in the routes
folder in the app directory and declare this route like so:
Route::post('/push', 'LogController')->name('log-message');
This route can be accessed as api/push
via a POST
request. A name log-message has been set on this route which allows us to use the Laravel URL helper function route()
to generate the endpoint anywhere inside the Laravel app. We will use this route shortly.
Next, open the welcome.blade.php
file located at resources/views
directory and update it as instructed below:
Just before the closing head
tag add this link:
<!-- File: ./resources/views/welcome.blade.php -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" />
Just before the end of the body
tag add these scripts:
<!-- File: ./resources/views/welcome.blade.php -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
Here, we added Toastr and Axios to the frontend of the application. Toastr is a simple notification library and Axios is HTTP client for the browser that makes network requests.
Next, add the following code below the code we just added:
<!-- File: ./resources/views/welcome.blade.php -->
<script>
function dispatchLog() {
let level = document.getElementById('level').value;
let message = document.getElementById('message').value;
let errToastr = () => toastr.error("Oops! An error encountered");
axios.post('{{ route('log-message') }}', { message, level })
.then(r => r.data.status ? toastr.success('Log dispatch successful') : errToastr())
.catch(errToastr);
}
</script>
The function above is to help dispatch our log inputs (message and level).
Axios is used to send both variables to our earlier declared route. When we get a response, we display a notification to the user.
Now let us attach the function in the script above to the dispatch button in our modal.
Look for this line of code in your ./resources/views/partials/modal.blade.php
file:
<button type="button" class="btn btn-danger">Dispatch</button>
Then replace it with the following:
<button type="button" class="btn btn-danger" onclick="dispatchLog()">Dispatch</button>
So, whenever the dispatch button is clicked, the dispatchLog
function is triggered.
If you run your app, it should work like this (the view on the left is the Pusher Channels debug console):
Pushing log messages without the trigger
When creating actual applications, you won’t have a trigger and you’ll want the logged messages to automatically get pushed to the Pusher Channel. This is why we created the Monolog handler in the previous part.
To use this handler, open the config/logging.php
file and add a new channel to the channels
array:
'channels' => [
// [...]
'pusher' => [
'driver' => 'monolog',
'level' => 'debug',
'handler' => PackageNamespace\PusherLogger\PusherLoggerHandler::class,
],
// [...]
],
If you changed your packages namespace, don’t forget to change it above.
Finally, in your stack
channel in the same channels
array, add the pusher
channel to the list of channels as seen below:
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'pusher'],
'ignore_exceptions' => false,
],
// [...]
],
To test, you can go to the routes/web.php
and add this to the file:
info('Testing');
This should push the message to the Pusher Channel.
Conclusion
In this part of the series, we have been able to set up the logic we need to be able to push logs to Pusher. In the next part of the series, we will create the Android application for our logging monitor.
The source code is available on GitHub.
25 March 2019
by Neo Ighodaro