Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

logger: macros evaluate logging level before invoking logger #2751

Merged
merged 8 commits into from
Mar 20, 2018
36 changes: 30 additions & 6 deletions source/common/common/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ class Logger {
std::string name() const { return logger_->name(); }
void setLevel(spdlog::level::level_enum level) const { logger_->set_level(level); }

/* This is simple mapping between Logger severity levels and spdlog severity levels.
* The only reason for this mapping is to go around the fact that spdlog defines level as err
* but the method to log at err level is called LOGGER.error not LOGGER.err. All other level are
* fine spdlog::info corresponds to LOGGER.info method.
*/
typedef enum {
trace = spdlog::level::trace,
debug = spdlog::level::debug,
info = spdlog::level::info,
warn = spdlog::level::warn,
error = spdlog::level::err,
critical = spdlog::level::critical,
off = spdlog::level::off
} levels;

private:
Logger(const std::string& name);

Expand Down Expand Up @@ -176,18 +191,27 @@ template <Id id> class Loggable {
* Base logging macros. It is expected that users will use the convenience macros below rather than
* invoke these directly.
*/
// Compare levels before invoking logger
#define ENVOY_LOG_COMP_AND_LOG(LOGGER, LEVEL, ...) \
do { \
if (static_cast<spdlog::level::level_enum>(Envoy::Logger::Logger::LEVEL) >= LOGGER.level()) { \
LOGGER.LEVEL(LOG_PREFIX __VA_ARGS__); \
} \
} while (0)

#ifdef NVLOG
#define ENVOY_LOG_trace_TO_LOGGER(LOGGER, ...)
#define ENVOY_LOG_debug_TO_LOGGER(LOGGER, ...)
#else
#define ENVOY_LOG_trace_TO_LOGGER(LOGGER, ...) LOGGER.trace(LOG_PREFIX __VA_ARGS__)
#define ENVOY_LOG_debug_TO_LOGGER(LOGGER, ...) LOGGER.debug(LOG_PREFIX __VA_ARGS__)
#define ENVOY_LOG_trace_TO_LOGGER(LOGGER, ...) ENVOY_LOG_COMP_AND_LOG(LOGGER, trace, ##__VA_ARGS__)
#define ENVOY_LOG_debug_TO_LOGGER(LOGGER, ...) ENVOY_LOG_COMP_AND_LOG(LOGGER, debug, ##__VA_ARGS__)
#endif

#define ENVOY_LOG_info_TO_LOGGER(LOGGER, ...) LOGGER.info(LOG_PREFIX __VA_ARGS__)
#define ENVOY_LOG_warn_TO_LOGGER(LOGGER, ...) LOGGER.warn(LOG_PREFIX __VA_ARGS__)
#define ENVOY_LOG_error_TO_LOGGER(LOGGER, ...) LOGGER.error(LOG_PREFIX __VA_ARGS__)
#define ENVOY_LOG_critical_TO_LOGGER(LOGGER, ...) LOGGER.critical(LOG_PREFIX __VA_ARGS__)
#define ENVOY_LOG_info_TO_LOGGER(LOGGER, ...) ENVOY_LOG_COMP_AND_LOG(LOGGER, info, ##__VA_ARGS__)
#define ENVOY_LOG_warn_TO_LOGGER(LOGGER, ...) ENVOY_LOG_COMP_AND_LOG(LOGGER, warn, ##__VA_ARGS__)
#define ENVOY_LOG_error_TO_LOGGER(LOGGER, ...) ENVOY_LOG_COMP_AND_LOG(LOGGER, error, ##__VA_ARGS__)
#define ENVOY_LOG_critical_TO_LOGGER(LOGGER, ...) \
ENVOY_LOG_COMP_AND_LOG(LOGGER, critical, ##__VA_ARGS__)

/**
* Convenience macro to log to a user-specified logger.
Expand Down
18 changes: 12 additions & 6 deletions source/common/http/filter/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -481,17 +481,23 @@ void Filter::scriptError(const Envoy::Lua::LuaException& e) {
void Filter::scriptLog(spdlog::level::level_enum level, const char* message) {
switch (level) {
case spdlog::level::trace:
return ENVOY_LOG(trace, "script log: {}", message);
ENVOY_LOG(trace, "script log: {}", message);
return;
case spdlog::level::debug:
return ENVOY_LOG(debug, "script log: {}", message);
ENVOY_LOG(debug, "script log: {}", message);
return;
case spdlog::level::info:
return ENVOY_LOG(info, "script log: {}", message);
ENVOY_LOG(info, "script log: {}", message);
return;
case spdlog::level::warn:
return ENVOY_LOG(warn, "script log: {}", message);
ENVOY_LOG(warn, "script log: {}", message);
return;
case spdlog::level::err:
return ENVOY_LOG(error, "script log: {}", message);
ENVOY_LOG(error, "script log: {}", message);
return;
case spdlog::level::critical:
return ENVOY_LOG(critical, "script log: {}", message);
ENVOY_LOG(critical, "script log: {}", message);
return;
case spdlog::level::off:
NOT_IMPLEMENTED;
}
Expand Down
44 changes: 44 additions & 0 deletions test/common/common/log_macros_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,48 @@ TEST(Logger, All) {
// Misc logging with no facility.
ENVOY_LOG_MISC(info, "fake message");
}

TEST(Logger, evaluateParams) {
uint32_t i = 1;

// set logger's level to low level.
// log message with higher severity and make sure that params were evaluated.
GET_MISC_LOGGER().set_level(spdlog::level::info);
ENVOY_LOG_MISC(warn, "test message '{}'", i++);
ASSERT_THAT(i, testing::Eq(2));
Copy link
Member

@mrice32 mrice32 Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: prefer EXPECT* for non-fatal failures, since ASSERT* will essentially abort the test case. Here and elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. Done.

}

TEST(Logger, doNotEvaluateParams) {
uint32_t i = 1;

// set logger's logging level high and log a message with lower severity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nitty, mind set ->Set here and above and do -> Do on 69?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching it. It has been corrected.

// params should not be evaluated.
GET_MISC_LOGGER().set_level(spdlog::level::critical);
ENVOY_LOG_MISC(error, "test message '{}'", i++);
ASSERT_THAT(i, testing::Eq(1));
}

TEST(Logger, logAsStatement) {
// Just log as part of if ... statement

if (true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same here as below, can we just add simple counter behavior above to ensure that this test not only compiles, but that it acts as expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

ENVOY_LOG_MISC(critical, "test message 1");
else
ENVOY_LOG_MISC(critical, "test message 2");

// do the same with curly brackets
if (true) {
ENVOY_LOG_MISC(critical, "test message 3");
} else {
ENVOY_LOG_MISC(critical, "test message 4");
}
}

#ifdef NVLOG
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the CI ever run in a mode with NVLOG defined? Put another way, will this test ever be executed by the CI or will it always be compiled out?

Copy link
Contributor Author

@cpakulski cpakulski Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI probably never runs with NVLOG defined. I invoked the test manually by running
bazel test --copt=-DNVLOG //test/common/common:log_macros_test

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we set one of the circleCI jobs to define NVLOG? Maybe the "release" job?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is probably good idea. I was surprised when my first PR #2676 was reverted even though it passed CI checks. If "release" CI job tests release image, it should probably use "release" compile flags.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking here: #2754

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ggreenway I don't think we should do it on the release job because that job is used for building the docker images.

Not to throw a wrench in this, but I'm wondering with all the macros we have now and this change, if we should just kill NVLOG entirely. There is some perf benefit but now that the logs don't evaluate it's probably small.

I think all that would be needed is to add some type of macro/guard for the few places where we use NVLOG to block an expensive iteration. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance overhead is one function call and comparison between integers. Not much.
I just hope that trace and debug logs do not display sensitive information. With NVLOG they are just removed. If NVLOG is killed, all is required to display those logs is to lower severity level.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at this point I would vote to just kill NVLOG and replace with some additional macros for the places in which we use it for larger guards, but no objection to merging this PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cpakulski are you interested in just getting rid of NVLOG as part of this PR?

TEST(Logger, doNotExpandTraceAndDebug) {
// The following two macros should not be expanded to no-op if NVLOG compile flag is defined.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I realize that this is just a test to ensure they compile, but do we also want to add the incrementing counter behavior used above just to ensure the statements act as stated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for review and suggestions.

ENVOY_LOG_TO_LOGGER(nonExistingLogger, trace, "test message 5");
ENVOY_LOG_TO_LOGGER(nonExistingLogger, debug, "test message 6");
}
#endif /* NVLOG */
} // namespace Envoy