GammaLib 2.0.0
Loading...
Searching...
No Matches
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
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 ***************************************************************************/
84GLog::GLog(const GFilename& filename, const bool& clobber)
85{
86 // Initialise private members for clean destruction
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
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
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 ******************************************************************************/
175void 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 ***************************************************************************/
261GLog& 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 ***************************************************************************/
278GLog& 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 ***************************************************************************/
295GLog& 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 ***************************************************************************/
318GLog& 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 ***************************************************************************/
342GLog& 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 ***************************************************************************/
364GLog& 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 ***************************************************************************/
387GLog& 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 ***************************************************************************/
410GLog& 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 ***************************************************************************/
437GLog& 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 ***************************************************************************/
456void 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 ***************************************************************************/
474long 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 ***************************************************************************/
505void 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 ***************************************************************************/
538void 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;
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
609 m_max_length = log.m_max_length;
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 ***************************************************************************/
665void 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 ***************************************************************************/
725void 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 ***************************************************************************/
772std::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 ***************************************************************************/
808void 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}
Information logger class definition.
Gammalib tools definition.
@ NORMAL
Definition GTypemaps.hpp:36
GVector log(const GVector &vector)
Computes natural logarithm of vector elements.
Definition GVector.cpp:1274
Filename class.
Definition GFilename.hpp:62
std::string url(void) const
Return Uniform Resource Locator (URL)
void clear(void)
Clear file name.
Information logger interface definition.
Definition GLog.hpp:62
int m_max_length
Maximum buffer length.
Definition GLog.hpp:127
void free_members(void)
Delete class members.
Definition GLog.cpp:640
void append(const std::string &string)
Append string to the buffer.
Definition GLog.cpp:808
virtual ~GLog(void)
Destructor.
Definition GLog.cpp:118
bool m_stderr
Dump in standard error.
Definition GLog.hpp:131
void copy_members(const GLog &log)
Copy class members.
Definition GLog.cpp:600
long int size(void) const
Return the number of characters in the log file.
Definition GLog.cpp:474
GLog(void)
Void constructor.
Definition GLog.cpp:66
void flush(const bool &force=false)
Flush string buffer into log file.
Definition GLog.cpp:665
int m_indent
Indentation of text.
Definition GLog.hpp:128
bool m_linestart
Signals that buffer is at line start.
Definition GLog.hpp:133
GLog & operator=(const GLog &log)
Assignment operator.
Definition GLog.cpp:139
void operator()(const char *msgFormat,...)
Message logging operator.
Definition GLog.cpp:175
void open(const GFilename &filename, const bool &clobber=false)
Open log file.
Definition GLog.cpp:505
std::string m_name
Name for prefix.
Definition GLog.hpp:136
const GFilename & filename(void) const
Return log filename.
Definition GLog.hpp:491
bool m_stdout
Dump in standard output.
Definition GLog.hpp:130
bool m_use_date
Dump date in prefix.
Definition GLog.hpp:132
const std::string & buffer(void) const
Return logger buffer.
Definition GLog.hpp:505
GFilename m_filename
Log file name.
Definition GLog.hpp:135
void header(const std::string &arg, int level)
Write string as header into logger.
Definition GLog.cpp:725
void close(void)
Close log file.
Definition GLog.cpp:538
FILE * m_file
Log file pointer.
Definition GLog.hpp:134
long int m_written_size
Number of char's written though this logger.
Definition GLog.hpp:129
void init_members(void)
Initialise class members.
Definition GLog.cpp:569
GLog & operator<<(GLog &log)
Insert logger into logger.
Definition GLog.cpp:204
void clear(void)
Clear object.
Definition GLog.cpp:456
GChatter m_chatter
Chattiness for print() method.
Definition GLog.hpp:138
std::string m_buffer
Output string buffer.
Definition GLog.hpp:137
std::string prefix(void) const
Return prefix.
Definition GLog.cpp:772
std::string strdate(void)
Return current date.
Definition GTools.cpp:727
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition GTools.cpp:1044