Skip to content

Commit 2f03fd7

Browse files
committed
Implement slog2 logging
slog2 is enabled by default, but guarded with #ifdef __QNX__. It can be controlled with /etc/vsomeip/logging.json, i.e.: ```json "logging": { "slog2": true } ``` This code allocates 4 pages (16kB) by default, but the caller can provide an environment variable VSOMEIP_SLOG2_NUM_PAGES to control the allocation
1 parent f58ba57 commit 2f03fd7

File tree

8 files changed

+175
-11
lines changed

8 files changed

+175
-11
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ target_include_directories(${VSOMEIP_NAME} INTERFACE
351351
# them (which shouldn't be required). ${Boost_LIBRARIES} includes absolute
352352
# build host paths as of writing, which also makes this important as it breaks
353353
# the build.
354-
target_link_libraries(${VSOMEIP_NAME} PRIVATE ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${DLT_LIBRARIES} ${SystemD_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
354+
target_link_libraries(${VSOMEIP_NAME} PRIVATE ${Boost_LIBRARIES} ${USE_RT} ${DL_LIBRARY} ${DLT_LIBRARIES} $<$<PLATFORM_ID:QNX>:slog2> ${SystemD_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
355355

356356
if(NOT WIN32)
357357
target_link_options(${VSOMEIP_NAME} PRIVATE "LINKER:-as-needed")

implementation/configuration/include/configuration.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class configuration {
7373
virtual bool is_v6() const = 0;
7474

7575
virtual bool has_console_log() const = 0;
76+
virtual bool has_slog2_log() const = 0;
7677
virtual bool has_file_log() const = 0;
7778
virtual bool has_dlt_log() const = 0;
7879
virtual const std::string& get_logfile() const = 0;

implementation/configuration/include/configuration_impl.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class configuration_impl : public configuration, public std::enable_shared_from_
8989
VSOMEIP_EXPORT bool is_v6() const;
9090

9191
VSOMEIP_EXPORT bool has_console_log() const;
92+
VSOMEIP_EXPORT bool has_slog2_log() const;
9293
VSOMEIP_EXPORT bool has_file_log() const;
9394
VSOMEIP_EXPORT bool has_dlt_log() const;
9495
VSOMEIP_EXPORT const std::string& get_logfile() const;
@@ -444,6 +445,7 @@ class configuration_impl : public configuration, public std::enable_shared_from_
444445
diagnosis_t diagnosis_mask_;
445446

446447
std::atomic_bool has_console_log_;
448+
std::atomic_bool has_slog2_log_;
447449
std::atomic_bool has_file_log_;
448450
std::atomic_bool has_dlt_log_;
449451
std::string logfile_;
@@ -517,6 +519,7 @@ class configuration_impl : public configuration, public std::enable_shared_from_
517519
ET_LOGGING_CONSOLE,
518520
ET_LOGGING_FILE,
519521
ET_LOGGING_DLT,
522+
ET_LOGGING_SLOG2,
520523
ET_LOGGING_LEVEL,
521524
ET_ROUTING,
522525
ET_SERVICE_DISCOVERY_ENABLE,

implementation/configuration/src/configuration_impl.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ namespace cfg {
4747

4848
configuration_impl::configuration_impl(const std::string& _path) :
4949
default_unicast_{"local"}, is_loaded_{false}, is_logging_loaded_{false}, prefix_{VSOMEIP_PREFIX}, diagnosis_{VSOMEIP_DIAGNOSIS_ADDRESS},
50-
diagnosis_mask_{0xFF00}, has_console_log_{true}, has_file_log_{false}, has_dlt_log_{false}, logfile_{"/tmp/vsomeip.log"},
50+
diagnosis_mask_{0xFF00}, has_console_log_{true},
51+
#ifdef __QNX__
52+
has_slog2_log_{true},
53+
#else
54+
has_slog2_log_{false},
55+
#endif
56+
has_file_log_{false}, has_dlt_log_{false}, logfile_{"/tmp/vsomeip.log"},
5157
loglevel_{vsomeip_v3::logger::level_e::LL_INFO}, is_sd_enabled_{VSOMEIP_SD_DEFAULT_ENABLED}, sd_protocol_{VSOMEIP_SD_DEFAULT_PROTOCOL},
5258
sd_multicast_{VSOMEIP_SD_DEFAULT_MULTICAST}, sd_port_{VSOMEIP_SD_DEFAULT_PORT},
5359
sd_initial_delay_min_{VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MIN}, sd_initial_delay_max_{VSOMEIP_SD_DEFAULT_INITIAL_DELAY_MAX},
@@ -98,7 +104,7 @@ configuration_impl::configuration_impl(const std::string& _path) :
98104
configuration_impl::configuration_impl(const configuration_impl& _other) :
99105
std::enable_shared_from_this<configuration_impl>(_other), default_unicast_{_other.default_unicast_}, is_loaded_{_other.is_loaded_},
100106
is_logging_loaded_{_other.is_logging_loaded_}, mandatory_{_other.mandatory_}, has_console_log_{_other.has_console_log_.load()},
101-
has_file_log_{_other.has_file_log_.load()}, has_dlt_log_{_other.has_dlt_log_.load()},
107+
has_slog2_log_{_other.has_slog2_log_.load()}, has_file_log_{_other.has_file_log_.load()}, has_dlt_log_{_other.has_dlt_log_.load()},
102108
max_configured_message_size_{_other.max_configured_message_size_}, max_local_message_size_{_other.max_local_message_size_},
103109
max_reliable_message_size_{_other.max_reliable_message_size_}, max_unreliable_message_size_{_other.max_unreliable_message_size_},
104110
buffer_shrink_threshold_{_other.buffer_shrink_threshold_}, permissions_uds_{VSOMEIP_DEFAULT_UDS_PERMISSIONS},
@@ -595,6 +601,15 @@ bool configuration_impl::load_logging(const configuration_element& _element, std
595601
is_configured_[ET_LOGGING_DLT] = true;
596602
}
597603
#endif
604+
} else if (its_key == "slog2") {
605+
if (is_configured_[ET_LOGGING_SLOG2]) {
606+
_warnings.insert("Multiple definitions for logging.slog2."
607+
" Ignoring definition from " + _element.name_);
608+
} else {
609+
std::string its_value(i->second.data());
610+
has_slog2_log_ = (its_value == "true");
611+
is_configured_[ET_LOGGING_SLOG2] = true;
612+
}
598613
} else if (its_key == "level") {
599614
if (is_configured_[ET_LOGGING_LEVEL]) {
600615
_warnings.insert("Multiple definitions for logging.level."
@@ -2784,6 +2799,10 @@ bool configuration_impl::has_console_log() const {
27842799
return has_console_log_;
27852800
}
27862801

2802+
bool configuration_impl::has_slog2_log() const {
2803+
return has_slog2_log_;
2804+
}
2805+
27872806
bool configuration_impl::has_file_log() const {
27882807
return has_file_log_;
27892808
}
@@ -2797,7 +2816,7 @@ const std::string& configuration_impl::get_logfile() const {
27972816
}
27982817

27992818
vsomeip_v3::logger::level_e configuration_impl::get_loglevel() const {
2800-
std::unique_lock<std::mutex> its_lock(mutex_loglevel_);
2819+
std::unique_lock its_lock(mutex_loglevel_);
28012820
return loglevel_;
28022821
}
28032822

implementation/logger/include/logger_impl.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class logger_impl {
3636
// alignas(4) to work around a bug in ancient MSVC15 which for some reason we still support...
3737
struct alignas(4) config {
3838
bool console_enabled;
39+
bool slog2_enabled;
3940
bool dlt_enabled;
4041
bool file_enabled;
4142
level_e loglevel;
@@ -45,6 +46,7 @@ class logger_impl {
4546
config get_configuration() const;
4647

4748
void log_to_file(std::string_view _msg);
49+
void log_to_slog2(level_e _level, std::string_view _msg);
4850

4951
#ifdef USE_DLT
5052
#ifndef ANDROID
@@ -58,6 +60,17 @@ class logger_impl {
5860

5961
std::mutex log_file_mutex_;
6062
std::ofstream log_file_;
63+
std::string app_name_;
64+
65+
#ifdef __QNX__
66+
// Flag whether slog2 was successfully initialized.
67+
bool slog2_is_initialized_ = false;
68+
69+
static slog2_buffer_set_config_t buffer_config;
70+
static slog2_buffer_t buffer_handle[1];
71+
static std::uint8_t levelAsSlog2(level_e const _level);
72+
#endif
73+
auto init_slog2(const std::shared_ptr<configuration>& _configuration) -> void;
6174
};
6275

6376
} // namespace logger

implementation/logger/src/logger_impl.cpp

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,30 @@
55

66
#include <vsomeip/runtime.hpp>
77

8+
#ifdef __QNX__
9+
#include <sys/slog2.h>
10+
extern char * __progname;
11+
#elif __linux__
12+
extern char * __progname;
13+
#endif
14+
815
#include "../include/logger_impl.hpp"
916
#include "../../configuration/include/configuration.hpp"
1017

1118
namespace vsomeip_v3 {
1219
namespace logger {
1320

14-
logger_impl::logger_impl() : config_{{false, false, false, level_e::LL_NONE}} { }
21+
logger_impl::logger_impl() : config_{{false, false, false, false, level_e::LL_NONE}} { }
22+
23+
#ifdef __QNX__
24+
slog2_buffer_set_config_t logger_impl::buffer_config = {0, "main", SLOG2_INFO, {"main", 4}, 1};
25+
slog2_buffer_t logger_impl::buffer_handle[1] = {0};
26+
#endif
1527

1628
void logger_impl::init(const std::shared_ptr<configuration>& _configuration) {
1729
logger_impl::get()->set_configuration(_configuration);
30+
31+
logger_impl::get()->init_slog2(_configuration);
1832
}
1933

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

64+
auto logger_impl::init_slog2(const std::shared_ptr<configuration>& _configuration) -> void {
65+
#ifdef __QNX__
66+
if (slog2_is_initialized_ || !_configuration)
67+
return;
68+
69+
logger_impl::buffer_config.buffer_set_name = __progname;
70+
logger_impl::buffer_config.num_buffers = 1;
71+
logger_impl::buffer_config.verbosity_level = log_level_as_slog2(_configuration->get_loglevel());
72+
73+
// Use a 16kB log buffer by default
74+
// Override with a size specified by environment variable
75+
int num_pages = 4;
76+
auto s = getenv("VSOMEIP_SLOG2_NUM_PAGES");
77+
if (s != nullptr) {
78+
char* endptr = nullptr;
79+
errno = 0;
80+
auto const tmp_num_pages = strtoul(s, &endptr, 0);
81+
82+
// Safety checks
83+
auto const at_least_one_digit_matched = endptr != s;
84+
auto const input_is_terminated = *endptr == '\0';
85+
auto const no_error = errno == 0;
86+
auto const within_range = tmp_num_pages > 0 && tmp_num_pages < 1024; // 1024 pages = 4MB, hard to imagine this not being enough
87+
88+
if (at_least_one_digit_matched && input_is_terminated && no_error && within_range) {
89+
num_pages = static_cast<decltype(num_pages)>(tmp_num_pages);
90+
}
91+
}
92+
93+
logger_impl::buffer_config.buffer_config[0].buffer_name = "vsomeip";
94+
logger_impl::buffer_config.buffer_config[0].num_pages = num_pages;
95+
96+
// Register the buffer set.
97+
if (-1 == slog2_register(&logger_impl::buffer_config, logger_impl::buffer_handle, 0)) {
98+
std::fprintf(stderr, "Error registering slogger2 buffer!\n");
99+
return;
100+
} else {
101+
slog2_is_initialized_ = true;
102+
}
103+
#else
104+
static_cast<void>(_configuration);
105+
#endif
106+
}
107+
108+
void logger_impl::log_to_slog2(level_e _level, std::string_view _msg) {
109+
#ifdef __QNX__
110+
auto const handle = logger_impl::buffer_handle[0];
111+
auto const user_code = 0;
112+
113+
slog2c(handle, user_code, log_level_as_slog2(_level), _msg.data());
114+
#else
115+
static_cast<void>(_level);
116+
static_cast<void>(_msg);
117+
#endif
118+
}
119+
49120
#ifdef USE_DLT
50121
#ifndef ANDROID
51122

implementation/logger/src/message.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
#include <sstream>
1212
#include <string>
1313

14+
#ifdef __QNX__
15+
#include <sys/slog2.h>
16+
extern char * __progname;
17+
#elif __linux__
18+
extern char * __progname;
19+
#endif
20+
1421
#include "../../configuration/include/configuration.hpp"
1522

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

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

110-
if (console_enabled_) {
111123
#ifndef ANDROID
124+
if (console_enabled_ && level_ <= threshold_level_) {
112125
// std::cout is threadsafe, but output may be interleaved if multiple things are
113126
// streamed. To avoid a lock, build the full logline first and stream as a
114127
// single argument. In C++20, could use std::osyncstream and/or std::format to simplify
115128
// this.
116129
// Unfortunately, building the string is a bit awkward - freely concatenating
117130
// string_views and strings is a C++26 feature.
118131
const std::string_view ts = timestamp();
119-
const std::string_view app = app_name();
132+
const std::string_view appn = app_name();
120133
const std::string_view lvl = level_as_view();
121134
const std::string_view msg = buffer_as_view();
122135
std::string output;
123-
output.reserve(ts.size() + app.size() + lvl.size() + msg.size() + 1);
136+
output.reserve(ts.size() + appn.size() + 1 + lvl.size() + 1 + msg.size() + 1);
124137
output += ts;
125-
output += app;
138+
output += " ";
139+
output += appn;
126140
output += lvl;
127141
output += msg;
128142
output += '\n';
@@ -167,7 +181,15 @@ message::~message() try {
167181
#endif // !ANDROID
168182
}
169183

170-
if (file_enabled_) {
184+
#ifdef __QNX__
185+
if (slog2_enabled_) {
186+
// Apply a severity filter using the pps settings, e.g.
187+
// echo buffer_name:n:7 >> /var/pps/slog2/verbose
188+
its_logger->log_to_slog2(level_, std::string(buffer_as_view()));
189+
}
190+
#endif
191+
192+
if (file_enabled_ && level_ <= threshold_level_) {
171193
// Delegate logging of the message the logger, which ensures that log_to_file() is
172194
// thread-safe. To keep the API simple, construct the string here, where we have all
173195
// the information readily available.

interface/vsomeip/internal/logger.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#include <string_view>
1414
#include <vector>
1515

16+
#ifdef __QNX__
17+
#include <sys/slog2.h>
18+
#endif
19+
1620
#include <vsomeip/export.hpp>
1721

1822
namespace vsomeip_v3 {
@@ -28,6 +32,35 @@ enum class VSOMEIP_IMPORT_EXPORT level_e : std::uint8_t {
2832
LL_VERBOSE = 6
2933
};
3034

35+
#ifdef __QNX__
36+
inline constexpr auto log_level_as_slog2(level_e const _level) -> std::uint8_t
37+
{
38+
uint8_t severity = 0;
39+
switch (_level) {
40+
case level_e::LL_FATAL:
41+
severity = SLOG2_CRITICAL;
42+
break;
43+
case level_e::LL_ERROR:
44+
severity = SLOG2_ERROR;
45+
break;
46+
case level_e::LL_WARNING:
47+
severity = SLOG2_WARNING;
48+
break;
49+
case level_e::LL_INFO:
50+
severity = SLOG2_INFO;
51+
break;
52+
case level_e::LL_DEBUG:
53+
severity = SLOG2_DEBUG1;
54+
break;
55+
case level_e::LL_VERBOSE:
56+
default:
57+
severity = SLOG2_DEBUG2;
58+
break;
59+
}
60+
return severity;
61+
}
62+
#endif
63+
3164
class message : public std::ostream {
3265
public:
3366
VSOMEIP_IMPORT_EXPORT explicit message(level_e _level);
@@ -55,7 +88,9 @@ class message : public std::ostream {
5588

5689
buffer buffer_;
5790
const level_e level_;
91+
level_e threshold_level_;
5892
bool console_enabled_{false};
93+
bool slog2_enabled_{false};
5994
bool dlt_enabled_{false};
6095
bool file_enabled_{false};
6196
std::chrono::system_clock::time_point when_;

0 commit comments

Comments
 (0)