mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2024-12-23 23:50:28 -06:00
fix reporter
This commit is contained in:
parent
b130caf154
commit
280c270d8f
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 nosqlbench
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.nosqlbench.api.engine.metrics.reporters;
|
||||||
|
|
||||||
|
import io.nosqlbench.api.config.standard.*;
|
||||||
|
import io.nosqlbench.api.labels.NBLabels;
|
||||||
|
import io.nosqlbench.components.NBComponent;
|
||||||
|
import io.nosqlbench.components.PeriodicTaskComponent;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpClient.Redirect;
|
||||||
|
import java.net.http.HttpClient.Version;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpRequest.BodyPublishers;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.net.http.HttpResponse.BodyHandler;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class PromPushReporterComponent extends PeriodicTaskComponent implements NBConfigurable {
|
||||||
|
private static final Logger logger = LogManager.getLogger(PromPushReporterComponent.class);
|
||||||
|
private HttpClient client;
|
||||||
|
private final URI uri;
|
||||||
|
private String bearerToken;
|
||||||
|
private boolean needsAuth;
|
||||||
|
|
||||||
|
public PromPushReporterComponent(
|
||||||
|
final String targetUriSpec,
|
||||||
|
final String config,
|
||||||
|
int seconds,
|
||||||
|
NBComponent component,
|
||||||
|
NBLabels labels
|
||||||
|
) {
|
||||||
|
super(component, labels, seconds, true);
|
||||||
|
|
||||||
|
uri = URI.create(targetUriSpec);
|
||||||
|
needsAuth = false;
|
||||||
|
ConfigLoader loader = new ConfigLoader();
|
||||||
|
List<Map> configs = loader.load(config, Map.class);
|
||||||
|
NBConfigModel cm = this.getConfigModel();
|
||||||
|
if (configs != null) {
|
||||||
|
logger.info("PromPushReporter process configuration: %s", config);
|
||||||
|
for (Map cmap : configs) {
|
||||||
|
NBConfiguration cfg = cm.apply(cmap);
|
||||||
|
this.applyConfig(cfg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("PromPushReporter default configuration");
|
||||||
|
HashMap<String, String> junk = new HashMap<>(Map.of());
|
||||||
|
NBConfiguration cfg = cm.apply(junk);
|
||||||
|
this.applyConfig(cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NBConfigModel getConfigModel() {
|
||||||
|
return ConfigModel.of(this.getClass())
|
||||||
|
.add(Param.defaultTo("apikeyfile", "$NBSTATEDIR/prompush/prompush_apikey")
|
||||||
|
.setDescription("The file that contains the api key, supersedes apikey"))
|
||||||
|
.add(Param.optional("apikey", String.class)
|
||||||
|
.setDescription("The api key to use"))
|
||||||
|
.asReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyConfig(NBConfiguration cfg) {
|
||||||
|
Path keyfilePath = null;
|
||||||
|
Optional<String> optionalApikeyfile = cfg.getEnvOptional("apikeyfile");
|
||||||
|
Optional<String> optionalApikey = cfg.getOptional("apikey");
|
||||||
|
bearerToken = null;
|
||||||
|
if (optionalApikeyfile.isPresent()) {
|
||||||
|
keyfilePath = optionalApikeyfile.map(Path::of).orElseThrow();
|
||||||
|
if (Files.isRegularFile(keyfilePath)) {
|
||||||
|
logger.info("Reading Bearer Token from %s", keyfilePath);
|
||||||
|
PromPushKeyFileReader keyfile = new PromPushKeyFileReader(keyfilePath);
|
||||||
|
bearerToken = keyfile.get();
|
||||||
|
}
|
||||||
|
} else if (optionalApikey.isPresent()) {
|
||||||
|
bearerToken = optionalApikey.get();
|
||||||
|
}
|
||||||
|
needsAuth = (null != bearerToken);
|
||||||
|
bearerToken = "Bearer " + bearerToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void task() {
|
||||||
|
final Clock nowclock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(1024 * 1024); // 1M pre-allocated to reduce heap churn
|
||||||
|
|
||||||
|
int total = 0;
|
||||||
|
for (final Object metric : getParent().findMetricsInTree("")) {
|
||||||
|
sb = PromExpositionFormat.format(nowclock, sb, metric);
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
PromPushReporterComponent.logger.debug("formatted {} metrics in prom expo format", total);
|
||||||
|
final String exposition = sb.toString();
|
||||||
|
logger.trace(() -> "prom exposition format:\n" + exposition);
|
||||||
|
|
||||||
|
final double backoffRatio = 1.5;
|
||||||
|
final double maxBackoffSeconds = 10;
|
||||||
|
double backOff = 1.0;
|
||||||
|
|
||||||
|
final int maxRetries = 5;
|
||||||
|
int remainingRetries = maxRetries;
|
||||||
|
final List<Exception> errors = new ArrayList<>();
|
||||||
|
boolean succeeded = false;
|
||||||
|
|
||||||
|
while (0 < remainingRetries) {
|
||||||
|
remainingRetries--;
|
||||||
|
final HttpClient client = getCachedClient();
|
||||||
|
final HttpRequest.Builder rb = HttpRequest.newBuilder().uri(uri);
|
||||||
|
if (needsAuth) {
|
||||||
|
rb.setHeader("Authorization", bearerToken);
|
||||||
|
}
|
||||||
|
final HttpRequest request = rb.POST(BodyPublishers.ofString(exposition)).build();
|
||||||
|
final BodyHandler<String> handler = HttpResponse.BodyHandlers.ofString();
|
||||||
|
HttpResponse<String> response = null;
|
||||||
|
try {
|
||||||
|
response = client.send(request, handler);
|
||||||
|
final int status = response.statusCode();
|
||||||
|
if ((200 > status) || (300 <= status)) {
|
||||||
|
final String errmsg = "status " + response.statusCode() + " while posting metrics to '" + this.uri + '\'';
|
||||||
|
throw new RuntimeException(errmsg);
|
||||||
|
}
|
||||||
|
PromPushReporterComponent.logger.debug("posted {} metrics to prom push endpoint '{}'", total, this.uri);
|
||||||
|
succeeded = true;
|
||||||
|
break;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
errors.add(e);
|
||||||
|
try {
|
||||||
|
Thread.sleep((int) backOff * 1000L);
|
||||||
|
} catch (final InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
backOff = Math.min(maxBackoffSeconds, backOff * backoffRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!succeeded) {
|
||||||
|
PromPushReporterComponent.logger.error("Failed to send push prom metrics after {} tries. Errors follow:", maxRetries);
|
||||||
|
for (final Exception error : errors) PromPushReporterComponent.logger.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized HttpClient getCachedClient() {
|
||||||
|
if (null == client) this.client = this.getNewClient();
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized HttpClient getNewClient() {
|
||||||
|
this.client = HttpClient.newBuilder()
|
||||||
|
.followRedirects(Redirect.NORMAL)
|
||||||
|
.connectTimeout(Duration.ofSeconds(60))
|
||||||
|
.version(Version.HTTP_2)
|
||||||
|
.build();
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
}
|
@ -17,14 +17,19 @@
|
|||||||
package io.nosqlbench.components;
|
package io.nosqlbench.components;
|
||||||
|
|
||||||
import io.nosqlbench.api.engine.metrics.DeltaHdrHistogramReservoir;
|
import io.nosqlbench.api.engine.metrics.DeltaHdrHistogramReservoir;
|
||||||
|
import io.nosqlbench.api.engine.metrics.DoubleSummaryGauge;
|
||||||
import io.nosqlbench.api.engine.metrics.instruments.NBFunctionGauge;
|
import io.nosqlbench.api.engine.metrics.instruments.NBFunctionGauge;
|
||||||
import io.nosqlbench.api.engine.metrics.instruments.NBMetricCounter;
|
import io.nosqlbench.api.engine.metrics.instruments.NBMetricCounter;
|
||||||
import io.nosqlbench.api.engine.metrics.instruments.NBMetricHistogram;
|
import io.nosqlbench.api.engine.metrics.instruments.NBMetricHistogram;
|
||||||
import io.nosqlbench.api.engine.metrics.instruments.NBMetricTimer;
|
import io.nosqlbench.api.engine.metrics.instruments.NBMetricTimer;
|
||||||
|
import io.nosqlbench.api.engine.metrics.reporters.PromPushReporterComponent;
|
||||||
import io.nosqlbench.api.labels.NBLabels;
|
import io.nosqlbench.api.labels.NBLabels;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.DoubleSummaryStatistics;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class NBBuilders {
|
public class NBBuilders {
|
||||||
@ -49,12 +54,25 @@ public class NBBuilders {
|
|||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public NBFunctionGauge gauge(String metricFamilyName, Supplier<Double> valueSource) {
|
public NBFunctionGauge gauge(String metricFamilyName, Supplier<Double> valueSource) {
|
||||||
NBFunctionGauge gauge = new NBFunctionGauge(base, valueSource, metricFamilyName);
|
NBFunctionGauge gauge = new NBFunctionGauge(base, valueSource, metricFamilyName);
|
||||||
base.addMetric(gauge);
|
base.addMetric(gauge);
|
||||||
return gauge;
|
return gauge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DoubleSummaryGauge summaryGauge(String name, String... statspecs) {
|
||||||
|
List<DoubleSummaryGauge.Stat> stats = Arrays.stream(statspecs).map(DoubleSummaryGauge.Stat::valueOf).toList();
|
||||||
|
DoubleSummaryStatistics reservoir = new DoubleSummaryStatistics();
|
||||||
|
DoubleSummaryGauge anyGauge = null;
|
||||||
|
for (DoubleSummaryGauge.Stat stat : stats) {
|
||||||
|
anyGauge = new DoubleSummaryGauge(base.getLabels().and(NBLabels.forKV("name",name,"stat", stat)), stat, reservoir);
|
||||||
|
base.addMetric(anyGauge);
|
||||||
|
}
|
||||||
|
return anyGauge;
|
||||||
|
}
|
||||||
|
|
||||||
public NBMetricHistogram histogram(String metricFamilyName, int hdrdigits) {
|
public NBMetricHistogram histogram(String metricFamilyName, int hdrdigits) {
|
||||||
NBLabels labels = base.getLabels().and("name", metricFamilyName);
|
NBLabels labels = base.getLabels().and("name", metricFamilyName);
|
||||||
NBMetricHistogram histogram = new NBMetricHistogram(labels, new DeltaHdrHistogramReservoir(labels, hdrdigits));
|
NBMetricHistogram histogram = new NBMetricHistogram(labels, new DeltaHdrHistogramReservoir(labels, hdrdigits));
|
||||||
@ -68,12 +86,16 @@ public class NBBuilders {
|
|||||||
AttachedMetricsSummaryReporter reporter = new AttachedMetricsSummaryReporter(base, extraLabels, seconds);
|
AttachedMetricsSummaryReporter reporter = new AttachedMetricsSummaryReporter(base, extraLabels, seconds);
|
||||||
return reporter;
|
return reporter;
|
||||||
}
|
}
|
||||||
public AttachedMetricsPushReporter pushReporter(String targetUri, int seconds,String... labelspecs) {
|
// public AttachedMetricCsvReporter csvReporter(int seconds, String dirpath, String... labelspecs) {
|
||||||
|
// logger.debug("attaching summary reporter to " + base.description());
|
||||||
|
// NBLabels extraLabels = NBLabels.forKV((Object[]) labelspecs);
|
||||||
|
// AttachedMetricCsvReporter reporter = new AttachedMetricCsvReporter(base, extraLabels, Path.of(dirpath), seconds);
|
||||||
|
// return reporter;
|
||||||
|
// }
|
||||||
|
public PromPushReporterComponent pushReporter(String targetUri, int seconds, String config, String... labelspecs) {
|
||||||
NBLabels extraLabels = NBLabels.forKV((Object[]) labelspecs);
|
NBLabels extraLabels = NBLabels.forKV((Object[]) labelspecs);
|
||||||
AttachedMetricsPushReporter reporter = new AttachedMetricsPushReporter(targetUri, base, seconds, extraLabels);
|
PromPushReporterComponent reporter = new PromPushReporterComponent(targetUri, config, seconds, base,extraLabels);
|
||||||
return reporter;
|
return reporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user