implement qlog JSONSEQ format, bump qlog version (#4609)

This commit is contained in:
Marten Seemann
2024-08-03 20:19:51 -07:00
committed by GitHub
parent f96923b5b2
commit d1f9af4cc6
9 changed files with 37 additions and 16 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ main
mockgen_tmp.go
*.qtr
*.qlog
*.sqlog
*.txt
race.[0-9]*

View File

@@ -78,9 +78,10 @@ var _ = Describe("qlog dir tests", Serial, func() {
Expect(len(childs)).To(Equal(2))
odcids := make([]string, 0)
vantagePoints := make([]string, 0)
qlogFileNameRegexp := regexp.MustCompile(`^([0-f]+)_(client|server).qlog$`)
qlogFileNameRegexp := regexp.MustCompile(`^([0-f]+)_(client|server).sqlog$`)
for _, child := range childs {
matches := qlogFileNameRegexp.FindStringSubmatch(child.Name())
Expect(matches).To(HaveLen(3))
odcids = append(odcids, matches[1])
vantagePoints = append(vantagePoints, matches[2])
}

View File

@@ -39,7 +39,7 @@ func NewQLOGConnectionTracer(_ context.Context, p logging.Perspective, connID qu
log.Fatalf("failed to create qlog dir %s: %v", qlogDir, err)
}
}
path := fmt.Sprintf("%s/%s.qlog", strings.TrimRight(qlogDir, "/"), connID)
path := fmt.Sprintf("%s/%s.sqlog", strings.TrimRight(qlogDir, "/"), connID)
f, err := os.Create(path)
if err != nil {
log.Printf("Failed to create qlog file %s: %s", path, err.Error())

View File

@@ -2,7 +2,6 @@ package qlog
import (
"bytes"
"encoding/json"
"io"
"net"
"net/netip"
@@ -36,7 +35,7 @@ func exportAndParse(buf *bytes.Buffer) []entry {
m := make(map[string]interface{})
line, err := buf.ReadBytes('\n')
Expect(err).ToNot(HaveOccurred())
Expect(json.Unmarshal(line, &m)).To(Succeed())
Expect(unmarshal(line, &m)).To(Succeed())
Expect(m).To(HaveKey("trace"))
var entries []entry
trace := m["trace"].(map[string]interface{})
@@ -50,7 +49,7 @@ func exportAndParse(buf *bytes.Buffer) []entry {
line, err := buf.ReadBytes('\n')
Expect(err).ToNot(HaveOccurred())
ev := make(map[string]interface{})
Expect(json.Unmarshal(line, &ev)).To(Succeed())
Expect(unmarshal(line, &ev)).To(Succeed())
Expect(ev).To(HaveLen(3))
Expect(ev).To(HaveKey("time"))
Expect(ev).To(HaveKey("name"))
@@ -89,8 +88,8 @@ var _ = Describe("Tracing", func() {
tracer.Close()
m := make(map[string]interface{})
Expect(json.Unmarshal(buf.Bytes(), &m)).To(Succeed())
Expect(m).To(HaveKeyWithValue("qlog_version", "draft-02"))
Expect(unmarshal(buf.Bytes(), &m)).To(Succeed())
Expect(m).To(HaveKeyWithValue("qlog_version", "0.3"))
Expect(m).To(HaveKey("title"))
Expect(m).To(HaveKey("trace"))
trace := m["trace"].(map[string]interface{})

View File

@@ -19,7 +19,7 @@ func DefaultTracer(ctx context.Context, p logging.Perspective, connID logging.Co
}
// DefaultConnectionTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable.
// File names are <odcid>_<perspective>.qlog.
// File names are <odcid>_<perspective>.sqlog.
// Returns nil if QLOGDIR is not set.
func DefaultConnectionTracer(_ context.Context, p logging.Perspective, connID logging.ConnectionID) *logging.ConnectionTracer {
var label string
@@ -33,7 +33,7 @@ func DefaultConnectionTracer(_ context.Context, p logging.Perspective, connID lo
}
// qlogDirTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable.
// File names are <odcid>_<label>.qlog.
// File names are <odcid>_<label>.sqlog.
// Returns nil if QLOGDIR is not set.
func qlogDirTracer(p logging.Perspective, connID logging.ConnectionID, label string) *logging.ConnectionTracer {
qlogDir := os.Getenv("QLOGDIR")
@@ -45,7 +45,7 @@ func qlogDirTracer(p logging.Perspective, connID logging.ConnectionID, label str
log.Fatalf("failed to create qlog dir %s: %v", qlogDir, err)
}
}
path := fmt.Sprintf("%s/%s_%s.qlog", strings.TrimRight(qlogDir, "/"), connID, label)
path := fmt.Sprintf("%s/%s_%s.sqlog", strings.TrimRight(qlogDir, "/"), connID, label)
f, err := os.Create(path)
if err != nil {
log.Printf("Failed to create qlog file %s: %s", path, err.Error())

View File

@@ -26,6 +26,13 @@ func scaleDuration(t time.Duration) time.Duration {
return time.Duration(scaleFactor) * t
}
func unmarshal(data []byte, v interface{}) error {
if data[0] == recordSeparator {
data = data[1:]
}
return json.Unmarshal(data, v)
}
func checkEncoding(data []byte, expected map[string]interface{}) {
// unmarshal the data
m := make(map[string]interface{})

View File

@@ -43,8 +43,8 @@ type topLevel struct {
func (topLevel) IsNil() bool { return false }
func (l topLevel) MarshalJSONObject(enc *gojay.Encoder) {
enc.StringKey("qlog_format", "NDJSON")
enc.StringKey("qlog_version", "draft-02")
enc.StringKey("qlog_format", "JSON-SEQ")
enc.StringKey("qlog_version", "0.3")
enc.StringKeyOmitEmpty("title", "quic-go qlog")
enc.ObjectKey("configuration", configuration{Version: quicGoVersion})
enc.ObjectKey("trace", l.trace)

View File

@@ -2,7 +2,6 @@ package qlog
import (
"bytes"
"encoding/json"
"net"
"time"
@@ -28,12 +27,12 @@ var _ = Describe("Tracing", func() {
tracer.Close()
m := make(map[string]interface{})
Expect(json.Unmarshal(buf.Bytes(), &m)).To(Succeed())
Expect(m).To(HaveKeyWithValue("qlog_version", "draft-02"))
Expect(unmarshal(buf.Bytes(), &m)).To(Succeed())
Expect(m).To(HaveKeyWithValue("qlog_version", "0.3"))
Expect(m).To(HaveKey("title"))
Expect(m).To(HaveKey("trace"))
trace := m["trace"].(map[string]interface{})
Expect(trace).To(HaveKey(("common_fields")))
Expect(trace).To(HaveKey("common_fields"))
commonFields := trace["common_fields"].(map[string]interface{})
Expect(commonFields).ToNot(HaveKey("ODCID"))
Expect(commonFields).ToNot(HaveKey("group_id"))

View File

@@ -12,6 +12,13 @@ import (
const eventChanSize = 50
const recordSeparator = 0x1e
func writeRecordSeparator(w io.Writer) error {
_, err := w.Write([]byte{recordSeparator})
return err
}
type writer struct {
w io.WriteCloser
@@ -44,6 +51,9 @@ func (w *writer) Run() {
defer close(w.runStopped)
buf := &bytes.Buffer{}
enc := gojay.NewEncoder(buf)
if err := writeRecordSeparator(buf); err != nil {
panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err))
}
if err := enc.Encode(&topLevel{trace: *w.tr}); err != nil {
panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err))
}
@@ -58,6 +68,10 @@ func (w *writer) Run() {
if w.encodeErr != nil { // if encoding failed, just continue draining the event channel
continue
}
if err := writeRecordSeparator(w.w); err != nil {
w.encodeErr = err
continue
}
if err := enc.Encode(ev); err != nil {
w.encodeErr = err
continue