Extending Laravel core logging

2019-03-14 09:28发布

I'm back with more Laravel problems as I'm having a problem understanding things.

Again, I'm attempting to create a package to do my own logging. After doing some extra reading and going through the core code and trying other approaches, I've come to the conclusion that all I need to do is extend the core functionality of the logging of laravel, so that it logs to a different path with a custom formatter.

I've created my package. Here is my service provider class:

use Illuminate\Log\LogServiceProvider;

class VmlogServiceProvider extends LogServiceProvider {


    /**
     * Bootstrap the application events.
     *
     * @return void
     */
    public function boot()
    {
        App::bind('log', function()
        {
            return new Vm\Vmlog\Vmlog;
        });     
        parent::boot();

    }

}

?>

Here's the VmLog class

<?php namespace Vm\Vmlog;

use App;
use Illuminate\Support\ServiceProvider;
use Log;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;

class Vmlog extends \Illuminate\Log\Writer {


    protected $path;
    protected $formatter;
    protected $stream;
    protected $rotatingStream;

    public function __construct() {

        $output = APP_HOST."|%datetime%|%level%|%level_name%|".__METHOD__."|%message%|%context%".PHP_EOL;
        $this->path = VM_LOGPATH.APP_VLCODE."/".APP_VLCODE."_".APP_INSTANCE.".log";
        $this->formatter = new LineFormatter($output, $dateFormat);

        parent::__construct();
    }

    /**
     * Register a file log handler.
     *
     * @param  string  $path
     * @param  string  $level
     * @return void
     */
    public function useFiles($path, $level = 'debug')
    {
        $level = $this->parseLevel($level);

        $this->stream = new StreamHandler($this->path, $level);
        $this->stream->setFormatter($this->formatter);

        $this->monolog->pushHandler($this->stream);
    }

    /**
     * Register a daily file log handler.
     *
     * @param  string  $path
     * @param  int     $days
     * @param  string  $level
     * @return void
     */
    public function useDailyFiles($path, $days = 0, $level = 'debug')
    {
        $level = $this->parseLevel($level);
        $this->rotatingStream = new RotatingFileHandler($this->path, $days, $level);
        $this->rotatingStream->setFormatter($this->formatter);

        $this->monolog->pushHandler($this->rotatingStream);
    }

}

?>

I've commented out the LogServiceProvider in app.php and added in my VmlogServiceProvider in it's place.

Yet, when I try run things, I get the following error.

Call to undefined method Illuminate\Support\Facades\Log::useDailyFiles()

I don't understand why this is happening. I've removed the core LogServiceProvider, I've added my own in it's place and binded it correctly, as per the documentation (I think). What am I doing wrong here?

2条回答
相关推荐>>
2楼-- · 2019-03-14 09:58

Why are you using the Boot method in the service provider?

Replacing Laravel's log

You may have meant to use the register method instead of the boot method in that service provider?

It looks like your implementation of this will override the default logger, rather than create an additional log. Is that your intention? In that case, note that you have used the boot method to register an instance of "log", but then the register method is re-doing that work. (perhaps replacing it back with the default? I'm not sure what behavior would result).

An additional log

If you want to add an additional log, you can do that without having to extend Laravel's service provider.

Within a new file and your own namespace, create a LogServiceProvider:

<?php namespace Fideloper\Log;

use Illuminate\Support\ServiceProvider;

class LogServiceProvider extends ServiceProvider {

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

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $logger = new Writer(new \Monolog\Logger('my-custom-log'), $this->app['events']);

        $logFile = 'my-custom-log.txt';

        $logger->useDailyFiles(storage_path().'/logs/'.$logFile);

        $this->app->instance('fideloper.log', $logger);

        $this->app->bind('Fideloper\Log\Writer', function($app)
        {
            return $app->make('fideloper.log');
        });
    }

    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        return array('fideloper.log');
    }

}

Then create a new log writer (similar to how you've done):

<?php namespace Fideloper\Log;

use Illuminate\Log\Writer as BaseWriter;

class Writer extends BaseWriter {}

Note that I didn't add any extra functionality to my extended writer class, but I could.

A con of this setup is that I haven't created or over-written the Log facade to use my new logger. Any call to Log::whatever() will still go to Laravel's default. I create a new Fideloper\Log\Writer object and it works because of Laravel's ability to provide class dependencies automatically.

$log = App::make('fideloper.log');

// Or get auto-created by laravel by making it a dependency
//   in a controller, for example:
<?php

use Fideloper\Log\Writer

class SomeController extends BaseController {

    public function __construct(Writer $log)
    {
        $this->log = $log;

        //Later
        $this->log->error('SOME CUSTOM ERROR');
    }

}
查看更多
Bombasti
3楼-- · 2019-03-14 10:02

I've managed to get it all sorted, so will provide the answer for completeness sake.

We basically mimic the LogServiceProvider class, but instead of calling the Laravel Writer class, we call out own Vmlog class, that simply extends the writer class. That way all functionality of the original logging is kept in place and we simply override the functions we need to. You also need to comment out the registration of the Laravel Log service provider and put your own one in the app.php file.

Here is the ServiceProvider Class.

<?php namespace vm\Vmlog;

use Monolog\Logger;
use Illuminate\Log\LogServiceProvider;
use Illuminate\Support\ServiceProvider;

class VmlogServiceProvider extends LogServiceProvider {

    /**
     * Bootstrap the application events.
     *
     * @return void
     */
    public function boot()
    {
        $this->package('vm/vmlog');
    }

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $logger = new Vmlog(new Logger('log'), $this->app['events']);

        $this->app->instance('log', $logger);

        if (isset($this->app['log.setup']))
        {
            call_user_func($this->app['log.setup'], $logger);
        }
    }
}

?>

Here is the Vmlog class that extends the Writer class.

<?php namespace vm\Vmlog;

use Illuminate\Support\ServiceProvider;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Illuminate\Events\Dispatcher;
use Monolog\Logger as MonologLogger;


class Vmlog extends \Illuminate\Log\Writer {


    protected $path;
    protected $dateFormat;
    protected $output;

    public function __construct(MonologLogger $monolog, Dispatcher $dispatcher = null) 
    {
        // Do stuff
        $this->dateFormat = 'Y-m-d\TH:i:s';
        $this->output = "|%datetime%|%level%|%level_name%|%message%|%context%".PHP_EOL;

        parent::__construct($monolog, $dispatcher);
    }

    /**
     * Register a file log handler.
     *
     * @param  string  $path
     * @param  string  $level
     * @return void
     */
    public function useFiles($path, $level = 'debug')
    {
        $level = $this->parseLevel($level);
        $this->path = VM_LOGPATH.APP_VLCODE."/".APP_VLCODE."_".APP_INSTANCE.".log";
        $formatter = new LineFormatter(APP_HOST.$this->output, $this->dateFormat);
        $stream = new StreamHandler($this->path, $level);
        $stream->setFormatter($formatter);

        $this->monolog->pushHandler($stream);
    }

    /**
     * Register a daily file log handler.
     *
     * @param  string  $path
     * @param  int     $days
     * @param  string  $level
     * @return void
     */
    public function useDailyFiles($path, $days = 0, $level = 'debug')
    {
        $level = $this->parseLevel($level);
        $this->path = VM_LOGPATH.APP_VLCODE."/".APP_VLCODE."_".APP_INSTANCE.".log";
        $formatter = new LineFormatter(APP_HOST.$this->output, $this->dateFormat);
        $stream = new RotatingFileHandler($this->path, $days, $level);
        $stream->setFormatter($formatter);

        $this->monolog->pushHandler($stream);
    }

}

?>

I still need to make it so that the log path is configured in the config files for the package, but this will do for now and for this answer.

查看更多
登录 后发表回答