GammaLib  2.1.0.dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GLog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GLog.cpp - Information logger *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2022 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GLog.cpp
23  * @brief Information logger class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 //#include <ctime>
32 #include <cstdarg> // for std::va_list type
33 #include <cstdio> // std::fopen, std::fgets, std::fclose, etc...
34 //#include <cstring> // std::memcpy
35 #include <string>
36 #include <iostream>
37 #include <sstream>
38 #include <fstream>
39 #include <unistd.h> // dup
40 #include "GLog.hpp"
41 #include "GTools.hpp"
42 
43 /* __ OpenMP section _____________________________________________________ */
44 #ifdef _OPENMP
45 #include <omp.h>
46 #endif
47 
48 /* __ Method name definitions ____________________________________________ */
49 
50 /* __ Macros _____________________________________________________________ */
51 
52 /* __ Coding definitions _________________________________________________ */
53 
54 /* __ Debug definitions __________________________________________________ */
55 
56 
57 /*==========================================================================
58  = =
59  = Constructors/destructors =
60  = =
61  ==========================================================================*/
62 
63 /***********************************************************************//**
64  * @brief Void constructor
65  ***************************************************************************/
67 {
68  // Initialise private members for clean destruction
69  init_members();
70 
71  // Return
72  return;
73 }
74 
75 
76 /***********************************************************************//**
77  * @brief Log file constructor
78  *
79  * @param[in] filename Name of log file.
80  * @param[in] clobber If true overwrite any existing file, otherwise append.
81  *
82  * Construct a logger with an open log file.
83  ***************************************************************************/
84 GLog::GLog(const GFilename& filename, const bool& clobber)
85 {
86  // Initialise private members for clean destruction
87  init_members();
88 
89  // Open file
90  open(filename, clobber);
91 
92  // Return
93  return;
94 }
95 
96 
97 /***********************************************************************//**
98  * @brief Copy constructor
99  *
100  * @param[in] log Object from which the instance should be built.
101  ***************************************************************************/
103 {
104  // Initialise private members for clean destruction
105  init_members();
106 
107  // Copy members
108  copy_members(log);
109 
110  // Return
111  return;
112 }
113 
114 
115 /***********************************************************************//**
116  * @brief Destructor
117  ***************************************************************************/
119 {
120  // Free members
121  free_members();
122 
123  // Return
124  return;
125 }
126 
127 
128 /*==========================================================================
129  = =
130  = Operators =
131  = =
132  ==========================================================================*/
133 
134 /***********************************************************************//**
135  * @brief Assignment operator
136  *
137  * @param[in] log Object which should be assigned.
138  ***************************************************************************/
140 {
141  // Execute only if object is not identical
142  if (this != &log) {
143 
144  // Free members
145  free_members();
146 
147  // Initialise private members for clean destruction
148  init_members();
149 
150  // Copy members
151  copy_members(log);
152 
153  } // endif: object was not identical
154 
155  // Return
156  return *this;
157 }
158 
159 
160 /**************************************************************************//**
161  * @brief Message logging operator
162  *
163  * @param[in] msgFormat Pointer to message format.
164  * @param[in] ... Message to put into log.
165  *
166  * This operator provides message logging capabilities in C style. It expects
167  * a C-style format string followed by an arbitrary number of arguments that
168  * is used to fill the format.
169  *
170  * @todo A static buffer of length 8192 is allocated. Although this is likely
171  * sufficient for any normal application there is nothing that prevents in
172  * principle the overflow of the buffer. A more sophisticated scheme should be
173  * implemented.
174  ******************************************************************************/
175 void GLog::operator()(const char *msgFormat, ...)
176 {
177  // Allocate message buffer
178  char buffer[8192];
179 
180  // Put message into buffer
181  std::va_list vl;
182  va_start(vl, msgFormat);
183  std::vsprintf(buffer, msgFormat, vl);
184  va_end(vl);
185 
186  // Append message to string buffer
187  append(std::string(buffer)+"\n");
188 
189  // Return
190  return;
191 
192 }
193 
194 
195 /***********************************************************************//**
196  * @brief Insert logger into logger
197  *
198  * @param[in] log Logger to be inserted.
199  *
200  * Insert a logger object into another logger object. This method handles
201  * information that has either been written into a file, or that is still
202  * present in the logger buffer.
203  ***************************************************************************/
205 {
206  // If log object has an open file then append the log buffer to this
207  // buffer
208  if (log.m_file == NULL) {
209  #pragma omp critical(GLog_insert_logger)
210  m_buffer.append(log.m_buffer);
211  }
212 
213  // ... otherwise read all information from file and append it to the
214  // buffer
215  else {
216 
217  // Force flush so that the log object writes the entire buffer
218  // into the file
219  log.flush(true);
220 
221  // Get the filename
222  GFilename filename = log.filename();
223 
224  // Close log's file
225  log.close();
226 
227  // Open log's file in read mode
228  std::ifstream file(filename.url().c_str(), std::ios::in);
229  if (file) {
230 
231  // Append all lines from file to this buffer
232  std::string line;
233  while(std::getline(file,line)) {
234  #pragma omp critical(GLog_insert_logger)
235  m_buffer.append(line+"\n");
236  }
237 
238  // Close log's file
239  file.close();
240  }
241 
242  // Open log's file in append mode
243  log.open(filename, false);
244  }
245 
246  // Flush
247  flush();
248 
249  // Return logger
250  return *this;
251 }
252 
253 
254 /***********************************************************************//**
255  * @brief Insert C++ string into logger
256  *
257  * @param[in] str C++ string to be inserted.
258  *
259  * This method insert a C++ string into the logger.
260  ***************************************************************************/
261 GLog& GLog::operator<<(const std::string& str)
262 {
263  // Add string to buffer
264  append(str);
265 
266  // Return logger
267  return *this;
268 }
269 
270 
271 /***********************************************************************//**
272  * @brief Insert C string into logger
273  *
274  * @param[in] str C string to be inserted.
275  *
276  * This method insert a C string into the logger.
277  ***************************************************************************/
278 GLog& GLog::operator<<(const char* str)
279 {
280  // Add string to buffer
281  append(std::string(str));
282 
283  // Return logger
284  return *this;
285 }
286 
287 
288 /***********************************************************************//**
289  * @brief Insert character value into logger
290  *
291  * @param[in] value Character value to be inserted.
292  *
293  * This method inserts a character value into the logger.
294  ***************************************************************************/
295 GLog& GLog::operator<<(const char& value)
296 {
297  // Allocate output stream
298  std::ostringstream oss;
299 
300  // Put value into stream
301  oss << value;
302 
303  // Append stream to buffer
304  append(oss.str());
305 
306  // Return logger
307  return *this;
308 }
309 
310 
311 /***********************************************************************//**
312  * @brief Insert unsigned character value into logger
313  *
314  * @param[in] value Unsigned character value to be inserted.
315  *
316  * This method inserts an unsigned character value into the logger.
317  ***************************************************************************/
318 GLog& GLog::operator<<(const unsigned char& value)
319 {
320  // Allocate output stream
321  std::ostringstream oss;
322 
323  // Put value into stream
324  oss << value;
325 
326  // Append stream to buffer
327  append(oss.str());
328 
329  // Return logger
330  return *this;
331 }
332 
333 
334 /***********************************************************************//**
335  * @brief Insert bool into logger
336  *
337  * @param[in] value Boolean to be inserted.
338  *
339  * Depending on the argument, this method inserts either the character string
340  * "true" or "false" into the logger.
341  ***************************************************************************/
342 GLog& GLog::operator<<(const bool& value)
343 {
344  // Add boolean to buffer
345  if (value) {
346  append("true");
347  }
348  else {
349  append("false");
350  }
351 
352  // Return logger
353  return *this;
354 }
355 
356 
357 /***********************************************************************//**
358  * @brief Insert integer into logger
359  *
360  * @param[in] value Integer to be inserted.
361  *
362  * This method inserts an integer into the logger.
363  ***************************************************************************/
364 GLog& GLog::operator<<(const int& value)
365 {
366  // Allocate output stream
367  std::ostringstream oss;
368 
369  // Put value into stream
370  oss << value;
371 
372  // Append stream to buffer
373  append(oss.str());
374 
375  // Return logger
376  return *this;
377 }
378 
379 
380 /***********************************************************************//**
381  * @brief Insert unsigned integer into logger
382  *
383  * @param[in] value Unsigned integer to be inserted.
384  *
385  * This method inserts an unsigned integer into the logger.
386  ***************************************************************************/
387 GLog& GLog::operator<<(const unsigned int& value)
388 {
389  // Allocate output stream
390  std::ostringstream oss;
391 
392  // Put value into stream
393  oss << value;
394 
395  // Append stream to buffer
396  append(oss.str());
397 
398  // Return logger
399  return *this;
400 }
401 
402 
403 /***********************************************************************//**
404  * @brief Insert double precision value into logger
405  *
406  * @param[in] value Double precision value to be inserted.
407  *
408  * This method inserts a double precision value into the logger.
409  ***************************************************************************/
410 GLog& GLog::operator<<(const double& value)
411 {
412  // Allocate output stream
413  std::ostringstream oss;
414 
415  // Put value into stream
416  oss << value;
417 
418  // Append stream to buffer
419  append(oss.str());
420 
421  // Return logger
422  return *this;
423 }
424 
425 
426 /***********************************************************************//**
427  * @brief Ostream function output operator
428  *
429  * @param[in] fn Ostream function.
430  *
431  * The function output operator is interpreted as a line feed operator.
432  *
433  * @todo This is a quick an dirty implementation of the std::endl operator.
434  * A clean GLog::endl operator should be implemented instead, yet this needs
435  * some deeper understanding of our the
436  ***************************************************************************/
437 GLog& GLog::operator<<(std::ostream& (*fn)(std::ostream&))
438 {
439  // Append CR to buffer
440  append("\n");
441 
442  // Return logger
443  return *this;
444 }
445 
446 
447 /*==========================================================================
448  = =
449  = Public methods =
450  = =
451  ==========================================================================*/
452 
453 /***********************************************************************//**
454  * @brief Clear object
455  ***************************************************************************/
456 void GLog::clear(void)
457 {
458  // Free members
459  free_members();
460 
461  // Init members
462  init_members();
463 
464  // Return
465  return;
466 }
467 
468 
469 /***********************************************************************//**
470  * @brief Return the number of characters in the log file
471  *
472  * @return Number of characters in log file.
473  ***************************************************************************/
474 long int GLog::size(void) const
475 {
476  // Initialise size
477  long int size = 0;
478 
479  // Put size determination in OpenMP critical zone
480  #pragma omp critical(GLog_flush)
481  {
482 
483  // Determine the number of characters written to disk
484  size = (m_file != NULL) ? std::ftell(m_file) : 0;
485 
486  // Add in buffer size
487  size += m_buffer.size();
488 
489  } // end of OpenMP critical zone
490 
491  // Return total size
492  return size;
493 }
494 
495 
496 /***********************************************************************//**
497  * @brief Open log file
498  *
499  * @param[in] filename Name of log file.
500  * @param[in] clobber If true overwrite any existing file, otherwise append.
501  *
502  * Opens a file for logging. If a log file was already open it is closed
503  * before opening a new file.
504  ***************************************************************************/
505 void GLog::open(const GFilename& filename, const bool& clobber)
506 {
507  // Store the filename
509 
510  // Close any existing file
511  close();
512 
513  // Put opening in OpenMP critical zone
514  #pragma omp critical(GLog_open)
515  {
516 
517  // Open file
518  if (clobber) {
519  m_file = std::fopen(filename.url().c_str(), "w");
520  }
521  else {
522  m_file = std::fopen(filename.url().c_str(), "a");
523  }
524 
525  } // end of OpenMP critical zone
526 
527  // Return
528  return;
529 }
530 
531 
532 /***********************************************************************//**
533  * @brief Close log file
534  *
535  * Close log file. This method even works if no file has been opened. In any
536  * case flushing of the buffer is enforced.
537  ***************************************************************************/
538 void GLog::close(void)
539 {
540  // Put closing in OpenMP critical zone
541  #pragma omp critical(GLog_open)
542  {
543 
544  // Flush buffer
545  flush(true);
546 
547  // Close any open file
548  if (m_file != NULL) {
549  std::fclose(m_file);
550  m_file = NULL;
551  }
552 
553  } // end of OpenMP critical zone
554 
555  // Return
556  return;
557 }
558 
559 
560 /*==========================================================================
561  = =
562  = Private methods =
563  = =
564  ==========================================================================*/
565 
566 /***********************************************************************//**
567  * @brief Initialise class members
568  ***************************************************************************/
570 {
571  // Initialise members
572  m_max_length = 8192;
573  m_indent = 0;
574  m_written_size = 0;
575  m_stdout = false;
576  m_stderr = false;
577  m_use_date = false;
578  m_linestart = true;
579  m_file = NULL;
580  m_chatter = NORMAL;
581  m_filename.clear();
582  m_name.clear();
583  m_buffer.clear();
584 
585  // Return
586  return;
587 }
588 
589 
590 /***********************************************************************//**
591  * @brief Copy class members
592  *
593  * @param[in] log Logger.
594  *
595  * Copy all class members from @p log to the current logger instance. This
596  * method will flush the buffer of @p log before copying. It will duplicate
597  * the file pointer on the log file so that the copy can safely work on the
598  * file without having to worry that the file pointer goes out of scope.
599  ***************************************************************************/
601 {
602  // Force flushing the buffer before doing the copy. This assures that the
603  // log file is left in a clean state, and in particular, that logging
604  // will be done in order. We have to circument const correctness for
605  // doing this.
606  const_cast<GLog*>(&log)->flush(true);
607 
608  // Copy buffer attributes
610  m_indent = log.m_indent;
611  m_stdout = log.m_stdout;
612  m_stderr = log.m_stderr;
613  m_use_date = log.m_use_date;
614  m_linestart = log.m_linestart;
615  m_filename = log.m_filename;
616  m_name = log.m_name;
617  m_buffer = log.m_buffer;
618  m_chatter = log.m_chatter;
619 
620  // Copy file pointer by duplicating the file descriptor and opening
621  // the duplicated file descriptor
622  if (log.m_file != NULL) {
623  #pragma omp critical(GLog_copy_members)
624  m_file = fdopen(dup(fileno(log.m_file)), "a");
625  }
626  else {
627  m_file = NULL;
628  }
629 
630  // Return
631  return;
632 }
633 
634 
635 /***********************************************************************//**
636  * @brief Delete class members
637  *
638  * Deletes class member after flushing the buffer and closing the log file.
639  ***************************************************************************/
641 {
642  // Flush buffer
643  flush(true);
644 
645  // Close log file
646  close();
647 
648  // Return
649  return;
650 }
651 
652 
653 /***********************************************************************//**
654  * @brief Flush string buffer into log file
655  *
656  * @param[in] force If true, force flushing.
657  *
658  * Flush string buffer if it is full or if flushing is enforced.
659  *
660  * This method writes the output string in the relevant streams. It
661  * decomposes the buffer in lines that a separated by a '\n' character.
662  * The following streams are currently implemented (and will be filled in
663  * parallel): stdout, stderr, and - if available - an ASCII file.
664  ***************************************************************************/
665 void GLog::flush(const bool& force)
666 {
667  // Put flushing in OpenMP critical zone
668  #pragma omp critical(GLog_flush)
669  {
670  // Check if buffer should be flushed
671  bool flush = (force || m_buffer.size() > m_max_length);
672 
673  // Flush buffer
674  if (flush) {
675 
676 
677  // Flush buffer until it is empty
678  while (m_buffer.size() > 0) {
679 
680  // Find next CR
681  std::string line;
682  std::size_t pos = m_buffer.find_first_of("\n", 0);
683  if (pos == std::string::npos) {
684  line = m_buffer;
685  m_linestart = false;
686  m_buffer.clear();
687  }
688  else {
689  line = m_buffer.substr(0, pos) + "\n";
690  m_buffer = m_buffer.substr(pos+1, m_buffer.size()-pos);
691  m_linestart = true;
692  }
693 
694  // Put line into file
695  if (m_file != NULL) {
696  std::fprintf(m_file, "%s", line.c_str());
697  }
698 
699  } // endwhile: flush until empty
700 
701  // And now flush the kernel buffer
702  std::fflush(m_file);
703 
704  } // endif: flush was required
705 
706  } // end of OpenMP critical zone
707 
708  // Return
709  return;
710 }
711 
712 
713 /***********************************************************************//**
714  * @brief Write string as header into logger
715  *
716  * @param[in] arg Header string.
717  * @param[in] level Header level (0,1,2,3).
718  *
719  * Writes a string as header into the logger. Various header levels exist:
720  * 0: 80 character wide centred header
721  * 1: Header framed by '=' characters
722  * 2: Header framed by '-' characters
723  * 3: Header enclosed by '===' string
724  ***************************************************************************/
725 void GLog::header(const std::string& arg, int level)
726 {
727  // Declare frame and text strings
728  std::string frame;
729  std::string text;
730 
731  // Create level dependent header strings
732  switch (level) {
733  case 0:
734  break;
735  case 1:
736  case 2:
737  text = "| " + arg + " |";
738  frame = (level == 1) ? "+" + gammalib::fill("=", text.length()-2) + "+"
739  : "+" + gammalib::fill("-", text.length()-2) + "+";
740  break;
741  case 3:
742  text = "=== " + arg + " ===";
743  break;
744  default:
745  break;
746  }
747 
748  // Write header into logger
749  if (frame.length() > 0) {
750  append(frame);
751  append("\n");
752  }
753  if (text.length() > 0) {
754  append(text);
755  append("\n");
756  }
757  if (frame.length() > 0) {
758  append(frame);
759  append("\n");
760  }
761 
762  // Return
763  return;
764 }
765 
766 
767 /***********************************************************************//**
768  * @brief Return prefix
769  *
770  * Returns the prefix for each line.
771  ***************************************************************************/
772 std::string GLog::prefix(void) const
773 {
774  // Initialize prefix to empty string
775  std::string prefix = "";
776 
777  // Add date if requested
778  if (m_use_date){
779  prefix.append(gammalib::strdate());
780  }
781 
782  // Add name if requested
783  if (m_name.length() > 0){
784  prefix.append(" "+m_name);
785  }
786 
787  // If there is a prefix then add separator
788  if (prefix.length() > 0){
789  prefix.append(": ");
790  }
791 
792  // Add any indent
793  prefix.append(gammalib::fill(" ", m_indent));
794 
795  // Return prefix
796  return prefix;
797 }
798 
799 
800 /***********************************************************************//**
801  * @brief Append string to the buffer
802  *
803  * @param[in] string String to append
804  *
805  * This method appends a string to the buffer and prepends, if required, the
806  * current date and name at the beginning of each line.
807  ***************************************************************************/
808 void GLog::append(const std::string& string)
809 {
810  // Put everything in OpenMP critical zone
811  #pragma omp critical(GLog_append)
812  {
813  // Copy string to append
814  std::string arg = string;
815 
816  // If the buffer is empty and at the beginning of a line or if the
817  // last character is a \n then prepend a prefix at the beginning of
818  // the string to be inserted.
819  if ((m_buffer.size() == 0 && m_linestart) ||
820  (m_buffer.size() > 0 && m_buffer[m_buffer.size()-1] == '\n')) {
821 
822  // Prepend prefix
823  arg.insert(0, prefix());
824 
825  }
826 
827  // Search the first CR (\n)
828  std::size_t pos = arg.find_first_of("\n",0);
829 
830  // Search all \n characters. Ignore the last CR.
831  while (pos != std::string::npos && pos < arg.size()-1) {
832 
833  // Prepend prefix
834  std::string pre = prefix();
835  arg.insert(pos+1, pre);
836 
837  // Search next CR
838  pos = arg.find_first_of("\n",pos+1+pre.size());
839 
840  } // endwhile
841 
842  // Add string to buffer
843  m_buffer.append(arg);
844 
845  // Increment written number of characters
846  m_written_size += arg.size();
847 
848  // Flush Buffer
849  flush();
850 
851  // Append string to stdout and/or stderr without any buffering
852  // if requested
853  if (m_stdout) {
854  std::cout << arg;
855  }
856  if (m_stderr) {
857  std::cerr << arg;
858  }
859 
860  } // end of OpenMP critical zone
861 
862  // Return
863  return;
864 }
const GFilename & filename(void) const
Return log filename.
Definition: GLog.hpp:491
void open(const GFilename &filename, const bool &clobber=false)
Open log file.
Definition: GLog.cpp:505
GLog & operator=(const GLog &log)
Assignment operator.
Definition: GLog.cpp:139
void free_members(void)
Delete class members.
Definition: GLog.cpp:640
std::string strdate(void)
Return current date.
Definition: GTools.cpp:727
int m_indent
Indentation of text.
Definition: GLog.hpp:128
long int m_written_size
Number of char&#39;s written though this logger.
Definition: GLog.hpp:129
Gammalib tools definition.
int m_max_length
Maximum buffer length.
Definition: GLog.hpp:127
Information logger interface definition.
Definition: GLog.hpp:62
std::string m_buffer
Output string buffer.
Definition: GLog.hpp:137
Filename class.
Definition: GFilename.hpp:62
void header(const std::string &arg, int level)
Write string as header into logger.
Definition: GLog.cpp:725
void copy_members(const GLog &log)
Copy class members.
Definition: GLog.cpp:600
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition: GVector.cpp:1274
void close(void)
Close log file.
Definition: GLog.cpp:538
void flush(const bool &force=false)
Flush string buffer into log file.
Definition: GLog.cpp:665
long int size(void) const
Return the number of characters in the log file.
Definition: GLog.cpp:474
void operator()(const char *msgFormat,...)
Message logging operator.
Definition: GLog.cpp:175
void append(const std::string &string)
Append string to the buffer.
Definition: GLog.cpp:808
bool m_linestart
Signals that buffer is at line start.
Definition: GLog.hpp:133
virtual ~GLog(void)
Destructor.
Definition: GLog.cpp:118
GLog & operator<<(GLog &log)
Insert logger into logger.
Definition: GLog.cpp:204
Information logger class definition.
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
void clear(void)
Clear file name.
Definition: GFilename.cpp:188
void init_members(void)
Initialise class members.
Definition: GLog.cpp:569
std::string m_name
Name for prefix.
Definition: GLog.hpp:136
FILE * m_file
Log file pointer.
Definition: GLog.hpp:134
const std::string & buffer(void) const
Return logger buffer.
Definition: GLog.hpp:505
bool m_use_date
Dump date in prefix.
Definition: GLog.hpp:132
GChatter m_chatter
Chattiness for print() method.
Definition: GLog.hpp:138
bool m_stderr
Dump in standard error.
Definition: GLog.hpp:131
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition: GTools.cpp:1044
std::string prefix(void) const
Return prefix.
Definition: GLog.cpp:772
bool m_stdout
Dump in standard output.
Definition: GLog.hpp:130
GFilename m_filename
Log file name.
Definition: GLog.hpp:135
GLog(void)
Void constructor.
Definition: GLog.cpp:66
void clear(void)
Clear object.
Definition: GLog.cpp:456
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489