Merge pull request #27 from matttproud/julius-api

Add REST API, expression browser, and text/JSON output formats.
This commit is contained in:
juliusv 2013-01-10 17:37:29 -08:00
commit f546548097
4 changed files with 148 additions and 2 deletions

11
api/api.go Normal file
View File

@ -0,0 +1,11 @@
package api
import (
"code.google.com/p/gorest"
)
type MetricsService struct {
gorest.RestService `root:"/api/" consumes:"application/json" produces:"application/json"`
query gorest.EndPoint `method:"GET" path:"/query?{expr:string}&{json:string}&{start:string}&{end:string}" output:"string"`
}

21
api/query.go Normal file
View File

@ -0,0 +1,21 @@
package api
import (
"github.com/matttproud/prometheus/rules"
"github.com/matttproud/prometheus/rules/ast"
"time"
)
func (serv MetricsService) Query(Expr string, Json string, Start string, End string) (result string) {
exprNode, err := rules.LoadExprFromString(Expr)
if err != nil {
return err.Error()
}
timestamp := time.Now()
format := ast.TEXT
if Json != "" {
format = ast.JSON
}
return ast.EvalToString(exprNode, &timestamp, format)
}

10
main.go
View File

@ -14,7 +14,9 @@
package main
import (
"code.google.com/p/gorest"
"fmt"
"github.com/matttproud/prometheus/api"
"github.com/matttproud/golang_instrumentation"
"github.com/matttproud/prometheus/config"
"github.com/matttproud/prometheus/retrieval"
@ -66,20 +68,24 @@ func main() {
}
go func() {
gorest.RegisterService(new(api.MetricsService))
exporter := registry.DefaultRegistry.YieldExporter()
http.Handle("/", gorest.Handle())
http.Handle("/metrics.json", exporter)
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.ListenAndServe(":9090", nil)
}()
for {
select {
case scrapeResult := <-scrapeResults:
fmt.Printf("scrapeResult -> %s\n", scrapeResult)
//fmt.Printf("scrapeResult -> %s\n", scrapeResult)
for _, sample := range scrapeResult.Samples {
persistence.AppendSample(&sample)
}
case ruleResult := <-ruleResults:
fmt.Printf("ruleResult -> %s\n", ruleResult)
//fmt.Printf("ruleResult -> %s\n", ruleResult)
for _, sample := range ruleResult.Samples {
persistence.AppendSample(sample)
}

View File

@ -2,11 +2,19 @@ package ast
import (
"fmt"
"encoding/json"
"sort"
"strings"
"time"
)
type OutputFormat int
const (
TEXT OutputFormat = iota
JSON
)
func binOpTypeToString(opType BinOpType) string {
opTypeMap := map[BinOpType]string{
ADD: "+",
@ -34,6 +42,16 @@ func aggrTypeToString(aggrType AggrType) string {
return aggrTypeMap[aggrType]
}
func exprTypeToString(exprType ExprType) string {
exprTypeMap := map[ExprType]string{
SCALAR: "scalar",
VECTOR: "vector",
MATRIX: "matrix",
STRING: "string",
}
return exprTypeMap[exprType]
}
func durationToString(duration time.Duration) string {
seconds := int64(duration / time.Second)
factors := map[string]int64{
@ -81,6 +99,96 @@ func (vector Vector) ToString() string {
return strings.Join(metricStrings, "\n")
}
func (matrix Matrix) ToString() string {
metricStrings := []string{}
for _, sampleSet := range matrix {
metricName, ok := sampleSet.Metric["name"]
if !ok {
panic("Tried to print matrix without metric name")
}
labelStrings := []string{}
for label, value := range sampleSet.Metric {
if label != "name" {
labelStrings = append(labelStrings, fmt.Sprintf("%v='%v'", label, value))
}
}
sort.Strings(labelStrings)
valueStrings := []string{}
for _, value := range sampleSet.Values {
valueStrings = append(valueStrings,
fmt.Sprintf("\n%v @[%v]", value.Value, value.Timestamp))
}
metricStrings = append(metricStrings,
fmt.Sprintf("%v{%v} => %v",
metricName,
strings.Join(labelStrings, ","),
strings.Join(valueStrings, ", ")))
}
sort.Strings(metricStrings)
return strings.Join(metricStrings, "\n")
}
func errorToJSON(err error) string {
errorStruct := struct {
Type string
Error string
}{
Type: "error",
Error: err.Error(),
}
errorJSON, err := json.MarshalIndent(errorStruct, "", "\t")
if err != nil {
return ""
}
return string(errorJSON)
}
func typedValueToJSON(data interface{}, typeStr string) string {
dataStruct := struct {
Type string
Value interface{}
}{
Type: typeStr,
Value: data,
}
dataJSON, err := json.MarshalIndent(dataStruct, "", "\t")
if err != nil {
return errorToJSON(err)
}
return string(dataJSON)
}
func EvalToString(node Node, timestamp *time.Time, format OutputFormat) string {
switch node.Type() {
case SCALAR:
scalar := node.(ScalarNode).Eval(timestamp)
switch format {
case TEXT: return fmt.Sprintf("scalar: %v", scalar)
case JSON: return typedValueToJSON(scalar, "scalar")
}
case VECTOR:
vector := node.(VectorNode).Eval(timestamp)
switch format {
case TEXT: return vector.ToString()
case JSON: return typedValueToJSON(vector, "vector")
}
case MATRIX:
matrix := node.(MatrixNode).Eval(timestamp)
switch format {
case TEXT: return matrix.ToString()
case JSON: return typedValueToJSON(matrix, "matrix")
}
case STRING:
str := node.(StringNode).Eval(timestamp)
switch format {
case TEXT: return str
case JSON: return typedValueToJSON(str, "string")
}
}
panic("Switch didn't cover all node types")
}
func (node *VectorLiteral) ToString() string {
metricName, ok := node.labels["name"]
if !ok {