github.com/dustinrc/deis@v1.10.1-0.20150917223407-0894a5fb979e/logger/main.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "log" 7 "os" 8 "os/signal" 9 "strconv" 10 "strings" 11 "syscall" 12 "time" 13 14 "github.com/coreos/go-etcd/etcd" 15 "github.com/deis/deis/logger/syslogd" 16 ) 17 18 var ( 19 logAddr string 20 logPort int 21 drainURI string 22 enablePublish bool 23 publishHost string 24 publishPath string 25 publishPort string 26 publishInterval int 27 publishTTL int 28 ) 29 30 func init() { 31 flag.StringVar(&logAddr, "log-addr", "0.0.0.0", "bind address for the logger") 32 flag.IntVar(&logPort, "log-port", 514, "bind port for the logger") 33 flag.StringVar(&drainURI, "drain-uri", "", "default drainURI, once set in etcd, this has no effect.") 34 flag.StringVar(&syslogd.LogRoot, "log-root", "/data/logs", "log path to store logs") 35 flag.BoolVar(&enablePublish, "enable-publish", false, "enable publishing to service discovery") 36 flag.StringVar(&publishHost, "publish-host", getopt("HOST", "127.0.0.1"), "service discovery hostname") 37 flag.IntVar(&publishInterval, "publish-interval", 10, "publish interval in seconds") 38 flag.StringVar(&publishPath, "publish-path", getopt("ETCD_PATH", "/deis/logs"), "path to publish host/port information") 39 flag.StringVar(&publishPort, "publish-port", getopt("ETCD_PORT", "4001"), "service discovery port") 40 flag.IntVar(&publishTTL, "publish-ttl", publishInterval*2, "publish TTL in seconds") 41 } 42 43 func main() { 44 flag.Parse() 45 46 client := etcd.NewClient([]string{"http://" + publishHost + ":" + publishPort}) 47 ticker := time.NewTicker(time.Duration(publishInterval) * time.Second) 48 signalChan := make(chan os.Signal, 1) 49 drainChan := make(chan string) 50 exitChan := make(chan bool) 51 cleanupChan := make(chan bool) 52 signal.Notify(signalChan, syscall.SIGTERM, syscall.SIGINT) 53 54 // ensure the drain key exists in etcd. 55 if _, err := client.Get(publishPath+"/drain", false, false); err != nil { 56 setEtcd(client, publishPath+"/drain", drainURI, 0) 57 } 58 59 go syslogd.Listen(exitChan, cleanupChan, drainChan, fmt.Sprintf("%s:%d", logAddr, logPort)) 60 if enablePublish { 61 publishKeys(client, publishHost, publishPath, strconv.Itoa(logPort), uint64(time.Duration(publishTTL)*time.Second)) 62 } 63 64 for { 65 select { 66 case <-ticker.C: 67 if enablePublish { 68 publishKeys(client, publishHost, publishPath, strconv.Itoa(logPort), uint64(time.Duration(publishTTL)*time.Second)) 69 } 70 // HACK (bacongobbler): poll etcd every publishInterval for changes in the log drain value. 71 // etcd's .Watch() implementation is broken when you use TTLs 72 // 73 // https://github.com/coreos/etcd/issues/2679 74 resp, err := client.Get(publishPath+"/drain", false, false) 75 if err != nil { 76 log.Printf("warning: could not retrieve drain URI from etcd: %v\n", err) 77 continue 78 } 79 if resp != nil && resp.Node != nil { 80 drainChan <- resp.Node.Value 81 } 82 case <-signalChan: 83 close(exitChan) 84 case <-cleanupChan: 85 ticker.Stop() 86 return 87 } 88 } 89 } 90 91 // publishKeys sets relevant etcd keys with a time-to-live. 92 func publishKeys(client *etcd.Client, host, etcdPath, port string, ttl uint64) { 93 setEtcd(client, etcdPath+"/host", host, ttl) 94 setEtcd(client, etcdPath+"/port", port, ttl) 95 } 96 97 func setEtcd(client *etcd.Client, key, value string, ttl uint64) { 98 _, err := client.Set(key, value, ttl) 99 if err != nil && !strings.Contains(err.Error(), "Key already exists") { 100 log.Println(err) 101 } 102 } 103 104 func getopt(name, dfault string) string { 105 value := os.Getenv(name) 106 if value == "" { 107 value = dfault 108 } 109 return value 110 }