Correct MultiplexLogHandler metadata priority documentation - #488
Correct MultiplexLogHandler metadata priority documentation#488ZayanKhan-12 wants to merge 1 commit into
Conversation
The doc comment claimed the first handler's values take priority for both metadata accessors, including that "the same rule applies when querying for the metadata property". The implementation - pinned by multiplexLogHandlerMetadata_readingHandlerMetadata - does the opposite for the metadata property: it merges handler metadata in initialization order with later handlers' values overriding earlier ones, provider values overriding stored metadata, and the multiplex handler's own provider applied last. Only the single-key subscript prefers the first handler that has the queried key. Document both behaviors accurately and pin the subscript's first-wins behavior with test assertions alongside the existing metadata-property expectations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@ZayanKhan-12 thank you for updating the docs to correctly describe the current implementation! Could you also please open an Issue describing this inconsistency, so we can have a discussion if this behavior needs to be changed? |
|
To me, the current multiplexHandler.metadata = [:]
assert(multiplexHandler.metadata.isEmpty, "Would expect this to be true, but will not be if `metadataProvider` has content")I would also not expect the subscript setter followed by querying the multiplexHandler[metadataKey: "foo"] = "bar"
multiplexHandler[metadataKey: "foo"] = "bar"
assert(multiplexHandler.metadata["foo"] == "bar", "Would expect this to be true, but will not be if `metadataProvider` has key `"foo"` with different value")And speaking of public struct MultiplexLogHandler: LogHandler {
public var metadataProvider: Logger.MetadataProvider? {
get {
var providers: [Logger.MetadataProvider] = []
for handler in self.handlers.reversed() {
if let provider = handler.metadataProvider {
providers.append(provider)
}
}
if let multiplexHandlerProvider = self._metadataProvider {
providers.append(multiplexHandlerProvider)
}
guard !providers.isEmpty else {
return nil
}
return .multiplex(providers)
}
set {
self.mutatingForEachHandler { $0.metadataProvider = newValue }
}
}
public var metadata: Logger.Metadata {
get {
var effective: Logger.Metadata = [:]
for handler in self.handlers.reversed() {
effective.merge(handler.metadata, uniquingKeysWith: { _, handlerMetadata in handlerMetadata })
}
return effective
}
set {
self.mutatingForEachHandler { $0.metadata = newValue }
}
}
}I.e. I guess that could be considered breaking; if so, we might considering deprecating this implementation and add a new handler that works as documented. Regardless, I think if we go the route of only changing the documentation, I think it could more clearly point out that the behaviour is somewhat unexpected and care must be taken when using subscript vs the |
|
This does look indeed surprising. Metadata provider values should only be taking into consideration when a log is emitted and not on |
@kukushechkin I took the liberty of opening a discussion: #498 |
Motivation
The
MultiplexLogHandlerdoc comment states that the first handler's values take priority when reading metadata, and that "The same rule applies when querying for themetadataproperty of the multiplex log handler". The implementation does the opposite for themetadataproperty — and that behavior is deliberately pinned by the existingmultiplexLogHandlerMetadata_readingHandlerMetadatatest, which expects"in": "in-2"(the second handler's value) for a conflicting key:So today:
subscript(metadataKey:)→ first handler with the key wins (matches the docs)metadataproperty → later handlers override earlier ones; provider values override stored metadata; the multiplex handler's own provider is applied last (contradicts the docs)Since the current merge behavior is tested and changing it would be a breaking behavior change, the documentation is what should be fixed.
Modifications
multiplexLogHandlerMetadata_readingHandlerMetadatapinning the subscript's first-handler-wins behavior for conflicting keys, so both documented behaviors are now covered by tests.Result
The public API documentation matches the implemented and tested behavior. No code behavior changes; all 164 tests pass.
If the maintainers would rather unify the two accessors' priority instead (a semver-major behavior change), happy to rework this in that direction — but the docs shouldn't promise something the tests forbid in the meantime.
🤖 Generated with Claude Code