forked from quic-go/quic-go
qlogwriter: add support for event_schemas in the trace header (#5361)
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
"github.com/quic-go/quic-go/internal/utils"
|
"github.com/quic-go/quic-go/internal/utils"
|
||||||
|
"github.com/quic-go/quic-go/qlog"
|
||||||
"github.com/quic-go/quic-go/qlogwriter"
|
"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)
|
log.Fatalf("failed to create qlog file: %s", err)
|
||||||
return nil
|
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()
|
go fileSeq.Run()
|
||||||
return fileSeq
|
return fileSeq
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
"github.com/quic-go/quic-go/internal/utils"
|
"github.com/quic-go/quic-go/internal/utils"
|
||||||
|
"github.com/quic-go/quic-go/qlog"
|
||||||
"github.com/quic-go/quic-go/qlogwriter"
|
"github.com/quic-go/quic-go/qlogwriter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,7 +46,12 @@ func NewQLOGConnectionTracer(_ context.Context, isClient bool, connID quic.Conne
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Printf("Created qlog file: %s\n", path)
|
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()
|
go fileSeq.Run()
|
||||||
return fileSeq
|
return fileSeq
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ func BenchmarkConnectionTracing(b *testing.B) {
|
|||||||
nopWriteCloser(io.Discard),
|
nopWriteCloser(io.Discard),
|
||||||
false,
|
false,
|
||||||
protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
|
protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
|
||||||
|
[]string{EventSchema},
|
||||||
)
|
)
|
||||||
go trace.Run()
|
go trace.Run()
|
||||||
tracer := trace.AddProducer()
|
tracer := trace.AddProducer()
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import (
|
|||||||
"github.com/quic-go/quic-go/qlogwriter"
|
"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.
|
// DefaultConnectionTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable.
|
||||||
// File names are <odcid>_<perspective>.sqlog.
|
// File names are <odcid>_<perspective>.sqlog.
|
||||||
// Returns nil if QLOGDIR is not set.
|
// 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())
|
log.Printf("Failed to create qlog file %s: %s", path, err.Error())
|
||||||
return nil
|
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()
|
go fileSeq.Run()
|
||||||
return fileSeq
|
return fileSeq
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ type traceHeader struct {
|
|||||||
VantagePointType string
|
VantagePointType string
|
||||||
GroupID *ConnectionID
|
GroupID *ConnectionID
|
||||||
ReferenceTime time.Time
|
ReferenceTime time.Time
|
||||||
|
EventSchemas []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l traceHeader) Encode(enc *jsontext.Encoder) error {
|
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"))
|
h.WriteToken(jsontext.String("trace"))
|
||||||
// trace
|
// trace
|
||||||
h.WriteToken(jsontext.BeginObject)
|
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"))
|
h.WriteToken(jsontext.String("vantage_point"))
|
||||||
// -- vantage_point
|
// -- vantage_point
|
||||||
h.WriteToken(jsontext.BeginObject)
|
h.WriteToken(jsontext.BeginObject)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func TestTraceMetadata(t *testing.T) {
|
|||||||
producer := trace.AddProducer()
|
producer := trace.AddProducer()
|
||||||
producer.Close()
|
producer.Close()
|
||||||
|
|
||||||
testTraceMetadata(t, buf, "transport", "")
|
testTraceMetadata(t, buf, "transport", "", []string{})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("connection trace", func(t *testing.T) {
|
t.Run("connection trace", func(t *testing.T) {
|
||||||
@@ -57,16 +57,27 @@ func TestTraceMetadata(t *testing.T) {
|
|||||||
nopWriteCloser(buf),
|
nopWriteCloser(buf),
|
||||||
false,
|
false,
|
||||||
protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
|
protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
|
||||||
|
[]string{"urn:ietf:params:qlog:events:foo", "urn:ietf:params:qlog:events:bar"},
|
||||||
)
|
)
|
||||||
go trace.Run()
|
go trace.Run()
|
||||||
producer := trace.AddProducer()
|
producer := trace.AddProducer()
|
||||||
producer.Close()
|
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
|
var m map[string]any
|
||||||
require.NoError(t, unmarshal(buf.Bytes(), &m))
|
require.NoError(t, unmarshal(buf.Bytes(), &m))
|
||||||
require.Equal(t, "0.3", m["qlog_version"])
|
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")
|
require.Contains(t, tr, "vantage_point")
|
||||||
vantagePoint := tr["vantage_point"].(map[string]any)
|
vantagePoint := tr["vantage_point"].(map[string]any)
|
||||||
require.Equal(t, expectedVantagePoint, vantagePoint["type"])
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,19 +56,19 @@ var _ Trace = &FileSeq{}
|
|||||||
|
|
||||||
// NewFileSeq creates a new JSON-SEQ qlog trace to log transport events.
|
// NewFileSeq creates a new JSON-SEQ qlog trace to log transport events.
|
||||||
func NewFileSeq(w io.WriteCloser) *FileSeq {
|
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.
|
// 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"
|
pers := "server"
|
||||||
if isClient {
|
if isClient {
|
||||||
pers = "client"
|
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()
|
now := time.Now()
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
enc := jsontext.NewEncoder(buf)
|
enc := jsontext.NewEncoder(buf)
|
||||||
@@ -79,6 +79,7 @@ func newFileSeq(w io.WriteCloser, pers string, odcid *ConnectionID) *FileSeq {
|
|||||||
VantagePointType: pers,
|
VantagePointType: pers,
|
||||||
GroupID: odcid,
|
GroupID: odcid,
|
||||||
ReferenceTime: now,
|
ReferenceTime: now,
|
||||||
|
EventSchemas: eventSchemas,
|
||||||
}).Encode(enc); err != nil {
|
}).Encode(enc); err != nil {
|
||||||
panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err))
|
panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user