github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/qlog/writer.go (about) 1 package qlog 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "log" 8 "time" 9 10 "github.com/francoispqt/gojay" 11 ) 12 13 const eventChanSize = 50 14 15 type writer struct { 16 w io.WriteCloser 17 18 referenceTime time.Time 19 tr *trace 20 21 events chan event 22 encodeErr error 23 runStopped chan struct{} 24 } 25 26 func newWriter(w io.WriteCloser, tr *trace) *writer { 27 return &writer{ 28 w: w, 29 tr: tr, 30 referenceTime: tr.CommonFields.ReferenceTime, 31 runStopped: make(chan struct{}), 32 events: make(chan event, eventChanSize), 33 } 34 } 35 36 func (w *writer) RecordEvent(eventTime time.Time, details eventDetails) { 37 w.events <- event{ 38 RelativeTime: eventTime.Sub(w.referenceTime), 39 eventDetails: details, 40 } 41 } 42 43 func (w *writer) Run() { 44 defer close(w.runStopped) 45 buf := &bytes.Buffer{} 46 enc := gojay.NewEncoder(buf) 47 if err := enc.Encode(&topLevel{trace: *w.tr}); err != nil { 48 panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) 49 } 50 if err := buf.WriteByte('\n'); err != nil { 51 panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) 52 } 53 if _, err := w.w.Write(buf.Bytes()); err != nil { 54 w.encodeErr = err 55 } 56 enc = gojay.NewEncoder(w.w) 57 for ev := range w.events { 58 if w.encodeErr != nil { // if encoding failed, just continue draining the event channel 59 continue 60 } 61 if err := enc.Encode(ev); err != nil { 62 w.encodeErr = err 63 continue 64 } 65 if _, err := w.w.Write([]byte{'\n'}); err != nil { 66 w.encodeErr = err 67 } 68 } 69 } 70 71 func (w *writer) Close() { 72 if err := w.close(); err != nil { 73 log.Printf("exporting qlog failed: %s\n", err) 74 } 75 } 76 77 func (w *writer) close() error { 78 close(w.events) 79 <-w.runStopped 80 if w.encodeErr != nil { 81 return w.encodeErr 82 } 83 return w.w.Close() 84 }