rosconsole_log4cxx.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 // Author: Josh Faust
00031 
00032 #if defined(__APPLE__) && defined(__GNUC__) && defined(__llvm__) && !defined(__clang__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 2)
00033 #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
00034 #endif
00035 
00036 #include "ros/console.h"
00037 #include "ros/assert.h"
00038 #include <ros/time.h>
00039 #include "log4cxx/appenderskeleton.h"
00040 #include "log4cxx/spi/loggingevent.h"
00041 #include "log4cxx/level.h"
00042 #include "log4cxx/propertyconfigurator.h"
00043 #ifdef _MSC_VER
00044   // Have to be able to encode wchar LogStrings on windows.
00045   #include "log4cxx/helpers/transcoder.h"
00046 #endif
00047 
00048 #include <boost/thread.hpp>
00049 #include <boost/shared_array.hpp>
00050 #include <boost/regex.hpp>
00051 
00052 #include <cstdarg>
00053 #include <cstdlib>
00054 #include <cstdio>
00055 #include <memory>
00056 #include <cstring>
00057 #include <stdexcept>
00058 
00059 namespace ros
00060 {
00061 namespace console
00062 {
00063 namespace impl
00064 {
00065 
00066 log4cxx::LevelPtr g_level_lookup[ levels::Count ] =
00067 {
00068   log4cxx::Level::getDebug(),
00069   log4cxx::Level::getInfo(),
00070   log4cxx::Level::getWarn(),
00071   log4cxx::Level::getError(),
00072   log4cxx::Level::getFatal(),
00073 };
00074 
00075 
00076 class ROSConsoleStdioAppender : public log4cxx::AppenderSkeleton
00077 {
00078 public:
00079   ~ROSConsoleStdioAppender()
00080   {
00081   }
00082 
00083 protected:
00084   virtual void append(const log4cxx::spi::LoggingEventPtr& event, 
00085                       log4cxx::helpers::Pool&)
00086   {
00087     levels::Level level = levels::Count;
00088     if (event->getLevel() == log4cxx::Level::getDebug())
00089     {
00090       level = levels::Debug;
00091     }
00092     else if (event->getLevel() == log4cxx::Level::getInfo())
00093     {
00094       level = levels::Info;
00095     }
00096     else if (event->getLevel() == log4cxx::Level::getWarn())
00097     {
00098       level = levels::Warn;
00099     }
00100     else if (event->getLevel() == log4cxx::Level::getError())
00101     {
00102       level = levels::Error;
00103     }
00104     else if (event->getLevel() == log4cxx::Level::getFatal())
00105     {
00106       level = levels::Fatal;
00107     }
00108 #ifdef _MSC_VER
00109     LOG4CXX_ENCODE_CHAR(tmpstr, event->getMessage());  // has to handle LogString with wchar types.
00110     std::string msg = tmpstr  // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
00111 #else
00112     std::string msg = event->getMessage();
00113 #endif
00114     const log4cxx::spi::LocationInfo& location_info = event->getLocationInformation();
00115     ::ros::console::backend::print(event.operator->(), level, msg.c_str(), location_info.getFileName(), location_info.getMethodName().c_str(), location_info.getLineNumber());
00116   }
00117 
00118   virtual void close()
00119   {
00120   }
00121   virtual bool requiresLayout() const
00122   {
00123     return false;
00124   }
00125 };
00126 
00127 void initialize()
00128 {
00129   // First set up some sane defaults programmatically.
00130   log4cxx::LoggerPtr ros_logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00131   ros_logger->setLevel(log4cxx::Level::getInfo());
00132 
00133   log4cxx::LoggerPtr roscpp_superdebug = log4cxx::Logger::getLogger("ros.roscpp.superdebug");
00134   roscpp_superdebug->setLevel(log4cxx::Level::getWarn());
00135 
00136   // Next try to load the default config file from ROS_ROOT/config/rosconsole.config
00137   char* ros_root_cstr = NULL;
00138 #ifdef _MSC_VER
00139   _dupenv_s(&ros_root_cstr, NULL, "ROS_ROOT");
00140 #else
00141   ros_root_cstr = getenv("ROS_ROOT");
00142 #endif
00143   if (ros_root_cstr)
00144   {
00145     std::string config_file = std::string(ros_root_cstr) + "/config/rosconsole.config";
00146     FILE* config_file_ptr = fopen( config_file.c_str(), "r" );
00147     if( config_file_ptr ) // only load it if the file exists, to avoid a warning message getting printed.
00148     {
00149       fclose( config_file_ptr );
00150       log4cxx::PropertyConfigurator::configure(config_file);
00151     }
00152   }
00153   char* config_file_cstr = NULL;
00154 #ifdef _MSC_VER
00155   _dupenv_s(&config_file_cstr, NULL, "ROSCONSOLE_CONFIG_FILE");
00156 #else
00157   config_file_cstr = getenv("ROSCONSOLE_CONFIG_FILE");
00158 #endif
00159   if ( config_file_cstr )
00160   {
00161     std::string config_file = config_file_cstr;
00162     log4cxx::PropertyConfigurator::configure(config_file);
00163   }
00164 
00165   log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00166   logger->addAppender(new ROSConsoleStdioAppender);
00167 #ifdef _MSC_VER
00168   if ( ros_root_cstr != NULL ) {
00169           free(ros_root_cstr);
00170   }
00171   if ( config_file_cstr != NULL ) {
00172           free(config_file_cstr);
00173   }
00174   if ( format_string != NULL ) {
00175           free(format_string);
00176   }
00177   // getenv implementations don't need free'ing.
00178 #endif
00179 }
00180 
00181 
00182 void print(void* handle, ::ros::console::Level level, const char* str, const char* file, const char* function, int line)
00183 {
00184   log4cxx::Logger* logger  = (log4cxx::Logger*)handle;
00185   try
00186   {
00187     logger->forcedLog(g_level_lookup[level], str, log4cxx::spi::LocationInfo(file, function, line));
00188   }
00189   catch (std::exception& e)
00190   {
00191     fprintf(stderr, "Caught exception while logging: [%s]\n", e.what());
00192   }
00193 }
00194 
00195 bool isEnabledFor(void* handle, ::ros::console::Level level)
00196 {
00197   log4cxx::Logger* logger  = (log4cxx::Logger*)handle;
00198   return logger->isEnabledFor(g_level_lookup[level]);
00199 }
00200 
00201 void* getHandle(const std::string& name)
00202 {
00203   return log4cxx::Logger::getLogger(name);
00204 }
00205 
00206 std::string getName(void* handle)
00207 {
00208   const log4cxx::spi::LoggingEvent* event = (const log4cxx::spi::LoggingEvent*)handle;
00209 #ifdef _MSC_VER
00210   LOG4CXX_ENCODE_CHAR(tmpstr, event->getLoggerName());  // has to handle LogString with wchar types.
00211   return tmpstr  // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
00212 #else
00213   return event->getLoggerName();
00214 #endif
00215 }
00216 
00217 bool get_loggers(std::map<std::string, levels::Level>& loggers)
00218 {
00219   log4cxx::spi::LoggerRepositoryPtr repo = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME)->getLoggerRepository();
00220 
00221   log4cxx::LoggerList current_loggers = repo->getCurrentLoggers();
00222   log4cxx::LoggerList::iterator it = current_loggers.begin();
00223   log4cxx::LoggerList::iterator end = current_loggers.end();
00224   for (; it != end; ++it)
00225   {
00226     std::string name;
00227     #ifdef _MSC_VER
00228       LOG4CXX_ENCODE_CHAR(name, (*it)->getName()); // has to handle LogString with wchar types.
00229     #else
00230       name = (*it)->getName();
00231     #endif
00232 
00233     const log4cxx::LevelPtr& log4cxx_level = (*it)->getEffectiveLevel();
00234     levels::Level level;
00235     if (log4cxx_level == log4cxx::Level::getDebug())
00236     {
00237       level = levels::Debug;
00238     }
00239     else if (log4cxx_level == log4cxx::Level::getInfo())
00240     {
00241       level = levels::Info;
00242     }
00243     else if (log4cxx_level == log4cxx::Level::getWarn())
00244     {
00245       level = levels::Warn;
00246     }
00247     else if (log4cxx_level == log4cxx::Level::getError())
00248     {
00249       level = levels::Error;
00250     }
00251     else if (log4cxx_level == log4cxx::Level::getFatal())
00252     {
00253       level = levels::Fatal;
00254     }
00255     loggers[name] = level;
00256   }
00257 
00258   return true;
00259 }
00260 
00261 bool set_logger_level(const std::string& name, levels::Level level)
00262 {
00263   log4cxx::LevelPtr log4cxx_level;
00264   if (level == levels::Debug)
00265   {
00266     log4cxx_level = log4cxx::Level::getDebug();
00267   }
00268   else if (level == levels::Info)
00269   {
00270     log4cxx_level = log4cxx::Level::getInfo();
00271   }
00272   else if (level == levels::Warn)
00273   {
00274     log4cxx_level = log4cxx::Level::getWarn();
00275   }
00276   else if (level == levels::Error)
00277   {
00278     log4cxx_level = log4cxx::Level::getError();
00279   }
00280   else if (level == levels::Fatal)
00281   {
00282     log4cxx_level = log4cxx::Level::getFatal();
00283   }
00284   else
00285   {
00286     return false;
00287   }
00288 
00289   log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(name);
00290   logger->setLevel(log4cxx_level);
00291   ::ros::console::backend::notifyLoggerLevelsChanged();
00292   return true;
00293 }
00294 
00295 class Log4cxxAppender : public log4cxx::AppenderSkeleton
00296 {
00297 public:
00298   Log4cxxAppender(ros::console::LogAppender* appender) : appender_(appender) {}
00299   ~Log4cxxAppender() {}
00300 
00301 protected:
00302   virtual void append(const log4cxx::spi::LoggingEventPtr& event, log4cxx::helpers::Pool& pool)
00303   {
00304     levels::Level level;
00305     if (event->getLevel() == log4cxx::Level::getFatal())
00306     {
00307       level = levels::Fatal;
00308     }
00309     else if (event->getLevel() == log4cxx::Level::getError())
00310     {
00311       level = levels::Error;
00312     }
00313     else if (event->getLevel() == log4cxx::Level::getWarn())
00314     {
00315       level = levels::Warn;
00316     }
00317     else if (event->getLevel() == log4cxx::Level::getInfo())
00318     {
00319       level = levels::Info;
00320     }
00321     else if (event->getLevel() == log4cxx::Level::getDebug())
00322     {
00323       level = levels::Debug;
00324     }
00325     else
00326     {
00327       return;
00328     }
00329 
00330     #ifdef _MSC_VER
00331       LOG4CXX_ENCODE_CHAR(tmpstr, event->getMessage());  // has to handle LogString with wchar types.
00332       std::string msg = tmpstr  // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
00333     #else
00334       std::string msg = event->getMessage();
00335     #endif
00336 
00337     const log4cxx::spi::LocationInfo& info = event->getLocationInformation();
00338     appender_->log(level, msg.c_str(), info.getFileName(), info.getMethodName().c_str(), info.getLineNumber());
00339   }
00340 
00341   virtual void close() {}
00342   virtual bool requiresLayout() const { return false; }
00343   ros::console::LogAppender* appender_;
00344 };
00345 
00346 Log4cxxAppender* g_log4cxx_appender;
00347 
00348 void register_appender(LogAppender* appender)
00349 {
00350   g_log4cxx_appender = new Log4cxxAppender(appender);
00351   const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00352   logger->addAppender(g_log4cxx_appender);
00353 }
00354 
00355 void shutdown()
00356 {
00357   const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00358   logger->removeAppender(g_log4cxx_appender);
00359   g_log4cxx_appender = 0;
00360   // reset this so that the logger doesn't get crashily destroyed
00361   // again during global destruction.  
00362   //
00363   // See https://code.ros.org/trac/ros/ticket/3271
00364   //
00365   log4cxx::Logger::getRootLogger()->getLoggerRepository()->shutdown();
00366 }
00367 
00368 } // namespace impl
00369 } // namespace console
00370 } // namespace ros


rosconsole
Author(s): Josh Faust
autogenerated on Fri Aug 28 2015 12:33:01