├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .gitignore ├── RELEASE.md ├── opentracing-apache-httpclient ├── src │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── opentracing │ │ │ └── contrib │ │ │ └── apache │ │ │ └── http │ │ │ └── client │ │ │ ├── Constants.java │ │ │ ├── HttpHeadersInjectAdapter.java │ │ │ ├── ApacheClientSpanDecorator.java │ │ │ ├── TracingHttpClientBuilder.java │ │ │ └── TracingClientExec.java │ └── test │ │ └── java │ │ └── io │ │ └── opentracing │ │ └── contrib │ │ └── apache │ │ └── http │ │ └── client │ │ └── TracingHttpClientBuilderTest.java └── pom.xml ├── .settings.xml ├── README.md ├── .travis.yml ├── travis └── publish.sh ├── mvnw.cmd ├── pom.xml ├── mvnw └── LICENSE /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opentracing-contrib/java-apache-httpclient/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | target 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | !.mvn/wrapper/maven-wrapper.jar 14 | 15 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 16 | hs_err_pid* 17 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # OpenTracing Release Process 2 | 3 | This repo uses semantic versions. Please keep this in mind when choosing version numbers. 4 | 5 | For the up-to-date release process, please refer the 6 | [release process from the OpenTracing Java API](https://github.com/opentracing/opentracing-java/blob/master/RELEASE.md). 7 | 8 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/src/main/java/io/opentracing/contrib/apache/http/client/Constants.java: -------------------------------------------------------------------------------- 1 | package io.opentracing.contrib.apache.http.client; 2 | 3 | /** 4 | * @author Pavol Loffay 5 | */ 6 | public final class Constants { 7 | 8 | private Constants() {} 9 | 10 | /** 11 | * SpanContext which will be used as a parent for created client span. 12 | */ 13 | public static final String PARENT_CONTEXT = TracingHttpClientBuilder.class.getName() + ".parentSpanContext"; 14 | } 15 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/src/main/java/io/opentracing/contrib/apache/http/client/HttpHeadersInjectAdapter.java: -------------------------------------------------------------------------------- 1 | package io.opentracing.contrib.apache.http.client; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | 6 | import org.apache.http.HttpRequest; 7 | 8 | import io.opentracing.propagation.TextMap; 9 | 10 | /** 11 | * @author Pavol Loffay 12 | */ 13 | public class HttpHeadersInjectAdapter implements TextMap { 14 | 15 | private HttpRequest httpRequest; 16 | 17 | public HttpHeadersInjectAdapter(HttpRequest httpRequest) { 18 | this.httpRequest = httpRequest; 19 | } 20 | 21 | @Override 22 | public void put(String key, String value) { 23 | httpRequest.setHeader(key, value); 24 | } 25 | 26 | @Override 27 | public Iterator> iterator() { 28 | throw new UnsupportedOperationException("This class should be used only with tracer#inject()"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | sonatype 9 | ${env.SONATYPE_USERNAME} 10 | ${env.SONATYPE_PASSWORD} 11 | 12 | 13 | bintray 14 | ${env.BINTRAY_USER} 15 | ${env.BINTRAY_KEY} 16 | 17 | 18 | jfrog-snapshots 19 | ${env.BINTRAY_USER} 20 | ${env.BINTRAY_KEY} 21 | 22 | 23 | github.com 24 | ${env.GH_USER} 25 | ${env.GH_TOKEN} 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status][ci-img]][ci] [![Released Version][maven-img]][maven] 2 | 3 | # OpenTracing Apache HttpClient 4 | 5 | Apache HttpClient instrumentation. 6 | 7 | ## Configuration 8 | 9 | ```java 10 | GlobalTracer.register(tracer); 11 | HttpClient httpClient = new TracingHttpClientBuilder() 12 | .build(); 13 | 14 | //custom configuration 15 | HttpClient httpClient = new TracingHttpClientBuilder( 16 | redirectStrategy, 17 | redirectHandlingDisabled, 18 | GlobalTracer.get(), 19 | spanDecorators) 20 | .build(); 21 | ``` 22 | 23 | ## Linking to parent span 24 | If parent span context is not passed created client spans will be in a new trace. 25 | 26 | ### SpanManager 27 | ```java 28 | spanManager.activate(parentSpan); // called in the same thread as client.execute() 29 | client.execute(new HttpGet("url")); 30 | ``` 31 | 32 | ### Manually 33 | ```java 34 | BasicHttpContext basicHttpContext = new BasicHttpContext(); 35 | basicHttpContext.setAttribute(Constants.PARENT_CONTEXT, parentSpan.context()); 36 | client.execute(new HttpGet("url"), basicHttpContext); 37 | ``` 38 | 39 | ## Development 40 | ```shell 41 | ./mvnw clean install 42 | ``` 43 | 44 | ## Release 45 | Follow instructions in [RELEASE](RELEASE.md) 46 | 47 | [ci-img]: https://travis-ci.org/opentracing-contrib/java-apache-httpclient.svg?branch=master 48 | [ci]: https://travis-ci.org/opentracing-contrib/java-apache-httpclient 49 | [maven-img]: https://img.shields.io/maven-central/v/io.opentracing.contrib/opentracing-apache-httpclient.svg?maxAge=2592000 50 | [maven]: http://search.maven.org/#search%7Cga%7C1%7Copentracing-apache-httpclient 51 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.opentracing.contrib 6 | opentracing-apache-httpclient-parent 7 | 0.3.4-SNAPSHOT 8 | 9 | 10 | opentracing-apache-httpclient 11 | 12 | 13 | 14 | io.opentracing 15 | opentracing-api 16 | 17 | 18 | io.opentracing 19 | opentracing-util 20 | 21 | 22 | 23 | org.apache.httpcomponents 24 | httpclient 25 | ${version.org.apache.httpcomponents-httpclient} 26 | 27 | 28 | 29 | junit 30 | junit 31 | test 32 | 33 | 34 | io.opentracing 35 | opentracing-mock 36 | test 37 | 38 | 39 | org.apache.httpcomponents 40 | httpclient 41 | ${version.org.apache.httpcomponents-httpclient} 42 | tests 43 | test 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/src/main/java/io/opentracing/contrib/apache/http/client/ApacheClientSpanDecorator.java: -------------------------------------------------------------------------------- 1 | package io.opentracing.contrib.apache.http.client; 2 | 3 | import io.opentracing.Span; 4 | import io.opentracing.tag.Tags; 5 | import org.apache.http.HttpHost; 6 | import org.apache.http.HttpRequest; 7 | import org.apache.http.HttpResponse; 8 | import org.apache.http.client.methods.HttpRequestWrapper; 9 | import org.apache.http.protocol.HttpContext; 10 | 11 | import java.net.URI; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.logging.Logger; 15 | 16 | /** 17 | * Decorate span at different stages of request processing. Do not finish span in decorator. 18 | * 19 | * @author Pavol Loffay 20 | */ 21 | public interface ApacheClientSpanDecorator { 22 | 23 | /** 24 | * Decorate span before request is fired. 25 | * 26 | * @param request request 27 | * @param httpContext context 28 | * @param span span to decorate 29 | */ 30 | void onRequest(HttpRequestWrapper request, HttpContext httpContext, Span span); 31 | 32 | /** 33 | * Decorate span after response is received. 34 | * 35 | * @param response response 36 | * @param httpContext context 37 | * @param span span to decorate 38 | */ 39 | void onResponse(HttpResponse response, HttpContext httpContext, Span span); 40 | 41 | /** 42 | * Decorate span span on error e.g. {@link java.net.UnknownHostException}/ 43 | * 44 | * @param request request 45 | * @param httpContext context 46 | * @param ex exception 47 | * @param span span to decorate 48 | */ 49 | void onError(HttpRequest request, HttpContext httpContext, Exception ex, Span span); 50 | 51 | /** 52 | * Decorator which adds standard set of tags and logs. 53 | */ 54 | class StandardTags implements ApacheClientSpanDecorator { 55 | private static final Logger log = Logger.getLogger(StandardTags.class.getName()); 56 | 57 | @Override 58 | public void onRequest(HttpRequestWrapper request, HttpContext httpContext, Span span) { 59 | URI uri = request.getURI(); 60 | HttpHost target = request.getTarget(); 61 | 62 | Tags.HTTP_METHOD.set(span, request.getRequestLine().getMethod()); 63 | 64 | if (uri != null && uri.isAbsolute()) { 65 | Tags.HTTP_URL.set(span, uri.toString()); 66 | Tags.PEER_HOSTNAME.set(span, uri.getHost()); 67 | int port = uri.getPort(); 68 | if (port == -1) { 69 | String scheme = uri.getScheme(); 70 | if (scheme != null) { 71 | port = scheme.equalsIgnoreCase("https") ? 443 : 80; 72 | } 73 | } 74 | Tags.PEER_PORT.set(span, port); 75 | } else if (target != null) { 76 | Tags.HTTP_URL.set(span, request.getTarget() + request.getRequestLine().getUri()); 77 | Tags.PEER_HOSTNAME.set(span, target.getHostName()); 78 | Tags.PEER_PORT.set(span, target.getPort() == -1 ? target.getSchemeName().equalsIgnoreCase("https") ? 443 : 80 : target.getPort()); 79 | } 80 | } 81 | 82 | @Override 83 | public void onResponse(HttpResponse response, HttpContext httpContext, Span span) { 84 | Tags.HTTP_STATUS.set(span, response.getStatusLine().getStatusCode()); 85 | } 86 | 87 | @Override 88 | public void onError(HttpRequest request, HttpContext httpContext, Exception ex, Span span) { 89 | Tags.ERROR.set(span, Boolean.TRUE); 90 | 91 | Map errorLogs = new HashMap<>(2); 92 | errorLogs.put("event", Tags.ERROR.getKey()); 93 | errorLogs.put("error.object", ex); 94 | span.log(errorLogs); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/src/main/java/io/opentracing/contrib/apache/http/client/TracingHttpClientBuilder.java: -------------------------------------------------------------------------------- 1 | package io.opentracing.contrib.apache.http.client; 2 | 3 | import io.opentracing.Tracer; 4 | import io.opentracing.contrib.apache.http.client.ApacheClientSpanDecorator.StandardTags; 5 | import io.opentracing.util.GlobalTracer; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | import org.apache.http.client.RedirectStrategy; 10 | import org.apache.http.impl.client.DefaultRedirectStrategy; 11 | import org.apache.http.impl.client.HttpClientBuilder; 12 | import org.apache.http.impl.execchain.ClientExecChain; 13 | 14 | /** 15 | * @author Pavol Loffay 16 | */ 17 | public class TracingHttpClientBuilder extends HttpClientBuilder { 18 | 19 | private final RedirectStrategy redirectStrategy; 20 | private final boolean redirectHandlingDisabled; 21 | private Tracer tracer; 22 | private List spanDecorators; 23 | private boolean injectDisabled; 24 | 25 | /** 26 | * When using this constructor tracer should be registered via 27 | * {@link GlobalTracer#register(Tracer)}. 28 | */ 29 | public TracingHttpClientBuilder() { 30 | this(DefaultRedirectStrategy.INSTANCE, 31 | false, 32 | GlobalTracer.get(), 33 | Collections.singletonList(new ApacheClientSpanDecorator.StandardTags())); 34 | } 35 | 36 | /** 37 | * @param redirectStrategy redirect strategy, do not call 38 | * {@link HttpClientBuilder#disableRedirectHandling()} 39 | * @param redirectHandlingDisabled disable redirect strategy, do not call 40 | * {@link org.apache.http.impl.client.HttpClientBuilder#setRedirectStrategy(RedirectStrategy)} 41 | */ 42 | public TracingHttpClientBuilder( 43 | RedirectStrategy redirectStrategy, 44 | boolean redirectHandlingDisabled) { 45 | this(redirectStrategy, 46 | redirectHandlingDisabled, 47 | GlobalTracer.get(), 48 | Collections.singletonList(new StandardTags())); 49 | } 50 | 51 | /** 52 | * @param redirectStrategy redirect strategy, do not call 53 | * {@link HttpClientBuilder#disableRedirectHandling()} 54 | * @param redirectHandlingDisabled disable redirect strategy, do not call 55 | * {@link org.apache.http.impl.client.HttpClientBuilder#setRedirectStrategy(RedirectStrategy)} 56 | * @param tracer tracer instance 57 | * @param spanDecorators decorators 58 | */ 59 | public TracingHttpClientBuilder( 60 | RedirectStrategy redirectStrategy, 61 | boolean redirectHandlingDisabled, 62 | Tracer tracer, 63 | List spanDecorators) { 64 | this.redirectStrategy = redirectStrategy; 65 | this.redirectHandlingDisabled = redirectHandlingDisabled; 66 | this.tracer = tracer; 67 | this.spanDecorators = new ArrayList<>(spanDecorators); 68 | 69 | super.setRedirectStrategy(redirectStrategy); 70 | if (redirectHandlingDisabled) { 71 | super.disableRedirectHandling(); 72 | } 73 | } 74 | 75 | public static TracingHttpClientBuilder create() { 76 | return new TracingHttpClientBuilder(); 77 | } 78 | 79 | public TracingHttpClientBuilder withTracer(Tracer tracer) { 80 | this.tracer = tracer; 81 | return this; 82 | } 83 | 84 | public TracingHttpClientBuilder withSpanDecorators(List decorators) { 85 | this.spanDecorators = new ArrayList<>(decorators); 86 | return this; 87 | } 88 | 89 | public TracingHttpClientBuilder disableInjection() { 90 | this.injectDisabled = true; 91 | return this; 92 | } 93 | 94 | @Override 95 | protected ClientExecChain decorateProtocolExec(final ClientExecChain requestExecutor) { 96 | return new TracingClientExec(requestExecutor, redirectStrategy, 97 | redirectHandlingDisabled, injectDisabled, tracer, spanDecorators); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | 4 | language: java 5 | jdk: 6 | - oraclejdk8 7 | 8 | cache: 9 | directories: 10 | - $HOME/.m2/repository 11 | 12 | before_install: 13 | # allocate commits to CI, not the owner of the deploy key 14 | - git config user.name "opentracingci" 15 | - git config user.email "opentracingci+opentracing@googlegroups.com" 16 | 17 | # setup https authentication credentials, used by ./mvnw release:prepare 18 | - git config credential.helper "store --file=.git/credentials" 19 | - echo "https://$GH_TOKEN:@github.com" > .git/credentials 20 | 21 | install: 22 | # Override default travis to use the maven wrapper 23 | - ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 24 | 25 | script: 26 | - ./travis/publish.sh 27 | 28 | branches: 29 | except: 30 | - /^[0-9]/ 31 | 32 | env: 33 | global: 34 | # Ex. travis encrypt -r org/repo GH_TOKEN=XXX-https://github.com/settings/tokens-XXX 35 | - secure: "mmCO91ji9Cb4lyt88Spezzl9KDhniOYnqMn7TjLHGhETAQ4aOa3B9Rf9YaBNKS0tj6KBuDKyau1Ld/wGQHrFIN9QA0XRW1qofHycA4giuJhYhA6fVZGhyqblpd1Kd+N1t3XeV2bZzQf62ViqoHx+QGed8GFaRDi/MD7gFGVhfXidCgzA+XnPXHvCR3YLJvn/agKPMU8BNNyb/RRT/NmEwcFsrkJjJTq+osq5bBG/2Wsz6Brg9dL9k7tTI/i9SGcdjDF69BoMGHzdyWTVlLHxe1ZsL30Fm8slSZPvV2omXAbaHzMlp5Aun0pPLrJKf1bOddBA5m4VCz+MvR4SLqi+XBe56iypMxBm3tHbGoxgMibDddQTW2oI98cIXpW0hNdUz7cn/3z2TY+QYzYICDx8uNzrJnVHrCOHPpRvWTcd5yfJOiFbOWME2qsN9VMKiNlSRC8cKqz0DWpUDwyAwxGChM0ghIrpHkondd68Opcj+kfZ6cYivMKywnnfAy1uudoe/YuWkTxVFtg0ws5VcDblDOtI76f2KysDEetlUKrkM6ik3ng16Dzs1yMPGGwvfQmI7LjYRQbQ8vtPfOs8cL+d+x4hLxy1LTp02wO7ujEy3cpgMcKtpRnaeXlJ8DGy1Z7QA8Ahm022UagylazeWSAHr2rt1FPsEsuzGnuO1+wu/7k=" 36 | # Ex. travis encrypt -r org/repo SONATYPE_USERNAME=your_sonatype_account 37 | - secure: "igpX3ABlbhGzT5juScj/KxVtLLV+w0+HUTCM2MeYisQFZfnAEuHjdQ8Q7sRaQBnbsYCXurRwdN7IQDdVhBKCmDHQ152GQRShlz08CK8qNSCm+ZAkYV3lOkb6GuO+mrA7+F/18Yj08r4Ea7PAfs5dN3D363gVV/WQLqoK1PjHd1Pg6Rca+XOJH7x9U4HzDyczPP3jajcBACBAEGV8G+CSNoXswnQo4vhNVbvHXElF5xC6b+MgzqB6L2/W3+kqEQr7O+KNKXbdfczDSfKAUE6T2gLo2PlYV13kWRAyhGrhTZhPYBqBaqusj9ynirfyws1jKlnKZv9RupGeih7iVDu0DEmAXwz1ZIvx0idm6H1cnpuvW+zVC+uwSvetLcLdri/TADMygCyovROb6HXlrqhNZv6AI/D9VtkqTbv26DJsUiV+iMVh79g5fnigkRGKNaVwlnWHvcgWpp/z64ZalVIStEslWPxg8iiY1oXnEO/ZYq1Pz0BAUzZk6+U4xBhTZ2OP+VN4dPbZgLcLLJrf6jT56QHUw1PvdFg1+gsSfnw8Kqf+EboeZjpU6y6r5j2fjeYrH5HFTBd4zBv6chQ6vdsS820Xbz747MY0gUK6KR0NsloZTRZqrr/CC26PyWLK0ledGVRSZdpAtSCvnQLgL7M3TFvBfYNj8AQ8aQ3UtW0gTgk=" 38 | # Ex. travis encrypt -r org/repo SONATYPE_PASSWORD=your_sonatype_password 39 | - secure: "XjmXh4PwiezA+XNpcyQUOB8a3TWmE5okoBzwaoQYcB8MVg0RFkya34HuoNCaINuhKOxVBJDRKgYhfUi/6lnNKNAgJxcxodxfV+ETXMxGNbDzgmYDwNSlpMATOjbRE3FEs00+KbI6OlTNxaOPBxdN6fV1SPKrZ2MvHSDfJDWTktSC1vE8VcKBpgB1tkNQxDnCA7fl1XxfOTvfJBCLURXsMXDkMDas6HrrYj0ou87BimWMHTC6TAMiEvxsv0J0KG9tuzAGYL0VAF7iPcWmuzV3yW0yMEJINO3T1l9ghulIwSsr3jYApVJl8w+1gw0cC0rW7/ZLtTHrn9ekwKlRliaInetR9PlCLPeKaDRpre6dLCUadcOR6hwFOKqNLOaTfp+PD85l40Fp5rLwTnmrmjPusiQ5YPoM2BUdF9jfjvNgo1Zr+dvG4zVTWxyLA+fiful+KcCxPG1kYxeX6AgVCXOvMgOFukppqsVTaIVrMLRZ7d7SnyriIDzy+1QRyJRC2vzoJH6Nc7hAov+UxMMvdfP3cP5zEMsM2pMiDRvTV3VAppbZ/v56xRJt9V1hyBJwmEByv7JTsDE5oM4NAF3X8F5u/JoR2eETVvkv+qjvelDKahbHPlc+a2ndrMWrr3E5ZeZs6PNsIdoFanPGn9gTuSNG3/jJ3YZWc/NVc2qd3Kra2PU=" 40 | # Ex. travis encrypt -r org/repo BINTRAY_USER=your_github_account 41 | - secure: "O9CRUyXklPLwrhEkzrs/qf6FZevvJDxKFOLkGg+NGh5rk11lIsefROVFVkB1hWD/YDtior2PJqjwjdAUFm5RsXT8LCPRSA8FHkE6no1RJPgVnhYvDdgu/CuDE+v4DI/FCszRnV6nt5TFgTZCUbF/Q0gR/mWTU4d722qR/5oHVaQumnv0V/GiSvThCT4YaZFatk2WGhh6Lj0shi00blc1e7bzaPykBRZ3PAIXq4fIyIpcIHUUA42JL5vVHFl4Yun+CcHLboDs3+5/K9aXnX8OMSWS4i0P3Fijhx8Aok1NkK6IkospBLRnqHMd97o99P3FXVAKa775L8yqFKlcF5YZkb87w/vYLBwqsnj2QuQHSEWXKv6O0OWjDBkA2WMpWfEkOkSh+yDqskyacctEVobkRCQ6JWoCvEuUMaqck0Hdzf6xZ6KnIxzpsJ23L7O1MLbvR7BhjRD0W8pUbuW0BcSkPbV6ee24q8wsVvXHsH/cFau+XlcdmXKdiajrifVD3RyTT0T59cTU9HHSoRpSOty4+M8MebJ78Lg9kHaFW+x2085P3PrDUdXFZjuz7L28u0YO8EXIxX8WfWXa4XilBdo8G/ZmQ3kt4MorgTfyMIbxsQmLhR2CJR5XOxaJ2CkCbRRozWcEB6OvDHst7NVvrvzpsnDlx7mOvXEtS1lNDzWTKds=" 42 | # Ex. travis encrypt -r org/repo BINTRAY_KEY=xxx-https://bintray.com/profile/edit-xxx 43 | - secure: "UoN2MbXr62pth4JzydpCG0zO3Gn1/dtvZv2Paal2brkbdaotSyNca7itmeVpTdetd3qAuOyrNE9Yd7QG8+u1Z9harVdic43EtCbZjrTa3ZWTjQ/rMwgMNYPW254aaVhX9I9Vkf7/rgUlHo+UNjKuEZRejgpOYFpv3Kiynn8EfXXaJRNEF3dklIqIgqn4xqZNjTnwvK/3LKEs0XEuxk2/WJnz+HCIv3YRP3/3s0pnZ3vRaZUccP8Vd/UL3y8vs4riPOpUXHRSU2LwpSIKAwY5qEjmcQ/xQXVCbfstXQVJUXUhddiCkjZXda+dKr1UcU1EBKNtRVKMD5uku39IPST5NlQCO8OUmNwSTyntmumQYfkEUk21rV3Mxh+2zAGyE9/PSRjSz0K9Ya0ZV6rvY2Ht3PfQ12K2+dOTlUqA11crEX7h4MdlD/RLd/wXmPyogEEAtr8JdCDX/idurIZO2MLNvv85Y9tQeiK9uOHLCFHPFPbEqg/CKrdP761nfSLbIK5w8HeXcuk1nJ/rayqwTRaVdUlBFlxnxcku4TXGGc4cxxoTuWA3r/EKzVYY1jtVn2clLDMbiXo7QWaSt6+2CZDIs6ilaEsUPVc1Cw+USdHGyhUKNPSNrstCNswPfK9MN8Jvh/I+K8FEPhi6ugaEdU23u9samZBrIRyp5wdTHZ22SQE=" 44 | -------------------------------------------------------------------------------- /travis/publish.sh: -------------------------------------------------------------------------------- 1 | set -euo pipefail 2 | set -x 3 | 4 | build_started_by_tag() { 5 | if [ "${TRAVIS_TAG}" == "" ]; then 6 | echo "[Publishing] This build was not started by a tag, publishing snapshot" 7 | return 1 8 | else 9 | echo "[Publishing] This build was started by the tag ${TRAVIS_TAG}, publishing release" 10 | return 0 11 | fi 12 | } 13 | 14 | is_pull_request() { 15 | if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then 16 | echo "[Not Publishing] This is a Pull Request" 17 | return 0 18 | else 19 | echo "[Publishing] This is not a Pull Request" 20 | return 1 21 | fi 22 | } 23 | 24 | is_travis_branch_master_or_release() { 25 | if [[ "${TRAVIS_BRANCH}" == "master" || "${TRAVIS_BRANCH}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then 26 | echo "[Publishing] Travis branch is ${TRAVIS_BRANCH}" 27 | return 0 28 | else 29 | echo "[Not Publishing] Travis branch is not master or v0.0.0" 30 | return 1 31 | fi 32 | } 33 | 34 | check_travis_branch_equals_travis_tag() { 35 | #Weird comparison comparing branch to tag because when you 'git push --tags' 36 | #the branch somehow becomes the tag value 37 | #github issue: https://github.com/travis-ci/travis-ci/issues/1675 38 | if [ "${TRAVIS_BRANCH}" != "${TRAVIS_TAG}" ]; then 39 | echo "Travis branch does not equal Travis tag, which it should, bailing out." 40 | echo " github issue: https://github.com/travis-ci/travis-ci/issues/1675" 41 | exit 1 42 | else 43 | echo "[Publishing] Branch (${TRAVIS_BRANCH}) same as Tag (${TRAVIS_TAG})" 44 | fi 45 | } 46 | 47 | check_release_tag() { 48 | tag="${TRAVIS_TAG}" 49 | if [[ "$tag" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(\-RC[[:digit:]]+)?$ ]]; then 50 | echo "Build started by version tag $tag. During the release process tags like this" 51 | echo "are created by the 'release' Maven plugin. Nothing to do here." 52 | exit 0 53 | elif [[ ! "$tag" =~ ^release-[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(\-RC[[:digit:]]+)?$ ]]; then 54 | echo "You must specify a tag of the format 'release-0.0.0' or 'release-0.0.0-RC0' to release this project." 55 | echo "The provided tag ${tag} doesn't match that. Aborting." 56 | exit 1 57 | fi 58 | } 59 | 60 | is_release_commit() { 61 | project_version=$(./mvnw help:evaluate -N -Dexpression=project.version|sed -n '/^[0-9]/p') 62 | if [[ "$project_version" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(\-RC[[:digit:]]+)?$ ]]; then 63 | echo "Build started by release commit $project_version. Will synchronize to maven central." 64 | return 0 65 | else 66 | return 1 67 | fi 68 | } 69 | 70 | release_version() { 71 | echo "${TRAVIS_TAG}" | sed 's/^release-//' 72 | } 73 | 74 | safe_checkout_remote_branch() { 75 | # We need to be on a branch for release:perform to be able to create commits, 76 | # and we want that branch to be master or v0.0.0 (for RCs). which has been checked before. 77 | # But we also want to make sure that we build and release exactly the tagged version, 78 | # so we verify that the remote branch is where our tag is. 79 | checkoutBranch=master 80 | if [[ "${TRAVIS_BRANCH}" =~ ^release-[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+\-RC[[:digit:]]+$ ]]; then 81 | checkoutBranch=v`release_version | sed 's/-RC[[:digit:]]\+//'` 82 | fi 83 | git checkout -B "${checkoutBranch}" 84 | git fetch origin "${checkoutBranch}":origin/"${checkoutBranch}" 85 | commit_local="$(git show --pretty='format:%H' ${checkoutBranch})" 86 | commit_remote="$(git show --pretty='format:%H' origin/${checkoutBranch})" 87 | if [ "$commit_local" != "$commit_remote" ]; then 88 | echo "${checkoutBranch} on remote 'origin' has commits since the version under release, aborting" 89 | exit 1 90 | fi 91 | } 92 | 93 | #---------------------- 94 | # MAIN 95 | #---------------------- 96 | 97 | if ! is_pull_request && build_started_by_tag; then 98 | check_travis_branch_equals_travis_tag 99 | check_release_tag 100 | fi 101 | 102 | ./mvnw install -nsu 103 | 104 | # If we are on a pull request, our only job is to run tests, which happened above via ./mvnw install 105 | if is_pull_request; then 106 | true 107 | # If we are on master, we will deploy the latest snapshot or release version 108 | # - If a release commit fails to deploy for a transient reason, delete the broken version from bintray and click rebuild 109 | elif is_travis_branch_master_or_release; then 110 | ./mvnw --batch-mode -s ./.settings.xml -Prelease -nsu -DskipTests deploy 111 | 112 | # If the deployment succeeded, sync it to Maven Central. Note: this needs to be done once per project, not module, hence -N 113 | if is_release_commit; then 114 | ./mvnw --batch-mode -s ./.settings.xml -nsu -N io.zipkin.centralsync-maven-plugin:centralsync-maven-plugin:sync 115 | fi 116 | 117 | # If we are on a release tag, the following will update any version references and push a version tag for deployment. 118 | elif build_started_by_tag; then 119 | safe_checkout_remote_branch 120 | ./mvnw --batch-mode -s ./.settings.xml -Prelease -nsu -DreleaseVersion="$(release_version)" -Darguments="-DskipTests" release:prepare 121 | fi 122 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in %* 125 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 126 | if ERRORLEVEL 1 goto error 127 | goto end 128 | 129 | :error 130 | set ERROR_CODE=1 131 | 132 | :end 133 | @endlocal & set ERROR_CODE=%ERROR_CODE% 134 | 135 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 136 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 137 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 138 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 139 | :skipRcPost 140 | 141 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 142 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 143 | 144 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 145 | 146 | exit /B %ERROR_CODE% 147 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | io.opentracing.contrib 6 | opentracing-apache-httpclient-parent 7 | 0.3.4-SNAPSHOT 8 | pom 9 | 10 | ${project.groupId}:${project.artifactId} 11 | OpenTracing instrumentation for Apache HttpClient 12 | https://github.com/opentracing-contrib/java-apache-httpclient 13 | 14 | 15 | https://github.com/opentracing-contrib/java-apache-httpclient 16 | scm:git:https://github.com/opentracing-contrib/java-apache-httpclient.git 17 | scm:git:https://github.com/opentracing-contrib/java-apache-httpclient.git 18 | HEAD 19 | 20 | 21 | 22 | 23 | The Apache Software License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | repo 26 | 27 | 28 | 29 | 30 | 31 | pavolloffay 32 | Pavol Loffay 33 | ploffay@redhat.com 34 | 35 | 36 | 37 | 38 | 1.7 39 | 1.7 40 | UTF-8 41 | 42 | 0.33.0 43 | 4.5.3 44 | 4.13.1 45 | 46 | 2.8.2 47 | 2.5.3 48 | 3.0.1 49 | 2.10.4 50 | 0.1.0 51 | 52 | 53 | 54 | opentracing-apache-httpclient 55 | 56 | 57 | 58 | 59 | 60 | io.opentracing 61 | opentracing-api 62 | ${version.io.opentracing} 63 | 64 | 65 | io.opentracing 66 | opentracing-util 67 | ${version.io.opentracing} 68 | 69 | 70 | 71 | junit 72 | junit 73 | ${version.junit} 74 | 75 | 76 | io.opentracing 77 | opentracing-mock 78 | ${version.io.opentracing} 79 | 80 | 81 | 82 | 83 | 84 | 85 | bintray 86 | https://api.bintray.com/maven/opentracing/maven/opentracing-apache-httpclient/;publish=1 87 | 88 | 89 | jfrog-snapshots 90 | http://oss.jfrog.org/artifactory/oss-snapshot-local 91 | 92 | 93 | 94 | 95 | 96 | 97 | maven-release-plugin 98 | ${version.maven-release-plugin} 99 | 100 | false 101 | release 102 | true 103 | @{project.version} 104 | 105 | 106 | 107 | io.zipkin.centralsync-maven-plugin 108 | centralsync-maven-plugin 109 | ${version.io.zikin.centralsync-maven-plugin} 110 | 111 | opentracing 112 | maven 113 | opentracing-apache-httpclient 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | release 122 | 123 | 124 | 125 | 126 | maven-source-plugin 127 | ${version.maven-source-plugin} 128 | 129 | 130 | attach-sources 131 | 132 | jar 133 | 134 | 135 | 136 | 137 | 138 | 139 | maven-javadoc-plugin 140 | ${version.maven-javadoc-plugin} 141 | 142 | false 143 | 144 | 145 | 146 | attach-javadocs 147 | 148 | jar 149 | 150 | package 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/src/main/java/io/opentracing/contrib/apache/http/client/TracingClientExec.java: -------------------------------------------------------------------------------- 1 | package io.opentracing.contrib.apache.http.client; 2 | 3 | import static io.opentracing.contrib.apache.http.client.Constants.PARENT_CONTEXT; 4 | 5 | import io.opentracing.References; 6 | import io.opentracing.Scope; 7 | import io.opentracing.Span; 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import org.apache.http.HttpException; 13 | import org.apache.http.HttpRequest; 14 | import org.apache.http.client.RedirectStrategy; 15 | import org.apache.http.client.methods.CloseableHttpResponse; 16 | import org.apache.http.client.methods.HttpExecutionAware; 17 | import org.apache.http.client.methods.HttpRequestWrapper; 18 | import org.apache.http.client.protocol.HttpClientContext; 19 | import org.apache.http.conn.routing.HttpRoute; 20 | import org.apache.http.impl.execchain.ClientExecChain; 21 | 22 | import io.opentracing.SpanContext; 23 | import io.opentracing.Tracer; 24 | import io.opentracing.propagation.Format; 25 | import io.opentracing.tag.Tags; 26 | 27 | /** 28 | * Tracing is added before {@link org.apache.http.impl.execchain.ProtocolExec} which is invoked as 29 | * the next to last. Note that {@link org.apache.http.impl.execchain.RedirectExec} is invoked before 30 | * so this exec has to deal with redirects. 31 | * 32 | * @author Pavol Loffay 33 | */ 34 | public class TracingClientExec implements ClientExecChain { 35 | static final String COMPONENT_NAME = "apache-httpclient"; 36 | 37 | /** 38 | * Id of {@link HttpClientContext#setAttribute(String, Object)} representing span associated with 39 | * the current client processing. Referenced span is local span not a span representing HTTP communication. 40 | */ 41 | protected static final String SPAN_PROP = TracingHttpClientBuilder.class.getName() + ".currentSpan"; 42 | /** 43 | * Tracing {@link ClientExecChain} is executed after redirect exec, so on redirects it is called 44 | * multiple times. This is used as an id for {@link HttpClientContext#setAttribute(String, Object)} 45 | * to store number of redirects. 46 | */ 47 | protected static final String REDIRECT_COUNT = TracingHttpClientBuilder.class.getName() + ".redirectCount"; 48 | 49 | private final RedirectStrategy redirectStrategy; 50 | private final ClientExecChain requestExecutor; 51 | private final boolean redirectHandlingDisabled; 52 | private final boolean injectDisabled; 53 | 54 | private final Tracer tracer; 55 | private final List spanDecorators; 56 | 57 | public TracingClientExec( 58 | ClientExecChain clientExecChain, 59 | RedirectStrategy redirectStrategy, 60 | boolean redirectHandlingDisabled, 61 | boolean injectDisabled, 62 | Tracer tracer, 63 | List spanDecorators) { 64 | this.requestExecutor = clientExecChain; 65 | this.redirectStrategy = redirectStrategy; 66 | this.redirectHandlingDisabled = redirectHandlingDisabled; 67 | this.injectDisabled = injectDisabled; 68 | this.tracer = tracer; 69 | this.spanDecorators = new ArrayList<>(spanDecorators); 70 | } 71 | 72 | @Override 73 | public CloseableHttpResponse execute( 74 | HttpRoute route, 75 | HttpRequestWrapper request, 76 | HttpClientContext clientContext, 77 | HttpExecutionAware execAware) throws IOException, HttpException { 78 | 79 | Span localSpan = handleLocalSpan(request, clientContext); 80 | CloseableHttpResponse response = null; 81 | try { 82 | return (response = handleNetworkProcessing(localSpan, route, request, clientContext, execAware)); 83 | } catch (Exception e) { 84 | localSpan.finish(); 85 | throw e; 86 | } finally { 87 | if (response != null) { 88 | /** 89 | * This exec runs after {@link org.apache.http.impl.execchain.RedirectExec} which loops 90 | * until there is no redirect or reaches max redirect count. 91 | * {@link RedirectStrategy} is used to decide whether localSpan should be finished or not. 92 | * If there is a redirect localSpan is not finished and redirect is logged. 93 | */ 94 | Integer redirectCount = clientContext.getAttribute(REDIRECT_COUNT, Integer.class); 95 | if (!redirectHandlingDisabled && 96 | clientContext.getRequestConfig().isRedirectsEnabled() && 97 | redirectStrategy.isRedirected(request, response, clientContext) && 98 | ++redirectCount < clientContext.getRequestConfig().getMaxRedirects()) { 99 | 100 | clientContext.setAttribute(REDIRECT_COUNT, redirectCount); 101 | } else { 102 | localSpan.finish(); 103 | } 104 | } 105 | } 106 | } 107 | 108 | protected Span handleLocalSpan(HttpRequest httpRequest, HttpClientContext clientContext) { 109 | Tracer.SpanBuilder spanBuilder = tracer.buildSpan(httpRequest.getRequestLine().getMethod()) 110 | .withTag(Tags.COMPONENT.getKey(), COMPONENT_NAME); 111 | 112 | if (clientContext.getAttribute(PARENT_CONTEXT, SpanContext.class) != null) { 113 | spanBuilder.ignoreActiveSpan() 114 | .asChildOf(clientContext.getAttribute(PARENT_CONTEXT, SpanContext.class)); 115 | } 116 | 117 | Span previousLocalSpan = clientContext.getAttribute(SPAN_PROP, Span.class); 118 | spanBuilder.addReference( 119 | References.FOLLOWS_FROM, 120 | previousLocalSpan == null ? null : previousLocalSpan.context() 121 | ); 122 | 123 | Span localSpan = spanBuilder.start(); 124 | clientContext.setAttribute(SPAN_PROP, localSpan); 125 | clientContext.setAttribute(REDIRECT_COUNT, 0); 126 | return localSpan; 127 | } 128 | 129 | protected CloseableHttpResponse handleNetworkProcessing( 130 | Span parentSpan, 131 | HttpRoute route, 132 | HttpRequestWrapper request, 133 | HttpClientContext clientContext, 134 | HttpExecutionAware execAware) throws IOException, HttpException { 135 | 136 | Span redirectSpan = tracer.buildSpan(request.getMethod()) 137 | .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT) 138 | .asChildOf(parentSpan) 139 | .start(); 140 | if(!injectDisabled) { 141 | tracer.inject(redirectSpan.context(), Format.Builtin.HTTP_HEADERS, new HttpHeadersInjectAdapter(request)); 142 | } 143 | 144 | try (Scope redirectScope = tracer.activateSpan(redirectSpan)){ 145 | for (ApacheClientSpanDecorator decorator : spanDecorators) { 146 | decorator.onRequest(request, clientContext, redirectSpan); 147 | } 148 | CloseableHttpResponse response = requestExecutor.execute(route, request, clientContext, execAware); 149 | for (ApacheClientSpanDecorator decorator : spanDecorators) { 150 | decorator.onResponse(response, clientContext, redirectSpan); 151 | } 152 | return response; 153 | } catch (IOException | HttpException | RuntimeException e) { 154 | for (ApacheClientSpanDecorator decorator: spanDecorators) { 155 | decorator.onError(request, clientContext, e, redirectSpan); 156 | } 157 | throw e; 158 | } finally { 159 | redirectSpan.finish(); 160 | } 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. 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, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # traverses directory structure from process work directory to filesystem root 188 | # first directory with .mvn subdirectory is considered project base directory 189 | find_maven_basedir() { 190 | local basedir=$(pwd) 191 | local wdir=$(pwd) 192 | while [ "$wdir" != '/' ] ; do 193 | if [ -d "$wdir"/.mvn ] ; then 194 | basedir=$wdir 195 | break 196 | fi 197 | wdir=$(cd "$wdir/.."; pwd) 198 | done 199 | echo "${basedir}" 200 | } 201 | 202 | # concatenates all lines of a file 203 | concat_lines() { 204 | if [ -f "$1" ]; then 205 | echo "$(tr -s '\n' ' ' < "$1")" 206 | fi 207 | } 208 | 209 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 210 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 211 | 212 | # For Cygwin, switch paths to Windows format before running java 213 | if $cygwin; then 214 | [ -n "$M2_HOME" ] && 215 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 216 | [ -n "$JAVA_HOME" ] && 217 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 218 | [ -n "$CLASSPATH" ] && 219 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 220 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 221 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 222 | fi 223 | 224 | # Provide a "standardized" way to retrieve the CLI args that will 225 | # work with both Windows and non-Windows executions. 226 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 227 | export MAVEN_CMD_LINE_ARGS 228 | 229 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 230 | 231 | # avoid using MAVEN_CMD_LINE_ARGS below since that would loose parameter escaping in $@ 232 | exec "$JAVACMD" \ 233 | $MAVEN_OPTS \ 234 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 235 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 236 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /opentracing-apache-httpclient/src/test/java/io/opentracing/contrib/apache/http/client/TracingHttpClientBuilderTest.java: -------------------------------------------------------------------------------- 1 | package io.opentracing.contrib.apache.http.client; 2 | 3 | 4 | import java.io.IOException; 5 | import java.net.URISyntaxException; 6 | import java.net.UnknownHostException; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.LinkedHashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.concurrent.ExecutionException; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | import org.apache.http.*; 20 | import org.apache.http.client.HttpClient; 21 | import org.apache.http.client.config.RequestConfig; 22 | import org.apache.http.client.methods.HttpGet; 23 | import org.apache.http.impl.client.CloseableHttpClient; 24 | import org.apache.http.impl.client.DefaultRedirectStrategy; 25 | import org.apache.http.localserver.LocalServerTestBase; 26 | import org.apache.http.message.BasicHttpRequest; 27 | import org.apache.http.protocol.BasicHttpContext; 28 | import org.apache.http.protocol.HttpContext; 29 | import org.apache.http.protocol.HttpRequestExecutor; 30 | import org.apache.http.protocol.HttpRequestHandler; 31 | import org.junit.After; 32 | import org.junit.Assert; 33 | import org.junit.Before; 34 | import org.junit.Test; 35 | 36 | import io.opentracing.Scope; 37 | import io.opentracing.Span; 38 | import io.opentracing.mock.MockSpan; 39 | import io.opentracing.mock.MockTracer; 40 | import io.opentracing.tag.Tags; 41 | import io.opentracing.util.ThreadLocalScopeManager; 42 | 43 | /** 44 | * @author Pavol Loffay 45 | */ 46 | public class TracingHttpClientBuilderTest extends LocalServerTestBase { 47 | 48 | private static MockTracer mockTracer = new MockTracer(new ThreadLocalScopeManager(), MockTracer.Propagator.TEXT_MAP); 49 | 50 | private HttpHost serverHost; 51 | 52 | @Before 53 | public void setUp() throws Exception { 54 | super.setUp(); 55 | this.clientBuilder = new TracingHttpClientBuilder(DefaultRedirectStrategy.INSTANCE, false, mockTracer, 56 | Collections.singletonList(new ApacheClientSpanDecorator.StandardTags())); 57 | 58 | this.serverBootstrap.registerHandler(RedirectHandler.MAPPING, new RedirectHandler()) 59 | .registerHandler(PropagationHandler.MAPPING, new PropagationHandler()); 60 | this.serverHost = super.start(); 61 | } 62 | 63 | @After 64 | public void shutDown() throws Exception { 65 | if(this.httpclient != null) { 66 | this.httpclient.close(); 67 | } 68 | if(this.server != null) { 69 | this.server.shutdown(1L, TimeUnit.SECONDS); 70 | } 71 | mockTracer.reset(); 72 | } 73 | 74 | @Test 75 | public void testStandardTags() throws IOException { 76 | { 77 | CloseableHttpClient client = clientBuilder.build(); 78 | client.execute(new HttpGet(serverUrl("/echo/a"))); 79 | } 80 | 81 | List mockSpans = mockTracer.finishedSpans(); 82 | Assert.assertEquals(2, mockSpans.size()); 83 | 84 | MockSpan mockSpan = mockSpans.get(0); 85 | Assert.assertEquals("GET", mockSpan.operationName()); 86 | 87 | Assert.assertEquals(6, mockSpan.tags().size()); 88 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, mockSpan.tags().get(Tags.SPAN_KIND.getKey())); 89 | Assert.assertEquals("GET", mockSpan.tags().get(Tags.HTTP_METHOD.getKey())); 90 | Assert.assertEquals(serverUrl("/echo/a"), mockSpan.tags().get(Tags.HTTP_URL.getKey())); 91 | Assert.assertEquals(200, mockSpan.tags().get(Tags.HTTP_STATUS.getKey())); 92 | Assert.assertEquals(serverHost.getPort(), mockSpan.tags().get(Tags.PEER_PORT.getKey())); 93 | Assert.assertEquals(serverHost.getHostName(), mockSpan.tags().get(Tags.PEER_HOSTNAME.getKey())); 94 | Assert.assertEquals(0, mockSpan.logEntries().size()); 95 | 96 | assertLocalSpan(mockSpans.get(1)); 97 | } 98 | 99 | @Test 100 | public void testBasicHttpRequest() throws IOException { 101 | { 102 | CloseableHttpClient client = clientBuilder.build(); 103 | client.execute(serverHost, new BasicHttpRequest("GET", "/echo/a", HttpVersion.HTTP_1_1)); 104 | } 105 | 106 | List mockSpans = mockTracer.finishedSpans(); 107 | Assert.assertEquals(2, mockSpans.size()); 108 | 109 | MockSpan mockSpan = mockSpans.get(0); 110 | Assert.assertEquals("GET", mockSpan.operationName()); 111 | 112 | Assert.assertEquals(6, mockSpan.tags().size()); 113 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, mockSpan.tags().get(Tags.SPAN_KIND.getKey())); 114 | Assert.assertEquals("GET", mockSpan.tags().get(Tags.HTTP_METHOD.getKey())); 115 | Assert.assertEquals(serverUrl("/echo/a"), mockSpan.tags().get(Tags.HTTP_URL.getKey())); 116 | Assert.assertEquals(200, mockSpan.tags().get(Tags.HTTP_STATUS.getKey())); 117 | Assert.assertEquals(serverHost.getPort(), mockSpan.tags().get(Tags.PEER_PORT.getKey())); 118 | Assert.assertEquals(serverHost.getHostName(), mockSpan.tags().get(Tags.PEER_HOSTNAME.getKey())); 119 | Assert.assertEquals(0, mockSpan.logEntries().size()); 120 | 121 | assertLocalSpan(mockSpans.get(1)); 122 | } 123 | 124 | @Test 125 | public void testRedirect() throws URISyntaxException, IOException { 126 | { 127 | HttpClient client = clientBuilder.build(); 128 | client.execute(new HttpGet(serverUrl(RedirectHandler.MAPPING))); 129 | } 130 | 131 | List mockSpans = mockTracer.finishedSpans(); 132 | Assert.assertEquals(3, mockSpans.size()); 133 | 134 | MockSpan mockSpan = mockSpans.get(0); 135 | Assert.assertEquals("GET", mockSpan.operationName()); 136 | Assert.assertEquals(6, mockSpan.tags().size()); 137 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, mockSpan.tags().get(Tags.SPAN_KIND.getKey())); 138 | Assert.assertEquals("GET", mockSpan.tags().get(Tags.HTTP_METHOD.getKey())); 139 | Assert.assertEquals(serverUrl("/redirect"), mockSpan.tags().get(Tags.HTTP_URL.getKey())); 140 | Assert.assertEquals(301, mockSpan.tags().get(Tags.HTTP_STATUS.getKey())); 141 | Assert.assertEquals(serverHost.getPort(), mockSpan.tags().get(Tags.PEER_PORT.getKey())); 142 | Assert.assertEquals(serverHost.getHostName(), mockSpan.tags().get(Tags.PEER_HOSTNAME.getKey())); 143 | 144 | mockSpan = mockSpans.get(1); 145 | Assert.assertEquals("GET", mockSpan.operationName()); 146 | Assert.assertEquals(6, mockSpan.tags().size()); 147 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, mockSpan.tags().get(Tags.SPAN_KIND.getKey())); 148 | Assert.assertEquals("GET", mockSpan.tags().get(Tags.HTTP_METHOD.getKey())); 149 | Assert.assertEquals(serverUrl("/propagation"), mockSpan.tags().get(Tags.HTTP_URL.getKey())); 150 | Assert.assertEquals(200, mockSpan.tags().get(Tags.HTTP_STATUS.getKey())); 151 | Assert.assertEquals(serverHost.getPort(), mockSpan.tags().get(Tags.PEER_PORT.getKey())); 152 | Assert.assertEquals(serverHost.getHostName(), mockSpan.tags().get(Tags.PEER_HOSTNAME.getKey())); 153 | 154 | assertLocalSpan(mockSpans.get(2)); 155 | } 156 | 157 | @Test 158 | public void testDisableRedirectHandling() throws URISyntaxException, IOException { 159 | { 160 | HttpClient client = new TracingHttpClientBuilder(DefaultRedirectStrategy.INSTANCE, true, mockTracer, 161 | Collections.singletonList(new ApacheClientSpanDecorator.StandardTags())) 162 | .build(); 163 | 164 | client.execute(new HttpGet(serverUrl(RedirectHandler.MAPPING))); 165 | } 166 | 167 | List mockSpans = mockTracer.finishedSpans(); 168 | Assert.assertEquals(2, mockSpans.size()); 169 | 170 | MockSpan mockSpan = mockSpans.get(0); 171 | Assert.assertEquals("GET", mockSpan.operationName()); 172 | 173 | Assert.assertEquals(6, mockSpan.tags().size()); 174 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, mockSpan.tags().get(Tags.SPAN_KIND.getKey())); 175 | Assert.assertEquals("GET", mockSpan.tags().get(Tags.HTTP_METHOD.getKey())); 176 | Assert.assertEquals(serverUrl("/redirect"), mockSpan.tags().get(Tags.HTTP_URL.getKey())); 177 | Assert.assertEquals(301, mockSpan.tags().get(Tags.HTTP_STATUS.getKey())); 178 | Assert.assertEquals(serverHost.getPort(), mockSpan.tags().get(Tags.PEER_PORT.getKey())); 179 | Assert.assertEquals(serverHost.getHostName(), mockSpan.tags().get(Tags.PEER_HOSTNAME.getKey())); 180 | Assert.assertEquals(0, mockSpan.logEntries().size()); 181 | 182 | assertLocalSpan(mockSpans.get(1)); 183 | } 184 | 185 | @Test 186 | public void testRequestConfigDisabledRedirects() throws URISyntaxException, IOException { 187 | { 188 | HttpClient client = clientBuilder 189 | .setDefaultRequestConfig(RequestConfig.custom() 190 | .setRedirectsEnabled(false) 191 | .build()) 192 | .build(); 193 | client.execute(new HttpGet(serverUrl(RedirectHandler.MAPPING))); 194 | } 195 | 196 | List mockSpans = mockTracer.finishedSpans(); 197 | Assert.assertEquals(2, mockSpans.size()); 198 | 199 | MockSpan mockSpan = mockSpans.get(0); 200 | Assert.assertEquals("GET", mockSpan.operationName()); 201 | 202 | Assert.assertEquals(6, mockSpan.tags().size()); 203 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, mockSpan.tags().get(Tags.SPAN_KIND.getKey())); 204 | Assert.assertEquals("GET", mockSpan.tags().get(Tags.HTTP_METHOD.getKey())); 205 | Assert.assertEquals(serverUrl("/redirect"), mockSpan.tags().get(Tags.HTTP_URL.getKey())); 206 | Assert.assertEquals(301, mockSpan.tags().get(Tags.HTTP_STATUS.getKey())); 207 | Assert.assertEquals(serverHost.getPort(), mockSpan.tags().get(Tags.PEER_PORT.getKey())); 208 | Assert.assertEquals(serverHost.getHostName(), mockSpan.tags().get(Tags.PEER_HOSTNAME.getKey())); 209 | Assert.assertEquals(0, mockSpan.logEntries().size()); 210 | 211 | assertLocalSpan(mockSpans.get(1)); 212 | } 213 | 214 | @Test 215 | public void testActiveParentSpan() throws IOException { 216 | { 217 | final MockSpan parent = mockTracer.buildSpan("parent").start(); 218 | try (Scope ignored = mockTracer.activateSpan(parent)) { 219 | CloseableHttpClient client = clientBuilder.build(); 220 | client.execute(new HttpGet(serverUrl("/echo/a"))); 221 | } finally { 222 | parent.finish(); 223 | } 224 | } 225 | 226 | List mockSpans = mockTracer.finishedSpans(); 227 | Assert.assertEquals(3, mockSpans.size()); 228 | 229 | Assert.assertEquals(mockSpans.get(0).context().traceId(), mockSpans.get(1).context().traceId()); 230 | Assert.assertEquals(mockSpans.get(0).parentId(), mockSpans.get(1).context().spanId()); 231 | 232 | assertLocalSpan(mockSpans.get(1)); 233 | } 234 | 235 | @Test 236 | public void testManualParentSpan() throws IOException { 237 | MockSpan parent = mockTracer.buildSpan("parent") 238 | .start(); 239 | 240 | { 241 | Span parentSpan = mockTracer.buildSpan("parent") 242 | .start(); 243 | mockTracer.activateSpan(parentSpan); 244 | HttpContext context = new BasicHttpContext(); 245 | context.setAttribute(Constants.PARENT_CONTEXT, parent.context()); 246 | 247 | CloseableHttpClient client = clientBuilder.build(); 248 | client.execute(new HttpGet(serverUrl("/echo/a")), context); 249 | } 250 | 251 | List mockSpans = mockTracer.finishedSpans(); 252 | Assert.assertEquals(2, mockSpans.size()); 253 | 254 | Assert.assertEquals(parent.context().traceId(), mockSpans.get(1).context().traceId()); 255 | Assert.assertEquals(parent.context().spanId(), mockSpans.get(1).parentId()); 256 | 257 | assertLocalSpan(mockSpans.get(1)); 258 | } 259 | 260 | @Test 261 | public void testPropagationAfterRedirect() throws IOException { 262 | { 263 | HttpClient client = clientBuilder.build(); 264 | client.execute(new HttpGet(serverUrl(RedirectHandler.MAPPING))); 265 | } 266 | 267 | List mockSpans = mockTracer.finishedSpans(); 268 | Assert.assertEquals(3, mockSpans.size()); 269 | 270 | // the last one is for redirect 271 | MockSpan mockSpan = mockSpans.get(1); 272 | Assert.assertEquals(PropagationHandler.lastRequest.getFirstHeader("traceId").getValue(), 273 | String.valueOf(mockSpan.context().traceId())); 274 | Assert.assertEquals(PropagationHandler.lastRequest.getFirstHeader("spanId").getValue(), 275 | String.valueOf(mockSpan.context().spanId())); 276 | 277 | assertLocalSpan(mockSpans.get(2)); 278 | } 279 | 280 | @Test 281 | public void testDisablePropagation() throws IOException { 282 | { 283 | HttpClient client = ((TracingHttpClientBuilder)clientBuilder).disableInjection().build(); 284 | client.execute(new HttpGet(serverUrl(RedirectHandler.MAPPING))); 285 | } 286 | 287 | List mockSpans = mockTracer.finishedSpans(); 288 | Assert.assertEquals(3, mockSpans.size()); 289 | 290 | // the last one is for redirect 291 | MockSpan mockSpan = mockSpans.get(1); 292 | Assert.assertEquals(PropagationHandler.lastRequest.getFirstHeader("traceId"), null); 293 | Assert.assertEquals(PropagationHandler.lastRequest.getFirstHeader("spanId"), null); 294 | 295 | assertLocalSpan(mockSpans.get(2)); 296 | } 297 | 298 | @Test 299 | public void testUnknownHostException() throws IOException { 300 | CloseableHttpClient client = clientBuilder.build(); 301 | 302 | try { 303 | client.execute(new HttpGet("http://notexisting.example.com")); 304 | } catch (UnknownHostException ex) { 305 | } 306 | 307 | List mockSpans = mockTracer.finishedSpans(); 308 | Assert.assertEquals(2, mockSpans.size()); 309 | 310 | MockSpan mockSpan = mockSpans.get(0); 311 | Assert.assertEquals(Boolean.TRUE, mockSpan.tags().get(Tags.ERROR.getKey())); 312 | 313 | // logs 314 | Assert.assertEquals(1, mockSpan.logEntries().size()); 315 | Assert.assertEquals(2, mockSpan.logEntries().get(0).fields().size()); 316 | Assert.assertEquals(Tags.ERROR.getKey(), mockSpan.logEntries().get(0).fields().get("event")); 317 | Assert.assertNotNull(mockSpan.logEntries().get(0).fields().get("error.object")); 318 | } 319 | 320 | @Test 321 | public void testMultipleSimultaneousRequests() throws ExecutionException, InterruptedException { 322 | int numberOfCalls = 100; 323 | 324 | Map parentSpans = new LinkedHashMap<>(numberOfCalls); 325 | 326 | ExecutorService executorService = Executors.newFixedThreadPool(20); 327 | List> futures = new ArrayList<>(numberOfCalls); 328 | for (int i = 0; i < numberOfCalls; i++) { 329 | 330 | final String requestUrl = serverUrl("/echo/a"); 331 | 332 | final MockSpan parentSpan = mockTracer.buildSpan("foo") 333 | .ignoreActiveSpan().start(); 334 | parentSpan.setTag("request-url", requestUrl); 335 | parentSpans.put(parentSpan.context().spanId(), parentSpan); 336 | 337 | futures.add(executorService.submit(new Runnable() { 338 | @Override 339 | public void run() { 340 | mockTracer.activateSpan(parentSpan); 341 | try { 342 | httpclient.execute(new HttpGet(requestUrl)); 343 | } catch (IOException e) { 344 | e.printStackTrace(); 345 | Assert.fail(); 346 | } 347 | } 348 | })); 349 | } 350 | 351 | // wait to finish all calls 352 | for (Future future: futures) { 353 | future.get(); 354 | } 355 | 356 | executorService.awaitTermination(1, TimeUnit.SECONDS); 357 | executorService.shutdown(); 358 | 359 | List mockSpans = mockTracer.finishedSpans(); 360 | assertOnErrors(mockSpans); 361 | Assert.assertEquals(numberOfCalls * 2, mockSpans.size()); //local + network span 362 | 363 | // parentId - span 364 | Map map = new HashMap<>(mockSpans.size()); 365 | for (MockSpan mockSpan: mockSpans) { 366 | map.put(mockSpan.context().spanId(), mockSpan); 367 | } 368 | 369 | for (MockSpan networkSpan: mockSpans) { 370 | if (networkSpan.tags().containsKey(Tags.COMPONENT.getKey())) { 371 | continue; 372 | } 373 | 374 | MockSpan localSpan = map.get(networkSpan.parentId()); 375 | MockSpan parentBeforeClientSpan = parentSpans.get(localSpan.parentId()); 376 | 377 | Assert.assertEquals(parentBeforeClientSpan.tags().get("request-url"), networkSpan.tags().get(Tags.HTTP_URL.getKey())); 378 | 379 | Assert.assertEquals(parentBeforeClientSpan.context().traceId(), localSpan.context().traceId()); 380 | Assert.assertEquals(parentBeforeClientSpan.context().traceId(), networkSpan.context().traceId()); 381 | Assert.assertEquals(parentBeforeClientSpan.context().spanId(), localSpan.parentId()); 382 | Assert.assertEquals(0, networkSpan.generatedErrors().size()); 383 | Assert.assertEquals(0, parentBeforeClientSpan.generatedErrors().size()); 384 | } 385 | } 386 | 387 | @Test 388 | public void testDefaultRetryStrategy() throws IOException { 389 | { 390 | CloseableHttpClient client = clientBuilder.setRequestExecutor(new HttpRequestExecutor() { 391 | private boolean passThrough = false; 392 | @Override 393 | public HttpResponse execute(HttpRequest request, HttpClientConnection conn, HttpContext context) throws IOException, HttpException { 394 | if (!passThrough) { 395 | passThrough = true; 396 | throw new IOException("close span and retry"); 397 | } 398 | return super.execute(request, conn, context); 399 | } 400 | }).build(); 401 | client.execute(serverHost, new BasicHttpRequest("GET", "/echo/a", HttpVersion.HTTP_1_1)); 402 | } 403 | 404 | List mockSpans = mockTracer.finishedSpans(); 405 | Assert.assertEquals(4, mockSpans.size()); 406 | 407 | MockSpan failedRequestSpan = mockSpans.get(0); 408 | MockSpan succeededRequestSpan = mockSpans.get(2); 409 | for (MockSpan span : new MockSpan[] {failedRequestSpan, succeededRequestSpan}) { 410 | Assert.assertEquals("GET", succeededRequestSpan.operationName()); 411 | Assert.assertEquals(6, span.tags().size()); 412 | Assert.assertEquals(Tags.SPAN_KIND_CLIENT, span.tags().get(Tags.SPAN_KIND.getKey())); 413 | Assert.assertEquals("GET", span.tags().get(Tags.HTTP_METHOD.getKey())); 414 | Assert.assertEquals(serverUrl("/echo/a"), span.tags().get(Tags.HTTP_URL.getKey())); 415 | Assert.assertEquals(serverHost.getPort(), span.tags().get(Tags.PEER_PORT.getKey())); 416 | Assert.assertEquals(serverHost.getHostName(), span.tags().get(Tags.PEER_HOSTNAME.getKey())); 417 | } 418 | 419 | Assert.assertEquals(null, failedRequestSpan.tags().get(Tags.HTTP_STATUS.getKey())); 420 | Assert.assertEquals(1, failedRequestSpan.logEntries().size()); 421 | Assert.assertEquals(2, failedRequestSpan.logEntries().get(0).fields().size()); 422 | Assert.assertEquals(Tags.ERROR.getKey(), failedRequestSpan.logEntries().get(0).fields().get("event")); 423 | Assert.assertNotNull(failedRequestSpan.logEntries().get(0).fields().get("error.object")); 424 | 425 | Assert.assertEquals(200, succeededRequestSpan.tags().get(Tags.HTTP_STATUS.getKey())); 426 | Assert.assertEquals(0, succeededRequestSpan.logEntries().size()); 427 | 428 | assertLocalSpan(mockSpans.get(1)); 429 | assertLocalSpan(mockSpans.get(3)); 430 | } 431 | 432 | public void assertLocalSpan(MockSpan mockSpan) { 433 | Assert.assertEquals(1, mockSpan.tags().size()); 434 | Assert.assertEquals(TracingClientExec.COMPONENT_NAME, mockSpan.tags().get(Tags.COMPONENT.getKey())); 435 | } 436 | 437 | protected String serverUrl(String path) { 438 | return serverHost.toString() + path; 439 | } 440 | 441 | public static void assertOnErrors(List spans) { 442 | for (MockSpan mockSpan: spans) { 443 | Assert.assertEquals(mockSpan.generatedErrors().toString(), 0, mockSpan.generatedErrors().size()); 444 | } 445 | } 446 | 447 | public static class RedirectHandler implements HttpRequestHandler { 448 | 449 | public static final String MAPPING = "/redirect"; 450 | 451 | @Override 452 | public void handle(HttpRequest request, HttpResponse response, HttpContext context) 453 | throws HttpException, IOException { 454 | 455 | response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY); 456 | response.addHeader("Location", PropagationHandler.MAPPING); 457 | } 458 | } 459 | 460 | public static class PropagationHandler implements HttpRequestHandler { 461 | public static final String MAPPING = "/propagation"; 462 | public static HttpRequest lastRequest; 463 | 464 | @Override 465 | public void handle(HttpRequest request, HttpResponse response, HttpContext context) 466 | throws HttpException, IOException { 467 | // TODO this is ugly... 468 | lastRequest = request; 469 | response.setStatusCode(HttpStatus.SC_OK); 470 | } 471 | } 472 | } 473 | --------------------------------------------------------------------------------