github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/qlog/connection_tracer_test.go (about)

     1  package qlog
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  	"net"
     8  	"net/netip"
     9  	"time"
    10  
    11  	"github.com/danielpfeifer02/quic-go-prio-packs"
    12  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol"
    13  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/qerr"
    14  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/utils"
    15  	"github.com/danielpfeifer02/quic-go-prio-packs/logging"
    16  
    17  	. "github.com/onsi/ginkgo/v2"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  type nopWriteCloserImpl struct{ io.Writer }
    22  
    23  func (nopWriteCloserImpl) Close() error { return nil }
    24  
    25  func nopWriteCloser(w io.Writer) io.WriteCloser {
    26  	return &nopWriteCloserImpl{Writer: w}
    27  }
    28  
    29  type entry struct {
    30  	Time  time.Time
    31  	Name  string
    32  	Event map[string]interface{}
    33  }
    34  
    35  var _ = Describe("Tracing", func() {
    36  	var (
    37  		tracer *logging.ConnectionTracer
    38  		buf    *bytes.Buffer
    39  	)
    40  
    41  	BeforeEach(func() {
    42  		buf = &bytes.Buffer{}
    43  		tracer = NewConnectionTracer(
    44  			nopWriteCloser(buf),
    45  			logging.PerspectiveServer,
    46  			protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
    47  		)
    48  	})
    49  
    50  	It("exports a trace that has the right metadata", func() {
    51  		tracer.Close()
    52  
    53  		m := make(map[string]interface{})
    54  		Expect(json.Unmarshal(buf.Bytes(), &m)).To(Succeed())
    55  		Expect(m).To(HaveKeyWithValue("qlog_version", "draft-02"))
    56  		Expect(m).To(HaveKey("title"))
    57  		Expect(m).To(HaveKey("trace"))
    58  		trace := m["trace"].(map[string]interface{})
    59  		Expect(trace).To(HaveKey(("common_fields")))
    60  		commonFields := trace["common_fields"].(map[string]interface{})
    61  		Expect(commonFields).To(HaveKeyWithValue("ODCID", "deadbeef"))
    62  		Expect(commonFields).To(HaveKeyWithValue("group_id", "deadbeef"))
    63  		Expect(commonFields).To(HaveKey("reference_time"))
    64  		referenceTime := time.Unix(0, int64(commonFields["reference_time"].(float64)*1e6))
    65  		Expect(referenceTime).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
    66  		Expect(commonFields).To(HaveKeyWithValue("time_format", "relative"))
    67  		Expect(trace).To(HaveKey("vantage_point"))
    68  		vantagePoint := trace["vantage_point"].(map[string]interface{})
    69  		Expect(vantagePoint).To(HaveKeyWithValue("type", "server"))
    70  	})
    71  
    72  	Context("Events", func() {
    73  		exportAndParse := func() []entry {
    74  			tracer.Close()
    75  
    76  			m := make(map[string]interface{})
    77  			line, err := buf.ReadBytes('\n')
    78  			Expect(err).ToNot(HaveOccurred())
    79  			Expect(json.Unmarshal(line, &m)).To(Succeed())
    80  			Expect(m).To(HaveKey("trace"))
    81  			var entries []entry
    82  			trace := m["trace"].(map[string]interface{})
    83  			Expect(trace).To(HaveKey("common_fields"))
    84  			commonFields := trace["common_fields"].(map[string]interface{})
    85  			Expect(commonFields).To(HaveKey("reference_time"))
    86  			referenceTime := time.Unix(0, int64(commonFields["reference_time"].(float64)*1e6))
    87  			Expect(trace).ToNot(HaveKey("events"))
    88  
    89  			for buf.Len() > 0 {
    90  				line, err := buf.ReadBytes('\n')
    91  				Expect(err).ToNot(HaveOccurred())
    92  				ev := make(map[string]interface{})
    93  				Expect(json.Unmarshal(line, &ev)).To(Succeed())
    94  				Expect(ev).To(HaveLen(3))
    95  				Expect(ev).To(HaveKey("time"))
    96  				Expect(ev).To(HaveKey("name"))
    97  				Expect(ev).To(HaveKey("data"))
    98  				entries = append(entries, entry{
    99  					Time:  referenceTime.Add(time.Duration(ev["time"].(float64)*1e6) * time.Nanosecond),
   100  					Name:  ev["name"].(string),
   101  					Event: ev["data"].(map[string]interface{}),
   102  				})
   103  			}
   104  			return entries
   105  		}
   106  
   107  		exportAndParseSingle := func() entry {
   108  			entries := exportAndParse()
   109  			Expect(entries).To(HaveLen(1))
   110  			return entries[0]
   111  		}
   112  
   113  		It("records connection starts", func() {
   114  			tracer.StartedConnection(
   115  				&net.UDPAddr{IP: net.IPv4(192, 168, 13, 37), Port: 42},
   116  				&net.UDPAddr{IP: net.IPv4(192, 168, 12, 34), Port: 24},
   117  				protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
   118  				protocol.ParseConnectionID([]byte{5, 6, 7, 8}),
   119  			)
   120  			entry := exportAndParseSingle()
   121  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   122  			Expect(entry.Name).To(Equal("transport:connection_started"))
   123  			ev := entry.Event
   124  			Expect(ev).To(HaveKeyWithValue("ip_version", "ipv4"))
   125  			Expect(ev).To(HaveKeyWithValue("src_ip", "192.168.13.37"))
   126  			Expect(ev).To(HaveKeyWithValue("src_port", float64(42)))
   127  			Expect(ev).To(HaveKeyWithValue("dst_ip", "192.168.12.34"))
   128  			Expect(ev).To(HaveKeyWithValue("dst_port", float64(24)))
   129  			Expect(ev).To(HaveKeyWithValue("src_cid", "01020304"))
   130  			Expect(ev).To(HaveKeyWithValue("dst_cid", "05060708"))
   131  		})
   132  
   133  		It("records the version, if no version negotiation happened", func() {
   134  			tracer.NegotiatedVersion(0x1337, nil, nil)
   135  			entry := exportAndParseSingle()
   136  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   137  			Expect(entry.Name).To(Equal("transport:version_information"))
   138  			ev := entry.Event
   139  			Expect(ev).To(HaveLen(1))
   140  			Expect(ev).To(HaveKeyWithValue("chosen_version", "1337"))
   141  		})
   142  
   143  		It("records the version, if version negotiation happened", func() {
   144  			tracer.NegotiatedVersion(0x1337, []logging.VersionNumber{1, 2, 3}, []logging.VersionNumber{4, 5, 6})
   145  			entry := exportAndParseSingle()
   146  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   147  			Expect(entry.Name).To(Equal("transport:version_information"))
   148  			ev := entry.Event
   149  			Expect(ev).To(HaveLen(3))
   150  			Expect(ev).To(HaveKeyWithValue("chosen_version", "1337"))
   151  			Expect(ev).To(HaveKey("client_versions"))
   152  			Expect(ev["client_versions"].([]interface{})).To(Equal([]interface{}{"1", "2", "3"}))
   153  			Expect(ev).To(HaveKey("server_versions"))
   154  			Expect(ev["server_versions"].([]interface{})).To(Equal([]interface{}{"4", "5", "6"}))
   155  		})
   156  
   157  		It("records idle timeouts", func() {
   158  			tracer.ClosedConnection(&quic.IdleTimeoutError{})
   159  			entry := exportAndParseSingle()
   160  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   161  			Expect(entry.Name).To(Equal("transport:connection_closed"))
   162  			ev := entry.Event
   163  			Expect(ev).To(HaveLen(2))
   164  			Expect(ev).To(HaveKeyWithValue("owner", "local"))
   165  			Expect(ev).To(HaveKeyWithValue("trigger", "idle_timeout"))
   166  		})
   167  
   168  		It("records handshake timeouts", func() {
   169  			tracer.ClosedConnection(&quic.HandshakeTimeoutError{})
   170  			entry := exportAndParseSingle()
   171  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   172  			Expect(entry.Name).To(Equal("transport:connection_closed"))
   173  			ev := entry.Event
   174  			Expect(ev).To(HaveLen(2))
   175  			Expect(ev).To(HaveKeyWithValue("owner", "local"))
   176  			Expect(ev).To(HaveKeyWithValue("trigger", "handshake_timeout"))
   177  		})
   178  
   179  		It("records a received stateless reset packet", func() {
   180  			tracer.ClosedConnection(&quic.StatelessResetError{
   181  				Token: protocol.StatelessResetToken{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
   182  			})
   183  			entry := exportAndParseSingle()
   184  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   185  			Expect(entry.Name).To(Equal("transport:connection_closed"))
   186  			ev := entry.Event
   187  			Expect(ev).To(HaveLen(3))
   188  			Expect(ev).To(HaveKeyWithValue("owner", "remote"))
   189  			Expect(ev).To(HaveKeyWithValue("trigger", "stateless_reset"))
   190  			Expect(ev).To(HaveKeyWithValue("stateless_reset_token", "00112233445566778899aabbccddeeff"))
   191  		})
   192  
   193  		It("records connection closing due to version negotiation failure", func() {
   194  			tracer.ClosedConnection(&quic.VersionNegotiationError{})
   195  			entry := exportAndParseSingle()
   196  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   197  			Expect(entry.Name).To(Equal("transport:connection_closed"))
   198  			ev := entry.Event
   199  			Expect(ev).To(HaveLen(1))
   200  			Expect(ev).To(HaveKeyWithValue("trigger", "version_mismatch"))
   201  		})
   202  
   203  		It("records application errors", func() {
   204  			tracer.ClosedConnection(&quic.ApplicationError{
   205  				Remote:       true,
   206  				ErrorCode:    1337,
   207  				ErrorMessage: "foobar",
   208  			})
   209  			entry := exportAndParseSingle()
   210  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   211  			Expect(entry.Name).To(Equal("transport:connection_closed"))
   212  			ev := entry.Event
   213  			Expect(ev).To(HaveLen(3))
   214  			Expect(ev).To(HaveKeyWithValue("owner", "remote"))
   215  			Expect(ev).To(HaveKeyWithValue("application_code", float64(1337)))
   216  			Expect(ev).To(HaveKeyWithValue("reason", "foobar"))
   217  		})
   218  
   219  		It("records transport errors", func() {
   220  			tracer.ClosedConnection(&quic.TransportError{
   221  				ErrorCode:    qerr.AEADLimitReached,
   222  				ErrorMessage: "foobar",
   223  			})
   224  			entry := exportAndParseSingle()
   225  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   226  			Expect(entry.Name).To(Equal("transport:connection_closed"))
   227  			ev := entry.Event
   228  			Expect(ev).To(HaveLen(3))
   229  			Expect(ev).To(HaveKeyWithValue("owner", "local"))
   230  			Expect(ev).To(HaveKeyWithValue("connection_code", "aead_limit_reached"))
   231  			Expect(ev).To(HaveKeyWithValue("reason", "foobar"))
   232  		})
   233  
   234  		It("records sent transport parameters", func() {
   235  			rcid := protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad})
   236  			tracer.SentTransportParameters(&logging.TransportParameters{
   237  				InitialMaxStreamDataBidiLocal:   1000,
   238  				InitialMaxStreamDataBidiRemote:  2000,
   239  				InitialMaxStreamDataUni:         3000,
   240  				InitialMaxData:                  4000,
   241  				MaxBidiStreamNum:                10,
   242  				MaxUniStreamNum:                 20,
   243  				MaxAckDelay:                     123 * time.Millisecond,
   244  				AckDelayExponent:                12,
   245  				DisableActiveMigration:          true,
   246  				MaxUDPPayloadSize:               1234,
   247  				MaxIdleTimeout:                  321 * time.Millisecond,
   248  				StatelessResetToken:             &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00},
   249  				OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}),
   250  				InitialSourceConnectionID:       protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
   251  				RetrySourceConnectionID:         &rcid,
   252  				ActiveConnectionIDLimit:         7,
   253  				MaxDatagramFrameSize:            protocol.InvalidByteCount,
   254  			})
   255  			entry := exportAndParseSingle()
   256  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   257  			Expect(entry.Name).To(Equal("transport:parameters_set"))
   258  			ev := entry.Event
   259  			Expect(ev).To(HaveKeyWithValue("owner", "local"))
   260  			Expect(ev).To(HaveKeyWithValue("original_destination_connection_id", "deadc0de"))
   261  			Expect(ev).To(HaveKeyWithValue("initial_source_connection_id", "deadbeef"))
   262  			Expect(ev).To(HaveKeyWithValue("retry_source_connection_id", "decafbad"))
   263  			Expect(ev).To(HaveKeyWithValue("stateless_reset_token", "112233445566778899aabbccddeeff00"))
   264  			Expect(ev).To(HaveKeyWithValue("max_idle_timeout", float64(321)))
   265  			Expect(ev).To(HaveKeyWithValue("max_udp_payload_size", float64(1234)))
   266  			Expect(ev).To(HaveKeyWithValue("ack_delay_exponent", float64(12)))
   267  			Expect(ev).To(HaveKeyWithValue("active_connection_id_limit", float64(7)))
   268  			Expect(ev).To(HaveKeyWithValue("initial_max_data", float64(4000)))
   269  			Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_local", float64(1000)))
   270  			Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_remote", float64(2000)))
   271  			Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_uni", float64(3000)))
   272  			Expect(ev).To(HaveKeyWithValue("initial_max_streams_bidi", float64(10)))
   273  			Expect(ev).To(HaveKeyWithValue("initial_max_streams_uni", float64(20)))
   274  			Expect(ev).ToNot(HaveKey("preferred_address"))
   275  			Expect(ev).ToNot(HaveKey("max_datagram_frame_size"))
   276  		})
   277  
   278  		It("records the server's transport parameters, without a stateless reset token", func() {
   279  			tracer.SentTransportParameters(&logging.TransportParameters{
   280  				OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}),
   281  				ActiveConnectionIDLimit:         7,
   282  			})
   283  			entry := exportAndParseSingle()
   284  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   285  			Expect(entry.Name).To(Equal("transport:parameters_set"))
   286  			ev := entry.Event
   287  			Expect(ev).ToNot(HaveKey("stateless_reset_token"))
   288  		})
   289  
   290  		It("records transport parameters without retry_source_connection_id", func() {
   291  			tracer.SentTransportParameters(&logging.TransportParameters{
   292  				StatelessResetToken: &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00},
   293  			})
   294  			entry := exportAndParseSingle()
   295  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   296  			Expect(entry.Name).To(Equal("transport:parameters_set"))
   297  			ev := entry.Event
   298  			Expect(ev).To(HaveKeyWithValue("owner", "local"))
   299  			Expect(ev).ToNot(HaveKey("retry_source_connection_id"))
   300  		})
   301  
   302  		It("records transport parameters with a preferred address", func() {
   303  			tracer.SentTransportParameters(&logging.TransportParameters{
   304  				PreferredAddress: &logging.PreferredAddress{
   305  					IPv4:                netip.AddrPortFrom(netip.AddrFrom4([4]byte{12, 34, 56, 78}), 123),
   306  					IPv6:                netip.AddrPortFrom(netip.AddrFrom16([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 456),
   307  					ConnectionID:        protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}),
   308  					StatelessResetToken: protocol.StatelessResetToken{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
   309  				},
   310  			})
   311  			entry := exportAndParseSingle()
   312  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   313  			Expect(entry.Name).To(Equal("transport:parameters_set"))
   314  			ev := entry.Event
   315  			Expect(ev).To(HaveKeyWithValue("owner", "local"))
   316  			Expect(ev).To(HaveKey("preferred_address"))
   317  			pa := ev["preferred_address"].(map[string]interface{})
   318  			Expect(pa).To(HaveKeyWithValue("ip_v4", "12.34.56.78"))
   319  			Expect(pa).To(HaveKeyWithValue("port_v4", float64(123)))
   320  			Expect(pa).To(HaveKeyWithValue("ip_v6", "102:304:506:708:90a:b0c:d0e:f10"))
   321  			Expect(pa).To(HaveKeyWithValue("port_v6", float64(456)))
   322  			Expect(pa).To(HaveKeyWithValue("connection_id", "0807060504030201"))
   323  			Expect(pa).To(HaveKeyWithValue("stateless_reset_token", "0f0e0d0c0b0a09080706050403020100"))
   324  		})
   325  
   326  		It("records transport parameters that enable the datagram extension", func() {
   327  			tracer.SentTransportParameters(&logging.TransportParameters{
   328  				MaxDatagramFrameSize: 1337,
   329  			})
   330  			entry := exportAndParseSingle()
   331  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   332  			Expect(entry.Name).To(Equal("transport:parameters_set"))
   333  			ev := entry.Event
   334  			Expect(ev).To(HaveKeyWithValue("max_datagram_frame_size", float64(1337)))
   335  		})
   336  
   337  		It("records received transport parameters", func() {
   338  			tracer.ReceivedTransportParameters(&logging.TransportParameters{})
   339  			entry := exportAndParseSingle()
   340  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   341  			Expect(entry.Name).To(Equal("transport:parameters_set"))
   342  			ev := entry.Event
   343  			Expect(ev).To(HaveKeyWithValue("owner", "remote"))
   344  			Expect(ev).ToNot(HaveKey("original_destination_connection_id"))
   345  		})
   346  
   347  		It("records restored transport parameters", func() {
   348  			tracer.RestoredTransportParameters(&logging.TransportParameters{
   349  				InitialMaxStreamDataBidiLocal:  100,
   350  				InitialMaxStreamDataBidiRemote: 200,
   351  				InitialMaxStreamDataUni:        300,
   352  				InitialMaxData:                 400,
   353  				MaxIdleTimeout:                 123 * time.Millisecond,
   354  			})
   355  			entry := exportAndParseSingle()
   356  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   357  			Expect(entry.Name).To(Equal("transport:parameters_restored"))
   358  			ev := entry.Event
   359  			Expect(ev).ToNot(HaveKey("owner"))
   360  			Expect(ev).ToNot(HaveKey("original_destination_connection_id"))
   361  			Expect(ev).ToNot(HaveKey("stateless_reset_token"))
   362  			Expect(ev).ToNot(HaveKey("retry_source_connection_id"))
   363  			Expect(ev).ToNot(HaveKey("initial_source_connection_id"))
   364  			Expect(ev).To(HaveKeyWithValue("max_idle_timeout", float64(123)))
   365  			Expect(ev).To(HaveKeyWithValue("initial_max_data", float64(400)))
   366  			Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_local", float64(100)))
   367  			Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_remote", float64(200)))
   368  			Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_uni", float64(300)))
   369  		})
   370  
   371  		It("records a sent long header packet, without an ACK", func() {
   372  			tracer.SentLongHeaderPacket(
   373  				&logging.ExtendedHeader{
   374  					Header: logging.Header{
   375  						Type:             protocol.PacketTypeHandshake,
   376  						DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
   377  						SrcConnectionID:  protocol.ParseConnectionID([]byte{4, 3, 2, 1}),
   378  						Length:           1337,
   379  						Version:          protocol.Version1,
   380  					},
   381  					PacketNumber: 1337,
   382  				},
   383  				987,
   384  				logging.ECNCE,
   385  				nil,
   386  				[]logging.Frame{
   387  					&logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987},
   388  					&logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true},
   389  				},
   390  			)
   391  			entry := exportAndParseSingle()
   392  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   393  			Expect(entry.Name).To(Equal("transport:packet_sent"))
   394  			ev := entry.Event
   395  			Expect(ev).To(HaveKey("raw"))
   396  			raw := ev["raw"].(map[string]interface{})
   397  			Expect(raw).To(HaveKeyWithValue("length", float64(987)))
   398  			Expect(raw).To(HaveKeyWithValue("payload_length", float64(1337)))
   399  			Expect(ev).To(HaveKey("header"))
   400  			hdr := ev["header"].(map[string]interface{})
   401  			Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake"))
   402  			Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337)))
   403  			Expect(hdr).To(HaveKeyWithValue("scid", "04030201"))
   404  			Expect(ev).To(HaveKey("frames"))
   405  			Expect(ev).To(HaveKeyWithValue("ecn", "CE"))
   406  			frames := ev["frames"].([]interface{})
   407  			Expect(frames).To(HaveLen(2))
   408  			Expect(frames[0].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "max_stream_data"))
   409  			Expect(frames[1].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "stream"))
   410  		})
   411  
   412  		It("records a sent short header packet, without an ACK", func() {
   413  			tracer.SentShortHeaderPacket(
   414  				&logging.ShortHeader{
   415  					DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}),
   416  					PacketNumber:     1337,
   417  				},
   418  				123,
   419  				logging.ECNUnsupported,
   420  				&logging.AckFrame{AckRanges: []logging.AckRange{{Smallest: 1, Largest: 10}}},
   421  				[]logging.Frame{&logging.MaxDataFrame{MaximumData: 987}},
   422  			)
   423  			entry := exportAndParseSingle()
   424  			ev := entry.Event
   425  			raw := ev["raw"].(map[string]interface{})
   426  			Expect(raw).To(HaveKeyWithValue("length", float64(123)))
   427  			Expect(raw).ToNot(HaveKey("payload_length"))
   428  			Expect(ev).To(HaveKey("header"))
   429  			Expect(ev).ToNot(HaveKey("ecn"))
   430  			hdr := ev["header"].(map[string]interface{})
   431  			Expect(hdr).To(HaveKeyWithValue("packet_type", "1RTT"))
   432  			Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337)))
   433  			Expect(ev).To(HaveKey("frames"))
   434  			frames := ev["frames"].([]interface{})
   435  			Expect(frames).To(HaveLen(2))
   436  			Expect(frames[0].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "ack"))
   437  			Expect(frames[1].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "max_data"))
   438  		})
   439  
   440  		It("records a received Long Header packet", func() {
   441  			tracer.ReceivedLongHeaderPacket(
   442  				&logging.ExtendedHeader{
   443  					Header: logging.Header{
   444  						Type:             protocol.PacketTypeInitial,
   445  						DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
   446  						SrcConnectionID:  protocol.ParseConnectionID([]byte{4, 3, 2, 1}),
   447  						Token:            []byte{0xde, 0xad, 0xbe, 0xef},
   448  						Length:           1234,
   449  						Version:          protocol.Version1,
   450  					},
   451  					PacketNumber: 1337,
   452  				},
   453  				789,
   454  				logging.ECT0,
   455  				[]logging.Frame{
   456  					&logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987},
   457  					&logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true},
   458  				},
   459  			)
   460  			entry := exportAndParseSingle()
   461  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   462  			Expect(entry.Name).To(Equal("transport:packet_received"))
   463  			ev := entry.Event
   464  			Expect(ev).To(HaveKey("raw"))
   465  			raw := ev["raw"].(map[string]interface{})
   466  			Expect(raw).To(HaveKeyWithValue("length", float64(789)))
   467  			Expect(raw).To(HaveKeyWithValue("payload_length", float64(1234)))
   468  			Expect(ev).To(HaveKeyWithValue("ecn", "ECT(0)"))
   469  			Expect(ev).To(HaveKey("header"))
   470  			hdr := ev["header"].(map[string]interface{})
   471  			Expect(hdr).To(HaveKeyWithValue("packet_type", "initial"))
   472  			Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337)))
   473  			Expect(hdr).To(HaveKeyWithValue("scid", "04030201"))
   474  			Expect(hdr).To(HaveKey("token"))
   475  			token := hdr["token"].(map[string]interface{})
   476  			Expect(token).To(HaveKeyWithValue("data", "deadbeef"))
   477  			Expect(ev).To(HaveKey("frames"))
   478  			Expect(ev["frames"].([]interface{})).To(HaveLen(2))
   479  		})
   480  
   481  		It("records a received Short Header packet", func() {
   482  			shdr := &logging.ShortHeader{
   483  				DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
   484  				PacketNumber:     1337,
   485  				PacketNumberLen:  protocol.PacketNumberLen3,
   486  				KeyPhase:         protocol.KeyPhaseZero,
   487  			}
   488  			tracer.ReceivedShortHeaderPacket(
   489  				shdr,
   490  				789,
   491  				logging.ECT1,
   492  				[]logging.Frame{
   493  					&logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987},
   494  					&logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true},
   495  				},
   496  			)
   497  			entry := exportAndParseSingle()
   498  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   499  			Expect(entry.Name).To(Equal("transport:packet_received"))
   500  			ev := entry.Event
   501  			Expect(ev).To(HaveKey("raw"))
   502  			raw := ev["raw"].(map[string]interface{})
   503  			Expect(raw).To(HaveKeyWithValue("length", float64(789)))
   504  			Expect(raw).To(HaveKeyWithValue("payload_length", float64(789-(1+8+3))))
   505  			Expect(ev).To(HaveKeyWithValue("ecn", "ECT(1)"))
   506  			Expect(ev).To(HaveKey("header"))
   507  			hdr := ev["header"].(map[string]interface{})
   508  			Expect(hdr).To(HaveKeyWithValue("packet_type", "1RTT"))
   509  			Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337)))
   510  			Expect(hdr).To(HaveKeyWithValue("key_phase_bit", "0"))
   511  			Expect(ev).To(HaveKey("frames"))
   512  			Expect(ev["frames"].([]interface{})).To(HaveLen(2))
   513  		})
   514  
   515  		It("records a received Retry packet", func() {
   516  			tracer.ReceivedRetry(
   517  				&logging.Header{
   518  					Type:             protocol.PacketTypeRetry,
   519  					DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}),
   520  					SrcConnectionID:  protocol.ParseConnectionID([]byte{4, 3, 2, 1}),
   521  					Token:            []byte{0xde, 0xad, 0xbe, 0xef},
   522  					Version:          protocol.Version1,
   523  				},
   524  			)
   525  			entry := exportAndParseSingle()
   526  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   527  			Expect(entry.Name).To(Equal("transport:packet_received"))
   528  			ev := entry.Event
   529  			Expect(ev).ToNot(HaveKey("raw"))
   530  			Expect(ev).To(HaveKey("header"))
   531  			header := ev["header"].(map[string]interface{})
   532  			Expect(header).To(HaveKeyWithValue("packet_type", "retry"))
   533  			Expect(header).ToNot(HaveKey("packet_number"))
   534  			Expect(header).To(HaveKey("version"))
   535  			Expect(header).To(HaveKey("dcid"))
   536  			Expect(header).To(HaveKey("scid"))
   537  			Expect(header).To(HaveKey("token"))
   538  			token := header["token"].(map[string]interface{})
   539  			Expect(token).To(HaveKeyWithValue("data", "deadbeef"))
   540  			Expect(ev).ToNot(HaveKey("frames"))
   541  		})
   542  
   543  		It("records a received Version Negotiation packet", func() {
   544  			tracer.ReceivedVersionNegotiationPacket(
   545  				protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
   546  				protocol.ArbitraryLenConnectionID{4, 3, 2, 1},
   547  				[]protocol.Version{0xdeadbeef, 0xdecafbad},
   548  			)
   549  			entry := exportAndParseSingle()
   550  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   551  			Expect(entry.Name).To(Equal("transport:packet_received"))
   552  			ev := entry.Event
   553  			Expect(ev).To(HaveKey("header"))
   554  			Expect(ev).ToNot(HaveKey("frames"))
   555  			Expect(ev).To(HaveKey("supported_versions"))
   556  			Expect(ev["supported_versions"].([]interface{})).To(Equal([]interface{}{"deadbeef", "decafbad"}))
   557  			header := ev["header"]
   558  			Expect(header).To(HaveKeyWithValue("packet_type", "version_negotiation"))
   559  			Expect(header).ToNot(HaveKey("packet_number"))
   560  			Expect(header).ToNot(HaveKey("version"))
   561  			Expect(header).To(HaveKeyWithValue("dcid", "0102030405060708"))
   562  			Expect(header).To(HaveKeyWithValue("scid", "04030201"))
   563  		})
   564  
   565  		It("records buffered packets", func() {
   566  			tracer.BufferedPacket(logging.PacketTypeHandshake, 1337)
   567  			entry := exportAndParseSingle()
   568  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   569  			Expect(entry.Name).To(Equal("transport:packet_buffered"))
   570  			ev := entry.Event
   571  			Expect(ev).To(HaveKey("header"))
   572  			hdr := ev["header"].(map[string]interface{})
   573  			Expect(hdr).To(HaveLen(1))
   574  			Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake"))
   575  			Expect(ev).To(HaveKey("raw"))
   576  			Expect(ev["raw"].(map[string]interface{})).To(HaveKeyWithValue("length", float64(1337)))
   577  			Expect(ev).To(HaveKeyWithValue("trigger", "keys_unavailable"))
   578  		})
   579  
   580  		It("records dropped packets", func() {
   581  			tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, 1337, logging.PacketDropPayloadDecryptError)
   582  			entry := exportAndParseSingle()
   583  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   584  			Expect(entry.Name).To(Equal("transport:packet_dropped"))
   585  			ev := entry.Event
   586  			Expect(ev).To(HaveKey("raw"))
   587  			Expect(ev["raw"].(map[string]interface{})).To(HaveKeyWithValue("length", float64(1337)))
   588  			Expect(ev).To(HaveKey("header"))
   589  			hdr := ev["header"].(map[string]interface{})
   590  			Expect(hdr).To(HaveLen(1))
   591  			Expect(hdr).To(HaveKeyWithValue("packet_type", "retry"))
   592  			Expect(ev).To(HaveKeyWithValue("trigger", "payload_decrypt_error"))
   593  		})
   594  
   595  		It("records dropped packets with a packet number", func() {
   596  			tracer.DroppedPacket(logging.PacketTypeHandshake, 42, 1337, logging.PacketDropDuplicate)
   597  			entry := exportAndParseSingle()
   598  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   599  			Expect(entry.Name).To(Equal("transport:packet_dropped"))
   600  			ev := entry.Event
   601  			Expect(ev).To(HaveKey("raw"))
   602  			Expect(ev["raw"].(map[string]interface{})).To(HaveKeyWithValue("length", float64(1337)))
   603  			Expect(ev).To(HaveKey("header"))
   604  			hdr := ev["header"].(map[string]interface{})
   605  			Expect(hdr).To(HaveLen(2))
   606  			Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake"))
   607  			Expect(hdr).To(HaveKeyWithValue("packet_number", float64(42)))
   608  			Expect(ev).To(HaveKeyWithValue("trigger", "duplicate"))
   609  		})
   610  
   611  		It("records metrics updates", func() {
   612  			now := time.Now()
   613  			rttStats := utils.NewRTTStats()
   614  			rttStats.UpdateRTT(15*time.Millisecond, 0, now)
   615  			rttStats.UpdateRTT(20*time.Millisecond, 0, now)
   616  			rttStats.UpdateRTT(25*time.Millisecond, 0, now)
   617  			Expect(rttStats.MinRTT()).To(Equal(15 * time.Millisecond))
   618  			Expect(rttStats.SmoothedRTT()).To(And(
   619  				BeNumerically(">", 15*time.Millisecond),
   620  				BeNumerically("<", 25*time.Millisecond),
   621  			))
   622  			Expect(rttStats.LatestRTT()).To(Equal(25 * time.Millisecond))
   623  			tracer.UpdatedMetrics(
   624  				rttStats,
   625  				4321,
   626  				1234,
   627  				42,
   628  			)
   629  			entry := exportAndParseSingle()
   630  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   631  			Expect(entry.Name).To(Equal("recovery:metrics_updated"))
   632  			ev := entry.Event
   633  			Expect(ev).To(HaveKeyWithValue("min_rtt", float64(15)))
   634  			Expect(ev).To(HaveKeyWithValue("latest_rtt", float64(25)))
   635  			Expect(ev).To(HaveKey("smoothed_rtt"))
   636  			Expect(time.Duration(ev["smoothed_rtt"].(float64)) * time.Millisecond).To(BeNumerically("~", rttStats.SmoothedRTT(), time.Millisecond))
   637  			Expect(ev).To(HaveKey("rtt_variance"))
   638  			Expect(time.Duration(ev["rtt_variance"].(float64)) * time.Millisecond).To(BeNumerically("~", rttStats.MeanDeviation(), time.Millisecond))
   639  			Expect(ev).To(HaveKeyWithValue("congestion_window", float64(4321)))
   640  			Expect(ev).To(HaveKeyWithValue("bytes_in_flight", float64(1234)))
   641  			Expect(ev).To(HaveKeyWithValue("packets_in_flight", float64(42)))
   642  		})
   643  
   644  		It("only logs the diff between two metrics updates", func() {
   645  			now := time.Now()
   646  			rttStats := utils.NewRTTStats()
   647  			rttStats.UpdateRTT(15*time.Millisecond, 0, now)
   648  			rttStats.UpdateRTT(20*time.Millisecond, 0, now)
   649  			rttStats.UpdateRTT(25*time.Millisecond, 0, now)
   650  			Expect(rttStats.MinRTT()).To(Equal(15 * time.Millisecond))
   651  
   652  			rttStats2 := utils.NewRTTStats()
   653  			rttStats2.UpdateRTT(15*time.Millisecond, 0, now)
   654  			rttStats2.UpdateRTT(15*time.Millisecond, 0, now)
   655  			rttStats2.UpdateRTT(15*time.Millisecond, 0, now)
   656  			Expect(rttStats2.MinRTT()).To(Equal(15 * time.Millisecond))
   657  
   658  			Expect(rttStats.LatestRTT()).To(Equal(25 * time.Millisecond))
   659  			tracer.UpdatedMetrics(
   660  				rttStats,
   661  				4321,
   662  				1234,
   663  				42,
   664  			)
   665  			tracer.UpdatedMetrics(
   666  				rttStats2,
   667  				4321,
   668  				12345, // changed
   669  				42,
   670  			)
   671  			entries := exportAndParse()
   672  			Expect(entries).To(HaveLen(2))
   673  			Expect(entries[0].Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   674  			Expect(entries[0].Name).To(Equal("recovery:metrics_updated"))
   675  			Expect(entries[0].Event).To(HaveLen(7))
   676  			Expect(entries[1].Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   677  			Expect(entries[1].Name).To(Equal("recovery:metrics_updated"))
   678  			ev := entries[1].Event
   679  			Expect(ev).ToNot(HaveKey("min_rtt"))
   680  			Expect(ev).ToNot(HaveKey("congestion_window"))
   681  			Expect(ev).ToNot(HaveKey("packets_in_flight"))
   682  			Expect(ev).To(HaveKeyWithValue("bytes_in_flight", float64(12345)))
   683  			Expect(ev).To(HaveKeyWithValue("smoothed_rtt", float64(15)))
   684  		})
   685  
   686  		It("records lost packets", func() {
   687  			tracer.LostPacket(protocol.EncryptionHandshake, 42, logging.PacketLossReorderingThreshold)
   688  			entry := exportAndParseSingle()
   689  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   690  			Expect(entry.Name).To(Equal("recovery:packet_lost"))
   691  			ev := entry.Event
   692  			Expect(ev).To(HaveKey("header"))
   693  			hdr := ev["header"].(map[string]interface{})
   694  			Expect(hdr).To(HaveLen(2))
   695  			Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake"))
   696  			Expect(hdr).To(HaveKeyWithValue("packet_number", float64(42)))
   697  			Expect(ev).To(HaveKeyWithValue("trigger", "reordering_threshold"))
   698  		})
   699  
   700  		It("records congestion state updates", func() {
   701  			tracer.UpdatedCongestionState(logging.CongestionStateCongestionAvoidance)
   702  			entry := exportAndParseSingle()
   703  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   704  			Expect(entry.Name).To(Equal("recovery:congestion_state_updated"))
   705  			ev := entry.Event
   706  			Expect(ev).To(HaveKeyWithValue("new", "congestion_avoidance"))
   707  		})
   708  
   709  		It("records PTO changes", func() {
   710  			tracer.UpdatedPTOCount(42)
   711  			entry := exportAndParseSingle()
   712  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   713  			Expect(entry.Name).To(Equal("recovery:metrics_updated"))
   714  			Expect(entry.Event).To(HaveKeyWithValue("pto_count", float64(42)))
   715  		})
   716  
   717  		It("records TLS key updates", func() {
   718  			tracer.UpdatedKeyFromTLS(protocol.EncryptionHandshake, protocol.PerspectiveClient)
   719  			entry := exportAndParseSingle()
   720  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   721  			Expect(entry.Name).To(Equal("security:key_updated"))
   722  			ev := entry.Event
   723  			Expect(ev).To(HaveKeyWithValue("key_type", "client_handshake_secret"))
   724  			Expect(ev).To(HaveKeyWithValue("trigger", "tls"))
   725  			Expect(ev).ToNot(HaveKey("key_phase"))
   726  			Expect(ev).ToNot(HaveKey("old"))
   727  			Expect(ev).ToNot(HaveKey("new"))
   728  		})
   729  
   730  		It("records TLS key updates, for 1-RTT keys", func() {
   731  			tracer.UpdatedKeyFromTLS(protocol.Encryption1RTT, protocol.PerspectiveServer)
   732  			entry := exportAndParseSingle()
   733  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   734  			Expect(entry.Name).To(Equal("security:key_updated"))
   735  			ev := entry.Event
   736  			Expect(ev).To(HaveKeyWithValue("key_type", "server_1rtt_secret"))
   737  			Expect(ev).To(HaveKeyWithValue("trigger", "tls"))
   738  			Expect(ev).To(HaveKeyWithValue("key_phase", float64(0)))
   739  			Expect(ev).ToNot(HaveKey("old"))
   740  			Expect(ev).ToNot(HaveKey("new"))
   741  		})
   742  
   743  		It("records QUIC key updates", func() {
   744  			tracer.UpdatedKey(1337, true)
   745  			entries := exportAndParse()
   746  			Expect(entries).To(HaveLen(2))
   747  			var keyTypes []string
   748  			for _, entry := range entries {
   749  				Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   750  				Expect(entry.Name).To(Equal("security:key_updated"))
   751  				ev := entry.Event
   752  				Expect(ev).To(HaveKeyWithValue("key_phase", float64(1337)))
   753  				Expect(ev).To(HaveKeyWithValue("trigger", "remote_update"))
   754  				Expect(ev).To(HaveKey("key_type"))
   755  				keyTypes = append(keyTypes, ev["key_type"].(string))
   756  			}
   757  			Expect(keyTypes).To(ContainElement("server_1rtt_secret"))
   758  			Expect(keyTypes).To(ContainElement("client_1rtt_secret"))
   759  		})
   760  
   761  		It("records dropped encryption levels", func() {
   762  			tracer.DroppedEncryptionLevel(protocol.EncryptionInitial)
   763  			entries := exportAndParse()
   764  			Expect(entries).To(HaveLen(2))
   765  			var keyTypes []string
   766  			for _, entry := range entries {
   767  				Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   768  				Expect(entry.Name).To(Equal("security:key_discarded"))
   769  				ev := entry.Event
   770  				Expect(ev).To(HaveKeyWithValue("trigger", "tls"))
   771  				Expect(ev).To(HaveKey("key_type"))
   772  				keyTypes = append(keyTypes, ev["key_type"].(string))
   773  			}
   774  			Expect(keyTypes).To(ContainElement("server_initial_secret"))
   775  			Expect(keyTypes).To(ContainElement("client_initial_secret"))
   776  		})
   777  
   778  		It("records dropped 0-RTT keys", func() {
   779  			tracer.DroppedEncryptionLevel(protocol.Encryption0RTT)
   780  			entries := exportAndParse()
   781  			Expect(entries).To(HaveLen(1))
   782  			entry := entries[0]
   783  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   784  			Expect(entry.Name).To(Equal("security:key_discarded"))
   785  			ev := entry.Event
   786  			Expect(ev).To(HaveKeyWithValue("trigger", "tls"))
   787  			Expect(ev).To(HaveKeyWithValue("key_type", "server_0rtt_secret"))
   788  		})
   789  
   790  		It("records dropped keys", func() {
   791  			tracer.DroppedKey(42)
   792  			entries := exportAndParse()
   793  			Expect(entries).To(HaveLen(2))
   794  			var keyTypes []string
   795  			for _, entry := range entries {
   796  				Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   797  				Expect(entry.Name).To(Equal("security:key_discarded"))
   798  				ev := entry.Event
   799  				Expect(ev).To(HaveKeyWithValue("key_phase", float64(42)))
   800  				Expect(ev).ToNot(HaveKey("trigger"))
   801  				Expect(ev).To(HaveKey("key_type"))
   802  				keyTypes = append(keyTypes, ev["key_type"].(string))
   803  			}
   804  			Expect(keyTypes).To(ContainElement("server_1rtt_secret"))
   805  			Expect(keyTypes).To(ContainElement("client_1rtt_secret"))
   806  		})
   807  
   808  		It("records when the timer is set", func() {
   809  			timeout := time.Now().Add(137 * time.Millisecond)
   810  			tracer.SetLossTimer(logging.TimerTypePTO, protocol.EncryptionHandshake, timeout)
   811  			entry := exportAndParseSingle()
   812  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   813  			Expect(entry.Name).To(Equal("recovery:loss_timer_updated"))
   814  			ev := entry.Event
   815  			Expect(ev).To(HaveLen(4))
   816  			Expect(ev).To(HaveKeyWithValue("event_type", "set"))
   817  			Expect(ev).To(HaveKeyWithValue("timer_type", "pto"))
   818  			Expect(ev).To(HaveKeyWithValue("packet_number_space", "handshake"))
   819  			Expect(ev).To(HaveKey("delta"))
   820  			delta := time.Duration(ev["delta"].(float64)*1e6) * time.Nanosecond
   821  			Expect(entry.Time.Add(delta)).To(BeTemporally("~", timeout, scaleDuration(10*time.Microsecond)))
   822  		})
   823  
   824  		It("records when the loss timer expires", func() {
   825  			tracer.LossTimerExpired(logging.TimerTypeACK, protocol.Encryption1RTT)
   826  			entry := exportAndParseSingle()
   827  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   828  			Expect(entry.Name).To(Equal("recovery:loss_timer_updated"))
   829  			ev := entry.Event
   830  			Expect(ev).To(HaveLen(3))
   831  			Expect(ev).To(HaveKeyWithValue("event_type", "expired"))
   832  			Expect(ev).To(HaveKeyWithValue("timer_type", "ack"))
   833  			Expect(ev).To(HaveKeyWithValue("packet_number_space", "application_data"))
   834  		})
   835  
   836  		It("records when the timer is canceled", func() {
   837  			tracer.LossTimerCanceled()
   838  			entry := exportAndParseSingle()
   839  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   840  			Expect(entry.Name).To(Equal("recovery:loss_timer_updated"))
   841  			ev := entry.Event
   842  			Expect(ev).To(HaveLen(1))
   843  			Expect(ev).To(HaveKeyWithValue("event_type", "cancelled"))
   844  		})
   845  
   846  		It("records an ECN state transition, without a trigger", func() {
   847  			tracer.ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger)
   848  			entry := exportAndParseSingle()
   849  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   850  			Expect(entry.Name).To(Equal("recovery:ecn_state_updated"))
   851  			ev := entry.Event
   852  			Expect(ev).To(HaveLen(1))
   853  			Expect(ev).To(HaveKeyWithValue("new", "unknown"))
   854  		})
   855  
   856  		It("records an ECN state transition, with a trigger", func() {
   857  			tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts)
   858  			entry := exportAndParseSingle()
   859  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   860  			Expect(entry.Name).To(Equal("recovery:ecn_state_updated"))
   861  			ev := entry.Event
   862  			Expect(ev).To(HaveLen(2))
   863  			Expect(ev).To(HaveKeyWithValue("new", "failed"))
   864  			Expect(ev).To(HaveKeyWithValue("trigger", "ACK doesn't contain ECN marks"))
   865  		})
   866  
   867  		It("records a generic event", func() {
   868  			tracer.Debug("foo", "bar")
   869  			entry := exportAndParseSingle()
   870  			Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond)))
   871  			Expect(entry.Name).To(Equal("transport:foo"))
   872  			ev := entry.Event
   873  			Expect(ev).To(HaveLen(1))
   874  			Expect(ev).To(HaveKeyWithValue("details", "bar"))
   875  		})
   876  	})
   877  })