This is an automated email from the ASF dual-hosted git repository.

rmiddleton pushed a commit to branch next_stable
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/next_stable by this push:
     new de439929 LOGCXX-514 (#153)
de439929 is described below

commit de4399299954ade73d90298e7f852b943f51d4c8
Author: Robert Middleton <[email protected]>
AuthorDate: Mon Dec 5 19:26:47 2022 -0500

    LOGCXX-514 (#153)
    
    * LOGCXX-514 Create a FMTLayout that uses libfmt to format messages
---
 .github/workflows/log4cxx-windows.yml              |   2 +-
 CMakeLists.txt                                     |  17 +-
 src/examples/cpp/CMakeLists.txt                    |   1 -
 src/main/cpp/CMakeLists.txt                        |  10 +
 src/main/cpp/fmtlayout.cpp                         | 120 +++++++++
 src/main/cpp/loggingevent.cpp                      |  12 +-
 src/main/include/log4cxx/fmtlayout.h               | 272 +++++++++++++++++++++
 src/main/include/log4cxx/spi/loggingevent.h        |   3 +
 src/test/cpp/CMakeLists.txt                        |   3 +
 src/test/cpp/fmttest.cpp                           | 197 +++++++++++++++
 src/test/resources/input/fmtLayout1.properties     |  21 ++
 src/test/resources/input/fmtLayout10.properties    |  21 ++
 .../resources/input/fmtLayout1_expanded.properties |  21 ++
 13 files changed, 695 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/log4cxx-windows.yml 
b/.github/workflows/log4cxx-windows.yml
index 2dcc0b69..012c41c0 100644
--- a/.github/workflows/log4cxx-windows.yml
+++ b/.github/workflows/log4cxx-windows.yml
@@ -58,7 +58,7 @@ jobs:
       run: |
         cd vcpkg
         ./bootstrap-vcpkg.bat
-        ./vcpkg install apr apr-util --triplet=x64-windows
+        ./vcpkg install apr apr-util fmt --triplet=x64-windows
 
     - name: 'Install zip'
       id: install-zip
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 74d38ab1..fca0f7e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,6 +63,13 @@ elseif(TARGET expat::expat)
   set(EXPAT_LIBRARIES expat::expat)
 endif()
 
+find_package(fmt 7.1 QUIET)
+if(${fmt_FOUND})
+    option(ENABLE_FMT_LAYOUT "Enable the FMT layout(if libfmt found)" ON)
+else()
+    set(ENABLE_FMT_LAYOUT "OFF")
+endif()
+
 # Request C++17, if available
 # This *should* fallback to an older standard if it is not available
 if( NOT "${CMAKE_CXX_STANDARD}")
@@ -191,7 +198,7 @@ get_directory_property( ATOMIC_IMPL DIRECTORY 
src/main/include DEFINITION ATOMIC
 get_directory_property( FILESYSTEM_IMPL DIRECTORY src/main/include DEFINITION 
FILESYSTEM_IMPL )
 get_directory_property( STD_MAKE_UNIQUE_IMPL DIRECTORY src/main/include 
DEFINITION STD_MAKE_UNIQUE_IMPL )
 
-foreach(varName HAS_STD_LOCALE  HAS_ODBC  HAS_MBSRTOWCS  HAS_WCSTOMBS  
HAS_FWIDE  HAS_LIBESMTP  HAS_SYSLOG)
+foreach(varName HAS_STD_LOCALE  HAS_ODBC  HAS_MBSRTOWCS  HAS_WCSTOMBS  
HAS_FWIDE  HAS_LIBESMTP  HAS_SYSLOG HAS_FMT)
   if(${varName} EQUAL 0)
     set(${varName} "OFF" )
   elseif(${varName} EQUAL 1)
@@ -293,3 +300,11 @@ message(STATUS "  ConsoleAppender ................. : ON")
 message(STATUS "  FileAppender .................... : ON")
 message(STATUS "  RollingFileAppender ............. : ON")
 message(STATUS "  MultiprocessRollingFileAppender . : 
${LOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER}")
+
+message(STATUS "Available layouts:")
+message(STATUS "  HTMLLayout ...................... : ON")
+message(STATUS "  JSONLayout ...................... : ON")
+message(STATUS "  PatternLayout ................... : ON")
+message(STATUS "  SimpleLayout .................... : ON")
+message(STATUS "  XMLLayout ....................... : ON")
+message(STATUS "  FMTLayout ....................... : ${ENABLE_FMT_LAYOUT}")
diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt
index d8668bba..2cce6747 100644
--- a/src/examples/cpp/CMakeLists.txt
+++ b/src/examples/cpp/CMakeLists.txt
@@ -49,7 +49,6 @@ configure_file( custom-appender.xml
     COPYONLY )
 
 # Custom handling for format string example, since it utilizes libfmt
-find_package(fmt 6.0 QUIET)
 if(${fmt_FOUND})
     add_executable( format-string format-string.cpp )
     target_compile_definitions(format-string PRIVATE 
${LOG4CXX_COMPILE_DEFINITIONS} ${APR_COMPILE_DEFINITIONS} 
${APR_UTIL_COMPILE_DEFINITIONS} )
diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
index 99066a10..acd4f46a 100644
--- a/src/main/cpp/CMakeLists.txt
+++ b/src/main/cpp/CMakeLists.txt
@@ -54,6 +54,12 @@ if()
        )
 endif()
 
+if(${ENABLE_FMT_LAYOUT})
+    set(extra_classes ${extra_classes}
+       fmtlayout.cpp
+       )
+endif()
+
 target_sources(log4cxx
   PRIVATE
   action.cpp
@@ -204,6 +210,10 @@ if("${FILESYSTEM_IMPL}" STREQUAL "std::filesystem" OR
   target_link_libraries(log4cxx PUBLIC 
$<$<AND:$<CXX_COMPILER_ID:GNU>,$<VERSION_LESS:$<CXX_COMPILER_VERSION>,9.0>>:stdc++fs>)
 endif()
 
+if(${ENABLE_FMT_LAYOUT})
+    target_link_libraries(log4cxx PUBLIC fmt::fmt)
+endif()
+
 if(LOG4CXX_ABI_CHECK)
     message("Getting dependencies for ABI compatability check...")
     # Get the latest version of abi-dumper and abi-compliance-checker
diff --git a/src/main/cpp/fmtlayout.cpp b/src/main/cpp/fmtlayout.cpp
new file mode 100644
index 00000000..20b7cf54
--- /dev/null
+++ b/src/main/cpp/fmtlayout.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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/fmtlayout.h>
+#include <log4cxx/spi/loggingevent.h>
+#include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/optionconverter.h>
+#include <log4cxx/level.h>
+#include <chrono>
+
+#include <fmt/format.h>
+#include <fmt/chrono.h>
+
+using namespace log4cxx;
+using namespace log4cxx::spi;
+
+struct FMTLayout::FMTLayoutPrivate{
+       FMTLayoutPrivate(){}
+
+       FMTLayoutPrivate(const LogString& pattern) :
+               conversionPattern(pattern)
+       {}
+
+       LogString conversionPattern;
+};
+
+IMPLEMENT_LOG4CXX_OBJECT(FMTLayout)
+
+FMTLayout::FMTLayout() :
+       m_priv(std::make_unique<FMTLayoutPrivate>())
+{}
+
+FMTLayout::FMTLayout(const LogString& pattern) :
+       m_priv(std::make_unique<FMTLayoutPrivate>(pattern))
+{}
+
+FMTLayout::~FMTLayout(){}
+
+void FMTLayout::setConversionPattern(const LogString& pattern)
+{
+       m_priv->conversionPattern = pattern;
+       helpers::Pool pool;
+       activateOptions(pool);
+}
+
+LogString FMTLayout::getConversionPattern() const
+{
+       return m_priv->conversionPattern;
+}
+
+void FMTLayout::setOption(const LogString& option, const LogString& value)
+{
+       if (helpers::StringHelper::equalsIgnoreCase(option,
+                       LOG4CXX_STR("CONVERSIONPATTERN"),
+                       LOG4CXX_STR("conversionpattern")))
+       {
+               m_priv->conversionPattern = 
helpers::OptionConverter::convertSpecialChars(value);
+       }
+}
+
+void FMTLayout::activateOptions(helpers::Pool&)
+{
+
+}
+
+void FMTLayout::format(LogString& output,
+       const spi::LoggingEventPtr& event,
+       log4cxx::helpers::Pool&) const
+{
+       LogString locationFull = fmt::format("{}({})",
+                                                                               
 event->getLocationInformation().getFileName(),
+                                                                               
 event->getLocationInformation().getLineNumber());
+       LogString ndc;
+       event->getNDC(ndc);
+
+       fmt::format_to(std::back_inserter(output),
+                                  m_priv->conversionPattern,
+                                  fmt::arg("d", event->getChronoTimeStamp()),
+                                  fmt::arg("c", event->getLoggerName()),
+                                  fmt::arg("logger", event->getLoggerName()),
+                                  fmt::arg("f", 
event->getLocationInformation().getShortFileName()),
+                                  fmt::arg("shortfilename", 
event->getLocationInformation().getShortFileName()),
+                                  fmt::arg("F", 
event->getLocationInformation().getFileName()),
+                                  fmt::arg("filename", 
event->getLocationInformation().getFileName()),
+                                  fmt::arg("l", locationFull),
+                                  fmt::arg("location", locationFull),
+                                  fmt::arg("L", 
event->getLocationInformation().getLineNumber()),
+                                  fmt::arg("line", 
event->getLocationInformation().getLineNumber()),
+                                  fmt::arg("m", event->getMessage()),
+                                  fmt::arg("message", event->getMessage()),
+                                  fmt::arg("M", 
event->getLocationInformation().getMethodName()),
+                                  fmt::arg("method", 
event->getLocationInformation().getMethodName()),
+                                  fmt::arg("n", LOG4CXX_EOL),
+                                  fmt::arg("newline", LOG4CXX_EOL),
+                                  fmt::arg("p", event->getLevel()->toString()),
+                                  fmt::arg("level", 
event->getLevel()->toString()),
+                                  fmt::arg("r", event->getTimeStamp()),
+                                  fmt::arg("t", event->getThreadName()),
+                                  fmt::arg("thread", event->getThreadName()),
+                                  fmt::arg("T", event->getThreadUserName()),
+                                  fmt::arg("threadname", 
event->getThreadUserName()),
+                                  fmt::arg("x", ndc),
+                                  fmt::arg("ndc", ndc)
+                                  );
+}
diff --git a/src/main/cpp/loggingevent.cpp b/src/main/cpp/loggingevent.cpp
index 8adcd0e6..adba3c43 100644
--- a/src/main/cpp/loggingevent.cpp
+++ b/src/main/cpp/loggingevent.cpp
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+#include <chrono>
 #include <log4cxx/spi/loggingevent.h>
 #include <log4cxx/ndc.h>
 
@@ -68,10 +69,11 @@ struct LoggingEvent::LoggingEventPrivate
                ndcLookupRequired(true),
                mdcCopyLookupRequired(true),
                message(message1),
-               timeStamp(apr_time_now()),
+               timeStamp(Date::currentTime()),
                locationInfo(locationInfo1),
                threadName(getCurrentThreadName()),
-               threadUserName(getCurrentThreadUserName())
+               threadUserName(getCurrentThreadUserName()),
+               chronoTimeStamp(std::chrono::microseconds(timeStamp))
        {
        }
 
@@ -138,6 +140,8 @@ struct LoggingEvent::LoggingEventPrivate
         * systems or SetThreadDescription on Windows.
         */
        const LogString& threadUserName;
+
+       std::chrono::time_point<std::chrono::system_clock> chronoTimeStamp;
 };
 
 IMPLEMENT_LOG4CXX_OBJECT(LoggingEvent)
@@ -432,3 +436,7 @@ const log4cxx::spi::LocationInfo& 
LoggingEvent::getLocationInformation() const
        return m_priv->locationInfo;
 }
 
+std::chrono::time_point<std::chrono::system_clock> 
LoggingEvent::getChronoTimeStamp() const{
+       return m_priv->chronoTimeStamp;
+}
+
diff --git a/src/main/include/log4cxx/fmtlayout.h 
b/src/main/include/log4cxx/fmtlayout.h
new file mode 100644
index 00000000..79fb9e28
--- /dev/null
+++ b/src/main/include/log4cxx/fmtlayout.h
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+#ifndef LOG4CXX_FMT_LAYOUT_H
+#define LOG4CXX_FMT_LAYOUT_H
+
+#if defined(_MSC_VER)
+       #pragma warning ( push )
+       #pragma warning ( disable: 4231 4251 4275 4786 )
+#endif
+
+
+#include <log4cxx/layout.h>
+
+namespace log4cxx
+{
+/**
+ * The FMTLayout class uses libfmt to layout messages.  This is an alternative 
to the PatternLayout class.
+ * Most of the standard PatternLayout arguments are also accepted as 
arguments, so that you can easily
+ * convert a PatternLayout to a FMTLayout.  For example, given the following 
PatternLayout:
+ *
+ * <pre>%c %-5p - %m%n</pre>
+ * which outputs something like:
+ * <pre>root INFO  - Hello there!</pre>
+ *
+ * The equivalent FMTLayout can be written as:
+ * <pre>{c} {p:<5} - {m}{n}</pre>
+ * Or more verbosely as:
+ * <pre>{logger} {level:<5} - {message}{newline}</pre>
+ *
+ * All replacements are done using the named arguments feature of {fmt}.
+ *
+ * <p>The recognized conversion strings are:</p>
+ *
+ * <table border="1" cellpadding="8">
+ *  <tr>
+ *      <th align="center"><strong>Conversion string</strong></th>
+ *      <th align="center"><strong>Effect</strong></th>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *        <p><strong>c</strong></p>
+ *        <p><strong>logger</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the logger of the logging event.  Unlike 
PatternConverter,
+ *          this does not take a parameter to shorten the name of the logger.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>C</strong></p>
+ *          <p><strong>class</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the class of the issuer of the logging event if the 
compiler
+ *          used supports a macro to retrieve the method of the currently 
compiled line and
+ *          if the LOG4CXX_TRACE-like macros are used to issue a logging 
request. In this
+ *          case the macro LOG4CXX_* is expanded at compile time to generate 
location info
+ *          of the logging event and adds the method name, besides file and 
line, if
+ *          available. In most cases the provided method contains the 
classname and can
+ *          therefore be retrieved form the location info as needed.
+ *          <p>
+ *              Currently supported compilers are those from Microsoft, GNU-C 
and Borland.
+ *          </p>
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>f</strong></p>
+ *          <p><strong>shortfilename</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the short file name where the logging request was 
issued.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>F</strong></p>
+ *          <p><strong>filename</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the file name where the logging request was issued.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>l</strong></p>
+ *          <p><strong>location</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output location information of the caller which generated 
the logging
+ *          event.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>L</strong></p>
+ *          <p><strong>line</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the line number from where the logging request was 
issued.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>m</strong></p>
+ *          <p><strong>message</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the application supplied message associated with 
the logging
+ *          event.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <strong>M</strong>
+ *          <p><strong>method</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the method of the issuer of the logging event if 
the compiler
+ *          used supports a macro to retrieve the method of the currently 
compiled line
+ *          and if the LOG4CXX_TRACE-like macros are used to issue a logging 
request. In
+ *          this case the macro LOG4CXX_* is expanded at compile time to 
generate location
+ *          info of the logging event and adds the method name, besides file 
and line, if
+ *          available. In most cases the provided method contains the 
classname which is
+ *          ignored in every attempt to retrieve the method from the location 
info.
+ *          <p>
+ *              Currently supported compilers are those from Microsoft, GNU-C 
and Borland.
+ *          </p>
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>n</strong></p>
+ *          <p><strong>newline</strong></p>
+ *      </td>
+ *      <td>
+ *          Outputs the platform dependent line separator character or 
characters.
+ *          <p>
+ *              This conversion character offers practically the same 
performance as using
+ *              non-portable line separator strings such as "\n", or "\r\n". 
Thus, it is the
+ *              preferred way of specifying a line separator.
+ *          </p>
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>p</strong></p>
+ *          <p><strong>level</strong></p>
+ *      </td>
+ *      <td>Used to output the level of the logging event.</td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>r</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the number of milliseconds elapsed since the start 
of the
+ *          application until the creation of the logging event.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>t</strong></p>
+ *          <p><strong>thread</strong></p>
+ *      </td>
+ *      <td>Used to output the ID of the thread that generated the logging 
event.</td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>T</strong></p>
+ *          <p><strong>threadname</strong></p>
+ *      </td>
+ *      <td>Used to output the name of the thread that generated the logging 
event.  May not be available on all platforms.</td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>x</strong></p>
+ *          <p><strong>ndc</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the NDC (nested diagnostic context) associated with 
the thread that
+ *          generated the logging event.
+ *      </td>
+ *  </tr>
+ *  <tr>
+ *      <td align="center">
+ *          <p><strong>mdc[1-6]</strong></p>
+ *      </td>
+ *      <td>
+ *          Used to output the MDC (mapped diagnostic context) associated with 
the thread that
+ *          generated the logging event. The keys must be specified in the 
configuration for
+ *          the layout.  A maximum of 6 MDC keys are available to be 
output(mdc1, mdc2, ..., mdc6)
+ *      </td>
+ *  </tr>
+ * </table>
+ */
+class LOG4CXX_EXPORT FMTLayout : public Layout
+{
+    LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(FMTLayoutPrivate, m_priv)
+
+       public:
+               DECLARE_LOG4CXX_OBJECT(FMTLayout)
+               BEGIN_LOG4CXX_CAST_MAP()
+               LOG4CXX_CAST_ENTRY(FMTLayout)
+               LOG4CXX_CAST_ENTRY_CHAIN(Layout)
+               END_LOG4CXX_CAST_MAP()
+
+               FMTLayout();
+
+               FMTLayout(const LogString& pattern);
+
+               ~FMTLayout();
+
+
+               /**
+                * Set the <strong>ConversionPattern</strong> option. This is 
the string which
+                * controls formatting and consists of a mix of literal content 
and
+                * conversion specifiers.
+                */
+               void setConversionPattern(const LogString& conversionPattern);
+
+               /**
+                * Returns the value of the <strong>ConversionPattern</strong> 
option.
+                */
+               LogString getConversionPattern() const;
+
+               /**
+               Returns the log statement in a format consisting of the
+               <code>level</code>, followed by " - " and then the
+               <code>message</code>. For example, <pre> INFO - "A message"
+               </pre>
+
+               @return A byte array in SimpleLayout format.
+               */
+               void format(LogString& output,
+                       const spi::LoggingEventPtr& event,
+                       helpers::Pool& pool) const override;
+
+               bool ignoresThrowable() const override
+               {
+                       return true;
+               }
+
+               void activateOptions(helpers::Pool& /* p */) override;
+               void setOption(const LogString& /* option */,
+                       const LogString& /* value */) override;
+};
+LOG4CXX_PTR_DEF(FMTLayout);
+}  // namespace log4cxx
+
+
+#if defined(_MSC_VER)
+       #pragma warning ( pop )
+#endif
+
+#endif //LOG4CXX_FMT_LAYOUT_H
diff --git a/src/main/include/log4cxx/spi/loggingevent.h 
b/src/main/include/log4cxx/spi/loggingevent.h
index 55816aef..8641fcf6 100644
--- a/src/main/include/log4cxx/spi/loggingevent.h
+++ b/src/main/include/log4cxx/spi/loggingevent.h
@@ -24,6 +24,7 @@
 #include <log4cxx/mdc.h>
 #include <log4cxx/spi/location/locationinfo.h>
 #include <vector>
+#include <chrono>
 
 
 namespace log4cxx
@@ -107,6 +108,8 @@ class LOG4CXX_EXPORT LoggingEvent :
                 was created. */
                log4cxx_time_t getTimeStamp() const;
 
+               std::chrono::time_point<std::chrono::system_clock> 
getChronoTimeStamp() const;
+
                /* Return the file where this log statement was written. */
                const log4cxx::spi::LocationInfo& getLocationInformation() 
const;
 
diff --git a/src/test/cpp/CMakeLists.txt b/src/test/cpp/CMakeLists.txt
index 0e9cad93..74a35e5b 100644
--- a/src/test/cpp/CMakeLists.txt
+++ b/src/test/cpp/CMakeLists.txt
@@ -58,6 +58,9 @@ set(ALL_LOG4CXX_TESTS
     locationtest
     locationdisabledtest
 )
+if(${ENABLE_FMT_LAYOUT})
+    set(ALL_LOG4CXX_TESTS ${ALL_LOG4CXX_TESTS} fmttest)
+endif()
 foreach(fileName IN LISTS ALL_LOG4CXX_TESTS)
     add_executable(${fileName} "${fileName}.cpp")
 endforeach()
diff --git a/src/test/cpp/fmttest.cpp b/src/test/cpp/fmttest.cpp
new file mode 100644
index 00000000..2b496e63
--- /dev/null
+++ b/src/test/cpp/fmttest.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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 "logunit.h"
+#include "testchar.h"
+#include "util/compare.h"
+#include "util/transformer.h"
+#include "util/absolutedateandtimefilter.h"
+#include "util/iso8601filter.h"
+#include "util/absolutetimefilter.h"
+#include "util/relativetimefilter.h"
+#include "util/controlfilter.h"
+#include "util/threadfilter.h"
+#include "util/linenumberfilter.h"
+#include "util/filenamefilter.h"
+#include "vectorappender.h"
+#include <log4cxx/fmtlayout.h>
+#include <log4cxx/propertyconfigurator.h>
+#include <log4cxx/helpers/date.h>
+#include <log4cxx/spi/loggingevent.h>
+#include <iostream>
+#include <iomanip>
+
+#define REGEX_STR(x) x
+#define PAT0 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* - 
Message [0-9]\\{1,2\\}")
+#define PAT1 ISO8601_PAT REGEX_STR(" ") PAT0
+#define PAT2 ABSOLUTE_DATE_AND_TIME_PAT REGEX_STR(" ") PAT0
+#define PAT3 ABSOLUTE_TIME_PAT REGEX_STR(" ") PAT0
+#define PAT4 RELATIVE_TIME_PAT REGEX_STR(" ") PAT0
+#define PAT5 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* : 
Message [0-9]\\{1,2\\}")
+#define PAT6 REGEX_STR("\\[[0-9A-FXx]*]\\ (DEBUG|INFO |WARN |ERROR|FATAL) 
.*patternlayouttest.cpp\\([0-9]\\{1,4\\}\\): Message [0-9]\\{1,3\\}")
+#define PAT11a REGEX_STR("^(DEBUG|INFO |WARN |ERROR|FATAL) \\[[0-9A-FXx]*]\\ 
log4j.PatternLayoutTest: Message [0-9]\\{1,2\\}")
+#define PAT11b REGEX_STR("^(DEBUG|INFO |WARN |ERROR|FATAL) \\[[0-9A-FXx]*]\\ 
root: Message [0-9]\\{1,2\\}")
+#define PAT12 REGEX_STR("^\\[[0-9A-FXx]*]\\ (DEBUG|INFO |WARN |ERROR|FATAL) ")\
+       REGEX_STR(".*patternlayouttest.cpp([0-9]\\{1,4\\}): ")\
+       REGEX_STR("Message [0-9]\\{1,2\\}")
+#define PAT_MDC_1 REGEX_STR("")
+
+using namespace log4cxx;
+using namespace log4cxx::helpers;
+
+LOGUNIT_CLASS(FMTTestCase)
+{
+       LOGUNIT_TEST_SUITE(FMTTestCase);
+       LOGUNIT_TEST(test1);
+       LOGUNIT_TEST(test1_expanded);
+       LOGUNIT_TEST(test10);
+//     LOGUNIT_TEST(test_date);
+       LOGUNIT_TEST_SUITE_END();
+
+       LoggerPtr root;
+       LoggerPtr logger;
+
+public:
+       void setUp()
+       {
+               root = Logger::getRootLogger();
+               MDC::clear();
+               logger = 
Logger::getLogger(LOG4CXX_TEST_STR("java.org.apache.log4j.PatternLayoutTest"));
+       }
+
+       void tearDown()
+       {
+               MDC::clear();
+               auto rep = root->getLoggerRepository();
+
+               if (rep)
+               {
+                       rep->resetConfiguration();
+               }
+       }
+
+       void test1()
+       {
+               
PropertyConfigurator::configure(LOG4CXX_FILE("input/fmtLayout1.properties"));
+               common();
+               LOGUNIT_ASSERT(Compare::compare(TEMP, 
LOG4CXX_FILE("witness/patternLayout.1")));
+       }
+
+       void test1_expanded()
+       {
+               
PropertyConfigurator::configure(LOG4CXX_FILE("input/fmtLayout1_expanded.properties"));
+               common();
+               LOGUNIT_ASSERT(Compare::compare(TEMP, 
LOG4CXX_FILE("witness/patternLayout.1")));
+       }
+
+       void test10()
+       {
+               
PropertyConfigurator::configure(LOG4CXX_FILE("input/fmtLayout10.properties"));
+               common();
+
+               ControlFilter filter1;
+               filter1 << PAT6;
+               ThreadFilter filter2;
+               LineNumberFilter filter3;
+               FilenameFilter filenameFilter(__FILE__, 
"patternlayouttest.cpp");
+
+
+               std::vector<Filter*> filters;
+               filters.push_back(&filenameFilter);
+               filters.push_back(&filter1);
+               filters.push_back(&filter2);
+               filters.push_back(&filter3);
+
+
+               try
+               {
+                       Transformer::transform(TEMP, FILTERED, filters);
+               }
+               catch (UnexpectedFormatException& e)
+               {
+                       std::cout << "UnexpectedFormatException :" << e.what() 
<< std::endl;
+                       throw;
+               }
+
+               LOGUNIT_ASSERT(Compare::compare(FILTERED, 
LOG4CXX_FILE("witness/patternLayout.10")));
+       }
+
+       void test_date(){
+               std::tm tm = {};
+               std::stringstream ss("2013-04-11 08:35:34");
+               ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
+               auto tp = 
std::chrono::system_clock::from_time_t(std::mktime(&tm));
+               uint64_t micros = 
std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch()).count();
+
+               log4cxx::helpers::Date::setGetCurrentTimeFunction([micros](){
+                       return micros;
+               });
+
+               log4cxx::spi::LoggingEventPtr logEvt = 
std::make_shared<log4cxx::spi::LoggingEvent>( "foo",
+                                                                               
                                                                                
                         Level::getInfo(),
+                                                                               
                                                                                
                         "A Message",
+                                                                               
                                                                                
                         log4cxx::spi::LocationInfo::getLocationUnavailable());
+               FMTLayout layout("{d:%Y-%m-%d %H:%M:%S} {message}" );
+               LogString output;
+               log4cxx::helpers::Pool pool;
+               layout.format( output, logEvt, pool);
+
+               log4cxx::helpers::Date::setGetCurrentTimeFunction(nullptr);
+
+               LOGUNIT_ASSERT_EQUAL("2013-04-11 09:35:34 A Message", output);
+       }
+
+       std::string createMessage(Pool & pool, int i)
+       {
+               std::string msg("Message ");
+               msg.append(pool.itoa(i));
+               return msg;
+       }
+
+       void common()
+       {
+               int i = -1;
+
+               Pool pool;
+
+
+               LOG4CXX_DEBUG(logger, createMessage(pool, ++i));
+               LOG4CXX_DEBUG(root, createMessage(pool, i));
+
+               LOG4CXX_INFO(logger, createMessage(pool, ++i));
+               LOG4CXX_INFO(root, createMessage(pool, i));
+
+               LOG4CXX_WARN(logger, createMessage(pool, ++i));
+               LOG4CXX_WARN(root, createMessage(pool, i));
+
+               LOG4CXX_ERROR(logger, createMessage(pool, ++i));
+               LOG4CXX_ERROR(root, createMessage(pool, i));
+
+               LOG4CXX_FATAL(logger, createMessage(pool, ++i));
+               LOG4CXX_FATAL(root, createMessage(pool, i));
+       }
+
+       private:
+               static const LogString FILTERED;
+               static const LogString TEMP;
+
+};
+
+const LogString FMTTestCase::TEMP(LOG4CXX_STR("output/fmtlayout"));
+const LogString FMTTestCase::FILTERED(LOG4CXX_STR("output/fmtlayoutfiltered"));
+
+
+LOGUNIT_TEST_SUITE_REGISTRATION(FMTTestCase);
diff --git a/src/test/resources/input/fmtLayout1.properties 
b/src/test/resources/input/fmtLayout1.properties
new file mode 100644
index 00000000..ecd14df4
--- /dev/null
+++ b/src/test/resources/input/fmtLayout1.properties
@@ -0,0 +1,21 @@
+# 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.
+#
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.file=output/fmtlayout
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.FMTLayout
+log4j.appender.testAppender.layout.ConversionPattern={p:<5} - {m}{n}
diff --git a/src/test/resources/input/fmtLayout10.properties 
b/src/test/resources/input/fmtLayout10.properties
new file mode 100644
index 00000000..3608cc74
--- /dev/null
+++ b/src/test/resources/input/fmtLayout10.properties
@@ -0,0 +1,21 @@
+# 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.
+#
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/fmtlayout
+log4j.appender.testAppender.Append= false 
+log4j.appender.testAppender.layout=org.apache.log4j.FMTLayout 
+log4j.appender.testAppender.layout.ConversionPattern=[{t}] {p:<5} {l}: {m}{n}
diff --git a/src/test/resources/input/fmtLayout1_expanded.properties 
b/src/test/resources/input/fmtLayout1_expanded.properties
new file mode 100644
index 00000000..70c32d8e
--- /dev/null
+++ b/src/test/resources/input/fmtLayout1_expanded.properties
@@ -0,0 +1,21 @@
+# 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.
+#
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.file=output/fmtlayout
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.FMTLayout
+log4j.appender.testAppender.layout.ConversionPattern={level:<5} - 
{message}{newline}

Reply via email to