From 747d03472bb96867663ead843916a58baceea450 Mon Sep 17 00:00:00 2001 From: Bharath Tirunagaru Date: Thu, 20 Aug 2026 22:34:35 -0700 Subject: [PATCH 1/3] feat(mw/log): add Linux syslog backend for LogMode::kSystem Adds a Linux syslog mw::log backend that fills the LogMode::kSystem slot on Linux (HGY aarch64 and x86 host), mirroring the existing QNX slog backend exactly. slog is target_compatible_with os:qnx and this new backend is target_compatible_with os:linux, so exactly one compiles per build and both can safely reuse the same kSystem slot without any LogMode/dispatch changes. - score/mw/log/detail/syslog/syslog_backend.{h,cpp}: SyslogBackend, a Backend implementation whose FlushSlot formats "appid,ctxid: payload" and forwards to score::os::Syslog::syslog() at a severity mapped from mw::log's LogLevel (kFatal->LOG_CRIT, kError->LOG_ERR, kWarn->LOG_WARNING, kInfo->LOG_INFO, kDebug/kVerbose->LOG_DEBUG); Init() calls openlog(app_id, LOG_PID|LOG_NDELAY, LOG_USER). - score/mw/log/detail/syslog/syslog_recorder_factory.{h,cpp}: CRTP SyslogRecorderFactory wiring a TextRecorder over SyslogBackend, using score::os::Syslog::Default() for the real OS wrapper. - score/mw/log/detail/syslog/{syslog_backend_test, syslog_recorder_factory_test}.cpp: unit tests against @score_baselibs//score/os/mocklib:syslog_mock. - score/mw/log/backend/syslog_registrant.cpp: registers CreateSyslogRecorder against LogMode::kSystem via BackendRegistrant, mirroring slog_registrant.cpp. - score/mw/log/backend/BUILD: new syslog cc_library (target_compatible_with os:linux, alwayslink), plus the detail/syslog BUILD for syslog_backend/syslog_recorder_factory. Depends on the companion score::os::Syslog OS wrapper contributed to the baselibs component (@score_baselibs//score/os:syslog). Signed-off-by: Bharath Tirunagaru --- score/mw/log/backend/BUILD | 16 + score/mw/log/backend/syslog_registrant.cpp | 62 ++++ score/mw/log/detail/syslog/BUILD | 114 +++++++ score/mw/log/detail/syslog/syslog_backend.cpp | 192 ++++++++++++ score/mw/log/detail/syslog/syslog_backend.h | 58 ++++ .../log/detail/syslog/syslog_backend_test.cpp | 289 ++++++++++++++++++ .../detail/syslog/syslog_recorder_factory.cpp | 44 +++ .../detail/syslog/syslog_recorder_factory.h | 44 +++ .../syslog/syslog_recorder_factory_test.cpp | 50 +++ 9 files changed, 869 insertions(+) create mode 100644 score/mw/log/backend/syslog_registrant.cpp create mode 100644 score/mw/log/detail/syslog/BUILD create mode 100644 score/mw/log/detail/syslog/syslog_backend.cpp create mode 100644 score/mw/log/detail/syslog/syslog_backend.h create mode 100644 score/mw/log/detail/syslog/syslog_backend_test.cpp create mode 100644 score/mw/log/detail/syslog/syslog_recorder_factory.cpp create mode 100644 score/mw/log/detail/syslog/syslog_recorder_factory.h create mode 100644 score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp diff --git a/score/mw/log/backend/BUILD b/score/mw/log/backend/BUILD index 846c781f..929d9f65 100644 --- a/score/mw/log/backend/BUILD +++ b/score/mw/log/backend/BUILD @@ -95,6 +95,22 @@ cc_library( alwayslink = True, ) +# Plugin: Linux syslog(3) System Logging +# Automatically included on Linux (HGY aarch64 / x86 host) builds. +cc_library( + name = "syslog", + srcs = ["syslog_registrant.cpp"], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + target_compatible_with = ["@platforms//os:linux"], + visibility = ["//visibility:public"], # platform_only + deps = [ + "//score/mw/log/detail/syslog:syslog_recorder_factory", + "@score_baselibs//score/mw/log:minimal", + ], + alwayslink = True, +) + # Plugin: Custom user-provided Logging backend # Opt-in. Build with --@score_logging//score/mw/log/flags:KCustom_Logging=True # and --@score_logging//score/mw/log/flags:custom_recorder_impl=//your:target. diff --git a/score/mw/log/backend/syslog_registrant.cpp b/score/mw/log/backend/syslog_registrant.cpp new file mode 100644 index 00000000..0643e415 --- /dev/null +++ b/score/mw/log/backend/syslog_registrant.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/log/backend_table.h" +#include "score/mw/log/detail/syslog/syslog_recorder_factory.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ +namespace +{ + +std::unique_ptr CreateSyslogRecorder(const Configuration& config, + score::cpp::pmr::memory_resource* memory_resource) +{ + SyslogRecorderFactory factory; + return factory.CreateLogRecorder(config, memory_resource); +} + +/* +Deviation from Rule A3-3-2: +- Static and thread-local objects shall be constant-initialized. +Justification: +- BackendRegistrant constructor executes during dynamic initialization to write a function + pointer into gBackendCreators[]. The target array is constant-initialized (zero-init + at load time), so it is valid before this constructor runs. The registrant struct itself + is trivially destructible. This follows the established pattern used by Runtime::Instance(). +Deviation from Rule M0-1-3: +- A project shall not contain unused variables. +Deviation from Rule M0-1-9: +- There shall be no dead code. +Justification: +- The variable IS used via its constructor's side effect during static initialization. + BackendRegistrant's constructor registers the CreateSyslogRecorder function pointer into + gBackendCreators[] at program startup. The variable itself doesn't need to be referenced + elsewhere - its purpose is fulfilled by the constructor's execution. This is an intentional + static registration pattern. +*/ +// coverity[autosar_cpp14_a3_3_2_violation] See above +// coverity[autosar_cpp14_m0_1_3_violation] See above +// coverity[autosar_cpp14_m0_1_9_violation] See above +const BackendRegistrant kSyslogRegistrant{LogMode::kSystem, &CreateSyslogRecorder}; + +} // namespace +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score diff --git a/score/mw/log/detail/syslog/BUILD b/score/mw/log/detail/syslog/BUILD new file mode 100644 index 00000000..24eb8f47 --- /dev/null +++ b/score/mw/log/detail/syslog/BUILD @@ -0,0 +1,114 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") +load("//:score/mw/common_features.bzl", "COMPILER_WARNING_FEATURES") + +cc_library( + name = "syslog_backend", + srcs = [ + "syslog_backend.cpp", + ], + hdrs = [ + "syslog_backend.h", + ], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + target_compatible_with = ["@platforms//os:linux"], + visibility = [ + "//score/mw/log:__subpackages__", + "@score_baselibs//score/mw/log:__subpackages__", + ], + deps = [ + "@score_baselibs//score/mw/log/detail:backend_interface", + "@score_baselibs//score/mw/log/detail:circular_allocator", + "@score_baselibs//score/mw/log/detail:initialization_reporter", + "@score_baselibs//score/os:syslog", + ], +) + +cc_library( + name = "syslog", + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + target_compatible_with = ["@platforms//os:linux"], + visibility = [ + "//score/mw/log:__subpackages__", + "@score_baselibs//score/mw/log:__subpackages__", + ], + deps = [ + ":syslog_backend", + ":syslog_recorder_factory", + ], +) + +cc_library( + name = "syslog_recorder_factory", + srcs = [ + "syslog_recorder_factory.cpp", + ], + hdrs = [ + "syslog_recorder_factory.h", + ], + features = COMPILER_WARNING_FEATURES, + tags = ["FFI"], + target_compatible_with = ["@platforms//os:linux"], + visibility = [ + "//score/mw/log:__subpackages__", + "@score_baselibs//score/mw/log:__subpackages__", + ], + deps = [ + ":syslog_backend", + "@score_baselibs//score/mw/log/detail:log_recorder_factory", + "@score_baselibs//score/mw/log/detail/text_recorder", + ], +) + +cc_test( + name = "syslog_recorder_factory_test", + srcs = [ + "syslog_recorder_factory_test.cpp", + ], + features = [ + "aborts_upon_exception", + ], + tags = ["unit"], + target_compatible_with = ["@platforms//os:linux"], + deps = [ + ":syslog_recorder_factory", + "@googletest//:gtest", + "@googletest//:gtest_main", + "@score_baselibs//score/language/futurecpp:futurecpp_test_support", + "@score_baselibs//score/mw/log/configuration", + ], +) + +cc_test( + name = "syslog_backend_test", + srcs = [ + "syslog_backend_test.cpp", + ], + features = [ + "aborts_upon_exception", + ], + tags = ["unit"], + target_compatible_with = ["@platforms//os:linux"], + deps = [ + ":syslog", + "@googletest//:gtest", + "@googletest//:gtest_main", + "@score_baselibs//score/language/futurecpp:futurecpp_test_support", + "@score_baselibs//score/mw/log/configuration", + "@score_baselibs//score/os/mocklib:syslog_mock", + ], +) diff --git a/score/mw/log/detail/syslog/syslog_backend.cpp b/score/mw/log/detail/syslog/syslog_backend.cpp new file mode 100644 index 00000000..2367bafd --- /dev/null +++ b/score/mw/log/detail/syslog/syslog_backend.cpp @@ -0,0 +1,192 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/log/detail/syslog/syslog_backend.h" + +#include "score/mw/log/detail/error.h" +#include "score/mw/log/detail/initialization_reporter.h" + +#include +#include +#include +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ + +namespace +{ + +std::size_t CheckTheMaxCapacity(const std::size_t capacity) noexcept +{ + const auto is_within_max_capacity = (capacity <= std::numeric_limits::max()); + if (is_within_max_capacity) + { + return capacity; + } + else + { + return static_cast(std::numeric_limits::max()); + } +} + +// syslog(3) priorities from . kInvalid marks a level that must not be emitted +// (e.g. kOff / out-of-range), mirroring slog's kInvalid handling but skipping the emit. +enum class SyslogPriority : std::int32_t +{ + kCrit = LOG_CRIT, + kErr = LOG_ERR, + kWarning = LOG_WARNING, + kInfo = LOG_INFO, + kDebug = LOG_DEBUG, + kInvalid = -1 +}; + +constexpr SyslogPriority ConvertMwLogLevelToSyslogPriority(const LogLevel level) +{ + SyslogPriority priority = SyslogPriority::kInvalid; + switch (level) + { + case LogLevel::kVerbose: + priority = SyslogPriority::kDebug; + break; + case LogLevel::kDebug: + priority = SyslogPriority::kDebug; + break; + case LogLevel::kInfo: + priority = SyslogPriority::kInfo; + break; + case LogLevel::kWarn: + priority = SyslogPriority::kWarning; + break; + case LogLevel::kError: + priority = SyslogPriority::kErr; + break; + case LogLevel::kFatal: + priority = SyslogPriority::kCrit; + break; + case LogLevel::kOff: + default: + priority = SyslogPriority::kInvalid; + break; + } + return priority; +} + +constexpr SyslogPriority ToSyslogPriority(const LogLevel log_level) noexcept +{ + if (log_level <= GetMaxLogLevelValue()) + { + return ConvertMwLogLevelToSyslogPriority(log_level); + } + else + { + return SyslogPriority::kInvalid; + } +} + +} // namespace + +SyslogBackend::SyslogBackend(const std::size_t number_of_slots, + const LogRecord& initial_slot_value, + const std::string_view app_id, + score::cpp::pmr::unique_ptr syslog_instance) noexcept + : Backend::Backend(), + app_id_{app_id.data(), app_id.size()}, + buffer_{CheckTheMaxCapacity(number_of_slots), initial_slot_value}, + syslog_instance_{std::move(syslog_instance)} +{ + Init(); +} + +score::cpp::optional SyslogBackend::ReserveSlot() noexcept +{ + const auto& slot = buffer_.AcquireSlotToWrite(); + if (slot.has_value()) + { + if (slot.value() < std::numeric_limits::max()) // LCOV_EXCL_BR_LINE: As it always true case,we can't + // control slot.value() it is received from AcquireSlotToWrite() function + // which wraps around and resulting in a value within the valid range. + { + // CircularAllocator has capacity limited by CheckTheMaxCapacity thus the cast is valid: + // We intentionally static cast to SlotIndex(uint8_t) to limit memory allocations + // to the required levels during startup, since there is no need to support slots greater + // than uint8 as per the current system needs. + // coverity[autosar_cpp14_a4_7_1_violation] + return SlotHandle{static_cast(slot.value())}; + } + } + return {}; +} + +LogRecord& SyslogBackend::GetLogRecord(const SlotHandle& slot) noexcept +{ + // static cast from std::uint8_t to std::size_t + return buffer_.GetUnderlyingBufferFor(static_cast(slot.GetSlotOfSelectedRecorder())); +} + +void SyslogBackend::FlushSlot(const SlotHandle& slot) noexcept +{ + // static cast from std::uint8_t to std::size_t + auto& log_entry = + buffer_.GetUnderlyingBufferFor(static_cast(slot.GetSlotOfSelectedRecorder())).GetLogEntry(); + + constexpr std::size_t kMaxIdLength{4U}; + + // Cast appid length to int32 without overflow. + const std::int32_t app_id_length = static_cast(std::min(kMaxIdLength, app_id_.size())); + + // Cast context length to int32 without overflow. + const std::int32_t ctx_id_length = + static_cast(std::min(kMaxIdLength, log_entry.ctx_id.GetStringView().size())); + + // Cast payload size to int32_t without overflow. + const std::int32_t payload_length = static_cast( + std::min(static_cast(std::numeric_limits::max()), log_entry.payload.size())); + + const auto priority = ToSyslogPriority(log_entry.log_level); + if (priority != SyslogPriority::kInvalid) + { + // Log message with appid and ctxid. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) no available alternative for syslog + syslog_instance_->syslog(static_cast(priority), + "%.*s,%.*s: %.*s", + app_id_length, + app_id_.c_str(), + ctx_id_length, + // above variable `ctx_id_length` contains corresponding length information + // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage) justified above + log_entry.ctx_id.GetStringView().data(), + payload_length, + log_entry.payload.data()); + } + + buffer_.ReleaseSlot(static_cast(slot.GetSlotOfSelectedRecorder())); +} + +void SyslogBackend::Init() noexcept +{ + // glibc openlog(3) stores the `ident` pointer (it does not copy the string); app_id_ is a + // member and outlives every syslog() call, so passing its c_str() is safe. + syslog_instance_->openlog(app_id_.c_str(), LOG_PID | LOG_NDELAY, LOG_USER); +} + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score diff --git a/score/mw/log/detail/syslog/syslog_backend.h b/score/mw/log/detail/syslog/syslog_backend.h new file mode 100644 index 00000000..644b2475 --- /dev/null +++ b/score/mw/log/detail/syslog/syslog_backend.h @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_LOG_DETAIL_SYSLOG_SYSLOG_BACKEND_H +#define SCORE_MW_LOG_DETAIL_SYSLOG_SYSLOG_BACKEND_H + +#include "score/os/syslog.h" +#include "score/mw/log/detail/backend.h" +#include "score/mw/log/detail/circular_allocator.h" + +#include +#include +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ + +class SyslogBackend final : public Backend +{ + public: + explicit SyslogBackend(const std::size_t number_of_slots, + const LogRecord& initial_slot_value, + const std::string_view app_id, + score::cpp::pmr::unique_ptr syslog_instance) noexcept; + + score::cpp::optional ReserveSlot() noexcept override; + void FlushSlot(const SlotHandle& slot) noexcept override; + LogRecord& GetLogRecord(const SlotHandle& slot) noexcept override; + + private: + void Init() noexcept; + + std::string app_id_; + CircularAllocator buffer_; + score::cpp::pmr::unique_ptr syslog_instance_; +}; + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score + +#endif // SCORE_MW_LOG_DETAIL_SYSLOG_SYSLOG_BACKEND_H diff --git a/score/mw/log/detail/syslog/syslog_backend_test.cpp b/score/mw/log/detail/syslog/syslog_backend_test.cpp new file mode 100644 index 00000000..827d7cb7 --- /dev/null +++ b/score/mw/log/detail/syslog/syslog_backend_test.cpp @@ -0,0 +1,289 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "gtest/gtest.h" + +#include "score/os/mocklib/mock_syslog.h" +#include "score/mw/log/configuration/configuration.h" +#include "score/mw/log/detail/syslog/syslog_backend.h" + +#include "score/assert_support.hpp" + +#include + +#include + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ +namespace +{ + +using ::testing::_; +using ::testing::StrEq; + +const std::string kDefaultApp{"a1"}; +const std::string kDefaultContext{"c1"}; +const std::string kDefaultMessage{"default message"}; + +struct SyslogBackendFixture : ::testing::Test +{ + void SetUp() override + { + syslog_mock_ = score::cpp::pmr::make_unique(score::cpp::pmr::get_default_resource()); + syslog_mock_raw_ptr_ = syslog_mock_.get(); + } + + protected: + void SimulateLogging(LogLevel log_level, + const std::string& app_id = kDefaultApp, + const std::string& ctx_id = kDefaultContext, + const std::string& message = kDefaultMessage) + { + SyslogBackend backend(config_.GetNumberOfSlots(), log_record_, app_id, std::move(syslog_mock_)); + + auto slot = backend.ReserveSlot(); + EXPECT_TRUE(slot.has_value()); + + auto& payload = backend.GetLogRecord(slot.value()); + auto& log_entry = payload.GetLogEntry(); + log_entry.ctx_id = LoggingIdentifier(std::string_view(ctx_id)); + log_entry.log_level = log_level; + log_entry.payload = ByteVector(message.begin(), message.end()); + + backend.FlushSlot(slot.value()); + } + + LogRecord log_record_{}; + Configuration config_{}; + score::cpp::pmr::unique_ptr syslog_mock_{}; + score::os::MockSyslog* syslog_mock_raw_ptr_; +}; + +TEST_F(SyslogBackendFixture, SyslogOpenlog) +{ + RecordProperty("Description", "Verifies the backend opens the syslog connection on construction."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)); + + SyslogBackend unit(config_.GetNumberOfSlots(), log_record_, config_.GetAppId(), std::move(syslog_mock_)); +} + +TEST_F(SyslogBackendFixture, SyslogOpenlogWithCapacityBiggerThanTheMaximum) +{ + RecordProperty("Description", "Verifies backend construction with slots' capacity bigger than the maximum."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + auto capacity = std::numeric_limits::max() + 1; + config_.SetNumberOfSlots(capacity); + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)); + + SyslogBackend unit(config_.GetNumberOfSlots(), log_record_, config_.GetAppId(), std::move(syslog_mock_)); +} + +TEST_F(SyslogBackendFixture, ReserveSlotShouldAcquireSlot) +{ + RecordProperty("Description", "Verifies the ability of reserving slot."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)); + + SyslogBackend unit(config_.GetNumberOfSlots(), log_record_, config_.GetAppId(), std::move(syslog_mock_)); + + auto slot = unit.ReserveSlot(); + EXPECT_TRUE(slot.has_value()); +} + +TEST_F(SyslogBackendFixture, LevelOffProducesNoEmit) +{ + RecordProperty("Description", "A kOff level shall not be emitted to syslog."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(_, _)).Times(0); + + SimulateLogging(LogLevel::kOff); +} + +TEST_F(SyslogBackendFixture, FatalLog) +{ + RecordProperty("Description", "Verifies the ability of logging fatal message."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_CRIT, _)).Times(1); + + SimulateLogging(LogLevel::kFatal); +} + +TEST_F(SyslogBackendFixture, ErrorLog) +{ + RecordProperty("Description", "Verifies the ability of logging error message."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_ERR, _)).Times(1); + + SimulateLogging(LogLevel::kError); +} + +TEST_F(SyslogBackendFixture, WarningLog) +{ + RecordProperty("Description", "Verifies the ability of logging warning message."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_WARNING, _)).Times(1); + + SimulateLogging(LogLevel::kWarn); +} + +TEST_F(SyslogBackendFixture, InfoLog) +{ + RecordProperty("Description", "Verifies the ability of logging info message."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_INFO, _)).Times(1); + + SimulateLogging(LogLevel::kInfo); +} + +TEST_F(SyslogBackendFixture, DebugLog) +{ + RecordProperty("Description", "Verifies the ability of logging debug message."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_DEBUG, _)).Times(1); + + SimulateLogging(LogLevel::kDebug); +} + +TEST_F(SyslogBackendFixture, VerboseLog) +{ + RecordProperty("Description", "Verifies verbose maps to LOG_DEBUG (syslog has no separate verbose level)."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_DEBUG, _)).Times(1); + + SimulateLogging(LogLevel::kVerbose); +} + +TEST_F(SyslogBackendFixture, MessageShouldContainAppCtxPayload) +{ + RecordProperty("Description", "Verifies log message contains application id, context id and payload."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_DEBUG, StrEq("MyAp,MyCt: Hello World"))).Times(1); + + SimulateLogging(LogLevel::kVerbose, "MyAp", "MyCt", "Hello World"); +} + +TEST_F(SyslogBackendFixture, BackendShouldHandleEmptyPayload) +{ + RecordProperty("Description", "Verifies the ability of the backend of handling empty payload."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_DEBUG, StrEq(",: "))).Times(1); + + SimulateLogging(LogLevel::kVerbose, "", "", ""); +} + +TEST_F(SyslogBackendFixture, LongIdentifiersShouldBeCropped) +{ + RecordProperty("Description", + "Verifies that the application or context IDs should be cropped if it exceeds 4 characters length."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(LOG_DEBUG, StrEq("1234,4567: "))).Times(1); + + SimulateLogging(LogLevel::kVerbose, "12345", "45678", ""); +} + +TEST_F(SyslogBackendFixture, NoSlotAvailableShouldReturnEmptyHandle) +{ + RecordProperty("Description", "Verifies returning empty handler in case of no available slots."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + SyslogBackend backend(config_.GetNumberOfSlots(), log_record_, config_.GetAppId(), std::move(syslog_mock_)); + + for (std::size_t i = 0; i < config_.GetNumberOfSlots(); ++i) + { + EXPECT_TRUE(backend.ReserveSlot().has_value()); + } + + EXPECT_FALSE(backend.ReserveSlot().has_value()); +} + +TEST_F(SyslogBackendFixture, TooMuchSlotsRequestedShallBeTruncated) +{ + RecordProperty("Description", "Verifies requesting too much slots shall be truncated."); + RecordProperty("TestingTechnique", "Requirements-based test"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + const auto kMaxSlotCount = std::numeric_limits::max(); + const std::size_t kSlotNumberOverflow = static_cast(kMaxSlotCount) + 2UL; + + SyslogBackend backend(kSlotNumberOverflow, log_record_, config_.GetAppId(), std::move(syslog_mock_)); + + for (std::size_t i = 0; i < kMaxSlotCount; ++i) + { + EXPECT_TRUE(backend.ReserveSlot().has_value()); + } + + EXPECT_FALSE(backend.ReserveSlot().has_value()); +} + +TEST_F(SyslogBackendFixture, ToSyslogPriorityInvalidLevel) +{ + RecordProperty("Description", "Tests ToSyslogPriority with an invalid log level, which must not be emitted."); + RecordProperty("TestingTechnique", "Boundary value analysis"); + RecordProperty("DerivationTechnique", "Analysis of requirements"); + + EXPECT_CALL(*syslog_mock_raw_ptr_, openlog(_, _, _)).Times(1); + EXPECT_CALL(*syslog_mock_raw_ptr_, MockedSyslog(_, _)).Times(0); + + // Pass a log level greater than GetMaxLogLevelValue() to trigger the `else` branch (kInvalid, no emit). + LogLevel invalid_log_level = static_cast(static_cast(LogLevel::kVerbose) + 1); + SimulateLogging(invalid_log_level); +} + +} // namespace +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score diff --git a/score/mw/log/detail/syslog/syslog_recorder_factory.cpp b/score/mw/log/detail/syslog/syslog_recorder_factory.cpp new file mode 100644 index 00000000..1f06ffb1 --- /dev/null +++ b/score/mw/log/detail/syslog/syslog_recorder_factory.cpp @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/mw/log/detail/syslog/syslog_recorder_factory.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ +std::unique_ptr SyslogRecorderFactory::CreateConcreteLogRecorder( + const Configuration& config, + score::cpp::pmr::memory_resource* memory_resource) +{ + auto backend = CreateSystemBackend(config, memory_resource); // LCOV_EXCL_LINE : no branches to test + constexpr bool kCheckLogLevelForConsole = false; + return std::make_unique(config, std::move(backend), kCheckLogLevelForConsole); +} + +std::unique_ptr SyslogRecorderFactory::CreateSystemBackend(const Configuration& config, + score::cpp::pmr::memory_resource* memory_resource) +{ + return std::make_unique(config.GetNumberOfSlots(), + LogRecord{config.GetSlotSizeInBytes()}, + config.GetAppId(), + score::os::Syslog::Default(memory_resource)); +} + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score diff --git a/score/mw/log/detail/syslog/syslog_recorder_factory.h b/score/mw/log/detail/syslog/syslog_recorder_factory.h new file mode 100644 index 00000000..4a68ecac --- /dev/null +++ b/score/mw/log/detail/syslog/syslog_recorder_factory.h @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#ifndef SCORE_MW_LOG_DETAIL_SYSLOG_SYSLOG_RECORDER_FACTORY_H +#define SCORE_MW_LOG_DETAIL_SYSLOG_SYSLOG_RECORDER_FACTORY_H + +#include "score/mw/log/detail/log_recorder_factory.hpp" +#include "score/mw/log/detail/syslog/syslog_backend.h" +#include "score/mw/log/detail/text_recorder/text_recorder.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ +class SyslogRecorderFactory : public LogRecorderFactory +{ + public: + std::unique_ptr CreateConcreteLogRecorder(const Configuration& config, + score::cpp::pmr::memory_resource* memory_resource); + + private: + std::unique_ptr CreateSystemBackend(const Configuration& config, + score::cpp::pmr::memory_resource* memory_resource); +}; + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score + +#endif // SCORE_MW_LOG_DETAIL_SYSLOG_SYSLOG_RECORDER_FACTORY_H diff --git a/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp b/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp new file mode 100644 index 00000000..0030ed3e --- /dev/null +++ b/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "gtest/gtest.h" + +#include "score/mw/log/detail/syslog/syslog_recorder_factory.h" + +namespace score +{ +namespace mw +{ +namespace log +{ +namespace detail +{ + +template +bool IsRecorderOfType(const std::unique_ptr& recorder) noexcept +{ + static_assert(std::is_base_of::value, + "Concrete recorder shall be derived from Recorder base class"); + + return dynamic_cast(recorder.get()) != nullptr; +} + +TEST(SyslogRecorderFactoryTest, CreateRecorder) +{ + Configuration config; + score::cpp::pmr::memory_resource* memory_resource = score::cpp::pmr::get_default_resource(); + + auto recorder = SyslogRecorderFactory{}.CreateConcreteLogRecorder(config, memory_resource); + + // Syslog uses TextRecorder + EXPECT_TRUE(IsRecorderOfType(recorder)); +} + +} // namespace detail +} // namespace log +} // namespace mw +} // namespace score From 7be39eac50018d5a6db2144cad92f57b2b714d60 Mon Sep 17 00:00:00 2001 From: Bharath Tirunagaru Date: Mon, 24 Aug 2026 11:02:21 -0700 Subject: [PATCH 2/3] Fix review comments - Set copyright year to 2026 on all newly added files, matching the companion fix in the baselibs syslog wrapper PR. Signed-off-by: Bharath Tirunagaru --- score/mw/log/detail/syslog/BUILD | 2 +- score/mw/log/detail/syslog/syslog_backend.cpp | 2 +- score/mw/log/detail/syslog/syslog_backend.h | 2 +- score/mw/log/detail/syslog/syslog_backend_test.cpp | 2 +- score/mw/log/detail/syslog/syslog_recorder_factory.cpp | 2 +- score/mw/log/detail/syslog/syslog_recorder_factory.h | 2 +- score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/score/mw/log/detail/syslog/BUILD b/score/mw/log/detail/syslog/BUILD index 24eb8f47..5e259f30 100644 --- a/score/mw/log/detail/syslog/BUILD +++ b/score/mw/log/detail/syslog/BUILD @@ -1,5 +1,5 @@ # ******************************************************************************* -# Copyright (c) 2025 Contributors to the Eclipse Foundation +# Copyright (c) 2026 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. diff --git a/score/mw/log/detail/syslog/syslog_backend.cpp b/score/mw/log/detail/syslog/syslog_backend.cpp index 2367bafd..ca32f6b7 100644 --- a/score/mw/log/detail/syslog/syslog_backend.cpp +++ b/score/mw/log/detail/syslog/syslog_backend.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Contributors to the Eclipse Foundation + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/score/mw/log/detail/syslog/syslog_backend.h b/score/mw/log/detail/syslog/syslog_backend.h index 644b2475..3e8a1441 100644 --- a/score/mw/log/detail/syslog/syslog_backend.h +++ b/score/mw/log/detail/syslog/syslog_backend.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Contributors to the Eclipse Foundation + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/score/mw/log/detail/syslog/syslog_backend_test.cpp b/score/mw/log/detail/syslog/syslog_backend_test.cpp index 827d7cb7..c9528183 100644 --- a/score/mw/log/detail/syslog/syslog_backend_test.cpp +++ b/score/mw/log/detail/syslog/syslog_backend_test.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Contributors to the Eclipse Foundation + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/score/mw/log/detail/syslog/syslog_recorder_factory.cpp b/score/mw/log/detail/syslog/syslog_recorder_factory.cpp index 1f06ffb1..0f21cb0b 100644 --- a/score/mw/log/detail/syslog/syslog_recorder_factory.cpp +++ b/score/mw/log/detail/syslog/syslog_recorder_factory.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Contributors to the Eclipse Foundation + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/score/mw/log/detail/syslog/syslog_recorder_factory.h b/score/mw/log/detail/syslog/syslog_recorder_factory.h index 4a68ecac..14db4339 100644 --- a/score/mw/log/detail/syslog/syslog_recorder_factory.h +++ b/score/mw/log/detail/syslog/syslog_recorder_factory.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Contributors to the Eclipse Foundation + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp b/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp index 0030ed3e..5dff9bd5 100644 --- a/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp +++ b/score/mw/log/detail/syslog/syslog_recorder_factory_test.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2025 Contributors to the Eclipse Foundation + * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. From 2575bbe09b04f81fd75469c03a849db009664e17 Mon Sep 17 00:00:00 2001 From: Bharath Tirunagaru Date: Mon, 24 Aug 2026 11:25:16 -0700 Subject: [PATCH 3/3] Add component requirements and diagrams for Linux syslog backend Addresses review feedback requesting requirements and static/dynamic diagrams for the new SyslogBackend before source-level review: - Extend comp_req__log__forward_to_system_logger with a Note covering the Linux implementation (syslog(3)), mirroring the existing QNX slogger2 note. No new requirement was needed since the existing System Backend requirements are already platform-generic. - Add SyslogBackend to the shared backend class diagram (mw_log_recorders.puml), matching how SlogBackend is represented. - Add a sequence diagram (syslog_backend_sequence.puml) showing a log call from LogStream construction through to the syslog(3) call. - Add a detailed-design page (syslog_backend.md) describing the backend and embedding both diagrams, following the same structure as file_output_backend.md, and link it into the docs via the same symlink + toctree convention used for that page. - Register the new files in the backend BUILD's architectural_design target. Signed-off-by: Bharath Tirunagaru --- .../mw_log/detailed_design/index.rst | 1 + .../mw_log/detailed_design/syslog_backend.md | 1 + docs/components/mw_log/requirements/index.rst | 4 +- score/mw/log/design/backend/BUILD | 2 + .../log/design/backend/mw_log_recorders.puml | 16 +++ score/mw/log/design/backend/syslog_backend.md | 33 ++++++ .../backend/syslog_backend_sequence.puml | 100 ++++++++++++++++++ 7 files changed, 156 insertions(+), 1 deletion(-) create mode 120000 docs/components/mw_log/detailed_design/syslog_backend.md create mode 100644 score/mw/log/design/backend/syslog_backend.md create mode 100644 score/mw/log/design/backend/syslog_backend_sequence.puml diff --git a/docs/components/mw_log/detailed_design/index.rst b/docs/components/mw_log/detailed_design/index.rst index ac686185..c7d2615f 100644 --- a/docs/components/mw_log/detailed_design/index.rst +++ b/docs/components/mw_log/detailed_design/index.rst @@ -9,4 +9,5 @@ The backend composition and recorder relationships are shown below: :maxdepth: 1 file_output_backend + syslog_backend datarouter_backend/README diff --git a/docs/components/mw_log/detailed_design/syslog_backend.md b/docs/components/mw_log/detailed_design/syslog_backend.md new file mode 120000 index 00000000..66846d22 --- /dev/null +++ b/docs/components/mw_log/detailed_design/syslog_backend.md @@ -0,0 +1 @@ +../../../../score/mw/log/design/backend/syslog_backend.md \ No newline at end of file diff --git a/docs/components/mw_log/requirements/index.rst b/docs/components/mw_log/requirements/index.rst index bf038458..97dd4f2c 100644 --- a/docs/components/mw_log/requirements/index.rst +++ b/docs/components/mw_log/requirements/index.rst @@ -153,7 +153,7 @@ System Backend .. comp_req:: Forward to System Logger :id: comp_req__log__forward_to_system_logger - :version: 1 + :version: 2 :reqtype: Functional :security: NO :safety: QM @@ -165,6 +165,8 @@ System Backend Note: Under QNX, slogger2 shall be used. + Note: Under Linux, syslog(3) shall be used. + .. comp_req:: System Backend Activation :id: comp_req__log__system_backend_activation :version: 1 diff --git a/score/mw/log/design/backend/BUILD b/score/mw/log/design/backend/BUILD index b420f289..fb033bbe 100644 --- a/score/mw/log/design/backend/BUILD +++ b/score/mw/log/design/backend/BUILD @@ -24,6 +24,7 @@ architectural_design( "datarouter_backend/shared_memory_reader_read.puml", "datarouter_backend/shared_memory_writer_allocandwrite.puml", "datarouter_backend/verbose_logging_sequence.puml", + "syslog_backend_sequence.puml", ], static = [ "datarouter_backend/README.md", @@ -36,6 +37,7 @@ architectural_design( "file_output_backend.md", "mw_log_file_backend.puml", "mw_log_recorders.puml", + "syslog_backend.md", ], visibility = ["//visibility:public"], ) diff --git a/score/mw/log/design/backend/mw_log_recorders.puml b/score/mw/log/design/backend/mw_log_recorders.puml index 7a3959d2..a7d9e5c7 100644 --- a/score/mw/log/design/backend/mw_log_recorders.puml +++ b/score/mw/log/design/backend/mw_log_recorders.puml @@ -82,6 +82,19 @@ class "mw::log::detail::SlogBackend" as SlogBackend { - Init(verbosity: std::uint8_t) : void } +class "mw::log::detail::SyslogBackend" as SyslogBackend { + - app_id_: std::string + - buffer_: CircularAllocator + - syslog_instance_: score::cpp::pmr::unique_ptr + __ + + SyslogBackend(const std::size_t,\n const LogRecord&,\n const std::string_view,\n score::cpp::pmr::unique_ptr) + + ReserveSlot(): score::cpp::optional + + FlushSlot(const SlotHandle&): void + + GetLogRecord(const SlotHandle&): LogRecord& + __ + - Init(): void +} + class "mw::log::detail::DataRouterBackend" as DataRouterBackend { } @@ -133,6 +146,7 @@ RecorderMock .up.|> Recorder ' Relationships - Backend interface FileOutputBackend .up.|> Backend SlogBackend .up.|> Backend +SyslogBackend .up.|> Backend DataRouterBackend .up.|> Backend BackendMock .up.|> Backend @@ -153,6 +167,8 @@ TextRecorder ..> DLTFormat : uses ' Backend composition SlogBackend *-- CircularAllocator SlogBackend --> LogRecord : uses +SyslogBackend *-- CircularAllocator +SyslogBackend --> LogRecord : uses ' External dependencies FileRecorder ..> fcntl : open\nSetNonBlocking / fctrl call diff --git a/score/mw/log/design/backend/syslog_backend.md b/score/mw/log/design/backend/syslog_backend.md new file mode 100644 index 00000000..e300283e --- /dev/null +++ b/score/mw/log/design/backend/syslog_backend.md @@ -0,0 +1,33 @@ +# SyslogBackend + +`SyslogBackend` implements `mw::log::detail::Backend` and is the Linux counterpart +of the QNX `SlogBackend`: it is selected by `SyslogRecorderFactory` when the +configured log mode contains `kSystem` and the target platform is Linux +(`target_compatible_with = ["@platforms//os:linux"]`), wrapping it in a +`TextRecorder` the same way `SlogBackend` is wrapped on QNX. + +Like `SlogBackend`, it stores `LogRecord`s in a `CircularAllocator` between +`ReserveSlot()` and `FlushSlot()`. `ReserveSlot()` and `GetLogRecord()` only +hand out and look up slots in that buffer; no data leaves the process until +`FlushSlot()` is called. + +`FlushSlot()` converts the `LogRecord`'s `LogLevel` to a syslog(3) priority via +`ConvertMwLogLevelToSyslogPriority()` and forwards the record's app ID, context +ID and payload to the injected `score::os::Syslog` seam as a single +`syslog(priority, "%.*s,%.*s: %.*s", ...)` call, which glibc delivers through +`vsyslog(3)`. `LogLevel::kOff` (and any out-of-range level) maps to +`SyslogPriority::kInvalid` and is dropped instead of being forwarded, mirroring +`SlogBackend`'s handling of its own invalid level. + +The `score::os::Syslog` seam is opened once, in the constructor's `Init()`, via +`openlog(app_id, LOG_PID | LOG_NDELAY, LOG_USER)`; the object-seam wrapper +(`Syslog` interface / `SyslogImpl` / `MockSyslog`) is what makes `SyslogBackend` +host-unit-testable without a real syslog daemon. + +MW_LOG_RECORDERS + +The sequence below shows a single log call from `LogStream` construction +through to the `syslog(3)` call made when the stream is destroyed and the slot +is flushed: + +SyslogBackendSequenceDesign diff --git a/score/mw/log/design/backend/syslog_backend_sequence.puml b/score/mw/log/design/backend/syslog_backend_sequence.puml new file mode 100644 index 00000000..bc89a47e --- /dev/null +++ b/score/mw/log/design/backend/syslog_backend_sequence.puml @@ -0,0 +1,100 @@ +@startuml syslog_backend_sequence + +participant "_:App_" as App +participant "FreeFunctions" as FreeFunctions +participant "LogStreamFactory" as LogStreamFactory +participant "Runtime" as Runtime +participant "_:LogStream_" as LogStream +participant "_:TextRecorder_" as TextRecorder +participant "_:SyslogBackend_" as SyslogBackend +participant "score::os::Syslog" as Syslog + +activate App + +App -> FreeFunctions : score::mw::log::Info() + +FreeFunctions -> LogStreamFactory : GetStream(LogLevel) + +activate LogStreamFactory + +LogStreamFactory -> Runtime : GetRecorder() +Runtime -> TextRecorder : creates +LogStreamFactory <-- Runtime : Recorder + +LogStreamFactory -> LogStream : construct + +activate LogStream + +LogStream -> TextRecorder : StartRecord(ctx, LogLevel) +activate TextRecorder + +TextRecorder -> SyslogBackend : ReserveSlot() +activate SyslogBackend +TextRecorder <-- SyslogBackend : score::cpp::optional +deactivate SyslogBackend + +LogStream <-- TextRecorder : score::cpp::optional +deactivate TextRecorder + +LogStreamFactory <-- LogStream : LogStream +deactivate LogStream + +FreeFunctions <-- LogStreamFactory : LogStream +deactivate LogStreamFactory + +App <-- FreeFunctions : LogStream + +App -> LogStream : << Some Data + +activate LogStream + +LogStream -> TextRecorder : Log(SlotHandle, Data) +activate TextRecorder + +TextRecorder -> SyslogBackend : GetLogRecord(SlotHandle) +activate SyslogBackend +TextRecorder <-- SyslogBackend : LogRecord& +deactivate SyslogBackend + +TextRecorder -> TextRecorder : format payload\ninto LogRecord + +LogStream <-- TextRecorder +deactivate TextRecorder + +App <-- LogStream : LogStream +deactivate LogStream + +App -> LogStream : Destruct + +activate LogStream + +LogStream -> TextRecorder : StopRecord(SlotHandle) +activate TextRecorder + +TextRecorder -> SyslogBackend : FlushSlot(SlotHandle) +activate SyslogBackend + +SyslogBackend -> Syslog : syslog(priority, "%.*s,%.*s: %.*s",\n app_id, ctx_id, payload) + +Syslog -> Syslog : glibc vsyslog(3) + +TextRecorder <-- SyslogBackend +deactivate SyslogBackend + +LogStream <-- TextRecorder +deactivate TextRecorder + +destroy LogStream + +App <-- LogStream + +deactivate App + +note right of Syslog #red + Priority is derived from LogLevel via + ConvertMwLogLevelToSyslogPriority(). LogLevel::kOff + (and any out-of-range level) maps to kInvalid and is + not forwarded to syslog(3). +end note + +@enduml