Skip to content

Commit 5bd1c34

Browse files
authored
Merge pull request #7 from rtkwlf/update_to_1_8_0
[PLAT-5583] Update ES Exporter fork to release 1.8.0
2 parents fd25030 + 2c0f753 commit 5bd1c34

File tree

5 files changed

+282
-1
lines changed

5 files changed

+282
-1
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.8.0
1+
2.8.0

collector/remote_info.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Copyright 2021 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package collector
15+
16+
import (
17+
"encoding/json"
18+
"fmt"
19+
"net/http"
20+
"net/url"
21+
"path"
22+
23+
"github.com/go-kit/log"
24+
"github.com/go-kit/log/level"
25+
"github.com/prometheus/client_golang/prometheus"
26+
)
27+
28+
// Labels for remote info metrics
29+
var defaulRemoteInfoLabels = []string{"remote_cluster"}
30+
var defaultRemoteInfoLabelValues = func(remote_cluster string) []string {
31+
return []string{
32+
remote_cluster,
33+
}
34+
}
35+
36+
type remoteInfoMetric struct {
37+
Type prometheus.ValueType
38+
Desc *prometheus.Desc
39+
Value func(remoteStats RemoteCluster) float64
40+
Labels func(remote_cluster string) []string
41+
}
42+
43+
// RemoteInfo information struct
44+
type RemoteInfo struct {
45+
logger log.Logger
46+
client *http.Client
47+
url *url.URL
48+
49+
up prometheus.Gauge
50+
totalScrapes, jsonParseFailures prometheus.Counter
51+
52+
remoteInfoMetrics []*remoteInfoMetric
53+
}
54+
55+
// NewClusterSettings defines Cluster Settings Prometheus metrics
56+
func NewRemoteInfo(logger log.Logger, client *http.Client, url *url.URL) *RemoteInfo {
57+
58+
return &RemoteInfo{
59+
logger: logger,
60+
client: client,
61+
url: url,
62+
63+
up: prometheus.NewGauge(prometheus.GaugeOpts{
64+
Name: prometheus.BuildFQName(namespace, "remote_info_stats", "up"),
65+
Help: "Was the last scrape of the ElasticSearch remote info endpoint successful.",
66+
}),
67+
totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{
68+
Name: prometheus.BuildFQName(namespace, "remote_info_stats", "total_scrapes"),
69+
Help: "Current total ElasticSearch remote info scrapes.",
70+
}),
71+
jsonParseFailures: prometheus.NewCounter(prometheus.CounterOpts{
72+
Name: prometheus.BuildFQName(namespace, "remote_info_stats", "json_parse_failures"),
73+
Help: "Number of errors while parsing JSON.",
74+
}),
75+
// Send all of the remote metrics
76+
remoteInfoMetrics: []*remoteInfoMetric{
77+
{
78+
Type: prometheus.GaugeValue,
79+
Desc: prometheus.NewDesc(
80+
prometheus.BuildFQName(namespace, "remote_info", "num_nodes_connected"),
81+
"Number of nodes connected", defaulRemoteInfoLabels, nil,
82+
),
83+
Value: func(remoteStats RemoteCluster) float64 {
84+
return float64(remoteStats.NumNodesConnected)
85+
},
86+
Labels: defaultRemoteInfoLabelValues,
87+
},
88+
{
89+
Type: prometheus.GaugeValue,
90+
Desc: prometheus.NewDesc(
91+
prometheus.BuildFQName(namespace, "remote_info", "num_proxy_sockets_connected"),
92+
"Number of proxy sockets connected", defaulRemoteInfoLabels, nil,
93+
),
94+
Value: func(remoteStats RemoteCluster) float64 {
95+
return float64(remoteStats.NumProxySocketsConnected)
96+
},
97+
Labels: defaultRemoteInfoLabelValues,
98+
},
99+
{
100+
Type: prometheus.GaugeValue,
101+
Desc: prometheus.NewDesc(
102+
prometheus.BuildFQName(namespace, "remote_info", "max_connections_per_cluster"),
103+
"Max connections per cluster", defaulRemoteInfoLabels, nil,
104+
),
105+
Value: func(remoteStats RemoteCluster) float64 {
106+
return float64(remoteStats.MaxConnectionsPerCluster)
107+
},
108+
Labels: defaultRemoteInfoLabelValues,
109+
},
110+
},
111+
}
112+
}
113+
114+
func (c *RemoteInfo) fetchAndDecodeRemoteInfoStats() (RemoteInfoResponse, error) {
115+
var rir RemoteInfoResponse
116+
117+
u := *c.url
118+
u.Path = path.Join(u.Path, "/_remote/info")
119+
120+
res, err := c.client.Get(u.String())
121+
if err != nil {
122+
return rir, fmt.Errorf("failed to get remote info from %s://%s:%s%s: %s",
123+
u.Scheme, u.Hostname(), u.Port(), u.Path, err)
124+
}
125+
126+
defer func() {
127+
err = res.Body.Close()
128+
if err != nil {
129+
_ = level.Warn(c.logger).Log(
130+
"msg", "failed to close http.Client",
131+
"err", err,
132+
)
133+
}
134+
}()
135+
136+
if res.StatusCode != http.StatusOK {
137+
return rir, fmt.Errorf("HTTP Request failed with code %d", res.StatusCode)
138+
}
139+
140+
if err := json.NewDecoder(res.Body).Decode(&rir); err != nil {
141+
c.jsonParseFailures.Inc()
142+
return rir, err
143+
}
144+
return rir, nil
145+
}
146+
147+
// Collect gets remote info values
148+
func (ri *RemoteInfo) Collect(ch chan<- prometheus.Metric) {
149+
ri.totalScrapes.Inc()
150+
defer func() {
151+
ch <- ri.up
152+
ch <- ri.totalScrapes
153+
ch <- ri.jsonParseFailures
154+
}()
155+
156+
remoteInfoResp, err := ri.fetchAndDecodeRemoteInfoStats()
157+
if err != nil {
158+
ri.up.Set(0)
159+
_ = level.Warn(ri.logger).Log(
160+
"msg", "failed to fetch and decode remote info",
161+
"err", err,
162+
)
163+
return
164+
}
165+
ri.totalScrapes.Inc()
166+
ri.up.Set(1)
167+
168+
// Remote Info
169+
for remote_cluster, remoteInfo := range remoteInfoResp {
170+
for _, metric := range ri.remoteInfoMetrics {
171+
ch <- prometheus.MustNewConstMetric(
172+
metric.Desc,
173+
metric.Type,
174+
metric.Value(remoteInfo),
175+
metric.Labels(remote_cluster)...,
176+
)
177+
}
178+
}
179+
}
180+
181+
// Describe add Indices metrics descriptions
182+
func (ri *RemoteInfo) Describe(ch chan<- *prometheus.Desc) {
183+
for _, metric := range ri.remoteInfoMetrics {
184+
ch <- metric.Desc
185+
}
186+
ch <- ri.up.Desc()
187+
ch <- ri.totalScrapes.Desc()
188+
ch <- ri.jsonParseFailures.Desc()
189+
}

collector/remote_info_response.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2021 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package collector
15+
16+
// RemoteInfoResponse is a representation of a Elasticsearch _remote/info
17+
type RemoteInfoResponse map[string]RemoteCluster
18+
19+
// RemoteClsuter defines the struct of the tree for the Remote Cluster
20+
type RemoteCluster struct {
21+
Seeds []string `json:"seeds"`
22+
Connected bool `json:"connected"`
23+
NumNodesConnected int64 `json:"num_nodes_connected"`
24+
NumProxySocketsConnected int64 `json:"num_proxy_sockets_connected"`
25+
MaxConnectionsPerCluster int64 `json:"max_connections_per_cluster"`
26+
InitialConnectTimeout string `json:"initial_connect_timeout"`
27+
SkipUnavailable bool `json:"skip_unavailable"`
28+
}

collector/remote_info_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2021 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package collector
15+
16+
import (
17+
"io"
18+
"net/http"
19+
"net/http/httptest"
20+
"net/url"
21+
"os"
22+
"testing"
23+
24+
"github.com/go-kit/log"
25+
)
26+
27+
func TestRemoteInfoStats(t *testing.T) {
28+
// Testcases created using:
29+
// docker run -d -p 9200:9200 elasticsearch:VERSION-alpine
30+
// curl http://localhost:9200/_cluster/settings/?include_defaults=true
31+
files := []string{"../fixtures/settings-5.4.2.json", "../fixtures/settings-merge-5.4.2.json"}
32+
for _, filename := range files {
33+
f, _ := os.Open(filename)
34+
defer f.Close()
35+
for hn, handler := range map[string]http.Handler{
36+
"plain": http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
37+
io.Copy(w, f)
38+
}),
39+
} {
40+
ts := httptest.NewServer(handler)
41+
defer ts.Close()
42+
43+
u, err := url.Parse(ts.URL)
44+
if err != nil {
45+
t.Fatalf("Failed to parse URL: %s", err)
46+
}
47+
c := NewRemoteInfo(log.NewNopLogger(), http.DefaultClient, u)
48+
nsr, err := c.fetchAndDecodeRemoteInfoStats()
49+
if err != nil {
50+
t.Fatalf("Failed to fetch or decode remote info stats: %s", err)
51+
}
52+
t.Logf("[%s/%s] Remote Info Stats Response: %+v", hn, filename, nsr)
53+
54+
}
55+
}
56+
}

main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ func main() {
104104
esInsecureSkipVerify = kingpin.Flag("es.ssl-skip-verify",
105105
"Skip SSL verification when connecting to Elasticsearch.").
106106
Default("false").Bool()
107+
esExportRemoteInfo = kingpin.Flag("es.remote_info",
108+
"Export stats associated with configured remote clusters.").
109+
Default("false").Envar("ES_REMOTE_INFO").Bool()
107110
logLevel = kingpin.Flag("log.level",
108111
"Sets the loglevel. Valid levels are debug, info, warn, error").
109112
Default("info").String()
@@ -234,6 +237,11 @@ func main() {
234237
prometheus.MustRegister(collector.NewIlmIndicies(logger, httpClient, esURL))
235238
}
236239

240+
if *esExportRemoteInfo {
241+
// Create Remote info Collector
242+
prometheus.MustRegister(collector.NewRemoteInfo(logger, httpClient, esURL))
243+
}
244+
237245
// Create a context that is cancelled on SIGKILL or SIGINT.
238246
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
239247
defer cancel()

0 commit comments

Comments
 (0)