Логирование выполняется внутренним логгером iceoryx. Логгер используется внутри системы для записи ошибок и общей информации о работе системы. Пользователь также может использовать его в коде приложения с той же целью.
Логгер отвечает за запись информации о состоянии системы в настраиваемый поток (включая файлы).
Логгер является потокобезопасным и, следовательно, может безопасно использоваться из нескольких потоков одновременно. В настоящее время логгер работает синхронно.
Поддерживаются следующие уровни логирования, упорядоченные по объёму отображаемой информации от наибольшего к наименьшему:
Для ERR и FATAL см. также уровни ошибок MODERATE, SEVERE (регистрируются с помощью LogErr) и FATAL (регистрируются с помощью LogFatal) в разделе «Уровни ошибок». Уровни ERR и FATAL должны использоваться только вместе с обработчиком ошибок, то есть должны сопровождаться соответствующим вызовом обработчика ошибок (в настоящее время это невозможно обеспечить).
Ошибки считаются состояниями системы, которые не должны возникать регулярно и обычно являются результатом внешнего сбоя, например, когда ОС не может предоставить определённый ресурс (например, семафор) или приложение не отвечает. Напротив, регулярное поведение, такое как приём данных получателем, когда данные не были отправлены, не является ошибкой. С другой стороны, потеря отправленных данных будет считаться ошибкой.
Существует два основных подхода к обработке ошибок:
В iceoryx мы используем второй подход.
Использование исключений в iceoryx запрещено, включая сторонний код, который может их генерировать. Это связано со следующими причинами:
Все функции помечены как noexcept. Обратите внимание, что это не означает, что внутри такой функции нельзя генерировать исключения. Вместо этого, когда внутри функции генерируется исключение (прямо или косвенно другой функцией), это исключение не распространяется дальше, и будет вызван std::terminate, вызывающий обработчик завершения. Цель состоит в том, чтобы это никогда не происходило во время выполнения.
Для этого необходимо исключить использование всех потенциально выбрасывающих (сторонних) функций во всей кодовой базе. Поскольку многие функции STL могут генерировать исключения, их также нельзя использовать, и их функциональность должна быть перереализована без использования исключений. В частности, всё, что выделяет динамическую память, может генерировать исключение std::bad_alloc при исчерпании памяти.
В качестве альтернативы исключениям мы используем обработчик ошибок и разновидность кодов возврата в виде iox::expected, описанных ниже. iox::expected можно использовать для передачи ошибки вызывающему объекту, который должен решить, обработать ошибку самостоятельно или... errorHandler(Error::SOME_ERROR_CODE)
Более подробную информацию о том, как настроить и использовать errorHandler, можно найти в справочнике по API.
Предположим, что myAlgorithm является частью внутреннего API и не должен вызываться с nullptr. Мы могли бы проверять это каждый раз, но этого можно избежать, если мы укажем, что вызывающий объект отвечает за обеспечение выполнения этих условий.
int32_t myAlgorithm(int32_t* ptr) {
IOX_EXPECTS(ptr!=nullptr);
//наблюдаем порядок, разыменовываем только после проверки nullptr
IOX_EXPECTS(*ptr > -1024 && *ptr < 1024);
int32_t intermediate = timesTwo(*ptr);
//здесь может не потребоваться проверка следующего вызова функции,
//но она ясно выражает наши ожидания
IOX_ENSURES(intermediate % 2 == 0);
int32_t result = abs(intermediate);
IOX_ENSURES(result % 2 == 0);
IOX_ENSURES(result >= 0);
IOX_ENSURES(result < 2048);
return result;
}
Обратите внимание, что в случае проверок nullptr также можно использовать ссылки в аргументах (или iox::not_null, если предполагается его сохранение, поскольку ссылки не копируются). Следует учитывать, что iox::not_null влечёт за собой затраты во время выполнения, которые могут быть нежелательны. Когда IOX_EXPECTS и IOX_ENSURES реализованы так, чтобы не оставлять следов в режиме выпуска, мы не несём затрат во время выполнения при их использовании. По этой причине рекомендуется использовать их для документирования и проверки предположений там, где это уместно.
Этот пример проверяет аргументы и, если они действительны, продолжает вычислять результат и возвращает его. В противном случае он создаёт объект Error из errorCode и возвращает его.
expected<SomeType, Error> func(Arg arg) {
int32_t errorCode = checkArg(arg);
if(isNoError(errorCode)) {
SomeType result = computeResult(arg);
// опционально делаем что-то с результатом
return result;
}
return Error(errorCode);
}
Вызывающий объект отвечает за обработку (или распространение) ошибки.
auto result = func(arg);
if(result.has_error()) {
auto& error = result.error();
//обрабатываем или распространяем ошибку
} else {
auto& value = result.value();
//продолжаем, используя значение
}
В качестве альтернативы можно использовать функциональный подход.
auto successFunc = [](SomeType& value) {
//продолжаем, используя значение
};
auto errorFunc = [](Error& error) {
//обрабатываем ошибку
};
func(arg).and_then(successFunc).or_else(errorFunc);
Для фатальных ошибок обработчик ошибок завершает выполнение двоичного файла. Чтобы протестировать эти пути, следует использовать функцию iox::testing::IOX_EXPECT_FATAL_FAILURE
вместо макроса gTest EXPECT_DEATH. Макрос gTest EXPECT_DEATH разветвляет процесс, что замедляет выполнение теста (особенно при включённом ThreadSanitizer) и вызывает проблемы с запуском потока. Функция IOX_EXPECT_FATAL_FAILURE
регистрирует временный обработчик ошибок и запускает предоставленную функцию в отдельном потоке. Когда вызывается обработчик ошибок, используется longjmp, чтобы предотвратить завершение работы и вместо этого обеспечить корректное завершение потока.
#include "iceoryx_hoofs/testing/fatal_failure.hpp"
TEST(MyTest, valueOnNulloptIsFatal) {
iox::optional<bool> sut;
IOX_EXPECT_FATAL_FAILURE<iox::HoofsError>([&] { sut.value(); }, iox::HoofsError::EXPECTS_ENSURES_FAILED);
}
Если нельзя использовать IOX_EXPECT_FATAL_FAILURE
, например, при тестировании PoshRuntime с его синглтоном, следует использовать EXPECT_DEATH с GTEST_FLAG(death_test_style) = "threadsafe";.
GTEST_FLAG(death_test_style) = "threadsafe";
EXPECT_DEATH(bad_call(), ".*");
Может быть желательно иметь централизованный обработчик ошибок, где ошибки времени выполнения на стороне приложения регистрируются и (возможно) обрабатываются. Это также можно сделать в RouDi (отправив информацию в RouDi), но RouDi уже...
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )