12 #include <boost/filesystem.hpp> 13 #include <boost/lexical_cast.hpp> 21 #include <fmt/format.h> 22 #include <fmt/chrono.h> 33 namespace fs = boost::filesystem;
38 static fs::path
findFile(
const fs::path& base,
const std::string& name)
40 for(fs::directory_iterator it(base); it != fs::directory_iterator(); ++it)
42 if(fs::is_directory(*it))
48 else if(it->path().leaf() == name)
59 " rosmon [actions] [options] some_package test.launch [arg1:=value1 ...]\n" 60 " rosmon [actions] [options] path/to/test.launch [arg1:=value1 ...]\n" 62 "Actions (default is to launch the launch file):\n" 63 " --benchmark Exit after loading the launch file\n" 64 " --list-args List launch file arguments\n" 67 " --disable-ui Disable fancy terminal UI\n" 68 " --flush-log Flush logfile after writing an entry\n" 69 " --flush-stdout Flush stdout after writing an entry\n" 70 " --help This help screen\n" 71 " --log=FILE Write log file to FILE (use 'syslog' for syslog)\n" 72 " --name=NAME Use NAME as ROS node name. By default, an anonymous\n" 74 " --no-start Don't automatically start the nodes in the beginning\n" 75 " --stop-timeout=SECONDS\n" 76 " Kill a process if it is still running this long\n" 77 " after the initial signal is send.\n" 78 " --disable-diagnostics\n" 79 " Disable publication of ros diagnostics message about\n" 81 " --diagnostics-prefix=PREFIX\n" 82 " Prefix for the ros diagnostics generated by this node.\n" 83 " By default this will be the node name.\n" 84 " --cpu-limit=[0-n]\n" 85 " Default CPU Usage limit of monitored process. n is the\n" 86 " number of CPU cores. This is the sum of system and user\n" 88 " --memory-limit=15MB\n" 89 " Default memory limit usage of monitored process.\n" 90 " --output-attr=obey|ignore\n" 91 " Obey or ignore output=\"*\" attributes on node tags.\n" 92 " Default is to ignore.\n" 93 " --auto-increment-spawn-delay=SECONDS\n" 94 " Add SECONDS spawn delay to each process.\n" 95 " This is ignored for processes having 'rosmon-spawn-delay' set.\n" 96 " By default this is disabled.\n" 98 "rosmon also obeys some environment variables:\n" 99 " ROSMON_COLOR_MODE Can be set to 'truecolor', '256colors', 'ansi'\n" 100 " to force a specific color mode\n" 101 " If unset, rosmon tries to detect the best\n" 102 " available color mode.\n" 117 fmt::print(
"{:>{}}: {}", event.
source, max_width, event.
message);
119 if(!event.
message.empty() &&
event.message.back() !=
'\n')
128 {
"disable-ui", no_argument,
nullptr,
'd'},
129 {
"benchmark", no_argument,
nullptr,
'b'},
130 {
"flush-log", no_argument,
nullptr,
'f'},
131 {
"flush-stdout", no_argument,
nullptr,
'F'},
132 {
"help", no_argument,
nullptr,
'h'},
133 {
"list-args", no_argument,
nullptr,
'L'},
134 {
"log", required_argument,
nullptr,
'l'},
135 {
"name", required_argument,
nullptr,
'n'},
136 {
"no-start", no_argument,
nullptr,
'S'},
137 {
"stop-timeout", required_argument,
nullptr,
's'},
138 {
"disable-diagnostics", no_argument,
nullptr,
'D'},
139 {
"cpu-limit", required_argument,
nullptr,
'c'},
140 {
"memory-limit", required_argument,
nullptr,
'm'},
141 {
"diagnostics-prefix", required_argument,
nullptr,
'p'},
142 {
"output-attr", required_argument,
nullptr,
'o'},
143 {
"auto-increment-spawn-delay", required_argument,
nullptr,
'a'},
144 {
nullptr, 0,
nullptr, 0}
155 std::size_t max_width = 0;
157 for(
const auto& node : monitor.
nodes())
159 max_width = std::max(max_width, node->fullName().size());
165 int main(
int argc,
char** argv)
169 std::string launchFilePath;
172 bool enableUI =
true;
173 bool flushLog =
false;
174 bool startNodes =
true;
178 bool disableDiagnostics =
false;
179 std::string diagnosticsPrefix;
180 double autoIncrementSpawnDelay = -1.;
187 int c = getopt_long(argc, argv,
"h",
OPTIONS, &option_index);
224 stopTimeout = boost::lexical_cast<
double>(optarg);
226 catch(boost::bad_lexical_cast&)
228 fmt::print(stderr,
"Bad value for --stop-timeout argument: '{}'\n", optarg);
234 fmt::print(stderr,
"Stop timeout cannot be negative\n");
239 disableDiagnostics =
true;
244 cpuLimit = boost::lexical_cast<
float>(optarg);
246 catch(boost::bad_lexical_cast&)
248 fmt::print(stderr,
"Bad value for --cpu-limit argument: '{}'\n", optarg);
254 fmt::print(stderr,
"CPU Limit cannot be negative\n");
264 fmt::print(stderr,
"Bad value for --memory-limit argument: '{}'\n", optarg);
270 if(optarg == std::string(
"obey"))
272 else if(optarg == std::string(
"ignore"))
276 fmt::print(stderr,
"Bad value for --output-attr argument: '{}'\n", optarg);
281 fmt::print(stderr,
"Prefix : {}", optarg);
282 diagnosticsPrefix = std::string(optarg);
285 fmt::print(stderr,
"Auto increment spawn delay of : {} seconds\n", optarg);
288 autoIncrementSpawnDelay = boost::lexical_cast<
double>(optarg);
290 catch(boost::bad_lexical_cast&)
292 fmt::print(stderr,
"Bad value for --auto-increment-spawn-delay: '{}'\n", optarg);
296 if(autoIncrementSpawnDelay < 0)
298 fmt::print(stderr,
"Auto increment spawn delay cannot be negative\n");
313 int firstArg = optind + 1;
314 for(; firstArg < argc; ++firstArg)
316 if(strstr(argv[firstArg],
":="))
323 if(firstArg - optind == 1)
325 launchFilePath = argv[optind];
327 else if(firstArg - optind == 2)
329 const char* packageName = argv[optind];
330 const char* fileName = argv[optind + 1];
332 std::string
package = rosmon::PackageRegistry::getPath(packageName);
335 fmt::print(stderr,
"Could not find path of package '{}'\n", packageName);
342 fmt::print(stderr,
"Could not find launch file '{}' in package '{}'\n",
343 fileName, packageName
348 launchFilePath = path.string();
357 boost::scoped_ptr<rosmon::Logger> logger;
358 std::string nodeLogPath;
361 setenv(
"ROSCONSOLE_FORMAT",
"[${function}]: ${message}", 0);
367 bool envSyslog =
false;
368 if(
auto env = getenv(
"ROSMON_SYSLOG"))
369 envSyslog = (strcmp(
env,
"1") == 0);
371 if(envSyslog || logFile ==
"syslog")
380 fmt::print(stderr,
"Systemd Journal not available: {}\n", e.what());
388 fmt::print(
"Tip: use --log=syslog or set environment variable ROSMON_SYSLOG=1 to send log output to syslog instead of a log file in /tmp.\n");
391 std::time_t t = std::time(
nullptr);
392 std::tm currentTime = fmt::localtime(t);
394 logFile = fmt::format(
"/tmp/rosmon_{:%Y_%m_%d_%H_%M_%S}.log", currentTime);
400 if(
auto val = getenv(
"ROS_LOG_DIR"))
402 else if(
auto val = getenv(
"ROS_HOME"))
403 nodeLogPath = fmt::format(
"{}/log", val);
404 else if(
auto val = getenv(
"HOME"))
405 nodeLogPath = fmt::format(
"{}/.ros/log", val);
412 config->setDefaultStopTimeout(stopTimeout);
413 config->setDefaultCPULimit(cpuLimit);
414 config->setDefaultMemoryLimit(memoryLimit);
415 config->setOutputAttrMode(outputAttrMode);
418 for(
int i = firstArg; i < argc; ++i)
420 char*
arg = strstr(argv[i],
":=");
424 fmt::print(stderr,
"You specified a non-argument after an argument\n");
428 char* name = argv[i];
432 char* value = arg + 2;
434 config->setArgument(name, value);
441 config->parse(launchFilePath, onlyArguments);
442 config->evaluateParameters();
446 fmt::print(stderr,
"Could not load launch file: {}\n", e.
what());
455 for(
const auto&
arg : config->arguments())
456 std::cout <<
arg.first << std::endl;
463 if(autoIncrementSpawnDelay > 0.)
464 config->applyAutoIncrementSpawnDelayToAll(
ros::WallDuration{autoIncrementSpawnDelay});
466 if(config->disableUI())
476 name = config->rosmonNodeName();
487 ros::init(dummyArgc, argv, name, init_options);
495 fmt::print(
"roscore is already running.\n");
499 fmt::print(
"roscore is not runnning.\n");
500 fmt::print(
"Waiting until it is up (abort with CTRL+C)...\n");
505 fmt::print(
"roscore is running now.\n");
514 if(!nodeLogPath.empty())
518 nodeLogPath = fmt::format(
"{}/{}", nodeLogPath, runID);
520 if(!fs::create_directories(nodeLogPath))
521 fmt::print(stderr,
"Warning: Could not create log directory '{}'\n", nodeLogPath);
523 config->setNodeLogDir(nodeLogPath);
531 if(config->nodes().empty())
533 fmt::print(
"No ROS nodes to be launched. Finished...\n");
542 boost::scoped_ptr<rosmon::UI> ui;
570 watcher->wait(waitDuration);
577 ui->log({
"[rosmon]",
"Shutting down..."});
584 watcher->wait(waitDuration);
599 watcher->wait(waitDuration);
606 bool coredumpsAvailable = std::any_of(monitor.
nodes().begin(), monitor.
nodes().end(),
610 if(ui && coredumpsAvailable)
612 ui->log({
"[rosmon]",
"\n"});
613 ui->log({
"[rosmon]",
"If you want to debug one of the crashed nodes, you can use the following commands"});
614 for(
auto& node : monitor.
nodes())
616 if(node->coredumpAvailable())
620 fmt::format(
"{:20}: # {}", node->name(), node->debuggerCommand())
virtual const char * what() const noexcept
ROSCONSOLE_BACKEND_DECL void(* function_print)(void *, ::ros::console::Level, const char *, const char *, const char *, int)
ROSCPP_DECL const std::string & getURI()
boost::signals2::signal< void(LogEvent)> logMessageSignal
Write log messages into a log file.
virtual void log(const LogEvent &event)=0
int main(int argc, char **argv)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
ROSCPP_DECL const std::string & getName()
std::shared_ptr< LaunchConfig > Ptr
static fs::path findFile(const fs::path &base, const std::string &name)
Write log messages to systemd journal.
constexpr double DEFAULT_STOP_TIMEOUT
bool getParam(const std::string &key, std::string &s) const
constexpr uint64_t DEFAULT_MEMORY_LIMIT
std::string env(const std::string &name)
Write log messages to syslog.
static const struct option OPTIONS[]
std::string arg(const std::string &name, const ParseContext &context)
constexpr double DEFAULT_CPU_LIMIT
static int get_max_node_name(const rosmon::monitor::Monitor &monitor)
std::tuple< uint64_t, bool > parseMemory(const std::string &memory)
ROSCPP_DECL void spinOnce()
void logToStdout(const rosmon::LogEvent &event, const int max_width)
std::shared_ptr< NodeMonitor > Ptr
const std::vector< NodeMonitor::Ptr > & nodes() const