Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ target_include_directories(${VSOMEIP_NAME} INTERFACE
# them (which shouldn't be required). ${Boost_LIBRARIES} includes absolute
# build host paths as of writing, which also makes this important as it breaks
# the build.
target_link_libraries(${VSOMEIP_NAME} PRIVATE ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${DLT_LIBRARIES} ${SystemD_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(${VSOMEIP_NAME} PRIVATE ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${DLT_LIBRARIES} $<$<PLATFORM_ID:QNX>:slog2> ${SystemD_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})

if(NOT WIN32)
target_link_options(${VSOMEIP_NAME} PRIVATE "LINKER:-as-needed")
Expand Down
1 change: 1 addition & 0 deletions implementation/configuration/include/configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class configuration {
virtual bool is_v6() const = 0;

virtual bool has_console_log() const = 0;
virtual bool has_slog2_log() const = 0;
virtual bool has_file_log() const = 0;
virtual bool has_dlt_log() const = 0;
virtual const std::string& get_logfile() const = 0;
Expand Down
3 changes: 3 additions & 0 deletions implementation/configuration/include/configuration_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class configuration_impl : public configuration, public std::enable_shared_from_
VSOMEIP_EXPORT bool is_v6() const;

VSOMEIP_EXPORT bool has_console_log() const;
VSOMEIP_EXPORT bool has_slog2_log() const;
VSOMEIP_EXPORT bool has_file_log() const;
VSOMEIP_EXPORT bool has_dlt_log() const;
VSOMEIP_EXPORT const std::string& get_logfile() const;
Expand Down Expand Up @@ -444,6 +445,7 @@ class configuration_impl : public configuration, public std::enable_shared_from_
diagnosis_t diagnosis_mask_;

std::atomic_bool has_console_log_;
std::atomic_bool has_slog2_log_;
std::atomic_bool has_file_log_;
std::atomic_bool has_dlt_log_;
std::string logfile_;
Expand Down Expand Up @@ -517,6 +519,7 @@ class configuration_impl : public configuration, public std::enable_shared_from_
ET_LOGGING_CONSOLE,
ET_LOGGING_FILE,
ET_LOGGING_DLT,
ET_LOGGING_SLOG2,
ET_LOGGING_LEVEL,
ET_ROUTING,
ET_SERVICE_DISCOVERY_ENABLE,
Expand Down
25 changes: 22 additions & 3 deletions implementation/configuration/src/configuration_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ namespace cfg {

configuration_impl::configuration_impl(const std::string& _path) :
default_unicast_{"local"}, is_loaded_{false}, is_logging_loaded_{false}, prefix_{VSOMEIP_PREFIX}, diagnosis_{VSOMEIP_DIAGNOSIS_ADDRESS},
diagnosis_mask_{0xFF00}, has_console_log_{true}, has_file_log_{false}, has_dlt_log_{false}, logfile_{"/tmp/vsomeip.log"},
diagnosis_mask_{0xFF00}, has_console_log_{true},
#ifdef __QNX__
has_slog2_log_{true},
#else
has_slog2_log_{false},
#endif
has_file_log_{false}, has_dlt_log_{false}, logfile_{"/tmp/vsomeip.log"},
loglevel_{vsomeip_v3::logger::level_e::LL_INFO}, is_sd_enabled_{VSOMEIP_SD_DEFAULT_ENABLED}, sd_protocol_{VSOMEIP_SD_DEFAULT_PROTOCOL},
sd_multicast_{VSOMEIP_SD_DEFAULT_MULTICAST}, sd_port_{VSOMEIP_SD_DEFAULT_PORT},
sd_initial_delay_min_{VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MIN}, sd_initial_delay_max_{VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MAX},
Expand Down Expand Up @@ -98,7 +104,7 @@ configuration_impl::configuration_impl(const std::string& _path) :
configuration_impl::configuration_impl(const configuration_impl& _other) :
std::enable_shared_from_this<configuration_impl>(_other), default_unicast_{_other.default_unicast_}, is_loaded_{_other.is_loaded_},
is_logging_loaded_{_other.is_logging_loaded_}, mandatory_{_other.mandatory_}, has_console_log_{_other.has_console_log_.load()},
has_file_log_{_other.has_file_log_.load()}, has_dlt_log_{_other.has_dlt_log_.load()},
has_slog2_log_{_other.has_slog2_log_.load()}, has_file_log_{_other.has_file_log_.load()}, has_dlt_log_{_other.has_dlt_log_.load()},
max_configured_message_size_{_other.max_configured_message_size_}, max_local_message_size_{_other.max_local_message_size_},
max_reliable_message_size_{_other.max_reliable_message_size_}, max_unreliable_message_size_{_other.max_unreliable_message_size_},
buffer_shrink_threshold_{_other.buffer_shrink_threshold_}, permissions_uds_{VSOMEIP_DEFAULT_UDS_PERMISSIONS},
Expand Down Expand Up @@ -595,6 +601,15 @@ bool configuration_impl::load_logging(const configuration_element& _element, std
is_configured_[ET_LOGGING_DLT] = true;
}
#endif
} else if (its_key == "slog2") {
if (is_configured_[ET_LOGGING_SLOG2]) {
_warnings.insert("Multiple definitions for logging.slog2."
" Ignoring definition from " + _element.name_);
} else {
std::string its_value(i->second.data());
has_slog2_log_ = (its_value == "true");
is_configured_[ET_LOGGING_SLOG2] = true;
}
} else if (its_key == "level") {
if (is_configured_[ET_LOGGING_LEVEL]) {
_warnings.insert("Multiple definitions for logging.level."
Expand Down Expand Up @@ -2784,6 +2799,10 @@ bool configuration_impl::has_console_log() const {
return has_console_log_;
}

bool configuration_impl::has_slog2_log() const {
return has_slog2_log_;
}

bool configuration_impl::has_file_log() const {
return has_file_log_;
}
Expand All @@ -2797,7 +2816,7 @@ const std::string& configuration_impl::get_logfile() const {
}

vsomeip_v3::logger::level_e configuration_impl::get_loglevel() const {
std::unique_lock<std::mutex> its_lock(mutex_loglevel_);
std::unique_lock its_lock(mutex_loglevel_);
return loglevel_;
}

Expand Down
13 changes: 13 additions & 0 deletions implementation/logger/include/logger_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class logger_impl {
// alignas(4) to work around a bug in ancient MSVC15 which for some reason we still support...
struct alignas(4) config {
bool console_enabled;
bool slog2_enabled;
bool dlt_enabled;
bool file_enabled;
level_e loglevel;
Expand All @@ -45,6 +46,7 @@ class logger_impl {
config get_configuration() const;

void log_to_file(std::string_view _msg);
void log_to_slog2(level_e _level, std::string_view _msg);

#ifdef USE_DLT
#ifndef ANDROID
Expand All @@ -58,6 +60,17 @@ class logger_impl {

std::mutex log_file_mutex_;
std::ofstream log_file_;
std::string app_name_;

#ifdef __QNX__
// Flag whether slog2 was successfully initialized.
bool slog2_is_initialized_ = false;

static slog2_buffer_set_config_t buffer_config;
static slog2_buffer_t buffer_handle[1];
static std::uint8_t levelAsSlog2(level_e const _level);
#endif
auto init_slog2(const std::shared_ptr<configuration>& _configuration) -> void;
};

} // namespace logger
Expand Down
73 changes: 72 additions & 1 deletion implementation/logger/src/logger_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,30 @@

#include <vsomeip/runtime.hpp>

#ifdef __QNX__
#include <sys/slog2.h>
extern char * __progname;
#elif __linux__
extern char * __progname;
#endif

#include "../include/logger_impl.hpp"
#include "../../configuration/include/configuration.hpp"

namespace vsomeip_v3 {
namespace logger {

logger_impl::logger_impl() : config_{{false, false, false, level_e::LL_NONE}} { }
logger_impl::logger_impl() : config_{{false, false, false, false, level_e::LL_NONE}} { }

#ifdef __QNX__
slog2_buffer_set_config_t logger_impl::buffer_config = {0, "main", SLOG2_INFO, {"main", 4}, 1};
slog2_buffer_t logger_impl::buffer_handle[1] = {0};
#endif

void logger_impl::init(const std::shared_ptr<configuration>& _configuration) {
logger_impl::get()->set_configuration(_configuration);

logger_impl::get()->init_slog2(_configuration);
}

logger_impl::config logger_impl::get_configuration() const {
Expand All @@ -27,6 +41,7 @@ void logger_impl::set_configuration(const std::shared_ptr<configuration>& _confi
config cfg; // NOLINT(cppcoreguidelines-pro-type-member-init)
cfg.loglevel = _configuration->get_loglevel();
cfg.console_enabled = _configuration->has_console_log();
cfg.slog2_enabled = _configuration->has_slog2_log();
cfg.dlt_enabled = _configuration->has_dlt_log();
{
std::scoped_lock its_lock{log_file_mutex_};
Expand All @@ -46,6 +61,62 @@ void logger_impl::log_to_file(std::string_view _msg) {
}
}

auto logger_impl::init_slog2(const std::shared_ptr<configuration>& _configuration) -> void {
#ifdef __QNX__
if (slog2_is_initialized_ || !_configuration)
return;

logger_impl::buffer_config.buffer_set_name = __progname;
logger_impl::buffer_config.num_buffers = 1;
logger_impl::buffer_config.verbosity_level = log_level_as_slog2(_configuration->get_loglevel());

// Use a 16kB log buffer by default
// Override with a size specified by environment variable
int num_pages = 4;
auto s = getenv("VSOMEIP_SLOG2_NUM_PAGES");
if (s != nullptr) {
char* endptr = nullptr;
errno = 0;
auto const tmp_num_pages = strtoul(s, &endptr, 0);

// Safety checks
auto const at_least_one_digit_matched = endptr != s;
auto const input_is_terminated = *endptr == '\0';
auto const no_error = errno == 0;
auto const within_range = tmp_num_pages > 0 && tmp_num_pages < 1024; // 1024 pages = 4MB, hard to imagine this not being enough

if (at_least_one_digit_matched && input_is_terminated && no_error && within_range) {
num_pages = static_cast<decltype(num_pages)>(tmp_num_pages);
}
}

logger_impl::buffer_config.buffer_config[0].buffer_name = "vsomeip";
logger_impl::buffer_config.buffer_config[0].num_pages = num_pages;

// Register the buffer set.
if (-1 == slog2_register(&logger_impl::buffer_config, logger_impl::buffer_handle, 0)) {
std::fprintf(stderr, "Error registering slogger2 buffer!\n");
return;
} else {
slog2_is_initialized_ = true;
}
#else
static_cast<void>(_configuration);
#endif
}

void logger_impl::log_to_slog2(level_e _level, std::string_view _msg) {
#ifdef __QNX__
auto const handle = logger_impl::buffer_handle[0];
auto const user_code = 0;

slog2c(handle, user_code, log_level_as_slog2(_level), _msg.data());
#else
static_cast<void>(_level);
static_cast<void>(_msg);
#endif
}

#ifdef USE_DLT
#ifndef ANDROID

Expand Down
34 changes: 28 additions & 6 deletions implementation/logger/src/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
#include <sstream>
#include <string>

#ifdef __QNX__
#include <sys/slog2.h>
extern char * __progname;
#elif __linux__
extern char * __progname;
#endif

#include "../../configuration/include/configuration.hpp"

#if defined(ANDROID) && !defined(ANDROID_CI_BUILD)
Expand Down Expand Up @@ -81,12 +88,18 @@ message::message(level_e _level) : std::ostream(&buffer_), level_{_level} {
// Store logger config locally to avoid repeated access to atomics
// TODO: Simplify once access to logger config no longer needs to be threadsafe
auto its_cfg = its_logger->get_configuration();
threshold_level_ = its_cfg.loglevel;
console_enabled_ = its_cfg.console_enabled;
slog2_enabled_ = its_cfg.slog2_enabled;
dlt_enabled_ = its_cfg.dlt_enabled;
file_enabled_ = its_cfg.file_enabled;

// Check whether this message should be logged at all
if ((console_enabled_ || dlt_enabled_ || file_enabled_) && level_ <= its_cfg.loglevel) {
if ((console_enabled_ || slog2_enabled_ || dlt_enabled_ || file_enabled_))
{
// Upstream applies a severity filter here, however we prefer to the filters on the individual log targets where possible (slog2,
// logcat if implemented.). Thus we apply filters in ~message on each log type. This does diverge from upstream, and can result in
// more messages being formatted than strictly necessary, but simplifies the integration with slog2.
buffer_.activate();
when_ = std::chrono::system_clock::now();
}
Expand All @@ -107,22 +120,23 @@ message::~message() try {
return;
}

if (console_enabled_) {
#ifndef ANDROID
if (console_enabled_ && level_ <= threshold_level_) {
// std::cout is threadsafe, but output may be interleaved if multiple things are
// streamed. To avoid a lock, build the full logline first and stream as a
// single argument. In C++20, could use std::osyncstream and/or std::format to simplify
// this.
// Unfortunately, building the string is a bit awkward - freely concatenating
// string_views and strings is a C++26 feature.
const std::string_view ts = timestamp();
const std::string_view app = app_name();
const std::string_view appn = app_name();
const std::string_view lvl = level_as_view();
const std::string_view msg = buffer_as_view();
std::string output;
output.reserve(ts.size() + app.size() + lvl.size() + msg.size() + 1);
output.reserve(ts.size() + appn.size() + 1 + lvl.size() + 1 + msg.size() + 1);
output += ts;
output += app;
output += " ";
output += appn;
output += lvl;
output += msg;
Comment on lines 140 to 141
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
output += lvl;
output += msg;
output += lvl;
output += " ";
output += msg;

output += '\n';
Expand Down Expand Up @@ -167,7 +181,15 @@ message::~message() try {
#endif // !ANDROID
}

if (file_enabled_) {
#ifdef __QNX__
if (slog2_enabled_) {
// Apply a severity filter using the pps settings, e.g.
// echo buffer_name:n:7 >> /var/pps/slog2/verbose
its_logger->log_to_slog2(level_, std::string(buffer_as_view()));
}
#endif

if (file_enabled_ && level_ <= threshold_level_) {
// Delegate logging of the message the logger, which ensures that log_to_file() is
// thread-safe. To keep the API simple, construct the string here, where we have all
// the information readily available.
Expand Down
35 changes: 35 additions & 0 deletions interface/vsomeip/internal/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#include <string_view>
#include <vector>

#ifdef __QNX__
#include <sys/slog2.h>
#endif

#include <vsomeip/export.hpp>

namespace vsomeip_v3 {
Expand All @@ -28,6 +32,35 @@ enum class VSOMEIP_IMPORT_EXPORT level_e : std::uint8_t {
LL_VERBOSE = 6
};

#ifdef __QNX__
inline constexpr auto log_level_as_slog2(level_e const _level) -> std::uint8_t
{
uint8_t severity = 0;
switch (_level) {
case level_e::LL_FATAL:
severity = SLOG2_CRITICAL;
break;
case level_e::LL_ERROR:
severity = SLOG2_ERROR;
break;
case level_e::LL_WARNING:
severity = SLOG2_WARNING;
break;
case level_e::LL_INFO:
severity = SLOG2_INFO;
break;
case level_e::LL_DEBUG:
severity = SLOG2_DEBUG1;
break;
case level_e::LL_VERBOSE:
default:
severity = SLOG2_DEBUG2;
break;
}
return severity;
}
#endif

class message : public std::ostream {
public:
VSOMEIP_IMPORT_EXPORT explicit message(level_e _level);
Expand Down Expand Up @@ -55,7 +88,9 @@ class message : public std::ostream {

buffer buffer_;
const level_e level_;
level_e threshold_level_;
bool console_enabled_{false};
bool slog2_enabled_{false};
bool dlt_enabled_{false};
bool file_enabled_{false};
std::chrono::system_clock::time_point when_;
Expand Down
Loading