PetaVision  Alpha
PVLog.hpp
1 #ifndef PVLOG_HPP_
2 #define PVLOG_HPP_
3 
4 #include <cstdarg>
5 #include <iostream>
6 #include <libgen.h>
7 #include <string>
8 #include <type_traits>
9 
10 //
11 // Logging with the C++ builder pattern.
12 //
13 // After PV_Init::initialize has been called, use the following macros instead
14 // of writing to stdout, stderr, std::cout, std::cerr, etc.
15 // This way, the file will go to stdout or stderr if the -l option is not used,
16 // but will go to the log file if it is.
17 //
18 // InfoLog() << "Some info" << std::endl;
19 // WarnLog() << "Print a warning" << std::endl;
20 // Fatal() << "Print an error" << std::endl;
21 // ErrorLog() << "Print an error" << std::endl;
22 // DebugLog() << "Some" << "stuff" << "to log" << std::endl;
23 //
24 // FatalIf uses printf style formatting:
25 // FatalIf(condition == true, "Condition %s was true. Exiting.\n", conditionName);
26 //
27 // These macros create objets that send to the stream returned one of by
28 // PV::getOutputStream() or PV::getErrorStream().
29 // InfoLog() sends its output to the output stream.
30 // WarnLog() prepends its output with "WARN " and sends to the error stream.
31 // Fatal() prepends its output to the error stream with "ERROR <file,line>: ", then exits.
32 // ErrorLog() prepends its output to the error stream with "ERROR <file,line>: "
33 // DebugLog() prepends its output with "DEBUG <file,line>: " and sends to the output stream.
34 // In release versions, DebugLog() does not print any output,
35 // unless PetaVision was built using cmake -DPV_LOG_DEBUG:Bool=ON.
36 //
37 // The <file,line> returned by several of these macros gives the basename of the file where
38 // the macro was invoked, and the line number within that file.
39 
40 // Because __LINE__ and __FILE__ evaluate to *this* file, not
41 // the file where the inline function is called. This is different
42 // from Clang, which puts in the __FILE__ and __LINE__ of the caller
43 #ifdef __GNUC__
44 #define InfoLog(...) PV::InfoLog __VA_ARGS__(__FILE__, __LINE__)
45 #define WarnLog(...) PV::WarnLog __VA_ARGS__(__FILE__, __LINE__)
46 #define Fatal(...) PV::Fatal __VA_ARGS__(__FILE__, __LINE__)
47 #define ErrorLog(...) PV::ErrorLog __VA_ARGS__(__FILE__, __LINE__)
48 #define DebugLog(...) PV::DebugLog __VA_ARGS__(__FILE__, __LINE__)
49 #define StackTrace(...) PV::StackTrace __VA_ARGS__(__FILE__, __LINE__)
50 #define FatalIf(x, ...) \
51  if (x) { \
52  Fatal().printf(__VA_ARGS__); \
53  }
54 #endif // __GNUC__
55 
56 namespace PV {
57 enum LogTypeEnum {
58  LogInfoType,
59  LogWarnType,
60  LogFatalType,
61  LogErrorType,
62  LogDebugType,
63  LogStackTraceType
64 };
65 
72 std::ostream &getOutputStream();
73 
79 std::ostream &getErrorStream();
80 
84 std::wostream &getWOutputStream();
85 
89 std::wostream &getWErrorStream();
90 
95 template <int T>
96 struct LogType {
97  static std::string prefix();
98 
99  // Should this log type generate output? This is used
100  // for suppressing debug output in release builds
101  static bool output();
102 
103  // Should this log type flush the output stream before printing its
104  // message? If output and error streams are going to a terminal,
105  // error messages can get lost if output is buffered and error
106  // is unbuffered.
107  static bool flushOutputStream();
108 
109  // Append prefix, file and line number? Used for suppressing
110  // this information for InfoLogType
111  static bool prependPrefix();
112 
113  // Should this log type print the file and line information?
114  static bool outputFileLine();
115 
116  // Should this log type exit once the message is printed?
117  static void exit();
118 };
119 
126 
127 // LogType traits
128 template <>
129 inline std::string InfoLogType::prefix() {
130  return std::string("INFO");
131 }
132 template <>
133 inline std::string WarnLogType::prefix() {
134  return std::string("WARN");
135 }
136 template <>
137 inline std::string FatalType::prefix() {
138  return std::string("ERROR");
139 }
140 template <>
141 inline std::string ErrorLogType::prefix() {
142  return std::string("ERROR");
143 }
144 template <>
145 inline std::string DebugLogType::prefix() {
146  return std::string("DEBUG");
147 }
148 template <>
149 inline std::string StackTraceType::prefix() {
150  return std::string("");
151 }
152 
153 template <>
154 inline bool InfoLogType::output() {
155  return true;
156 }
157 template <>
158 inline bool WarnLogType::output() {
159  return true;
160 }
161 template <>
162 inline bool FatalType::output() {
163  return true;
164 }
165 template <>
166 inline bool ErrorLogType::output() {
167  return true;
168 }
169 template <>
170 inline bool StackTraceType::output() {
171  return true;
172 }
173 template <>
174 inline bool DebugLogType::output() {
175 #if defined(NDEBUG) || defined(PV_DEBUG_OUTPUT)
176  return true;
177 #else
178  return false;
179 #endif // defined(NDEBUG) || defined(PV_DEBUG_OUTPUT)
180 }
181 
182 template <>
183 inline bool InfoLogType::flushOutputStream() {
184  return true;
185 }
186 template <>
187 inline bool WarnLogType::flushOutputStream() {
188  return true;
189 }
190 template <>
191 inline bool FatalType::flushOutputStream() {
192  return true;
193 }
194 template <>
195 inline bool ErrorLogType::flushOutputStream() {
196  return true;
197 }
198 template <>
199 inline bool StackTraceType::flushOutputStream() {
200  return true;
201 }
202 template <>
203 inline bool DebugLogType::flushOutputStream() {
204  return true;
205 }
206 
207 template <>
208 inline bool InfoLogType::prependPrefix() {
209  return false;
210 }
211 template <>
212 inline bool WarnLogType::prependPrefix() {
213  return true;
214 }
215 template <>
216 inline bool FatalType::prependPrefix() {
217  return true;
218 }
219 template <>
220 inline bool ErrorLogType::prependPrefix() {
221  return true;
222 }
223 template <>
224 inline bool StackTraceType::prependPrefix() {
225  return false;
226 }
227 template <>
228 inline bool DebugLogType::prependPrefix() {
229  return true;
230 }
231 
232 template <>
233 inline bool InfoLogType::outputFileLine() {
234  return false;
235 }
236 template <>
237 inline bool WarnLogType::outputFileLine() {
238  return false;
239 }
240 template <>
241 inline bool FatalType::outputFileLine() {
242  return true;
243 }
244 template <>
245 inline bool ErrorLogType::outputFileLine() {
246  return true;
247 }
248 template <>
249 inline bool StackTraceType::outputFileLine() {
250  return false;
251 }
252 template <>
253 inline bool DebugLogType::outputFileLine() {
254  return true;
255 }
256 
257 template <>
258 inline void InfoLogType::exit() {}
259 template <>
260 inline void WarnLogType::exit() {}
261 template <>
262 inline void FatalType::exit() {
263  ::exit(EXIT_FAILURE);
264 }
265 template <>
266 inline void ErrorLogType::exit() {}
267 template <>
268 inline void StackTraceType::exit() {}
269 template <>
270 inline void DebugLogType::exit() {}
271 
272 // Log traits, for definining the stream to be used by the logger
273 template <typename C, typename LT, typename T = std::char_traits<C>>
275  typedef T char_traits;
276  typedef LT LogType;
277 
278  typedef std::basic_ostream<C, T> &(*StrFunc)(std::basic_ostream<C, T> &);
279  static std::basic_ostream<C, T> &stream();
280 };
281 
282 template <>
283 inline std::ostream &LogStreamTraits<char, InfoLogType>::stream() {
284  return getOutputStream();
285 }
286 template <>
287 inline std::wostream &LogStreamTraits<wchar_t, InfoLogType>::stream() {
288  return getWOutputStream();
289 }
290 template <>
291 inline std::ostream &LogStreamTraits<char, WarnLogType>::stream() {
292  return getErrorStream();
293 }
294 template <>
295 inline std::wostream &LogStreamTraits<wchar_t, WarnLogType>::stream() {
296  return getWErrorStream();
297 }
298 template <>
299 inline std::ostream &LogStreamTraits<char, FatalType>::stream() {
300  return getErrorStream();
301 }
302 template <>
303 inline std::wostream &LogStreamTraits<wchar_t, ErrorLogType>::stream() {
304  return getWErrorStream();
305 }
306 template <>
307 inline std::ostream &LogStreamTraits<char, ErrorLogType>::stream() {
308  return getErrorStream();
309 }
310 template <>
311 inline std::wostream &LogStreamTraits<wchar_t, FatalType>::stream() {
312  return getWErrorStream();
313 }
314 template <>
315 inline std::ostream &LogStreamTraits<char, DebugLogType>::stream() {
316  return getOutputStream();
317 }
318 template <>
319 inline std::wostream &LogStreamTraits<wchar_t, DebugLogType>::stream() {
320  return getWOutputStream();
321 }
322 template <>
323 inline std::ostream &LogStreamTraits<char, StackTraceType>::stream() {
324  return getErrorStream();
325 }
326 template <>
327 inline std::wostream &LogStreamTraits<wchar_t, StackTraceType>::stream() {
328  return getWErrorStream();
329 }
330 
331 template <typename C, typename LT, typename T = std::char_traits<C>>
332 struct Log {
333  typedef std::basic_ostream<C, T> basic_ostream;
335  typedef LT LogType;
336 
337  Log(const char *file = __FILE__, int line = __LINE__) : _stream(LogStreamType::stream()) {
338  outputPrefix(file, line);
339  }
340 
341  Log(basic_ostream &s, const char *file = __FILE__, int line = __LINE__) : _stream(s) {
342  outputPrefix(file, line);
343  }
344 
345  ~Log() {
346  if (LogType::flushOutputStream()) {
347  _stream.flush();
348  }
349  LogType::exit();
350  }
351 
352  void outputPrefix(const char *file, int line) {
353  // In release this is optimised away if log type is debug
354  if (LogType::output()) {
355  if (LogType::flushOutputStream()) {
356  getOutputStream().flush();
357  }
358  if (LogType::prependPrefix()) {
359  _stream << LogType::prefix() << " ";
360  }
361  if (LogType::outputFileLine()) {
362  _stream << "<" << basename((char *)file) << ":" << line << ">: ";
363  }
364  }
365  }
366 
367  template <typename V>
368  Log &operator<<(V const &value) {
369  if (LogType::output()) {
370  _stream << value;
371  }
372  return *this;
373  }
374 
375  Log &operator<<(typename LogStreamType::StrFunc func) {
376  if (LogType::output()) {
377  func(_stream);
378  }
379  return *this;
380  }
381 
382  Log &flush() {
383  if (LogType::output()) {
384  _stream.flush();
385  }
386  return *this;
387  }
388 
398  int printf(char const *fmt, ...) {
399  int chars_printed;
400  if (LogType::output()) {
401  va_list args1, args2;
402  va_start(args1, fmt);
403  va_copy(args2, args1);
404  char c;
405  int chars_needed = vsnprintf(&c, 1, fmt, args1);
406  chars_needed++;
407  char output_string[chars_needed];
408  chars_printed = vsnprintf(output_string, chars_needed, fmt, args2);
409  _stream << output_string;
410  va_end(args1);
411  va_end(args2);
412  return chars_needed;
413  }
414  else {
415  chars_printed = 0;
416  }
417  return chars_printed;
418  }
419 
420  private:
421  std::basic_ostream<C, T> &_stream;
422 };
423 
424 #ifdef __GNUC__
425 // Macros that call these functions defined outside of namespace
426 typedef Log<char, DebugLogType> DebugLog;
427 typedef Log<char, InfoLogType> InfoLog;
428 typedef Log<char, WarnLogType> WarnLog;
429 typedef Log<char, FatalType> Fatal;
430 typedef Log<char, ErrorLogType> ErrorLog;
432 
439 
440 #else
441 // Clang __FILE__ and __LINE__ evalaute to the caller of the function
442 // thus the macros aren't needed
448 typedef Log<char, StackTraceType> StackTrace;
449 
450 typedef Log<wchar_t, DebugLogType> WDebug;
451 typedef Log<wchar_t, InfoLogType> WInfo;
452 typedef Log<wchar_t, WarnLogType> WWarn;
453 typedef Log<wchar_t, FatalType> WFatal;
454 typedef Log<wchar_t, ErrorLogType> WError;
455 typedef Log<wchar_t, StackTraceType> WStackTrace;
456 #endif // __GNUC__
457 
458 // setLogFile sets the file that the DebugLog, InfoLog, WarnLog, and Fatal streams write to.
459 void setLogFile(std::string const &logFile, std::ios_base::openmode mode = std::ios_base::out);
460 void setLogFile(char const *logFile, std::ios_base::openmode mode = std::ios_base::out);
461 void setWLogFile(std::wstring const &logFile, std::ios_base::openmode mode = std::ios_base::out);
462 void setWLogFile(char const *logFile, std::ios_base::openmode mode = std::ios_base::out);
463 
464 } // end namespace PV
465 
466 #endif // PVLOG_HPP_
int printf(char const *fmt,...)
Definition: PVLog.hpp:398