Skip to content

Commit 7244adc

Browse files
committed
feat(open-telemetry#5408): use stdout as default output
Signed-off-by: thomasgouveia <[email protected]>
1 parent fc7212e commit 7244adc

File tree

6 files changed

+91
-79
lines changed

6 files changed

+91
-79
lines changed

exporters/otlp/otlplog/otlplogfile/config.go

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,71 @@
33

44
package otlplogfile // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlplogfile"
55

6-
import "time"
6+
import (
7+
"errors"
8+
"fmt"
9+
"io"
10+
"os"
11+
"time"
12+
)
713

8-
type fnOpt func(config) config
9-
10-
func (f fnOpt) applyOption(c config) config { return f(c) }
11-
12-
// Option sets the configuration value for an Exporter.
13-
type Option interface {
14-
applyOption(config) config
15-
}
14+
// Option configures a field of the configuration or return an error if needed.
15+
type Option func(*config) (*config, error)
1616

1717
// config contains options for the OTLP Log file exporter.
1818
type config struct {
19-
// Path to a file on disk where records must be appended.
20-
// This file is preferably a json line file as stated in the specification.
21-
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/file-exporter.md#json-lines-file
22-
// See: https://jsonlines.org
23-
path string
19+
// Out is the output where the records should be written.
20+
out io.WriteCloser
2421
// Duration represents the interval when the buffer should be flushed.
2522
flushInterval time.Duration
2623
}
2724

28-
func newConfig(options []Option) config {
29-
c := config{
30-
path: "/var/log/opentelemetry/logs.jsonl",
25+
func newConfig(options []Option) (*config, error) {
26+
c := &config{
27+
out: os.Stdout,
3128
flushInterval: 5 * time.Second,
3229
}
30+
31+
var configErr error
3332
for _, opt := range options {
34-
c = opt.applyOption(c)
33+
if _, err := opt(c); err != nil {
34+
configErr = errors.Join(configErr, err)
35+
}
3536
}
36-
return c
37+
38+
if configErr != nil {
39+
return nil, configErr
40+
}
41+
42+
return c, nil
3743
}
3844

39-
// WithFlushInterval configures the duration after which the buffer is periodically flushed to the disk.
40-
func WithFlushInterval(flushInterval time.Duration) Option {
41-
return fnOpt(func(c config) config {
42-
c.flushInterval = flushInterval
43-
return c
44-
})
45+
// WithFile configures a file where the records will be exported.
46+
// An error is returned if the file could not be created or opened.
47+
func WithFile(path string) Option {
48+
return func(c *config) (*config, error) {
49+
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o644)
50+
if err != nil {
51+
return nil, fmt.Errorf("failed to open file: %w", err)
52+
}
53+
54+
return WithWriter(file)(c)
55+
}
56+
}
57+
58+
// WithWriter configures the destination where the exporter should output
59+
// the records. By default, if not specified, stdout is used.
60+
func WithWriter(w io.WriteCloser) Option {
61+
return func(c *config) (*config, error) {
62+
c.out = w
63+
return c, nil
64+
}
4565
}
4666

47-
// WithPath defines a path to a file where the log records will be written.
48-
// If not set, will default to /var/log/opentelemetry/logs.jsonl.
49-
func WithPath(path string) Option {
50-
return fnOpt(func(c config) config {
51-
c.path = path
52-
return c
53-
})
67+
// WithFlushInterval configures the duration after which the buffer is periodically flushed to the output.
68+
func WithFlushInterval(flushInterval time.Duration) Option {
69+
return func(c *config) (*config, error) {
70+
c.flushInterval = flushInterval
71+
return c, nil
72+
}
5473
}

exporters/otlp/otlplog/otlplogfile/example_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
func Example() {
1616
ctx := context.Background()
1717
exp, err := otlplogfile.New(
18-
otlplogfile.WithPath("/tmp/otlp-logs.jsonl"),
18+
otlplogfile.WithFile("/tmp/otlp-logs.jsonl"),
1919
otlplogfile.WithFlushInterval(time.Second),
2020
)
2121
if err != nil {

exporters/otlp/otlplog/otlplogfile/exporter.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
// defined here: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.36.0/specification/protocol/file-exporter.md
2121
type Exporter struct {
2222
mu sync.Mutex
23-
fw *writer.FileWriter
23+
w *writer.Writer
2424
stopped bool
2525
}
2626

@@ -29,15 +29,18 @@ var _ log.Exporter = &Exporter{}
2929

3030
// New returns a new [Exporter].
3131
func New(options ...Option) (*Exporter, error) {
32-
cfg := newConfig(options)
32+
cfg, err := newConfig(options)
33+
if err != nil {
34+
return nil, err
35+
}
3336

34-
fw, err := writer.NewFileWriter(cfg.path, cfg.flushInterval)
37+
w, err := writer.New(cfg.out, cfg.flushInterval)
3538
if err != nil {
3639
return nil, err
3740
}
3841

3942
return &Exporter{
40-
fw: fw,
43+
w: w,
4144
stopped: false,
4245
}, nil
4346
}
@@ -65,7 +68,7 @@ func (e *Exporter) Export(ctx context.Context, records []log.Record) error {
6568
return err
6669
}
6770

68-
return e.fw.Export(by)
71+
return e.w.Export(by)
6972
}
7073

7174
// ForceFlush flushes data to the file.
@@ -77,7 +80,7 @@ func (e *Exporter) ForceFlush(_ context.Context) error {
7780
return nil
7881
}
7982

80-
return e.fw.Flush()
83+
return e.w.Flush()
8184
}
8285

8386
// Shutdown shuts down the exporter. Buffered data is written to disk,
@@ -91,5 +94,5 @@ func (e *Exporter) Shutdown(_ context.Context) error {
9194
}
9295

9396
e.stopped = true
94-
return e.fw.Shutdown()
97+
return e.w.Shutdown()
9598
}

exporters/otlp/otlplog/otlplogfile/exporter_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ import (
2323

2424
// tempFile creates a temporary file for the given test case and returns its path on disk.
2525
// The file is automatically cleaned up when the test ends.
26-
func tempFile(tb testing.TB) string {
26+
func tempFile(tb testing.TB) *os.File {
2727
f, err := os.CreateTemp(tb.TempDir(), tb.Name())
2828
assert.NoError(tb, err, "must not error when creating temporary file")
2929
tb.Cleanup(func() {
3030
assert.NoError(tb, os.RemoveAll(path.Dir(f.Name())), "must clean up files after being written")
3131
})
32-
return f.Name()
32+
return f
3333
}
3434

3535
// makeRecords is a helper function to generate an array of log record with the desired size.
@@ -48,10 +48,10 @@ func makeRecords(count int, message string) []sdklog.Record {
4848
}
4949

5050
func TestExporter(t *testing.T) {
51-
filepath := tempFile(t)
51+
file := tempFile(t)
5252
records := makeRecords(1, "hello, world!")
5353

54-
exporter, err := New(WithPath(filepath))
54+
exporter, err := New(WithWriter(file))
5555
assert.NoError(t, err)
5656
t.Cleanup(func() {
5757
assert.NoError(t, exporter.Shutdown(context.TODO()))
@@ -64,8 +64,8 @@ func TestExporter(t *testing.T) {
6464
}
6565

6666
func TestExporterConcurrentSafe(t *testing.T) {
67-
filepath := tempFile(t)
68-
exporter, err := New(WithPath(filepath))
67+
file := tempFile(t)
68+
exporter, err := New(WithWriter(file))
6969
require.NoError(t, err, "New()")
7070

7171
const goroutines = 10

exporters/otlp/otlplog/otlplogfile/internal/writer/writer.go

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,29 @@
44
package writer // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlplogfile/internal/writer"
55

66
import (
7-
"fmt"
87
"io"
9-
"os"
108
"sync"
119
"time"
1210
)
1311

14-
// FileWriter writes data to a configured file.
12+
// Writer writes data to the configured io.WriteCloser.
1513
// It is buffered to reduce I/O operations to improve performance.
16-
type FileWriter struct {
17-
path string
18-
file io.WriteCloser
19-
mu sync.Mutex
14+
type Writer struct {
15+
out io.WriteCloser
16+
mu sync.Mutex
2017

2118
flushInterval time.Duration
2219
flushTicker *time.Ticker
2320
stopTicker chan struct{}
2421
}
2522

26-
var _ flusher = (*FileWriter)(nil)
23+
var _ flusher = (*Writer)(nil)
2724

28-
// NewFileWriter initializes a file writer for the file at the given path.
29-
func NewFileWriter(path string, flushInterval time.Duration) (*FileWriter, error) {
30-
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o644)
31-
if err != nil {
32-
return nil, fmt.Errorf("failed to open file: %w", err)
33-
}
34-
35-
fw := &FileWriter{
36-
path: path,
25+
// New initializes a writer for the given io.WriteCloser.
26+
func New(w io.WriteCloser, flushInterval time.Duration) (*Writer, error) {
27+
fw := &Writer{
3728
flushInterval: flushInterval,
38-
file: newBufferedWriteCloser(file),
29+
out: newBufferedWriteCloser(w),
3930
}
4031

4132
if fw.flushInterval > 0 {
@@ -46,37 +37,37 @@ func NewFileWriter(path string, flushInterval time.Duration) (*FileWriter, error
4637
}
4738

4839
// Export writes the given data in the file.
49-
func (w *FileWriter) Export(data []byte) error {
40+
func (w *Writer) Export(data []byte) error {
5041
w.mu.Lock()
5142
defer w.mu.Unlock()
5243

53-
if _, err := w.file.Write(data); err != nil {
44+
if _, err := w.out.Write(data); err != nil {
5445
return err
5546
}
5647

5748
// As stated in the specification, line separator is \n.
58-
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.36.0/specification/protocol/file-exporter.md#json-lines-file
59-
if _, err := io.WriteString(w.file, "\n"); err != nil {
49+
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/file-exporter.md#json-lines-file
50+
if _, err := io.WriteString(w.out, "\n"); err != nil {
6051
return err
6152
}
6253

6354
return nil
6455
}
6556

6657
// Shutdown stops the flusher. It also stops the flush ticker if set.
67-
func (w *FileWriter) Shutdown() error {
58+
func (w *Writer) Shutdown() error {
6859
w.mu.Lock()
6960
defer w.mu.Unlock()
7061

7162
if w.flushTicker != nil {
7263
close(w.stopTicker)
7364
}
74-
return w.file.Close()
65+
return w.out.Close()
7566
}
7667

7768
// Flush writes buffered data to disk.
78-
func (w *FileWriter) Flush() error {
79-
ff, ok := w.file.(flusher)
69+
func (w *Writer) Flush() error {
70+
ff, ok := w.out.(flusher)
8071
if !ok {
8172
return nil
8273
}
@@ -88,11 +79,11 @@ func (w *FileWriter) Flush() error {
8879
}
8980

9081
// startFlusher starts the flusher to periodically flush the buffer.
91-
func (w *FileWriter) startFlusher() {
82+
func (w *Writer) startFlusher() {
9283
w.mu.Lock()
9384
defer w.mu.Unlock()
9485

95-
ff, ok := w.file.(flusher)
86+
ff, ok := w.out.(flusher)
9687
if !ok {
9788
return
9889
}

exporters/otlp/otlplog/otlplogfile/internal/writer/writer_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ func tempFile(tb testing.TB) *os.File {
3131
func TestNewFileWriter(t *testing.T) {
3232
f := tempFile(t)
3333

34-
writer, err := NewFileWriter(f.Name(), 0)
34+
writer, err := New(f, 0)
3535
// nolint: errcheck
3636
defer writer.Shutdown()
3737

3838
assert.NoError(t, err, "must not error when creating the file writer")
39-
assert.Equal(t, f.Name(), writer.path, "writer file path must be the same than the file path")
4039

4140
// Ensure file was created
4241
_, err = os.Stat(f.Name())
@@ -46,7 +45,7 @@ func TestNewFileWriter(t *testing.T) {
4645
func TestFileWriterExport(t *testing.T) {
4746
f := tempFile(t)
4847

49-
writer, err := NewFileWriter(f.Name(), 0)
48+
writer, err := New(f, 0)
5049
// nolint: errcheck
5150
defer writer.Shutdown()
5251
require.NoError(t, err, "must not error when creating the file writer")
@@ -66,15 +65,15 @@ func TestFileWriterExport(t *testing.T) {
6665
func TestFileWriterShutdown(t *testing.T) {
6766
f := tempFile(t)
6867

69-
writer, err := NewFileWriter(f.Name(), 0)
68+
writer, err := New(f, 0)
7069
require.NoError(t, err, "must not error when creating the file writer")
7170
assert.NoError(t, writer.Shutdown(), "must not error when calling Shutdown()")
7271
}
7372

7473
func TestFileWriterConcurrentSafe(t *testing.T) {
7574
f := tempFile(t)
7675

77-
writer, err := NewFileWriter(f.Name(), 0)
76+
writer, err := New(f, 0)
7877
require.NoError(t, err, "must not error when creating the file writer")
7978

8079
const goroutines = 10

0 commit comments

Comments
 (0)