diff --git a/ChangeLog b/ChangeLog index c540b8d..9fb2193 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,7 +3,7 @@ * Version 1.2.0 released ========================= - Add GLog::is_open() method (#1837) + Add GLog::is_open() and GLog::is_empty() methods (#1837) Fix invalid pointer problem in GLog copies (#1823) Fix GSkyMap::publish() bug for empty sky maps (#1835) Fix loss of application name and version (#1835) diff --git a/NEWS b/NEWS index 3ad2514..6a02702 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,7 @@ The following methods have been added: - GApplication::log_header2() - GApplication::log_header3() - GLog::is_open() +- GLog::is_empty() The following methods have been removed: - ... @@ -71,7 +72,11 @@ duplicating the file descriptor and opening the log file in the copied instance using the duplicated descriptor. This solves the problems (#1823). A GLog::is_open() method has been added to assess whether a log file is -actually opened (#1837). +actually opened. The GLog::is_empty() method has been added to assess whether +a log file is actually empty (#1837). + +The GLog::size() method now returns the actual size of the log file, including +the number of characters on disk. 5. Base module diff --git a/include/GLog.hpp b/include/GLog.hpp index 0723fdf..713ceb8 100644 --- a/include/GLog.hpp +++ b/include/GLog.hpp @@ -84,10 +84,12 @@ public: // Methods void clear(void); - int size(void) const; + long int size(void) const; std::string classname(void) const; bool is_open(void) const; - void open(const GFilename& filename, const bool& clobber = false); + bool is_empty(void) const; + void open(const GFilename& filename, + const bool& clobber = false); void close(void); void flush(const bool& force = false); void date(const bool& flag); @@ -160,6 +162,18 @@ bool GLog::is_open(void) const /***********************************************************************//** + * @brief Signal if log file is empty + * + * @return True if log file is empty, false otherwise. + ***************************************************************************/ +inline +bool GLog::is_empty(void) const +{ + return (size() == 0); +} + + +/***********************************************************************//** * @brief Write string as centred header into logger * * @param[in] arg Header string. diff --git a/pyext/GLog.i b/pyext/GLog.i index a36d332..4b2c5b1 100644 --- a/pyext/GLog.i +++ b/pyext/GLog.i @@ -45,10 +45,12 @@ public: // Methods void clear(void); - int size(void) const; + long int size(void) const; std::string classname(void) const; bool is_open(void) const; - void open(const GFilename& filename, const bool& clobber = false); + bool is_empty(void) const; + void open(const GFilename& filename, + const bool& clobber = false); void close(void); void flush(const bool& force = false); void date(const bool& flag); diff --git a/src/app/GApplication.cpp b/src/app/GApplication.cpp index 6f13ae7..81e3dc2 100644 --- a/src/app/GApplication.cpp +++ b/src/app/GApplication.cpp @@ -348,15 +348,28 @@ void GApplication::logFileOpen(const bool& clobber) /***********************************************************************//** * @brief Close log file * - * Logs trailer and close application log file. + * Writer trailer into the log file and close application log file. + * + * The trailer is only written in case that there is some content in the + * log file. ***************************************************************************/ void GApplication::logFileClose(void) { // Continue only if log file is open if (log.is_open()) { - // Write trailer into log file - log_trailer(); + // If log file is not empty then write the trailer into log file. + // This kluge avoids writing a trailer in an empty log file that + // is for example occuring in copy operations. + if (!log.is_empty()) { + + // Put line feed + log << std::endl; + + // Write trailer into logger + log_trailer(); + + } // endif: logger was not empty // Close log file log.close(); @@ -850,6 +863,8 @@ void GApplication::init_members(void) * @brief Copy class members * * @param[in] app Application. + * + * Copies all class members. ***************************************************************************/ void GApplication::copy_members(const GApplication& app) { diff --git a/src/app/GLog.cpp b/src/app/GLog.cpp index 44793c4..5200d3e 100644 --- a/src/app/GLog.cpp +++ b/src/app/GLog.cpp @@ -460,12 +460,21 @@ void GLog::clear(void) /***********************************************************************//** - * @brief Return the number of characters that are actually in the buffer + * @brief Return the number of characters in the log file + * + * @return Number of characters in log file. ***************************************************************************/ -int GLog::size(void) const +long int GLog::size(void) const { - // Return - return m_buffer.size(); +std::cout << "GLog::size m_buffer.size()=" << m_buffer.size() << std::endl; + // Determine the number of characters written to disk + long int size = (m_file != NULL) ? std::ftell(m_file) : 0; + + // Add in buffer size + size += m_buffer.size(); + + // Return total size + return size; } @@ -556,7 +565,9 @@ void GLog::init_members(void) * @param[in] log Logger. * * Copy all class members from @p log to the current logger instance. This - * method will flush the buffer of @p log before copying. + * method will flush the buffer of @p log before copying. It will duplicate + * the file pointer on the log file so that the copy can safely work on the + * file without having to worry that the file pointer goes out of scope. ***************************************************************************/ void GLog::copy_members(const GLog& log) { @@ -566,7 +577,7 @@ void GLog::copy_members(const GLog& log) // doing this. const_cast(&log)->flush(true); - // Copy attributes + // Copy buffer attributes m_max_length = log.m_max_length; m_indent = log.m_indent; m_stdout = log.m_stdout; @@ -615,16 +626,17 @@ void GLog::free_members(void) * * @param[in] force If true, force flushing. * - * Flush string buffer if it is full or if flushing is enforced. This method - * writes the output string in the relevant streams. It decomposes the - * buffer in lines that a separated by a '\n' character. The following - * streams are currently implemented (and will be filled in parallel): - * stdout, stderr, and an ASCII file. + * Flush string buffer if it is full or if flushing is enforced. + * + * This method writes the output string in the relevant streams. It + * decomposes the buffer in lines that a separated by a '\n' character. + * The following streams are currently implemented (and will be filled in + * parallel): stdout, stderr, and - if available - an ASCII file. ***************************************************************************/ void GLog::flush(const bool& force) { // Check if buffer should be flushed - bool flush = (force || size() > m_max_length); + bool flush = (force || m_buffer.size() > m_max_length); // Flush buffer if (flush) { @@ -801,7 +813,7 @@ void GLog::append(std::string arg) // character is a \n then prepend a prefix at the beginning of the string // to be inserted. if ((m_buffer.size() == 0 && m_linestart) || - (m_buffer.size() > 0 && m_buffer[m_buffer.size()-1] == '\n')) { + (m_buffer.size() > 0 && m_buffer[m_buffer.size()-1] == '\n')) { // Prepend prefix arg.insert(0, prefix()); diff --git a/test/data/test_GApplication.par b/test/data/test_GApplication.par index d242e9e..99f9db2 100644 --- a/test/data/test_GApplication.par +++ b/test/data/test_GApplication.par @@ -5,4 +5,4 @@ chatter, i, h, 2,0,4, "Chattiness of output" clobber, b, h, yes,,, "Overwrite existing output files with new output files?" debug, b, h, no,,, "Debugging mode activated" mode, s, h, ql,,, "Mode of automatic parameters" -logfile, f, h, test_logfile.log,,, "Log filename" +logfile, f, h, test_application.log,,, "Log filename" diff --git a/test/test_GApplication.cpp b/test/test_GApplication.cpp index aa4af10..5d3af7f 100644 --- a/test/test_GApplication.cpp +++ b/test/test_GApplication.cpp @@ -44,11 +44,11 @@ void TestGApplication::set(void) name("GApplication"); // Append tests - append(static_cast(&TestGApplication::test_constructor), + append(static_cast(&TestGApplication::test_GLog), "Test GLog constructor"); - append(static_cast(&TestGApplication::test_stream_logger), + append(static_cast(&TestGApplication::test_GLog_stream), "Test stream logger"); - append(static_cast(&TestGApplication::test_C_logger), + append(static_cast(&TestGApplication::test_GLog_C), "Test C logger"); append(static_cast(&TestGApplication::test_GApplicationPar), "Test GApplicationPar class"); @@ -73,16 +73,106 @@ TestGApplication* TestGApplication::clone(void) const /***********************************************************************//** - * @brief Test GLog constructor + * @brief Test GLog class **************************************************************************/ -void TestGApplication::test_constructor(void) +void TestGApplication::test_GLog(void) { // Void constructor GLog log1; - // Copy constructor + // Check empty logger + test_assert(log1.is_empty(), "Check whether logger is empty"); + test_value(log1.size(), 0, "Check whether logger size is 0"); + test_assert(!log1.is_open(), "Check whether log file is closed"); + + // Copy empty logger GLog log2 = log1; + // Check whether copied logger isempty + test_assert(log2.is_empty(), "Check whether copied logger is empty"); + test_value(log2.size(), 0, "Check whether copied logger size is 0"); + test_assert(!log2.is_open(), "Check whether copied log file is closed"); + + // Write a few characters and check the logger size + log2 << "Honeymoon"; + test_value(log2.size(), 9, "Check logger size in memory"); + + // Flush to disk for a closed file. This should make the text disappear. + log2.flush(true); + test_value(log2.size(), 0, + "Check logger size in memory after flushing without file"); + test_assert(log2.is_empty(), + "Check whether logger is empty after flushing without file"); + + // Open log file and check whether it's open now + log2.open("test_logfile.log", true); + test_assert(log2.is_open(), "Check whether log file is now open"); + + // Write a few characters and check the logger size + log2 << "Honeymoon"; + test_value(log2.size(), 9, "Check logger size in memory"); + + // Flush to disk and check again the logger size + log2.flush(true); + test_value(log2.size(), 9, + "Check logger size on disk after flushing to disk"); + test_assert(!log2.is_empty(), + "Check whether logger is not empty after flushing to disk"); + + // Write more characters and check the logger size + log2 << " and Milk!"; + test_value(log2.size(), 19, "Check logger size on disk+memory"); + + // Close log file and check whether the logger is empty again + log2.close(); + test_assert(log2.is_empty(), "Check whether logger is empty again"); + test_value(log2.size(), 0, "Check whether logger size is again 0"); + + // Open log file again and check whether it's open now + log1.open("test_logfile.log", true); + test_assert(log1.is_open(), "Check whether log file is now open"); + + // Write a few characters and check the logger size + log1 << "Honeymoon"; + test_value(log1.size(), 9, "Check logger size in memory"); + + // Copy filled logger + log2 = log1; + + // Now fill both loggers with more characters + log1 << " and"; + log2 << " Milk!"; + + // Check the logger size in memory. They should be different. + test_value(log1.size(), 13, "Check logger 1 size in memory"); + test_value(log2.size(), 15, "Check logger 2 size in memory"); + + // Flush both loggers. Each logger flushes what he has. + log1.flush(true); + log2.flush(true); + + // Check that logger size on disk. Both loggers flushed their + // content, first logger 1, then logger 2, and the sum should + // be on disk. Both logger sizes are now identical. + test_value(log1.size(), 19, "Check logger 1 size on disk"); + test_value(log2.size(), 19, "Check logger 2 size on disk"); + + // Close loggers + log1.close(); + log2.close(); + + // Check if log file exists + char line[100]; + FILE *fp = fopen("test_logfile.log", "r"); + test_assert(fp != NULL, "Check if logfile exists"); + + // If log file exists then check it and close file + if (fp != NULL) { + fgets(line, 100, fp); + test_value(line, "Honeymoon and Milk!", "Check log file content"); + fclose(fp); + } + // Return return; } @@ -91,7 +181,7 @@ void TestGApplication::test_constructor(void) /***********************************************************************//** * @brief Test stream logger **************************************************************************/ -void TestGApplication::test_stream_logger(void) +void TestGApplication::test_GLog_stream(void) { // Test GLog log; @@ -114,7 +204,7 @@ void TestGApplication::test_stream_logger(void) /***********************************************************************//** * @brief Test C logger **************************************************************************/ -void TestGApplication::test_C_logger(void) +void TestGApplication::test_GLog_C(void) { // Test GLog log; @@ -377,7 +467,7 @@ void TestGApplication::test_GApplication(void) // Test log filename test_value(app1.par_filename(), "test_GApplication.par", "Check application parameter file name"); - test_value(app1.log_filename(), "test_logfile.log", + test_value(app1.log_filename(), "test_application.log", "Check application log file name"); // Write something into the logfile and close it @@ -406,7 +496,7 @@ void TestGApplication::test_GApplication(void) // Check if log file exists char line[200]; - FILE *fp = fopen("test_logfile.log", "r"); + FILE *fp = fopen("test_application.log", "r"); test_assert(fp != NULL, "Check if logfile exists"); // If log file exists then check it line by line @@ -491,21 +581,24 @@ void TestGApplication::test_GApplication(void) test_value(line, " mode ......................: ql\n", "Check log file line 24"); fgets(line, 100, fp); - test_value(line, " logfile ...................: test_logfile.log\n", + test_value(line, " logfile ...................: test_application.log\n", "Check log file line 25"); fgets(line, 32, fp); test_value(line, "Application \"test_GApplication\"", "Check log file line 26"); + // Empty remaining characters (not checked since they are machine // dependent) fgets(line, 200, fp); + fgets(line, 100, fp); + test_value(line, "\n", "Check log file line 27"); fgets(line, 32, fp); test_value(line, "Application \"test_GApplication\"", - "Check log file line 27"); + "Check log file line 28"); // Close log file fclose(fp); - + } // endif: log file existed // Return diff --git a/test/test_GApplication.hpp b/test/test_GApplication.hpp index a243c50..a9e0919 100644 --- a/test/test_GApplication.hpp +++ b/test/test_GApplication.hpp @@ -47,9 +47,9 @@ public: virtual void set(void); virtual TestGApplication* clone(void) const; virtual std::string classname(void) const { return "TestGApplication"; } - void test_constructor(void); - void test_stream_logger(void); - void test_C_logger(void); + void test_GLog(void); + void test_GLog_stream(void); + void test_GLog_C(void); void test_GApplicationPar(void); void test_GApplication(void); };