2020-02-23 12:35:47 +01:00
package main
import (
2022-05-31 01:55:28 +02:00
"embed"
2020-02-23 12:35:47 +01:00
"flag"
"fmt"
2021-09-20 13:49:28 +02:00
"io"
2020-02-23 12:35:47 +01:00
"net/http"
2020-05-16 10:59:30 +02:00
"os"
2020-02-23 12:35:47 +01:00
"strings"
"time"
2020-03-10 18:35:58 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/csvimport"
2024-02-06 21:58:11 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/datadogsketches"
2023-12-21 17:29:10 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/datadogv1"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/datadogv2"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/graphite"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/influx"
2020-09-26 03:29:45 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/native"
2023-10-05 14:39:51 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/newrelic"
2023-07-27 22:26:45 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/opentelemetry"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/opentsdb"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/opentsdbhttp"
2020-07-10 11:00:35 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/prometheusimport"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/promremotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/remotewrite"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmagent/vmimport"
2021-08-05 08:44:29 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/auth"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/buildinfo"
2022-05-25 08:57:11 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/envflag"
2020-12-03 20:40:30 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
2020-02-23 12:35:47 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
2021-03-15 20:37:13 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/influxutils"
2020-02-23 12:35:47 +01:00
graphiteserver "github.com/VictoriaMetrics/VictoriaMetrics/lib/ingestserver/graphite"
2020-02-25 18:09:46 +01:00
influxserver "github.com/VictoriaMetrics/VictoriaMetrics/lib/ingestserver/influx"
2020-02-23 12:35:47 +01:00
opentsdbserver "github.com/VictoriaMetrics/VictoriaMetrics/lib/ingestserver/opentsdb"
opentsdbhttpserver "github.com/VictoriaMetrics/VictoriaMetrics/lib/ingestserver/opentsdbhttp"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/procutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape"
2020-09-28 03:11:55 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/common"
2024-04-02 16:51:18 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/opentelemetry/firehose"
2022-07-21 18:58:22 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/pushmetrics"
2023-11-20 10:21:59 +01:00
"github.com/VictoriaMetrics/metrics"
2020-02-23 12:35:47 +01:00
)
var (
2024-02-09 02:15:04 +01:00
httpListenAddrs = flagutil . NewArrayString ( "httpListenAddr" , "TCP address to listen for incoming http requests. " +
2020-02-26 19:57:51 +01:00
"Set this flag to empty value in order to disable listening on any port. This mode may be useful for running multiple vmagent instances on the same server. " +
2023-12-09 23:25:01 +01:00
"Note that /targets and /metrics pages aren't available if -httpListenAddr=''. See also -tls and -httpListenAddr.useProxyProtocol" )
2024-02-09 02:15:04 +01:00
useProxyProtocol = flagutil . NewArrayBool ( "httpListenAddr.useProxyProtocol" , "Whether to use proxy protocol for connections accepted at the corresponding -httpListenAddr . " +
2023-03-08 10:26:53 +01:00
"See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . " +
"With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing" )
2022-05-13 15:49:22 +02:00
influxListenAddr = flag . String ( "influxListenAddr" , "" , "TCP and UDP address to listen for InfluxDB line protocol data. Usually :8089 must be set. Doesn't work if empty. " +
2023-01-27 08:08:35 +01:00
"This flag isn't needed when ingesting data over HTTP - just send it to http://<vmagent>:8429/write . " +
"See also -influxListenAddr.useProxyProtocol" )
influxUseProxyProtocol = flag . Bool ( "influxListenAddr.useProxyProtocol" , false , "Whether to use proxy protocol for connections accepted at -influxListenAddr . " +
"See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt" )
graphiteListenAddr = flag . String ( "graphiteListenAddr" , "" , "TCP and UDP address to listen for Graphite plaintext data. Usually :2003 must be set. Doesn't work if empty. " +
"See also -graphiteListenAddr.useProxyProtocol" )
graphiteUseProxyProtocol = flag . Bool ( "graphiteListenAddr.useProxyProtocol" , false , "Whether to use proxy protocol for connections accepted at -graphiteListenAddr . " +
"See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt" )
2023-02-22 11:24:26 +01:00
opentsdbListenAddr = flag . String ( "opentsdbListenAddr" , "" , "TCP and UDP address to listen for OpenTSDB metrics. " +
2020-02-23 12:35:47 +01:00
"Telnet put messages and HTTP /api/put messages are simultaneously served on TCP port. " +
2023-01-27 08:08:35 +01:00
"Usually :4242 must be set. Doesn't work if empty. See also -opentsdbListenAddr.useProxyProtocol" )
opentsdbUseProxyProtocol = flag . Bool ( "opentsdbListenAddr.useProxyProtocol" , false , "Whether to use proxy protocol for connections accepted at -opentsdbListenAddr . " +
"See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt" )
2023-02-22 11:24:26 +01:00
opentsdbHTTPListenAddr = flag . String ( "opentsdbHTTPListenAddr" , "" , "TCP address to listen for OpenTSDB HTTP put requests. Usually :4242 must be set. Doesn't work if empty. " +
2023-01-27 08:08:35 +01:00
"See also -opentsdbHTTPListenAddr.useProxyProtocol" )
opentsdbHTTPUseProxyProtocol = flag . Bool ( "opentsdbHTTPListenAddr.useProxyProtocol" , false , "Whether to use proxy protocol for connections accepted " +
"at -opentsdbHTTPListenAddr . See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt" )
2024-01-21 20:58:26 +01:00
configAuthKey = flagutil . NewPassword ( "configAuthKey" , "Authorization key for accessing /config page. It must be passed via authKey query arg" )
reloadAuthKey = flagutil . NewPassword ( "reloadAuthKey" , "Auth key for /-/reload http endpoint. It must be passed as authKey=..." )
2023-04-01 06:27:45 +02:00
dryRun = flag . Bool ( "dryRun" , false , "Whether to check config files without running vmagent. The following files are checked: " +
2023-03-29 18:05:58 +02:00
"-promscrape.config, -remoteWrite.relabelConfig, -remoteWrite.urlRelabelConfig, -remoteWrite.streamAggr.config . " +
2022-02-08 14:37:38 +01:00
"Unknown config entries aren't allowed in -promscrape.config by default. This can be changed by passing -promscrape.config.strictParse=false command-line flag" )
2020-02-23 12:35:47 +01:00
)
var (
2020-02-25 18:09:46 +01:00
influxServer * influxserver . Server
2020-02-23 12:35:47 +01:00
graphiteServer * graphiteserver . Server
opentsdbServer * opentsdbserver . Server
opentsdbhttpServer * opentsdbhttpserver . Server
2022-05-31 11:27:54 +02:00
)
var (
2022-05-31 01:55:28 +02:00
//go:embed static
staticFiles embed . FS
staticServer = http . FileServer ( http . FS ( staticFiles ) )
2020-02-23 12:35:47 +01:00
)
func main ( ) {
2020-05-16 10:59:30 +02:00
// Write flags and help message to stdout, since it is easier to grep or pipe.
flag . CommandLine . SetOutput ( os . Stdout )
2020-06-05 09:37:36 +02:00
flag . Usage = usage
2020-02-23 12:35:47 +01:00
envflag . Parse ( )
2020-09-29 18:48:53 +02:00
remotewrite . InitSecretFlags ( )
2020-02-23 12:35:47 +01:00
buildinfo . Init ( )
logger . Init ( )
2020-05-21 14:22:01 +02:00
2020-11-25 21:59:13 +01:00
if promscrape . IsDryRun ( ) {
if err := promscrape . CheckConfig ( ) ; err != nil {
logger . Fatalf ( "error when checking -promscrape.config: %s" , err )
}
2023-03-08 09:30:21 +01:00
logger . Infof ( "-promscrape.config is ok; exiting with 0 status code" )
2020-11-25 21:59:13 +01:00
return
}
2020-05-21 14:22:01 +02:00
if * dryRun {
if err := promscrape . CheckConfig ( ) ; err != nil {
2020-11-25 21:59:13 +01:00
logger . Fatalf ( "error when checking -promscrape.config: %s" , err )
2020-05-21 14:22:01 +02:00
}
2023-04-01 06:27:45 +02:00
if err := remotewrite . CheckRelabelConfigs ( ) ; err != nil {
logger . Fatalf ( "error when checking relabel configs: %s" , err )
}
if err := remotewrite . CheckStreamAggrConfigs ( ) ; err != nil {
2023-03-29 18:05:58 +02:00
logger . Fatalf ( "error when checking -remoteWrite.streamAggr.config: %s" , err )
}
2023-03-08 09:30:21 +01:00
logger . Infof ( "all the configs are ok; exiting with 0 status code" )
2020-05-21 14:22:01 +02:00
return
}
2024-02-09 02:15:04 +01:00
listenAddrs := * httpListenAddrs
if len ( listenAddrs ) == 0 {
listenAddrs = [ ] string { ":8429" }
}
logger . Infof ( "starting vmagent at %q..." , listenAddrs )
2020-02-23 12:35:47 +01:00
startTime := time . Now ( )
2024-03-30 05:38:29 +01:00
remotewrite . StartIngestionRateLimiter ( )
2021-08-05 08:46:19 +02:00
remotewrite . Init ( )
2020-09-28 03:11:55 +02:00
common . StartUnmarshalWorkers ( )
2020-02-25 18:09:46 +01:00
if len ( * influxListenAddr ) > 0 {
2023-01-27 08:08:35 +01:00
influxServer = influxserver . MustStart ( * influxListenAddr , * influxUseProxyProtocol , func ( r io . Reader ) error {
2023-12-04 21:10:00 +01:00
return influx . InsertHandlerForReader ( nil , r , false )
2021-09-20 13:49:28 +02:00
} )
2020-02-25 18:09:46 +01:00
}
2020-02-23 12:35:47 +01:00
if len ( * graphiteListenAddr ) > 0 {
2023-01-27 08:08:35 +01:00
graphiteServer = graphiteserver . MustStart ( * graphiteListenAddr , * graphiteUseProxyProtocol , graphite . InsertHandler )
2020-02-23 12:35:47 +01:00
}
if len ( * opentsdbListenAddr ) > 0 {
2022-08-24 15:18:50 +02:00
httpInsertHandler := getOpenTSDBHTTPInsertHandler ( )
2023-01-27 08:08:35 +01:00
opentsdbServer = opentsdbserver . MustStart ( * opentsdbListenAddr , * opentsdbUseProxyProtocol , opentsdb . InsertHandler , httpInsertHandler )
2020-02-23 12:35:47 +01:00
}
if len ( * opentsdbHTTPListenAddr ) > 0 {
2022-08-24 15:18:50 +02:00
httpInsertHandler := getOpenTSDBHTTPInsertHandler ( )
2023-01-27 08:08:35 +01:00
opentsdbhttpServer = opentsdbhttpserver . MustStart ( * opentsdbHTTPListenAddr , * opentsdbHTTPUseProxyProtocol , httpInsertHandler )
2020-02-23 12:35:47 +01:00
}
2023-11-25 13:42:37 +01:00
promscrape . Init ( remotewrite . PushDropSamplesOnFailure )
2020-02-23 12:35:47 +01:00
2024-02-09 02:15:04 +01:00
go httpserver . Serve ( listenAddrs , useProxyProtocol , requestHandler )
2020-02-23 12:35:47 +01:00
logger . Infof ( "started vmagent in %.3f seconds" , time . Since ( startTime ) . Seconds ( ) )
2024-01-15 12:37:02 +01:00
pushmetrics . Init ( )
2020-02-23 12:35:47 +01:00
sig := procutil . WaitForSigterm ( )
logger . Infof ( "received signal %s" , sig )
2024-03-30 05:38:29 +01:00
remotewrite . StopIngestionRateLimiter ( )
2024-01-15 12:37:02 +01:00
pushmetrics . Stop ( )
2020-02-23 12:35:47 +01:00
startTime = time . Now ( )
2024-02-09 02:15:04 +01:00
logger . Infof ( "gracefully shutting down webservice at %q" , listenAddrs )
if err := httpserver . Stop ( listenAddrs ) ; err != nil {
logger . Fatalf ( "cannot stop the webservice: %s" , err )
2020-02-23 12:35:47 +01:00
}
2024-02-09 02:15:04 +01:00
logger . Infof ( "successfully shut down the webservice in %.3f seconds" , time . Since ( startTime ) . Seconds ( ) )
2020-02-23 12:35:47 +01:00
promscrape . Stop ( )
2020-02-25 18:09:46 +01:00
if len ( * influxListenAddr ) > 0 {
influxServer . MustStop ( )
}
2020-02-23 12:35:47 +01:00
if len ( * graphiteListenAddr ) > 0 {
graphiteServer . MustStop ( )
}
if len ( * opentsdbListenAddr ) > 0 {
opentsdbServer . MustStop ( )
}
if len ( * opentsdbHTTPListenAddr ) > 0 {
opentsdbhttpServer . MustStop ( )
}
2020-09-28 03:11:55 +02:00
common . StopUnmarshalWorkers ( )
2020-02-23 12:35:47 +01:00
remotewrite . Stop ( )
logger . Infof ( "successfully stopped vmagent in %.3f seconds" , time . Since ( startTime ) . Seconds ( ) )
}
2022-08-24 15:18:50 +02:00
func getOpenTSDBHTTPInsertHandler ( ) func ( req * http . Request ) error {
if ! remotewrite . MultitenancyEnabled ( ) {
return func ( req * http . Request ) error {
path := strings . Replace ( req . URL . Path , "//" , "/" , - 1 )
if path != "/api/put" {
return fmt . Errorf ( "unsupported path requested: %q; expecting '/api/put'" , path )
}
return opentsdbhttp . InsertHandler ( nil , req )
}
}
return func ( req * http . Request ) error {
path := strings . Replace ( req . URL . Path , "//" , "/" , - 1 )
at , err := getAuthTokenFromPath ( path )
if err != nil {
return fmt . Errorf ( "cannot obtain auth token from path %q: %w" , path , err )
}
return opentsdbhttp . InsertHandler ( at , req )
}
}
func getAuthTokenFromPath ( path string ) ( * auth . Token , error ) {
p , err := httpserver . ParsePath ( path )
if err != nil {
return nil , fmt . Errorf ( "cannot parse multitenant path: %w" , err )
}
if p . Prefix != "insert" {
return nil , fmt . Errorf ( ` unsupported multitenant prefix: %q; expected "insert" ` , p . Prefix )
}
if p . Suffix != "opentsdb/api/put" {
return nil , fmt . Errorf ( "unsupported path requested: %q; expecting 'opentsdb/api/put'" , p . Suffix )
}
2023-10-01 20:52:25 +02:00
return auth . NewTokenPossibleMultitenant ( p . AuthToken )
2022-08-24 15:18:50 +02:00
}
2020-02-23 12:35:47 +01:00
func requestHandler ( w http . ResponseWriter , r * http . Request ) bool {
2020-12-14 13:02:57 +01:00
if r . URL . Path == "/" {
2023-02-23 03:58:44 +01:00
if r . Method != http . MethodGet {
2021-04-02 21:54:06 +02:00
return false
}
2022-03-21 14:34:49 +01:00
w . Header ( ) . Add ( "Content-Type" , "text/html; charset=utf-8" )
2021-04-30 08:19:08 +02:00
fmt . Fprintf ( w , "<h2>vmagent</h2>" )
fmt . Fprintf ( w , "See docs at <a href='https://docs.victoriametrics.com/vmagent.html'>https://docs.victoriametrics.com/vmagent.html</a></br>" )
fmt . Fprintf ( w , "Useful endpoints:</br>" )
httpserver . WriteAPIHelp ( w , [ ] [ 2 ] string {
2022-06-06 23:57:05 +02:00
{ "targets" , "status for discovered active targets" } ,
{ "service-discovery" , "labels before and after relabeling for discovered targets" } ,
2022-12-10 11:09:21 +01:00
{ "metric-relabel-debug" , "debug metric relabeling" } ,
2021-12-02 12:51:49 +01:00
{ "api/v1/targets" , "advanced information about discovered targets in JSON format" } ,
{ "config" , "-promscrape.config contents" } ,
{ "metrics" , "available service metrics" } ,
{ "flags" , "command-line flags" } ,
{ "-/reload" , "reload configuration" } ,
2021-04-30 08:19:08 +02:00
} )
2020-10-06 14:09:07 +02:00
return true
}
2021-08-05 08:44:29 +02:00
2020-02-23 12:35:47 +01:00
path := strings . Replace ( r . URL . Path , "//" , "/" , - 1 )
2022-11-26 01:40:23 +01:00
if strings . HasPrefix ( path , "/prometheus/api/v1/import/prometheus" ) || strings . HasPrefix ( path , "/api/v1/import/prometheus" ) {
prometheusimportRequests . Inc ( )
if err := prometheusimport . InsertHandler ( nil , r ) ; err != nil {
prometheusimportErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
2023-01-27 23:37:04 +01:00
statusCode := http . StatusNoContent
if strings . HasPrefix ( path , "/prometheus/api/v1/import/prometheus/metrics/job/" ) ||
strings . HasPrefix ( path , "/api/v1/import/prometheus/metrics/job/" ) {
// Return 200 status code for pushgateway requests.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3636
statusCode = http . StatusOK
}
w . WriteHeader ( statusCode )
2022-11-26 01:40:23 +01:00
return true
}
2023-10-02 21:18:03 +02:00
if strings . HasPrefix ( path , "/datadog/" ) {
2022-06-07 14:16:48 +02:00
// Trim suffix from paths starting from /datadog/ in order to support legacy DataDog agent.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2670
path = strings . TrimSuffix ( path , "/" )
}
2020-02-23 12:35:47 +01:00
switch path {
2022-10-01 12:47:52 +02:00
case "/prometheus/api/v1/write" , "/api/v1/write" :
2023-02-24 02:36:52 +01:00
if common . HandleVMProtoServerHandshake ( w , r ) {
return true
}
2020-02-23 12:35:47 +01:00
prometheusWriteRequests . Inc ( )
2021-08-05 08:44:29 +02:00
if err := promremotewrite . InsertHandler ( nil , r ) ; err != nil {
2020-02-23 12:35:47 +01:00
prometheusWriteErrors . Inc ( )
2021-07-07 11:59:03 +02:00
httpserver . Errorf ( w , r , "%s" , err )
2020-02-23 12:35:47 +01:00
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/api/v1/import" , "/api/v1/import" :
2020-02-23 12:35:47 +01:00
vmimportRequests . Inc ( )
2021-08-05 08:44:29 +02:00
if err := vmimport . InsertHandler ( nil , r ) ; err != nil {
2020-02-23 12:35:47 +01:00
vmimportErrors . Inc ( )
2021-07-07 11:59:03 +02:00
httpserver . Errorf ( w , r , "%s" , err )
2020-02-23 12:35:47 +01:00
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/api/v1/import/csv" , "/api/v1/import/csv" :
2020-03-10 18:35:58 +01:00
csvimportRequests . Inc ( )
2021-08-05 08:44:29 +02:00
if err := csvimport . InsertHandler ( nil , r ) ; err != nil {
2020-03-10 18:35:58 +01:00
csvimportErrors . Inc ( )
2021-07-07 11:59:03 +02:00
httpserver . Errorf ( w , r , "%s" , err )
2020-03-10 18:35:58 +01:00
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/api/v1/import/native" , "/api/v1/import/native" :
2020-09-26 03:29:45 +02:00
nativeimportRequests . Inc ( )
2021-08-05 08:44:29 +02:00
if err := native . InsertHandler ( nil , r ) ; err != nil {
2020-09-26 03:29:45 +02:00
nativeimportErrors . Inc ( )
2021-07-07 11:59:03 +02:00
httpserver . Errorf ( w , r , "%s" , err )
2020-09-26 03:29:45 +02:00
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
2022-10-01 12:47:52 +02:00
case "/influx/write" , "/influx/api/v2/write" , "/write" , "/api/v2/write" :
2020-02-23 12:35:47 +01:00
influxWriteRequests . Inc ( )
2021-08-05 08:44:29 +02:00
if err := influx . InsertHandlerForHTTP ( nil , r ) ; err != nil {
2020-02-23 12:35:47 +01:00
influxWriteErrors . Inc ( )
2021-07-07 11:59:03 +02:00
httpserver . Errorf ( w , r , "%s" , err )
2020-02-23 12:35:47 +01:00
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
2022-10-01 12:47:52 +02:00
case "/influx/query" , "/query" :
2020-02-23 12:35:47 +01:00
influxQueryRequests . Inc ( )
2021-03-15 20:37:13 +01:00
influxutils . WriteDatabaseNames ( w )
2020-02-23 12:35:47 +01:00
return true
2024-02-29 16:58:49 +01:00
case "/opentelemetry/api/v1/push" , "/opentelemetry/v1/metrics" :
2023-07-27 22:26:45 +02:00
opentelemetryPushRequests . Inc ( )
2024-04-02 16:51:18 +02:00
if err := opentelemetry . InsertHandler ( nil , r ) ; err != nil {
2023-07-27 22:26:45 +02:00
opentelemetryPushErrors . Inc ( )
2024-04-02 16:51:18 +02:00
httpserver . Errorf ( w , r , "%s" , err )
return true
2023-07-27 22:26:45 +02:00
}
2024-04-02 16:51:18 +02:00
firehose . WriteSuccessResponse ( w , r )
2023-07-27 22:26:45 +02:00
return true
2023-10-16 00:25:23 +02:00
case "/newrelic" :
2023-10-05 14:39:51 +02:00
newrelicCheckRequest . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2023-10-16 00:25:23 +02:00
case "/newrelic/inventory/deltas" :
2023-10-05 14:39:51 +02:00
newrelicInventoryRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "payload": { "version": 1, "state": { }, "reset": "false"}} ` )
return true
2023-10-16 00:25:23 +02:00
case "/newrelic/infra/v2/metrics/events/bulk" :
2023-10-05 14:39:51 +02:00
newrelicWriteRequests . Inc ( )
if err := newrelic . InsertHandlerForHTTP ( nil , r ) ; err != nil {
newrelicWriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2021-09-28 21:47:45 +02:00
case "/datadog/api/v1/series" :
2023-12-21 17:29:10 +01:00
datadogv1WriteRequests . Inc ( )
if err := datadogv1 . InsertHandlerForHTTP ( nil , r ) ; err != nil {
datadogv1WriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
case "/datadog/api/v2/series" :
datadogv2WriteRequests . Inc ( )
if err := datadogv2 . InsertHandlerForHTTP ( nil , r ) ; err != nil {
datadogv2WriteErrors . Inc ( )
2021-09-28 21:47:45 +02:00
httpserver . Errorf ( w , r , "%s" , err )
return true
}
// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2024-02-06 21:58:11 +01:00
case "/datadog/api/beta/sketches" :
datadogsketchesWriteRequests . Inc ( )
if err := datadogsketches . InsertHandlerForHTTP ( nil , r ) ; err != nil {
datadogsketchesWriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( 202 )
return true
2021-09-28 21:47:45 +02:00
case "/datadog/api/v1/validate" :
datadogValidateRequests . Inc ( )
// See https://docs.datadoghq.com/api/latest/authentication/#validate-api-key
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
fmt . Fprintf ( w , ` { "valid":true} ` )
return true
case "/datadog/api/v1/check_run" :
datadogCheckRunRequests . Inc ( )
// See https://docs.datadoghq.com/api/latest/service-checks/#submit-a-service-check
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2022-06-07 14:16:48 +02:00
case "/datadog/intake" :
2021-09-28 21:47:45 +02:00
datadogIntakeRequests . Inc ( )
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
fmt . Fprintf ( w , ` { } ` )
return true
2022-06-13 08:52:13 +02:00
case "/datadog/api/v1/metadata" :
datadogMetadataRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
fmt . Fprintf ( w , ` { } ` )
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/targets" , "/targets" :
2020-02-25 17:13:11 +01:00
promscrapeTargetsRequests . Inc ( )
2020-12-14 13:02:57 +01:00
promscrape . WriteHumanReadableTargetsStatus ( w , r )
2020-02-25 17:13:11 +01:00
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/service-discovery" , "/service-discovery" :
2022-06-06 23:57:05 +02:00
promscrapeServiceDiscoveryRequests . Inc ( )
promscrape . WriteServiceDiscovery ( w , r )
return true
2022-12-10 11:09:21 +01:00
case "/prometheus/metric-relabel-debug" , "/metric-relabel-debug" :
promscrapeMetricRelabelDebugRequests . Inc ( )
promscrape . WriteMetricRelabelDebug ( w , r )
return true
case "/prometheus/target-relabel-debug" , "/target-relabel-debug" :
promscrapeTargetRelabelDebugRequests . Inc ( )
2022-12-10 21:44:09 +01:00
promscrape . WriteTargetRelabelDebug ( w , r )
2022-12-10 11:09:21 +01:00
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/api/v1/targets" , "/api/v1/targets" :
promscrapeAPIV1TargetsRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
state := r . FormValue ( "state" )
promscrape . WriteAPIV1Targets ( w , state )
return true
case "/prometheus/target_response" , "/target_response" :
2022-02-03 17:57:36 +01:00
promscrapeTargetResponseRequests . Inc ( )
if err := promscrape . WriteTargetResponse ( w , r ) ; err != nil {
promscrapeTargetResponseErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/config" , "/config" :
2024-01-21 20:58:26 +01:00
if ! httpserver . CheckAuthFlag ( w , r , configAuthKey . Get ( ) , "configAuthKey" ) {
2021-11-01 15:44:51 +01:00
return true
}
2021-10-12 15:23:42 +02:00
promscrapeConfigRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "text/plain; charset=utf-8" )
promscrape . WriteConfigData ( w )
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/api/v1/status/config" , "/api/v1/status/config" :
2022-05-25 08:57:11 +02:00
// See https://prometheus.io/docs/prometheus/latest/querying/api/#config
2024-01-21 20:58:26 +01:00
if ! httpserver . CheckAuthFlag ( w , r , configAuthKey . Get ( ) , "configAuthKey" ) {
2022-05-25 08:57:11 +02:00
return true
}
promscrapeStatusConfigRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
var bb bytesutil . ByteBuffer
promscrape . WriteConfigData ( & bb )
fmt . Fprintf ( w , ` { "status":"success","data": { "yaml":%q}} ` , bb . B )
return true
2022-10-01 12:47:52 +02:00
case "/prometheus/-/reload" , "/-/reload" :
2024-01-21 20:58:26 +01:00
if ! httpserver . CheckAuthFlag ( w , r , reloadAuthKey . Get ( ) , "reloadAuthKey" ) {
return true
}
2020-04-30 01:00:32 +02:00
promscrapeConfigReloadRequests . Inc ( )
2020-04-30 01:15:39 +02:00
procutil . SelfSIGHUP ( )
2020-05-07 18:29:27 +02:00
w . WriteHeader ( http . StatusOK )
2020-04-30 01:00:32 +02:00
return true
2020-11-04 19:29:18 +01:00
case "/ready" :
2024-02-24 01:07:51 +01:00
if rdy := promscrape . PendingScrapeConfigs . Load ( ) ; rdy > 0 {
2020-11-04 19:29:18 +01:00
errMsg := fmt . Sprintf ( "waiting for scrapes to init, left: %d" , rdy )
http . Error ( w , errMsg , http . StatusTooEarly )
} else {
2020-11-13 09:25:39 +01:00
w . Header ( ) . Set ( "Content-Type" , "text/plain; charset=utf-8" )
2020-11-04 19:29:18 +01:00
w . WriteHeader ( http . StatusOK )
w . Write ( [ ] byte ( "OK" ) )
}
return true
2022-05-31 01:55:28 +02:00
default :
if strings . HasPrefix ( r . URL . Path , "/static" ) {
staticServer . ServeHTTP ( w , r )
return true
}
if remotewrite . MultitenancyEnabled ( ) {
return processMultitenantRequest ( w , r , path )
}
return false
2020-02-23 12:35:47 +01:00
}
}
2021-08-05 08:46:19 +02:00
func processMultitenantRequest ( w http . ResponseWriter , r * http . Request , path string ) bool {
p , err := httpserver . ParsePath ( path )
if err != nil {
// Cannot parse multitenant path. Skip it - probably it will be parsed later.
return false
}
if p . Prefix != "insert" {
httpserver . Errorf ( w , r , ` unsupported multitenant prefix: %q; expected "insert" ` , p . Prefix )
return true
}
at , err := auth . NewToken ( p . AuthToken )
if err != nil {
httpserver . Errorf ( w , r , "cannot obtain auth token: %s" , err )
return true
}
2022-11-26 01:40:23 +01:00
if strings . HasPrefix ( p . Suffix , "prometheus/api/v1/import/prometheus" ) {
prometheusimportRequests . Inc ( )
if err := prometheusimport . InsertHandler ( at , r ) ; err != nil {
prometheusimportErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
}
2022-06-07 14:16:48 +02:00
if strings . HasPrefix ( p . Suffix , "datadog/" ) {
// Trim suffix from paths starting from /datadog/ in order to support legacy DataDog agent.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2670
p . Suffix = strings . TrimSuffix ( p . Suffix , "/" )
}
2021-08-05 08:46:19 +02:00
switch p . Suffix {
case "prometheus/" , "prometheus" , "prometheus/api/v1/write" :
prometheusWriteRequests . Inc ( )
if err := promremotewrite . InsertHandler ( at , r ) ; err != nil {
prometheusWriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
case "prometheus/api/v1/import" :
vmimportRequests . Inc ( )
if err := vmimport . InsertHandler ( at , r ) ; err != nil {
vmimportErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
case "prometheus/api/v1/import/csv" :
csvimportRequests . Inc ( )
if err := csvimport . InsertHandler ( at , r ) ; err != nil {
csvimportErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
case "prometheus/api/v1/import/native" :
nativeimportRequests . Inc ( )
if err := native . InsertHandler ( at , r ) ; err != nil {
nativeimportErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
case "influx/write" , "influx/api/v2/write" :
influxWriteRequests . Inc ( )
if err := influx . InsertHandlerForHTTP ( at , r ) ; err != nil {
influxWriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( http . StatusNoContent )
return true
case "influx/query" :
influxQueryRequests . Inc ( )
influxutils . WriteDatabaseNames ( w )
return true
2024-02-29 17:02:48 +01:00
case "opentelemetry/api/v1/push" , "opentelemetry/v1/metrics" :
2023-07-27 22:26:45 +02:00
opentelemetryPushRequests . Inc ( )
2024-04-02 16:51:18 +02:00
if err := opentelemetry . InsertHandler ( at , r ) ; err != nil {
2023-07-27 22:26:45 +02:00
opentelemetryPushErrors . Inc ( )
2024-04-02 16:51:18 +02:00
httpserver . Errorf ( w , r , "%s" , err )
return true
2023-07-27 22:26:45 +02:00
}
2024-04-02 16:51:18 +02:00
firehose . WriteSuccessResponse ( w , r )
2023-07-27 22:26:45 +02:00
return true
2023-10-16 00:25:23 +02:00
case "newrelic" :
2023-10-05 14:39:51 +02:00
newrelicCheckRequest . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2023-10-16 00:25:23 +02:00
case "newrelic/inventory/deltas" :
2023-10-05 14:39:51 +02:00
newrelicInventoryRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "payload": { "version": 1, "state": { }, "reset": "false"}} ` )
return true
2023-10-16 00:25:23 +02:00
case "newrelic/infra/v2/metrics/events/bulk" :
2023-10-05 14:39:51 +02:00
newrelicWriteRequests . Inc ( )
if err := newrelic . InsertHandlerForHTTP ( at , r ) ; err != nil {
newrelicWriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2021-09-28 21:47:45 +02:00
case "datadog/api/v1/series" :
2023-12-21 17:29:10 +01:00
datadogv1WriteRequests . Inc ( )
if err := datadogv1 . InsertHandlerForHTTP ( at , r ) ; err != nil {
datadogv1WriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
case "datadog/api/v2/series" :
datadogv2WriteRequests . Inc ( )
if err := datadogv2 . InsertHandlerForHTTP ( at , r ) ; err != nil {
datadogv2WriteErrors . Inc ( )
2021-09-28 21:47:45 +02:00
httpserver . Errorf ( w , r , "%s" , err )
return true
}
// See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2024-02-07 00:15:25 +01:00
case "datadog/api/beta/sketches" :
datadogsketchesWriteRequests . Inc ( )
if err := datadogsketches . InsertHandlerForHTTP ( at , r ) ; err != nil {
datadogsketchesWriteErrors . Inc ( )
httpserver . Errorf ( w , r , "%s" , err )
return true
}
w . WriteHeader ( 202 )
return true
2021-09-28 21:47:45 +02:00
case "datadog/api/v1/validate" :
datadogValidateRequests . Inc ( )
// See https://docs.datadoghq.com/api/latest/authentication/#validate-api-key
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
fmt . Fprintf ( w , ` { "valid":true} ` )
return true
case "datadog/api/v1/check_run" :
datadogCheckRunRequests . Inc ( )
// See https://docs.datadoghq.com/api/latest/service-checks/#submit-a-service-check
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
w . WriteHeader ( 202 )
fmt . Fprintf ( w , ` { "status":"ok"} ` )
return true
2022-06-07 14:16:48 +02:00
case "datadog/intake" :
2021-09-28 21:47:45 +02:00
datadogIntakeRequests . Inc ( )
2021-11-09 17:03:50 +01:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2021-09-28 21:47:45 +02:00
fmt . Fprintf ( w , ` { } ` )
return true
2022-06-13 08:54:11 +02:00
case "datadog/api/v1/metadata" :
datadogMetadataRequests . Inc ( )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
fmt . Fprintf ( w , ` { } ` )
return true
2021-08-05 08:46:19 +02:00
default :
httpserver . Errorf ( w , r , "unsupported multitenant path suffix: %q" , p . Suffix )
return true
}
}
2020-02-23 12:35:47 +01:00
var (
2020-03-10 18:35:58 +01:00
prometheusWriteRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/write", protocol="promremotewrite"} ` )
prometheusWriteErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/api/v1/write", protocol="promremotewrite"} ` )
vmimportRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/import", protocol="vmimport"} ` )
vmimportErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/api/v1/import", protocol="vmimport"} ` )
2020-02-23 12:35:47 +01:00
2020-03-10 18:35:58 +01:00
csvimportRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/import/csv", protocol="csvimport"} ` )
csvimportErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/api/v1/import/csv", protocol="csvimport"} ` )
2020-02-23 12:35:47 +01:00
2020-07-10 11:00:35 +02:00
prometheusimportRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/import/prometheus", protocol="prometheusimport"} ` )
prometheusimportErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/api/v1/import/prometheus", protocol="prometheusimport"} ` )
2020-09-26 03:29:45 +02:00
nativeimportRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/import/native", protocol="nativeimport"} ` )
nativeimportErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/api/v1/import/native", protocol="nativeimport"} ` )
2021-09-28 21:47:45 +02:00
influxWriteRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/influx/write", protocol="influx"} ` )
influxWriteErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/influx/write", protocol="influx"} ` )
influxQueryRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/influx/query", protocol="influx"} ` )
2023-12-21 17:29:10 +01:00
datadogv1WriteRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/api/v1/series", protocol="datadog"} ` )
datadogv1WriteErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/datadog/api/v1/series", protocol="datadog"} ` )
datadogv2WriteRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/api/v2/series", protocol="datadog"} ` )
datadogv2WriteErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/datadog/api/v2/series", protocol="datadog"} ` )
2020-02-23 12:35:47 +01:00
2024-02-06 21:58:11 +01:00
datadogsketchesWriteRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/api/beta/sketches", protocol="datadog"} ` )
datadogsketchesWriteErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/datadog/api/beta/sketches", protocol="datadog"} ` )
2021-09-28 21:47:45 +02:00
datadogValidateRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/api/v1/validate", protocol="datadog"} ` )
datadogCheckRunRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/api/v1/check_run", protocol="datadog"} ` )
2022-06-07 14:16:48 +02:00
datadogIntakeRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/intake", protocol="datadog"} ` )
2022-06-13 08:52:13 +02:00
datadogMetadataRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/datadog/api/v1/metadata", protocol="datadog"} ` )
2020-02-25 17:13:11 +01:00
2024-02-29 17:02:48 +01:00
opentelemetryPushRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/opentelemetry/v1/metrics", protocol="opentelemetry"} ` )
opentelemetryPushErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/opentelemetry/v1/metrics", protocol="opentelemetry"} ` )
2023-07-27 22:26:45 +02:00
2023-10-16 00:25:23 +02:00
newrelicWriteRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/newrelic/infra/v2/metrics/events/bulk", protocol="newrelic"} ` )
newrelicWriteErrors = metrics . NewCounter ( ` vm_http_request_errors_total { path="/newrelic/infra/v2/metrics/events/bulk", protocol="newrelic"} ` )
2023-10-05 14:39:51 +02:00
2023-10-16 00:25:23 +02:00
newrelicInventoryRequests = metrics . NewCounter ( ` vm_http_requests_total { path="/newrelic/inventory/deltas", protocol="newrelic"} ` )
newrelicCheckRequest = metrics . NewCounter ( ` vm_http_requests_total { path="/newrelic", protocol="newrelic"} ` )
2023-10-05 14:39:51 +02:00
2022-06-06 23:57:05 +02:00
promscrapeTargetsRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/targets"} ` )
promscrapeServiceDiscoveryRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/service-discovery"} ` )
2022-12-10 11:09:21 +01:00
promscrapeMetricRelabelDebugRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/metric-relabel-debug"} ` )
promscrapeTargetRelabelDebugRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/target-relabel-debug"} ` )
promscrapeAPIV1TargetsRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/targets"} ` )
2020-04-30 01:00:32 +02:00
2022-02-03 17:57:36 +01:00
promscrapeTargetResponseRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/target_response"} ` )
promscrapeTargetResponseErrors = metrics . NewCounter ( ` vmagent_http_request_errors_total { path="/target_response"} ` )
2022-05-25 08:57:11 +02:00
promscrapeConfigRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/config"} ` )
promscrapeStatusConfigRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/api/v1/status/config"} ` )
2021-10-12 15:23:42 +02:00
2020-04-30 01:00:32 +02:00
promscrapeConfigReloadRequests = metrics . NewCounter ( ` vmagent_http_requests_total { path="/-/reload"} ` )
2020-02-23 12:35:47 +01:00
)
2020-06-05 09:37:36 +02:00
func usage ( ) {
const s = `
vmagent collects metrics data via popular data ingestion protocols and routes it to VictoriaMetrics .
2021-04-20 19:16:17 +02:00
See the docs at https : //docs.victoriametrics.com/vmagent.html .
2020-06-05 09:37:36 +02:00
`
2020-12-03 20:40:30 +01:00
flagutil . Usage ( s )
2020-06-05 09:37:36 +02:00
}