Merge pull request #471 from sirupsen/terminal-actual-fd

text_formatter: detect tty based on fd
This commit is contained in:
Simon Eskildsen 2017-02-06 19:32:13 -05:00 committed by GitHub
commit 03bf27ef26
8 changed files with 64 additions and 20 deletions

View File

@ -87,7 +87,8 @@ func init() {
// Log as JSON instead of the default ASCII formatter. // Log as JSON instead of the default ASCII formatter.
log.SetFormatter(&log.JSONFormatter{}) log.SetFormatter(&log.JSONFormatter{})
// Output to stdout instead of the default stderr, could also be a file. // Output to stdout instead of the default stderr
// Can be any io.Writer, see below for File example
log.SetOutput(os.Stdout) log.SetOutput(os.Stdout)
// Only log the warning severity or above. // Only log the warning severity or above.
@ -138,7 +139,15 @@ var log = logrus.New()
func main() { func main() {
// The API for setting attributes is a little different than the package level // The API for setting attributes is a little different than the package level
// exported logger. See Godoc. // exported logger. See Godoc.
log.Out = os.Stderr log.Out = os.Stdout
// You could set this to any `io.Writer` such as a file
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
log.WithFields(logrus.Fields{ log.WithFields(logrus.Fields{
"animal": "walrus", "animal": "walrus",

View File

@ -2,6 +2,7 @@ package main
import ( import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
// "os"
) )
var log = logrus.New() var log = logrus.New()
@ -9,6 +10,14 @@ var log = logrus.New()
func init() { func init() {
log.Formatter = new(logrus.JSONFormatter) log.Formatter = new(logrus.JSONFormatter)
log.Formatter = new(logrus.TextFormatter) // default log.Formatter = new(logrus.TextFormatter) // default
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
// if err == nil {
// log.Out = file
// } else {
// log.Info("Failed to log to file, using default stderr")
// }
log.Level = logrus.DebugLevel log.Level = logrus.DebugLevel
} }

View File

@ -80,11 +80,14 @@ func BenchmarkLargeJSONFormatter(b *testing.B) {
} }
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
logger := New()
entry := &Entry{ entry := &Entry{
Time: time.Time{}, Time: time.Time{},
Level: InfoLevel, Level: InfoLevel,
Message: "message", Message: "message",
Data: fields, Data: fields,
Logger: logger,
} }
var d []byte var d []byte
var err error var err error

View File

@ -3,6 +3,6 @@
package logrus package logrus
// IsTerminal returns true if stderr's file descriptor is a terminal. // IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool { func IsTerminal(f io.Writer) bool {
return true return true
} }

View File

@ -9,14 +9,20 @@
package logrus package logrus
import ( import (
"io"
"os"
"syscall" "syscall"
"unsafe" "unsafe"
) )
// IsTerminal returns true if stderr's file descriptor is a terminal. // IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool { func IsTerminal(f io.Writer) bool {
fd := syscall.Stderr
var termios Termios var termios Termios
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) switch v := f.(type) {
case *os.File:
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
return err == 0 return err == 0
default:
return false
}
} }

View File

@ -9,7 +9,13 @@ import (
) )
// IsTerminal returns true if the given file descriptor is a terminal. // IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal() bool { func IsTerminal(f io.Writer) bool {
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA) var termios Termios
switch v := f.(type) {
case *os.File:
_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
return err == nil return err == nil
default:
return false
}
} }

View File

@ -19,9 +19,13 @@ var (
) )
// IsTerminal returns true if stderr's file descriptor is a terminal. // IsTerminal returns true if stderr's file descriptor is a terminal.
func IsTerminal() bool { func IsTerminal(f io.Writer) bool {
fd := syscall.Stderr switch v := f.(type) {
case *os.File:
var st uint32 var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0 return r != 0 && e == 0
default:
return false
}
} }

View File

@ -3,9 +3,9 @@ package logrus
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"runtime"
"sort" "sort"
"strings" "strings"
"sync"
"time" "time"
) )
@ -20,12 +20,10 @@ const (
var ( var (
baseTimestamp time.Time baseTimestamp time.Time
isTerminal bool
) )
func init() { func init() {
baseTimestamp = time.Now() baseTimestamp = time.Now()
isTerminal = IsTerminal()
} }
type TextFormatter struct { type TextFormatter struct {
@ -50,6 +48,10 @@ type TextFormatter struct {
// that log extremely frequently and don't use the JSON formatter this may not // that log extremely frequently and don't use the JSON formatter this may not
// be desired. // be desired.
DisableSorting bool DisableSorting bool
// Whether the logger's out is to a terminal
isTerminal bool
terminalOnce sync.Once
} }
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
@ -70,8 +72,13 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
prefixFieldClashes(entry.Data) prefixFieldClashes(entry.Data)
isColorTerminal := isTerminal && (runtime.GOOS != "windows") f.terminalOnce.Do(func() {
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors if entry.Logger != nil {
f.isTerminal = IsTerminal(entry.Logger.Out)
}
})
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
timestampFormat := f.TimestampFormat timestampFormat := f.TimestampFormat
if timestampFormat == "" { if timestampFormat == "" {