Laravel logging with Monolog\Handler\BrowserConsol

2019-06-24 00:20发布

How can Laravel 5's logging be changed to Monolog\Handler\BrowserConsoleHandler?

What doesn't work in Laravel 5 but does work in a standalone PHP file:

use Illuminate\Support\Facades\Log;    
use Monolog\Handler\BrowserConsoleHandler;
use Monolog\Logger;

// create a log channel
$log = Log::getMonolog();
// $log = new Logger('Testlogger'); //doesn't make any difference
$log->pushHandler(new BrowserConsoleHandler(\Psr\Log\LogLevel::INFO));

// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');

All that happens is that my logs appear in the logfile but don't find their way to the browser. If I try the code in a single PHP file without framework it works, so I assume it's a Laravel problem.

I get it working with Firebug and FirePHP installed and $log->pushHandler(new FirePHPHandler()); instead of BrowserConsoleHandler but this is not a solution since it sends the logs with headers but I already sent some debug-echos when the logger wants to send the headers.
BrowserConsoleHandler on the other hand adds a JavaScript snippet to the end of the site that perfectly fits my needs.

So, did anyone succeed in adding BrowserConsoleHandler to Laravel's logging? How?

1条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-06-24 00:58

After reading plenty of source code and getting xdebug to work I finally figuered it out:

BrowserConsoleHandler sends the script snipped after finishing the php script by register_shutdown_function(). At this time, Laravel already sent the full response to the browser. So the script snipped from BrowseConsoleHandler gets generated but never sent to the browser.

As a workaround you can build your own Middleware (http://laravel.com/docs/5.0/middleware) that invokes the code generation manually and adds it to the response before it gets sent.

Create app/Http/Middleware/LogBrowserConsole.php:

<?php
namespace App\Http\Middleware;

use Illuminate\Contracts\Routing\Middleware;
use Illuminate\Support\Facades\Log;
use Monolog\Handler\BrowserConsoleHandler;

class LogBrowserConsole implements Middleware {

  public function handle($request, \Closure $next)
  {
    // add BrowserConsoleHandler to Laravel's Logger
    $log = Log::getMonolog();
    $log->pushHandler(new BrowserConsoleHandler(\Psr\Log\LogLevel::INFO));

    // invokes all your stuff like it would do without the middleware but with the new logger
    $response = $next($request);

    // after the request is done we care about the log entries
    $handlers = $log->getHandlers();

    $scriptSnippet = "";
    foreach($handlers as $handler){ // only handle BrowserConsoleHandler
        if($handler instanceof BrowserConsoleHandler){
            ob_start(); //start output buffer so we can save echo to variable
            $handler->send(); // create the scriptSnipped
            $scriptSnippet .= ob_get_clean();
        }
    }

    // write scriptSnippet to end of response content
    $content = $response->getContent();
    $response->setContent($content.$scriptSnippet);

    return $response;
  }
}

Register the Middleware in app/Http/Kernel.php:

protected $routeMiddleware = [
    'log.browserconsole' => 'App\Http\Middleware\LogBrowserConsole'
];

and invoke your Controller with the Middleware in app/Http/routes.php:

Route::get('test', ['middleware' => 'log.browserconsole', 'uses'=>'TestController@test']);

Alternatively, if you want to use the Middleware for every request you can add it to

protected $middleware = [
'App\Http\Middleware\LogBrowserConsole'
];

in app/Http/Kernel.php.

Your Route would look like Route::get('test', 'TestController@test');


Now, your Log::debug() etc. messages get sent to the logfile (the default LogHandler is still available, you just added another) and the script snipped from BrowserConsoleHandler gets built and sent to the browser with all your log items.

Keep in mind to eventually change the log level \Psr\LogLevel::INFO in app/Http/Middleware/LogBrowserConsole to fit your needs.

查看更多
登录 后发表回答