Skip to content

Add ENVOY_LOG_ONCE#12830

Merged
ggreenway merged 17 commits intoenvoyproxy:masterfrom
oschaaf:envoy-log-once-proposal
Sep 2, 2020
Merged

Add ENVOY_LOG_ONCE#12830
ggreenway merged 17 commits intoenvoyproxy:masterfrom
oschaaf:envoy-log-once-proposal

Conversation

@oschaaf
Copy link
Member

@oschaaf oschaaf commented Aug 26, 2020

Sometimes there's error flow control in the hot path that deserves logging,
but plain logging there risks generating a large amount of log spam.
It would be great to have a low-barrier and easy-to-consume way to emit a single
or bounded number of log lines in these cases. This proposes a means to emit
a single log line in those cases.

Cross referencing Nighthawk issue to prompted this:
envoyproxy/nighthawk#484

Signed-off-by: Otto van der Schaaf oschaaf@we-amp.com


Risk Level: Low
Testing: Unit test

Sometimes there's error flow control in the hot path that deserves logging,
but plain logging there risks generating a large amount of log spam.
It would be great to have a low-barrier and easy-to-consume way to emit a single
or bounded number of log lines in these cases. This proposes a means to emit
a single log line in those cases.

Cross referencing Nighthawk issue to prompted this:
envoyproxy/nighthawk#484

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Member Author

oschaaf commented Aug 26, 2020

/assign htuch

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@ggreenway
Copy link
Member

Instead of once, what about the strategy used in ENVOY_BUG, which IIRC takes an action on powers of two counts of the event occurring, which results in exponential backoff. It gives more signal about whether something really only happened once, or is happening repeatedly.

@oschaaf
Copy link
Member Author

oschaaf commented Aug 27, 2020

@ggreenway Thanks for the feedback; I should first get this simplest of cases to work, because CI indicates something isn't right. Having said that, and assuming I can fix that using the current approach: with respect to logging, I wonder if there's a silver bullet. Maybe we should consider offering a series so one can choose the best one? Some variants that I can think of:

  • log once and never again (e.g. )
  • log every nth occurrence, maybe append number of actual occurrences to the log message
  • log at most once per period, maybe append the actual rate to the log message

and then maybe have flags or macro variants that set things up to configure strictness of the log gating of these macros;
it might be possible to trade in precision for performance (at the extreme just use thread local state).
I recon this last part could perhaps be useful for certain types of debug level logging in hot paths.

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Member Author

oschaaf commented Aug 27, 2020

I had to amend the expected number of argument evaluations in the test for when fine grained logging is enabled / FANCY_LOG is used: when enabled, ENVOY_LOG will evaluate arguments regardless of the configured log level, whereas without the feature enabled, it will only evaluate when the log level filter doesn't squelch the message.

I think that with 63b8a08 this will pass CI, but isn't it kind of tricky to have the argument evaluation behaviour change based on a flag? (because of potential side effects?)

@htuch
Copy link
Member

htuch commented Aug 27, 2020

I think we don't want to evaluate ENVOY_LOG arguments if the log level doesn't match, this seems a bug in the fancy logger.

@htuch
Copy link
Member

htuch commented Aug 28, 2020

@oschaaf can you file a bug and attach a TODO for fancy logger?

Looking at how we use this kind of macro, how about a small modification to make this a bit more general and make this ENVOY_LOG_FIRST_N where it logs the first N times.

Periodic isn't perfect, as it reference a real-world time source
directly. Depending on feedback / if it's a lot of work to do
this right, it can be backed out for now.

Will need a follow up with more tests for the new macros.

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Member Author

oschaaf commented Aug 29, 2020

@htuch Added ENVOY_LOG_FIRST_N, ENVOY_LOG_EVERY_NTH, ENVOY_LOG_PERIODIC in 2d47780
The periodic version references a real-world time source directly though.
Depending on feedback / if it's a lot of work to do that right I can back that one out.

Regardless, to take this foward, I will need another follow to add more tests. But I'm curious to hear your thoughts on
so far.

ps: rebuilding Envoy takes a while when touching logging.h, a fast & easy means to toy around with the code resides here: https://gist.github.com/oschaaf/4113ac357d7537a470c09c1f2285d731

@oschaaf
Copy link
Member Author

oschaaf commented Aug 29, 2020

Filed #12885 for the FANCY_LOG variadic macro evaluation issue observed here.

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
}
// Allow threads to accrue on future.wait() to maximize concurrency on the call
// we are testing.
sleep(1);
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 we want to avoid relying on real-time for tests. @jmarantz do you see a way to cleanly add an injected time source for the periodic macro that @oschaaf introduced in this PR? I'm thinking maybe we need a time source singleton for logging and do singleton injection, to avoid having to worry about time source at call site.

Copy link
Member Author

Choose a reason for hiding this comment

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

In 371fa5d I rewrote the test to not rely on sleep().
Also backed out ENVOY_LOG_PERIODIC with the intent to do a follow up for adding that after this.
(so we can take a look and discuss that separately)

ENVOY_LOG_FIRST_N(LEVEL, 1, ##__VA_ARGS__); \
} while (0)

#define ENVOY_LOG_EVERY_NTH(LEVEL, N, ...) \
Copy link
Member

Choose a reason for hiding this comment

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

As per earlier discussion, every Pow2 could be good as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh yes good catch, my intent was actually to add that. Will do.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, added ENVOY_LOG_WITH_BACKOFF in 371fa5d + tests

Copy link
Member Author

Choose a reason for hiding this comment

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

/cc @ggreenway ^^

- Back out controversial ENVOY_LOG_PERIODIC, will split that out.
- Don't rely on sleep() in tests
- Add ENVOY_LOG_WITH_BACKOFF

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
htuch
htuch previously requested changes Sep 1, 2020
Copy link
Member

@htuch htuch left a comment

Choose a reason for hiding this comment

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

LGTM modulo comments.
/wait

} \
} while (0)

#define ENVOY_LOG_WITH_BACKOFF(LEVEL, ...) \
Copy link
Member

Choose a reason for hiding this comment

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

Nit: ENVOY_LOG_EVERY_POW_2 (more consistent with some internal terminology and clearer what backoff means).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in de23f2a

}
// Allow threads to accrue on future.wait() to maximize concurrency on the call
// we are testing.
while (runcount != num_threads) {
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Ooh, that's awesome. Switched to use absl::Barrier in de23f2a

- Use absl::Barrier
- Rename ENVOY_LOG_WITH_BACKOFF  to ENVOY_LOG_EVERY_POW_2

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
…oposal

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf
Copy link
Member Author

oschaaf commented Sep 2, 2020

Coverage flaked on an unrelated issue
/retest

@repokitteh-read-only
Copy link

Retrying Azure Pipelines, to retry CircleCI checks, use /retest-circle.
Cannot retry non-completed check: envoy-presubmit (Linux-x64 compile_time_options), please wait.

🐱

Caused by: a #12830 (comment) was created by @oschaaf.

see: more, trace.

@oschaaf
Copy link
Member Author

oschaaf commented Sep 2, 2020

/retest

@repokitteh-read-only
Copy link

Retrying Azure Pipelines, to retry CircleCI checks, use /retest-circle.
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #12830 (comment) was created by @oschaaf.

see: more, trace.

Copy link
Member

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

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

Other than sorting out the thread-safety of static variables, I like this a lot.


#define ENVOY_LOG_FIRST_N(LEVEL, N, ...) \
do { \
static auto* countdown = new std::atomic<uint64_t>(); \
Copy link
Member

Choose a reason for hiding this comment

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

This isn't thread-safe. If two threads do this at the same time, there could be a leak. I think this ends up being better as static std::atomic<uint64_t> countdown;. Either the compiler will see that it is initialized by zeroing, and do so at program start, or it will double-zero the same memory by initializing multiple times, but should avoid a leak at least.

Copy link
Member Author

Choose a reason for hiding this comment

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

Wouldn't that lead up to a potential deinitialization fiasco? I was following the lead of

#define CONSTRUCT_ON_FIRST_USE(type, ...) \

Reading up I am led to believe this would be thread safe since c++ 11 but I can't find a source that I would consider authorative,. Will dig some more.

Copy link
Member Author

Choose a reason for hiding this comment

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

Relevant on the subject of leaking and stack vs heap allocation in the construct on first use idiom: Why doesn’t the Construct On First Use Idiom use a static object instead of a static pointer?

Copy link
Member Author

Choose a reason for hiding this comment

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

https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables has:

If multiple threads attempt to initialize the same static local variable concurrently, the initialization occurs exactly once (similar behavior can be obtained for arbitrary functions with std::call_once). Note: usual implementations of this feature use variants of the double-checked locking pattern, which reduces runtime overhead for already-initialized local statics to a single non-atomic boolean comparison.

Copy link
Member

Choose a reason for hiding this comment

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

Did some reading, and you're correct. TIL something new about static initialization.

@ggreenway
Copy link
Member

/wait

@ggreenway ggreenway dismissed htuch’s stale review September 2, 2020 21:41

Concerns were addressed.

@ggreenway ggreenway merged commit f1c0031 into envoyproxy:master Sep 2, 2020
oschaaf added a commit to oschaaf/envoy that referenced this pull request Sep 2, 2020
Follow up to envoyproxy#12830 with another sparse log variant.
The periodic version relies on a real world time source, which can
be discussed separately here.

Signed-off-by: Otto van der Schaaf <oschaaf@we-amp.com>
@oschaaf oschaaf mentioned this pull request Sep 2, 2020
htuch pushed a commit that referenced this pull request Sep 9, 2020
Follow up to #12830 with another sparse log variant.
The periodic version relies on a real world time source, which can
be discussed separately here.

Risk Level: Low
Testing: Unit tests

Signed-off-by: Otto van der Schaaf oschaaf@we-amp.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants