-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add filter capability #411
Conversation
@@ -46,7 +47,18 @@ public function __construct($level = Logger::DEBUG, $bubble = true) | |||
*/ | |||
public function isHandling(array $record) | |||
{ | |||
return $record['level'] >= $this->level; | |||
if ($this->filters) { | |||
$handled = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really want to make the Log level (inside $this->level) useless with these filters? This is nothing I would expect if I set a Log level while creating a log handler. The filters should only be additional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimmi20 You're free to add or not a filter, and of course you are free to create your own filtering rule.
But the default behavior is kept is you don't add a filter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@llaville If I create a handler with a specific log level I would not expect that a filter may change it. You should add a comment about this into the usage.md.
@mimmi20 With the latest commit (bbc87f0) it allows to combine the standard level filter with others rules defined by user. For example, to combine the standard level filter we must define it like $filter3 = function($record, $handlerLevel) {
return ($record['level'] >= $handlerLevel);
};
$logger = new Logger('my_logger');
// add the filter rule globally
$logger->pushFilter($filter3); Hope it will help to clarify and fix the situation |
I've created a new repository on my account to show a real case (situation) where it could useful to have such filter feature. |
Instead of adding a concept of filters in Monolog, I would implement it with a special filtering handler decorating another handler (as done for the FilterHandler which is based on the level only) |
@stof I don't agree with you. First FilterHandler provide only the basic way of level filtering, and did not allow to use any handler. My concept enlarge the restricted level only criteria, and allow to filter based on each record elements. If you found a new solution rather than using a special filtering handler, I would like to see it ! Filter concept should be used whatever handlers you want to add on stack, and not a special one. |
It can be used with any handler, given it wraps it (and it does not require changing all handlers). And I'm not saying you should use the FilterHandler itself (which indeed only supports filtering based on the level), but a similar approach: use Monolog\Handler\AbstractHandler;
use Monolog\Handler\HandlerInterface;
class AdvancedFilterHandler extends AbstractHandler
{
private $filters;
private $handler;
private $bubble;
/**
* @param callable|HandlerInterface $handler Handler or factory callable($record, $this).
* @param callable[] $filters A list of filters to apply
* @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct($handler, array $filters, $bubble = true)
{
$this->filters = $filters;
$this->handler = $handler;
$this->bubble = $bubble;
}
/**
* {@inheritdoc}
*/
public function isHandling(array $record)
{
foreach ($this->filters as $filter) {
if (!call_user_func($filter, $record)) {
return false;
}
}
return true;
}
// The following methods are copied from FilterHandler
/**
* {@inheritdoc}
*/
public function handle(array $record)
{
if (!$this->isHandling($record)) {
return false;
}
// The same logic as in FingersCrossedHandler
if (!$this->handler instanceof HandlerInterface) {
if (!is_callable($this->handler)) {
throw new \RuntimeException(
"The given handler (" . json_encode($this->handler)
. ") is not a callable nor a Monolog\\Handler\\HandlerInterface object"
);
}
$this->handler = call_user_func($this->handler, $record, $this);
if (!$this->handler instanceof HandlerInterface) {
throw new \RuntimeException("The factory callable should return a HandlerInterface");
}
}
if ($this->processors) {
foreach ($this->processors as $processor) {
$record = call_user_func($processor, $record);
}
}
$this->handler->handle($record);
return false === $this->bubble;
}
/**
* {@inheritdoc}
*/
public function handleBatch(array $records)
{
$filtered = array();
foreach ($records as $record) {
if ($this->isHandling($record)) {
$filtered[] = $record;
}
}
$this->handler->handleBatch($filtered);
}
} And then using it: // Create some filters
$filter1 = function($record) {
return (preg_match('/^add/', $record['message']) === 1);
};
$filter2 = function($record) {
return ($record['level'] == Logger::NOTICE);
};
// Create some handlers
$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG);
$filterStream = new AdvancedFilterHandler(
$stream,
[$filter1]
);
$errlog = new AdvancedFilterHandler(
new ErrorLogHandler(),
[$filter2]
);
// Create the main logger of the app
$logger = new Logger('my_logger');
$logger->pushHandler($filterStream);
$logger->pushHandler($errlog);
// Stream handler accept this message, and was denied by ErrorLog handler
$logger->addDebug('add a debug message');
// Stream handler deny this message, and was accepted by ErrorLog handler
$logger->addNotice('a notice message'); |
I agree with @stof there is no real benefit to filtering at the handler level when we do everything else with nesting/composition of handlers. GroupHandler + FilterHandler can filter a whole subset of handlers for example. Besides I find filtering to be much more useful when analyzing logs than when logging, because what you filter out can't be found again later, and it's always hard to know which info you'll need until a problem occurs and you're trying to figure out what happened. But I'd be happy to discuss a more advanced version of the FilterHandler if that makes sense. |
Hello, I'm back from hollidays two days ago, and I readed your comments. It's fine for me to use an AdvancedFilterHandler (thanks to @stof to provide the source code). @Seldaek Why not include a such advanced filter version in standard Monolog distribution ! |
@llaville yes maybe it would fit in monolog itself. I'll open a new issue for that. |
Let me introduce myself.
I'm author of the PEAR Net_Growl package that allow to send notification to Growl from PHP on Mac/OSX and Windows.
More documentation about the package can be found at http://growl.laurent-laville.org/
As I wanted to propose/add a Growl handler to Monolog, but was a bit disappointed by lack of filtering logs capability in Monolog, I've choosen to propose you now this new feature as a PR.
I've of course modifed the source code, but also added unit tests and modified the documentation.
To read it more easily, you can have a look directly on my forked version at https://github.com/llaville/monolog/blob/FilterFeature/doc/usage.md#filtering-log-records
That will explains how to filter logs globally and locally, and allow a fine grain to accept/deny logs messages.
Laurent