Remove Guava from zpages (#2150)

* Remove Guava from zpages

* Clean
This commit is contained in:
Anuraag Agrawal 2020-12-01 14:14:36 +09:00 committed by GitHub
parent 7ed6d8d4e4
commit af77508af7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 103 deletions

View File

@ -13,7 +13,7 @@ dependencies {
implementation project(':opentelemetry-api'),
project(':opentelemetry-sdk')
implementation libraries.guava
testImplementation libraries.guava
compileOnly 'com.sun.net.httpserver:http:20070405'
}

View File

@ -5,33 +5,28 @@
package io.opentelemetry.sdk.extension.zpages;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.data.SpanData;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
final class TracezSpanBuckets {
private final ImmutableMap<LatencyBoundary, SpanBucket> latencyBuckets;
private final ImmutableMap<StatusCode, SpanBucket> errorBuckets;
private final Map<LatencyBoundary, SpanBucket> latencyBuckets = new HashMap<>();
private final Map<StatusCode, SpanBucket> errorBuckets = new HashMap<>();
TracezSpanBuckets() {
ImmutableMap.Builder<LatencyBoundary, SpanBucket> latencyBucketsBuilder =
ImmutableMap.builder();
for (LatencyBoundary bucket : LatencyBoundary.values()) {
latencyBucketsBuilder.put(bucket, new SpanBucket(/* isLatencyBucket= */ true));
latencyBuckets.put(bucket, new SpanBucket(/* isLatencyBucket= */ true));
}
latencyBuckets = latencyBucketsBuilder.build();
ImmutableMap.Builder<StatusCode, SpanBucket> errorBucketsBuilder = ImmutableMap.builder();
for (StatusCode code : StatusCode.values()) {
if (code == StatusCode.ERROR) {
errorBucketsBuilder.put(code, new SpanBucket(/* isLatencyBucket= */ false));
errorBuckets.put(code, new SpanBucket(/* isLatencyBucket= */ false));
}
}
errorBuckets = errorBucketsBuilder.build();
}
void addToBucket(ReadableSpan span) {

View File

@ -5,11 +5,6 @@
package io.opentelemetry.sdk.extension.zpages;
import static com.google.common.html.HtmlEscapers.htmlEscaper;
import static com.google.common.net.UrlEscapers.urlFormParameterEscaper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.common.AttributeConsumer;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.ReadableAttributes;
@ -19,11 +14,11 @@ import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.SpanData.Event;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Formatter;
import java.util.HashMap;
@ -34,6 +29,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
final class TracezZPageHandler extends ZPageHandler {
@ -90,7 +86,7 @@ final class TracezZPageHandler extends ZPageHandler {
// * for error based sampled spans [0, 15], 0 means all, otherwise the error code
private static final String PARAM_SAMPLE_SUB_TYPE = "zsubtype";
// Map from LatencyBoundary to human readable string on the UI
private static final ImmutableMap<LatencyBoundary, String> LATENCY_BOUNDARIES_STRING_MAP =
private static final Map<LatencyBoundary, String> LATENCY_BOUNDARIES_STRING_MAP =
buildLatencyBoundaryStringMap();
private static final Logger logger = Logger.getLogger(TracezZPageHandler.class.getName());
@Nullable private final TracezDataAggregator dataAggregator;
@ -172,7 +168,14 @@ final class TracezZPageHandler extends ZPageHandler {
// If numOfSamples is smaller than 0, print the text "N/A", otherwise print the text "0"
if (numOfSamples > 0) {
out.print("<td class=\"align-center border-left-dark\"><a href=\"?");
out.print(PARAM_SPAN_NAME + "=" + urlFormParameterEscaper().escape(spanName));
try {
out.print(
PARAM_SPAN_NAME
+ "="
+ URLEncoder.encode(spanName, StandardCharsets.UTF_8.toString()).replace(' ', '+'));
} catch (UnsupportedEncodingException e) {
// Can't happen
}
out.print("&" + PARAM_SAMPLE_TYPE + "=" + type.getValue());
out.print("&" + PARAM_SAMPLE_SUB_TYPE + "=" + subtype);
out.print("\">" + numOfSamples + "</a></td>");
@ -210,7 +213,7 @@ final class TracezZPageHandler extends ZPageHandler {
out.print("<tr>");
}
zebraStripe = !zebraStripe;
out.print("<td>" + htmlEscaper().escape(spanName) + "</td>");
out.print("<td>" + escapeHtml(spanName) + "</td>");
// Running spans column
int numOfRunningSpans =
@ -241,8 +244,7 @@ final class TracezZPageHandler extends ZPageHandler {
private static void emitSpanNameAndCount(
PrintStream out, String spanName, int count, SampleType type) {
out.print(
"<p class=\"align-center\"><b> Span Name: " + htmlEscaper().escape(spanName) + "</b></p>");
out.print("<p class=\"align-center\"><b> Span Name: " + escapeHtml(spanName) + "</b></p>");
String typeString =
type == SampleType.RUNNING
? "running"
@ -306,8 +308,10 @@ final class TracezZPageHandler extends ZPageHandler {
int lastEntryDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
long lastEpochNanos = span.getStartEpochNanos();
List<Event> timedEvents = new ArrayList<>(span.getEvents());
Collections.sort(timedEvents, new EventComparator());
List<Event> timedEvents =
span.getEvents().stream()
.sorted(Comparator.comparingLong(Event::getEpochNanos))
.collect(Collectors.toList());
for (Event event : timedEvents) {
calendar.setTimeInMillis(TimeUnit.NANOSECONDS.toMillis(event.getEpochNanos()));
formatter.format(
@ -326,9 +330,9 @@ final class TracezZPageHandler extends ZPageHandler {
zebraStripe ? ZEBRA_STRIPE_COLOR : "#fff");
SpanData.Status status = span.getStatus();
if (status != null) {
formatter.format("%s | ", htmlEscaper().escape(status.toString()));
formatter.format("%s | ", escapeHtml(status.toString()));
}
formatter.format("%s</pre></td>", htmlEscaper().escape(renderAttributes(span.getAttributes())));
formatter.format("%s</pre></td>", escapeHtml(renderAttributes(span.getAttributes())));
zebraStripe = !zebraStripe;
return zebraStripe;
}
@ -375,7 +379,7 @@ final class TracezZPageHandler extends ZPageHandler {
calendar.get(Calendar.SECOND),
microsField,
deltaString,
htmlEscaper().escape(renderEvent(event)));
escapeHtml(renderEvent(event)));
}
private static String renderAttributes(ReadableAttributes attributes) {
@ -475,7 +479,9 @@ final class TracezZPageHandler extends ZPageHandler {
if (spans != null) {
Formatter formatter = new Formatter(out, Locale.US);
spans =
ImmutableList.sortedCopyOf(new SpanDataComparator(/* incremental= */ true), spans);
spans.stream()
.sorted(Comparator.comparingLong(SpanData::getStartEpochNanos))
.collect(Collectors.toList());
emitSpanDetails(out, formatter, spans);
}
}
@ -540,41 +546,55 @@ final class TracezZPageHandler extends ZPageHandler {
throw new IllegalArgumentException("No value string available for: " + latencyBoundary);
}
private static ImmutableMap<LatencyBoundary, String> buildLatencyBoundaryStringMap() {
private static Map<LatencyBoundary, String> buildLatencyBoundaryStringMap() {
Map<LatencyBoundary, String> latencyBoundaryMap = new HashMap<>();
for (LatencyBoundary latencyBoundary : LatencyBoundary.values()) {
latencyBoundaryMap.put(latencyBoundary, latencyBoundaryToString(latencyBoundary));
}
return ImmutableMap.copyOf(latencyBoundaryMap);
return latencyBoundaryMap;
}
private static final class EventComparator implements Comparator<Event>, Serializable {
private static final long serialVersionUID = 0;
@Override
public int compare(Event e1, Event e2) {
return Long.compare(e1.getEpochNanos(), e2.getEpochNanos());
private static String escapeHtml(String html) {
StringBuilder escaped = null;
for (int i = 0; i < html.length(); i++) {
char c = html.charAt(i);
switch (c) {
case '"':
escaped = lazyStringBuilder(escaped, html, i);
escaped.append("&quot;");
break;
case '\'':
escaped = lazyStringBuilder(escaped, html, i);
escaped.append("&#39;");
break;
case '&':
escaped = lazyStringBuilder(escaped, html, i);
escaped.append("&amp;");
break;
case '<':
escaped = lazyStringBuilder(escaped, html, i);
escaped.append("&lt;");
break;
case '>':
escaped = lazyStringBuilder(escaped, html, i);
escaped.append("&gt;");
break;
default:
if (escaped != null) {
escaped.append(c);
}
}
}
return escaped != null ? escaped.toString() : html;
}
private static final class SpanDataComparator implements Comparator<SpanData>, Serializable {
private static final long serialVersionUID = 0;
private final boolean incremental;
/**
* Returns a new {@code SpanDataComparator}.
*
* @param incremental {@code true} if sorting spans incrementally
*/
private SpanDataComparator(boolean incremental) {
this.incremental = incremental;
}
@Override
public int compare(SpanData s1, SpanData s2) {
return incremental
? Long.compare(s1.getStartEpochNanos(), s2.getStartEpochNanos())
: Long.compare(s2.getStartEpochNanos(), s1.getEndEpochNanos());
private static StringBuilder lazyStringBuilder(
@Nullable StringBuilder sb, String str, int currentCharIdx) {
if (sb != null) {
return sb;
}
sb = new StringBuilder(str.length());
sb.append(str.substring(0, currentCharIdx - 1));
return sb;
}
}

View File

@ -5,27 +5,22 @@
package io.opentelemetry.sdk.extension.zpages;
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.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** 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().omitEmptyStrings();
// Splitter for splitting URL query parameters' key value
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)
@ -42,24 +37,36 @@ final class ZPageHttpHandler implements HttpHandler {
* @param queryString the query string for buiding the query map.
* @return the query map built based on the query string.
*/
@VisibleForTesting
static ImmutableMap<String, String> parseQueryString(String queryString)
throws UnsupportedEncodingException {
// Visible for testing
static Map<String, String> parseQueryString(String queryString) {
if (queryString == null) {
return ImmutableMap.of();
return Collections.emptyMap();
}
Map<String, String> queryMap = new HashMap<String, String>();
for (String param : QUERY_SPLITTER.split(queryString)) {
List<String> keyValuePair = QUERY_KEYVAL_SPLITTER.splitToList(param);
if (keyValuePair.size() > 1) {
if (keyValuePair.get(0).equals(PARAM_SPAN_NAME)) {
queryMap.put(keyValuePair.get(0), URLDecoder.decode(keyValuePair.get(1), "UTF-8"));
} else {
queryMap.put(keyValuePair.get(0), keyValuePair.get(1));
}
}
}
return ImmutableMap.copyOf(queryMap);
Arrays.stream(queryString.split("&"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.forEach(
param -> {
List<String> keyValuePair =
Arrays.stream(param.split("="))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
if (keyValuePair.size() > 1) {
if (keyValuePair.get(0).equals(PARAM_SPAN_NAME)) {
try {
queryMap.put(
keyValuePair.get(0), URLDecoder.decode(keyValuePair.get(1), "UTF-8"));
} catch (UnsupportedEncodingException e) {
// Ignore encoding exception.
}
} else {
queryMap.put(keyValuePair.get(0), keyValuePair.get(1));
}
}
});
return Collections.unmodifiableMap(queryMap);
}
@Override
@ -73,9 +80,10 @@ final class ZPageHttpHandler implements HttpHandler {
httpExchange.getResponseBody());
} else {
final String queryString;
try (InputStreamReader requestBodyReader =
new InputStreamReader(httpExchange.getRequestBody(), "utf-8")) {
queryString = CharStreams.toString(requestBodyReader);
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(httpExchange.getRequestBody(), "utf-8"))) {
// Query strings can only have one line
queryString = reader.readLine();
}
boolean error =
zpageHandler.processRequest(

View File

@ -5,9 +5,10 @@
package io.opentelemetry.sdk.extension.zpages;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -22,14 +23,17 @@ final class ZPageLogo {
* @return OpenTelemetry logo in base64 encoding.
*/
public static String getLogoBase64() {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
InputStream in = ZPageLogo.class.getClassLoader().getResourceAsStream("logo.png");
byte[] bytes = ByteStreams.toByteArray(in);
return BaseEncoding.base64().encode(bytes);
try (InputStream is = ZPageLogo.class.getClassLoader().getResourceAsStream("logo.png")) {
readTo(is, os);
}
} catch (Throwable t) {
logger.log(Level.WARNING, "error while getting OpenTelemetry Logo", t);
return "";
}
byte[] bytes = os.toByteArray();
return Base64.getEncoder().encodeToString(bytes);
}
/**
@ -38,14 +42,21 @@ final class ZPageLogo {
* @return OpenTelemetry favicon in base64 encoding.
*/
public static String getFaviconBase64() {
try {
InputStream in = ZPageLogo.class.getClassLoader().getResourceAsStream("favicon.png");
byte[] bytes = ByteStreams.toByteArray(in);
return BaseEncoding.base64().encode(bytes);
ByteArrayOutputStream os = new ByteArrayOutputStream();
try (InputStream is = ZPageLogo.class.getClassLoader().getResourceAsStream("favicon.png")) {
readTo(is, os);
} catch (Throwable t) {
logger.log(Level.WARNING, "error while getting OpenTelemetry Logo", t);
return "";
}
byte[] bytes = os.toByteArray();
return Base64.getEncoder().encodeToString(bytes);
}
private static void readTo(InputStream is, ByteArrayOutputStream os) throws IOException {
int b;
while ((b = is.read()) != -1) {
os.write(b);
}
}
}

View File

@ -5,15 +5,12 @@
package io.opentelemetry.sdk.extension.zpages;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.sun.net.httpserver.HttpServer;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.TracerSdkManagement;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
@ -68,7 +65,7 @@ public final class ZPageServer {
new TraceConfigzZPageHandler(TRACER_SDK_MANAGEMENT);
// Handler for index page, **please include all available ZPageHandlers in the constructor**
private static final ZPageHandler indexZPageHandler =
new IndexZPageHandler(ImmutableList.of(tracezZPageHandler, traceConfigzZPageHandler));
new IndexZPageHandler(Arrays.asList(tracezZPageHandler, traceConfigzZPageHandler));
private static final Object mutex = new Object();
private static final AtomicBoolean isTracezSpanProcesserAdded = new AtomicBoolean(false);
@ -166,7 +163,9 @@ public final class ZPageServer {
*/
public static void startHttpServerAndRegisterAllPages(int port) throws IOException {
synchronized (mutex) {
checkState(server == null, "The HttpServer is already started.");
if (server != null) {
throw new IllegalStateException("The HttpServer is already started.");
}
server = HttpServer.create(new InetSocketAddress(port), HTTPSERVER_BACKLOG);
ZPageServer.registerAllPagesToHttpServer(server);
server.start();
@ -187,7 +186,7 @@ public final class ZPageServer {
*
* @return the boolean indicating if TracezSpanProcessor is added.
*/
@VisibleForTesting
// Visible for testing
static boolean getIsTracezSpanProcesserAdded() {
return isTracezSpanProcesserAdded.get();
}

View File

@ -343,9 +343,8 @@ class TracezZPageHandlerTest {
assertThat(output.toString()).doesNotContain("<b> Span Name: Span</b>");
}
private static ImmutableMap<String, String> generateQueryMap(
String spanName, String type, String subtype)
throws UnsupportedEncodingException, URISyntaxException {
private static Map<String, String> generateQueryMap(String spanName, String type, String subtype)
throws URISyntaxException {
return ZPageHttpHandler.parseQueryString(
new URI(
"tracez?zspanname="