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

Fixed some locations to not touch exceptions when safe_exception is configured #3543

Merged
merged 6 commits into from Mar 5, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Expand Up @@ -39,6 +39,7 @@ Use subheadings with the "=====" level for adding notes for unreleased changes:
[float]
===== Bug fixes
* Added missing support for TracerBuilder in OpenTelemetry bridge - {pull}3535[#3535]
* Fixed some locations to not touch exceptions when `safe_exception` is configured - {pull}3543[#3543]

[float]
===== Potentially breaking changes
Expand Down
Expand Up @@ -1175,6 +1175,11 @@ public boolean isRedactExceptions() {
return (safeExceptions.get() & 1) != 0;
}

@Override
public boolean isAvoidTouchingExceptions() {
return isRedactExceptions() || !captureExceptionDetails();
}

@Override
public boolean isUseServletAttributesForExceptionPropagation() {
return (safeExceptions.get() & 2) == 0;
Expand Down
Expand Up @@ -603,7 +603,7 @@ public static String onMethodEnter() {

@Advice.AssignReturned.ToReturned
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static String onMethodExit(@Advice.Thrown Throwable throwable) {
public static String onMethodExit() {
throw new RuntimeException("This exception should be suppressed");
}
}
Expand Down
Expand Up @@ -28,6 +28,7 @@
import co.elastic.apm.agent.tracer.Outcome;
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.Tracer;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
Expand Down Expand Up @@ -146,8 +147,10 @@ public static void onExit(@Advice.Thrown @Nullable Throwable thrown,
span.getContext().getHttp().withStatusCode(statusLine.getStatusCode());
}

if (thrown instanceof CircularRedirectException) {
span.withOutcome(Outcome.FAILURE);
if(thrown != null && !tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()) {
if (thrown instanceof CircularRedirectException) {
span.withOutcome(Outcome.FAILURE);
}
}

span.captureException(thrown)
Expand Down
Expand Up @@ -23,6 +23,7 @@
import co.elastic.apm.agent.tracer.ElasticContext;
import co.elastic.apm.agent.tracer.Outcome;
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.method.MethodDescription;
Expand Down Expand Up @@ -123,10 +124,13 @@ public static void onAfterExecute(@Advice.Return @Nullable HttpResponse response
}
span.captureException(t);
} finally {
// in case of circular redirect, we get an exception but status code won't be available without response
// thus we have to deal with span outcome directly
if (t instanceof CircularRedirectException) {
span.withOutcome(Outcome.FAILURE);

if(t != null && !tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()) {
// in case of circular redirect, we get an exception but status code won't be available without response
// thus we have to deal with span outcome directly
if (t instanceof CircularRedirectException) {
span.withOutcome(Outcome.FAILURE);
}
}

span.deactivate().end();
Expand Down
Expand Up @@ -20,6 +20,9 @@


import co.elastic.apm.agent.httpclient.common.ApacheHttpClientApiAdapter;
import co.elastic.apm.agent.tracer.GlobalTracer;
import co.elastic.apm.agent.tracer.Tracer;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.StatusLine;
Expand All @@ -32,6 +35,8 @@
public class ApacheHttpClient4ApiAdapter implements ApacheHttpClientApiAdapter<HttpRequest, HttpRequestWrapper, HttpHost, CloseableHttpResponse> {
private static final ApacheHttpClient4ApiAdapter INSTANCE = new ApacheHttpClient4ApiAdapter();

private final Tracer tracer = GlobalTracer.get();

private ApacheHttpClient4ApiAdapter() {
}

Expand Down Expand Up @@ -65,6 +70,9 @@ public int getResponseCode(CloseableHttpResponse closeableHttpResponse) {

@Override
public boolean isCircularRedirectException(Throwable t) {
if (t == null || tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()) {
return false;
}
return t instanceof CircularRedirectException;
}

Expand Down
Expand Up @@ -21,6 +21,9 @@
import co.elastic.apm.agent.httpclient.common.ApacheHttpClientApiAdapter;
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
import co.elastic.apm.agent.tracer.GlobalTracer;
import co.elastic.apm.agent.tracer.Tracer;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import org.apache.hc.client5.http.CircularRedirectException;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.routing.RoutingSupport;
Expand All @@ -38,6 +41,8 @@ public class ApacheHttpClient5ApiAdapter implements ApacheHttpClientApiAdapter<H

private static final Logger logger = LoggerFactory.getLogger(ApacheHttpClient5ApiAdapter.class);

private final Tracer tracer = GlobalTracer.get();

private ApacheHttpClient5ApiAdapter() {
}

Expand Down Expand Up @@ -78,6 +83,9 @@ public int getResponseCode(CloseableHttpResponse closeableHttpResponse) {

@Override
public boolean isCircularRedirectException(Throwable t) {
if (t == null || tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()) {
return false;
}
return t instanceof CircularRedirectException;
}

Expand Down
Expand Up @@ -106,7 +106,7 @@ public static void onEnter() {
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static void onExit(@Advice.Thrown @Nullable Throwable thrown, @Advice.Return @Nullable Object returnValue) {
public static void onExit() {
CassandraHelper.inSyncExecute(false);
}
}
Expand Down
Expand Up @@ -86,8 +86,7 @@ public static Object[] onBeforeExecute(@Advice.Argument(0) String method,
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static void onAfterExecute(@Advice.Thrown @Nullable Throwable t,
@Advice.Enter @Nullable Object[] entryArgs) {
public static void onAfterExecute(@Advice.Enter @Nullable Object[] entryArgs) {
if (entryArgs != null) {
final Span<?> span = (Span<?>) entryArgs[0];
if (span != null) {
Expand Down
Expand Up @@ -77,8 +77,7 @@ public static Object[] onBeforeExecute(@Advice.Argument(0) Request request,
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static void onAfterExecute(@Advice.Thrown @Nullable Throwable t,
@Advice.Enter @Nullable Object[] entryArgs) {
public static void onAfterExecute(@Advice.Enter @Nullable Object[] entryArgs) {
if (entryArgs != null) {
final Span<?> span = (Span<?>) entryArgs[0];
if (span != null) {
Expand Down
Expand Up @@ -30,6 +30,7 @@
import co.elastic.apm.agent.tracer.Outcome;
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.Tracer;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import co.elastic.apm.agent.tracer.pooling.Allocator;
import co.elastic.apm.agent.tracer.pooling.ObjectPool;
import org.apache.http.HttpEntity;
Expand Down Expand Up @@ -154,16 +155,18 @@ public void finishClientSpan(@Nullable Response response, Span<?> span, @Nullabl
cluster = response.getHeader("x-found-handling-cluster");

} else if (t != null) {
if (t instanceof ResponseException) {
ResponseException esre = (ResponseException) t;
HttpHost host = esre.getResponse().getHost();
address = host.getHostName();
port = host.getPort();
url = host.toURI();
statusCode = esre.getResponse().getStatusLine().getStatusCode();
} else if (t instanceof CancellationException) {
// We can't tell whether a cancelled search is related to a failure or not
span.withOutcome(Outcome.UNKNOWN);
if (!tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()) {
if (t instanceof ResponseException) {
ResponseException esre = (ResponseException) t;
HttpHost host = esre.getResponse().getHost();
address = host.getHostName();
port = host.getPort();
url = host.toURI();
statusCode = esre.getResponse().getStatusLine().getStatusCode();
} else if (t instanceof CancellationException) {
// We can't tell whether a cancelled search is related to a failure or not
span.withOutcome(Outcome.UNKNOWN);
}
}
span.captureException(t);
}
Expand Down
Expand Up @@ -27,6 +27,7 @@
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.Tracer;
import co.elastic.apm.agent.tracer.Transaction;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import co.elastic.apm.agent.tracer.dispatch.AbstractHeaderGetter;
import co.elastic.apm.agent.tracer.dispatch.TextHeaderGetter;
import co.elastic.apm.agent.tracer.dispatch.TextHeaderSetter;
Expand Down Expand Up @@ -257,7 +258,11 @@ public void exitServerListenerMethod(@Nullable Throwable thrown,
if (null != thrown) {
// when there is a runtime exception thrown in one of the listener methods the calling code will catch it
// and make this the last listener method called
terminateStatus = Status.fromThrowable(thrown);
if (tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()) {
terminateStatus = Status.UNKNOWN;
} else {
terminateStatus = Status.fromThrowable(thrown);
}
setTerminateStatus = true;

} else if (transaction.getOutcome() == Outcome.UNKNOWN) {
Expand Down
Expand Up @@ -79,8 +79,7 @@ public static Object onEnter(@Advice.This Object thiz) {
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static void onExit(@Advice.Thrown Throwable thrown,
@Nullable @Advice.Enter Object context) {
public static void onExit(@Nullable @Advice.Enter Object context) {
if (context != null) {
((ElasticContext<?>) context).deactivate();
}
Expand Down
Expand Up @@ -23,6 +23,7 @@
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
import co.elastic.apm.agent.tracer.Span;
import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.description.method.MethodDescription;
Expand Down Expand Up @@ -112,7 +113,9 @@ public static Object[] afterSend(@Advice.Enter @Nullable Object[] enter,
return null;
}
Object[] overrideThrowable = null;
if (throwable != null && throwable.getMessage().contains("Magic v1 does not support record headers")) {
if (throwable != null
&& !tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()
&& throwable.getMessage().contains("Magic v1 does not support record headers")) {
// Probably our fault - ignore span and retry. May happen when using a new client with an old (< 0.11.0)
// broker. In such cases we DO check the version, but the first version check may be not yet up to date.
logger.info("Adding header to Kafka record is not allowed with the used broker, attempting to resend record");
Expand Down
Expand Up @@ -18,6 +18,7 @@
*/
package co.elastic.apm.agent.process;

import co.elastic.apm.agent.tracer.configuration.CoreConfiguration;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
Expand Down Expand Up @@ -145,7 +146,8 @@ public static void onExit(@Advice.This Process process, @Advice.Thrown Throwable
return;
}

if (thrown instanceof IllegalThreadStateException) {
if (!tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions()
&& thrown instanceof IllegalThreadStateException) {
// this call to exitValue was invoked before the process had terminated
return;
}
Expand Down
Expand Up @@ -239,6 +239,10 @@ public static <HttpServletRequest, HttpServletResponse, ServletContext, ServletC
for (int i = 0; i < size; i++) {
String attributeName = requestExceptionAttributes.get(i);
Object throwable = adapter.getAttribute(httpServletRequest, attributeName);
// We don't check tracer.getConfig(CoreConfiguration.class).isAvoidTouchingExceptions() here,
// because in that case the corrupt pointer is already stored in the request attributes
// In that case we already lost, because the GC will complain about the broken pointer
// when the request attributes entry is GCed
if (throwable instanceof Throwable) {
t2 = (Throwable) throwable;

Expand Down
Expand Up @@ -58,7 +58,7 @@ public static void onEnter() {
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static void onExit(@Advice.Thrown @Nullable Throwable thrown) {
public static void onExit() {
JavaConcurrent.allowContextPropagationOnCurrentThread();
}

Expand Down
Expand Up @@ -60,7 +60,7 @@ public static void onEnter() {
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false)
public static void onExit(@Advice.Thrown @Nullable Throwable thrown) {
public static void onExit() {
JavaConcurrent.allowContextPropagationOnCurrentThread();
}

Expand Down
Expand Up @@ -50,6 +50,8 @@ public interface CoreConfiguration {

TimeDuration getSpanMinDuration();

boolean isAvoidTouchingExceptions();

boolean isUseServletAttributesForExceptionPropagation();

enum EventType {
Expand Down