implement marshalling of the trace

This commit is contained in:
Marten Seemann
2020-01-21 12:51:01 +07:00
parent bfd745106c
commit 4763719051
3 changed files with 182 additions and 0 deletions

50
qlog/qlog.go Normal file
View File

@@ -0,0 +1,50 @@
package qlog
import (
"io"
"github.com/francoispqt/gojay"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
// A Tracer records events to be exported to a qlog.
type Tracer interface {
Export() error
}
type tracer struct {
w io.WriteCloser
odcid protocol.ConnectionID
perspective protocol.Perspective
events []event
}
var _ Tracer = &tracer{}
// NewTracer creates a new tracer to record a qlog.
func NewTracer(w io.WriteCloser, p protocol.Perspective, odcid protocol.ConnectionID) Tracer {
return &tracer{
w: w,
perspective: p,
odcid: odcid,
}
}
// Export writes a qlog.
func (t *tracer) Export() error {
enc := gojay.NewEncoder(t.w)
tl := &topLevel{
traces: traces{
{
VantagePoint: vantagePoint{Type: t.perspective},
CommonFields: commonFields{ODCID: connectionID(t.odcid), GroupID: connectionID(t.odcid)},
EventFields: eventFields[:],
Events: t.events,
},
}}
if err := enc.Encode(tl); err != nil {
return err
}
return t.w.Close()
}

60
qlog/qlog_test.go Normal file
View File

@@ -0,0 +1,60 @@
package qlog
import (
"bytes"
"encoding/json"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type nopWriteCloserImpl struct{ io.Writer }
func (nopWriteCloserImpl) Close() error { return nil }
func nopWriteCloser(w io.Writer) io.WriteCloser {
return &nopWriteCloserImpl{Writer: w}
}
var _ = Describe("Tracer", func() {
var (
tracer Tracer
buf *bytes.Buffer
)
BeforeEach(func() {
buf = &bytes.Buffer{}
tracer = NewTracer(
nopWriteCloser(buf),
protocol.PerspectiveServer,
protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
)
})
It("exports a trace that has the right metadata", func() {
Expect(tracer.Export()).To(Succeed())
m := make(map[string]interface{})
Expect(json.Unmarshal(buf.Bytes(), &m)).To(Succeed())
Expect(m).To(HaveKeyWithValue("qlog_version", "draft-02-wip"))
Expect(m).To(HaveKey("title"))
Expect(m).To(HaveKey("traces"))
traces := m["traces"].([]interface{})
Expect(traces).To(HaveLen(1))
trace := traces[0].(map[string]interface{})
Expect(trace).To(HaveKey(("common_fields")))
commonFields := trace["common_fields"].(map[string]interface{})
Expect(commonFields).To(HaveKeyWithValue("ODCID", "deadbeef"))
Expect(commonFields).To(HaveKeyWithValue("group_id", "deadbeef"))
Expect(trace).To(HaveKey("event_fields"))
for i, ef := range trace["event_fields"].([]interface{}) {
Expect(ef.(string)).To(Equal(eventFields[i]))
}
Expect(trace).To(HaveKey("vantage_point"))
vantagePoint := trace["vantage_point"].(map[string]interface{})
Expect(vantagePoint).To(HaveKeyWithValue("type", "server"))
})
})

72
qlog/trace.go Normal file
View File

@@ -0,0 +1,72 @@
package qlog
import (
"github.com/francoispqt/gojay"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
type topLevel struct {
traces traces
}
func (topLevel) IsNil() bool { return false }
func (l topLevel) MarshalJSONObject(enc *gojay.Encoder) {
enc.StringKey("qlog_version", "draft-02-wip")
enc.StringKeyOmitEmpty("title", "quic-go qlog")
enc.ArrayKey("traces", l.traces)
}
type vantagePoint struct {
Name string
Type protocol.Perspective
}
func (p vantagePoint) IsNil() bool { return false }
func (p vantagePoint) MarshalJSONObject(enc *gojay.Encoder) {
enc.StringKeyOmitEmpty("name", p.Name)
switch p.Type {
case protocol.PerspectiveClient:
enc.StringKey("type", "client")
case protocol.PerspectiveServer:
enc.StringKey("type", "server")
}
}
type commonFields struct {
ODCID connectionID
GroupID connectionID
ProtocolType string
}
func (f commonFields) MarshalJSONObject(enc *gojay.Encoder) {
enc.StringKey("ODCID", f.ODCID.String())
enc.StringKey("group_id", f.ODCID.String())
enc.StringKeyOmitEmpty("protocol_type", f.ProtocolType)
}
func (f commonFields) IsNil() bool { return false }
type traces []trace
func (t traces) IsNil() bool { return t == nil }
func (t traces) MarshalJSONArray(enc *gojay.Encoder) {
for _, tr := range t {
enc.Object(tr)
}
}
type trace struct {
VantagePoint vantagePoint
CommonFields commonFields
EventFields []string
Events events
}
func (trace) IsNil() bool { return false }
func (t trace) MarshalJSONObject(enc *gojay.Encoder) {
enc.ObjectKey("vantage_point", t.VantagePoint)
enc.ObjectKey("common_fields", t.CommonFields)
enc.SliceStringKey("event_fields", t.EventFields)
enc.ArrayKey("events", t.Events)
}