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  }