logging: stdout outlet: include time in output if tty or forced through config

This commit is contained in:
Christian Schwarz 2017-11-14 21:51:19 +01:00
parent ed68bffea5
commit 476348689a
4 changed files with 77 additions and 37 deletions

14
Gopkg.lock generated
View File

@ -55,6 +55,12 @@
packages = ["."]
revision = "7cafcd837844e784b526369c9bce262804aebc60"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
@ -91,9 +97,15 @@
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "bf42f188b9bc6f2cf5b8ee5a912ef1aedd0eba4c"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d654c5e91ee04baf1a373bb241b94ab41d3838fb69a56e91fe54cc8b962eab56"
inputs-digest = "b2174984fa0452d4469597063f891904b70081586b239262690cf1b6477a3116"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -3,6 +3,7 @@ package cmd
import (
"crypto/tls"
"crypto/x509"
"github.com/mattn/go-isatty"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/zrepl/zrepl/logger"
@ -15,14 +16,15 @@ type LoggingConfig struct {
Outlets logger.Outlets
}
type SetNoMetadataFormatter interface {
SetNoMetadata(noMetadata bool)
}
type MetadataFlags int64
type OutletCommon struct {
MinLevel logger.Level
Formatter EntryFormatter
}
const (
MetadataTime MetadataFlags = 1 << iota
MetadataLevel
MetadataNone MetadataFlags = 0
MetadataAll MetadataFlags = ^0
)
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
@ -109,13 +111,12 @@ func parseOutlet(i interface{}) (o logger.Outlet, level logger.Level, err error)
return
}
common := &OutletCommon{}
common.MinLevel, err = logger.ParseLevel(in.Level)
minLevel, err := logger.ParseLevel(in.Level)
if err != nil {
err = errors.Wrap(err, "cannot parse 'level' field")
return
}
common.Formatter, err = parseLogFormat(in.Format)
formatter, err := parseLogFormat(in.Format)
if err != nil {
err = errors.Wrap(err, "cannot parse")
return
@ -123,29 +124,45 @@ func parseOutlet(i interface{}) (o logger.Outlet, level logger.Level, err error)
switch in.Outlet {
case "stdout":
o, err = parseStdoutOutlet(i, common)
o, err = parseStdoutOutlet(i, formatter)
case "tcp":
o, err = parseTCPOutlet(i, common)
o, err = parseTCPOutlet(i, formatter)
case "syslog":
o, err = parseSyslogOutlet(i, common)
o, err = parseSyslogOutlet(i, formatter)
default:
err = errors.Errorf("unknown outlet type '%s'", in.Outlet)
}
return o, common.MinLevel, err
return o, minLevel, err
}
func parseStdoutOutlet(i interface{}, common *OutletCommon) (WriterOutlet, error) {
func parseStdoutOutlet(i interface{}, formatter EntryFormatter) (WriterOutlet, error) {
var in struct {
Date bool
}
if err := mapstructure.Decode(i, &in); err != nil {
return WriterOutlet{}, errors.Wrap(err, "invalid structure for stdout outlet")
}
flags := MetadataAll
writer := os.Stdout
if !isatty.IsTerminal(writer.Fd()) && !in.Date {
flags &= ^MetadataTime
}
formatter.SetMetadataFlags(flags)
return WriterOutlet{
common.Formatter,
formatter,
os.Stdout,
}, nil
}
func parseTCPOutlet(i interface{}, common *OutletCommon) (out *TCPOutlet, err error) {
func parseTCPOutlet(i interface{}, formatter EntryFormatter) (out *TCPOutlet, err error) {
out = &TCPOutlet{}
out.Formatter = common.Formatter
out.Formatter = formatter
out.Formatter.SetMetadataFlags(MetadataAll)
var in struct {
Net string
@ -206,7 +223,7 @@ func parseTCPOutlet(i interface{}, common *OutletCommon) (out *TCPOutlet, err er
}
func parseSyslogOutlet(i interface{}, common *OutletCommon) (out *SyslogOutlet, err error) {
func parseSyslogOutlet(i interface{}, formatter EntryFormatter) (out *SyslogOutlet, err error) {
var in struct {
RetryInterval string `mapstructure:"retry_interval"`
@ -216,10 +233,8 @@ func parseSyslogOutlet(i interface{}, common *OutletCommon) (out *SyslogOutlet,
}
out = &SyslogOutlet{}
out.Formatter = common.Formatter
if f, ok := out.Formatter.(SetNoMetadataFormatter); ok {
f.SetNoMetadata(true)
}
out.Formatter = formatter
out.Formatter.SetMetadataFlags(MetadataNone)
out.RetryInterval = 0 // default to 0 as we assume local syslog will just work
if in.RetryInterval != "" {

View File

@ -11,6 +11,7 @@ import (
)
type EntryFormatter interface {
SetMetadataFlags(flags MetadataFlags)
Format(e *logger.Entry) ([]byte, error)
}
@ -32,25 +33,28 @@ const (
type NoFormatter struct{}
func (f NoFormatter) SetMetadataFlags(flags MetadataFlags) {}
func (f NoFormatter) Format(e *logger.Entry) ([]byte, error) {
return []byte(e.Message), nil
}
type HumanFormatter struct {
NoMetadata bool
metadataFlags MetadataFlags
}
var _ SetNoMetadataFormatter = &HumanFormatter{}
func (f *HumanFormatter) SetNoMetadata(noMetadata bool) {
f.NoMetadata = noMetadata
func (f *HumanFormatter) SetMetadataFlags(flags MetadataFlags) {
f.metadataFlags = flags
}
func (f *HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
var line bytes.Buffer
if !f.NoMetadata {
if f.metadataFlags&MetadataTime != 0 {
fmt.Fprintf(&line, "%s ", e.Time.Format(time.RFC3339))
}
if f.metadataFlags&MetadataLevel != 0 {
fmt.Fprintf(&line, "[%s]", e.Level.Short())
}
@ -100,7 +104,13 @@ func (f *HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
return line.Bytes(), nil
}
type JSONFormatter struct{}
type JSONFormatter struct {
metadataFlags MetadataFlags
}
func (f *JSONFormatter) SetMetadataFlags(flags MetadataFlags) {
f.metadataFlags = flags
}
func (f *JSONFormatter) Format(e *logger.Entry) ([]byte, error) {
data := make(logger.Fields, len(e.Fields)+3)
@ -128,21 +138,21 @@ func (f *JSONFormatter) Format(e *logger.Entry) ([]byte, error) {
}
type LogfmtFormatter struct {
NoMetadata bool
metadataFlags MetadataFlags
}
var _ SetNoMetadataFormatter = &LogfmtFormatter{}
func (f *LogfmtFormatter) SetNoMetadata(noMetadata bool) {
f.NoMetadata = noMetadata
func (f *LogfmtFormatter) SetMetadataFlags(flags MetadataFlags) {
f.metadataFlags = flags
}
func (f *LogfmtFormatter) Format(e *logger.Entry) ([]byte, error) {
var buf bytes.Buffer
enc := logfmt.NewEncoder(&buf)
if !f.NoMetadata {
if f.metadataFlags&MetadataTime != 0 {
enc.EncodeKeyval(FieldTime, e.Time)
}
if f.metadataFlags&MetadataLevel != 0 {
enc.EncodeKeyval(FieldLevel, e.Level)
}

View File

@ -115,8 +115,11 @@ Formats
- minimum :ref:`log level <logging-levels>`
* - ``format``
- output :ref:`format <logging-formats>`
* - ``time``
- always include time in output (``true`` or ``false``)
Writes all log entries with minimum level ``level`` formatted by ``format`` to stdout.
If stdout is a tty, interactive usage is assumed and the current time is included in the output.
Can only be specified once.