github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/syslog/syslog.go (about)

     1  package syslog
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	sl "github.com/racksec/srslog"
    14  )
    15  
    16  const rfc5424time = "2006-01-02T15:04:05.999999Z07:00"
    17  const priority = sl.LOG_USER | sl.LOG_INFO
    18  
    19  type Syslog struct {
    20  	writer *sl.Writer
    21  	closed bool
    22  
    23  	mu sync.RWMutex
    24  }
    25  
    26  func Dial(transport, address string, caCerts []string) (*Syslog, error) {
    27  	var (
    28  		syslog *sl.Writer
    29  		config *tls.Config = nil
    30  	)
    31  
    32  	if transport == "tls" {
    33  		certpool, err := x509.SystemCertPool()
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  
    38  		for _, cert := range caCerts {
    39  			content, err := ioutil.ReadFile(cert)
    40  			if err != nil {
    41  				return nil, err
    42  			}
    43  
    44  			ok := certpool.AppendCertsFromPEM(content)
    45  			if !ok {
    46  				return nil, errors.New("syslog drainer certificate error")
    47  			}
    48  		}
    49  		// srslog uses "tcp+tls" to specify "tls" connections
    50  		transport = "tcp+tls"
    51  
    52  		config = &tls.Config{
    53  			RootCAs: certpool,
    54  		}
    55  	}
    56  
    57  	syslog, err := sl.DialWithTLSConfig(transport, address, priority, "", config)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	return &Syslog{
    63  		writer: syslog,
    64  		closed: false,
    65  	}, nil
    66  }
    67  
    68  func (s *Syslog) Write(hostname, tag string, ts time.Time, msg string) error {
    69  	s.mu.RLock()
    70  	defer s.mu.RUnlock()
    71  	if s.writer == nil {
    72  		return errors.New("connection already closed")
    73  	}
    74  
    75  	s.writer.SetFormatter(getSyslogFormatter(hostname, ts, tag))
    76  	_, err := s.writer.Write([]byte(msg))
    77  	return err
    78  }
    79  
    80  func (s *Syslog) Close() error {
    81  	s.mu.Lock()
    82  	defer s.mu.Unlock()
    83  
    84  	if s.writer == nil {
    85  		return errors.New("connection already closed")
    86  	}
    87  
    88  	err := s.writer.Close()
    89  	if err == nil {
    90  		s.writer = nil
    91  	}
    92  
    93  	return err
    94  }
    95  
    96  // generate custom formatter based on hostname and tag
    97  func getSyslogFormatter(hostname string, ts time.Time, tag string) sl.Formatter {
    98  	return func(priority sl.Priority, _, _, content string) string {
    99  		// strip whitespaces
   100  		s := strings.Replace(content, "\n", " ", -1)
   101  		s = strings.Replace(s, "\r", " ", -1)
   102  		s = strings.Replace(s, "\x00", " ", -1)
   103  
   104  		msg := fmt.Sprintf("<%d>1 %s %s %s - - - %s\n",
   105  			priority, ts.Format(rfc5424time), hostname, tag, s)
   106  		return msg
   107  	}
   108  }