You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by sw...@apache.org on 2026/10/23 03:10:30 UTC
(logging-log4cxx) 01/01: Allow ODBCAppender testing with change code
This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch simplify_odbc_appender_interface
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit f06c6c5b28dc27eeb726ed40d2ff772b51b5fab5
Author: Stephen Webb <sw...@gmail.com>
AuthorDate: Fri Oct 23 14:10:06 2026 +1100
Allow ODBCAppender testing with change code
---
src/main/cpp/odbcappender.cpp | 102 ++++++++++-----------
src/main/include/log4cxx/db/odbcappender.h | 40 ++++----
.../include/log4cxx/private/odbcappender_priv.h | 6 +-
src/test/cpp/db/odbcappendertestcase.cpp | 58 ++++++++++--
.../odbcAppenderDSN-Log4cxxSQLiteTest.properties | 2 +
.../input/xml/odbcAppenderDSN-Log4cxxTest.xml | 5 +-
6 files changed, 129 insertions(+), 84 deletions(-)
diff --git a/src/main/cpp/odbcappender.cpp b/src/main/cpp/odbcappender.cpp
index 0be160b5..ccffa2e5 100644
--- a/src/main/cpp/odbcappender.cpp
+++ b/src/main/cpp/odbcappender.cpp
@@ -65,29 +65,22 @@ using namespace LOG4CXX_NS::db;
using namespace LOG4CXX_NS::spi;
using namespace LOG4CXX_NS::pattern;
-SQLException::SQLException(short fHandleType,
- void* hInput, const char* prolog,
- LOG4CXX_NS::helpers::Pool& p)
- : Exception(formatMessage(fHandleType, hInput, prolog, p))
+SQLException::SQLException
+ ( short fHandleType
+ , void* hInput
+ , const LogString& context
+ )
+ : Exception(formatMessage(fHandleType, hInput, context))
{
}
-
-SQLException::SQLException(const char* msg)
- : Exception(msg)
-{
-}
-
-SQLException::SQLException(const SQLException& src)
- : Exception(src)
-{
-}
-
-const char* SQLException::formatMessage(short fHandleType,
- void* hInput, const char* prolog, LOG4CXX_NS::helpers::Pool& p)
+const LogString SQLException::formatMessage
+ ( short fHandleType
+ , void* hInput
+ , const LogString& context
+ )
{
- std::string strReturn(prolog);
- strReturn.append(" - ");
+ LogString result;
#if LOG4CXX_HAVE_ODBC
SQLCHAR SqlState[6];
SQLCHAR Msg[SQL_MAX_MESSAGE_LENGTH];
@@ -102,15 +95,16 @@ const char* SQLException::formatMessage(short fHandleType,
while ((rc2 = SQLGetDiagRecA(fHandleType, hInput, i, SqlState, &NativeError,
Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
{
- strReturn.append((char*) Msg);
+ helpers::Transcoder::decode((char*)Msg, result);
i++;
}
-
+ if (1 < i)
+ result.append(LOG4CXX_STR(" - "));
+ result.append(context);
#else
- strReturn.append("log4cxx built without ODBC support");
+ result.append(LOG4CXX_STR("log4cxx built without ODBC support"));
#endif
-
- return apr_pstrdup((apr_pool_t*) p.getAPRPool(), strReturn.c_str());
+ return result;
}
@@ -129,7 +123,7 @@ ODBCAppender::ODBCAppender()
{
flushBuffer(_priv->pool);
}
- catch (SQLException& e)
+ catch (const SQLException& e)
{
_priv->errorHandler->error(LOG4CXX_STR("Error flushing connection"),
e, ErrorCode::GENERIC_FAILURE);
@@ -267,7 +261,7 @@ void ODBCAppender::append( const spi::LoggingEventPtr& event )
if (_priv->buffer.size() >= _priv->bufferSize)
{
- flushBuffer(_priv->pool);
+ flushBuffer();
}
#endif
@@ -280,7 +274,7 @@ void ODBCAppender::closeConnection(ODBCAppender::SQLHDBC /* con */)
{
}
-ODBCAppender::SQLHDBC ODBCAppender::getConnection(LOG4CXX_NS::helpers::Pool& p)
+ODBCAppender::SQLHDBC ODBCAppender::getConnection()
{
#if LOG4CXX_HAVE_ODBC
SQLRETURN ret = -1;
@@ -291,7 +285,7 @@ ODBCAppender::SQLHDBC ODBCAppender::getConnection(LOG4CXX_NS::helpers::Pool& p)
if (ret < 0)
{
- SQLException ex(SQL_HANDLE_ENV, _priv->env, "Failed to allocate SQL handle", p);
+ SQLException ex(SQL_HANDLE_ENV, _priv->env, LOG4CXX_STR("Failed to allocate SQL handle"));
_priv->env = SQL_NULL_HENV;
throw ex;
}
@@ -300,7 +294,7 @@ ODBCAppender::SQLHDBC ODBCAppender::getConnection(LOG4CXX_NS::helpers::Pool& p)
if (ret < 0)
{
- SQLException ex(SQL_HANDLE_ENV, _priv->env, "Failed to set odbc version", p);
+ SQLException ex(SQL_HANDLE_ENV, _priv->env, LOG4CXX_STR("Failed to set odbc version"));
SQLFreeHandle(SQL_HANDLE_ENV, _priv->env);
_priv->env = SQL_NULL_HENV;
throw ex;
@@ -313,7 +307,7 @@ ODBCAppender::SQLHDBC ODBCAppender::getConnection(LOG4CXX_NS::helpers::Pool& p)
if (ret < 0)
{
- SQLException ex(SQL_HANDLE_DBC, _priv->connection, "Failed to allocate sql handle", p);
+ SQLException ex(SQL_HANDLE_DBC, _priv->connection, LOG4CXX_STR("Failed to allocate sql handle"));
_priv->connection = SQL_NULL_HDBC;
throw ex;
}
@@ -344,7 +338,7 @@ ODBCAppender::SQLHDBC ODBCAppender::getConnection(LOG4CXX_NS::helpers::Pool& p)
if (ret < 0)
{
- SQLException ex(SQL_HANDLE_DBC, _priv->connection, "Failed to connect to database", p);
+ SQLException ex(SQL_HANDLE_DBC, _priv->connection, LOG4CXX_STR("from SQLConect using ") + _priv->databaseURL);
SQLFreeHandle(SQL_HANDLE_DBC, _priv->connection);
_priv->connection = SQL_NULL_HDBC;
throw ex;
@@ -361,14 +355,19 @@ void ODBCAppender::close()
{
if (_priv->setClosed())
{
+ try
+ {
#if LOG4CXX_HAVE_ODBC
- if (!_priv->buffer.empty() && 0 == _priv->preparedStatement)
+ if (!_priv->buffer.empty() && 0 == _priv->preparedStatement)
+ _priv->setPreparedStatement(getConnection());
+#endif
+ _priv->close();
+ }
+ catch (const SQLException& e)
{
- Pool p;
- _priv->setPreparedStatement(getConnection(p), p);
+ _priv->errorHandler->error(LOG4CXX_STR("Error closing connection"),
+ e, ErrorCode::GENERIC_FAILURE);
}
-#endif
- _priv->close();
}
}
@@ -376,10 +375,9 @@ void ODBCAppender::ODBCAppenderPriv::close()
{
try
{
- Pool p;
- flushBuffer(p);
+ flushBuffer();
}
- catch (SQLException& e)
+ catch (const SQLException& e)
{
this->errorHandler->error(LOG4CXX_STR("Error closing connection"),
e, ErrorCode::GENERIC_FAILURE);
@@ -402,12 +400,12 @@ void ODBCAppender::ODBCAppenderPriv::close()
}
#if LOG4CXX_HAVE_ODBC
-void ODBCAppender::ODBCAppenderPriv::setPreparedStatement(SQLHDBC con, Pool& p)
+void ODBCAppender::ODBCAppenderPriv::setPreparedStatement(SQLHDBC con)
{
auto ret = SQLAllocHandle( SQL_HANDLE_STMT, con, &this->preparedStatement);
if (ret < 0)
{
- throw SQLException( SQL_HANDLE_DBC, con, "Failed to allocate statement handle.", p);
+ throw SQLException( SQL_HANDLE_DBC, con, LOG4CXX_STR("Failed to allocate statement handle."));
}
#if LOG4CXX_LOGCHAR_IS_WCHAR
@@ -417,7 +415,7 @@ void ODBCAppender::ODBCAppenderPriv::setPreparedStatement(SQLHDBC con, Pool& p)
#endif
if (ret < 0)
{
- throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to prepare sql statement.", p);
+ throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, LOG4CXX_STR("Failed to prepare sql statement."));
}
int parameterNumber = 0;
@@ -438,7 +436,7 @@ void ODBCAppender::ODBCAppenderPriv::setPreparedStatement(SQLHDBC con, Pool& p)
);
if (ret < 0)
{
- throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to describe parameter", p);
+ throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, LOG4CXX_STR("Failed to describe parameter"));
}
if (SQL_CHAR == targetType || SQL_VARCHAR == targetType || SQL_LONGVARCHAR == targetType)
{
@@ -527,12 +525,12 @@ void ODBCAppender::ODBCAppenderPriv::setPreparedStatement(SQLHDBC con, Pool& p)
);
if (ret < 0)
{
- throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to bind parameter", p);
+ throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, LOG4CXX_STR("Failed to bind parameter"));
}
}
}
-void ODBCAppender::ODBCAppenderPriv::setParameterValues(const spi::LoggingEventPtr& event, Pool& p)
+void ODBCAppender::ODBCAppenderPriv::setParameterValues(const spi::LoggingEventPtr& event)
{
for (auto& item : this->parameterValue)
{
@@ -598,16 +596,16 @@ void ODBCAppender::ODBCAppenderPriv::setParameterValues(const spi::LoggingEventP
}
#endif
-void ODBCAppender::flushBuffer(Pool& p)
+void ODBCAppender::flushBuffer()
{
#if LOG4CXX_HAVE_ODBC
if (0 == _priv->preparedStatement)
- _priv->setPreparedStatement(getConnection(p), p);
- _priv->flushBuffer(p);
+ _priv->setPreparedStatement(getConnection());
+ _priv->flushBuffer();
#endif
}
-void ODBCAppender::ODBCAppenderPriv::flushBuffer(Pool& p)
+void ODBCAppender::ODBCAppenderPriv::flushBuffer()
{
if (0 == this->preparedStatement)
;
@@ -618,14 +616,14 @@ void ODBCAppender::ODBCAppenderPriv::flushBuffer(Pool& p)
#if LOG4CXX_HAVE_ODBC
else try
{
- this->setParameterValues(logEvent, p);
+ this->setParameterValues(logEvent);
auto ret = SQLExecute(this->preparedStatement);
if (ret < 0)
{
- throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to execute prepared statement", p);
+ throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, LOG4CXX_STR("Failed to execute prepared statement"));
}
}
- catch (SQLException& e)
+ catch (const SQLException& e)
{
this->errorHandler->error(LOG4CXX_STR("Failed to execute sql"), e,
ErrorCode::FLUSH_FAILURE);
diff --git a/src/main/include/log4cxx/db/odbcappender.h b/src/main/include/log4cxx/db/odbcappender.h
index fcbd09eb..df8e5be1 100644
--- a/src/main/include/log4cxx/db/odbcappender.h
+++ b/src/main/include/log4cxx/db/odbcappender.h
@@ -37,16 +37,18 @@ namespace db
{
class LOG4CXX_EXPORT SQLException : public LOG4CXX_NS::helpers::Exception
{
- public:
- SQLException(short fHandleType,
- void* hInput, const char* prolog,
- LOG4CXX_NS::helpers::Pool& p);
- SQLException(const char* msg);
- SQLException(const SQLException& src);
- private:
- const char* formatMessage(short fHandleType,
- void* hInput, const char* prolog,
- LOG4CXX_NS::helpers::Pool& p);
+public:
+ SQLException
+ ( short fHandleType
+ , void* hInput
+ , const LogString& context
+ );
+private:
+ const LogString formatMessage
+ ( short fHandleType
+ , void* hInput
+ , const LogString& context
+ );
};
/**
@@ -202,14 +204,7 @@ class LOG4CXX_EXPORT ODBCAppender : public AppenderSkeleton
* By default this creates a single connection which is held open
* until the object is garbage collected.
*/
- virtual SQLHDBC getConnection(LOG4CXX_NS::helpers::Pool& p) /*throw(SQLException)*/;
-
- /**
- * Closes the appender, flushing the buffer first then closing the default
- * connection if it is open.
- */
- public:
- void close() override;
+ virtual SQLHDBC getConnection() /*throw(SQLException)*/;
/**
* loops through the buffer of LoggingEvents, gets a
@@ -218,7 +213,14 @@ class LOG4CXX_EXPORT ODBCAppender : public AppenderSkeleton
*
* If a statement fails the LoggingEvent stays in the buffer!
*/
- virtual void flushBuffer(LOG4CXX_NS::helpers::Pool& p);
+ virtual void flushBuffer();
+
+ public:
+ /**
+ * Closes the appender, flushing the buffer first then closing the default
+ * connection if it is open.
+ */
+ void close() override;
/**
* Does this appender require a layout?
diff --git a/src/main/include/log4cxx/private/odbcappender_priv.h b/src/main/include/log4cxx/private/odbcappender_priv.h
index f46c614b..d07cadd2 100644
--- a/src/main/include/log4cxx/private/odbcappender_priv.h
+++ b/src/main/include/log4cxx/private/odbcappender_priv.h
@@ -128,15 +128,15 @@ struct ODBCAppender::ODBCAppenderPriv : public AppenderSkeleton::AppenderSkeleto
std::vector<LogString> mappedName;
std::vector<DataBinding> parameterValue;
#if LOG4CXX_HAVE_ODBC
- void setPreparedStatement(SQLHDBC con, helpers::Pool& p);
- void setParameterValues(const spi::LoggingEventPtr& event, helpers::Pool& p);
+ void setPreparedStatement(SQLHDBC con);
+ void setParameterValues(const spi::LoggingEventPtr& event);
#endif
#if LOG4CXX_EVENTS_AT_EXIT
helpers::AtExitRegistry::Raii atExitRegistryRaii;
#endif
- void flushBuffer(helpers::Pool& p);
+ void flushBuffer();
void close();
};
diff --git a/src/test/cpp/db/odbcappendertestcase.cpp b/src/test/cpp/db/odbcappendertestcase.cpp
index 040be189..45d5958c 100644
--- a/src/test/cpp/db/odbcappendertestcase.cpp
+++ b/src/test/cpp/db/odbcappendertestcase.cpp
@@ -18,6 +18,10 @@
#include <log4cxx/logmanager.h>
#include <log4cxx/db/odbcappender.h>
#include <log4cxx/xml/domconfigurator.h>
+#include <log4cxx/helpers/fileinputstream.h>
+#include <log4cxx/helpers/loglog.h>
+#include <log4cxx/helpers/properties.h>
+#include <log4cxx/helpers/system.h>
#include "../appenderskeletontestcase.h"
#include <apr_time.h>
@@ -40,10 +44,7 @@ class ODBCAppenderTestCase : public AppenderSkeletonTestCase
LOGUNIT_TEST(testDefaultThreshold);
LOGUNIT_TEST(testSetOptionThreshold);
LOGUNIT_TEST(testNegativeBufferSizeOption);
-//#define DataSourceName_Log4cxxTest_Is_Valid
-#ifdef DataSourceName_Log4cxxTest_Is_Valid
LOGUNIT_TEST(testConnectUsingDSN);
-#endif
LOGUNIT_TEST_SUITE_END();
@@ -67,11 +68,25 @@ class ODBCAppenderTestCase : public AppenderSkeletonTestCase
LOGUNIT_ASSERT_EQUAL((size_t) 1, appender.getBufferSize());
}
-// 'odbcAppenderDSN-Log4cxxTest.xml' requires the data souce name 'Log4cxxTest'
-// containing a 'ApplicationLogs' database
-// with 'UnitTestLog' table
-// containing the fields shown below:
+// testConnectUsingDSN requires the environment variable LOG4CXX_ODBC_TEST_DATA_FILE_PATH
+// that names a properties file that has entries specifiing
+// the data souce name (e.g. Log4cxxTest) identifying a database
+// and a table name (e.g. UnitTestLog )
+// where the table contains the fields shown
+// in the following SQL data definition language statements:
+// SQLite:
+// CREATE TABLE UnitTestLog(
+// Item INTEGER PRIMARY KEY AUTOINCREMENT,
+// Thread VARCHAR(20),
+// LogTime DATETIME NOT NULL,
+// LogName VARCHAR(50),
+// LogLevel VARCHAR(10),
+// FileName VARCHAR(300),
+// FileLine INTEGER,
+// Message VARCHAR(1000)
+// );
//
+// MSSQLServer:
// USE [ApplicationLogs]
// GO
//
@@ -95,6 +110,35 @@ class ODBCAppenderTestCase : public AppenderSkeletonTestCase
//
void testConnectUsingDSN()
{
+ LogString odbcDataFileNameVar = LOG4CXX_STR("LOG4CXX_ODBC_TEST_DATA_FILE_PATH");
+ auto odbcDataFileName = helpers::System::getProperty(odbcDataFileNameVar);
+ if (odbcDataFileName.empty())
+ {
+ helpers::LogLog::warn(
+ LOG4CXX_STR("Set the ") + odbcDataFileNameVar + LOG4CXX_STR(" environment variable"
+ " and re-run testConnectUsingDSN to initialise it with the required values")
+ );
+ return;
+ }
+ File odbcDataFile(odbcDataFileName);
+ if (!odbcDataFile.exists())
+ {
+ std::ofstream f(odbcDataFileName);
+ const char odbcData[] =
+ "LOG4CXX_TEST_ODBC_DSN=\n"
+ "LOG4CXX_TEST_ODBC_TABLE_NAME=\n"
+ ;
+ f.write(odbcData, sizeof (odbcData));
+ f.close();
+ LOGUNIT_ASSERT(odbcDataFile.exists());
+ helpers::LogLog::warn(LOG4CXX_STR("Edit values into '") + odbcDataFileName + LOG4CXX_STR("' and re-run testConnectUsingDSN"));
+ return;
+ }
+ auto odbcDataStream = std::make_shared<helpers::FileInputStream>(odbcDataFile);
+ helpers::Properties& odbcData = spi::Configurator::properties();
+ odbcData.load(odbcDataStream);
+ LOGUNIT_ASSERT(!odbcData.getProperty(LOG4CXX_STR("LOG4CXX_TEST_ODBC_DSN")).empty());
+ LOGUNIT_ASSERT(!odbcData.getProperty(LOG4CXX_STR("LOG4CXX_TEST_ODBC_TABLE_NAME")).empty());
auto status = xml::DOMConfigurator::configure("input/xml/odbcAppenderDSN-Log4cxxTest.xml");
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);
auto odbc = Logger::getLogger("DB.UnitTest");
diff --git a/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxSQLiteTest.properties b/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxSQLiteTest.properties
new file mode 100644
index 00000000..62ab4976
--- /dev/null
+++ b/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxSQLiteTest.properties
@@ -0,0 +1,2 @@
+LOG4CXX_TEST_ODBC_DSN=Log4cxxSQLiteTest
+LOG4CXX_TEST_ODBC_TABLE_NAME=UnitTestLog
\ No newline at end of file
diff --git a/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml b/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml
index 04e278da..3e947e27 100644
--- a/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml
+++ b/src/test/resources/input/xml/odbcAppenderDSN-Log4cxxTest.xml
@@ -26,9 +26,8 @@
</layout>
</appender>
<appender name="PreparedAppender" class="ODBCAppender">
- <param name="DSN" value="Log4cxxTest"/>
- <!--param name="sql" value="INSERT INTO UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" /-->
- <param name="sql" value="INSERT INTO ApplicationLogs.dbo.UnitTestLog (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" />
+ <param name="DSN" value="${LOG4CXX_TEST_ODBC_DSN}"/>
+ <param name="sql" value="INSERT INTO ${LOG4CXX_TEST_ODBC_TABLE_NAME} (Thread,LogName,LogTime,LogLevel,FileName,FileLine,Message) VALUES (?,?,?,?,?,?,?)" />
<param name="ColumnMapping" value="thread"/>
<param name="ColumnMapping" value="logger"/>
<param name="ColumnMapping" value="time"/>