Skip to content

Commit 6c30881

Browse files
authored
Support detaching from thread state without creating a new span (#770)
Allow detachment from thread state without creating a new span.
1 parent c292b0c commit 6c30881

File tree

8 files changed

+374
-166
lines changed

8 files changed

+374
-166
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
type: improvement
2+
improvement:
3+
description: Allow detachment from thread state without creating a new span.
4+
links:
5+
- https://github.com/palantir/tracing-java/pull/770

tracing-jaxrs/src/main/java/com/palantir/tracing/jaxrs/JaxRsTracers.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package com.palantir.tracing.jaxrs;
1818

19-
import com.palantir.tracing.DeferredTracer;
19+
import com.palantir.tracing.CloseableSpan;
20+
import com.palantir.tracing.Detached;
21+
import com.palantir.tracing.DetachedSpan;
2022
import com.palantir.tracing.Tracers;
2123
import java.io.IOException;
2224
import java.io.OutputStream;
@@ -36,19 +38,18 @@ public static StreamingOutput wrap(StreamingOutput delegate) {
3638
private static class TracingAwareStreamingOutput implements StreamingOutput {
3739

3840
private final StreamingOutput delegate;
39-
private DeferredTracer deferredTracer;
41+
private final Detached detached;
4042

4143
TracingAwareStreamingOutput(StreamingOutput delegate) {
4244
this.delegate = delegate;
43-
this.deferredTracer = new DeferredTracer("streaming-output");
45+
this.detached = DetachedSpan.detach();
4446
}
4547

4648
@Override
4749
public void write(OutputStream output) throws IOException, WebApplicationException {
48-
deferredTracer.withTrace(() -> {
50+
try (CloseableSpan ignored = detached.childSpan("streaming-output")) {
4951
delegate.write(output);
50-
return true;
51-
});
52+
}
5253
}
5354
}
5455
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.palantir.tracing;
18+
19+
import com.google.errorprone.annotations.MustBeClosed;
20+
import com.palantir.logsafe.Safe;
21+
import com.palantir.tracing.api.SpanType;
22+
import java.util.Map;
23+
import javax.annotation.CheckReturnValue;
24+
25+
/**
26+
* Detached tracing component which is not bound to thread state, and can be used on any thread.
27+
*
28+
* This class must not be implemented externally.
29+
*/
30+
public interface Detached {
31+
32+
/**
33+
* Equivalent to {@link Tracer#startSpan(String, SpanType)}, but using this {@link DetachedSpan} as the parent
34+
* instead of thread state.
35+
*/
36+
@MustBeClosed
37+
default CloseableSpan childSpan(@Safe String operationName, SpanType type) {
38+
return childSpan(operationName, NoTagTranslator.INSTANCE, NoTagTranslator.INSTANCE, type);
39+
}
40+
41+
/**
42+
* Equivalent to {@link #childSpan(String, Map, SpanType)} using a {@link SpanType#LOCAL span}.
43+
*/
44+
@MustBeClosed
45+
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata) {
46+
return childSpan(operationName, metadata, SpanType.LOCAL);
47+
}
48+
49+
@MustBeClosed
50+
default <T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data) {
51+
return childSpan(operationName, translator, data, SpanType.LOCAL);
52+
}
53+
54+
/**
55+
* Equivalent to {@link #childSpan(String, SpanType)}, but using {@link Map metadata} tags.
56+
*/
57+
@MustBeClosed
58+
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata, SpanType type) {
59+
return childSpan(operationName, MapTagTranslator.INSTANCE, metadata, type);
60+
}
61+
62+
@MustBeClosed
63+
<T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data, SpanType type);
64+
65+
/**
66+
* Equivalent to {@link Tracer#startSpan(String)}, but using this {@link DetachedSpan} as the parent instead of
67+
* thread state.
68+
*/
69+
@MustBeClosed
70+
default CloseableSpan childSpan(@Safe String operationName) {
71+
return childSpan(operationName, SpanType.LOCAL);
72+
}
73+
74+
/**
75+
* Starts a child {@link DetachedSpan} using this instance as the parent.
76+
*/
77+
@CheckReturnValue
78+
DetachedSpan childDetachedSpan(String operation, SpanType type);
79+
80+
/**
81+
* Starts a child {@link DetachedSpan} using this instance as the parent. Equivalent to
82+
* {@link #childDetachedSpan(String, SpanType)} using {@link SpanType#LOCAL}.
83+
*/
84+
@CheckReturnValue
85+
default DetachedSpan childDetachedSpan(@Safe String operation) {
86+
return childDetachedSpan(operation, SpanType.LOCAL);
87+
}
88+
89+
/**
90+
* Attaches the current {@link DetachedSpan} state to the current thread without creating additional spans.
91+
* This is useful when a long-lived {@link DetachedSpan} measures many smaller operations (like async-io)
92+
* in which we don't want to produce spans for each task, but do need tracing state associated for logging
93+
* and potential child traces.
94+
* @apiNote This must be executed within a try-with-resources block, and the parent detached span must still be
95+
* completed separately.
96+
*/
97+
@MustBeClosed
98+
CloseableSpan attach();
99+
}

tracing/src/main/java/com/palantir/tracing/DetachedSpan.java

Lines changed: 11 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525

2626
/**
2727
* Span which is not bound to thread state, and can be completed on any other thread.
28+
*
29+
* This class must not be implemented externally.
2830
*/
29-
public interface DetachedSpan {
31+
public interface DetachedSpan extends Detached {
3032

3133
/**
3234
* Marks the beginning of a span, which you can {@link #complete} on any other thread. Further work on this
@@ -69,45 +71,15 @@ static DetachedSpan start(
6971
}
7072

7173
/**
72-
* Equivalent to {@link Tracer#startSpan(String, SpanType)}, but using this {@link DetachedSpan} as the parent
73-
* instead of thread state.
74-
*/
75-
@MustBeClosed
76-
default CloseableSpan childSpan(@Safe String operationName, SpanType type) {
77-
return childSpan(operationName, NoTagTranslator.INSTANCE, NoTagTranslator.INSTANCE, type);
78-
}
79-
80-
/**
81-
* Equivalent to {@link #childSpan(String, Map, SpanType)} using a {@link SpanType#LOCAL span}.
82-
*/
83-
@MustBeClosed
84-
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata) {
85-
return childSpan(operationName, metadata, SpanType.LOCAL);
86-
}
87-
88-
@MustBeClosed
89-
default <T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data) {
90-
return childSpan(operationName, translator, data, SpanType.LOCAL);
91-
}
92-
93-
/**
94-
* Equivalent to {@link #childSpan(String, SpanType)}, but using {@link Map metadata} tags.
95-
*/
96-
@MustBeClosed
97-
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata, SpanType type) {
98-
return childSpan(operationName, MapTagTranslator.INSTANCE, metadata, type);
99-
}
100-
101-
@MustBeClosed
102-
<T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data, SpanType type);
103-
104-
/**
105-
* Equivalent to {@link Tracer#startSpan(String)}, but using this {@link DetachedSpan} as the parent instead of
106-
* thread state.
74+
* Creates a {@link Detached} instance based on the current tracing state without adding a new span.
75+
* Note that if there is no tracing state present a no-op instance is returned. This is the inverse of
76+
* {@link #attach()}.
77+
*
78+
* @see DetachedSpan#attach()
10779
*/
108-
@MustBeClosed
109-
default CloseableSpan childSpan(@Safe String operationName) {
110-
return childSpan(operationName, SpanType.LOCAL);
80+
@CheckReturnValue
81+
static Detached detach() {
82+
return Tracer.detachInternal();
11183
}
11284

11385
@MustBeClosed
@@ -123,32 +95,6 @@ default CloseableSpan completeAndStartChild(String operationName) {
12395
return completeAndStartChild(operationName, SpanType.LOCAL);
12496
}
12597

126-
/**
127-
* Starts a child {@link DetachedSpan} using this instance as the parent.
128-
*/
129-
@CheckReturnValue
130-
DetachedSpan childDetachedSpan(String operation, SpanType type);
131-
132-
/**
133-
* Starts a child {@link DetachedSpan} using this instance as the parent. Equivalent to
134-
* {@link #childDetachedSpan(String, SpanType)} using {@link SpanType#LOCAL}.
135-
*/
136-
@CheckReturnValue
137-
default DetachedSpan childDetachedSpan(@Safe String operation) {
138-
return childDetachedSpan(operation, SpanType.LOCAL);
139-
}
140-
141-
/**
142-
* Attaches the current {@link DetachedSpan} state to the current thread without creating additional spans.
143-
* This is useful when a long-lived {@link DetachedSpan} measures many smaller operations (like async-io)
144-
* in which we don't want to produce spans for each task, but do need tracing state associated for logging
145-
* and potential child traces.
146-
* @apiNote This must be executed within a try-with-resources block, and the parent detached span must still be
147-
* completed separately.
148-
*/
149-
@MustBeClosed
150-
CloseableSpan attach();
151-
15298
/**
15399
* Completes this span. After complete is invoked, other methods are not expected to produce spans, but they must
154100
* not throw either in order to avoid confusing failures.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* (c) Copyright 2021 Palantir Technologies Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.palantir.tracing;
18+
19+
import com.google.errorprone.annotations.MustBeClosed;
20+
import com.palantir.logsafe.Safe;
21+
import com.palantir.tracing.api.SpanType;
22+
23+
/**
24+
* {@link Detached} implementation which represents state with no trace.
25+
* Operations which create child traces will each generate a unique traceId and sample independently.
26+
*/
27+
enum NopDetached implements Detached {
28+
INSTANCE;
29+
30+
private static final CloseableSpan COMPLETE_SPAN = Tracer::fastCompleteSpan;
31+
32+
@Override
33+
@MustBeClosed
34+
public <T> CloseableSpan childSpan(
35+
String operationName, TagTranslator<? super T> translator, T data, SpanType type) {
36+
return CloseableTracer.startSpan(operationName, translator, data, type)::close;
37+
}
38+
39+
@Override
40+
@MustBeClosed
41+
public CloseableSpan childSpan(@Safe String operationName, SpanType type) {
42+
Tracer.fastStartSpan(operationName, type);
43+
return COMPLETE_SPAN;
44+
}
45+
46+
@Override
47+
@MustBeClosed
48+
public CloseableSpan attach() {
49+
return NopCloseableSpan.INSTANCE;
50+
}
51+
52+
@Override
53+
public DetachedSpan childDetachedSpan(String operation, SpanType type) {
54+
return DetachedSpan.start(operation, type);
55+
}
56+
57+
private enum NopCloseableSpan implements CloseableSpan {
58+
INSTANCE;
59+
60+
@Override
61+
public void close() {
62+
// nop
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)