github.com/adacta-ru/mattermost-server/v5@v5.31.1/mlog/syslog.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package mlog
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"crypto/x509"
    10  	"encoding/base64"
    11  	"errors"
    12  	"fmt"
    13  	"io/ioutil"
    14  
    15  	"github.com/mattermost/logr"
    16  	"github.com/wiggin77/merror"
    17  	syslog "github.com/wiggin77/srslog"
    18  )
    19  
    20  // Syslog outputs log records to local or remote syslog.
    21  type Syslog struct {
    22  	logr.Basic
    23  	w *syslog.Writer
    24  }
    25  
    26  // SyslogParams provides parameters for dialing a syslog daemon.
    27  type SyslogParams struct {
    28  	IP       string `json:"IP"`
    29  	Port     int    `json:"Port"`
    30  	Tag      string `json:"Tag"`
    31  	TLS      bool   `json:"TLS"`
    32  	Cert     string `json:"Cert"`
    33  	Insecure bool   `json:"Insecure"`
    34  }
    35  
    36  // NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
    37  func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) {
    38  	network := "tcp"
    39  	var config *tls.Config
    40  
    41  	if params.TLS {
    42  		network = "tcp+tls"
    43  		config = &tls.Config{InsecureSkipVerify: params.Insecure}
    44  		if params.Cert != "" {
    45  			pool, err := getCertPool(params.Cert)
    46  			if err != nil {
    47  				return nil, err
    48  			}
    49  			config.RootCAs = pool
    50  		}
    51  	}
    52  	raddr := fmt.Sprintf("%s:%d", params.IP, params.Port)
    53  
    54  	writer, err := syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, params.Tag, config)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	s := &Syslog{w: writer}
    60  	s.Basic.Start(s, s, filter, formatter, maxQueue)
    61  
    62  	return s, nil
    63  }
    64  
    65  // Shutdown stops processing log records after making best effort to flush queue.
    66  func (s *Syslog) Shutdown(ctx context.Context) error {
    67  	errs := merror.New()
    68  
    69  	err := s.Basic.Shutdown(ctx)
    70  	errs.Append(err)
    71  
    72  	err = s.w.Close()
    73  	errs.Append(err)
    74  
    75  	return errs.ErrorOrNil()
    76  }
    77  
    78  // getCertPool returns a x509.CertPool containing the cert(s)
    79  // from `cert`, which can be a path to a .pem or .crt file,
    80  // or a base64 encoded cert.
    81  func getCertPool(cert string) (*x509.CertPool, error) {
    82  	if cert == "" {
    83  		return nil, errors.New("no cert provided")
    84  	}
    85  
    86  	// first treat as a file and try to read.
    87  	serverCert, err := ioutil.ReadFile(cert)
    88  	if err != nil {
    89  		// maybe it's a base64 encoded cert
    90  		serverCert, err = base64.StdEncoding.DecodeString(cert)
    91  		if err != nil {
    92  			return nil, errors.New("cert cannot be read")
    93  		}
    94  	}
    95  
    96  	pool := x509.NewCertPool()
    97  	if ok := pool.AppendCertsFromPEM(serverCert); ok {
    98  		return pool, nil
    99  	}
   100  	return nil, errors.New("cannot parse cert")
   101  }
   102  
   103  // Write converts the log record to bytes, via the Formatter,
   104  // and outputs to syslog.
   105  func (s *Syslog) Write(rec *logr.LogRec) error {
   106  	_, stacktrace := s.IsLevelEnabled(rec.Level())
   107  
   108  	buf := rec.Logger().Logr().BorrowBuffer()
   109  	defer rec.Logger().Logr().ReleaseBuffer(buf)
   110  
   111  	buf, err := s.Formatter().Format(rec, stacktrace, buf)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	txt := buf.String()
   116  
   117  	switch rec.Level() {
   118  	case logr.Panic, logr.Fatal:
   119  		err = s.w.Crit(txt)
   120  	case logr.Error:
   121  		err = s.w.Err(txt)
   122  	case logr.Warn:
   123  		err = s.w.Warning(txt)
   124  	case logr.Debug, logr.Trace:
   125  		err = s.w.Debug(txt)
   126  	default:
   127  		// logr.Info plus all custom levels.
   128  		err = s.w.Info(txt)
   129  	}
   130  
   131  	if err != nil {
   132  		reporter := rec.Logger().Logr().ReportError
   133  		reporter(fmt.Errorf("syslog write fail: %w", err))
   134  		// syslog writer will try to reconnect.
   135  	}
   136  	return err
   137  }
   138  
   139  // String returns a string representation of this target.
   140  func (s *Syslog) String() string {
   141  	return "SyslogTarget"
   142  }