gambit is hosted by Hepforge, IPPP Durham
GAMBIT  v1.5.0-2191-ga4742ac
a Global And Modular Bsm Inference Tool
logging.cpp
Go to the documentation of this file.
1 // GAMBIT: Global and Modular BSM Inference Tool
2 // *********************************************
21 
22 
23 // Standard libraries
24 #include <set>
25 #include <fstream>
26 #include <cstdarg>
27 #include <stdexcept>
28 #include <algorithm>
29 #include <limits>
30 #include <chrono>
31 #include <omp.h>
32 
33 // Gambit
34 #include "gambit/Logs/logger.hpp"
36 #include "gambit/Logs/logging.hpp"
41 #include "gambit/cmake/cmake_variables.hpp"
42 
43 // Code!
44 namespace Gambit
45 {
46 
47  namespace Logging
48  {
49 
50  using namespace LogTags;
51 
52  const bool verbose = false;
53 
54  // If you add to the following message tags, make sure to update the enum in log_tags.hpp that tracks the number of them!
55 
56  // Function to retrieve the 'msgtypes' set of tags
57  const std::set<LogTag>& msgtypes()
58  {
59  static LogTag msg_a[] = {debug, info, warn, err};
60  static const std::set<LogTag> msgtypes_set(msg_a, msg_a+sizeof(msg_a)/sizeof(msg_a[0]));
61  return msgtypes_set;
62  }
63 
64  // Function to retrieve the 'flags' set of tags
65  const std::set<LogTag>& flags()
66  {
67  static LogTag flags_a[] = {fatal, nonfatal};
68  static const std::set<LogTag> flags_set(flags_a, flags_a+sizeof(flags_a)/sizeof(flags_a[0]));
69  return flags_set;
70  }
71 
72  // Function to retrieve the 'echoes' set of tags
73  const std::set<LogTag>& echoes()
74  {
75  static LogTag echoes_a[] = {repeat_to_cout, repeat_to_cerr};
76  static const std::set<LogTag> echoes_set(echoes_a, echoes_a+sizeof(echoes_a)/sizeof(echoes_a[0]));
77  return echoes_set;
78  }
79 
80  // Function to retrieve the 'components' set of tags (needed by module and backend macros so they can add to it)
81  std::set<int>& components()
82  {
83  // We add the core components here, but the module and backend numbers are added later, so the set cannot be const.
85  static std::set<int> components_set(core_a, core_a+sizeof(core_a)/sizeof(core_a[0]));
86  return components_set;
87  }
88 
89  // String names for enums
90  static std::map<int,std::string> create_tag_names()
91  {
92  std::map<int,std::string> m;
93  m[debug] = "Debug";
94  m[info] = "Info";
95  m[warn] = "Warning";
96  m[err] = "Error";
97  /* Flags */
98  m[fatal] = "Fatal";
99  m[nonfatal]= "Non-fatal";
100  /* Repeaters */
101  m[repeat_to_cout] = "Echo > stout";
102  m[repeat_to_cerr]= "Echo > sterr";
103  /* Component tags */
104  m[def] = "Default";
105  m[core] = "Core";
106  m[logs] = "Logger";
107  m[models] = "Models";
108  m[dependency_resolver] = "Dependency Resolver";
109  m[scanner] = "Scanner";
110  m[inifile] = "IniFile";
111  m[printers]= "Printers";
112  m[utils] = "Utilities";
113  m[backends]= "Backends";
114 
115  // Test numbers:
116  if (verbose)
117  {
118  std::cout<<"Checking LogTag numbers..."<<std::endl;
119  for(std::map<int,std::string>::iterator tag = m.begin(); tag != m.end(); ++tag)
120  {
121  std::cout<<" "<<tag->first<<" : "<<tag->second<<std::endl;
122  }
123  }
124 
125  return m;
126  }
127 
128  // Function to retrieve the 'tag2str' map outside of this compilation unit
129  // (needed by module and backend macros so they can add to it)
130  std::map<int,std::string>& tag2str()
131  {
132  // Can't be const because we will add to it from the module/backend macros
133  static std::map<int,std::string> tag2str_map = create_tag_names();
134  return tag2str_map;
135  }
136 
137  // Function to return the next unused tag index
139  {
140  for(int i=0; i<std::numeric_limits<int>::max(); ++i)
141  {
142  if( tag2str().count(i) == 0 ) { return i; }
143  }
144  // Uh oh, seems like we ran out of integers. If this happens you are screwed, and we have to rewrite the code to use long ints instead, or you have to unhook some modules.
145  // Cannot log this because we are outside the LogMaster class code.
146  std::ostringstream ss;
147  ss << "Error in logger.cpp! It seems that you have so many logging tags that you have exceeded the maximum allowed integer. There is no way you can fix this except to have fewer modules hooked up to gambit all at once. Otherwise we have to rewrite the logger to work with long ints or some such" << std::endl;
148  throw std::overflow_error( ss.str() );
149  }
150 
151  // Function to do the reverse search (brute force)
152  int str2tag(const std::string& tagname)
153  {
154  for(std::map<int,std::string>::iterator tag = tag2str().begin(); tag != tag2str().end(); ++tag)
155  {
156  if (tag->second == tagname) { return tag->first; }
157  }
158  // Uh oh, no match found. Return fail code and let caller deal with it
159  return -1;
160  }
161 
163  void checktags()
164  {
165  std::cout<<"Checking message type LogTags..."<<std::endl;
166  for(std::set<LogTag>::iterator tag = msgtypes().begin(); tag != msgtypes().end(); ++tag)
167  {
168  std::cout<<" "<<*tag<<" : "<<tag2str()[*tag]<<std::endl;
169  }
170  std::cout<<"Checking message flag LogTags..."<<std::endl;
171  for(std::set<LogTag>::iterator tag = flags().begin(); tag != flags().end(); ++tag)
172  {
173  std::cout<<" "<<*tag<<" : "<<tag2str()[*tag]<<std::endl;
174  }
175  std::cout<<"Checking message echo LogTags..."<<std::endl;
176  for(std::set<LogTag>::iterator tag = echoes().begin(); tag != echoes().end(); ++tag)
177  {
178  std::cout<<" "<<*tag<<" : "<<tag2str()[*tag]<<std::endl;
179  }
180  std::cout<<"Checking Gambit component LogTags..."<<std::endl;
181  for(std::set<int>::iterator tag = components().begin(); tag != components().end(); ++tag)
182  {
183  std::cout<<" "<<*tag<<" : "<<tag2str()[*tag]<<std::endl;
184  }
185  std::cout<<"LogTag check finished."<<std::endl;
186  }
187 
188 
191  : message(mail.message), received_at(mail.received_at)
192  {
193  // First task is to scan through the tags and figure out where the message is supposed to go
194  //std::cout<<"Sorting tags..."<<std::endl;
195  for(std::set<int>::iterator tag = mail.tags.begin(); tag != mail.tags.end(); ++tag)
196  {
197  // Debugging crap... to be deleted.
198  // std::cout<<"Sorting tag "<<tag2str()[*tag]<<std::endl;
199  // std::cout<<"empty?"<<components().empty()<<std::endl;
200  // for(std::set<int>::iterator tag2 = components().begin(); tag2 != components().end(); ++tag2)
201  // {
202  // std::cout<<"componentI: "<<*tag2<<std::endl;
203  // std::cout<<"component: "<<tag2str()[*tag2]<<std::endl;
204  // }
205  // for(std::set<LogTag>::iterator tag3 = msgtypes().begin(); tag3 != msgtypes().end(); ++tag3)
206  // {
207  // std::cout<<"msgtypeI: "<<*tag3<<std::endl;
208  // std::cout<<"msgtype: "<<tag2str()[*tag3]<<std::endl;
209  // }
210  // std::cout<<"core:"<<*(components().find(core))<<std::endl;
211  // std::cout<<"t1 "<<*(components().find(*tag))<<std::endl;
212  // std::cout<<"t2 "<<*(components().end())<<std::endl;
213  // std::cout<<"tag "<<*tag<<std::endl;
214  // std::cout<<"tag "<<static_cast<LogTag>(*tag)<<std::endl;
215  // std::cout<<"mt1 "<<*(msgtypes().find(static_cast<LogTag>(*tag)))<<std::endl;
216  // std::cout<<"mt2 "<<*(msgtypes().end())<<std::endl;
217  if ( msgtypes().find(static_cast<LogTag>(*tag)) != msgtypes().end() )
218  {
219  // If tag is a message type, add it to the type_tags set
220  //std::cout<<"Identified tag '"<<tag2str()[*tag]<<"' as message type"<<std::endl;
221  type_tags.insert(static_cast<LogTag>(*tag));
222  }
223  else if ( components().find(*tag) != components().end() )
224  {
225  // std::cout<<"Adding tag "<<tag2str()[*tag]<<std::endl;
226  // If tag names a gambit core component, add it to the component_tags set
227  //std::cout<<"Identified tag '"<<tag2str()[*tag]<<"' as Gambit component"<<std::endl;
228  component_tags.insert(*tag);
229  }
230  else if ( flags().find(static_cast<LogTag>(*tag)) != flags().end() )
231  {
232  // If tag is an auxiliary message flag, add it to the flag_tags set
233  //std::cout<<"Identified tag '"<<tag2str()[*tag]<<"' as message flag"<<std::endl;
234  flag_tags.insert(static_cast<LogTag>(*tag));
235  }
236  else if ( echoes().find(static_cast<LogTag>(*tag)) != echoes().end() )
237  {
238  // If tag is a message echo flag, add it to the echo_tags set
239  //std::cout<<"Identified tag '"<<tag2str()[*tag]<<"' as message echo flag"<<std::endl;
240  echo_tags.insert(static_cast<LogTag>(*tag));
241  }
242  else
243  {
244  // If tag was not in of those categories, it shouldn't have been a valid LogTag, and so there should have been a compiler error before now. Since there wasn't, there is something wrong with the LogTag definitions, the tag categories, or this function.
245  // TODO: Gambit error! Really bad one that can't be logged.
246  // I think we are converging on the idea that this type of error should just throw an ordinary exception.
247  std::ostringstream ss;
248  ss << "Error in SortedMessage constructor! One of the tags received could not be found in any of the const LogTag sets. This is supposed to be impossible. Please check that all tags in the LogTags enum (in logger.hpp) are also listed in one (and only one) of the (const) category sets (also in logger.hpp). If this seems fine the problem may be in the code which generates the integer codes for the modules and backends (not yet written...). Tag was number: "<< *tag<<"; name: "<< tag2str()[*tag];
249  throw std::logic_error( ss.str() );
250  }
251  } //end tag sorting
252  } // end SortedMessage constructor
253 
255 
256  // Apparantly this cannot be virtual, so provide an implementation for it
258 
260 
263  StdLogger::StdLogger(std::ostream& logstream)
264  : my_stream(logstream)
265  , MPIrank(0)
266  , MPIsize(1)
267  {
268  #ifdef WITH_MPI
269  if(GMPI::Is_initialized())
270  {
271  GMPI::Comm COMM_WORLD;
272  MPIsize = COMM_WORLD.Get_size();
273  MPIrank = COMM_WORLD.Get_rank();
274  }
275  #endif
276  // Check error bits on stream and throw exception in anything is bad
277  if( my_stream.fail() | my_stream.bad() )
278  {
279  std::ostringstream ss;
280  ss << "IO error while constructing StdLogger! Error bit on in supplied ostream.";
281  throw std::runtime_error( ss.str() );
282  }
283  }
284 
286  StdLogger::StdLogger(const std::string& filename)
287  : my_own_fstream(filename, std::ofstream::out)
289  , MPIrank(0)
290  , MPIsize(1)
291  {
292  #ifdef WITH_MPI
293  if(GMPI::Is_initialized() && !GMPI::Is_finalized())
294  {
295  GMPI::Comm COMM_WORLD;
296  MPIsize = COMM_WORLD.Get_size();
297  MPIrank = COMM_WORLD.Get_rank();
298  }
299  #endif
300  // Check error bits on stream and throw exception in anything is bad
301  if( my_stream.fail() | my_stream.bad() )
302  {
303  std::ostringstream ss;
304  #ifdef WITH_MPI
305  ss << "rank "<<MPIrank<<": ";
306  #endif
307  ss << "IO error while constructing StdLogger! Tried to open ofstream to file \""<<filename<<"\", but encountered error bit in the created ostream.";
308  throw std::runtime_error( ss.str() );
309  }
310  }
311 
313 
315  void StdLogger::write(const SortedMessage& mail)
316  {
317  // Message reception time (UTC)
319  // Seconds elapsed since start_time
320  std::chrono::duration<double> diff = mail.received_at - start_time;
321  my_stream<<"("<<diff.count()<<" [s])";
322  // MPI rank
323  // Might as well add this even if different ranks output to different
324  // files, since people might like to cat together the different files
325  // later on or something.
326  if(MPIsize>1) my_stream << "(Rank " << MPIrank << ")";
327 
328  // Message tags
330  writetags(mail.type_tags);
331  writetags(mail.flag_tags);
332  my_stream<<":"<<std::endl;
333  // Message proper
334  my_stream<<mail.message<<std::endl;
335  my_stream<<"--<>--<>--<>--<>--<>--<>--<>--"<<std::endl;
336  // (I picked a weird end of message boundary so that it would be easily distinguished from formatting that may appear in the message body)
337  }
338 
339  void StdLogger::writetags(const std::set<LogTag>& tags)
340  {
341  // If we can figure out how to cast set<LogTag> to set<int> then we can do this better
342  std::set<int> int_tags;
343  for(std::set<LogTag>::iterator tag = tags.begin(); tag != tags.end(); ++tag)
344  {
345  int_tags.insert(*tag); //static_cast<int>(*tag);
346  }
347  writetags(int_tags);
348  }
349 
350  void StdLogger::writetags(const std::set<int>& tags)
351  {
352  std::set<int>::iterator it;
353 
354  if( not tags.empty() )
355  {
356  my_stream<<"[";
357  bool firstloop = true;
358  for (it = tags.begin(); it != tags.end(); ++it)
359  {
360  if (not firstloop) { my_stream<<","; }
361  //std::cout<<"test: "<<*it<<std::endl;
362  //std::cout<<"test2:"<<tag2str().at(*it)<<std::endl;
363  my_stream << tag2str().at(*it); // replace with a lookup of the correct string
364  firstloop = false;
365  }
366  my_stream<<"]";
367  }
368  }
369 
371  // Possibly unnecessary...
373  {
374  my_stream.flush();
375  }
376 
379  // (do we need a cache? linux filesystem probably does this for us...)
380  // ~StdLogger::StdLogger()
381 
382 
383  } //end namespace Logging
384 } // end namespace Gambit
std::set< LogTag > flag_tags
Definition: logging.hpp:97
structure for storing log messages and metadata after tags are sorted
Definition: logging.hpp:89
std::map< int, std::string > & tag2str()
Definition: logging.cpp:130
int getfreetag()
Definition: logging.cpp:138
std::set< LogTag > type_tags
Definition: logging.hpp:95
const std::set< LogTag > & echoes()
Definition: logging.cpp:73
const std::string & message
Definition: logging.hpp:91
Header for logging classes.
std::set< int > tags
Definition: logging.hpp:77
structure for storing log messages and metadata
Definition: logging.hpp:74
virtual ~StdLogger()
Destructor.
Definition: logging.cpp:312
Helper functions for dealing with POSIX signals.
std::ofstream my_own_fstream
Definition: logging.hpp:148
STL namespace.
int str2tag(const std::string &)
Definition: logging.cpp:152
StdLogger(std::ostream &)
Constructor.
Definition: logging.cpp:263
Logging access header for GAMBIT.
std::ostream & my_stream
Definition: logging.hpp:149
std::set< int > & components()
Definition: logging.cpp:81
virtual ~BaseLogger()
Virtual destructor so we can delete the loggers by pointer to base.
Definition: logging.cpp:257
General small utility functions.
EXPORT_SYMBOLS str return_time_and_date(const time_point &in)
Get date and time.
void checktags()
Function to inspect tags and their associated strings. For testing purposes only. ...
Definition: logging.cpp:163
int MPIrank
MPI variables.
Definition: logging.hpp:152
const Utils::time_point & received_at
Definition: logging.hpp:92
std::set< int > component_tags
Definition: logging.hpp:96
const bool verbose
Definition: logging.cpp:52
const std::set< LogTag > & flags()
Definition: logging.cpp:65
void writetags(const std::set< LogTag > &)
Look up names corresponding to tags and write them out to the stream.
Definition: logging.cpp:339
Header for logging classes.
virtual void write(const SortedMessage &)
Write message.
Definition: logging.cpp:315
SortedMessage(const Message &mail)
Constructor for SortedMessage struct.
Definition: logging.cpp:190
A simple C++ wrapper for the MPI C bindings.
Exception objects required for standalone compilation.
std::set< LogTag > echo_tags
Definition: logging.hpp:98
virtual void flush()
Flush stream buffer.
Definition: logging.cpp:372
TODO: see if we can use this one:
Definition: Analysis.hpp:33
const std::set< LogTag > & msgtypes()
Definition: logging.cpp:57