
org.cloudfoundry.promregator.fetcher.CFMetricsFetcher Maven / Gradle / Ivy
package org.cloudfoundry.promregator.fetcher;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.UUID;
import org.apache.http.HttpHost;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.cloudfoundry.promregator.auth.AuthenticationEnricher;
import org.cloudfoundry.promregator.endpoint.EndpointConstants;
import org.cloudfoundry.promregator.rewrite.AbstractMetricFamilySamplesEnricher;
import io.prometheus.client.Collector.MetricFamilySamples;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram.Timer;
/**
* A MetricsFetcher is a class which retrieves Prometheus metrics at an endpoint URL, which is run
* by a CF instance. For the sake of retrieving the metrics reliably, it addresses the call to a single CF instance
* only using CF Direct HTTP Endpoint Routing.
*
* It implements the MetricsFetcher interface to allow running it in a ThreadPool(Executor). The result of the
* Callable is the Prometheus metrics data upon success. In case retrieving the data failed, null
is returned.
*
*/
public class CFMetricsFetcher implements MetricsFetcher {
private static final String HTTP_HEADER_CF_APP_INSTANCE = "X-CF-APP-INSTANCE";
private static final Logger log = Logger.getLogger(CFMetricsFetcher.class);
private String endpointUrl;
private String instanceId;
private final RequestConfig config;
private AuthenticationEnricher ae;
private Gauge.Child up;
private AbstractMetricFamilySamplesEnricher mfse;
final static CloseableHttpClient httpclient = HttpClients.createDefault();
private MetricsFetcherMetrics mfm;
private UUID promregatorUUID;
/**
* creates a new Metrics Fetcher by defining the target endpoint where the metrics can be read, the instance identifier
* of the instance, which shall be queried.
* Additional configuration options can be provided using the CFMetricsFetcherConfig reference.
* @param endpointUrl the endpoint URL, which shall be used to query the CF app for the Prometheus metrics.
* @param instanceId the instance Id in format : , which identifies the instance uniquely.
* @param config additional configurations specifing additional properties for retrieving data.
*/
public CFMetricsFetcher(String endpointUrl, String instanceId, CFMetricsFetcherConfig config) {
this.endpointUrl = endpointUrl;
this.instanceId = instanceId;
this.ae = config.getAuthenticationEnricher();
this.mfse = config.getMetricFamilySamplesEnricher();
this.mfm = config.getMetricsFetcherMetrics();
this.up = config.getUpChild();
this.promregatorUUID = config.getPromregatorInstanceIdentifier();
Builder requestConfigBuilder = RequestConfig.custom()
.setRedirectsEnabled(true)
.setCircularRedirectsAllowed(false)
.setMaxRedirects(10)
.setSocketTimeout(config.getSocketReadTimeoutInMillis())
.setConnectTimeout(config.getConnectionTimeoutInMillis());
if (config.getProxyHost() != null && config.getProxyPort() != 0) {
requestConfigBuilder = requestConfigBuilder.setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort(), "http"));
}
this.config = requestConfigBuilder.build();
}
@Override
public HashMap call() throws Exception {
log.debug(String.format("Reading metrics from %s for instance %s", this.endpointUrl, this.instanceId));
HttpGet httpget = new HttpGet(this.endpointUrl);
if (this.config != null) {
httpget.setConfig(this.config);
}
// see also https://docs.cloudfoundry.org/concepts/http-routing.html
httpget.setHeader(HTTP_HEADER_CF_APP_INSTANCE, this.instanceId);
// provided for recursive scraping / loopback detection
httpget.setHeader(EndpointConstants.HTTP_HEADER_PROMREGATOR_INSTANCE_IDENTIFIER, this.promregatorUUID.toString());
if (this.ae != null) {
this.ae.enrichWithAuthentication(httpget);
}
CloseableHttpResponse response = null;
boolean available = false;
try {
Timer timer = null;
if (this.mfm.getLatencyRequest() != null) {
timer = this.mfm.getLatencyRequest().startTimer();
}
String result = null;
try {
response = httpclient.execute(httpget);
if (response.getStatusLine().getStatusCode() != 200) {
log.warn(String.format("Target server at '%s' and instance '%s' responded with a non-200 status code: %d", this.endpointUrl, this.instanceId, response.getStatusLine().getStatusCode()));
return null;
}
result = EntityUtils.toString(response.getEntity());
} catch (HttpHostConnectException hhce) {
log.warn(String.format("Unable to connect to server trying to fetch metrics from %s, instance %s", this.endpointUrl, this.instanceId), hhce);
return null;
} catch (SocketTimeoutException ste) {
log.warn(String.format("Read timeout for data from socket while trying to fetch metrics from %s, instance %s", this.endpointUrl, this.instanceId), ste);
return null;
} catch (ConnectTimeoutException cte) {
log.warn(String.format("Timeout while trying to connect to %s, instance %s for fetching metrics", this.endpointUrl, this.instanceId), cte);
return null;
} finally {
if (timer != null) {
timer.observeDuration();
}
}
log.debug(String.format("Successfully received metrics from %s for instance %s", this.endpointUrl, this.instanceId));
if (this.mfm.getRequestSize() != null) {
this.mfm.getRequestSize().observe(result.length());
}
TextFormat004Parser parser = new TextFormat004Parser(result);
HashMap emfs = parser.parse();
// we got a proper response
available = true;
emfs = this.mfse.determineEnumerationOfMetricFamilySamples(emfs);
return emfs;
} catch (ClientProtocolException e) {
log.warn("Client communication error while fetching metrics from target server", e);
return null;
} catch (IOException e) {
log.warn("IO Exception while fetching metrics from target server", e);
return null;
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.info("Unable to properly close Metrics fetch HTTP connection", e);
// bad luck!
}
}
if (this.up != null) {
if (available) {
this.up.set(1.0);
} else {
if (this.mfm.getFailedRequests() != null)
this.mfm.getFailedRequests().inc();
this.up.set(0.0);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy