rosconsole_log4cxx.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2008, Willow Garage, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 // Author: Josh Faust
31 
32 #if defined(__APPLE__) && defined(__GNUC__) && defined(__llvm__) && !defined(__clang__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
33 #error This code is known to provoke a compiler crash with llvm-gcc 4.2. You will have better luck with clang++. See code.ros.org/trac/ros/ticket/3626
34 #endif
35 
36 #include "ros/console.h"
37 #include "ros/assert.h"
38 #include <ros/time.h>
39 #define ROSCONSOLE_CONSOLE_IMPL_EXPORTS
40 #include "ros/console_impl.h"
41 
42 #include "log4cxx/appenderskeleton.h"
43 #include "log4cxx/spi/loggingevent.h"
44 #include "log4cxx/level.h"
45 #include "log4cxx/propertyconfigurator.h"
46 #ifdef _MSC_VER
47  // Have to be able to encode wchar LogStrings on windows.
48  #include "log4cxx/helpers/transcoder.h"
49 #endif
50 
51 #include <boost/thread.hpp>
52 #include <boost/shared_array.hpp>
53 #include <boost/regex.hpp>
54 
55 #include <cstdarg>
56 #include <cstdlib>
57 #include <cstdio>
58 #include <memory>
59 #include <cstring>
60 #include <stdexcept>
61 
62 namespace ros
63 {
64 namespace console
65 {
66 namespace impl
67 {
68 
69 log4cxx::LevelPtr g_level_lookup[ levels::Count ] =
70 {
71  log4cxx::Level::getDebug(),
72  log4cxx::Level::getInfo(),
73  log4cxx::Level::getWarn(),
74  log4cxx::Level::getError(),
75  log4cxx::Level::getFatal(),
76 };
77 
78 
79 class ROSConsoleStdioAppender : public log4cxx::AppenderSkeleton
80 {
81 public:
83  {
84  }
85 
86 protected:
87  virtual void append(const log4cxx::spi::LoggingEventPtr& event,
88  log4cxx::helpers::Pool&)
89  {
91  if (event->getLevel() == log4cxx::Level::getDebug())
92  {
93  level = levels::Debug;
94  }
95  else if (event->getLevel() == log4cxx::Level::getInfo())
96  {
97  level = levels::Info;
98  }
99  else if (event->getLevel() == log4cxx::Level::getWarn())
100  {
101  level = levels::Warn;
102  }
103  else if (event->getLevel() == log4cxx::Level::getError())
104  {
105  level = levels::Error;
106  }
107  else if (event->getLevel() == log4cxx::Level::getFatal())
108  {
109  level = levels::Fatal;
110  }
111 #ifdef _MSC_VER
112  LOG4CXX_ENCODE_CHAR(tmpstr, event->getMessage()); // has to handle LogString with wchar types.
113  std::string msg = tmpstr; // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
114 #else
115  std::string msg = event->getMessage();
116 #endif
117  const log4cxx::spi::LocationInfo& location_info = event->getLocationInformation();
118  ::ros::console::backend::print(event.operator->(), level, msg.c_str(), location_info.getFileName(), location_info.getMethodName().c_str(), location_info.getLineNumber());
119  }
120 
121  virtual void close()
122  {
123  }
124  virtual bool requiresLayout() const
125  {
126  return false;
127  }
128 };
129 
130 void initialize()
131 {
132  // First set up some sane defaults programmatically.
133  log4cxx::LoggerPtr ros_logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
134  ros_logger->setLevel(log4cxx::Level::getInfo());
135 
136  log4cxx::LoggerPtr roscpp_superdebug = log4cxx::Logger::getLogger("ros.roscpp.superdebug");
137  roscpp_superdebug->setLevel(log4cxx::Level::getWarn());
138 
139  // Next try to load the default config file from ROS_ROOT/config/rosconsole.config
140  char* ros_root_cstr = NULL;
141 #ifdef _MSC_VER
142  _dupenv_s(&ros_root_cstr, NULL, "ROS_ROOT");
143 #else
144  ros_root_cstr = getenv("ROS_ROOT");
145 #endif
146  if (ros_root_cstr)
147  {
148  std::string config_file = std::string(ros_root_cstr) + "/config/rosconsole.config";
149  FILE* config_file_ptr = fopen( config_file.c_str(), "r" );
150  if( config_file_ptr ) // only load it if the file exists, to avoid a warning message getting printed.
151  {
152  fclose( config_file_ptr );
153  log4cxx::PropertyConfigurator::configure(config_file);
154  }
155  }
156  char* config_file_cstr = NULL;
157 #ifdef _MSC_VER
158  _dupenv_s(&config_file_cstr, NULL, "ROSCONSOLE_CONFIG_FILE");
159 #else
160  config_file_cstr = getenv("ROSCONSOLE_CONFIG_FILE");
161 #endif
162  if ( config_file_cstr )
163  {
164  std::string config_file = config_file_cstr;
165  log4cxx::PropertyConfigurator::configure(config_file);
166  }
167 
168  log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
169  logger->addAppender(new ROSConsoleStdioAppender);
170 #ifdef _MSC_VER
171  if ( ros_root_cstr != NULL ) {
172  free(ros_root_cstr);
173  }
174  if ( config_file_cstr != NULL ) {
175  free(config_file_cstr);
176  }
177  // getenv implementations don't need free'ing.
178 #endif
179 }
180 
181 
182 void print(void* handle, ::ros::console::Level level, const char* str, const char* file, const char* function, int line)
183 {
184  log4cxx::Logger* logger = (log4cxx::Logger*)handle;
185  try
186  {
187  logger->forcedLog(g_level_lookup[level], str, log4cxx::spi::LocationInfo(file, function, line));
188  }
189  catch (std::exception& e)
190  {
191  fprintf(stderr, "Caught exception while logging: [%s]\n", e.what());
192  }
193 }
194 
195 bool isEnabledFor(void* handle, ::ros::console::Level level)
196 {
197  log4cxx::Logger* logger = (log4cxx::Logger*)handle;
198  return logger->isEnabledFor(g_level_lookup[level]);
199 }
200 
201 void* getHandle(const std::string& name)
202 {
203  return log4cxx::Logger::getLogger(name);
204 }
205 
206 std::string getName(void* handle)
207 {
208  const log4cxx::spi::LoggingEvent* event = (const log4cxx::spi::LoggingEvent*)handle;
209 #ifdef _MSC_VER
210  LOG4CXX_ENCODE_CHAR(tmpstr, event->getLoggerName()); // has to handle LogString with wchar types.
211  return tmpstr; // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
212 #else
213  return event->getLoggerName();
214 #endif
215 }
216 
217 bool get_loggers(std::map<std::string, levels::Level>& loggers)
218 {
219  log4cxx::spi::LoggerRepositoryPtr repo = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME)->getLoggerRepository();
220 
221  log4cxx::LoggerList current_loggers = repo->getCurrentLoggers();
222  log4cxx::LoggerList::iterator it = current_loggers.begin();
223  log4cxx::LoggerList::iterator end = current_loggers.end();
224  for (; it != end; ++it)
225  {
226  #ifdef _MSC_VER
227  LOG4CXX_ENCODE_CHAR(tmpstr, (*it)->getName()); // has to handle LogString with wchar types.
228  std::string name = tmpstr;
229  #else
230  std::string name = (*it)->getName();
231  #endif
232 
233  const log4cxx::LevelPtr& log4cxx_level = (*it)->getEffectiveLevel();
234  levels::Level level;
235  if (log4cxx_level == log4cxx::Level::getDebug())
236  {
237  level = levels::Debug;
238  }
239  else if (log4cxx_level == log4cxx::Level::getInfo())
240  {
241  level = levels::Info;
242  }
243  else if (log4cxx_level == log4cxx::Level::getWarn())
244  {
245  level = levels::Warn;
246  }
247  else if (log4cxx_level == log4cxx::Level::getError())
248  {
249  level = levels::Error;
250  }
251  else if (log4cxx_level == log4cxx::Level::getFatal())
252  {
253  level = levels::Fatal;
254  }
255  else
256  {
257  return false;
258  }
259  loggers[name] = level;
260  }
261 
262  return true;
263 }
264 
265 bool set_logger_level(const std::string& name, levels::Level level)
266 {
267  log4cxx::LevelPtr log4cxx_level;
268  if (level == levels::Debug)
269  {
270  log4cxx_level = log4cxx::Level::getDebug();
271  }
272  else if (level == levels::Info)
273  {
274  log4cxx_level = log4cxx::Level::getInfo();
275  }
276  else if (level == levels::Warn)
277  {
278  log4cxx_level = log4cxx::Level::getWarn();
279  }
280  else if (level == levels::Error)
281  {
282  log4cxx_level = log4cxx::Level::getError();
283  }
284  else if (level == levels::Fatal)
285  {
286  log4cxx_level = log4cxx::Level::getFatal();
287  }
288  else
289  {
290  return false;
291  }
292 
293  log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(name);
294  logger->setLevel(log4cxx_level);
296  return true;
297 }
298 
299 class Log4cxxAppender : public log4cxx::AppenderSkeleton
300 {
301 public:
302  Log4cxxAppender(ros::console::LogAppender* appender) : appender_(appender) {}
304  {
305  return appender_;
306  }
308 
309 protected:
310  virtual void append(const log4cxx::spi::LoggingEventPtr& event, log4cxx::helpers::Pool& pool)
311  {
312  (void)pool;
313  levels::Level level;
314  if (event->getLevel() == log4cxx::Level::getFatal())
315  {
316  level = levels::Fatal;
317  }
318  else if (event->getLevel() == log4cxx::Level::getError())
319  {
320  level = levels::Error;
321  }
322  else if (event->getLevel() == log4cxx::Level::getWarn())
323  {
324  level = levels::Warn;
325  }
326  else if (event->getLevel() == log4cxx::Level::getInfo())
327  {
328  level = levels::Info;
329  }
330  else if (event->getLevel() == log4cxx::Level::getDebug())
331  {
332  level = levels::Debug;
333  }
334  else
335  {
336  return;
337  }
338 
339  #ifdef _MSC_VER
340  LOG4CXX_ENCODE_CHAR(tmpstr, event->getMessage()); // has to handle LogString with wchar types.
341  std::string msg = tmpstr; // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
342  #else
343  std::string msg = event->getMessage();
344  #endif
345 
346  const log4cxx::spi::LocationInfo& info = event->getLocationInformation();
347  appender_->log(level, msg.c_str(), info.getFileName(), info.getMethodName().c_str(), info.getLineNumber());
348  }
349 
350  virtual void close() {}
351  virtual bool requiresLayout() const { return false; }
353 };
354 
356 
357 void register_appender(LogAppender* appender)
358 {
359  g_log4cxx_appender = new Log4cxxAppender(appender);
360  const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
361  logger->addAppender(g_log4cxx_appender);
362 }
363 
364 void deregister_appender(LogAppender* appender){
365  if(g_log4cxx_appender->getAppender() == appender)
366  {
367  const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
368  logger->removeAppender(g_log4cxx_appender);
369  delete g_log4cxx_appender;
370  g_log4cxx_appender = 0;
371  }
372 }
373 void shutdown()
374 {
375  if(g_log4cxx_appender)
376  {
377  const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
378  logger->removeAppender(g_log4cxx_appender);
379  g_log4cxx_appender = 0;
380  }
381  // reset this so that the logger doesn't get crashily destroyed
382  // again during global destruction.
383  //
384  // See https://code.ros.org/trac/ros/ticket/3271
385  //
386  log4cxx::Logger::getRootLogger()->getLoggerRepository()->shutdown();
387 }
388 
389 } // namespace impl
390 } // namespace console
391 } // namespace ros
log4cxx::LevelPtr g_level_lookup[levels::Count]
ROSCONSOLE_CONSOLE_IMPL_DECL void print(void *handle, ::ros::console::Level level, const char *str, const char *file, const char *function, int line)
ROSCONSOLE_DECL void notifyLoggerLevelsChanged()
Tells the system that a logger&#39;s level has changed.
Definition: rosconsole.cpp:654
virtual void append(const log4cxx::spi::LoggingEventPtr &event, log4cxx::helpers::Pool &)
ROSCONSOLE_CONSOLE_IMPL_DECL void shutdown()
ROSCONSOLE_CONSOLE_IMPL_DECL std::string getName(void *handle)
ros::console::LogAppender * appender_
#define ROSCONSOLE_ROOT_LOGGER_NAME
Definition: console.h:299
ROSCONSOLE_CONSOLE_IMPL_DECL void register_appender(LogAppender *appender)
ROSCONSOLE_CONSOLE_IMPL_DECL void initialize()
ROSCONSOLE_CONSOLE_IMPL_DECL bool get_loggers(std::map< std::string, levels::Level > &loggers)
ROSCONSOLE_CONSOLE_IMPL_DECL bool set_logger_level(const std::string &name, levels::Level level)
virtual void append(const log4cxx::spi::LoggingEventPtr &event, log4cxx::helpers::Pool &pool)
ROSCONSOLE_CONSOLE_IMPL_DECL bool isEnabledFor(void *handle, ::ros::console::Level level)
ROSCONSOLE_CONSOLE_IMPL_DECL void deregister_appender(LogAppender *appender)
Log4cxxAppender(ros::console::LogAppender *appender)
const ros::console::LogAppender * getAppender() const
Log4cxxAppender * g_log4cxx_appender
ROSCONSOLE_CONSOLE_IMPL_DECL void * getHandle(const std::string &name)
void print(ros::console::Level level, const std::string &s)
Definition: example.cpp:38


rosconsole
Author(s): Josh Faust
autogenerated on Thu Apr 4 2019 02:50:43