diff --git a/README.md b/README.md index 754b661..85a92d0 100644 --- a/README.md +++ b/README.md @@ -104,8 +104,15 @@ colcon build --symlink-install --cmake-clean-cache --packages-select rcl_logging [SYSLOG(3)](https://man7.org/linux/man-pages/man3/syslog.3.html) is really simple that does not have much interfaces to control on application side, it just writes the log data on `rsyslog` Unix Domain Socket. So we need to configure `rsyslog` how it manages the log message with `/etc/rsyslog.conf`, for example file system sink and forward the message to `fluent-bit`. -At this moment, `rcl_logging_syslog` uses syslog facility only `local1`. -More facilities should be supported in the future via environmental variables at `rcl_logging_syslog` initialization, so that user application can use appropriate facility configured by `rsyslog`. +| environmental variable | default | Note | +| :----------------------| :------ | :--- | +| `RCL_LOGGING_SYSLOG_FACILITY` | `LOG_LOCAL1` | [syslog facility](https://man7.org/linux/man-pages/man3/syslog.3.html): either of `LOG_CRON`, `LOG_DAEMON`, `LOG_SYSLOG`, `LOG_USER`, and `LOG_LOCAL0-7`. (`/etc/rsyslog.conf` must be configured accordingly.) | + +Examples: + +```bash +export RCL_LOGGING_SYSLOG_FACILITY="LOG_LOCAL4" +``` See more details for https://www.rsyslog.com/doc/index.html. diff --git a/src/rcl_logging_syslog.cpp b/src/rcl_logging_syslog.cpp index 3986a60..01c65b4 100644 --- a/src/rcl_logging_syslog.cpp +++ b/src/rcl_logging_syslog.cpp @@ -18,6 +18,7 @@ #include #include +#include #include @@ -32,12 +33,92 @@ #include #include +static const char * facility_env_name = "RCL_LOGGING_SYSLOG_FACILITY"; + // see https://codebrowser.dev/glibc/glibc/misc/syslog.c.html#LogTag // This memory needs to be kept until closelog() static std::shared_ptr syslog_identity; static const char *logger_name = "rcl_logging_syslog"; +typedef struct facility_index { + const char *c_name; + const int c_value; +} FACILITY_INDEX; + +const FACILITY_INDEX facility_table[] = + { + { "LOG_CRON", LOG_CRON }, + { "LOG_DAEMON", LOG_DAEMON }, + { "LOG_SYSLOG", LOG_SYSLOG }, + { "LOG_USER", LOG_USER }, + { "LOG_LOCAL0", LOG_LOCAL0 }, + { "LOG_LOCAL1", LOG_LOCAL1 }, + { "LOG_LOCAL2", LOG_LOCAL2 }, + { "LOG_LOCAL3", LOG_LOCAL3 }, + { "LOG_LOCAL4", LOG_LOCAL4 }, + { "LOG_LOCAL5", LOG_LOCAL5 }, + { "LOG_LOCAL6", LOG_LOCAL6 }, + { "LOG_LOCAL7", LOG_LOCAL7 }, + { NULL, -1 } + }; + +static const char *get_facility_name(int facility) { + for (auto f = facility_table; f->c_name != NULL; f++) { + if (f->c_value == facility) { + return f->c_name; + } + } + return "Unknown facility"; +} + +static int get_syslog_facility(void) +{ + // default is LOG_LOCAL1 + int syslog_facility = LOG_LOCAL1; + + try { + std::string facility_string = rcpputils::get_env_var(facility_env_name); + if (facility_string.empty()) { + return syslog_facility; + } + + if (!facility_string.compare("LOG_USER")) { + syslog_facility = LOG_USER; + } else if (!facility_string.compare("LOG_DAEMON")) { + syslog_facility = LOG_DAEMON; + } else if (!facility_string.compare("LOG_SYSLOG")) { + syslog_facility = LOG_SYSLOG; + } else if (!facility_string.compare("LOG_CRON")) { + syslog_facility = LOG_CRON; + } else if (!facility_string.compare("LOG_LOCAL0")) { + syslog_facility = LOG_LOCAL0; + } else if (!facility_string.compare("LOG_LOCAL1")) { + syslog_facility = LOG_LOCAL1; + } else if (!facility_string.compare("LOG_LOCAL2")) { + syslog_facility = LOG_LOCAL2; + } else if (!facility_string.compare("LOG_LOCAL3")) { + syslog_facility = LOG_LOCAL3; + } else if (!facility_string.compare("LOG_LOCAL4")) { + syslog_facility = LOG_LOCAL4; + } else if (!facility_string.compare("LOG_LOCAL5")) { + syslog_facility = LOG_LOCAL5; + } else if (!facility_string.compare("LOG_LOCAL6")) { + syslog_facility = LOG_LOCAL6; + } else if (!facility_string.compare("LOG_LOCAL7")) { + syslog_facility = LOG_LOCAL7; + } else { + syslog_facility = LOG_LOCAL1; + } + } catch (const std::runtime_error & error) { + throw std::runtime_error( + std::string("failed to get env var '") + facility_env_name + "': " + error.what() + ); + } + + return syslog_facility; +} + static int rcutil_to_syslog_level(int rcutil_level) { int syslog_level; @@ -126,10 +207,14 @@ rcl_logging_ret_t rcl_logging_external_initialize( }); syslog_identity = std::make_shared(basec); + // Check and fetch the syslog facility from environmental variable + int syslog_facility = get_syslog_facility(); + RCUTILS_LOG_DEBUG_NAMED( + logger_name, + "syslog facility is set to %s", get_facility_name(syslog_facility)); + // Use user specified filename, or executable name to openlog(3) identity. - // TODO(@fujitatomoya): facility should be set by environmental variable, - // so that user can control the log facility as it set on rsyslog.conf. - openlog(syslog_identity->c_str(), LOG_PID, LOG_LOCAL1); + openlog(syslog_identity->c_str(), LOG_PID, syslog_facility); return RCL_LOGGING_RET_OK; }