Skip to content

Commit

Permalink
Enhance OpenCensus tracing instrumentation.
Browse files Browse the repository at this point in the history
* Add method and instrumentation to record message events with content-length when sending/receiving
request/response.

* Refine API, add `volatile` modifiers to internal attributes.

* Use `registerSpanNamesForCollection` to replace `setSampleToLocalSpanStore`.

* Add get/set method for recordEvent so users can controll whether to expose collected spans.

* Add more tests and rewrite/fix test cases with JUnit 3 style.

* Fix/refine javadoc and messages.
  • Loading branch information
HailongWen authored and mattwhisenhunt committed Feb 6, 2018
1 parent 2f67205 commit d93eb99
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,6 @@ static String executeAndGetValueOfSomeCustomHeader(HttpRequest request) {
/** OpenCensus tracing component. */
private Tracer tracer = OpenCensusUtils.getTracer();

/** Prefix for tracing span name. */
private static final String traceSpanNamePrefix = "Sent." + HttpRequest.class.getName() + ".";

/**
* @param transport HTTP transport
* @param requestMethod HTTP request method or {@code null} for none
Expand Down Expand Up @@ -865,9 +862,12 @@ public HttpResponse execute() throws IOException {
Preconditions.checkNotNull(requestMethod);
Preconditions.checkNotNull(url);

Span span = tracer.spanBuilder(traceSpanNamePrefix + "execute").startSpan();
Span span = tracer
.spanBuilder(OpenCensusUtils.SPAN_NAME_HTTP_REQUEST_EXECUTE)
.setRecordEvents(OpenCensusUtils.isRecordEvent())
.startSpan();
do {
span.addAnnotation("retry #" + numRetries);
span.addAnnotation("retry #" + (numRetries - retriesRemaining));
// Cleanup any unneeded response from a previous iteration
if (response != null) {
response.ignore();
Expand Down Expand Up @@ -911,7 +911,7 @@ public HttpResponse execute() throws IOException {
headers.setUserAgent(originalUserAgent + " " + USER_AGENT_SUFFIX);
}
}
OpenCensusUtils.propagateTracingContext(headers);
OpenCensusUtils.propagateTracingContext(span, headers);

// headers
HttpHeaders.serializeHeaders(headers, logbuf, curlbuf, logger, lowLevelHttpRequest);
Expand Down Expand Up @@ -994,8 +994,12 @@ public HttpResponse execute() throws IOException {
lowLevelHttpRequest.setTimeout(connectTimeout, readTimeout);
// switch tracing scope to current span
Scope ws = tracer.withSpan(span);
OpenCensusUtils.recordSentMessageEvent(span, lowLevelHttpRequest.getContentLength());
try {
LowLevelHttpResponse lowLevelHttpResponse = lowLevelHttpRequest.execute();
if (lowLevelHttpResponse != null) {
OpenCensusUtils.recordReceivedMessageEvent(span, lowLevelHttpResponse.getContentLength());
}
// Flag used to indicate if an exception is thrown before the response is constructed.
boolean responseConstructed = false;
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@
package com.google.api.client.util;

import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpStatusCodes;
import com.google.common.annotations.VisibleForTesting;

import io.opencensus.contrib.http.util.HttpPropagationUtil;
import io.opencensus.trace.BlankSpan;
import io.opencensus.trace.EndSpanOptions;
import io.opencensus.trace.NetworkEvent;
import io.opencensus.trace.NetworkEvent.Type;
import io.opencensus.trace.Span;
import io.opencensus.trace.Status;
import io.opencensus.trace.Tracer;
import io.opencensus.trace.Tracing;
import io.opencensus.trace.propagation.TextFormat;

import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
Expand All @@ -42,26 +48,49 @@ public class OpenCensusUtils {

private static final Logger LOGGER = Logger.getLogger(OpenCensusUtils.class.getName());

/**
* Span name for tracing {@link HttpRequest#execute()}.
*/
public static final String SPAN_NAME_HTTP_REQUEST_EXECUTE =
"Sent." + HttpRequest.class.getName() + ".execute";

/**
* OpenCensus tracing component.
* When no OpenCensus implementation is provided, it will return a no-op tracer.
*/
static Tracer tracer = Tracing.getTracer();
private static Tracer tracer = Tracing.getTracer();

/**
* Sequence id generator for message event.
*/
private static AtomicLong idGenerator = new AtomicLong();

/**
* Whether spans should be recorded locally. Defaults to true.
*/
private static volatile boolean isRecordEvent = true;

/**
* {@link TextFormat} used in tracing context propagation.
*/
@Nullable
static TextFormat propagationTextFormat = null;
@VisibleForTesting
static volatile TextFormat propagationTextFormat = null;

/**
* {@link TextFormat.Setter} for {@link activeTextFormat}.
* {@link TextFormat.Setter} for {@link #propagationTextFormat}.
*/
@Nullable
static TextFormat.Setter propagationTextFormatSetter = null;
@VisibleForTesting
static volatile TextFormat.Setter propagationTextFormatSetter = null;

/**
* Sets the {@link TextFormat} used in context propagation.
*
* <p>This API allows users of google-http-client to specify other text format, or disable context
* propagation by setting it to {@code null}. It should be used along with {@link
* #setPropagationTextFormatSetter} for setting purpose. </p>
*
* @param textFormat the text format.
*/
public static void setPropagationTextFormat(@Nullable TextFormat textFormat) {
Expand All @@ -70,12 +99,28 @@ public static void setPropagationTextFormat(@Nullable TextFormat textFormat) {

/**
* Sets the {@link TextFormat.Setter} used in context propagation.
*
* <p>This API allows users of google-http-client to specify other text format setter, or disable
* context propagation by setting it to {@code null}. It should be used along with {@link
* #setPropagationTextFormat} for setting purpose. </p>
*
* @param textFormatSetter the {@code TextFormat.Setter} for the text format.
*/
public static void setPropagationTextFormatSetter(@Nullable TextFormat.Setter textFormatSetter) {
propagationTextFormatSetter = textFormatSetter;
}

/**
* Sets whether spans should be recorded locally.
*
* <p> This API allows users of google-http-client to turn on/off local span collection. </p>
*
* @param recordEvent record span locally if true.
*/
public static void setIsRecordEvent(boolean recordEvent) {
isRecordEvent = recordEvent;
}

/**
* Returns the tracing component of OpenCensus.
*
Expand All @@ -85,15 +130,27 @@ public static Tracer getTracer() {
return tracer;
}

/**
* Returns whether spans should be recorded locally.
*
* @return whether spans should be recorded locally.
*/
public static boolean isRecordEvent() {
return isRecordEvent;
}

/**
* Propagate information of current tracing context. This information will be injected into HTTP
* header.
*
* @param span the span to be propagated.
* @param headers the headers used in propagation.
*/
public static void propagateTracingContext(HttpHeaders headers) {
Preconditions.checkNotNull(headers);
public static void propagateTracingContext(Span span, HttpHeaders headers) {
Preconditions.checkArgument(span != null, "span should not be null.");
Preconditions.checkArgument(headers != null, "headers should not be null.");
if (propagationTextFormat != null && propagationTextFormatSetter != null) {
Span span = tracer.getCurrentSpan();
if (span != null && !span.equals(BlankSpan.INSTANCE)) {
if (!span.equals(BlankSpan.INSTANCE)) {
propagationTextFormat.inject(span.getContext(), headers, propagationTextFormatSetter);
}
}
Expand All @@ -107,7 +164,7 @@ public static void propagateTracingContext(HttpHeaders headers) {
*/
public static EndSpanOptions getEndSpanOptions(@Nullable Integer statusCode) {
// Always sample the span, but optionally export it.
EndSpanOptions.Builder builder = EndSpanOptions.builder().setSampleToLocalSpanStore(true);
EndSpanOptions.Builder builder = EndSpanOptions.builder();
if (statusCode == null) {
builder.setStatus(Status.UNKNOWN);
} else if (!HttpStatusCodes.isSuccess(statusCode)) {
Expand Down Expand Up @@ -139,6 +196,48 @@ public static EndSpanOptions getEndSpanOptions(@Nullable Integer statusCode) {
return builder.build();
}

/**
* Records a new message event which contains the size of the request content.
* Note that the size represents the message size in application layer, i.e., content-length.
*
* @param span The {@code span} in which the send event occurs.
* @param size Size of the request.
*/
public static void recordSentMessageEvent(Span span, long size) {
recordMessageEvent(span, size, Type.SENT);
}

/**
* Records a new message event which contains the size of the response content.
* Note that the size represents the message size in application layer, i.e., content-length.
*
* @param span The {@code span} in which the receive event occurs.
* @param size Size of the response.
*/
public static void recordReceivedMessageEvent(Span span, long size) {
recordMessageEvent(span, size, Type.RECV);
}

/**
* Records a message event of a certain {@link NetowrkEvent.Type}.
* This method is package protected since {@link NetworkEvent} might be deprecated in future
* releases.
*
* @param span The {@code span} in which the event occurs.
* @param size Size of the message.
* @param eventType The {@code NetworkEvent.Type} of the message event.
*/
@VisibleForTesting
static void recordMessageEvent(Span span, long size, Type eventType) {
Preconditions.checkArgument(span != null, "span should not be null.");
if (size < 0) size = 0;
NetworkEvent event = NetworkEvent
.builder(eventType, idGenerator.getAndIncrement())
.setUncompressedMessageSize(size)
.build();
span.addNetworkEvent(event);
}

static {
try {
propagationTextFormat = HttpPropagationUtil.getCloudTraceFormat();
Expand All @@ -149,7 +248,16 @@ public void put(HttpHeaders carrier, String key, String value) {
}
};
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Cannot initiate OpenCensus modules, tracing disabled", e);
LOGGER.log(
Level.WARNING, "Cannot initialize default OpenCensus HTTP propagation text format.", e);
}

try {
Tracing.getExportComponent().getSampledSpanStore().registerSpanNamesForCollection(
Collections.<String>singletonList(SPAN_NAME_HTTP_REQUEST_EXECUTE));
} catch (Exception e) {
LOGGER.log(
Level.WARNING, "Cannot register default OpenCensus span names for collection.", e);
}
}

Expand Down
Loading

0 comments on commit d93eb99

Please sign in to comment.