From ad6f9e364c094d7afd67da291c89bc7366a151cb Mon Sep 17 00:00:00 2001 From: "Terry (Tianyu) Wang" Date: Thu, 30 Jul 2020 18:24:50 +0000 Subject: [PATCH] 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 --- sdk_extensions/zpages/build.gradle | 5 + .../zpages/TraceConfigzZPageHandler.java | 414 ++++++++++++++++++ .../extensions/zpages/TracezZPageHandler.java | 2 +- .../extensions/zpages/ZPageHttpHandler.java | 7 +- .../sdk/extensions/zpages/ZPageServer.java | 27 +- .../sdk/extensions/zpages/ZPageStyle.java | 4 +- .../sdk/extensions/zpages/SpanBucketTest.java | 6 +- .../zpages/TraceConfigzZPageHandlerTest.java | 310 +++++++++++++ .../zpages/ZPageHttpHandlerTest.java | 2 +- 9 files changed, 764 insertions(+), 13 deletions(-) create mode 100644 sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandler.java create mode 100644 sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandlerTest.java diff --git a/sdk_extensions/zpages/build.gradle b/sdk_extensions/zpages/build.gradle index 1a8e83455d..03fa926452 100644 --- a/sdk_extensions/zpages/build.gradle +++ b/sdk_extensions/zpages/build.gradle @@ -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" } diff --git a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandler.java b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandler.java new file mode 100644 index 0000000000..56ff21aecc --- /dev/null +++ b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandler.java @@ -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 tag. + * + * @param out the {@link PrintStream} {@code out}. + */ + private static void emitHtmlStyle(PrintStream out) { + out.print(""); + } + + /** + * 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 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(""); + } else { + out.print(""); + } + out.print("Update " + rowName + ""); + out.print( + ""); + out.print("(" + paramDefaultValue + ")"); + out.print(""); + } + + /** + * 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(""); + out.print(""); + out.print( + ""); + out.print(""); + 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("
" + + "Update active TraceConfigDefault
"); + } + + /** + * 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(""); + } else { + out.print(""); + } + out.print("" + paramName + ""); + out.print("" + paramValue + ""); + out.print(""); + } + + /** + * 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(""); + out.print(""); + out.print(""); + out.print(""); + out.print(""); + 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("
NameValue
"); + } + + /** + * Emits HTML body content to the {@link PrintStream} {@code out}. Content emitted by this + * function should be enclosed by tag. + * + * @param out the {@link PrintStream} {@code out}. + */ + private void emitHtmlBody(PrintStream out) { + out.print( + ""); + out.print("

Trace Configuration

"); + out.print("
"); + out.print( + ""); + emitChangeTable(out); + // Button for submit + out.print(""); + out.print("
"); + // Button for restore default + out.print("
"); + out.print( + ""); + out.print(""); + out.print("
"); + out.print("

Active Tracing Parameters

"); + emitActiveTable(out); + } + + @Override + public void emitHtml(Map queryMap, OutputStream outputStream) { + // PrintStream for emiting HTML contents + try (PrintStream out = new PrintStream(outputStream, /* autoFlush= */ false, "UTF-8")) { + out.print(""); + out.print(""); + out.print(""); + out.print(""); + out.print( + ""); + out.print( + ""); + out.print( + ""); + out.print("" + TRACE_CONFIGZ_NAME + ""); + emitHtmlStyle(out); + out.print(""); + out.print(""); + 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(""); + out.print(""); + } 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 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); + } + } +} diff --git a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandler.java b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandler.java index 9d044e0b21..7999a60d88 100644 --- a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandler.java +++ b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandler.java @@ -538,7 +538,7 @@ final class TracezZPageHandler extends ZPageHandler { + "rel=\"stylesheet\">"); out.print( ""); - out.print("TraceZ"); + out.print("" + TRACEZ_NAME + ""); emitHtmlStyle(out); out.print(""); out.print(""); diff --git a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandler.java b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandler.java index 48c29f5f67..94deb0a339 100644 --- a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandler.java +++ b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandler.java @@ -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); diff --git a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageServer.java b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageServer.java index 5054b6307a..d128b4d585 100644 --- a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageServer.java +++ b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageServer.java @@ -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. + * + *

It displays a change table which allows users to change active configuration. + * + *

It displays an active value table which displays current active configuration. + * + *

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}. */ diff --git a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageStyle.java b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageStyle.java index a345467624..5e2d1f861d 100644 --- a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageStyle.java +++ b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageStyle.java @@ -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;}"; } diff --git a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/SpanBucketTest.java b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/SpanBucketTest.java index 47ec24ff35..0041d6bffb 100644 --- a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/SpanBucketTest.java +++ b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/SpanBucketTest.java @@ -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++) { diff --git a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandlerTest.java b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandlerTest.java new file mode 100644 index 0000000000..214d4b51b2 --- /dev/null +++ b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TraceConfigzZPageHandlerTest.java @@ -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 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 queryMap = + new ImmutableMap.Builder() + .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 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 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 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"); + } +} diff --git a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandlerTest.java b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandlerTest.java index 5ad8948f0a..1babcacc91 100644 --- a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandlerTest.java +++ b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/ZPageHttpHandlerTest.java @@ -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"); } }