Implemented TraceConfigZ zPage (#1441)

* Implemented TraceConfigZ zPage (#22)

* Added link to index zpage

* Used AutoValue for TableRow classes

* Changed wording of HTML content

* Changed Builder to interface, fixed build issues

* Removed tableRow classes, added omitEmpty in parseQueryMap

* Added test for invalid inputs

* Changed to use illegalArgumentException
This commit is contained in:
Terry (Tianyu) Wang 2020-07-30 18:24:50 +00:00 committed by GitHub
parent 78ed6490ba
commit ad6f9e364c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 764 additions and 13 deletions

View File

@ -13,7 +13,12 @@ dependencies {
project(':opentelemetry-sdk')
implementation libraries.guava
compileOnly 'com.sun.net.httpserver:http:20070405'
annotationProcessor libraries.auto_value
testAnnotationProcessor libraries.auto_value
signature "org.codehaus.mojo.signature:java17:1.0@signature"
}

View File

@ -0,0 +1,414 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* 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.opentelemetry.sdk.extensions.zpages;
import io.opentelemetry.sdk.trace.Samplers;
import io.opentelemetry.sdk.trace.TracerSdkProvider;
import io.opentelemetry.sdk.trace.config.TraceConfig;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
final class TraceConfigzZPageHandler extends ZPageHandler {
private static final String TRACE_CONFIGZ_URL = "/traceconfigz";
private static final String TRACE_CONFIGZ_NAME = "TraceConfigZ";
private static final String TRACE_CONFIGZ_DESCRIPTION =
"TraceConfigZ displays information about the current active tracing configuration"
+ " and allows users to change it";
private static final String QUERY_STRING_ACTION = "action";
private static final String QUERY_STRING_ACTION_CHANGE = "change";
private static final String QUERY_STRING_ACTION_DEFAULT = "default";
private static final String QUERY_STRING_SAMPLING_PROBABILITY = "samplingprobability";
private static final String QUERY_STRING_MAX_NUM_OF_ATTRIBUTES = "maxnumofattributes";
private static final String QUERY_STRING_MAX_NUM_OF_EVENTS = "maxnumofevents";
private static final String QUERY_STRING_MAX_NUM_OF_LINKS = "maxnumoflinks";
private static final String QUERY_STRING_MAX_NUM_OF_ATTRIBUTES_PER_EVENT =
"maxnumofattributesperevent";
private static final String QUERY_STRING_MAX_NUM_OF_ATTRIBUTES_PER_LINK =
"maxnumofattributesperlink";
// Background color used for zebra striping rows in table
private static final String ZEBRA_STRIPE_COLOR = "#e6e6e6";
private static final Logger logger = Logger.getLogger(TraceConfigzZPageHandler.class.getName());
private final TracerSdkProvider tracerProvider;
TraceConfigzZPageHandler(TracerSdkProvider tracerProvider) {
this.tracerProvider = tracerProvider;
}
@Override
public String getUrlPath() {
return TRACE_CONFIGZ_URL;
}
@Override
public String getPageName() {
return TRACE_CONFIGZ_NAME;
}
@Override
public String getPageDescription() {
return TRACE_CONFIGZ_DESCRIPTION;
}
/**
* Emits CSS styles to the {@link PrintStream} {@code out}. Content emited by this function should
* be enclosed by <head></head> tag.
*
* @param out the {@link PrintStream} {@code out}.
*/
private static void emitHtmlStyle(PrintStream out) {
out.print("<style>");
out.print(ZPageStyle.style);
out.print("</style>");
}
/**
* Emits a row of the change tracing parameter table to the {@link PrintStream} {@code out}. Each
* row corresponds to one tracing parameter.
*
* @param out the {@link PrintStream} {@code out}.
* @param rowName the display name of the corresponding tracing parameter.
* @param paramName the name of the corresponding tracing parameter (this will be used to
* construct the query parameter in URL).
* @param inputPlaceHolder placeholder for the <input> HTML element.
* @param paramDefaultValue the default value of the corresponding tracing parameter.
* @param zebraStripeColor hex code of the color used for zebra striping rows.
* @param zebraStripe boolean indicating if the row is zebra striped.
*/
private static void emitChangeTableRow(
PrintStream out,
String rowName,
String paramName,
String inputPlaceHolder,
String paramDefaultValue,
String zebraStripeColor,
boolean zebraStripe) {
if (zebraStripe) {
out.print("<tr style=\"background-color: " + zebraStripeColor + ";\">");
} else {
out.print("<tr>");
}
out.print("<td>Update " + rowName + "</td>");
out.print(
"<td class=\"border-left-dark\"><input type=text size=15 name="
+ paramName
+ " value=\"\" placeholder=\""
+ inputPlaceHolder
+ "\" /></td>");
out.print("<td class=\"border-left-dark\">(" + paramDefaultValue + ")</td>");
out.print("</tr>");
}
/**
* Emits the change tracing parameter table to the {@link PrintStream} {@code out}.
*
* @param out the {@link PrintStream} {@code out}.
*/
private static void emitChangeTable(PrintStream out) {
out.print("<table style=\"border-spacing: 0; border: 1px solid #363636;\">");
out.print("<tr class=\"bg-color\">");
out.print(
"<th colspan=2 style=\"text-align: left;\" class=\"header-text\">"
+ "<b>Update active TraceConfig</b></th>");
out.print("<th colspan=1 class=\"header-text border-left-white\"><b>Default</b></th>");
emitChangeTableRow(
/* out= */ out,
/* rowName= */ "SamplingProbability to",
/* paramName= */ QUERY_STRING_SAMPLING_PROBABILITY,
/* inputPlaceHolder= */ "[0.0, 1.0]",
/* paramDefaultValue= */ TraceConfig.getDefault().getSampler().getDescription(),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ false);
emitChangeTableRow(
/* out= */ out,
/* rowName= */ "MaxNumberOfAttributes to",
/* paramName= */ QUERY_STRING_MAX_NUM_OF_ATTRIBUTES,
/* inputPlaceHolder= */ "",
/* paramDefaultValue= */ Integer.toString(
TraceConfig.getDefault().getMaxNumberOfAttributes()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ true);
emitChangeTableRow(
/* out= */ out,
/* rowName= */ "MaxNumberOfEvents to",
/* paramName= */ QUERY_STRING_MAX_NUM_OF_EVENTS,
/* inputPlaceHolder= */ "",
/* paramDefaultValue= */ Integer.toString(TraceConfig.getDefault().getMaxNumberOfEvents()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ false);
emitChangeTableRow(
/* out= */ out,
/* rowName= */ "MaxNumberOfLinks to",
/* paramName= */ QUERY_STRING_MAX_NUM_OF_LINKS,
/* inputPlaceHolder= */ "",
/* paramDefaultValue= */ Integer.toString(TraceConfig.getDefault().getMaxNumberOfLinks()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ true);
emitChangeTableRow(
/* out= */ out,
/* rowName= */ "MaxNumberOfAttributesPerEvent to",
/* paramName= */ QUERY_STRING_MAX_NUM_OF_ATTRIBUTES_PER_EVENT,
/* inputPlaceHolder= */ "",
/* paramDefaultValue= */ Integer.toString(
TraceConfig.getDefault().getMaxNumberOfAttributesPerEvent()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ false);
emitChangeTableRow(
/* out= */ out,
/* rowName= */ "MaxNumberOfAttributesPerLink to",
/* paramName= */ QUERY_STRING_MAX_NUM_OF_ATTRIBUTES_PER_LINK,
/* inputPlaceHolder= */ "",
/* paramDefaultValue= */ Integer.toString(
TraceConfig.getDefault().getMaxNumberOfAttributesPerLink()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ true);
out.print("</table>");
}
/**
* Emits a row of the active tracing parameter table to the {@link PrintStream} {@code out}. Each
* row corresponds to one tracing parameter.
*
* @param out the {@link PrintStream} {@code out}.
* @param paramName the name of the corresponding tracing parameter.
* @param paramValue the value of the corresponding tracing parameter.
* @param zebraStripeColor hex code of the color used for zebra striping rows.
* @param zebraStripe boolean indicating if the row is zebra striped.
*/
private static void emitActiveTableRow(
PrintStream out,
String paramName,
String paramValue,
String zebraStripeColor,
boolean zebraStripe) {
if (zebraStripe) {
out.print("<tr style=\"background-color: " + zebraStripeColor + ";\">");
} else {
out.print("<tr>");
}
out.print("<td>" + paramName + "</td>");
out.print("<td class=\"border-left-dark\">" + paramValue + "</td>");
out.print("</tr>");
}
/**
* Emits the active tracing parameters table to the {@link PrintStream} {@code out}.
*
* @param out the {@link PrintStream} {@code out}.
*/
private void emitActiveTable(PrintStream out) {
out.print("<table style=\"border-spacing: 0; border: 1px solid #363636;\">");
out.print("<tr class=\"bg-color\">");
out.print("<th class=\"header-text\"><b>Name</b></th>");
out.print("<th class=\"header-text border-left-white\"><b>Value</b></th>");
out.print("</tr>");
emitActiveTableRow(
/* out= */ out,
/* paramName= */ "Sampler",
/* paramValue=*/ this.tracerProvider.getActiveTraceConfig().getSampler().getDescription(),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ false);
emitActiveTableRow(
/* out= */ out,
/* paramName= */ "MaxNumOfAttributes",
/* paramValue=*/ Integer.toString(
this.tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributes()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ true);
emitActiveTableRow(
/* out= */ out,
/* paramName= */ "MaxNumOfEvents",
/* paramValue=*/ Integer.toString(
this.tracerProvider.getActiveTraceConfig().getMaxNumberOfEvents()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ false);
emitActiveTableRow(
/* out= */ out,
/* paramName= */ "MaxNumOfLinks",
/* paramValue=*/ Integer.toString(
this.tracerProvider.getActiveTraceConfig().getMaxNumberOfLinks()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ true);
emitActiveTableRow(
/* out= */ out,
/* paramName= */ "MaxNumOfAttributesPerEvent",
/* paramValue=*/ Integer.toString(
this.tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerEvent()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe= */ false);
emitActiveTableRow(
/* out= */ out,
/* paramName= */ "MaxNumOfAttributesPerLink",
/* paramValue=*/ Integer.toString(
this.tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerLink()),
/* zebraStripeColor= */ ZEBRA_STRIPE_COLOR,
/* zebraStripe=*/ true);
out.print("</table>");
}
/**
* Emits HTML body content to the {@link PrintStream} {@code out}. Content emitted by this
* function should be enclosed by <body></body> tag.
*
* @param out the {@link PrintStream} {@code out}.
*/
private void emitHtmlBody(PrintStream out) {
out.print(
"<a href=\"/\"><img style=\"height: 90px;\" src=\"data:image/png;base64,"
+ ZPageLogo.getLogoBase64()
+ "\" /></a>");
out.print("<h1>Trace Configuration</h1>");
out.print("<form class=\"form-flex\" action=\"" + TRACE_CONFIGZ_URL + "\" method=\"get\">");
out.print(
"<input type=\"hidden\" name=\"action\" value=\"" + QUERY_STRING_ACTION_CHANGE + "\" />");
emitChangeTable(out);
// Button for submit
out.print("<button class=\"button\" type=\"submit\" value=\"Submit\">Submit</button>");
out.print("</form>");
// Button for restore default
out.print("<form class=\"form-flex\" action=\"" + TRACE_CONFIGZ_URL + "\" method=\"get\">");
out.print(
"<input type=\"hidden\" name=\"action\" value=\"" + QUERY_STRING_ACTION_DEFAULT + "\" />");
out.print("<button class=\"button\" type=\"submit\" value=\"Submit\">Restore Default</button>");
out.print("</form>");
out.print("<h2>Active Tracing Parameters</h2>");
emitActiveTable(out);
}
@Override
public void emitHtml(Map<String, String> queryMap, OutputStream outputStream) {
// PrintStream for emiting HTML contents
try (PrintStream out = new PrintStream(outputStream, /* autoFlush= */ false, "UTF-8")) {
out.print("<!DOCTYPE html>");
out.print("<html lang=\"en\">");
out.print("<head>");
out.print("<meta charset=\"UTF-8\">");
out.print(
"<link rel=\"shortcut icon\" href=\"data:image/png;base64,"
+ ZPageLogo.getFaviconBase64()
+ "\" type=\"image/png\">");
out.print(
"<link href=\"https://fonts.googleapis.com/css?family=Open+Sans:300\""
+ "rel=\"stylesheet\">");
out.print(
"<link href=\"https://fonts.googleapis.com/css?family=Roboto\" rel=\"stylesheet\">");
out.print("<title>" + TRACE_CONFIGZ_NAME + "</title>");
emitHtmlStyle(out);
out.print("</head>");
out.print("<body>");
try {
// Apply updated trace configuration based on query parameters
applyTraceConfig(queryMap);
emitHtmlBody(out);
} catch (Throwable t) {
out.print("Error while generating HTML: " + t.toString());
logger.log(Level.WARNING, "error while generating HTML", t);
}
out.print("</body>");
out.print("</html>");
} catch (Throwable t) {
logger.log(Level.WARNING, "error while generating HTML", t);
}
}
/**
* Apply updated trace configuration through the tracerProvider based on query parameters.
*
* @param queryMap the map containing URL query parameters.
* @throws NumberFormatException if one of the {@code double}/{@code integer} valued query string
* does not contain a parsable {@code double}/{@code integer}.
*/
private void applyTraceConfig(Map<String, String> queryMap) {
String action = queryMap.get(QUERY_STRING_ACTION);
if (action == null) {
return;
}
if (action.equals(QUERY_STRING_ACTION_CHANGE)) {
TraceConfig.Builder newConfigBuilder = this.tracerProvider.getActiveTraceConfig().toBuilder();
String samplingProbabilityStr = queryMap.get(QUERY_STRING_SAMPLING_PROBABILITY);
if (samplingProbabilityStr != null) {
try {
double samplingProbability = Double.parseDouble(samplingProbabilityStr);
if (samplingProbability == 0) {
newConfigBuilder.setSampler(Samplers.alwaysOff());
} else if (samplingProbability == 1) {
newConfigBuilder.setSampler(Samplers.alwaysOn());
} else {
newConfigBuilder.setSampler(Samplers.probability(samplingProbability));
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("SamplingProbability must be of the type double", e);
}
}
String maxNumOfAttributesStr = queryMap.get(QUERY_STRING_MAX_NUM_OF_ATTRIBUTES);
if (maxNumOfAttributesStr != null) {
try {
int maxNumOfAttributes = Integer.parseInt(maxNumOfAttributesStr);
newConfigBuilder.setMaxNumberOfAttributes(maxNumOfAttributes);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("MaxNumOfAttributes must be of the type integer", e);
}
}
String maxNumOfEventsStr = queryMap.get(QUERY_STRING_MAX_NUM_OF_EVENTS);
if (maxNumOfEventsStr != null) {
try {
int maxNumOfEvents = Integer.parseInt(maxNumOfEventsStr);
newConfigBuilder.setMaxNumberOfEvents(maxNumOfEvents);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("MaxNumOfEvents must be of the type integer", e);
}
}
String maxNumOfLinksStr = queryMap.get(QUERY_STRING_MAX_NUM_OF_LINKS);
if (maxNumOfLinksStr != null) {
try {
int maxNumOfLinks = Integer.parseInt(maxNumOfLinksStr);
newConfigBuilder.setMaxNumberOfLinks(maxNumOfLinks);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("MaxNumOfLinks must be of the type integer", e);
}
}
String maxNumOfAttributesPerEventStr =
queryMap.get(QUERY_STRING_MAX_NUM_OF_ATTRIBUTES_PER_EVENT);
if (maxNumOfAttributesPerEventStr != null) {
try {
int maxNumOfAttributesPerEvent = Integer.parseInt(maxNumOfAttributesPerEventStr);
newConfigBuilder.setMaxNumberOfAttributesPerEvent(maxNumOfAttributesPerEvent);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"MaxNumOfAttributesPerEvent must be of the type integer", e);
}
}
String maxNumOfAttributesPerLinkStr =
queryMap.get(QUERY_STRING_MAX_NUM_OF_ATTRIBUTES_PER_LINK);
if (maxNumOfAttributesPerLinkStr != null) {
try {
int maxNumOfAttributesPerLink = Integer.parseInt(maxNumOfAttributesPerLinkStr);
newConfigBuilder.setMaxNumberOfAttributesPerLink(maxNumOfAttributesPerLink);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
"MaxNumOfAttributesPerLink must be of the type integer", e);
}
}
this.tracerProvider.updateActiveTraceConfig(newConfigBuilder.build());
} else if (action.equals(QUERY_STRING_ACTION_DEFAULT)) {
TraceConfig defaultConfig = TraceConfig.getDefault().toBuilder().build();
this.tracerProvider.updateActiveTraceConfig(defaultConfig);
}
}
}

View File

@ -538,7 +538,7 @@ final class TracezZPageHandler extends ZPageHandler {
+ "rel=\"stylesheet\">");
out.print(
"<link href=\"https://fonts.googleapis.com/css?family=Roboto\" rel=\"stylesheet\">");
out.print("<title>TraceZ</title>");
out.print("<title>" + TRACEZ_NAME + "</title>");
emitHtmlStyle(out);
out.print("</head>");
out.print("<body>");

View File

@ -32,9 +32,10 @@ import java.util.Map;
/** An {@link HttpHandler} that will be used to render HTML pages using any {@code ZPageHandler}. */
final class ZPageHttpHandler implements HttpHandler {
// Splitter for splitting URL query parameters
private static final Splitter QUERY_SPLITTER = Splitter.on("&").trimResults();
private static final Splitter QUERY_SPLITTER = Splitter.on("&").trimResults().omitEmptyStrings();
// Splitter for splitting URL query parameters' key value
private static final Splitter QUERY_KEYVAL_SPLITTER = Splitter.on("=").trimResults();
private static final Splitter QUERY_KEYVAL_SPLITTER =
Splitter.on("=").trimResults().omitEmptyStrings();
// Query string parameter name for span name
private static final String PARAM_SPAN_NAME = "zspanname";
// The corresponding ZPageHandler for the zPage (e.g. TracezZPageHandler)
@ -66,8 +67,6 @@ final class ZPageHttpHandler implements HttpHandler {
} else {
queryMap.put(keyValuePair.get(0), keyValuePair.get(1));
}
} else {
queryMap.put(keyValuePair.get(0), "");
}
}
return ImmutableMap.copyOf(queryMap);

View File

@ -69,12 +69,16 @@ public final class ZPageServer {
TracezSpanProcessor.newBuilder().build();
private static final TracezDataAggregator tracezDataAggregator =
new TracezDataAggregator(tracezSpanProcessor);
private static final TracerSdkProvider tracerProvider = OpenTelemetrySdk.getTracerProvider();
// Handler for /tracez page
private static final ZPageHandler tracezZPageHandler =
new TracezZPageHandler(tracezDataAggregator);
// Handler for index page, **please include all available zPages in the constructor**
// Handler for /traceconfigz page
private static final ZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
// Handler for index page, **please include all available ZPageHandlers in the constructor**
private static final ZPageHandler indexZPageHandler =
new IndexZPageHandler(ImmutableList.of(tracezZPageHandler));
new IndexZPageHandler(ImmutableList.of(tracezZPageHandler, traceConfigzZPageHandler));
private static final Object mutex = new Object();
private static final AtomicBoolean isTracezSpanProcesserAdded = new AtomicBoolean(false);
@ -86,7 +90,6 @@ public final class ZPageServer {
/** Function that adds the {@link TracezSpanProcessor} to the {@link TracerSdkProvider}. */
private static void addTracezSpanProcessor() {
if (isTracezSpanProcesserAdded.compareAndSet(false, true)) {
TracerSdkProvider tracerProvider = OpenTelemetrySdk.getTracerProvider();
tracerProvider.addSpanProcessor(tracezSpanProcessor);
}
}
@ -121,6 +124,23 @@ public final class ZPageServer {
server.createContext(tracezZPageHandler.getUrlPath(), new ZPageHttpHandler(tracezZPageHandler));
}
/**
* Registers a {@code ZPageHandler} for tracing config. The page displays information about all
* active configuration and allow changing the active configuration.
*
* <p>It displays a change table which allows users to change active configuration.
*
* <p>It displays an active value table which displays current active configuration.
*
* <p>Refreshing the page will show the updated active configuration.
*
* @param server the {@link HttpServer} for the page to register to.
*/
static void registerTraceConfigzZPageHandler(HttpServer server) {
server.createContext(
traceConfigzZPageHandler.getUrlPath(), new ZPageHttpHandler(traceConfigzZPageHandler));
}
/**
* Registers all zPages to the given {@link HttpServer} {@code server}.
*
@ -130,6 +150,7 @@ public final class ZPageServer {
// For future zPages, register them to the server in here
registerIndexZPageHandler(server);
registerTracezZPageHandler(server);
registerTraceConfigzZPageHandler(server);
}
/** Method for stopping the {@link HttpServer} {@code server}. */

View File

@ -39,5 +39,7 @@ final class ZPageStyle {
+ ".align-right{text-align: right;}"
+ "pre.no-margin{margin: 0;}"
+ "pre.wrap-text{white-space:pre-wrap;}"
+ "td.bg-white{background-color: #fff;}";
+ "td.bg-white{background-color: #fff;}"
+ "button.button{background-color: #fff; margin-top: 15px;}"
+ "form.form-flex{display: flex; flex-direction: column; align-items: center;}";
}

View File

@ -40,7 +40,7 @@ public final class SpanBucketTest {
@Test
public void verifyLatencyBucketSizeLimit() {
SpanBucket latencyBucket = new SpanBucket(true);
SpanBucket latencyBucket = new SpanBucket(/* isLatencyBucket= */ true);
Span[] spans = new Span[LATENCY_BUCKET_SIZE + 1];
for (int i = 0; i < LATENCY_BUCKET_SIZE + 1; i++) {
spans[i] = tracer.spanBuilder(SPAN_NAME).startSpan();
@ -60,7 +60,7 @@ public final class SpanBucketTest {
@Test
public void verifyErrorBucketSizeLimit() {
SpanBucket errorBucket = new SpanBucket(false);
SpanBucket errorBucket = new SpanBucket(/* isLatencyBucket= */ false);
Span[] spans = new Span[ERROR_BUCKET_SIZE + 1];
for (int i = 0; i < ERROR_BUCKET_SIZE + 1; i++) {
spans[i] = tracer.spanBuilder(SPAN_NAME).startSpan();
@ -82,7 +82,7 @@ public final class SpanBucketTest {
public void verifyThreadSafety() throws InterruptedException {
int numberOfThreads = 4;
int numberOfSpans = 4;
SpanBucket spanBucket = new SpanBucket(true);
SpanBucket spanBucket = new SpanBucket(/* isLatencyBucket= */ true);
final CountDownLatch startSignal = new CountDownLatch(1);
final CountDownLatch endSignal = new CountDownLatch(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++) {

View File

@ -0,0 +1,310 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* 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.opentelemetry.sdk.extensions.zpages;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.Samplers;
import io.opentelemetry.sdk.trace.TracerSdkProvider;
import io.opentelemetry.sdk.trace.config.TraceConfig;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link TraceConfigzZPageHandler}. */
@RunWith(JUnit4.class)
public final class TraceConfigzZPageHandlerTest {
private static final TracerSdkProvider tracerProvider = OpenTelemetrySdk.getTracerProvider();
private static final Map<String, String> emptyQueryMap = ImmutableMap.of();
@Test
public void changeTable_emitRowsCorrectly() {
OutputStream output = new ByteArrayOutputStream();
String querySamplingProbability = "samplingprobability";
String queryMaxNumOfAttributes = "maxnumofattributes";
String queryMaxNumOfEvents = "maxnumofevents";
String queryMaxNumOfLinks = "maxnumoflinks";
String queryMaxNumOfAttributesPerEvent = "maxnumofattributesperevent";
String queryMaxNumOfAttributesPerLink = "maxnumofattributesperlink";
TraceConfigzZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
traceConfigzZPageHandler.emitHtml(emptyQueryMap, output);
assertThat(output.toString()).contains("SamplingProbability to");
assertThat(output.toString()).contains("name=" + querySamplingProbability);
assertThat(output.toString())
.contains("(" + TraceConfig.getDefault().getSampler().getDescription() + ")");
assertThat(output.toString()).contains("MaxNumberOfAttributes to");
assertThat(output.toString()).contains("name=" + queryMaxNumOfAttributes);
assertThat(output.toString())
.contains(
"(" + Integer.toString(TraceConfig.getDefault().getMaxNumberOfAttributes()) + ")");
assertThat(output.toString()).contains("MaxNumberOfEvents to");
assertThat(output.toString()).contains("name=" + queryMaxNumOfEvents);
assertThat(output.toString())
.contains("(" + Integer.toString(TraceConfig.getDefault().getMaxNumberOfEvents()) + ")");
assertThat(output.toString()).contains("MaxNumberOfLinks to");
assertThat(output.toString()).contains("name=" + queryMaxNumOfLinks);
assertThat(output.toString())
.contains("(" + Integer.toString(TraceConfig.getDefault().getMaxNumberOfLinks()) + ")");
assertThat(output.toString()).contains("MaxNumberOfAttributesPerEvent to");
assertThat(output.toString()).contains("name=" + queryMaxNumOfAttributesPerEvent);
assertThat(output.toString())
.contains(
"("
+ Integer.toString(TraceConfig.getDefault().getMaxNumberOfAttributesPerEvent())
+ ")");
assertThat(output.toString()).contains("MaxNumberOfAttributesPerLink to");
assertThat(output.toString()).contains("name=" + queryMaxNumOfAttributesPerLink);
assertThat(output.toString())
.contains(
"("
+ Integer.toString(TraceConfig.getDefault().getMaxNumberOfAttributesPerLink())
+ ")");
}
@Test
public void activeTable_emitRowsCorrectly() {
OutputStream output = new ByteArrayOutputStream();
TraceConfigzZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
traceConfigzZPageHandler.emitHtml(emptyQueryMap, output);
assertThat(output.toString()).contains("Sampler");
assertThat(output.toString())
.contains(">" + tracerProvider.getActiveTraceConfig().getSampler().getDescription() + "<");
assertThat(output.toString()).contains("MaxNumberOfAttributes");
assertThat(output.toString())
.contains(
">"
+ Integer.toString(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributes())
+ "<");
assertThat(output.toString()).contains("MaxNumberOfEvents");
assertThat(output.toString())
.contains(
">"
+ Integer.toString(tracerProvider.getActiveTraceConfig().getMaxNumberOfEvents())
+ "<");
assertThat(output.toString()).contains("MaxNumberOfLinks");
assertThat(output.toString())
.contains(
">"
+ Integer.toString(tracerProvider.getActiveTraceConfig().getMaxNumberOfLinks())
+ "<");
assertThat(output.toString()).contains("MaxNumberOfAttributesPerEvent");
assertThat(output.toString())
.contains(
">"
+ Integer.toString(
tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerEvent())
+ "<");
assertThat(output.toString()).contains("MaxNumberOfAttributesPerLink");
assertThat(output.toString())
.contains(
">"
+ Integer.toString(
tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerLink())
+ "<");
}
@Test
public void appliesChangesCorrectly_formSubmit() {
OutputStream output = new ByteArrayOutputStream();
String querySamplingProbability = "samplingprobability";
String queryMaxNumOfAttributes = "maxnumofattributes";
String queryMaxNumOfEvents = "maxnumofevents";
String queryMaxNumOfLinks = "maxnumoflinks";
String queryMaxNumOfAttributesPerEvent = "maxnumofattributesperevent";
String queryMaxNumOfAttributesPerLink = "maxnumofattributesperlink";
String newSamplingProbability = "0.001";
String newMaxNumOfAttributes = "16";
String newMaxNumOfEvents = "16";
String newMaxNumOfLinks = "16";
String newMaxNumOfAttributesPerEvent = "16";
String newMaxNumOfAttributesPerLink = "16";
Map<String, String> queryMap =
new ImmutableMap.Builder<String, String>()
.put("action", "change")
.put(querySamplingProbability, newSamplingProbability)
.put(queryMaxNumOfAttributes, newMaxNumOfAttributes)
.put(queryMaxNumOfEvents, newMaxNumOfEvents)
.put(queryMaxNumOfLinks, newMaxNumOfLinks)
.put(queryMaxNumOfAttributesPerEvent, newMaxNumOfAttributesPerEvent)
.put(queryMaxNumOfAttributesPerLink, newMaxNumOfAttributesPerLink)
.build();
TraceConfigzZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(tracerProvider.getActiveTraceConfig().getSampler().getDescription())
.isEqualTo(
Samplers.probability(Double.parseDouble(newSamplingProbability)).getDescription());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributes())
.isEqualTo(Integer.parseInt(newMaxNumOfAttributes));
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfEvents())
.isEqualTo(Integer.parseInt(newMaxNumOfEvents));
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfLinks())
.isEqualTo(Integer.parseInt(newMaxNumOfLinks));
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerEvent())
.isEqualTo(Integer.parseInt(newMaxNumOfAttributesPerEvent));
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerLink())
.isEqualTo(Integer.parseInt(newMaxNumOfAttributesPerLink));
}
@Test
public void appliesChangesCorrectly_restoreDefault() {
OutputStream output = new ByteArrayOutputStream();
Map<String, String> queryMap = ImmutableMap.of("action", "default");
TraceConfigzZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(tracerProvider.getActiveTraceConfig().getSampler().getDescription())
.isEqualTo(TraceConfig.getDefault().getSampler().getDescription());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributes())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfAttributes());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfEvents())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfEvents());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfLinks())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfLinks());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerEvent())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfAttributesPerEvent());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerLink())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfAttributesPerLink());
}
@Test
public void appliesChangesCorrectly_doNotCrashOnNullParameters() {
OutputStream output = new ByteArrayOutputStream();
Map<String, String> queryMap = ImmutableMap.of("action", "change");
TraceConfigzZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(tracerProvider.getActiveTraceConfig().getSampler().getDescription())
.isEqualTo(TraceConfig.getDefault().getSampler().getDescription());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributes())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfAttributes());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfEvents())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfEvents());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfLinks())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfLinks());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerEvent())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfAttributesPerEvent());
assertThat(tracerProvider.getActiveTraceConfig().getMaxNumberOfAttributesPerLink())
.isEqualTo(TraceConfig.getDefault().getMaxNumberOfAttributesPerLink());
}
@Test
public void applyChanges_emitErrorOnInvalidInput() {
// Invalid samplingProbability (not type of double)
OutputStream output = new ByteArrayOutputStream();
TraceConfigzZPageHandler traceConfigzZPageHandler =
new TraceConfigzZPageHandler(tracerProvider);
Map<String, String> queryMap =
ImmutableMap.of("action", "change", "samplingprobability", "invalid");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("SamplingProbability must be of the type double");
// Invalid samplingProbability (< 0)
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "samplingprobability", "-1");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("probability must be in range [0.0, 1.0]");
// Invalid samplingProbability (> 1)
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "samplingprobability", "1.1");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("probability must be in range [0.0, 1.0]");
// Invalid maxNumOfAttributes
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "maxnumofattributes", "invalid");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("MaxNumOfAttributes must be of the type integer");
// Invalid maxNumOfEvents
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "maxnumofevents", "invalid");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("MaxNumOfEvents must be of the type integer");
// Invalid maxNumLinks
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "maxnumoflinks", "invalid");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("MaxNumOfLinks must be of the type integer");
// Invalid maxNumOfAttributesPerEvent
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "maxnumofattributesperevent", "invalid");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString())
.contains("MaxNumOfAttributesPerEvent must be of the type integer");
// Invalid maxNumOfAttributesPerLink
output = new ByteArrayOutputStream();
traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider);
queryMap = ImmutableMap.of("action", "change", "maxnumofattributesperlink", "invalid");
traceConfigzZPageHandler.emitHtml(queryMap, output);
assertThat(output.toString()).contains("Error while generating HTML: ");
assertThat(output.toString()).contains("MaxNumOfAttributesPerLink must be of the type integer");
}
}

View File

@ -39,6 +39,6 @@ public final class ZPageHttpHandlerTest {
URI uri =
new URI("http://localhost:8000/tracez/tracez?zspanname=Test&ztype=1&zsubtype=5&noval");
assertThat(ZPageHttpHandler.parseQueryMap(uri))
.containsExactly("zspanname", "Test", "ztype", "1", "zsubtype", "5", "noval", "");
.containsExactly("zspanname", "Test", "ztype", "1", "zsubtype", "5");
}
}