自定义HandlerWrapper与MonologBu​​ndle

时间:2016-09-02 11:27:25

标签: php symfony monolog

我正在使用Symfony 3.1,我尝试以这种方式配置Monolog,不会记录来自Googlebot的请求。为此我写了UserAgentProcessor已经按预期工作了。在下一步中,我尝试编写BotFilter,如下所示:

<?php

namespace AppBundle\Handler;

use Monolog\Handler\HandlerWrapper;

class FilterBotsHandler extends HandlerWrapper
{

    /**
     * {@inheritdoc}
     */
    public function isHandling(array $record)
    {
        if (stripos($record['extra']['userAgent'], 'bot') !== false){
            return false;
        } else {
            return $this->handler->isHandling($record);
        }
    }
}

这受到HandlerWrapper抽象类中的评论的启发(看看here)。

现在我想将该过滤器添加到我的monolog yml-configuration中。我尝试将其添加到我的服务中,但这是不可能的,因为HandlerWrapper需要一个Handler实例用于其构造函数。我研究了如何在没有服务的情况下使用过滤器,但据我所知,monolog bundle只接受内置类型和通用服务类型。

现在问题是:我如何在配置中使用过滤器?

3 个答案:

答案 0 :(得分:5)

  

我正在使用Symfony 3.1,我尝试以这种方式配置Monolog,不会记录来自GoogleBot的请求......

  1. 防止机器人访问您网站的快速方法是将这两行放入服务器上的/robots.txt文件中。在“web”目录中创建robots.txt文件并粘贴以下内容:

    User-agent: *
    Disallow: /
    
      

    https://support.google.com/webmasters/answer/6062608?hl=en&visit_id=1-636097099675465769-3677253464&rd=1

    当您需要避免访问完全时,这是推荐的选项,这意味着您的网站将不再被搜索引擎和其他机器人编入索引。您无需在应用程序中配置/实现任何内容即可实现它。

    1. 现在,如果你需要机器人进入,但你不想在日志中注册它。不是在某处编写日志文件,而是使用一些处理程序来过滤或修改日志条目,然后再将它们发送到其他处理程序。默认情况下,fingers_crossed环境中使用一个名为prod的强大内置处理程序。它在请求期间存储所有日志消息,但只有在其中一条消息到达action_level时才将它们传递给第二个处理程序:

      # app/config/config.yml
      monolog:
          handlers:
              filter_for_errors:
                  type: fingers_crossed
                  # if *one* log is error or higher, pass *all* to file_log
                  action_level: error
                  handler: file_log
      
              # now passed *all* logs, but only if one log is error or higher
              file_log:
                  type: stream
                  path: "%kernel.logs_dir%/%kernel.environment%.log"
      

      因此,在您的prod.log文件中,只会注册包含某些错误的消息/请求,因此 bots 在此级别中无效。

      有关此http://symfony.com/doc/current/logging.html

    2. 的更多详细信息
      1. 您尝试做的事情是不可取的,因为处理程序将依赖于http请求而不是日志记录,这些记录将脱离上下文,但您可以注册自己的处理程序

        让我们创建自定义处理程序类:

        namespace AppBundle\Monolog\Handler;
        
        use Monolog\Handler\AbstractHandler;
        
        class StopBotLogHandler extends AbstractHandler
        {
            public function isBotRequestDetected()
            {
                // here your code to detect Bot requests, return true or false
                // something like this: 
                // return isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/bot|crawl|slurp|spider/i', $_SERVER['HTTP_USER_AGENT']);
            }
        
            /**
             * Checks whether the given record will be handled by this handler.
             *
             * This is mostly done for performance reasons, to avoid calling processors for nothing.
             *
             * Handlers should still check the record levels within handle(), returning false in isHandling()
             * is no guarantee that handle() will not be called, and isHandling() might not be called
             * for a given record.
             *
             * @param array $record Partial log record containing only a level key (e.g: array('level' => 100) for DEBUG level)
             *
             * @return bool
             */
            public function isHandling(array $record)
            {
                return $this->isBotRequestDetected();
            }
        
            /**
             * Handles a record.
             *
             * All records may be passed to this method, and the handler should discard
             * those that it does not want to handle.
             *
             * The return value of this function controls the bubbling process of the handler stack.
             * Unless the bubbling is interrupted (by returning true), the Logger class will keep on
             * calling further handlers in the stack with a given log record.
             *
             * @param array $record The record to handle
             *
             * @return bool true means that this handler handled the record, and that bubbling is not permitted.
             *              false means the record was either not processed or that this handler allows bubbling.
             */
            public function handle(array $record)
            {
                // do nothing, just returns true whether the request is detected as "bot", this will break the handlers loop.
                //             else returns false and other handler will handle the record.
        
                return $this->isBotRequestDetected();
            }
        }
        

        每当您向记录器添加记录时,它都会遍历处理程序堆栈。每个处理程序决定它是否完全处理记录,如果是,则记录的传播结束。

        重要提示:阅读isHandling()handle()方法的phpdoc,了解详情。

        接下来,让我们将该类注册为“无标签”服务:

        # app/config/services.yml
        services:
            monolog.handler.stop_bot_log:
                class: AppBundle\Monolog\Handler\StopBotLogHandler
                public: false
        

        然后,将其处理程序添加到handlers列表:

        # app/config/config_prod.yml
        monolog:
            handlers:
                # ...
        
                stopbotlog:
                    type: service
                    id: monolog.handler.stop_bot_log
                    priority: 1
        

        请注意,type属性必须等于serviceid必须是定义之前的服务名称,priority必须大于0才能确保它的处理程序将在任何其他处理程序之前执行。

        GoogleBot 向网站应用程序发出请求时,stopbotlog 处理程序会停止所有处理程序,并且不会注册任何日志消息。

        请记住,这不是推荐的方法!根据您的需求,实施选项1或2就足够了。

      2. 如果要忽略对处理程序组的bot请求,可以覆盖monolog.handler.group.class容器参数并覆盖组handler行为:

        namespace AppBundle\Handler;
        
        use Monolog\Handler\GroupHandler;
        
        class NoBotGroupHandler extends GroupHandler
        {
            public function isBotRequestDetected()
            {
                // here your code to detect Bot requests, return true or false
            }
        
            public function handle(array $record)
            {
                if ($this->isBotRequestDetected()) {
                    // ignore bot request for handlers list
                    return false === $this->bubble;
                }
        
                return parent::handle($record);
            }
        }
        

        config_prod.ymlservices.yml

        parameters:
            monolog.handler.group.class: AppBundle\Handler\NoBotGroupHandler
        

        就是这样!现在,您可以停止自定义句柄列表的bot日志:

        # config_prod.yml
        monolog:
            handlers:
                grouped:
                    type: group
                    members: [main, console, chromephp]
        

        最后,如果您在分析日志文件时遇到困难,我建议您使用这个神奇的工具:https://github.com/EasyCorp/easy-log-handler

答案 1 :(得分:1)

这是一个非常肮脏的伎俩,但如果你真的需要它,你可能会这样做。

假设您要使用类型stream包装处理程序:

FilterBotsHandler

中添加构造函数
public function __constructor($path, $level, $bubble, $permissions) {
    $this->handler = new Monolog\Handler\StreamHandler($path, $level, $bubble, $permissions);
}

然后重新定义参数monolog.handler.stream.class

parameters:
    monolog.handler.stream.class:   AppBundle\Handler\FilterBotsHandler

确保在MonologBu​​ndle定义之后定义此参数。

就是这样。应该工作。

答案 2 :(得分:0)

您可以在CompilerPass中添加AppBundlemonolog添加LoggerCompilerPass服务。这样的配置程序也可以是configurator,它可以在请求和机器人检测时动态替换所有处理程序,并将空处理程序数组推送到EventDispatcher,这可以在配置程序调用时保留。

换句话说,配置程序由Listener添加到DI并添加到Kernel onRequestUser-AgentMonolog\Logger检查NullHandler标题的事件寻找机器人,然后清除android:layout_above="@+id/MyButton"(在配置器中传递)所有处理程序(如果空处理程序数组失败,则放置<RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button... android:id="@+id/MyButton" android:layout_alignParentBottom="true" /> <ScrollView android:layout_above="@+id/MyButton" android:layout_width="match_parent" android:layout_height="match_parent" >...</ScrollView> </RelativeLayout> 。)

DI配置程序是在运行时更改服务的唯一方法,可以作为服务定义级别应用。如果不需要,可以附加或分离此类定义,并且它不会真正改变您的应用程序中的任何内容。