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
18 changes: 14 additions & 4 deletions score/json/examples/logging.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@

{
"appId": "JSON",
"appDesc": "JSON example programs",
"logMode" : "kConsole",
"logLevel": "kVerbose"
"ecuId": "ECU1",
"appId": "log",
"logLevel": "kError",
"logMode": "kConsole|kFile",
"logLevelThresholdConsole": "kInfo",
"logFilePath": "./",
"logFileSizePolicy": {
"enabled": false,
"maxFileSizeInBytes": 500,
"numberOfFiles": 2,
"overwriteExistingFiles": false,
"truncateOnRotation": false
}
}
6 changes: 6 additions & 0 deletions score/mw/log/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ in parallel
* **logFilePath** -- used for file logging, if ```logMode``` includes
```kFile```, this is the directory, where the logfile ```appId.dlt``` will be
put
* **logFileSizePolicy** -- an object that defines the rules for log file rotation.
* **enabled** (`true`/`false`): Enables or disables the file rotation/sizing feature.
* **maxFileSizeInBytes** (integer): The maximum size in bytes that a single log file can reach before rotation is triggered.
* **numberOfFiles** (integer): The total number of log files to use in the rotation cycle (e.g., `appId_1.dlt`, `appId_2.dlt`).
* **overwriteExistingFiles** (`true`/`false`): If `true`, allows the logger to overwrite an existing log file when it rotates back to it. If `false`, rotation will stop if the next file in the cycle already exists.
* **truncateOnRotation** (`true`/`false`): If `true`, the contents of the next log file in the cycle will be deleted (truncated) before new logs are written to it.
* **logLevel** -- default value: LogLevel::kWarn, global log level threshold
for the application
* **logLevelThresholdConsole** -- if ```logMode``` includes ```kConsole```,
Expand Down
50 changes: 50 additions & 0 deletions score/mw/log/configuration/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,56 @@ void Configuration::SetDynamicDatarouterIdentifiers(const bool enable_dynamic_id
{
dynamic_datarouter_identifiers_ = enable_dynamic_identifiers;
}
bool Configuration::IsCircularFileLogging() const noexcept
{
return circular_file_logging_;
}

void Configuration::SetCircularFileLogging(const bool circular_file_logging) noexcept
{
circular_file_logging_ = circular_file_logging;
}

std::size_t Configuration::GetMaxLogFileSizeBytes() const noexcept
{
return max_log_file_size_bytes_;
}

void Configuration::SetMaxLogFileSizeBytes(const std::size_t max_log_file_size_bytes) noexcept
{
max_log_file_size_bytes_ = max_log_file_size_bytes;
}

bool Configuration::IsOverwriteLogOnFull() const noexcept
{
return overwrite_log_on_full_;
}

void Configuration::SetOverwriteLogOnFull(const bool overwrite_log_on_full) noexcept
{
// The JSON configuration key for this parameter is "overwriteLogOnFull".
overwrite_log_on_full_ = overwrite_log_on_full;
}

std::size_t Configuration::GetNoOfLogFiles() const noexcept
{
return no_of_log_files_;
}

void Configuration::SetNoOfLogFiles(const std::size_t no_of_log_files) noexcept
{
no_of_log_files_ = no_of_log_files;
}

bool Configuration::IsTruncateOnRotation() const noexcept
{
return truncate_on_rotation_;
}

void Configuration::SetTruncateOnRotation(const bool truncate_on_rotation) noexcept
{
truncate_on_rotation_ = truncate_on_rotation;
}

} // namespace detail
} // namespace log
Expand Down
30 changes: 30 additions & 0 deletions score/mw/log/configuration/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ class Configuration final
bool GetDynamicDatarouterIdentifiers() const noexcept;
void SetDynamicDatarouterIdentifiers(const bool enable_dynamic_identifiers) noexcept;

bool IsCircularFileLogging() const noexcept;
void SetCircularFileLogging(const bool) noexcept;

std::size_t GetMaxLogFileSizeBytes() const noexcept;
void SetMaxLogFileSizeBytes(const std::size_t) noexcept;

bool IsOverwriteLogOnFull() const noexcept;
void SetOverwriteLogOnFull(const bool) noexcept;

std::size_t GetNoOfLogFiles() const noexcept;
void SetNoOfLogFiles(const std::size_t) noexcept;

bool IsTruncateOnRotation() const noexcept;
void SetTruncateOnRotation(const bool) noexcept;

/// \brief Returns true if the log level is enabled for the context.
/// \param use_console_default_level Set to true if threshold for console logging should be considered as default
/// log level. Otherwise default_log_level_ will be used instead.
Expand Down Expand Up @@ -138,6 +153,21 @@ class Configuration final

/// \brief Toggle between dynamic datarouter identifiers.
bool dynamic_datarouter_identifiers_{false};

/// \brief Enable circular file logging.
bool circular_file_logging_{false};

/// \brief Maximum log file size in bytes for circular file logging.
std::size_t max_log_file_size_bytes_{10485760}; // 10 MB default

/// \brief Overwrite log file on full.
bool overwrite_log_on_full_{false};

/// \brief Number of log files to keep.
std::size_t no_of_log_files_{1};

/// \brief Truncate on rotation.
bool truncate_on_rotation_{false};
};

} // namespace detail
Expand Down
101 changes: 101 additions & 0 deletions score/mw/log/configuration/target_config_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ constexpr StringLiteral kSlotSizeBytesKey{"slotSizeBytes"};
constexpr StringLiteral kDatarouterUidKey{"datarouterUid"};
constexpr StringLiteral kDynamicDatarouterIdentifiersKey{"dynamicDatarouterIdentifiers"};

constexpr StringLiteral kLogFileSizePolicyKey{"logFileSizePolicy"};
constexpr StringLiteral kEnabledKey{"enabled"};
constexpr StringLiteral kMaxFileSizeInBytesKey{"maxFileSizeInBytes"};
constexpr StringLiteral kNumberOfFilesKey{"numberOfFiles"};
constexpr StringLiteral kOverwriteExistingFilesKey{"overwriteExistingFiles"};
constexpr StringLiteral kTruncateOnRotationKey{"truncateOnRotation"};

// Suppress Coverity warning because:
// 1. 'constexpr' cannot be used with std::unordered_map.
// 2. Defining it as a local variable does not resolve the Coverity warning.
Expand Down Expand Up @@ -429,6 +436,95 @@ score::ResultBlank ParseDynamicDatarouterIdentifiers(const score::json::Object&
// clang-format on
}

score::ResultBlank ParseCircularFileLogging(const score::json::Object& root, Configuration& config) noexcept
{
const auto file_rotation_obj_result = GetElementAsRef<score::json::Object>(root, kLogFileSizePolicyKey);
if (!file_rotation_obj_result.has_value())
{
return {}; // The whole fileRotation object is optional.
}

// clang-format off
return GetElementAndThen<bool>(
file_rotation_obj_result.value().get(),
kEnabledKey,
[&config](auto value) noexcept { config.SetCircularFileLogging(value); }
);
// clang-format on
}

score::ResultBlank ParseMaxLogFileSizeBytes(const score::json::Object& root, Configuration& config) noexcept
{
const auto file_rotation_obj_result = GetElementAsRef<score::json::Object>(root, kLogFileSizePolicyKey);
if (!file_rotation_obj_result.has_value())
{
return {}; // The whole fileRotation object is optional.
}

// Disabling clang-format to address Coverity warning: autosar_cpp14_a7_1_7_violation
// clang-format off
return GetElementAndThen<std::size_t>(
file_rotation_obj_result.value().get(),
kMaxFileSizeInBytesKey,
[&config](auto value) noexcept { config.SetMaxLogFileSizeBytes(value); }
);
// clang-format on
}

score::ResultBlank ParseOverwriteLogOnFull(const score::json::Object& root, Configuration& config) noexcept
{
const auto file_rotation_obj_result = GetElementAsRef<score::json::Object>(root, kLogFileSizePolicyKey);
if (!file_rotation_obj_result.has_value())
{
return {}; // The whole fileRotation object is optional.
}

// Disabling clang-format to address Coverity warning: autosar_cpp14_a7_1_7_violation
// clang-format off
return GetElementAndThen<bool>(
file_rotation_obj_result.value().get(),
kOverwriteExistingFilesKey,
[&config](auto value) noexcept { config.SetOverwriteLogOnFull(value); }
);
// clang-format on
}

score::ResultBlank ParseNoOfLogFiles(const score::json::Object& root, Configuration& config) noexcept
{
const auto file_rotation_obj_result = GetElementAsRef<score::json::Object>(root, kLogFileSizePolicyKey);
if (!file_rotation_obj_result.has_value())
{
return {}; // The whole fileRotation object is optional.
}

// Disabling clang-format to address Coverity warning: autosar_cpp14_a7_1_7_violation
// clang-format off
return GetElementAndThen<std::size_t>(
file_rotation_obj_result.value().get(),
kNumberOfFilesKey,
[&config](auto value) noexcept { config.SetNoOfLogFiles(value); }
);
// clang-format on
}

score::ResultBlank ParseTruncateOnRotation(const score::json::Object& root, Configuration& config) noexcept
{
const auto file_rotation_obj_result = GetElementAsRef<score::json::Object>(root, kLogFileSizePolicyKey);
if (!file_rotation_obj_result.has_value())
{
return {}; // The whole fileRotation object is optional.
}

// Disabling clang-format to address Coverity warning: autosar_cpp14_a7_1_7_violation
// clang-format off
return GetElementAndThen<bool>(
file_rotation_obj_result.value().get(),
kTruncateOnRotationKey,
[&config](auto value) noexcept { config.SetTruncateOnRotation(value); }
);
// clang-format on
}

void ParseConfigurationElements(const score::json::Object& root, const std::string& path, Configuration& config) noexcept
{
ReportOnError(ParseEcuId(root, config), path);
Expand All @@ -446,6 +542,11 @@ void ParseConfigurationElements(const score::json::Object& root, const std::stri
ReportOnError(ParseSlotSizeBytes(root, config), path);
ReportOnError(ParseDatarouterUid(root, config), path);
ReportOnError(ParseDynamicDatarouterIdentifiers(root, config), path);
ReportOnError(ParseCircularFileLogging(root, config), path);
ReportOnError(ParseMaxLogFileSizeBytes(root, config), path);
ReportOnError(ParseOverwriteLogOnFull(root, config), path);
ReportOnError(ParseNoOfLogFiles(root, config), path);
ReportOnError(ParseTruncateOnRotation(root, config), path);
}

// Suppress "AUTOSAR C++14 A15-5-3" rule findings: "The std::terminate() function shall not be called implicitly".
Expand Down
40 changes: 40 additions & 0 deletions score/mw/log/configuration/target_config_reader_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,46 @@ TEST_F(TargetConfigReaderFixture, ConfigReaderShallFallBackToContextLogLevelDefa
EXPECT_EQ(GetReader().ReadConfig()->GetContextLogLevel(), ContextLogLevelMap{});
}

TEST_F(TargetConfigReaderFixture, ConfigReaderShallParseFileRotationStructure)
{
RecordProperty("Requirement", "N/A");
RecordProperty("ASIL", "B");
RecordProperty("Description",
"TargetConfigReader shall parse the new nested fileRotation configuration structure correctly.");
RecordProperty("TestingTechnique", "Requirements-based test");
RecordProperty("DerivationTechnique", "Analysis of requirements");

// Create a temporary config file with the new structure
const std::string new_config_content = R"({
"logFileSizePolicy": {
"enabled": true,
"maxFileSizeInBytes": 12345,
"numberOfFiles": 5,
"overwriteExistingFiles": true,
"truncateOnRotation": true
}
})";
const std::string temp_file_path = "new_config.json";
std::ofstream temp_file(temp_file_path);
temp_file << new_config_content;
temp_file.close();

SetConfigurationFiles({temp_file_path});

const auto config_result = GetReader().ReadConfig();
ASSERT_TRUE(config_result.has_value());
const auto& config = config_result.value();

EXPECT_TRUE(config.IsCircularFileLogging());
EXPECT_EQ(config.GetMaxLogFileSizeBytes(), 12345);
EXPECT_EQ(config.GetNoOfLogFiles(), 5);
EXPECT_TRUE(config.IsOverwriteLogOnFull());
EXPECT_TRUE(config.IsTruncateOnRotation());

// Clean up the temporary file
std::remove(temp_file_path.c_str());
}

} // namespace
} // namespace detail
} // namespace log
Expand Down
8 changes: 7 additions & 1 deletion score/mw/log/detail/recorder_factory_stub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,15 @@ std::unique_ptr<Backend> CreateConsoleLoggingBackend(const Configuration& config
LogRecord{config.GetSlotSizeInBytes()});
return std::make_unique<FileOutputBackend>(std::move(message_builder),
STDOUT_FILENO,
std::string{},
std::move(allocator),
score::os::FcntlImpl::Default(memory_resource),
score::os::Unistd::Default(memory_resource));
score::os::Unistd::Default(memory_resource),
config.IsCircularFileLogging(),
false, // overwrite log on full
config.GetMaxLogFileSizeBytes(),
1, // no of log files
false); // delete old log files
}

std::unique_ptr<Recorder> RecorderFactory::CreateFromConfiguration(
Expand Down
4 changes: 4 additions & 0 deletions score/mw/log/detail/text_recorder/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ cc_library(
"//visibility:public",
],
deps = [
"@score_baselibs//score/filesystem",
"@score_baselibs//score/language/futurecpp",
"@score_baselibs//score/mw/log/detail:types_and_errors",
"@score_baselibs//score/os",
"@score_baselibs//score/os:fcntl",
"@score_baselibs//score/os:unistd",
],
)
Expand Down Expand Up @@ -212,6 +215,7 @@ cc_test(
deps = [
":non_blocking_writer",
"@score_baselibs//score/os/mocklib:unistd_mock",
"@score_baselibs//score/os/mocklib:fcntl_mock",
#"@score_baselibs//score/mw/log/test/console_logging_environment",
"@googletest//:gtest_main",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,15 @@ std::unique_ptr<Backend> ConsoleRecorderFactory::CreateConsoleLoggingBackend(

return std::make_unique<FileOutputBackend>(std::move(message_builder),
STDOUT_FILENO,
"",
std::move(allocator),
score::os::FcntlImpl::Default(memory_resource),
score::os::Unistd::Default(memory_resource));
score::os::Unistd::Default(memory_resource),
false,
false,
0,
1,
false);
}

} // namespace detail
Expand Down
31 changes: 20 additions & 11 deletions score/mw/log/detail/text_recorder/file_output_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,30 @@ namespace detail

FileOutputBackend::FileOutputBackend(std::unique_ptr<IMessageBuilder> message_builder,
const std::int32_t file_descriptor,
const std::string& file_path,
std::unique_ptr<CircularAllocator<LogRecord>> allocator,
score::cpp::pmr::unique_ptr<score::os::Fcntl> fcntl_instance,
score::cpp::pmr::unique_ptr<score::os::Unistd> unistd) noexcept
score::cpp::pmr::unique_ptr<score::os::Fcntl> fcntl,
score::cpp::pmr::unique_ptr<score::os::Unistd> unistd,
const bool circular_file_logging,
const bool overwrite_log_on_full,
const std::size_t max_log_file_size_bytes,
const std::size_t no_of_log_files,
const bool truncate_on_rotation) noexcept
: Backend(),
buffer_allocator_(std::move(allocator)),
slot_drainer_(std::move(message_builder), buffer_allocator_, file_descriptor, std::move(unistd))
slot_drainer_(std::move(message_builder),
buffer_allocator_,
file_descriptor,
file_path,
std::move(unistd),
std::move(fcntl),
circular_file_logging,
overwrite_log_on_full,
max_log_file_size_bytes,
no_of_log_files,
truncate_on_rotation)

{
const auto flags = fcntl_instance->fcntl(file_descriptor, score::os::Fcntl::Command::kFileGetStatusFlags);
if (flags.has_value())
{
std::ignore = fcntl_instance->fcntl(
file_descriptor,
score::os::Fcntl::Command::kFileSetStatusFlags,
flags.value() | score::os::Fcntl::Open::kNonBlocking | score::os::Fcntl::Open::kCloseOnExec);
}
}

score::cpp::optional<SlotHandle> FileOutputBackend::ReserveSlot() noexcept
Expand Down
Loading
Loading