2015-01-21 11:07:45 -08:00
// Copyright 2013 The Prometheus Authors
2013-02-22 12:07:35 -08: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-10 17:27:03 -08:00
package api
import (
2013-02-06 08:08:05 -08:00
"encoding/json"
2013-01-17 16:54:26 -08:00
"errors"
2013-10-22 11:31:52 -07:00
"fmt"
2013-02-07 02:38:01 -08:00
"net/http"
2013-01-17 15:07:00 -08:00
"sort"
2013-10-22 11:31:52 -07:00
"strconv"
2013-01-12 12:22:59 -08:00
"time"
2013-06-25 05:02:27 -07:00
2013-08-12 08:18:02 -07:00
"github.com/golang/glog"
2013-06-25 05:02:27 -07: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 07:16:49 -08:00
"github.com/prometheus/prometheus/web/httputils"
2013-01-10 17:27:03 -08:00
)
2013-01-12 12:22:59 -08:00
2013-10-22 11:31:52 -07: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 05:51:42 -07:00
}
2015-03-27 08:48:03 -07:00
func httpJSONError ( w http . ResponseWriter , err error , code int ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( code )
fmt . Fprintln ( w , ast . ErrorToJSON ( err ) )
}
2015-03-21 08:58:45 -07:00
func parseTimestampOrNow ( t string ) ( clientmodel . Timestamp , error ) {
if t == "" {
return clientmodel . Now ( ) , nil
}
2015-03-21 09:54:30 -07:00
tFloat , err := strconv . ParseFloat ( t , 64 )
if err != nil {
return 0 , err
}
return clientmodel . TimestampFromUnixNano ( int64 ( tFloat * float64 ( time . Second / time . Nanosecond ) ) ) , nil
2015-03-21 08:58:45 -07:00
}
func parseDuration ( d string ) ( time . Duration , error ) {
dFloat , err := strconv . ParseFloat ( d , 64 )
if err != nil {
return 0 , err
}
2015-03-21 09:50:45 -07:00
return time . Duration ( dFloat * float64 ( time . Second / time . Nanosecond ) ) , nil
2015-03-21 08:58:45 -07:00
}
2014-12-10 07:16:49 -08:00
// Query handles the /api/query endpoint.
2013-10-22 11:31:52 -07:00
func ( serv MetricsService ) Query ( w http . ResponseWriter , r * http . Request ) {
setAccessControlHeaders ( w )
2015-03-27 08:48:03 -07:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2013-10-16 06:59:47 -07:00
2014-12-10 07:16:49 -08:00
params := httputils . GetQueryParams ( r )
2013-10-22 11:31:52 -07:00
expr := params . Get ( "expr" )
2015-03-20 15:10:58 -07:00
2015-03-21 08:58:45 -07:00
timestamp , err := parseTimestampOrNow ( params . Get ( "timestamp" ) )
if err != nil {
2015-03-27 08:48:03 -07:00
httpJSONError ( w , fmt . Errorf ( "invalid query timestamp %s" , err ) , http . StatusBadRequest )
2015-03-21 08:58:45 -07:00
return
2015-03-20 15:10:58 -07:00
}
2013-01-10 17:27:03 -08:00
2013-10-22 11:31:52 -07:00
exprNode , err := rules . LoadExprFromString ( expr )
if err != nil {
fmt . Fprint ( w , ast . ErrorToJSON ( err ) )
return
2013-01-12 12:22:59 -08:00
}
2013-01-10 18:17:58 -08:00
2013-06-03 08:07:03 -07:00
queryStats := stats . NewTimerGroup ( )
2015-03-27 08:48:03 -07:00
result := ast . EvalToString ( exprNode , timestamp , ast . JSON , serv . Storage , queryStats )
2014-06-05 07:25:37 -07:00
glog . V ( 1 ) . Infof ( "Instant query: %s\nQuery stats:\n%s\n" , expr , queryStats )
2013-10-22 11:31:52 -07:00
fmt . Fprint ( w , result )
2013-01-10 17:27:03 -08:00
}
2013-01-15 02:30:55 -08:00
2014-12-10 07:16:49 -08:00
// QueryRange handles the /api/query_range endpoint.
2013-10-22 11:31:52 -07:00
func ( serv MetricsService ) QueryRange ( w http . ResponseWriter , r * http . Request ) {
setAccessControlHeaders ( w )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2014-12-10 07:16:49 -08:00
params := httputils . GetQueryParams ( r )
2013-10-22 11:31:52 -07:00
expr := params . Get ( "expr" )
2014-10-20 05:51:39 -07:00
2015-03-21 08:58:45 -07:00
duration , err := parseDuration ( params . Get ( "range" ) )
2013-01-15 02:30:55 -08:00
if err != nil {
2015-03-27 08:48:03 -07:00
httpJSONError ( w , fmt . Errorf ( "invalid query range: %s" , err ) , http . StatusBadRequest )
2013-10-22 11:31:52 -07:00
return
2013-01-15 02:30:55 -08:00
}
2015-03-21 08:58:45 -07:00
step , err := parseDuration ( params . Get ( "step" ) )
if err != nil {
2015-03-27 08:48:03 -07:00
httpJSONError ( w , fmt . Errorf ( "invalid query resolution: %s" , err ) , http . StatusBadRequest )
2013-10-22 11:31:52 -07:00
return
2013-01-17 15:07:00 -08:00
}
2013-01-15 02:30:55 -08:00
2015-03-21 08:58:45 -07:00
end , err := parseTimestampOrNow ( params . Get ( "end" ) )
if err != nil {
2015-03-27 08:48:03 -07:00
httpJSONError ( w , fmt . Errorf ( "invalid query timestamp: %s" , err ) , http . StatusBadRequest )
2015-03-21 08:58:45 -07:00
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 02:18:35 -07:00
if end == 0 {
2015-03-21 08:58:45 -07:00
end = clientmodel . Now ( )
2013-01-17 15:07:00 -08:00
}
2013-01-15 02:30:55 -08:00
2015-03-21 08:58:45 -07:00
exprNode , err := rules . LoadExprFromString ( expr )
if err != nil {
fmt . Fprint ( w , ast . ErrorToJSON ( err ) )
return
2013-01-17 15:07:00 -08:00
}
2015-03-21 08:58:45 -07:00
if exprNode . Type ( ) != ast . VectorType {
fmt . Fprint ( w , ast . ErrorToJSON ( errors . New ( "expression does not evaluate to vector type" ) ) )
return
2013-01-17 15:07:00 -08:00
}
2013-01-15 02:30:55 -08:00
2014-10-20 05:51:39 -07: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 07:16:49 -08: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 05:51:39 -07:00
return
}
2013-01-17 15:07:00 -08:00
// Align the start to step "tick" boundary.
2015-03-21 08:58:45 -07:00
end = end . Add ( - time . Duration ( end . UnixNano ( ) % int64 ( step ) ) )
2013-01-15 02:30:55 -08:00
2013-06-03 08:07:03 -07:00
queryStats := stats . NewTimerGroup ( )
2013-03-21 10:06:15 -07:00
matrix , err := ast . EvalVectorRange (
2013-01-17 15:07:00 -08:00
exprNode . ( ast . VectorNode ) ,
2015-03-21 08:58:45 -07:00
end . Add ( - duration ) ,
end ,
step ,
2013-06-03 08:07:03 -07:00
serv . Storage ,
queryStats )
2013-03-21 10:06:15 -07:00
if err != nil {
2013-10-22 11:31:52 -07:00
fmt . Fprint ( w , ast . ErrorToJSON ( err ) )
return
2013-03-21 10:06:15 -07:00
}
2013-01-15 02:30:55 -08:00
2013-06-03 08:07:03 -07:00
sortTimer := queryStats . GetTimer ( stats . ResultSortTime ) . Start ( )
2013-01-17 15:07:00 -08:00
sort . Sort ( matrix )
2013-06-03 08:07:03 -07:00
sortTimer . Stop ( )
2014-12-10 07:16:49 -08:00
jsonTimer := queryStats . GetTimer ( stats . JSONEncodeTime ) . Start ( )
2013-06-03 08:07:03 -07:00
result := ast . TypedValueToJSON ( matrix , "matrix" )
jsonTimer . Stop ( )
2014-06-05 07:25:37 -07:00
glog . V ( 1 ) . Infof ( "Range query: %s\nQuery stats:\n%s\n" , expr , queryStats )
2013-10-22 11:31:52 -07:00
fmt . Fprint ( w , result )
2013-01-15 02:30:55 -08:00
}
2013-02-06 08:08:05 -08:00
2014-12-10 08:46:56 -08:00
// Metrics handles the /api/metrics endpoint.
2013-10-22 11:31:52 -07:00
func ( serv MetricsService ) Metrics ( w http . ResponseWriter , r * http . Request ) {
setAccessControlHeaders ( w )
2015-03-27 08:48:03 -07:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2013-10-16 06:59:47 -07:00
2014-09-19 09:18:44 -07:00
metricNames := serv . Storage . GetLabelValuesForLabelName ( clientmodel . MetricNameLabel )
2013-03-26 03:45:56 -07:00
sort . Sort ( metricNames )
2013-02-06 08:08:05 -08:00
resultBytes , err := json . Marshal ( metricNames )
if err != nil {
2013-08-12 09:22:48 -07:00
glog . Error ( "Error marshalling metric names: " , err )
2015-03-27 08:48:03 -07:00
httpJSONError ( w , fmt . Errorf ( "Error marshalling metric names: %s" , err ) , http . StatusInternalServerError )
2013-10-22 11:31:52 -07:00
return
2013-02-06 08:08:05 -08:00
}
2013-10-22 11:31:52 -07:00
w . Write ( resultBytes )
2013-02-06 08:08:05 -08:00
}