mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-26 11:35:35 +00:00
89 lines
1.8 KiB
Go
89 lines
1.8 KiB
Go
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
//go:build !plan9
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
"tailscale.com/tstime"
|
||
|
)
|
||
|
|
||
|
// recorder knows how to send the provided bytes to the configured tsrecorder
|
||
|
// instance in asciinema format.
|
||
|
type recorder struct {
|
||
|
start time.Time
|
||
|
clock tstime.Clock
|
||
|
|
||
|
// failOpen specifies whether the session should be allowed to
|
||
|
// continue if writing to the recording fails.
|
||
|
failOpen bool
|
||
|
|
||
|
// backOff is set to true if we've failed open and should stop
|
||
|
// attempting to write to tsrecorder.
|
||
|
backOff bool
|
||
|
|
||
|
mu sync.Mutex // guards writes to conn
|
||
|
conn io.WriteCloser // connection to a tsrecorder instance
|
||
|
}
|
||
|
|
||
|
// Write appends timestamp to the provided bytes and sends them to the
|
||
|
// configured tsrecorder.
|
||
|
func (rec *recorder) Write(p []byte) (err error) {
|
||
|
if len(p) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
if rec.backOff {
|
||
|
return nil
|
||
|
}
|
||
|
j, err := json.Marshal([]any{
|
||
|
rec.clock.Now().Sub(rec.start).Seconds(),
|
||
|
"o",
|
||
|
string(p),
|
||
|
})
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error marhalling payload: %w", err)
|
||
|
}
|
||
|
j = append(j, '\n')
|
||
|
if err := rec.writeCastLine(j); err != nil {
|
||
|
if !rec.failOpen {
|
||
|
return fmt.Errorf("error writing payload to recorder: %w", err)
|
||
|
}
|
||
|
rec.backOff = true
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (rec *recorder) Close() error {
|
||
|
rec.mu.Lock()
|
||
|
defer rec.mu.Unlock()
|
||
|
if rec.conn == nil {
|
||
|
return nil
|
||
|
}
|
||
|
err := rec.conn.Close()
|
||
|
rec.conn = nil
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// writeCastLine sends bytes to the tsrecorder. The bytes should be in
|
||
|
// asciinema format.
|
||
|
func (rec *recorder) writeCastLine(j []byte) error {
|
||
|
rec.mu.Lock()
|
||
|
defer rec.mu.Unlock()
|
||
|
if rec.conn == nil {
|
||
|
return errors.New("recorder closed")
|
||
|
}
|
||
|
_, err := rec.conn.Write(j)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("recorder write error: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|