使用 MonologBundle 自定义 HandlerWrapper
Custom HandlerWrapper with MonologBundle
我正在使用 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
摘要 class 中评论的启发(看看 here)。
现在我想将该过滤器添加到我的独白 yml 配置中。我尝试将它添加到我的服务中,但这是不可能的,因为 HandlerWrapper
需要一个 Handler 实例作为其构造函数。我研究了如何在没有服务的情况下使用过滤器,但据我所知,独白包只接受内置类型和通用服务类型。
现在的问题是:如何在我的配置中使用过滤器?
虽然很坑爹,但如果你真的需要,你可以这样做。
假设你想包装一个类型为 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
确保此参数将被定义在它被 MonologBundle 定义之后。
就是这样。应该可以。
I am using Symfony 3.1 and I try to configure Monolog in such a way, that requests from the GoogleBot are not logged...
防止漫游器访问您站点的快速方法是将这两行放入服务器上的 /robots.txt
文件中。在 'web' 目录下创建一个 robots.txt
文件并粘贴以下内容:
User-agent: *
Disallow: /
当您需要避免完全访问时,这是推荐的选项,这意味着您的网站将不再被搜索引擎和其他机器人编入索引。您不需要 configure/implement 应用程序中的任何东西来实现它。
现在,如果您需要机器人进入,但又不想在日志中注册它。 some 处理程序不是在某处写入日志文件,而是用于在将日志条目发送到 other 处理程序之前过滤或修改日志条目。默认情况下,在 prod
环境中使用一个名为 fingers_crossed
的强大的内置处理程序。它在请求期间存储所有日志消息,但仅当其中一条消息到达 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
文件中只会注册包含一些错误的 messages/requests,因此 bots 在此级别无效.
有关此内容的更多详细信息http://symfony.com/doc/current/logging.html
你尝试做的事情是不可取的,因为 handler 将依赖于 http 请求而不是日志记录,这将脱离上下文,但是你可以很容易地在 Symfony 中注册自己的 handler:
让我们创建自定义处理程序 class:
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 了解更多详情。
接下来,让我们将 class 注册为服务 "without tags":
# 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
属性必须等于service
,id
必须是之前定义的服务名,priority
必须大于0
以确保其 处理程序 将在任何其他处理程序之前执行。
当 GoogleBot 向网站应用程序执行请求时,stopbotlog
处理程序 停止所有 处理程序 在他之后,不要注册任何日志消息。
请记住,这不是推荐的方法!根据您的需要,实施选项 1 或 2 应该就足够了。
如果您想忽略处理程序组的机器人请求,您可以覆盖 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.yml
或 services.yml
:
parameters:
monolog.handler.group.class: AppBundle\Handler\NoBotGroupHandler
就是这样!现在,您可以停止自定义句柄列表的机器人日志:
# config_prod.yml
monolog:
handlers:
grouped:
type: group
members: [main, console, chromephp]
最后,如果您难以分析日志文件,我建议您使用这个神奇的工具:https://github.com/EasyCorp/easy-log-handler
您可以在 AppBundle
中写入 CompilerPass
,其中添加了 configurator to monolog
service. Such configurator can be also a request event listener,它可以根据请求和机器人检测动态替换所有处理程序,并将空处理程序数组推送到 Logger
可以暂停配置器调用。
换句话说,配置器由 CompilerPass
添加到 DI 并添加到 EventDispatcher
作为 Listener
到 Kernel
事件 onRequest
检查 User-Agent
header 寻找机器人,然后清除 Monolog\Logger
(在配置器中传递)所有处理程序(或者如果空处理程序数组失败则放置 NullHandler
)。
DI 配置器是在运行时更改服务的唯一方法,可以作为服务定义级别应用。如果不需要,可以附加或分离此类定义,它不会真正改变您的应用程序中的任何内容。
我正在使用 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
摘要 class 中评论的启发(看看 here)。
现在我想将该过滤器添加到我的独白 yml 配置中。我尝试将它添加到我的服务中,但这是不可能的,因为 HandlerWrapper
需要一个 Handler 实例作为其构造函数。我研究了如何在没有服务的情况下使用过滤器,但据我所知,独白包只接受内置类型和通用服务类型。
现在的问题是:如何在我的配置中使用过滤器?
虽然很坑爹,但如果你真的需要,你可以这样做。
假设你想包装一个类型为 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
确保此参数将被定义在它被 MonologBundle 定义之后。
就是这样。应该可以。
I am using Symfony 3.1 and I try to configure Monolog in such a way, that requests from the GoogleBot are not logged...
防止漫游器访问您站点的快速方法是将这两行放入服务器上的
/robots.txt
文件中。在 'web' 目录下创建一个robots.txt
文件并粘贴以下内容:User-agent: * Disallow: /
当您需要避免完全访问时,这是推荐的选项,这意味着您的网站将不再被搜索引擎和其他机器人编入索引。您不需要 configure/implement 应用程序中的任何东西来实现它。
现在,如果您需要机器人进入,但又不想在日志中注册它。 some 处理程序不是在某处写入日志文件,而是用于在将日志条目发送到 other 处理程序之前过滤或修改日志条目。默认情况下,在
时才将它们传递给第二个处理程序prod
环境中使用一个名为fingers_crossed
的强大的内置处理程序。它在请求期间存储所有日志消息,但仅当其中一条消息到达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
文件中只会注册包含一些错误的 messages/requests,因此 bots 在此级别无效.有关此内容的更多详细信息http://symfony.com/doc/current/logging.html
你尝试做的事情是不可取的,因为 handler 将依赖于 http 请求而不是日志记录,这将脱离上下文,但是你可以很容易地在 Symfony 中注册自己的 handler:
让我们创建自定义处理程序 class:
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 了解更多详情。接下来,让我们将 class 注册为服务 "without tags":
# 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
属性必须等于service
,id
必须是之前定义的服务名,priority
必须大于0
以确保其 处理程序 将在任何其他处理程序之前执行。当 GoogleBot 向网站应用程序执行请求时,
stopbotlog
处理程序 停止所有 处理程序 在他之后,不要注册任何日志消息。请记住,这不是推荐的方法!根据您的需要,实施选项 1 或 2 应该就足够了。
如果您想忽略处理程序组的机器人请求,您可以覆盖 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.yml
或 services.yml
:
parameters:
monolog.handler.group.class: AppBundle\Handler\NoBotGroupHandler
就是这样!现在,您可以停止自定义句柄列表的机器人日志:
# config_prod.yml
monolog:
handlers:
grouped:
type: group
members: [main, console, chromephp]
最后,如果您难以分析日志文件,我建议您使用这个神奇的工具:https://github.com/EasyCorp/easy-log-handler
您可以在 AppBundle
中写入 CompilerPass
,其中添加了 configurator to monolog
service. Such configurator can be also a request event listener,它可以根据请求和机器人检测动态替换所有处理程序,并将空处理程序数组推送到 Logger
可以暂停配置器调用。
换句话说,配置器由 CompilerPass
添加到 DI 并添加到 EventDispatcher
作为 Listener
到 Kernel
事件 onRequest
检查 User-Agent
header 寻找机器人,然后清除 Monolog\Logger
(在配置器中传递)所有处理程序(或者如果空处理程序数组失败则放置 NullHandler
)。
DI 配置器是在运行时更改服务的唯一方法,可以作为服务定义级别应用。如果不需要,可以附加或分离此类定义,它不会真正改变您的应用程序中的任何内容。