diff --git a/SilKit/source/config/Test_YamlParser.cpp b/SilKit/source/config/Test_YamlParser.cpp index 5be69f932..0b3e0da7c 100644 --- a/SilKit/source/config/Test_YamlParser.cpp +++ b/SilKit/source/config/Test_YamlParser.cpp @@ -237,6 +237,8 @@ TEST_F(Test_YamlParser, yaml_complete_configuration) EXPECT_EQ(config.logging.sinks.at(0).level, SilKit::Services::Logging::Level::Critical); EXPECT_EQ(config.logging.sinks.at(0).logName, "MyLog1"); + EXPECT_EQ(config.logging.sinks.at(0).format, Sink::Format::Json); + EXPECT_EQ(config.healthCheck.softResponseTimeout.value(), 500ms); EXPECT_EQ(config.healthCheck.hardResponseTimeout.value(), 5000ms); @@ -263,6 +265,39 @@ TEST_F(Test_YamlParser, yaml_complete_configuration) ASSERT_FALSE(config.middleware.registryAsFallbackProxy); } +TEST_F(Test_YamlParser, yaml_file_sink_defaults_to_json_format) +{ + auto config = Deserialize(R"( +Logging: + Sinks: + - Type: File + LogName: MyLog1 +)"); + ASSERT_EQ(config.logging.sinks.size(), 1u); + EXPECT_EQ(config.logging.sinks.at(0).type, Sink::Type::File); + EXPECT_EQ(config.logging.sinks.at(0).format, Sink::Format::Json); + + auto configExplicit = Deserialize(R"( +Logging: + Sinks: + - Type: File + Format: Simple + LogName: MyLog1 +)"); + ASSERT_EQ(configExplicit.logging.sinks.size(), 1u); + EXPECT_EQ(configExplicit.logging.sinks.at(0).type, Sink::Type::File); + EXPECT_EQ(configExplicit.logging.sinks.at(0).format, Sink::Format::Simple); + + auto configStdout = Deserialize(R"( +Logging: + Sinks: + - Type: Stdout +)"); + ASSERT_EQ(configStdout.logging.sinks.size(), 1u); + EXPECT_EQ(configStdout.logging.sinks.at(0).type, Sink::Type::Stdout); + EXPECT_EQ(configStdout.logging.sinks.at(0).format, Sink::Format::Simple); +} + TEST_F(Test_YamlParser, yaml_elements_in_random_order) { auto config = Deserialize(R"( @@ -334,10 +369,12 @@ TEST_F(Test_YamlParser, yaml_native_type_conversions) Sink sink; logger.logFromRemotes = true; sink.type = Sink::Type::File; + sink.format = Sink::Format::Json; sink.level = SilKit::Services::Logging::Level::Trace; sink.logName = "filename"; logger.sinks.push_back(sink); sink.type = Sink::Type::Stdout; + sink.format = Sink::Format::Simple; sink.logName = ""; logger.sinks.push_back(sink); auto txt = Serialize(logger); diff --git a/SilKit/source/config/YamlReader.cpp b/SilKit/source/config/YamlReader.cpp index e6c24cbe5..b7dd7f83a 100644 --- a/SilKit/source/config/YamlReader.cpp +++ b/SilKit/source/config/YamlReader.cpp @@ -215,6 +215,12 @@ void YamlReader::Read(SilKit::Config::Sink& obj) { OptionalRead(obj.type, "Type"); OptionalRead(obj.level, "Level"); + + // A File sink defaults to the Json format. An explicitly configured Format takes precedence. + if (obj.type == SilKit::Config::Sink::Type::File) + { + obj.format = SilKit::Config::Sink::Format::Json; + } OptionalRead(obj.format, "Format"); auto&& child = GetChildSafe("Experimental"); diff --git a/docs/changelog/versions/latest.md b/docs/changelog/versions/latest.md index f349af95a..c6d28a47b 100644 --- a/docs/changelog/versions/latest.md +++ b/docs/changelog/versions/latest.md @@ -1,3 +1,6 @@ # [5.0.7] - UNRELEASED -> This changelog entry is still empty. \ No newline at end of file +## Changed + +- `config`: default format of file logging is set to JSON. Set "Format: Simple" in the sink to get previous behaviour. +- `docs`: added description of the logging "Format". \ No newline at end of file diff --git a/docs/configuration/logging-configuration.rst b/docs/configuration/logging-configuration.rst index 4463ba0c6..6e53e6c21 100644 --- a/docs/configuration/logging-configuration.rst +++ b/docs/configuration/logging-configuration.rst @@ -12,16 +12,20 @@ Logging Configuration Overview ======================================== -Within the SIL Kit, the Logger provides features for local and distributed logging on different log -levels. +Within the SIL Kit, the Logger provides features for local and distributed logging on different log levels. + +- Logging output is configured through **sinks**, where each sink defines where messages are written (for example *Stdout*, *File*, or *Remote*) and which minimum log level is emitted. + +- Log messages can be classified by **topics** (for example middleware internals, service controllers, or user-level logging). Topic-based filtering can be configured per sink to include or exclude specific topics. + +- For sinks of type *Stdout* and *File*, the output **format** can be selected as *Simple* (human-readable text) or *Json* (structured one-object-per-line output). Configuration ======================================== The Logging configuration allows to configure the logging behavior of the simulation participant. Within the SIL Kit, the Logger uses so-called sinks to store log messages. -Multiple sinks can be configured at the same time. For example, to send log -messages with log level ``Debug`` or higher to a remote logger and write ``Trace`` level -logs to a file, the following configuration could be used: +Multiple sinks can be configured at the same time. +For example, to send log messages with log level ``Debug`` or higher to a remote logger and write ``Trace`` level logs to a file, the following configuration could be used: .. code-block:: yaml @@ -70,13 +74,22 @@ logs to a file, the following configuration could be used: *Remote* send the log messages over the underlying middleware. Note that this can result in a significant amount of traffic, which can impact the simulation performance, in particular when using a low log level. + * - Format + - The output format used for log messages written by the sink. Valid + options are *Simple* and *Json* (see + :ref:`Log message format`). Applies to + sinks of type *Stdout* and *File*. If not set, sinks of type *File* + default to *Json*, while all other sinks default to *Simple*. * - Level - The minimum log level of a message to be logged by the sink. All messages with a lower log level are ignored. Valid options are *Critical*, *Error*, *Warn*, *Info*, *Debug*, *Trace*, and *Off*. * - LogName - - The filename used by sinks of type *File*. The - resulting filename is ``__.txt``. + - The filename used by sinks of type *File*. The resulting filename is + ``__.txt`` for the + *Simple* format and + ``__.jsonl`` for the + *Json* format. * - Experimental: EnabledTopics - Optional allow-list of logging topics for this sink. If this list is non-empty, only messages with topics from this list are written to the sink. @@ -84,6 +97,55 @@ logs to a file, the following configuration could be used: - Optional block-list of logging topics for this sink. Disabled topics are always filtered out, even if they are also listed in *EnabledTopics*. +.. _sec:cfg-participant-logformat: + +Log message format +======================================== + +The *Format* property of a sink controls how log messages are rendered. +It applies to sinks of type *Stdout* and *File*. +Sinks of type *Remote* always transmit the messages in the *Simple* format. + +.. note:: + + If the *Format* property is not set explicitly, sinks of type *File* default to *Json*, while sinks of type *Stdout* default to *Simple*. + +Simple + Human-readable, single-line text output. + This is the default for *Stdout* sinks. + For *File* sinks, the messages are written to a ``.txt`` file. + + .. code-block:: text + + [2026-07-09 11:59:50.287] [CanWriter] [trace] [TimeSync] Finished Simulation Step., ExecutionTime: 0.4058, VirtualTimeNS: 60000000 + + +Json + One JSON object per line, which is convenient for automated processing of the log. + + .. code-block:: text + + {"ts":"1783591190287458","log":"CanWriter","lvl":"trace", "topic": "TimeSync", "msg": "Finished Simulation Step.", "kv": {"ExecutionTime":"0.4058","VirtualTimeNS":"60000000"} } + + Each entry contains the UNIX epoch timestamp in microseconds (``ts``), the participant name (``log``), the log level (``lvl``), and the message (``msg``). + Depending on the message, the ``topic`` (see :ref:`logging topics`) and the optional field ``kv`` (structured key-value data) are added. + For *File* sinks, the messages are written to a ``.jsonl`` file. + +The following example configures a *File* sink using the *Simple* format and a *Stdout* sink using the *Json* format: + +.. code-block:: yaml + + Logging: + Sinks: + - Type: File + Format: Simple + Level: Trace + LogName: ParticipantLog + - Type: Stdout + Format: Json + Level: Info + + Experimental: Topic-based sink filtering ======================================== @@ -125,6 +187,7 @@ With the next example, topics that produce verbose log messages at startup are d - Asio - ServiceDiscovery +.. _sec:cfg-participant-logtopics: The following topic names are currently assigned by SIL Kit components: