package qlogwriter
import (
"bytes"
"fmt"
"io"
"log"
"slices"
"sync"
"time"
"github.com/quic-go/quic-go/qlogwriter/jsontext"
)
type Trace interface {
AddProducer () Recorder
SupportsSchemas (schema string ) bool
}
type Recorder interface {
RecordEvent (Event )
io .Closer
}
type Event interface {
Name () string
Encode (encoder *jsontext .Encoder , eventTime time .Time ) error
}
const RecordSeparator byte = 0x1e
var recordSeparator = []byte {RecordSeparator }
type event struct {
Time time .Time
Event Event
}
const eventChanSize = 50
type FileSeq struct {
w io .WriteCloser
enc *jsontext .Encoder
referenceTime time .Time
runStopped chan struct {}
encodeErr error
events chan event
done chan struct {}
mx sync .Mutex
producers int
closed bool
eventSchemas []string
}
var _ Trace = &FileSeq {}
func NewFileSeq (w io .WriteCloser ) *FileSeq {
return newFileSeq (w , "transport" , nil , nil )
}
func NewConnectionFileSeq (w io .WriteCloser , isClient bool , odcid ConnectionID , eventSchemas []string ) *FileSeq {
pers := "server"
if isClient {
pers = "client"
}
return newFileSeq (w , pers , &odcid , eventSchemas )
}
func newFileSeq(w io .WriteCloser , pers string , odcid *ConnectionID , eventSchemas []string ) *FileSeq {
now := time .Now ()
buf := &bytes .Buffer {}
enc := jsontext .NewEncoder (buf )
if _ , err := buf .Write (recordSeparator ); err != nil {
panic (fmt .Sprintf ("qlog encoding into a bytes.Buffer failed: %s" , err ))
}
if err := (&traceHeader {
VantagePointType : pers ,
GroupID : odcid ,
ReferenceTime : now ,
EventSchemas : eventSchemas ,
}).Encode (enc ); err != nil {
panic (fmt .Sprintf ("qlog encoding into a bytes.Buffer failed: %s" , err ))
}
_ , encodeErr := w .Write (buf .Bytes ())
return &FileSeq {
w : w ,
referenceTime : now ,
enc : jsontext .NewEncoder (w ),
runStopped : make (chan struct {}),
encodeErr : encodeErr ,
events : make (chan event , eventChanSize ),
done : make (chan struct {}),
eventSchemas : eventSchemas ,
}
}
func (t *FileSeq ) SupportsSchemas (schema string ) bool {
return slices .Contains (t .eventSchemas , schema )
}
func (t *FileSeq ) AddProducer () Recorder {
t .mx .Lock ()
defer t .mx .Unlock ()
if t .closed {
return nil
}
t .producers ++
return &Writer {t : t }
}
func (t *FileSeq ) record (eventTime time .Time , details Event ) {
t .mx .Lock ()
if t .closed {
t .mx .Unlock ()
return
}
t .mx .Unlock ()
t .events <- event {Time : eventTime , Event : details }
}
func (t *FileSeq ) Run () {
defer close (t .runStopped )
for {
select {
case <- t .done :
for {
select {
case e := <- t .events :
t .encodeEvent (e )
default :
if t .encodeErr != nil {
log .Printf ("exporting qlog failed: %s\n" , t .encodeErr )
}
return
}
}
case e := <- t .events :
t .encodeEvent (e )
}
}
}
func (t *FileSeq ) encodeEvent (e event ) {
if t .encodeErr != nil {
return
}
if _ , err := t .w .Write (recordSeparator ); err != nil {
t .encodeErr = err
return
}
h := encoderHelper {enc : t .enc }
h .WriteToken (jsontext .BeginObject )
h .WriteToken (jsontext .String ("time" ))
h .WriteToken (jsontext .Float (float64 (e .Time .Sub (t .referenceTime ).Nanoseconds ()) / 1e6 ))
h .WriteToken (jsontext .String ("name" ))
h .WriteToken (jsontext .String (e .Event .Name ()))
h .WriteToken (jsontext .String ("data" ))
if err := e .Event .Encode (t .enc , e .Time ); err != nil {
t .encodeErr = err
return
}
h .WriteToken (jsontext .EndObject )
if h .err != nil {
t .encodeErr = h .err
}
}
func (t *FileSeq ) removeProducer () {
t .mx .Lock ()
t .producers --
last := t .producers == 0
if last {
t .closed = true
}
t .mx .Unlock ()
if last {
close (t .done )
<-t .runStopped
_ = t .w .Close ()
}
}
type Writer struct {
t *FileSeq
}
func (w *Writer ) Close () error {
w .t .removeProducer ()
return nil
}
func (w *Writer ) RecordEvent (ev Event ) {
w .t .record (time .Now (), ev )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .