Skip to content

Commit 9515ec7

Browse files
authored
Introduce priorities for transaction name (#748)
Now uses a transaction name according to use_path_as_transaction_name rather than ServletClass#doGet. But if a name can be determined from a high level framework, like Spring MVC, that takes precedence. User-supplied names from the API always take precedence over any others. fixes #673
1 parent 6bdd74e commit 9515ec7

File tree

44 files changed

+237
-160
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+237
-160
lines changed

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/AbstractSpan.java

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
import java.util.concurrent.atomic.AtomicLong;
4141

4242
public abstract class AbstractSpan<T extends AbstractSpan> extends TraceContextHolder<T> {
43+
public static final int PRIO_USER_SUPPLIED = 1000;
44+
public static final int PRIO_HIGH_LEVEL_FRAMEWORK = 100;
45+
public static final int PRIO_METHOD_SIGNATURE = 100;
46+
public static final int PRIO_LOW_LEVEL_FRAMEWORK = 10;
47+
public static final int PRIO_DEFAULT = 0;
4348
private static final Logger logger = LoggerFactory.getLogger(AbstractSpan.class);
4449
protected static final double MS_IN_MICROS = TimeUnit.MILLISECONDS.toMicros(1);
4550
protected final TraceContext traceContext;
@@ -56,6 +61,7 @@ public abstract class AbstractSpan<T extends AbstractSpan> extends TraceContextH
5661
private ChildDurationTimer childDurations = new ChildDurationTimer();
5762
protected AtomicInteger references = new AtomicInteger();
5863
protected volatile boolean finished = true;
64+
private int namePriority = PRIO_DEFAULT;
5965

6066
public int getReferenceCount() {
6167
return references.get();
@@ -143,18 +149,36 @@ public double getDurationMs() {
143149
}
144150

145151
/**
146-
* Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')
152+
* Only intended to be used by {@link co.elastic.apm.agent.report.serialize.DslJsonSerializer}
147153
*/
148-
public StringBuilder getName() {
154+
public StringBuilder getNameForSerialization() {
149155
return name;
150156
}
151157

152158
/**
153-
* Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')
159+
* Resets and returns the name {@link StringBuilder} if the provided priority is {@code >=} {@link #namePriority} one.
160+
* Otherwise, returns {@code null}
161+
*
162+
* @param namePriority the priority for the name. See also the {@code AbstractSpan#PRIO_*} constants.
163+
* @return the name {@link StringBuilder} if the provided priority is {@code >=} {@link #namePriority}, {@code null} otherwise.
164+
*/
165+
@Nullable
166+
public StringBuilder getAndOverrideName(int namePriority) {
167+
if (namePriority >= this.namePriority) {
168+
this.namePriority = namePriority;
169+
this.name.setLength(0);
170+
return name;
171+
} else {
172+
return null;
173+
}
174+
}
175+
176+
/**
177+
* Only intended for testing purposes as this allocates a {@link String}
178+
* @return
154179
*/
155-
public void setName(@Nullable String name) {
156-
this.name.setLength(0);
157-
this.name.append(name);
180+
public String getNameAsString() {
181+
return name.toString();
158182
}
159183

160184
/**
@@ -168,7 +192,27 @@ public void setName(@Nullable String name) {
168192
* @return {@code this}, for chaining
169193
*/
170194
public T appendToName(String s) {
171-
name.append(s);
195+
return appendToName(s, PRIO_DEFAULT);
196+
}
197+
198+
public T appendToName(String s, int priority) {
199+
if (priority >= namePriority) {
200+
this.name.append(s);
201+
this.namePriority = priority;
202+
}
203+
return (T) this;
204+
}
205+
206+
public T withName(@Nullable String name) {
207+
return withName(name, PRIO_DEFAULT);
208+
}
209+
210+
public T withName(@Nullable String name, int priority) {
211+
if (priority >= namePriority && name != null && !name.isEmpty()) {
212+
this.name.setLength(0);
213+
this.name.append(name);
214+
this.namePriority = priority;
215+
}
172216
return (T) this;
173217
}
174218

@@ -194,6 +238,7 @@ public void resetState() {
194238
traceContext.resetState();
195239
childDurations.resetState();
196240
references.set(0);
241+
namePriority = PRIO_DEFAULT;
197242
}
198243

199244
public boolean isChildOf(AbstractSpan<?> parent) {

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Span.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,6 @@ public SpanContext getContext() {
118118
return context;
119119
}
120120

121-
public Span withName(@Nullable String name) {
122-
setName(name);
123-
return this;
124-
}
125-
126121
/**
127122
* Keywords of specific relevance in the span's domain (eg: 'db', 'template', 'ext', etc)
128123
*/

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,6 @@ public TransactionContext getContextEnsureVisibility() {
138138
}
139139
}
140140

141-
public Transaction withName(@Nullable String name) {
142-
setName(name);
143-
return this;
144-
}
145-
146141
/**
147142
* Keyword of specific relevance in the service's domain (eg: 'request', 'backgroundjob')
148143
*/

apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ private void serializeTransactions(final List<Transaction> transactions) {
526526
private void serializeTransaction(final Transaction transaction) {
527527
jw.writeByte(OBJECT_START);
528528
writeTimestamp(transaction.getTimestamp());
529-
writeField("name", transaction.getName());
529+
writeField("name", transaction.getNameForSerialization());
530530
serializeTraceContext(transaction.getTraceContext(), false);
531531
writeField("type", transaction.getType());
532532
writeField("duration", transaction.getDurationMs());
@@ -567,7 +567,7 @@ private void serializeSpans(final List<Span> spans) {
567567

568568
private void serializeSpan(final Span span) {
569569
jw.writeByte(OBJECT_START);
570-
writeField("name", span.getName());
570+
writeField("name", span.getNameForSerialization());
571571
writeTimestamp(span.getTimestamp());
572572
serializeTraceContext(span.getTraceContext(), true);
573573
writeField("duration", span.getDurationMs());

apm-agent-core/src/test/java/co/elastic/apm/agent/TransactionUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class TransactionUtils {
3838

3939
public static void fillTransaction(Transaction t) {
4040
t.start(TraceContext.asRoot(), null, (long) 0, ConstantSampler.of(true), TransactionUtils.class.getClassLoader());
41-
t.setName("GET /api/types");
41+
t.withName("GET /api/types");
4242
t.withType("request");
4343
t.withResult("success");
4444

apm-agent-core/src/test/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentationTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,18 @@ void tearDown() {
8888
void testTraceMethod() {
8989
TestClass.traceMe();
9090
assertThat(reporter.getTransactions()).hasSize(1);
91-
assertThat(reporter.getFirstTransaction().getName().toString()).isEqualTo("TestClass#traceMe");
91+
assertThat(reporter.getFirstTransaction().getNameAsString()).isEqualTo("TestClass#traceMe");
9292
assertThat(reporter.getSpans()).hasSize(1);
93-
assertThat(reporter.getFirstSpan().getName().toString()).isEqualTo("TestClass#traceMeToo");
93+
assertThat(reporter.getFirstSpan().getNameAsString()).isEqualTo("TestClass#traceMeToo");
9494
}
9595

9696
@Test
9797
void testExcludedMethod() {
9898
TestClass.traceMeAsWell();
9999
assertThat(reporter.getTransactions()).hasSize(1);
100-
assertThat(reporter.getFirstTransaction().getName().toString()).isEqualTo("TestClass#traceMeAsWell");
100+
assertThat(reporter.getFirstTransaction().getNameAsString()).isEqualTo("TestClass#traceMeAsWell");
101101
assertThat(reporter.getSpans()).hasSize(1);
102-
assertThat(reporter.getFirstSpan().getName().toString()).isEqualTo("TestClass#traceMeToo");
102+
assertThat(reporter.getFirstSpan().getNameAsString()).isEqualTo("TestClass#traceMeToo");
103103
}
104104

105105
@Test
@@ -122,7 +122,7 @@ void testNotMatched_Constructor() {
122122
assertThat(reporter.getTransactions()).isEmpty();
123123
testClass.traceMe();
124124
assertThat(reporter.getTransactions()).hasSize(1);
125-
assertThat(reporter.getFirstTransaction().getName().toString()).isEqualTo("TestExcludeConstructor#traceMe");
125+
assertThat(reporter.getFirstTransaction().getNameAsString()).isEqualTo("TestExcludeConstructor#traceMe");
126126
}
127127

128128
@Test

apm-agent-core/src/test/java/co/elastic/apm/agent/impl/ElasticApmTracerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ void testDisableMidTransaction() {
263263
try (Scope spanScope = span.activateInScope()) {
264264
when(config.getConfig(CoreConfiguration.class).isActive()).thenReturn(false);
265265
span.withName("test");
266-
assertThat(span.getName().toString()).isEqualTo("test");
266+
assertThat(span.getNameAsString()).isEqualTo("test");
267267
assertThat(tracerImpl.getActive()).isSameAs(span);
268268
assertThat(span.isChildOf(transaction)).isTrue();
269269
span.end();
@@ -272,7 +272,7 @@ void testDisableMidTransaction() {
272272
try (Scope spanScope = span2.activateInScope()) {
273273
when(config.getConfig(CoreConfiguration.class).isActive()).thenReturn(false);
274274
span2.withName("test2");
275-
assertThat(span2.getName().toString()).isEqualTo("test2");
275+
assertThat(span2.getNameAsString()).isEqualTo("test2");
276276
assertThat(tracerImpl.getActive()).isSameAs(span2);
277277
assertThat(span2.isChildOf(transaction)).isTrue();
278278
span2.end();

apm-agent-core/src/test/java/co/elastic/apm/agent/impl/transaction/SpanTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ void resetState() {
4747
.withUser("readonly_user");
4848
span.resetState();
4949
assertThat(span.getContext().hasContent()).isFalse();
50-
assertThat((CharSequence) span.getName()).isNullOrEmpty();
50+
assertThat(span.getNameAsString()).isNullOrEmpty();
5151
assertThat(span.getType()).isNull();
5252
assertThat(span.getSubtype()).isNull();
5353
assertThat(span.getAction()).isNull();

apm-agent-core/src/test/java/co/elastic/apm/agent/metrics/builtin/SystemMetricsTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import co.elastic.apm.agent.metrics.MetricRegistry;
2929
import co.elastic.apm.agent.report.ReporterConfiguration;
3030
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.api.condition.DisabledOnOs;
32+
import org.junit.jupiter.api.condition.OS;
3133
import org.junit.jupiter.params.ParameterizedTest;
3234
import org.junit.jupiter.params.provider.CsvSource;
3335

@@ -36,6 +38,7 @@
3638
import static org.assertj.core.api.Assertions.assertThat;
3739
import static org.mockito.Mockito.mock;
3840

41+
@DisabledOnOs(OS.MAC)
3942
class SystemMetricsTest {
4043

4144
private MetricRegistry metricRegistry = new MetricRegistry(mock(ReporterConfiguration.class));

apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import javax.annotation.Nullable;
4040
import java.lang.invoke.MethodHandle;
4141

42+
import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_USER_SUPPLIED;
4243
import static net.bytebuddy.matcher.ElementMatchers.named;
4344
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
4445

@@ -73,7 +74,7 @@ public SetNameInstrumentation() {
7374
public static void setName(@Advice.FieldValue(value = "span", typing = Assigner.Typing.DYNAMIC) TraceContextHolder<?> context,
7475
@Advice.Argument(0) String name) {
7576
if (context instanceof AbstractSpan) {
76-
((AbstractSpan) context).setName(name);
77+
((AbstractSpan) context).withName(name, PRIO_USER_SUPPLIED);
7778
}
7879
}
7980
}

0 commit comments

Comments
 (0)