Beispiel #1
0
func GetBuckets(token string, min, max time.Time) ([]*Bucket, error) {
	var buckets []*Bucket
	startQuery := time.Now()
	rows, err := db.PGR.Query("select name, bucket, source, token, vals from metrics where token = $1 and bucket > $2 and bucket <= $3 order by bucket desc",
		token, min, max)
	if err != nil {
		return nil, err
	}
	utils.MeasureT(startQuery, "buckets.get-all")

	startParse := time.Now()
	defer rows.Close()
	for rows.Next() {
		var tmp []byte
		b := new(Bucket)
		buckets = append(buckets, b)
		rows.Scan(&b.Name, &b.Time, &b.Source, &b.Token, &tmp)
		if len(tmp) == 0 {
			b.Vals = []float64{}
			continue
		}
		encoding.DecodeArray(tmp, &b.Vals)
	}
	utils.MeasureT(startParse, "buckets.vals.decode")
	return buckets, nil
}
Beispiel #2
0
func recieveLogs(w http.ResponseWriter, r *http.Request, ch chan<- *store.Bucket) {
	defer utils.MeasureT(time.Now(), "http-receiver")
	if r.Method != "POST" {
		http.Error(w, "Invalid Request", 400)
		return
	}
	defer r.Body.Close()
	token, err := utils.ParseToken(r)
	if err != nil {
		utils.MeasureE("http-auth", err)
		http.Error(w, "Invalid Request", 400)
		return
	}
	defer utils.MeasureT(time.Now(), token+"-http-receive")

	buckets, err := store.NewBucket(token, bufio.NewReader(r.Body))
	if err != nil {
		http.Error(w, "Invalid Request", 400)
		return
	}

	for i := range buckets {
		ch <- buckets[i]
	}
}
Beispiel #3
0
func GetMetrics(token, name string, resolution int64, min, max time.Time) ([]*Metric, error) {
	startQuery := time.Now()
	rows, err := db.PGR.Query("select * from get_metrics($1, $2, $3, $4, $5)",
		token, name, resolution, min, max)
	if err != nil {
		utils.MeasureE("get-metrics-error", err)
		return nil, err
	}
	utils.MeasureT(startQuery, "get-metrics.query")
	startParse := time.Now()
	defer rows.Close()
	var metrics []*Metric
	for rows.Next() {
		startLoop := time.Now()
		var tmp []byte
		b := new(Bucket)
		rows.Scan(&b.Name, &b.Source, &b.Time, &tmp)
		if len(tmp) == 0 {
			b.Vals = []float64{}
			continue
		}
		encoding.DecodeArray(tmp, &b.Vals)
		m := new(Metric)
		m.Time = b.Time
		m.Name = b.Name
		m.Source = b.Source
		m.Mean = b.Mean()
		metrics = append(metrics, m)
		utils.MeasureT(startLoop, "get-metrics.scan-struct-loop")
	}
	utils.MeasureT(startParse, "parse.get-metrics")
	return metrics, nil
}
Beispiel #4
0
func GetMetrics(token, name string, resolution int64, min, max time.Time) ([]*Metric, error) {
	defer utils.MeasureT("get-metrics", time.Now())

	rows, err := pg.Query("select * from get_buckets($1, $2, $3, $4, $5)",
		token, name, resolution, min, max)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	var metrics []*Metric
	for rows.Next() {
		var tmp []byte
		k := BKey{}
		rows.Scan(&k.Name, &k.Source, &k.Time, &tmp)
		b := Bucket{Key: k}

		if len(tmp) == 0 {
			b.Vals = []float64{}
			continue
		}
		encoding.DecodeArray(tmp, &b.Vals, '{', '}', ',')
		m := new(Metric)
		m.Time = k.Time
		m.Name = k.Name
		m.Source = k.Source
		m.Mean = b.Mean()
		metrics = append(metrics, m)
	}

	return metrics, nil
}
Beispiel #5
0
func ScanBuckets(mailbox string) <-chan *Bucket {
	buckets := make(chan *Bucket)

	go func(ch chan *Bucket) {
		defer utils.MeasureT("redis.scan-buckets", time.Now())
		defer close(ch)

		rc := redisPool.Get()
		defer rc.Close()

		rc.Send("MULTI")
		rc.Send("SMEMBERS", mailbox)
		rc.Send("DEL", mailbox)
		reply, err := redis.Values(rc.Do("EXEC"))
		if err != nil {
			fmt.Printf("at=%q error=%s\n", "redset-smembers", err)
			return
		}

		var delCount int64
		var members []string
		redis.Scan(reply, &members, &delCount)
		for _, member := range members {
			k, err := ParseKey(member)
			if err != nil {
				fmt.Printf("at=parse-key error=%s\n", err)
				continue
			}
			ch <- &Bucket{Key: *k}
		}
	}(buckets)

	return buckets
}
Beispiel #6
0
func (b *Bucket) Put(partitions uint64) error {
	defer utils.MeasureT("bucket.put", time.Now())

	b.Lock()
	vals := b.Vals
	key := b.String()
	partition := b.Partition([]byte(key), partitions)
	b.Unlock()

	rc := redisPool.Get()
	defer rc.Close()
	libratoMailBox := fmt.Sprintf("librato_outlet.%d", partition)
	//pgMailBox := fmt.Sprintf("postgres_outlet.%d", partition)

	rc.Send("MULTI")
	rc.Send("RPUSH", key, vals)
	rc.Send("EXPIRE", key, 300)
	rc.Send("SADD", libratoMailBox, key)
	rc.Send("EXPIRE", libratoMailBox, 300)
	//rc.Send("SADD", pgMailBox, key)
	//rc.Send("EXPIRE", pgMailBox, 300)
	_, err := rc.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}
Beispiel #7
0
func (s *RedisStore) Putback(partition string, id *bucket.Id) error {
	defer utils.MeasureT("bucket.putback", time.Now())
	rc := s.redisPool.Get()
	defer rc.Close()
	_, err := rc.Do("SADD", partition, id.String())
	return err
}
Beispiel #8
0
func (l *LibratoOutlet) post(u, p string, body *bytes.Buffer) error {
	defer utils.MeasureT("librato-post", time.Now())
	req, err := http.NewRequest("POST", libratoUrl, body)
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("User-Agent", "l2met/0")
	req.SetBasicAuth(u, p)
	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode/100 != 2 {
		var m string
		s, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			m = fmt.Sprintf("error=failed-request code=%d", resp.StatusCode)
		} else {
			m = fmt.Sprintf("error=failed-request code=%d resp=body=%s req-body=%s",
				resp.StatusCode, s, body)
		}
		return errors.New(m)
	}
	return nil
}
Beispiel #9
0
func getBuckets(w http.ResponseWriter, r *http.Request) {
	defer utils.MeasureT(time.Now(), "get-buckets")

	if r.Method != "GET" {
		http.Error(w, "Invalid Request", 400)
		return
	}

	token, err := utils.ParseToken(r)
	if err != nil {
		errmsg := map[string]string{"error": "Missing authorization."}
		utils.WriteJson(w, 401, errmsg)
		return
	}

	q := r.URL.Query()
	limit, err := strconv.ParseInt(q.Get("limit"), 10, 32)
	if err != nil {
		errmsg := map[string]string{"error": "Missing limit parameter."}
		utils.WriteJson(w, 400, errmsg)
		return
	}

	max := utils.RoundTime(time.Now(), time.Minute)
	min := max.Add(-1 * time.Minute * time.Duration(limit))
	buckets, err := store.GetBuckets(token, min, max)
	if err != nil {
		errmsg := map[string]string{"error": "Unable to find buckets"}
		utils.WriteJson(w, 500, errmsg)
		return
	}
	utils.WriteJson(w, 200, buckets)
}
Beispiel #10
0
func NewBucket(token string, rdr *bufio.Reader) <-chan *Bucket {
	buckets := make(chan *Bucket, 1000)
	go func(c chan<- *Bucket) {
		defer close(c)
		defer utils.MeasureT("new-bucket", time.Now())
		lp := logplex.NewReader(rdr)
		for {
			packet, err := lp.ReadMsg()
			if err != nil {
				if err == io.EOF {
					break
				}
				fmt.Printf("at=logplex-error err=%s\n", err)
				return
			}
			d, err := encoding.ParseMsgData(packet.Msg)
			if err != nil {
				continue
			}

			name, ok := d["measure"]
			if !ok {
				continue
			}

			source, ok := d["source"]
			if !ok {
				source = ""
			}

			var val float64
			tmpVal, ok := d["val"]
			if ok {
				val, err = strconv.ParseFloat(tmpVal, 64)
				if err != nil {
					fmt.Printf("at=error error=\"unable to parse val.\"\n")
					continue
				}
			} else {
				val = float64(1)
			}

			t, err := packet.Time()
			if err != nil {
				fmt.Printf("at=time-error error=%s\n", err)
				continue
			}
			t = utils.RoundTime(t, time.Minute)

			k := BKey{Token: token, Name: name, Source: source, Time: t}
			b := &Bucket{Key: k}
			b.Vals = append(b.Vals, val)
			c <- b
		}
	}(buckets)
	return buckets
}
Beispiel #11
0
func fetch(t time.Time, outbox chan<- *store.Bucket) {
	fmt.Printf("at=start_fetch minute=%d\n", t.Minute())
	defer utils.MeasureT("postgres_outlet.fetch", time.Now())

	mailbox := fmt.Sprintf("postgres_outlet.%d", partitionId)
	for bucket := range store.ScanBuckets(mailbox) {
		outbox <- bucket
	}
}
Beispiel #12
0
func allBucketIds(min, max time.Time) ([]int64, error) {
	var buckets []int64
	startQuery := time.Now()
	r, err := db.PGR.Query("select id from metrics where bucket >= $1 and bucket < $2 order by bucket desc",
		min, max)
	if err != nil {
		return nil, err
	}
	utils.MeasureT(startQuery, "metrics.query")
	startParse := time.Now()
	defer r.Close()
	for r.Next() {
		var id int64
		r.Scan(&id)
		buckets = append(buckets, id)
	}
	utils.MeasureT(startParse, "metrics.vals.parse")
	return buckets, nil
}
Beispiel #13
0
func getMetrics(w http.ResponseWriter, r *http.Request) {
	defer utils.MeasureT("http-metrics", time.Now())

	// Support CORS.
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Headers", "Authorization")
	w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
	if r.Method == "OPTIONS" {
		return
	}

	names := metricsPat.FindStringSubmatch(r.URL.Path)
	if len(names) < 2 {
		fmt.Printf("at=error error=%q\n", "Name parameter not provided.")
		errmsg := map[string]string{"error": "Name parameter not provided."}
		utils.WriteJson(w, 401, errmsg)
		return
	}
	name := names[1]

	token, err := utils.ParseToken(r)
	if err != nil {
		fmt.Printf("at=error error=%q\n", err)
		errmsg := map[string]string{"error": "Missing authorization."}
		utils.WriteJson(w, 401, errmsg)
		return
	}

	q := r.URL.Query()
	limit, err := strconv.ParseInt(q.Get("limit"), 10, 32)
	if err != nil {
		errmsg := map[string]string{"error": "Missing limit parameter."}
		utils.WriteJson(w, 400, errmsg)
		return
	}

	resolution, err := strconv.ParseInt(q.Get("resolution"), 10, 32)
	if err != nil {
		errmsg := map[string]string{"error": "Missing resolution parameter."}
		utils.WriteJson(w, 400, errmsg)
		return
	}

	max := utils.RoundTime(time.Now(), (time.Minute * time.Duration(resolution)))
	min := max.Add(-1 * time.Minute * time.Duration(limit*resolution))

	metrics, err := store.GetMetrics(token, name, resolution, min, max)
	if err != nil {
		errmsg := map[string]string{"error": "Unable to find metrics."}
		utils.WriteJson(w, 500, errmsg)
		return
	}
	utils.WriteJson(w, 200, metrics)
}
Beispiel #14
0
func CacheGet(c Cachable) ([]byte, bool) {
	defer utils.MeasureT(time.Now(), "cache-get")
	rc := rp.Get()
	defer rc.Close()
	bs, err := redis.Bytes(rc.Do("GET", c.Key()))
	if err != nil {
		utils.MeasureE("redis-get", err)
		return nil, false
	}
	return bs, true
}
Beispiel #15
0
func fetch(t time.Time, outbox chan<- *store.Bucket) {
	fmt.Printf("at=start_fetch minute=%d\n", t.Minute())
	defer utils.MeasureT("postgres_outlet.fetch", time.Now())

	pid, err := utils.LockPartition("postgres_outlet", numPartitions, lockTTL)
	if err != nil {
		log.Fatal("Unable to lock partition.")
	}

	mailbox := fmt.Sprintf("postgres_outlet.%d", pid)
	for bucket := range store.ScanBuckets(mailbox) {
		outbox <- bucket
	}
}
Beispiel #16
0
func (s *RedisStore) putback(id *bucket.Id) error {
	defer utils.MeasureT("bucket.putback", time.Now())
	rc := s.redisPool.Get()
	defer rc.Close()
	key := id.String()
	partition := s.bucketPartition([]byte(key))
	rc.Send("MULTI")
	rc.Send("SADD", partition, key)
	rc.Send("EXPIRE", partition, 300)
	_, err := rc.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}
Beispiel #17
0
func DecodeArray(b []byte, dest *[]float64) {
	defer utils.MeasureT(time.Now(), "encoding.decode-array")
	// pq returns something like: {1.0, 2.0}
	// let us remove the { and the }
	trimed := b[1:(len(b) - 1)]
	// Assuming the numbers are seperated by commas.
	numbers := strings.Split(string(trimed), ",")
	// Showing that we can do cool things with floats.
	for _, x := range numbers {
		f, err := strconv.ParseFloat(x, 64)
		if err == nil {
			*dest = append(*dest, f)
		}
	}
}
Beispiel #18
0
func CacheSet(c Cachable) error {
	defer utils.MeasureT(time.Now(), "cache-set")
	rc := rp.Get()
	defer rc.Close()
	bs, err := json.Marshal(c)
	if err != nil {
		utils.MeasureE("cache-json-encode", err)
		return err
	}
	_, err = rc.Do("SET", c.Key(), bs)
	if err != nil {
		utils.MeasureE("cache-set", err)
		return err
	}
	return nil
}
Beispiel #19
0
// Pull data from the http request, stick it in a channel and close the request.
// We don't do any validation on the data. Always respond with 200.
func recvLogs(w http.ResponseWriter, r *http.Request, recv *receiver.Receiver) {
	defer utils.MeasureT("http-receiver", time.Now())
	if r.Method != "POST" {
		http.Error(w, "Invalid Request", 400)
		return
	}
	token, err := utils.ParseToken(r)
	if err != nil {
		http.Error(w, "Invalid Request", 400)
		return
	}
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		http.Error(w, "Invalid Request", 400)
		return
	}
	recv.Receive(token, b)
}
Beispiel #20
0
// Fetch should kick off the librato outlet process.
// Its responsibility is to get the ids of buckets for the current time,
// make empty Buckets, then place the buckets in an inbox to be filled
// (load the vals into the bucket) and processed.
func fetch(out chan<- *store.Bucket) {
	for _ = range time.Tick(time.Duration(*processInterval) * time.Second) {
		go func(out chan<- *store.Bucket) {
			startPoll := time.Now()
			max := utils.RoundTime(time.Now(), time.Minute)
			min := max.Add(-time.Minute)
			ids, err := allBucketIds(min, max)
			if err != nil {
				utils.MeasureE("find-failed", err)
				return
			}
			for i := range ids {
				b := store.Bucket{Id: ids[i]}
				out <- &b
			}
			utils.MeasureT(startPoll, "librato.fetch")
		}(out)
	}
}
Beispiel #21
0
func (b *Bucket) Get() error {
	defer utils.MeasureT("bucket.get", time.Now())

	rc := redisPool.Get()
	defer rc.Close()

	//Fill in the vals.
	reply, err := redis.Values(rc.Do("LRANGE", b.String(), 0, -1))
	if err != nil {
		return err
	}
	for _, item := range reply {
		v, ok := item.([]byte)
		if !ok {
			continue
		}
		err = encoding.DecodeArray(v, &b.Vals, '[', ']', ' ')
	}
	return nil
}
Beispiel #22
0
// Pull data from the http request, stick it in a channel and close the request.
// We don't do any validation on the data. Always respond with 200.
func recvLogs(w http.ResponseWriter, r *http.Request, recv *receiver.Receiver) {
	defer utils.MeasureT("http-receiver", time.Now())
	if r.Method != "POST" {
		http.Error(w, "Invalid Request", 400)
		return
	}
	user, pass, err := utils.ParseAuth(r)
	if err != nil {
		fmt.Printf("measure.failed-auth erro=%s user=%s pass=%s user-agent=%s token=%s client=%s\n",
			err, user, pass, r.Header.Get("User-Agent"), r.Header.Get("Logplex-Drain-Token"), r.Header.Get("X-Forwarded-For"))
		http.Error(w, "Invalid Request", 400)
		return
	}
	b, err := ioutil.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		http.Error(w, "Invalid Request", 400)
		return
	}
	recv.Receive(user, pass, b, r.URL.Query())
}
Beispiel #23
0
func receiveLogs(w http.ResponseWriter, r *http.Request, inbox chan<- *LogRequest) {
	defer utils.MeasureT("http-receiver", time.Now())
	if r.Method != "POST" {
		http.Error(w, "Invalid Request", 400)
		return
	}
	token, err := utils.ParseToken(r)
	if err != nil {
		http.Error(w, "Invalid Request", 400)
		return
	}

	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Invalid Request", 400)
		r.Body.Close()
		return
	}
	r.Body.Close()
	inbox <- &LogRequest{token, b}
}
Beispiel #24
0
func (b *Bucket) Get() {
	defer utils.MeasureT(time.Now(), "bucket.get")
	db.PGRLocker.Lock()
	rows, err := db.PGR.Query("select name, bucket, source, token, vals from metrics where id = $1",
		b.Id)
	if err != nil {
		fmt.Printf("at=error error=%s\n", err)
		return
	}
	rows.Next()
	var tmp []byte
	rows.Scan(&b.Name, &b.Time, &b.Source, &b.Token, &tmp)
	rows.Close()
	db.PGRLocker.Unlock()

	if len(tmp) == 0 {
		b.Vals = []float64{}
		return
	}
	encoding.DecodeArray(tmp, &b.Vals)
}
Beispiel #25
0
func (s *RedisStore) Put(b *bucket.Bucket) error {
	defer utils.MeasureT("bucket.put", time.Now())

	rc := s.redisPool.Get()
	defer rc.Close()

	b.Lock()
	key := b.Id.String()
	value := b.Vals
	b.Unlock()

	//TODO(ryandotsmith): Ensure consistent keys are being written.
	partition := s.bucketPartition("outlet", []byte(key))
	rc.Send("MULTI")
	rc.Send("RPUSH", key, value)
	rc.Send("EXPIRE", key, 300)
	rc.Send("SADD", partition, key)
	rc.Send("EXPIRE", partition, 300)
	_, err := rc.Do("EXEC")
	if err != nil {
		return err
	}
	return nil
}
Beispiel #26
0
func main() {
	//The store will be used by receivers and outlets.
	var st store.Store
	if conf.UsingRedis {
		st = store.NewRedisStore(conf.RedisHost,
			conf.RedisPass, conf.MaxPartitions, conf.MaxRedisConns)
		fmt.Printf("at=initialized-redis-store\n")
	} else {
		st = store.NewMemStore()
		fmt.Printf("at=initialized-mem-store\n")
	}

	//It is not possible to run both librato and graphite outlets
	//in the same process.
	switch conf.Outlet {
	case "librato":
		rdr := outlet.NewBucketReader(conf.BufferSize,
			conf.Concurrency, conf.FlushtInterval, st)
		outlet := outlet.NewLibratoOutlet(conf.BufferSize,
			conf.Concurrency, conf.NumOutletRetry, rdr)
		outlet.Start()
		if conf.Verbose {
			go outlet.Report()
		}
	case "graphite":
		rdr := &outlet.BucketReader{Store: st, Interval: conf.FlushtInterval}
		outlet := outlet.NewGraphiteOutlet(conf.BufferSize, rdr)
		outlet.Start()
	default:
		fmt.Println("No outlet running. Run `l2met -h` for outlet help.")
	}

	//The HTTP Outlet can be ran in addition to the librato or graphite outlet.
	if conf.UsingHttpOutlet {
		httpOutlet := new(outlet.HttpOutlet)
		httpOutlet.Store = st
		http.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
			httpOutlet.ServeReadBucket(w, r)
		})
	}

	if conf.UsingReciever {
		recv := receiver.NewReceiver(conf.BufferSize,
			conf.Concurrency, conf.FlushtInterval, st)
		recv.Start()
		if conf.Verbose {
			go recv.Report()
		}
		http.HandleFunc("/logs", func(w http.ResponseWriter, r *http.Request) {
			startReceiveT := time.Now()
			if r.Method != "POST" {
				http.Error(w, "Invalid Request", 400)
				return
			}
			user, pass, err := auth.Parse(r)
			if err != nil {
				fmt.Printf("measure.failed-auth erro=%s user=%s pass=%s user-agent=%s token=%s client=%s\n",
					err, user, pass, r.Header.Get("User-Agent"), r.Header.Get("Logplex-Drain-Token"), r.Header.Get("X-Forwarded-For"))
				http.Error(w, "Invalid Request", 400)
				return
			}
			b, err := ioutil.ReadAll(r.Body)
			r.Body.Close()
			if err != nil {
				http.Error(w, "Invalid Request", 400)
				return
			}
			v := r.URL.Query()
			v.Add("user", user)
			v.Add("password", pass)
			recv.Receive(b, v)
			utils.MeasureT("http-receiver", startReceiveT)
		})
	}

	//The only thing that constitutes a healthy l2met
	//is the health of the store. In some cases, this might mean
	//a redis health check.
	http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		ok := st.Health()
		if !ok {
			msg := "Store is unavailable."
			fmt.Printf("error=%q\n", msg)
			http.Error(w, msg, 500)
		}
	})

	http.HandleFunc("/sign", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "Invalid Method. Must be POST.", 400)
			return
		}
		user, pass, err := auth.ParseRaw(r.Header.Get("Authorization"))
		if err != nil {
			fmt.Printf("measure.failed-auth erro=%s user=%s pass=%s user-agent=%s token=%s client=%s\n",
				err, user, pass, r.Header.Get("User-Agent"), r.Header.Get("Logplex-Drain-Token"), r.Header.Get("X-Forwarded-For"))
			http.Error(w, "Unable to parse auth headers.", 400)
			return
		}
		matched := false
		for i := range conf.Secrets {
			if user == conf.Secrets[i] {
				matched = true
				break
			}
		}
		if !matched {
			http.Error(w, "Authentication failed.", 401)
			return
		}
		b, err := ioutil.ReadAll(r.Body)
		r.Body.Close()
		if err != nil {
			http.Error(w, "Unable to read body of POST.", 400)
			return
		}
		signed, err := auth.Sign(b)
		if err != nil {
			http.Error(w, "Unable to sign body.", 500)
			return
		}
		fmt.Fprint(w, string(signed))
	})

	//Start the HTTP server.
	if e := http.ListenAndServe(fmt.Sprintf(":%d", conf.Port), nil); e != nil {
		log.Fatal("Unable to start HTTP server.")
	}
	fmt.Printf("at=l2met-initialized port=%d\n", conf.Port)
}