Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog/@unreleased/pr-770.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: improvement
improvement:
description: Allow detachment from thread state without creating a new span.
links:
- https://github.com/palantir/tracing-java/pull/770
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package com.palantir.tracing.jaxrs;

import com.palantir.tracing.DeferredTracer;
import com.palantir.tracing.CloseableSpan;
import com.palantir.tracing.Detached;
import com.palantir.tracing.DetachedSpan;
import com.palantir.tracing.Tracers;
import java.io.IOException;
import java.io.OutputStream;
Expand All @@ -36,19 +38,18 @@ public static StreamingOutput wrap(StreamingOutput delegate) {
private static class TracingAwareStreamingOutput implements StreamingOutput {

private final StreamingOutput delegate;
private DeferredTracer deferredTracer;
private final Detached detached;

TracingAwareStreamingOutput(StreamingOutput delegate) {
this.delegate = delegate;
this.deferredTracer = new DeferredTracer("streaming-output");
this.detached = DetachedSpan.detach();
}

@Override
public void write(OutputStream output) throws IOException, WebApplicationException {
deferredTracer.withTrace(() -> {
try (CloseableSpan ignored = detached.childSpan("streaming-output")) {
delegate.write(output);
return true;
});
}
}
}
}
99 changes: 99 additions & 0 deletions tracing/src/main/java/com/palantir/tracing/Detached.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.tracing;

import com.google.errorprone.annotations.MustBeClosed;
import com.palantir.logsafe.Safe;
import com.palantir.tracing.api.SpanType;
import java.util.Map;
import javax.annotation.CheckReturnValue;

/**
* Detached tracing component which is not bound to thread state, and can be used on any thread.
*
* This class must not be implemented externally.
*/
public interface Detached {

/**
* Equivalent to {@link Tracer#startSpan(String, SpanType)}, but using this {@link DetachedSpan} as the parent
* instead of thread state.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName, SpanType type) {
return childSpan(operationName, NoTagTranslator.INSTANCE, NoTagTranslator.INSTANCE, type);
}

/**
* Equivalent to {@link #childSpan(String, Map, SpanType)} using a {@link SpanType#LOCAL span}.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata) {
return childSpan(operationName, metadata, SpanType.LOCAL);
}

@MustBeClosed
default <T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data) {
return childSpan(operationName, translator, data, SpanType.LOCAL);
}

/**
* Equivalent to {@link #childSpan(String, SpanType)}, but using {@link Map metadata} tags.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata, SpanType type) {
return childSpan(operationName, MapTagTranslator.INSTANCE, metadata, type);
}

@MustBeClosed
<T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data, SpanType type);

/**
* Equivalent to {@link Tracer#startSpan(String)}, but using this {@link DetachedSpan} as the parent instead of
* thread state.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName) {
return childSpan(operationName, SpanType.LOCAL);
}

/**
* Starts a child {@link DetachedSpan} using this instance as the parent.
*/
@CheckReturnValue
DetachedSpan childDetachedSpan(String operation, SpanType type);

/**
* Starts a child {@link DetachedSpan} using this instance as the parent. Equivalent to
* {@link #childDetachedSpan(String, SpanType)} using {@link SpanType#LOCAL}.
*/
@CheckReturnValue
default DetachedSpan childDetachedSpan(@Safe String operation) {
return childDetachedSpan(operation, SpanType.LOCAL);
}

/**
* Attaches the current {@link DetachedSpan} state to the current thread without creating additional spans.
* This is useful when a long-lived {@link DetachedSpan} measures many smaller operations (like async-io)
* in which we don't want to produce spans for each task, but do need tracing state associated for logging
* and potential child traces.
* @apiNote This must be executed within a try-with-resources block, and the parent detached span must still be
* completed separately.
*/
@MustBeClosed
CloseableSpan attach();
}
76 changes: 11 additions & 65 deletions tracing/src/main/java/com/palantir/tracing/DetachedSpan.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

/**
* Span which is not bound to thread state, and can be completed on any other thread.
*
* This class must not be implemented externally.
*/
public interface DetachedSpan {
public interface DetachedSpan extends Detached {

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

/**
* Equivalent to {@link Tracer#startSpan(String, SpanType)}, but using this {@link DetachedSpan} as the parent
* instead of thread state.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName, SpanType type) {
return childSpan(operationName, NoTagTranslator.INSTANCE, NoTagTranslator.INSTANCE, type);
}

/**
* Equivalent to {@link #childSpan(String, Map, SpanType)} using a {@link SpanType#LOCAL span}.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata) {
return childSpan(operationName, metadata, SpanType.LOCAL);
}

@MustBeClosed
default <T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data) {
return childSpan(operationName, translator, data, SpanType.LOCAL);
}

/**
* Equivalent to {@link #childSpan(String, SpanType)}, but using {@link Map metadata} tags.
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName, @Safe Map<String, String> metadata, SpanType type) {
return childSpan(operationName, MapTagTranslator.INSTANCE, metadata, type);
}

@MustBeClosed
<T> CloseableSpan childSpan(@Safe String operationName, TagTranslator<? super T> translator, T data, SpanType type);

/**
* Equivalent to {@link Tracer#startSpan(String)}, but using this {@link DetachedSpan} as the parent instead of
* thread state.
* Creates a {@link Detached} instance based on the current tracing state without adding a new span.
* Note that if there is no tracing state present a no-op instance is returned. This is the inverse of
* {@link #attach()}.
*
* @see DetachedSpan#attach()
*/
@MustBeClosed
default CloseableSpan childSpan(@Safe String operationName) {
return childSpan(operationName, SpanType.LOCAL);
@CheckReturnValue
static Detached detach() {
return Tracer.detachInternal();
}

@MustBeClosed
Expand All @@ -123,32 +95,6 @@ default CloseableSpan completeAndStartChild(String operationName) {
return completeAndStartChild(operationName, SpanType.LOCAL);
}

/**
* Starts a child {@link DetachedSpan} using this instance as the parent.
*/
@CheckReturnValue
DetachedSpan childDetachedSpan(String operation, SpanType type);

/**
* Starts a child {@link DetachedSpan} using this instance as the parent. Equivalent to
* {@link #childDetachedSpan(String, SpanType)} using {@link SpanType#LOCAL}.
*/
@CheckReturnValue
default DetachedSpan childDetachedSpan(@Safe String operation) {
return childDetachedSpan(operation, SpanType.LOCAL);
}

/**
* Attaches the current {@link DetachedSpan} state to the current thread without creating additional spans.
* This is useful when a long-lived {@link DetachedSpan} measures many smaller operations (like async-io)
* in which we don't want to produce spans for each task, but do need tracing state associated for logging
* and potential child traces.
* @apiNote This must be executed within a try-with-resources block, and the parent detached span must still be
* completed separately.
*/
@MustBeClosed
CloseableSpan attach();

/**
* Completes this span. After complete is invoked, other methods are not expected to produce spans, but they must
* not throw either in order to avoid confusing failures.
Expand Down
65 changes: 65 additions & 0 deletions tracing/src/main/java/com/palantir/tracing/NopDetached.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* (c) Copyright 2021 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.palantir.tracing;

import com.google.errorprone.annotations.MustBeClosed;
import com.palantir.logsafe.Safe;
import com.palantir.tracing.api.SpanType;

/**
* {@link Detached} implementation which represents state with no trace.
* Operations which create child traces will each generate a unique traceId and sample independently.
*/
enum NopDetached implements Detached {
INSTANCE;

private static final CloseableSpan COMPLETE_SPAN = Tracer::fastCompleteSpan;

@Override
@MustBeClosed
public <T> CloseableSpan childSpan(
String operationName, TagTranslator<? super T> translator, T data, SpanType type) {
return CloseableTracer.startSpan(operationName, translator, data, type)::close;
}

@Override
@MustBeClosed
public CloseableSpan childSpan(@Safe String operationName, SpanType type) {
Tracer.fastStartSpan(operationName, type);
return COMPLETE_SPAN;
}

@Override
@MustBeClosed
public CloseableSpan attach() {
return NopCloseableSpan.INSTANCE;
}

@Override
public DetachedSpan childDetachedSpan(String operation, SpanType type) {
return DetachedSpan.start(operation, type);
}

private enum NopCloseableSpan implements CloseableSpan {
INSTANCE;

@Override
public void close() {
// nop
}
}
}
Loading