Skip to content

Commit

Permalink
Adds LocalSpanThreadBinder, for async local spans (openzipkin#207)
Browse files Browse the repository at this point in the history
When I added LocalSpanState, I forgot to add LocalSpanThreadBinder.
Without this, you can't do things like close local spans with async
callbacks.
  • Loading branch information
adriancole committed Jul 27, 2016
1 parent d14ba2e commit aca24d9
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
13 changes: 13 additions & 0 deletions brave-core/src/main/java/com/github/kristofa/brave/Brave.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class Brave {
private final AnnotationSubmitter serverSpanAnnotationSubmitter;
private final ServerSpanThreadBinder serverSpanThreadBinder;
private final ClientSpanThreadBinder clientSpanThreadBinder;
private final LocalSpanThreadBinder localSpanThreadBinder;

/**
* Builds Brave api objects with following defaults if not overridden:
Expand Down Expand Up @@ -185,6 +186,17 @@ public ClientSpanThreadBinder clientSpanThreadBinder() {
return clientSpanThreadBinder;
}

/**
* Helper object that can be used to propagate local trace state. Typically over different
* threads.
*
* @return {@link LocalSpanThreadBinder}.
* @see LocalSpanThreadBinder
*/
public LocalSpanThreadBinder localSpanThreadBinder() {
return localSpanThreadBinder;
}

/**
* Can be used to submit application specific annotations to the current server span.
*
Expand Down Expand Up @@ -220,5 +232,6 @@ private Brave(Builder builder) {
serverSpanAnnotationSubmitter = AnnotationSubmitter.create(SpanAndEndpoint.ServerSpanAndEndpoint.create(builder.state));
serverSpanThreadBinder = new ServerSpanThreadBinder(builder.state);
clientSpanThreadBinder = new ClientSpanThreadBinder(builder.state);
localSpanThreadBinder = new LocalSpanThreadBinder(builder.state);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.github.kristofa.brave;

import com.twitter.zipkin.gen.Span;

import static com.github.kristofa.brave.internal.Util.checkNotNull;

/**
* Allows binding span from local request thread to a async callback thread that process the
* result.
*
* <p> To be used for async local call the result of which is processed in a separate callback
* thread. After calling {@link LocalTracer#startNewSpan(String, String)}, call {@link
* #getCurrentLocalSpan()} and save the result to pass to the callback method (e.g., local final
* variable) In the callback method, call {@link #setCurrentSpan} before calling {@link
* LocalTracer#finishSpan()}
*/
public final class LocalSpanThreadBinder {

private final LocalSpanState state;

/**
* Creates a new instance.
*
* @param state local span state, cannot be <code>null</code>
*/
public LocalSpanThreadBinder(LocalSpanState state) {
this.state = checkNotNull(state, "state");
}

/**
* This should be called in the thread in which the local request made after starting new local
* span. <p> It returns the current local span which you can keep and bind to the callback thread
*
* @return Returned Span can be bound to different callback thread.
* @see #setCurrentSpan(Span)
*/
public Span getCurrentLocalSpan() {
return state.getCurrentLocalSpan();
}

/**
* Binds given span to current thread. This should typically be called when code is invoked in
* async local callback before the {@link LocalTracer#finishSpan()}
*
* @param span Span to bind to current execution thread. Cannot be <code>null</code>.
*/
public void setCurrentSpan(Span span) {
state.setCurrentLocalSpan(span);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.github.kristofa.brave;

import com.twitter.zipkin.gen.Span;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

public class LocalSpanThreadBinderTest {

private LocalSpanState mockLocalSpanState;
private Span mockSpan;
private LocalSpanThreadBinder binder;

@Before
public void setup() {
mockLocalSpanState = mock(LocalSpanState.class);
binder = new LocalSpanThreadBinder(mockLocalSpanState);
mockSpan = mock(Span.class);
}

@Test(expected = NullPointerException.class)
public void testConstructorNullState() {
new LocalSpanThreadBinder(null);
}

@Test
public void testGetCurrentLocalSpanNullSpan() {
assertNull(binder.getCurrentLocalSpan());
verify(mockLocalSpanState).getCurrentLocalSpan();
verifyNoMoreInteractions(mockLocalSpanState);
}

@Test
public void testGetCurrentLocalSpan() {
when(mockLocalSpanState.getCurrentLocalSpan()).thenReturn(mockSpan);
assertSame(mockSpan, binder.getCurrentLocalSpan());
verify(mockLocalSpanState).getCurrentLocalSpan();
verifyNoMoreInteractions(mockLocalSpanState);
}

@Test
public void testSetCurrentSpanNull() {
binder.setCurrentSpan(null);
verify(mockLocalSpanState).setCurrentLocalSpan(null);
verifyNoMoreInteractions(mockLocalSpanState);
}

@Test
public void testSetCurrentSpan() {
binder.setCurrentSpan(mockSpan);
verify(mockLocalSpanState).setCurrentLocalSpan(mockSpan);
verifyNoMoreInteractions(mockLocalSpanState);
}

}

0 comments on commit aca24d9

Please sign in to comment.