Skip to content

Commit 59a45a5

Browse files
committed
Add APIs for manual inter process context propagation
closes #334
1 parent 934ae1b commit 59a45a5

File tree

15 files changed

+684
-96
lines changed

15 files changed

+684
-96
lines changed

apm-agent-api/src/main/java/co/elastic/apm/api/AbstractSpanImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
package co.elastic.apm.api;
2121

2222
import javax.annotation.Nonnull;
23+
import java.util.Collections;
24+
import java.util.Map;
25+
import java.util.function.BiConsumer;
2326

2427
public abstract class AbstractSpanImpl implements Span {
2528
@Nonnull
@@ -89,4 +92,16 @@ public boolean isSampled() {
8992
// co.elastic.apm.agent.plugin.api.AbstractSpanInstrumentation.IsSampledInstrumentation
9093
return false;
9194
}
95+
96+
@Nonnull
97+
@Override
98+
public Map<String, String> getTraceHeaders() {
99+
// co.elastic.apm.agent.plugin.api.AbstractSpanInstrumentation.GetTraceHeadersInstrumentation
100+
return Collections.emptyMap();
101+
}
102+
103+
@Override
104+
public void injectTraceHeaders(BiConsumer<String, String> addHeader) {
105+
// co.elastic.apm.agent.plugin.api.AbstractSpanInstrumentation.InjectTraceHeadersInstrumentation
106+
}
92107
}

apm-agent-api/src/main/java/co/elastic/apm/api/ElasticApm.java

Lines changed: 59 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,8 +19,12 @@
1919
*/
2020
package co.elastic.apm.api;
2121

22+
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
23+
2224
import javax.annotation.Nonnull;
23-
import javax.annotation.Nullable;
25+
import java.util.Collections;
26+
import java.util.Map;
27+
import java.util.function.Function;
2428

2529
/**
2630
* This class is the main entry point of the public API for the Elastic APM agent.
@@ -35,115 +39,85 @@
3539
* ElasticApm.currentTransaction().setName("SuchController#muchMethod");
3640
* }</pre>
3741
*/
38-
public class ElasticApm {
39-
40-
private ElasticApm() {
41-
// do not instantiate
42-
}
42+
/*
43+
* Implementation note:
44+
* The parameters of static methods are linked eagerly.
45+
* In order to be able to refer to Java 8 types but still be Java 7 compatible,
46+
* The Java 7 compatible code is extracted to a super class.
47+
* We take advantage of the fact that static methods are inherited as well.
48+
* So on Java 8, you can just call ElasticApm.startTransaction(),
49+
* even though that method is defined in ElasticApm's super class ElasticApmJava7.
50+
*
51+
* When stuck on Java 7, just call ElasticApmJava7.startTransaction().
52+
* Observation: actually, it also seems to work to call ElasticApm.startTransaction().
53+
* I assume the JVM does not eagerly link the methods when only referring to static methods of the super class.
54+
*/
55+
public class ElasticApm extends ElasticApmJava7 {
4356

4457
/**
45-
* Use this method to create a custom transaction.
46-
* <p>
47-
* Note that the agent will do this for you automatically when ever your application receives an incoming HTTP request.
48-
* You only need to use this method to create custom transactions.
49-
* </p>
58+
* Similar to {@link ElasticApm#startTransaction()} but creates this transaction as the child of a remote parent.
59+
*
5060
* <p>
51-
* It is important to call {@link Transaction#end()} when the transaction has ended.
52-
* A best practice is to use the transaction in a try-catch-finally block.
5361
* Example:
5462
* </p>
5563
* <pre>
56-
* Transaction transaction = ElasticApm.startTransaction();
57-
* try {
58-
* transaction.setName("MyController#myAction");
59-
* transaction.setType(Transaction.TYPE_REQUEST);
60-
* // do your thing...
61-
* } catch (Exception e) {
62-
* transaction.captureException(e);
63-
* throw e;
64-
* } finally {
65-
* transaction.end();
66-
* }
64+
* Transaction transaction = ElasticApm.startTransactionWithRemoteParent(request::getHeader);
6765
* </pre>
6866
* <p>
69-
* Note: Transactions created via this method can not be retrieved by calling {@link #currentSpan()} or {@link #currentTransaction()}.
70-
* See {@link Transaction#activate()} on how to achieve that.
71-
* </p>
72-
*
73-
* @return the started transaction.
74-
*/
75-
@Nonnull
76-
public static Transaction startTransaction() {
77-
Object transaction = doStartTransaction();
78-
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
79-
}
80-
81-
private static Object doStartTransaction() {
82-
// co.elastic.apm.api.ElasticApmInstrumentation.StartTransactionInstrumentation.doStartTransaction
83-
return null;
84-
}
85-
86-
/**
87-
* Returns the currently running transaction.
88-
* <p>
89-
* If there is no current transaction, this method will return a noop transaction,
90-
* which means that you never have to check for {@code null} values.
67+
* Note: If the protocol supports multi-value headers, use {@link #startTransactionWithRemoteParent(Function, Function)}
9168
* </p>
9269
* <p>
93-
* NOTE: Transactions created via {@link #startTransaction()} can not be retrieved by calling this method.
94-
* See {@link Transaction#activate()} on how to achieve that.
70+
* Note: This method can only be used on Java 8+.
71+
* If you are stuck on Java 7, use {@link ElasticApm#startTransactionWithRemoteParent(Map)}.
9572
* </p>
9673
*
97-
* @return The currently running transaction, or a noop transaction (never {@code null}).
74+
* @param getFirstHeader a function which receives a header name and returns the fist header with that name
75+
* @return the started transaction
76+
* @since 1.3.0
9877
*/
9978
@Nonnull
100-
public static Transaction currentTransaction() {
101-
Object transaction = doGetCurrentTransaction();
102-
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
103-
}
104-
105-
private static Object doGetCurrentTransaction() {
106-
// co.elastic.apm.api.ElasticApmInstrumentation.CurrentTransactionInstrumentation.doGetCurrentTransaction
107-
return null;
79+
@IgnoreJRERequirement
80+
public static Transaction startTransactionWithRemoteParent(final Function<String, String> getFirstHeader) {
81+
return startTransactionWithRemoteParent(getFirstHeader, new Function<String, Iterable<String>>() {
82+
@Override
83+
public Iterable<String> apply(String key) {
84+
return Collections.singletonList(getFirstHeader.apply(key));
85+
}
86+
});
10887
}
10988

11089
/**
111-
* Returns the currently active span or transaction.
90+
* Similar to {@link ElasticApm#startTransaction()} but creates this transaction as the child of a remote parent.
91+
*
11292
* <p>
113-
* If there is no current span, this method will return a noop span,
114-
* which means that you never have to check for {@code null} values.
93+
* Example:
11594
* </p>
95+
* <pre>
96+
* Transaction transaction = ElasticApm.startTransactionWithRemoteParent(request::getHeader, request::getHeaders);
97+
* </pre>
11698
* <p>
117-
* Note that even if this method is returning a noop span,
118-
* you can still {@link Span#captureException(Throwable) capture exceptions} on it.
119-
* These exceptions will not have a link to a Span or a Transaction.
99+
* Note: If the protocol does not support multi-value headers, use {@link #startTransactionWithRemoteParent(Function)}
120100
* </p>
121101
* <p>
122-
* NOTE: Transactions created via {@link Span#createSpan()} can not be retrieved by calling this method.
123-
* See {@link Span#activate()} on how to achieve that.
102+
* Note: This method can only be used on Java 8+.
103+
* If you are stuck on Java 7, use {@link ElasticApm#startTransactionWithRemoteParent(Map)}.
124104
* </p>
125-
* @return The currently active span, or transaction, or a noop span (never {@code null}).
105+
*
106+
* @param getFirstHeader a function which receives a header name and returns the fist header with that name
107+
* @param getAllHeaders a function which receives a header name and returns all headers with that name
108+
* @return the started transaction
109+
* @since 1.3.0
126110
*/
127111
@Nonnull
128-
public static Span currentSpan() {
129-
Object span = doGetCurrentSpan();
130-
return span != null ? new SpanImpl(span) : NoopSpan.INSTANCE;
112+
@IgnoreJRERequirement
113+
public static Transaction startTransactionWithRemoteParent(Function<String, String> getFirstHeader, Function<String, Iterable<String>> getAllHeaders) {
114+
Object transaction = doStartTransactionWithRemoteParentFunction(getFirstHeader, getAllHeaders);
115+
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
131116
}
132117

133-
private static Object doGetCurrentSpan() {
134-
// co.elastic.apm.api.ElasticApmInstrumentation.CurrentSpanInstrumentation.doGetCurrentSpan
118+
@IgnoreJRERequirement
119+
private static Object doStartTransactionWithRemoteParentFunction(Function<String, String> getFirstHeader, Function<String, Iterable<String>> getAllHeaders) {
120+
// co.elastic.apm.agent.plugin.api.ElasticApmApiInstrumentation.StartTransactionWithRemoteParentInstrumentation
135121
return null;
136122
}
137-
138-
/**
139-
* Captures an exception and reports it to the APM server.
140-
*
141-
* @param e the exception to record
142-
* @deprecated use {@link #currentSpan()}.{@link Span#captureException(Throwable) captureException(Throwable)} instead
143-
*/
144-
@Deprecated
145-
public static void captureException(@Nullable Throwable e) {
146-
// co.elastic.apm.api.ElasticApmInstrumentation.CaptureExceptionInstrumentation.captureException
147-
}
148-
149123
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*-
2+
* #%L
3+
* Elastic APM Java agent
4+
* %%
5+
* Copyright (C) 2018 Elastic and contributors
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package co.elastic.apm.api;
21+
22+
import javax.annotation.Nonnull;
23+
import javax.annotation.Nullable;
24+
import java.util.Map;
25+
26+
27+
public class ElasticApmJava7 {
28+
29+
ElasticApmJava7() {
30+
// do not instantiate
31+
}
32+
33+
/**
34+
* Use this method to create a custom transaction.
35+
* <p>
36+
* Note that the agent will do this for you automatically when ever your application receives an incoming HTTP request.
37+
* You only need to use this method to create custom transactions.
38+
* </p>
39+
* <p>
40+
* It is important to call {@link Transaction#end()} when the transaction has ended.
41+
* A best practice is to use the transaction in a try-catch-finally block.
42+
* Example:
43+
* </p>
44+
* <pre>
45+
* Transaction transaction = ElasticApm.startTransaction();
46+
* try {
47+
* transaction.setName("MyController#myAction");
48+
* transaction.setType(Transaction.TYPE_REQUEST);
49+
* // do your thing...
50+
* } catch (Exception e) {
51+
* transaction.captureException(e);
52+
* throw e;
53+
* } finally {
54+
* transaction.end();
55+
* }
56+
* </pre>
57+
* <p>
58+
* Note: Transactions created via this method can not be retrieved by calling {@link #currentSpan()} or {@link #currentTransaction()}.
59+
* See {@link Transaction#activate()} on how to achieve that.
60+
* </p>
61+
*
62+
* @return the started transaction.
63+
*/
64+
@Nonnull
65+
public static Transaction startTransaction() {
66+
Object transaction = doStartTransaction();
67+
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
68+
}
69+
70+
private static Object doStartTransaction() {
71+
// co.elastic.apm.api.ElasticApmInstrumentation.StartTransactionInstrumentation.doStartTransaction
72+
return null;
73+
}
74+
75+
/**
76+
* Similar to {@link #startTransaction()} but creates this transaction as the child of a remote parent.
77+
*
78+
* <p>
79+
* Note: If you are using Java 8 or newer, it is recommended to use {@link ElasticApm#startTransactionWithRemoteParent(java.util.function.Function)}
80+
* </p>
81+
*
82+
* @param headers a map, representing the headers of the incoming request
83+
* @return the started transaction
84+
* @since 1.3.0
85+
*/
86+
@Nonnull
87+
public static Transaction startTransactionWithRemoteParent(Map<String, ? extends Iterable<String>> headers) {
88+
Object transaction = doStartTransactionWithRemoteParentMap(headers);
89+
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
90+
}
91+
92+
private static Object doStartTransactionWithRemoteParentMap(Map<String, ? extends Iterable<String>> headers) {
93+
// co.elastic.apm.agent.plugin.api.ElasticApmApiInstrumentation.StartTransactionWithRemoteParentInstrumentationMap
94+
return null;
95+
}
96+
97+
/**
98+
* Returns the currently running transaction.
99+
* <p>
100+
* If there is no current transaction, this method will return a noop transaction,
101+
* which means that you never have to check for {@code null} values.
102+
* </p>
103+
* <p>
104+
* NOTE: Transactions created via {@link #startTransaction()} can not be retrieved by calling this method.
105+
* See {@link Transaction#activate()} on how to achieve that.
106+
* </p>
107+
*
108+
* @return The currently running transaction, or a noop transaction (never {@code null}).
109+
*/
110+
@Nonnull
111+
public static Transaction currentTransaction() {
112+
Object transaction = doGetCurrentTransaction();
113+
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
114+
}
115+
116+
private static Object doGetCurrentTransaction() {
117+
// co.elastic.apm.api.ElasticApmInstrumentation.CurrentTransactionInstrumentation.doGetCurrentTransaction
118+
return null;
119+
}
120+
121+
/**
122+
* Returns the currently active span or transaction.
123+
* <p>
124+
* If there is no current span, this method will return a noop span,
125+
* which means that you never have to check for {@code null} values.
126+
* </p>
127+
* <p>
128+
* Note that even if this method is returning a noop span,
129+
* you can still {@link Span#captureException(Throwable) capture exceptions} on it.
130+
* These exceptions will not have a link to a Span or a Transaction.
131+
* </p>
132+
* <p>
133+
* NOTE: Transactions created via {@link Span#createSpan()} can not be retrieved by calling this method.
134+
* See {@link Span#activate()} on how to achieve that.
135+
* </p>
136+
*
137+
* @return The currently active span, or transaction, or a noop span (never {@code null}).
138+
*/
139+
@Nonnull
140+
public static Span currentSpan() {
141+
Object span = doGetCurrentSpan();
142+
return span != null ? new SpanImpl(span) : NoopSpan.INSTANCE;
143+
}
144+
145+
private static Object doGetCurrentSpan() {
146+
// co.elastic.apm.api.ElasticApmInstrumentation.CurrentSpanInstrumentation.doGetCurrentSpan
147+
return null;
148+
}
149+
150+
/**
151+
* Captures an exception and reports it to the APM server.
152+
*
153+
* @param e the exception to record
154+
* @deprecated use {@link #currentSpan()}.{@link Span#captureException(Throwable) captureException(Throwable)} instead
155+
*/
156+
@Deprecated
157+
public static void captureException(@Nullable Throwable e) {
158+
// co.elastic.apm.api.ElasticApmInstrumentation.CaptureExceptionInstrumentation.captureException
159+
}
160+
161+
}

0 commit comments

Comments
 (0)