Skip to content

Commit 460d71a

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

File tree

15 files changed

+680
-95
lines changed

15 files changed

+680
-95
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: 53 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,11 @@
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.Map;
26+
import java.util.function.Function;
2427

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

4456
/**
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>
57+
* Similar to {@link ElasticApm#startTransaction()} but creates this transaction as the child of a remote parent.
58+
*
5059
* <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.
5360
* Example:
5461
* </p>
5562
* <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-
* }
63+
* Transaction transaction = ElasticApm.startTransactionWithRemoteParent(request::getHeader);
6764
* </pre>
6865
* <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.
66+
* Note: If the protocol supports multi-value headers, use {@link #startTransactionWithRemoteParent(Function, Function)}
9167
* </p>
9268
* <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.
69+
* Note: This method can only be used on Java 8+.
70+
* If you are stuck on Java 7, use {@link ElasticApm#startTransactionWithRemoteParent(Map)}.
9571
* </p>
9672
*
97-
* @return The currently running transaction, or a noop transaction (never {@code null}).
73+
* @param getFirstHeader a function which receives a header name and returns the fist header with that name
74+
* @return the started transaction
75+
* @since 1.3.0
9876
*/
9977
@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;
78+
@IgnoreJRERequirement
79+
public static Transaction startTransactionWithRemoteParent(final Function<String, String> getFirstHeader) {
80+
return startTransactionWithRemoteParent(getFirstHeader, null);
10881
}
10982

11083
/**
111-
* Returns the currently active span or transaction.
84+
* Similar to {@link ElasticApm#startTransaction()} but creates this transaction as the child of a remote parent.
85+
*
11286
* <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.
87+
* Example:
11588
* </p>
89+
* <pre>
90+
* Transaction transaction = ElasticApm.startTransactionWithRemoteParent(request::getHeader, request::getHeaders);
91+
* </pre>
11692
* <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.
93+
* Note: If the protocol does not support multi-value headers, use {@link #startTransactionWithRemoteParent(Function)}
12094
* </p>
12195
* <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.
96+
* Note: This method can only be used on Java 8+.
97+
* If you are stuck on Java 7, use {@link ElasticApm#startTransactionWithRemoteParent(Map)}.
12498
* </p>
125-
* @return The currently active span, or transaction, or a noop span (never {@code null}).
99+
*
100+
* @param getFirstHeader a function which receives a header name and returns the fist header with that name
101+
* @param getAllHeaders a function which receives a header name and returns all headers with that name
102+
* @return the started transaction
103+
* @since 1.3.0
126104
*/
127105
@Nonnull
128-
public static Span currentSpan() {
129-
Object span = doGetCurrentSpan();
130-
return span != null ? new SpanImpl(span) : NoopSpan.INSTANCE;
106+
@IgnoreJRERequirement
107+
public static Transaction startTransactionWithRemoteParent(Function<String, String> getFirstHeader, Function<String, Iterable<String>> getAllHeaders) {
108+
Object transaction = doStartTransactionWithRemoteParentFunction(getFirstHeader, getAllHeaders);
109+
return transaction != null ? new TransactionImpl(transaction) : NoopTransaction.INSTANCE;
131110
}
132111

133-
private static Object doGetCurrentSpan() {
134-
// co.elastic.apm.api.ElasticApmInstrumentation.CurrentSpanInstrumentation.doGetCurrentSpan
112+
@IgnoreJRERequirement
113+
private static Object doStartTransactionWithRemoteParentFunction(Function<String, String> getFirstHeader, Function<String, Iterable<String>> getAllHeaders) {
114+
// co.elastic.apm.agent.plugin.api.ElasticApmApiInstrumentation.StartTransactionWithRemoteParentInstrumentation
135115
return null;
136116
}
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-
149117
}
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)