强制将空闲日志登录到队列

时间:2020-02-11 07:15:17

标签: laravel monolog

我有一个难题,因为我需要一个好的记录器来记录应用程序中发生的事情,如果调用Log::error的话,它还应该通知Devs和Sys通过松弛管理员。当前它正在正常工作,但是它增加了请求响应时间的开销。

以下是我的设置:

//config/logging.php
    'default' => env('LOG_CHANNEL', 'stack'),
    //truncated
    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['daily', 'slack'],
        ],

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => 'debug',
            'days' => 0,
        ],

        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'App',
            'emoji' => ':boom:',
            'level' => 'error',
        ]
    ]
    //truncated

//UserController
public function show(User $user)
{
    //just a sample code, the important part is where the Log facade is called
    try {
        //business logic
    } catch (Exception $e) {
        Log::error(get_class(), [
            'user_id' => $user->id,
            'message' => $e->getMessage()
        ]);
    }  

    return view('user.show', compact($user));
}

它已经在工作,但是可以肯定的是,即使上面代码的添加时间可以忽略不计,我们仍然可以改进它以减少开销,但是实际代码更加复杂并且有很多迭代

如何更改“松弛”记录器的行为,以便在触发它时将其推入队列?我宁愿只编写一次代码就忘了它,而不是记住我必须将其推送到按需记录器(例如

Log::chanel(['daily', 'slack'])->...

OR

//this is good for more on particular event notification but not not error notification which can happen anywhere
Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new AlertDevInSlackNotification)`

注意:

  • 我尝试将一些代码添加到bootstrap/app.php中,但是它不起作用
//bootstrap/app.php
$app->configureMonologUsing(function($monolog) use ($app) {
    //some code here. does not work, just getting page not working
});
  • 就像当我调用此日志级别和此通道时,我希望将其排队

2 个答案:

答案 0 :(得分:0)

您可以这样做。

1。创建作业ex:名称为LogSlackQueue.php

public class LogSlackQueue implements ShouldQueue {
     ...
     ...
     public function handle() {
          Log::channel(['daily', 'slack'])->info($your_input);
     }
}

2。然后用作

LogSlackQueue::dispatch($your_input)

如果您不想执行上述操作,则需要弄清楚以创建自定义提供程序

答案 1 :(得分:0)

感谢@ZeroOne提出了解决问题的方法。我希望它是自动的,并且任何具有Log::error()的现有代码都会自动提示开发者。

下面是我的解决方法。

//CustomSlackServiceProvider.php
try {
    //listen to all events
    Event::listen('*', function($event, $details) {
        //check if the event message logged event which is triggered when we call Log::<level>
        if($event == "Illuminate\Log\Events\MessageLogged") {
            //$details contain all the information we need and it comes in array of object
            foreach($details as $detail) {
                //check if the log level is from error to emergency
                if(in_array($detail->level, ['emergency', 'critical', 'alert', 'error'])) {
                    //trigger the notification
                    Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new AlertDevInSlackNotification($detail->message, $detail->level, $detail->context));
                }
            }
        }
    });
} catch (Exception $e) {

}

//AlertDevInSlackNotification.php
class AlertDevInSlackNotification extends Notification implements ShouldQueue
{
    use Queueable;

    private $class;
    private $level;
    private $context;

    public function __construct($class, $level, $context)
    {
        $this->class = $class;
        $this->level = strtoupper($level);
        $this->context = $context;

        //prevent congestion in primary queue - make sure this queue exists
        $this->queue = 'alert';
    }

    public function via($notifiable)
    {
        return ['slack'];
    }

    public function toSlack($notifiable)
    {
        return (new SlackMessage)
            ->content($this->level.': '.$this->class)
            ->attachment(function($attachment) {
                $attachment->fields($this->context);
            });
    }

更新:

上面的代码将在您触发Log::error()时起作用。

但是要侦听诸如语法错误之类的错误所引发的事件,该错误将导致“不允许'闭包'的序列化”。您可以改为这样做来提高覆盖率:

    public function boot()
    {
        try {
            //listen to all events
            Event::listen('*', function($event, $details) {
                //check if the event message logged event which is triggered when we call Log::<level>
                if($event == "Illuminate\Log\Events\MessageLogged") {
                    // dump($event);
                    //$details contain all the information we need and it comes in array of object
                    foreach($details as $detail) {
                        $this->path = '';
                        $this->level = '';
                        $this->context = [];
                        $this->message = '';

                        //check if the log level is from error to emergency
                        if(in_array($detail->level, ['emergency', 'critical', 'alert', 'error'])) {
                            //@todo - exclude: Error while reading line from the server. [tcp://cache:6379] = restart

                            //check if the context has exception and is an instance of exception
                            //This is to prevent: "Serialization of 'Closure' is not allowed" which prevents jobs from being pushed to the queue
                            if(isset($detail->context['exception'])) {
                                if($detail->context['exception'] instanceof Exception) {

                                    $this->level = $detail->level;
                                    //to keep consistency on all the log message, putting the filename as the header
                                    $this->message = $detail->context['exception']->getFile();
                                    $this->context['user'] = auth()->check() ? auth()->user()->id.' - '. auth()->user()->first_name.' '.auth()->user()->last_name : null;
                                    $this->context['message'] = $detail->context['exception']->getMessage();
                                    $this->context['line'] = $detail->context['exception']->getLine();
                                    $this->context['path'] = request()->path();

                                    $this->runNotification();
                                    continue;
                                }
                            }

                            $this->level = $detail->level;
                            $this->context = $detail->context;
                            $this->message = $detail->message;

                            $this->runNotification();
                            continue;
                        }
                    }
                }
            });
        } catch (Exception $e) {

        }
    }

    public function runNotification()
    {
        Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new AlertDevInSlackNotification($this->message, $this->level, $this->context));
    }