github.com/rochacon/deis@v1.0.2-0.20150903015341-6839b592a1ff/logger/syslogd/syslogd.go (about) 1 package syslogd 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "path" 9 "regexp" 10 11 "github.com/deis/deis/logger/syslog" 12 13 "github.com/deis/deis/logger/drain" 14 ) 15 16 // LogRoot is the log path to store logs. 17 var LogRoot string 18 19 type handler struct { 20 // To simplify implementation of our handler we embed helper 21 // syslog.BaseHandler struct. 22 *syslog.BaseHandler 23 drainURI string 24 } 25 26 // Simple fiter for named/bind messages which can be used with BaseHandler 27 func filter(m syslog.SyslogMessage) bool { 28 return true 29 } 30 31 func newHandler() *handler { 32 h := handler{ 33 BaseHandler: syslog.NewBaseHandler(5, filter, false), 34 } 35 36 go h.mainLoop() // BaseHandler needs some goroutine that reads from its queue 37 return &h 38 } 39 40 // check if a file path exists 41 func fileExists(path string) (bool, error) { 42 _, err := os.Stat(path) 43 if err == nil { 44 return true, nil 45 } 46 if os.IsNotExist(err) { 47 return false, nil 48 } 49 return false, err 50 } 51 52 func getLogFile(message string) (io.Writer, error) { 53 r := regexp.MustCompile(`^.* ([-_a-z0-9]+)\[[a-z0-9-_\.]+\].*`) 54 match := r.FindStringSubmatch(message) 55 if match == nil { 56 return nil, fmt.Errorf("Could not find app name in message: %s", message) 57 } 58 appName := match[1] 59 filePath := path.Join(LogRoot, appName+".log") 60 // check if file exists 61 exists, err := fileExists(filePath) 62 if err != nil { 63 return nil, err 64 } 65 // return a new file or the existing file for appending 66 var file io.Writer 67 if exists { 68 file, err = os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0644) 69 } else { 70 file, err = os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) 71 } 72 return file, err 73 } 74 75 func writeToDisk(m syslog.SyslogMessage) error { 76 file, err := getLogFile(m.String()) 77 if err != nil { 78 return err 79 } 80 bytes := []byte(m.String() + "\n") 81 file.Write(bytes) 82 return nil 83 } 84 85 // mainLoop reads from BaseHandler queue using h.Get and logs messages to stdout 86 func (h *handler) mainLoop() { 87 for { 88 m := h.Get() 89 if m == nil { 90 break 91 } 92 if h.drainURI != "" { 93 drain.SendToDrain(m.String(), h.drainURI) 94 } 95 err := writeToDisk(m) 96 if err != nil { 97 log.Println(err) 98 } 99 } 100 h.End() 101 } 102 103 // Listen starts a new syslog server which runs until it receives a signal. 104 func Listen(exitChan, cleanupDone chan bool, drainChan chan string, bindAddr string) { 105 fmt.Println("Starting syslog...") 106 // If LogRoot doesn't exist, create it 107 // equivalent to Python's `if not os.path.exists(filename)` 108 if _, err := os.Stat(LogRoot); os.IsNotExist(err) { 109 if err = os.MkdirAll(LogRoot, 0777); err != nil { 110 log.Fatalf("unable to create LogRoot at %s: %v", LogRoot, err) 111 } 112 } 113 // Create a server with one handler and run one listen goroutine 114 s := syslog.NewServer() 115 h := newHandler() 116 s.AddHandler(h) 117 s.Listen(bindAddr) 118 fmt.Println("Syslog server started...") 119 fmt.Println("deis-logger running") 120 121 // Wait for terminating signal 122 for { 123 select { 124 case <-exitChan: 125 // Shutdown the server 126 fmt.Println("Shutting down...") 127 s.Shutdown() 128 cleanupDone <- true 129 case d := <-drainChan: 130 h.drainURI = d 131 } 132 } 133 }