2022-04-08 05:55:03 +08:00
|
|
|
package prometheus_client
|
2015-10-23 00:17:57 +08:00
|
|
|
|
|
|
|
|
import (
|
2017-01-22 07:37:53 +08:00
|
|
|
"context"
|
2019-03-02 03:19:31 +08:00
|
|
|
"crypto/tls"
|
2015-10-23 00:17:57 +08:00
|
|
|
"fmt"
|
2018-08-02 06:43:34 +08:00
|
|
|
"net"
|
2015-10-29 06:19:13 +08:00
|
|
|
"net/http"
|
2019-03-09 06:54:16 +08:00
|
|
|
"net/url"
|
2016-07-10 21:47:47 +08:00
|
|
|
"sync"
|
2016-11-15 19:33:39 +08:00
|
|
|
"time"
|
2015-10-29 06:19:13 +08:00
|
|
|
|
2021-11-25 03:33:45 +08:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
|
|
|
|
2016-01-28 05:21:36 +08:00
|
|
|
"github.com/influxdata/telegraf"
|
2021-04-10 01:15:04 +08:00
|
|
|
"github.com/influxdata/telegraf/config"
|
2016-11-15 19:33:39 +08:00
|
|
|
"github.com/influxdata/telegraf/internal"
|
2020-06-26 02:44:22 +08:00
|
|
|
tlsint "github.com/influxdata/telegraf/plugins/common/tls"
|
2016-01-28 07:15:14 +08:00
|
|
|
"github.com/influxdata/telegraf/plugins/outputs"
|
2022-04-08 05:55:03 +08:00
|
|
|
v1 "github.com/influxdata/telegraf/plugins/outputs/prometheus_client/v1"
|
|
|
|
|
v2 "github.com/influxdata/telegraf/plugins/outputs/prometheus_client/v2"
|
2015-10-23 00:17:57 +08:00
|
|
|
)
|
|
|
|
|
|
2019-04-03 03:42:48 +08:00
|
|
|
var (
|
2019-11-27 07:46:31 +08:00
|
|
|
defaultListen = ":9273"
|
|
|
|
|
defaultPath = "/metrics"
|
2021-04-10 01:15:04 +08:00
|
|
|
defaultExpirationInterval = config.Duration(60 * time.Second)
|
2019-04-03 03:42:48 +08:00
|
|
|
)
|
2016-03-23 00:34:33 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
type Collector interface {
|
|
|
|
|
Describe(ch chan<- *prometheus.Desc)
|
|
|
|
|
Collect(ch chan<- prometheus.Metric)
|
|
|
|
|
Add(metrics []telegraf.Metric) error
|
|
|
|
|
}
|
2018-02-02 03:12:16 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
type PrometheusClient struct {
|
2021-04-10 01:15:04 +08:00
|
|
|
Listen string `toml:"listen"`
|
|
|
|
|
MetricVersion int `toml:"metric_version"`
|
|
|
|
|
BasicUsername string `toml:"basic_username"`
|
|
|
|
|
BasicPassword string `toml:"basic_password"`
|
|
|
|
|
IPRange []string `toml:"ip_range"`
|
|
|
|
|
ExpirationInterval config.Duration `toml:"expiration_interval"`
|
|
|
|
|
Path string `toml:"path"`
|
|
|
|
|
CollectorsExclude []string `toml:"collectors_exclude"`
|
|
|
|
|
StringAsLabel bool `toml:"string_as_label"`
|
|
|
|
|
ExportTimestamp bool `toml:"export_timestamp"`
|
2019-11-27 07:46:31 +08:00
|
|
|
tlsint.ServerConfig
|
2018-08-02 06:43:34 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
Log telegraf.Logger `toml:"-"`
|
|
|
|
|
|
|
|
|
|
server *http.Server
|
|
|
|
|
url *url.URL
|
|
|
|
|
collector Collector
|
|
|
|
|
wg sync.WaitGroup
|
2018-02-02 03:12:16 +08:00
|
|
|
}
|
|
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
func (p *PrometheusClient) Init() error {
|
2017-12-12 10:00:19 +08:00
|
|
|
defaultCollectors := map[string]bool{
|
|
|
|
|
"gocollector": true,
|
|
|
|
|
"process": true,
|
|
|
|
|
}
|
2017-10-19 05:51:08 +08:00
|
|
|
for _, collector := range p.CollectorsExclude {
|
2017-12-12 10:00:19 +08:00
|
|
|
delete(defaultCollectors, collector)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registry := prometheus.NewRegistry()
|
2018-10-20 04:32:54 +08:00
|
|
|
for collector := range defaultCollectors {
|
2017-10-19 05:51:08 +08:00
|
|
|
switch collector {
|
|
|
|
|
case "gocollector":
|
2021-11-25 03:33:45 +08:00
|
|
|
err := registry.Register(collectors.NewGoCollector())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-10-19 05:51:08 +08:00
|
|
|
case "process":
|
2021-11-25 03:33:45 +08:00
|
|
|
err := registry.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-10-19 05:51:08 +08:00
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("unrecognized collector %s", collector)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
switch p.MetricVersion {
|
|
|
|
|
default:
|
|
|
|
|
fallthrough
|
|
|
|
|
case 1:
|
2021-04-10 01:15:04 +08:00
|
|
|
p.collector = v1.NewCollector(time.Duration(p.ExpirationInterval), p.StringAsLabel, p.Log)
|
2019-11-27 07:46:31 +08:00
|
|
|
err := registry.Register(p.collector)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
case 2:
|
2021-04-10 01:15:04 +08:00
|
|
|
p.collector = v2.NewCollector(time.Duration(p.ExpirationInterval), p.StringAsLabel, p.ExportTimestamp)
|
2019-11-27 07:46:31 +08:00
|
|
|
err := registry.Register(p.collector)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2019-03-02 05:26:11 +08:00
|
|
|
}
|
2017-12-12 10:00:19 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
ipRange := make([]*net.IPNet, 0, len(p.IPRange))
|
|
|
|
|
for _, cidr := range p.IPRange {
|
|
|
|
|
_, ipNet, err := net.ParseCIDR(cidr)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing ip_range: %v", err)
|
|
|
|
|
}
|
2015-10-29 06:19:13 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
ipRange = append(ipRange, ipNet)
|
2017-09-20 02:27:11 +08:00
|
|
|
}
|
|
|
|
|
|
2020-03-05 02:13:44 +08:00
|
|
|
authHandler := internal.AuthHandler(p.BasicUsername, p.BasicPassword, "prometheus", onAuthError)
|
2019-11-27 07:46:31 +08:00
|
|
|
rangeHandler := internal.IPRangeHandler(ipRange, onError)
|
|
|
|
|
promHandler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError})
|
2021-07-28 05:17:42 +08:00
|
|
|
landingPageHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2021-11-25 03:33:45 +08:00
|
|
|
_, err := w.Write([]byte("Telegraf Output Plugin: Prometheus Client "))
|
|
|
|
|
if err != nil {
|
|
|
|
|
p.Log.Errorf("Error occurred when writing HTTP reply: %v", err)
|
|
|
|
|
}
|
2021-07-28 05:17:42 +08:00
|
|
|
})
|
2019-11-27 07:46:31 +08:00
|
|
|
|
2017-01-22 07:37:53 +08:00
|
|
|
mux := http.NewServeMux()
|
2019-11-27 07:46:31 +08:00
|
|
|
if p.Path == "" {
|
2021-07-28 05:17:42 +08:00
|
|
|
p.Path = "/metrics"
|
2019-11-27 07:46:31 +08:00
|
|
|
}
|
|
|
|
|
mux.Handle(p.Path, authHandler(rangeHandler(promHandler)))
|
2021-07-28 05:17:42 +08:00
|
|
|
mux.Handle("/", authHandler(rangeHandler(landingPageHandler)))
|
2017-01-22 07:37:53 +08:00
|
|
|
|
2019-02-23 06:18:36 +08:00
|
|
|
tlsConfig, err := p.TLSConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2019-11-27 07:46:31 +08:00
|
|
|
|
2019-02-23 06:18:36 +08:00
|
|
|
p.server = &http.Server{
|
|
|
|
|
Addr: p.Listen,
|
|
|
|
|
Handler: mux,
|
|
|
|
|
TLSConfig: tlsConfig,
|
2015-10-23 00:17:57 +08:00
|
|
|
}
|
2015-10-29 06:19:13 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *PrometheusClient) listen() (net.Listener, error) {
|
|
|
|
|
if p.server.TLSConfig != nil {
|
|
|
|
|
return tls.Listen("tcp", p.Listen, p.server.TLSConfig)
|
2019-03-02 03:19:31 +08:00
|
|
|
}
|
2021-02-09 00:18:40 +08:00
|
|
|
return net.Listen("tcp", p.Listen)
|
2019-11-27 07:46:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *PrometheusClient) Connect() error {
|
|
|
|
|
listener, err := p.listen()
|
2019-03-02 03:19:31 +08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
scheme := "http"
|
|
|
|
|
if p.server.TLSConfig != nil {
|
|
|
|
|
scheme = "https"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.url = &url.URL{
|
|
|
|
|
Scheme: scheme,
|
|
|
|
|
Host: listener.Addr().String(),
|
|
|
|
|
Path: p.Path,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.Log.Infof("Listening on %s", p.URL())
|
2019-03-09 06:54:16 +08:00
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
p.wg.Add(1)
|
2017-07-06 05:28:44 +08:00
|
|
|
go func() {
|
2019-11-27 07:46:31 +08:00
|
|
|
defer p.wg.Done()
|
2019-03-02 03:19:31 +08:00
|
|
|
err := p.server.Serve(listener)
|
2018-02-02 03:12:16 +08:00
|
|
|
if err != nil && err != http.ErrServerClosed {
|
2019-11-27 07:46:31 +08:00
|
|
|
p.Log.Errorf("Server error: %v", err)
|
2017-07-06 05:28:44 +08:00
|
|
|
}
|
|
|
|
|
}()
|
2018-02-02 03:12:16 +08:00
|
|
|
|
2015-10-23 00:17:57 +08:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 02:13:44 +08:00
|
|
|
func onAuthError(_ http.ResponseWriter) {
|
2019-03-09 06:54:16 +08:00
|
|
|
}
|
|
|
|
|
|
2019-11-27 07:46:31 +08:00
|
|
|
func onError(rw http.ResponseWriter, code int) {
|
|
|
|
|
http.Error(rw, http.StatusText(code), code)
|
|
|
|
|
}
|
2019-03-09 06:54:16 +08:00
|
|
|
|
2021-11-25 03:33:45 +08:00
|
|
|
// URL returns the address the plugin is listening on. If not listening
|
2019-11-27 07:46:31 +08:00
|
|
|
// an empty string is returned.
|
|
|
|
|
func (p *PrometheusClient) URL() string {
|
|
|
|
|
if p.url != nil {
|
|
|
|
|
return p.url.String()
|
2019-03-09 06:54:16 +08:00
|
|
|
}
|
2019-11-27 07:46:31 +08:00
|
|
|
return ""
|
2019-03-09 06:54:16 +08:00
|
|
|
}
|
|
|
|
|
|
2015-10-23 00:17:57 +08:00
|
|
|
func (p *PrometheusClient) Close() error {
|
2019-11-27 07:46:31 +08:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
2017-01-22 07:37:53 +08:00
|
|
|
defer cancel()
|
2019-11-27 07:46:31 +08:00
|
|
|
|
2017-07-26 06:41:18 +08:00
|
|
|
err := p.server.Shutdown(ctx)
|
2019-11-27 07:46:31 +08:00
|
|
|
p.wg.Wait()
|
|
|
|
|
p.url = nil
|
|
|
|
|
prometheus.Unregister(p.collector)
|
2017-07-26 06:41:18 +08:00
|
|
|
return err
|
2015-10-23 00:17:57 +08:00
|
|
|
}
|
|
|
|
|
|
2016-01-28 07:15:14 +08:00
|
|
|
func (p *PrometheusClient) Write(metrics []telegraf.Metric) error {
|
2019-11-27 07:46:31 +08:00
|
|
|
return p.collector.Add(metrics)
|
2015-10-23 00:17:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
2016-01-28 05:21:36 +08:00
|
|
|
outputs.Add("prometheus_client", func() telegraf.Output {
|
2016-11-15 19:33:39 +08:00
|
|
|
return &PrometheusClient{
|
2019-11-27 07:46:31 +08:00
|
|
|
Listen: defaultListen,
|
|
|
|
|
Path: defaultPath,
|
|
|
|
|
ExpirationInterval: defaultExpirationInterval,
|
2018-02-17 06:07:26 +08:00
|
|
|
StringAsLabel: true,
|
2016-11-15 19:33:39 +08:00
|
|
|
}
|
2015-10-23 00:17:57 +08:00
|
|
|
})
|
|
|
|
|
}
|