Установите пользовательские фильтры для повышения журнала регистрации с пользовательским атрибутом и уровнем серьезности - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть настройки журнала, в которых у меня есть 2 типа сообщений журнала:

  • 1, основанный исключительно на уровне серьезности
  • 1, основанный исключительно на атрибуте пользовательского тега

Эти атрибуты определены следующим образом:

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)

Я хочу создать функцию фильтра, которая позволяет добавлять сообщение в мой журнал на основе одного из двух критериев (обратите внимание, что сообщения журнала, основанные на атрибуте пользовательского тега, всегда печатаются с информацией уровня серьезности, основанной на тривиальные уровни серьезности регистратора).

Итак, я хочу иметь фильтр, который разрешает сообщение, основываясь на том, есть ли в сообщении пользовательский тег, и если его нет, в зависимости от серьезности сообщения.

Я попытался установить относительно простой фильтр, который выполняет следующие действия:

sink_->set_filter(
    trivial::severity >= severityLevel
    || (expr::has_attr(tag_attr) && tag_attr == "JSON" && logJson_)
);

Но поскольку вполне возможно, что severityLevel может быть либо Debug, Info, Warning, Error или Fatal, если уровень настроен как Debug или Info, атрибут пользовательского тега игнорируется фильтром.

Я пробовал использовать лямбду с ++ 11, как показано ниже:

sink_->set_filter([this, severityLevel](const auto& attr_set) {
    if (<condition for custom tag first>) {
        return true;
    } else if (<condition for severity level second>) {
        return true;
    } else {
        return false;
    }
});

Но тогда у меня нет идеи, как на самом деле проверить мои условия. Я пробовал следующее:

if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
    return true;
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
    return true;
} else {
    return false;
}

Но компилятор выдает несколько ошибок по этому поводу:

Core/Source/Log/Logger.cpp: In lambda function:
Core/Source/Log/Logger.cpp:127:48: error: expected primary-expression before '>' token
         if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
                                                ^
Core/Source/Log/Logger.cpp:127:50: error: expected primary-expression before ')' token
         if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
                                                  ^
Core/Source/Log/Logger.cpp:129:72: error: expected primary-expression before '>' token
         } else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
                                                                        ^
Core/Source/Log/Logger.cpp:129:74: error: expected primary-expression before ')' token
         } else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
                                                                          ^
Core/Source/Log/Logger.cpp: In lambda function:
Core/Source/Log/Logger.cpp:134:5: error: control reaches end of non-void function [-Werror=return-type]
     });
     ^
cc1plus: all warnings being treated as errors
scons: *** [obj/release/Core/Source/Log/Logger.os] Error 1
====5 errors, 0 warnings====

Я сам изучал документацию буст-журнала об извлечении атрибутов, но не могу найти нужную информацию.

EDIT:

Для потомков я добавлю, как я решил свою проблему (благодаря ответу Андрея):

sink_->set_filter([this, severityLevel](const auto& attr_set) {
    if (attr_set[tag_attr] == "JSON") {
        return logJson_;
    } else if (attr_set[severity] >= severityLevel) {
        return true;
    } else {
        return false;
    }
});

1 Ответ

0 голосов
/ 07 сентября 2018

Фильтр можно записать несколькими способами, я покажу несколько альтернатив.

Во-первых, используя шаблоны выражений, вы можете написать это так:

sink_->set_filter(
    (expr::has_attr(tag_attr) && tag_attr == "JSON" && logJson_) ||
    trivial::severity >= severityLevel
);

В соответствии с обычными правилами короткого замыкания C ++ атрибут tag будет проверен первым, и если это условие выполнится успешно, серьезность не будет проверена. Если тег отсутствует или отсутствует в формате JSON или logJson_ не соответствует действительности, проверяется уровень серьезности.

Обратите внимание, что фильтр выше сохранит копии своих аргументов (включая logJson_ и severityLevel) в точке построения, поэтому, если вы измените logJson_ позже, фильтр будет продолжать использовать старое значение. Это важное отличие от ваших последующих попыток с лямбдами C ++ 14, которые обращаются к logJson_ через захваченный указатель this. Если вы действительно хотите сохранить ссылку на вашего участника logJson_ в фильтре, вы можете использовать phoenix::ref:

sink_->set_filter(
    (expr::has_attr(tag_attr) && tag_attr == "JSON" && boost::phoenix::ref(logJson_)) ||
    trivial::severity >= severityLevel
);

Однако вы должны помнить, что фильтр может вызываться одновременно в нескольких потоках, поэтому доступ к logJson_ не защищен. Вам нужно будет реализовать собственную синхронизацию потоков, если вы хотите обновить logJson_ во время выполнения.

За исключением проблем с многопоточностью, ваша вторая попытка с использованием лямбды почти правильна. Компилятор жалуется, потому что лямбда-функция является шаблоном, а результат выражения attr_set["Tag"] зависит от одного из параметров шаблона (а именно, от типа attr_set). В этом случае программист должен определить, что следующее выражение extract<std::string>() является экземпляром шаблона, а не последовательностью сравнений. Это делается путем добавления ключевого слова template:

if (attr_set["Tag"].template extract<std::string>() == "JSON" && logJson_) {
    return true;
} else if (attr_set["Severity"].template extract<trivial::severity_level>() >= severityLevel) {
    return true;
} else {
    return false;
}

Обратите внимание, что вы можете использовать автономную функцию для того же эффекта, которая не требует квалификации шаблона:

if (boost::log::extract<std::string>("Tag", attr_set) == "JSON" && logJson_) {
    return true;
} else if (boost::log::extract<trivial::severity_level>("Severity", attr_set) >= severityLevel) {
    return true;
} else {
    return false;
}

Наконец, предпочтительным способом извлечения значений атрибутов является использование ключевых слов атрибутов, которые вы объявили ранее. Это не только позволяет избежать причуда квалификации шаблона, но и устраняет много дублирования кода.

BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)

if (attr_set[tag_attr] == "JSON" && logJson_) {
    return true;
} else if (attr_set[severity] >= severityLevel) {
    return true;
} else {
    return false;
}

Имя и тип значения атрибута выводятся из объявления ключевого слова в этом случае. Такое использование ключевых слов атрибутов задокументировано в конце этого раздела.

...