2015-01-21 19:07:45 +00:00
// Copyright 2013 The Prometheus Authors
2013-02-22 20:07:35 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2013-01-11 01:27:03 +00:00
package api
import (
2013-02-06 16:08:05 +00:00
"encoding/json"
2013-01-18 00:54:26 +00:00
"errors"
2013-10-22 18:31:52 +00:00
"fmt"
2013-02-07 10:38:01 +00:00
"net/http"
2013-01-17 23:07:00 +00:00
"sort"
2013-10-22 18:31:52 +00:00
"strconv"
2013-01-12 20:22:59 +00:00
"time"
2013-06-25 12:02:27 +00:00
2013-08-12 15:18:02 +00:00
"github.com/golang/glog"
2013-06-25 12:02:27 +00:00
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/rules/ast"
"github.com/prometheus/prometheus/stats"
2014-12-10 15:16:49 +00:00
"github.com/prometheus/prometheus/web/httputils"
2013-01-11 01:27:03 +00:00
)
2013-01-12 20:22:59 +00:00
2013-10-22 18:31:52 +00:00
// Enables cross-site script calls.
func setAccessControlHeaders ( w http . ResponseWriter ) {
w . Header ( ) . Set ( "Access-Control-Allow-Headers" , "Accept, Authorization, Content-Type, Origin" )
w . Header ( ) . Set ( "Access-Control-Allow-Methods" , "GET" )
w . Header ( ) . Set ( "Access-Control-Allow-Origin" , "*" )
w . Header ( ) . Set ( "Access-Control-Expose-Headers" , "Date" )
2013-04-11 12:51:42 +00:00
}
2015-03-21 15:58:45 +00:00
func parseTimestampOrNow ( t string ) ( clientmodel . Timestamp , error ) {
if t == "" {
return clientmodel . Now ( ) , nil
} else {
tFloat , err := strconv . ParseFloat ( t , 64 )
if err != nil {
return 0 , err
}
return clientmodel . TimestampFromUnixNano ( int64 ( tFloat ) * int64 ( time . Second / time . Nanosecond ) ) , nil
}
}
func parseDuration ( d string ) ( time . Duration , error ) {
dFloat , err := strconv . ParseFloat ( d , 64 )
if err != nil {
return 0 , err
}
return time . Duration ( dFloat ) * ( time . Second / time . Nanosecond ) , nil
}
2014-12-10 15:16:49 +00:00
// Query handles the /api/query endpoint.
2013-10-22 18:31:52 +00:00
func ( serv MetricsService ) Query ( w http . ResponseWriter , r * http . Request ) {
setAccessControlHeaders ( w )
2013-10-16 13:59:47 +00:00
2014-12-10 15:16:49 +00:00
params := httputils . GetQueryParams ( r )
2013-10-22 18:31:52 +00:00
expr := params . Get ( "expr" )
asText := params . Get ( "asText" )
2015-03-20 22:10:58 +00:00
2015-03-21 15:58:45 +00:00
timestamp , err := parseTimestampOrNow ( params . Get ( "timestamp" ) )
if err != nil {
http . Error ( w , fmt . Sprintf ( "invalid query timestamp %s" , err ) , http . StatusBadRequest )
return
2015-03-20 22:10:58 +00:00
}
2013-01-11 01:27:03 +00:00
2013-01-12 20:22:59 +00:00
var format ast . OutputFormat
2013-07-30 15:18:07 +00:00
// BUG(julius): Use Content-Type negotiation.
2013-07-24 00:18:49 +00:00
if asText == "" {
2013-01-12 20:22:59 +00:00
format = ast . JSON
2013-10-22 18:31:52 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2013-01-12 20:22:59 +00:00
} else {
2014-12-25 00:28:35 +00:00
format = ast . Text
2013-10-22 18:31:52 +00:00
w . Header ( ) . Set ( "Content-Type" , "text/plain" )
}
exprNode , err := rules . LoadExprFromString ( expr )
if err != nil {
fmt . Fprint ( w , ast . ErrorToJSON ( err ) )
return
2013-01-12 20:22:59 +00:00
}
2013-01-11 02:17:58 +00:00
2013-06-03 15:07:03 +00:00
queryStats := stats . NewTimerGroup ( )
result := ast . EvalToString ( exprNode , timestamp , format , serv . Storage , queryStats )
2014-06-05 14:25:37 +00:00
glog . V ( 1 ) . Infof ( "Instant query: %s\nQuery stats:\n%s\n" , expr , queryStats )
2013-10-22 18:31:52 +00:00
fmt . Fprint ( w , result )
2013-01-11 01:27:03 +00:00
}
2013-01-15 10:30:55 +00:00
2014-12-10 15:16:49 +00:00
// QueryRange handles the /api/query_range endpoint.
2013-10-22 18:31:52 +00:00
func ( serv MetricsService ) QueryRange ( w http . ResponseWriter , r * http . Request ) {
setAccessControlHeaders ( w )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2014-12-10 15:16:49 +00:00
params := httputils . GetQueryParams ( r )
2013-10-22 18:31:52 +00:00
expr := params . Get ( "expr" )
2014-10-20 12:51:39 +00:00
2015-03-21 15:58:45 +00:00
duration , err := parseDuration ( params . Get ( "range" ) )
2013-01-15 10:30:55 +00:00
if err != nil {
2015-03-21 15:58:45 +00:00
http . Error ( w , fmt . Sprintf ( "invalid query range: %s" , err ) , http . StatusBadRequest )
2013-10-22 18:31:52 +00:00
return
2013-01-15 10:30:55 +00:00
}
2015-03-21 15:58:45 +00:00
step , err := parseDuration ( params . Get ( "step" ) )
if err != nil {
http . Error ( w , fmt . Sprintf ( "invalid query resolution: %s" , err ) , http . StatusBadRequest )
2013-10-22 18:31:52 +00:00
return
2013-01-17 23:07:00 +00:00
}
2013-01-15 10:30:55 +00:00
2015-03-21 15:58:45 +00:00
end , err := parseTimestampOrNow ( params . Get ( "end" ) )
if err != nil {
http . Error ( w , fmt . Sprintf ( "invalid query timestamp: %s" , err ) , http . StatusBadRequest )
return
}
// TODO(julius): Remove this special-case handling a while after PromDash and
// other API consumers have been changed to no longer set "end=0" for setting
// the current time as the end time. Instead, the "end" parameter should
// simply be omitted or set to an empty string for that case.
2013-03-18 09:18:35 +00:00
if end == 0 {
2015-03-21 15:58:45 +00:00
end = clientmodel . Now ( )
2013-01-17 23:07:00 +00:00
}
2013-01-15 10:30:55 +00:00
2015-03-21 15:58:45 +00:00
exprNode , err := rules . LoadExprFromString ( expr )
if err != nil {
fmt . Fprint ( w , ast . ErrorToJSON ( err ) )
return
2013-01-17 23:07:00 +00:00
}
2015-03-21 15:58:45 +00:00
if exprNode . Type ( ) != ast . VectorType {
fmt . Fprint ( w , ast . ErrorToJSON ( errors . New ( "expression does not evaluate to vector type" ) ) )
return
2013-01-17 23:07:00 +00:00
}
2013-01-15 10:30:55 +00:00
2014-10-20 12:51:39 +00:00
// For safety, limit the number of returned points per timeseries.
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
if duration / step > 11000 {
2014-12-10 15:16:49 +00:00
fmt . Fprint ( w , ast . ErrorToJSON ( errors . New ( "exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)" ) ) )
2014-10-20 12:51:39 +00:00
return
}
2013-01-17 23:07:00 +00:00
// Align the start to step "tick" boundary.
2015-03-21 15:58:45 +00:00
end = end . Add ( - time . Duration ( end . UnixNano ( ) % int64 ( step ) ) )
2013-01-15 10:30:55 +00:00
2013-06-03 15:07:03 +00:00
queryStats := stats . NewTimerGroup ( )
2013-03-21 17:06:15 +00:00
matrix , err := ast . EvalVectorRange (
2013-01-17 23:07:00 +00:00
exprNode . ( ast . VectorNode ) ,
2015-03-21 15:58:45 +00:00
end . Add ( - duration ) ,
end ,
step ,
2013-06-03 15:07:03 +00:00
serv . Storage ,
queryStats )
2013-03-21 17:06:15 +00:00
if err != nil {
2013-10-22 18:31:52 +00:00
fmt . Fprint ( w , ast . ErrorToJSON ( err ) )
return
2013-03-21 17:06:15 +00:00
}
2013-01-15 10:30:55 +00:00
2013-06-03 15:07:03 +00:00
sortTimer := queryStats . GetTimer ( stats . ResultSortTime ) . Start ( )
2013-01-17 23:07:00 +00:00
sort . Sort ( matrix )
2013-06-03 15:07:03 +00:00
sortTimer . Stop ( )
2014-12-10 15:16:49 +00:00
jsonTimer := queryStats . GetTimer ( stats . JSONEncodeTime ) . Start ( )
2013-06-03 15:07:03 +00:00
result := ast . TypedValueToJSON ( matrix , "matrix" )
jsonTimer . Stop ( )
2014-06-05 14:25:37 +00:00
glog . V ( 1 ) . Infof ( "Range query: %s\nQuery stats:\n%s\n" , expr , queryStats )
2013-10-22 18:31:52 +00:00
fmt . Fprint ( w , result )
2013-01-15 10:30:55 +00:00
}
2013-02-06 16:08:05 +00:00
2014-12-10 16:46:56 +00:00
// Metrics handles the /api/metrics endpoint.
2013-10-22 18:31:52 +00:00
func ( serv MetricsService ) Metrics ( w http . ResponseWriter , r * http . Request ) {
setAccessControlHeaders ( w )
2013-10-16 13:59:47 +00:00
2014-09-19 16:18:44 +00:00
metricNames := serv . Storage . GetLabelValuesForLabelName ( clientmodel . MetricNameLabel )
2013-03-26 10:45:56 +00:00
sort . Sort ( metricNames )
2013-02-06 16:08:05 +00:00
resultBytes , err := json . Marshal ( metricNames )
if err != nil {
2013-08-12 16:22:48 +00:00
glog . Error ( "Error marshalling metric names: " , err )
2013-10-22 18:31:52 +00:00
http . Error ( w , err . Error ( ) , http . StatusInternalServerError )
return
2013-02-06 16:08:05 +00:00
}
2014-06-06 09:55:53 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2013-10-22 18:31:52 +00:00
w . Write ( resultBytes )
2013-02-06 16:08:05 +00:00
}