2019-05-22 21:16:55 +00:00
package vmselect
import (
2021-07-07 14:04:23 +00:00
"embed"
2020-06-30 21:02:02 +00:00
"errors"
2019-05-22 21:16:55 +00:00
"flag"
2019-08-23 06:46:45 +00:00
"fmt"
2019-05-22 21:16:55 +00:00
"net/http"
2022-05-03 17:55:15 +00:00
"net/http/httputil"
"net/url"
2019-05-22 21:16:55 +00:00
"strings"
"time"
2020-09-10 21:28:19 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/graphite"
2020-11-04 14:46:10 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
2019-05-22 21:16:55 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/prometheus"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/promql"
2020-09-21 22:21:20 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/searchutils"
2019-05-22 21:16:55 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmstorage"
2020-12-08 18:49:32 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
2020-11-04 14:46:10 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
2019-05-22 21:16:55 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
2022-05-31 23:29:19 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/querytracer"
2019-05-28 14:17:19 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/timerpool"
2019-05-22 21:16:55 +00:00
"github.com/VictoriaMetrics/metrics"
)
var (
2020-11-23 13:35:59 +00:00
deleteAuthKey = flag . String ( "deleteAuthKey" , "" , "authKey for metrics' deletion via /api/v1/admin/tsdb/delete_series and /tags/delSeries" )
2020-01-17 13:43:47 +00:00
maxConcurrentRequests = flag . Int ( "search.maxConcurrentRequests" , getDefaultMaxConcurrentRequests ( ) , "The maximum number of concurrent search requests. " +
2020-02-04 13:46:13 +00:00
"It shouldn't be high, since a single request can saturate all the CPU cores. See also -search.maxQueueDuration" )
2020-09-21 22:21:20 +00:00
maxQueueDuration = flag . Duration ( "search.maxQueueDuration" , 10 * time . Second , "The maximum time the request waits for execution when -search.maxConcurrentRequests " +
"limit is reached; see also -search.maxQueryDuration" )
2021-06-18 16:04:42 +00:00
resetCacheAuthKey = flag . String ( "search.resetCacheAuthKey" , "" , "Optional authKey for resetting rollup cache via /internal/resetRollupResultCache call" )
logSlowQueryDuration = flag . Duration ( "search.logSlowQueryDuration" , 5 * time . Second , "Log queries with execution time exceeding this value. Zero disables slow query logging" )
2022-05-03 17:55:15 +00:00
vmalertProxyURL = flag . String ( "vmalert.proxyURL" , "" , "Optional URL for proxying alerting API requests from Grafana. For example, if -vmalert.proxyURL is set to http://vmalert:8880 , then requests to /api/v1/rules are proxied to http://vmalert:8880/api/v1/rules" )
2019-05-22 21:16:55 +00:00
)
2021-06-18 16:04:42 +00:00
var slowQueries = metrics . NewCounter ( ` vm_slow_queries_total ` )
2020-01-17 13:43:47 +00:00
func getDefaultMaxConcurrentRequests ( ) int {
2020-12-08 18:49:32 +00:00
n := cgroup . AvailableCPUs ( )
2020-01-17 13:43:47 +00:00
if n <= 4 {
n *= 2
}
if n > 16 {
// A single request can saturate all the CPU cores, so there is no sense
// in allowing higher number of concurrent requests - they will just contend
// for unavailable CPU time.
n = 16
}
return n
}
2019-05-22 21:16:55 +00:00
// Init initializes vmselect
func Init ( ) {
2020-11-04 14:46:10 +00:00
tmpDirPath := * vmstorage . DataPath + "/tmp"
fs . RemoveDirContents ( tmpDirPath )
netstorage . InitTmpBlocksDir ( tmpDirPath )
2019-05-22 21:16:55 +00:00
promql . InitRollupResultCache ( * vmstorage . DataPath + "/cache/rollupResult" )
2019-08-05 15:27:50 +00:00
2019-05-22 21:16:55 +00:00
concurrencyCh = make ( chan struct { } , * maxConcurrentRequests )
2022-05-03 17:55:15 +00:00
initVMAlertProxy ( )
2019-05-22 21:16:55 +00:00
}
// Stop stops vmselect
func Stop ( ) {
promql . StopRollupResultCache ( )
}
2019-08-05 15:27:50 +00:00
var concurrencyCh chan struct { }
var (
concurrencyLimitReached = metrics . NewCounter ( ` vm_concurrent_select_limit_reached_total ` )
concurrencyLimitTimeout = metrics . NewCounter ( ` vm_concurrent_select_limit_timeout_total ` )
_ = metrics . NewGauge ( ` vm_concurrent_select_capacity ` , func ( ) float64 {
return float64 ( cap ( concurrencyCh ) )
} )
_ = metrics . NewGauge ( ` vm_concurrent_select_current ` , func ( ) float64 {
return float64 ( len ( concurrencyCh ) )
} )
)
2021-07-09 14:04:28 +00:00
//go:embed vmui
var vmuiFiles embed . FS
2021-07-07 14:28:06 +00:00
2022-06-06 21:57:05 +00:00
var vmuiFileServer = http . FileServer ( http . FS ( vmuiFiles ) )
2021-07-07 14:28:06 +00:00
2021-07-07 09:59:03 +00:00
// RequestHandler handles remote read API requests
2019-05-22 21:16:55 +00:00
func RequestHandler ( w http . ResponseWriter , r * http . Request ) bool {
2020-02-04 14:13:59 +00:00
startTime := time . Now ( )
2021-07-07 14:28:06 +00:00
defer requestDuration . UpdateDuration ( startTime )
2022-05-31 23:29:19 +00:00
tracerEnabled := searchutils . GetBool ( r , "trace" )
2022-06-08 18:05:17 +00:00
qt := querytracer . New ( tracerEnabled , r . URL . Path )
2021-07-07 14:28:06 +00:00
2019-05-22 21:16:55 +00:00
// Limit the number of concurrent queries.
select {
case concurrencyCh <- struct { } { } :
defer func ( ) { <- concurrencyCh } ( )
2019-08-05 15:27:50 +00:00
default :
// Sleep for a while until giving up. This should resolve short bursts in requests.
concurrencyLimitReached . Inc ( )
2020-09-21 22:21:20 +00:00
d := searchutils . GetMaxQueryDuration ( r )
if d > * maxQueueDuration {
d = * maxQueueDuration
}
t := timerpool . Get ( d )
2019-08-05 15:27:50 +00:00
select {
case concurrencyCh <- struct { } { } :
2022-05-31 23:29:19 +00:00
qt . Printf ( "wait in queue because -search.maxConcurrentRequests=%d concurrent requests are executed" , * maxConcurrentRequests )
2019-08-05 15:27:50 +00:00
timerpool . Put ( t )
defer func ( ) { <- concurrencyCh } ( )
case <- t . C :
timerpool . Put ( t )
concurrencyLimitTimeout . Inc ( )
2019-08-23 06:46:45 +00:00
err := & httpserver . ErrorWithStatusCode {
2020-01-17 11:24:37 +00:00
Err : fmt . Errorf ( "cannot handle more than %d concurrent search requests during %s; possible solutions: " +
2020-09-21 22:21:20 +00:00
"increase `-search.maxQueueDuration`; increase `-search.maxQueryDuration`; increase `-search.maxConcurrentRequests`; " +
"increase server capacity" ,
* maxConcurrentRequests , d ) ,
2019-08-23 06:46:45 +00:00
StatusCode : http . StatusServiceUnavailable ,
}
2020-07-20 11:00:33 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2019-08-05 15:27:50 +00:00
return true
}
2019-05-22 21:16:55 +00:00
}
2021-06-18 16:04:42 +00:00
if * logSlowQueryDuration > 0 {
actualStartTime := time . Now ( )
defer func ( ) {
d := time . Since ( actualStartTime )
if d >= * logSlowQueryDuration {
remoteAddr := httpserver . GetQuotedRemoteAddr ( r )
2021-07-07 09:59:03 +00:00
requestURI := httpserver . GetRequestURI ( r )
2021-06-18 16:04:42 +00:00
logger . Warnf ( "slow query according to -search.logSlowQueryDuration=%s: remoteAddr=%s, duration=%.3f seconds; requestURI: %q" ,
* logSlowQueryDuration , remoteAddr , d . Seconds ( ) , requestURI )
slowQueries . Inc ( )
}
} ( )
}
2019-05-22 21:16:55 +00:00
path := strings . Replace ( r . URL . Path , "//" , "/" , - 1 )
2020-07-08 15:55:25 +00:00
if path == "/internal/resetRollupResultCache" {
if len ( * resetCacheAuthKey ) > 0 && r . FormValue ( "authKey" ) != * resetCacheAuthKey {
sendPrometheusError ( w , r , fmt . Errorf ( "invalid authKey=%q for %q" , r . FormValue ( "authKey" ) , path ) )
return true
}
promql . ResetRollupResultCache ( )
return true
2020-02-21 11:53:18 +00:00
}
2021-02-04 18:00:22 +00:00
// Strip /prometheus and /graphite prefixes in order to provide path compatibility with cluster version
//
2021-04-20 17:16:17 +00:00
// See https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#url-format
2021-02-04 18:00:22 +00:00
switch {
2021-09-15 13:18:12 +00:00
case strings . HasPrefix ( path , "/prometheus/" ) :
2021-02-04 18:00:22 +00:00
path = path [ len ( "/prometheus" ) : ]
2021-09-15 13:18:12 +00:00
case strings . HasPrefix ( path , "/graphite/" ) :
2021-02-04 18:00:22 +00:00
path = path [ len ( "/graphite" ) : ]
}
2021-09-15 13:18:12 +00:00
// vmui access.
if strings . HasPrefix ( path , "/vmui" ) {
r . URL . Path = path
vmuiFileServer . ServeHTTP ( w , r )
return true
}
2021-09-21 10:28:12 +00:00
if path == "/graph" {
// Redirect to /graph/, otherwise vmui redirects to /vmui/, which can be inaccessible in user env.
// Use relative redirect, since, since the hostname and path prefix may be incorrect if VictoriaMetrics
// is hidden behind vmauth or similar proxy.
_ = r . ParseForm ( )
newURL := "graph/?" + r . Form . Encode ( )
http . Redirect ( w , r , newURL , http . StatusFound )
return true
}
if strings . HasPrefix ( path , "/graph/" ) {
2021-09-15 13:18:12 +00:00
// This is needed for serving /graph URLs from Prometheus datasource in Grafana.
r . URL . Path = strings . Replace ( path , "/graph/" , "/vmui/" , 1 )
vmuiFileServer . ServeHTTP ( w , r )
return true
}
2019-05-22 21:16:55 +00:00
if strings . HasPrefix ( path , "/api/v1/label/" ) {
2021-02-04 17:28:44 +00:00
s := path [ len ( "/api/v1/label/" ) : ]
2019-05-22 21:16:55 +00:00
if strings . HasSuffix ( s , "/values" ) {
labelValuesRequests . Inc ( )
labelName := s [ : len ( s ) - len ( "/values" ) ]
httpserver . EnableCORS ( w , r )
2022-05-31 23:29:19 +00:00
if err := prometheus . LabelValuesHandler ( qt , startTime , labelName , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
labelValuesErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
}
}
2020-11-16 12:49:46 +00:00
if strings . HasPrefix ( path , "/tags/" ) && ! isGraphiteTagsPath ( path ) {
2021-02-04 17:28:44 +00:00
tagName := path [ len ( "/tags/" ) : ]
2020-11-16 01:31:09 +00:00
graphiteTagValuesRequests . Inc ( )
if err := graphite . TagValuesHandler ( startTime , tagName , w , r ) ; err != nil {
graphiteTagValuesErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-16 01:31:09 +00:00
return true
}
return true
}
2021-09-16 08:18:26 +00:00
if strings . HasPrefix ( path , "/functions" ) {
graphiteFunctionsRequests . Inc ( )
2021-11-09 16:03:50 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-16 08:18:26 +00:00
fmt . Fprintf ( w , "%s" , ` { } ` )
return true
}
2019-05-22 21:16:55 +00:00
switch path {
case "/api/v1/query" :
queryRequests . Inc ( )
httpserver . EnableCORS ( w , r )
2022-05-31 23:29:19 +00:00
if err := prometheus . QueryHandler ( qt , startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
queryErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
case "/api/v1/query_range" :
queryRangeRequests . Inc ( )
httpserver . EnableCORS ( w , r )
2022-05-31 23:29:19 +00:00
if err := prometheus . QueryRangeHandler ( qt , startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
queryRangeErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
case "/api/v1/series" :
seriesRequests . Inc ( )
httpserver . EnableCORS ( w , r )
2022-05-31 23:29:19 +00:00
if err := prometheus . SeriesHandler ( qt , startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
seriesErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
case "/api/v1/series/count" :
seriesCountRequests . Inc ( )
httpserver . EnableCORS ( w , r )
2020-02-04 14:13:59 +00:00
if err := prometheus . SeriesCountHandler ( startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
seriesCountErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
case "/api/v1/labels" :
labelsRequests . Inc ( )
httpserver . EnableCORS ( w , r )
2022-05-31 23:29:19 +00:00
if err := prometheus . LabelsHandler ( qt , startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
labelsErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
2019-06-10 15:55:20 +00:00
case "/api/v1/labels/count" :
labelsCountRequests . Inc ( )
httpserver . EnableCORS ( w , r )
2020-02-04 14:13:59 +00:00
if err := prometheus . LabelsCountHandler ( startTime , w , r ) ; err != nil {
2019-06-10 15:55:20 +00:00
labelsCountErrors . Inc ( )
sendPrometheusError ( w , r , err )
return true
}
return true
2020-04-22 16:57:36 +00:00
case "/api/v1/status/tsdb" :
2020-07-08 15:55:25 +00:00
statusTSDBRequests . Inc ( )
2022-06-08 15:43:05 +00:00
httpserver . EnableCORS ( w , r )
2022-06-09 16:46:26 +00:00
if err := prometheus . TSDBStatusHandler ( qt , startTime , w , r ) ; err != nil {
2020-07-08 15:55:25 +00:00
statusTSDBErrors . Inc ( )
2020-04-22 16:57:36 +00:00
sendPrometheusError ( w , r , err )
return true
}
return true
2020-07-08 15:55:25 +00:00
case "/api/v1/status/active_queries" :
statusActiveQueriesRequests . Inc ( )
promql . WriteActiveQueries ( w )
return true
2020-12-27 10:53:50 +00:00
case "/api/v1/status/top_queries" :
topQueriesRequests . Inc ( )
2022-06-08 15:43:05 +00:00
httpserver . EnableCORS ( w , r )
2020-12-27 10:53:50 +00:00
if err := prometheus . QueryStatsHandler ( startTime , w , r ) ; err != nil {
topQueriesErrors . Inc ( )
sendPrometheusError ( w , r , fmt . Errorf ( "cannot query status endpoint: %w" , err ) )
return true
}
return true
2019-05-22 21:16:55 +00:00
case "/api/v1/export" :
exportRequests . Inc ( )
2020-02-04 14:13:59 +00:00
if err := prometheus . ExportHandler ( startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
exportErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2019-05-22 21:16:55 +00:00
return true
}
return true
2020-10-12 17:01:51 +00:00
case "/api/v1/export/csv" :
exportCSVRequests . Inc ( )
if err := prometheus . ExportCSVHandler ( startTime , w , r ) ; err != nil {
exportCSVErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-10-12 17:01:51 +00:00
return true
}
return true
2020-09-26 01:29:45 +00:00
case "/api/v1/export/native" :
exportNativeRequests . Inc ( )
if err := prometheus . ExportNativeHandler ( startTime , w , r ) ; err != nil {
exportNativeErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-09-26 01:29:45 +00:00
return true
}
return true
2019-05-22 21:16:55 +00:00
case "/federate" :
federateRequests . Inc ( )
2020-02-04 14:13:59 +00:00
if err := prometheus . FederateHandler ( startTime , w , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
federateErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2019-05-22 21:16:55 +00:00
return true
}
return true
2020-09-10 21:28:19 +00:00
case "/metrics/find" , "/metrics/find/" :
graphiteMetricsFindRequests . Inc ( )
httpserver . EnableCORS ( w , r )
if err := graphite . MetricsFindHandler ( startTime , w , r ) ; err != nil {
graphiteMetricsFindErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-09-10 21:28:19 +00:00
return true
}
return true
case "/metrics/expand" , "/metrics/expand/" :
graphiteMetricsExpandRequests . Inc ( )
httpserver . EnableCORS ( w , r )
if err := graphite . MetricsExpandHandler ( startTime , w , r ) ; err != nil {
graphiteMetricsExpandErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-09-10 21:28:19 +00:00
return true
}
return true
case "/metrics/index.json" , "/metrics/index.json/" :
graphiteMetricsIndexRequests . Inc ( )
httpserver . EnableCORS ( w , r )
if err := graphite . MetricsIndexHandler ( startTime , w , r ) ; err != nil {
graphiteMetricsIndexErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-09-10 21:28:19 +00:00
return true
}
return true
2020-11-23 10:33:17 +00:00
case "/tags/tagSeries" :
graphiteTagsTagSeriesRequests . Inc ( )
if err := graphite . TagsTagSeriesHandler ( startTime , w , r ) ; err != nil {
graphiteTagsTagSeriesErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-23 10:33:17 +00:00
return true
}
return true
case "/tags/tagMultiSeries" :
graphiteTagsTagMultiSeriesRequests . Inc ( )
if err := graphite . TagsTagMultiSeriesHandler ( startTime , w , r ) ; err != nil {
graphiteTagsTagMultiSeriesErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-23 10:33:17 +00:00
return true
}
return true
2020-11-15 23:25:38 +00:00
case "/tags" :
graphiteTagsRequests . Inc ( )
if err := graphite . TagsHandler ( startTime , w , r ) ; err != nil {
graphiteTagsErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-15 23:25:38 +00:00
return true
}
return true
2020-11-16 08:55:55 +00:00
case "/tags/findSeries" :
graphiteTagsFindSeriesRequests . Inc ( )
if err := graphite . TagsFindSeriesHandler ( startTime , w , r ) ; err != nil {
graphiteTagsFindSeriesErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-16 08:55:55 +00:00
return true
}
return true
2020-11-16 12:49:46 +00:00
case "/tags/autoComplete/tags" :
graphiteTagsAutoCompleteTagsRequests . Inc ( )
httpserver . EnableCORS ( w , r )
if err := graphite . TagsAutoCompleteTagsHandler ( startTime , w , r ) ; err != nil {
graphiteTagsAutoCompleteTagsErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-16 12:49:46 +00:00
return true
}
return true
2020-11-16 13:22:36 +00:00
case "/tags/autoComplete/values" :
graphiteTagsAutoCompleteValuesRequests . Inc ( )
httpserver . EnableCORS ( w , r )
if err := graphite . TagsAutoCompleteValuesHandler ( startTime , w , r ) ; err != nil {
graphiteTagsAutoCompleteValuesErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-16 13:22:36 +00:00
return true
}
return true
2020-11-23 13:26:20 +00:00
case "/tags/delSeries" :
graphiteTagsDelSeriesRequests . Inc ( )
2020-11-23 13:35:59 +00:00
authKey := r . FormValue ( "authKey" )
if authKey != * deleteAuthKey {
httpserver . Errorf ( w , r , "invalid authKey %q. It must match the value from -deleteAuthKey command line flag" , authKey )
return true
}
2020-11-23 13:26:20 +00:00
if err := graphite . TagsDelSeriesHandler ( startTime , w , r ) ; err != nil {
graphiteTagsDelSeriesErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2020-11-23 13:26:20 +00:00
return true
}
return true
2021-07-29 06:48:43 +00:00
case "/api/v1/rules" , "/rules" :
2021-07-29 03:15:35 +00:00
// Return dumb placeholder for https://prometheus.io/docs/prometheus/latest/querying/api/#rules
2019-12-03 17:32:57 +00:00
rulesRequests . Inc ( )
2022-05-03 17:55:15 +00:00
mayProxyVMAlertRequests ( w , r , ` { "status":"success","data": { "groups":[]}} ` )
2019-12-03 17:32:57 +00:00
return true
2021-08-02 14:28:09 +00:00
case "/api/v1/alerts" , "/alerts" :
// Return dumb placeholder for https://prometheus.io/docs/prometheus/latest/querying/api/#alerts
2019-12-03 17:32:57 +00:00
alertsRequests . Inc ( )
2022-05-03 17:55:15 +00:00
mayProxyVMAlertRequests ( w , r , ` { "status":"success","data": { "alerts":[]}} ` )
2019-12-03 17:32:57 +00:00
return true
2020-02-04 13:53:15 +00:00
case "/api/v1/metadata" :
2021-04-05 20:25:05 +00:00
// Return dumb placeholder for https://prometheus.io/docs/prometheus/latest/querying/api/#querying-metric-metadata
2020-02-04 13:53:15 +00:00
metadataRequests . Inc ( )
2021-11-09 16:03:50 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2020-02-04 13:53:15 +00:00
fmt . Fprintf ( w , "%s" , ` { "status":"success","data": { }} ` )
return true
2022-04-29 10:02:08 +00:00
case "/api/v1/status/buildinfo" :
buildInfoRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
fmt . Fprintf ( w , "%s" , ` { "status":"success","data": { }} ` )
return true
2021-04-05 20:25:05 +00:00
case "/api/v1/query_exemplars" :
// Return dumb placeholder for https://prometheus.io/docs/prometheus/latest/querying/api/#querying-exemplars
queryExemplarsRequests . Inc ( )
2021-11-09 16:03:50 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-12-23 09:53:50 +00:00
fmt . Fprintf ( w , "%s" , ` { "status":"success","data":[]} ` )
2021-04-05 20:25:05 +00:00
return true
2019-05-22 21:16:55 +00:00
case "/api/v1/admin/tsdb/delete_series" :
deleteRequests . Inc ( )
authKey := r . FormValue ( "authKey" )
if authKey != * deleteAuthKey {
2020-07-20 11:00:33 +00:00
httpserver . Errorf ( w , r , "invalid authKey %q. It must match the value from -deleteAuthKey command line flag" , authKey )
2019-05-22 21:16:55 +00:00
return true
}
2020-02-04 14:13:59 +00:00
if err := prometheus . DeleteHandler ( startTime , r ) ; err != nil {
2019-05-22 21:16:55 +00:00
deleteErrors . Inc ( )
2021-07-07 09:59:03 +00:00
httpserver . Errorf ( w , r , "%s" , err )
2019-05-22 21:16:55 +00:00
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
default :
return false
}
}
2020-11-16 12:49:46 +00:00
func isGraphiteTagsPath ( path string ) bool {
switch path {
// See https://graphite.readthedocs.io/en/stable/tags.html for a list of Graphite Tags API paths.
// Do not include `/tags/<tag_name>` here, since this will fool the caller.
case "/tags/tagSeries" , "/tags/tagMultiSeries" , "/tags/findSeries" ,
"/tags/autoComplete/tags" , "/tags/autoComplete/values" , "/tags/delSeries" :
return true
default :
return false
}
}
2019-05-22 21:16:55 +00:00
func sendPrometheusError ( w http . ResponseWriter , r * http . Request , err error ) {
2021-07-07 09:59:03 +00:00
logger . Warnf ( "error in %q: %s" , httpserver . GetRequestURI ( r ) , err )
2019-05-22 21:16:55 +00:00
2021-11-09 16:03:50 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2019-08-23 06:46:45 +00:00
statusCode := http . StatusUnprocessableEntity
2020-06-30 21:02:02 +00:00
var esc * httpserver . ErrorWithStatusCode
if errors . As ( err , & esc ) {
2019-08-23 06:46:45 +00:00
statusCode = esc . StatusCode
}
2019-05-22 21:16:55 +00:00
w . WriteHeader ( statusCode )
prometheus . WriteErrorResponse ( w , statusCode , err )
}
var (
2021-07-07 10:25:16 +00:00
requestDuration = metrics . NewHistogram ( ` vmselect_request_duration_seconds ` )
2019-05-22 21:16:55 +00:00
labelValuesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/label/ { }/values"} ` )
labelValuesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/label/ { }/values"} ` )
queryRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/query"} ` )
queryErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/query"} ` )
queryRangeRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/query_range"} ` )
queryRangeErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/query_range"} ` )
seriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/series"} ` )
seriesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/series"} ` )
seriesCountRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/series/count"} ` )
seriesCountErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/series/count"} ` )
labelsRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/labels"} ` )
labelsErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/labels"} ` )
2019-06-10 15:55:20 +00:00
labelsCountRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/labels/count"} ` )
labelsCountErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/labels/count"} ` )
2020-07-08 15:55:25 +00:00
statusTSDBRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/status/tsdb"} ` )
statusTSDBErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/status/tsdb"} ` )
statusActiveQueriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/status/active_queries"} ` )
2020-04-22 16:57:36 +00:00
2020-12-27 10:53:50 +00:00
topQueriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/status/top_queries"} ` )
topQueriesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/status/top_queries"} ` )
2019-05-22 21:16:55 +00:00
deleteRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/admin/tsdb/delete_series"} ` )
deleteErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/admin/tsdb/delete_series"} ` )
exportRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/export"} ` )
exportErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/export"} ` )
2020-10-12 17:01:51 +00:00
exportCSVRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/export/csv"} ` )
exportCSVErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/export/csv"} ` )
2020-09-26 01:29:45 +00:00
exportNativeRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/export/native"} ` )
exportNativeErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/api/v1/export/native"} ` )
2019-05-22 21:16:55 +00:00
federateRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/federate"} ` )
federateErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/federate"} ` )
2019-12-03 17:32:57 +00:00
2020-09-10 21:28:19 +00:00
graphiteMetricsFindRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/metrics/find"} ` )
graphiteMetricsFindErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/metrics/find"} ` )
graphiteMetricsExpandRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/metrics/expand"} ` )
graphiteMetricsExpandErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/metrics/expand"} ` )
graphiteMetricsIndexRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/metrics/index.json"} ` )
graphiteMetricsIndexErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/metrics/index.json"} ` )
2020-11-23 10:33:17 +00:00
graphiteTagsTagSeriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/tagSeries"} ` )
graphiteTagsTagSeriesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/tagSeries"} ` )
graphiteTagsTagMultiSeriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/tagMultiSeries"} ` )
graphiteTagsTagMultiSeriesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/tagMultiSeries"} ` )
2020-11-15 23:25:38 +00:00
graphiteTagsRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags"} ` )
graphiteTagsErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags"} ` )
2020-11-16 01:31:09 +00:00
graphiteTagValuesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/<tag_name>"} ` )
graphiteTagValuesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/<tag_name>"} ` )
2020-11-16 08:55:55 +00:00
graphiteTagsFindSeriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/findSeries"} ` )
graphiteTagsFindSeriesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/findSeries"} ` )
2020-11-16 12:49:46 +00:00
graphiteTagsAutoCompleteTagsRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/autoComplete/tags"} ` )
graphiteTagsAutoCompleteTagsErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/autoComplete/tags"} ` )
2020-11-16 13:22:36 +00:00
graphiteTagsAutoCompleteValuesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/autoComplete/values"} ` )
graphiteTagsAutoCompleteValuesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/autoComplete/values"} ` )
2020-11-23 13:26:20 +00:00
graphiteTagsDelSeriesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/tags/delSeries"} ` )
graphiteTagsDelSeriesErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/tags/delSeries"} ` )
2021-09-16 08:18:26 +00:00
graphiteFunctionsRequests = metrics . NewCounter ( ` vm_http_request_total { path="/functions"} ` )
2021-04-05 20:25:05 +00:00
rulesRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/rules"} ` )
alertsRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/alerts"} ` )
metadataRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/metadata"} ` )
2022-04-29 08:36:28 +00:00
buildInfoRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/buildinfo"} ` )
2021-04-05 20:25:05 +00:00
queryExemplarsRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/api/v1/query_exemplars"} ` )
2019-05-22 21:16:55 +00:00
)
2022-05-03 17:55:15 +00:00
func mayProxyVMAlertRequests ( w http . ResponseWriter , r * http . Request , stubResponse string ) {
if len ( * vmalertProxyURL ) == 0 {
// Return dumb placeholder for https://prometheus.io/docs/prometheus/latest/querying/api/#rules
w . Header ( ) . Set ( "Content-Type" , "application/json" )
fmt . Fprintf ( w , "%s" , stubResponse )
return
}
defer func ( ) {
err := recover ( )
if err == nil || err == http . ErrAbortHandler {
// Suppress http.ErrAbortHandler panic.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1353
return
}
// Forward other panics to the caller.
panic ( err )
} ( )
2022-05-04 20:35:57 +00:00
r . Host = vmalertProxyHost
vmalertProxy . ServeHTTP ( w , r )
2022-05-03 17:55:15 +00:00
}
2022-05-04 20:35:57 +00:00
var (
vmalertProxyHost string
vmalertProxy * httputil . ReverseProxy
)
2022-05-03 17:55:15 +00:00
// initVMAlertProxy must be called after flag.Parse(), since it uses command-line flags.
func initVMAlertProxy ( ) {
if len ( * vmalertProxyURL ) == 0 {
return
}
proxyURL , err := url . Parse ( * vmalertProxyURL )
if err != nil {
2022-05-04 20:35:57 +00:00
logger . Fatalf ( "cannot parse -vmalert.proxyURL=%q: %s" , * vmalertProxyURL , err )
2022-05-03 17:55:15 +00:00
}
2022-05-04 20:35:57 +00:00
vmalertProxyHost = proxyURL . Host
vmalertProxy = httputil . NewSingleHostReverseProxy ( proxyURL )
2022-05-03 17:55:15 +00:00
}