A library for working with phylogenetic and population genetic data.
v0.32.0
logging.cpp
Go to the documentation of this file.
1 /*
2  Genesis - A toolkit for working with phylogenetic data.
3  Copyright (C) 2014-2024 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 <mutex>
37 #include <stdexcept>
38 #include <string>
39 
44 
45 namespace genesis {
46 namespace utils {
47 
48 // =============================================================================
49 // Settings
50 // =============================================================================
51 
52 // We use a global mutex to ensure that output streams are synchronized,
53 // and avoid garbled output should multiple threads try log things concurrently.
54 static std::mutex genesis_log_mutex_;
55 
56 // TODO use different init for log details depending on DEBUG
57 
58 // init static members
60  false, // count
61  false, // date
62  false, // time
63  false, // runtime
64  false, // rundiff
65  false, // file
66  false, // line
67  false, // function
68  true // level
69 };
71 long Logging::count_ = 0;
72 clock_t Logging::last_clock_ = 0;
73 std::vector<std::ostream*> Logging::ostreams_;
74 std::vector<std::unique_ptr<std::ofstream>> Logging::fstreams_;
76 std::string Logging::debug_indent = " ";
77 
85 void Logging::max_level (const LoggingLevel level)
86 {
87  if (level > LOG_LEVEL_MAX) {
88  throw std::runtime_error(
89  "Logging max level set to " + std::to_string( level ) + ", but compile time max level is " +
90  std::to_string( LOG_LEVEL_MAX ) + ", so that everything above that will not be logged."
91  );
92  }
93  max_level_ = level;
94 }
95 
99 void Logging::report_percentage (const int percentage)
100 {
101  if (percentage <= 0) {
102  throw std::runtime_error( "Logging report percentage less than 1% not possible." );
103  }
104  if (percentage > 100) {
105  throw std::runtime_error( "Logging report percentage greater than 100% not meaningful." );
106  }
107  report_percentage_ = percentage;
108 }
109 
113 std::string Logging::level_to_string(const LoggingLevel level)
114 {
115  static const char* const buffer[] = {
116  "NONE", "ERR ", "WARN", "INFO", "PROG",
117  "MSG ", "MSG1", "MSG2", "MSG3", "MSG4",
118  "DBG ", "DBG1", "DBG2", "DBG3", "DBG4"
119  };
120  return buffer[level];
121 }
122 
127 {
128  // check whether stdout was already added.
129  for (std::ostream* os : ostreams_) {
130  if (os == &std::cout) {
131  return;
132  }
133  }
134 
135  // if not, add it as output stream.
136  ostreams_.push_back( &std::cout );
137 }
138 
142 void Logging::log_to_stream (std::ostream& os)
143 {
144  ostreams_.push_back( &os );
145 }
146 
152 void Logging::log_to_file( std::string const& filename )
153 {
154  // std::ofstream* file_stream = new std::ofstream();
155  // utils::file_output_stream( filename, *file_stream );
156  // fstreams_.push_back( file_stream );
157 
158  // Although we are in untils namespace here, we specify the namespace full,
159  // in order to avoid ambiguous overload when compiled with C++17.
160  fstreams_.push_back( genesis::utils::make_unique<std::ofstream>());
161  utils::file_output_stream( filename, *fstreams_.back() );
162 }
163 
168 {
169  ostreams_.clear();
170  fstreams_.clear();
171 }
172 
173 // =============================================================================
174 // Destructor (does the actual work)
175 // =============================================================================
176 
182 {
183  // build the details for the log message into a buffer
184  clock_t now_clock = clock();
185  std::ostringstream det_buff;
186  det_buff.str("");
187  if (details_.count) {
188  det_buff.fill('0');
189  det_buff.width(4);
190  det_buff << count_ << " ";
191  }
192  if (details_.date) {
193  det_buff << current_date() << " ";
194  }
195  if (details_.time) {
196  det_buff << current_time() << " ";
197  }
198  if (details_.runtime) {
199  det_buff << std::fixed
200  << std::setprecision(6)
201  << double(now_clock) / CLOCKS_PER_SEC
202  << " ";
203  }
204  if (details_.rundiff) {
205  double val = 0.0;
206  if (last_clock_ > 0) {
207  val = (double) (now_clock - last_clock_) / CLOCKS_PER_SEC;
208  }
209  det_buff << std::fixed
210  << std::setprecision(6)
211  << val
212  << " ";
213  last_clock_ = now_clock;
214  }
215  if (details_.file) {
216  det_buff << file_ << (details_.line ? "" : " ");
217  }
218  if (details_.line) {
219  det_buff << ":" << line_ << " ";
220  }
221  if (details_.function) {
222  det_buff << "(" << function_ << ") ";
223  }
224  if (details_.level) {
225  det_buff << level_to_string(level_) << " ";
226  }
227 
228  // add spaces for nested debug levels
229  if (level_ > kDebug) {
230  for (int i = 0; i < level_ - kDebug; i++) {
231  det_buff << debug_indent;
232  }
233  }
234 
235  // make multi line log messages align to the length of the detail header,
236  // and trim trailing whitespace, as we only want one newline at the end
237  std::string msg = det_buff.str();
238  if (msg.length() > 0) {
239  msg += utils::replace_all(
240  buff_.str(), "\n", "\n" + std::string(msg.length(), ' ')
241  );
242  } else {
243  msg += buff_.str();
244  }
245  msg = utils::trim_right(msg);
246 
247  // output the message to every stream, thread safe!
248  std::lock_guard<std::mutex> lock( genesis_log_mutex_ );
249  for( auto& out : ostreams_ ) {
250  (*out) << msg << std::endl << std::flush;
251  }
252  for( auto& out : fstreams_ ) {
253  (*out) << msg << std::endl << std::flush;
254  }
255 
256  // inc log message counter
257  count_++;
258 }
259 
260 // =============================================================================
261 // Singleton accessors
262 // =============================================================================
263 
269 std::ostringstream& Logging::get(
270  const std::string& file, const int line, const std::string& function,
271  const LoggingLevel level
272 )
273 {
274  return get(file, line, function, level, details);
275 }
276 
284 std::ostringstream& Logging::get(
285  const std::string& file, const int line, const std::string& function,
286  const LoggingLevel level, const LoggingDetails dets
287 )
288 {
289  // save the information given when called from the macros
290  file_ = file;
291  line_ = line;
292  function_ = function;
293  level_ = level;
294  details_ = dets;
295  buff_.str("");
296  return buff_;
297 }
298 
299 } // namespace utils
300 } // 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 (or any other delimiters).
Definition: string.cpp:803
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:181
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:727
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:269
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:167
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: function/genome_locus.hpp:52
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:113
genesis::utils::Logging::level_
LoggingLevel level_
Definition: logging.hpp:589
genesis::utils::file_output_stream
void file_output_stream(std::string const &file_name, 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:72
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:152
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:142
genesis::utils::genesis_log_mutex_
static std::mutex genesis_log_mutex_
Definition: logging.cpp:54
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:126
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::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::Logging::details_
LoggingDetails details_
Definition: logging.hpp:590
genesis::utils::Logging::function_
std::string function_
Definition: logging.hpp:588