Log messages except some categories

Hi! im logging all erors and warnings, and sending to email. Problem is if someone, for example, accessing not existing page (404) , i get email. And yesterday google spider was trying to access robots.txt and i didn’t have that. So i got 35 emails ;/ and one problem to resolve with message logging. As you understand problem is not in robots.txt file but in general. someone wants to get some page , it doesnt exist and it goes to email. not cool

and question is How can i ignore some cateories rom log router defined categories?


        'log'=>array(

            'class'=>'CLogRouter',

            'routes'=>array(

                array( 

                    'class'=>'CFileLogRoute',

                    'levels'=>'error, warning', //logging all errors and warnings

                ),

                array(

                    'class'=>'CEmailLogRoute',

                    'levels'=>'error', // send to emails only errors

                    'email'=>'admin@example.com',

                    'categories'=>'!exception.CHttpException.404' // and here i want to put ignore statement to not send 404 errors

                ),

            ),

        ),

can i do that somehow?

or there i should do some workaround.

maybe i can remove " exception.CHttpException.* " from error category. this would resolve my problem as well.

You could try to configure a custom log filter (which should extend CLogFilter). See source of CLogFilter::filter(&$logs). It should work, if you remove the unwanted entries from $logs there. Each $logs item is an array with these values:


array(string $message, integer $level, string $category, integer $time);

I had a similar problem with favicon.ico.

Some firefox were trying to access to favicon in strange position, and there were lot of email.

I solved with a rewriteUrl that was sending to the proper favicon.ico whatever favicon.ico was requested.

Pay attention that "access to unexisting page" can even be an attack: someone can try to discover an admin module or other protected pages.

In my opinion is better to save all the log. Create your robot.txt (and that is a good stuff in general) and continue with the log as it is.

@zaccaria:

You could still save all logs with CFileLogRoute. It’s rather about what messages to filter out for CEmailLogRoute.

Yes, I know.

Was just a metodological question. As none read the log file, is better to send email for relevant notice.

I guess that this is why exist CEmailLogRouter, because people are reading emails much more often of how much are reading the log file.

So the questio is: what are log that worth to be sent via email? Of corse software failure are worth, because programmer can patch the problem before a customer call (I think that this is called ‘proactivity’ from 2.0 philosophs).

Should ‘page not found’ be sent via mail? In my opinion yes.

A page not find can be or a bug in the application (broken link) and so should be logged, or an address that a user manually entered, maybe with malicious ideas, and in this case should be also logged.

There are bot (or paranoid browser) that look for special files. In my opinion is better to place this few file and continue reciving all signalation.

Of corse is only an opinion, not a best practice.

Too many emails cause that you ignore them after some time. I’d rather not get an email for each 404 as this is a too common error on any site. BTW you would make it very easy for an attacker to bomb your inbox ;).

Thanks guys!

and Mike i took your advice and created new LogFilter from CLogFilter. If you are interested ill give what i got

My class


<?php

class LogFilter extends CLogFilter {

    public $ignoreCategories; // =array('category','category.*','some.category.tree.*');

    

    public function filter(&$logs)

    {                   

          // unset categories marked as "ignored"

          if($logs) foreach($logs as $logKey => $log) {

            $logCategory = $log[2]; //log category

            foreach($this->ignoreCategories as $nocat) {

                //exact match

                if($logCategory === $nocat) {

                    unset($logs[$logKey]);

                }

                //asterix match

                else if(strpos($nocat,'.*') !== false) {

                    $nocat = str_replace('.*','',$nocat).'.'; //remove asterix item from array and add dot at the and 

                    if(strpos($logCategory.'.',$nocat) !== false) {

                        unset($logs[$logKey]);

                    }

                    

                }

            }

            

          }


          $this->format($logs);

          

          return $logs;

    }

}

and configuration line in main.php


 'log'=>array(

            'class'=>'CLogRouter',

            'routes'=>array(

                array( 

                    'class'=>'CFileLogRoute',

                    'levels'=>'error, warning', //logging all errors and warnings

                ),

                array(

                    'class'=>'CEmailLogRoute',

                    'levels'=>'error', // send to emails only errors

                    'email'=>'admin@example.com',


		    'filter'=>array(

			'class'=>'LogFilter',

			'ignoreCategories'=>array(

			       'exception.CHttpException.*'

				/*,'some.category.*'*/

			)

		    ),


                ),

            ),

        ),

;)

ahh, its now senging blank emails. cant disable logging even if $logs is empty

Hmm. I’d say you could create a ticket for this. The way filters are implemented now, they are meant to format/decorate the log entries before sending. I think it makes sense to also enable filtering out a message with a filter. The fix would be a small change in CLogRoute::collectLogs().

Instead of:


    public function collectLogs($logger, $processLogs=false)

    {

        $logs=$logger->getLogs($this->levels,$this->categories);

        $this->logs=empty($this->logs) ? $logs : array_merge($this->logs,$logs);

        if($processLogs && !empty($this->logs))

        {

            if($this->filter!==null)

                Yii::createComponent($this->filter)->filter($this->logs);

            $this->processLogs($this->logs);

        }

    }




it needs to be:


    public function collectLogs($logger, $processLogs=false)

    {

        $logs=$logger->getLogs($this->levels,$this->categories);

        $this->logs=empty($this->logs) ? $logs : array_merge($this->logs,$logs);

        if($processLogs && !empty($this->logs))

        {

            if($this->filter!==null)

                Yii::createComponent($this->filter)->filter($this->logs);


            if (!empty($this->logs))

                $this->processLogs($this->logs);

        }

    }




As a workaround you could extend CEmailLogRoute and override that method there.

Yes, now its working, thanks again :) i sent ticket, too

Where is that ticket located, I need it too.

Sorry, cant find this ticket, but i think it was fixed (i didnt implement in my code, so not tested, yet) (bow)

It was not fixed, I am using the latest version and still empty logs.

it looks like it has been deleted permanetly, because on http://code.google.com/p/yii/issues/entry

i couldn’t find any reference to my ticket. Maybe add your ticket and post it here. I will vote for it, too.

Here’s a complete example of filtering out / ignoring 404 errors based on narkomanC’s filter. This will not require changes to the core files and will not send blank email alerts.

config/main.php


		'log' => array(

			'class' => 'CLogRouter',

			'routes' => array(

				array(

					'class' => 'CDbLogRoute',

					'logTableName' => 'yii_log',

					'connectionID' => 'db',

					'levels' => 'error, warning',

				),

				array(

					'class' => 'AdvancedEmailLogRoute',

					'filter' => array(

						'class'=>'AdvancedLogFilter',

						'ignoreCategories' => array(

							// Ignore 404s

							'exception.CHttpException.404',

						),

					),

					'levels' => 'error',

					'emails' => 'myemail@mydomain.com',

				),

			),

		),

protected/components/AdvancedLogFilter.php


<?php

class AdvancedLogFilter extends CLogFilter {

	public $ignoreCategories; // =array('category','category.*','some.category.tree.*');


	public function filter(&$logs) {

		// unset categories marked as "ignored"

		if($logs) foreach($logs as $logKey => $log) {

			$logCategory = $log[2]; //log category

			foreach($this->ignoreCategories as $nocat) {

				// Exact match

				if($logCategory === $nocat) {

					unset($logs[$logKey]);

					continue;

				}

				// Wildcard match

				else if(strpos($nocat,'.*') !== false) {

					$nocat = str_replace('.*','',$nocat).'.'; //remove asterix item from array and add dot at the and

					if(strpos($logCategory.'.',$nocat) !== false) {

						unset($logs[$logKey]);

					}

				}

			}

		}


		$this->format($logs);

		return $logs;

	}

}

protected/components/AdvancedEmailLogRoute.php


<?php

class AdvancedEmailLogRoute extends CEmailLogRoute {

	protected function processLogs($logs) {

		if (empty($logs)) {

			return;

		}

		parent::processLogs($logs);

	}

}

thanx

exactly what I was looking for! Works like a charm!

This is great.

However I wanted to get the other CLogFilter options to work as well. So I added a couple lines of code…

here’s my config.




<?php

            'routes'=>array(

                array(

                    'class'  => 'AdvancedEmailLogRoute',

                    'levels' => 'error, warning',

                    'emails' => 'support@mycompany.com',

                    'filter' => array(

                        'class'         => 'AdvancedLogFilter',

                        'prefixSession' => true,

                        'prefixUser'    => true,

                        'logUser'       => true,

                        // 'logVars' => array(),

                        'ignoreCategories' => array(

                            // Ignore 404s

                            'exception.CHttpException.404',

                        ),

                    ),

                ),




and I added the getContext back in.




<?php

class AdvancedLogFilter extends CLogFilter {

    public $ignoreCategories; // =array('category','category.*','some.category.tree.*');


    public function filter(&$logs) {

        // unset categories marked as "ignored"

        if($logs) foreach($logs as $logKey => $log) {

            $logCategory = $log[2]; //log category

            foreach($this->ignoreCategories as $nocat) {

                // Exact match

                if($logCategory === $nocat) {

                    unset($logs[$logKey]);

                    continue;

                }

                // Wildcard match

                else if(strpos($nocat,'.*') !== false) {

                    $nocat = str_replace('.*','',$nocat).'.'; //remove asterix item from array and add dot at the and

                    if(strpos($logCategory.'.',$nocat) !== false) {

                        unset($logs[$logKey]);

                    }

                }

            }

        }


        if (!empty($logs))

        {

            if(($message=$this->getContext())!=='')

                array_unshift($logs,array($message,CLogger::LEVEL_INFO,'application',YII_BEGIN_TIME));

            $this->format($logs);

        }

        $this->format($logs);

        return $logs;

    }

}



Enjoy

excellent, thanks a lot !

I’ve added several logs on overloaded method processLogs() to be more accurate and it’s just perfect !

There is a property for exceptions:




			'class'=>'CLogRouter',

			'routes'=>array(

				array(

					'class'=>'CFileLogRoute',

					'levels'=>'error, warning',

					'except'=>'exception.CHttpException.404',

				),

				array(

					'class'=>'CFileLogRoute',

					'logFile'=>'404.log',

					'categories'=>'exception.CHttpException.404',

				),