diff --git a/sdk_extensions/zpages/build.gradle b/sdk_extensions/zpages/build.gradle index 5322ba81bf8..f5d6546ad4e 100644 --- a/sdk_extensions/zpages/build.gradle +++ b/sdk_extensions/zpages/build.gradle @@ -17,10 +17,6 @@ dependencies { 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 index 56ff21aeccf..5e0f25f8de1 100644 --- 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 @@ -274,7 +274,7 @@ private void emitHtmlBody(PrintStream out) { + ZPageLogo.getLogoBase64() + "\" />"); out.print("

Trace Configuration

"); - out.print("
"); + out.print(""); out.print( ""); emitChangeTable(out); @@ -282,7 +282,7 @@ private void emitHtmlBody(PrintStream out) { out.print(""); out.print("
"); // Button for restore default - out.print("
"); + out.print(""); out.print( ""); out.print(""); @@ -313,8 +313,6 @@ public void emitHtml(Map queryMap, OutputStream outputStream) { 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()); @@ -327,6 +325,44 @@ public void emitHtml(Map queryMap, OutputStream outputStream) { } } + @Override + public boolean processRequest( + String requestMethod, Map queryMap, OutputStream outputStream) { + if (requestMethod.equalsIgnoreCase("POST")) { + try { + applyTraceConfig(queryMap); + } catch (Throwable t) { + 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 + ""); + out.print(""); + out.print(""); + out.print("Error while applying trace config changes: " + t.toString()); + out.print(""); + out.print(""); + logger.log(Level.WARNING, "error while applying trace config changes", t); + } catch (Throwable e) { + logger.log(Level.WARNING, "error while applying trace config changes", e); + return true; + } + return true; + } + } + return false; + } + /** * Apply updated trace configuration through the tracerProvider based on query parameters. * diff --git a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHandler.java b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHandler.java index ceceac94e40..3d04f8187d1 100644 --- a/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHandler.java +++ b/sdk_extensions/zpages/src/main/java/io/opentelemetry/sdk/extensions/zpages/ZPageHandler.java @@ -46,6 +46,19 @@ public abstract class ZPageHandler { */ public abstract String getPageDescription(); + /** + * Process requests that require changes (POST/PUT/DELETE). + * + * @param requestMethod the request method HttpHandler received. + * @param queryMap the map of the URL query parameters. + * @return true if theres an error while processing the request. + */ + public boolean processRequest( + String requestMethod, Map queryMap, OutputStream outputStream) { + // base no-op method + return false; + } + /** * Emits the generated HTML page to the {@code outputStream}. * 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 94deb0a3394..90ca26a860e 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 @@ -19,11 +19,12 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; +import com.google.common.io.CharStreams; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import java.io.IOException; +import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; -import java.net.URI; import java.net.URLDecoder; import java.util.HashMap; import java.util.List; @@ -47,19 +48,19 @@ final class ZPageHttpHandler implements HttpHandler { } /** - * Build a query map from the {@code uri}. + * Build a query map from the query string. * - * @param uri the {@link URI} for buiding the query map - * @return the query map built based on the @{code uri} + * @param queryString the query string for buiding the query map. + * @return the query map built based on the query string. */ @VisibleForTesting - static ImmutableMap parseQueryMap(URI uri) throws UnsupportedEncodingException { - String queryStrings = uri.getRawQuery(); - if (queryStrings == null) { + static ImmutableMap parseQueryString(String queryString) + throws UnsupportedEncodingException { + if (queryString == null) { return ImmutableMap.of(); } Map queryMap = new HashMap(); - for (String param : QUERY_SPLITTER.split(queryStrings)) { + for (String param : QUERY_SPLITTER.split(queryString)) { List keyValuePair = QUERY_KEYVAL_SPLITTER.splitToList(param); if (keyValuePair.size() > 1) { if (keyValuePair.get(0).equals(PARAM_SPAN_NAME)) { @@ -75,9 +76,25 @@ static ImmutableMap parseQueryMap(URI uri) throws UnsupportedEnc @Override public final void handle(HttpExchange httpExchange) throws IOException { try { + String requestMethod = httpExchange.getRequestMethod(); httpExchange.sendResponseHeaders(200, 0); - zpageHandler.emitHtml( - parseQueryMap(httpExchange.getRequestURI()), httpExchange.getResponseBody()); + if (requestMethod.equalsIgnoreCase("GET")) { + zpageHandler.emitHtml( + parseQueryString(httpExchange.getRequestURI().getRawQuery()), + httpExchange.getResponseBody()); + } else { + final String queryString; + try (InputStreamReader requestBodyReader = + new InputStreamReader(httpExchange.getRequestBody(), "utf-8")) { + queryString = CharStreams.toString(requestBodyReader); + } + boolean error = + zpageHandler.processRequest( + requestMethod, parseQueryString(queryString), httpExchange.getResponseBody()); + if (!error) { + zpageHandler.emitHtml(parseQueryString(queryString), httpExchange.getResponseBody()); + } + } } finally { httpExchange.close(); } 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 index 7de3b50a90c..aa862a88987 100644 --- 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 @@ -26,6 +26,7 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.Map; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** Unit tests for {@link TraceConfigzZPageHandler}. */ @@ -33,6 +34,31 @@ public final class TraceConfigzZPageHandlerTest { private static final TracerSdkProvider tracerProvider = OpenTelemetrySdk.getTracerProvider(); private static final Map emptyQueryMap = ImmutableMap.of(); + @BeforeEach + void setup() { + // Restore default config + OutputStream output = new ByteArrayOutputStream(); + Map queryMap = ImmutableMap.of("action", "default"); + + TraceConfigzZPageHandler traceConfigzZPageHandler = + new TraceConfigzZPageHandler(tracerProvider); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); + 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 void changeTable_emitRowsCorrectly() { OutputStream output = new ByteArrayOutputStream(); @@ -154,6 +180,7 @@ void appliesChangesCorrectly_formSubmit() { TraceConfigzZPageHandler traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); traceConfigzZPageHandler.emitHtml(queryMap, output); assertThat(tracerProvider.getActiveTraceConfig().getSampler().getDescription()) @@ -179,6 +206,7 @@ void appliesChangesCorrectly_restoreDefault() { TraceConfigzZPageHandler traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); traceConfigzZPageHandler.emitHtml(queryMap, output); assertThat(tracerProvider.getActiveTraceConfig().getSampler().getDescription()) @@ -203,6 +231,7 @@ void appliesChangesCorrectly_doNotCrashOnNullParameters() { TraceConfigzZPageHandler traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); traceConfigzZPageHandler.emitHtml(queryMap, output); assertThat(tracerProvider.getActiveTraceConfig().getSampler().getDescription()) @@ -228,9 +257,9 @@ void applyChanges_emitErrorOnInvalidInput() { Map queryMap = ImmutableMap.of("action", "change", "samplingprobability", "invalid"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("SamplingProbability must be of the type double"); // Invalid samplingProbability (< 0) @@ -238,9 +267,9 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "samplingprobability", "-1"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("probability must be in range [0.0, 1.0]"); // Invalid samplingProbability (> 1) @@ -248,9 +277,9 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "samplingprobability", "1.1"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("probability must be in range [0.0, 1.0]"); // Invalid maxNumOfAttributes @@ -258,9 +287,9 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "maxnumofattributes", "invalid"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("MaxNumOfAttributes must be of the type integer"); // Invalid maxNumOfEvents @@ -268,9 +297,9 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "maxnumofevents", "invalid"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("MaxNumOfEvents must be of the type integer"); // Invalid maxNumLinks @@ -278,9 +307,9 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "maxnumoflinks", "invalid"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("MaxNumOfLinks must be of the type integer"); // Invalid maxNumOfAttributesPerEvent @@ -288,9 +317,9 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "maxnumofattributesperevent", "invalid"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()) .contains("MaxNumOfAttributesPerEvent must be of the type integer"); @@ -299,9 +328,75 @@ void applyChanges_emitErrorOnInvalidInput() { traceConfigzZPageHandler = new TraceConfigzZPageHandler(tracerProvider); queryMap = ImmutableMap.of("action", "change", "maxnumofattributesperlink", "invalid"); - traceConfigzZPageHandler.emitHtml(queryMap, output); + traceConfigzZPageHandler.processRequest("POST", queryMap, output); - assertThat(output.toString()).contains("Error while generating HTML: "); + assertThat(output.toString()).contains("Error while applying trace config changes: "); assertThat(output.toString()).contains("MaxNumOfAttributesPerLink must be of the type integer"); } + + @Test + void applyChanges_shouldNotUpdateOnGetRequest() { + 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"; + + // Apply new config + 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); + + // GET request, Should not apply changes + 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()); + + // POST request, Should apply changes + traceConfigzZPageHandler.processRequest("POST", queryMap, output); + 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)); + } } diff --git a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandlerTest.java b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandlerTest.java index e7e9e9a18c9..4edfbbadeff 100644 --- a/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandlerTest.java +++ b/sdk_extensions/zpages/src/test/java/io/opentelemetry/sdk/extensions/zpages/TracezZPageHandlerTest.java @@ -357,14 +357,15 @@ void spanDetails_shouldNotBreakOnUnknownType() { private static ImmutableMap generateQueryMap( String spanName, String type, String subtype) throws UnsupportedEncodingException, URISyntaxException { - return ZPageHttpHandler.parseQueryMap( + return ZPageHttpHandler.parseQueryString( new URI( - "tracez?zspanname=" - + urlFormParameterEscaper().escape(spanName) - + "&ztype=" - + type - + "&zsubtype=" - + subtype)); + "tracez?zspanname=" + + urlFormParameterEscaper().escape(spanName) + + "&ztype=" + + type + + "&zsubtype=" + + subtype) + .getRawQuery()); } @Test 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 31b225ad966..ce173b2d582 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 @@ -29,14 +29,19 @@ public final class ZPageHttpHandlerTest { @Test void parseEmptyQuery() throws URISyntaxException, UnsupportedEncodingException { URI uri = new URI("http://localhost:8000/tracez"); - assertThat(ZPageHttpHandler.parseQueryMap(uri)).isEmpty(); + String queryString = ""; + assertThat(ZPageHttpHandler.parseQueryString(uri.getRawQuery())).isEmpty(); + assertThat(ZPageHttpHandler.parseQueryString(queryString)).isEmpty(); } @Test void parseNormalQuery() throws URISyntaxException, UnsupportedEncodingException { URI uri = new URI("http://localhost:8000/tracez/tracez?zspanname=Test&ztype=1&zsubtype=5&noval"); - assertThat(ZPageHttpHandler.parseQueryMap(uri)) + String queryString = "zspanname=Test&ztype=1&zsubtype=5&noval"; + assertThat(ZPageHttpHandler.parseQueryString(uri.getRawQuery())) + .containsOnly(entry("zspanname", "Test"), entry("ztype", "1"), entry("zsubtype", "5")); + assertThat(ZPageHttpHandler.parseQueryString(queryString)) .containsOnly(entry("zspanname", "Test"), entry("ztype", "1"), entry("zsubtype", "5")); } }