00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "ros/console.h"
00033 #include "ros/assert.h"
00034 #include <ros/time.h>
00035 #include "log4cxx/appenderskeleton.h"
00036 #include "log4cxx/spi/loggingevent.h"
00037 #include "log4cxx/level.h"
00038 #include "log4cxx/propertyconfigurator.h"
00039
00040 #include <boost/thread.hpp>
00041 #include <boost/shared_array.hpp>
00042 #include <boost/regex.hpp>
00043
00044 #include <cstdarg>
00045 #include <cstdlib>
00046 #include <cstdio>
00047 #include <memory>
00048 #include <cstring>
00049 #include <stdexcept>
00050
00051 namespace ros
00052 {
00053 namespace console
00054 {
00055
00056 bool g_initialized = false;
00057 bool g_shutting_down = false;
00058 boost::mutex g_init_mutex;
00059
00060 log4cxx::LevelPtr g_level_lookup[ levels::Count ] =
00061 {
00062 log4cxx::Level::getDebug(),
00063 log4cxx::Level::getInfo(),
00064 log4cxx::Level::getWarn(),
00065 log4cxx::Level::getError(),
00066 log4cxx::Level::getFatal(),
00067 };
00068
00069 #define COLOR_NORMAL "\033[0m"
00070 #define COLOR_RED "\033[31m"
00071 #define COLOR_GREEN "\033[32m"
00072 #define COLOR_YELLOW "\033[33m"
00073
00074 const char* g_format_string = "[${severity}] [${time}]: ${message}";
00075
00076 struct Token
00077 {
00078 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event) = 0;
00079 };
00080 typedef boost::shared_ptr<Token> TokenPtr;
00081 typedef std::vector<TokenPtr> V_Token;
00082
00083 typedef std::map<std::string, std::string> M_string;
00084 M_string g_extra_fixed_tokens;
00085
00086 void setFixedFilterToken(const std::string& key, const std::string& val)
00087 {
00088 g_extra_fixed_tokens[key] = val;
00089 }
00090
00091 struct FixedToken : public Token
00092 {
00093 FixedToken(const std::string& str)
00094 : str_(str)
00095 {}
00096
00097 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00098 {
00099 return str_.c_str();
00100 }
00101
00102 std::string str_;
00103 };
00104
00105 struct FixedMapToken : public Token
00106 {
00107 FixedMapToken(const std::string& str)
00108 : str_(str)
00109 {}
00110
00111 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00112 {
00113 M_string::iterator it = g_extra_fixed_tokens.find(str_);
00114 if (it == g_extra_fixed_tokens.end())
00115 {
00116 return ("${" + str_ + "}").c_str();
00117 }
00118
00119 return it->second.c_str();
00120 }
00121
00122 std::string str_;
00123 };
00124
00125 struct PlaceHolderToken : public Token
00126 {
00127 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00128 {
00129 return "PLACEHOLDER";
00130 }
00131 };
00132
00133 struct SeverityToken : public Token
00134 {
00135 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00136 {
00137 if (event->getLevel() == log4cxx::Level::getFatal())
00138 {
00139 return "FATAL";
00140 }
00141 else if (event->getLevel() == log4cxx::Level::getError())
00142 {
00143 return "ERROR";
00144 }
00145 else if (event->getLevel() == log4cxx::Level::getWarn())
00146 {
00147 return " WARN";
00148 }
00149 else if (event->getLevel() == log4cxx::Level::getInfo())
00150 {
00151 return " INFO";
00152 }
00153 else if (event->getLevel() == log4cxx::Level::getDebug())
00154 {
00155 return "DEBUG";
00156 }
00157
00158 return "UNKNO";
00159 }
00160 };
00161
00162 struct MessageToken : public Token
00163 {
00164 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00165 {
00166 return event->getMessage().c_str();
00167 }
00168 };
00169
00170 struct TimeToken : public Token
00171 {
00172 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00173 {
00174 std::stringstream ss;
00175 if (ros::Time::isValid() && ros::Time::isSimTime())
00176 {
00177 ss << ros::WallTime::now() << ", " << ros::Time::now();
00178 }
00179 else
00180 {
00181 ss << ros::WallTime::now();
00182 }
00183 return ss.str();
00184 }
00185 };
00186
00187 struct ThreadToken : public Token
00188 {
00189 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00190 {
00191 std::stringstream ss;
00192 ss << boost::this_thread::get_id();
00193 return ss.str();
00194 }
00195 };
00196
00197 struct LoggerToken : public Token
00198 {
00199 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00200 {
00201 return event->getLoggerName();
00202 }
00203 };
00204
00205 struct FileToken : public Token
00206 {
00207 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00208 {
00209 return event->getLocationInformation().getFileName();
00210 }
00211 };
00212
00213 struct FunctionToken : public Token
00214 {
00215 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00216 {
00217 return event->getLocationInformation().getMethodName();
00218 }
00219 };
00220
00221 struct LineToken : public Token
00222 {
00223 virtual std::string getString(const log4cxx::spi::LoggingEventPtr& event)
00224 {
00225 std::stringstream ss;
00226 ss << event->getLocationInformation().getLineNumber();
00227 return ss.str();
00228 }
00229 };
00230
00231 TokenPtr createTokenFromType(const std::string& type)
00232 {
00233 if (type == "severity")
00234 {
00235 return TokenPtr(new SeverityToken());
00236 }
00237 else if (type == "message")
00238 {
00239 return TokenPtr(new MessageToken());
00240 }
00241 else if (type == "time")
00242 {
00243 return TokenPtr(new TimeToken());
00244 }
00245 else if (type == "thread")
00246 {
00247 return TokenPtr(new ThreadToken());
00248 }
00249 else if (type == "logger")
00250 {
00251 return TokenPtr(new LoggerToken());
00252 }
00253 else if (type == "file")
00254 {
00255 return TokenPtr(new FileToken());
00256 }
00257 else if (type == "line")
00258 {
00259 return TokenPtr(new LineToken());
00260 }
00261 else if (type == "function")
00262 {
00263 return TokenPtr(new FunctionToken());
00264 }
00265
00266 return TokenPtr(new FixedMapToken(type));
00267 }
00268
00269 struct Formatter
00270 {
00271 void init(const char* fmt)
00272 {
00273 format_ = fmt;
00274
00275 boost::regex e("\\$\\{([a-z|A-Z]+)\\}");
00276 boost::match_results<std::string::const_iterator> results;
00277 std::string::const_iterator start, end;
00278 start = format_.begin();
00279 end = format_.end();
00280 bool matched_once = false;
00281 std::string last_suffix;
00282 while (boost::regex_search(start, end, results, e))
00283 {
00284 #if 0
00285 for (size_t i = 0; i < results.size(); ++i)
00286 {
00287 std::cout << i << "|" << results.prefix() << "|" << results[i] << "|" << results.suffix() << std::endl;
00288 }
00289 #endif
00290
00291 std::string token = results[1];
00292 last_suffix = results.suffix();
00293 tokens_.push_back(TokenPtr(new FixedToken(results.prefix())));
00294 tokens_.push_back(createTokenFromType(token));
00295
00296 start = results[0].second;
00297 matched_once = true;
00298 }
00299
00300 if (matched_once)
00301 {
00302 tokens_.push_back(TokenPtr(new FixedToken(last_suffix)));
00303 }
00304 else
00305 {
00306 tokens_.push_back(TokenPtr(new FixedToken(format_)));
00307 }
00308 }
00309
00310 void print(const log4cxx::spi::LoggingEventPtr& event)
00311 {
00312 const char* color = NULL;
00313 FILE* f = stdout;
00314
00315 if (event->getLevel() == log4cxx::Level::getFatal())
00316 {
00317 color = COLOR_RED;
00318 f = stderr;
00319 }
00320 else if (event->getLevel() == log4cxx::Level::getError())
00321 {
00322 color = COLOR_RED;
00323 f = stderr;
00324 }
00325 else if (event->getLevel() == log4cxx::Level::getWarn())
00326 {
00327 color = COLOR_YELLOW;
00328 }
00329 else if (event->getLevel() == log4cxx::Level::getInfo())
00330 {
00331 color = COLOR_NORMAL;
00332 }
00333 else if (event->getLevel() == log4cxx::Level::getDebug())
00334 {
00335 color = COLOR_GREEN;
00336 }
00337
00338 ROS_ASSERT(color != NULL);
00339
00340 std::stringstream ss;
00341 ss << color;
00342 V_Token::iterator it = tokens_.begin();
00343 V_Token::iterator end = tokens_.end();
00344 for (; it != end; ++it)
00345 {
00346 ss << (*it)->getString(event);
00347 }
00348 ss << COLOR_NORMAL;
00349
00350 fprintf(f, "%s\n", ss.str().c_str());
00351 }
00352
00353 std::string format_;
00354 V_Token tokens_;
00355
00356 };
00357 Formatter g_formatter;
00358
00359 class ROSConsoleStdioAppender : public log4cxx::AppenderSkeleton
00360 {
00361 public:
00362 ~ROSConsoleStdioAppender()
00363 {
00364 }
00365
00366 protected:
00367 virtual void append(const log4cxx::spi::LoggingEventPtr& event, log4cxx::helpers::Pool& pool)
00368 {
00369 g_formatter.print(event);
00370 }
00371
00372 virtual void close()
00373 {
00374 }
00375 virtual bool requiresLayout() const
00376 {
00377 return false;
00378 }
00379 };
00380
00384 void do_initialize()
00385 {
00386
00387 const char* ros_root_cstr = getenv("ROS_ROOT");
00388 if (!ros_root_cstr)
00389 {
00390 log4cxx::LoggerPtr ros_logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00391 ros_logger->setLevel(log4cxx::Level::getInfo());
00392
00393 log4cxx::LoggerPtr roscpp_superdebug = log4cxx::Logger::getLogger("ros.roscpp.superdebug");
00394 roscpp_superdebug->setLevel(log4cxx::Level::getWarn());
00395 }
00396 else
00397 {
00398 std::string config_file = std::string(ros_root_cstr) + "/config/rosconsole.config";
00399 log4cxx::PropertyConfigurator::configure(config_file);
00400 }
00401
00402 const char* config_file_cstr = getenv("ROSCONSOLE_CONFIG_FILE");
00403 if ( config_file_cstr )
00404 {
00405 std::string config_file = config_file_cstr;
00406
00407 log4cxx::PropertyConfigurator::configure(config_file);
00408 }
00409
00410
00411 const char* format_string = getenv("ROSCONSOLE_FORMAT");
00412 if (format_string)
00413 {
00414 g_format_string = format_string;
00415 }
00416
00417 g_formatter.init(g_format_string);
00418
00419 log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger(ROSCONSOLE_ROOT_LOGGER_NAME);
00420 logger->addAppender(new ROSConsoleStdioAppender);
00421 }
00422
00423 void initialize()
00424 {
00425 boost::mutex::scoped_lock lock(g_init_mutex);
00426
00427 if (!g_initialized)
00428 {
00429 do_initialize();
00430 g_initialized = true;
00431 }
00432 }
00433
00434 void shutdown()
00435 {
00436 g_shutting_down = true;
00437 }
00438
00439 void vformatToBuffer(boost::shared_array<char>& buffer, size_t& buffer_size, const char* fmt, va_list args)
00440 {
00441 va_list arg_copy;
00442 va_copy(arg_copy, args);
00443 size_t total = vsnprintf(buffer.get(), buffer_size, fmt, args);
00444 if (total >= buffer_size)
00445 {
00446 buffer_size = total + 1;
00447 buffer.reset(new char[buffer_size]);
00448
00449 vsnprintf(buffer.get(), buffer_size, fmt, arg_copy);
00450 va_end(arg_copy);
00451 }
00452 }
00453
00454 void formatToBuffer(boost::shared_array<char>& buffer, size_t& buffer_size, const char* fmt, ...)
00455 {
00456 va_list args;
00457 va_start(args, fmt);
00458
00459 vformatToBuffer(buffer, buffer_size, fmt, args);
00460
00461 va_end(args);
00462 }
00463
00464 std::string formatToString(const char* fmt, ...)
00465 {
00466 boost::shared_array<char> buffer;
00467 size_t size = 0;
00468
00469 va_list args;
00470 va_start(args, fmt);
00471
00472 vformatToBuffer(buffer, size, fmt, args);
00473
00474 va_end(args);
00475
00476 return std::string(buffer.get(), size);
00477 }
00478
00479 #define INITIAL_BUFFER_SIZE 4096
00480 static boost::mutex g_print_mutex;
00481 static boost::shared_array<char> g_print_buffer(new char[INITIAL_BUFFER_SIZE]);
00482 static size_t g_print_buffer_size = INITIAL_BUFFER_SIZE;
00483 static boost::thread::id g_printing_thread_id;
00484
00485 void print(FilterBase* filter, log4cxx::Logger* logger, Level level,
00486 const char* file, int line, const char* function, const char* fmt, ...)
00487 {
00488 if (g_shutting_down)
00489 return;
00490
00491 if (g_printing_thread_id == boost::this_thread::get_id())
00492 {
00493 fprintf(stderr, "Warning: recursive print statement has occurred. Throwing out recursive print.\n");
00494 return;
00495 }
00496
00497 boost::mutex::scoped_lock lock(g_print_mutex);
00498
00499 g_printing_thread_id = boost::this_thread::get_id();
00500
00501 va_list args;
00502 va_start(args, fmt);
00503
00504 vformatToBuffer(g_print_buffer, g_print_buffer_size, fmt, args);
00505
00506 va_end(args);
00507
00508 log4cxx::LoggerPtr logger_ptr(logger);
00509 bool enabled = true;
00510
00511 if (filter)
00512 {
00513 FilterParams params;
00514 params.file = file;
00515 params.function = function;
00516 params.line = line;
00517 params.level = level;
00518 params.logger = logger_ptr;
00519 params.message = g_print_buffer.get();
00520 enabled = filter->isEnabled(params);
00521 logger_ptr = params.logger;
00522 level = params.level;
00523
00524 if (!params.out_message.empty())
00525 {
00526 size_t msg_size = params.out_message.size();
00527 if (g_print_buffer_size <= msg_size)
00528 {
00529 g_print_buffer_size = msg_size + 1;
00530 g_print_buffer.reset(new char[g_print_buffer_size]);
00531 }
00532
00533 memcpy(g_print_buffer.get(), params.out_message.c_str(), msg_size + 1);
00534 }
00535 }
00536
00537 if (enabled)
00538 {
00539 try
00540 {
00541 logger_ptr->forcedLog(g_level_lookup[level], g_print_buffer.get(), log4cxx::spi::LocationInfo(file, function, line));
00542 }
00543 catch (std::exception& e)
00544 {
00545 fprintf(stderr, "Caught exception while logging: [%s]\n", e.what());
00546 }
00547 }
00548
00549 g_printing_thread_id = boost::thread::id();
00550 }
00551
00552 void print(FilterBase* filter, log4cxx::Logger* logger, Level level,
00553 const std::stringstream& ss, const char* file, int line, const char* function)
00554 {
00555 if (g_shutting_down)
00556 return;
00557
00558 if (g_printing_thread_id == boost::this_thread::get_id())
00559 {
00560 fprintf(stderr, "Warning: recursive print statement has occurred. Throwing out recursive print.\n");
00561 return;
00562 }
00563
00564 boost::mutex::scoped_lock lock(g_print_mutex);
00565
00566 g_printing_thread_id = boost::this_thread::get_id();
00567
00568 log4cxx::LoggerPtr logger_ptr(logger);
00569 bool enabled = true;
00570 std::string str = ss.str();
00571
00572 if (filter)
00573 {
00574 FilterParams params;
00575 params.file = file;
00576 params.function = function;
00577 params.line = line;
00578 params.level = level;
00579 params.logger = logger_ptr;
00580 params.message = g_print_buffer.get();
00581 enabled = filter->isEnabled(params);
00582 logger_ptr = params.logger;
00583 level = params.level;
00584
00585 if (!params.out_message.empty())
00586 {
00587 str = params.out_message;
00588 }
00589 }
00590
00591 if (enabled)
00592 {
00593 if (g_shutting_down)
00594 {
00595 fprintf(stderr, "%s\n", g_print_buffer.get());
00596 return;
00597 }
00598
00599 try
00600 {
00601 logger->forcedLog(g_level_lookup[level], str, log4cxx::spi::LocationInfo(file, function, line));
00602 }
00603 catch (std::exception& e)
00604 {
00605 fprintf(stderr, "Caught exception while logging: [%s]\n", e.what());
00606 }
00607 }
00608
00609 g_printing_thread_id = boost::thread::id();
00610 }
00611
00612 typedef std::vector<LogLocation*> V_LogLocation;
00613 V_LogLocation g_log_locations;
00614 boost::mutex g_locations_mutex;
00615 void registerLogLocation(LogLocation* loc)
00616 {
00617 boost::mutex::scoped_lock lock(g_locations_mutex);
00618
00619 g_log_locations.push_back(loc);
00620 }
00621
00622 void checkLogLocationEnabledNoLock(LogLocation* loc)
00623 {
00624 loc->logger_enabled_ = loc->logger_->isEnabledFor(g_level_lookup[loc->level_]);
00625 }
00626
00627 void initializeLogLocation(LogLocation* loc, const std::string& name, Level level)
00628 {
00629 boost::mutex::scoped_lock lock(g_locations_mutex);
00630
00631 if (loc->initialized_)
00632 {
00633 return;
00634 }
00635
00636 loc->logger_ = &(*log4cxx::Logger::getLogger(name));
00637 loc->level_ = level;
00638
00639 g_log_locations.push_back(loc);
00640
00641 checkLogLocationEnabledNoLock(loc);
00642
00643 loc->initialized_ = true;
00644 }
00645
00646 void setLogLocationLevel(LogLocation* loc, Level level)
00647 {
00648 boost::mutex::scoped_lock lock(g_locations_mutex);
00649 loc->level_ = level;
00650 }
00651
00652 void checkLogLocationEnabled(LogLocation* loc)
00653 {
00654 boost::mutex::scoped_lock lock(g_locations_mutex);
00655 checkLogLocationEnabledNoLock(loc);
00656 }
00657
00658 void notifyLoggerLevelsChanged()
00659 {
00660 boost::mutex::scoped_lock lock(g_locations_mutex);
00661
00662 V_LogLocation::iterator it = g_log_locations.begin();
00663 V_LogLocation::iterator end = g_log_locations.end();
00664 for ( ; it != end; ++it )
00665 {
00666 LogLocation* loc = *it;
00667 checkLogLocationEnabledNoLock(loc);
00668 }
00669 }
00670
00671 class StaticInit
00672 {
00673 public:
00674 StaticInit()
00675 {
00676 ROSCONSOLE_AUTOINIT;
00677 }
00678 };
00679 StaticInit g_static_init;
00680
00681 }
00682 }