OpenVDB  11.0.0
logging.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #ifndef OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED
5 #define OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED
6 
7 #include <openvdb/version.h>
8 
9 #ifdef OPENVDB_USE_LOG4CPLUS
10 
11 #include <log4cplus/appender.h>
12 #include <log4cplus/configurator.h>
13 #include <log4cplus/consoleappender.h>
14 #include <log4cplus/layout.h>
15 #include <log4cplus/logger.h>
16 #include <log4cplus/spi/loggingevent.h>
17 #include <algorithm> // for std::remove()
18 #include <cstring> // for ::strrchr()
19 #include <memory>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 
24 
25 namespace openvdb {
27 namespace OPENVDB_VERSION_NAME {
28 namespace logging {
29 
30 /// @brief Message severity level
31 enum class Level {
32  Debug = log4cplus::DEBUG_LOG_LEVEL,
33  Info = log4cplus::INFO_LOG_LEVEL,
34  Warn = log4cplus::WARN_LOG_LEVEL,
35  Error = log4cplus::ERROR_LOG_LEVEL,
36  Fatal = log4cplus::FATAL_LOG_LEVEL
37 };
38 
39 /// @cond OPENVDB_DOCS_INTERNAL
40 
41 namespace internal {
42 
43 /// @brief log4cplus layout that outputs text in different colors
44 /// for different log levels, using ANSI escape codes
45 class ColoredPatternLayout: public log4cplus::PatternLayout
46 {
47 public:
48  explicit ColoredPatternLayout(const std::string& progName_, bool useColor = true)
49  : log4cplus::PatternLayout(
50  progName_.empty() ? std::string{"%5p: %m%n"} : (progName_ + " %5p: %m%n"))
51  , mUseColor(useColor)
52  , mProgName(progName_)
53  {
54  }
55 
56  ~ColoredPatternLayout() override {}
57 
58  const std::string& progName() const { return mProgName; }
59 
60  void formatAndAppend(log4cplus::tostream& strm,
61  const log4cplus::spi::InternalLoggingEvent& event) override
62  {
63  if (!mUseColor) {
64  log4cplus::PatternLayout::formatAndAppend(strm, event);
65  return;
66  }
67  log4cplus::tostringstream s;
68  switch (event.getLogLevel()) {
69  case log4cplus::DEBUG_LOG_LEVEL: s << "\033[32m"; break; // green
70  case log4cplus::ERROR_LOG_LEVEL:
71  case log4cplus::FATAL_LOG_LEVEL: s << "\033[31m"; break; // red
72  case log4cplus::INFO_LOG_LEVEL: s << "\033[36m"; break; // cyan
73  case log4cplus::WARN_LOG_LEVEL: s << "\033[35m"; break; // magenta
74  }
75  log4cplus::PatternLayout::formatAndAppend(s, event);
76  strm << s.str() << "\033[0m" << std::flush;
77  }
78 
79 // Disable deprecation warnings for std::auto_ptr.
80 #if defined(__ICC)
81  #pragma warning push
82  #pragma warning disable:1478
83 #elif defined(__clang__)
84  #pragma clang diagnostic push
85  #pragma clang diagnostic ignored "-Wdeprecated-declarations"
86 #elif defined(__GNUC__)
87  #pragma GCC diagnostic push
88  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
89 #endif
90 
91 #if defined(LOG4CPLUS_VERSION) && defined(LOG4CPLUS_MAKE_VERSION)
92  #if LOG4CPLUS_VERSION >= LOG4CPLUS_MAKE_VERSION(2, 0, 0)
93  // In log4cplus 2.0.0, std::auto_ptr was replaced with std::unique_ptr.
94  using Ptr = std::unique_ptr<log4cplus::Layout>;
95  #else
96  using Ptr = std::auto_ptr<log4cplus::Layout>;
97  #endif
98 #else
99  using Ptr = std::auto_ptr<log4cplus::Layout>;
100 #endif
101 
102  static Ptr create(const std::string& progName_, bool useColor = true)
103  {
104  return Ptr{new ColoredPatternLayout{progName_, useColor}};
105  }
106 
107 #if defined(__ICC)
108  #pragma warning pop
109 #elif defined(__clang__)
110  #pragma clang diagnostic pop
111 #elif defined(__GNUC__)
112  #pragma GCC diagnostic pop
113 #endif
114 
115 private:
116  bool mUseColor = true;
117  std::string mProgName;
118 }; // class ColoredPatternLayout
119 
120 
121 inline log4cplus::Logger
122 getLogger()
123 {
124  return log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("openvdb"));
125 }
126 
127 
128 inline log4cplus::SharedAppenderPtr
129 getAppender()
130 {
131  return getLogger().getAppender(LOG4CPLUS_TEXT("OPENVDB"));
132 }
133 
134 } // namespace internal
135 
136 /// @endcond
137 
138 
139 /// @brief Return the current logging level.
140 inline Level
142 {
143  switch (internal::getLogger().getLogLevel()) {
144  case log4cplus::DEBUG_LOG_LEVEL: return Level::Debug;
145  case log4cplus::INFO_LOG_LEVEL: return Level::Info;
146  case log4cplus::WARN_LOG_LEVEL: return Level::Warn;
147  case log4cplus::ERROR_LOG_LEVEL: return Level::Error;
148  case log4cplus::FATAL_LOG_LEVEL: break;
149  }
150  return Level::Fatal;
151 }
152 
153 
154 /// @brief Set the logging level. (Lower-level messages will be suppressed.)
155 inline void
157 {
158  internal::getLogger().setLogLevel(static_cast<log4cplus::LogLevel>(lvl));
159 }
160 
161 
162 /// @brief If "-debug", "-info", "-warn", "-error" or "-fatal" is found
163 /// in the given array of command-line arguments, set the logging level
164 /// appropriately and remove the relevant argument(s) from the array.
165 inline void
166 setLevel(int& argc, char* argv[])
167 {
168  for (int i = 1; i < argc; ++i) { // note: skip argv[0]
169  const std::string arg{argv[i]};
170  bool remove = true;
171  if (arg == "-debug") { setLevel(Level::Debug); }
172  else if (arg == "-error") { setLevel(Level::Error); }
173  else if (arg == "-fatal") { setLevel(Level::Fatal); }
174  else if (arg == "-info") { setLevel(Level::Info); }
175  else if (arg == "-warn") { setLevel(Level::Warn); }
176  else { remove = false; }
177  if (remove) argv[i] = nullptr;
178  }
179  auto end = std::remove(argv + 1, argv + argc, nullptr);
180  argc = static_cast<int>(end - argv);
181 }
182 
183 
184 /// @brief Specify a program name to be displayed in log messages.
185 inline void
186 setProgramName(const std::string& progName, bool useColor = true)
187 {
188  // Change the layout of the OpenVDB appender to use colored text
189  // and to incorporate the supplied program name.
190  if (auto appender = internal::getAppender()) {
191  appender->setLayout(internal::ColoredPatternLayout::create(progName, useColor));
192  }
193 }
194 
195 
196 /// @brief Initialize the logging system if it is not already initialized.
197 inline void
198 initialize(bool useColor = true)
199 {
201 
202  if (internal::getAppender()) return; // already initialized
203 
204  // Create the OpenVDB logger if it doesn't already exist.
205  auto logger = internal::getLogger();
206 
207  // Disable "additivity", so that OpenVDB-related messages are directed
208  // to the OpenVDB logger only and are not forwarded up the logger tree.
209  logger.setAdditivity(false);
210 
211  // Attach a console appender to the OpenVDB logger.
212  if (auto appender = log4cplus::SharedAppenderPtr{new log4cplus::ConsoleAppender}) {
213  appender->setName(LOG4CPLUS_TEXT("OPENVDB"));
214  logger.addAppender(appender);
215  }
216 
217  setLevel(Level::Warn);
218  setProgramName("", useColor);
219 }
220 
221 
222 /// @brief Initialize the logging system from command-line arguments.
223 /// @details If "-debug", "-info", "-warn", "-error" or "-fatal" is found
224 /// in the given array of command-line arguments, set the logging level
225 /// appropriately and remove the relevant argument(s) from the array.
226 inline void
227 initialize(int& argc, char* argv[], bool useColor = true)
228 {
229  initialize();
230 
231  setLevel(argc, argv);
232 
233  auto progName = (argc > 0 ? argv[0] : "");
234  if (const char* ptr = ::strrchr(progName, '/')) progName = ptr + 1;
235  setProgramName(progName, useColor);
236 }
237 
238 } // namespace logging
239 } // namespace OPENVDB_VERSION_NAME
240 } // namespace openvdb
241 
242 
243 #define OPENVDB_LOG(level, message) \
244  do { \
245  auto _log = openvdb::logging::internal::getLogger(); \
246  if (_log.isEnabledFor(log4cplus::level##_LOG_LEVEL)) { \
247  std::ostringstream _buf; \
248  _buf << message; \
249  _log.forcedLog(log4cplus::level##_LOG_LEVEL, _buf.str(), __FILE__, __LINE__); \
250  } \
251  } while (0);
252 
253 /// Log an info message of the form '<TT>someVar << "some text" << ...</TT>'.
254 #define OPENVDB_LOG_INFO(message) OPENVDB_LOG(INFO, message)
255 /// Log a warning message of the form '<TT>someVar << "some text" << ...</TT>'.
256 #define OPENVDB_LOG_WARN(message) OPENVDB_LOG(WARN, message)
257 /// Log an error message of the form '<TT>someVar << "some text" << ...</TT>'.
258 #define OPENVDB_LOG_ERROR(message) OPENVDB_LOG(ERROR, message)
259 /// Log a fatal error message of the form '<TT>someVar << "some text" << ...</TT>'.
260 #define OPENVDB_LOG_FATAL(message) OPENVDB_LOG(FATAL, message)
261 #ifdef DEBUG
262 /// In debug builds only, log a debugging message of the form '<TT>someVar << "text" << ...</TT>'.
263 #define OPENVDB_LOG_DEBUG(message) OPENVDB_LOG(DEBUG, message)
264 #else
265 /// In debug builds only, log a debugging message of the form '<TT>someVar << "text" << ...</TT>'.
266 #define OPENVDB_LOG_DEBUG(message)
267 #endif
268 /// @brief Log a debugging message in both debug and optimized builds.
269 /// @warning Don't use this in performance-critical code.
270 #define OPENVDB_LOG_DEBUG_RUNTIME(message) OPENVDB_LOG(DEBUG, message)
271 
272 #else // ifdef OPENVDB_USE_LOG4CPLUS
273 
274 #include <iostream>
275 
276 #define OPENVDB_LOG_INFO(mesg)
277 #define OPENVDB_LOG_WARN(mesg) do { std::cerr << "WARNING: " << mesg << std::endl; } while (0);
278 #define OPENVDB_LOG_ERROR(mesg) do { std::cerr << "ERROR: " << mesg << std::endl; } while (0);
279 #define OPENVDB_LOG_FATAL(mesg) do { std::cerr << "FATAL: " << mesg << std::endl; } while (0);
280 #define OPENVDB_LOG_DEBUG(mesg)
281 #define OPENVDB_LOG_DEBUG_RUNTIME(mesg)
282 
283 namespace openvdb {
285 namespace OPENVDB_VERSION_NAME {
286 namespace logging {
287 
288 enum class Level { Debug, Info, Warn, Error, Fatal };
289 
290 inline Level getLevel() { return Level::Warn; }
291 inline void setLevel(Level) {}
292 inline void setLevel(int&, char*[]) {}
293 inline void setProgramName(const std::string&, bool = true) {}
294 inline void initialize() {}
295 inline void initialize(int&, char*[], bool = true) {}
296 
297 } // namespace logging
298 } // namespace OPENVDB_VERSION_NAME
299 } // namespace openvdb
300 
301 #endif // OPENVDB_USE_LOG4CPLUS
302 
303 
304 namespace openvdb {
306 namespace OPENVDB_VERSION_NAME {
307 namespace logging {
308 
309 /// @brief A LevelScope object sets the logging level to a given level
310 /// and restores it to the current level when the object goes out of scope.
312 {
314  explicit LevelScope(Level newLevel): level(getLevel()) { setLevel(newLevel); }
315  ~LevelScope() { setLevel(level); }
316 };
317 
318 } // namespace logging
319 } // namespace OPENVDB_VERSION_NAME
320 } // namespace openvdb
321 
322 #endif // OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED
LevelScope(Level newLevel)
Definition: logging.h:314
Level level
Definition: logging.h:313
void setProgramName(const std::string &progName, bool useColor=true)
Specify a program name to be displayed in log messages.
Definition: logging.h:186
void initialize(int &argc, char *argv[], bool useColor=true)
Initialize the logging system from command-line arguments.
Definition: logging.h:227
Definition: Exceptions.h:13
void setLevel(int &argc, char *argv[])
If "-debug", "-info", "-warn", "-error" or "-fatal" is found in the given array of command-line argum...
Definition: logging.h:166
Level getLevel()
Return the current logging level.
Definition: logging.h:141
~LevelScope()
Definition: logging.h:315
A LevelScope object sets the logging level to a given level and restores it to the current level when...
Definition: logging.h:311
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
Level
Message severity level.
Definition: logging.h:31
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:212