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 }