mirror of
https://github.com/zrepl/zrepl.git
synced 2024-12-22 15:11:16 +01:00
add syslog outlet
This commit is contained in:
parent
e0e362c4ff
commit
c4c38d5b23
@ -12,6 +12,10 @@ type LoggingConfig struct {
|
||||
Outlets logger.Outlets
|
||||
}
|
||||
|
||||
type SetNoMetadataFormatter interface {
|
||||
SetNoMetadata(noMetadata bool)
|
||||
}
|
||||
|
||||
func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||
|
||||
c = &LoggingConfig{}
|
||||
@ -31,6 +35,11 @@ func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||
Address string
|
||||
RetryInterval string `mapstructure:"retry_interval"`
|
||||
}
|
||||
Syslog struct {
|
||||
Enable bool
|
||||
Format string
|
||||
RetryInterval string `mapstructure:"retry_interval"`
|
||||
}
|
||||
}
|
||||
if err = mapstructure.Decode(i, &asMap); err != nil {
|
||||
return nil, errors.Wrap(err, "mapstructure error")
|
||||
@ -41,19 +50,19 @@ func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||
if asMap.Stdout.Level != "" {
|
||||
|
||||
out := WriterOutlet{
|
||||
HumanFormatter{},
|
||||
&HumanFormatter{},
|
||||
os.Stdout,
|
||||
}
|
||||
|
||||
level, err := logger.ParseLevel(asMap.Stdout.Level)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse stdout log level")
|
||||
return nil, errors.Wrap(err, "cannot parse 'level'")
|
||||
}
|
||||
|
||||
if asMap.Stdout.Format != "" {
|
||||
out.Formatter, err = parseLogFormat(asMap.Stdout.Format)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse log format")
|
||||
return nil, errors.Wrap(err, "cannot parse 'format'")
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +76,7 @@ func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||
|
||||
out.Formatter, err = parseLogFormat(asMap.TCP.Format)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse log 'format'")
|
||||
return nil, errors.Wrap(err, "cannot parse 'format'")
|
||||
}
|
||||
|
||||
lvl, err := logger.ParseLevel(asMap.TCP.Level)
|
||||
@ -85,6 +94,34 @@ func parseLogging(i interface{}) (c *LoggingConfig, err error) {
|
||||
c.Outlets.Add(out, lvl)
|
||||
}
|
||||
|
||||
if asMap.Syslog.Enable {
|
||||
|
||||
out := &SyslogOutlet{}
|
||||
|
||||
out.Formatter = &HumanFormatter{}
|
||||
if asMap.Syslog.Format != "" {
|
||||
out.Formatter, err = parseLogFormat(asMap.Syslog.Format)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse 'format'")
|
||||
}
|
||||
}
|
||||
|
||||
if f, ok := out.Formatter.(SetNoMetadataFormatter); ok {
|
||||
f.SetNoMetadata(true)
|
||||
}
|
||||
|
||||
out.RetryInterval = 0 // default to 0 as we assume local syslog will just work
|
||||
if asMap.Syslog.RetryInterval != "" {
|
||||
out.RetryInterval, err = time.ParseDuration(asMap.Syslog.RetryInterval)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse 'retry_interval'")
|
||||
}
|
||||
}
|
||||
|
||||
c.Outlets.Add(out, logger.Debug)
|
||||
|
||||
}
|
||||
|
||||
return c, nil
|
||||
|
||||
}
|
||||
@ -100,7 +137,7 @@ func parseLogFormat(i interface{}) (f EntryFormatter, err error) {
|
||||
|
||||
switch is {
|
||||
case "human":
|
||||
return HumanFormatter{}, nil
|
||||
return &HumanFormatter{}, nil
|
||||
case "json":
|
||||
return &JSONFormatter{}, nil
|
||||
default:
|
||||
|
@ -36,13 +36,23 @@ func (f NoFormatter) Format(e *logger.Entry) ([]byte, error) {
|
||||
return []byte(e.Message), nil
|
||||
}
|
||||
|
||||
type HumanFormatter struct{}
|
||||
type HumanFormatter struct {
|
||||
NoMetadata bool
|
||||
}
|
||||
|
||||
func (f HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
|
||||
var _ SetNoMetadataFormatter = &HumanFormatter{}
|
||||
|
||||
func (f *HumanFormatter) SetNoMetadata(noMetadata bool) {
|
||||
f.NoMetadata = noMetadata
|
||||
}
|
||||
|
||||
func (f *HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
|
||||
|
||||
var line bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&line, "[%s]", e.Level.Short())
|
||||
if !f.NoMetadata {
|
||||
fmt.Fprintf(&line, "[%s]", e.Level.Short())
|
||||
}
|
||||
|
||||
prefixFields := []string{logJobField, logTaskField, logFSField}
|
||||
prefixed := make(map[string]bool, len(prefixFields)+2)
|
||||
@ -69,7 +79,10 @@ func (f HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
|
||||
prefixed[logIncFromField], prefixed[logIncToField] = true, true
|
||||
}
|
||||
|
||||
fmt.Fprintf(&line, ": %s", e.Message)
|
||||
if line.Len() > 0 {
|
||||
fmt.Fprint(&line, ": ")
|
||||
}
|
||||
fmt.Fprint(&line, e.Message)
|
||||
|
||||
for field, value := range e.Fields {
|
||||
|
||||
@ -88,7 +101,7 @@ func (f HumanFormatter) Format(e *logger.Entry) (out []byte, err error) {
|
||||
|
||||
type JSONFormatter struct{}
|
||||
|
||||
func (f JSONFormatter) Format(e *logger.Entry) ([]byte, error) {
|
||||
func (f *JSONFormatter) Format(e *logger.Entry) ([]byte, error) {
|
||||
data := make(logger.Fields, len(e.Fields)+3)
|
||||
for k, v := range e.Fields {
|
||||
switch v := v.(type) {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zrepl/zrepl/logger"
|
||||
"io"
|
||||
"log/syslog"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
@ -61,3 +62,47 @@ func (h *TCPOutlet) WriteEntry(ctx context.Context, e logger.Entry) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SyslogOutlet struct {
|
||||
Formatter EntryFormatter
|
||||
RetryInterval time.Duration
|
||||
writer *syslog.Writer
|
||||
lastConnectAttempt time.Time
|
||||
}
|
||||
|
||||
func (o *SyslogOutlet) WriteEntry(ctx context.Context, entry logger.Entry) error {
|
||||
|
||||
bytes, err := o.Formatter.Format(&entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s := string(bytes)
|
||||
|
||||
if o.writer == nil {
|
||||
now := time.Now()
|
||||
if now.Sub(o.lastConnectAttempt) < o.RetryInterval {
|
||||
return nil // not an error toward logger
|
||||
}
|
||||
o.writer, err = syslog.New(syslog.LOG_LOCAL0, "zrepl")
|
||||
o.lastConnectAttempt = time.Now()
|
||||
if err != nil {
|
||||
o.writer = nil
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch entry.Level {
|
||||
case logger.Debug:
|
||||
return o.writer.Debug(s)
|
||||
case logger.Info:
|
||||
return o.writer.Info(s)
|
||||
case logger.Warn:
|
||||
return o.writer.Warning(s)
|
||||
case logger.Error:
|
||||
return o.writer.Err(s)
|
||||
default:
|
||||
return o.writer.Err(s) // write as error as reaching this case is in fact an error
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func init() {
|
||||
func testCmdGlobalInit(cmd *cobra.Command, args []string) {
|
||||
|
||||
out := logger.NewOutlets()
|
||||
out.Add(WriterOutlet{NoFormatter{}, os.Stdout}, logger.Info)
|
||||
out.Add(WriterOutlet{&NoFormatter{}, os.Stdout}, logger.Info)
|
||||
log := logger.NewLogger(out, 1*time.Second)
|
||||
testCmdGlobal.log = log
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user