mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
api for annotations
This commit is contained in:
38
nb-api/src/main/java/io/nosqlbench/nb/api/Layer.java
Normal file
38
nb-api/src/main/java/io/nosqlbench/nb/api/Layer.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package io.nosqlbench.nb.api;
|
||||
|
||||
public enum Layer {
|
||||
|
||||
/**
|
||||
* Events which describe command line arguments, such as parsing,
|
||||
* named scenario mapping, or critical errors
|
||||
*/
|
||||
CLI,
|
||||
|
||||
/**
|
||||
* Events which describe scenario execution, such as parameters,
|
||||
* lifecycle events, and critical errors
|
||||
*/
|
||||
Scenario,
|
||||
|
||||
/**
|
||||
* Events which describe scripting details, such as extensions,
|
||||
* sending programmatic annotations, or critical errors
|
||||
*/
|
||||
Script,
|
||||
|
||||
/**
|
||||
* Events which are associated with a particular activity instance,
|
||||
* such as parameters, starting and stopping, and critical errors
|
||||
*/
|
||||
Activity,
|
||||
|
||||
/**
|
||||
* Events which are associated with a particular activity thread
|
||||
*/
|
||||
Motor,
|
||||
|
||||
/**
|
||||
* Events which are associated with a particular operation or op template
|
||||
*/
|
||||
Operation
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package io.nosqlbench.nb.api.annotation;
|
||||
|
||||
import io.nosqlbench.nb.spi.Named;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of this type is responsible for taking annotation details and
|
||||
* logging them in a useful place.
|
||||
*/
|
||||
public interface Annotator extends Named {
|
||||
|
||||
/**
|
||||
* Submit an annotation to some type of annotation store or logging or eventing mechanism.
|
||||
* Implementations of this service are responsible for mapping the scenarioName, target,
|
||||
* and details into the native schema of the target annotation or logging system in whichever
|
||||
* way would be the least surprising for a user.
|
||||
*
|
||||
* The target is the nominative data which identifies the identity of the annotation. This
|
||||
* must include enough information to allow the annotation to be homed and located within
|
||||
* a target system such that is is visible where it should be seen. This includes all
|
||||
* metadata which may be used to filter or locate the annotation, including timestamps.
|
||||
*
|
||||
* The details contain payload information to be displayed within the body of the annotation.
|
||||
*
|
||||
* @param sessionName The name of the scenario
|
||||
* @param startEpochMillis The epoch millisecond instant of the annotation, set this to 0 to have it
|
||||
* automatically set to the current system time.
|
||||
* @param endEpochMillis The epoch millisecond instant at the end of the interval. If this is
|
||||
* equal to the start instant, then this is an annotation for a point in time.
|
||||
* This will be the default behavior if this value is 0.
|
||||
* @param target The target of the annotation, fields which are required to associate the
|
||||
* annotation with the correct instance of a dashboard, metrics, etc
|
||||
* @param details A map of details
|
||||
*/
|
||||
void recordAnnotation(
|
||||
String sessionName,
|
||||
long startEpochMillis,
|
||||
long endEpochMillis,
|
||||
Map<String, String> target,
|
||||
Map<String, String> details);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.nb.api.Layer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a general purpose representation of an event that describes
|
||||
* a significant workflow detail to users running tests. It can be
|
||||
* an event that describes an instant, or it can describe an interval
|
||||
* in time (being associated with the interval of time between two
|
||||
* canonical events.)
|
||||
*
|
||||
* This view of an annotation event captures the semantics of what
|
||||
* any reportable annotation should look like from the perspective of
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* If this is the same as {@link #getEnd()}, then the annotation is
|
||||
* for an instant in time.
|
||||
*
|
||||
* @return The beginning of the interval of time that the annotation describes
|
||||
*/
|
||||
long getStart();
|
||||
|
||||
/**
|
||||
* If this is the same as {@link #getStart()}, then the annotation
|
||||
* is for an instant in time.
|
||||
*
|
||||
* @return The end of the interval of time that the annotation describes
|
||||
*/
|
||||
long getEnd();
|
||||
|
||||
/**
|
||||
* Annotations must be associated with a processing layer in NoSQLBench.
|
||||
* For more details on layers, see {@link Layer}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Layer getLayer();
|
||||
|
||||
/**
|
||||
* The labels which identify what this annotation pertains to. The following labels
|
||||
* should be provided for every annotation, when available:
|
||||
* <UL>
|
||||
* <LI>appname: "nosqlbench"</LI>
|
||||
* <LI>alias: The name of the activity alias, if available</LI>
|
||||
* <LI>workload: The name of the workload file, if named scenarios are used</LI>
|
||||
* <LI>scenario: The name of the named scenario, if named scenarios are used</LI>
|
||||
* <LI>step: The name of the named scenario step, if named scenario are used</LI>
|
||||
* <LI>usermode: "named_scenario" or "adhoc_activity"</LI>
|
||||
* </UL>
|
||||
*
|
||||
* @return The labels map
|
||||
*/
|
||||
Map<String, String> getLabels();
|
||||
|
||||
/**
|
||||
* The details are an ordered map of all the content that you would want the user to see.
|
||||
*
|
||||
* @return The details map
|
||||
*/
|
||||
Map<String, String> getDetails();
|
||||
|
||||
static BuilderFacets.WantsSession newBuilder() {
|
||||
return new AnnotationBuilder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.nb.api.Layer;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class AnnotationBuilder implements BuilderFacets.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;
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder layer(Layer layer) {
|
||||
this.layer = layer;
|
||||
this.label("layer", layer.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder interval(long start, long end) {
|
||||
start(start);
|
||||
end(end);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder now() {
|
||||
start(System.currentTimeMillis());
|
||||
end(this.start);
|
||||
return this;
|
||||
}
|
||||
|
||||
private AnnotationBuilder start(long start) {
|
||||
this.start = start;
|
||||
return this;
|
||||
}
|
||||
|
||||
private AnnotationBuilder end(long end) {
|
||||
this.end = end;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder at(long at) {
|
||||
this.start(at);
|
||||
this.end(at);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationBuilder label(String name, String value) {
|
||||
this.labels.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderFacets.WantsMoreDetailsOrBuild detail(String name, String value) {
|
||||
this.details.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Annotation build() {
|
||||
return new MutableAnnotation(session, layer, start, end, labels, details).asReadOnly();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderFacets.WantsInterval session(String session) {
|
||||
this.session = session;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.nb.spi.Named;
|
||||
|
||||
/**
|
||||
* An implementation of this type is responsible for taking annotation details and
|
||||
* logging them in a useful place.
|
||||
*/
|
||||
public interface Annotator extends Named {
|
||||
|
||||
/**
|
||||
* Submit an annotation to some type of annotation store, logging or eventing mechanism.
|
||||
* Implementations of this service are responsible for mapping the scenario and labels
|
||||
* into appropriate key data, and the details in to a native payload. The least surprising
|
||||
* and most obvious mapping should be used in each case.
|
||||
*
|
||||
* For details on constructing a useful annotation to submit to this service, see {@link Annotation#newBuilder()}
|
||||
*/
|
||||
void recordAnnotation(Annotation annotation);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.nb.api.Layer;
|
||||
|
||||
public interface BuilderFacets {
|
||||
|
||||
interface All extends
|
||||
WantsSession, WantsInterval, WantsLayer, WantsLabels, WantsMoreDetailsOrBuild, WantsMoreLabelsOrDetails {
|
||||
}
|
||||
|
||||
interface WantsSession {
|
||||
/**
|
||||
* The session is the global name of a NoSQLBench process which run a scenario. It is required.
|
||||
*/
|
||||
WantsInterval session(String session);
|
||||
}
|
||||
|
||||
interface WantsInterval {
|
||||
|
||||
/**
|
||||
* Specify the instant of the annotated event.
|
||||
*
|
||||
* @param epochMillis
|
||||
*/
|
||||
WantsLayer at(long epochMillis);
|
||||
|
||||
/**
|
||||
* An interval annotation spans the time between two instants.
|
||||
*/
|
||||
WantsLayer interval(long startMillis, long endMillis);
|
||||
|
||||
/**
|
||||
* Use the current UTC time as the annotation instant.
|
||||
*/
|
||||
WantsLayer now();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
interface WantsMoreDetailsOrBuild {
|
||||
WantsMoreDetailsOrBuild detail(String name, String value);
|
||||
|
||||
Annotation build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.nb.api.Layer;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MutableAnnotation implements Annotation {
|
||||
|
||||
private String session = "SESSION_UNNAMED";
|
||||
private Layer layer;
|
||||
private long start = 0L;
|
||||
private long end = 0L;
|
||||
private Map<String, String> labels = new LinkedHashMap<>();
|
||||
private Map<String, String> details = new LinkedHashMap<>();
|
||||
|
||||
public MutableAnnotation(String session, Layer layer, long start, long end, LinkedHashMap<String, String> labels,
|
||||
LinkedHashMap<String, String> details) {
|
||||
this.session = session;
|
||||
this.layer = layer;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.details = details;
|
||||
this.labels = labels;
|
||||
}
|
||||
|
||||
public void setSession(String sessionName) {
|
||||
this.session = sessionName;
|
||||
}
|
||||
|
||||
public void setStart(long intervalStart) {
|
||||
this.start = intervalStart;
|
||||
}
|
||||
|
||||
public void setEnd(long intervalEnd) {
|
||||
this.end = intervalEnd;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Layer getLayer() {
|
||||
return this.layer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("session: ").append(getSession()).append("\n");
|
||||
|
||||
sb.append("[").append(new Date(getStart()));
|
||||
if (getStart() != getEnd()) {
|
||||
sb.append(" - ").append(new Date(getEnd()));
|
||||
}
|
||||
sb.append("]\n");
|
||||
sb.append("details:\n");
|
||||
formatMap(sb, getDetails());
|
||||
sb.append("labels:\n");
|
||||
formatMap(sb, getLabels());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void formatMap(StringBuilder sb, Map<String, String> details) {
|
||||
details.forEach((k, v) -> {
|
||||
sb.append(" ").append(k).append(": ");
|
||||
if (v.contains("\n")) {
|
||||
sb.append("\n");
|
||||
|
||||
String[] lines = v.split("\n+");
|
||||
for (String line : lines) {
|
||||
sb.append(" " + line + "\n");
|
||||
}
|
||||
// Arrays.stream(lines).sequential().map(s -> " "+s+"\n").forEach(sb::append);
|
||||
} else {
|
||||
sb.append(v).append("\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public Annotation asReadOnly() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package io.nosqlbench.nb.api.annotations;
|
||||
|
||||
import io.nosqlbench.nb.api.Layer;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class AnnotationBuilderTest {
|
||||
|
||||
private static final long time = 1600000000000L;
|
||||
|
||||
@Test
|
||||
public void testBasicAnnotation() {
|
||||
|
||||
Annotation an1 = Annotation.newBuilder()
|
||||
.session("test-session")
|
||||
.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" +
|
||||
"[Sun Sep 13 07:26:40 CDT 2020]\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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user