mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
Merge pull request #1485 from nosqlbench/nosqlbench-1467
Nosqlbench vector fixes
This commit is contained in:
commit
6cee58aadb
@ -51,7 +51,7 @@ public class AmqpAdapterMetrics {
|
||||
|
||||
public AmqpAdapterMetrics(final AmqpBaseOpDispenser amqpBaseOpDispenser, final NBLabeledElement labeledParent) {
|
||||
this.amqpBaseOpDispenser = amqpBaseOpDispenser;
|
||||
labels=labeledParent.getLabels().and("name", AmqpAdapterMetrics.class.getSimpleName());
|
||||
labels=labeledParent.getLabels().andTypes("name", AmqpAdapterMetrics.class.getSimpleName());
|
||||
}
|
||||
|
||||
public void initS4JAdapterInstrumentation() {
|
||||
|
@ -71,13 +71,13 @@ public class NamingFolio {
|
||||
* by name, type, table, keyspace. For now it just returns everything in fully qualified form.
|
||||
*/
|
||||
public String nameFor(NBLabeledElement labeled, String... fields) {
|
||||
NBLabels labelsPlus = labeled.getLabels().and(fields);
|
||||
NBLabels labelsPlus = labeled.getLabels().andTypes(fields);
|
||||
String name = namer.apply(labelsPlus.asMap());
|
||||
return name;
|
||||
}
|
||||
|
||||
public String nameFor(NBLabeledElement labeled, Map<String,String> fields) {
|
||||
NBLabels labelsPlus = labeled.getLabels().and(fields);
|
||||
NBLabels labelsPlus = labeled.getLabels().andTypes(fields);
|
||||
String name = namer.apply(labelsPlus.asMap());
|
||||
return name;
|
||||
|
||||
|
@ -42,6 +42,6 @@ public class CqlTableColumn extends CqlColumnBase {
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return super.getLabels().and("table", table.getName());
|
||||
return super.getLabels().andTypes("table", table.getName());
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,6 @@ public class CqlTypeColumn extends CqlColumnBase {
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return super.getLabels().and("name", this.type.getName());
|
||||
return super.getLabels().andTypes("name", this.type.getName());
|
||||
}
|
||||
}
|
||||
|
@ -171,6 +171,6 @@ public class DiagTask_gauge extends BaseDiagTask implements Gauge<Double> {
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return super.getLabels().and("stat",this.stat.toString());
|
||||
return super.getLabels().andTypes("stat",this.stat.toString());
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public class KafkaAdapterMetrics {
|
||||
|
||||
public KafkaAdapterMetrics(final KafkaBaseOpDispenser kafkaBaseOpDispenser, final NBLabeledElement labeledParent) {
|
||||
this.kafkaBaseOpDispenser = kafkaBaseOpDispenser;
|
||||
labels=labeledParent.getLabels().and("name",KafkaAdapterMetrics.class.getSimpleName());
|
||||
labels=labeledParent.getLabels().andTypes("name",KafkaAdapterMetrics.class.getSimpleName());
|
||||
}
|
||||
|
||||
public void initS4JAdapterInstrumentation() {
|
||||
|
@ -114,7 +114,8 @@ public class OpsLoader {
|
||||
stderrStream,
|
||||
new os.Path(Path.of(System.getProperty("user.dir"))),
|
||||
Option.empty(),
|
||||
Option.empty()
|
||||
Option.empty(),
|
||||
null
|
||||
);
|
||||
|
||||
String stdoutOutput = stdoutBuffer.toString(StandardCharsets.UTF_8);
|
||||
|
@ -69,7 +69,7 @@ public class RawOpDef extends RawOpFields {
|
||||
Object op = map.remove(keyName);
|
||||
if (op instanceof CharSequence s) {
|
||||
if (!keyName.equals("stmt")) {
|
||||
logger.warn("Used implied stmt field under name '" + keyName + "'. You can just use 'stmt: ... "+ s +"' or the equivalent to avoid this warning.");
|
||||
logger.info("Used implied stmt field under name '" + keyName + "'. You can just use 'stmt: ... "+ s +"' or the equivalent to avoid this warning.");
|
||||
}
|
||||
map.put("stmt",s.toString());
|
||||
// setOp(new LinkedHashMap<String,Object>(Map.of("stmt",s.toString())));
|
||||
@ -79,7 +79,7 @@ public class RawOpDef extends RawOpFields {
|
||||
}
|
||||
if (found.size() > 1) {
|
||||
throw new BasicError("You used " + found + " as an op name, but only one of these is allowed at a time.");
|
||||
} else if ((getName() == null || getName().isEmpty()) && op == null && map.size() > 0) {
|
||||
} else if ((getName() == null || getName().isEmpty()) && op == null && !map.isEmpty()) {
|
||||
Map.Entry<String, Object> first = map.entrySet().iterator().next();
|
||||
setName(first.getKey());
|
||||
setOp(first.getValue());
|
||||
@ -90,7 +90,7 @@ public class RawOpDef extends RawOpFields {
|
||||
|
||||
if (_op) {
|
||||
if (_params) {
|
||||
if (map.size() > 0) {
|
||||
if (!map.isEmpty()) {
|
||||
throw new OpConfigError("If you have scoped op and params, you may not have dangling fields. Op template named '" + this.getName() + "' is invalid. Move dangling params ("+ map.keySet() +") under another field.");
|
||||
}
|
||||
} else { // no params. Op was a scoped field and there are dangling fields, so assume they belong to params
|
||||
|
@ -154,6 +154,7 @@ public abstract class BaseDriverAdapter<R extends Op, S> implements DriverAdapte
|
||||
public NBConfigModel getConfigModel() {
|
||||
return ConfigModel.of(BaseDriverAdapter.class)
|
||||
.add(Param.optional("alias"))
|
||||
.add(Param.optional("labels",String.class,"Labels which will apply to metrics and annotations for this activity only"))
|
||||
.add(Param.defaultTo("strict", true, "strict op field mode, which requires that provided op fields are recognized and used"))
|
||||
.add(Param.optional(List.of("op", "stmt", "statement"), String.class, "op template in statement form"))
|
||||
.add(Param.optional("tags", String.class, "tags to be used to filter operations"))
|
||||
|
@ -17,6 +17,7 @@
|
||||
package io.nosqlbench.adapters.api.templating;
|
||||
|
||||
import io.nosqlbench.adapters.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.api.config.NBLabelSpec;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.config.fieldreaders.DynamicFieldReader;
|
||||
@ -328,19 +329,19 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, NBLabeledElement,
|
||||
* The activity configuration, used to resolve nested config parameters
|
||||
* @param preprocessors
|
||||
* Map->Map transformers.
|
||||
* @param labels
|
||||
*/
|
||||
public ParsedOp(
|
||||
OpTemplate opTemplate,
|
||||
NBConfiguration activityCfg,
|
||||
List<Function<Map<String, Object>, Map<String, Object>>> preprocessors,
|
||||
NBLabeledElement parent) {
|
||||
NBLabeledElement parent
|
||||
) {
|
||||
this._opTemplate = opTemplate;
|
||||
this.activityCfg = activityCfg;
|
||||
labels=parent.getLabels().and("op", this.getName());
|
||||
|
||||
Map<String, Object> map = opTemplate.getOp().orElseThrow(() ->
|
||||
new OpConfigError("ParsedOp constructor requires a non-null value for the op field, but it was missing."));
|
||||
|
||||
for (Function<Map<String, Object>, Map<String, Object>> preprocessor : preprocessors) {
|
||||
map = preprocessor.apply(map);
|
||||
}
|
||||
@ -353,6 +354,19 @@ public class ParsedOp implements LongFunction<Map<String, ?>>, NBLabeledElement,
|
||||
activityCfg.getMap())
|
||||
);
|
||||
|
||||
NBLabels opLabels = parent.getLabels().andTypes("op", this.getName());
|
||||
if (tmap.isStatic("labels")) {
|
||||
Object labelSpecObject = tmap.takeStaticValue("labels", Object.class);
|
||||
if (labelSpecObject instanceof String labelsSpec) {
|
||||
NBLabels op_specific_labels = NBLabelSpec.parseLabels(labelsSpec);
|
||||
opLabels=opLabels.and(op_specific_labels);
|
||||
} else {
|
||||
throw new OpConfigError("parsing labels as type '" + labelSpecObject.getClass().getSimpleName() +"' is not supported.");
|
||||
}
|
||||
} else if (tmap.isDynamic("labels")) {
|
||||
throw new OpConfigError("Labels may not be dynamic.");
|
||||
}
|
||||
this.labels=opLabels;
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,9 +27,9 @@ public class CoreActivityInstrumentation implements ActivityInstrumentation {
|
||||
|
||||
private static final String STRICTMETRICNAMES = "strictmetricnames";
|
||||
|
||||
private static final String WAIT_TIME = ".waittime";
|
||||
private static final String SERVICE_TIME = ".servicetime";
|
||||
private static final String RESPONSE_TIME = ".responsetime";
|
||||
private static final String WAIT_TIME = "_waittime";
|
||||
private static final String SERVICE_TIME = "_servicetime";
|
||||
private static final String RESPONSE_TIME = "_responsetime";
|
||||
|
||||
private final Activity activity;
|
||||
private final ActivityDef def;
|
||||
@ -106,7 +106,7 @@ public class CoreActivityInstrumentation implements ActivityInstrumentation {
|
||||
|
||||
@Override
|
||||
public synchronized Timer getOrCreateResultSuccessTimer() {
|
||||
return ActivityMetrics.timer(this.activity,"result-success", this.activity.getHdrDigits());
|
||||
return ActivityMetrics.timer(this.activity,"result_success", this.activity.getHdrDigits());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,9 +152,9 @@ public class HybridRateLimiter implements RateLimiter {
|
||||
|
||||
|
||||
protected void init(final NBLabeledElement activityDef) {
|
||||
delayGauge = ActivityMetrics.gauge(activityDef, this.label + ".waittime", new RateLimiters.WaitTimeGauge(this));
|
||||
avgRateGauge = ActivityMetrics.gauge(activityDef, this.label + ".config.cyclerate", new RateLimiters.RateGauge(this));
|
||||
burstRateGauge = ActivityMetrics.gauge(activityDef, this.label + ".config.burstrate", new RateLimiters.BurstRateGauge(this));
|
||||
delayGauge = ActivityMetrics.gauge(activityDef, this.label + "_waittime", new RateLimiters.WaitTimeGauge(this));
|
||||
avgRateGauge = ActivityMetrics.gauge(activityDef, this.label + "_config_cyclerate", new RateLimiters.RateGauge(this));
|
||||
burstRateGauge = ActivityMetrics.gauge(activityDef, this.label + "_config_burstrate", new RateLimiters.BurstRateGauge(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,6 +17,7 @@
|
||||
package io.nosqlbench.engine.api.activityimpl;
|
||||
|
||||
import com.codahale.metrics.Timer;
|
||||
import io.nosqlbench.api.config.NBLabelSpec;
|
||||
import io.nosqlbench.engine.api.activityapi.core.*;
|
||||
import io.nosqlbench.engine.api.activityapi.core.progress.ActivityMetricProgressMeter;
|
||||
import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay;
|
||||
@ -93,27 +94,33 @@ public class SimpleActivity implements Activity {
|
||||
private final NBLabels labels;
|
||||
|
||||
public SimpleActivity(ActivityDef activityDef, NBLabeledElement parentLabels) {
|
||||
labels = parentLabels.getLabels().and("activity",activityDef.getAlias());
|
||||
NBLabels activityLabels = parentLabels.getLabels()
|
||||
.andTypes("activity", activityDef.getAlias());
|
||||
Optional<String> auxLabelSpec = activityDef.getParams().getOptionalString("labels");
|
||||
if (auxLabelSpec.isPresent()) {
|
||||
activityLabels = activityLabels.and(NBLabelSpec.parseLabels(auxLabelSpec.get()));
|
||||
}
|
||||
this.labels = activityLabels;
|
||||
this.activityDef = activityDef;
|
||||
this.parentLabels = parentLabels;
|
||||
if (activityDef.getAlias().equals(ActivityDef.DEFAULT_ALIAS)) {
|
||||
Optional<String> workloadOpt = activityDef.getParams().getOptionalString(
|
||||
"workload",
|
||||
"yaml"
|
||||
"workload",
|
||||
"yaml"
|
||||
);
|
||||
if (workloadOpt.isPresent()) {
|
||||
activityDef.getParams().set("alias", workloadOpt.get());
|
||||
} else {
|
||||
activityDef.getParams().set("alias",
|
||||
activityDef.getActivityType().toUpperCase(Locale.ROOT)
|
||||
+ nameEnumerator);
|
||||
activityDef.getActivityType().toUpperCase(Locale.ROOT)
|
||||
+ nameEnumerator);
|
||||
nameEnumerator++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SimpleActivity(String activityDefString, NBLabeledElement parentLabels) {
|
||||
this(ActivityDef.parseActivityDef(activityDefString),parentLabels);
|
||||
this(ActivityDef.parseActivityDef(activityDefString), parentLabels);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -124,8 +131,8 @@ public class SimpleActivity implements Activity {
|
||||
public synchronized NBErrorHandler getErrorHandler() {
|
||||
if (null == this.errorHandler) {
|
||||
errorHandler = new NBErrorHandler(
|
||||
() -> activityDef.getParams().getOptionalString("errors").orElse("stop"),
|
||||
() -> getExceptionMetrics());
|
||||
() -> activityDef.getParams().getOptionalString("errors").orElse("stop"),
|
||||
() -> getExceptionMetrics());
|
||||
}
|
||||
return errorHandler;
|
||||
}
|
||||
@ -324,12 +331,12 @@ public class SimpleActivity implements Activity {
|
||||
public synchronized void initOrUpdateRateLimiters(ActivityDef activityDef) {
|
||||
|
||||
activityDef.getParams().getOptionalNamedParameter("striderate")
|
||||
.map(RateSpec::new)
|
||||
.ifPresent(spec -> strideLimiter = RateLimiters.createOrUpdate(this, "strides", strideLimiter, spec));
|
||||
.map(RateSpec::new)
|
||||
.ifPresent(spec -> strideLimiter = RateLimiters.createOrUpdate(this, "strides", strideLimiter, spec));
|
||||
|
||||
activityDef.getParams().getOptionalNamedParameter("cyclerate", "targetrate", "rate")
|
||||
.map(RateSpec::new).ifPresent(
|
||||
spec -> cycleLimiter = RateLimiters.createOrUpdate(this, "cycles", cycleLimiter, spec));
|
||||
.map(RateSpec::new).ifPresent(
|
||||
spec -> cycleLimiter = RateLimiters.createOrUpdate(this, "cycles", cycleLimiter, spec));
|
||||
|
||||
}
|
||||
|
||||
@ -338,7 +345,8 @@ public class SimpleActivity implements Activity {
|
||||
* length of the sequence as determined by the provided ratios. Also, modify the ActivityDef with reasonable
|
||||
* defaults when requested.
|
||||
*
|
||||
* @param seq - The {@link OpSequence} to derive the defaults from
|
||||
* @param seq
|
||||
* - The {@link OpSequence} to derive the defaults from
|
||||
*/
|
||||
public synchronized void setDefaultsFromOpSequence(OpSequence<?> seq) {
|
||||
Optional<String> strideOpt = getParams().getOptionalString("stride");
|
||||
@ -358,15 +366,15 @@ public class SimpleActivity implements Activity {
|
||||
} else {
|
||||
if (0 == activityDef.getCycleCount()) {
|
||||
throw new RuntimeException(
|
||||
"You specified cycles, but the range specified means zero cycles: " + getParams().get("cycles")
|
||||
"You specified cycles, but the range specified means zero cycles: " + getParams().get("cycles")
|
||||
);
|
||||
}
|
||||
long stride = getParams().getOptionalLong("stride").orElseThrow();
|
||||
long cycles = this.activityDef.getCycleCount();
|
||||
if (cycles < stride) {
|
||||
throw new RuntimeException(
|
||||
"The specified cycles (" + cycles + ") are less than the stride (" + stride + "). This means there aren't enough cycles to cause a stride to be executed." +
|
||||
" If this was intended, then set stride low enough to allow it."
|
||||
"The specified cycles (" + cycles + ") are less than the stride (" + stride + "). This means there aren't enough cycles to cause a stride to be executed." +
|
||||
" If this was intended, then set stride low enough to allow it."
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -376,7 +384,7 @@ public class SimpleActivity implements Activity {
|
||||
|
||||
if (0 < stride && 0 != cycleCount % stride) {
|
||||
logger.warn(() -> "The stride does not evenly divide cycles. Only full strides will be executed," +
|
||||
"leaving some cycles unused. (stride=" + stride + ", cycles=" + cycleCount + ')');
|
||||
"leaving some cycles unused. (stride=" + stride + ", cycles=" + cycleCount + ')');
|
||||
}
|
||||
|
||||
Optional<String> threadSpec = activityDef.getParams().getOptionalString("threads");
|
||||
@ -407,15 +415,15 @@ public class SimpleActivity implements Activity {
|
||||
|
||||
if (activityDef.getThreads() > activityDef.getCycleCount()) {
|
||||
logger.warn(() -> "threads=" + activityDef.getThreads() + " and cycles=" + activityDef.getCycleSummary()
|
||||
+ ", you should have more cycles than threads.");
|
||||
+ ", you should have more cycles than threads.");
|
||||
}
|
||||
|
||||
} else if (1000 < cycleCount) {
|
||||
logger.warn(() -> "For testing at scale, it is highly recommended that you " +
|
||||
"set threads to a value higher than the default of 1." +
|
||||
" hint: you can use threads=auto for reasonable default, or" +
|
||||
" consult the topic on threads with `help threads` for" +
|
||||
" more information.");
|
||||
"set threads to a value higher than the default of 1." +
|
||||
" hint: you can use threads=auto for reasonable default, or" +
|
||||
" consult the topic on threads with `help threads` for" +
|
||||
" more information.");
|
||||
}
|
||||
|
||||
if (0 < this.activityDef.getCycleCount() && 0 == seq.getOps().size()) {
|
||||
@ -426,11 +434,11 @@ public class SimpleActivity implements Activity {
|
||||
/**
|
||||
* Given a function that can create an op of type <O> from a CommandTemplate, generate
|
||||
* an indexed sequence of ready to call operations.
|
||||
*
|
||||
* <p>
|
||||
* This method works almost exactly like the ,
|
||||
* except that it uses the {@link CommandTemplate} semantics, which are more general and allow
|
||||
* for map-based specification of operations with bindings in each field.
|
||||
*
|
||||
* <p>
|
||||
* It is recommended to use the CommandTemplate form
|
||||
* than the
|
||||
*
|
||||
@ -440,8 +448,8 @@ public class SimpleActivity implements Activity {
|
||||
* @return
|
||||
*/
|
||||
protected <O extends Op> OpSequence<OpDispenser<? extends O>> createOpSequenceFromCommands(
|
||||
Function<CommandTemplate, OpDispenser<O>> opinit,
|
||||
boolean strict
|
||||
Function<CommandTemplate, OpDispenser<O>> opinit,
|
||||
boolean strict
|
||||
) {
|
||||
Function<OpTemplate, CommandTemplate> f = CommandTemplate::new;
|
||||
Function<OpTemplate, OpDispenser<? extends O>> opTemplateOFunction = f.andThen(opinit);
|
||||
@ -450,10 +458,10 @@ public class SimpleActivity implements Activity {
|
||||
}
|
||||
|
||||
protected <O extends Op> OpSequence<OpDispenser<? extends O>> createOpSourceFromParsedOps(
|
||||
Map<String, DriverAdapter> adapterCache,
|
||||
Map<String, OpMapper<Op>> mapperCache,
|
||||
List<DriverAdapter> adapters,
|
||||
List<ParsedOp> pops
|
||||
Map<String, DriverAdapter> adapterCache,
|
||||
Map<String, OpMapper<Op>> mapperCache,
|
||||
List<DriverAdapter> adapters,
|
||||
List<ParsedOp> pops
|
||||
) {
|
||||
try {
|
||||
|
||||
@ -466,9 +474,9 @@ public class SimpleActivity implements Activity {
|
||||
}
|
||||
|
||||
SequencerType sequencerType = getParams()
|
||||
.getOptionalString("seq")
|
||||
.map(SequencerType::valueOf)
|
||||
.orElse(SequencerType.bucket);
|
||||
.getOptionalString("seq")
|
||||
.map(SequencerType::valueOf)
|
||||
.orElse(SequencerType.bucket);
|
||||
SequencePlanner<OpDispenser<? extends O>> planner = new SequencePlanner<>(sequencerType);
|
||||
|
||||
int dryrunCount = 0;
|
||||
@ -512,10 +520,10 @@ public class SimpleActivity implements Activity {
|
||||
|
||||
|
||||
protected <O extends Op> OpSequence<OpDispenser<? extends O>> createOpSourceFromCommands(
|
||||
Function<ParsedOp, OpDispenser<? extends O>> opinit,
|
||||
NBConfiguration cfg,
|
||||
List<Function<Map<String, Object>, Map<String, Object>>> parsers,
|
||||
boolean strict
|
||||
Function<ParsedOp, OpDispenser<? extends O>> opinit,
|
||||
NBConfiguration cfg,
|
||||
List<Function<Map<String, Object>, Map<String, Object>>> parsers,
|
||||
boolean strict
|
||||
) {
|
||||
Function<OpTemplate, ParsedOp> f = t -> new ParsedOp(t, cfg, parsers, this);
|
||||
Function<OpTemplate, OpDispenser<? extends O>> opTemplateOFunction = f.andThen(opinit);
|
||||
@ -525,7 +533,7 @@ public class SimpleActivity implements Activity {
|
||||
|
||||
protected List<ParsedOp> loadParsedOps(NBConfiguration cfg, Optional<DriverAdapter> defaultAdapter) {
|
||||
List<ParsedOp> parsedOps = loadOpTemplates(defaultAdapter).stream().map(
|
||||
ot -> new ParsedOp(ot, cfg, List.of(), this)
|
||||
ot -> new ParsedOp(ot, cfg, List.of(), this)
|
||||
).toList();
|
||||
return parsedOps;
|
||||
}
|
||||
@ -546,21 +554,21 @@ public class SimpleActivity implements Activity {
|
||||
// There were no ops, and it was because they were all filtered out
|
||||
if (0 < unfilteredOps.size()) {
|
||||
throw new BasicError("There were no active op templates with tag filter '"
|
||||
+ tagfilter + "', since all " + unfilteredOps.size() + " were filtered out.");
|
||||
+ tagfilter + "', since all " + unfilteredOps.size() + " were filtered out.");
|
||||
}
|
||||
if (defaultDriverAdapter.isPresent() && defaultDriverAdapter.get() instanceof SyntheticOpTemplateProvider sotp) {
|
||||
filteredOps = sotp.getSyntheticOpTemplates(opsDocList, this.activityDef.getParams());
|
||||
Objects.requireNonNull(filteredOps);
|
||||
if (0 == filteredOps.size()) {
|
||||
throw new BasicError("Attempted to create synthetic ops from driver '" + defaultDriverAdapter.get().getAdapterName() + '\'' +
|
||||
" but no ops were created. You must provide either a workload or an op parameter. Activities require op templates.");
|
||||
" but no ops were created. You must provide either a workload or an op parameter. Activities require op templates.");
|
||||
}
|
||||
} else {
|
||||
throw new BasicError("""
|
||||
No op templates were provided. You must provide one of these activity parameters:
|
||||
1) workload=some.yaml
|
||||
2) op='inline template'
|
||||
3) driver=stdout (or any other drive that can synthesize ops)""");
|
||||
No op templates were provided. You must provide one of these activity parameters:
|
||||
1) workload=some.yaml
|
||||
2) op='inline template'
|
||||
3) driver=stdout (or any other drive that can synthesize ops)""");
|
||||
}
|
||||
if (0 == filteredOps.size()) {
|
||||
throw new BasicError("There were no active op templates with tag filter '" + tagfilter + '\'');
|
||||
@ -569,19 +577,19 @@ public class SimpleActivity implements Activity {
|
||||
|
||||
if (0 == filteredOps.size()) {
|
||||
throw new OpConfigError("No op templates found. You must provide either workload=... or op=..., or use " +
|
||||
"a default driver (driver=___). This includes " +
|
||||
ServiceLoader.load(DriverAdapter.class).stream()
|
||||
.filter(p -> {
|
||||
AnnotatedType[] annotatedInterfaces = p.type().getAnnotatedInterfaces();
|
||||
for (AnnotatedType ai : annotatedInterfaces) {
|
||||
if (ai.getType().equals(SyntheticOpTemplateProvider.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map(d -> d.get().getAdapterName())
|
||||
.collect(Collectors.joining(",")));
|
||||
"a default driver (driver=___). This includes " +
|
||||
ServiceLoader.load(DriverAdapter.class).stream()
|
||||
.filter(p -> {
|
||||
AnnotatedType[] annotatedInterfaces = p.type().getAnnotatedInterfaces();
|
||||
for (AnnotatedType ai : annotatedInterfaces) {
|
||||
if (ai.getType().equals(SyntheticOpTemplateProvider.class)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.map(d -> d.get().getAdapterName())
|
||||
.collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
return filteredOps;
|
||||
@ -590,7 +598,7 @@ public class SimpleActivity implements Activity {
|
||||
/**
|
||||
* Given a function that can create an op of type <O> from an OpTemplate, generate
|
||||
* an indexed sequence of ready to call operations.
|
||||
*
|
||||
* <p>
|
||||
* This method uses the following conventions to derive the sequence:
|
||||
*
|
||||
* <OL>
|
||||
@ -604,9 +612,11 @@ public class SimpleActivity implements Activity {
|
||||
* where the sequence length is the sum of the ratios.</LI>
|
||||
* </OL>
|
||||
*
|
||||
* @param <O> A holder for an executable operation for the native driver used by this activity.
|
||||
* @param opinit A function to map an OpTemplate to the executable operation form required by
|
||||
* the native driver for this activity.
|
||||
* @param <O>
|
||||
* A holder for an executable operation for the native driver used by this activity.
|
||||
* @param opinit
|
||||
* A function to map an OpTemplate to the executable operation form required by
|
||||
* the native driver for this activity.
|
||||
* @param defaultAdapter
|
||||
* @return The sequence of operations as determined by filtering and ratios
|
||||
*/
|
||||
@ -624,9 +634,9 @@ public class SimpleActivity implements Activity {
|
||||
}
|
||||
|
||||
SequencerType sequencerType = getParams()
|
||||
.getOptionalString("seq")
|
||||
.map(SequencerType::valueOf)
|
||||
.orElse(SequencerType.bucket);
|
||||
.getOptionalString("seq")
|
||||
.map(SequencerType::valueOf)
|
||||
.orElse(SequencerType.bucket);
|
||||
SequencePlanner<OpDispenser<? extends O>> planner = new SequencePlanner<>(sequencerType);
|
||||
|
||||
try {
|
||||
@ -654,7 +664,7 @@ public class SimpleActivity implements Activity {
|
||||
if (stmt.isPresent()) {
|
||||
String op = stmt.get();
|
||||
workloadSource = "commandline:" + stmt.get();
|
||||
if (op.startsWith("{")||op.startsWith("[")) {
|
||||
if (op.startsWith("{") || op.startsWith("[")) {
|
||||
return OpsLoader.loadString(stmt.get(), OpTemplateFormat.json, activityDef.getParams(), null);
|
||||
} else {
|
||||
return OpsLoader.loadString(stmt.get(), OpTemplateFormat.inline, activityDef.getParams(), null);
|
||||
@ -670,7 +680,6 @@ public class SimpleActivity implements Activity {
|
||||
} catch (Exception e) {
|
||||
throw new OpConfigError("Error loading op templates: " + e, workloadSource, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,7 +16,15 @@
|
||||
|
||||
package io.nosqlbench.engine.api.activityimpl.uniform;
|
||||
|
||||
import io.nosqlbench.adapters.api.activityconfig.OpsLoader;
|
||||
import io.nosqlbench.adapters.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.adapters.api.activityconfig.yaml.OpsDocList;
|
||||
import io.nosqlbench.adapters.api.activityimpl.OpDispenser;
|
||||
import io.nosqlbench.adapters.api.activityimpl.OpMapper;
|
||||
import io.nosqlbench.adapters.api.activityimpl.uniform.DriverAdapter;
|
||||
import io.nosqlbench.adapters.api.activityimpl.uniform.decorators.SyntheticOpTemplateProvider;
|
||||
import io.nosqlbench.adapters.api.activityimpl.uniform.flowtypes.Op;
|
||||
import io.nosqlbench.adapters.api.templating.ParsedOp;
|
||||
import io.nosqlbench.api.Shutdownable;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
@ -25,15 +33,7 @@ import io.nosqlbench.api.engine.activityimpl.ActivityDef;
|
||||
import io.nosqlbench.api.errors.BasicError;
|
||||
import io.nosqlbench.api.errors.OpConfigError;
|
||||
import io.nosqlbench.engine.api.activityapi.planning.OpSequence;
|
||||
import io.nosqlbench.adapters.api.activityconfig.OpsLoader;
|
||||
import io.nosqlbench.adapters.api.activityconfig.yaml.OpTemplate;
|
||||
import io.nosqlbench.adapters.api.activityconfig.yaml.OpsDocList;
|
||||
import io.nosqlbench.adapters.api.activityimpl.OpDispenser;
|
||||
import io.nosqlbench.adapters.api.activityimpl.OpMapper;
|
||||
import io.nosqlbench.engine.api.activityimpl.SimpleActivity;
|
||||
import io.nosqlbench.adapters.api.activityimpl.uniform.decorators.SyntheticOpTemplateProvider;
|
||||
import io.nosqlbench.adapters.api.activityimpl.uniform.flowtypes.Op;
|
||||
import io.nosqlbench.adapters.api.templating.ParsedOp;
|
||||
import io.nosqlbench.nb.annotations.ServiceSelector;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -152,6 +152,7 @@ public class StandardActivity<R extends Op, S> extends SimpleActivity implements
|
||||
setDefaultsFromOpSequence(sequence);
|
||||
}
|
||||
|
||||
|
||||
public OpSequence<OpDispenser<? extends Op>> getOpSequence() {
|
||||
return sequence;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ public class StandardAction<A extends StandardActivity<R, ?>, R extends Op> impl
|
||||
while (op != null) {
|
||||
|
||||
int tries = 0;
|
||||
while (tries++ < maxTries) {
|
||||
while (++tries < maxTries) {
|
||||
Throwable error = null;
|
||||
long startedAt = System.nanoTime();
|
||||
|
||||
|
@ -46,7 +46,7 @@ public class NBCLIScenarioParser {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger("SCENARIOS");
|
||||
private static final String SEARCH_IN = "activities";
|
||||
public static final String WORKLOAD_SCENARIO_STEP = "WORKLOAD_SCENARIO_STEP";
|
||||
public static final String WORKLOAD_SCENARIO_STEP = "STEP";
|
||||
|
||||
public static boolean isFoundWorkload(String workload, String... includes) {
|
||||
Optional<Content<?>> found = NBIO.all()
|
||||
@ -190,8 +190,9 @@ public class NBCLIScenarioParser {
|
||||
buildingCmd.put("alias", "alias=" + WORKLOAD_SCENARIO_STEP);
|
||||
}
|
||||
|
||||
// TODO: simplify this
|
||||
String alias = buildingCmd.get("alias");
|
||||
for (String token : new String[]{"WORKLOAD", "SCENARIO", "STEP"}) {
|
||||
for (String token : new String[]{"STEP"}) {
|
||||
if (!alias.contains(token)) {
|
||||
logger.warn("Your alias template '" + alias + "' does not contain " + token + ", which will " +
|
||||
"cause your metrics to be combined under the same name. It is strongly advised that you " +
|
||||
@ -206,6 +207,7 @@ public class NBCLIScenarioParser {
|
||||
alias = alias.replaceAll("STEP", sanitize(stepName));
|
||||
alias = (alias.startsWith("alias=") ? alias : "alias=" + alias);
|
||||
buildingCmd.put("alias", alias);
|
||||
buildingCmd.put("labels","labels=workload:"+sanitize(workloadToken));
|
||||
|
||||
logger.debug(() -> "rebuilt command: " + String.join(" ", buildingCmd.values()));
|
||||
buildCmdBuffer.addAll(buildingCmd.values());
|
||||
@ -219,7 +221,12 @@ public class NBCLIScenarioParser {
|
||||
public static String sanitize(String word) {
|
||||
String sanitized = word;
|
||||
sanitized = sanitized.replaceAll("\\..+$", "");
|
||||
sanitized = sanitized.replaceAll("[^a-zA-Z0-9]+", "");
|
||||
sanitized = sanitized.replaceAll("-","_");
|
||||
sanitized = sanitized.replaceAll("[^a-zA-Z0-9_]+", "");
|
||||
|
||||
if (!word.equals(sanitized)) {
|
||||
logger.warn("The identifier or value '" + word + "' was sanitized to '" + sanitized + "' to be compatible with monitoring systems. You should probably change this to make diagnostics easier.");
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
@ -326,7 +333,7 @@ public class NBCLIScenarioParser {
|
||||
|
||||
OpsDocList stmts = null;
|
||||
try {
|
||||
stmts = OpsLoader.loadContent(content, Map.of());
|
||||
stmts = OpsLoader.loadContent(content, new LinkedHashMap<>());
|
||||
if (stmts.getStmtDocs().size() == 0) {
|
||||
logger.warn("Encountered yaml with no docs in '" + referenced + "'");
|
||||
continue;
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package io.nosqlbench.engine.cli;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.nosqlbench.api.annotations.Annotation;
|
||||
import io.nosqlbench.api.annotations.Layer;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
@ -84,6 +86,8 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
|
||||
private NBLabels labels;
|
||||
private String sessionName;
|
||||
private String sessionCode;
|
||||
private long sessionTime;
|
||||
|
||||
public NBCLI(final String commandName) {
|
||||
this.commandName = commandName;
|
||||
@ -95,7 +99,7 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
* for scenario encapsulation and concurrent testing.
|
||||
*
|
||||
* @param args
|
||||
* Command Line Args
|
||||
* Command Line Args
|
||||
*/
|
||||
public static void main(final String[] args) {
|
||||
try {
|
||||
@ -151,23 +155,30 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
// logger = LogManager.getLogger("NBCLI");
|
||||
|
||||
NBCLI.loggerConfig.setConsoleLevel(NBLogLevel.ERROR);
|
||||
|
||||
this.sessionTime = System.currentTimeMillis();
|
||||
final NBCLIOptions globalOptions = new NBCLIOptions(args, Mode.ParseGlobalsOnly);
|
||||
this.labels=NBLabels.forKV("command",commandName).and(globalOptions.getLabelMap());
|
||||
this.sessionName = SessionNamer.format(globalOptions.getSessionName());
|
||||
|
||||
this.sessionCode = SystemId.genSessionCode(sessionTime);
|
||||
this.sessionName = SessionNamer.format(globalOptions.getSessionName(),sessionTime).replaceAll("SESSIONCODE",sessionCode);
|
||||
this.labels = NBLabels.forKV("command", commandName, "appname", "nosqlbench")
|
||||
.andInstances("node",SystemId.getNodeId())
|
||||
.andInstances("nodeid",SystemId.getPackedNodeId())
|
||||
// .andInstances("sesscode",sessionCode)
|
||||
.andInstances("session",sessionName)
|
||||
.and(globalOptions.getLabelMap());
|
||||
|
||||
NBCLI.loggerConfig
|
||||
.setSessionName(sessionName)
|
||||
.setConsoleLevel(globalOptions.getConsoleLogLevel())
|
||||
.setConsolePattern(globalOptions.getConsoleLoggingPattern())
|
||||
.setLogfileLevel(globalOptions.getScenarioLogLevel())
|
||||
.setLogfilePattern(globalOptions.getLogfileLoggingPattern())
|
||||
.setLoggerLevelOverrides(globalOptions.getLogLevelOverrides())
|
||||
.setMaxLogs(globalOptions.getLogsMax())
|
||||
.setLogsDirectory(globalOptions.getLogsDirectory())
|
||||
.setAnsiEnabled(globalOptions.isEnableAnsi())
|
||||
.setDedicatedVerificationLogger(globalOptions.isDedicatedVerificationLogger())
|
||||
.activate();
|
||||
.setSessionName(sessionName)
|
||||
.setConsoleLevel(globalOptions.getConsoleLogLevel())
|
||||
.setConsolePattern(globalOptions.getConsoleLoggingPattern())
|
||||
.setLogfileLevel(globalOptions.getScenarioLogLevel())
|
||||
.setLogfilePattern(globalOptions.getLogfileLoggingPattern())
|
||||
.setLoggerLevelOverrides(globalOptions.getLogLevelOverrides())
|
||||
.setMaxLogs(globalOptions.getLogsMax())
|
||||
.setLogsDirectory(globalOptions.getLogsDirectory())
|
||||
.setAnsiEnabled(globalOptions.isEnableAnsi())
|
||||
.setDedicatedVerificationLogger(globalOptions.isDedicatedVerificationLogger())
|
||||
.activate();
|
||||
ConfigurationFactory.setConfigurationFactory(NBCLI.loggerConfig);
|
||||
|
||||
NBCLI.logger = LogManager.getLogger("NBCLI");
|
||||
@ -210,17 +221,16 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
final String reportPromPushTo = globalOptions.wantsReportPromPushTo();
|
||||
|
||||
|
||||
|
||||
final int mOpts = (dockerMetrics ? 1 : 0)
|
||||
+ ((null != dockerMetricsAt) ? 1 : 0)
|
||||
+ ((null != reportGraphiteTo) ? 1 : 0);
|
||||
+ ((null != dockerMetricsAt) ? 1 : 0)
|
||||
+ ((null != reportGraphiteTo) ? 1 : 0);
|
||||
|
||||
if ((1 < mOpts) && ((null == reportGraphiteTo) || (null == annotatorsConfig)))
|
||||
throw new BasicError("You have multiple conflicting options which attempt to set\n" +
|
||||
" the destination for metrics and annotations. Please select only one of\n" +
|
||||
" --docker-metrics, --docker-metrics-at <addr>, or other options like \n" +
|
||||
" --report-graphite-to <addr> and --annotators <config>\n" +
|
||||
" For more details, see run 'nb help docker-metrics'");
|
||||
" the destination for metrics and annotations. Please select only one of\n" +
|
||||
" --docker-metrics, --docker-metrics-at <addr>, or other options like \n" +
|
||||
" --report-graphite-to <addr> and --annotators <config>\n" +
|
||||
" For more details, see run 'nb help docker-metrics'");
|
||||
|
||||
String graphiteMetricsAddress = null;
|
||||
|
||||
@ -229,28 +239,44 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
NBCLI.logger.info("Docker metrics is enabled. Docker must be installed for this to work");
|
||||
final DockerMetricsManager dmh = new DockerMetricsManager();
|
||||
final Map<String, String> dashboardOptions = Map.of(
|
||||
DockerMetricsManager.GRAFANA_TAG, globalOptions.getDockerGrafanaTag(),
|
||||
DockerMetricsManager.PROM_TAG, globalOptions.getDockerPromTag(),
|
||||
DockerMetricsManager.TSDB_RETENTION, String.valueOf(globalOptions.getDockerPromRetentionDays()),
|
||||
DockerMetricsManager.GRAPHITE_SAMPLE_EXPIRY, "10m",
|
||||
DockerMetricsManager.GRAPHITE_CACHE_SIZE, "5000",
|
||||
DockerMetricsManager.GRAPHITE_LOG_LEVEL, globalOptions.getGraphiteLogLevel(),
|
||||
DockerMetricsManager.GRAPHITE_LOG_FORMAT, "logfmt"
|
||||
DockerMetricsManager.GRAFANA_TAG, globalOptions.getDockerGrafanaTag(),
|
||||
DockerMetricsManager.PROM_TAG, globalOptions.getDockerPromTag(),
|
||||
DockerMetricsManager.TSDB_RETENTION, String.valueOf(globalOptions.getDockerPromRetentionDays()),
|
||||
DockerMetricsManager.GRAPHITE_SAMPLE_EXPIRY, "10m",
|
||||
DockerMetricsManager.GRAPHITE_CACHE_SIZE, "5000",
|
||||
DockerMetricsManager.GRAPHITE_LOG_LEVEL, globalOptions.getGraphiteLogLevel(),
|
||||
DockerMetricsManager.GRAPHITE_LOG_FORMAT, "logfmt"
|
||||
|
||||
);
|
||||
dmh.startMetrics(dashboardOptions);
|
||||
final String warn = "Docker Containers are started, for grafana and prometheus, hit" +
|
||||
" these urls in your browser: http://<host>:3000 and http://<host>:9090";
|
||||
" these urls in your browser: http://<host>:3000 and http://<host>:9090";
|
||||
NBCLI.logger.warn(warn);
|
||||
graphiteMetricsAddress = "localhost";
|
||||
} else if (null != dockerMetricsAt) graphiteMetricsAddress = dockerMetricsAt;
|
||||
} else if (null != dockerMetricsAt) {
|
||||
graphiteMetricsAddress = dockerMetricsAt;
|
||||
}
|
||||
|
||||
if (null != graphiteMetricsAddress) {
|
||||
reportGraphiteTo = graphiteMetricsAddress + ":9109";
|
||||
annotatorsConfig = "[{type:'log',level:'info'},{type:'grafana',baseurl:'http://" + graphiteMetricsAddress + ":3000" +
|
||||
"/'," +
|
||||
"tags:'appname:nosqlbench',timeoutms:5000,onerror:'warn'}]";
|
||||
} else annotatorsConfig = "[{type:'log',level:'info'}]";
|
||||
if (annotatorsConfig == null || annotatorsConfig.isBlank()) {
|
||||
List<Map<String, String>> annotatorsConfigs = new ArrayList<>();
|
||||
annotatorsConfigs.add(Map.of(
|
||||
"type", "log",
|
||||
"level", "info"
|
||||
));
|
||||
|
||||
if (null != graphiteMetricsAddress) {
|
||||
reportGraphiteTo = graphiteMetricsAddress + ":9109";
|
||||
annotatorsConfigs.add(Map.of(
|
||||
"type", "grafana",
|
||||
"baseurl", "http://" + graphiteMetricsAddress + ":3000",
|
||||
"tags", "appname:nosqlbench",
|
||||
"timeoutms", "5000",
|
||||
"onerror", "warn"
|
||||
));
|
||||
}
|
||||
Gson gson = new GsonBuilder().create();
|
||||
annotatorsConfig = gson.toJson(annotatorsConfigs);
|
||||
}
|
||||
|
||||
final NBCLIOptions options = new NBCLIOptions(args);
|
||||
NBCLI.logger = LogManager.getLogger("NBCLI");
|
||||
@ -313,19 +339,19 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
NBCLI.logger.debug(() -> "user requests to copy out " + resourceToCopy);
|
||||
|
||||
Optional<Content<?>> tocopy = NBIO.classpath()
|
||||
.searchPrefixes("activities")
|
||||
.searchPrefixes(options.wantsIncludes())
|
||||
.pathname(resourceToCopy).extensionSet(RawOpsLoader.YAML_EXTENSIONS).first();
|
||||
.searchPrefixes("activities")
|
||||
.searchPrefixes(options.wantsIncludes())
|
||||
.pathname(resourceToCopy).extensionSet(RawOpsLoader.YAML_EXTENSIONS).first();
|
||||
|
||||
if (tocopy.isEmpty()) tocopy = NBIO.classpath()
|
||||
.searchPrefixes().searchPrefixes(options.wantsIncludes())
|
||||
.searchPrefixes(options.wantsIncludes())
|
||||
.pathname(resourceToCopy).first();
|
||||
.searchPrefixes().searchPrefixes(options.wantsIncludes())
|
||||
.searchPrefixes(options.wantsIncludes())
|
||||
.pathname(resourceToCopy).first();
|
||||
|
||||
final Content<?> data = tocopy.orElseThrow(
|
||||
() -> new BasicError(
|
||||
"Unable to find " + resourceToCopy +
|
||||
" in classpath to copy out")
|
||||
() -> new BasicError(
|
||||
"Unable to find " + resourceToCopy +
|
||||
" in classpath to copy out")
|
||||
);
|
||||
|
||||
final Path writeTo = Path.of(data.asPath().getFileName().toString());
|
||||
@ -363,7 +389,7 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
if (options.wantsTopicalHelp()) {
|
||||
final Optional<String> helpDoc = MarkdownFinder.forHelpTopic(options.wantsTopicalHelpFor());
|
||||
System.out.println(helpDoc.orElseThrow(
|
||||
() -> new RuntimeException("No help could be found for " + options.wantsTopicalHelpFor())
|
||||
() -> new RuntimeException("No help could be found for " + options.wantsTopicalHelpFor())
|
||||
));
|
||||
return NBCLI.EXIT_OK;
|
||||
}
|
||||
@ -378,12 +404,12 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
NBCLI.logger.debug("initializing annotators with config:'{}'", annotatorsConfig);
|
||||
Annotators.init(annotatorsConfig);
|
||||
Annotators.recordAnnotation(
|
||||
Annotation.newBuilder()
|
||||
.session(sessionName)
|
||||
.now()
|
||||
.layer(Layer.CLI)
|
||||
.detail("cli", String.join("\n", args))
|
||||
.build()
|
||||
Annotation.newBuilder()
|
||||
.element(this)
|
||||
.now()
|
||||
.layer(Layer.Session)
|
||||
.detail("cli", String.join("\n", args))
|
||||
.build()
|
||||
);
|
||||
|
||||
if ((null != reportPromPushTo) || (null != reportGraphiteTo) || (null != options.wantsReportCsvTo())) {
|
||||
@ -410,13 +436,13 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
}
|
||||
|
||||
for (
|
||||
final LoggerConfigData histoLogger : options.getHistoLoggerConfigs())
|
||||
final LoggerConfigData histoLogger : options.getHistoLoggerConfigs())
|
||||
ActivityMetrics.addHistoLogger(sessionName, histoLogger.pattern, histoLogger.file, histoLogger.interval);
|
||||
for (
|
||||
final LoggerConfigData statsLogger : options.getStatsLoggerConfigs())
|
||||
final LoggerConfigData statsLogger : options.getStatsLoggerConfigs())
|
||||
ActivityMetrics.addStatsLogger(sessionName, statsLogger.pattern, statsLogger.file, statsLogger.interval);
|
||||
for (
|
||||
final LoggerConfigData classicConfigs : options.getClassicHistoConfigs())
|
||||
final LoggerConfigData classicConfigs : options.getClassicHistoConfigs())
|
||||
ActivityMetrics.addClassicHistos(sessionName, classicConfigs.pattern, classicConfigs.file, classicConfigs.interval);
|
||||
|
||||
// intentionally not shown for warn-only
|
||||
@ -429,21 +455,21 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
}
|
||||
|
||||
final Scenario scenario = new Scenario(
|
||||
sessionName,
|
||||
options.getScriptFile(),
|
||||
options.getScriptingEngine(),
|
||||
options.getProgressSpec(),
|
||||
options.wantsStackTraces(),
|
||||
options.wantsCompileScript(),
|
||||
options.getReportSummaryTo(),
|
||||
String.join("\n", args),
|
||||
options.getLogsDirectory(),
|
||||
Maturity.Unspecified,
|
||||
this);
|
||||
sessionName,
|
||||
options.getScriptFile(),
|
||||
options.getScriptingEngine(),
|
||||
options.getProgressSpec(),
|
||||
options.wantsStackTraces(),
|
||||
options.wantsCompileScript(),
|
||||
options.getReportSummaryTo(),
|
||||
String.join("\n", args),
|
||||
options.getLogsDirectory(),
|
||||
Maturity.Unspecified,
|
||||
this);
|
||||
|
||||
final ScriptBuffer buffer = new BasicScriptBuffer()
|
||||
.add(options.getCommands()
|
||||
.toArray(new Cmd[0]));
|
||||
.add(options.getCommands()
|
||||
.toArray(new Cmd[0]));
|
||||
final String scriptData = buffer.getParsedScript();
|
||||
|
||||
if (options.wantsShowScript()) {
|
||||
|
@ -16,20 +16,19 @@
|
||||
|
||||
package io.nosqlbench.engine.cli;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabelSpec;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.engine.util.Unit;
|
||||
import io.nosqlbench.api.errors.BasicError;
|
||||
import io.nosqlbench.api.logging.NBLogLevel;
|
||||
import io.nosqlbench.api.system.NBEnvironment;
|
||||
import io.nosqlbench.api.system.NBStatePath;
|
||||
import io.nosqlbench.engine.api.metrics.IndicatorMode;
|
||||
import io.nosqlbench.engine.cli.Cmd.CmdType;
|
||||
import io.nosqlbench.engine.core.lifecycle.scenario.Scenario.Engine;
|
||||
import io.nosqlbench.nb.annotations.Maturity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@ -44,14 +43,13 @@ public class NBCLIOptions {
|
||||
|
||||
|
||||
private static final String NB_STATE_DIR = "--statedir";
|
||||
private static final String NB_STATEDIR_PATHS = "$NBSTATEDIR:$PWD/.nosqlbench:$HOME/.nosqlbench";
|
||||
public static final String ARGS_FILE_DEFAULT = "$NBSTATEDIR/argsfile";
|
||||
private static final String INCLUDE = "--include";
|
||||
|
||||
private static final String userHome = System.getProperty("user.home");
|
||||
|
||||
|
||||
private static final Map<String,String> DEFAULT_LABELS=Map.of("appname","nosqlbench");
|
||||
private static final Map<String, String> DEFAULT_LABELS = Map.of("appname", "nosqlbench");
|
||||
private static final String METRICS_PREFIX = "--metrics-prefix";
|
||||
private static final String ANNOTATE_EVENTS = "--annotate";
|
||||
private static final String ANNOTATORS_CONFIG = "--annotators";
|
||||
@ -136,7 +134,7 @@ public class NBCLIOptions {
|
||||
// private static final String DEFAULT_CONSOLE_LOGGING_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";
|
||||
|
||||
|
||||
private final Map<String,String> labels = new LinkedHashMap<>(DEFAULT_LABELS);
|
||||
private NBLabels labels = NBLabels.forKV();
|
||||
private final List<Cmd> cmdList = new ArrayList<>();
|
||||
private int logsMax;
|
||||
private boolean wantsVersionShort;
|
||||
@ -151,7 +149,8 @@ public class NBCLIOptions {
|
||||
private int reportInterval = 10;
|
||||
private String metricsPrefix = "nosqlbench";
|
||||
private String wantsMetricsForActivity;
|
||||
private String sessionName = "";
|
||||
private String sessionName = "SESSIONCODE";
|
||||
// private String sessionName = "scenario_%tY%tm%td_%tH%tM%tS_%tL";
|
||||
private boolean showScript;
|
||||
private NBLogLevel consoleLevel = NBLogLevel.WARN;
|
||||
private final List<String> histoLoggerConfigs = new ArrayList<>();
|
||||
@ -185,9 +184,8 @@ public class NBCLIOptions {
|
||||
private String[] annotateEvents = {"ALL"};
|
||||
private String dockerMetricsHost;
|
||||
private String annotatorsConfig = "";
|
||||
private String statedirs = NBCLIOptions.NB_STATEDIR_PATHS;
|
||||
private String statedirs = NBStatePath.NB_STATEDIR_PATHS;
|
||||
private Path statepath;
|
||||
private final List<String> statePathAccesses = new ArrayList<>();
|
||||
private final String hdrForChartFileName = NBCLIOptions.DEFAULT_CHART_HDR_LOG_NAME;
|
||||
private String dockerPromRetentionDays = "3650d";
|
||||
private String reportSummaryTo = NBCLIOptions.REPORT_SUMMARY_TO_DEFAULT;
|
||||
@ -210,8 +208,8 @@ public class NBCLIOptions {
|
||||
return this.annotatorsConfig;
|
||||
}
|
||||
|
||||
public Map<String,String> getLabelMap() {
|
||||
return Collections.unmodifiableMap(this.labels);
|
||||
public NBLabels getLabelMap() {
|
||||
return this.labels;
|
||||
}
|
||||
|
||||
public String getChartHdrFileName() {
|
||||
@ -305,8 +303,7 @@ public class NBCLIOptions {
|
||||
nonincludes.addLast(arglist.removeFirst());
|
||||
}
|
||||
}
|
||||
statedirs = (null != this.statedirs) ? statedirs : NBCLIOptions.NB_STATEDIR_PATHS;
|
||||
setStatePath();
|
||||
this.statepath = NBStatePath.initialize(statedirs);
|
||||
|
||||
arglist = nonincludes;
|
||||
nonincludes = new LinkedList<>();
|
||||
@ -336,7 +333,7 @@ public class NBCLIOptions {
|
||||
case NBCLIArgsFile.ARGS_FILE_REQUIRED:
|
||||
case NBCLIArgsFile.ARGS_PIN:
|
||||
case NBCLIArgsFile.ARGS_UNPIN:
|
||||
if (null == this.statepath) this.setStatePath();
|
||||
this.statepath = NBStatePath.initialize(statedirs);
|
||||
arglist = argsfile.process(arglist);
|
||||
break;
|
||||
case NBCLIOptions.ANSI:
|
||||
@ -487,63 +484,13 @@ public class NBCLIOptions {
|
||||
}
|
||||
|
||||
private void setLabels(String labeldata) {
|
||||
this.labels.clear();
|
||||
this.labels = NBLabels.forKV();
|
||||
addLabels(labeldata);
|
||||
}
|
||||
|
||||
private void addLabels(String labeldata) {
|
||||
Map<String,String> newLabels = parseLabels(labeldata);
|
||||
this.labels.putAll(newLabels);
|
||||
}
|
||||
|
||||
private Map<String, String> parseLabels(String labeldata) {
|
||||
Map<String,String> setLabelsTo = new LinkedHashMap<>();
|
||||
for (String component : labeldata.split("[,; ]")) {
|
||||
String[] parts = component.split("\\W", 2);
|
||||
if (parts.length!=2) {
|
||||
throw new BasicError("Unable to parse labels to set:" + labeldata);
|
||||
}
|
||||
setLabelsTo.put(parts[0],parts[1]);
|
||||
}
|
||||
return setLabelsTo;
|
||||
}
|
||||
|
||||
|
||||
private Path setStatePath() {
|
||||
if (0 < statePathAccesses.size())
|
||||
throw new BasicError("The state dir must be set before it is used by other\n" +
|
||||
" options. If you want to change the statedir, be sure you do it before\n" +
|
||||
" dependent options. These parameters were called before this --statedir:\n" +
|
||||
this.statePathAccesses.stream().map(s -> "> " + s).collect(Collectors.joining("\n")));
|
||||
if (null != this.statepath) return statepath;
|
||||
|
||||
final List<String> paths = NBEnvironment.INSTANCE.interpolateEach(":", this.statedirs);
|
||||
Path selected = null;
|
||||
|
||||
for (final String pathName : paths) {
|
||||
final Path path = Path.of(pathName);
|
||||
if (Files.exists(path)) {
|
||||
if (Files.isDirectory(path)) {
|
||||
selected = path;
|
||||
break;
|
||||
}
|
||||
System.err.println("ERROR: possible state dir path is not a directory: '" + path + '\'');
|
||||
}
|
||||
}
|
||||
if (null == selected) selected = Path.of(paths.get(paths.size() - 1));
|
||||
|
||||
if (!Files.exists(selected)) try {
|
||||
Files.createDirectories(
|
||||
selected,
|
||||
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwx---"))
|
||||
);
|
||||
} catch (final IOException e) {
|
||||
throw new BasicError("Could not create state directory at '" + selected + "': " + e.getMessage());
|
||||
}
|
||||
|
||||
NBEnvironment.INSTANCE.put(NBEnvironment.NBSTATEDIR, selected.toString());
|
||||
|
||||
return selected;
|
||||
NBLabels newLabels = NBLabelSpec.parseLabels(labeldata);
|
||||
this.labels = this.labels.and(newLabels);
|
||||
}
|
||||
|
||||
private void parseAllOptions(final String[] args) {
|
||||
@ -846,14 +793,10 @@ public class NBCLIOptions {
|
||||
|
||||
public String getProgressSpec() {
|
||||
final ProgressSpec spec = this.parseProgressSpec(progressSpec);// sanity check
|
||||
// System.err.println("Console is already logging info or more, so progress data on console is " +
|
||||
// "suppressed.");
|
||||
if (IndicatorMode.console == spec.indicatorMode)
|
||||
if (consoleLevel.isGreaterOrEqualTo(NBLogLevel.INFO)) spec.indicatorMode = IndicatorMode.logonly;
|
||||
else // System.err.println("Command line includes script calls, so progress data on console is " +
|
||||
// "suppressed.");
|
||||
if (cmdList.stream().anyMatch(cmd -> CmdType.script == cmd.getCmdType()))
|
||||
spec.indicatorMode = IndicatorMode.logonly;
|
||||
else if (cmdList.stream().anyMatch(cmd -> CmdType.script == cmd.getCmdType()))
|
||||
spec.indicatorMode = IndicatorMode.logonly;
|
||||
return spec.toString();
|
||||
}
|
||||
|
||||
|
@ -103,10 +103,11 @@ public class NBCLIScenarioParserTest {
|
||||
List<Cmd> cmds = opts.getCommands();
|
||||
assertThat(cmds.size()).isEqualTo(1);
|
||||
assertThat(cmds.get(0).getParams()).isEqualTo(Map.of(
|
||||
"alias", "scenariotest_templatetest_withtemplate",
|
||||
"alias", "with_template",
|
||||
"cycles", "20",
|
||||
"cycles-test", "20",
|
||||
"driver", "stdout",
|
||||
"labels","workload:scenario_test",
|
||||
"workload", "scenario-test"
|
||||
));
|
||||
}
|
||||
@ -117,9 +118,10 @@ public class NBCLIScenarioParserTest {
|
||||
List<Cmd> cmds = opts.getCommands();
|
||||
assertThat(cmds.size()).isEqualTo(1);
|
||||
assertThat(cmds.get(0).getParams()).isEqualTo(Map.of(
|
||||
"alias", "scenariotest_schemaonly_schema",
|
||||
"alias", "schema",
|
||||
"cycles-test", "20",
|
||||
"driver", "stdout",
|
||||
"labels","workload:scenario_test",
|
||||
"tags", "block:\"schema.*\"",
|
||||
"workload", "scenario-test"
|
||||
));
|
||||
@ -158,7 +160,7 @@ public class NBCLIScenarioParserTest {
|
||||
@Test
|
||||
public void testSanitizer() {
|
||||
String sanitized = NBCLIScenarioParser.sanitize("A-b,c_d");
|
||||
assertThat(sanitized).isEqualTo("Abcd");
|
||||
assertThat(sanitized).isEqualTo("A_bc_d");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -167,9 +169,10 @@ public class NBCLIScenarioParserTest {
|
||||
List<Cmd> cmds = opts.getCommands();
|
||||
assertThat(cmds.size()).isEqualTo(1);
|
||||
assertThat(cmds.get(0).getParams()).isEqualTo(Map.of(
|
||||
"alias", "scenariotest_schemaonly_schema",
|
||||
"alias", "schema",
|
||||
"cycles-test", "20",
|
||||
"driver", "stdout",
|
||||
"labels","workload:scenario_test",
|
||||
"tags", "block:\"schema.*\"",
|
||||
"workload", "scenario-test"
|
||||
));
|
||||
|
@ -33,12 +33,6 @@
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.nosqlbench</groupId>
|
||||
<artifactId>engine-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.nosqlbench</groupId>
|
||||
<artifactId>nb-api</artifactId>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -126,6 +126,8 @@ public class GrafanaClientConfig {
|
||||
|
||||
private URI makeUri(String pathAndQuery) {
|
||||
try {
|
||||
String baseUri = getBaseUri().toString();
|
||||
baseUri = (!baseUri.endsWith("/")&&!pathAndQuery.startsWith("/")) ? baseUri+"/" : baseUri;
|
||||
return new URI(getBaseUri().toString() + pathAndQuery);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -145,13 +147,16 @@ public class GrafanaClientConfig {
|
||||
|
||||
public GrafanaClientConfig setBaseUri(String baseuri) {
|
||||
try {
|
||||
baseuri = baseuri.endsWith("/") ? baseuri : baseuri + "/";
|
||||
URI uri = new URI(baseuri);
|
||||
String userinfo = uri.getRawUserInfo();
|
||||
if (userinfo != null) {
|
||||
String[] unpw = userinfo.split(":");
|
||||
basicAuth(unpw[0], unpw.length == 2 ? unpw[1] : "");
|
||||
uri = new URI(baseuri.replace(userinfo + "@", ""));
|
||||
String expanded = baseuri.replace(userinfo + "@", "");
|
||||
uri = new URI(expanded);
|
||||
}
|
||||
|
||||
this.baseUrl = uri;
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.engine.clients.grafana;
|
||||
|
||||
import io.nosqlbench.api.apps.BundledApp;
|
||||
import io.nosqlbench.api.system.NBEnvironment;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@Service(value = BundledApp.class,selector = "grafana-apikey")
|
||||
@CommandLine.Command(
|
||||
name="gafana-apikey",
|
||||
description = "create and cache a grafana apikey for a given grafana server"
|
||||
)
|
||||
public class GrafanaTokenAuthenticator implements BundledApp {
|
||||
|
||||
@CommandLine.Parameters
|
||||
private final String keyfile = NBEnvironment.INSTANCE.get("apikeyfile");
|
||||
@Override
|
||||
public int applyAsInt(String[] value) {
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,12 +21,12 @@ import io.nosqlbench.api.annotations.Annotator;
|
||||
import io.nosqlbench.api.config.params.ParamsParser;
|
||||
import io.nosqlbench.api.config.standard.*;
|
||||
import io.nosqlbench.api.errors.BasicError;
|
||||
import io.nosqlbench.api.errors.OnError;
|
||||
import io.nosqlbench.api.metadata.SystemId;
|
||||
import io.nosqlbench.engine.clients.grafana.GrafanaClient;
|
||||
import io.nosqlbench.engine.clients.grafana.GrafanaClientConfig;
|
||||
import io.nosqlbench.engine.clients.grafana.transfer.GAnnotation;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import io.nosqlbench.api.errors.OnError;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -36,6 +36,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service(value = Annotator.class, selector = "grafana")
|
||||
public class GrafanaMetricsAnnotator implements Annotator, NBConfigurable {
|
||||
@ -58,9 +59,12 @@ public class GrafanaMetricsAnnotator implements Annotator, NBConfigurable {
|
||||
ga.setTime(annotation.getStart());
|
||||
ga.setTimeEnd(annotation.getEnd());
|
||||
|
||||
annotation.getLabels().forEach((k, v) -> {
|
||||
annotation.getLabels().onlyTypes().asMap().forEach((k, v) -> {
|
||||
ga.getTags().add(k + ":" + v);
|
||||
});
|
||||
annotation.getLabels().onlyInstances().asMap().forEach((k,v)->{
|
||||
ga.addText(" " + k + ":" + v);
|
||||
});
|
||||
ga.getTags().add("layer:" + annotation.getLayer().toString());
|
||||
|
||||
if (annotation.getStart() == annotation.getEnd()) {
|
||||
@ -69,15 +73,12 @@ public class GrafanaMetricsAnnotator implements Annotator, NBConfigurable {
|
||||
ga.getTags().add("span:interval");
|
||||
}
|
||||
|
||||
Map<String, String> labels = annotation.getLabels();
|
||||
Map<String, String> labels = annotation.getLabels().asMap();
|
||||
|
||||
Optional.ofNullable(labels.get("alertId"))
|
||||
.map(Integer::parseInt).ifPresent(ga::setAlertId);
|
||||
|
||||
ga.setText(annotation.toString());
|
||||
|
||||
annotation.getSession();
|
||||
|
||||
ga.addText(annotation.toString());
|
||||
|
||||
// Target
|
||||
Optional.ofNullable(labels.get("type"))
|
||||
@ -175,7 +176,8 @@ public class GrafanaMetricsAnnotator implements Annotator, NBConfigurable {
|
||||
public NBConfigModel getConfigModel() {
|
||||
return ConfigModel.of(this.getClass())
|
||||
.add(Param.required("baseurl", String.class)
|
||||
.setDescription("The base url of the grafana node, like http://localhost:3000/"))
|
||||
.setDescription("The base url of the grafana node, like http://localhost:3000/")
|
||||
.setRegex(Pattern.compile("http.+\\\\/")))
|
||||
.add(Param.defaultTo("apikeyfile", "$NBSTATEDIR/grafana/grafana_apikey")
|
||||
.setDescription("The file that contains the api key, supersedes apikey"))
|
||||
.add(Param.optional("apikey", String.class)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -121,8 +121,8 @@ public class GAnnotation {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
public void addText(String text) {
|
||||
this.text = this.text == null ? text : this.text + "\n" + text;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -36,7 +36,7 @@ public class GrafanaClientTest {
|
||||
client.getConfig().basicAuth("admin", "admin");
|
||||
GAnnotation a = new GAnnotation();
|
||||
a.setDashboardId(2);
|
||||
a.setText("testingAnnotation");
|
||||
a.addText("testingAnnotation");
|
||||
GAnnotation created = client.createAnnotation(a);
|
||||
logger.info(created);
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.engine.clients.grafana.annotator;
|
||||
|
||||
import io.nosqlbench.api.annotations.Annotation;
|
||||
import io.nosqlbench.api.annotations.Layer;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.system.NBStatePath;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GrafanaMetricsAnnotatorTest implements NBLabeledElement {
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testPost() {
|
||||
String ipaddr="CHANGEME";
|
||||
NBStatePath.initialize();
|
||||
GrafanaMetricsAnnotator ganno = new GrafanaMetricsAnnotator();
|
||||
ganno.applyConfig(ganno.getConfigModel().apply(Map.of(
|
||||
"baseurl","http://"+ipaddr+":3000/",
|
||||
"tags","appname:nosqlbench",
|
||||
"timeoutms","5000",
|
||||
"onerror","warn"
|
||||
)));
|
||||
ganno.recordAnnotation(Annotation.newBuilder()
|
||||
.element(this)
|
||||
.now()
|
||||
.layer(Layer.Session)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return NBLabels.forMap(Map.of("testlabelname","testlabelvalue"));
|
||||
}
|
||||
}
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package io.nosqlbench.engine.core.lifecycle.activity;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.engine.api.activityapi.core.*;
|
||||
import io.nosqlbench.engine.api.activityimpl.MotorState;
|
||||
import io.nosqlbench.api.annotations.Annotation;
|
||||
@ -51,7 +53,7 @@ import java.util.stream.Collectors;
|
||||
* This allows the state tracking to work consistently for all observers.</p>
|
||||
*/
|
||||
|
||||
public class ActivityExecutor implements ActivityController, ParameterMap.Listener, ProgressCapable, Callable<ExecutionResult> {
|
||||
public class ActivityExecutor implements NBLabeledElement, ActivityController, ParameterMap.Listener, ProgressCapable, Callable<ExecutionResult> {
|
||||
|
||||
// TODO Encapsulate valid state transitions to be only modifiable within the appropriate type view.
|
||||
|
||||
@ -99,12 +101,9 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
|
||||
logger.info(() -> "stopped: " + this.getActivityDef().getAlias() + " with " + motors.size() + " slots");
|
||||
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(sessionId)
|
||||
.element(this)
|
||||
.interval(this.startedAt, this.stoppedAt)
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", getActivityDef().getAlias())
|
||||
.label("driver", getActivityDef().getActivityType())
|
||||
.label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none"))
|
||||
.detail("params", getActivityDef().toString())
|
||||
.build()
|
||||
);
|
||||
@ -126,12 +125,9 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
|
||||
logger.info(() -> "stopped: " + this.getActivityDef().getAlias() + " with " + motors.size() + " slots");
|
||||
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(sessionId)
|
||||
.element(this)
|
||||
.interval(this.startedAt, this.stoppedAt)
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", getActivityDef().getAlias())
|
||||
.label("driver", getActivityDef().getActivityType())
|
||||
.label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none"))
|
||||
.detail("params", getActivityDef().toString())
|
||||
.build()
|
||||
);
|
||||
@ -505,12 +501,9 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
|
||||
|
||||
logger.info(() -> "starting activity " + activity.getAlias() + " for cycles " + activity.getCycleSummary());
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(sessionId)
|
||||
.element(this)
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", getActivityDef().getAlias())
|
||||
.label("driver", getActivityDef().getActivityType())
|
||||
.label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none"))
|
||||
.detail("params", getActivityDef().toString())
|
||||
.build()
|
||||
);
|
||||
@ -533,4 +526,8 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return activity.getLabels();
|
||||
}
|
||||
}
|
||||
|
@ -80,10 +80,9 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return this.parentComponent.getLabels().and("scenario", this.scenarioName);
|
||||
return this.parentComponent.getLabels().andTypes("scenario", this.scenarioName);
|
||||
}
|
||||
|
||||
public enum State {
|
||||
@ -139,18 +138,9 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
}
|
||||
|
||||
public static Scenario forTesting(final String name, final Engine engine, final String reportSummaryTo, final Maturity minMaturity) {
|
||||
return new Scenario(name, null, engine, "console:10s", true, true, reportSummaryTo, "", Path.of("logs"), minMaturity, NBLabeledElement.forKV("test-name", "name"));
|
||||
return new Scenario(name, null, engine, "console:10s", true, true, reportSummaryTo, "", Path.of("logs"), minMaturity, NBLabeledElement.forKV("test_name", "name"));
|
||||
}
|
||||
|
||||
// public Scenario(final String name, final Engine engine, final String reportSummaryTo, final Maturity minMaturity) {
|
||||
// scenarioName = name;
|
||||
// this.reportSummaryTo = reportSummaryTo;
|
||||
// this.engine = engine;
|
||||
// commandLine = "";
|
||||
// this.minMaturity = minMaturity;
|
||||
// logsPath = Path.of("logs");
|
||||
// }
|
||||
//
|
||||
public Scenario setLogger(final Logger logger) {
|
||||
this.logger = logger;
|
||||
return this;
|
||||
@ -259,7 +249,7 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
this.startedAtMillis = System.currentTimeMillis();
|
||||
Annotators.recordAnnotation(
|
||||
Annotation.newBuilder()
|
||||
.session(scenarioName)
|
||||
.element(this)
|
||||
.now()
|
||||
.layer(Layer.Scenario)
|
||||
.detail("engine", engine.toString())
|
||||
@ -358,10 +348,10 @@ public class Scenario implements Callable<ExecutionMetricsResult>, NBLabeledElem
|
||||
|
||||
// We report the scenario state via annotation even for short runs
|
||||
final Annotation annotation = Annotation.newBuilder()
|
||||
.session(scenarioName)
|
||||
.element(this)
|
||||
.interval(startedAtMillis, this.endedAtMillis)
|
||||
.layer(Layer.Scenario)
|
||||
.label("state", state.toString())
|
||||
// .labels("state", state.toString())
|
||||
.detail("command_line", commandLine)
|
||||
.build();
|
||||
|
||||
|
@ -68,15 +68,6 @@ public class ScenarioController implements NBLabeledElement {
|
||||
* @param activityDef string in alias=value1;driver=value2;... format
|
||||
*/
|
||||
public synchronized void start(ActivityDef activityDef) {
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(scenario.getScenarioName())
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", activityDef.getAlias())
|
||||
.detail("command", "start")
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
doStartActivity(activityDef);
|
||||
}
|
||||
|
||||
@ -84,6 +75,14 @@ public class ScenarioController implements NBLabeledElement {
|
||||
private synchronized ActivityRuntimeInfo doStartActivity(ActivityDef activityDef) {
|
||||
if (!this.activityInfoMap.containsKey(activityDef.getAlias())) {
|
||||
Activity activity = this.activityLoader.loadActivity(activityDef, this);
|
||||
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.element(activity)
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
ActivityExecutor executor = new ActivityExecutor(activity, this.scenario.getScenarioName());
|
||||
Future<ExecutionResult> startedActivity = activitiesExecutor.submit(executor);
|
||||
ActivityRuntimeInfo activityRuntimeInfo = new ActivityRuntimeInfo(activity, startedActivity, executor);
|
||||
@ -125,14 +124,6 @@ public class ScenarioController implements NBLabeledElement {
|
||||
* @param activityDef A definition for an activity to run
|
||||
*/
|
||||
public synchronized void run(ActivityDef activityDef, long timeoutMs) {
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(this.scenario.getScenarioName())
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", activityDef.getAlias())
|
||||
.detail("command", "run")
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
doStartActivity(activityDef);
|
||||
awaitActivity(activityDef, timeoutMs);
|
||||
@ -179,14 +170,6 @@ public class ScenarioController implements NBLabeledElement {
|
||||
* @param activityDef An activity def, including at least the alias parameter.
|
||||
*/
|
||||
public synchronized void stop(ActivityDef activityDef) {
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(this.scenario.getScenarioName())
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", activityDef.getAlias())
|
||||
.detail("command", "stop")
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
ActivityRuntimeInfo runtimeInfo = this.activityInfoMap.get(activityDef.getAlias());
|
||||
if (null == runtimeInfo) {
|
||||
@ -196,6 +179,14 @@ public class ScenarioController implements NBLabeledElement {
|
||||
scenariologger.debug("STOP {}", activityDef.getAlias());
|
||||
|
||||
runtimeInfo.stopActivity();
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.element(runtimeInfo.getActivity())
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.detail("command", "stop")
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,20 +231,21 @@ public class ScenarioController implements NBLabeledElement {
|
||||
* @param activityDef An activity def, including at least the alias parameter.
|
||||
*/
|
||||
public synchronized void forceStop(ActivityDef activityDef) {
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.session(this.scenario.getScenarioName())
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.label("alias", activityDef.getAlias())
|
||||
.detail("command", "forceStop")
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
ActivityRuntimeInfo runtimeInfo = this.activityInfoMap.get(activityDef.getAlias());
|
||||
|
||||
if (null == runtimeInfo) {
|
||||
throw new RuntimeException("could not force stop missing activity:" + activityDef);
|
||||
}
|
||||
|
||||
Annotators.recordAnnotation(Annotation.newBuilder()
|
||||
.element(runtimeInfo.getActivity())
|
||||
.now()
|
||||
.layer(Layer.Activity)
|
||||
.detail("command", "forceStop")
|
||||
.detail("params", activityDef.toString())
|
||||
.build());
|
||||
|
||||
scenariologger.debug("FORCE STOP {}", activityDef.getAlias());
|
||||
|
||||
runtimeInfo.forceStopActivity();
|
||||
|
@ -66,6 +66,7 @@ public class LoggerConfig extends ConfigurationFactory {
|
||||
* we squelch them to some reasonable level so they aren't a nuisance.
|
||||
*/
|
||||
public static Map<String, Level> BUILTIN_OVERRIDES = Map.of(
|
||||
// ERROR StatusConsoleListener Unable to locate appender "SCENARIO_APPENDER" for logger config "oshi.util"
|
||||
"oshi.util", Level.INFO
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,6 +16,9 @@
|
||||
|
||||
package io.nosqlbench.api.annotations;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -30,11 +33,7 @@ import java.util.Map;
|
||||
* NoSQLBench. It is up to the downstream consumers to map these
|
||||
* to concrete fields or identifiers as appropriate.
|
||||
*/
|
||||
public interface Annotation {
|
||||
/**
|
||||
* @return The named session that the annotation is associated with
|
||||
*/
|
||||
String getSession();
|
||||
public interface Annotation extends NBLabeledElement {
|
||||
|
||||
/**
|
||||
* If this is the same as {@link #getEnd()}, then the annotation is
|
||||
@ -74,7 +73,7 @@ public interface Annotation {
|
||||
*
|
||||
* @return The labels map
|
||||
*/
|
||||
Map<String, String> getLabels();
|
||||
NBLabels getLabels();
|
||||
|
||||
/**
|
||||
* The details are an ordered map of all the content that you would want the user to see.
|
||||
@ -83,15 +82,15 @@ public interface Annotation {
|
||||
*/
|
||||
Map<String, String> getDetails();
|
||||
|
||||
static AnnotationBuilderFacets.WantsSession newBuilder() {
|
||||
static AnnotationBuilderFacets.WantsLabeledElement newBuilder() {
|
||||
return new AnnotationBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should return {@link Span#interval} if the span of time is not an instant, and
|
||||
* {@link Span#instant}, otherwise.
|
||||
* This should return {@link Temporal#interval} if the span of time is not an instant, and
|
||||
* {@link Temporal#instant}, otherwise.
|
||||
*/
|
||||
Span getSpan();
|
||||
Temporal getTemporal();
|
||||
|
||||
String asJson();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,6 +16,8 @@
|
||||
|
||||
package io.nosqlbench.api.annotations;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.TimeZone;
|
||||
@ -24,15 +26,15 @@ public class AnnotationBuilder implements AnnotationBuilderFacets.All {
|
||||
private String session;
|
||||
private long start;
|
||||
private long end;
|
||||
private final LinkedHashMap<String, String> labels = new LinkedHashMap<>();
|
||||
private final LinkedHashMap<String, String> details = new LinkedHashMap<>();
|
||||
private Layer layer;
|
||||
private final TimeZone timezone = TimeZone.getTimeZone(ZoneId.of("GMT"));
|
||||
|
||||
private NBLabeledElement element;
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder layer(Layer layer) {
|
||||
this.layer = layer;
|
||||
this.label("layer", layer.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -68,12 +70,6 @@ public class AnnotationBuilder implements AnnotationBuilderFacets.All {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder label(String name, String value) {
|
||||
this.labels.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationBuilderFacets.WantsMoreDetailsOrBuild detail(String name, String value) {
|
||||
this.details.put(name, value);
|
||||
@ -82,14 +78,13 @@ public class AnnotationBuilder implements AnnotationBuilderFacets.All {
|
||||
|
||||
@Override
|
||||
public Annotation build() {
|
||||
return new MutableAnnotation(timezone, session, layer, start, end, labels, details).asReadOnly();
|
||||
return new MutableAnnotation(timezone, session, layer, start, end, element, details).asReadOnly();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationBuilderFacets.WantsInterval session(String session) {
|
||||
this.session = session;
|
||||
public AnnotationBuilderFacets.WantsInterval element(NBLabeledElement element) {
|
||||
this.element = element;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,17 +16,19 @@
|
||||
|
||||
package io.nosqlbench.api.annotations;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
|
||||
public interface AnnotationBuilderFacets {
|
||||
|
||||
interface All extends
|
||||
WantsSession, WantsInterval, WantsLayer, WantsLabels, WantsMoreDetailsOrBuild, WantsMoreLabelsOrDetails {
|
||||
WantsLabeledElement, WantsInterval, WantsLayer, WantsMoreDetailsOrBuild {
|
||||
}
|
||||
|
||||
interface WantsSession {
|
||||
interface WantsLabeledElement {
|
||||
/**
|
||||
* The session is the global name of a NoSQLBench process which run a scenario. It is required.
|
||||
*/
|
||||
WantsInterval session(String session);
|
||||
WantsInterval element(NBLabeledElement element);
|
||||
}
|
||||
|
||||
interface WantsInterval {
|
||||
@ -50,22 +52,11 @@ public interface AnnotationBuilderFacets {
|
||||
}
|
||||
|
||||
interface WantsLayer {
|
||||
WantsMoreLabelsOrDetails layer(Layer layer);
|
||||
}
|
||||
|
||||
interface WantsLabels {
|
||||
WantsMoreLabelsOrDetails label(String name, String value);
|
||||
}
|
||||
|
||||
interface WantsMoreLabelsOrDetails {
|
||||
WantsMoreLabelsOrDetails label(String name, String value);
|
||||
|
||||
WantsMoreDetailsOrBuild detail(String name, String value);
|
||||
WantsMoreDetailsOrBuild layer(Layer layer);
|
||||
}
|
||||
|
||||
interface WantsMoreDetailsOrBuild {
|
||||
WantsMoreDetailsOrBuild detail(String name, String value);
|
||||
|
||||
Annotation build();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,7 +22,7 @@ public enum Layer {
|
||||
* Events which describe command line arguments, such as parsing,
|
||||
* named scenario mapping, or critical errors
|
||||
*/
|
||||
CLI,
|
||||
Session,
|
||||
|
||||
/**
|
||||
* Events which describe scenario execution, such as parameters,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,12 +19,16 @@ package io.nosqlbench.api.annotations;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class MutableAnnotation implements Annotation {
|
||||
|
||||
@ -41,13 +45,11 @@ public class MutableAnnotation implements Annotation {
|
||||
@Expose
|
||||
private long end = 0L;
|
||||
|
||||
@Expose
|
||||
private Map<String, String> labels = new LinkedHashMap<>();
|
||||
|
||||
@Expose
|
||||
private Map<String, String> details = new LinkedHashMap<>();
|
||||
|
||||
private final ZoneId zoneid = ZoneId.of("GMT");
|
||||
private NBLabeledElement element;
|
||||
|
||||
public MutableAnnotation(
|
||||
TimeZone timezone,
|
||||
@ -55,50 +57,40 @@ public class MutableAnnotation implements Annotation {
|
||||
Layer layer,
|
||||
long start,
|
||||
long end,
|
||||
LinkedHashMap<String, String> labels,
|
||||
NBLabeledElement element,
|
||||
LinkedHashMap<String, String> details) {
|
||||
setLabels(labels);
|
||||
setElement(element);
|
||||
setSession(session);
|
||||
setLayer(layer);
|
||||
setStart(start);
|
||||
setEnd(end);
|
||||
setDetails(details);
|
||||
labels.put("appname", "nosqlbench");
|
||||
}
|
||||
|
||||
private void setElement(NBLabeledElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
public void setSession(String sessionName) {
|
||||
this.session = sessionName;
|
||||
this.labels.put("session", sessionName);
|
||||
}
|
||||
|
||||
public void setStart(long intervalStart) {
|
||||
this.start = intervalStart;
|
||||
this.labels.put("span", getSpan().toString());
|
||||
}
|
||||
|
||||
public void setEnd(long intervalEnd) {
|
||||
this.end = intervalEnd;
|
||||
this.labels.put("span", getSpan().toString());
|
||||
}
|
||||
|
||||
public void setLabels(Map<String, String> labels) {
|
||||
this.labels = labels;
|
||||
}
|
||||
|
||||
public void setLayer(Layer layer) {
|
||||
this.layer = layer;
|
||||
this.labels.put("layer", layer.toString());
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStart() {
|
||||
return start;
|
||||
@ -115,11 +107,8 @@ public class MutableAnnotation implements Annotation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getLabels() {
|
||||
// if (!labels.containsKey("span")) {
|
||||
// labels.put("span",getSpan().toString());
|
||||
// }
|
||||
return labels;
|
||||
public NBLabels getLabels() {
|
||||
return element.getLabels();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,7 +119,6 @@ public class MutableAnnotation implements Annotation {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("session: ").append(getSession()).append("\n");
|
||||
|
||||
ZonedDateTime startTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(getStart()), zoneid);
|
||||
ZonedDateTime endTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(getStart()), zoneid);
|
||||
@ -144,17 +132,17 @@ public class MutableAnnotation implements Annotation {
|
||||
}
|
||||
sb.append("]\n");
|
||||
|
||||
sb.append("span:").append(getSpan()).append("\n");
|
||||
sb.append("span:").append(getTemporal()).append("\n");
|
||||
sb.append("details:\n");
|
||||
formatMap(sb, getDetails());
|
||||
sb.append("labels:\n");
|
||||
formatMap(sb, getLabels());
|
||||
formatMap(sb, getLabels().asMap());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void formatMap(StringBuilder sb, Map<String, String> details) {
|
||||
details.forEach((k, v) -> {
|
||||
sb.append(" ").append(k).append(": ");
|
||||
sb.append(" ").append(k).append(":");
|
||||
if (v.contains("\n")) {
|
||||
sb.append("\n");
|
||||
|
||||
@ -164,7 +152,7 @@ public class MutableAnnotation implements Annotation {
|
||||
}
|
||||
// Arrays.stream(lines).sequential().map(s -> " "+s+"\n").forEach(sb::append);
|
||||
} else {
|
||||
sb.append(v).append("\n");
|
||||
sb.append(" ").append(v).append("\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -173,8 +161,8 @@ public class MutableAnnotation implements Annotation {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span getSpan() {
|
||||
return (getStart() == getEnd()) ? Span.instant : Span.interval;
|
||||
public Temporal getTemporal() {
|
||||
return (getStart() == getEnd()) ? Temporal.instant : Temporal.interval;
|
||||
}
|
||||
|
||||
public String asJson() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
package io.nosqlbench.api.annotations;
|
||||
|
||||
public enum Span {
|
||||
public enum Temporal {
|
||||
/**
|
||||
* A span of time of size zero.
|
||||
*/
|
@ -16,33 +16,65 @@
|
||||
|
||||
package io.nosqlbench.api.config;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MapLabels implements NBLabels {
|
||||
private final Map<String,String> labels;
|
||||
private String[] instanceFields = new String[0];
|
||||
|
||||
public MapLabels(final Map<String, String> labels) {
|
||||
public MapLabels(final Map<String, String> labels, String... instanceFields) {
|
||||
verifyValidNamesAndValues(labels);
|
||||
// verifyValidValues(labels);
|
||||
this.labels = Collections.unmodifiableMap(labels);
|
||||
this.instanceFields = instanceFields;
|
||||
}
|
||||
|
||||
public MapLabels(final Map<String,String> parentLabels, final Map<String,String> childLabels) {
|
||||
final Map<String,String> combined = new LinkedHashMap<>();
|
||||
parentLabels.forEach(combined::put);
|
||||
|
||||
public MapLabels(final Map<String,String> parentLabels, final Map<String,String> childLabels, String... instanceFields) {
|
||||
final Map<String, String> combined = new LinkedHashMap<>(parentLabels);
|
||||
childLabels.forEach((k,v) -> {
|
||||
if (combined.containsKey(k))
|
||||
throw new RuntimeException("Can't overlap label keys (for instance " + k + ") between parent and child elements. parent:" + parentLabels + ", child:" + childLabels);
|
||||
combined.put(k,v);
|
||||
});
|
||||
verifyValidNamesAndValues(combined);
|
||||
// verifyValidValues(combined);
|
||||
this.instanceFields = instanceFields;
|
||||
labels=Collections.unmodifiableMap(combined);
|
||||
}
|
||||
|
||||
private final Pattern validNamesPattern = Pattern.compile("^[a-zA-Z_][a-zA-Z0-9_]+");
|
||||
private void verifyValidNamesAndValues(Map<String, String> labels) {
|
||||
labels.forEach((label,value) -> {
|
||||
if (!validNamesPattern.matcher(label).matches()) {
|
||||
throw new RuntimeException("Invalid label name '" + label + "', only a-z,A-Z,_ are allowed as the initial character, and a-z,A-Z,0-9,_ are allowed after.");
|
||||
}
|
||||
// if (!validNamesPattern.matcher(value).matches()) {
|
||||
// throw new RuntimeException("Invalid label value '" + value + "', only a-z,A-Z,_ are allowed as the initial character, and a-z,A-Z,0-9,_ are allowed after.");
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyValidValues(Map<String, String> labels) {
|
||||
for (String value : labels.values()) {
|
||||
if (!validNamesPattern.matcher(value).matches()) {
|
||||
throw new RuntimeException("Invalid label value '" + value + "', only a-z,A-Z,_ are allowed as the initial character, and a-z,A-Z,0-9,_ are allowed after.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String linearizeValues(final char delim, final String... included) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final List<String> includedNames = new ArrayList<>();
|
||||
if (0 < included.length) Collections.addAll(includedNames, included);
|
||||
else this.labels.keySet().forEach(includedNames::add);
|
||||
else includedNames.addAll(this.labels.keySet());
|
||||
|
||||
for (String includedName : includedNames) {
|
||||
final boolean optional= includedName.startsWith("[") && includedName.endsWith("]");
|
||||
@ -65,7 +97,7 @@ public class MapLabels implements NBLabels {
|
||||
|
||||
final List<String> includedNames = new ArrayList<>();
|
||||
if (0 < included.length) Collections.addAll(includedNames, included);
|
||||
else this.labels.keySet().forEach(includedNames::add);
|
||||
else includedNames.addAll(this.labels.keySet());
|
||||
String rawName = null;
|
||||
if (null != bareName) {
|
||||
rawName = this.labels.get(bareName);
|
||||
@ -92,14 +124,28 @@ public class MapLabels implements NBLabels {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels and(final String... labelsAndValues) {
|
||||
if (0 != (labelsAndValues.length % 2))
|
||||
throw new RuntimeException("Must provide even number of keys and values: " + Arrays.toString(labelsAndValues));
|
||||
final Map<String,String> childLabels = new LinkedHashMap<>();
|
||||
for (int i = 0; i < labelsAndValues.length; i+=2) childLabels.put(labelsAndValues[i], labelsAndValues[i + 1]);
|
||||
public MapLabels andTypes(final String... labelsAndValues) {
|
||||
final Map<String, String> childLabels = getStringStringMap(labelsAndValues);
|
||||
return new MapLabels(labels,childLabels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapLabels and(NBLabels labels) {
|
||||
return new MapLabels(this.labels,labels.asMap(), concat(this.instanceFields,labels.getInstanceFields()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MapLabels andInstances(final String... labelsAndValues) {
|
||||
final Map<String, String> childLabels = getStringStringMap(labelsAndValues);
|
||||
String[] childInstanceFields = getNamesArray(labelsAndValues);
|
||||
return new MapLabels(this.labels,childLabels,concat(this.instanceFields,getNamesArray(labelsAndValues)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapLabels andInstances(Map<String, String> instanceLabelsAndValues) {
|
||||
return new MapLabels(this.labels,instanceLabelsAndValues,instanceLabelsAndValues.keySet().toArray(new String[0]));
|
||||
}
|
||||
@Override
|
||||
public NBLabels modifyName(final String nameToModify, final Function<String, String> transform) {
|
||||
if (!this.labels.containsKey(nameToModify))
|
||||
@ -134,7 +180,7 @@ public class MapLabels implements NBLabels {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String only(final String name) {
|
||||
public String valueOf(final String name) {
|
||||
if (!this.labels.containsKey(name))
|
||||
throw new RuntimeException("The specified key does not exist: '" + name + '\'');
|
||||
final String only = labels.get(name);
|
||||
@ -148,7 +194,55 @@ public class MapLabels implements NBLabels {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels and(final Map<String, String> moreLabels) {
|
||||
public NBLabels onlyTypes() {
|
||||
Map<String,String> typesOnlyMap = new LinkedHashMap<>(this.labels);
|
||||
for (String instanceField : this.instanceFields) {
|
||||
typesOnlyMap.remove(instanceField);
|
||||
}
|
||||
return new MapLabels(typesOnlyMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels onlyInstances() {
|
||||
Map<String,String> instancesOnlyMap = new LinkedHashMap<>();
|
||||
for (String instanceField : this.instanceFields) {
|
||||
instancesOnlyMap.put(instanceField,this.labels.get(instanceField));
|
||||
}
|
||||
return new MapLabels(instancesOnlyMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getInstanceFields() {
|
||||
return instanceFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels andTypes(final Map<String, String> moreLabels) {
|
||||
return new MapLabels(this.labels, moreLabels);
|
||||
}
|
||||
|
||||
private String[] concat(String[] a, String[] b) {
|
||||
String[] c = new String[a.length+b.length];
|
||||
System.arraycopy(a,0,c,0,a.length);
|
||||
System.arraycopy(b,0,c,a.length,b.length);
|
||||
return c;
|
||||
}
|
||||
|
||||
private static String[] getNamesArray(final String... labelsAndValues) {
|
||||
String[] keys = new String[labelsAndValues.length>>1];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
keys[i]=labelsAndValues[i<<1];
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
@NotNull
|
||||
private static Map<String, String> getStringStringMap(String[] labelsAndValues) {
|
||||
if (0 != (labelsAndValues.length % 2))
|
||||
throw new RuntimeException("Must provide even number of keys and values: " + Arrays.toString(labelsAndValues));
|
||||
final Map<String,String> childLabels = new LinkedHashMap<>();
|
||||
for (int i = 0; i < labelsAndValues.length; i+=2) childLabels.put(labelsAndValues[i], labelsAndValues[i + 1]);
|
||||
return childLabels;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import io.nosqlbench.api.errors.BasicError;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NBLabelSpec {
|
||||
|
||||
public static NBLabels parseLabels(String labeldata) {
|
||||
NBLabels buf = NBLabels.forKV();
|
||||
|
||||
Map<String, String> parsedLabels = new LinkedHashMap<>();
|
||||
for (String component : labeldata.split("[,; ]+")) {
|
||||
String[] parts = component.split("[:=]+", 2);
|
||||
if (parts.length != 2) {
|
||||
throw new BasicError("Unable to parse labels to set:" + labeldata);
|
||||
}
|
||||
if (parts[1].startsWith("#")) {
|
||||
buf = buf.and(NBLabels.forKV().andInstances(parts[0], parts[1].substring(1)));
|
||||
} else if (parts[1].startsWith("$")) {
|
||||
buf = buf.and(NBLabels.forKV().andTypes(parts[0], parts[1].substring(1)));
|
||||
} else {
|
||||
if (parts[1].matches(".*[0-9]+.*")) {
|
||||
throw new BasicError("You have specified an auxiliary tag which contains numbers in its value (" + component + "), but you have not designated it as a dimension, as in " + parts[0] + ":$" + parts[1] + " or an instance value, as in " + parts[0] + ":#" + parts[1]);
|
||||
}
|
||||
buf = buf.and(NBLabels.forKV().andTypes(parts[0], parts[1]));
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
}
|
@ -48,79 +48,4 @@ public interface NBLabeledElement extends NBComponent {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// NBLabeledElement EMPTY = forMap(Map.of());
|
||||
//
|
||||
// Map<String, String> getLabels();
|
||||
//
|
||||
// /**
|
||||
// * TODO: Should throw an error when new keys are duplicated
|
||||
// * @param keyvalues
|
||||
// * @return
|
||||
// */
|
||||
// default Map<String, String> getLabelsAnd(final String... keyvalues) {
|
||||
// final LinkedHashMap<String, String> map = new LinkedHashMap<>(this.getLabels());
|
||||
// for (int idx = 0; idx < keyvalues.length; idx+=2) map.put(keyvalues[idx], keyvalues[idx + 1]);
|
||||
// return map;
|
||||
// }
|
||||
//
|
||||
//// default NBLabeledElement and(String... keyvalues) {
|
||||
////
|
||||
//// }
|
||||
//
|
||||
// default Map<String, String> getLabelsAnd(final Map<String,String> extra) {
|
||||
// final LinkedHashMap<String,String> map = new LinkedHashMap<>(this.getLabels());
|
||||
// map.putAll(extra);
|
||||
// return map;
|
||||
// }
|
||||
//
|
||||
// static MapLabels forMap(final Map<String,String> labels) {
|
||||
// return new MapLabels(labels);
|
||||
// }
|
||||
//
|
||||
// class MapLabels implements NBLabeledElement {
|
||||
// private final Map<String, String> labels;
|
||||
//
|
||||
// public MapLabels(final Map<String,String> labels) {
|
||||
// this.labels = labels;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Map<String, String> getLabels() {
|
||||
// return this.labels;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Create a single String representation of the label set, preserving key order,
|
||||
// * with optional additional labels, in the form of:
|
||||
// * <pre>{@code
|
||||
// * key1:value1,key2:value2,...
|
||||
// * }</pre>
|
||||
// * @param and
|
||||
// * @return
|
||||
// */
|
||||
// default String linearizeLabels(final Map<String,String> and) {
|
||||
// final StringBuilder sb= new StringBuilder();
|
||||
// final Map<String, String> allLabels = getLabelsAnd(and);
|
||||
// final ArrayList<String> sortedLabels = new ArrayList<>(allLabels.keySet());
|
||||
// for (final String label : sortedLabels) sb.append(label).append(':').append(allLabels.get(label)).append(',');
|
||||
// sb.setLength(sb.length()-",".length());
|
||||
// return sb.toString();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Equivalent to {@link #linearizeLabels(Map)}, except that additional key-value pairs can
|
||||
// * be expressed as a pairs of Strings in the argument list.
|
||||
// * @param and - An even numbered list of strings as key1, value1, key2, value2, ...
|
||||
// * @return A linearized string representation
|
||||
// */
|
||||
// default String linearizeLabels(final String... and) {
|
||||
// return this.linearizeLabels(this.getLabelsAnd(and));
|
||||
// }
|
||||
//
|
||||
// default String linearizeLabelsByValueGraphite(final String... and) {
|
||||
// return this.linearizeLabelsByValueDelim(".",and);
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
@ -131,27 +131,29 @@ public interface NBLabels {
|
||||
* @param labelName The named label to modify
|
||||
* @param transform A Lambda which will modify the existing value.
|
||||
* @return A new NBLabels value, separate from the original
|
||||
* @@throws RuntimeException if either the key is not found or the values is null.
|
||||
* @throws RuntimeException if either the key is not found or the values is null.
|
||||
*/
|
||||
NBLabels modifyValue(String labelName, Function<String,String> transform);
|
||||
|
||||
/**
|
||||
* Create a new NBLabels value with the additional keys and values appended.
|
||||
*
|
||||
* @param labelsAndValues
|
||||
* @param typeLabelsAndValues
|
||||
* Keys and values in "key1", "value1", "key2", "value2", ... form
|
||||
* @return A new NBLabels instance
|
||||
*/
|
||||
NBLabels and(String... labelsAndValues);
|
||||
NBLabels andTypes(String... typeLabelsAndValues);
|
||||
|
||||
NBLabels and(NBLabels labels);
|
||||
/**
|
||||
* Create a new NBLabels value with the additional keys and values appended.
|
||||
*
|
||||
* @param labels
|
||||
* a map of keys and values
|
||||
* @param typeLabelsAndValues a map of keys and values
|
||||
* @return A new NBLabels instance
|
||||
*/
|
||||
NBLabels and(Map<String, String> labels);
|
||||
NBLabels andTypes(Map<String, String> typeLabelsAndValues);
|
||||
NBLabels andInstances(String... instanceLabelsAndValues);
|
||||
|
||||
NBLabels andInstances(Map<String,String> instanceLabelsAndValues);
|
||||
|
||||
/**
|
||||
* Return the value of the specified label key.
|
||||
@ -162,7 +164,7 @@ public interface NBLabels {
|
||||
* @throws RuntimeException
|
||||
* if the specified label does not exist in the set, or the value is null.
|
||||
*/
|
||||
String only(String name);
|
||||
String valueOf(String name);
|
||||
|
||||
/**
|
||||
* Return a map representation of the label set, regardless of the underlying form.
|
||||
@ -170,4 +172,13 @@ public interface NBLabels {
|
||||
* @return a {@link Map} of keys and values, in deterministic order
|
||||
*/
|
||||
Map<String, String> asMap();
|
||||
|
||||
/**
|
||||
* @return a new set of labels which includes only those which are not using per-instance semantics.
|
||||
*/
|
||||
NBLabels onlyTypes();
|
||||
NBLabels onlyInstances();
|
||||
|
||||
String[] getInstanceFields();
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
@ -106,7 +105,8 @@ public class ActivityMetrics {
|
||||
* @return the timer, perhaps a different one if it has already been registered
|
||||
*/
|
||||
public static Timer timer(NBLabeledElement parent, String metricFamilyName, int hdrdigits) {
|
||||
final NBLabels labels = parent.getLabels().and("name",metricFamilyName);
|
||||
final NBLabels labels = parent.getLabels().andTypes("name",sanitize(metricFamilyName));
|
||||
|
||||
|
||||
Timer registeredTimer = (Timer) register(labels, () ->
|
||||
new NBMetricTimer(labels,
|
||||
@ -134,7 +134,7 @@ public class ActivityMetrics {
|
||||
* @return the histogram, perhaps a different one if it has already been registered
|
||||
*/
|
||||
public static Histogram histogram(NBLabeledElement labeled, String metricFamilyName, int hdrdigits) {
|
||||
final NBLabels labels = labeled.getLabels().and("name", metricFamilyName);
|
||||
final NBLabels labels = labeled.getLabels().andTypes("name", sanitize(metricFamilyName));
|
||||
return (Histogram) register(labels, () ->
|
||||
new NBMetricHistogram(
|
||||
labels,
|
||||
@ -157,7 +157,7 @@ public class ActivityMetrics {
|
||||
* @return the counter, perhaps a different one if it has already been registered
|
||||
*/
|
||||
public static Counter counter(NBLabeledElement parent, String metricFamilyName) {
|
||||
final NBLabels labels = parent.getLabels().and("name",metricFamilyName);
|
||||
final NBLabels labels = parent.getLabels().andTypes("name",metricFamilyName);
|
||||
return (Counter) register(labels, () -> new NBMetricCounter(labels));
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ public class ActivityMetrics {
|
||||
* @return the meter, perhaps a different one if it has already been registered
|
||||
*/
|
||||
public static Meter meter(NBLabeledElement parent, String metricFamilyName) {
|
||||
final NBLabels labels = parent.getLabels().and("name",metricFamilyName);
|
||||
final NBLabels labels = parent.getLabels().andTypes("name",sanitize(metricFamilyName));
|
||||
return (Meter) register(labels, () -> new NBMetricMeter(labels));
|
||||
}
|
||||
|
||||
@ -191,7 +191,7 @@ public class ActivityMetrics {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Gauge<T> gauge(NBLabeledElement parent, String metricFamilyName, Gauge<T> gauge) {
|
||||
final NBLabels labels = parent.getLabels().and("name",metricFamilyName);
|
||||
final NBLabels labels = parent.getLabels().andTypes("name",sanitize(metricFamilyName));
|
||||
|
||||
return (Gauge<T>) register(labels, () -> new NBMetricGauge(labels,gauge));
|
||||
}
|
||||
@ -342,4 +342,16 @@ public class ActivityMetrics {
|
||||
.forEach(get()::remove);
|
||||
}
|
||||
|
||||
public static String sanitize(String word) {
|
||||
String sanitized = word;
|
||||
sanitized = sanitized.replaceAll("\\..+$", "");
|
||||
sanitized = sanitized.replaceAll("-","_");
|
||||
sanitized = sanitized.replaceAll("[^a-zA-Z0-9_]+", "");
|
||||
|
||||
if (!word.equals(sanitized)) {
|
||||
logger.warn("The identifier or value '" + word + "' was sanitized to '" + sanitized + "' to be compatible with monitoring systems. You should probably change this to make diagnostics easier.");
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ package io.nosqlbench.api.engine.metrics.reporters;
|
||||
import com.codahale.metrics.*;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
@ -34,8 +36,9 @@ import java.util.Map;
|
||||
* exposition format</a>
|
||||
*/
|
||||
|
||||
public enum PromExpositionFormat {
|
||||
;
|
||||
public class PromExpositionFormat {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger("METRICS");
|
||||
public static String format(final Clock clock, final Metric... metrics) {
|
||||
return PromExpositionFormat.format(clock, new StringBuilder(), metrics).toString();
|
||||
}
|
||||
@ -64,7 +67,7 @@ public enum PromExpositionFormat {
|
||||
|
||||
if (metric instanceof final Counting counting) {
|
||||
buffer.append("# TYPE ")
|
||||
.append(labels.modifyValue("name", n -> n+"_total").only("name")).append(" counter\n");
|
||||
.append(labels.modifyValue("name", n -> n+"_total").valueOf("name")).append(" counter\n");
|
||||
|
||||
final long count = counting.getCount();
|
||||
buffer
|
||||
@ -77,12 +80,12 @@ public enum PromExpositionFormat {
|
||||
}
|
||||
if (metric instanceof final Sampling sampling) {
|
||||
// Use the summary form
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append(" summary\n");
|
||||
buffer.append("# TYPE ").append(labels.valueOf("name")).append(" summary\n");
|
||||
final Snapshot snapshot = sampling.getSnapshot();
|
||||
for (final double quantile : new double[]{0.5, 0.75, 0.90, 0.95, 0.98, 0.99, 0.999}) {
|
||||
final double value = snapshot.getValue(quantile);
|
||||
buffer
|
||||
.append(labels.and("quantile", String.valueOf(quantile)).linearize("name"))
|
||||
.append(labels.andTypes("quantile", String.valueOf(quantile)).linearize("name"))
|
||||
.append(' ')
|
||||
.append(value)
|
||||
.append('\n');
|
||||
@ -92,25 +95,25 @@ public enum PromExpositionFormat {
|
||||
.append(' ')
|
||||
.append(snapshotCount)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append("_max").append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.valueOf("name")).append("_max").append(" gauge\n");
|
||||
final long maxValue = snapshot.getMax();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_max").linearize("name"))
|
||||
.append(' ')
|
||||
.append(maxValue)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_min").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_min").valueOf("name")).append(" gauge\n");
|
||||
final long minValue = snapshot.getMin();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_min").linearize("name"))
|
||||
.append(' ')
|
||||
.append(minValue)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_mean").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_mean").valueOf("name")).append(" gauge\n");
|
||||
final double meanValue = snapshot.getMean();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_mean").linearize("name"))
|
||||
.append(' ')
|
||||
.append(meanValue)
|
||||
.append('\n');
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_stdev").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_stdev").valueOf("name")).append(" gauge\n");
|
||||
final double stdDev = snapshot.getStdDev();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_stdev").linearize("name"))
|
||||
.append(' ')
|
||||
@ -119,7 +122,7 @@ public enum PromExpositionFormat {
|
||||
|
||||
}
|
||||
if (metric instanceof final Gauge gauge) {
|
||||
buffer.append("# TYPE ").append(labels.only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.valueOf("name")).append(" gauge\n");
|
||||
final Object value = gauge.getValue();
|
||||
if (value instanceof final Number number) {
|
||||
final double doubleValue = number.doubleValue();
|
||||
@ -143,28 +146,28 @@ public enum PromExpositionFormat {
|
||||
);
|
||||
}
|
||||
if (metric instanceof final Metered meter) {
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_1mRate").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_1mRate").valueOf("name")).append(" gauge\n");
|
||||
final double oneMinuteRate = meter.getOneMinuteRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_1mRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(oneMinuteRate)
|
||||
.append('\n');
|
||||
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_5mRate").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_5mRate").valueOf("name")).append(" gauge\n");
|
||||
final double fiveMinuteRate = meter.getFiveMinuteRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_5mRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(fiveMinuteRate)
|
||||
.append('\n');
|
||||
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_15mRate").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_15mRate").valueOf("name")).append(" gauge\n");
|
||||
final double fifteenMinuteRate = meter.getFifteenMinuteRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_15mRate").linearize("name"))
|
||||
.append(' ')
|
||||
.append(fifteenMinuteRate)
|
||||
.append('\n');
|
||||
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_meanRate").only("name")).append(" gauge\n");
|
||||
buffer.append("# TYPE ").append(labels.modifyValue("name",n->n+"_meanRate").valueOf("name")).append(" gauge\n");
|
||||
final double meanRate = meter.getMeanRate();
|
||||
buffer.append(labels.modifyValue("name",n->n+"_meanRate").linearize("name"))
|
||||
.append(' ')
|
||||
@ -176,8 +179,6 @@ public enum PromExpositionFormat {
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static String labels(final Map<String, String> labels, final String... additional) {
|
||||
@ -221,4 +222,16 @@ public enum PromExpositionFormat {
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitize(String word) {
|
||||
String sanitized = word;
|
||||
sanitized = sanitized.replaceAll("\\..+$", "");
|
||||
sanitized = sanitized.replaceAll("-","_");
|
||||
sanitized = sanitized.replaceAll("[^a-zA-Z0-9_]+", "");
|
||||
|
||||
if (!word.equals(sanitized)) {
|
||||
logger.warn("The identifier or value '" + word + "' was sanitized to '" + sanitized + "' to be compatible with monitoring systems. You should probably change this to make diagnostics easier.");
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -23,6 +23,7 @@ import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.hardware.NetworkIF;
|
||||
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@ -36,32 +37,37 @@ public class SystemId {
|
||||
* when you are managing configuration or results for a set of systems which
|
||||
* share a common IP addressing scheme. This identifier should be stable as long
|
||||
* as the node's addresses do not change.
|
||||
*
|
||||
* If you are needing an identifier for a node but wish to expose any address data,
|
||||
* <p>
|
||||
* If you are needing an identifier for a node but do not with wish to expose any address data,
|
||||
* you can use the {@link #getNodeFingerprint()} which takes this value and hashes
|
||||
* it with SHA-1 to produce a hex string.
|
||||
*
|
||||
* @return A address for the node, likely to be unique and stable for its lifetime
|
||||
*/
|
||||
public static String getNodeId() {
|
||||
return getMainInetAddrDirect().map(InetAddress::getHostAddress).orElse("UNKNOWN_HOST_ID");
|
||||
}
|
||||
|
||||
private static String getNodeIdOSHI() {
|
||||
SystemInfo sysinfo = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = sysinfo.getHardware();
|
||||
List<NetworkIF> interfaces = hal.getNetworkIFs();
|
||||
|
||||
Optional<String> first = interfaces.stream()
|
||||
.filter(i -> !i.getName().startsWith("docker" ))
|
||||
.filter(i -> !i.getName().equals("lo" ))
|
||||
.sorted((o1, o2) -> {
|
||||
if (o1.getName().startsWith("e" ) && o2.getName().startsWith("e" )) {
|
||||
return 0;
|
||||
}
|
||||
if (o1.getName().startsWith("e" )) {
|
||||
return -1;
|
||||
}
|
||||
if (o2.getName().startsWith("e" )) {
|
||||
return 1;
|
||||
}
|
||||
.filter(i -> !i.getName().startsWith("docker"))
|
||||
.filter(i -> !i.getName().equals("lo"))
|
||||
.sorted((o1, o2) -> {
|
||||
if (o1.getName().startsWith("e") && o2.getName().startsWith("e")) {
|
||||
return 0;
|
||||
})
|
||||
}
|
||||
if (o1.getName().startsWith("e")) {
|
||||
return -1;
|
||||
}
|
||||
if (o2.getName().startsWith("e")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
.flatMap(iface -> Arrays.stream(iface.getIPv4addr().clone()))
|
||||
.filter(addr -> !(addr.startsWith("127.")))
|
||||
.findFirst();
|
||||
@ -69,10 +75,51 @@ public class SystemId {
|
||||
return systemID;
|
||||
}
|
||||
|
||||
private static Optional<InetAddress> getMainInetAddrDirect() {
|
||||
List<NetworkInterface> ifaces = getInterfacesDirect();
|
||||
Optional<NetworkInterface> first = ifaces.stream()
|
||||
.filter(i -> !i.getName().startsWith("docker"))
|
||||
.filter(i -> !i.getName().equals("lo"))
|
||||
.sorted((o1, o2) -> {
|
||||
if (o1.getName().startsWith("e") && o2.getName().startsWith("e")) return 0;
|
||||
if (o1.getName().startsWith("e")) return -1;
|
||||
if (o2.getName().startsWith("e")) return 1;
|
||||
return 0;
|
||||
}).findFirst();
|
||||
if (first.isEmpty()) return Optional.empty();
|
||||
|
||||
Optional<InetAddress> firstInetAddrForInterface = first.get().getInterfaceAddresses().stream()
|
||||
.map(ia -> ia.getAddress())
|
||||
.sorted((i1, i2) -> {
|
||||
if (i1 instanceof Inet4Address && i2 instanceof Inet4Address) return 0;
|
||||
if (i1 instanceof Inet4Address) return -1;
|
||||
if (i2 instanceof Inet4Address) return 1;
|
||||
return 0;
|
||||
}).findFirst();
|
||||
return firstInetAddrForInterface;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Using this to bypass OSHI because it calls logger init before we want it.
|
||||
* TODO: Maybe remove OSHI altogether if there is a reasonable Java HAL view in current Java editions.
|
||||
*
|
||||
* @return a list of network interfaces
|
||||
*/
|
||||
private static List<NetworkInterface> getInterfacesDirect() {
|
||||
try {
|
||||
Enumeration<NetworkInterface> ni = NetworkInterface.getNetworkInterfaces();
|
||||
return new ArrayList<>(Collections.list(ni));
|
||||
} catch (SocketException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a stable string identifier consisting of hexadecimal characters.
|
||||
* The internal data used for this value is based on a stable ordering of non-local
|
||||
* ip addresses available on the system.
|
||||
*
|
||||
* @return A stable node identifier
|
||||
*/
|
||||
public static String getNodeFingerprint() {
|
||||
@ -81,9 +128,9 @@ public class SystemId {
|
||||
MessageDigest sha1_digest = MessageDigest.getInstance("SHA-1");
|
||||
byte[] addrBytes = sha1_digest.digest(addrId.getBytes(StandardCharsets.UTF_8));
|
||||
String fingerprint = "";
|
||||
for (int i=0; i < addrBytes.length; i++) {
|
||||
for (int i = 0; i < addrBytes.length; i++) {
|
||||
fingerprint +=
|
||||
Integer.toString( ( addrBytes[i] & 0xff ) + 0x100, 16).substring( 1 );
|
||||
Integer.toString((addrBytes[i] & 0xff) + 0x100, 16).substring(1);
|
||||
}
|
||||
return fingerprint.toUpperCase(Locale.ROOT);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
@ -125,4 +172,75 @@ public class SystemId {
|
||||
return gson.toJson(details);
|
||||
|
||||
}
|
||||
|
||||
private final static String radixSymbols = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~-"; // 64 symbols, for 2^6!
|
||||
private final int brailleStart = '⠀';
|
||||
private final int brailleEnd = '⣿';
|
||||
private final int brailleRadix = brailleEnd - brailleStart;
|
||||
|
||||
public static String getBrailleNodeId() {
|
||||
String nodeId = getNodeId();
|
||||
String[] fields = nodeId.split("\\.");
|
||||
byte[] addr;
|
||||
try {
|
||||
InetAddress inetAddr = Inet4Address.getByName(nodeId);
|
||||
addr = inetAddr.getAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return braille((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]);
|
||||
}
|
||||
|
||||
private static String braille(int value) {
|
||||
StringBuilder buf = new StringBuilder(4);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int mask = value & 0xF;
|
||||
value >>= 8;
|
||||
int charat = '⠀' + mask;
|
||||
buf.append((char) charat);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String braille(long value) {
|
||||
StringBuilder buf = new StringBuilder(8);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int mask = (int) value & 0xF;
|
||||
value >>= 8;
|
||||
int charat = '⠀' + mask;
|
||||
buf.append((char) charat);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String genSessionCode(long epochMillis) {
|
||||
return pack(epochMillis) + "_" + getPackedNodeId();
|
||||
}
|
||||
|
||||
public static String getPackedNodeId() {
|
||||
String nodeId = getNodeId();
|
||||
String[] fields = nodeId.split("\\.");
|
||||
byte[] addr;
|
||||
try {
|
||||
InetAddress inetAddr = Inet4Address.getByName(nodeId);
|
||||
addr = inetAddr.getAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return pack((addr[0] << 24) + (addr[1] << 16) + (addr[2] << 8) + addr[3]);
|
||||
}
|
||||
|
||||
public static String pack(long bitfield) {
|
||||
StringBuilder sb = new StringBuilder(11);
|
||||
while (bitfield > 0) {
|
||||
long tail = bitfield & 0b00111111;
|
||||
bitfield >>= 6;
|
||||
sb.append(radixSymbols.charAt((int) tail));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String genSessionBits() {
|
||||
return getBrailleNodeId() + ":" + braille(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.system;
|
||||
|
||||
import io.nosqlbench.api.errors.BasicError;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NBStatePath {
|
||||
|
||||
public static final String NB_STATEDIR_PATHS = "$NBSTATEDIR:$PWD/.nosqlbench:$HOME/.nosqlbench";
|
||||
|
||||
private static final List<String> statePathAccesses = new ArrayList<>();
|
||||
private static Path statepath;
|
||||
|
||||
public static Path initialize() {
|
||||
return initialize(NB_STATEDIR_PATHS);
|
||||
}
|
||||
public static Path initialize(String statedirs) {
|
||||
if (statedirs==null || statedirs.isBlank()) {
|
||||
statedirs = NB_STATEDIR_PATHS;
|
||||
}
|
||||
|
||||
if (statepath!=null) {
|
||||
return statepath;
|
||||
}
|
||||
|
||||
if (0 < statePathAccesses.size())
|
||||
throw new BasicError("The state dir must be set before it is used by other\n" +
|
||||
" options. If you want to change the statedir, be sure you do it before\n" +
|
||||
" dependent options. These parameters were called before this --statedir:\n" +
|
||||
statePathAccesses.stream().map(s -> "> " + s).collect(Collectors.joining("\n")));
|
||||
if (null != statepath) return statepath;
|
||||
|
||||
final List<String> paths = NBEnvironment.INSTANCE.interpolateEach(":", statedirs);
|
||||
Path selected = null;
|
||||
|
||||
for (final String pathName : paths) {
|
||||
final Path path = Path.of(pathName);
|
||||
if (Files.exists(path)) {
|
||||
if (Files.isDirectory(path)) {
|
||||
selected = path;
|
||||
break;
|
||||
}
|
||||
System.err.println("ERROR: possible state dir path is not a directory: '" + path + '\'');
|
||||
}
|
||||
}
|
||||
if (null == selected) selected = Path.of(paths.get(paths.size() - 1));
|
||||
|
||||
if (!Files.exists(selected)) try {
|
||||
Files.createDirectories(
|
||||
selected,
|
||||
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxrwx---"))
|
||||
);
|
||||
} catch (final IOException e) {
|
||||
throw new BasicError("Could not create state directory at '" + selected + "': " + e.getMessage());
|
||||
}
|
||||
|
||||
NBEnvironment.INSTANCE.put(NBEnvironment.NBSTATEDIR, selected.toString());
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
}
|
@ -21,14 +21,40 @@ import org.junit.jupiter.api.Test;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
public class MapLabelsTest {
|
||||
|
||||
@Test
|
||||
public void testLinearizeValues() {
|
||||
final MapLabels l1 = new MapLabels(Map.of("key-a", "value-a", "key-c", "value-c"));
|
||||
final String result = l1.linearizeValues('_', "key-a", "[key-b]", "key-c");
|
||||
assertThat(result).isEqualTo("value-a_value-c");
|
||||
final MapLabels l1 = new MapLabels(Map.of("key_a", "value_a", "key_c", "value_c"));
|
||||
final String result = l1.linearizeValues('_', "key_a", "[key_b]", "key_c");
|
||||
assertThat(result).isEqualTo("value_a_value_c");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstances() {
|
||||
final MapLabels l1 = new MapLabels(Map.of("key_a", "value_a", "key_c", "value_c"),"key_c");
|
||||
NBLabels typesOnly = l1.onlyTypes();
|
||||
assertThat(typesOnly.linearizeValues()).isEqualTo("value_a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstanceCombination() {
|
||||
final MapLabels l1 = new MapLabels(Map.of("key_a", "value_a"),Map.of("key_c", "value_c"),"key_c");
|
||||
final MapLabels l2 = new MapLabels(Map.of("key_dog", "value_dog"),Map.of( "key_cat", "value_cat"),"key_dog");
|
||||
final MapLabels l3 = l1.and(l2);
|
||||
assertThat(l3.linearizeValues()).matches("value_a.value_c.value_dog.value_cat");
|
||||
|
||||
assertThat(l3.onlyTypes().linearizeValues()).matches("value_a.value_cat");
|
||||
assertThat(l3.onlyInstances().linearizeValues()).matches("value_c.value_dog");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInvalidCharacters() {
|
||||
assertThatThrownBy(() -> new MapLabels(Map.of("a-b","c-d"))).isOfAnyClassIn(RuntimeException.class);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,28 +17,61 @@
|
||||
package io.nosqlbench.nb.api;
|
||||
|
||||
import io.nosqlbench.api.metadata.SystemId;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class SystemIdTest {
|
||||
private static final Logger logger = LogManager.getLogger(SystemIdTest.class);
|
||||
|
||||
@Test
|
||||
public void testHostInfo() {
|
||||
String info = SystemId.getHostSummary();
|
||||
System.out.println(info);
|
||||
String hostSummary = SystemId.getHostSummary();
|
||||
logger.info("host summary: " + hostSummary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNostId() {
|
||||
String info = SystemId.getNodeId();
|
||||
assertThat(info).matches("\\d+\\.\\d+\\.\\d+\\.\\d+");
|
||||
String nodeId = SystemId.getNodeId();
|
||||
assertThat(nodeId).matches("\\d+\\.\\d+\\.\\d+\\.\\d+");
|
||||
logger.info("node id: " + nodeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNodeFingerprint() {
|
||||
String hash = SystemId.getNodeFingerprint();
|
||||
assertThat(hash).matches("[A-Z0-9]+");
|
||||
String nodeFingerprint = SystemId.getNodeFingerprint();
|
||||
assertThat(nodeFingerprint).matches("[A-Z0-9]+");
|
||||
logger.info("node fingerprint: " + nodeFingerprint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrailleNodeId() {
|
||||
String brailleNodeId = SystemId.getBrailleNodeId();
|
||||
assertThat(brailleNodeId).matches("[⠀-⣿]{4}"); // note, that is not a space. It is the starting braille value of empty
|
||||
logger.info("braille node id: " + brailleNodeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackedNodeId() {
|
||||
String packedNodeId = SystemId.getPackedNodeId();
|
||||
assertThat(packedNodeId).matches("[0-9A-Za-z_-]+");
|
||||
logger.info("packed node id: " + packedNodeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenSessionCode() {
|
||||
String sessionCode=SystemId.genSessionCode(234L);
|
||||
assertThat(sessionCode).matches("[0-9a-zA-Z~-]+_[0-9a-zA-Z~-]+");
|
||||
logger.info("session code: " + sessionCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenSessionBits() {
|
||||
String sessionBits = SystemId.genSessionBits();
|
||||
assertThat(sessionBits).matches("[⠀-⣿]+:[⠀-⣿]+");
|
||||
logger.info("session bits: " + sessionBits);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,6 +18,7 @@ package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.api.annotations.Annotation;
|
||||
import io.nosqlbench.api.annotations.Layer;
|
||||
import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -30,36 +31,31 @@ public class AnnotationBuilderTest {
|
||||
public void testBasicAnnotation() {
|
||||
|
||||
Annotation an1 = Annotation.newBuilder()
|
||||
.session("test-session")
|
||||
.element(NBLabeledElement.forKV("test_element","value"))
|
||||
.at(time)
|
||||
.layer(Layer.Scenario)
|
||||
.label("labelka", "labelvb")
|
||||
.label("labelkc", "labelvd")
|
||||
.detail("detailk1", "detailv1")
|
||||
.detail("detailk2", "detailv21\ndetailv22")
|
||||
.detail("detailk3", "v1\nv2\nv3\n")
|
||||
.build();
|
||||
|
||||
String represented = an1.toString();
|
||||
assertThat(represented).isEqualTo("session: test-session\n" +
|
||||
"[2020-09-13T12:26:40Z]\n" +
|
||||
"span:instant\n" +
|
||||
"details:\n" +
|
||||
" detailk1: detailv1\n" +
|
||||
" detailk2: \n" +
|
||||
" detailv21\n" +
|
||||
" detailv22\n" +
|
||||
" detailk3: \n" +
|
||||
" v1\n" +
|
||||
" v2\n" +
|
||||
" v3\n" +
|
||||
"labels:\n" +
|
||||
" layer: Scenario\n" +
|
||||
" labelka: labelvb\n" +
|
||||
" labelkc: labelvd\n" +
|
||||
" session: test-session\n" +
|
||||
" span: instant\n" +
|
||||
" appname: nosqlbench\n");
|
||||
assertThat(represented).isEqualTo(
|
||||
"""
|
||||
[2020-09-13T12:26:40Z]
|
||||
span:instant
|
||||
details:
|
||||
detailk1: detailv1
|
||||
detailk2:
|
||||
detailv21
|
||||
detailv22
|
||||
detailk3:
|
||||
v1
|
||||
v2
|
||||
v3
|
||||
labels:
|
||||
test_element: value
|
||||
""");
|
||||
|
||||
}
|
||||
|
||||
|
56
nb-api/src/test/resources/log4j2.xml
Normal file
56
nb-api/src/test/resources/log4j2.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2022-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.
|
||||
-->
|
||||
|
||||
<Configuration status="debug" strict="true" name="XMLConfigTest">
|
||||
|
||||
<Filter type="ThresholdFilter" level="trace"/>
|
||||
|
||||
<Appenders>
|
||||
<Appender type="Console" name="STDOUT">
|
||||
<Layout type="PatternLayout" pattern="%7r %-5level [%t] %-12logger{0} %msg%n%throwable"/>
|
||||
<Filters>
|
||||
<Filter type="MarkerFilter" marker="FLOW" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="DENY" onMismatch="ACCEPT"/>
|
||||
</Filters>
|
||||
</Appender>
|
||||
<Appender type="Console" name="FLOW">
|
||||
<Layout type="PatternLayout" pattern="%C{1}.%M %m %ex%n"/><!-- class and line number -->
|
||||
<Filters>
|
||||
<Filter type="MarkerFilter" marker="FLOW" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
|
||||
<Filter type="MarkerFilter" marker="EXCEPTION" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Appender>
|
||||
<Appender type="File" name="APPSLOG" fileName="docs/apps.log">
|
||||
<Layout type="PatternLayout">
|
||||
<Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
|
||||
</Layout>
|
||||
</Appender>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
|
||||
<Logger name="io.nosqlbench.docsys" level="info" additivity="false">
|
||||
<AppenderRef ref="APPSLOG"/>
|
||||
</Logger>
|
||||
|
||||
<Root level="trace">
|
||||
<AppenderRef ref="STDOUT"/>
|
||||
</Root>
|
||||
|
||||
</Loggers>
|
||||
|
||||
</Configuration>
|
@ -158,7 +158,7 @@ public class ScriptExampleTests {
|
||||
"logs/histostats.csv"));
|
||||
String logdata = strings.stream().collect(Collectors.joining("\n"));
|
||||
assertThat(logdata).contains("min,p25,p50,p75,p90,p95,");
|
||||
assertThat(logdata.split("Tag=testhistostatslogger.cycles.servicetime,").length).isGreaterThanOrEqualTo(1);
|
||||
assertThat(logdata.split("Tag=testhistostatslogger.cycles_servicetime,").length).isGreaterThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -179,7 +179,7 @@ public class ScriptExampleTests {
|
||||
List<String> strings = Files.readAllLines(Paths.get("hdrhistodata.log"));
|
||||
String logdata = strings.stream().collect(Collectors.joining("\n"));
|
||||
assertThat(logdata).contains(",HIST");
|
||||
assertThat(logdata.split("Tag=testhistologger.cycles.servicetime,").length).isGreaterThanOrEqualTo(1);
|
||||
assertThat(logdata.split("Tag=testhistologger.cycles_servicetime,").length).isGreaterThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -241,8 +241,8 @@ public class ScriptExampleTests {
|
||||
@Test
|
||||
public void testReportedCoDelayStrict() {
|
||||
ExecutionMetricsResult scenarioResult = runScenario("cocycledelay_strict");
|
||||
assertThat(scenarioResult.getIOLog()).contains("step1 cycles.waittime=");
|
||||
assertThat(scenarioResult.getIOLog()).contains("step2 cycles.waittime=");
|
||||
assertThat(scenarioResult.getIOLog()).contains("step1 cycles_waittime=");
|
||||
assertThat(scenarioResult.getIOLog()).contains("step2 cycles_waittime=");
|
||||
String iolog = scenarioResult.getIOLog();
|
||||
System.out.println(iolog);
|
||||
// TODO: ensure that waittime is staying the same or increasing
|
||||
|
@ -31,13 +31,13 @@ for (i = 0; i < 5; i++) {
|
||||
print("scenario exited prematurely, aborting.");
|
||||
break;
|
||||
}
|
||||
print("backlogging, cycles=" + metrics.co_cycle_delay_bursty.cycles.servicetime.count +
|
||||
" waittime=" + metrics.co_cycle_delay_bursty.cycles.waittime.value +
|
||||
print("backlogging, cycles=" + metrics.co_cycle_delay_bursty.cycles_servicetime.count +
|
||||
" waittime=" + metrics.co_cycle_delay_bursty.cycles_waittime.value +
|
||||
" diagrate=" + activities.co_cycle_delay_bursty.diagrate +
|
||||
" cyclerate=" + activities.co_cycle_delay_bursty.cyclerate
|
||||
);
|
||||
}
|
||||
print('step1 metrics.waittime=' + metrics.co_cycle_delay_bursty.cycles.waittime.value);
|
||||
print('step1 metrics.waittime=' + metrics.co_cycle_delay_bursty.cycles_waittime.value);
|
||||
activities.co_cycle_delay_bursty.diagrate = "10000";
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
@ -45,19 +45,19 @@ for (i = 0; i < 10; i++) {
|
||||
print("scenario exited prematurely, aborting.");
|
||||
break;
|
||||
}
|
||||
print("recovering, cycles=" + metrics.co_cycle_delay_bursty.cycles.servicetime.count +
|
||||
" waittime=" + metrics.co_cycle_delay_bursty.cycles.waittime.value +
|
||||
print("recovering, cycles=" + metrics.co_cycle_delay_bursty.cycles_servicetime.count +
|
||||
" waittime=" + metrics.co_cycle_delay_bursty.cycles_waittime.value +
|
||||
" diagrate=" + activities.co_cycle_delay_bursty.diagrate +
|
||||
" cyclerate=" + activities.co_cycle_delay_bursty.cyclerate
|
||||
);
|
||||
|
||||
scenario.waitMillis(1000);
|
||||
if (metrics.co_cycle_delay_bursty.cycles.waittime.value < 50000000) {
|
||||
if (metrics.co_cycle_delay_bursty.cycles_waittime.value < 50000000) {
|
||||
print("waittime trended back down as expected, exiting on iteration " + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//scenario.awaitActivity("co_cycle_delay");
|
||||
print('step2 metrics.waittime=' + metrics.co_cycle_delay_bursty.cycles.waittime.value);
|
||||
print('step2 metrics.waittime=' + metrics.co_cycle_delay_bursty.cycles_waittime.value);
|
||||
scenario.stop(co_cycle_delay_bursty);
|
||||
print("stopped activity co_cycle_delay_bursty");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -26,18 +26,18 @@ co_cycle_delay = {
|
||||
print('starting activity co_cycle_delay');
|
||||
scenario.start(co_cycle_delay);
|
||||
scenario.waitMillis(4000);
|
||||
print('step1 cycles.waittime=' + metrics.co_cycle_delay.cycles.waittime.value);
|
||||
print('step1 cycles_waittime=' + metrics.co_cycle_delay.cycles_waittime.value);
|
||||
activities.co_cycle_delay.diagrate="10000";
|
||||
for(i=0;i<5;i++) {
|
||||
if (! scenario.isRunningActivity('co_cycle_delay')) {
|
||||
print("scenario exited prematurely, aborting.");
|
||||
break;
|
||||
}
|
||||
print("iteration " + i + " waittime now " + metrics.co_cycle_delay.cycles.waittime.value);
|
||||
print("iteration " + i + " waittime now " + metrics.co_cycle_delay.cycles_waittime.value);
|
||||
scenario.waitMillis(1000);
|
||||
}
|
||||
|
||||
|
||||
//scenario.awaitActivity("co_cycle_delay");
|
||||
print('step2 cycles.waittime=' + metrics.co_cycle_delay.cycles.waittime.value);
|
||||
print('step2 cycles_waittime=' + metrics.co_cycle_delay.cycles_waittime.value);
|
||||
print("awaited activity");
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -26,8 +26,8 @@ activitydef = {
|
||||
|
||||
scenario.run(activitydef);
|
||||
|
||||
print("current cycle = " + metrics.cycle_rate.cycles.servicetime.count);
|
||||
print("mean cycle rate = " + metrics.cycle_rate.cycles.servicetime.meanRate);
|
||||
print("current cycle = " + metrics.cycle_rate.cycles_servicetime.count);
|
||||
print("mean cycle rate = " + metrics.cycle_rate.cycles_servicetime.meanRate);
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -34,13 +34,13 @@ print("measured cycle increment per second is expected to adjust to 1000");
|
||||
|
||||
print('cyclerate now:' + activities.cycle_rate_change.cyclerate);
|
||||
|
||||
var lastcount=metrics.cycle_rate_change.cycles.servicetime.count;
|
||||
var lastcount=metrics.cycle_rate_change.cycles_servicetime.count;
|
||||
for(i=0;i<20;i++) {
|
||||
scenario.waitMillis(1000);
|
||||
var nextcount=metrics.cycle_rate_change.cycles.servicetime.count;
|
||||
var nextcount=metrics.cycle_rate_change.cycles_servicetime.count;
|
||||
var cycles = (nextcount - lastcount);
|
||||
print("new this second: " + (nextcount - lastcount));
|
||||
print(" waittime: " + metrics.cycle_rate_change.cycles.waittime.value);
|
||||
print(" waittime: " + metrics.cycle_rate_change.cycles_waittime.value);
|
||||
lastcount=nextcount;
|
||||
if (cycles>700 && cycles<1300) {
|
||||
print("cycles adjusted, exiting on iteration " + i);
|
||||
|
@ -27,7 +27,7 @@ activitydef = {
|
||||
scenario.start(activitydef);
|
||||
scenario.waitMillis(500);
|
||||
|
||||
csvlogger.add(metrics.csvmetrics.cycles.servicetime);
|
||||
csvlogger.add(metrics.csvmetrics.cycles_servicetime);
|
||||
csvlogger.start(500,"MILLISECONDS");
|
||||
|
||||
scenario.waitMillis(2000);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -26,10 +26,10 @@ activitydef = {
|
||||
scenario.start(activitydef);
|
||||
|
||||
scenario.waitMillis(500);
|
||||
while (metrics.testactivity.cycles.servicetime.count < 1000) {
|
||||
print('waiting 10ms because cycles<10000 : ' + metrics.testactivity.cycles.servicetime.count);
|
||||
while (metrics.testactivity.cycles_servicetime.count < 1000) {
|
||||
print('waiting 10ms because cycles<10000 : ' + metrics.testactivity.cycles_servicetime.count);
|
||||
scenario.waitMillis(10);
|
||||
|
||||
}
|
||||
scenario.stop(activitydef);
|
||||
print("count: " + metrics.testactivity.cycles.servicetime.count);
|
||||
print("count: " + metrics.testactivity.cycles_servicetime.count);
|
||||
|
4
pom.xml
4
pom.xml
@ -61,7 +61,7 @@
|
||||
<module.adapter-tcp>adapter-tcp</module.adapter-tcp>
|
||||
<module.adapter-dynamodb>adapter-dynamodb</module.adapter-dynamodb>
|
||||
<module.adapter-mongodb>adapter-mongodb</module.adapter-mongodb>
|
||||
<module.adapter-venice>adapter-venice</module.adapter-venice>
|
||||
<!-- <module.adapter-venice>adapter-venice</module.adapter-venice>-->
|
||||
<module.adapter-pulsar>adapter-pulsar</module.adapter-pulsar>
|
||||
<module.adapter-s4j>adapter-s4j</module.adapter-s4j>
|
||||
<module.adapter-kafka>adapter-kafka</module.adapter-kafka>
|
||||
@ -102,7 +102,7 @@
|
||||
<module>adapters-api</module>
|
||||
|
||||
<!-- driver modules -->
|
||||
<module>adapter-venice</module>
|
||||
<!-- <module>adapter-venice</module>-->
|
||||
<module>adapter-diag</module>
|
||||
<module>adapter-stdout</module>
|
||||
<module>adapter-cqld4</module>
|
||||
|
Loading…
Reference in New Issue
Block a user