This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
The following commit(s) were added to refs/heads/master by this push:
new 7ed16a26 Support user provided Socket implementation in the next ABI
version (#663)
7ed16a26 is described below
commit 7ed16a2697e96466bc6ca88f18f9b1f60a9f55a0
Author: Stephen Webb <[email protected]>
AuthorDate: Fri May 15 11:13:17 2026 +1000
Support user provided Socket implementation in the next ABI version (#663)
* Introduce enhanced internal logging support
* Only expand LOGLOG_DEBUG and LOGLOG_TRACE macros when building with
LOG4CXX_TEST_ONLY_BUILD
* Test the next ABI on MacOS
* Exceptions must be exported using the new ABI
---
.github/workflows/log4cxx-macos.yml | 14 +-
.github/workflows/log4cxx-windows.yml | 9 +-
src/main/cpp/CMakeLists.txt | 5 +-
src/main/cpp/aprsocket.cpp | 34 +-
src/main/cpp/exception.cpp | 4 +
src/main/cpp/loglog.cpp | 43 +-
src/main/cpp/socket.cpp | 42 +-
src/main/cpp/socketappenderskeleton.cpp | 22 +-
src/main/cpp/stringtokenizer.cpp | 7 +-
src/main/include/log4cxx/helpers/exception.h | 18 +-
src/main/include/log4cxx/helpers/loglog.h | 90 +++-
src/main/include/log4cxx/helpers/socket.h | 21 +-
src/main/include/log4cxx/helpers/stringtokenizer.h | 1 -
src/main/include/log4cxx/log4cxx.h.in | 14 +-
.../include/log4cxx/net/socketappenderskeleton.h | 13 +
src/main/include/log4cxx/private/aprsocket.h | 15 +-
.../log4cxx/private/socketappenderskeleton_priv.h | 2 +
src/test/cpp/helpers/stringtokenizertestcase.cpp | 31 +-
src/test/cpp/net/CMakeLists.txt | 7 +-
src/test/cpp/net/bsdsocket.cpp | 472 +++++++++++++++++++++
src/test/cpp/net/bsdsocket.h | 68 +++
src/test/cpp/net/socketappendertestcase.cpp | 32 +-
22 files changed, 884 insertions(+), 80 deletions(-)
diff --git a/.github/workflows/log4cxx-macos.yml
b/.github/workflows/log4cxx-macos.yml
index b0f823bd..79f426a5 100644
--- a/.github/workflows/log4cxx-macos.yml
+++ b/.github/workflows/log4cxx-macos.yml
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- name: [osx-14-clang, osx-15-clang, osx-g++]
+ name: [osx-14-clang, osx-15-clang, osx-26-clang, osx-g++]
include:
- name: osx-14-clang
os: macos-14
@@ -33,6 +33,7 @@ jobs:
qt: OFF
multiprocess: ON
cfstring: OFF
+ next_abi: OFF
- name: osx-15-clang
os: macos-15
cxx: clang++
@@ -40,6 +41,15 @@ jobs:
qt: ON
multiprocess: OFF
cfstring: ON
+ next_abi: OFF
+ - name: osx-26-clang
+ os: macos-26
+ cxx: clang++
+ odbc: ON
+ qt: ON
+ multiprocess: OFF
+ cfstring: ON
+ next_abi: ON
- name: osx-g++
os: macos-latest
cxx: g++-14
@@ -47,6 +57,7 @@ jobs:
qt: OFF
multiprocess: OFF
cfstring: ON
+ next_abi: OFF
steps:
- uses: actions/checkout@v4
@@ -76,6 +87,7 @@ jobs:
-DLOG4CXX_ENABLE_ODBC=${{ matrix.odbc }} \
-DLOG4CXX_MULTIPROCESS_ROLLING_FILE_APPENDER=${{ matrix.multiprocess
}} \
-DLOG4CXX_CFSTRING=${{ matrix.cfstring }} \
+ -DLOG4CXX_BUILD_NEXT_ABI=${{ matrix.next_abi }} \
-DCMAKE_BUILD_TYPE=Debug \
..
cmake --build .
diff --git a/.github/workflows/log4cxx-windows.yml
b/.github/workflows/log4cxx-windows.yml
index a93dd718..cc7e2a14 100644
--- a/.github/workflows/log4cxx-windows.yml
+++ b/.github/workflows/log4cxx-windows.yml
@@ -23,15 +23,21 @@ jobs:
strategy:
fail-fast: false
matrix:
- name: [windows-2022, windows-2025-qt]
+ name: [windows-2022, windows-2025, windows-2025-qt]
include:
- name: windows-2022
os: windows-2022
qt: OFF
+ next_abi: OFF
+ - name: windows-2025
+ os: windows-2025
+ qt: OFF
+ next_abi: ON
- name: windows-2025-qt
os: windows-2025
qt: ON
qt_component: qtbase
+ next_abi: OFF
steps:
- uses: actions/checkout@v4
@@ -85,6 +91,7 @@ jobs:
"-DCMAKE_TOOLCHAIN_FILE=$ROOT/vcpkg/scripts/buildsystems/vcpkg.cmake" `
"-DVCPKG_TARGET_TRIPLET=x64-windows" `
"-DCMAKE_INSTALL_PREFIX=$ROOT/Libraries" `
+ "-DLOG4CXX_BUILD_NEXT_ABI=${{ matrix.next_abi }}" `
-S "$ROOT/main" `
-B "$ROOT/build"
diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt
index 5273700f..a672f9b0 100644
--- a/src/main/cpp/CMakeLists.txt
+++ b/src/main/cpp/CMakeLists.txt
@@ -32,7 +32,10 @@ else()
list(APPEND PUBLIC_COMPILE_DEFINITIONS LOG4CXX_STATIC)
endif()
if(LOG4CXX_TEST_ONLY_BUILD)
- list(APPEND PRIVATE_COMPILE_DEFINITIONS
"ENABLE_FAILING_APPENDER_SIMULATION_TESTING=1")
+ list(APPEND PRIVATE_COMPILE_DEFINITIONS
+ "ENABLE_FAILING_APPENDER_SIMULATION_TESTING=1"
+ "LOGLOG_THRESHOLD=1"
+ )
endif()
target_compile_definitions(log4cxx
PUBLIC ${PUBLIC_COMPILE_DEFINITIONS}
diff --git a/src/main/cpp/aprsocket.cpp b/src/main/cpp/aprsocket.cpp
index 8115b7f7..ac06322c 100644
--- a/src/main/cpp/aprsocket.cpp
+++ b/src/main/cpp/aprsocket.cpp
@@ -29,7 +29,7 @@ namespace helpers
{
struct APRSocket::APRSocketPriv : public Socket::SocketPrivate {
- APRSocketPriv(InetAddressPtr& address, int port)
+ APRSocketPriv(LOG4CXX_16_CONST InetAddressPtr& address, int port)
: Socket::SocketPrivate(address, port)
, socket(nullptr)
{}
@@ -40,13 +40,15 @@ struct APRSocket::APRSocketPriv : public
Socket::SocketPrivate {
{}
Pool pool;
- apr_socket_t* socket;
+ apr_socket_t* socket{ NULL };
+ bool connected{ false };
};
#define _priv static_cast<APRSocketPriv*>(m_priv.get())
-APRSocket::APRSocket(InetAddressPtr& address, int port) :
- Socket(std::make_unique<APRSocketPriv>(address, port)){
+APRSocket::APRSocket(LOG4CXX_16_CONST InetAddressPtr& address, int port)
+ : Socket(std::make_unique<APRSocketPriv>(address, port))
+{
apr_status_t status =
apr_socket_create(&_priv->socket, APR_INET, SOCK_STREAM,
APR_PROTO_TCP, _priv->pool.getAPRPool());
@@ -55,14 +57,23 @@ APRSocket::APRSocket(InetAddressPtr& address, int port) :
{
throw SocketException(status);
}
+ open();
+}
- LOG4CXX_ENCODE_CHAR(host, address->getHostAddress());
+void APRSocket::open()
+{
+ LOG4CXX_ENCODE_CHAR(host, _priv->address->getHostAddress());
// create socket address (including port)
apr_sockaddr_t* client_addr;
- status =
- apr_sockaddr_info_get(&client_addr, host.c_str(), APR_INET,
- port, 0, _priv->pool.getAPRPool());
+ apr_status_t status = apr_sockaddr_info_get
+ ( &client_addr
+ , host.c_str()
+ , APR_INET
+ , _priv->port
+ , 0
+ , _priv->pool.getAPRPool()
+ );
if (status != APR_SUCCESS)
{
@@ -76,6 +87,13 @@ APRSocket::APRSocket(InetAddressPtr& address, int port) :
{
throw ConnectException(status);
}
+ _priv->connected = true;
+}
+
+// Is the socket available for use?
+bool APRSocket::is_open()
+{
+ return _priv->socket && _priv->connected;
}
APRSocket::APRSocket(apr_socket_t* s, apr_pool_t* pool) :
diff --git a/src/main/cpp/exception.cpp b/src/main/cpp/exception.cpp
index e0edabdc..264f96c2 100644
--- a/src/main/cpp/exception.cpp
+++ b/src/main/cpp/exception.cpp
@@ -433,6 +433,10 @@ SocketException& SocketException::operator=(const
SocketException& src)
return *this;
}
+ConnectException::ConnectException(const LogString& msg) : SocketException(msg)
+{
+}
+
ConnectException::ConnectException(log4cxx_status_t status) :
SocketException(status)
{
}
diff --git a/src/main/cpp/loglog.cpp b/src/main/cpp/loglog.cpp
index df5b7e71..1f8875fe 100644
--- a/src/main/cpp/loglog.cpp
+++ b/src/main/cpp/loglog.cpp
@@ -24,6 +24,8 @@
#endif
#include <log4cxx/private/log4cxx_private.h>
#include <log4cxx/helpers/aprinitializer.h>
+#include <log4cxx/helpers/date.h>
+#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/systemerrwriter.h>
#include <log4cxx/helpers/optionconverter.h>
#include <mutex>
@@ -69,6 +71,13 @@ struct LogLog::LogLogPrivate {
this->suffix.clear();
}
}
+ LogString elapsedMicroseconds()
+ {
+ LogString result;
+ auto microsecondInterval = Date::currentTime() -
APRInitializer::getStartTime();
+ StringHelper::toString(microsecondInterval, result);
+ return result;
+ }
};
LogLog::LogLog() :
@@ -97,6 +106,16 @@ bool LogLog::isDebugEnabled()
&& p->debugEnabled;
}
+bool LogLog::isDebugEnabledFor(const LoggerPtr& category)
+{
+ return isDebugEnabled() && category && category->isDebugEnabled();
+}
+
+bool LogLog::isTraceEnabledFor(const LoggerPtr& category)
+{
+ return isDebugEnabled() && category && category->isTraceEnabled();
+}
+
void LogLog::setInternalDebugging(bool debugEnabled1)
{
auto p = getInstance().m_priv.get();
@@ -127,7 +146,6 @@ void LogLog::debug(const LogString& msg)
}
std::lock_guard<std::mutex> lock(p->mutex);
-
emit_log(p->debugPrefix, msg, p->suffix);
}
}
@@ -169,6 +187,29 @@ void LogLog::error(const LogString& msg, const
std::exception& e)
}
}
+#if !LOG4CXX_LOGCHAR_IS_UTF8
+void LogLog::trace(const LoggerPtr& category, const std::string& msg)
+{
+ LOG4CXX_DECODE_CHAR(lsMsg, msg);
+ trace(category, lsMsg);
+}
+#endif
+
+void LogLog::trace(const LoggerPtr& category, const LogString& msg)
+{
+ auto p = getInstance().m_priv.get();
+ if (p && !p->quietMode) // Not deleted by onexit processing?
+ {
+ if (!p->debugEnabled)
+ {
+ return;
+ }
+
+ std::lock_guard<std::mutex> lock(p->mutex);
+ emit_log(p->debugPrefix, p->elapsedMicroseconds() +
LOG4CXX_STR(" ") + category->getName() + LOG4CXX_STR("::") + msg, p->suffix);
+ }
+}
+
void LogLog::setQuietMode(bool quietMode1)
{
auto p = getInstance().m_priv.get();
diff --git a/src/main/cpp/socket.cpp b/src/main/cpp/socket.cpp
index 4df85cbd..4b000f9c 100644
--- a/src/main/cpp/socket.cpp
+++ b/src/main/cpp/socket.cpp
@@ -15,11 +15,10 @@
* limitations under the License.
*/
#include <log4cxx/helpers/socket.h>
-#include <log4cxx/helpers/bytebuffer.h>
-#include <log4cxx/helpers/transcoder.h>
-
#include <log4cxx/private/socket_priv.h>
#include <log4cxx/private/aprsocket.h>
+#include <log4cxx/helpers/loader.h>
+#include <log4cxx/helpers/loglog.h>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;
@@ -45,8 +44,45 @@ int Socket::getPort() const
return m_priv->port;
}
+void Socket::setAttributes(const InetAddressPtr& newAddress, int newPort)
+{
+ m_priv->address = newAddress;
+ m_priv->port = newPort;
+}
+
+#if LOG4CXX_ABI_VERSION <= 15
SocketUniquePtr Socket::create(InetAddressPtr& address, int port){
return std::make_unique<APRSocket>(address, port);
}
+#endif
+SocketUniquePtr Socket::create(LOG4CXX_16_CONST InetAddressPtr& address, int
port, const LogString& concreteClassName)
+{
+#if 15 < LOG4CXX_ABI_VERSION
+ if (!concreteClassName.empty())
+ {
+ if (LogLog::isDebugEnabled())
+ {
+ LogLog::debug(LOG4CXX_STR("Desired ") +
Socket::getStaticClass().getName()
+ + LOG4CXX_STR(" sub-class: [") +
concreteClassName + LOG4CXX_STR("]"));
+ }
+ auto& classObj = Loader::loadClass(concreteClassName);
+ auto newObject = classObj.newInstance();
+ auto pSocket = dynamic_cast<Socket*>(newObject);
+ if (!pSocket)
+ {
+ LogLog::error(concreteClassName + LOG4CXX_STR(" is not
a ") + Socket::getStaticClass().getName() + LOG4CXX_STR(" sub-class"));
+ delete newObject;
+ }
+ else
+ {
+ auto result = std::unique_ptr<Socket>(pSocket);
+ pSocket->setAttributes(address, port);
+ pSocket->open();
+ return result;
+ }
+ }
+#endif
+ return std::make_unique<APRSocket>(address, port);
+}
diff --git a/src/main/cpp/socketappenderskeleton.cpp
b/src/main/cpp/socketappenderskeleton.cpp
index 00ca2aae..bceaf47e 100644
--- a/src/main/cpp/socketappenderskeleton.cpp
+++ b/src/main/cpp/socketappenderskeleton.cpp
@@ -98,7 +98,7 @@ void
SocketAppenderSkeleton::SocketAppenderSkeletonPriv::connect()
msg += LOG4CXX_STR("].");
LogLog::debug(msg);
}
- this->setOutputSink(Socket::create(this->address,
this->port));
+ this->setOutputSink(Socket::create(this->address,
this->port, this->socketSubclass));
}
catch (SocketException& e)
{
@@ -131,6 +131,12 @@ void SocketAppenderSkeleton::setOption(const LogString&
option, const LogString&
{
setReconnectionDelay(OptionConverter::toInt(value,
getDefaultDelay()));
}
+#if 15 < LOG4CXX_ABI_VERSION
+ else if (StringHelper::equalsIgnoreCase(option,
LOG4CXX_STR("SOCKETSUBCLASS"), LOG4CXX_STR("socketsubclass")))
+ {
+ setSocketSubclass(value);
+ }
+#endif
else
{
AppenderSkeleton::setOption(option, value);
@@ -193,7 +199,7 @@ void
SocketAppenderSkeleton::SocketAppenderSkeletonPriv::retryConnect()
msg += LOG4CXX_STR("].");
LogLog::debug(msg);
}
- this->setOutputSink(Socket::create(this->address,
this->port));
+ this->setOutputSink(Socket::create(this->address,
this->port, this->socketSubclass));
if (LogLog::isDebugEnabled())
{
LogString msg(LOG4CXX_STR("Connection
established to [")
@@ -314,3 +320,15 @@ int SocketAppenderSkeleton::getReconnectionDelay() const
{
return _priv->reconnectionDelay;
}
+
+#if 15 < LOG4CXX_ABI_VERSION
+void SocketAppenderSkeleton::setSocketSubclass(const LogString& newValue)
+{
+ _priv->socketSubclass = newValue;
+}
+
+const LogString& SocketAppenderSkeleton::getSocketSubclass() const
+{
+ return _priv->socketSubclass;
+}
+#endif
\ No newline at end of file
diff --git a/src/main/cpp/stringtokenizer.cpp b/src/main/cpp/stringtokenizer.cpp
index fe846cc3..ad3fd9e3 100644
--- a/src/main/cpp/stringtokenizer.cpp
+++ b/src/main/cpp/stringtokenizer.cpp
@@ -15,13 +15,8 @@
* limitations under the License.
*/
-#include <log4cxx/logstring.h>
#include <log4cxx/helpers/stringtokenizer.h>
#include <log4cxx/helpers/exception.h>
-#if !defined(LOG4CXX)
- #define LOG4CXX 1
-#endif
-#include <log4cxx/private/log4cxx_private.h>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;
@@ -30,7 +25,7 @@ struct StringTokenizer::StringTokenizerPrivate{
StringTokenizerPrivate(const LogString& str, const LogString& delim1) :
src(str), delim(delim1), pos(0){}
LogString src;
LogString delim;
- size_t pos;
+ LogString::size_type pos;
};
diff --git a/src/main/include/log4cxx/helpers/exception.h
b/src/main/include/log4cxx/helpers/exception.h
index 143e00c0..c5215ee7 100644
--- a/src/main/include/log4cxx/helpers/exception.h
+++ b/src/main/include/log4cxx/helpers/exception.h
@@ -26,6 +26,7 @@
#ifdef _MSC_VER
#pragma warning ( push )
#pragma warning (disable : 4275) // ::std::exception needs to have
dll-interface
+ #pragma warning (disable : 4251) // ::std::basic_string needs to have
dll-interface
#endif
namespace LOG4CXX_NS
@@ -97,14 +98,14 @@ class LOG4CXX_EXPORT IOException : public Exception
IOException(log4cxx_status_t stat);
IOException(const LogString& msg);
IOException(const char* msg);
- IOException(const LogString& type, log4cxx_status_t stat);
+ IOException(const LogString& type, log4cxx_status_t stat);
#if !LOG4CXX_LOGCHAR_IS_UTF8
- IOException(const char* msg, log4cxx_status_t stat);
+ IOException(const char* msg, log4cxx_status_t stat);
#endif
- IOException(const IOException& src);
- IOException& operator=(const IOException&);
- private:
- static LogString formatMessage(log4cxx_status_t stat);
+ IOException(const IOException& src);
+ IOException& operator=(const IOException&);
+ private:
+ static LogString formatMessage(log4cxx_status_t stat);
};
class LOG4CXX_EXPORT MissingResourceException : public Exception
@@ -199,7 +200,7 @@ class LOG4CXX_EXPORT ClassNotFoundException : public
Exception
};
-class NoSuchElementException : public Exception
+class LOG4CXX_EXPORT NoSuchElementException : public Exception
{
public:
NoSuchElementException();
@@ -207,7 +208,7 @@ class NoSuchElementException : public Exception
NoSuchElementException& operator=(const
NoSuchElementException&);
};
-class IllegalStateException : public Exception
+class LOG4CXX_EXPORT IllegalStateException : public Exception
{
public:
IllegalStateException();
@@ -234,6 +235,7 @@ remotely (e.g., no process is listening on the remote
address/port).
class LOG4CXX_EXPORT ConnectException : public SocketException
{
public:
+ ConnectException(const LogString& msg);
ConnectException(log4cxx_status_t status);
ConnectException(const ConnectException& src);
ConnectException& operator=(const ConnectException&);
diff --git a/src/main/include/log4cxx/helpers/loglog.h
b/src/main/include/log4cxx/helpers/loglog.h
index f163b36c..82fde47d 100644
--- a/src/main/include/log4cxx/helpers/loglog.h
+++ b/src/main/include/log4cxx/helpers/loglog.h
@@ -19,6 +19,7 @@
#define _LOG4CXX_HELPERS_LOG_LOG_H
#include <log4cxx/logstring.h>
+#include <log4cxx/logger.h>
#include <log4cxx/helpers/widelife.h>
#include <exception>
#include <mutex>
@@ -57,6 +58,16 @@ class LOG4CXX_EXPORT LogLog
**/
static bool isDebugEnabled();
+ /**
+ * Are \c category debug messages sent to SystemErrWriter?
+ **/
+ static bool isDebugEnabledFor(const LoggerPtr& category);
+
+ /**
+ * Are \c category trace messages sent to SystemErrWriter?
+ **/
+ static bool isTraceEnabledFor(const LoggerPtr& category);
+
/**
Start/stop outputing debug messages if \c newValue is
true/false respectively.
*/
@@ -91,7 +102,17 @@ class LOG4CXX_EXPORT LogLog
*/
static void error(const LogString& msg, const std::exception&
ex);
+ /**
+ Output \c msg to SystemErrWriter if internal debug logging is
enabled.
+ */
+ static void trace(const LoggerPtr& category, const LogString&
msg);
+#if !LOG4CXX_LOGCHAR_IS_UTF8
+ /**
+ Output \c msg to SystemErrWriter if internal debug logging is
enabled.
+ */
+ static void trace(const LoggerPtr& category, const std::string&
msg);
+#endif
/**
Change quiet mode to \c newValue.
@@ -118,14 +139,69 @@ class LOG4CXX_EXPORT LogLog
} // namespace helpers
} // namespace log4cxx
-#define LOGLOG_DEBUG(log) { \
- if (LogLog::isDebugEnabled()) \
- LOG4CXX_NS::helpers::LogLog::debug(log) ; }
+#if defined(LOGLOG_THRESHOLD) && LOGLOG_THRESHOLD <= 10000
+/**
+Send \c message to helpers::SystemErrWriter if \c logger is enabled for
<code>DEBUG</code> events.
+
+\usage
+~~~{.cpp}
+LOGLOG_DEBUG(m_log, "setRemote:"
+ << " hostAddress " << address->getHostAddress()
+ << " port " << port
+ << " inetType " << m_inetType
+ << " ipVersion " << m_inetFamily
+ );
+~~~
+
+@param logger the logger that has the enabled status.
+@param message a valid parameter to an <code>operator<<(std::ostream&.
...)</code> overload.
+*/
+#define LOGLOG_DEBUG(logger, message) { \
+ if (LOG4CXX_NS::helpers::LogLog::isDebugEnabledFor(logger)) { \
+ LOG4CXX_NS::helpers::MessageBuffer buf; \
+ LOG4CXX_NS::helpers::LogLog::trace(logger, buf.str(buf
<< message)); } }
+#else // !defined(LOGLOG_THRESHOLD) || 10000 < LOGLOG_THRESHOLD
+#define LOGLOG_DEBUG(logger, message)
+#endif
+
+#if defined(LOGLOG_THRESHOLD) && LOGLOG_THRESHOLD <= 5000
+/**
+Send \c message to helpers::SystemErrWriter if \c logger is enabled for
<code>TRACE</code> events.
+
+\usage
+~~~{.cpp}
+LOGLOG_TRACE(m_log, "write:"
+ << " byteCount " << byteCount
+ << " to " << *m_remoteAddress
+ << "\n" << MessageData(pMessage, byteCount)
+ );
+~~~
+
+@param logger the logger that has the enabled status.
+@param message a valid parameter to an <code>operator<<(std::ostream&.
...)</code> overload.
+*/
+#define LOGLOG_TRACE(logger, message) { \
+ if (LOG4CXX_NS::helpers::LogLog::isTraceEnabledFor(logger)) { \
+ LOG4CXX_NS::helpers::MessageBuffer buf; \
+ LOG4CXX_NS::helpers::LogLog::trace(logger, buf.str(buf
<< message)); } }
+#else // !defined(LOGLOG_THRESHOLD) || 5000 < LOGLOG_THRESHOLD
+#define LOGLOG_TRACE(logger, message)
+#endif
-#define LOGLOG_WARN(log) { \
- LOG4CXX_NS::helpers::LogLog::warn(log) ; }
+/**
+Send \c message to helpers::SystemErrWriter using the configured warning
output style.
+@param message a valid parameter to an <code>operator<<(std::ostream&.
...)</code> overload.
+*/
+#define LOGLOG_WARN(message) { \
+ LOG4CXX_NS::helpers::MessageBuffer buf; \
+ LOG4CXX_NS::helpers::LogLog::warn(buf.str(buf << message)); }
-#define LOGLOG_ERROR(log) { \
- LOG4CXX_NS::helpers::LogLog::warn(log); }
+/**
+Send \c message to helpers::SystemErrWriter using the configured error output
style.
+@param message a valid parameter to an <code>operator<<(std::ostream&.
...)</code> overload.
+*/
+#define LOGLOG_ERROR(message) { \
+ LOG4CXX_NS::helpers::MessageBuffer buf; \
+ LOG4CXX_NS::helpers::LogLog::error(buf.str(buf << message)); }
#endif //_LOG4CXX_HELPERS_LOG_LOG_H
diff --git a/src/main/include/log4cxx/helpers/socket.h
b/src/main/include/log4cxx/helpers/socket.h
index 36fd7afe..a46af82d 100644
--- a/src/main/include/log4cxx/helpers/socket.h
+++ b/src/main/include/log4cxx/helpers/socket.h
@@ -21,6 +21,12 @@
#include <log4cxx/helpers/inetaddress.h>
#include <log4cxx/helpers/pool.h>
+#if 15 < LOG4CXX_ABI_VERSION
+#define LOG4CXX_16_CONST const
+#else
+#define LOG4CXX_16_CONST
+#endif
+
namespace LOG4CXX_NS
{
@@ -60,6 +66,12 @@ class LOG4CXX_EXPORT Socket : public helpers::Object
When true, an exception is thrown if the write would block.
*/
virtual void setNonBlocking(bool newValue) = 0;
+
+ /// Open this socket.
+ virtual void open() = 0;
+
+ /// Is this available for use?
+ virtual bool is_open() = 0;
#endif
/** Close this socket. */
@@ -71,10 +83,17 @@ class LOG4CXX_EXPORT Socket : public helpers::Object
/** Returns the value of this socket's port field. */
int getPort() const;
+ /** Use \c newAddress and \c newPort as the destination. */
+ void setAttributes(const InetAddressPtr& newAddress, int
newPort);
+
/** Create a concrete instance of this class
*/
+#if LOG4CXX_ABI_VERSION <= 15
static SocketUniquePtr create(InetAddressPtr& address, int
port);
-
+ static SocketUniquePtr create(LOG4CXX_16_CONST InetAddressPtr&
address, int port, const LogString& concreteClassName);
+#else
+ static SocketUniquePtr create(const InetAddressPtr& address,
int port, const LogString& concreteClassName = {});
+#endif
private:
Socket(const Socket&);
Socket& operator=(const Socket&);
diff --git a/src/main/include/log4cxx/helpers/stringtokenizer.h
b/src/main/include/log4cxx/helpers/stringtokenizer.h
index 8e406dec..23e3241a 100644
--- a/src/main/include/log4cxx/helpers/stringtokenizer.h
+++ b/src/main/include/log4cxx/helpers/stringtokenizer.h
@@ -19,7 +19,6 @@
#define _LOG4CXX_HELPERS_STRING_TOKENIZER_H
#include <log4cxx/logstring.h>
-#include <log4cxx/helpers/exception.h>
namespace LOG4CXX_NS
{
diff --git a/src/main/include/log4cxx/log4cxx.h.in
b/src/main/include/log4cxx/log4cxx.h.in
index 4e6c2931..12602d4b 100644
--- a/src/main/include/log4cxx/log4cxx.h.in
+++ b/src/main/include/log4cxx/log4cxx.h.in
@@ -90,16 +90,16 @@ __pragma( warning( pop ) )
#if defined(_WIN32) && defined(_MSC_VER)
#if defined(LOG4CXX_STATIC) // Linking a static library?
-#define LOG4CXX_EXPORT
+# define LOG4CXX_EXPORT
#elif defined(LOG4CXX) // Building a DLL?
-#define LOG4CXX_EXPORT __declspec(dllexport)
+# define LOG4CXX_EXPORT __declspec(dllexport)
#else // Linking against a DLL?
-#define LOG4CXX_EXPORT __declspec(dllimport)
+# define LOG4CXX_EXPORT __declspec(dllimport)
#endif // !LOG4CXX_STATIC
-#elif defined(__GNUC__) && 4 <= __GNUC__ && 15 < LOG4CXX_ABI_VERSION
- #define LOG4CXX_EXPORT __attribute__ ((visibility ("default")))
-#else // !(defined(_WIN32) && defined(_MSC_VER)) || LOG4CXX_ABI_VERSION <= 15
|| __GNUC__ < 4
- #define LOG4CXX_EXPORT
+#elif defined(__has_attribute) && __has_attribute(visibility)
+# define LOG4CXX_EXPORT __attribute__ ((visibility ("default")))
+#else // !defined(__has_attribute)
+# define LOG4CXX_EXPORT
#endif
#define LOG4CXX_NS @LOG4CXX_NS@
diff --git a/src/main/include/log4cxx/net/socketappenderskeleton.h
b/src/main/include/log4cxx/net/socketappenderskeleton.h
index c96f50ec..b9eb19ed 100644
--- a/src/main/include/log4cxx/net/socketappenderskeleton.h
+++ b/src/main/include/log4cxx/net/socketappenderskeleton.h
@@ -179,6 +179,16 @@ class LOG4CXX_EXPORT SocketAppenderSkeleton : public
AppenderSkeleton
@deprecated This method will be removed in a future version.
*/
void fireConnector();
+#else
+ /**
+ * Use \c newSubclass as the helpers::Socket interface instead
of the default implementation.
+ * */
+ void setSocketSubclass(const LogString& newSubclass);
+
+ /**
+ The class name used for the helpers::Socket interface
implemention.
+ */
+ const LogString& getSocketSubclass() const;
#endif
/**
@@ -189,11 +199,14 @@ class LOG4CXX_EXPORT SocketAppenderSkeleton : public
AppenderSkeleton
RemoteHost | (\ref inetAddress "1") | - |
Port | {int} | (\ref defaultPort "2") |
LocationInfo | True,False | False |
+ SocketSubclass | (\ref socketSubclass "3") | APRSocket |
\anchor inetAddress (1) A valid internet address.
\anchor defaultPort (2) Provided by the derived class.
+ \anchor socketSubclass (3) A registered class derived from
helpers::Socket.
+
\sa AppenderSkeleton::setOption()
*/
void setOption(const LogString& option, const LogString& value)
override;
diff --git a/src/main/include/log4cxx/private/aprsocket.h
b/src/main/include/log4cxx/private/aprsocket.h
index addb3898..98195d3e 100644
--- a/src/main/include/log4cxx/private/aprsocket.h
+++ b/src/main/include/log4cxx/private/aprsocket.h
@@ -30,17 +30,26 @@ namespace helpers
class LOG4CXX_EXPORT APRSocket : public helpers::Socket
{
public:
+ /** An uninitialised socket
+ */
+ APRSocket();
+
/** Creates a stream socket and connects it to the specified
port
number at the specified IP address.
*/
- APRSocket(InetAddressPtr& address, int port);
+ APRSocket(LOG4CXX_16_CONST InetAddressPtr& address, int port);
APRSocket(apr_socket_t*, apr_pool_t* pool);
-
size_t write(ByteBuffer&) override;
void setNonBlocking(bool newValue) LOG4CXX_16_VIRTUAL_SPECIFIER;
- /** Closes this socket. */
+ /// Is this available for use?
+ bool is_open() LOG4CXX_16_VIRTUAL_SPECIFIER;
+
+ /// Establish a connection on this socket.
+ void open() LOG4CXX_16_VIRTUAL_SPECIFIER;
+
+ /// Disconnect this socket.
void close() override;
apr_socket_t* getSocketPtr() const;
diff --git a/src/main/include/log4cxx/private/socketappenderskeleton_priv.h
b/src/main/include/log4cxx/private/socketappenderskeleton_priv.h
index e92f0f4e..fcf62616 100644
--- a/src/main/include/log4cxx/private/socketappenderskeleton_priv.h
+++ b/src/main/include/log4cxx/private/socketappenderskeleton_priv.h
@@ -76,6 +76,7 @@ struct SocketAppenderSkeleton::SocketAppenderSkeletonPriv :
public AppenderSkele
int port;
int reconnectionDelay;
bool locationInfo;
+
void close();
/**
@@ -101,6 +102,7 @@ struct SocketAppenderSkeleton::SocketAppenderSkeletonPriv :
public AppenderSkele
void retryConnect();
+ LogString socketSubclass;
};
} // namespace net
diff --git a/src/test/cpp/helpers/stringtokenizertestcase.cpp
b/src/test/cpp/helpers/stringtokenizertestcase.cpp
index b0b9bf42..0a940eb3 100644
--- a/src/test/cpp/helpers/stringtokenizertestcase.cpp
+++ b/src/test/cpp/helpers/stringtokenizertestcase.cpp
@@ -16,6 +16,7 @@
*/
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/stringtokenizer.h>
+#include <log4cxx/helpers/exception.h>
#include "../logunit.h"
#include "../insertwide.h"
@@ -26,9 +27,9 @@ using namespace log4cxx::helpers;
LOGUNIT_CLASS(StringTokenizerTestCase)
{
LOGUNIT_TEST_SUITE(StringTokenizerTestCase);
- LOGUNIT_TEST(testNextTokenEmptyString);
+ LOGUNIT_TEST_EXCEPTION(testNextTokenEmptyString,
NoSuchElementException);
LOGUNIT_TEST(testHasMoreTokensEmptyString);
- LOGUNIT_TEST(testNextTokenAllDelim);
+ LOGUNIT_TEST_EXCEPTION(testNextTokenAllDelim, NoSuchElementException);
LOGUNIT_TEST(testHasMoreTokensAllDelim);
LOGUNIT_TEST(test1);
LOGUNIT_TEST(test2);
@@ -44,17 +45,7 @@ public:
LogString src;
LogString delim(LOG4CXX_STR(" "));
StringTokenizer tokenizer(src, delim);
-
- try
- {
- LogString token(tokenizer.nextToken());
- }
- catch (NoSuchElementException& ex)
- {
- return;
- }
-
- LOGUNIT_ASSERT(false);
+ LogString token(tokenizer.nextToken());
}
void testHasMoreTokensEmptyString()
@@ -70,17 +61,7 @@ public:
LogString src(LOG4CXX_STR("==="));
LogString delim(LOG4CXX_STR("="));
StringTokenizer tokenizer(src, delim);
-
- try
- {
- LogString token(tokenizer.nextToken());
- }
- catch (NoSuchElementException& ex)
- {
- return;
- }
-
- LOGUNIT_ASSERT(false);
+ LogString token(tokenizer.nextToken());
}
void testHasMoreTokensAllDelim()
@@ -106,7 +87,7 @@ public:
{
LogString token(tokenizer.nextToken());
}
- catch (NoSuchElementException& ex)
+ catch (const NoSuchElementException&)
{
return;
}
diff --git a/src/test/cpp/net/CMakeLists.txt b/src/test/cpp/net/CMakeLists.txt
index 7d43f790..9c9e87c9 100644
--- a/src/test/cpp/net/CMakeLists.txt
+++ b/src/test/cpp/net/CMakeLists.txt
@@ -34,8 +34,13 @@ endif()
if(HAS_LIBESMTP)
list(APPEND NET_TESTS smtpappendertestcase)
endif(HAS_LIBESMTP)
+
+if(${log4cxx_ABI_VER} GREATER 15)
+set(OTHER_SOCKET_IMPL bsdsocket.cpp)
+endif()
+
foreach(fileName IN LISTS NET_TESTS)
- add_executable(${fileName} "${fileName}.cpp")
+ add_executable(${fileName} "${fileName}.cpp" ${OTHER_SOCKET_IMPL})
endforeach()
set(ALL_LOG4CXX_TESTS ${ALL_LOG4CXX_TESTS} ${NET_TESTS} PARENT_SCOPE)
diff --git a/src/test/cpp/net/bsdsocket.cpp b/src/test/cpp/net/bsdsocket.cpp
new file mode 100644
index 00000000..8ae9391b
--- /dev/null
+++ b/src/test/cpp/net/bsdsocket.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 "bsdsocket.h"
+#include <log4cxx/helpers/bytebuffer.h>
+#include <log4cxx/private/socket_priv.h>
+#include <log4cxx/helpers/exception.h>
+#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/transcoder.h>
+#include <string>
+#include <memory>
+#ifdef WIN32
+#undef UNICODE
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma comment(lib, "Ws2_32.lib")
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <cstring>
+#include <unistd.h>
+#endif
+#include <sstream>
+#undef min
+#include <algorithm>
+
+// Types
+using HandleType = int;
+using InfoType = struct addrinfo;
+using SockAddrType = struct sockaddr_storage;
+#ifdef WIN32
+using SockSizeType = int;
+#else
+using SockSizeType = unsigned int;
+#endif
+using SockAddrPtr = std::unique_ptr<SockAddrType>;
+
+/// Streamable array elements
+template <typename T, typename S, typename D = T>
+class SeparatedArray
+{
+ const T *m_vec;
+ size_t m_len;
+ S m_separator;
+ size_t m_perLine;
+public:
+ SeparatedArray(const T *vec, size_t len, S separ, size_t perLine = 10)
+ : m_vec(vec)
+ , m_len(len)
+ , m_separator(separ)
+ , m_perLine(perLine)
+ {}
+ void Write(std::ostream& os) const
+ {
+ if (0 < m_perLine && m_perLine <= m_len)
+ os << std::endl;
+ for (size_t i = 0; i < m_len; ++i)
+ {
+ if (0 < i)
+ {
+ if (0 < m_perLine && 0 == (i % m_perLine))
+ os << std::endl;
+ else
+ os << m_separator;
+ }
+ os << (D)m_vec[i];
+ }
+ }
+};
+
+/// Put \c S separated type \c D elements of \c v onto \c os
+ template <typename T, typename S, typename D>
+ std::ostream&
+ operator<<(std::ostream& os, const SeparatedArray<T, S, D>& v)
+{ v.Write(os); return os; }
+
+/// An overload that puts the internet address in \c data onto \c os
+ inline std::ostream&
+operator<<(std::ostream& os, const struct sockaddr& data)
+{
+ if (AF_INET == data.sa_family)
+ {
+ using Formatter = SeparatedArray<unsigned char, unsigned char,
unsigned>;
+ const struct sockaddr_in& ipv4Data = reinterpret_cast<const
struct sockaddr_in&>(data);
+ const unsigned char* octet = (const unsigned
char*)&ipv4Data.sin_addr;
+ os << std::dec << Formatter(octet, 4, '.') << ':' <<
ntohs(ipv4Data.sin_port);
+ }
+ else
+ {
+ using Formatter = SeparatedArray<unsigned short, unsigned char,
unsigned>;
+ const struct sockaddr_in6& ipv6Data = reinterpret_cast<const
struct sockaddr_in6&>(data);
+ const unsigned short* hextet = (const unsigned
short*)&ipv6Data.sin6_addr;
+ os << '[' << std::hex << Formatter(hextet, 8, ':') << ']';
+ os << ':' << std::dec << ntohs(ipv6Data.sin6_port);
+ }
+ return os;
+}
+
+/// An overload that puts the internet address in \c data onto \c os
+ inline std::ostream&
+operator<<(std::ostream& os, const struct sockaddr_storage& data)
+{
+ os << reinterpret_cast<const struct sockaddr&>(data);
+ return os;
+}
+
+namespace
+{
+
+class UnexpectedSystemError : public LOG4CXX_NS::helpers::SocketException
+{
+ using BaseType = LOG4CXX_NS::helpers::SocketException;
+public: // ...stuctors
+ UnexpectedSystemError(const char* context, const std::string& data = {})
+#ifdef WIN32
+ : BaseType(MakeMessage(::GetLastError(), context, data))
+#else
+ : BaseType(MakeMessage(errno, context, data))
+#endif
+ { }
+ UnexpectedSystemError(int id, const char* context, const std::string&
data = {})
+ : BaseType(MakeMessage(id, context, data))
+ { }
+public: // Class methods
+ static LOG4CXX_NS::LogString MakeConnectMessage()
+ {
+#ifdef WIN32
+ return MakeMessage(WSAGetLastError(), "connect");
+#else
+ return MakeMessage(errno, "connect");
+#endif
+ }
+ static LOG4CXX_NS::LogString MakeMessage(int id, const char* context,
const std::string& data = {})
+ {
+ std::stringstream ss;
+ ss << "System error 0x" << std::hex << id << ": " << context;
+ if (!data.empty())
+ ss << ' ' << data;
+ LOG4CXX_DECODE_CHAR(result, ss.str());
+ return result;
+ }
+};
+
+/// An adaptor of message data for printing
+class MessageData
+{
+ const unsigned char *m_vec;
+ size_t m_len;
+public:
+ /// A streamable version of the \c len bytes in \c vec
+ MessageData(const void *vec, size_t len)
+ : m_vec(reinterpret_cast<const unsigned char *>(vec))
+ , m_len(len)
+ {}
+ void Write(std::ostream& os) const
+ {
+ for (size_t i = 0; i < m_len; ++i)
+ {
+ if ('&' == m_vec[i])
+ os << "&";
+ else if (isprint(m_vec[i]))
+ os << m_vec[i];
+ else
+ os << "&#" << std::hex << int(m_vec[i]) << ';';
+ }
+ }
+};
+ std::ostream&
+operator<<(std::ostream& s, const MessageData& v)
+{ v.Write(s); return s; }
+
+#ifdef WIN32
+class SystemInitialiser
+{
+public:
+ SystemInitialiser()
+ {
+ WORD version = MAKEWORD(2, 2);
+ WSADATA data;
+ if (WSAStartup(version, &data) != 0)
+ {
+ throw UnexpectedSystemError(WSAGetLastError(),
"initialising WSA");
+ }
+ }
+};
+void CheckWSA()
+{
+ static SystemInitialiser _;
+}
+#endif
+
+} // namespace
+
+namespace LOG4CXX_NS::helpers
+{
+
+struct BSDSocket::Data : public Socket::SocketPrivate
+{
+ int m_inetType; //!< SOCK_DGRAM or SOCK_STREAM
+ int m_inetFamily; //!< AF_INET or AF_INET6
+ InfoType* m_remote{ 0 }; //!< A chain of interfaces on which
data may be sent and received
+ HandleType m_handle{ -1 }; //!< The handle for send and receive
+ SockAddrPtr m_remoteAddress{ std::make_unique<SockAddrType>() };
+ SockSizeType m_remoteAddressSize{ 0 };
+ LoggerPtr m_log{ Logger::getLogger("BSDSocket") };
+
+ Data(bool isDatagramType = false, bool ipV6 = false)
+ : m_inetType{ isDatagramType ? SOCK_DGRAM : SOCK_STREAM }
+ , m_inetFamily{ ipV6 ? AF_INET6 : AF_INET }
+ {
+#ifdef WIN32
+ CheckWSA();
+#endif
+ }
+
+ Data(const InetAddressPtr& address, int port, bool isDatagramType =
false, bool ipV6 = false)
+ : Socket::SocketPrivate(address, port)
+ , m_inetType{ isDatagramType ? SOCK_DGRAM : SOCK_STREAM }
+ , m_inetFamily{ ipV6 ? AF_INET6 : AF_INET }
+ {
+#ifdef WIN32
+ CheckWSA();
+#endif
+ }
+
+ /// Prepare the socket for use.
+ void open();
+
+ /// Release resources required by an open socket.
+ void close();
+
+ /// Send the bytes in \c data.
+ size_t write(ByteBuffer& data);
+
+ /// Initialise the port field of \c addr to \c port
+ void setInternetPort(struct sockaddr* addr, int port);
+
+ /// Set up \c m_remote for a \c m_inetType connection on \c port to \c
node using \c m_inetFamily
+ void setRemote(const InetAddressPtr& address, int port);
+
+ /// Initialise \c m_remoteAddress from \c data
+ void setRemoteAddress(const struct sockaddr* data, size_t dataSize);
+};
+
+// Prepare the channel for use.
+ void
+BSDSocket::Data::open()
+{
+ LOGLOG_DEBUG(m_log, "open:");
+ setRemote(this->address, this->port);
+ m_handle = (HandleType)socket(m_inetFamily, m_inetType, SOCK_DGRAM ==
m_inetType ? IPPROTO_UDP : IPPROTO_TCP);
+ if (m_handle < 0)
+ throw UnexpectedSystemError("creating socket");
+
+#ifdef WIN32
+ // Prevent a WSAECONNRESET when a previous send operation reported a
ICMP Port Unreachable message
+ // to allow sending when the destination port is not yet receiving data
+ static const int SIO_UDP_CONNRESET = _WSAIOW(IOC_VENDOR, 12);
+ BOOL bNewBehavior = FALSE;
+ DWORD dwBytesReturned = 0;
+ WSAIoctl
+ ( m_handle
+ , SIO_UDP_CONNRESET // dwIoControlCode
+ , &bNewBehavior // lpvInBuffer
+ , sizeof (bNewBehavior) // cbInBuffer
+ , NULL // lpvOutBuffer
+ , 0 // cbOutBuffer
+ , &dwBytesReturned // lpcbBytesReturned
+ , NULL // lpOverlapped
+ , NULL // lpCompletionRoutine
+ );
+#endif
+ LOGLOG_DEBUG(m_log, "open:"
+ << " connect " << m_handle
+ << " to address " << *m_remoteAddress
+ );
+ auto rs = connect(m_handle, (const struct sockaddr
*)m_remoteAddress.get(), m_remoteAddressSize);
+ if (rs < 0)
+ throw
ConnectException(UnexpectedSystemError::MakeConnectMessage());
+ LOGLOG_DEBUG(m_log, "open:" << " handle " << m_handle);
+}
+
+// Release resources required by an open channel.
+ void
+BSDSocket::Data::close()
+{
+ LOGLOG_DEBUG(m_log, "close:" << " handle " << m_handle);
+#ifdef WIN32
+ closesocket(m_handle);
+#else
+ ::close(m_handle);
+#endif
+ m_handle = -1;
+ }
+
+// Send the \c size bytes at \c data.
+ size_t
+BSDSocket::Data::write(ByteBuffer& data)
+{
+ auto pMessage = data.current();
+ auto byteCount = static_cast<int>(data.remaining());
+ LOGLOG_TRACE(m_log, "write:"
+ << " byteCount " << byteCount
+ << " to " << *m_remoteAddress
+ << "\n" << MessageData(pMessage, byteCount)
+ );
+ int rs = sendto
+ ( m_handle
+ , pMessage
+ , byteCount
+ , 0
+ , reinterpret_cast<struct sockaddr*>(m_remoteAddress.get())
+ , (int)m_remoteAddressSize
+ );
+ if (rs < 0)
+ throw UnexpectedSystemError("send");
+ data.increment_position(byteCount);
+ return byteCount;
+}
+
+// Set up \c m_remote for a \c m_inetType connection on \c port to \c address
+ void
+BSDSocket::Data::setRemote(const InetAddressPtr& address, int port)
+{
+ LOG4CXX_ENCODE_CHAR(hostAddress, address->getHostAddress());
+ LOGLOG_DEBUG(m_log, "setRemote:"
+ << " hostAddress " << hostAddress
+ << " port " << port
+ << " inetType " << m_inetType
+ << " ipVersion " << m_inetFamily
+ );
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof (hints));
+ hints.ai_family = m_inetFamily;
+ hints.ai_socktype = m_inetType;
+ auto service = std::to_string(port);
+ int rs = getaddrinfo(hostAddress.c_str(), service.c_str(), &hints,
&m_remote);
+#ifdef WIN32
+ if (0 != rs)
+ throw UnexpectedSystemError(rs, "getaddrinfo");
+#else
+ if (0 == rs)
+ ;
+ else if (EAI_SYSTEM == rs)
+ throw UnexpectedSystemError("getaddrinfo");
+ else
+ throw UnexpectedSystemError(rs, gai_strerror(rs),
"getaddrinfo");
+#endif
+ setRemoteAddress(m_remote->ai_addr, m_remote->ai_addrlen);
+ setInternetPort(m_remote->ai_addr, port);
+}
+
+// Initialise \c m_remoteAddress from \c data
+ void
+BSDSocket::Data::setRemoteAddress(const struct sockaddr* data, size_t dataSize)
+{
+ LOGLOG_DEBUG(m_log, "setRemoteAddress:" << " size " << dataSize << "
address " << *data);
+ m_remoteAddressSize = SockSizeType(std::min(dataSize, sizeof
(SockAddrType)));
+ memcpy(m_remoteAddress.get(), data, m_remoteAddressSize);
+}
+
+// Initialise the port field of \c addr to \c port
+ void
+BSDSocket::Data::setInternetPort(struct sockaddr* addr, int port)
+{
+ LOGLOG_DEBUG(m_log, "setInternetPort: " << port);
+ if (AF_INET == addr->sa_family)
+ {
+ struct sockaddr_in* ipv4Data = reinterpret_cast<struct
sockaddr_in*>(addr);
+ ipv4Data->sin_port = htons(port);
+ }
+ else
+ {
+ struct sockaddr_in6* ipv6Data = reinterpret_cast<struct
sockaddr_in6*>(addr);
+ ipv6Data->sin6_port = htons(port);
+ }
+}
+
+IMPLEMENT_LOG4CXX_OBJECT(BSDSocket)
+
+ const int
+BSDSocket::DefaultPort{ 4560 };
+
+#define _priv static_cast<Data*>(m_priv.get())
+
+// A \c isDatagramType connection
+BSDSocket::BSDSocket(bool isDatagramType, bool ipV6)
+ : Socket{ std::make_unique<Data>(isDatagramType, ipV6) }
+{
+}
+
+// A \c inetType connection on \c port to \c address (or as a server if 0)
+BSDSocket::BSDSocket(const InetAddressPtr& address, int port, bool
isDatagramType, bool ipV6)
+ : Socket{ std::make_unique<Data>(address, port, isDatagramType, ipV6) }
+{
+}
+
+BSDSocket::~BSDSocket()
+{
+}
+
+ void
+BSDSocket::setNonBlocking(bool newValue)
+{
+#ifdef WIN32
+ u_long ulValue = newValue;
+ if (ioctlsocket(_priv->m_handle, FIONBIO, &ulValue) == SOCKET_ERROR)
+ throw UnexpectedSystemError(WSAGetLastError(), "ioctlsocket" );
+#else
+ int fd_flags = fcntl(_priv->m_handle, F_GETFL, 0);
+#if defined(O_NONBLOCK)
+ fd_flags |= O_NONBLOCK;
+#elif defined(O_NDELAY)
+ fd_flags |= O_NDELAY;
+#elif defined(FNDELAY)
+ fd_flags |= FNDELAY;
+#else
+#error Making sockets non-blocking not supported on your platform.
+#endif
+ if (fcntl(_priv->m_handle, F_SETFL, fd_flags) == -1)
+ throw UnexpectedSystemError("fcntl");
+#endif
+}
+
+// Prepare the channel for use.
+ void
+BSDSocket::open()
+{
+ _priv->open();
+}
+
+// Is the port available for use?
+ bool
+BSDSocket::is_open()
+{
+ return 0 <= _priv->m_handle;
+}
+
+// Release resources required by an open channel.
+ void
+BSDSocket::close()
+{
+ _priv->close();
+}
+
+// Send the \c size bytes at \c data.
+ size_t
+BSDSocket::write(ByteBuffer& data)
+{
+ return _priv->write(data);
+}
+
+} // namespace LOG4CXX_NS::helpers
diff --git a/src/test/cpp/net/bsdsocket.h b/src/test/cpp/net/bsdsocket.h
new file mode 100644
index 00000000..6da5757a
--- /dev/null
+++ b/src/test/cpp/net/bsdsocket.h
@@ -0,0 +1,68 @@
+/*
+ * 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_BSD_SOCKET_H_
+#define LOG4CXX_BSD_SOCKET_H_
+#include <log4cxx/helpers/socket.h>
+
+namespace LOG4CXX_NS::helpers
+{
+
+/// A class for writing to a network socket.
+class BSDSocket : public Socket
+{
+public: // Class attributes
+ static const int DefaultPort;
+
+public:
+ DECLARE_LOG4CXX_OBJECT(BSDSocket)
+ BEGIN_LOG4CXX_CAST_MAP()
+ LOG4CXX_CAST_ENTRY(Socket)
+ LOG4CXX_CAST_ENTRY(BSDSocket)
+ END_LOG4CXX_CAST_MAP()
+
+public: // ...structors
+ /// A \c isDatagramType connection
+ BSDSocket(bool isDatagramType = false, bool ipV6 = false);
+ /// A \c isDatagramType connection on \c port to \c address
+ BSDSocket(const InetAddressPtr& address, int port = DefaultPort, bool
isDatagramType = false, bool ipV6 = false);
+
+ /// Release resources
+ ~BSDSocket();
+
+public: // Hooked methods
+ /// Is this available for use?
+ bool is_open() override;
+
+ /// Prepare the socket for use.
+ void open() override;
+
+ /// Release resources required by an open socket.
+ void close() override;
+
+ /// Send the bytes in \c data.
+ size_t write(ByteBuffer& data) override;
+
+ /// Use \c newValue for the behaviour when the network buffer (on an
accepted socket connection) is full.
+ void setNonBlocking(bool newValue) override;
+
+private: // Class methods
+ struct Data;
+};
+
+} // namespace LOG4CXX_NS::helpers
+
+#endif // LOG4CXX_BSD_SOCKET_H_
diff --git a/src/test/cpp/net/socketappendertestcase.cpp
b/src/test/cpp/net/socketappendertestcase.cpp
index aed44689..c52fe33a 100644
--- a/src/test/cpp/net/socketappendertestcase.cpp
+++ b/src/test/cpp/net/socketappendertestcase.cpp
@@ -16,6 +16,7 @@
*/
#include "../appenderskeletontestcase.h"
+#include <log4cxx/logmanager.h>
#include <log4cxx/patternlayout.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/net/xmlsocketappender.h>
@@ -44,8 +45,10 @@ class SocketAppenderTestCase : public
AppenderSkeletonTestCase
//
LOGUNIT_TEST(testDefaultThreshold);
LOGUNIT_TEST(testSetOptionThreshold);
- LOGUNIT_TEST(testRetryConnect);
-
+ LOGUNIT_TEST(testRetryConnectDefault);
+#if 15 < LOG4CXX_ABI_VERSION
+ LOGUNIT_TEST(testRetryConnectBSD);
+#endif
LOGUNIT_TEST_SUITE_END();
#ifdef _DEBUG
@@ -59,20 +62,29 @@ class SocketAppenderTestCase : public
AppenderSkeletonTestCase
public:
+ void tearDown() override
+ {
+ LogManager::shutdown();
+ }
AppenderSkeleton* createAppenderSkeleton() const
{
return new log4cxx::net::SocketAppender();
}
- void testRetryConnect()
+ void testRetryConnect(const LogString& socketImpl = {})
{
int tcpPort = 44445;
+ int millisecondDelay = 50;
auto appender = std::make_shared<net::SocketAppender>();
appender->setLayout(std::make_shared<log4cxx::PatternLayout>(LOG4CXX_STR("%d
[%T] %m%n")));
appender->setRemoteHost(LOG4CXX_STR("localhost"));
- appender->setReconnectionDelay(50); // milliseconds
+ appender->setReconnectionDelay(millisecondDelay);
appender->setPort(tcpPort);
+#if 15 < LOG4CXX_ABI_VERSION
+ if (!socketImpl.empty())
+ appender->setSocketSubclass(socketImpl);
+#endif
appender->activateOptions();
BasicConfigurator::configure(appender);
@@ -179,6 +191,18 @@ class SocketAppenderTestCase : public
AppenderSkeletonTestCase
}
LOGUNIT_ASSERT_EQUAL(logEventCount,
(int)messageCount.size());
}
+
+ void testRetryConnectDefault()
+ {
+ testRetryConnect();
+ }
+
+#if 15 < LOG4CXX_ABI_VERSION
+ void testRetryConnectBSD()
+ {
+ testRetryConnect(LOG4CXX_STR("BSDSocket"));
+ }
+#endif
};
LOGUNIT_TEST_SUITE_REGISTRATION(SocketAppenderTestCase);