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 }