Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ratpack httpclient #4787

Merged
merged 15 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
plugins {
id("otel.library-instrumentation")
id("otel.nullaway-conventions")
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ratpack;

import io.opentelemetry.instrumentation.ratpack.internal.ContextHolder;
import ratpack.exec.ExecInitializer;
import ratpack.exec.Execution;

final class OpenTelemetryExecInitializer implements ExecInitializer {
public static final ExecInitializer INSTANCE = new OpenTelemetryExecInitializer();

@Override
public void init(Execution execution) {
// Propagates ContextHolder to child execution because the response interceptor is triggered in
// another execution segment
execution
.maybeParent()
.flatMap(parent -> parent.maybeGet(ContextHolder.class))
.ifPresent(execution::add);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ratpack;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.ratpack.internal.ContextHolder;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import ratpack.exec.Execution;
import ratpack.http.client.HttpClient;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;

final class OpenTelemetryHttpClient {

private final Instrumenter<RequestSpec, HttpResponse> instrumenter;

OpenTelemetryHttpClient(Instrumenter<RequestSpec, HttpResponse> instrumenter) {
this.instrumenter = instrumenter;
}

public HttpClient instrument(HttpClient httpClient) throws Exception {
return httpClient.copyWith(
httpClientSpec -> {
httpClientSpec.requestIntercept(
requestSpec -> {
Context parentOtelCtx = Context.current();
if (!instrumenter.shouldStart(parentOtelCtx, requestSpec)) {
return;
}

Context otelCtx = instrumenter.start(parentOtelCtx, requestSpec);
Span span = Span.fromContext(otelCtx);
String path = requestSpec.getUri().getPath();
span.setAttribute(SemanticAttributes.HTTP_ROUTE, path);
Execution.current().add(new ContextHolder(otelCtx, requestSpec));
});

httpClientSpec.responseIntercept(
httpResponse -> {
Execution execution = Execution.current();
ContextHolder contextHolder = execution.get(ContextHolder.class);
execution.remove(ContextHolder.class);
instrumenter.end(
contextHolder.context(), contextHolder.requestSpec(), httpResponse, null);
});

httpClientSpec.errorIntercept(
ex -> {
Execution execution = Execution.current();
ContextHolder contextHolder = execution.get(ContextHolder.class);
execution.remove(ContextHolder.class);
instrumenter.end(contextHolder.context(), contextHolder.requestSpec(), null, ex);
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ratpack;

import io.opentelemetry.instrumentation.api.instrumenter.http.CapturedHttpHeaders;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.List;
import javax.annotation.Nullable;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;

final class RatpackHttpClientAttributesExtractor
extends HttpClientAttributesExtractor<RequestSpec, HttpResponse> {

RatpackHttpClientAttributesExtractor(CapturedHttpHeaders capturedHttpHeaders) {
super(capturedHttpHeaders);
}

@Nullable
@Override
protected String url(RequestSpec requestSpec) {
return requestSpec.getUri().toString();
}

@Nullable
@Override
protected String flavor(RequestSpec requestSpec, @Nullable HttpResponse httpResponse) {
return SemanticAttributes.HttpFlavorValues.HTTP_1_1;
}

@Nullable
@Override
protected String method(RequestSpec requestSpec) {
return requestSpec.getMethod().getName();
}

@Override
protected List<String> requestHeader(RequestSpec requestSpec, String name) {
return requestSpec.getHeaders().getAll(name);
}

@Nullable
@Override
protected Long requestContentLength(
RequestSpec requestSpec, @Nullable HttpResponse httpResponse) {
return null;
}

@Nullable
@Override
protected Long requestContentLengthUncompressed(
RequestSpec requestSpec, @Nullable HttpResponse httpResponse) {
return null;
}

@Nullable
@Override
protected Integer statusCode(RequestSpec requestSpec, HttpResponse httpResponse) {
return httpResponse.getStatusCode();
}

@Nullable
@Override
protected Long responseContentLength(RequestSpec requestSpec, HttpResponse httpResponse) {
return null;
}

@Nullable
@Override
protected Long responseContentLengthUncompressed(
RequestSpec requestSpec, HttpResponse httpResponse) {
return null;
}

@Override
protected List<String> responseHeader(
RequestSpec requestSpec, HttpResponse httpResponse, String name) {
return httpResponse.getHeaders().getAll(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import ratpack.exec.ExecInitializer;
import ratpack.exec.ExecInterceptor;
import ratpack.handling.HandlerDecorator;
import ratpack.http.Request;
import ratpack.http.Response;
import ratpack.http.client.HttpClient;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;
import ratpack.registry.RegistrySpec;

/**
Expand Down Expand Up @@ -42,9 +46,13 @@ public static RatpackTracingBuilder builder(OpenTelemetry openTelemetry) {
}

private final OpenTelemetryServerHandler serverHandler;
private final OpenTelemetryHttpClient httpClientInstrumenter;

RatpackTracing(Instrumenter<Request, Response> serverInstrumenter) {
RatpackTracing(
Instrumenter<Request, Response> serverInstrumenter,
Instrumenter<RequestSpec, HttpResponse> clientInstrumenter) {
serverHandler = new OpenTelemetryServerHandler(serverInstrumenter);
httpClientInstrumenter = new OpenTelemetryHttpClient(clientInstrumenter);
}

/** Returns instance of {@link OpenTelemetryServerHandler} to support Ratpack Registry binding. */
Expand All @@ -57,9 +65,20 @@ public ExecInterceptor getOpenTelemetryExecInterceptor() {
return OpenTelemetryExecInterceptor.INSTANCE;
}

/** Returns instance of {@link ExecInitializer} to support Ratpack Registry binding. */
public ExecInitializer getOpenTelemetryExecInitializer() {
return OpenTelemetryExecInitializer.INSTANCE;
}

/** Configures the {@link RegistrySpec} with OpenTelemetry. */
public void configureServerRegistry(RegistrySpec registry) {
registry.add(HandlerDecorator.prepend(serverHandler));
registry.add(OpenTelemetryExecInterceptor.INSTANCE);
registry.add(OpenTelemetryExecInitializer.INSTANCE);
}

/** Returns instrumented instance of {@link HttpClient} with OpenTelemetry. */
public HttpClient instrumentHttpClient(HttpClient httpClient) throws Exception {
return httpClientInstrumenter.instrument(httpClient);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpServerMetrics;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor;
import io.opentelemetry.instrumentation.ratpack.internal.RatpackHttpNetAttributesExtractor;
import io.opentelemetry.instrumentation.ratpack.internal.RatpackNetAttributesExtractor;
import java.util.ArrayList;
import java.util.List;
import ratpack.http.Request;
import ratpack.http.Response;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;

/** A builder for {@link RatpackTracing}. */
public final class RatpackTracingBuilder {
Expand All @@ -30,6 +33,9 @@ public final class RatpackTracingBuilder {
new ArrayList<>();
private CapturedHttpHeaders capturedHttpHeaders = CapturedHttpHeaders.server(Config.get());

private final List<AttributesExtractor<? super RequestSpec, ? super HttpResponse>>
additionalHttpClientExtractors = new ArrayList<>();

RatpackTracingBuilder(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
}
Expand All @@ -44,6 +50,12 @@ public RatpackTracingBuilder addAttributeExtractor(
return this;
}

public RatpackTracingBuilder addClientAttributeExtractor(
AttributesExtractor<? super RequestSpec, ? super HttpResponse> attributesExtractor) {
additionalHttpClientExtractors.add(attributesExtractor);
return this;
}

/**
* Configure the instrumentation to capture chosen HTTP request and response headers as span
* attributes.
Expand Down Expand Up @@ -72,6 +84,21 @@ public RatpackTracing build() {
.addRequestMetrics(HttpServerMetrics.get())
.newServerInstrumenter(RatpackGetter.INSTANCE);

return new RatpackTracing(instrumenter);
return new RatpackTracing(instrumenter, httpClientInstrumenter());
}

private Instrumenter<RequestSpec, HttpResponse> httpClientInstrumenter() {
RatpackHttpNetAttributesExtractor netAttributes = new RatpackHttpNetAttributesExtractor();
RatpackHttpClientAttributesExtractor httpAttributes =
new RatpackHttpClientAttributesExtractor(capturedHttpHeaders);

return Instrumenter.<RequestSpec, HttpResponse>builder(
openTelemetry, INSTRUMENTATION_NAME, HttpSpanNameExtractor.create(httpAttributes))
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributes))
.addAttributesExtractor(netAttributes)
.addAttributesExtractor(httpAttributes)
.addAttributesExtractors(additionalHttpClientExtractors)
.addRequestMetrics(HttpServerMetrics.get())
.newClientInstrumenter(RequestHeaderSetter.INSTANCE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ratpack;

import io.opentelemetry.context.propagation.TextMapSetter;
import ratpack.api.Nullable;
import ratpack.http.client.RequestSpec;

enum RequestHeaderSetter implements TextMapSetter<RequestSpec> {
INSTANCE;

@Override
public void set(@Nullable RequestSpec carrier, String key, String value) {
if (carrier != null) {
carrier.getHeaders().set(key, value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ratpack.internal;

import io.opentelemetry.context.Context;
import ratpack.http.client.RequestSpec;

public final class ContextHolder {
private final Context context;
private final RequestSpec requestSpec;

public ContextHolder(Context context, RequestSpec requestSpec) {
this.context = context;
this.requestSpec = requestSpec;
}

public Context context() {
return context;
}

public RequestSpec requestSpec() {
return requestSpec;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ratpack.internal;

import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import javax.annotation.Nullable;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;

public final class RatpackHttpNetAttributesExtractor
extends NetClientAttributesExtractor<RequestSpec, HttpResponse> {
@Override
public String transport(RequestSpec request, @Nullable HttpResponse response) {
return SemanticAttributes.NetTransportValues.IP_TCP;
}

@Override
@Nullable
public String peerName(RequestSpec request, @Nullable HttpResponse response) {
return request.getUri().getHost();
}

@Override
public Integer peerPort(RequestSpec request, @Nullable HttpResponse response) {
return request.getUri().getPort();
}

@Override
@Nullable
public String peerIp(RequestSpec request, @Nullable HttpResponse response) {
return null;
}
}
Loading