1515 * limitations under the License.
1616 */
1717#include < log4cxx/basicconfigurator.h>
18- #include < log4cxx/fileappender.h>
1918#include < log4cxx/logmanager.h>
2019#include < log4cxx/patternlayout.h>
2120#include < log4cxx/helpers/pool.h>
2221#include < log4cxx/helpers/loglog.h>
2322#include < log4cxx/helpers/stringhelper.h>
23+ #include < log4cxx/helpers/transcoder.h>
24+ #include < log4cxx/helpers/fileoutputstream.h>
25+ #include < log4cxx/rolling/rollingfileappender.h>
26+ #include < log4cxx/rolling/timebasedrollingpolicy.h>
2427#include " logunit.h"
2528#include < apr_time.h>
29+ #include < apr_thread_proc.h>
30+ #include < fstream>
2631
2732using namespace log4cxx ;
2833using namespace log4cxx ::helpers;
2934
30- auto getLogger (const std::string & name = std::string() ) -> LoggerPtr {
35+ auto getLogger (const LogString & name = {} ) -> LoggerPtr {
3136 static struct log4cxx_initializer {
3237 log4cxx_initializer () {
3338 auto layout = std::make_shared<PatternLayout>(LOG4CXX_STR (" %d %m%n" ));
@@ -59,7 +64,9 @@ LOGUNIT_CLASS(FileAppenderTest)
5964 LOGUNIT_TEST (testDirectoryCreation);
6065 LOGUNIT_TEST (testgetSetThreshold);
6166 LOGUNIT_TEST (testIsAsSevereAsThreshold);
62- LOGUNIT_TEST (testBufferedOutput);
67+ LOGUNIT_TEST (testPeriodicFlush);
68+ LOGUNIT_TEST (writeFinalBufferOutput);
69+ LOGUNIT_TEST (checkFinalBufferOutput);
6370 LOGUNIT_TEST_SUITE_END ();
6471
6572#ifdef _DEBUG
@@ -119,7 +126,8 @@ LOGUNIT_CLASS(FileAppenderTest)
119126 LOGUNIT_ASSERT (appender->isAsSevereAsThreshold (debug));
120127 }
121128
122- void testBufferedOutput ()
129+ // Check a file is periodically flushed
130+ void testPeriodicFlush ()
123131 {
124132 auto logger = getLogger ();
125133 int requiredMsgCount = 10000 ;
@@ -136,6 +144,8 @@ LOGUNIT_CLASS(FileAppenderTest)
136144 // wait 1.2 sec and check the buffer is flushed
137145 apr_sleep (1200000 );
138146 size_t flushedLength = file.length (p);
147+
148+ // Check the file extended
139149 if (helpers::LogLog::isDebugEnabled ())
140150 {
141151 LogString msg (LOG4CXX_STR (" initialLength " ));
@@ -146,6 +156,129 @@ LOGUNIT_CLASS(FileAppenderTest)
146156 }
147157 LOGUNIT_ASSERT (initialLength < flushedLength);
148158 }
159+
160+ // Used to check the buffer is flushed at exit
161+ void writeFinalBufferOutput ()
162+ {
163+ int requiredMsgCount = 100 ;
164+ auto logger = getLogger (LOG4CXX_STR (" 100message" ));
165+
166+ // Set up a new file
167+ LogString dir{ LOG4CXX_STR (" output/newdir" ) };
168+ auto writer = std::make_shared<rolling::RollingFileAppender>();
169+ writer->setLayout (std::make_shared<PatternLayout>(LOG4CXX_STR (" %d %m%n" )));
170+ writer->setFile (dir + LOG4CXX_STR (" /100message.log" ));
171+ writer->setBufferedIO (true );
172+ writer->setBufferedSeconds (1 );
173+ auto policy = std::make_shared<rolling::TimeBasedRollingPolicy>();
174+ policy->setFileNamePattern (dir + LOG4CXX_STR (" /100message-%d{yyyy}.log" ));
175+ writer->setRollingPolicy (policy);
176+ helpers::Pool p;
177+ writer->activateOptions (p);
178+ logger->setAdditivity (false );
179+ logger->addAppender (writer);
180+
181+ for ( int x = 0 ; x < requiredMsgCount; x++ )
182+ {
183+ LOG4CXX_INFO ( logger, " This is test message " << x );
184+ }
185+ }
186+
187+ void checkFinalBufferOutput ()
188+ {
189+ helpers::Pool p;
190+ // start a separate instance of this to write messages to the file
191+ helpers::FileOutputStream output (LOG4CXX_STR (" output/newdir/100message-writer.out" ), false );
192+ auto thisProgram = GetExecutableFileName ();
193+ const char * args[] = {thisProgram.c_str (), " writeFinalBufferOutput" , 0 };
194+ apr_procattr_t * attr = NULL ;
195+ setTestAttributes (&attr, output.getFilePtr (), p);
196+ apr_proc_t pid;
197+ startTestInstance (&pid, attr, args, p);
198+
199+ int exitCode;
200+ apr_exit_why_e reason;
201+ apr_proc_wait (&pid, &exitCode, &reason, APR_WAIT);
202+ if (exitCode != 0 )
203+ {
204+ LogString msg = LOG4CXX_STR (" child exit code: " );
205+ helpers::StringHelper::toString (exitCode, p, msg);
206+ msg += LOG4CXX_STR (" ; reason: " );
207+ helpers::StringHelper::toString (reason, p, msg);
208+ helpers::LogLog::warn (msg);
209+ }
210+ LOGUNIT_ASSERT_EQUAL (exitCode, 0 );
211+
212+ // Check all required messages are in the file
213+ std::ifstream input (" output/newdir/100message.log" );
214+ std::vector<int > messageCount;
215+ int lineCount{ 0 };
216+ for (std::string line; std::getline (input, line);)
217+ {
218+ ++lineCount;
219+ auto pos = line.rfind (' ' );
220+ if (line.npos != pos && pos + 1 < line.size ())
221+ {
222+ try
223+ {
224+ auto msgNumber = std::stoull (line.substr (pos));
225+ if (messageCount.size () <= msgNumber)
226+ messageCount.resize (msgNumber + 1 );
227+ ++messageCount[msgNumber];
228+ }
229+ catch (std::exception const & ex)
230+ {
231+ LogString msg;
232+ helpers::Transcoder::decode (ex.what (), msg);
233+ msg += LOG4CXX_STR (" processing\n " );
234+ helpers::Transcoder::decode (line, msg);
235+ helpers::LogLog::warn (msg);
236+ }
237+ }
238+ }
239+ LogString msg = LOG4CXX_STR (" lineCount: " );
240+ helpers::StringHelper::toString (lineCount, p, msg);
241+ helpers::LogLog::debug (msg);
242+ for (auto & count : messageCount)
243+ LOGUNIT_ASSERT_EQUAL (count, messageCount.front ());
244+ }
245+
246+ private:
247+
248+ void setTestAttributes (apr_procattr_t ** attr, apr_file_t * output, helpers::Pool& p)
249+ {
250+ if (apr_procattr_create (attr, p.getAPRPool ()) != APR_SUCCESS)
251+ {
252+ LOGUNIT_FAIL (" apr_procattr_create" );
253+ }
254+ if (apr_procattr_cmdtype_set (*attr, APR_PROGRAM) != APR_SUCCESS)
255+ {
256+ LOGUNIT_FAIL (" apr_procattr_cmdtype_set" );
257+ }
258+ if (apr_procattr_child_out_set (*attr, output, NULL ) != APR_SUCCESS)
259+ {
260+ LOGUNIT_FAIL (" apr_procattr_child_out_set" );
261+ }
262+ if (apr_procattr_child_err_set (*attr, output, NULL ) != APR_SUCCESS)
263+ {
264+ LOGUNIT_FAIL (" apr_procattr_child_err_set" );
265+ }
266+ }
267+
268+ void startTestInstance (apr_proc_t * pid, apr_procattr_t * attr, const char ** argv, helpers::Pool& p)
269+ {
270+ if (apr_proc_create (pid, argv[0 ], argv, NULL , attr, p.getAPRPool ()) != APR_SUCCESS)
271+ {
272+ LOGUNIT_FAIL (" apr_proc_create" );
273+ }
274+ }
275+
276+ std::string GetExecutableFileName ()
277+ {
278+ auto lsProgramFilePath = spi::Configurator::properties ().getProperty (LOG4CXX_STR (" PROGRAM_FILE_PATH" ));
279+ LOG4CXX_ENCODE_CHAR (programFilePath, lsProgramFilePath);
280+ return programFilePath;
281+ }
149282};
150283
151284LOGUNIT_TEST_SUITE_REGISTRATION (FileAppenderTest);
0 commit comments