Search code examples
phploggingzend-framework2verbosity

ZF2 Zend Logger - Filters by priority


I'm using the Zend Logger with following configuration in the module_config.php:

'log' => [
    \Base\Log\ConsoleLoggerAwareInterface::class => [
        'writers' => [
            'standard-output' => [
                'name' => 'stream',
                'options' => [
                    'stream' => 'php://output',
                ],
            ],
        ],
    ],
]

But as it is I can't influence the log to filter or suppress messages. The goal is to improve the logging to be smarter.

I think I need to add a filter to the writer but I can't find an example how to do that in the module_config.php.

Is there also a way to call a script (e.g. from within a cron) by using a verbosity level?

I hope you know what I'm trying to achive.

Edit:

I have this example in my code:

$this->consoleLogger->emerg('EMERG');
$this->consoleLogger->alert('ALERT');
$this->consoleLogger->crit('CRIT');
$this->consoleLogger->err('ERR');
$this->consoleLogger->warn('WARN');
$this->consoleLogger->notice('NOTICE');
$this->consoleLogger->info('INFO');
$this->consoleLogger->debug('DEBUG');

Should it then not output the filtered ones?


Solution

  • Q: How to filter a Log Writer to a specific error level?

    Add a filters key to the particular writer's configuration. Here's direct instantiation to remove any configuration peculiarities: this outputs only "WARN" and "EMERG" messages:

    $config = [
        'writers' => [
            'standard-output' => [
                'name' => 'stream',
                'options' => [
                    'stream' => 'php://output',
                    'filters' => \Zend\Log\Logger::WARN,
                ],
            ],
        ],
    ];
    
    $logger = new \Zend\Log\Logger($config);
    $logger->emerg('EMERG');
    $logger->warn('WARN');
    $logger->debug('DEBUG');
    

    Adding the filters configuration to your modules_config.php should have a similar effect. If not, check your zend-log version (with eg composer show) and advise.


    Q: How to change error level filter with the -v command line parameter?

    AFAIK, there is no automatic way to bind the standard verbose flag (-v) with a particular logging level. So you'll have to write your own filter. One thing that's neat to know is that the filters key can take:

    • an int (as done above, which translates to the built-in log level);
    • a string (corresponding to a class name implementing \Zend\Log\Filter\FilterInterface);
    • an object (instance of \Zend\Log\Filter\FilterInterface);
    • or an array of these.

    You can use this flexibility to solve your need of binding a command line parameter to a log value. Here is a custom class that shows emergency events by default, but for every v on the command line increases the shown priority:

    class CliLoggingFilter implements \Zend\Log\Filter\FilterInterface
    {
        public function __construct()
        {
            $this->level = \Zend\Log\Logger::EMERG;
            if (array_key_exists('v', $args = getopt('v'))) {
                $this->level += count($args['v']);
            }
        }
    
        public function filter(array $event)
        {
            return ($event['priority'] <= $this->level);
        }
    }
    

    You'd then have a configuration like: 'filters' => CliLoggingFilter::class.

    $ php public/index.php
    2016-07-25T10:57:28-04:00 EMERG (0): EMERG
    $ php public/index.php -vvvv
    2016-07-25T10:57:32-04:00 EMERG (0): EMERG
    2016-07-25T10:57:32-04:00 WARN (4): WARN
    $ php public/index.php -vvvvvvv
    2016-07-25T10:57:34-04:00 EMERG (0): EMERG
    2016-07-25T10:57:34-04:00 WARN (4): WARN
    2016-07-25T10:57:34-04:00 DEBUG (7): DEBUG
    

    Q: How to change all routes to use -v?

    AFAIK, there is no way to specify a global command line parameter. You need to either (a) update all your console routes to accept the argument or (b) pass the log level a different way.

    Updating all your routes isn't terribly hard. You can define a variable that holds the value and then include that in the configuration, like so:

    $globalConsoleRouteParams = '[--verbose|-v]';
    return [ 'console' => 'router' => 'routes' => [
        'foo' => [ 'options' => [ 'route' => "foo $globalConsoleRouteParams ..." ] ],
        'bar' => [ 'options' => [ 'route' => "bar $globalConsoleRouteParams ..." ] ],
        // ...
    ]];
    

    Alternatively, you could use say environment variables to pass your desired log level, plus perhaps any additional configuration you might desire. Modifying our earlier example:

    class CliLoggingFilter implements \Zend\Log\Filter\FilterInterface
    {
        public function __construct()
        {
            $this->level = \Zend\Log\Logger::EMERG;
            if (false !== ($level = getenv('LOG_LEVEL'))) {
                $this->level = $level;
            }
        }
        // ...
    }
    

    Then it may be invoked like

    $ LOG_LEVEL=7 php index.php foo