diff --git a/.gitignore b/.gitignore
index 8e63c675..82da0099 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,7 @@ composer.lock
# PhpStorm project files
.idea
+
+# Vim swapfiles.
+.*.swp
+.*.swo
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index cb8b3d93..0f13e74d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -21,6 +21,7 @@
+ Added new filter for matching a logger name.
Made Logger::isInitialized() public.
Improved LoggerAppenderMail to set the Content-type header as defined in layout.
diff --git a/src/examples/php/filter_namematch.php b/src/examples/php/filter_namematch.php
new file mode 100644
index 00000000..8815748d
--- /dev/null
+++ b/src/examples/php/filter_namematch.php
@@ -0,0 +1,26 @@
+info("Messages from foo are denied due to the second filter");
+$loggerBar->info("Messages from bar are accepted");
diff --git a/src/examples/resources/filter_namematch.xml b/src/examples/resources/filter_namematch.xml
new file mode 100644
index 00000000..b219019a
--- /dev/null
+++ b/src/examples/resources/filter_namematch.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/php/LoggerAutoloader.php b/src/main/php/LoggerAutoloader.php
index 86098c51..c47459a4 100644
--- a/src/main/php/LoggerAutoloader.php
+++ b/src/main/php/LoggerAutoloader.php
@@ -6,15 +6,15 @@
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
+ *
* @package log4php
*/
@@ -26,15 +26,15 @@
/**
* Class autoloader.
- *
+ *
* @package log4php
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
class LoggerAutoloader {
-
+
/** Maps classnames to files containing the class. */
private static $classes = array(
-
+
// Base
'LoggerAppender' => '/LoggerAppender.php',
'LoggerAppenderPool' => '/LoggerAppenderPool.php',
@@ -52,7 +52,7 @@ class LoggerAutoloader {
'LoggerReflectionUtils' => '/LoggerReflectionUtils.php',
'LoggerRoot' => '/LoggerRoot.php',
'LoggerThrowableInformation' => '/LoggerThrowableInformation.php',
-
+
// Appenders
'LoggerAppenderConsole' => '/appenders/LoggerAppenderConsole.php',
'LoggerAppenderDailyFile' => '/appenders/LoggerAppenderDailyFile.php',
@@ -68,7 +68,7 @@ class LoggerAutoloader {
'LoggerAppenderRollingFile' => '/appenders/LoggerAppenderRollingFile.php',
'LoggerAppenderSocket' => '/appenders/LoggerAppenderSocket.php',
'LoggerAppenderSyslog' => '/appenders/LoggerAppenderSyslog.php',
-
+
// Configurators
'LoggerConfigurationAdapter' => '/configurators/LoggerConfigurationAdapter.php',
'LoggerConfigurationAdapterINI' => '/configurators/LoggerConfigurationAdapterINI.php',
@@ -80,6 +80,7 @@ class LoggerAutoloader {
'LoggerFilterDenyAll' => '/filters/LoggerFilterDenyAll.php',
'LoggerFilterLevelMatch' => '/filters/LoggerFilterLevelMatch.php',
'LoggerFilterLevelRange' => '/filters/LoggerFilterLevelRange.php',
+ 'LoggerFilterNameMatch' => '/filters/LoggerFilterNameMatch.php',
'LoggerFilterStringMatch' => '/filters/LoggerFilterStringMatch.php',
// Helpers
@@ -87,7 +88,7 @@ class LoggerAutoloader {
'LoggerOptionConverter' => '/helpers/LoggerOptionConverter.php',
'LoggerPatternParser' => '/helpers/LoggerPatternParser.php',
'LoggerUtils' => '/helpers/LoggerUtils.php',
-
+
// Pattern converters
'LoggerPatternConverter' => '/pattern/LoggerPatternConverter.php',
'LoggerPatternConverterClass' => '/pattern/LoggerPatternConverterClass.php',
@@ -113,7 +114,7 @@ class LoggerAutoloader {
'LoggerPatternConverterSessionID' => '/pattern/LoggerPatternConverterSessionID.php',
'LoggerPatternConverterSuperglobal' => '/pattern/LoggerPatternConverterSuperglobal.php',
'LoggerPatternConverterThrowable' => '/pattern/LoggerPatternConverterThrowable.php',
-
+
// Layouts
'LoggerLayoutHtml' => '/layouts/LoggerLayoutHtml.php',
'LoggerLayoutPattern' => '/layouts/LoggerLayoutPattern.php',
@@ -121,14 +122,14 @@ class LoggerAutoloader {
'LoggerLayoutSimple' => '/layouts/LoggerLayoutSimple.php',
'LoggerLayoutTTCC' => '/layouts/LoggerLayoutTTCC.php',
'LoggerLayoutXml' => '/layouts/LoggerLayoutXml.php',
-
+
// Renderers
'LoggerRendererDefault' => '/renderers/LoggerRendererDefault.php',
'LoggerRendererException' => '/renderers/LoggerRendererException.php',
'LoggerRendererMap' => '/renderers/LoggerRendererMap.php',
'LoggerRenderer' => '/renderers/LoggerRenderer.php',
);
-
+
/**
* Loads a class.
* @param string $className The name of the class to load.
diff --git a/src/main/php/filters/LoggerFilterNameMatch.php b/src/main/php/filters/LoggerFilterNameMatch.php
new file mode 100644
index 00000000..50b7daee
--- /dev/null
+++ b/src/main/php/filters/LoggerFilterNameMatch.php
@@ -0,0 +1,130 @@
+The filter admits four options: {@link $stringToMatch}, {@link $caseSensitive},
+ * {@link $exactMatch}, and
+ * {@link $acceptOnMatch}. If the value of the {@link $stringToMatch} option is included
+ * in name of the {@link LoggerLoggingEvent}, then the {@link decide()} method returns
+ * {@link LoggerFilter::ACCEPT} if the AcceptOnMatch option value is true and
+ * {@link LoggerFilter::DENY} if it is false. If there is no match, {@link LoggerFilter::NEUTRAL}
+ * is returned. Matching is case-sensitive by default. Setting {@link $caseSensitive}
+ * to false makes matching case insensitive. An exact match can be required by setting
+ * {@link $exactMatch} to true.
+ *
+ *
+ * An example for this filter:
+ *
+ * {@example ../../examples/php/filter_namematch.php 19}
+ *
+ *
+ * The corresponding XML file:
+ *
+ * {@example ../../examples/resources/filter_namematch.xml 18}
+ *
+ * @package log4php
+ * @subpackage filters
+ * @since 2.3.1
+ */
+class LoggerFilterNameMatch extends LoggerFilter {
+
+ /**
+ * @var boolean
+ */
+ protected $acceptOnMatch = true;
+
+ /**
+ * @var boolean
+ */
+ protected $caseSensitive = true;
+
+ /**
+ * @var boolean
+ */
+ protected $exactMatch = false;
+
+ /**
+ * @var string
+ */
+ protected $stringToMatch;
+
+ /**
+ * @param mixed $acceptOnMatch a boolean or a string ('true' or 'false')
+ */
+ public function setAcceptOnMatch($acceptOnMatch) {
+ $this->setBoolean('acceptOnMatch', $acceptOnMatch);
+ }
+
+ /**
+ * @param mixed $caseSensitive a boolean or a string ('true' or 'false')
+ */
+ public function setCaseSensitive($caseSensitive) {
+ $this->setBoolean('caseSensitive', $caseSensitive);
+ }
+
+ /**
+ * @param mixed $caseSensitive a boolean or a string ('true' or 'false')
+ */
+ public function setExactMatch($exactMatch) {
+ $this->setBoolean('exactMatch', $exactMatch);
+ }
+
+ /**
+ * @param string $s the string to match
+ */
+ public function setStringToMatch($string) {
+ $this->setString('stringToMatch', $string);
+ }
+
+ /**
+ * @return integer a {@link LOGGER_FILTER_NEUTRAL} is there is no string match.
+ */
+ public function decide(LoggerLoggingEvent $event) {
+ $msg = $event->getLoggerName();
+
+ if($msg === null or $this->stringToMatch === null) {
+ return LoggerFilter::NEUTRAL;
+ }
+
+ if($this->caseSensitive) {
+ return $this->testString((function_exists('mb_strpos') ? 'mb_strpos' : 'strpos'), $msg);
+ } else {
+ return $this->testString((function_exists('mb_stripos') ? 'mb_stripos' : 'stripos'), $msg);
+ }
+ }
+
+
+ protected function testString($method, $msg) {
+ if($method($msg, $this->stringToMatch) !== false) {
+ if($this->exactMatch) {
+ $lenFunc = function_exists('mb_strlen') ? 'mb_strlen' : 'strlen';
+ if($lenFunc($this->stringToMatch) === $lenFunc($msg)) {
+ // We were looking for an exact match, and we found one.
+ return ($this->acceptOnMatch) ? LoggerFilter::ACCEPT : LoggerFilter::DENY;
+ }
+ } else { // No exact match required.
+ return ($this->acceptOnMatch) ? LoggerFilter::ACCEPT : LoggerFilter::DENY;
+ }
+ }
+ return LoggerFilter::NEUTRAL;
+ }
+}
diff --git a/src/site/xdoc/docs/filters.xml b/src/site/xdoc/docs/filters.xml
index 555205d7..000e650d 100644
--- a/src/site/xdoc/docs/filters.xml
+++ b/src/site/xdoc/docs/filters.xml
@@ -156,6 +156,10 @@ array(
LoggerFilterLevelRange |
Filters based on logging event level range. |
+
+ | LoggerFilterNameMatch |
+ Filters by searching for a string in the logger name. |
+
| LoggerFilterStringMatch |
Filters by searching for a string in the logging event message. |
@@ -265,6 +269,79 @@ array(
+]]>
+
+
+
+
+ This filter allows or denies logging events if the logger name contains a given string. If no match
+ is found the filter remains neutral.
+
+
+ Configurable parameters
+
+
+
+
+ | Parameter |
+ Type |
+ Required |
+ Default |
+ Description |
+
+
+
+
+ | stringToMatch |
+ LoggerLevel |
+ Yes |
+ - |
+ The string to match. |
+
+
+ | caseSensitive |
+ boolean |
+ No |
+ true |
+ If true, matches are case sensitive. If false, matches ignore case differences. |
+
+
+ | exactMatch |
+ LoggerLevel |
+ No |
+ false |
+ If true, the stringToMatch must have the same text content as the logger name. This
+ parameter co-operates with the caseSensitive parameter; that is, if your
+ match is case insensitive, then "Example" is considered an exact match with
+ "example". |
+
+
+ | acceptOnMatch |
+ boolean |
+ No |
+ true |
+ If true and a match is found, the matching log event is accepted. If false and a
+ match is found, the matching log event is denied. |
+
+
+
+
+ Example
+
+ One common pattern is to give each class in your application a logger via
+ $this->logger = Logger::getLogger(__CLASS__) in the constructor. You could use this
+ filter to log any events from your Payment class for easier debugging and greater
+ visibility.
+
+ The following filter configuration only accepts events which contain the string "Payment" in
+ the logger name.
+
+
+
+
+
+
]]>
diff --git a/src/test/php/filters/LoggerFilterNameMatchTest.php b/src/test/php/filters/LoggerFilterNameMatchTest.php
new file mode 100644
index 00000000..b3316213
--- /dev/null
+++ b/src/test/php/filters/LoggerFilterNameMatchTest.php
@@ -0,0 +1,106 @@
+setAcceptOnMatch("true");
+ $filter->setStringToMatch("AcCePtEd");
+
+ $eventFromAccepted = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('AcCePtEd'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromAccepted2 = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Accepted'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromElsewhere = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Elsewhere'), LoggerLevel::getLevelInfo(), "Irrelevant");
+
+
+ // Events are case-sensitive by default.
+ $this->assertEquals($filter->decide($eventFromAccepted), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromAccepted2), LoggerFilter::NEUTRAL);
+ $this->assertEquals($filter->decide($eventFromElsewhere), LoggerFilter::NEUTRAL);
+
+ // But we can make them case insensitive.
+ $filter->setCaseSensitive("false");
+ $this->assertEquals($filter->decide($eventFromAccepted), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromAccepted2), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromElsewhere), LoggerFilter::NEUTRAL);
+ }
+
+ public function testPartialMatch() {
+ $filter = new LoggerFilterNameMatch();
+ $filter->setAcceptOnMatch("true");
+ $filter->setStringToMatch("Accept");
+
+ $eventFromAccept = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Accept'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromAccepted = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Accepted'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromElsewhere = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Elsewhere'), LoggerLevel::getLevelInfo(), "Irrelevant");
+
+ // Partial matches are accepted.
+ $this->assertEquals($filter->decide($eventFromAccept), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromAccepted), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromElsewhere), LoggerFilter::NEUTRAL);
+ }
+
+ public function testExactMatch() {
+ $filter = new LoggerFilterNameMatch();
+ $filter->setAcceptOnMatch("true");
+ $filter->setStringToMatch("Accept");
+ $filter->setExactMatch("true");
+
+ $eventFromAccept = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Accept'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromAccepted = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Accepted'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromElsewhere = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Elsewhere'), LoggerLevel::getLevelInfo(), "Irrelevant");
+
+ // Partial matches are accepted.
+ $this->assertEquals($filter->decide($eventFromAccept), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromAccepted), LoggerFilter::NEUTRAL);
+ $this->assertEquals($filter->decide($eventFromElsewhere), LoggerFilter::NEUTRAL);
+ }
+
+ public function testAcceptOnMatchTrue() {
+ $filter = new LoggerFilterNameMatch();
+ $filter->setAcceptOnMatch("true");
+ $filter->setStringToMatch("Accept");
+
+ $eventFromAccept = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Accept'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromNeutral = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Neutral'), LoggerLevel::getLevelInfo(), "Irrelevant");
+
+ $this->assertEquals($filter->decide($eventFromAccept), LoggerFilter::ACCEPT);
+ $this->assertEquals($filter->decide($eventFromNeutral), LoggerFilter::NEUTRAL);
+ }
+
+ public function testAcceptOnMatchFalse() {
+ $filter = new LoggerFilterNameMatch();
+ $filter->setAcceptOnMatch("false");
+ $filter->setStringToMatch("Deny");
+
+ $eventFromDeny = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Deny'), LoggerLevel::getLevelInfo(), "Irrelevant");
+ $eventFromNeutral = new LoggerLoggingEvent("LoggerFilterNameMatchTest", new Logger('Neutral'), LoggerLevel::getLevelInfo(), "Irrelevant");
+
+ $this->assertEquals($filter->decide($eventFromDeny), LoggerFilter::DENY);
+ $this->assertEquals($filter->decide($eventFromNeutral), LoggerFilter::NEUTRAL);
+ }
+}