rosconsole
rosconsole copied to clipboard
Logging to journald?
We are currently looking into rosconsole to find a way to directly write to the system journal using sd_journal_send. One could of course simply use the builtin feature of log4cxx to log to syslog and use the compatibility layer of systemd for syslog, but this comes with one big limitation: Syslog uses the program_invocation_short_name to identify the sender, which is not the perfect way to identify a program in a ROS context, especially when one has two instances of the same binary with different node names.
The big benefit of systemd's journal is that one can set the SYSLOG_IDENTIFIER. The obvious idea is to set this value to the node name which makes it really convenient to read the journal and I have code ready that does exactly this. Sadly this can't be done given the current dependency structure since the node name is only available in ros/this_node.h in roscpp which is a downstream package.
...
void SystemJournalAppender::append(const log4cxx::spi::LoggingEventPtr& event,
log4cxx::helpers::Pool&)
{
const log4cxx::spi::LocationInfo& location_info = event->getLocationInformation();
::sd_journal_send(
"MESSAGE=%s", event->getMessage().c_str(),
"PRIORITY=%i", event->getLevel()->getSyslogEquivalent(),
"CODE_FILE=%s", location_info.getFileName(),
"CODE_LINE=%i", location_info.getLineNumber(),
"CODE_FUNC=%s", location_info.getMethodName().c_str(),
"SYSLOG_IDENTIFIER=%s", ros::this_node::getName().c_str(),
NULL);
}
I'm opening this ticket to ask for advice on how to continue here. I've two suggestions:
- Add the node name as an argument to ROSCONSOLE_AUTOINIT and ros::console::initialize. This internally sets a global variable which can be used in the log appender.
- Use pluginlib to load additional plugins during runtime, just before the log4cxx configuration is parsed log4cxx::PropertyConfigurator::configure(config_file). This requires some additional work to make pluginlib use console_bridge instead of rosconsole (https://github.com/ros/pluginlib/issues/81). The advantage would be that it allows others to implement their own custom log appenders.
I personally prefer solution 2 but this would of course be a significant change.
I started implementing what is needed for solution 2:
- https://github.com/magazino/rosconsole/tree/log4cxx-appender registers the ROSConsoleStdioAppender class as a log4cxx appender which can be configured using the log4cxx config file. This would allow me to disable the print to stdout/stderr.
- https://github.com/magazino/pluginlib/tree/kinetic-console-bridge (not yet ported to melodic) replaces rosconsole with console bridge. This is required to use pluginlib in rosconsole.
- https://github.com/magazino/rosconsole/tree/custom-appenders adds pluginlib as a dependency and loads all available ronsconsole log4cxx plugins right before the config file is parsed. This sets the basis to write custom log appenders.
- https://github.com/magazino/systemd_ros adds a custom log4cxx appender which writes to the journal.
I don't know where to call REGISTER_ROSCONSOLE_BRIDGE and add rosconsole_bridge to forward the console bridge output of pluginlib. I was wondering why rosconsole_bridge is a separate package at all and not part of rosconsole.
Would be great to get some feedback on my ideas and how to move forward.
Any updates here? This would be very useful!
This would be very nice!
As a backup solution, wouldn't it be possible to create a node that subscribes to /rosout
and copy the logs to Journald?