func main() { port := flag.Int("p", 8080, "port to bind to") verbose := flag.Bool("v", false, "enable verbose logging") debug := flag.Bool("vv", false, "enable more verbose (debug) logging") whisperdata := flag.String("w", config.WhisperData, "location where whisper files are stored") maxglobs := flag.Int("maxexpand", config.MaxGlobs, "maximum expansion depth to perform on input via curly braces ({a,b,c})") maxprocs := flag.Int("maxprocs", runtime.NumCPU()*80/100, "GOMAXPROCS") logdir := flag.String("logdir", "/var/log/carbonserver/", "logging directory") logtostdout := flag.Bool("stdout", false, "log also to stdout") scanFrequency := flag.Duration("scanfreq", 0, "file index scan frequency (0 to disable file index)") interval := flag.Duration("i", 60*time.Second, "interval to report internal statistics to graphite") flag.Parse() mlog.SetOutput(*logdir, "carbonserver", *logtostdout) expvar.NewString("BuildVersion").Set(BuildVersion) log.Println("starting carbonserver", BuildVersion) loglevel := mlog.Normal if *verbose { loglevel = mlog.Debug } if *debug { loglevel = mlog.Trace } logger = mlog.Level(loglevel) config.WhisperData = strings.TrimRight(*whisperdata, "/") logger.Logf("reading whisper files from: %s", config.WhisperData) config.MaxGlobs = *maxglobs logger.Logf("maximum brace expansion set to: %d", config.MaxGlobs) if *scanFrequency != 0 { logger.Logln("use file cache with scan frequency", *scanFrequency) force := make(chan struct{}) go fileListUpdater(*whisperdata, time.Tick(*scanFrequency), force) force <- struct{}{} } runtime.GOMAXPROCS(*maxprocs) logger.Logf("set GOMAXPROCS=%d", *maxprocs) httputil.PublishTrackedConnections("httptrack") expvar.Publish("requestBuckets", expvar.Func(renderTimeBuckets)) // +1 to track every over the number of buckets we track timeBuckets = make([]int64, config.Buckets+1) http.HandleFunc("/metrics/find/", httputil.TrackConnections(httputil.TimeHandler(findHandler, bucketRequestTimes))) http.HandleFunc("/render/", httputil.TrackConnections(httputil.TimeHandler(fetchHandler, bucketRequestTimes))) http.HandleFunc("/info/", httputil.TrackConnections(httputil.TimeHandler(infoHandler, bucketRequestTimes))) // nothing in the config? check the environment if config.GraphiteHost == "" { if host := os.Getenv("GRAPHITEHOST") + ":" + os.Getenv("GRAPHITEPORT"); host != ":" { config.GraphiteHost = host } } // only register g2g if we have a graphite host if config.GraphiteHost != "" { logger.Logf("Using graphite host %v", config.GraphiteHost) // register our metrics with graphite graphite, err := g2g.NewGraphite(config.GraphiteHost, *interval, 10*time.Second) if err != nil { log.Fatalf("unable to connect to to graphite: %v: %v", config.GraphiteHost, err) } hostname, _ := os.Hostname() hostname = strings.Replace(hostname, ".", "_", -1) graphite.Register(fmt.Sprintf("carbon.server.%s.render_requests", hostname), Metrics.RenderRequests) graphite.Register(fmt.Sprintf("carbon.server.%s.render_errors", hostname), Metrics.RenderErrors) graphite.Register(fmt.Sprintf("carbon.server.%s.notfound", hostname), Metrics.NotFound) graphite.Register(fmt.Sprintf("carbon.server.%s.find_requests", hostname), Metrics.FindRequests) graphite.Register(fmt.Sprintf("carbon.server.%s.find_errors", hostname), Metrics.FindErrors) graphite.Register(fmt.Sprintf("carbon.server.%s.find_zero", hostname), Metrics.FindZero) for i := 0; i <= config.Buckets; i++ { graphite.Register(fmt.Sprintf("carbon.server.%s.requests_in_%dms_to_%dms", hostname, i*100, (i+1)*100), bucketEntry(i)) } } listen := fmt.Sprintf(":%d", *port) logger.Logf("listening on %s", listen) err := http.ListenAndServe(listen, nil) if err != nil { log.Fatalf("%s", err) } logger.Logf("stopped") }
func main() { configFile := flag.String("c", "", "config file (json)") port := flag.Int("p", 0, "port to listen on") maxprocs := flag.Int("maxprocs", 0, "GOMAXPROCS") debugLevel := flag.Int("d", 0, "enable debug logging") logtostdout := flag.Bool("stdout", false, "write logging output also to stdout") logdir := flag.String("logdir", "/var/log/carbonzipper/", "logging directory") flag.Parse() expvar.NewString("BuildVersion").Set(BuildVersion) if *configFile == "" { log.Fatal("missing config file") } cfgjs, err := ioutil.ReadFile(*configFile) if err != nil { log.Fatal("unable to load config file:", err) } cfgjs = stripCommentHeader(cfgjs) if cfgjs == nil { log.Fatal("error removing header comment from ", *configFile) } err = json.Unmarshal(cfgjs, &Config) if err != nil { log.Fatal("error parsing config file: ", err) } if len(Config.Backends) == 0 { log.Fatal("no Backends loaded -- exiting") } // command line overrides config file if *port != 0 { Config.Port = *port } if *maxprocs != 0 { Config.MaxProcs = *maxprocs } // set up our logging mlog.SetOutput(*logdir, "carbonzipper", *logtostdout) logger = mlog.Level(*debugLevel) logger.Logln("starting carbonzipper", BuildVersion) logger.Logln("setting GOMAXPROCS=", Config.MaxProcs) runtime.GOMAXPROCS(Config.MaxProcs) if Config.ConcurrencyLimitPerServer != 0 { logger.Logln("Setting concurrencyLimit", Config.ConcurrencyLimitPerServer) Limiter = newServerLimiter(Config.Backends, Config.ConcurrencyLimitPerServer) } // +1 to track every over the number of buckets we track timeBuckets = make([]int64, Config.Buckets+1) httputil.PublishTrackedConnections("httptrack") expvar.Publish("requestBuckets", expvar.Func(renderTimeBuckets)) // export config via expvars expvar.Publish("Config", expvar.Func(func() interface{} { return Config })) Metrics.CacheSize = expvar.Func(func() interface{} { return Config.pathCache.ec.Size() }) expvar.Publish("cacheSize", Metrics.CacheSize) Metrics.CacheItems = expvar.Func(func() interface{} { return Config.pathCache.ec.Items() }) expvar.Publish("cacheItems", Metrics.CacheItems) http.HandleFunc("/metrics/find/", httputil.TrackConnections(httputil.TimeHandler(findHandler, bucketRequestTimes))) http.HandleFunc("/render/", httputil.TrackConnections(httputil.TimeHandler(renderHandler, bucketRequestTimes))) http.HandleFunc("/info/", httputil.TrackConnections(httputil.TimeHandler(infoHandler, bucketRequestTimes))) http.HandleFunc("/lb_check", lbCheckHandler) // nothing in the config? check the environment if Config.GraphiteHost == "" { if host := os.Getenv("GRAPHITEHOST") + ":" + os.Getenv("GRAPHITEPORT"); host != ":" { Config.GraphiteHost = host } } // only register g2g if we have a graphite host if Config.GraphiteHost != "" { logger.Logln("Using graphite host", Config.GraphiteHost) // register our metrics with graphite graphite, err := g2g.NewGraphite(Config.GraphiteHost, 60*time.Second, 10*time.Second) if err != nil { log.Fatal("unable to connect to to graphite: ", Config.GraphiteHost, ":", err) } hostname, _ := os.Hostname() hostname = strings.Replace(hostname, ".", "_", -1) graphite.Register(fmt.Sprintf("carbon.zipper.%s.find_requests", hostname), Metrics.FindRequests) graphite.Register(fmt.Sprintf("carbon.zipper.%s.find_errors", hostname), Metrics.FindErrors) graphite.Register(fmt.Sprintf("carbon.zipper.%s.render_requests", hostname), Metrics.RenderRequests) graphite.Register(fmt.Sprintf("carbon.zipper.%s.render_errors", hostname), Metrics.RenderErrors) graphite.Register(fmt.Sprintf("carbon.zipper.%s.info_requests", hostname), Metrics.InfoRequests) graphite.Register(fmt.Sprintf("carbon.zipper.%s.info_errors", hostname), Metrics.InfoErrors) graphite.Register(fmt.Sprintf("carbon.zipper.%s.timeouts", hostname), Metrics.Timeouts) for i := 0; i <= Config.Buckets; i++ { graphite.Register(fmt.Sprintf("carbon.zipper.%s.requests_in_%dms_to_%dms", hostname, i*100, (i+1)*100), bucketEntry(i)) } graphite.Register(fmt.Sprintf("carbon.zipper.%s.cache_size", hostname), Metrics.CacheSize) graphite.Register(fmt.Sprintf("carbon.zipper.%s.cache_items", hostname), Metrics.CacheItems) } // configure the storage client storageClient.Transport = &http.Transport{ MaxIdleConnsPerHost: Config.MaxIdleConnsPerHost, } go probeTlds() // force run now probeForce <- 1 go Config.pathCache.ec.ApproximateCleaner(10 * time.Second) portStr := fmt.Sprintf(":%d", Config.Port) logger.Logln("listening on", portStr) log.Fatal(http.ListenAndServe(portStr, nil)) }
func main() { z := flag.String("z", "", "zipper") port := flag.Int("p", 8080, "port") l := flag.Int("l", 20, "concurrency limit") cacheType := flag.String("cache", "mem", "cache type to use") mc := flag.String("mc", "", "comma separated memcached server list") memsize := flag.Int("memsize", 0, "in-memory cache size in MB (0 is unlimited)") cpus := flag.Int("cpus", 0, "number of CPUs to use") tz := flag.String("tz", "", "timezone,offset to use for dates with no timezone") graphiteHost := flag.String("graphite", "", "graphite destination host") logdir := flag.String("logdir", "/var/log/carbonapi/", "logging directory") logtostdout := flag.Bool("stdout", false, "log also to stdout") interval := flag.Duration("i", 60*time.Second, "interval to report internal statistics to graphite") idleconns := flag.Int("idleconns", 10, "max idle connections") pidFile := flag.String("pid", "", "pidfile (default: empty, don't create pidfile)") flag.Parse() if *logdir == "" { mlog.SetRawStream(os.Stdout) } else { mlog.SetOutput(*logdir, "carbonapi", *logtostdout) } expvar.NewString("BuildVersion").Set(BuildVersion) logger.Logln("starting carbonapi", BuildVersion) if p := os.Getenv("PORT"); p != "" { *port, _ = strconv.Atoi(p) } Limiter = newLimiter(*l) if *z == "" { logger.Fatalln("no zipper provided") } if _, err := url.Parse(*z); err != nil { logger.Fatalln("unable to parze zipper:", err) } logger.Logln("using zipper", *z) Zipper = zipper{ z: *z, client: &http.Client{ Transport: &http.Transport{ MaxIdleConnsPerHost: *idleconns, }, }, } switch *cacheType { case "memcache": if *mc == "" { logger.Fatalln("memcache cache requested but no memcache servers provided") } servers := strings.Split(*mc, ",") logger.Logln("using memcache servers:", servers) queryCache = &memcachedCache{client: memcache.New(servers...)} findCache = &memcachedCache{client: memcache.New(servers...)} case "mem": qcache := &expireCache{ec: ecache.New(uint64(*memsize * 1024 * 1024))} queryCache = qcache go queryCache.(*expireCache).ec.ApproximateCleaner(10 * time.Second) findCache = &expireCache{ec: ecache.New(0)} go findCache.(*expireCache).ec.ApproximateCleaner(10 * time.Second) Metrics.CacheSize = expvar.Func(func() interface{} { return qcache.ec.Size() }) expvar.Publish("cache_size", Metrics.CacheSize) Metrics.CacheItems = expvar.Func(func() interface{} { return qcache.ec.Items() }) expvar.Publish("cache_items", Metrics.CacheItems) case "null": queryCache = &nullCache{} findCache = &nullCache{} } if *tz != "" { fields := strings.Split(*tz, ",") if len(fields) != 2 { logger.Fatalf("expected two fields for tz,seconds, got %d", len(fields)) } var err error offs, err := strconv.Atoi(fields[1]) if err != nil { logger.Fatalf("unable to parse seconds: %s: %s", fields[1], err) } defaultTimeZone = time.FixedZone(fields[0], offs) logger.Logf("using fixed timezone %s, offset %d ", defaultTimeZone.String(), offs) } if *cpus != 0 { logger.Logln("using GOMAXPROCS", *cpus) runtime.GOMAXPROCS(*cpus) } if envhost := os.Getenv("GRAPHITEHOST") + ":" + os.Getenv("GRAPHITEPORT"); envhost != ":" || *graphiteHost != "" { var host string switch { case envhost != ":" && *graphiteHost != "": host = *graphiteHost case envhost != ":": host = envhost case *graphiteHost != "": host = *graphiteHost } logger.Logln("Using graphite host", host) logger.Logln("setting stats interval to", *interval) // register our metrics with graphite graphite := g2g.NewGraphite(host, *interval, 10*time.Second) hostname, _ := os.Hostname() hostname = strings.Replace(hostname, ".", "_", -1) graphite.Register(fmt.Sprintf("carbon.api.%s.requests", hostname), Metrics.Requests) graphite.Register(fmt.Sprintf("carbon.api.%s.request_cache_hits", hostname), Metrics.RequestCacheHits) graphite.Register(fmt.Sprintf("carbon.api.%s.find_requests", hostname), Metrics.FindRequests) graphite.Register(fmt.Sprintf("carbon.api.%s.find_cache_hits", hostname), Metrics.FindCacheHits) graphite.Register(fmt.Sprintf("carbon.api.%s.render_requests", hostname), Metrics.RenderRequests) graphite.Register(fmt.Sprintf("carbon.api.%s.memcache_timeouts", hostname), Metrics.MemcacheTimeouts) if Metrics.CacheSize != nil { graphite.Register(fmt.Sprintf("carbon.api.%s.cache_size", hostname), Metrics.CacheSize) graphite.Register(fmt.Sprintf("carbon.api.%s.cache_items", hostname), Metrics.CacheItems) } go mstats.Start(*interval) graphite.Register(fmt.Sprintf("carbon.api.%s.alloc", hostname), &mstats.Alloc) graphite.Register(fmt.Sprintf("carbon.api.%s.total_alloc", hostname), &mstats.TotalAlloc) graphite.Register(fmt.Sprintf("carbon.api.%s.num_gc", hostname), &mstats.NumGC) graphite.Register(fmt.Sprintf("carbon.api.%s.pause_ns", hostname), &mstats.PauseNS) } render := func(w http.ResponseWriter, r *http.Request) { var stats renderStats t0 := time.Now() renderHandler(w, r, &stats) since := time.Since(t0) logger.Logln(r.RequestURI, since.Nanoseconds()/int64(time.Millisecond), stats.zipperRequests) } if *pidFile != "" { pidfile.SetPidfilePath(*pidFile) err := pidfile.Write() if err != nil { logger.Fatalln("error during pidfile.Write():", err) } } r := http.DefaultServeMux r.HandleFunc("/render/", render) r.HandleFunc("/render", render) r.HandleFunc("/metrics/find/", findHandler) r.HandleFunc("/metrics/find", findHandler) r.HandleFunc("/info/", passthroughHandler) r.HandleFunc("/info", passthroughHandler) r.HandleFunc("/lb_check", lbcheckHandler) r.HandleFunc("/", usageHandler) logger.Logln("listening on port", *port) handler := handlers.CompressHandler(r) handler = handlers.CORS()(handler) handler = handlers.CombinedLoggingHandler(mlog.GetOutput(), handler) err := gracehttp.Serve(&http.Server{ Addr: ":" + strconv.Itoa(*port), Handler: handler, }) if err != nil { logger.Fatalln(err) } }