diff --git a/integrationtests/tools/qlog.go b/integrationtests/tools/qlog.go index 8527ffe7d..19917f25e 100644 --- a/integrationtests/tools/qlog.go +++ b/integrationtests/tools/qlog.go @@ -11,6 +11,7 @@ import ( "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/qlog" "github.com/quic-go/quic-go/qlogwriter" ) @@ -41,7 +42,12 @@ func NewQlogConnectionTracer(logger io.Writer) func(ctx context.Context, isClien log.Fatalf("failed to create qlog file: %s", err) return nil } - fileSeq := qlogwriter.NewConnectionFileSeq(utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), isClient, connID) + fileSeq := qlogwriter.NewConnectionFileSeq( + utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), + isClient, + connID, + []string{qlog.EventSchema}, + ) go fileSeq.Run() return fileSeq } diff --git a/interop/utils/logging.go b/interop/utils/logging.go index 405a93a35..53bac8d2d 100644 --- a/interop/utils/logging.go +++ b/interop/utils/logging.go @@ -11,6 +11,7 @@ import ( "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/qlog" "github.com/quic-go/quic-go/qlogwriter" ) @@ -45,7 +46,12 @@ func NewQLOGConnectionTracer(_ context.Context, isClient bool, connID quic.Conne return nil } log.Printf("Created qlog file: %s\n", path) - fileSeq := qlogwriter.NewConnectionFileSeq(utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), isClient, connID) + fileSeq := qlogwriter.NewConnectionFileSeq( + utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), + isClient, + connID, + []string{qlog.EventSchema}, + ) go fileSeq.Run() return fileSeq } diff --git a/qlog/benchmark_test.go b/qlog/benchmark_test.go index a16f6bee0..caf40b417 100644 --- a/qlog/benchmark_test.go +++ b/qlog/benchmark_test.go @@ -28,6 +28,7 @@ func BenchmarkConnectionTracing(b *testing.B) { nopWriteCloser(io.Discard), false, protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), + []string{EventSchema}, ) go trace.Run() tracer := trace.AddProducer() diff --git a/qlog/qlog_dir.go b/qlog/qlog_dir.go index 12f009377..c014a1c3f 100644 --- a/qlog/qlog_dir.go +++ b/qlog/qlog_dir.go @@ -12,6 +12,9 @@ import ( "github.com/quic-go/quic-go/qlogwriter" ) +// EventSchema is the qlog event schema for QUIC +const EventSchema = "urn:ietf:params:qlog:events:quic-12" + // DefaultConnectionTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable. // File names are _.sqlog. // Returns nil if QLOGDIR is not set. @@ -35,7 +38,12 @@ func DefaultConnectionTracer(_ context.Context, isClient bool, connID Connection log.Printf("Failed to create qlog file %s: %s", path, err.Error()) return nil } - fileSeq := qlogwriter.NewConnectionFileSeq(utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), isClient, connID) + fileSeq := qlogwriter.NewConnectionFileSeq( + utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), + isClient, + connID, + []string{EventSchema}, + ) go fileSeq.Run() return fileSeq } diff --git a/qlogwriter/trace.go b/qlogwriter/trace.go index cba8b2988..2b09bc788 100644 --- a/qlogwriter/trace.go +++ b/qlogwriter/trace.go @@ -42,6 +42,7 @@ type traceHeader struct { VantagePointType string GroupID *ConnectionID ReferenceTime time.Time + EventSchemas []string } func (l traceHeader) Encode(enc *jsontext.Encoder) error { @@ -59,6 +60,15 @@ func (l traceHeader) Encode(enc *jsontext.Encoder) error { h.WriteToken(jsontext.String("trace")) // trace h.WriteToken(jsontext.BeginObject) + if len(l.EventSchemas) > 0 { + h.WriteToken(jsontext.String("event_schemas")) + h.WriteToken(jsontext.BeginArray) + for _, schema := range l.EventSchemas { + h.WriteToken(jsontext.String(schema)) + } + h.WriteToken(jsontext.EndArray) + } + h.WriteToken(jsontext.String("vantage_point")) // -- vantage_point h.WriteToken(jsontext.BeginObject) diff --git a/qlogwriter/trace_test.go b/qlogwriter/trace_test.go index b55e141f0..69d018b1c 100644 --- a/qlogwriter/trace_test.go +++ b/qlogwriter/trace_test.go @@ -48,7 +48,7 @@ func TestTraceMetadata(t *testing.T) { producer := trace.AddProducer() producer.Close() - testTraceMetadata(t, buf, "transport", "") + testTraceMetadata(t, buf, "transport", "", []string{}) }) t.Run("connection trace", func(t *testing.T) { @@ -57,16 +57,27 @@ func TestTraceMetadata(t *testing.T) { nopWriteCloser(buf), false, protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), + []string{"urn:ietf:params:qlog:events:foo", "urn:ietf:params:qlog:events:bar"}, ) go trace.Run() producer := trace.AddProducer() producer.Close() - testTraceMetadata(t, buf, "server", "deadbeef") + testTraceMetadata(t, + buf, + "server", + "deadbeef", + []string{"urn:ietf:params:qlog:events:foo", "urn:ietf:params:qlog:events:bar"}, + ) }) } -func testTraceMetadata(t *testing.T, buf *bytes.Buffer, expectedVantagePoint, expectedGroupID string) { +func testTraceMetadata(t *testing.T, + buf *bytes.Buffer, + expectedVantagePoint, + expectedGroupID string, + expectedEventSchemas []string, +) { var m map[string]any require.NoError(t, unmarshal(buf.Bytes(), &m)) require.Equal(t, "0.3", m["qlog_version"]) @@ -95,4 +106,13 @@ func testTraceMetadata(t *testing.T, buf *bytes.Buffer, expectedVantagePoint, ex require.Contains(t, tr, "vantage_point") vantagePoint := tr["vantage_point"].(map[string]any) require.Equal(t, expectedVantagePoint, vantagePoint["type"]) + if len(expectedEventSchemas) > 0 { + require.Contains(t, tr, "event_schemas") + eventSchemas := tr["event_schemas"].([]any) + for i, schema := range eventSchemas { + require.Equal(t, expectedEventSchemas[i], schema) + } + } else { + require.NotContains(t, tr, "event_schemas") + } } diff --git a/qlogwriter/writer.go b/qlogwriter/writer.go index 19cfde128..fc4c6682d 100644 --- a/qlogwriter/writer.go +++ b/qlogwriter/writer.go @@ -56,19 +56,19 @@ var _ Trace = &FileSeq{} // NewFileSeq creates a new JSON-SEQ qlog trace to log transport events. func NewFileSeq(w io.WriteCloser) *FileSeq { - return newFileSeq(w, "transport", nil) + return newFileSeq(w, "transport", nil, nil) } // NewConnectionFileSeq creates a new qlog trace to log connection events. -func NewConnectionFileSeq(w io.WriteCloser, isClient bool, odcid ConnectionID) *FileSeq { +func NewConnectionFileSeq(w io.WriteCloser, isClient bool, odcid ConnectionID, eventSchemas []string) *FileSeq { pers := "server" if isClient { pers = "client" } - return newFileSeq(w, pers, &odcid) + return newFileSeq(w, pers, &odcid, eventSchemas) } -func newFileSeq(w io.WriteCloser, pers string, odcid *ConnectionID) *FileSeq { +func newFileSeq(w io.WriteCloser, pers string, odcid *ConnectionID, eventSchemas []string) *FileSeq { now := time.Now() buf := &bytes.Buffer{} enc := jsontext.NewEncoder(buf) @@ -79,6 +79,7 @@ func newFileSeq(w io.WriteCloser, pers string, odcid *ConnectionID) *FileSeq { VantagePointType: pers, GroupID: odcid, ReferenceTime: now, + EventSchemas: eventSchemas, }).Encode(enc); err != nil { panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) }