Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# LogDispatcher
Dispatch logs of any level to different handlers: database or third party services
# LogRouter

Route and filter Laravel Monolog entries to different handlers through a simple config.
50 changes: 50 additions & 0 deletions src/Config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

return [

/*
|---------------------------------------------------------------------------
| Log level indicates minimum log level Monolog should log.
|
| - 100: debug
| - 200: info
| - 250: notice
| - 300: warning
| - 400: error
| - 500: critical
| - 550: alert
| - 600: emergency
|---------------------------------------------------------------------------
*/

/*
|---------------------------------------------------------------------------
| List of Routes
|---------------------------------------------------------------------------
|
| Auto Make: App::makeWith():
| Each route needs to have a name as the key that will be used for the
| logger channel name. The route config must specific a handler, at least
| one level and a params. If no params is required an empty array is
| required.
|
| Callback Make:
| For complex handlers a closure can be set for the handler.
|
| RouteBuilder:
| If handler returns a instance of RouteBuilderInterface then the
|
*/

'routes' => [
'test' => [
'aSync' => true,
'handler' => Monolog\Handler\StreamHandler::class,
'levels' => [ 'warning' ],
'params' => [
'stream' => storage_path('log/router/test.log'),
],
],
],

];
143 changes: 143 additions & 0 deletions src/Listeners/LogRouter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

namespace WebChefs\LogRouter\Listeners;

// Framework
use Illuminate\Support\Arr;
use Illuminate\Console\Application;
use Illuminate\Log\Events\MessageLogged;

// Vendor
use Monolog\Logger as Monolog;
use Monolog\Handler\FilterHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\HandlerInterface;

// Aliases
use Config;

class LogRouter
{

const LEVELS = [
'debug' => Monolog::DEBUG,
'info' => Monolog::INFO,
'notice' => Monolog::NOTICE,
'warning' => Monolog::WARNING,
'error' => Monolog::ERROR,
'critical' => Monolog::CRITICAL,
'alert' => Monolog::ALERT,
'emergency' => Monolog::EMERGENCY,
];

const ASYNC = false;

protected $monolog;
protected $routes;

/**
* Setup that builds the logger and sets the handlers based on a config. We
* dont setup in a constructor as monolog is note serializable.
*
* @return void
*/
public function ensureSetup()
{
// Check if we already setup
if (!is_null($this->monolog)) {
return;
}

$this->makeLogger();
$this->setHandlers();
}

/**
* Handle the event.
*
* @param OrderShipped $event
* @return void
*/
public function handle(MessageLogged $event)
{
$this->ensureSetup();
$this->routeMessage($event);
}

protected function routeMessage(MessageLogged $event)
{
$this->monolog->{$event->level}($event->message . ' from =' . get_class($this), $event->context);
}

/**
* Build the monolog object
*
* @return void
*/
protected function makeLogger()
{
$channel = 'log_router.' . app()->environment();
$this->monolog = new Monolog($channel);
}

/**
* Set/push the handlers to the monolog object. A handler is only set to
* active if it has at least one handler.
*
* @return void
*/
protected function setHandlers()
{
$routes = collect(Config::get('log_router.routes'))
->transform(function($item) { return collect($item); })
->filter(function($item) {
return $item->get('aSync', false) === static::ASYNC;
});

$this->routes = $routes->map(function ($route, $name) {
$class = $route->get('handler');
$filterLevels = $route->get('levels', array_keys(static::LEVELS));
$params = $route->get('params', []);

$handler = $this->makeHandler($class, $filterLevels, $params);
$this->monolog->pushHandler($handler);
return $handler;
});

// $handler = new StreamHandler(storage_path('log/router/test.log'));
// $handler = $this->handlerDefaults($handler);
// $this->monolog->pushHandler($handler);
}

protected function makeHandler($handler, $levels, $params)
{
$handler = app()->makeWith($handler, $params);
$handler = $this->handlerDefaults($handler);

$levels = $this->makeLevels($levels);
return new FilterHandler($handler, $levels);
}

protected function makeLevels($levels)
{
return collect($levels)->transform(function($level) {
return Arr::get(self::LEVELS, $level);
})->all();
}

/**
* By default set all handlers to the lowest log level as the dispatcher
* will handle the level allocations.
*
* @param HandlerInterface $handler
*
* @return HandlerInterface
*/
protected function handlerDefaults(HandlerInterface $handler)
{
$handler->setLevel(Monolog::DEBUG);
$handler->setBubble(true);
return $handler;
}

}
13 changes: 13 additions & 0 deletions src/Listeners/LogRouterAsync.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace WebChefs\LogRouter\Listeners;

// Framework
use Illuminate\Contracts\Queue\ShouldQueue;

class LogRouterAsync extends LogRouter
implements ShouldQueue
{

const ASYNC = true;
}
159 changes: 159 additions & 0 deletions src/LogRouterServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

namespace WebChefs\LogRouter;

// Package
use WebChefs\LogRouter\Commands\TestLog;

// Framework
use Illuminate\Log\Events\MessageLogged;

// use Illuminate\Console\Application as Artisan;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
// use Illuminate\Database\Console\Migrations\MigrateCommand;
// use Illuminate\Database\Console\Migrations\RefreshCommand;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

// // Aliases
// use Log;
// use Event;
// use Config;

class LogRouterServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
// protected $defer = false;

/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Log\Events\MessageLogged' => [
'WebChefs\LogRouter\Listeners\LogRouter',
'WebChefs\LogRouter\Listeners\LogRouterAsync',
],
];

/**
* List of Commands defined by this package.
*
* @var array
*/
protected $commands = [
TestLog::class,
];

/**
* Register the service provider. Register is called before Boot.
*
* @return void
*/
public function register()
{
// Register our commands with Artisan
$this->commands($this->commands);

// Log Dispatcher singleton
// $this->app->singleton('Core\LogDispatcher\LogDispatcherContract', 'Core\LogDispatcher\LogDispatcher');

// Make available our config data.
$this->mergeConfigFrom(__DIR__ . '/Config.php', 'log_router');
}

/**
* All services provides have been registered. Publish the plug-in
* configuration and pass data to views.
*/
public function boot()
{
parent::boot();

// $events->listen('artisan.firing: migrate:*', function ($artisan, $input, $output) use ($events) {
// LogDispatcher::disable();
// $events->forget('illuminate.log');
// });

// $events->listen('artisan.firing: seed:*', function ($artisan, $input, $output) use ($events) {
// LogDispatcher::disable();
// $events->forget('illuminate.log');
// });

$this->publishes([
__DIR__ . '/Config.php' => config_path('log_router.php')
]);
}

/**
* Get the services provided by the provider.
*
* @return array
*/
// public function provides()
// {
// return ['Core\LogDispatcher\LogDispatcherContract', 'LogDispatcherContract'];
// }

/*
|--------------------------------------------------------------------------
| Events List
|--------------------------------------------------------------------------
|
| For reference purposes we list all Laravel Events based on some of the
| following references.
|
| Ref:
| - http://stackoverflow.com/questions/13059744/where-can-i-get-a-complete-list-of-laravel-events-fired-by-the-core-libaries
| - https://laracasts.com/discuss/channels/general-discussion/where-can-i-get-a-complete-list-of-laravel-5-events?page=1
|
|--------------------------------------------------------------------------
|
| laravel.log
| laravel.query
| laravel.resolving
| laravel.composing: {viewname}
| laravel.started: {bundlename}
| laravel.controller.factory
| laravel.config.loader
| laravel.language.loader
| laravel.view.loader
| laravel.view.engine
| laravel.done
|
| view.filter
|
| eloquent.saving
| eloquent.updated
| eloquent.created
| eloquent.saved
| eloquent.deleting
| eloquent.deleted
|
| $this->events->fire('auth.attempt', $payload);
| $this->events->fire('auth.login', [$user, $remember]);
| $this->events->fire('auth.logout', [$user]);
| $this->events->fire('cache.'.$event, $payload);
| $this->laravel['events']->fire('cache:clearing', [$storeName]);
| $this->laravel['events']->fire('cache:cleared', [$storeName]);
| $events->fire('artisan.start', [$this]);
| $this->events->fire('illuminate.query', array($query, $bindings, $time, $this->getName()));
| $this->events->fire('connection.'.$this->getName().'.'.$event, $this);
| $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
| $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
| $this['events']->fire('locale.changed', array($locale));
| $this['events']->fire($class = get_class($provider), array($provider)); //after provider registered.
| $this->app['events']->fire('kernel.handled', [$request, $response]);
| $this->dispatcher->fire('illuminate.log', compact('level', 'message', 'context'));
| $this->events->fire('mailer.sending', array($message));
| $this->events->fire('illuminate.queue.failed', array($connection, $job, $data));
| $this->events->fire('illuminate.queue.stopping');
| $this->events->fire('router.matched', [$route, $request]);
| $this->events->fire('composing: '.$view->getName(), array($view));
| $this->events->fire('creating: '.$view->getName(), array($view));
*/
}