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     else
00256     {
00257       return false;
00258     }
00259     loggers[name] = level;
00260   }
00261 
00262   return true;
00263 }
00264 
00265 bool set_logger_level(const std::string& name, levels::Level level)
00266 {
00267   log4cxx::LevelPtr log4cxx_level;
00268   if (level == levels::Debug)
00269   {
00270     log4cxx_level = log4cxx::Level::getDebug();
00271   }
00272   else if (level == levels::Info)
00273   {
00274     log4cxx_level = log4cxx::Level::getInfo();
00275   }
00276   else if (level == levels::Warn)
00277   {
00278     log4cxx_level = log4cxx::Level::getWarn();
00279   }
00280   else if (level == levels::Error)
00281   {
00282     log4cxx_level = log4cxx::Level::getError();
00283   }
00284   else if (level == levels::Fatal)
00285   {
00286     log4cxx_level = log4cxx::Level::getFatal();
00287   }
00288   else
00289   {
00290     return false;
00291   }
00292 
00293   log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(name);
00294   logger->setLevel(log4cxx_level);
00295   ::ros::console::backend::notifyLoggerLevelsChanged();
00296   return true;
00297 }
00298 
00299 class Log4cxxAppender : public log4cxx::AppenderSkeleton
00300 {
00301 public:
00302   Log4cxxAppender(ros::console::LogAppender* appender) : appender_(appender) {}
00303   ~Log4cxxAppender() {}
00304 
00305 protected:
00306   virtual void append(const log4cxx::spi::LoggingEventPtr& event, log4cxx::helpers::Pool& pool)
00307   {
00308     (void)pool;
00309     levels::Level level;
00310     if (event->getLevel() == log4cxx::Level::getFatal())
00311     {
00312       level = levels::Fatal;
00313     }
00314     else if (event->getLevel() == log4cxx::Level::getError())
00315     {
00316       level = levels::Error;
00317     }
00318     else if (event->getLevel() == log4cxx::Level::getWarn())
00319     {
00320       level = levels::Warn;
00321     }
00322     else if (event->getLevel() == log4cxx::Level::getInfo())
00323     {
00324       level = levels::Info;
00325     }
00326     else if (event->getLevel() == log4cxx::Level::getDebug())
00327     {
00328       level = levels::Debug;
00329     }
00330     else
00331     {
00332       return;
00333     }
00334 
00335     #ifdef _MSC_VER
00336       LOG4CXX_ENCODE_CHAR(tmpstr, event->getMessage());  // has to handle LogString with wchar types.
00337       std::string msg = tmpstr  // tmpstr gets instantiated inside the LOG4CXX_ENCODE_CHAR macro
00338     #else
00339       std::string msg = event->getMessage();
00340     #endif
00341 
00342     const log4cxx::spi::LocationInfo& info = event->getLocationInformation();
00343     appender_->log(level, msg.c_str(), info.getFileName(), info.getMethodName().c_str(), info.getLineNumber());
00344   }
00345 
00346   virtual void close() {}
00347   virtual bool requiresLayout() const { return false; }
00348   ros::console::LogAppender* appender_;
00349 };
00350 
00351 Log4cxxAppender* g_log4cxx_appender;
00352 
00353 void register_appender(LogAppender* appender)
00354 {
00355   g_log4cxx_appender = new Log4cxxAppender(appender);
00356   const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00357   logger->addAppender(g_log4cxx_appender);
00358 }
00359 
00360 void shutdown()
00361 {
00362   const log4cxx::LoggerPtr& logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00363   logger->removeAppender(g_log4cxx_appender);
00364   g_log4cxx_appender = 0;
00365   // reset this so that the logger doesn't get crashily destroyed
00366   // again during global destruction.  
00367   //
00368   // See https://code.ros.org/trac/ros/ticket/3271
00369   //
00370   log4cxx::Logger::getRootLogger()->getLoggerRepository()->shutdown();
00371 }
00372 
00373 } // namespace impl
00374 } // namespace console
00375 } // namespace ros


rosconsole
Author(s): Josh Faust
autogenerated on Tue Mar 7 2017 04:00:48