Skip to content

Correct MultiplexLogHandler metadata priority documentation - #488

Open
ZayanKhan-12 wants to merge 1 commit into
apple:mainfrom
ZayanKhan-12:fix/multiplex-metadata-docs
Open

Correct MultiplexLogHandler metadata priority documentation#488
ZayanKhan-12 wants to merge 1 commit into
apple:mainfrom
ZayanKhan-12:fix/multiplex-metadata-docs

Conversation

@ZayanKhan-12

Copy link
Copy Markdown

Motivation

The MultiplexLogHandler doc comment states that the first handler's values take priority when reading metadata, and that "The same rule applies when querying for the metadata property of the multiplex log handler". The implementation does the opposite for the metadata property — and that behavior is deliberately pinned by the existing multiplexLogHandlerMetadata_readingHandlerMetadata test, which expects "in": "in-2" (the second handler's value) for a conflicting key:

effective.merge(handler.metadata, uniquingKeysWith: { _, handlerMetadata in handlerMetadata })

So today:

  • subscript(metadataKey:) → first handler with the key wins (matches the docs)
  • metadata property → 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

  • Rewrote the "Effective Logger.Metadata" section of the doc comment to describe both accessors accurately, including the provider layering (handler metadata → handler providers → multiplex's own provider).
  • Added assertions to multiplexLogHandlerMetadata_readingHandlerMetadata pinning 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

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>
@kukushechkin

Copy link
Copy Markdown
Contributor

@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?

@kukushechkin kukushechkin added the semver/none No version bump required. label Jul 23, 2026
@samuelmurray

Copy link
Copy Markdown
Contributor

To me, the current metadata accessor is quite surprising, not only in that it contradicts the documentation, but also that it includes values from the metadataProvider at all.

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 metadata to yield a different value.

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 metadataProvider, the sorting of that also uses the "first handler has lowest priority" currently.
To match the current documentation, the implementation should be something like:

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. metadata is reversed so precedence matches subscript, and no metadataProvider is used. For metadataProvider, the sorting of handlers is reversed, followed lastly by the multiplex handler, which takes highest precedence.

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 metadata accessor.

@FranzBusch

Copy link
Copy Markdown
Member

This does look indeed surprising. Metadata provider values should only be taking into consideration when a log is emitted and not on metadata access of the Logger or the LogHandler.

@samuelmurray

Copy link
Copy Markdown
Contributor

Could you also please open an Issue describing this inconsistency, so we can have a discussion if this behavior needs to be changed?

@kukushechkin I took the liberty of opening a discussion: #498

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver/none No version bump required.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants