nquic_qlog behaviour (nquic v1.0.0)

View Source

qlog tracing for nquic connections (draft-ietf-quic-qlog-main-schema).

qlog is the IETF-standard observability format for QUIC. A backend records per-connection events to a sink (file, gen_event manager, caller-supplied module); tools like qvis render the resulting NDJSON stream into time-series and packet-level diagrams.

This module defines the backend behaviour and a tiny dispatcher the protocol calls at known hook points. Backends do all the formatting work; the dispatcher only branches on whether a backend is attached.

The protocol carries the active backend on #conn_state.qlog as undefined | qlog_state(). When the field is undefined every event/3 call returns immediately in a single field read.

Hook points

Currently wired:

  • packet_sent: every wire packet leaves with a payload, header, PN, encryption level. Fired from nquic_protocol_send.
  • packet_received: every successfully decrypted packet. Fired from nquic_protocol_recv.

Future hook points (documented in plans/current/3_TRANSPORT_GAPS_PLAN.md §C2):

  • transport:datagrams_received
  • transport:packet_lost
  • recovery:metrics_updated
  • recovery:congestion_state_updated
  • recovery:loss_timer_updated

Configuration

#{qlog => Backend} on nquic:listen_opts/0 and nquic:connect_opts/0. Backend is one of:

  • {file, Path}: open a per-connection NDJSON file at Path (nquic_qlog_file).
  • {Module, InitArgs}: caller-supplied backend that implements this behaviour.

Summary

Callbacks

Initialise the backend. Args is the second element of the backend_config() tuple. The returned state is opaque to the dispatcher and threaded through event/2 / terminate/2.

Functions

Construct a qlog_state() from a backend_config() for the connection whose original DCID is CID. {ok, undefined} if Cfg is the sentinel undefined (the option was not set).

Tear down the backend.

Record a qlog event. Zero-overhead when no backend is attached. The dispatcher is intentionally unhelpful about misbehaving backends: it threads the new backend state back into the connection state and lets errors propagate. Backends MUST NOT crash on malformed event payloads.

Types

backend_config()

-type backend_config() :: {file, file:filename_all()} | {module(), term()}.

event_data()

-type event_data() :: #{atom() => term()}.

event_name()

-type event_name() ::
          transport_packet_sent | transport_packet_received | transport_packet_lost |
          recovery_metrics_updated.

qlog_state()

-type qlog_state() :: {module(), term()}.

Callbacks

event(BackendState, Event)

-callback event(BackendState :: term(), Event :: {event_name(), event_data()}) ->
                   {ok, BackendState :: term()}.

init/2

-callback init(nquic:connection_id(), Args :: term()) -> {ok, BackendState :: term()} | {error, term()}.

Initialise the backend. Args is the second element of the backend_config() tuple. The returned state is opaque to the dispatcher and threaded through event/2 / terminate/2.

terminate(BackendState, Reason)

-callback terminate(BackendState :: term(), Reason :: term()) -> ok.

Functions

attach/2

-spec attach(nquic:connection_id(), undefined | backend_config()) ->
                {ok, undefined | qlog_state()} | {error, term()}.

Construct a qlog_state() from a backend_config() for the connection whose original DCID is CID. {ok, undefined} if Cfg is the sentinel undefined (the option was not set).

detach/1

-spec detach(undefined | qlog_state()) -> ok.

Tear down the backend.

event/3

-spec event(undefined | qlog_state(), event_name(), event_data()) -> undefined | qlog_state().

Record a qlog event. Zero-overhead when no backend is attached. The dispatcher is intentionally unhelpful about misbehaving backends: it threads the new backend state back into the connection state and lets errors propagate. Backends MUST NOT crash on malformed event payloads.