How To Get Real Time App Crashes From Your Laravel App

How To Get Real Time App Crashes From Your Laravel App

Given Ncube

You may have written all the tests, followed the SOLID principles, or whatever is hot these days but when your app is in production and getting real users things get a little wild, bugs may go unnoticed, runtime errors you may never have expected. In this post, I'm going to show you how to set up a real time error reporting system in Laravel that will notify you as soon an error occurs in your application. There are already other robust tools for this like Flare, Sentry but these services are paid and may not be the best solution for that side project you may never launch.

So the idea is simple, listen for the Illuminate\Log\Events\MessageLogged event, send the details of the error to the developers for debugging, thanks to Laravel's error handling it just got easier.

Here, we're going to send reports via Telegram as soon as an error occurs but you can use email, Slack, or something else. To do that were are going to use Laravel Notification Channels to send the notification.

The first step is to create an event listener to listen for Illuminate\Log\Events\MessageLoged. Open your EventServiceProvider in the $listen array append the following code.

use Illuminate\Log\Events\MessageLogged;
use App\Listeners\ReportIncident;

/**
 * The event listener mappings for the application.
 *
 * @var array
 */
protected $listen = [
	Registered::class => [
		SendEmailVerificationNotification::class,
	],
	MessageLogged::class => [	
		ReportIncident::class,
	]	
];

Then run this command to generate the event

php artisan event:generate

Then generate the notification to report errors

php artisan make:notification AppCrashed

Now in your ReportIncident.php in the handle method add the following

use App\Notifications\AppCrashed;

User::where('email', 'youradminusermail')->first()->notify(new AppCrashed($event));

So we're notifying the admin user of the incident

Now for the AppCrashed add this in the constructor to accept the MessageLogged event

use Illuminate\Log\Events\MessageLogged;


private $logged;

/**
 * Create a new notification instance.
 *
 * @return void
 */
public function __construct(MessageLogged $logged)
{
	$this->logged = $logged;
}

Now modify the via method to send the notification via Telegram

use NotificationChannels\Telegram\TelegramMessage;

public function via($notifiable)
{
	return [TelegramChannel::class, 'database'];
}

As you can see we're importing that TelegramMessage class but we don't have it yet. Talk to @BotFather on Telegram and create a new bot then follow the setup instructions here. If you now have your bot API key, let's Install the Telegram Notification Channel

composer require laravel-notification-channels/telegram

Make sure you've all your environment variables setup as described in the documentation.

Next, add this method


/**
 * Get the telegram representation of the notification.
 *
 * @param  mixed  $notifiable
 * @return \NotificationChannels\Telegram\TelegramMessage;
 */
public function toTelegram($notifiable)
{
	return TelegramMessage::create()
		->to('telegram-chat-or-user-id')
		->content("Hi admin, \nsomething terrible has happened.\n\n*App name:* " . config('app.name') . "\n\n*Page:* " . url()->current() . "\n\n*Exception Message:* " . $this->logged->message . "\n\n*Severity:* " . $this->logged->level)
		->button('View Incident', 'www.yourdomain.com/admin/incidents');
}

To get the Telegram chat or user id send a message to your bot or create a channel then send a message in the channel. After that head over to api.telegram.org/bot<you-bot-api-key>/getUpdates and look at the chat or user id of the message.

We're almost done, as you may have noticed earlier in the via method we also added the database channel let's set that up so that we can view more information about the error later on.

/**
 * Get the array representation of the notification.
 *
 * @param  mixed  $notifiable
 * @return array
 */
public function toArray($notifiable)
{
	$logged = collect($this->logged->context)->map(function ($error) {
		if ($error instanceof \JsonSerializable) {
			return $error;
		}

		return (string) $error;
	});

	return [
		'logged' => $logged->toJson(JSON_PRETTY_PRINT),
		'message' => $this->logged->message,
		'level' => $this->logged->level,
		'page' => url()->current(),
		'app' => config('app.name'),
	];	
}

This method is simply going to store the incident in the database for future analysis. the logged key stores array of JSON or string representation of the context values in this case it may be the stack trace. Go ahead and create a route in your application that displays all the info above.

Now if an error happens in your app, you'll get this in Telegram

Laravel App Crashlytics via Telegram

You can get creative and improve this, I hope this can help you ship with fewer bugs.

[convertkit=2908863]