Add Writer and WriterLevel to Entry

This lets you do things like:

    cmd := exec.Command("command")
    stdout := logrus.WithField("fd", "stdout").Writer()
    defer stdout.Close()
    stderr := logrus.WithField("fd", "stderr").Writer()
    defer stderr.Close()
    cmd.Stdout = stdout
    cmd.Stderr = stderr
This commit is contained in:
David Glasser 2016-06-20 15:55:49 -07:00
parent 0208149b40
commit 1fccefa2f4
2 changed files with 44 additions and 10 deletions

View File

@ -359,3 +359,28 @@ func TestLogrusInterface(t *testing.T) {
e := logger.WithField("another", "value") e := logger.WithField("another", "value")
fn(e) fn(e)
} }
// Implements io.Writer using channels for synchronization, so we can wait on
// the Entry.Writer goroutine to write in a non-racey way. This does assume that
// there is a single call to Logger.Out for each message.
type channelWriter chan []byte
func (cw channelWriter) Write(p []byte) (int, error) {
cw <- p
return len(p), nil
}
func TestEntryWriter(t *testing.T) {
cw := channelWriter(make(chan []byte, 1))
log := New()
log.Out = cw
log.Formatter = new(JSONFormatter)
log.WithField("foo", "bar").WriterLevel(WarnLevel).Write([]byte("hello\n"))
bs := <-cw
var fields Fields
err := json.Unmarshal(bs, &fields)
assert.Nil(t, err)
assert.Equal(t, fields["foo"], "bar")
assert.Equal(t, fields["level"], "warning")
}

View File

@ -11,39 +11,48 @@ func (logger *Logger) Writer() *io.PipeWriter {
} }
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
return NewEntry(logger).WriterLevel(level)
}
func (entry *Entry) Writer() *io.PipeWriter {
return entry.WriterLevel(InfoLevel)
}
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
reader, writer := io.Pipe() reader, writer := io.Pipe()
var printFunc func(args ...interface{}) var printFunc func(args ...interface{})
switch level { switch level {
case DebugLevel: case DebugLevel:
printFunc = logger.Debug printFunc = entry.Debug
case InfoLevel: case InfoLevel:
printFunc = logger.Info printFunc = entry.Info
case WarnLevel: case WarnLevel:
printFunc = logger.Warn printFunc = entry.Warn
case ErrorLevel: case ErrorLevel:
printFunc = logger.Error printFunc = entry.Error
case FatalLevel: case FatalLevel:
printFunc = logger.Fatal printFunc = entry.Fatal
case PanicLevel: case PanicLevel:
printFunc = logger.Panic printFunc = entry.Panic
default: default:
printFunc = logger.Print printFunc = entry.Print
} }
go logger.writerScanner(reader, printFunc) go entry.writerScanner(reader, printFunc)
runtime.SetFinalizer(writer, writerFinalizer) runtime.SetFinalizer(writer, writerFinalizer)
return writer return writer
} }
func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
scanner := bufio.NewScanner(reader) scanner := bufio.NewScanner(reader)
for scanner.Scan() { for scanner.Scan() {
printFunc(scanner.Text()) printFunc(scanner.Text())
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
logger.Errorf("Error while reading from Writer: %s", err) entry.Errorf("Error while reading from Writer: %s", err)
} }
reader.Close() reader.Close()
} }