Skip to content

Add quic_logging_impl.(h|cc) to QUICHE platform implementation.#5758

Merged
htuch merged 8 commits intoenvoyproxy:masterfrom
wu-bin:quic_platform_logging
Feb 8, 2019
Merged

Add quic_logging_impl.(h|cc) to QUICHE platform implementation.#5758
htuch merged 8 commits intoenvoyproxy:masterfrom
wu-bin:quic_platform_logging

Conversation

@wu-bin
Copy link
Contributor

@wu-bin wu-bin commented Jan 29, 2019

Description:

Add quic_logging_impl.(h|cc) and unit tests to QUICHE platform implementation.

QUICHE code uses stream-style logging macros to do logging, e.g.:
QUIC_LOG(INFO) << "x = " << x;

The implementation in this PR uses a temporary std::ostringstream (one per macro invocation) to convert the stream into a std::string, then send the string to spdlog.

Risk Level: minimal: code not used yet
Testing:
[Unit test in release mode] bazel test --experimental_remap_main_repo -c opt test/extensions/quic_listeners/quiche/platform:quic_platform_test
[Unit test in debug mode] bazel test --experimental_remap_main_repo test/extensions/quic_listeners/quiche/platform:quic_platform_test

Docs Changes: none
Release Notes: none
[Optional Fixes #Issue]
[Optional Deprecated:]

@mpwarres
Copy link
Contributor

Tagging issue #2557

@alyssawilk
Copy link
Contributor

Looks like you want to check CI on this one. FWIW you can also set up circle ci to email you on build failures - I don't think it does by default but it's super useful to get the reports

@alyssawilk
Copy link
Contributor

/wait

@wu-bin
Copy link
Contributor Author

wu-bin commented Jan 31, 2019

@alyssawilk : This PR needs to wait for the resolution of #5767

@alyssawilk
Copy link
Contributor

Totally fine - this just flags it on github dashboards as something I don't need to look at :-)
When the blocking issue is resolved and you push new code, it'll auto-remove the wait tag.

Copy link
Contributor

@mpwarres mpwarres left a comment

Choose a reason for hiding this comment

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

Looks awesome! I know this is blocked behind other things, but figured I'd give some comments in the meantime.

case 1: \
logstream

#define QUIC_LOGGER() Envoy::Logger::Registry::getLog(Envoy::Logger::Id::misc)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: does QUIC_LOGGER have to be a macro? Wondering if you could instead just define a GetQuicLogger() function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced QUIC_LOGGER() by quic::GetLogger() function.


#include "absl/base/optimization.h"

#define QUIC_LOG_IMPL_INTERNAL(condition, logstream) \
Copy link
Contributor

Choose a reason for hiding this comment

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

A brief comment explaining roughly how this works, and that it is done in order to instantiate logstream only if condition is true, would be nice.

Side question: I was noticing that base/logging.h does this slightly differently, placing the log stream expression in the default case of a switch(0), do you have any idea why that is? I agree your formulation seems simpler, just wondering if there's some reason behind the base/logging.h form.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment added.

I was following the style in go/onelog, not base/logging.h. But the style in go/onelog no longer compiles after I updated my clang to 7.0, so I've switched to the base/logging.h style.

About the switch(0):
If a macro is implemented in the form of "if (x) {} else {}", the switch(0) can be used to suppress a warning about ambiguous else blocks. Suppose there is

#define MACRO() if (x) {} else {}

And a call site:

if (y) MACRO();

Which expands to:

if (y) if (x) {} else {};

The "else" is bind to the second "if", but some compiler worries that the intent is to bind to the first "if", so they give warnings. The switch(0) trick eliminates this warning.

QUIC_LOG_IMPL_INTERNAL((condition) && quic::IsLogLevelEnabled(quic::severity), \
quic::QuicLogEmitter(quic::severity).stream())

#define QUIC_LOG_IMPL(severity) QUIC_LOG_IF_IMPL(severity, true)
Copy link
Contributor

Choose a reason for hiding this comment

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

(optional) slight preference for defining this directly in terms of QUIC_LOG_IMPL_INTERNAL, even though it's longer textually.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

QUIC_LOG_IF_IMPL is easier to read and I think it compiles to the same code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough, SG

static const QuicLogLevel WARNING = spdlog::level::warn;
static const QuicLogLevel ERROR = spdlog::level::err;
static const QuicLogLevel FATAL = spdlog::level::critical;

Copy link
Contributor

Choose a reason for hiding this comment

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

should there be a TODO for DFATAL here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I forgot that, added to this PR, please take a look.

Copy link
Contributor

Choose a reason for hiding this comment

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

LG

static const QuicLogLevel INFO = spdlog::level::info;
static const QuicLogLevel WARNING = spdlog::level::warn;
static const QuicLogLevel ERROR = spdlog::level::err;
static const QuicLogLevel FATAL = spdlog::level::critical;
Copy link
Contributor

Choose a reason for hiding this comment

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

does logging spdylog::level::critical kill the process? I couldn't find indication of that in spdlog documentation. If it does, perhaps this could be verified with a death test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't, but I've updated this PR to kill the process when QUIC_LOG(FATAL) is hit.

Copy link
Contributor

Choose a reason for hiding this comment

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

LG, thanks

QUIC_LOG_IF(ERROR, false) << i++;
EXPECT_EQ(11, i);

QUIC_LOG_IF(ERROR, true) << i++;
Copy link
Contributor

Choose a reason for hiding this comment

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

would it be possible to have the test verify, for at least one of the log statements that actually executes, that the expected message actually makes it to spdlog? Might not be too bad if you mock spdlog::sinks::sink or base_sink.

If this ends up being more complicated, then maybe not worth the effort.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I plan to work on it in a follow up PR, in the context of quic_mock_log_impl.h.

Copy link
Contributor

Choose a reason for hiding this comment

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

SG. Could you add a TODO comment for that?

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!

#define QUIC_LOG_INFO_IS_ON_IMPL quic::IsLogLevelEnabled(quic::INFO)
#define QUIC_LOG_WARNING_IS_ON_IMPL quic::IsLogLevelEnabled(quic::WARNING)
#define QUIC_LOG_ERROR_IS_ON_IMPL quic::IsLogLevelEnabled(quic::ERROR)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we may need TODOs for CHECK and DCHECK macros (including the various (D)CHECK_ variants), since google3 quic_logging_impl.h implicitly provides those as well (by directly #including base/logging.h), and QUICHE code definitely uses them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SG, added a TODO.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks

…ging

Update the QUIC_LOG_IMPL_INTERNAL macro.

Signed-off-by: Bin Wu <wub@google.com>
Signed-off-by: Bin Wu <wub@google.com>
mpwarres
mpwarres previously approved these changes Feb 6, 2019
Copy link
Contributor

@mpwarres mpwarres left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for implementing this!

QUIC_LOG_IMPL_INTERNAL((condition) && quic::IsLogLevelEnabled(quic::severity), \
quic::QuicLogEmitter(quic::severity).stream())

#define QUIC_LOG_IMPL(severity) QUIC_LOG_IF_IMPL(severity, true)
Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough, SG

#define QUIC_LOG_INFO_IS_ON_IMPL quic::IsLogLevelEnabled(quic::INFO)
#define QUIC_LOG_WARNING_IS_ON_IMPL quic::IsLogLevelEnabled(quic::WARNING)
#define QUIC_LOG_ERROR_IS_ON_IMPL quic::IsLogLevelEnabled(quic::ERROR)

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks

static const QuicLogLevel INFO = spdlog::level::info;
static const QuicLogLevel WARNING = spdlog::level::warn;
static const QuicLogLevel ERROR = spdlog::level::err;
static const QuicLogLevel FATAL = spdlog::level::critical;
Copy link
Contributor

Choose a reason for hiding this comment

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

LG, thanks

static const QuicLogLevel WARNING = spdlog::level::warn;
static const QuicLogLevel ERROR = spdlog::level::err;
static const QuicLogLevel FATAL = spdlog::level::critical;

Copy link
Contributor

Choose a reason for hiding this comment

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

LG

QUIC_LOG_IF(ERROR, false) << i++;
EXPECT_EQ(11, i);

QUIC_LOG_IF(ERROR, true) << i++;
Copy link
Contributor

Choose a reason for hiding this comment

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

SG. Could you add a TODO comment for that?

// Test the behaviors of the cross products of
//
// {QUIC_LOG, QUIC_DLOG} x {FATAL, DFATAL} x {debug, release}
TEST(QuicPlatformTest, QuicFatalLog) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Cool, nice to have a test for this

quic::GetLogger().set_level(quic::INFO);

if (QUIC_PREDICT_FALSE(rand() % RAND_MAX == 123456789)) {
QUIC_LOG(INFO) << "Go buy some lottery tickets.";
Copy link
Contributor

Choose a reason for hiding this comment

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

ha! :-)

Signed-off-by: Bin Wu <wub@google.com>
building it unless "--define quiche=enabled" is used.

Signed-off-by: Bin Wu <wub@google.com>
Signed-off-by: Bin Wu <wub@google.com>
Signed-off-by: Bin Wu <wub@google.com>
@wu-bin
Copy link
Contributor Author

wu-bin commented Feb 7, 2019

/retest

@repokitteh-read-only
Copy link

🔨 rebuilding ci/circleci: coverage (failed build)

🐱

Caused by: a #5758 (comment) was created by @wu-bin.

see: more, trace.

@wu-bin
Copy link
Contributor Author

wu-bin commented Feb 7, 2019

@alyssawilk : Can you take a look at this and (if no problem) merge it? Thanks!

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Very cool!

QuicLogEmitter::~QuicLogEmitter() {
if (is_perror_) {
// TODO(wub): Change to a thread-safe version of strerror.
stream_ << ": " << strerror(saved_errno_) << " [" << saved_errno_ << "]";
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is as simple as switching to strerror_r might as well do it here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

strerror is prevalent in Envoy code, I'd prefer to stick to it in this PR.
In the future, I can add a thread safe version of strerror to some Envoy utility class, and use that everywhere, but it will be in low priority.

template <typename T> inline NullLogStream& operator<<(NullLogStream& s, const T&) { return s; }

inline spdlog::logger& GetLogger() {
return Envoy::Logger::Registry::getLog(Envoy::Logger::Id::misc);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should probably declare a quic-specific logger in
source/common/common/logger.h
If it ends up being complicated a TODO is fine.

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. (seems easier than I thought, hopefully ci won't complain:)

static const QuicLogLevel INFO = spdlog::level::info;
static const QuicLogLevel WARNING = spdlog::level::warn;
static const QuicLogLevel ERROR = spdlog::level::err;
static const QuicLogLevel FATAL = spdlog::level::critical;
Copy link
Contributor

Choose a reason for hiding this comment

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

Just something to keep in mid as we code, in Envoy only critical logs flush immediately.
It won't cause problems and I think while we could set Enovy QUIC logs to flush at error it makes more sense to stick with the defaults, but worth being aware when we debug.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, thanks!


int i = 0;

QUIC_LOG(INFO) << (i = 10);
Copy link
Contributor

Choose a reason for hiding this comment

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

Mind using EXPECT_LOG_[NOT_]ONTAINS macros instead of or in additon to the math checks?
It'll also verify we did the log mapping correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added some EXPECT_LOG_CONTAINS.
These macros forcefully set the log level to trace(example), so things like EXPECT_NO_LOGS do not work for the test where log is not emitted due to log level.

After this PR, I'll work on quic_mock_log_impl.h and add tests for the "no log expected" case.

Signed-off-by: Bin Wu <wub@google.com>
Copy link
Contributor Author

@wu-bin wu-bin left a comment

Choose a reason for hiding this comment

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

@alyssawilk Thanks for the review! PTAL.

QuicLogEmitter::~QuicLogEmitter() {
if (is_perror_) {
// TODO(wub): Change to a thread-safe version of strerror.
stream_ << ": " << strerror(saved_errno_) << " [" << saved_errno_ << "]";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

strerror is prevalent in Envoy code, I'd prefer to stick to it in this PR.
In the future, I can add a thread safe version of strerror to some Envoy utility class, and use that everywhere, but it will be in low priority.

static const QuicLogLevel INFO = spdlog::level::info;
static const QuicLogLevel WARNING = spdlog::level::warn;
static const QuicLogLevel ERROR = spdlog::level::err;
static const QuicLogLevel FATAL = spdlog::level::critical;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, thanks!

template <typename T> inline NullLogStream& operator<<(NullLogStream& s, const T&) { return s; }

inline spdlog::logger& GetLogger() {
return Envoy::Logger::Registry::getLog(Envoy::Logger::Id::misc);
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. (seems easier than I thought, hopefully ci won't complain:)


int i = 0;

QUIC_LOG(INFO) << (i = 10);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added some EXPECT_LOG_CONTAINS.
These macros forcefully set the log level to trace(example), so things like EXPECT_NO_LOGS do not work for the test where log is not emitted due to log level.

After this PR, I'll work on quic_mock_log_impl.h and add tests for the "no log expected" case.

@htuch htuch merged commit 2cf6524 into envoyproxy:master Feb 8, 2019
fredlas pushed a commit to fredlas/envoy that referenced this pull request Mar 5, 2019
…yproxy#5758)

Add quic_logging_impl.(h|cc) and unit tests to QUICHE platform implementation.

QUICHE code uses stream-style logging macros to do logging, e.g.:
QUIC_LOG(INFO) << "x = " << x;

The implementation in this PR uses a temporary std::ostringstream (one per macro invocation) to convert the stream into a std::string, then send the string to spdlog.

Risk Level: minimal: code not used yet
Testing:
[Unit test in release mode] bazel test --experimental_remap_main_repo -c opt test/extensions/quic_listeners/quiche/platform:quic_platform_test
[Unit test in debug mode] bazel test --experimental_remap_main_repo test/extensions/quic_listeners/quiche/platform:quic_platform_test

Signed-off-by: Bin Wu <wub@google.com>
Signed-off-by: Fred Douglas <fredlas@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants