libqi-api  release-2.5.3-2016-11-18
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
log.hxx
Go to the documentation of this file.
1 #pragma once
2 /*
3  * Copyright (c) 2012 Aldebaran Robotics. All rights reserved.
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the COPYING file.
6  */
7 
8 #ifndef _QI_DETAIL_LOG_HXX_
9 #define _QI_DETAIL_LOG_HXX_
10 
11 #include <boost/noncopyable.hpp>
12 #include <boost/preprocessor/cat.hpp>
13 
14 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
15 # define _qiLogDebug(...) qi::log::LogStream(qi::LogLevel_Debug, "", __FUNCTION__, 0, __VA_ARGS__).self()
16 #else
17 # define _qiLogDebug(...) qi::log::LogStream(qi::LogLevel_Debug, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
18 #endif
19 
20 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
21 # define _qiLogVerbose(...) qi::log::LogStream(qi::LogLevel_Verbose, "", __FUNCTION__, 0, __VA_ARGS__).self()
22 #else
23 # define _qiLogVerbose(...) qi::log::LogStream(qi::LogLevel_Verbose, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
24 #endif
25 
26 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
27 # define _qiLogInfo(...) qi::log::LogStream(qi::LogLevel_Info, "", __FUNCTION__, 0, __VA_ARGS__).self()
28 #else
29 # define _qiLogInfo(...) qi::log::LogStream(qi::LogLevel_Info, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
30 #endif
31 
32 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
33 # define _qiLogWarning(...) qi::log::LogStream(qi::LogLevel_Warning, "", __FUNCTION__, 0, __VA_ARGS__).self()
34 #else
35 # define _qiLogWarning(...) qi::log::LogStream(qi::LogLevel_Warning, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
36 #endif
37 
38 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
39 # define _qiLogError(...) qi::log::LogStream(qi::LogLevel_Error, "", __FUNCTION__, 0, __VA_ARGS__).self()
40 #else
41 # define _qiLogError(...) qi::log::LogStream(qi::LogLevel_Error, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
42 #endif
43 
44 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
45 # define _qiLogFatal(...) qi::log::LogStream(qi::LogLevel_Fatal, "", __FUNCTION__, 0, __VA_ARGS__).self()
46 #else
47 # define _qiLogFatal(...) qi::log::LogStream(qi::LogLevel_Fatal, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__).self()
48 #endif
49 
50 
51 # define _QI_FORMAT_ELEM(_, a, elem) % (elem)
52 
53 # define _QI_LOG_FORMAT(Msg, ...) \
54  QI_CAT(_QI_LOG_FORMAT_HASARG_, _QI_LOG_ISEMPTY(__VA_ARGS__))(Msg, __VA_ARGS__)
55 
56 #define _QI_LOG_FORMAT_HASARG_0(Msg, ...) \
57  boost::str(::qi::log::detail::getFormat(Msg) QI_VAARGS_APPLY(_QI_FORMAT_ELEM, _, __VA_ARGS__ ))
58 
59 #define _QI_LOG_FORMAT_HASARG_1(Msg, ...) Msg
60 
61 #define _QI_SECOND(a, ...) __VA_ARGS__
62 
63 /* For fast category access, we use lookup to a fixed name symbol.
64  * The user is required to call qiLogCategory somewhere in scope.
65  *
66  * _QI_LOG_VARIABLE_SUFFIX is used to make variable name (_qi_log_category)
67  * unique when using unity(blob) builds
68  */
69 #ifndef _QI_LOG_VARIABLE_SUFFIX
70 # define _QI_LOG_VARIABLE_SUFFIX _x // dummy/default suffix
71 #endif
72 
73 # define _QI_LOG_CATEGORY_GET() BOOST_PP_CAT(_qi_log_category, _QI_LOG_VARIABLE_SUFFIX)
74 
75 #if defined(NO_QI_LOG_DETAILED_CONTEXT) || defined(NDEBUG)
76 # define _QI_LOG_MESSAGE(Type, Message) \
77  do \
78  { \
79  if (::qi::log::isVisible(_QI_LOG_CATEGORY_GET(), ::qi::Type)) \
80  ::qi::log::log(::qi::Type, \
81  _QI_LOG_CATEGORY_GET(), \
82  Message, \
83  "", __FUNCTION__, 0); \
84  } \
85  while (false)
86 #else
87 # define _QI_LOG_MESSAGE(Type, Message) \
88  do \
89  { \
90  if (::qi::log::isVisible(_QI_LOG_CATEGORY_GET(), ::qi::Type)) \
91  ::qi::log::log(::qi::Type, \
92  _QI_LOG_CATEGORY_GET(), \
93  Message, \
94  __FILE__, __FUNCTION__, __LINE__); \
95  } \
96  while (false)
97 #endif
98 
99 /* Tricky, we do not want to hit category_get if a category is specified
100 * Usual glitch of off-by-one list size: put argument 'TypeCased' in the vaargs
101 * Basically we want variadic macro, but it does not exist, so emulate it using _QI_LOG_EMPTY.
102 */
103 # define _QI_LOG_MESSAGE_STREAM(Type, TypeCased, ...) \
104  QI_CAT(_QI_LOG_MESSAGE_STREAM_HASCAT_, _QI_LOG_ISEMPTY( __VA_ARGS__))(Type, TypeCased, __VA_ARGS__)
105 
106 // no extra argument
107 #define _QI_LOG_MESSAGE_STREAM_HASCAT_1(Type, TypeCased, ...) \
108  ::qi::log::isVisible(_QI_LOG_CATEGORY_GET(), ::qi::Type) \
109  && BOOST_PP_CAT(_qiLog, TypeCased)(_QI_LOG_CATEGORY_GET())
110 
111 // Visual bouncer for macro evalution order glitch.
112 #ifdef _MSC_VER
113 #define _QI_LOG_MESSAGE_STREAM_HASCAT_0(...) QI_DELAY(_QI_LOG_MESSAGE_STREAM_HASCAT_0) ## _BOUNCE(__VA_ARGS__)
114 #else
115 #define _QI_LOG_MESSAGE_STREAM_HASCAT_0(...) _QI_LOG_MESSAGE_STREAM_HASCAT_0_BOUNCE(__VA_ARGS__)
116 #endif
117 
118 // At leas one argument: category. Check for a format argument
119 #define _QI_LOG_MESSAGE_STREAM_HASCAT_0_BOUNCE(Type, TypeCased, cat, ...) \
120  QI_CAT(_QI_LOG_MESSAGE_STREAM_HASCAT_HASFORMAT_, _QI_LOG_ISEMPTY( __VA_ARGS__))(Type, TypeCased, cat, __VA_ARGS__)
121 
122 
123 // No format argument
124 #define _QI_LOG_MESSAGE_STREAM_HASCAT_HASFORMAT_1(Type, TypeCased, cat, ...) \
125  BOOST_PP_CAT(_qiLog,TypeCased)(cat)
126 
127 // Format argument
128 #define _QI_LOG_MESSAGE_STREAM_HASCAT_HASFORMAT_0(Type, TypeCased, cat, ...) \
129  BOOST_PP_CAT(_qiLog, TypeCased)(cat, _QI_LOG_FORMAT(__VA_ARGS__))
130 
131 
132 /* Detecting empty arg is tricky.
133  * Trick 1 below does not work with gcc, because x ## "foo" produces a preprocessor error.
134  * Trick 2 rely on ##__VA_ARGS__
135 */
136 #ifdef _MSC_VER
137 
138 #define _WQI_IS_EMPTY_HELPER___ a,b
139 #define WQI_IS_EMPTY(a,...) QI_CAT_20(QI_LIST_VASIZE,((QI_CAT_22(_WQI_IS_EMPTY_HELPER, QI_CAT_24(QI_CAT_26(_, a), _)))))
140 
141 #define _QI_FIRST_ARG(a, ...) a
142 #define _QI_LOG_ISEMPTY(...) WQI_IS_EMPTY(QI_CAT_18(_, _QI_FIRST_ARG(__VA_ARGS__, 12)))
143 
144 #else
145 
146 #define _QI_LOG_REVERSE 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0
147 #define _QI_LOG_REVERSEEMPTY 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1
148 #define _QI_LOG_ARGN(a, b, c, d, e, f, g, h, i, N, ...) N
149 #define _QI_LOG_NARG_(dummy, ...) _QI_LOG_ARGN(__VA_ARGS__)
150 #define _QI_LOG_NARG(...) _QI_LOG_NARG_(dummy, ##__VA_ARGS__, _QI_LOG_REVERSE)
151 #define _QI_LOG_ISEMPTY(...) _QI_LOG_NARG_(dummy, ##__VA_ARGS__, _QI_LOG_REVERSEEMPTY)
152 
153 #endif
154 
155 namespace qi {
156  namespace log{
157  namespace detail {
158 
159  // Used to remove warning "statement has no effect"
160  inline bool qiFalse() {return false;}
161 
162  class NullStream {
163  public:
165  {
166  }
167 
168  NullStream &self()
169  {
170  return *this;
171  }
172  template <typename T>
174  {
175  return self();
176  }
177 
178  NullStream& operator<<(std::ostream& (*QI_UNUSED(f))(std::ostream&))
179  {
180  return self();
181  }
182  };
183 
184  // Hack required to silence spurious warning in compile-time disabled macros
185  // We need an operator with priority below << and above &&
186  inline bool operator<(bool b, const NullStream& ns)
187  {
188  return false;
189  }
190 
191  struct Category
192  {
195  {}
196 
197  Category(const std::string &name)
198  : name(name)
200  {}
201 
202  std::string name;
203  qi::LogLevel maxLevel; //max level among all subscribers
204  std::vector<qi::LogLevel> levels; //level by subscribers
205 
206  void setLevel(SubscriberId sub, qi::LogLevel level);
207  };
208 
209  QI_API boost::format getFormat(const std::string& s);
210 
211  // given a set of rules in the format documented in the public header,
212  // return a list of (category name, LogLevel) tuples.
213  QI_API std::vector<std::tuple<std::string, qi::LogLevel>> parseFilterRules(
214  const std::string &rules);
215  }
216 
217  //inlined for perf
218  inline bool isVisible(CategoryType category, qi::LogLevel level)
219  {
220  return category && level <= category->maxLevel;
221  }
222 
224  class LogStream: public std::stringstream, boost::noncopyable
225  {
226  public:
227  LogStream(const qi::LogLevel level,
228  const char *file,
229  const char *function,
230  const int line,
231  const char *category)
232  : _logLevel(level)
233  , _category(category)
234  , _categoryType(0)
235  , _file(file)
236  , _function(function)
237  , _line(line)
238  {
239  }
240  LogStream(const qi::LogLevel level,
241  const char *file,
242  const char *function,
243  const int line,
244  CategoryType category)
245  : _logLevel(level)
246  , _category(0)
247  , _categoryType(category)
248  , _file(file)
249  , _function(function)
250  , _line(line)
251  {
252  }
253  LogStream(const qi::LogLevel level,
254  const char *file,
255  const char *function,
256  const int line,
257  const char *category,
258  const std::string& message)
259  : _logLevel(level)
260  , _category(category)
261  , _categoryType(0)
262  , _file(file)
263  , _function(function)
264  , _line(line)
265  {
266  *this << message;
267  }
268 
270  {
271  if (_category)
272  qi::log::log(_logLevel, _category, this->str().c_str(), _file, _function, _line);
273  else
274  qi::log::log(_logLevel, _categoryType, this->str(), _file, _function, _line);
275  }
276 
277  LogStream& self() {
278  return *this;
279  }
280 
281  private:
282  qi::LogLevel _logLevel;
283  const char *_category;
284  CategoryType _categoryType;
285  const char *_file;
286  const char *_function;
287  int _line;
288  };
289  }
290 }
291 
292 #endif // _QI_DETAIL_LOG_HXX_
LogStream(const qi::LogLevel level, const char *file, const char *function, const int line, const char *category)
Definition: log.hxx:227
void setLevel(SubscriberId sub, qi::LogLevel level)
LogStream(const qi::LogLevel level, const char *file, const char *function, const int line, CategoryType category)
Definition: log.hxx:240
silent log level
Definition: log.hpp:133
#define QI_API
Definition: api.hpp:33
bool operator<(bool b, const NullStream &ns)
Definition: log.hxx:186
bool qiFalse()
Definition: log.hxx:160
LogLevel
Log level verbosity.
Definition: log.hpp:132
LogStream(const qi::LogLevel level, const char *file, const char *function, const int line, const char *category, const std::string &message)
Definition: log.hxx:253
unsigned int SubscriberId
Subscriber Identifier.
Definition: log.hpp:203
qi::LogLevel maxLevel
Definition: log.hxx:203
Category(const std::string &name)
Definition: log.hxx:197
std::vector< qi::LogLevel > levels
Definition: log.hxx:204
std::vector< std::tuple< std::string, qi::LogLevel > > parseFilterRules(const std::string &rules)
void log(const qi::LogLevel verb, const char *category, const char *msg, const char *file="", const char *fct="", const int line=0)
Log function. You should call qiLog* macros instead.
NullStream & operator<<(std::ostream &(*QI_UNUSED(f))(std::ostream &))
Definition: log.hxx:178
NullStream & operator<<(const T &QI_UNUSED(val))
Definition: log.hxx:173
boost::format getFormat(const std::string &s)
bool isVisible(CategoryType category, qi::LogLevel level)
Check if the given combination of category and level is enable.
Definition: log.hxx:218
#define QI_UNUSED(x)
This macro tags a parameter as unused.
Definition: macro.hpp:277
std::string name
Definition: log.hxx:202