github.com/blystad/deis@v0.11.0/logger/syslog/server.go (about)

     1  // Package syslog implements a syslog server library. It is based on RFC 3164,
     2  // as such it does not properly parse packets with an RFC 5424 header format.
     3  package syslog
     4  
     5  import (
     6  	"bytes"
     7  	"log"
     8  	"net"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  	"time"
    13  	"unicode"
    14  )
    15  
    16  // Server is the wrapper for a syslog server.
    17  type Server struct {
    18  	conns    []net.PacketConn
    19  	handlers []Handler
    20  	shutdown bool
    21  	l        FatalLogger
    22  }
    23  
    24  // NewServer creates an idle server.
    25  func NewServer() *Server {
    26  	return &Server{l: log.New(os.Stderr, "", log.LstdFlags)}
    27  }
    28  
    29  // SetLogger sets logger for server errors. A running server is rather quiet and
    30  // logs only fatal errors using FatalLogger interface. By default standard Go
    31  // logger is used so errors are writen to stderr and after that whole
    32  // application is halted. Using SetLogger you can change this behavior (log
    33  // erross elsewhere and don't halt whole application).
    34  func (s *Server) SetLogger(l FatalLogger) {
    35  	s.l = l
    36  }
    37  
    38  // AddHandler adds h to internal ordered list of handlers
    39  func (s *Server) AddHandler(h Handler) {
    40  	s.handlers = append(s.handlers, h)
    41  }
    42  
    43  // Listen starts gorutine that receives syslog messages on specified address.
    44  // addr can be a path (for unix domain sockets) or host:port (for UDP).
    45  func (s *Server) Listen(addr string) error {
    46  	var c net.PacketConn
    47  	if strings.IndexRune(addr, ':') != -1 {
    48  		a, err := net.ResolveUDPAddr("udp", addr)
    49  		if err != nil {
    50  			return err
    51  		}
    52  		c, err = net.ListenUDP("udp", a)
    53  		if err != nil {
    54  			return err
    55  		}
    56  	} else {
    57  		a, err := net.ResolveUnixAddr("unixgram", addr)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		c, err = net.ListenUnixgram("unixgram", a)
    62  		if err != nil {
    63  			return err
    64  		}
    65  	}
    66  	s.conns = append(s.conns, c)
    67  	go s.receiver(c)
    68  	return nil
    69  }
    70  
    71  // Shutdown stops server.
    72  func (s *Server) Shutdown() {
    73  	s.shutdown = true
    74  	for _, c := range s.conns {
    75  		err := c.Close()
    76  		if err != nil {
    77  			s.l.Fatalln(err)
    78  		}
    79  	}
    80  	s.passToHandlers(nil)
    81  	s.conns = nil
    82  	s.handlers = nil
    83  }
    84  
    85  func isNotAlnum(r rune) bool {
    86  	return !(unicode.IsLetter(r) || unicode.IsNumber(r))
    87  }
    88  
    89  func isNulCrLf(r rune) bool {
    90  	return r == 0 || r == '\r' || r == '\n'
    91  }
    92  
    93  func (s *Server) passToHandlers(m *Message) {
    94  	for _, h := range s.handlers {
    95  		m = h.Handle(m)
    96  		if m == nil {
    97  			break
    98  		}
    99  	}
   100  }
   101  
   102  func (s *Server) receiver(c net.PacketConn) {
   103  	//q := (chan<- Message)(s.q)
   104  	buf := make([]byte, 1024)
   105  	for {
   106  		n, addr, err := c.ReadFrom(buf)
   107  		if err != nil {
   108  			if !s.shutdown {
   109  				s.l.Fatalln("Read error:", err)
   110  			}
   111  			return
   112  		}
   113  		pkt := buf[:n]
   114  
   115  		m := new(Message)
   116  		m.Source = addr
   117  		m.Time = time.Now()
   118  
   119  		// Parse priority (if exists)
   120  		prio := 13 // default priority
   121  		hasPrio := false
   122  		if pkt[0] == '<' {
   123  			n = 1 + bytes.IndexByte(pkt[1:], '>')
   124  			if n > 1 && n < 5 {
   125  				p, err := strconv.Atoi(string(pkt[1:n]))
   126  				if err == nil && p >= 0 {
   127  					hasPrio = true
   128  					prio = p
   129  					pkt = pkt[n+1:]
   130  				}
   131  			}
   132  		}
   133  		m.Severity = Severity(prio & 0x07)
   134  		m.Facility = Facility(prio >> 3)
   135  
   136  		// Parse header (if exists)
   137  		if hasPrio && len(pkt) >= 16 && pkt[15] == ' ' {
   138  			// Get timestamp
   139  			layout := "Jan _2 15:04:05"
   140  			ts, err := time.Parse(layout, string(pkt[:15]))
   141  			if err == nil && !ts.IsZero() {
   142  				// Get hostname
   143  				n = 16 + bytes.IndexByte(pkt[16:], ' ')
   144  				if n != 15 {
   145  					m.Timestamp = ts
   146  					m.Hostname = string(pkt[16:n])
   147  					pkt = pkt[n+1:]
   148  				}
   149  			}
   150  			// TODO: check for version an new format of header as
   151  			// described in RFC 5424.
   152  		}
   153  
   154  		// Parse msg part
   155  		msg := string(bytes.TrimRightFunc(pkt, isNulCrLf))
   156  		n = strings.IndexFunc(msg, isNotAlnum)
   157  		if n != -1 {
   158  			m.Tag = msg[:n]
   159  			m.Content = msg[n:]
   160  		} else {
   161  			m.Content = msg
   162  		}
   163  		msg = strings.TrimFunc(msg, unicode.IsSpace)
   164  		n = strings.IndexFunc(msg, unicode.IsSpace)
   165  		if n != -1 {
   166  			m.Tag1 = msg[:n]
   167  			m.Content1 = strings.TrimLeftFunc(msg[n+1:], unicode.IsSpace)
   168  		} else {
   169  			m.Content1 = msg
   170  		}
   171  
   172  		s.passToHandlers(m)
   173  	}
   174  }