diff --git a/lib/include/srsran/srslog/shared_types.h b/lib/include/srsran/srslog/shared_types.h index c508219ef..90aa8c6d0 100644 --- a/lib/include/srsran/srslog/shared_types.h +++ b/lib/include/srsran/srslog/shared_types.h @@ -31,6 +31,18 @@ enum class backend_priority very_high }; +/// syslog log local types +enum class syslog_local_type { + local0, + local1, + local2, + local3, + local4, + local5, + local6, + local7, +}; + } // namespace srslog #endif // SRSLOG_SHARED_TYPES_H diff --git a/lib/include/srsran/srslog/srslog.h b/lib/include/srsran/srslog/srslog.h index 632d38a91..b9af1f5bc 100644 --- a/lib/include/srsran/srslog/srslog.h +++ b/lib/include/srsran/srslog/srslog.h @@ -184,6 +184,14 @@ sink& fetch_file_sink(const std::string& path, size_t max_size = 0, std::unique_ptr f = get_default_log_formatter()); +/// Returns an instance of a sink that writes into syslog +/// preamble: The string prepended to every message, If ident is "", the program name is used. +/// log_local: custom unused facilities that syslog provides which can be used by the user +/// NOTE: Any '#' characters in the path will get removed. +sink& fetch_syslog_sink(const std::string& preamble_ = "", + syslog_local_type log_local_ = syslog_local_type::local0, + std::unique_ptr f = get_default_log_formatter()); + /// Installs a custom user defined sink in the framework getting associated to /// the specified id. Returns true on success, otherwise false. /// WARNING: This function is an advanced feature and users should really know diff --git a/lib/src/srslog/sinks/syslog_sink.h b/lib/src/srslog/sinks/syslog_sink.h new file mode 100644 index 000000000..2c0b8b02a --- /dev/null +++ b/lib/src/srslog/sinks/syslog_sink.h @@ -0,0 +1,97 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSLOG_SYSLOG_SINK_H +#define SRSLOG_SYSLOG_SINK_H + +#include "srsran/srslog/shared_types.h" +#include "srsran/srslog/sink.h" +#include + +namespace srslog { + +/// This sink implementation writes to syslog. +class syslog_sink : public sink +{ +public: + syslog_sink(std::unique_ptr f, + std::string preamble_ = "", + syslog_local_type log_local_ = syslog_local_type::local0) : + sink(std::move(f)) + { + create_syslog(preamble_, syslog_translate(log_local_)); + } + + syslog_sink(const syslog_sink& other) = delete; + syslog_sink& operator=(const syslog_sink& other) = delete; + + detail::error_string write(detail::memory_buffer buffer) override + { + std::string entry(buffer.data(), buffer.size()); + if (entry.find("[E]") != std::string::npos) { + syslog(LOG_ERR, "%s", buffer.data()); + } else if (entry.find("[W]") != std::string::npos) { + syslog(LOG_WARNING, "%s", buffer.data()); + } else if (entry.find("[I]") != std::string::npos) { + syslog(LOG_INFO, "%s", buffer.data()); + } else if (entry.find("[D]") != std::string::npos) { + syslog(LOG_DEBUG, "%s", buffer.data()); + } else { + syslog(LOG_ERR, "%s", buffer.data()); + } + // openlog syslog does not return any value. + return {}; + } + + detail::error_string flush() override { return {}; } + +private: + /// Creates a new syslog + detail::error_string create_syslog(std::string preamble, int log_local) + { + if (preamble == "") { + openlog(NULL, LOG_CONS | LOG_PID | LOG_NDELAY, log_local); + } else { + openlog(preamble.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, log_local); + } + return {}; + } + + static int syslog_translate(syslog_local_type log_local) + { + switch (log_local) { + case syslog_local_type::local0: + return LOG_LOCAL0; + case syslog_local_type::local1: + return LOG_LOCAL1; + case syslog_local_type::local2: + return LOG_LOCAL2; + case syslog_local_type::local3: + return LOG_LOCAL3; + case syslog_local_type::local4: + return LOG_LOCAL4; + case syslog_local_type::local5: + return LOG_LOCAL5; + case syslog_local_type::local6: + return LOG_LOCAL6; + case syslog_local_type::local7: + return LOG_LOCAL7; + default: + return LOG_LOCAL0; + break; + } + }; +}; + +} // namespace srslog + +#endif // SRSLOG_SYSLOG_SINK_H diff --git a/lib/src/srslog/srslog.cpp b/lib/src/srslog/srslog.cpp index b50e6c627..947cea375 100644 --- a/lib/src/srslog/srslog.cpp +++ b/lib/src/srslog/srslog.cpp @@ -13,6 +13,7 @@ #include "srsran/srslog/srslog.h" #include "formatters/json_formatter.h" #include "sinks/file_sink.h" +#include "sinks/syslog_sink.h" #include "srslog_instance.h" using namespace srslog; @@ -165,6 +166,25 @@ sink& srslog::fetch_file_sink(const std::string& path, size_t max_size, std::uni return *s; } +sink& srslog::fetch_syslog_sink(const std::string& preamble_, + syslog_local_type log_local_, + std::unique_ptr f) +{ + std::string sink_id = preamble_ + std::to_string(static_cast(log_local_)); + if (auto* s = find_sink(sink_id)) { + return *s; + } + + //: TODO: GCC5 or lower versions emits an error if we use the new() expression + // directly, use redundant piecewise_construct instead. + auto& s = srslog_instance::get().get_sink_repo().emplace( + std::piecewise_construct, + std::forward_as_tuple(sink_id), + std::forward_as_tuple(new syslog_sink(std::move(f), preamble_, log_local_))); + + return *s; +} + bool srslog::install_custom_sink(const std::string& id, std::unique_ptr s) { assert(!id.empty() && "Empty path string"); diff --git a/lib/test/srslog/CMakeLists.txt b/lib/test/srslog/CMakeLists.txt index 46a59303e..b875acfe0 100644 --- a/lib/test/srslog/CMakeLists.txt +++ b/lib/test/srslog/CMakeLists.txt @@ -35,6 +35,10 @@ target_include_directories(file_sink_test PUBLIC ../../) target_link_libraries(file_sink_test srslog) add_test(file_sink_test file_sink_test) +add_executable(syslog_sink_test syslog_sink_test.cpp) +target_include_directories(syslog_sink_test PUBLIC ../../) +target_link_libraries(syslog_sink_test srslog) + add_executable(file_utils_test file_utils_test.cpp) target_include_directories(file_utils_test PUBLIC ../../) target_link_libraries(file_utils_test srslog) diff --git a/lib/test/srslog/syslog_sink_test.cpp b/lib/test/srslog/syslog_sink_test.cpp new file mode 100644 index 000000000..51f958fb4 --- /dev/null +++ b/lib/test/srslog/syslog_sink_test.cpp @@ -0,0 +1,63 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "src/srslog/sinks/syslog_sink.h" +#include "srsran/srslog/srslog.h" +#include "test_dummies.h" +#include "testing_helpers.h" + +#include +#include + +using namespace srslog; + +/// Syslog sink name. +static constexpr char sink_name[] = "srslog_syslog_sink"; + +static bool find_string_infile(std::string filename, std::string pattern) +{ + std::ifstream file(filename); + std::string line; + bool found = false; + + if (file.is_open()) { + while (std::getline(file, line)) { + if (line.find(pattern) != std::string::npos) { // WILL SEARCH 2015-1113 in file + found = true; + } + } + } else { + printf("WARNING: Could not open file %s", filename.c_str()); + } + return found; +} + +static bool syslog_basic_test() +{ + syslog_sink syslog_sink(get_default_log_formatter()); + + // Build a 1000 byte entry. + std::string entry(1000, 'a'); + + syslog_sink.write(detail::memory_buffer(entry)); + + syslog_sink.flush(); + + ASSERT_EQ(find_string_infile("/var/log/syslog", entry), true); + return true; +} + +int main() +{ + TEST_FUNCTION(syslog_basic_test); + return 0; +} \ No newline at end of file