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  }