mirror of https://github.com/sirupsen/logrus.git
Merge pull request #471 from sirupsen/terminal-actual-fd
text_formatter: detect tty based on fd
This commit is contained in:
commit
03bf27ef26
13
README.md
13
README.md
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 == "" {
|
||||||
|
|
Loading…
Reference in New Issue