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

=multiplex #139 more properly handle logLevel in multiplex handler #140

Merged
merged 5 commits into from
Jul 6, 2020

Conversation

ktoso
Copy link
Member

@ktoso ktoso commented Jun 27, 2020

Motivation:

Log level handling is a bit too naive in the multiplex handler when underlying loggers have varying levels.

Modifications:

  • effective log level has to be the lowest of all passed in handlers / same on level modification
  • when dispatching, the multiplex handler has to filter again based on the specific handler.

Result:

@ktoso ktoso marked this pull request as draft June 27, 2020 09:18
@ktoso ktoso requested a review from tomerd June 29, 2020 13:09
@ktoso ktoso added this to the 1.3.0 milestone Jun 29, 2020
@ktoso ktoso force-pushed the wip-loglevel-handling-multiplex branch from e56b909 to e26592e Compare June 29, 2020 14:05
@ktoso ktoso removed this from the 1.3.0 milestone Jun 29, 2020
@ktoso ktoso force-pushed the wip-loglevel-handling-multiplex branch 2 times, most recently from 2a7153a to af24c58 Compare June 29, 2020 14:08
logging2.history.assertExist(level: .debug, message: "debug")
logging2.history.assertExist(level: .info, message: "info")
logging2.history.assertExist(level: .warning, message: "warning")
}
Copy link
Member Author

Choose a reason for hiding this comment

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

this test explains the feature

@ktoso ktoso marked this pull request as ready for review June 29, 2020 14:11
@tomerd tomerd added the 🔨 semver/patch No public API change. label Jun 30, 2020
@ktoso
Copy link
Member Author

ktoso commented Jun 30, 2020

I now realized the current behavior is documented:

/// The first `LogHandler` passed to the initialisation function of `MultiplexLogHandler` control the `logLevel` as
/// well as the `metadata` for this `LogHandler`. Any subsequent `LogHandler`s used to initialise a
/// `MultiplexLogHandler` are merely to emit the log message to another place.

but it's admittably a bit weird... not sure what to do here tbh.

The current behavior has some upsides (detailed control) but it is somewhat unexpected as witnessed by #139 as well as my going down this rabbit hole.

Do we have opinions what do do here @glbrntt @Lukasa (optionally, only if you have time and want to chime in @weissi) @tomerd ?

@ktoso ktoso marked this pull request as draft June 30, 2020 01:16
@tomerd
Copy link
Member

tomerd commented Jun 30, 2020

Do we have opinions what do do here @glbrntt @Lukasa (optionally, only if you have time and want to chime in @weissi) @tomerd ?

for the log level, I think the semantics proposed by this PR is an improvement, but it does make the metadata story more murky since its still pinned to handler[0]

@Lukasa
Copy link
Contributor

Lukasa commented Jun 30, 2020

Yeah, I think this behavioural change needs to happen. The logical semantics of MultiplexLogHandler, in my mind, would be that its effective log level is the lowest of all log handlers it wraps, and that it only dispatches the messages to log handlers whose actual log level is lower than that of the message. This makes it maximally useful.

@ktoso ktoso added this to the 1.2.1 milestone Jun 30, 2020
@ktoso
Copy link
Member Author

ktoso commented Jun 30, 2020

The logical semantics of MultiplexLogHandler, in my mind, would be that its effective log level is the lowest of all log handlers it wraps, and that it only dispatches the messages to log handlers whose actual log level is lower than that of the message.

Cool, very much agreed, that's what the current PR brings it to 👍

I'll sanity check the metadata handling again here soon; I guess the semantics could be:

  • do not just expose any of the underlying ones
  • when set, set onto all underlying ones

or something like that; rather than the [0] based one

@glbrntt
Copy link
Contributor

glbrntt commented Jun 30, 2020

I agree with what's been said above: the current behaviour is weird; this change introduces the behaviour I would have expected.

@tomerd tomerd added 🆕 semver/minor Adds new public API. and removed 🔨 semver/patch No public API change. labels Jul 1, 2020
@ktoso ktoso modified the milestones: 1.2.1, 1.3.0 Jul 4, 2020
@ktoso ktoso force-pushed the wip-loglevel-handling-multiplex branch from a0e545a to d3164d6 Compare July 5, 2020 14:13
/// If the log level is _set_ on a `Logger` backed by an `MultiplexLogHandler` the log level will apply to *all*
/// underlying log handlers. Allowing a logger to still select at what level it wants to log, regardless if the underlying
/// handler is a multiplex or a normal one. If for some reason one might want to not allow changing a log level of a specific
/// handled passed into the multiplex log handler, this is possible by wrapping it in a handler which ignores any log level changes.
Copy link
Member Author

Choose a reason for hiding this comment

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

docs and examples on how the log level works

/// A query through the multiplex log handler the key `one` naturally returns `handler1`'s value, and a query for `two`
/// naturally returns `handler2`'s value. Querying for the key `all` will return `handler1`'s value, as that handler was indicated
/// "more important" than the second handler. The same rule applies when querying for the `metadata` property of the
/// multiplex log handler - it constructs `Metadata` uniquing values
Copy link
Member Author

Choose a reason for hiding this comment

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

docs and examples how metadata works

Copy link
Member Author

Choose a reason for hiding this comment

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

I actually had a "smart and configurable" EffectiveMetadataMode based impl but I stepped back from it for a few hours to re-think and it was getting too complex IMO for what it is -- realistically this is not a "big problem that needs fancy solving" -- normally users just care about setting metadata on things and maybe some handlers have something specific to them, the conflicting cases should be rare and this simple rule is better than slamming more complexity into it.

// I even had an impl which would "collect all values if the same key is present in a few handlers into a logger metadata value Array etc... Way too fancy for what we're looking at I then realized.


/// Create a `MultiplexLogHandler`.
///
/// - parameters:
/// - handlers: An array of `LogHandler`s, each of which will receive the log messages sent to this `Logger`.
/// The array must not be empty.
public init(_ handlers: [LogHandler]) {
assert(!handlers.isEmpty)
assert(!handlers.isEmpty, "MultiplexLogHandler.handlers MUST NOT be empty")
Copy link
Member Author

Choose a reason for hiding this comment

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

explicit messages always better IMO

self.handlers = handlers
self.effectiveLogLevel = MultiplexLogHandler.effectiveLogLevel(handlers.map { $0.logLevel })
self.effectiveLogLevel = handlers.map({ $0.logLevel }).min() ?? .trace
Copy link
Member Author

Choose a reason for hiding this comment

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

the ?? will never trigger but it's semantically "aha makes sense, we take the lowest" so I prefer that to writing ! here

@ktoso ktoso force-pushed the wip-loglevel-handling-multiplex branch from d3164d6 to 1974886 Compare July 5, 2020 14:22
effectiveMetadata.reserveCapacity(self.handlers.first!.metadata.count) // !-safe, we always have at least one handler
return self.handlers.reduce(into: effectiveMetadata) { effectiveMetadata, handler in
effectiveMetadata.merge(handler.metadata, uniquingKeysWith: { l, _ in l })
}
Copy link
Member Author

Choose a reason for hiding this comment

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

We pick the "left" over any following ones "first handlers in the list are more important"

return value
}
}
return nil
Copy link
Member Author

Choose a reason for hiding this comment

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

Again, implements the "first is most important"

@ktoso ktoso force-pushed the wip-loglevel-handling-multiplex branch 2 times, most recently from 55f9711 to cdb4869 Compare July 5, 2020 14:24
@ktoso
Copy link
Member Author

ktoso commented Jul 5, 2020

I poked at the metadata quite a bit over the weekend and think I've arrived at what is a good tradeoff here.

The metadata handling change is: cdb4869

And please see the comments in-line. Short version though: we should not magically rely on the first handler, but we should prefer more-first passed in handler metadata values over latter ones. This is simple to explain and implement without some crazy things (I had some advanced implementations with merge strategies and whatnot, but they ended up feeling wrong in their own ways, so I believe a simple rule like this (see added docs) is a good tradeoff, esp since we should not expect to be hitting this in reality much -- it is the underlying handlers doing their job, and barely anyone should be reading from the logger metadata to "just have a look at it" after all).

@ktoso ktoso force-pushed the wip-loglevel-handling-multiplex branch from cdb4869 to 003a288 Compare July 5, 2020 14:27
@ktoso ktoso requested review from tomerd, Lukasa and glbrntt July 5, 2020 14:28
@ktoso ktoso marked this pull request as ready for review July 5, 2020 14:28
Copy link
Member

@tomerd tomerd left a comment

Choose a reason for hiding this comment

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

lgtm

Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

One little grammar nit in a doc comment, otherwise this is a good change.

Sources/Logging/Logging.swift Outdated Show resolved Hide resolved
Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

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

Looks good aside from a couple of typos.

Sources/Logging/Logging.swift Outdated Show resolved Hide resolved
Sources/Logging/Logging.swift Outdated Show resolved Hide resolved
Sources/Logging/Logging.swift Outdated Show resolved Hide resolved
Co-authored-by: Cory Benfield <[email protected]>
Co-authored-by: George Barnett <[email protected]>
@ktoso
Copy link
Member Author

ktoso commented Jul 6, 2020

Thanks for the review and cleanups folks 👍

@ktoso ktoso merged commit 0c61f36 into apple:master Jul 6, 2020
@ktoso ktoso deleted the wip-loglevel-handling-multiplex branch July 6, 2020 09:34
@ktoso ktoso removed this from the 1.3.0 milestone Jul 6, 2020
@tomerd tomerd added this to the 1.3.0 milestone Jul 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🆕 semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Issue setting different levels on log handlers in a MultiplexLogHandler
4 participants