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
8 changes: 8 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ python.toolchain(
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rapidjson", version = "1.1.0.bcr.20241007")
bazel_dep(name = "score_baselibs", version = "0.2.13")

# Temporary cross-PR dependency; remove after the baselibs syslog change is released.
git_override(
module_name = "score_baselibs",
commit = "7268b8f342e4be81138ee2e77e9ea1d9681c0dfa",
remote = "https://github.com/qualcomm/baselibs.git",
)

bazel_dep(name = "score_communication", version = "0.4.0")

# Rust dependencies
Expand Down
91 changes: 43 additions & 48 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/components/mw_log/detailed_design/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The backend composition and recorder relationships are shown below:
:maxdepth: 1

file_output_backend
syslog_backend
datarouter_backend/index


Expand Down
1 change: 1 addition & 0 deletions docs/components/mw_log/detailed_design/syslog_backend.md
4 changes: 3 additions & 1 deletion docs/components/mw_log/requirements/requirements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,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
Expand All @@ -144,6 +144,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
Expand Down
2 changes: 1 addition & 1 deletion score/datarouter/error/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ cc_library(
features = COMPILER_WARNING_FEATURES,
visibility = ["@score_logging//score/datarouter:__subpackages__"],
deps = [
"@score_baselibs//score/memory:string_literal",
"@score_baselibs//score/result",
"@score_baselibs//score/string_manipulation:string_literal",
],
)
16 changes: 16 additions & 0 deletions score/mw/log/backend/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
62 changes: 62 additions & 0 deletions score/mw/log/backend/syslog_registrant.cpp
Original file line number Diff line number Diff line change
@@ -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<Recorder> 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
2 changes: 2 additions & 0 deletions score/mw/log/design/backend/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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/index.md",
Expand All @@ -35,6 +36,7 @@ architectural_design(
"file_output_backend.md",
"mw_log_file_backend.puml",
"mw_log_recorders.puml",
"syslog_backend.md",
],
# score_tooling 2.x parses every .puml included by this target.
visibility = ["//visibility:public"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ package "Backend: Logging" as backend_logging {
component "Runtime" as Runtime <<unit>>
component "LogStream" as LogStream <<unit>>
component "DataRouterRecorder" as DataRouterRecorder <<unit>>
component "TextRecorder" as TextRecorder <<unit>>
component "SyslogBackend" as SyslogBackend <<unit>>
component "Syslog" as Syslog <<unit>>
component "DLTFormater" as DLTFormater <<unit>>
component "DataRouterBackend" as DataRouterBackend <<unit>>
component "TRACE" as TRACE <<unit>>
Expand Down Expand Up @@ -62,6 +65,10 @@ interface "IDataRouterBackendMessageClientFactory" as IDataRouterBackendMessageC
interface "IDataRouterBackendMessageClient" as IDataRouterBackendMessageClient
interface "IRuntimeDataRouterRecorder" as IRuntimeDataRouterRecorder
interface "IAppLogStream" as IAppLogStream
interface "IRuntimeTextRecorder" as IRuntimeTextRecorder
interface "ILogStreamTextRecorder" as ILogStreamTextRecorder
interface "ITextRecorderSyslogBackend" as ITextRecorderSyslogBackend
interface "ISyslogBackendSyslog" as ISyslogBackendSyslog
interface "IClientSignalHandling" as IClientSignalHandling
interface "IClientSender" as IClientSender
interface "IClientReceiver" as IClientReceiver
Expand Down Expand Up @@ -114,6 +121,18 @@ MessageClient )- IDataRouterBackendMessageClient
Runtime -( IRuntimeDataRouterRecorder
DataRouterRecorder )- IRuntimeDataRouterRecorder

Runtime -( IRuntimeTextRecorder
TextRecorder )- IRuntimeTextRecorder

LogStream -( ILogStreamTextRecorder
TextRecorder )- ILogStreamTextRecorder

TextRecorder -( ITextRecorderSyslogBackend
SyslogBackend )- ITextRecorderSyslogBackend

SyslogBackend -( ISyslogBackendSyslog
Syslog )- ISyslogBackendSyslog

App -( IAppLogStream
LogStream )- IAppLogStream

Expand Down
16 changes: 16 additions & 0 deletions score/mw/log/design/backend/mw_log_recorders.puml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,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<mw::log::detail::LogRecord>
- syslog_instance_: score::cpp::pmr::unique_ptr<score::os::Syslog>
__
+ SyslogBackend(const std::size_t,\n const LogRecord&,\n const std::string_view,\n score::cpp::pmr::unique_ptr<score::os::Syslog>)
+ ReserveSlot(): score::cpp::optional<SlotHandle>
+ FlushSlot(const SlotHandle&): void
+ GetLogRecord(const SlotHandle&): LogRecord&
__
- Init(): void
}

class "mw::log::detail::DataRouterBackend" as DataRouterBackend {
}

Expand Down Expand Up @@ -146,6 +159,7 @@ RecorderMock .up.|> Recorder
' Relationships - Backend interface
FileOutputBackend .up.|> Backend
SlogBackend .up.|> Backend
SyslogBackend .up.|> Backend
DataRouterBackend .up.|> Backend
BackendMock .up.|> Backend

Expand All @@ -166,6 +180,8 @@ TextRecorder ..> DLTFormat : uses
' Backend composition
SlogBackend *-- CircularAllocator
SlogBackend --> LogRecord : uses
SyslogBackend *-- CircularAllocator
SyslogBackend --> LogRecord : uses

' External dependencies
FileRecorder ..> fcntl : open\nSetNonBlocking / fctrl call
Expand Down
45 changes: 45 additions & 0 deletions score/mw/log/design/backend/syslog_backend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!-- ----------------------------------------------------------------------------
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
----------------------------------------------------------------------------- -->
# 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.

<img alt="MW_LOG_RECORDERS" src="https://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/eclipse-score/logging/refs/heads/main/score/mw/log/design/backend/mw_log_recorders.puml">

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:

<img alt="SyslogBackendSequenceDesign" src="https://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/eclipse-score/logging/refs/heads/main/score/mw/log/design/backend/syslog_backend_sequence.puml">
110 changes: 110 additions & 0 deletions score/mw/log/design/backend/syslog_backend_sequence.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
' *******************************************************************************
' 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
' *******************************************************************************
@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<SlotHandle>
deactivate SyslogBackend

LogStream <-- TextRecorder : score::cpp::optional<SlotHandle>
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

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
Loading
Loading