package recorder import ( "context" "io" "sync" "github.com/go-gost/core/metrics" "github.com/go-gost/core/recorder" xmetrics "github.com/go-gost/x/metrics" ) type fileRecorderOptions struct { recorder string sep string } // FileRecorderOption configures FileRecorder options. type FileRecorderOption func(opts *fileRecorderOptions) // RecorderFileRecorderOption sets the recorder name for metrics labeling. func RecorderFileRecorderOption(recorder string) FileRecorderOption { return func(opts *fileRecorderOptions) { opts.recorder = recorder } } // SepFileRecorderOption sets the record separator written after each record. func SepFileRecorderOption(sep string) FileRecorderOption { return func(opts *fileRecorderOptions) { opts.sep = sep } } type fileRecorder struct { recorder string out io.WriteCloser sep string mu sync.Mutex closeOnce sync.Once } // FileRecorder records data to file. func FileRecorder(out io.WriteCloser, opts ...FileRecorderOption) recorder.Recorder { var options fileRecorderOptions for _, opt := range opts { opt(&options) } return &fileRecorder{ recorder: options.recorder, out: out, sep: options.sep, } } func (r *fileRecorder) Record(ctx context.Context, b []byte, opts ...recorder.RecordOption) error { if r.out == nil { return nil } xmetrics.GetCounter(xmetrics.MetricRecorderRecordsCounter, metrics.Labels{"recorder": r.recorder}).Inc() if r.sep != "" { r.mu.Lock() // mutex is used to prevent unordered writes to the file defer r.mu.Unlock() } if _, err := r.out.Write(b); err != nil { return err } if r.sep != "" { _, err := io.WriteString(r.out, r.sep) return err } return nil } func (r *fileRecorder) Close() error { var err error r.closeOnce.Do(func() { if r.out != nil { err = r.out.Close() } }) return err }