forked from quic-go/quic-go
qlog: split serializiation and event definitions, remove logging abstraction (#5356)
* qlog: implement a Trace and a Writer struct * qlog: rename Trace to FileSeq * split qlog trace writer and QUIC qlog events into separate packages * use the new qlog.Recorder instead of the logging.ConnectionTracer
This commit is contained in:
90
testutils/events/event_recorder.go
Normal file
90
testutils/events/event_recorder.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go/qlogwriter"
|
||||
)
|
||||
|
||||
// Event is a recorded event with the event time.
|
||||
type Event struct {
|
||||
Time time.Time
|
||||
Event qlogwriter.Event
|
||||
}
|
||||
|
||||
// Trace is a qlog.Trace that returns a qlog recorder.
|
||||
type Trace struct {
|
||||
Recorder qlogwriter.Recorder
|
||||
}
|
||||
|
||||
var _ qlogwriter.Trace = &Trace{}
|
||||
|
||||
func (t *Trace) AddProducer() qlogwriter.Recorder {
|
||||
return t.Recorder
|
||||
}
|
||||
|
||||
// Recorder is a qlog.Recorder that records events.
|
||||
// Events can be retrieved using the Events method.
|
||||
type Recorder struct {
|
||||
mx sync.Mutex
|
||||
events []Event
|
||||
}
|
||||
|
||||
var _ qlogwriter.Recorder = &Recorder{}
|
||||
|
||||
// Events returns all recorded events.
|
||||
// If filter is provided, only events of the given type(s) are returned.
|
||||
func (r *Recorder) RecordEvent(ev qlogwriter.Event) {
|
||||
r.mx.Lock()
|
||||
r.events = append(r.events, Event{Time: time.Now(), Event: ev})
|
||||
r.mx.Unlock()
|
||||
}
|
||||
|
||||
// Events returns all recorded events, including the event time.
|
||||
// If filter is provided, only events of the given type(s) are returned.
|
||||
func (r *Recorder) Events(filter ...qlogwriter.Event) []qlogwriter.Event {
|
||||
eventsWithTime := r.EventsWithTime(filter...)
|
||||
events := make([]qlogwriter.Event, 0, len(eventsWithTime))
|
||||
for _, ev := range eventsWithTime {
|
||||
events = append(events, ev.Event)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func (r *Recorder) EventsWithTime(filter ...qlogwriter.Event) []Event {
|
||||
r.mx.Lock()
|
||||
events := r.events
|
||||
r.mx.Unlock()
|
||||
|
||||
if len(filter) == 0 {
|
||||
return events
|
||||
}
|
||||
|
||||
// Some events have the same name when serialized, but use different structs.
|
||||
// We therefore need to filter by type, and can't use the event name.
|
||||
filterTypes := make([]reflect.Type, 0, len(filter))
|
||||
for _, f := range filter {
|
||||
filterTypes = append(filterTypes, reflect.TypeOf(f))
|
||||
}
|
||||
|
||||
var filtered []Event
|
||||
for _, ev := range events {
|
||||
eventType := reflect.TypeOf(ev.Event)
|
||||
if slices.Contains(filterTypes, eventType) {
|
||||
filtered = append(filtered, ev)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Clear clears the recorded events.
|
||||
func (r *Recorder) Clear() {
|
||||
r.mx.Lock()
|
||||
r.events = nil
|
||||
r.mx.Unlock()
|
||||
}
|
||||
|
||||
func (r *Recorder) Close() error { return nil }
|
||||
101
testutils/events/event_recorder_test.go
Normal file
101
testutils/events/event_recorder_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/synctest"
|
||||
"github.com/quic-go/quic-go/qlog"
|
||||
"github.com/quic-go/quic-go/qlogwriter"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRecorder(t *testing.T) {
|
||||
recorder := &Recorder{}
|
||||
defer recorder.Close()
|
||||
|
||||
recorder.RecordEvent(qlog.MTUUpdated{Value: 1000})
|
||||
recorder.RecordEvent(qlog.ALPNInformation{ChosenALPN: "foobar"})
|
||||
recorder.RecordEvent(qlog.ECNStateUpdated{State: qlog.ECNStateCapable})
|
||||
recorder.RecordEvent(qlog.MTUUpdated{Value: 1200})
|
||||
|
||||
require.Equal(t,
|
||||
[]qlogwriter.Event{
|
||||
qlog.MTUUpdated{Value: 1000},
|
||||
qlog.ALPNInformation{ChosenALPN: "foobar"},
|
||||
qlog.ECNStateUpdated{State: qlog.ECNStateCapable},
|
||||
qlog.MTUUpdated{Value: 1200},
|
||||
},
|
||||
recorder.Events(),
|
||||
)
|
||||
|
||||
require.Empty(t, recorder.Events(qlog.PacketBuffered{}))
|
||||
require.Equal(t,
|
||||
[]qlogwriter.Event{
|
||||
qlog.MTUUpdated{Value: 1000},
|
||||
qlog.MTUUpdated{Value: 1200},
|
||||
},
|
||||
recorder.Events(qlog.MTUUpdated{}),
|
||||
)
|
||||
|
||||
recorder.Clear()
|
||||
require.Empty(t, recorder.Events())
|
||||
require.Empty(t, recorder.Events(qlog.MTUUpdated{}))
|
||||
}
|
||||
|
||||
func TestRecorderFilterEventsSameName(t *testing.T) {
|
||||
// some events have the same name when serialized, but use different structs
|
||||
require.Equal(t,
|
||||
qlog.PacketReceived{}.Name(),
|
||||
qlog.VersionNegotiationReceived{}.Name(),
|
||||
)
|
||||
|
||||
recorder := &Recorder{}
|
||||
defer recorder.Close()
|
||||
|
||||
recorder.RecordEvent(qlog.PacketReceived{
|
||||
Header: qlog.PacketHeader{PacketType: qlog.PacketTypeHandshake},
|
||||
})
|
||||
recorder.RecordEvent(qlog.VersionNegotiationReceived{
|
||||
Header: qlog.PacketHeaderVersionNegotiation{},
|
||||
SupportedVersions: []qlog.Version{0xdeadbeef, 0xdecafbad},
|
||||
})
|
||||
|
||||
require.Equal(t,
|
||||
[]qlogwriter.Event{
|
||||
qlog.PacketReceived{
|
||||
Header: qlog.PacketHeader{PacketType: qlog.PacketTypeHandshake},
|
||||
},
|
||||
},
|
||||
recorder.Events(qlog.PacketReceived{}),
|
||||
)
|
||||
require.Equal(t,
|
||||
[]qlogwriter.Event{
|
||||
qlog.VersionNegotiationReceived{
|
||||
Header: qlog.PacketHeaderVersionNegotiation{},
|
||||
SupportedVersions: []qlog.Version{0xdeadbeef, 0xdecafbad},
|
||||
},
|
||||
},
|
||||
recorder.Events(qlog.VersionNegotiationReceived{}),
|
||||
)
|
||||
}
|
||||
|
||||
func TestRecorderEventsWithTime(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
recorder := &Recorder{}
|
||||
start := time.Now()
|
||||
recorder.RecordEvent(qlog.MTUUpdated{Value: 1000})
|
||||
time.Sleep(time.Minute)
|
||||
recorder.RecordEvent(qlog.ECNStateUpdated{State: qlog.ECNStateCapable})
|
||||
time.Sleep(time.Minute)
|
||||
recorder.RecordEvent(qlog.MTUUpdated{Value: 1200})
|
||||
|
||||
require.Equal(t,
|
||||
[]Event{
|
||||
{Time: start, Event: qlog.MTUUpdated{Value: 1000}},
|
||||
{Time: start.Add(2 * time.Minute), Event: qlog.MTUUpdated{Value: 1200}},
|
||||
},
|
||||
recorder.EventsWithTime(qlog.MTUUpdated{}),
|
||||
)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user