Skip to content

Commit 8d38009

Browse files
Merge pull request #2279 from newrelic/w3c_sampled
Added sampling options when an inbound traceparent exists
2 parents 76029c1 + f983d50 commit 8d38009

File tree

9 files changed

+159
-9
lines changed

9 files changed

+159
-9
lines changed

newrelic-agent/src/main/java/com/newrelic/agent/HeadersUtil.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public static void parseAndAcceptDistributedTraceHeaders(Transaction tx, Inbound
261261
W3CTracePayload w3CTracePayload = W3CTracePayload.parseHeaders(tx, traceParent, traceState);
262262
if (w3CTracePayload != null) {
263263
if (w3CTracePayload.getPayload() != null) {
264-
tx.acceptDistributedTracePayload(w3CTracePayload.getPayload());
264+
tx.acceptDistributedTracePayload(w3CTracePayload.getPayload(), w3CTracePayload.getTraceParent());
265265
}
266266
if (w3CTracePayload.getTraceParent() != null) {
267267
tx.getSpanProxy().setInitiatingW3CTraceParent(w3CTracePayload.getTraceParent());

newrelic-agent/src/main/java/com/newrelic/agent/Transaction.java

+25-3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import com.newrelic.agent.tracing.DistributedTraceService;
6161
import com.newrelic.agent.tracing.DistributedTraceServiceImpl;
6262
import com.newrelic.agent.tracing.SpanProxy;
63+
import com.newrelic.agent.tracing.W3CTraceParent;
6364
import com.newrelic.agent.transaction.PriorityTransactionName;
6465
import com.newrelic.agent.transaction.TransactionCache;
6566
import com.newrelic.agent.transaction.TransactionCounts;
@@ -314,16 +315,37 @@ public boolean acceptDistributedTracePayload(String payload) {
314315
}
315316
}
316317

317-
public boolean acceptDistributedTracePayload(DistributedTracePayload payload) {
318-
if (getAgentConfig().getDistributedTracingConfig().isEnabled()) {
318+
public boolean acceptDistributedTracePayload(DistributedTracePayload payload, W3CTraceParent parent) {
319+
DistributedTracingConfig dtConfig = getAgentConfig().getDistributedTracingConfig();
320+
if (dtConfig.isEnabled()) {
319321
long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(
320322
System.nanoTime() - this.getTransactionTimer().getStartTimeInNanos());
321323
long txnStartTimeSinceEpochInMillis = System.currentTimeMillis() - elapsedMillis;
322324
spanProxy.get().setTimestamp(txnStartTimeSinceEpochInMillis);
323325
boolean accepted = spanProxy.get().acceptDistributedTracePayload(payload);
324326
if (accepted) {
325327
this.transportDurationInMillis = spanProxy.get().getTransportDurationInMillis();
326-
this.setPriorityIfNotNull(spanProxy.get().getInboundDistributedTracePayload().priority);
328+
if (parent != null) {
329+
if (parent.sampled()) { // traceparent exists and sampled is 1
330+
if (DistributedTracingConfig.SAMPLE_ALWAYS_ON.equals(dtConfig.getRemoteParentSampled())) {
331+
this.setPriorityIfNotNull(2.0f);
332+
} else if (DistributedTracingConfig.SAMPLE_ALWAYS_OFF.equals(dtConfig.getRemoteParentSampled())) {
333+
this.setPriorityIfNotNull(0.0f);
334+
} else {
335+
this.setPriorityIfNotNull(spanProxy.get().getInboundDistributedTracePayload().priority);
336+
}
337+
} else { // traceparent exists and sampled is 0
338+
if (DistributedTracingConfig.SAMPLE_ALWAYS_ON.equals(dtConfig.getRemoteParentNotSampled())) {
339+
this.setPriorityIfNotNull(2.0f);
340+
} else if (DistributedTracingConfig.SAMPLE_ALWAYS_OFF.equals(dtConfig.getRemoteParentNotSampled())) {
341+
this.setPriorityIfNotNull(0.0f);
342+
} else {
343+
this.setPriorityIfNotNull(spanProxy.get().getInboundDistributedTracePayload().priority);
344+
}
345+
}
346+
} else {
347+
this.setPriorityIfNotNull(spanProxy.get().getInboundDistributedTracePayload().priority);
348+
}
327349
}
328350
return accepted;
329351
} else {

newrelic-agent/src/main/java/com/newrelic/agent/TransactionApiImpl.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ public void acceptDistributedTracePayload(String payload) {
509509
public void acceptDistributedTracePayload(DistributedTracePayload payload) {
510510
Transaction tx = getTransactionIfExists();
511511
if (tx != null) {
512-
tx.acceptDistributedTracePayload(payload);
512+
tx.acceptDistributedTracePayload(payload, null);
513513
}
514514
}
515515

newrelic-agent/src/main/java/com/newrelic/agent/config/DistributedTracingConfig.java

+20
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ public class DistributedTracingConfig extends BaseConfig {
2121
public static final String DISTRIBUTED_TRACING_ENABLED = SYSTEM_PROPERTY_ROOT + ENABLED;
2222
public static final String ENABLED_ENV_KEY = "NEW_RELIC_DISTRIBUTED_TRACING_ENABLED";
2323
public static final String EXCLUDE_NEWRELIC_HEADER = "exclude_newrelic_header";
24+
public static final String SAMPLER = "sampler";
25+
public static final String REMOTE_PARENT_SAMPLED = "remote_parent_sampled";
26+
public static final String REMOTE_PARENT_NOT_SAMPLED = "remote_parent_not_sampled";
27+
public static final String SAMPLE_ALWAYS_ON = "always_on";
28+
public static final String SAMPLE_ALWAYS_OFF = "always_off";
29+
public static final String SAMPLE_DEFAULT = "default";
2430

2531
private final boolean enabled;
2632
private final String trustedAccountKey;
2733
private final String accountId;
2834
private final String primaryApplicationId;
2935
private final boolean includeNewRelicHeader;
36+
private final String remoteParentSampled;
37+
private final String remoteParentNotSampled;
3038

3139
DistributedTracingConfig(Map<String, Object> props) {
3240
super(props, SYSTEM_PROPERTY_ROOT);
@@ -35,6 +43,10 @@ public class DistributedTracingConfig extends BaseConfig {
3543
this.accountId = getProperty(ACCOUNT_ID);
3644
this.primaryApplicationId = getProperty(PRIMARY_APPLICATION_ID);
3745
this.includeNewRelicHeader = !getProperty(EXCLUDE_NEWRELIC_HEADER, false);
46+
47+
BaseConfig samplerConfig = new BaseConfig(nestedProps(SAMPLER), SYSTEM_PROPERTY_ROOT+"."+SAMPLER);
48+
this.remoteParentSampled = samplerConfig.getProperty(REMOTE_PARENT_SAMPLED, SAMPLE_DEFAULT);
49+
this.remoteParentNotSampled = samplerConfig.getProperty(REMOTE_PARENT_NOT_SAMPLED, SAMPLE_DEFAULT);
3850
}
3951

4052
public String getTrustedAccountKey() {
@@ -56,4 +68,12 @@ public String getAccountId() {
5668
public boolean isIncludeNewRelicHeader() {
5769
return includeNewRelicHeader;
5870
}
71+
72+
public String getRemoteParentSampled() {
73+
return remoteParentSampled;
74+
}
75+
76+
public String getRemoteParentNotSampled() {
77+
return remoteParentNotSampled;
78+
}
5979
}

newrelic-agent/src/main/resources/newrelic.yml

+15
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,21 @@ common: &default_settings
311311
# Default is false.
312312
exclude_newrelic_header: false
313313

314+
# Agent version 8.20.0+ utilize these settings for controlling how to sample when we have a remote parent
315+
# involved. That is, if there is a valid traceparent with a value in the sampled flag on the inbound headers.
316+
# If the inbound traceparent is marked as sampled then the remote_parent_sampled setting will be used.
317+
# If the inbound traceparent is marked as NOT sampled then the remote_parent_not_sampled setting will be used.
318+
# Otherwise New Relic's standard sampling will be used.
319+
# Note: Reservoir limits are still in effect on top of these settings, so spans may be dropped even if they
320+
# are marked for sampling, if the reservoir reaches its limit (max_samples_stored).
321+
# Possible values:
322+
# default: New Relic's standard sampling rules will be used.
323+
# always_on: Always sample spans in this case.
324+
# always_off: Always skip sampling in this case.
325+
sampler:
326+
remote_parent_sampled: default
327+
remote_parent_not_sampled: default
328+
314329
# New Relic's distributed tracing UI uses Span events to display traces across different services.
315330
# Span events capture attributes that describe execution context and provide linking metadata.
316331
# Span events require distributed tracing to be enabled.

newrelic-agent/src/test/java/com/newrelic/agent/HeadersUtilTest.java

+93
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
import com.newrelic.agent.tracers.ClassMethodSignature;
1919
import com.newrelic.agent.tracers.DefaultTracer;
2020
import com.newrelic.agent.tracers.Tracer;
21+
import com.newrelic.agent.transaction.TransactionTimer;
2122
import com.newrelic.agent.util.MockDistributedTraceService;
2223
import com.newrelic.api.agent.HeaderType;
2324
import com.newrelic.api.agent.InboundHeaders;
2425
import com.newrelic.test.marker.RequiresFork;
2526
import org.junit.Test;
2627
import org.junit.experimental.categories.Category;
28+
import org.mockito.Mockito;
2729

2830
import java.nio.charset.StandardCharsets;
2931
import java.util.Base64;
@@ -105,6 +107,97 @@ public void testGetSyntheticsInfoHeader() {
105107
(ImmutableMap.of("X-NewRelic-Synthetics-Info", synthInfoValue), HeaderType.HTTP)));
106108
}
107109

110+
@Test
111+
public void testInboundParentSampledTrueConfigAlwaysOn() {
112+
Transaction tx = setupAndCreateTx(DistributedTracingConfig.SAMPLE_ALWAYS_ON, DistributedTracingConfig.SAMPLE_DEFAULT);
113+
InboundHeaders inboundHeaders = createInboundHeaders(ImmutableMap.of(
114+
"traceparent", "01-0123456789abcdef0123456789abcdef-0123456789abcdef-01", // last entry is the sampled flag = true
115+
"tracestate", "trustyrusty@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05164d3b4b0d09cb05--0.5-1563574856827"
116+
), HeaderType.HTTP);
117+
118+
HeadersUtil.parseAndAcceptDistributedTraceHeaders(tx, inboundHeaders);
119+
assertTrue(tx.getSpanProxy().getInitiatingW3CTraceParent().sampled());
120+
assertTrue(tx.getPriority() == 2.0f);
121+
122+
Transaction.clearTransaction();
123+
}
124+
125+
@Test
126+
public void testInboundParentSampledTrueConfigAlwaysOff() {
127+
Transaction tx = setupAndCreateTx(DistributedTracingConfig.SAMPLE_ALWAYS_OFF, DistributedTracingConfig.SAMPLE_DEFAULT);
128+
InboundHeaders inboundHeaders = createInboundHeaders(ImmutableMap.of(
129+
"traceparent", "01-0123456789abcdef0123456789abcdef-0123456789abcdef-01", // last entry is the sampled flag = true
130+
"tracestate", "trustyrusty@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05164d3b4b0d09cb05--0.5-1563574856827"
131+
), HeaderType.HTTP);
132+
133+
HeadersUtil.parseAndAcceptDistributedTraceHeaders(tx, inboundHeaders);
134+
assertTrue(tx.getSpanProxy().getInitiatingW3CTraceParent().sampled());
135+
assertTrue(tx.getPriority() == 0.0f);
136+
137+
Transaction.clearTransaction();
138+
}
139+
140+
@Test
141+
public void testInboundParentSampledFalseConfigAlwaysOn() {
142+
Transaction tx = setupAndCreateTx(DistributedTracingConfig.SAMPLE_DEFAULT, DistributedTracingConfig.SAMPLE_ALWAYS_ON);
143+
InboundHeaders inboundHeaders = createInboundHeaders(ImmutableMap.of(
144+
"traceparent", "01-0123456789abcdef0123456789abcdef-0123456789abcdef-00", // last entry is the sampled flag = true
145+
"tracestate", "trustyrusty@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05164d3b4b0d09cb05--0.5-1563574856827"
146+
), HeaderType.HTTP);
147+
148+
HeadersUtil.parseAndAcceptDistributedTraceHeaders(tx, inboundHeaders);
149+
assertFalse(tx.getSpanProxy().getInitiatingW3CTraceParent().sampled());
150+
assertTrue(tx.getPriority() == 2.0f);
151+
152+
Transaction.clearTransaction();
153+
}
154+
155+
@Test
156+
public void testInboundParentSampledFalseConfigAlwaysOff() {
157+
Transaction tx = setupAndCreateTx(DistributedTracingConfig.SAMPLE_DEFAULT, DistributedTracingConfig.SAMPLE_ALWAYS_OFF);
158+
InboundHeaders inboundHeaders = createInboundHeaders(ImmutableMap.of(
159+
"traceparent", "01-0123456789abcdef0123456789abcdef-0123456789abcdef-00", // last entry is the sampled flag = true
160+
"tracestate", "trustyrusty@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05164d3b4b0d09cb05--0.5-1563574856827"
161+
), HeaderType.HTTP);
162+
163+
HeadersUtil.parseAndAcceptDistributedTraceHeaders(tx, inboundHeaders);
164+
assertFalse(tx.getSpanProxy().getInitiatingW3CTraceParent().sampled());
165+
assertTrue(tx.getPriority() == 0.0f);
166+
167+
Transaction.clearTransaction();
168+
}
169+
170+
private Transaction setupAndCreateTx(String remoteParentSampled, String remoteParentNotSampled) {
171+
System.out.println("Setting up config: "+remoteParentSampled+"; "+remoteParentNotSampled);
172+
ConfigService mockConfigService = new MockConfigService(AgentConfigImpl.createAgentConfig(
173+
ImmutableMap.of(
174+
AgentConfigImpl.APP_NAME,
175+
"Unit Test",
176+
AgentConfigImpl.DISTRIBUTED_TRACING,
177+
ImmutableMap.of(
178+
DistributedTracingConfig.ENABLED, true,
179+
DistributedTracingConfig.SAMPLER,
180+
ImmutableMap.of(
181+
DistributedTracingConfig.REMOTE_PARENT_SAMPLED, remoteParentSampled,
182+
DistributedTracingConfig.REMOTE_PARENT_NOT_SAMPLED, remoteParentNotSampled)
183+
),
184+
AgentConfigImpl.SPAN_EVENTS,
185+
ImmutableMap.of(
186+
SpanEventsConfig.ENABLED, true,
187+
SpanEventsConfig.COLLECT_SPAN_EVENTS, true)
188+
)));
189+
MockServiceManager mockServiceManager = new MockServiceManager(mockConfigService);
190+
mockServiceManager.setDistributedTraceService(new MockDistributedTraceService());
191+
ServiceFactory.setServiceManager(mockServiceManager);
192+
Transaction tx = Mockito.spy(Transaction.getTransaction());
193+
TransactionTimer timer = Mockito.mock(TransactionTimer.class);
194+
Mockito.when(timer.getStartTimeInNanos()).thenReturn(System.nanoTime());
195+
Mockito.when(tx.getTransactionTimer()).thenReturn(timer);
196+
tx.setPriorityIfNotNull(0.5f); // something > 0 and < 1
197+
198+
return tx;
199+
}
200+
108201
private InboundHeaders createInboundHeaders(final Map<String, String> map, final HeaderType type) {
109202
return new InboundHeaders() {
110203
@Override

newrelic-agent/src/test/java/com/newrelic/agent/tracers/DefaultTracerClmTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ public void testTransactionABParenting() {
888888
DefaultTracer span5Tracer = new OtherRootTracer(txB, new ClassMethodSignature("class",
889889
"span5", "()V"), null, DefaultTracer.NULL_METRIC_NAME_FORMATTER);
890890
txB.getTransactionActivity().tracerStarted(span5Tracer);
891-
span5Tracer.getTransaction().acceptDistributedTracePayload(payload);
891+
span5Tracer.getTransaction().acceptDistributedTracePayload(payload, null);
892892

893893
txB.setTransactionName(com.newrelic.api.agent.TransactionNamePriority.CUSTOM_HIGH, true, "Transaction B");
894894
txB.setThrowable(new Throwable(), TransactionErrorPriority.API, false);

newrelic-agent/src/test/java/com/newrelic/agent/tracers/DefaultTracerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ public void testTransactionABParenting() {
924924
DefaultTracer span5Tracer = new OtherRootTracer(txB, new ClassMethodSignature("class",
925925
"span5", "()V"), null, DefaultTracer.NULL_METRIC_NAME_FORMATTER);
926926
txB.getTransactionActivity().tracerStarted(span5Tracer);
927-
span5Tracer.getTransaction().acceptDistributedTracePayload(payload);
927+
span5Tracer.getTransaction().acceptDistributedTracePayload(payload, null);
928928

929929
txB.setTransactionName(com.newrelic.api.agent.TransactionNamePriority.CUSTOM_HIGH, true, "Transaction B");
930930
txB.setThrowable(new Throwable(), TransactionErrorPriority.API, false);

newrelic-agent/src/test/java/com/newrelic/agent/tracing/W3CTraceStateSupportTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public void testParseHeadersNoPriorityOrSampledTriggersResamplingDecision() {
120120
Collections.singletonList("02-12341234123412341234123412341234-4321432143214321-01"),
121121
Arrays.asList("190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05164d3b4b0d09cb05---1563574856827")).getPayload();
122122
assertNotNull(inboundPayload);
123-
transaction.acceptDistributedTracePayload(inboundPayload);
123+
transaction.acceptDistributedTracePayload(inboundPayload, null);
124124

125125
transaction.createDistributedTracePayload("meatball!");
126126
String[] outboundPayload = new W3CTraceStateHeader(true, true).create(transaction.getSpanProxy()).split("-");
@@ -159,7 +159,7 @@ public void testParseHeadersDup() {
159159
Arrays.asList("190@nr=0-0-709288-8599547-f85f42fd82a4cf1d-164d3b4b0d09cb05164d3b4b0d09cb05-1-0.789-1563574856827", "congo@=0-qzx932-abc123",
160160
"congo@=0-very-qzx932-abc123")).getPayload();
161161
assertNotNull(inboundPayload);
162-
transaction.acceptDistributedTracePayload(inboundPayload);
162+
transaction.acceptDistributedTracePayload(inboundPayload, null);
163163

164164
String newSpanId = TransactionGuidFactory.generate16CharGuid();
165165
transaction.createDistributedTracePayload(newSpanId);

0 commit comments

Comments
 (0)