Skip to content

Commit

Permalink
Intro Sink, alias for WriteSyncerCloser interface
Browse files Browse the repository at this point in the history
  • Loading branch information
dimroc committed Apr 11, 2018
1 parent 40845ba commit 1bc0592
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 49 deletions.
39 changes: 22 additions & 17 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ package zap

import (
"io"
"io/ioutil"
"os"
"sort"
"time"
Expand Down Expand Up @@ -167,13 +166,14 @@ func NewDevelopmentConfig() Config {

// Build constructs a logger from the Config and Options.
func (cfg Config) Build(opts ...Option) (*Logger, error) {
return cfg.BuildWithSinks(DefaultSinkFactories(), opts...)
return cfg.BuildWithSinks(DefaultSinks(), opts...)
}

// BuildWithSinks uses the map of sink factories to constrct a logger from the
// BuildWithSinks uses the map of sinks to construct a logger from the
// passed options. For example, the option "stdout" will construct a logger
// that routes output to the stdout sink.
func (cfg Config) BuildWithSinks(sf map[string]SinkFactory, opts ...Option) (*Logger, error) {
// that routes output to the stdout sink, but "/filepath" will fallback
// to writing to file.
func (cfg Config) BuildWithSinks(sf map[string]Sink, opts ...Option) (*Logger, error) {
enc, err := cfg.buildEncoder()
if err != nil {
return nil, err
Expand Down Expand Up @@ -235,7 +235,7 @@ func (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {
return opts
}

func (cfg Config) openSinks(sf map[string]SinkFactory) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
func (cfg Config) openSinks(sf map[string]Sink) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
sink, closeOut, err := Open(sf, cfg.OutputPaths...)
if err != nil {
return nil, nil, err
Expand All @@ -252,18 +252,23 @@ func (cfg Config) buildEncoder() (zapcore.Encoder, error) {
return newEncoder(cfg.Encoding, cfg.EncoderConfig)
}

// SinkFactory defines the Create() method used to create sink writers and
// closers.
type SinkFactory struct {
Writer zapcore.WriteSyncer
Closer io.Closer
// Sink defines the interface to write to and close logger destinations.
type Sink interface {
zapcore.WriteSyncer
io.Closer
}

// DefaultSinkFactories generates a map of regularly used sink factories,
// like stdout and stderr.
func DefaultSinkFactories() map[string]SinkFactory {
return map[string]SinkFactory{
"stdout": SinkFactory{os.Stdout, ioutil.NopCloser(os.Stdout)},
"stderr": SinkFactory{os.Stderr, ioutil.NopCloser(os.Stderr)},
// DefaultSinks generates a map of regularly used writeSyncers, coupled
// with the appropriate Closer.
func DefaultSinks() map[string]Sink {
return map[string]Sink{
"stdout": NopCloserSink{os.Stdout},
"stderr": NopCloserSink{os.Stderr},
}
}

// NopCloserSink wraps a WriteSyncer with a no-op Close() method.
type NopCloserSink struct{ zapcore.WriteSyncer }

// Close does nothing (no-op).
func (NopCloserSink) Close() error { return nil }
22 changes: 10 additions & 12 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ import (
"go.uber.org/multierr"
)

// Open is a high-level wrapper that takes a map of sink factories and a
// variadic number of paths. The map of sink factories customize how to open
// and close a particular path, with the default being the creation of a file
// at said path.
// It then combines all of the writers into a locked WriteSyncer. It also
// Open is a high-level wrapper that takes a map of sinks and a
// variadic number of paths. The map customize how to open and close a
// particular path, with the default being the creation of a file
// at said path. e.g. "stdout", "/file/destination".
// It then combines all of the writers into a locked WriteSyncer and
// returns any error encountered and a function to close any opened files.
//
// Passing no paths returns a no-op WriteSyncer. The special paths "stdout" and
// "stderr" are interpreted as os.Stdout and os.Stderr, respectively.
func Open(sf map[string]SinkFactory, paths ...string) (zapcore.WriteSyncer, func(), error) {
func Open(sf map[string]Sink, paths ...string) (zapcore.WriteSyncer, func(), error) {
writers, close, err := open(sf, paths)
if err != nil {
return nil, nil, err
Expand All @@ -49,7 +49,7 @@ func Open(sf map[string]SinkFactory, paths ...string) (zapcore.WriteSyncer, func
return writer, close, nil
}

func open(sf map[string]SinkFactory, paths []string) ([]zapcore.WriteSyncer, func(), error) {
func open(sf map[string]Sink, paths []string) ([]zapcore.WriteSyncer, func(), error) {
var openErr error
writers := make([]zapcore.WriteSyncer, 0, len(paths))
closers := make([]io.Closer, 0, len(paths))
Expand All @@ -59,11 +59,9 @@ func open(sf map[string]SinkFactory, paths []string) ([]zapcore.WriteSyncer, fun
}
}
for _, path := range paths {
factory, ok := sf[path]
if ok {
writer, closer := factory.Writer, factory.Closer
writers = append(writers, writer)
closers = append(closers, closer)
if sink, ok := sf[path]; ok {
writers = append(writers, sink)
closers = append(closers, sink)
continue
}
f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
Expand Down
40 changes: 20 additions & 20 deletions writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
)

func TestOpenNoPaths(t *testing.T) {
ws, cleanup, err := Open(DefaultSinkFactories())
ws, cleanup, err := Open(DefaultSinks())
defer cleanup()

assert.NoError(t, err, "Expected opening no paths to succeed.")
Expand All @@ -49,23 +49,21 @@ func TestOpen(t *testing.T) {
defer os.Remove(temp.Name())

tests := []struct {
paths []string
filenames []string
error string
paths []string
error string
}{
{[]string{"stdout"}, []string{os.Stdout.Name()}, ""},
{[]string{"stderr"}, []string{os.Stderr.Name()}, ""},
{[]string{temp.Name()}, []string{temp.Name()}, ""},
{[]string{"/foo/bar/baz"}, []string{}, "open /foo/bar/baz: no such file or directory"},
{[]string{"stdout"}, ""},
{[]string{"stderr"}, ""},
{[]string{temp.Name()}, ""},
{[]string{"/foo/bar/baz"}, "open /foo/bar/baz: no such file or directory"},
{
paths: []string{"stdout", "/foo/bar/baz", temp.Name(), "/baz/quux"},
filenames: []string{os.Stdout.Name(), temp.Name()},
error: "open /foo/bar/baz: no such file or directory; open /baz/quux: no such file or directory",
paths: []string{"stdout", "/foo/bar/baz", temp.Name(), "/baz/quux"},
error: "open /foo/bar/baz: no such file or directory; open /baz/quux: no such file or directory",
},
}

for _, tt := range tests {
wss, cleanup, err := open(DefaultSinkFactories(), tt.paths)
_, cleanup, err := Open(DefaultSinks(), tt.paths...)
if err == nil {
defer cleanup()
}
Expand All @@ -75,13 +73,6 @@ func TestOpen(t *testing.T) {
} else {
assert.Equal(t, tt.error, err.Error(), "Unexpected error opening paths %v.", tt.paths)
}
names := make([]string, len(wss))
for i, ws := range wss {
f, ok := ws.(*os.File)
require.True(t, ok, "Expected all WriteSyncers returned from open() to be files.")
names[i] = f.Name()
}
assert.Equal(t, tt.filenames, names, "Opened unexpected files given paths %v.", tt.paths)
}
}

Expand All @@ -98,7 +89,7 @@ func TestOpenFails(t *testing.T) {
}

for _, tt := range tests {
_, cleanup, err := Open(DefaultSinkFactories(), tt.paths...)
_, cleanup, err := Open(DefaultSinks(), tt.paths...)
require.Nil(t, cleanup, "Cleanup function should never be nil")
assert.Error(t, err, "Open with non-existent directory should fail")
}
Expand All @@ -118,6 +109,15 @@ func (w *testWriter) Sync() error {
return nil
}

func TestOpenCustomSink(t *testing.T) {
tw := &testWriter{"test", t}
sinks := map[string]Sink{"tester": NopCloserSink{tw}}
w, cleanup, err := Open(sinks, "tester")
assert.Nil(t, err)
defer cleanup()
w.Write([]byte("test"))
}

func TestCombineWriteSyncers(t *testing.T) {
tw := &testWriter{"test", t}
w := CombineWriteSyncers(tw)
Expand Down

0 comments on commit 1bc0592

Please sign in to comment.