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

Next attempt to extract common logic from instrumentations. #523

Merged
merged 7 commits into from
Jun 17, 2020
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
Expand Up @@ -20,6 +20,15 @@
*
* <p>For example, this can be used to track nested calls to super() in constructors by calling
* #incrementCallDepth at the beginning of each constructor.
*
* <p>This works the following way. When you enter some method that you want to track, you call
* {@link #incrementCallDepth} method. If returned number is larger than 0, then you have already
* been in this method and are in recursive call now. When you then leave the method, you call
* {@link #decrementCallDepth} method. If returned number is larger than 0, then you have already
* been in this method and are in recursive call now.
*
* <p>In short, the semantic of both methods is the same: they will return value 0 if and only if
* current method invocation is the first one for the current call stack.
*/
public class CallDepthThreadLocalMap {

Expand All @@ -31,24 +40,36 @@ protected ThreadLocalDepth computeValue(Class<?> type) {
}
};

public static Depth getCallDepth(final Class<?> k) {
return TLS.get(k).get();
}

public static int incrementCallDepth(final Class<?> k) {
return TLS.get(k).get().increment();
return TLS.get(k).get().getAndIncrement();
}

public static int decrementCallDepth(final Class<?> k) {
trask marked this conversation as resolved.
Show resolved Hide resolved
return TLS.get(k).get().decrementAndGet();
}

public static void reset(final Class<?> k) {
TLS.get(k).get().depth = 0;
}

private static final class Depth {
public static final class Depth {
private int depth;

private Depth() {
this.depth = 0;
}

private int increment() {
public int getAndIncrement() {
return this.depth++;
}

public int decrementAndGet() {
return --this.depth;
}
}

private static final class ThreadLocalDepth extends ThreadLocal<Depth> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public Span onPeerConnection(final Span span, final InetSocketAddress remoteConn
if (remoteConnection != null) {
onPeerConnection(span, remoteConnection.getAddress());

span.setAttribute(MoreTags.NET_PEER_NAME, remoteConnection.getHostName());
span.setAttribute(MoreTags.NET_PEER_PORT, remoteConnection.getPort());
}
return span;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.trace.Span;

/** @deprecated use {@link DatabaseClientTracer} instead. */
@Deprecated
public abstract class DatabaseClientDecorator<CONNECTION> extends ClientDecorator {

protected abstract String dbType();
Expand All @@ -38,13 +40,7 @@ public Span afterStart(final Span span) {
return super.afterStart(span);
}

/**
* This should be called when the connection is being used, not when it's created.
*
* @param span
* @param connection
* @return
*/
/** This should be called when the connection is being used, not when it's created. */
public Span onConnection(final Span span, final CONNECTION connection) {
assert span != null;
if (connection != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.bootstrap.instrumentation.decorator;

import static io.opentelemetry.trace.Span.Kind.CLIENT;

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.instrumentation.api.MoreTags;
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Status;
import io.opentelemetry.trace.Tracer;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutionException;

public abstract class DatabaseClientTracer<CONNECTION, QUERY> {
private static final String DB_QUERY = "DB Query";

protected final Tracer tracer;

public DatabaseClientTracer() {
tracer = OpenTelemetry.getTracerProvider().get(getInstrumentationName(), getVersion());
}

// TODO make abstract when implemented in all subclasses
protected String getInstrumentationName() {
return null;
}

private String getVersion() {
return null;
}

public Scope withSpan(Span span) {
return tracer.withSpan(span);
}

public Span startSpan(CONNECTION connection, QUERY query, String originType) {
String normalizedQuery = normalizeQuery(query);

final Span span =
tracer
.spanBuilder(spanName(normalizedQuery))
.setSpanKind(CLIENT)
.setAttribute(Tags.DB_TYPE, dbType())
.setAttribute("span.origin.type", originType)
.startSpan();

onConnection(span, connection);
onPeerConnection(span, connection);
onStatement(span, normalizedQuery);

return span;
}

public void end(Span span) {
span.end();
}

public void endExceptionally(Span span, Throwable throwable) {
onError(span, throwable);
end(span);
}

protected Span afterStart(final Span span) {
assert span != null;
span.setAttribute(Tags.DB_TYPE, dbType());
return span;
}

/** This should be called when the connection is being used, not when it's created. */
public Span onConnection(final Span span, final CONNECTION connection) {
assert span != null;
if (connection != null) {
span.setAttribute(Tags.DB_USER, dbUser(connection));
span.setAttribute(Tags.DB_INSTANCE, dbInstance(connection));
span.setAttribute(Tags.DB_URL, dbUrl(connection));
}
return span;
}

public Span onError(final Span span, final Throwable throwable) {
assert span != null;
if (throwable != null) {
span.setStatus(Status.UNKNOWN);
addThrowable(
span, throwable instanceof ExecutionException ? throwable.getCause() : throwable);
}
return span;
}

public static void addThrowable(final Span span, final Throwable throwable) {
span.setAttribute(MoreTags.ERROR_MSG, throwable.getMessage());
span.setAttribute(MoreTags.ERROR_TYPE, throwable.getClass().getName());

final StringWriter errorString = new StringWriter();
throwable.printStackTrace(new PrintWriter(errorString));
span.setAttribute(MoreTags.ERROR_STACK, errorString.toString());
}

protected void onPeerConnection(Span span, final CONNECTION connection) {
onPeerConnection(span, peerAddress(connection));
}

protected void onPeerConnection(final Span span, final InetSocketAddress remoteConnection) {
assert span != null;
if (remoteConnection != null) {
onPeerConnection(span, remoteConnection.getAddress());

span.setAttribute(MoreTags.NET_PEER_PORT, remoteConnection.getPort());
}
}

protected void onPeerConnection(final Span span, final InetAddress remoteAddress) {
assert span != null;
if (remoteAddress != null) {
span.setAttribute(MoreTags.NET_PEER_NAME, remoteAddress.getHostName());
span.setAttribute(MoreTags.NET_PEER_IP, remoteAddress.getHostAddress());
}
}

public void onStatement(final Span span, final String statement) {
assert span != null;
span.setAttribute(Tags.DB_STATEMENT, statement);
}

// TODO: "When it's impossible to get any meaningful representation of the span name, it can be
// populated using the same value as db.instance" (c) spec
protected String spanName(final String query) {
return query == null ? DB_QUERY : query;
}

protected abstract String normalizeQuery(QUERY query);

protected abstract String dbType();

protected abstract String dbUser(CONNECTION connection);

protected abstract String dbInstance(CONNECTION connection);

// TODO make abstract after implementing in all subclasses
protected String dbUrl(final CONNECTION connection) {
return null;
}

protected abstract InetSocketAddress peerAddress(CONNECTION connection);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public HttpServerTracer() {
tracer = OpenTelemetry.getTracerProvider().get(getInstrumentationName(), getVersion());
}

protected abstract String getInstrumentationName();

protected abstract String getVersion();

public Span startSpan(REQUEST request, Method origin, String originType) {
final Span.Builder builder =
tracer
Expand All @@ -66,10 +70,6 @@ public Span startSpan(REQUEST request, Method origin, String originType) {
return span;
}

protected abstract String getVersion();

protected abstract String getInstrumentationName();

protected void onConnection(Span span, REQUEST request) {
SemanticAttributes.NET_PEER_IP.set(span, peerHostIP(request));
final Integer port = peerPort(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CallDepthThreadLocalMapTest extends Specification {
setup:
Class<?> k1 = String
Class<?> k2 = Integer
Class<?> k3 = Double

expect:
CallDepthThreadLocalMap.incrementCallDepth(k1) == 0
Expand All @@ -46,5 +47,17 @@ class CallDepthThreadLocalMapTest extends Specification {

CallDepthThreadLocalMap.incrementCallDepth(k1) == 1
CallDepthThreadLocalMap.incrementCallDepth(k2) == 1

expect:
CallDepthThreadLocalMap.decrementCallDepth(k1) == 1
CallDepthThreadLocalMap.decrementCallDepth(k2) == 1

CallDepthThreadLocalMap.decrementCallDepth(k1) == 0
CallDepthThreadLocalMap.decrementCallDepth(k2) == 0

and:
CallDepthThreadLocalMap.incrementCallDepth(k3) == 0
CallDepthThreadLocalMap.decrementCallDepth(k3) == 0

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ class BaseDecoratorTest extends AgentSpecification {

then:
if (connection.getAddress()) {
2 * span.setAttribute(MoreTags.NET_PEER_NAME, connection.hostName)
1 * span.setAttribute(MoreTags.NET_PEER_NAME, connection.hostName)
1 * span.setAttribute(MoreTags.NET_PEER_IP, connection.address.hostAddress)
} else {
1 * span.setAttribute(MoreTags.NET_PEER_NAME, connection.hostName)
0 * span.setAttribute(MoreTags.NET_PEER_NAME, connection.hostName)
}
1 * span.setAttribute(MoreTags.NET_PEER_PORT, connection.port)
0 * _
Expand Down
Loading