Skip to content

Instantly share code, notes, and snippets.

@codefromthecrypt
Created December 13, 2016 00:40
Show Gist options
  • Save codefromthecrypt/8078fdc876c5288a6a6939cfd6578255 to your computer and use it in GitHub Desktop.
Save codefromthecrypt/8078fdc876c5288a6a6939cfd6578255 to your computer and use it in GitHub Desktop.
experiment
public final class HttpClient4Instrumentation {
public static class Config extends ClientHandler.Config<HttpRequest, HttpResponse> {
@Override protected Parser<HttpRequest, String> spanNameParser() {
return r -> r.getRequestLine().getMethod();
}
@Override protected TagsParser<HttpRequest> requestTagsParser() {
return TagsParser.builder(HttpRequest.class)
.addParser(TraceKeys.HTTP_URL, input -> input.getRequestLine().getUri())
.build();
}
@Override protected TagsParser<HttpResponse> responseTagsParser() {
return TagsParser.builder(HttpResponse.class)
.addParser(TraceKeys.HTTP_STATUS_CODE, input -> {
int httpStatus = input.getStatusLine().getStatusCode();
return httpStatus < 200 || httpStatus > 299 ? String.valueOf(httpStatus) : null;
}).build();
}
}
// all this still will be set via builder
Span.Factory spanFactory = Tracer.builder().build(); // add reporter etc
ClientHandler<HttpRequest, HttpResponse> clientHandler = ClientHandler.create(new Config());
Injector<HttpMessage> contextInjector = Propagation.B3_STRING.injector(HttpMessage::setHeader);
public HttpRequestInterceptor requestInterceptor() {
return (request, context) -> {
TraceContext parent = null; // TODO! lookup current trace context
Span span = spanFactory.start(parent);
try {
clientHandler.handleSend(request, span);
contextInjector.inject(span.context(), request);
context.setAttribute(ClientHandler.CONTEXT_KEY, span);
} catch (RuntimeException e) {
throw clientHandler.handleError(e, span);
}
};
}
public HttpResponseInterceptor responseInterceptor() {
return (response, context) -> {
Span span = (Span) context.getAttribute(ClientHandler.CONTEXT_KEY);
if (span == null) return;
clientHandler.handleReceive(response, span);
};
}
}
@codefromthecrypt
Copy link
Author

client handler impl

  abstract Parser<Req, String> spanNameParser();

  abstract Parser<Req, Endpoint> serverAddressParser();

  abstract TagsParser<Req> requestTagsParser();

  abstract TagsParser<Resp> responseTagsParser();

  public Req handleSend(Req request, Span span) {
    if (span.isNoop()) return request;

    span.name(spanNameParser().parse(request));
    requestTagsParser().addTagsToSpan(request, span);
    Endpoint serverAddress = serverAddressParser().parse(request);
    if (serverAddress != null) {
      span.addAddress(Constants.SERVER_ADDR, serverAddress);
    }
    span.addAnnotation(Constants.CLIENT_SEND);
    return request;
  }

  public Resp handleReceive(Resp response, Span span) {
    if (span.isNoop()) return response;

    try {
      return responseTagsParser().addTagsToSpan(response, span);
    } finally {
      span.addAnnotation(Constants.CLIENT_RECV);
      span.finish();
    }
  }

  public <T extends Throwable> T handleError(T throwable, Span span) {
    if (span.isNoop()) return throwable;

    try {
      span.addTag(Constants.ERROR, throwable.getMessage());
      return throwable;
    } finally {
      span.finish();
    }
  }

  ClientHandler() {
  }
}

@codefromthecrypt
Copy link
Author

teach how to learn a key regardless if that's from a request or response type

/**
   * Implement this to teach TagsParser how to parse a key, potentially across multiple
   * libraries.
   */
  public interface TagParserFactory {
    /**
     * Returns a parser that can handle the input type and the binary annotation key or null if
     * unsupported
     *
     * @param type the class of the input to {@link Parser#parse(Object)}, typically a request or
     * response object.
     * @param key the {@link KeyValueAnnotation#getKey()} which the parser will create.
     */
    Parser<?, String> create(Class<?> type, String key);
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment