zrepl/cmd/logging_outlets.go

125 lines
2.6 KiB
Go
Raw Permalink Normal View History

package cmd
import (
"context"
2017-09-24 14:34:50 +02:00
"crypto/tls"
"github.com/pkg/errors"
"github.com/zrepl/zrepl/logger"
"io"
2017-09-24 02:05:41 +02:00
"log/syslog"
"net"
"time"
)
type WriterOutlet struct {
Formatter EntryFormatter
Writer io.Writer
}
func (h WriterOutlet) WriteEntry(ctx context.Context, entry logger.Entry) error {
bytes, err := h.Formatter.Format(&entry)
if err != nil {
return err
}
_, err = h.Writer.Write(bytes)
h.Writer.Write([]byte("\n"))
return err
}
type TCPOutlet struct {
Formatter EntryFormatter
Net, Address string
Dialer net.Dialer
TLS *tls.Config
// Specifies how much time must pass between a connection error and a reconnection attempt
// Log entries written to the outlet during this time interval are silently dropped.
RetryInterval time.Duration
// nil if there was an error sending / connecting to remote server
conn net.Conn
// Last time an error occurred when sending / connecting to remote server
retry time.Time
}
func (h *TCPOutlet) WriteEntry(ctx context.Context, e logger.Entry) error {
b, err := h.Formatter.Format(&e)
if err != nil {
return err
}
if h.conn == nil {
if time.Now().Sub(h.retry) < h.RetryInterval {
// cool-down phase, drop the log entry
return nil
}
2017-09-24 14:34:50 +02:00
if h.TLS != nil {
h.conn, err = tls.DialWithDialer(&h.Dialer, h.Net, h.Address, h.TLS)
} else {
h.conn, err = h.Dialer.DialContext(ctx, h.Net, h.Address)
}
if err != nil {
2017-09-24 14:34:50 +02:00
h.conn = nil
h.retry = time.Now()
return errors.Wrap(err, "cannot dial")
}
}
_, err = h.conn.Write(b)
if err == nil {
_, err = h.conn.Write([]byte("\n"))
}
if err != nil {
h.conn.Close()
h.conn = nil
h.retry = time.Now()
return errors.Wrap(err, "cannot write")
}
return nil
}
2017-09-24 02:05:41 +02:00
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
}
}