Implementation of a MapFilter.
https://github.com/apache/logging-log4cxx/pull/24
https://github.com/apache/logging-log4cxx/pull/25
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 94fd8d3..2e64860 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -107,6 +107,7 @@
<action issue="LOGCXX-262" type="fix">socketappendertestcase and xmlsocketappendertestcase not run</action>
<action issue="LOGCXX-249" type="fix">Console appender crashes if layout is not set</action>
+ <action issue="24" system="GHPR" type="add">Implementation of map-based filter.</action>
<action issue="21" system="GHPR" type="add">Added support for building log4cxx as a statically linked library on Windows.</action>
<action issue="14" system="GHPR" type="add">Replaced ant build with cmake.</action>
<action issue="13" system="GHPR" type="add">JSONLayout</action>
diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
index 321edf0..8f7d609 100644
--- a/src/main/cpp/CMakeLists.txt
+++ b/src/main/cpp/CMakeLists.txt
@@ -87,6 +87,7 @@
logmanager.cpp
logstream.cpp
manualtriggeringpolicy.cpp
+ mapfilter.cpp
mdc.cpp
messagebuffer.cpp
messagepatternconverter.cpp
diff --git a/src/main/cpp/Makefile.am b/src/main/cpp/Makefile.am
index 236a9a6..3643623 100644
--- a/src/main/cpp/Makefile.am
+++ b/src/main/cpp/Makefile.am
@@ -93,6 +93,7 @@
logmanager.cpp \
logstream.cpp \
manualtriggeringpolicy.cpp \
+ mapfilter.cpp \
messagebuffer.cpp \
messagepatternconverter.cpp \
methodlocationpatternconverter.cpp \
diff --git a/src/main/cpp/mapfilter.cpp b/src/main/cpp/mapfilter.cpp
new file mode 100644
index 0000000..09e38ca
--- /dev/null
+++ b/src/main/cpp/mapfilter.cpp
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * 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.
+ */
+
+#include <log4cxx/logstring.h>
+#include <log4cxx/filter/mapfilter.h>
+#include <log4cxx/spi/loggingevent.h>
+#include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/optionconverter.h>
+
+using namespace log4cxx;
+using namespace log4cxx::filter;
+using namespace log4cxx::spi;
+using namespace log4cxx::helpers;
+
+IMPLEMENT_LOG4CXX_OBJECT(MapFilter)
+
+MapFilter::MapFilter() : acceptOnMatch(true), mustMatchAll(false)
+{
+
+}
+
+void MapFilter::setOption( const LogString& option,
+ const LogString& value)
+{
+ if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ACCEPTONMATCH"), LOG4CXX_STR("acceptonmatch")))
+ {
+ acceptOnMatch = OptionConverter::toBoolean(value, acceptOnMatch);
+ }
+ else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("OPERATOR"), LOG4CXX_STR("operator")))
+ {
+ mustMatchAll = StringHelper::equalsIgnoreCase(value, LOG4CXX_STR("AND"), LOG4CXX_STR("and")) ? true : false;
+ }
+ else if (!option.empty() && !value.empty())
+ {
+ keyVals[option] = value;
+ }
+}
+
+Filter::FilterDecision MapFilter::decide(
+ const log4cxx::spi::LoggingEventPtr& event) const
+{
+ if (keyVals.empty())
+ {
+ return Filter::NEUTRAL;
+ }
+
+ bool matched = true;
+
+ for (KeyVals::const_iterator it = keyVals.begin(); it != keyVals.end(); ++it)
+ {
+ LogString curval;
+ event->getMDC(it->first, curval);
+
+ if (curval.empty() || curval != it->second)
+ {
+ matched = false;
+ }
+ else
+ {
+ matched = true;
+ }
+
+ if (mustMatchAll != matched)
+ {
+ break;
+ }
+ }
+
+ if (acceptOnMatch)
+ {
+ return matched ? Filter::ACCEPT : Filter::DENY;
+ }
+ else
+ {
+ return matched ? Filter::DENY : Filter::ACCEPT;
+ }
+}
diff --git a/src/main/include/log4cxx/filter/mapfilter.h b/src/main/include/log4cxx/filter/mapfilter.h
index d6db108..768fbd6 100644
--- a/src/main/include/log4cxx/filter/mapfilter.h
+++ b/src/main/include/log4cxx/filter/mapfilter.h
@@ -19,26 +19,98 @@
#include <log4cxx/spi/filter.h>
+#if defined(_MSC_VER)
+ #pragma warning ( push )
+ #pragma warning ( disable: 4251 )
+#endif
+
namespace log4cxx
{
namespace filter
{
-
+/**
+ * A Filter that operates on a Map and can be used like in the following example:
+ * <pre>
+ * <filter class="MapFilter">
+ * <param name="user.ip" value="127.0.0.1" />
+ * <param name="user.name" value="test2" />
+ * <param name="Operator" value="AND" />
+ * <param name="AcceptOnMatch" value="false" />
+ * </filter>
+ * </pre>
+ */
class LOG4CXX_EXPORT MapFilter: public log4cxx::spi::Filter
{
+ typedef std::map < LogString, LogString > KeyVals;
+
+ private:
+ bool acceptOnMatch;
+ bool mustMatchAll; // true = AND; false = OR
+ KeyVals keyVals;
+
public:
DECLARE_LOG4CXX_OBJECT(MapFilter)
BEGIN_LOG4CXX_CAST_MAP()
- LOG4CXX_CAST_ENTRY(log4cxx::spi::Filter)
+ LOG4CXX_CAST_ENTRY(MapFilter)
+ LOG4CXX_CAST_ENTRY_CHAIN(log4cxx::spi::Filter)
END_LOG4CXX_CAST_MAP()
MapFilter();
+ /**
+ Set options
+ */
+ virtual void setOption(const LogString& option,
+ const LogString& value);
+ inline void setKeyValue(const LogString& strKey, const LogString& strValue)
+ {
+ this->keyVals[strKey] = strValue;
+ }
+
+ inline const LogString& getValue(const LogString& strKey) const
+ {
+ static const LogString empty;
+ const KeyVals::const_iterator it(this->keyVals.find(strKey));
+
+ return (it != keyVals.end() ? it->second : empty);
+ }
+
+ inline void setAcceptOnMatch(bool acceptOnMatch1)
+ {
+ this->acceptOnMatch = acceptOnMatch1;
+ }
+
+ inline bool getAcceptOnMatch() const
+ {
+ return acceptOnMatch;
+ }
+
+ inline bool getMustMatchAll() const
+ {
+ return mustMatchAll;
+ }
+
+ inline void setMustMatchAll(bool mustMatchAll1)
+ {
+ this->mustMatchAll = mustMatchAll1;
+ }
+
+ /**
+ Returns {@link log4cxx::spi::Filter#NEUTRAL NEUTRAL}
+ is there is no string match.
+ */
FilterDecision decide(const spi::LoggingEventPtr& event) const;
+}; // class MapFilter
-};
-}
-}
+LOG4CXX_PTR_DEF(MapFilter);
+
+} // namespace filter
+} // namespace log4cxx
+
+#if defined(_MSC_VER)
+ #pragma warning (pop)
#endif
+
+#endif // _LOG4CXX_FILTER_MAPFILTER_H
diff --git a/src/test/cpp/Makefile.am b/src/test/cpp/Makefile.am
index 8b6c95d..1f42361 100644
--- a/src/test/cpp/Makefile.am
+++ b/src/test/cpp/Makefile.am
@@ -67,6 +67,7 @@
filter/levelmatchfiltertest.cpp \
filter/levelrangefiltertest.cpp \
filter/loggermatchfiltertest.cpp \
+ filter/mapfiltertest.cpp \
filter/stringmatchfiltertest.cpp
helpers = \
diff --git a/src/test/cpp/filter/CMakeLists.txt b/src/test/cpp/filter/CMakeLists.txt
index 83dc498..3b4bcb0 100644
--- a/src/test/cpp/filter/CMakeLists.txt
+++ b/src/test/cpp/filter/CMakeLists.txt
@@ -4,6 +4,7 @@
levelmatchfiltertest.cpp
levelrangefiltertest.cpp
loggermatchfiltertest.cpp
+ mapfiltertest.cpp
stringmatchfiltertest.cpp
)
set(ALL_LOG4CXX_TESTS ${ALL_LOG4CXX_TESTS} filtertests PARENT_SCOPE)
diff --git a/src/test/cpp/filter/mapfiltertest.cpp b/src/test/cpp/filter/mapfiltertest.cpp
new file mode 100644
index 0000000..bfb9e53
--- /dev/null
+++ b/src/test/cpp/filter/mapfiltertest.cpp
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * 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.
+ */
+#include <log4cxx/filter/mapfilter.h>
+#include <log4cxx/logger.h>
+#include <log4cxx/spi/filter.h>
+#include <log4cxx/spi/loggingevent.h>
+#include "../logunit.h"
+
+using namespace log4cxx;
+using namespace log4cxx::filter;
+using namespace log4cxx::spi;
+using namespace log4cxx::helpers;
+
+/**
+ * Unit tests for MapFilter.
+ */
+LOGUNIT_CLASS(MapFilterTest)
+{
+ LOGUNIT_TEST_SUITE(MapFilterTest);
+ LOGUNIT_TEST(test1);
+ LOGUNIT_TEST(test2);
+ LOGUNIT_TEST(test3);
+ LOGUNIT_TEST(test4);
+ LOGUNIT_TEST_SUITE_END();
+
+public:
+
+ /**
+ * Check that MapFilter.decide() returns Filter.NEUTRAL
+ * when there are no map entries specified.
+ */
+ void test1()
+ {
+ LoggingEventPtr event(new LoggingEvent(
+ LOG4CXX_STR("MapFilterTest"),
+ Level::getInfo(),
+ LOG4CXX_STR("Hello, World"),
+ LOG4CXX_LOCATION));
+ FilterPtr filter(new MapFilter());
+ Pool p;
+ filter->activateOptions(p);
+ LOGUNIT_ASSERT_EQUAL(Filter::NEUTRAL, filter->decide(event));
+ }
+
+ /**
+ * Check that MapFilter.decide() returns Filter.ACCEPT or Filter.DENY
+ * based on Accept on Match setting when key/value does not match
+ */
+ void test2()
+ {
+ LoggingEventPtr event(new LoggingEvent(
+ LOG4CXX_STR("MapFilterTest"),
+ Level::getInfo(),
+ LOG4CXX_STR("Hello, World"),
+ LOG4CXX_LOCATION));
+ MDC::put(LOG4CXX_STR("my.ip"), LOG4CXX_STR("localhost"));
+ MapFilterPtr filter(new MapFilter());
+ filter->setKeyValue(LOG4CXX_STR("my.ip"), LOG4CXX_STR("127.0.0.1"));
+ Pool p;
+ filter->activateOptions(p);
+
+ filter->setAcceptOnMatch(true);
+ LOGUNIT_ASSERT_EQUAL(Filter::DENY, filter->decide(event));
+
+ filter->setAcceptOnMatch(false);
+ LOGUNIT_ASSERT_EQUAL(Filter::ACCEPT, filter->decide(event));
+ }
+
+ /**
+ * Check that MapFilter.decide() returns Filter.ACCEPT or Filter.DENY
+ * based on Accept on Match setting when key/value matches
+ */
+ void test3()
+ {
+ LoggingEventPtr event(new LoggingEvent(
+ LOG4CXX_STR("MapFilterTest"),
+ Level::getInfo(),
+ LOG4CXX_STR("Hello, World"),
+ LOG4CXX_LOCATION));
+ MDC::put(LOG4CXX_STR("my.ip"), LOG4CXX_STR("127.0.0.1"));
+ MapFilterPtr filter(new MapFilter());
+ filter->setKeyValue(LOG4CXX_STR("my.ip"), LOG4CXX_STR("127.0.0.1"));
+ Pool p;
+ filter->activateOptions(p);
+
+ filter->setAcceptOnMatch(true);
+ LOGUNIT_ASSERT_EQUAL(Filter::ACCEPT, filter->decide(event));
+
+ filter->setAcceptOnMatch(false);
+ LOGUNIT_ASSERT_EQUAL(Filter::DENY, filter->decide(event));
+ }
+
+ /**
+ * Check that MapFilter.decide() ANDs or ORs multiple key/values
+ * based on operator setting
+ */
+ void test4()
+ {
+ LoggingEventPtr event(new LoggingEvent(
+ LOG4CXX_STR("MapFilterTest"),
+ Level::getInfo(),
+ LOG4CXX_STR("Hello, World"),
+ LOG4CXX_LOCATION));
+ MDC::put(LOG4CXX_STR("my.ip"), LOG4CXX_STR("127.0.0.1"));
+ MDC::put(LOG4CXX_STR("my.name"), LOG4CXX_STR("Test"));
+ MapFilterPtr filter(new MapFilter());
+ filter->setKeyValue(LOG4CXX_STR("my.ip"), LOG4CXX_STR("127.0.0.1"));
+ filter->setKeyValue(LOG4CXX_STR("my.name"), LOG4CXX_STR("Unknown"));
+ filter->setAcceptOnMatch(true);
+ Pool p;
+ filter->activateOptions(p);
+
+ filter->setMustMatchAll(true); // AND T/F
+ LOGUNIT_ASSERT_EQUAL(Filter::DENY, filter->decide(event)); // does not match second
+
+ filter->setMustMatchAll(false); // OR T/F
+ LOGUNIT_ASSERT_EQUAL(Filter::ACCEPT, filter->decide(event)); // matches first
+
+ filter->setKeyValue(LOG4CXX_STR("my.name"), LOG4CXX_STR("Test"));
+
+ filter->setMustMatchAll(true); // AND T/T
+ LOGUNIT_ASSERT_EQUAL(Filter::ACCEPT, filter->decide(event)); // matches all
+
+ filter->setMustMatchAll(false); // OR T/T
+ LOGUNIT_ASSERT_EQUAL(Filter::ACCEPT, filter->decide(event)); // matches first
+
+ filter->setKeyValue(LOG4CXX_STR("my.ip"), LOG4CXX_STR("localhost"));
+
+ filter->setMustMatchAll(true); // AND F/T
+ LOGUNIT_ASSERT_EQUAL(Filter::DENY, filter->decide(event)); // does not match first
+
+ filter->setMustMatchAll(false); // OR F/T
+ LOGUNIT_ASSERT_EQUAL(Filter::ACCEPT, filter->decide(event)); // matches second
+
+ filter->setKeyValue(LOG4CXX_STR("my.name"), LOG4CXX_STR("Unkonwn"));
+
+ filter->setMustMatchAll(true); // AND F/F
+ LOGUNIT_ASSERT_EQUAL(Filter::DENY, filter->decide(event)); // does not match first
+
+ filter->setMustMatchAll(false); // OR F/F
+ LOGUNIT_ASSERT_EQUAL(Filter::DENY, filter->decide(event)); // matches none
+ }
+
+};
+
+
+LOGUNIT_TEST_SUITE_REGISTRATION(MapFilterTest);
+
+