resolveAtEdge
Snapshot everything that must be sampled at the moment the line was logged onto event, for a queueing capture edge to hand to the drain.
This is the one call a capture edge makes, and it MUST run synchronously on the thread/coroutine that logged. It resolves all three emit-time-sensitive inputs:
the ambient trace (resolveTrace → NormalizedLogEvent.activeTrace, #1034) — an ambient TraceContextProvider reads the caller's thread/coroutine-local context, which is gone by the time the drain runs capture;
the CaptureConfig.attributeMapper (NormalizedLogEvent.resolvedAttributes, #1630) — a mapper that folds ambient state (the session in progress, a request id) into attributes must see the state the line was emitted under, not whatever it has become by drain time;
the instant itself (NormalizedLogEvent.emittedEpochNanos, #1993) — the event time is now, on this caller, and no later reading of the clock can recover it. A value already on the event is left alone, so an edge whose platform event carries its own emit time may supply it.
Returns null when the event carries nothing to export — the edge should then drop it rather than queue it. That is either because capture would drop it anyway (below CaptureConfig.minLevel, or one of the exporter's own loggers — so the mapper is never paid for a line that produces no record), or because the configured CaptureConfig.attributeMapper threw. A throwing mapper drops just that record and never propagates into the application's logging call, exactly as it did when the mapper still ran on the drain behind the appender's best-effort guard.