GraphLab: Distributed Graph-Parallel API  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
logger.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009 Carnegie Mellon University.
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an "AS
13  * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language
15  * governing permissions and limitations under the License.
16  *
17  * For more about this software visit:
18  *
19  * http://www.graphlab.ml.cmu.edu
20  *
21  */
22 
23 
24 /**
25  * @file logger.hpp
26  * Usage:
27  * First include logger.hpp. To logger, use the logger() function
28  * There are 2 output levels. A "soft" output level which is
29  * set by calling global_logger.set_log_level(), as well as a "hard" output
30  * level OUTPUTLEVEL which is set in the source code (logger.h).
31  *
32  * when you call "logger()" with a loglevel and if the loglevel is greater than
33  * both of the output levels, the string will be written.
34  * written to a logger file. Otherwise, logger() has no effect.
35  *
36  * The difference between the hard level and the soft level is that the
37  * soft level can be changed at runtime, while the hard level optimizes away
38  * logging calls at compile time.
39  */
40 
41 #ifndef GRAPHLAB_LOG_LOG_HPP
42 #define GRAPHLAB_LOG_LOG_HPP
43 #include <fstream>
44 #include <sstream>
45 #include <cstdlib>
46 #include <cassert>
47 #include <cstring>
48 #include <cstdarg>
49 #include <pthread.h>
50 #include <graphlab/util/timer.hpp>
51 /**
52  * \def LOG_FATAL
53  * Used for fatal and probably irrecoverable conditions
54  * \def LOG_ERROR
55  * Used for errors which are recoverable within the scope of the function
56  * \def LOG_WARNING
57  * Logs interesting conditions which are probably not fatal
58  * \def LOG_EMPH
59  * Outputs as LOG_INFO, but in LOG_WARNING colors. Useful for
60  * outputting information you want to emphasize.
61  * \def LOG_INFO
62  * Used for providing general useful information
63  * \def LOG_DEBUG
64  * Debugging purposes only
65  */
66 #define LOG_NONE 6
67 #define LOG_FATAL 5
68 #define LOG_ERROR 4
69 #define LOG_WARNING 3
70 #define LOG_EMPH 2
71 #define LOG_INFO 1
72 #define LOG_DEBUG 0
73 
74 /**
75  * \def OUTPUTLEVEL
76  * The minimum level to logger at
77  * \def LOG_NONE
78  * OUTPUTLEVEL to LOG_NONE to disable logging
79  */
80 
81 #ifndef OUTPUTLEVEL
82 #define OUTPUTLEVEL LOG_EMPH
83 #endif
84 /// If set, logs to screen will be printed in color
85 #define COLOROUTPUT
86 
87 
88 /**
89  * \def logger(lvl,fmt,...)
90  * extracts the filename, line number
91  * and function name and calls _log. It will be optimized
92  * away if LOG_NONE is set
93  * This relies on a few compiler macros. As far as I know, these
94  * macros are pretty standard among most other C++ compilers.
95  */
96 #if OUTPUTLEVEL == LOG_NONE
97 // totally disable logging
98 #define logger(lvl,fmt,...)
99 #define logbuf(lvl,fmt,...)
100 #define logstream(lvl) null_stream()
101 
102 #define logger_once(lvl,fmt,...)
103 #define logstream_once(lvl) null_stream()
104 
105 #define logger_ontick(sec,lvl,fmt,...)
106 #define logstream_ontick(sec, lvl) null_stream()
107 
108 #else
109 
110 #define logger(lvl,fmt,...) \
111  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__,fmt,##__VA_ARGS__))
112 
113 
114 #define logbuf(lvl,buf,len) \
115  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, \
116  __func__ ,__LINE__,buf,len))
117 
118 #define logstream(lvl) \
119  (log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__) )
120 
121 #define logger_once(lvl,fmt,...) \
122 { \
123  static bool __printed__ = false; \
124  if (!__printed__) { \
125  __printed__ = true; \
126  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__,fmt,##__VA_ARGS__)); \
127  } \
128 }
129 
130 #define logstream_once(lvl) \
131 (*({ \
132  static bool __printed__ = false; \
133  bool __prev_printed__ = __printed__; \
134  if (!__printed__) __printed__ = true; \
135  &(log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__, !__prev_printed__) ); \
136 }))
137 
138 #define logger_ontick(sec,lvl,fmt,...) \
139 { \
140  static float last_print = -sec - 1; \
141  float curtime = graphlab::timer::approx_time_seconds(); \
142  if (last_print + sec <= curtime) { \
143  last_print = curtime; \
144  (log_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__,fmt,##__VA_ARGS__)); \
145  } \
146 }
147 
148 #define logstream_ontick(sec,lvl) \
149 (*({ \
150  static float last_print = -sec - 1; \
151  float curtime = graphlab::timer::approx_time_seconds(); \
152  bool print_now = false; \
153  if (last_print + sec <= curtime) { \
154  last_print = curtime; \
155  print_now = true; \
156  } \
157  &(log_stream_dispatch<(lvl >= OUTPUTLEVEL)>::exec(lvl,__FILE__, __func__ ,__LINE__, print_now) ); \
158 }))
159 
160 
161 #endif
162 
163 namespace logger_impl {
164 struct streambuff_tls_entry {
165  std::stringstream streambuffer;
166  bool streamactive;
167 };
168 }
169 
170 
171 /** Hack! This is defined in assertions.cpp */
172 void __print_back_trace();
173 
174 /**
175  logging class.
176  This writes to a file, and/or the system console.
177 */
179  public:
180  /** Default constructor. By default, log_to_console is off,
181  there is no logger file, and logger level is set to LOG_WARNING
182  */
183  file_logger();
184 
185  ~file_logger(); /// destructor. flushes and closes the current logger file
186 
187  /** Closes the current logger file if one exists.
188  if 'file' is not an empty string, it will be opened and
189  all subsequent logger output will be written into 'file'.
190  Any existing content of 'file' will be cleared.
191  Return true on success and false on failure.
192  */
193  bool set_log_file(std::string file);
194 
195  /// If consolelog is true, subsequent logger output will be written to stderr
196  void set_log_to_console(bool consolelog) {
197  log_to_console = consolelog;
198  }
199 
200  /// Returns the current logger file.
201  std::string get_log_file(void) {
202  return log_file;
203  }
204 
205  /// Returns true if output is being written to stderr
207  return log_to_console;
208  }
209 
210  /// Returns the current logger level
212  return log_level;
213  }
214 
215  file_logger& start_stream(int lineloglevel,const char* file,const char* function, int line, bool do_start = true);
216 
217  template <typename T>
218  file_logger& operator<<(T a) {
219  // get the stream buffer
220  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
221  pthread_getspecific(streambuffkey));
222  if (streambufentry != NULL) {
223  std::stringstream& streambuffer = streambufentry->streambuffer;
224  bool& streamactive = streambufentry->streamactive;
225 
226  if (streamactive) streambuffer << a;
227  }
228  return *this;
229  }
230 
231  file_logger& operator<<(const char* a) {
232  // get the stream buffer
233  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
234  pthread_getspecific(streambuffkey));
235  if (streambufentry != NULL) {
236  std::stringstream& streambuffer = streambufentry->streambuffer;
237  bool& streamactive = streambufentry->streamactive;
238 
239  if (streamactive) {
240  streambuffer << a;
241  if (a[strlen(a)-1] == '\n') {
242  stream_flush();
243  }
244  }
245  }
246  return *this;
247  }
248 
249  file_logger& operator<<(std::ostream& (*f)(std::ostream&)){
250  // get the stream buffer
251  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
252  pthread_getspecific(streambuffkey));
253  if (streambufentry != NULL) {
254  std::stringstream& streambuffer = streambufentry->streambuffer;
255  bool& streamactive = streambufentry->streamactive;
256 
257  typedef std::ostream& (*endltype)(std::ostream&);
258  if (streamactive) {
259  if (endltype(f) == endltype(std::endl)) {
260  streambuffer << "\n";
261  stream_flush();
262  if(streamloglevel >= LOG_ERROR) {
263  __print_back_trace();
264  }
265  if(streamloglevel == LOG_FATAL) {
266  throw "log fatal";
267  // exit(EXIT_FAILURE);
268  }
269  }
270  }
271  }
272  return *this;
273  }
274 
275 
276 
277  /** Sets the current logger level. All logging commands below the current
278  logger level will not be written. */
279  void set_log_level(int new_log_level) {
280  log_level = new_log_level;
281  }
282 
283  /**
284  * logs the message if loglevel>=OUTPUTLEVEL
285  * This function should not be used directly. Use logger()
286  *
287  * @param loglevel Type of message \see LOG_DEBUG LOG_INFO LOG_WARNING LOG_ERROR LOG_FATAL
288  * @param file File where the logger call originated
289  * @param function Function where the logger call originated
290  * @param line Line number where the logger call originated
291  * @param fmt printf format string
292  * @param arg var args. The parameters that match the format string
293  */
294  void _log(int loglevel,const char* file,const char* function,
295  int line,const char* fmt, va_list arg );
296 
297 
298  void _logbuf(int loglevel,const char* file,const char* function,
299  int line, const char* buf, int len);
300 
301  void _lograw(int loglevel, const char* buf, int len);
302 
303  void stream_flush() {
304  // get the stream buffer
305  logger_impl::streambuff_tls_entry* streambufentry = reinterpret_cast<logger_impl::streambuff_tls_entry*>(
306  pthread_getspecific(streambuffkey));
307  if (streambufentry != NULL) {
308  std::stringstream& streambuffer = streambufentry->streambuffer;
309 
310  streambuffer.flush();
311  _lograw(streamloglevel,
312  streambuffer.str().c_str(),
313  (int)(streambuffer.str().length()));
314  streambuffer.str("");
315  }
316  }
317  private:
318  std::ofstream fout;
319  std::string log_file;
320 
321  pthread_key_t streambuffkey;
322 
323  int streamloglevel;
324  pthread_mutex_t mut;
325 
326  bool log_to_console;
327  int log_level;
328 
329 };
330 
331 
332 file_logger& global_logger();
333 
334 /**
335 Wrapper to generate 0 code if the output level is lower than the log level
336 */
337 template <bool dostuff>
338 struct log_dispatch {};
339 
340 template <>
341 struct log_dispatch<true> {
342  inline static void exec(int loglevel,const char* file,const char* function,
343  int line,const char* fmt, ... ) {
344  va_list argp;
345  va_start(argp, fmt);
346  global_logger()._log(loglevel, file, function, line, fmt, argp);
347  va_end(argp);
348  if(loglevel >= LOG_ERROR) {
349  __print_back_trace();
350  }
351  if(loglevel == LOG_FATAL) {
352  throw "log fatal";
353  // exit(EXIT_FAILURE);
354  }
355 
356  }
357 };
358 
359 template <>
360 struct log_dispatch<false> {
361  inline static void exec(int loglevel,const char* file,const char* function,
362  int line,const char* fmt, ... ) {}
363 };
364 
365 
366 struct null_stream {
367  template<typename T>
368  inline null_stream operator<<(T t) { return null_stream(); }
369  inline null_stream operator<<(const char* a) { return null_stream(); }
370  inline null_stream operator<<(std::ostream& (*f)(std::ostream&)) { return null_stream(); }
371 };
372 
373 
374 template <bool dostuff>
375 struct log_stream_dispatch {};
376 
377 template <>
378 struct log_stream_dispatch<true> {
379  inline static file_logger& exec(int lineloglevel,const char* file,const char* function, int line, bool do_start = true) {
380  return global_logger().start_stream(lineloglevel, file, function, line, do_start);
381  }
382 };
383 
384 template <>
385 struct log_stream_dispatch<false> {
386  inline static null_stream exec(int lineloglevel,const char* file,const char* function, int line, bool do_start = true) {
387  return null_stream();
388  }
389 };
390 
391 void textcolor(FILE* handle, int attr, int fg);
392 void reset_color(FILE* handle);
393 
394 #include <graphlab/logger/assertions.hpp>
395 
396 #endif
397