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
10 changes: 10 additions & 0 deletions brave/src/main/java/brave/TracingCustomizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,15 @@
* @since 5.7
*/
public interface TracingCustomizer {
/** Use to avoid comparing against null references */
TracingCustomizer NOOP = new TracingCustomizer() {
@Override public void customize(Tracing.Builder builder) {
}

@Override public String toString() {
return "NoopTracingCustomizer{}";
}
};

void customize(Tracing.Builder builder);
}
59 changes: 59 additions & 0 deletions brave/src/main/java/brave/propagation/ExtraFieldCustomizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2013-2019 The OpenZipkin Authors
*
* 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 brave.propagation;

import brave.Tracing;
import brave.TracingCustomizer;

/**
* This allows configuration plugins to collaborate on building an instance of {@link
* ExtraFieldPropagation.Factory}.
*
* <p>For example, a customizer can {@link ExtraFieldPropagation.FactoryBuilder#addField(String)
* add an extra field field} without affecting the {@link ExtraFieldPropagation#newFactoryBuilder(Propagation.Factory)
* trace propagation format}.
*
* <p>This also allows one object to customize both {@link Tracing}, via {@link TracingCustomizer},
* and extra fields {@link ExtraFieldPropagation}, by implementing both customizer interfaces.
*
* <h3>Integration examples</h3>
*
* <p>In practice, a dependency injection tool applies a collection of these instances prior to
* {@link ExtraFieldPropagation.FactoryBuilder#build() building the tracing instance}. For example,
* an injected {@code List<ExtraFieldPropagationCustomizer>} parameter to a provider of {@link
* Propagation.Factory}.
*
* <p>Here are some examples, in alphabetical order:
* <pre><ul>
* <li><a href="https://dagger.dev/multibindings.html">Dagger Set Multibindings</a></li>
* <li><a href="http://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/multibindings/Multibinder.html">Guice Set Multibinder</a></li>
* <li><a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-autowired-annotation">Spring Autowired Collections</a></li>
* </ul></pre>
*
* @see TracingCustomizer
* @since 5.7
*/
public interface ExtraFieldCustomizer {
/** Use to avoid comparing against null references */
ExtraFieldCustomizer NOOP = new ExtraFieldCustomizer() {
@Override public void customize(ExtraFieldPropagation.FactoryBuilder builder) {
}

@Override public String toString() {
return "NoopExtraFieldCustomizer{}";
}
};

void customize(ExtraFieldPropagation.FactoryBuilder builder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,15 @@
* @since 5.7
*/
public interface HttpTracingCustomizer {
/** Use to avoid comparing against null references */
HttpTracingCustomizer NOOP = new HttpTracingCustomizer() {
@Override public void customize(HttpTracing.Builder builder) {
}

@Override public String toString() {
return "NoopHttpTracingCustomizer{}";
}
};

void customize(HttpTracing.Builder builder);
}
11 changes: 5 additions & 6 deletions spring-beans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Bean Factories exist for the following types:
* EndpointFactoryBean - for configuring the service name, IP etc representing this host
* TracingFactoryBean - wires most together, like reporter and log integration
* HttpTracingFactoryBean - for http tagging and sampling policy
* CurrentTraceContextFactoryBean - for scope decorations such as MDC (logging) field correlation
* ExtraFieldPropagationFactoryBean - for propagating extra fields over headers, like "customer-id"

Here are some example beans using the factories in this module:
```xml
Expand Down Expand Up @@ -41,15 +43,12 @@ Here's an advanced example, which propagates the request-scoped header "x-vcap-r
with trace headers:

```xml
<bean id="propagationFactory" class="brave.propagation.ExtraFieldPropagation" factory-method="newFactory">
<constructor-arg index="0">
<util:constant static-field="brave.propagation.B3Propagation.FACTORY"/>
</constructor-arg>
<constructor-arg index="1">
<bean id="propagationFactory" class="brave.spring.beans.ExtraFieldPropagationFactoryBean">
<property name="fields">
<list>
<value>x-vcap-request-id</value>
</list>
</constructor-arg>
</property>
</bean>

<bean id="tracing" class="brave.spring.beans.TracingFactoryBean">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2013-2019 The OpenZipkin Authors
*
* 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 brave.spring.beans;

import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldCustomizer;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.Propagation;
import java.util.List;
import org.springframework.beans.factory.FactoryBean;

/** Spring XML config does not support chained builders. This converts accordingly */
public class ExtraFieldPropagationFactoryBean implements FactoryBean {
Propagation.Factory propagationFactory = B3Propagation.FACTORY;
List<String> fields;
List<ExtraFieldCustomizer> customizers;

@Override public Propagation.Factory getObject() {
ExtraFieldPropagation.FactoryBuilder builder =
ExtraFieldPropagation.newFactoryBuilder(propagationFactory);
if (fields != null) {
for (String field : fields) builder.addField(field);
}
if (customizers != null) {
for (ExtraFieldCustomizer customizer : customizers) customizer.customize(builder);
}
return builder.build();
}

@Override public Class<? extends Propagation.Factory> getObjectType() {
return Propagation.Factory.class;
}

@Override public boolean isSingleton() {
return true;
}

public void setPropagationFactory(Propagation.Factory propagationFactory) {
this.propagationFactory = propagationFactory;
}

public void setFields(List<String> fields) {
this.fields = fields;
}

public void setCustomizers(List<ExtraFieldCustomizer> customizers) {
this.customizers = customizers;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2013-2019 The OpenZipkin Authors
*
* 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 brave.spring.beans;

import brave.propagation.B3Propagation;
import brave.propagation.B3SinglePropagation;
import brave.propagation.ExtraFieldCustomizer;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.Propagation;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.api.InstanceOfAssertFactory;
import org.junit.After;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class ExtraFieldPropagationFactoryBeanTest {
XmlBeans context;

@After public void close() {
if (context != null) context.close();
}

@Test public void propagationFactory_default() {
context = new XmlBeans(""
+ "<bean id=\"propagationFactory\" class=\"brave.spring.beans.ExtraFieldPropagationFactoryBean\"/>"
);

assertThat(context.getBean("propagationFactory", Propagation.Factory.class))
.extracting("delegate")
.isEqualTo(B3Propagation.FACTORY);
}

@Test public void propagationFactory() {
context = new XmlBeans(""
+ "<bean id=\"propagationFactory\" class=\"brave.spring.beans.ExtraFieldPropagationFactoryBean\">\n"
+ " <property name=\"propagationFactory\">\n"
+ " <util:constant static-field=\""
+ B3SinglePropagation.class.getName()
+ ".FACTORY\"/>\n"
+ " </property>\n"
+ "</bean>"
);

assertThat(context.getBean("propagationFactory", Propagation.Factory.class))
.extracting("delegate")
.isEqualTo(B3SinglePropagation.FACTORY);
}

@Test public void fields() {
context = new XmlBeans(""
+ "<bean id=\"propagationFactory\" class=\"brave.spring.beans.ExtraFieldPropagationFactoryBean\">\n"
+ " <property name=\"fields\">\n"
+ " <list>\n"
+ " <value>customer-id</value>\n"
+ " <value>x-vcap-request-id</value>\n"
+ " </list>\n"
+ " </property>"
+ "</bean>"
);

assertThat(context.getBean("propagationFactory", Propagation.Factory.class))
.extracting("keyNames").asInstanceOf(InstanceOfAssertFactories.ARRAY)
.containsExactly("customer-id", "x-vcap-request-id");
}

public static final ExtraFieldCustomizer CUSTOMIZER_ONE = mock(ExtraFieldCustomizer.class);
public static final ExtraFieldCustomizer CUSTOMIZER_TWO = mock(ExtraFieldCustomizer.class);

@Test public void customizers() {
context = new XmlBeans(""
+ "<bean id=\"propagationFactory\" class=\"brave.spring.beans.ExtraFieldPropagationFactoryBean\">\n"
+ " <property name=\"customizers\">\n"
+ " <list>\n"
+ " <util:constant static-field=\"" + getClass().getName() + ".CUSTOMIZER_ONE\"/>\n"
+ " <util:constant static-field=\"" + getClass().getName() + ".CUSTOMIZER_TWO\"/>\n"
+ " </list>\n"
+ " </property>"
+ "</bean>"
);

context.getBean("propagationFactory", Propagation.Factory.class);

verify(CUSTOMIZER_ONE).customize(any(ExtraFieldPropagation.FactoryBuilder.class));
verify(CUSTOMIZER_TWO).customize(any(ExtraFieldPropagation.FactoryBuilder.class));
}
}