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:

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, 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.

Every drop capture would make is decided here, before the mapper runs (#1745): the cheap pre-record drops (droppedBeforeRecord — below CaptureConfig.minLevel, or one of the exporter's own loggers) and the trace/sampling gate (droppedByTraceGate — an unsampled trace, or an untraced event under UntracedPolicy.DROP). Both gates read exactly what recordFor later reads, so an event that survives this call is one no gate can drop, and a mapper is never paid for a record that will not exist. That matters most for a consumer running DROP, who has by definition asked for most lines to be discarded — the configuration in which the wasted mapping was largest.