A library for working with phylogenetic and population genetic data.
v0.27.0
logging.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2021 Lucas Czech
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  Contact:
19  Lucas Czech <lczech@carnegiescience.edu>
20  Department of Plant Biology, Carnegie Institution For Science
21  260 Panama Street, Stanford, CA 94305, USA
22 */
23 
32 
33 #include <fstream>
34 #include <iomanip>
35 #include <iostream>
36 #include <stdexcept>
37 #include <string>
38 
39 #if defined(GENESIS_PTHREADS) || defined(GENESIS_OPENMP)
40 # include <mutex>
41 #endif
42 
47 
48 namespace genesis {
49 namespace utils {
50 
51 // =============================================================================
52 // Settings
53 // =============================================================================
54 
55 #if defined(GENESIS_PTHREADS) || defined(GENESIS_OPENMP)
56  static std::mutex log_mutex;
57 #endif
58 
59 // TODO use different init for log details depending on DEBUG
60 
61 // init static members
63  false, // count
64  false, // date
65  false, // time
66  false, // runtime
67  false, // rundiff
68  false, // file
69  false, // line
70  false, // function
71  true // level
72 };
74 long Logging::count_ = 0;
75 clock_t Logging::last_clock_ = 0;
76 std::vector<std::ostream*> Logging::ostreams_;
77 std::vector<std::unique_ptr<std::ofstream>> Logging::fstreams_;
79 std::string Logging::debug_indent = " ";
80 
88 void Logging::max_level (const LoggingLevel level)
89 {
90  if (level > LOG_LEVEL_MAX) {
91  throw std::runtime_error(
92  "Logging max level set to " + std::to_string( level ) + ", but compile time max level is " +
93  std::to_string( LOG_LEVEL_MAX ) + ", so that everything above that will not be logged."
94  );
95  }
96  max_level_ = level;
97 }
98 
102 void Logging::report_percentage (const int percentage)
103 {
104  if (percentage <= 0) {
105  throw std::runtime_error( "Logging report percentage less than 1% not possible." );
106  }
107  if (percentage > 100) {
108  throw std::runtime_error( "Logging report percentage greater than 100% not meaningful." );
109  }
110  report_percentage_ = percentage;
111 }
112 
116 std::string Logging::level_to_string(const LoggingLevel level)
117 {
118  static const char* const buffer[] = {
119  "NONE", "ERR ", "WARN", "INFO", "PROG",
120  "MSG ", "MSG1", "MSG2", "MSG3", "MSG4",
121  "DBG ", "DBG1", "DBG2", "DBG3", "DBG4"
122  };
123  return buffer[level];
124 }
125 
130 {
131  // check whether stdout was already added.
132  for (std::ostream* os : ostreams_) {
133  if (os == &std::cout) {
134  return;
135  }
136  }
137 
138  // if not, add it as output stream.
139  ostreams_.push_back( &std::cout );
140 }
141 
145 void Logging::log_to_stream (std::ostream& os)
146 {
147  ostreams_.push_back( &os );
148 }
149 
155 void Logging::log_to_file( std::string const& filename )
156 {
157  // std::ofstream* file_stream = new std::ofstream();
158  // utils::file_output_stream( filename, *file_stream );
159  // fstreams_.push_back( file_stream );
160 
161  // Although we are in untils namespace here, we specify the namespace full,
162  // in order to avoid ambiguous overload when compiled with C++17.
163  fstreams_.push_back( genesis::utils::make_unique<std::ofstream>());
164  utils::file_output_stream( filename, *fstreams_.back() );
165 }
166 
171 {
172  ostreams_.clear();
173  fstreams_.clear();
174 }
175 
176 // =============================================================================
177 // Destructor (does the actual work)
178 // =============================================================================
179 
185 {
186  // build the details for the log message into a buffer
187  clock_t now_clock = clock();
188  std::ostringstream det_buff;
189  det_buff.str("");
190  if (details_.count) {
191  det_buff.fill('0');
192  det_buff.width(4);
193  det_buff << count_ << " ";
194  }
195  if (details_.date) {
196  det_buff << current_date() << " ";
197  }
198  if (details_.time) {
199  det_buff << current_time() << " ";
200  }
201  if (details_.runtime) {
202  det_buff << std::fixed
203  << std::setprecision(6)
204  << double(now_clock) / CLOCKS_PER_SEC
205  << " ";
206  }
207  if (details_.rundiff) {
208  double val = 0.0;
209  if (last_clock_ > 0) {
210  val = (double) (now_clock - last_clock_) / CLOCKS_PER_SEC;
211  }
212  det_buff << std::fixed
213  << std::setprecision(6)
214  << val
215  << " ";
216  last_clock_ = now_clock;
217  }
218  if (details_.file) {
219  det_buff << file_ << (details_.line ? "" : " ");
220  }
221  if (details_.line) {
222  det_buff << ":" << line_ << " ";
223  }
224  if (details_.function) {
225  det_buff << "(" << function_ << ") ";
226  }
227  if (details_.level) {
228  det_buff << level_to_string(level_) << " ";
229  }
230 
231  // add spaces for nested debug levels
232  if (level_ > kDebug) {
233  for (int i = 0; i < level_ - kDebug; i++) {
234  det_buff << debug_indent;
235  }
236  }
237 
238  // make multi line log messages align to the length of the detail header,
239  // and trim trailing whitespace, as we only want one newline at the end
240  std::string msg = det_buff.str();
241  if (msg.length() > 0) {
242  msg += utils::replace_all(
243  buff_.str(), "\n", "\n" + std::string(msg.length(), ' ')
244  );
245  } else {
246  msg += buff_.str();
247  }
248  msg = utils::trim_right(msg);
249 
250  // output the message to every stream, thread safe!
251 # if defined(GENESIS_PTHREADS) || defined(GENESIS_OPENMP)
252  log_mutex.lock();
253 # endif
254 
255  for( auto& out : ostreams_ ) {
256  (*out) << msg << std::endl << std::flush;
257  }
258  for( auto& out : fstreams_ ) {
259  (*out) << msg << std::endl << std::flush;
260  }
261 
262 # if defined(GENESIS_PTHREADS) || defined(GENESIS_OPENMP)
263  log_mutex.unlock();
264 # endif
265 
266  // inc log message counter
267  count_++;
268 }
269 
270 // =============================================================================
271 // Singleton accessors
272 // =============================================================================
273 
279 std::ostringstream& Logging::get(
280  const std::string& file, const int line, const std::string& function,
281  const LoggingLevel level
282 )
283 {
284  return get(file, line, function, level, details);
285 }
286 
294 std::ostringstream& Logging::get(
295  const std::string& file, const int line, const std::string& function,
296  const LoggingLevel level, const LoggingDetails dets
297 )
298 {
299  // save the information given when called from the macros
300  file_ = file;
301  line_ = line;
302  function_ = function;
303  level_ = level;
304  details_ = dets;
305  buff_.str("");
306  return buff_;
307 }
308 
309 } // namespace utils
310 } // namespace genesis
genesis::utils::LoggingDetails
POD stuct containing the settings for which information is included with each logging message.
Definition: logging.hpp:267
LOG_LEVEL_MAX
#define LOG_LEVEL_MAX
Static maximal logging level.
Definition: logging.hpp:66
genesis::utils::Logging::fstreams_
static std::vector< std::unique_ptr< std::ofstream > > fstreams_
Definition: logging.hpp:610
genesis::utils::trim_right
std::string trim_right(std::string const &s, std::string const &delimiters)
Return a copy of the input string, with left trimmed white spaces.
Definition: string.cpp:578
genesis::utils::Logging::report_percentage_
static int report_percentage_
Definition: logging.hpp:596
genesis::utils::Logging::~Logging
~Logging()
Destructor that is invoked at the end of each log line and does the actual output.
Definition: logging.cpp:184
genesis::utils::current_date
std::string current_date()
Returns the current date as a string in the format "2014-12-31".
Definition: date_time.cpp:68
genesis::utils::LoggingDetails::date
bool date
Include the current date.
Definition: logging.hpp:290
genesis::utils::replace_all
std::string replace_all(std::string const &text, std::string const &search, std::string const &replace)
Return a copy of a string, where all occurrences of a search string are replaced by a replace string.
Definition: string.cpp:530
genesis::utils::Logging::count_
static long count_
Definition: logging.hpp:599
date_time.hpp
Provides functions for date and time access.
genesis::utils::Logging::get
std::ostringstream & get(const std::string &file, const int line, const std::string &function, const LoggingLevel level)
Getter for the singleton instance of log, is called by the standard macros.
Definition: logging.cpp:279
genesis::utils::Logging::ostreams_
static std::vector< std::ostream * > ostreams_
Definition: logging.hpp:609
genesis::utils::Logging::clear
static void clear()
Remove all output streams, so that nothing is logged any more.
Definition: logging.cpp:170
genesis::utils::LoggingDetails::count
bool count
Include a counter of how many messages have been logged so far.
Definition: logging.hpp:287
output_stream.hpp
genesis::utils::LoggingDetails::level
bool level
Include the level (e.g. Info, Debug) of the message.
Definition: logging.hpp:324
genesis::utils::Logging::last_clock_
static clock_t last_clock_
Definition: logging.hpp:602
genesis::utils::LoggingDetails::file
bool file
Include the filename where the log message was generated.
Definition: logging.hpp:308
std.hpp
Provides some valuable additions to STD.
genesis::utils::current_time
std::string current_time()
Returns the current time as a string in the format "13:37:42".
Definition: date_time.cpp:88
genesis::utils::Logging::details
static LoggingDetails details
Settings for which information is included with each log message. See LoggingDetails for usage.
Definition: logging.hpp:488
genesis::utils::LoggingDetails::function
bool function
Include the function name where the log message was generated.
Definition: logging.hpp:321
genesis::population::to_string
std::string to_string(GenomeLocus const &locus)
Definition: functions/genome_locus.hpp:48
string.hpp
Provides some commonly used string utility functions.
genesis::utils::Logging::LoggingLevel
LoggingLevel
Levels of severity used for logging.
Definition: logging.hpp:405
genesis::utils::Logging::max_level
static LoggingLevel max_level()
Get the highest log level that is reported.
Definition: logging.hpp:491
logging.hpp
Provides easy and fast logging functionality.
genesis::utils::Logging::level_to_string
static std::string level_to_string(const LoggingLevel level)
Return a string representation of a log level.
Definition: logging.cpp:116
genesis::utils::Logging::level_
LoggingLevel level_
Definition: logging.hpp:589
genesis::utils::Logging::line_
int line_
Definition: logging.hpp:587
genesis::utils::Logging::report_percentage
static int report_percentage()
Get the current percentage for reporting LOG_PROG messages.
Definition: logging.hpp:498
genesis
Container namespace for all symbols of genesis in order to keep them separate when used as a library.
Definition: placement/formats/edge_color.cpp:42
genesis::utils::LoggingDetails::line
bool line
Include the line of the file where the log message was generated.
Definition: logging.hpp:314
genesis::utils::LoggingDetails::runtime
bool runtime
Include the current run time of the program in sec.
Definition: logging.hpp:296
genesis::utils::Logging::log_to_file
static void log_to_file(const std::string &fn)
Add an output file to which log messages are written.
Definition: logging.cpp:155
genesis::utils::Logging::log_to_stream
static void log_to_stream(std::ostream &os)
Add an output stream to which log messages are written.
Definition: logging.cpp:145
genesis::utils::LoggingDetails::time
bool time
Include the current time.
Definition: logging.hpp:293
genesis::utils::Logging::max_level_
static LoggingLevel max_level_
Definition: logging.hpp:593
genesis::utils::Logging::log_to_stdout
static void log_to_stdout()
Add stdout as output stream to which log messages are written.
Definition: logging.cpp:129
genesis::utils::Logging::file_
std::string file_
Definition: logging.hpp:586
genesis::utils::Logging::kDebug
@ kDebug
Basic debugging message. See LOG_DBG.
Definition: logging.hpp:437
genesis::utils::log_mutex
static std::mutex log_mutex
Definition: logging.cpp:56
genesis::utils::Logging::debug_indent
static std::string debug_indent
Indention string for Debug Levels 1-4.
Definition: logging.hpp:508
genesis::utils::Logging::buff_
std::ostringstream buff_
Definition: logging.hpp:585
genesis::utils::LoggingDetails::rundiff
bool rundiff
Include the run time difference to the last log message in sec.
Definition: logging.hpp:305
genesis::utils::file_output_stream
void file_output_stream(std::string const &filename, std::ofstream &out_stream, std::ios_base::openmode mode=std::ios_base::out, bool create_dirs=true)
Helper function to obtain an output stream to a file.
Definition: output_stream.hpp:69
genesis::utils::Logging::details_
LoggingDetails details_
Definition: logging.hpp:590
genesis::utils::Logging::function_
std::string function_
Definition: logging.hpp:588