github.com/jiasir/deis@v1.12.2/logger/syslogish/server.go (about) 1 package syslogish 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "net" 8 "regexp" 9 "strings" 10 "sync" 11 12 "github.com/deis/deis/logger/drain" 13 "github.com/deis/deis/logger/storage" 14 ) 15 16 const queueSize = 500 17 18 var appRegex *regexp.Regexp 19 20 func init() { 21 appRegex = regexp.MustCompile(`^.* ([-_a-z0-9]+)\[[a-z0-9-_\.]+\].*`) 22 } 23 24 // Server implements a UDP-based "syslog-like" server. Like syslog, as described by RFC 3164, it 25 // expects that each packet contains a single log message and that, conversely, log messages are 26 // encapsulated in their entirety by a single packet, however, no attempt is made to parse the 27 // messages received or validate that they conform to the specification. 28 type Server struct { 29 conn net.PacketConn 30 listening bool 31 storageQueue chan string 32 storageAdapter storage.Adapter 33 drainageQueue chan string 34 drain drain.LogDrain 35 adapterMutex sync.RWMutex 36 drainMutex sync.RWMutex 37 } 38 39 // NewServer returns a pointer to a new Server instance. 40 func NewServer(bindHost string, bindPort int) (*Server, error) { 41 addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindHost, bindPort)) 42 if err != nil { 43 return nil, err 44 } 45 c, err := net.ListenUDP("udp", addr) 46 if err != nil { 47 return nil, err 48 } 49 return &Server{ 50 conn: c, 51 storageQueue: make(chan string, queueSize), 52 drainageQueue: make(chan string, queueSize), 53 }, nil 54 } 55 56 // SetStorageAdapter permits a server's underlying storage.Adapter to be reconfigured (replaced) 57 // at runtime. 58 func (s *Server) SetStorageAdapter(storageAdapter storage.Adapter) { 59 // Get an exclusive lock before updating the internal pointer to the storage adapter. Other 60 // goroutines holding read locks might depend on that pointer as it currently exists. 61 s.adapterMutex.Lock() 62 defer s.adapterMutex.Unlock() 63 s.storageAdapter = storageAdapter 64 } 65 66 // SetDrain permits a server's underlying drain.LogDrain to be reconfigured (replaced) at runtime. 67 func (s *Server) SetDrain(drain drain.LogDrain) { 68 // Get an exclusive lock before updating the internal pointer to the log drain. Other 69 // goroutines holding read locks might depend on that pointer as it currently exists. 70 s.drainMutex.Lock() 71 defer s.drainMutex.Unlock() 72 s.drain = drain 73 } 74 75 // Listen starts the server's main loop. 76 func (s *Server) Listen() { 77 // Should only ever be called once 78 if !s.listening { 79 s.listening = true 80 go s.receive() 81 go s.processStorage() 82 go s.processDrainage() 83 log.Println("syslogish server running") 84 } 85 } 86 87 func (s *Server) receive() { 88 // Make buffer the same size as the max for a UDP packet 89 buf := make([]byte, 65535) 90 for { 91 n, _, err := s.conn.ReadFrom(buf) 92 if err != nil { 93 log.Fatal("syslogish server read error", err) 94 } 95 message := strings.TrimSuffix(string(buf[:n]), "\n") 96 select { 97 case s.storageQueue <- message: 98 default: 99 } 100 } 101 } 102 103 func (s *Server) processStorage() { 104 for message := range s.storageQueue { 105 app, err := getAppName(message) 106 if err != nil { 107 log.Println(err) 108 return 109 } 110 // Get a read lock to ensure the storage adapater pointer can't be nilled by the configurer 111 // in the time between we check if it's nil and the time we invoke .Write() upon it. 112 s.adapterMutex.RLock() 113 // DONT'T defer unlocking... defered statements are executed when the function returns, but 114 // we are inside an infinite loop here. If we defer, we would never release the lock. 115 // Instead, release it manually below. 116 if s.storageAdapter != nil { 117 s.storageAdapter.Write(app, message) 118 // We don't bother trapping errors here, so failed writes to storage are silent. This is by 119 // design. If we sent a log message to STDOUT in response to the failure, deis-logspout 120 // would read it and forward it back to deis-logger, which would fail again to write to 121 // storage and spawn ANOTHER log message. The effect would be an infinite loop of 122 // unstoreable log messages that would nevertheless fill up journal logs and eventually 123 // overake the disk. 124 // 125 // Treating this as a fatal event would cause the deis-logger unit to restart-- sending 126 // even more log messages to STDOUT. The overall effect would be the same as described 127 // above with the added disadvantages of flapping. 128 } 129 s.adapterMutex.RUnlock() 130 // Add the message to the drainage queue. This allows the storage loop to continue right 131 // away instead of waiting while the message is sent to an external service-- since that 132 // could be a bottleneck and error prone depending on rate limiting, network congestion, etc. 133 select { 134 case s.drainageQueue <- message: 135 default: 136 } 137 } 138 } 139 140 func (s *Server) processDrainage() { 141 for message := range s.drainageQueue { 142 // Get a read lock to ensure the drain pointer can't be nilled by the configurer in the time 143 // between we check if it's nil and the time we invoke .Send() upon it. 144 s.drainMutex.RLock() 145 // DONT'T defer unlocking... defered statements are executed when the function returns, but 146 // we are inside an infinite loop here. If we defer, we would never release the lock. 147 // Instead, release it manually below. 148 if s.drain != nil { 149 s.drain.Send(message) 150 // We don't bother trapping errors here, so failed sends to the drain are silent. This is 151 // by design. If we sent a log message to STDOUT in response to the failure, deis-logspout 152 // would read it and forward it back to deis-logger, which would fail again to send to the 153 // drain and spawn ANOTHER log message. The effect would be an infinite loop of undrainable 154 // log messages that would nevertheless fill up journal logs and eventually overake the disk. 155 // 156 // Treating this as a fatal event would cause the deis-logger unit to restart-- sending 157 // even more log messages to STDOUT. The overall effect would be the same as described 158 // above with the added disadvantages of flapping. 159 } 160 s.drainMutex.RUnlock() 161 } 162 } 163 164 func getAppName(message string) (string, error) { 165 match := appRegex.FindStringSubmatch(message) 166 if match == nil { 167 return "", fmt.Errorf("Could not find app name in message: %s", message) 168 } 169 return match[1], nil 170 } 171 172 // ReadLogs returns a specified number of log lines (if available) for a specified app by 173 // delegating to the server's underlying storage.Adapter. 174 func (s *Server) ReadLogs(app string, lines int) ([]string, error) { 175 // Get a read lock to ensure the storage adapater pointer can't be updated by another 176 // goroutine in the time between we check if it's nil and the time we invoke .Read() upon 177 // it. 178 s.adapterMutex.RLock() 179 defer s.adapterMutex.RUnlock() 180 if s.storageAdapter == nil { 181 return nil, fmt.Errorf("Could not find logs for '%s'. No storage adapter specified.", app) 182 } 183 return s.storageAdapter.Read(app, lines) 184 } 185 186 // DestroyLogs deletes all logs for a specified app by delegating to the server's underlying 187 // storage.Adapter. 188 func (s *Server) DestroyLogs(app string) error { 189 // Get a read lock to ensure the storage adapater pointer can't be updated by another 190 // goroutine in the time between we check if it's nil and the time we invoke .Destroy() upon 191 // it. 192 s.adapterMutex.RLock() 193 defer s.adapterMutex.RUnlock() 194 if s.storageAdapter == nil { 195 return fmt.Errorf("Could not destroy logs for '%s'. No storage adapter specified.", app) 196 } 197 return s.storageAdapter.Destroy(app) 198 } 199 200 // ReopenLogs delegate to the server's underlying storage.Adapter to, if applicable, refresh 201 // references to underlying storage mechanisms. This is useful, for instance, to ensure logging 202 // continues smoothly after log rotation when file-based storage is in use. 203 func (s *Server) ReopenLogs() error { 204 // Get a read lock to ensure the storage adapater pointer can't be updated by another 205 // goroutine in the time between we check if it's nil and the time we invoke .Reopen() upon 206 // it. 207 s.adapterMutex.RLock() 208 defer s.adapterMutex.RUnlock() 209 if s.storageAdapter == nil { 210 return errors.New("Could not reopen logs. No storage adapter specified.") 211 } 212 return s.storageAdapter.Reopen() 213 }