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