Laravel Error Logging via Better Stack (Logtail)
Logs (formally Logtail) is a sub-service of Better Stack that serves as a centralized logging platform for your applications. This guide will show you how to integrate your Laravel error logs in Better Stack and change the default logger to include the stack trace.
Better Stack
-
Create your account on betterstack.com - Its free to get started.
-
Under
Logs & Metrics
clickconnect source
-
Give it a
name
and selectphp
, then create source:
- Copy your
Source token
:
Laravel
- Install logtail/monolog-logtail package:
composer require logtail/monolog-logtail
- Edit
.env
:
LOG_CHANNEL=logtail
LOG_LEVEL=warning
LOGTAIL_SOURCE_TOKEN=7hAj17pv1SmRMvDqJpG8SDKy
- Edit
config/logging.php
:
use Logtail\Monolog\LogtailHandler;
...
...
...
'logtail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => LogtailHandler::class,
'with' => [
'sourceToken' => env('LOGTAIL_SOURCE_TOKEN'),
],
],
...
...
...
At this point, your Laravel error logs will be sent to Better Stack. However, the default Laravel handler does not include the stack trace. This can be easily fixed by inheriting from the default Laravel handler and adding in the stack trace.
Adding the Stack Trace
edit app/Exceptions/Handler.php
:
Laravel 8
public function report(Throwable $e)
{
$e = $this->mapException($e);
if ($this->shouldntReport($e)) {
return;
}
if (Reflector::isCallable($reportCallable = [$e, 'report'])) {
if ($this->container->call($reportCallable) !== false) {
return;
}
}
foreach ($this->reportCallbacks as $reportCallback) {
if ($reportCallback->handles($e)) {
if ($reportCallback($e) === false) {
return;
}
}
}
try {
$logger = $this->container->make(LoggerInterface::class);
} catch (Exception $ex) {
throw $e;
}
$context = $this->exceptionContext($e);
$trace = $e->getTrace();
$trace = array_slice($trace, 0, 20); // Limit the trace to 20 lines
$context['trace'] = $trace;
$logger->error($e->getMessage(), array_merge(
$context,
$this->context(),
['exception' => $e]
)
);
}
Laravel 9
public function report(Throwable $e)
{
$e = $this->mapException($e);
if ($this->shouldntReport($e)) {
return;
}
if (Reflector::isCallable($reportCallable = [$e, 'report'])
&& $this->container->call($reportCallable) !== false) {
return;
}
foreach ($this->reportCallbacks as $reportCallback) {
if ($reportCallback->handles($e) && $reportCallback($e) === false) {
return;
}
}
try {
$logger = $this->container->make(LoggerInterface::class);
} catch (\Exception $ex) {
throw $e;
}
$level = Arr::first(
$this->levels, fn($level, $type) => $e instanceof $type, LogLevel::ERROR
);
$context = $this->buildExceptionContext($e);
$trace = $e->getTrace();
$trace = array_slice($trace, 0, 20); // Limit the trace to 20 lines
$context['trace'] = $trace;
method_exists($logger, $level)
? $logger->{$level}($e->getMessage(), $context)
: $logger->log($level, $e->getMessage(), $context);
}
Laravel 10
protected function reportThrowable(Throwable $e): void
{
$this->reportedExceptionMap[$e] = true;
if (Reflector::isCallable($reportCallable = [$e, 'report']) &&
$this->container->call($reportCallable) !== false) {
return;
}
foreach ($this->reportCallbacks as $reportCallback) {
if ($reportCallback->handles($e) && $reportCallback($e) === false) {
return;
}
}
try {
$logger = $this->newLogger();
} catch (Exception) {
throw $e;
}
$level = Arr::first(
$this->levels, fn ($level, $type) => $e instanceof $type, LogLevel::ERROR
);
$context = $this->buildExceptionContext($e);
$trace = $e->getTrace();
$trace = array_slice($trace, 0, 20); // Limit the trace to 20 lines
$context['trace'] = $trace;
method_exists($logger, $level)
? $logger->{$level}($e->getMessage(), $context)
: $logger->log($level, $e->getMessage(), $context);
}
That's it!
Conclusion
Your Laravel error logs will now be centralized in Better Stack with the stack trace included, offering you and your team a better insight into your application's errors.