mirror of
https://github.com/zrepl/zrepl.git
synced 2024-12-20 06:00:52 +01:00
aab43af27c
Also: clarify semantics of RetryInterval
125 lines
2.6 KiB
Go
125 lines
2.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"github.com/pkg/errors"
|
|
"github.com/zrepl/zrepl/logger"
|
|
"io"
|
|
"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
|
|
}
|
|
|
|
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 {
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
}
|