summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/logr/v2/targets
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/logr/v2/targets')
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/file.go78
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/syslog.go112
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go56
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/tcp.go251
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem43
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/utils.go33
-rw-r--r--vendor/github.com/mattermost/logr/v2/targets/writer.go38
7 files changed, 611 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/logr/v2/targets/file.go b/vendor/github.com/mattermost/logr/v2/targets/file.go
new file mode 100644
index 00000000..71133fac
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/file.go
@@ -0,0 +1,78 @@
+package targets
+
+import (
+ "errors"
+ "io"
+
+ "github.com/mattermost/logr/v2"
+ "gopkg.in/natefinch/lumberjack.v2"
+)
+
+type FileOptions struct {
+ // Filename is the file to write logs to. Backup log files will be retained
+ // in the same directory. It uses <processname>-lumberjack.log in
+ // os.TempDir() if empty.
+ Filename string `json:"filename"`
+
+ // MaxSize is the maximum size in megabytes of the log file before it gets
+ // rotated. It defaults to 100 megabytes.
+ MaxSize int `json:"max_size"`
+
+ // MaxAge is the maximum number of days to retain old log files based on the
+ // timestamp encoded in their filename. Note that a day is defined as 24
+ // hours and may not exactly correspond to calendar days due to daylight
+ // savings, leap seconds, etc. The default is not to remove old log files
+ // based on age.
+ MaxAge int `json:"max_age"`
+
+ // MaxBackups is the maximum number of old log files to retain. The default
+ // is to retain all old log files (though MaxAge may still cause them to get
+ // deleted.)
+ MaxBackups int `json:"max_backups"`
+
+ // Compress determines if the rotated log files should be compressed
+ // using gzip. The default is not to perform compression.
+ Compress bool `json:"compress"`
+}
+
+func (fo FileOptions) CheckValid() error {
+ if fo.Filename == "" {
+ return errors.New("filename cannot be empty")
+ }
+ return nil
+}
+
+// File outputs log records to a file which can be log rotated based on size or age.
+// Uses `https://github.com/natefinch/lumberjack` for rotation.
+type File struct {
+ out io.WriteCloser
+}
+
+// NewFileTarget creates a target capable of outputting log records to a rotated file.
+func NewFileTarget(opts FileOptions) *File {
+ lumber := &lumberjack.Logger{
+ Filename: opts.Filename,
+ MaxSize: opts.MaxSize,
+ MaxBackups: opts.MaxBackups,
+ MaxAge: opts.MaxAge,
+ Compress: opts.Compress,
+ }
+ f := &File{out: lumber}
+ return f
+}
+
+// Init is called once to initialize the target.
+func (f *File) Init() error {
+ return nil
+}
+
+// Write outputs bytes to this file target.
+func (f *File) Write(p []byte, rec *logr.LogRec) (int, error) {
+ return f.out.Write(p)
+}
+
+// Shutdown is called once to free/close any resources.
+// Target queue is already drained when this is called.
+func (f *File) Shutdown() error {
+ return f.out.Close()
+}
diff --git a/vendor/github.com/mattermost/logr/v2/targets/syslog.go b/vendor/github.com/mattermost/logr/v2/targets/syslog.go
new file mode 100644
index 00000000..fc3fcc5f
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/syslog.go
@@ -0,0 +1,112 @@
+// +build !windows,!nacl,!plan9
+
+package targets
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+
+ "github.com/mattermost/logr/v2"
+ syslog "github.com/wiggin77/srslog"
+)
+
+// Syslog outputs log records to local or remote syslog.
+type Syslog struct {
+ params *SyslogOptions
+ writer *syslog.Writer
+}
+
+// SyslogOptions provides parameters for dialing a syslog daemon.
+type SyslogOptions struct {
+ IP string `json:"ip,omitempty"` // deprecated
+ Host string `json:"host"`
+ Port int `json:"port"`
+ TLS bool `json:"tls"`
+ Cert string `json:"cert"`
+ Insecure bool `json:"insecure"`
+ Tag string `json:"tag"`
+}
+
+func (so SyslogOptions) CheckValid() error {
+ if so.Host == "" && so.IP == "" {
+ return errors.New("missing host")
+ }
+ if so.Port == 0 {
+ return errors.New("missing port")
+ }
+ return nil
+}
+
+// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
+func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) {
+ if params == nil {
+ return nil, errors.New("params cannot be nil")
+ }
+
+ s := &Syslog{
+ params: params,
+ }
+ return s, nil
+}
+
+// Init is called once to initialize the target.
+func (s *Syslog) Init() error {
+ network := "tcp"
+ var config *tls.Config
+
+ if s.params.TLS {
+ network = "tcp+tls"
+ config = &tls.Config{InsecureSkipVerify: s.params.Insecure}
+ if s.params.Cert != "" {
+ pool, err := GetCertPool(s.params.Cert)
+ if err != nil {
+ return err
+ }
+ config.RootCAs = pool
+ }
+ }
+ raddr := fmt.Sprintf("%s:%d", s.params.IP, s.params.Port)
+ if raddr == ":0" {
+ // If no IP:port provided then connect to local syslog.
+ raddr = ""
+ network = ""
+ }
+
+ var err error
+ s.writer, err = syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, s.params.Tag, config)
+ return err
+}
+
+// Write outputs bytes to this file target.
+func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) {
+ txt := string(p)
+ n := len(txt)
+ var err error
+
+ switch rec.Level() {
+ case logr.Panic, logr.Fatal:
+ err = s.writer.Crit(txt)
+ case logr.Error:
+ err = s.writer.Err(txt)
+ case logr.Warn:
+ err = s.writer.Warning(txt)
+ case logr.Debug, logr.Trace:
+ err = s.writer.Debug(txt)
+ default:
+ // logr.Info plus all custom levels.
+ err = s.writer.Info(txt)
+ }
+
+ if err != nil {
+ n = 0
+ // syslog writer will try to reconnect.
+ }
+ return n, err
+}
+
+// Shutdown is called once to free/close any resources.
+// Target queue is already drained when this is called.
+func (s *Syslog) Shutdown() error {
+ return s.writer.Close()
+}
diff --git a/vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go b/vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go
new file mode 100644
index 00000000..e4086e96
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go
@@ -0,0 +1,56 @@
+// +build windows nacl plan9
+
+package targets
+
+import (
+ "errors"
+
+ "github.com/mattermost/logr/v2"
+ syslog "github.com/wiggin77/srslog"
+)
+
+const (
+ unsupported = "Syslog target is not supported on this platform."
+)
+
+// Syslog outputs log records to local or remote syslog.
+type Syslog struct {
+ params *SyslogOptions
+ writer *syslog.Writer
+}
+
+// SyslogOptions provides parameters for dialing a syslog daemon.
+type SyslogOptions struct {
+ IP string `json:"ip,omitempty"` // deprecated
+ Host string `json:"host"`
+ Port int `json:"port"`
+ TLS bool `json:"tls"`
+ Cert string `json:"cert"`
+ Insecure bool `json:"insecure"`
+ Tag string `json:"tag"`
+}
+
+func (so SyslogOptions) CheckValid() error {
+ return errors.New(unsupported)
+}
+
+// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
+func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) {
+ return nil, errors.New(unsupported)
+}
+
+// Init is called once to initialize the target.
+func (s *Syslog) Init() error {
+ return errors.New(unsupported)
+}
+
+// Write outputs bytes to this file target.
+func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) {
+ return 0, errors.New(unsupported)
+}
+
+// Shutdown is called once to free/close any resources.
+// Target queue is already drained when this is called.
+func (s *Syslog) Shutdown() error {
+ return errors.New(unsupported)
+}
diff --git a/vendor/github.com/mattermost/logr/v2/targets/tcp.go b/vendor/github.com/mattermost/logr/v2/targets/tcp.go
new file mode 100644
index 00000000..ce73e034
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/tcp.go
@@ -0,0 +1,251 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+package targets
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/mattermost/logr/v2"
+)
+
+const (
+ DialTimeoutSecs = 30
+ WriteTimeoutSecs = 30
+ RetryBackoffMillis int64 = 100
+ MaxRetryBackoffMillis int64 = 30 * 1000 // 30 seconds
+)
+
+// Tcp outputs log records to raw socket server.
+type Tcp struct {
+ options *TcpOptions
+ addy string
+
+ mutex sync.Mutex
+ conn net.Conn
+ monitor chan struct{}
+ shutdown chan struct{}
+}
+
+// TcpOptions provides parameters for dialing a socket server.
+type TcpOptions struct {
+ IP string `json:"ip,omitempty"` // deprecated
+ Host string `json:"host"`
+ Port int `json:"port"`
+ TLS bool `json:"tls"`
+ Cert string `json:"cert"`
+ Insecure bool `json:"insecure"`
+}
+
+func (to TcpOptions) CheckValid() error {
+ if to.Host == "" && to.IP == "" {
+ return errors.New("missing host")
+ }
+ if to.Port == 0 {
+ return errors.New("missing port")
+ }
+ return nil
+}
+
+// NewTcpTarget creates a target capable of outputting log records to a raw socket, with or without TLS.
+func NewTcpTarget(options *TcpOptions) *Tcp {
+ tcp := &Tcp{
+ options: options,
+ addy: fmt.Sprintf("%s:%d", options.IP, options.Port),
+ monitor: make(chan struct{}),
+ shutdown: make(chan struct{}),
+ }
+ return tcp
+}
+
+// Init is called once to initialize the target.
+func (tcp *Tcp) Init() error {
+ return nil
+}
+
+// getConn provides a net.Conn. If a connection already exists, it is returned immediately,
+// otherwise this method blocks until a new connection is created, timeout or shutdown.
+func (tcp *Tcp) getConn(reporter func(err interface{})) (net.Conn, error) {
+ tcp.mutex.Lock()
+ defer tcp.mutex.Unlock()
+
+ if tcp.conn != nil {
+ return tcp.conn, nil
+ }
+
+ type result struct {
+ conn net.Conn
+ err error
+ }
+
+ connChan := make(chan result)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*DialTimeoutSecs)
+ defer cancel()
+
+ go func(ctx context.Context, ch chan result) {
+ conn, err := tcp.dial(ctx)
+ if err != nil {
+ reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err))
+ return
+ }
+ tcp.conn = conn
+ tcp.monitor = make(chan struct{})
+ go monitor(tcp.conn, tcp.monitor)
+ ch <- result{conn: conn, err: err}
+ }(ctx, connChan)
+
+ select {
+ case <-tcp.shutdown:
+ return nil, errors.New("shutdown")
+ case res := <-connChan:
+ return res.conn, res.err
+ }
+}
+
+// dial connects to a TCP socket, and optionally performs a TLS handshake.
+// A non-nil context must be provided which can cancel the dial.
+func (tcp *Tcp) dial(ctx context.Context) (net.Conn, error) {
+ var dialer net.Dialer
+ dialer.Timeout = time.Second * DialTimeoutSecs
+ conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.options.IP, tcp.options.Port))
+ if err != nil {
+ return nil, err
+ }
+
+ if !tcp.options.TLS {
+ return conn, nil
+ }
+
+ tlsconfig := &tls.Config{
+ ServerName: tcp.options.IP,
+ InsecureSkipVerify: tcp.options.Insecure,
+ }
+ if tcp.options.Cert != "" {
+ pool, err := GetCertPool(tcp.options.Cert)
+ if err != nil {
+ return nil, err
+ }
+ tlsconfig.RootCAs = pool
+ }
+
+ tlsConn := tls.Client(conn, tlsconfig)
+ if err := tlsConn.Handshake(); err != nil {
+ return nil, err
+ }
+ return tlsConn, nil
+}
+
+func (tcp *Tcp) close() error {
+ tcp.mutex.Lock()
+ defer tcp.mutex.Unlock()
+
+ var err error
+ if tcp.conn != nil {
+ close(tcp.monitor)
+ err = tcp.conn.Close()
+ tcp.conn = nil
+ }
+ return err
+}
+
+// Shutdown stops processing log records after making best effort to flush queue.
+func (tcp *Tcp) Shutdown() error {
+ err := tcp.close()
+ close(tcp.shutdown)
+ return err
+}
+
+// Write converts the log record to bytes, via the Formatter, and outputs to the socket.
+// Called by dedicated target goroutine and will block until success or shutdown.
+func (tcp *Tcp) Write(p []byte, rec *logr.LogRec) (int, error) {
+ try := 1
+ backoff := RetryBackoffMillis
+ for {
+ select {
+ case <-tcp.shutdown:
+ return 0, nil
+ default:
+ }
+
+ reporter := rec.Logger().Logr().ReportError
+
+ conn, err := tcp.getConn(reporter)
+ if err != nil {
+ reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err))
+ backoff = tcp.sleep(backoff)
+ continue
+ }
+
+ err = conn.SetWriteDeadline(time.Now().Add(time.Second * WriteTimeoutSecs))
+ if err != nil {
+ reporter(fmt.Errorf("log target %s set write deadline error: %w", tcp.String(), err))
+ }
+
+ count, err := conn.Write(p)
+ if err == nil {
+ return count, nil
+ }
+
+ reporter(fmt.Errorf("log target %s write error: %w", tcp.String(), err))
+
+ _ = tcp.close()
+
+ backoff = tcp.sleep(backoff)
+ try++
+ }
+}
+
+// monitor continuously tries to read from the connection to detect socket close.
+// This is needed because TCP target uses a write only socket and Linux systems
+// take a long time to detect a loss of connectivity on a socket when only writing;
+// the writes simply fail without an error returned.
+func monitor(conn net.Conn, done <-chan struct{}) {
+ buf := make([]byte, 1)
+ for {
+ select {
+ case <-done:
+ return
+ case <-time.After(1 * time.Second):
+ }
+
+ err := conn.SetReadDeadline(time.Now().Add(time.Second * 30))
+ if err != nil {
+ continue
+ }
+
+ _, err = conn.Read(buf)
+
+ if errt, ok := err.(net.Error); ok && errt.Timeout() {
+ // read timeout is expected, keep looping.
+ continue
+ }
+
+ // Any other error closes the connection, forcing a reconnect.
+ conn.Close()
+ return
+ }
+}
+
+// String returns a string representation of this target.
+func (tcp *Tcp) String() string {
+ return fmt.Sprintf("TcpTarget[%s:%d]", tcp.options.IP, tcp.options.Port)
+}
+
+func (tcp *Tcp) sleep(backoff int64) int64 {
+ select {
+ case <-tcp.shutdown:
+ case <-time.After(time.Millisecond * time.Duration(backoff)):
+ }
+
+ nextBackoff := backoff + (backoff >> 1)
+ if nextBackoff > MaxRetryBackoffMillis {
+ nextBackoff = MaxRetryBackoffMillis
+ }
+ return nextBackoff
+}
diff --git a/vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem b/vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem
new file mode 100644
index 00000000..6ce8d042
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem
@@ -0,0 +1,43 @@
+-----BEGIN CERTIFICATE-----
+MIIDjzCCAnegAwIBAgIRAPYfRSwdzKopBKxYxKqslJUwDQYJKoZIhvcNAQELBQAw
+JzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0xOTAz
+MjIwMDE0MTVaFw0yMjAzMDYwMDE0MTVaMDsxOTA3BgNVBAMTME1hdHRlcm1vc3Qs
+IEluYy4gSW50ZXJuYWwgSW50ZXJtZWRpYXRlIEF1dGhvcml0eTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMjliRdmvnNL4u/Jr/M2dPwQmTJXEBY/Vq9Q
+vAU52X3tRMCPxcaFz+x6ftuvdO2NdohXGAmtx9QU5LZcvFeTDpoVEBo9A+4jtLvD
+DZYaTNLpJmoSoJHaDbdWX+OAOqyDiWS741LuiMKWHhew9QOisat2ZINPxjmAd9wE
+xthTMgzsv7MUqnMer8U5OGQ0Qy7wAmNRc+2K3qPwkxe2RUvcte50DUFNgxEginsh
+vrkOXR383vUCZfu72qu8oggjiQpyTllu5je2Ap6JLjYLkEMiMqrYADuWor/ZHwa6
+WrFqVETxWfAV5u9Eh0wZM/KKYwRQuw9y+Nans77FmUl1tVWWNN8CAwEAAaOBoTCB
+njAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQY4Uqswyr2hO/HetZt2RDxJdTIPjBi
+BgNVHSMEWzBZgBRFZXVg2Z5tNIsWeWjBLEy2yzKbMKErpCkwJzElMCMGA1UEAwwc
+TWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQYIUEifGUOM+bIFZo1tkjZB5YGBr
+0xEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAEdexL30Q0zBHmPAH8
+LhdK7dbzW1CmILbxRZlKAwRN+hKRXiMW3MHIkhNuoV9Aev602Q+ja4lWsRi/ktOL
+ni1FWx5gSScgdG8JGj47dOmoT3vXKX7+umiv4rQLPDl9/DKMuv204OYJq6VT+uNU
+6C6kL157jGJEO76H4fMZ8oYsD7Sq0zjiNKtuCYii0ngH3j3gB1jACLqRgveU7MdT
+pqOV2KfY31+h8VBtkUvljNztQ9xNY8Fjmt0SMf7E3FaUcaar3ZCr70G5aU3dKbe7
+47vGOBa5tCqw4YK0jgDKid3IJQul9a3J1mSsH8Wy3to9cAV4KGZBQLnzCX15a/+v
+3yVh
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDfjCCAmagAwIBAgIUEifGUOM+bIFZo1tkjZB5YGBr0xEwDQYJKoZIhvcNAQEL
+BQAwJzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0x
+OTAzMjEyMTI4NDNaFw0yOTAzMTgyMTI4NDNaMCcxJTAjBgNVBAMMHE1hdHRlcm1v
+c3QsIEluYy4gSW50ZXJuYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDH0Xq5rMBGpKOVWTpb5MnaJIWFP/vOtvEk+7hVrfOfe1/5x0Kk3UgAHj85
+otaEZD1Lhn/JLkEqCiE/UXMJFwJDlNcO4CkdKBSpYX4bKAqy5q/X3QwioMSNpJG1
++YYrNGBH0sgKcKjyCaLhmqYLD0xZDVOmWIYBU9jUPyXw5U0tnsVrTqGMxVkm1xCY
+krCWN1ZoUrLvL0MCZc5qpxoPTopr9UO9cqSBSuy6BVWVuEWBZhpqHt+ul8VxhzzY
+q1k4l7r2qw+/wm1iJBedTeBVeWNag8JaVfLgu+/W7oJVlPO32Po7pnvHp8iJ3b4K
+zXyVHaTX4S6Em+6LV8855TYrShzlAgMBAAGjgaEwgZ4wHQYDVR0OBBYEFEVldWDZ
+nm00ixZ5aMEsTLbLMpswMGIGA1UdIwRbMFmAFEVldWDZnm00ixZ5aMEsTLbLMpsw
+oSukKTAnMSUwIwYDVQQDDBxNYXR0ZXJtb3N0LCBJbmMuIEludGVybmFsIENBghQS
+J8ZQ4z5sgVmjW2SNkHlgYGvTETAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAN
+BgkqhkiG9w0BAQsFAAOCAQEAPiCWFmopyAkY2T3Zyo4yaRPhX1+VOTMKJtY6EUhq
+/GHz6kzEyvCUBf0N892cibGxekrEoItY9NqO6RQRfowg+Gn5kc13z4NyL2W8/eoT
+Xy0ZvfaQbU++fQ6pVtWtMblDMU9xiYd7/MDvJpO328l1Vhcdp8kEi+lCvpy0sCRc
+PxzPhbgCMAbZEGx+4TMQd4SZKzlRxW/2fflpReh6v1Dv0VDUSYQWwsUnaLpdKHfh
+a5k0vuySYcszE4YKlY0zakeFlJfp7fBp1xTwcdW8aTfw15EicPMwTc6xxA4JJUJx
+cddu817n1nayK5u6r9Qh1oIVkr0nC9YELMMy4dpPgJ88SA==
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/mattermost/logr/v2/targets/utils.go b/vendor/github.com/mattermost/logr/v2/targets/utils.go
new file mode 100644
index 00000000..6e605af2
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/utils.go
@@ -0,0 +1,33 @@
+package targets
+
+import (
+ "crypto/x509"
+ "encoding/base64"
+ "errors"
+ "io/ioutil"
+)
+
+// GetCertPool returns a x509.CertPool containing the cert(s)
+// from `cert`, which can be a path to a .pem or .crt file,
+// or a base64 encoded cert.
+func GetCertPool(cert string) (*x509.CertPool, error) {
+ if cert == "" {
+ return nil, errors.New("no cert provided")
+ }
+
+ // first treat as a file and try to read.
+ serverCert, err := ioutil.ReadFile(cert)
+ if err != nil {
+ // maybe it's a base64 encoded cert
+ serverCert, err = base64.StdEncoding.DecodeString(cert)
+ if err != nil {
+ return nil, errors.New("cert cannot be read")
+ }
+ }
+
+ pool := x509.NewCertPool()
+ if ok := pool.AppendCertsFromPEM(serverCert); ok {
+ return pool, nil
+ }
+ return nil, errors.New("cannot parse cert")
+}
diff --git a/vendor/github.com/mattermost/logr/v2/targets/writer.go b/vendor/github.com/mattermost/logr/v2/targets/writer.go
new file mode 100644
index 00000000..d9f64d76
--- /dev/null
+++ b/vendor/github.com/mattermost/logr/v2/targets/writer.go
@@ -0,0 +1,38 @@
+package targets
+
+import (
+ "io"
+ "io/ioutil"
+
+ "github.com/mattermost/logr/v2"
+)
+
+// Writer outputs log records to any `io.Writer`.
+type Writer struct {
+ out io.Writer
+}
+
+// NewWriterTarget creates a target capable of outputting log records to an io.Writer.
+func NewWriterTarget(out io.Writer) *Writer {
+ if out == nil {
+ out = ioutil.Discard
+ }
+ w := &Writer{out: out}
+ return w
+}
+
+// Init is called once to initialize the target.
+func (w *Writer) Init() error {
+ return nil
+}
+
+// Write outputs bytes to this file target.
+func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) {
+ return w.out.Write(p)
+}
+
+// Shutdown is called once to free/close any resources.
+// Target queue is already drained when this is called.
+func (w *Writer) Shutdown() error {
+ return nil
+}