From 25f900f8e839b9d4a9f04690c32e39f6da93c71f Mon Sep 17 00:00:00 2001 From: Stephen Webb Date: Wed, 9 Aug 2023 13:44:12 +1000 Subject: [PATCH 1/2] Replace multi-process question with an answer (#248) --- src/main/cpp/outputstreamwriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/cpp/outputstreamwriter.cpp b/src/main/cpp/outputstreamwriter.cpp index 90d199319..4ddf93c93 100644 --- a/src/main/cpp/outputstreamwriter.cpp +++ b/src/main/cpp/outputstreamwriter.cpp @@ -81,7 +81,7 @@ void OutputStreamWriter::write(const LogString& str, Pool& p) if (str.length() > 0) { #ifdef LOG4CXX_MULTI_PROCESS - // Why does this need to happen for multiproces?? why?? + // Ensure the logging event is a single write system call to keep events from each process separate size_t bufSize = str.length() * 2; char* rawbuf = new char[bufSize]; ByteBuffer buf(rawbuf, (size_t) bufSize); From 78d8e82a375940e82967923117bd026582d1dda8 Mon Sep 17 00:00:00 2001 From: Stephen Webb Date: Wed, 9 Aug 2023 14:48:13 +1000 Subject: [PATCH 2/2] Allow a QString in a LOG4CXX_ macro when 'Qt support/integration' is ON (#247) * Update Qt support documentation * Change configuration examples to links * Fix NDC example file name * Improve example code --- .github/workflows/log4cxx-ubuntu.yml | 19 ++++- src/cmake/win32_target_environment_path.cmake | 3 + src/examples/cpp/CMakeLists.txt | 9 ++- src/examples/cpp/MyApp-qt.cpp | 20 +++++ src/examples/cpp/auto-configured.cpp | 4 +- src/examples/cpp/com/foo/bar-qt.cpp | 10 +++ src/examples/cpp/com/foo/config-qt.cpp | 71 ++++++++++++++++++ src/examples/cpp/com/foo/config-qt.h | 21 ++++++ .../cpp/{trivial.cpp => ndc-example.cpp} | 0 src/main/cpp-qt/configuration.cpp | 9 ++- src/main/cpp/domconfigurator.cpp | 6 +- src/main/cpp/file.cpp | 2 +- src/main/cpp/hexdump.cpp | 10 ++- src/main/cpp/level.cpp | 2 +- src/main/cpp/messagebuffer.cpp | 4 +- src/main/cpp/timebasedrollingpolicy.cpp | 4 + src/main/cpp/transcoder.cpp | 2 +- src/main/include/log4cxx-qt/logger.h | 22 ++++++ src/main/include/log4cxx-qt/messagebuffer.h | 74 +++++++++++++++++++ src/main/include/log4cxx-qt/transcoder.h | 47 ++++++++++++ src/main/include/log4cxx/file.h | 2 +- src/main/include/log4cxx/level.h | 2 +- .../include/log4cxx/xml/domconfigurator.h | 6 +- src/site/markdown/development/build-cmake.md | 1 + src/site/markdown/example-programs.md | 13 +++- src/site/markdown/filters/filters.md | 4 +- src/site/markdown/qt-support.md | 27 ++++++- 27 files changed, 362 insertions(+), 32 deletions(-) create mode 100644 src/examples/cpp/MyApp-qt.cpp create mode 100644 src/examples/cpp/com/foo/bar-qt.cpp create mode 100644 src/examples/cpp/com/foo/config-qt.cpp create mode 100644 src/examples/cpp/com/foo/config-qt.h rename src/examples/cpp/{trivial.cpp => ndc-example.cpp} (100%) create mode 100644 src/main/include/log4cxx-qt/logger.h create mode 100644 src/main/include/log4cxx-qt/messagebuffer.h create mode 100644 src/main/include/log4cxx-qt/transcoder.h diff --git a/.github/workflows/log4cxx-ubuntu.yml b/.github/workflows/log4cxx-ubuntu.yml index ee8ac96c4..45b2fbfc7 100644 --- a/.github/workflows/log4cxx-ubuntu.yml +++ b/.github/workflows/log4cxx-ubuntu.yml @@ -30,18 +30,30 @@ jobs: os: ubuntu-20.04 cxx: g++ cc: gcc + fmt: OFF + qt: ON + odbc: OFF - name: ubuntu20-clang os: ubuntu-20.04 cxx: clang++ cc: clang + fmt: ON + qt: OFF + odbc: ON - name: ubuntu22-gcc os: ubuntu-22.04 cxx: g++ cc: gcc + fmt: OFF + qt: OFF + odbc: OFF - name: ubuntu22-clang os: ubuntu-22.04 cxx: clang++ cc: clang + fmt: ON + qt: OFF + odbc: OFF steps: - uses: actions/checkout@v3 @@ -51,14 +63,17 @@ jobs: - name: 'Configure Dependencies' run: | sudo apt-get update - sudo apt-get install -y libapr1-dev libaprutil1-dev libfmt-dev unixodbc-dev + sudo apt-get install -y libapr1-dev libaprutil1-dev + if [ ${{ matrix.fmt }} == ON ]; then sudo apt-get install -y libfmt-dev; fi + if [ ${{ matrix.odbc }} == ON ]; then sudo apt-get install -y unixodbc-dev; fi + if [ ${{ matrix.qt }} == ON ]; then sudo apt-get install -y qtbase5-dev; fi - name: 'run cmake - posix' run: | cd main mkdir build cd build - cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DLOG4CXX_ENABLE_ODBC=ON .. + cmake -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DCMAKE_C_COMPILER=${{ matrix.cc }} -DLOG4CXX_ENABLE_ODBC=${{ matrix.odbc }} -DLOG4CXX_QT_SUPPORT=${{ matrix.qt }} .. cmake --build . - name: run unit tests diff --git a/src/cmake/win32_target_environment_path.cmake b/src/cmake/win32_target_environment_path.cmake index ea122c892..3de5bc941 100644 --- a/src/cmake/win32_target_environment_path.cmake +++ b/src/cmake/win32_target_environment_path.cmake @@ -8,6 +8,9 @@ function(get_target_environment_path varName) set(EXPAT_DLL_DIR "${EXPAT_LIB_DIR}/../bin") set(LOG4CXX_DLL_DIR "$>;") set(PATH_FOR_TESTS ${CMAKE_PROGRAM_PATH};${APR_DLL_DIR};${APR_UTIL_DLL_DIR};${LOG4CXX_DLL_DIR};${EXPAT_DLL_DIR}\;) + if(LOG4CXX_QT_SUPPORT) + list(APPEND PATH_FOR_TESTS "$>\;") + endif(LOG4CXX_QT_SUPPORT) list(REMOVE_DUPLICATES PATH_FOR_TESTS) # Note: we need to include the APR DLLs on our path so that the tests will run. diff --git a/src/examples/cpp/CMakeLists.txt b/src/examples/cpp/CMakeLists.txt index f1cf57d32..4508a6ec3 100644 --- a/src/examples/cpp/CMakeLists.txt +++ b/src/examples/cpp/CMakeLists.txt @@ -15,7 +15,10 @@ # limitations under the License. # -set(ALL_LOG4CXX_EXAMPLES auto-configured console delayedloop stream trivial custom-appender MyApp1 MyApp2) +set(ALL_LOG4CXX_EXAMPLES auto-configured console delayedloop stream ndc-example custom-appender MyApp1 MyApp2) +if(LOG4CXX_QT_SUPPORT) + list(APPEND ALL_LOG4CXX_EXAMPLES MyApp-qt) +endif(LOG4CXX_QT_SUPPORT) if( WIN32 ) include(win32_target_environment_path) get_target_environment_path(ESCAPED_PATH) @@ -34,6 +37,10 @@ foreach(exampleName IN LISTS ALL_LOG4CXX_EXAMPLES) if(${exampleName} STREQUAL MyApp2) target_sources(${PROGRAM_NAME} PRIVATE com/foo/config2.cpp com/foo/bar.cpp) endif() + if(${exampleName} STREQUAL MyApp-qt) + target_sources(${PROGRAM_NAME} PRIVATE com/foo/config-qt.cpp com/foo/bar-qt.cpp) + target_link_libraries(${PROGRAM_NAME} PRIVATE log4cxx-qt) + endif() if(${exampleName} STREQUAL auto-configured) target_sources(${PROGRAM_NAME} PRIVATE com/foo/config3.cpp ) endif() diff --git a/src/examples/cpp/MyApp-qt.cpp b/src/examples/cpp/MyApp-qt.cpp new file mode 100644 index 000000000..9040c5eb7 --- /dev/null +++ b/src/examples/cpp/MyApp-qt.cpp @@ -0,0 +1,20 @@ +#include +#include "com/foo/config-qt.h" +#include "com/foo/bar.h" + +int main(int argc, char **argv) { + int result = EXIT_SUCCESS; + QCoreApplication app(argc, argv); + com::foo::ConfigureLogging(); + try { + auto logger = com::foo::getLogger("MyApp"); + LOG4CXX_INFO(logger, QString("Message %1").arg(1)); + com::foo::Bar bar; + bar.doIt(); + LOG4CXX_INFO(logger, QString("Message %1").arg(2)); + } + catch(std::exception&) { + result = EXIT_FAILURE; + } + return result; +} diff --git a/src/examples/cpp/auto-configured.cpp b/src/examples/cpp/auto-configured.cpp index fb0a9a5d1..1933f8f7b 100644 --- a/src/examples/cpp/auto-configured.cpp +++ b/src/examples/cpp/auto-configured.cpp @@ -16,9 +16,9 @@ */ #include "com/foo/config.h" -extern auto rootLogger = com::foo::getLogger(); +auto rootLogger = com::foo::getLogger(); -static struct ExampleStaticData { +struct ExampleStaticData { ExampleStaticData() { LOG4CXX_DEBUG(rootLogger, "static initializer message"); } diff --git a/src/examples/cpp/com/foo/bar-qt.cpp b/src/examples/cpp/com/foo/bar-qt.cpp new file mode 100644 index 000000000..34d446818 --- /dev/null +++ b/src/examples/cpp/com/foo/bar-qt.cpp @@ -0,0 +1,10 @@ +#include "com/foo/bar.h" +#include "com/foo/config-qt.h" + +using namespace com::foo; + +LoggerPtr Bar::m_logger(getLogger("com.foo.bar")); + +void Bar::doIt() { + LOG4CXX_DEBUG(m_logger, QString("Did it again!") << QString(" - again!")); +} diff --git a/src/examples/cpp/com/foo/config-qt.cpp b/src/examples/cpp/com/foo/config-qt.cpp new file mode 100644 index 000000000..648890874 --- /dev/null +++ b/src/examples/cpp/com/foo/config-qt.cpp @@ -0,0 +1,71 @@ +/* + * 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 "config-qt.h" +#include +#include +#include +#include +#include +#include +#include + +namespace com { namespace foo { + +// Provide the name of the configuration file to Log4cxx. +// Reload the configuration on a QFileSystemWatcher::fileChanged event. +void ConfigureLogging() { + static struct log4cxx_finalizer { + ~log4cxx_finalizer() { + log4cxx::LogManager::shutdown(); + } + } finaliser; + QFileInfo app{QCoreApplication::applicationFilePath()}; + QString basename{app.baseName()}; + QVector paths = + { QString(".") + , app.absoluteDir().absolutePath() + }; + QVector names = + { QString(basename + ".xml") + , QString(basename + ".properties") + , QString("MyApp.properties") + , QString("log4cxx.xml") + , QString("log4cxx.properties") + , QString("log4j.xml") + , QString("log4j.properties") + }; +#if defined(_DEBUG) + log4cxx::helpers::LogLog::setInternalDebugging(true); +#endif + log4cxx::qt::Configuration::configureFromFileAndWatch(paths, names); +} + +// Retrieve the \c name logger pointer. +auto getLogger(const QString& name) -> LoggerPtr { + return name.isEmpty() + ? log4cxx::LogManager::getRootLogger() + : log4cxx::LogManager::getLogger(name.toStdString()); +} + +// Retrieve the \c name logger pointer. +auto getLogger(const char* name) -> LoggerPtr { + return name + ? log4cxx::LogManager::getLogger(name) + : log4cxx::LogManager::getRootLogger(); +} + +} } // namespace com::foo diff --git a/src/examples/cpp/com/foo/config-qt.h b/src/examples/cpp/com/foo/config-qt.h new file mode 100644 index 000000000..238199ff0 --- /dev/null +++ b/src/examples/cpp/com/foo/config-qt.h @@ -0,0 +1,21 @@ +#ifndef COM_FOO_CONFIG_QT_H_ +#define COM_FOO_CONFIG_QT_H_ +#include + +/// Methods specific to foo.com +namespace com { namespace foo { + +// Provide the name of the configuration file to Log4cxx. +void ConfigureLogging(); + +/// The logger pointer we use +using LoggerPtr = log4cxx::LoggerPtr; + +/// Retrieve the \c name logger pointer. +extern auto getLogger(const QString& name) -> LoggerPtr; + +/// Retrieve the \c name logger pointer. +extern auto getLogger(const char* name = NULL) -> LoggerPtr; + +} } // namespace com::foo +#endif // COM_FOO_CONFIG_QT_H_ diff --git a/src/examples/cpp/trivial.cpp b/src/examples/cpp/ndc-example.cpp similarity index 100% rename from src/examples/cpp/trivial.cpp rename to src/examples/cpp/ndc-example.cpp diff --git a/src/main/cpp-qt/configuration.cpp b/src/main/cpp-qt/configuration.cpp index ef8589feb..17654b71b 100644 --- a/src/main/cpp-qt/configuration.cpp +++ b/src/main/cpp-qt/configuration.cpp @@ -15,6 +15,7 @@ * limitations under the License. */ #include +#include #include #include #include @@ -107,16 +108,16 @@ Configuration::configureFromFileAndWatch(const QVector& directories, QString canidate_str = dir + "/" + fname; QFile candidate(canidate_str); - QString debugMsg = LOG4CXX_STR("Checking file "); - debugMsg.append(canidate_str); - LogLog::debug(debugMsg.toStdString()); + LOG4CXX_DECODE_QSTRING(msg, "Checking file " + canidate_str); + LogLog::debug(msg); if (candidate.exists()) { log4cxx::spi::ConfigurationStatus configStatus = tryLoadFile(canidate_str); if( configStatus == log4cxx::spi::ConfigurationStatus::Configured ){ return {configStatus, canidate_str}; } - LogLog::debug("Unable to load file: trying next"); + LOG4CXX_DECODE_QSTRING(failmsg, "Unable to load " + canidate_str + ": trying next"); + LogLog::debug(failmsg); } } } diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp index 4d3b9e03a..0fd1805e3 100644 --- a/src/main/cpp/domconfigurator.cpp +++ b/src/main/cpp/domconfigurator.cpp @@ -865,7 +865,7 @@ spi::ConfigurationStatus DOMConfigurator::configure(const std::wstring& filename } #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR spi::ConfigurationStatus DOMConfigurator::configure(const std::basic_string& filename) { File file(filename); @@ -894,7 +894,7 @@ spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::wstring& } #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::basic_string& filename) { return configureAndWatch(filename, FileWatchdog::DEFAULT_DELAY); @@ -958,7 +958,7 @@ spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::wstring& } #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR spi::ConfigurationStatus DOMConfigurator::configureAndWatch(const std::basic_string& filename, long delay) { File file(filename); diff --git a/src/main/cpp/file.cpp b/src/main/cpp/file.cpp index 56fe76823..88a04a9b1 100644 --- a/src/main/cpp/file.cpp +++ b/src/main/cpp/file.cpp @@ -95,7 +95,7 @@ File::File(const wchar_t* name1) } #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR File::File(const std::basic_string& name1) : m_priv(std::make_unique(decodeLS(name1))) { diff --git a/src/main/cpp/hexdump.cpp b/src/main/cpp/hexdump.cpp index caa45ac3f..163cda79b 100644 --- a/src/main/cpp/hexdump.cpp +++ b/src/main/cpp/hexdump.cpp @@ -14,6 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#include +/* Prevent error C2491: 'std::numpunct<_Elem>::id': definition of dllimport static data member not allowed */ +#if defined(_MSC_VER) && (LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR) +#define __FORCE_INSTANCE +#endif #include #include #include @@ -32,8 +38,8 @@ LogString log4cxx::hexdump(const void* bytes, uint32_t len, HexdumpFlags flags){ const wchar_t fill_char = L'0'; const wchar_t space_fill_char = L' '; #else - const char fill_char = '0'; - const char space_fill_char = ' '; + const logchar fill_char = '0'; + const logchar space_fill_char = ' '; #endif if(flags & HexdumpFlags::AddStartingNewline){ diff --git a/src/main/cpp/level.cpp b/src/main/cpp/level.cpp index 7d794c511..8fb6af198 100644 --- a/src/main/cpp/level.cpp +++ b/src/main/cpp/level.cpp @@ -173,7 +173,7 @@ void Level::toString(std::wstring& dst) const #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR LevelPtr Level::toLevel(const std::basic_string& sArg) { return toLevel(sArg, Level::getDebug()); diff --git a/src/main/cpp/messagebuffer.cpp b/src/main/cpp/messagebuffer.cpp index 6972dc703..4a546a7c6 100644 --- a/src/main/cpp/messagebuffer.cpp +++ b/src/main/cpp/messagebuffer.cpp @@ -17,7 +17,7 @@ #include /* Prevent error C2491: 'std::numpunct<_Elem>::id': definition of dllimport static data member not allowed */ -#if defined(_MSC_VER) && LOG4CXX_UNICHAR_API +#if defined(_MSC_VER) && (LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR) #define __FORCE_INSTANCE #endif #include @@ -587,7 +587,7 @@ const std::basic_string& MessageBuffer::str(std::basic_ostream #endif // LOG4CXX_WCHAR_T_API -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR struct UniCharMessageBuffer::UniCharMessageBufferPrivate : public StringOrStream {}; UniCharMessageBuffer::UniCharMessageBuffer() : diff --git a/src/main/cpp/timebasedrollingpolicy.cpp b/src/main/cpp/timebasedrollingpolicy.cpp index 26798677c..f304ca9bd 100644 --- a/src/main/cpp/timebasedrollingpolicy.cpp +++ b/src/main/cpp/timebasedrollingpolicy.cpp @@ -171,7 +171,11 @@ const std::string TimeBasedRollingPolicy::createFile(const std::string& fileName if (stat == APR_SUCCESS) { +#ifdef WIN32 + snprintf(szUid, MAX_FILE_LEN, "%p", uid); +#else snprintf(szUid, MAX_FILE_LEN, "%u", uid); +#endif } log4cxx::filesystem::path path(fileName); diff --git a/src/main/cpp/transcoder.cpp b/src/main/cpp/transcoder.cpp index 6cbe9f267..7ad9ad270 100644 --- a/src/main/cpp/transcoder.cpp +++ b/src/main/cpp/transcoder.cpp @@ -576,7 +576,7 @@ void Transcoder::encode(unsigned int sv, std::wstring& dst) -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR void Transcoder::decode(const std::basic_string& src, LogString& dst) { #if LOG4CXX_LOGCHAR_IS_UNICHAR diff --git a/src/main/include/log4cxx-qt/logger.h b/src/main/include/log4cxx-qt/logger.h new file mode 100644 index 000000000..f79d7fdbb --- /dev/null +++ b/src/main/include/log4cxx-qt/logger.h @@ -0,0 +1,22 @@ +/* + * 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_QT_LOGGER_H +#define _LOG4CXX_QT_LOGGER_H +#include +#include +#endif // _LOG4CXX_QT_LOGGER_H diff --git a/src/main/include/log4cxx-qt/messagebuffer.h b/src/main/include/log4cxx-qt/messagebuffer.h new file mode 100644 index 000000000..bd4c9b767 --- /dev/null +++ b/src/main/include/log4cxx-qt/messagebuffer.h @@ -0,0 +1,74 @@ +/* + * 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_QT_MESSAGE_BUFFER_H +#define _LOG4CXX_QT_MESSAGE_BUFFER_H +#include + +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR + inline log4cxx::helpers::UniCharMessageBuffer& +operator<<(log4cxx::helpers::UniCharMessageBuffer& mb, const QString& msg) +{ + return mb << msg.utf16(); +} + +#if LOG4CXX_WCHAR_T_API + inline log4cxx::helpers::WideMessageBuffer& +operator<<(log4cxx::helpers::WideMessageBuffer& mb, const QString& msg) +{ + return mb << msg.toStdWString(); +} + + inline log4cxx::helpers::WideMessageBuffer& +operator<<(log4cxx::helpers::MessageBuffer& mb, const QString& msg) +{ + return mb << msg.toStdWString(); +} +#else // !LOG4CXX_WCHAR_T_API + inline log4cxx::helpers::UniCharMessageBuffer& +operator<<(log4cxx::helpers::MessageBuffer& mb, const QString& msg) +{ + return mb << msg.utf16(); +} +#endif // !LOG4CXX_WCHAR_T_API + +#else // !(LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR) + +#if LOG4CXX_WCHAR_T_API + inline log4cxx::helpers::WideMessageBuffer& +operator<<(log4cxx::helpers::WideMessageBuffer& mb, const QString& msg) +{ + return mb << msg.toStdWString(); +} + + inline log4cxx::helpers::WideMessageBuffer& +operator<<(log4cxx::helpers::MessageBuffer& mb, const QString& msg) +{ + return mb << msg.toStdWString(); +} +#else // !LOG4CXX_WCHAR_T_API + inline log4cxx::helpers::CharMessageBuffer& +operator<<(log4cxx::helpers::CharMessageBuffer& mb, const QString& msg) +{ + LOG4CXX_DECODE_QSTRING(tmp, msg); + return mb << tmp; +} +#endif // !LOG4CXX_WCHAR_T_API + +#endif // !(LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR) + +#endif // _LOG4CXX_QT_MESSAGE_BUFFER_H diff --git a/src/main/include/log4cxx-qt/transcoder.h b/src/main/include/log4cxx-qt/transcoder.h new file mode 100644 index 000000000..71002a2c1 --- /dev/null +++ b/src/main/include/log4cxx-qt/transcoder.h @@ -0,0 +1,47 @@ +/* + * 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_QT_TRANSCODER_H +#define _LOG4CXX_QT_TRANSCODER_H +#include +#include + +#if LOG4CXX_LOGCHAR_IS_UTF8 +#define LOG4CXX_DECODE_QSTRING(var, src) \ + log4cxx::LogString var = (src).toStdString() + +#define LOG4CXX_ENCODE_QSTRING(var, src) \ + QString var = QString::fromStdString(src) +#endif // LOG4CXX_LOGCHAR_IS_UTF8 + +#if LOG4CXX_LOGCHAR_IS_WCHAR +#define LOG4CXX_DECODE_QSTRING(var, src) \ + log4cxx::LogString var = (src).toStdWString() + +#define LOG4CXX_ENCODE_QSTRING(var, src) \ + QString var = QString::fromStdWString(src) +#endif // LOG4CXX_LOGCHAR_IS_WCHAR + +#if LOG4CXX_LOGCHAR_IS_UNICHAR +#define LOG4CXX_DECODE_QSTRING(var, src) \ + log4cxx::LogString var = (src).utf16() + +#define LOG4CXX_ENCODE_QSTRING(var, src) \ + QString var = QString::fromUtf16((char16_t*)src.c_str()) +#endif // LOG4CXX_LOGCHAR_IS_UNICHAR + +#endif // _LOG4CXX_QT_TRANSCODER_H diff --git a/src/main/include/log4cxx/file.h b/src/main/include/log4cxx/file.h index cc669eaf0..7bd9592a8 100644 --- a/src/main/include/log4cxx/file.h +++ b/src/main/include/log4cxx/file.h @@ -66,7 +66,7 @@ class LOG4CXX_EXPORT File */ File(const std::wstring& path); #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR /** * Construct a new instance. Use setPath to specify path using a LogString. * @param path file path. diff --git a/src/main/include/log4cxx/level.h b/src/main/include/log4cxx/level.h index 8644b81b2..b0b98d5be 100644 --- a/src/main/include/log4cxx/level.h +++ b/src/main/include/log4cxx/level.h @@ -126,7 +126,7 @@ class LOG4CXX_EXPORT Level : public helpers::Object */ void toString(std::wstring& name) const; #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR /** Convert the string passed as argument to a level. If the conversion fails, then this method returns DEBUG. diff --git a/src/main/include/log4cxx/xml/domconfigurator.h b/src/main/include/log4cxx/xml/domconfigurator.h index 9f49c69c3..026fea953 100644 --- a/src/main/include/log4cxx/xml/domconfigurator.h +++ b/src/main/include/log4cxx/xml/domconfigurator.h @@ -221,7 +221,7 @@ class LOG4CXX_EXPORT DOMConfigurator : #if LOG4CXX_WCHAR_T_API static spi::ConfigurationStatus configure(const std::wstring& filename); #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR static spi::ConfigurationStatus configure(const std::basic_string& filename); #endif #if LOG4CXX_CFSTRING_API @@ -237,7 +237,7 @@ class LOG4CXX_EXPORT DOMConfigurator : #if LOG4CXX_WCHAR_T_API static spi::ConfigurationStatus configureAndWatch(const std::wstring& configFilename); #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR static spi::ConfigurationStatus configureAndWatch(const std::basic_string& configFilename); #endif #if LOG4CXX_CFSTRING_API @@ -260,7 +260,7 @@ class LOG4CXX_EXPORT DOMConfigurator : static spi::ConfigurationStatus configureAndWatch(const std::wstring& configFilename, long delay); #endif -#if LOG4CXX_UNICHAR_API +#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR static spi::ConfigurationStatus configureAndWatch(const std::basic_string& configFilename, long delay); #endif diff --git a/src/site/markdown/development/build-cmake.md b/src/site/markdown/development/build-cmake.md index 242430b90..e8601183d 100644 --- a/src/site/markdown/development/build-cmake.md +++ b/src/site/markdown/development/build-cmake.md @@ -71,6 +71,7 @@ $ cmake --build buildtrees/Log4cxx --target install --config Release | -DAPR_STATIC=yes | Link to the APR static library. By default, the Log4cxx shared library is linked to the APR shared library. If BUILD_SHARED_LIBS=off, the static APR library is always used. | |-DLOG4CXX_TEST_PROGRAM_PATH=path| An extra path to prepend to the PATH for test programs. Log4cxx requires zip, sed, and grep on the PATH in order for the tests to work properly. | | -DPREFER_BOOST=on | Prefer the Boost version of dependent libraries over standard library | +| -DLOG4CXX_QT_SUPPORT=ON | Enable QString API and log4cxx::qt namespace methods, requires QtCore, choice of ON, OFF (default). | ## A note on C++ version and Boost diff --git a/src/site/markdown/example-programs.md b/src/site/markdown/example-programs.md index dfbbb83ef..72992d80c 100644 --- a/src/site/markdown/example-programs.md +++ b/src/site/markdown/example-programs.md @@ -72,15 +72,14 @@ The *com::foo::Bar* class is defined in header file *com/foo/bar.h*. The *com::foo::Bar* class is implemented in the file *com/foo/bar.cpp*. \include com/foo/bar.cpp -The header file *com/foo/config.h* defines the com::foo::getLogger() function +The header file \ref com/foo/config.h defines the com::foo::getLogger() function and a *LoggerPtr* type for convenience. -\include com/foo/config.h -The file *com/foo/config.cpp* which implements the com::foo::getLogger() function + +The file \ref com/foo/config1.cpp implements the com::foo::getLogger() function defines *initAndShutdown* as a *static struct* so its constructor is invoked on the first call to the com::foo::getLogger() function and its destructor is automatically called during application exit. -\include com/foo/config1.cpp The invocation of the [BasicConfigurator::configure](@ref log4cxx.BasicConfigurator.configure) @@ -189,5 +188,11 @@ forwarded logging events to a remote Log4cxx server, which would log according to local server policy, for example by forwarding the log event to a second Log4cxx server. +\example com/foo/config.h +This header file is for encapsulating Log4cxx configuration. + +\example com/foo/config1.cpp +This file is a simplified example of encapsulated Log4cxx configuration. + \example com/foo/config3.cpp This file is an example of how to use the current module name to select the Log4cxx configuration file. diff --git a/src/site/markdown/filters/filters.md b/src/site/markdown/filters/filters.md index 4f4bbbb24..4e3ab354f 100644 --- a/src/site/markdown/filters/filters.md +++ b/src/site/markdown/filters/filters.md @@ -28,7 +28,7 @@ To uniquely stamp each request to relate it to a particular source, you can push contextual information into the *Nested Diagnostic Context* (NDC) using the *log4cxx::NDC* class or the *Mapped Diagnostic Context* provided by *log4cxx::MDC* class. -For an example using log4cxx::NDC refer to \ref trivial.cpp. +For an example using log4cxx::NDC refer to \ref ndc-example.cpp. The NDC is managed per thread as a *stack* of contextual information. When the layout specifies that the NDC is to be included, @@ -93,5 +93,5 @@ The following pages have information on specific filters: * @subpage map-filter * @subpage location-info-filter -\example trivial.cpp +\example ndc-example.cpp This example shows how to add a context string to each logging message using the NDC. diff --git a/src/site/markdown/qt-support.md b/src/site/markdown/qt-support.md index a324a2612..7e0bb13eb 100644 --- a/src/site/markdown/qt-support.md +++ b/src/site/markdown/qt-support.md @@ -26,8 +26,14 @@ may use the `QDebug` classes. By default, this will print to stderr, thus bypassing the logger entirely. In order to have these messages routed to Log4cxx, a message handler for Qt must be installed. -Log4cxx provides a separate library, log4cxx-qt, which contains useful -utilities for working with Qt. +Log4cxx provides a cmake build option `LOG4CXX_QT_SUPPORT=ON` +which adds the log4cxx::qt namespace methods +for directing Qt messages to Log4cxx and +using the Qt event loop to process a configuration file change. +Use the target `log4cxx-qt` instead of `log4cxx` +in your `target_link_libraries` cmake directive. +Also, including `log4cxx-qt/logger.h` allows you to use QString values +in the LOG4CXX_WARN, LOG4CXX_INFO, LOG4CXX_DEBUG etc. macros. To install a message handler that will route the Qt logging messages through Log4cxx, include the messagehandler.h and call @@ -43,3 +49,20 @@ qInstallMessageHandler( log4cxx::qt::messageHandler ); Note that by default, this message handler also calls `abort` upon a fatal message. + +For how to use the Qt event loop to monitor the configuration file, +see the \ref com/foo/config-qt.h and \ref com/foo/config-qt.cpp example files. + +Note that when using the above technique +you *must* configure Log4cxx after creating your QCoreApplication instance +(see the \ref MyApp-qt.cpp file for an example of this). + +\example MyApp-qt.cpp +This file is an example of how to configure Log4cxx in a Qt application. + +\example com/foo/config-qt.h +This header file is for Log4cxx configuration in a Qt application. + +\example com/foo/config-qt.cpp +This file is an example of how to use the Qt event loop to monitor the Log4cxx configuration file. +