Add a forceIgnoring mechanism to the agent kernel (#689)

IgnoredTracerContext can propagate the unsampled flag into the following tracing context through the invalid snapshot.
This commit is contained in:
gzlicanyi 2024-05-17 13:43:51 +08:00 committed by GitHub
parent 4dfc1e85b5
commit b608d74c15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 202 additions and 15 deletions

View File

@ -12,6 +12,7 @@ Release Notes.
* Fix typos in `URLParser`.
* Add support for `Derby`/`Sybase`/`SQLite`/`DB2`/`OceanBase` jdbc url format in `URLParser`.
* Optimize spring-plugins:scheduled-annotation-plugin compatibility about Spring 6.1.x support.
* Add a forceIgnoring mechanism in a CROSS_THREAD scenario.
All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/213?closed=1)

View File

@ -138,4 +138,10 @@ public interface AbstractTracerContext {
* Get current primary endpoint name
*/
String getPrimaryEndpointName();
/**
* Change the current context to be in ignoring status.
*/
AbstractTracerContext forceIgnoring();
}

View File

@ -163,7 +163,14 @@ public class ContextManager implements BootService {
throw new IllegalArgumentException("ContextSnapshot can't be null.");
}
if (!snapshot.isFromCurrent()) {
get().continued(snapshot);
// Invalid snapshot is only created by {@link IgnoredTracerContext#capture()}.
// When the snapshot is not valid, need to force ignoring the current tracing context.
if (snapshot.isValid()) {
get().continued(snapshot);
} else {
AbstractTracerContext context = get().forceIgnoring();
CONTEXT.set(context);
}
}
}

View File

@ -47,6 +47,13 @@ public class IgnoredTracerContext implements AbstractTracerContext {
this.profileStatusContext = ProfileStatusContext.createWithNone();
}
public IgnoredTracerContext(int stackDepth) {
this.stackDepth = stackDepth;
this.correlationContext = new CorrelationContext();
this.extensionContext = new ExtensionContext();
this.profileStatusContext = ProfileStatusContext.createWithNone();
}
@Override
public void inject(ContextCarrier carrier) {
this.correlationContext.inject(carrier);
@ -134,6 +141,11 @@ public class IgnoredTracerContext implements AbstractTracerContext {
return null;
}
@Override
public AbstractTracerContext forceIgnoring() {
return this;
}
public static class ListenerManager {
private static List<IgnoreTracerContextListener> LISTENERS = new LinkedList<>();

View File

@ -419,6 +419,14 @@ public class TracingContext implements AbstractTracerContext {
return primaryEndpoint.getName();
}
@Override
public AbstractTracerContext forceIgnoring() {
for (AbstractSpan span: activeSpanStack) {
span.forceIgnoring();
}
return new IgnoredTracerContext(activeSpanStack.size());
}
/**
* Re-check current trace need profiling, encase third part plugin change the operation name.
*

View File

@ -124,4 +124,9 @@ public interface AbstractSpan extends AsyncSpan {
* Should skip analysis in the backend.
*/
void skipAnalysis();
/**
* Set to ignored status.
*/
void forceIgnoring();
}

View File

@ -63,7 +63,8 @@ public abstract class AbstractTracingSpan implements AbstractSpan {
private volatile boolean isAsyncStopped = false;
/**
* The context to which the span belongs
* The context to which the span belongs.
* This should not be called when {@link #ignored} is true.
*/
protected final TracingContext owner;
@ -99,6 +100,11 @@ public abstract class AbstractTracingSpan implements AbstractSpan {
*/
protected boolean skipAnalysis;
/**
* The ignore flag of this span.
*/
protected boolean ignored;
protected AbstractTracingSpan(int spanId, int parentSpanId, String operationName, TracingContext owner) {
this.operationName = operationName;
this.spanId = spanId;
@ -318,7 +324,9 @@ public abstract class AbstractTracingSpan implements AbstractSpan {
if (isInAsyncMode) {
throw new RuntimeException("Prepare for async repeatedly. Span is already in async mode.");
}
ContextManager.awaitFinishAsync(this);
if (!ignored) {
ContextManager.awaitFinishAsync(this);
}
isInAsyncMode = true;
return this;
}
@ -332,13 +340,18 @@ public abstract class AbstractTracingSpan implements AbstractSpan {
throw new RuntimeException("Can not do async finish for the span repeatedly.");
}
this.endTime = System.currentTimeMillis();
owner.asyncStop(this);
if (!ignored) {
owner.asyncStop(this);
}
isAsyncStopped = true;
return this;
}
@Override
public boolean isProfiling() {
if (ignored) {
return false;
}
return this.owner.profileStatus().isProfiling();
}
@ -346,4 +359,9 @@ public abstract class AbstractTracingSpan implements AbstractSpan {
public void skipAnalysis() {
this.skipAnalysis = true;
}
@Override
public void forceIgnoring() {
this.ignored = true;
}
}

View File

@ -111,7 +111,9 @@ public class ExitSpan extends StackBasedTracingSpan implements ExitTypeSpan {
@Override
public ExitSpan inject(final ContextCarrier carrier) {
this.owner.inject(this, carrier);
if (!ignored) {
this.owner.inject(this, carrier);
}
return this;
}

View File

@ -123,6 +123,10 @@ public class NoopSpan implements AbstractSpan {
public void skipAnalysis() {
}
@Override
public void forceIgnoring() {
}
@Override
public AbstractSpan prepareForAsync() {
return this;

View File

@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.skywalking.apm.agent.core.context;
import org.apache.skywalking.apm.agent.core.boot.ServiceManager;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.context.ids.NewDistributedTraceId;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.context.trace.LocalSpan;
import org.apache.skywalking.apm.agent.core.context.trace.NoopSpan;
import org.apache.skywalking.apm.agent.core.profile.ProfileStatusContext;
import org.apache.skywalking.apm.agent.core.test.tools.AgentServiceRule;
import org.apache.skywalking.apm.agent.core.test.tools.SegmentStorage;
import org.apache.skywalking.apm.agent.core.test.tools.SegmentStoragePoint;
import org.apache.skywalking.apm.agent.core.test.tools.TracingSegmentRunner;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.Assert;
@RunWith(TracingSegmentRunner.class)
public class ContinuedContextTest {
@SegmentStoragePoint
private SegmentStorage tracingData;
@Rule
public AgentServiceRule agentServiceRule = new AgentServiceRule();
@BeforeClass
public static void beforeClass() {
Config.Agent.KEEP_TRACING = true;
}
@AfterClass
public static void afterClass() {
Config.Agent.KEEP_TRACING = false;
ServiceManager.INSTANCE.shutdown();
}
@Test
public void testContinued() {
NewDistributedTraceId distributedTraceId = new NewDistributedTraceId();
ContextSnapshot snapshot = new ContextSnapshot(
"1, 2, 3",
1,
distributedTraceId,
"/for-test-continued",
new CorrelationContext(),
new ExtensionContext(),
ProfileStatusContext.createWithNone()
);
AbstractSpan span = ContextManager.createLocalSpan("test-span");
ContextManager.continued(snapshot);
Assert.assertEquals(distributedTraceId.getId(), ContextManager.getGlobalTraceId());
ContextManager.stopSpan();
}
@Test
public void testContinuedWithIgnoredSnapshot() {
ContextSnapshot snapshot =
new ContextSnapshot(null, -1, null, null, new CorrelationContext(), new ExtensionContext(), ProfileStatusContext.createWithNone());
AbstractSpan span = ContextManager.createLocalSpan("test-span");
ContextManager.continued(snapshot);
Assert.assertTrue(span instanceof LocalSpan);
AbstractSpan span2 = ContextManager.createLocalSpan("test-span2");
Assert.assertTrue(span2 instanceof NoopSpan);
ContextManager.stopSpan();
ContextManager.stopSpan();
}
}

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import java.util.List;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
@ -55,7 +56,6 @@ public class OnExceptionInterceptorTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private ContextSnapshot contextSnapshot;
private SendCallBackEnhanceInfo enhanceInfo;
@ -65,6 +65,7 @@ public class OnExceptionInterceptorTest {
@Before
public void setUp() {
exceptionInterceptor = new OnExceptionInterceptor();
contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot();
enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot);
when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo);

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import java.util.List;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
@ -57,7 +58,6 @@ public class OnSuccessInterceptorTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private ContextSnapshot contextSnapshot;
@Mock
private SendResult sendResult;
@ -70,6 +70,7 @@ public class OnSuccessInterceptorTest {
@Before
public void setUp() {
successInterceptor = new OnSuccessInterceptor();
contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot();
enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot);
when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo);

View File

@ -23,6 +23,7 @@ import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.when;
import java.util.List;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
@ -55,7 +56,6 @@ public class OnExceptionInterceptorTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private ContextSnapshot contextSnapshot;
private SendCallBackEnhanceInfo enhanceInfo;
@ -69,6 +69,7 @@ public class OnExceptionInterceptorTest {
@Test
public void testOnException() throws Throwable {
contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot();
enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot);
when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo);

View File

@ -25,6 +25,7 @@ import java.util.List;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
@ -57,7 +58,6 @@ public class OnSuccessInterceptorTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private ContextSnapshot contextSnapshot;
@Mock
private SendResult sendResult;
@ -70,6 +70,7 @@ public class OnSuccessInterceptorTest {
@Before
public void setUp() {
successInterceptor = new OnSuccessInterceptor();
contextSnapshot = MockContextSnapshot.INSTANCE.mockContextSnapshot();
enhanceInfo = new SendCallBackEnhanceInfo("test", contextSnapshot);
when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn(enhanceInfo);

View File

@ -26,6 +26,8 @@ public enum MockContextSnapshot {
private ContextSnapshot contextSnapshot;
private ContextSnapshot ignoreContextSnapshot;
MockContextSnapshot() {
contextSnapshot = new ContextSnapshot(
"1, 2, 3",
@ -36,9 +38,21 @@ public enum MockContextSnapshot {
new ExtensionContext(),
ProfileStatusContext.createWithNone()
);
ignoreContextSnapshot = new ContextSnapshot(
null,
-1,
null,
null,
new CorrelationContext(),
new ExtensionContext(),
ProfileStatusContext.createWithNone());
}
public ContextSnapshot mockContextSnapshot() {
return contextSnapshot;
}
public ContextSnapshot mockIgnoreContextSnapshot() {
return ignoreContextSnapshot;
}
}

View File

@ -22,7 +22,7 @@ import static org.junit.Assert.assertThat;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.Callable;
import org.apache.skywalking.apm.agent.core.context.ContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.MockContextSnapshot;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
@ -36,7 +36,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@ -69,9 +68,6 @@ public class CallableOrRunnableInterceptorTest {
}
};
@Mock
private ContextSnapshot contextSnapshot;
private Object[] arguments;
private Method callMethod;
@ -100,7 +96,7 @@ public class CallableOrRunnableInterceptorTest {
@Test
public void testCall() throws Throwable {
enhancedInstance.setSkyWalkingDynamicField(contextSnapshot);
enhancedInstance.setSkyWalkingDynamicField(MockContextSnapshot.INSTANCE.mockContextSnapshot());
callableCallInterceptor.beforeMethod(enhancedInstance, callMethod, arguments, null, null);
callableCallInterceptor.afterMethod(enhancedInstance, callMethod, arguments, null, "result");
@ -111,4 +107,16 @@ public class CallableOrRunnableInterceptorTest {
}
@Test
public void testCallWithIgnoreSnapshot() throws Throwable {
enhancedInstance.setSkyWalkingDynamicField(MockContextSnapshot.INSTANCE.mockIgnoreContextSnapshot());
callableCallInterceptor.beforeMethod(enhancedInstance, callMethod, arguments, null, null);
callableCallInterceptor.afterMethod(enhancedInstance, callMethod, arguments, null, "result");
assertThat(segmentStorage.getTraceSegments().size(), is(0));
assertThat(segmentStorage.getIgnoredTracerContexts().size(), is(1));
}
}