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

Testing for NONE log level #42

Merged
merged 6 commits into from Aug 18, 2021
Merged

Testing for NONE log level #42

merged 6 commits into from Aug 18, 2021

Conversation

binkley
Copy link

@binkley binkley commented Aug 17, 2021

Several commits together. Key points:

  • I started from writing tests, and wound up with the below to get the tests passing (see "does not log" block of tests)
  • IMPORTANT -- please review how NONE is handled, especially for:
    public fun isLevelEnabled(level: Level): Boolean = NONE != level && minLevel() <= level
  • Short-circuit checks in methods like log to skip processing if the log level is disabled
  • Formatting courtesy of IntelliJ
  • Remove useless code in batect.yml (which I added myself previously: I had not noticed the GRADLE_OPTS env var)

Tests and code to make them pass for either:
* `NONE` as the min level of a logger
* `NONE` as the caller's logging level
No need to start Gradle daemon processes for an ephemeral container.  No
reuse of the daemons after container finishes.
No daemon mode is provided via an env var.
@mjstrasser
Copy link
Member

mjstrasser commented Aug 17, 2021

Thanks @binkley for helping me to sort out log level checking. I had got myself confused with these things.

I wrote out the following today. Does it make sense? Do your changes implement it? I will put it into the documentation soon.

Log levels and checking

Background

  • Each logger has a minimum logging level per sink, set by configuration.
  • The minimum value of the per-sink levels is the minimum level for the logger, which can be checked at any time.

Rules

  • If a log request’s level is less than the minimum for the logger that created it, no log event should be created and emitted.
  • When a log event is emitted, it will be dispatched to at least one sink.

Code

Applicable to both Klogger and NoCoLogger.

  • trace(), debug() etc. functions are shims for log() with a specified value of Level.
  • isTraceEnabled(), isDebugEnabled() etc. functions are shims for isLevelEnabled() with a specified value of Level.
  • log() function with value arguments: only emits an event if isLevelEnabled() for the requested level is true.
  • log() function with lambda argument: only calls the lambda and emits an event if isLevelEnabled() for the requested level is true.

@mjstrasser
Copy link
Member

I am also unsure what NONE is for and why I created it.

@@ -45,7 +46,7 @@ public interface BaseLogger {
* Check whether this logger will emit log events at the specified logging
* level.
*/
public fun isLevelEnabled(level: Level): Boolean = minLevel() <= level
public fun isLevelEnabled(level: Level): Boolean = NONE != level && minLevel() <= level
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO this is prettier

public fun isLevelEnabled(level: Level): Boolean = when(level) 
{
NONE -> false
else -> minLevel() <= level
}

Copy link
Member

Choose a reason for hiding this comment

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

Maybe. 😄

Copy link
Author

Choose a reason for hiding this comment

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

Agreed!

Copy link
Author

Choose a reason for hiding this comment

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

NONE is used in SLF4J for example with dynamic changes in log level. I've actually used this in a prod system that was overflowing a downstream system with logging. We changed the log level in prod via JMX to NONE.

Copy link
Author

Choose a reason for hiding this comment

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

If you keep NONE, I'd update the above docs in the "Rules" section to call out NONE as special.


public suspend fun log(level: Level, exception: Exception, event: suspend Klogger.() -> Any?) {
if (isLevelEnabled(level)) emitEvent(level, exception, event())
if (!isLevelEnabled(level)) return
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 the original is clearer. An early return is only needed if what follows is more complex.

But I won’t go to the barricades over this. I can see the consistency with the earlier, more complex functions.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, two reasons:

  1. Consistency -- as noted
  2. Personal preference. I like to see early return. It's like a "pre condition", so I know what follows is the real work

}

public suspend fun log(level: Level, event: suspend Klogger.() -> Any?) {
if (isLevelEnabled(level)) emitEvent(level, null, event())
if (!isLevelEnabled(level)) return
Copy link
Member

Choose a reason for hiding this comment

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

Ditto.


describe("KtLogger") {
describe("does not log") {
Copy link
Member

Choose a reason for hiding this comment

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

Description is incomplete. It should be describe("does not log at level NONE") or similar.

randomString().let { message ->
logger.log(eventLevel, message)

if (!logger.isLevelEnabled(eventLevel))
Copy link
Member

Choose a reason for hiding this comment

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

Why not put if (logger.isLevelEnabled(eventLevel)) and switch the assertions? Using ! is a slight extra impediment to understandability.

Copy link
Author

Choose a reason for hiding this comment

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

Artifact of refactoring. In Java, IntelliJ warns me about this. Not sure how to enable that for Kotlin support. I'm always of the same view as you just stated. (Another example of my preference for early return, to avoid this situation.)

@mjstrasser mjstrasser merged commit 1d781bd into klogging:main Aug 18, 2021
mjstrasser added a commit that referenced this pull request Jul 10, 2023
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.

None yet

4 participants