Commit 18c1194e7d762eae0686a0b8b3e01c55e7beb35a

Authored by knodlseder
1 parent 7fedf106

first working implementation of logger

include/GLog.hpp
... ... @@ -20,40 +20,94 @@
20 20 #define GLOG_HPP
21 21  
22 22 /* __ Includes ___________________________________________________________ */
23   -#include <time.h>
24   -#include <vector>
25 23 #include <string>
26   -#include <iostream>
  24 +
  25 +/* __ Forward declarations _______________________________________________ */
27 26  
28 27  
29 28 /***********************************************************************//**
30 29 * @class GLog
31 30 *
32 31 * @brief Information logger interface defintion.
  32 + *
  33 + * This class implements an interface for logging of text messages to the
  34 + * standard output and/or error streams and into an ASCII log file. It
  35 + * provides a C++ style interface (allowing to stream messages using code
  36 + * such as
  37 + * log << "This is a message";
  38 + * and a C style interface that allows making use of C style text formating
  39 + * using code such as
  40 + * log("These are %d messages with %d items.", n, m);
  41 + * The logger interface implements an internal character buffer that is
  42 + * flushed once a maximum size parameter is exceeded. The buffer limit can
  43 + * be modified using the max_size() method; by default it is set to 8192
  44 + * characters. Note that the buffer is flushed each time a carriage return
  45 + * occurs, irrespectively of the buffer size.
  46 + * The logger allows prepending of the current date and the task name. Note
  47 + * that the current date is the one that applies when the buffer is actually
  48 + * flushed, hence when a good precision in the date is required the maximum
  49 + * buffer length should be set to 0 (i.e. the buffer is flushed after each
  50 + * logging).
33 51 ***************************************************************************/
34 52 class GLog {
35 53  
36   - // I/O friends
37   - friend std::ostream& operator<< (std::ostream& os, const GLog& log);
38   -
39 54 public:
40 55 // Constructors and destructors
41 56 GLog(void);
  57 + GLog(const std::string& filename, bool clobber = false);
42 58 GLog(const GLog& log);
43 59 ~GLog(void);
44 60  
45 61 // Operators
46 62 GLog& operator= (const GLog& log);
  63 + void operator()(const char *msgFormat, ...);
  64 + GLog& operator<<(const std::string& value);
  65 + GLog& operator<<(const bool& value);
  66 + GLog& operator<<(const int& value);
  67 + GLog& operator<<(const double& value);
  68 + GLog& operator<<(const char& value);
  69 + GLog& operator<<(std::ostream& (*fn)(std::ostream&));
47 70  
48 71 // Methods
49   -
  72 + void clear(void);
  73 + int size(void) const;
  74 + void open(const std::string& filename, bool clobber = false);
  75 + void close(void);
  76 + void date(bool flag);
  77 + void cout(bool flag);
  78 + void cerr(bool flag);
  79 + void name(const std::string& name);
  80 + void max_size(int size);
  81 + void indent(int indent);
  82 + void header0(const std::string& arg) { header(arg, 0); }
  83 + void header1(const std::string& arg) { header(arg, 1); }
  84 + void header2(const std::string& arg) { header(arg, 2); }
  85 + bool date(void) const { return m_use_date; }
  86 + bool cout(void) const { return m_stdout; }
  87 + bool cerr(void) const { return m_stderr; }
  88 + std::string name(void) const { return m_name; }
  89 + int max_size(void) const { return m_max_length; }
  90 + int indent(void) const { return m_indent; }
  91 +
50 92 protected:
51 93 // Protected methods
52   - void init_members(void);
53   - void copy_members(const GLog& log);
54   - void free_members(void);
  94 + void init_members(void);
  95 + void copy_members(const GLog& log);
  96 + void free_members(void);
  97 + void flush(bool force = false);
  98 + void header(const std::string& arg, int level);
  99 + std::string strdate(void);
55 100  
56 101 // Protected data members
  102 + int m_max_length; //!< Maximum buffer length
  103 + int m_indent; //!< Indentation of text
  104 + bool m_stdout; //!< Dump in standard output
  105 + bool m_stderr; //!< Dump in standard error
  106 + bool m_use_date; //!< Dump date in prefix
  107 + bool m_newline; //!< Signal beginning of new line
  108 + FILE* m_file; //!< Log file pointer
  109 + std::string m_name; //!< Name for prefix
  110 + std::string m_buffer; //!< Output string buffer
57 111 };
58 112  
59 113 #endif /* GLOG_HPP */
... ...
src/app/GLog.cpp
... ... @@ -17,7 +17,16 @@
17 17 */
18 18  
19 19 /* __ Includes ___________________________________________________________ */
  20 +#ifdef HAVE_CONFIG_H
  21 +#include <config.h>
  22 +#endif
  23 +#include <string>
  24 +#include <iostream>
  25 +#include <sstream>
  26 +#include <time.h>
  27 +#include <stdarg.h> // for "va_list" type
20 28 #include "GLog.hpp"
  29 +#include "GTools.hpp"
21 30  
22 31 /* __ Method name definitions ____________________________________________ */
23 32  
... ... @@ -48,6 +57,27 @@ GLog::GLog(void)
48 57  
49 58  
50 59 /***********************************************************************//**
  60 + * @brief Log file constructor
  61 + *
  62 + * @param[in] filename Name of log file.
  63 + * @param[in] clobber If true overwrite any existing file, otherwise append.
  64 + *
  65 + * Construct a logger with an open log file.
  66 + ***************************************************************************/
  67 +GLog::GLog(const std::string& filename, bool clobber)
  68 +{
  69 + // Initialise private members for clean destruction
  70 + init_members();
  71 +
  72 + // Open file
  73 + open(filename, clobber);
  74 +
  75 + // Return
  76 + return;
  77 +}
  78 +
  79 +
  80 +/***********************************************************************//**
51 81 * @brief Copy constructor
52 82 *
53 83 * @param[in] log Object from which the instance should be built.
... ... @@ -110,12 +140,364 @@ GLog&amp; GLog::operator= (const GLog&amp; log)
110 140 }
111 141  
112 142  
  143 +/**************************************************************************//**
  144 + * @brief Message logging operator
  145 + *
  146 + * @param[in] msgFormat Pointer to message format.
  147 + * @param[in] ... Message to put into log.
  148 + *
  149 + * This operator provides message logging capabilities in C style. It expects
  150 + * a C-style format string followed by an arbitrary number of arguments that
  151 + * is used to fill the format.
  152 + *
  153 + * @todo A static buffer of length 8192 is allocated. Although this is likely
  154 + * sufficient for any normal application there is nothing that prevents in
  155 + * principle the overflow of the buffer. A more sophisticated scheme should be
  156 + * implemented.
  157 + ******************************************************************************/
  158 +void GLog::operator()(const char *msgFormat, ...)
  159 +{
  160 + // Allocate message buffer
  161 + char buffer[8192];
  162 +
  163 + // Put message into buffer
  164 + va_list vl;
  165 + va_start(vl, msgFormat);
  166 + vsprintf(buffer, msgFormat, vl);
  167 + va_end(vl);
  168 +
  169 + // Append message to string buffer
  170 + m_buffer.append(buffer);
  171 + m_buffer.append("\n");
  172 +
  173 + // Flush buffer
  174 + flush(true);
  175 +
  176 + // Return
  177 + return;
  178 +
  179 +}
  180 +
  181 +
  182 +/***********************************************************************//**
  183 + * @brief Insert string into output stream
  184 + *
  185 + * @param[in] value String to be inserted.
  186 + ***************************************************************************/
  187 +GLog& GLog::operator<<(const std::string& value)
  188 +{
  189 + // Add string to buffer
  190 + m_buffer.append(value);
  191 +
  192 + // Flush buffer
  193 + flush();
  194 +
  195 + // Return logger
  196 + return *this;
  197 +}
  198 +
  199 +
  200 +/***********************************************************************//**
  201 + * @brief Insert bool into output stream
  202 + *
  203 + * @param[in] value Boolean to be inserted.
  204 + ***************************************************************************/
  205 +GLog& GLog::operator<<(const bool& value)
  206 +{
  207 + // Add boolean to buffer
  208 + if (value)
  209 + m_buffer.append("true");
  210 + else
  211 + m_buffer.append("false");
  212 +
  213 + // Flush buffer
  214 + flush();
  215 +
  216 + // Return logger
  217 + return *this;
  218 +}
  219 +
  220 +
  221 +/***********************************************************************//**
  222 + * @brief Insert integer into output stream
  223 + *
  224 + * @param[in] value Integer to be inserted.
  225 + ***************************************************************************/
  226 +GLog& GLog::operator<<(const int& value)
  227 +{
  228 + // Allocate output stream
  229 + std::ostringstream oss;
  230 +
  231 + // Put value into stream
  232 + oss << value;
  233 +
  234 + // Append stream to buffer
  235 + m_buffer.append(oss.str());
  236 +
  237 + // Flush buffer
  238 + flush();
  239 +
  240 + // Return logger
  241 + return *this;
  242 +}
  243 +
  244 +
  245 +/***********************************************************************//**
  246 + * @brief Insert double precision value into output stream
  247 + *
  248 + * @param[in] value Double precision value to be inserted.
  249 + ***************************************************************************/
  250 +GLog& GLog::operator<<(const double& value)
  251 +{
  252 + // Allocate output stream
  253 + std::ostringstream oss;
  254 +
  255 + // Put value into stream
  256 + oss << value;
  257 +
  258 + // Append stream to buffer
  259 + m_buffer.append(oss.str());
  260 +
  261 + // Flush buffer
  262 + flush();
  263 +
  264 + // Return logger
  265 + return *this;
  266 +}
  267 +
  268 +
  269 +/***********************************************************************//**
  270 + * @brief Insert character value into output stream
  271 + *
  272 + * @param[in] value Character value to be inserted.
  273 + ***************************************************************************/
  274 +GLog& GLog::operator<<(const char& value)
  275 +{
  276 + // Allocate output stream
  277 + std::ostringstream oss;
  278 +
  279 + // Put value into stream
  280 + oss << value;
  281 +
  282 + // Append stream to buffer
  283 + m_buffer.append(oss.str());
  284 +
  285 + // Flush buffer
  286 + flush();
  287 +
  288 + // Return logger
  289 + return *this;
  290 +}
  291 +
  292 +
  293 +/***********************************************************************//**
  294 + * @brief Ostream function output operator
  295 + *
  296 + * @param[in] fn Ostream function.
  297 + *
  298 + * The function output operator is interpreted as a line feed operator.
  299 + *
  300 + * @todo This is a quick an dirty implementation of the std::endl operator.
  301 + * A clean GLog::endl operator should be implemented instead, yet this needs
  302 + * some deeper understanding of our the
  303 + ***************************************************************************/
  304 +GLog& GLog::operator<<(std::ostream& (*fn)(std::ostream&))
  305 +{
  306 + // Append CR to buffer
  307 + m_buffer.append("\n");
  308 +
  309 + // Flush buffer
  310 + flush(true);
  311 +
  312 + // Return output stream
  313 + return *this;
  314 +}
  315 +
  316 +
113 317 /*==========================================================================
114 318 = =
115 319 = Public methods =
116 320 = =
117 321 ==========================================================================*/
118 322  
  323 +/***********************************************************************//**
  324 + * @brief Clear object
  325 + ***************************************************************************/
  326 +void GLog::clear(void)
  327 +{
  328 + // Free members
  329 + free_members();
  330 +
  331 + // Init members
  332 + init_members();
  333 +
  334 + // Return
  335 + return;
  336 +}
  337 +
  338 +
  339 +/***********************************************************************//**
  340 + * @brief Return the number of characters that are actually in the buffer
  341 + ***************************************************************************/
  342 +int GLog::size(void) const
  343 +{
  344 + // Return
  345 + return m_buffer.size();
  346 +}
  347 +
  348 +
  349 +/***********************************************************************//**
  350 + * @brief Open log file
  351 + *
  352 + * @param[in] filename Name of log file.
  353 + * @param[in] clobber If true overwrite any existing file, otherwise append.
  354 + *
  355 + * Opens a file for logging. If a log file was already open it is closed
  356 + * before opening a new file.
  357 + ***************************************************************************/
  358 +void GLog::open(const std::string& filename, bool clobber)
  359 +{
  360 + // Close any existing file
  361 + close();
  362 +
  363 + // Open file
  364 + if (clobber)
  365 + m_file = fopen(filename.c_str(), "w");
  366 + else
  367 + m_file = fopen(filename.c_str(), "a");
  368 +
  369 + // Return
  370 + return;
  371 +}
  372 +
  373 +
  374 +/***********************************************************************//**
  375 + * @brief Close log file
  376 + *
  377 + * Close log file. This method even works if no file has been opened. In any
  378 + * case flushing of the buffer is enforced.
  379 + ***************************************************************************/
  380 +void GLog::close(void)
  381 +{
  382 + // Flush buffer
  383 + flush(true);
  384 +
  385 + // Close any open file
  386 + if (m_file != NULL) {
  387 + fclose(m_file);
  388 + m_file = NULL;
  389 + }
  390 +
  391 + // Return
  392 + return;
  393 +}
  394 +
  395 +
  396 +/***********************************************************************//**
  397 + * @brief Set data flag that controls date prefixing
  398 + *
  399 + * @param[in] flag Enable/disable date flag (true/false).
  400 + ***************************************************************************/
  401 +void GLog::date(bool flag)
  402 +{
  403 + // Set flag
  404 + m_use_date = flag;
  405 +
  406 + // Return
  407 + return;
  408 +}
  409 +
  410 +
  411 +/***********************************************************************//**
  412 + * @brief Enables/disables logging into standard output stream
  413 + *
  414 + * @param[in] flag Enable/disable logging (true/false).
  415 + *
  416 + * Enables or disables logging into the standard output stream.
  417 + ***************************************************************************/
  418 +void GLog::cout(bool flag)
  419 +{
  420 + // Set flag
  421 + m_stdout = flag;
  422 +
  423 + // Return
  424 + return;
  425 +}
  426 +
  427 +
  428 +/***********************************************************************//**
  429 + * @brief Enables/disables logging into standard error stream
  430 + *
  431 + * @param[in] flag Enable/disable logging (true/false).
  432 + *
  433 + * Enables or disables logging into the standard error stream.
  434 + ***************************************************************************/
  435 +void GLog::cerr(bool flag)
  436 +{
  437 + // Set flag
  438 + m_stderr = flag;
  439 +
  440 + // Return
  441 + return;
  442 +}
  443 +
  444 +
  445 +/***********************************************************************//**
  446 + * @brief Set name to put into prefix
  447 + *
  448 + * @param[in] name Name to put into prefix.
  449 + ***************************************************************************/
  450 +void GLog::name(const std::string& name)
  451 +{
  452 + // Set name
  453 + m_name = name;
  454 +
  455 + // Return
  456 + return;
  457 +}
  458 +
  459 +
  460 +/***********************************************************************//**
  461 + * @brief Set the maximum buffer size
  462 + *
  463 + * @param[in] size Maximum buffer size.
  464 + *
  465 + * Set the maximum number of characters allowed in the internal buffer before
  466 + * the buffer is flushed into the specified streams or file.
  467 + *
  468 + * @todo No maximum buffer size checking is performed. This could be
  469 + * implemented but is not really crucial since the physical buffer size can
  470 + * indeed be larger than this maximum size.
  471 + ***************************************************************************/
  472 +void GLog::max_size(int size)
  473 +{
  474 + // Set size
  475 + m_max_length = size;
  476 +
  477 + // Return
  478 + return;
  479 +}
  480 +
  481 +
  482 +/***********************************************************************//**
  483 + * @brief Set indentation
  484 + *
  485 + * @param[in] indent Indentation in number of characters.
  486 + *
  487 + * Set the indentation of the logger. This specifies the number of whitespace
  488 + * characters that is added between the prefix and the log message of each
  489 + * line. Indentation allows for easy formatting of text in the log file.
  490 + ***************************************************************************/
  491 +void GLog::indent(int indent)
  492 +{
  493 + // Set size
  494 + m_indent = indent;
  495 +
  496 + // Return
  497 + return;
  498 +}
  499 +
  500 +
119 501 /*==========================================================================
120 502 = =
121 503 = Private methods =
... ... @@ -128,6 +510,15 @@ GLog&amp; GLog::operator= (const GLog&amp; log)
128 510 void GLog::init_members(void)
129 511 {
130 512 // Initialise members
  513 + m_max_length = 8192;
  514 + m_indent = 0;
  515 + m_stdout = false;
  516 + m_stderr = false;
  517 + m_use_date = false;
  518 + m_newline = true;
  519 + m_file = NULL;
  520 + m_name.clear();
  521 + m_buffer.clear();
131 522  
132 523 // Return
133 524 return;
... ... @@ -142,6 +533,15 @@ void GLog::init_members(void)
142 533 void GLog::copy_members(const GLog& log)
143 534 {
144 535 // Copy attributes
  536 + m_max_length = log.m_max_length;
  537 + m_indent = log.m_indent;
  538 + m_stdout = log.m_stdout;
  539 + m_stderr = log.m_stderr;
  540 + m_use_date = log.m_use_date;
  541 + m_newline = log.m_newline;
  542 + m_name = log.m_name;
  543 + m_buffer = log.m_buffer;
  544 + m_file = log.m_file;
145 545  
146 546 // Return
147 547 return;
... ... @@ -150,33 +550,180 @@ void GLog::copy_members(const GLog&amp; log)
150 550  
151 551 /***********************************************************************//**
152 552 * @brief Delete class members
  553 + *
  554 + * Deletes class member after flushing the buffer and closing the log file.
153 555 ***************************************************************************/
154 556 void GLog::free_members(void)
155 557 {
  558 + // Flush buffer
  559 + flush(true);
  560 +
  561 + // Close log file
  562 + close();
  563 +
156 564 // Return
157 565 return;
158 566 }
159 567  
160 568  
161   -/*==========================================================================
162   - = =
163   - = Friends =
164   - = =
165   - ==========================================================================*/
  569 +/***********************************************************************//**
  570 + * @brief Flush string buffer into log file
  571 + *
  572 + * @param[in] force If true, force flushing.
  573 + *
  574 + * Flush string buffer if it is full or if flushing is enforced. This method
  575 + * writes the output string in the relevant streams. It decomposes the
  576 + * buffer in lines that a separated by a '\n' character and preprends, if
  577 + * requested, the current date and name. The following streams are currently
  578 + * implemented (and will be filled in parallel): stdout, stderr, and an ASCII
  579 + * file.
  580 + ***************************************************************************/
  581 +void GLog::flush(bool force)
  582 +{
  583 + // Check if buffer should be flushed
  584 + bool flush = (force || size() > m_max_length);
  585 +
  586 + // Flush buffer
  587 + if (flush) {
  588 +
  589 + // Flush buffer until it is empty
  590 + while (m_buffer.size() > 0) {
  591 +
  592 + // Initialise newline for next line
  593 + bool newline = m_newline;
  594 +
  595 + // Find next CR
  596 + std::string line;
  597 + size_t pos = m_buffer.find_first_of("\n", 0);
  598 + if (pos == std::string::npos) {
  599 + line = m_buffer;
  600 + newline = false;
  601 + m_buffer.clear();
  602 + }
  603 + else {
  604 + line = m_buffer.substr(0, pos) + "\n";
  605 + m_buffer = m_buffer.substr(pos+1, m_buffer.size()-pos);
  606 + newline = true;
  607 + }
  608 +
  609 + // Prepend prefix if we are at the beginning of a new line
  610 + if (m_newline) {
  611 + std::string prefix = "";
  612 + if (m_use_date)
  613 + prefix.append(strdate());
  614 + if (m_name.length() > 0)
  615 + prefix.append(" "+m_name);
  616 + if (prefix.length() > 0)
  617 + prefix.append(": ");
  618 + line = prefix + fill(" ", m_indent) + line;
  619 + }
  620 +
  621 + // Put line into requested streams
  622 + if (m_stdout)
  623 + std::cout << line;
  624 + if (m_stderr)
  625 + std::cerr << line;
  626 + if (m_file != NULL)
  627 + fprintf(m_file, line.c_str());
  628 +
  629 + // Set newline flag for next line
  630 + m_newline = newline;
  631 +
  632 + } // endwhile: flush until empty
  633 +
  634 + } // endif: flush was required
  635 +
  636 + // Return
  637 + return;
  638 +}
  639 +
166 640  
167 641 /***********************************************************************//**
168   - * @brief Put object in output stream
  642 + * @brief Write string as header into logger
  643 + *
  644 + * @param[in] arg Header string.
  645 + * @param[in] level Header level (0,1,...).
169 646 *
170   - * @param[in] os Output stream into which the model will be dumped
171   - * @param[in] log Object to be dumped
  647 + * Writes a string as header into the logger. Various header levels exist:
  648 + * 0: 80 character wide centred header
  649 + * 1: Header framed by '=' characters
  650 + * 2: Header framed by '-' characters
172 651 ***************************************************************************/
173   -std::ostream& operator<< (std::ostream& os, const GLog& log)
  652 +void GLog::header(const std::string& arg, int level)
174 653 {
175   - // Put object in stream
176   - os << "=== GLog ===" << std::endl;
  654 + // Declare frame and text strings
  655 + std::string frame;
  656 + std::string text;
  657 +
  658 + // Create level dependent header strings
  659 + switch (level) {
  660 + case 0:
  661 + break;
  662 + case 1:
  663 + case 2:
  664 + text = "| " + arg + " |";
  665 + frame = (level == 1) ? "+" + fill("=", text.length()-2) + "+"
  666 + : "+" + fill("-", text.length()-2) + "+";
  667 + break;
  668 + default:
  669 + break;
  670 + }
  671 +
  672 + // Write header into logger
  673 + if (text.length() > 0) {
  674 + m_buffer.append(frame);
  675 + m_buffer.append("\n");
  676 + m_buffer.append(text);
  677 + m_buffer.append("\n");
  678 + m_buffer.append(frame);
  679 + m_buffer.append("\n");
  680 + flush(true);
  681 + }
177 682  
178   - // Return output stream
179   - return os;
  683 + // Return
  684 + return;
  685 +}
  686 +
  687 +
  688 +/***********************************************************************//**
  689 + * @brief Return current date
  690 + *
  691 + * Returns the current date as string in the format yyyy-mm-ddThh:mm:ss.
  692 + ***************************************************************************/
  693 +std::string GLog::strdate(void)
  694 +{
  695 + // Allocate variables
  696 + struct tm timeStruct;
  697 + time_t now;
  698 + char buffer[100];
  699 +
  700 + // Get time
  701 + now = time(NULL);
  702 + #ifdef HAVE_GMTIME_R
  703 + gmtime_r(&now, &timeStruct);
  704 + #else
  705 + memcpy(&timeStruct, gmtime(&now), sizeof(struct tm));
  706 + #endif
  707 +
  708 + // Write message type, time and task name to buffer
  709 + sprintf(buffer, "%04d-%02d-%02dT%02d:%02d:%02d",
  710 + timeStruct.tm_year + 1900,
  711 + timeStruct.tm_mon + 1,
  712 + timeStruct.tm_mday,
  713 + timeStruct.tm_hour,
  714 + timeStruct.tm_min,
  715 + timeStruct.tm_sec);
  716 +
  717 + // Build string from buffer
  718 + std::string date = buffer;
  719 +
  720 + // Return date
  721 + return date;
180 722 }
181 723  
182 724  
  725 +/*==========================================================================
  726 + = =
  727 + = Friends =
  728 + = =
  729 + ==========================================================================*/
... ...