├── .gitignore
├── .mvn
└── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── .settings.xml
├── .travis.yml
├── LICENSE
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
├── main
│ └── java
│ │ └── io
│ │ └── opentracing
│ │ └── contrib
│ │ ├── aws
│ │ └── xray
│ │ │ ├── AWSXRayMetadataNamespaces.java
│ │ │ ├── AWSXRayScope.java
│ │ │ ├── AWSXRayScopeManager.java
│ │ │ ├── AWSXRaySpan.java
│ │ │ ├── AWSXRaySpanContext.java
│ │ │ ├── AWSXRayTags.java
│ │ │ ├── AWSXRayTracer.java
│ │ │ └── AWSXRayUtils.java
│ │ └── tag
│ │ └── ExtraTags.java
└── test
│ ├── java
│ └── io
│ │ └── opentracing
│ │ ├── contrib
│ │ └── aws
│ │ │ └── xray
│ │ │ ├── AWSXRaySpanBuilderTests.java
│ │ │ ├── AWSXRaySpanTests.java
│ │ │ ├── AWSXRayTestParent.java
│ │ │ └── AWSXRayTracerTests.java
│ │ └── propagation
│ │ └── TextMapAdapter.java
│ └── resources
│ └── logback.xml
└── travis
└── publish.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Java / maven build artifacts
2 | *.class
3 | *.log
4 | target/
5 |
6 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
7 | hs_err_pid*
8 |
9 | # IDE artifacts
10 | .idea/
11 | *.iml
12 |
--------------------------------------------------------------------------------
/.mvn/wrapper/MavenWrapperDownloader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2007-present the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import java.net.*;
17 | import java.io.*;
18 | import java.nio.channels.*;
19 | import java.util.Properties;
20 |
21 | public class MavenWrapperDownloader {
22 |
23 | private static final String WRAPPER_VERSION = "0.5.5";
24 | /**
25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
26 | */
27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
29 |
30 | /**
31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
32 | * use instead of the default one.
33 | */
34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
35 | ".mvn/wrapper/maven-wrapper.properties";
36 |
37 | /**
38 | * Path where the maven-wrapper.jar will be saved to.
39 | */
40 | private static final String MAVEN_WRAPPER_JAR_PATH =
41 | ".mvn/wrapper/maven-wrapper.jar";
42 |
43 | /**
44 | * Name of the property which should be used to override the default download url for the wrapper.
45 | */
46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
47 |
48 | public static void main(String args[]) {
49 | System.out.println("- Downloader started");
50 | File baseDirectory = new File(args[0]);
51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
52 |
53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom
54 | // wrapperUrl parameter.
55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
56 | String url = DEFAULT_DOWNLOAD_URL;
57 | if(mavenWrapperPropertyFile.exists()) {
58 | FileInputStream mavenWrapperPropertyFileInputStream = null;
59 | try {
60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
61 | Properties mavenWrapperProperties = new Properties();
62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
64 | } catch (IOException e) {
65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
66 | } finally {
67 | try {
68 | if(mavenWrapperPropertyFileInputStream != null) {
69 | mavenWrapperPropertyFileInputStream.close();
70 | }
71 | } catch (IOException e) {
72 | // Ignore ...
73 | }
74 | }
75 | }
76 | System.out.println("- Downloading from: " + url);
77 |
78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
79 | if(!outputFile.getParentFile().exists()) {
80 | if(!outputFile.getParentFile().mkdirs()) {
81 | System.out.println(
82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
83 | }
84 | }
85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
86 | try {
87 | downloadFileFromURL(url, outputFile);
88 | System.out.println("Done");
89 | System.exit(0);
90 | } catch (Throwable e) {
91 | System.out.println("- Error downloading");
92 | e.printStackTrace();
93 | System.exit(1);
94 | }
95 | }
96 |
97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception {
98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
99 | String username = System.getenv("MVNW_USERNAME");
100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
101 | Authenticator.setDefault(new Authenticator() {
102 | @Override
103 | protected PasswordAuthentication getPasswordAuthentication() {
104 | return new PasswordAuthentication(username, password);
105 | }
106 | });
107 | }
108 | URL website = new URL(urlString);
109 | ReadableByteChannel rbc;
110 | rbc = Channels.newChannel(website.openStream());
111 | FileOutputStream fos = new FileOutputStream(destination);
112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
113 | fos.close();
114 | rbc.close();
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/opentracing-contrib/java-xray-tracer/c8cc506679be76604b91fa369bc6f87ce5942f87/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar
3 |
--------------------------------------------------------------------------------
/.settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
21 |
22 |
23 | sonatype
24 | ${env.SONATYPE_USER}
25 | ${env.SONATYPE_PASSWORD}
26 |
27 |
28 | bintray
29 | ${env.BINTRAY_USER}
30 | ${env.BINTRAY_KEY}
31 |
32 |
33 | jfrog-snapshots
34 | ${env.BINTRAY_USER}
35 | ${env.BINTRAY_KEY}
36 |
37 |
38 | github.com
39 | ${env.GH_USER}
40 | ${env.GH_TOKEN}
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | dist: trusty
3 |
4 | language: java
5 | jdk:
6 | - openjdk8
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 | # setup https authentication credentials, used by ./mvnw release:prepare
17 | - git config credential.helper "store --file=.git/credentials"
18 | - echo "https://$GH_TOKEN:@github.com" > .git/credentials
19 |
20 | script:
21 | - ./travis/publish.sh
22 |
23 | install:
24 | - ./mvnw install -DskipTests=true -Dmaven.javadoc.skip=true -B -V
25 |
26 | branches:
27 | only:
28 | - master
29 |
30 | after_success:
31 | - mvn jacoco:report coveralls:report
32 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/opentracing-contrib/java-xray-tracer) [](https://coveralls.io/github/opentracing-contrib/java-xray-tracer?branch=master) [](https://opensource.org/licenses/Apache-2.0)
2 |
3 | # opentracing-java-aws-xray
4 | Java OpenTracing implementation backed by AWS X-Ray.
5 |
6 | **WARNING: this code is currently in beta: please test thoroughly before deploying to production, and report any issues.**
7 |
8 | ## Overview
9 |
10 | The [OpenTracing specification](https://opentracing.io) is a vendor-neutral API for instrumentation and distributed
11 | tracing. This library provides an implementation which is backed by the [AWS X-Ray Java SDK](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html),
12 | and for the most part just provides a thin wrapper around the underlying X-Ray classes.
13 |
14 | ## Using this library
15 |
16 | The code is not yet being deployed to Maven central (we're working on that!), so you'll have to build your own version. This should be as simple as:
17 |
18 | - cloning this repository locally
19 | - running `mvn package` [1]
20 | - including the resulting JAR file in your `/lib` folder (or similar)
21 |
22 | [1] If you don't already have Maven installed, you can use the `mvnw` command (or `mvnw.cmd` for Windows) instead which uses the [Maven wrapper plugin](https://github.com/takari/maven-wrapper) to download and start the correct version of Maven.
23 |
24 | ## AWS compatibility
25 |
26 | #### Integrating with AWS systems
27 |
28 | Since this library mostly just wraps the standard X-Ray classes, it *should* work seamlessly in code which makes use of
29 | multiple AWS services: the standard AWS SDK will add the necessary trace headers automatically, and recover them on
30 | remote servers (e.g. when invoking lambda functions). However, this hasn't yet been extensively tested, so feedback
31 | and bug reports are very welcome!
32 |
33 |
34 | #### Naming conventions
35 |
36 | The OpenTracing standard and the AWS X-Ray system each use different naming conventions for some of the same concepts
37 | (e.g. HTTP response codes). Since the goal of this project is to largely hide the fact that we're using X-Ray under the
38 | hood, and to only expose the OpenTracing API:
39 |
40 | - client code should prefer using the [OpenTracing naming conventions](https://opentracing.io/specification/conventions/)
41 | for tag names (however, if you supply the X-Ray-specific names, values will still end up in the right place)
42 | - this library will silently convert *some* known tag names values to their X-Ray equivalents
43 | - X-Ray traces further subdivide tagged values into separate sub-objects for e.g. HTTP request and response data:
44 | - where possible, ensure values end up in the correct place on the trace
45 | - all other values are stored in the `metadata` section, under a `default` namespace if not specified
46 | - see the [X-Ray Segment Documents](https://docs.aws.amazon.com/xray/latest/devguide/xray-api-segmentdocuments.html)
47 | for further details
48 |
49 | The following OpenTracing names will be translated to fit the X-Ray names:
50 |
51 | | OpenTracing tag name | X-Ray trace name |
52 | |-----------------------|---------------------------------|
53 | | `version` | `service.version` |
54 | | `db.instance` | `sql.url` |
55 | | `db.statement` | `sql.sanitized_query` |
56 | | `db.type` | `sql.database_type` |
57 | | `db.user` | `sql.user` |
58 | | `db.driver` | `sql.driver` |
59 | | `db.version` | `sql.version` |
60 | | `http.method` | `http.request.method` |
61 | | `http.url` | `http.request.url` |
62 | | `http.client_ip` | `http.request.client_ip` |
63 | | `http.user_agent` | `http.request.user_agent` |
64 | | `http.status_code` | `http.response.status` |
65 | | `http.content_length` | `http.response.content_length` |
66 | | `foo` | `metadata.default.foo` |
67 | | `widget.foo` | `metadata.widget.foo` |
68 |
69 | Additionally, the following special tag names are defined in `AWSXRayTags` and can be used to directly modify the
70 | behaviour of the underlying X-Ray trace `Entity` (NB some of these only work for `Segment`, i.e. top-level spans):
71 |
72 | | | Behaviour | `Segment` | `Subsegment` |
73 | |-----------------------|---------------------------------|-----------|--------------|
74 | | `error` | sets the `isError()` flag | Y | Y |
75 | | `fault` | sets the `isFault()` flag | Y | Y |
76 | | `throttle` | sets the `isThrottle()` flag | Y | Y |
77 | | `isSampled` | sets the `isSampled()` flag | Y | - |
78 | | `user` | sets the `user` value | Y | - |
79 | | `origin` | sets the `origin` value | Y | - |
80 | | `parentId` | sets the `parentId` value | Y | Y |
81 |
82 | #### Context injection / extraction
83 |
84 | This library supports basic [injection and extraction of SpanContext](https://opentracing.io/specification/#inject-a-spancontext-into-a-carrier):
85 |
86 | - in most cases it is expected that this library will be used in AWS-hosted systems, and calls between AWS services
87 | using the official SDK will already handle passing trace IDs across so no further work is required
88 |
89 | - for non-AWS systems, the current `SpanContext` can be converted into e.g. a set of HTTP
90 | headers using `Tracer.inject` and sent over the wire; on the other side, `Tracer.extract` can be used to convert
91 | the headers back into a `SpanContext`
92 |
93 | - for compatibility between AWS and non-AWS services, we store the trace context information in a single header `X-Amzn-Trace-Id` - see the
94 | [Amazon trace header documentation](https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader)
95 | for more details
96 |
97 | ## Known limitations
98 |
99 | This library does not currently provide a full implementation of the OpenTracing API: the X-Ray classes themselves
100 | already provide some of the same features, and in other cases the APIs are incompatible. The following limitations
101 | currently apply:
102 |
103 | #### References
104 |
105 | OpenTracing provides for arbitrary [references between spans](https://opentracing.io/specification/#references-between-spans),
106 | including parent-child and follows-from relationships. In practice X-Ray only supports parent-child relationships, and
107 | each span can have at most one parent. Calls to add references of different types, or multiple parent-child relationships,
108 | will generally be ignored.
109 |
110 | #### Logging
111 |
112 | OpenTracing provides methods to add [logs to the trace](https://opentracing.io/specification/#log-structured-data).
113 | These methods will work as expected: structured data are stored in X-Ray metadata under a "log" namespace, but this
114 | approach isn't advised since the resulting JSON format is clunky. A better approach in AWS is to make use of
115 | [CloudWatch](https://aws.amazon.com/cloudwatch/).
116 |
117 | ## License
118 |
119 | This project is licensed under the [Apache 2.0](/LICENSE) License.
120 |
--------------------------------------------------------------------------------
/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 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------
/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 set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
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 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | io.opentracing.contrib
7 | java-xray-tracer
8 | 1.0.0-SNAPSHOT
9 |
10 | https://github.com/opentracing-contrib/java-xray-tracer
11 | OpenTracing implementation for AWS X-Ray
12 |
13 |
14 | http://github.com/opentracing-contrib/java-xray-tracer
15 | scm:git:https://github.com/opentracing-contrib/java-xray-tracer.git
16 | scm:git:https://github.com/opentracing-contrib/java-xray-tracer.git
17 | HEAD
18 |
19 |
20 |
21 | GitHub
22 | https://github.com/opentracing-contrib/java-xray-tracer/issues
23 |
24 |
25 |
26 |
27 | The Apache Software License, Version 2.0
28 | http://www.apache.org/licenses/LICENSE-2.0.txt
29 | repo
30 |
31 |
32 |
33 |
34 |
35 | ashleymercer
36 | Ashley Mercer
37 | https://github.com/ashleymercer
38 |
39 |
40 |
41 |
42 | 1.8
43 | 1.8
44 | UTF-8
45 | UTF-8
46 |
47 |
50 | 2.2.1
51 | 5.4.0
52 | 2.28.2
53 | 0.31.0
54 | 1.7.26
55 |
56 |
57 |
58 |
59 |
60 | com.amazonaws
61 | aws-xray-recorder-sdk-core
62 | ${aws.xray.sdk.version}
63 |
64 |
65 | io.opentracing
66 | opentracing-api
67 | ${opentracing.version}
68 |
69 |
70 | org.slf4j
71 | slf4j-api
72 | ${slf4j.version}
73 |
74 |
75 |
76 |
77 | ch.qos.logback
78 | logback-classic
79 | 1.2.3
80 | test
81 |
82 |
83 | org.mockito
84 | mockito-core
85 | ${mockito.version}
86 |
87 |
88 | org.slf4j
89 | jcl-over-slf4j
90 | ${slf4j.version}
91 |
92 |
93 | io.opentracing
94 | opentracing-util
95 | ${opentracing.version}
96 | test
97 |
98 |
99 | org.junit.jupiter
100 | junit-jupiter-api
101 | ${junit.version}
102 | test
103 |
104 |
105 | org.junit.jupiter
106 | junit-jupiter-engine
107 | ${junit.version}
108 | test
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | maven-compiler-plugin
117 | 3.8.0
118 |
119 | true
120 |
121 | -Xlint:all
122 | -Werror
123 |
124 |
125 |
126 |
127 |
128 |
129 | maven-surefire-plugin
130 | 2.22.0
131 |
132 |
133 | maven-failsafe-plugin
134 | 2.22.0
135 |
136 |
137 |
138 |
139 | org.eluder.coveralls
140 | coveralls-maven-plugin
141 | 4.3.0
142 |
143 |
144 | org.jacoco
145 | jacoco-maven-plugin
146 | 0.8.3
147 |
148 |
149 | prepare-agent
150 |
151 | prepare-agent
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | maven-release-plugin
160 | 2.5.3
161 |
162 | false
163 | release
164 | true
165 | @{project.version}
166 |
167 |
168 |
169 | io.zipkin.centralsync-maven-plugin
170 | centralsync-maven-plugin
171 | 0.1.0
172 |
173 | opentracing
174 | maven
175 | opentracing-xray-tracer
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | bintray
185 | https://api.bintray.com/maven/opentracing/maven/opentracing-xray-tracer/;publish=1
186 |
187 |
188 | jfrog-snapshots
189 | http://oss.jfrog.org/artifactory/oss-snapshot-local
190 |
191 |
192 |
193 |
194 |
195 | release
196 |
197 |
198 |
199 |
200 |
201 | org.apache.maven.plugins
202 | maven-source-plugin
203 | 3.1.0
204 |
205 |
206 | attach-sources
207 |
208 | jar
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 | org.apache.maven.plugins
217 | maven-javadoc-plugin
218 | 3.1.0
219 |
220 | false
221 |
222 |
223 |
224 | attach-javadocs
225 |
226 | jar
227 |
228 | package
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRayMetadataNamespaces.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.entities.Entity;
4 | import io.opentracing.Span;
5 |
6 | /**
7 | * When storing tagged values on X-Ray traces, we try to use the
8 | * standard X-Ray naming conventions where possible; other values
9 | * will end up being stored in the "metadata" part of the trace
10 | * which requires at least a three-part key:
11 | *
12 | *
13 | * - the literal value "metadata"
14 | * - a "namespace" which logically groups values
15 | * - one or more sub-keys within the namespace
16 | *
17 | *
18 | * This class provides some different metadata namespaces which can
19 | * be used externally if required.
20 | *
21 | * When storing values on the {@link Span}, if the tag name does not
22 | * correspond to one of the standard "known" names, and also does not
23 | * conform to the above pattern, then it will be coerced to the above
24 | * by being placed in the {@link #DEFAULT} namespace. Examples:
25 | *
26 | *
27 | *
28 | * Original key |
29 | * X-Ray key |
30 | * Comments |
31 | *
32 | *
33 | * db.user |
34 | * sql.user |
35 | * X-Ray traces use a "sql" element for database info |
36 | *
37 | *
38 | * foo_value |
39 | * metadata.default.foo_value |
40 | * Stored in "metadata" under the "default" namespace |
41 | *
42 | *
43 | * widget.bar_value |
44 | * metadata.widget.bar_value |
45 | * Stored in "metadata" under the custom "widget" namespace |
46 | *
47 | *
48 | *
49 | * @author ashley.mercer@skylightipv.com
50 | * @see Entity#getMetadata()
51 | * @see X-Ray Segment documentation
52 | */
53 | @SuppressWarnings("WeakerAccess")
54 | public final class AWSXRayMetadataNamespaces {
55 |
56 | private AWSXRayMetadataNamespaces(){}
57 |
58 | /**
59 | * The default namespace for anything which doesn't explicitly
60 | * specify its own namespace.
61 | */
62 | public static final String DEFAULT = "default";
63 |
64 | /**
65 | * Log statements are also stored in the metadata since X-Ray doesn't
66 | * have another mechanism to expose them currently.
67 | */
68 | public static final String LOG = "log";
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRayScope.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import io.opentracing.Scope;
4 |
5 | /**
6 | * @see Scope
7 | * @author ashley.mercer@skylightipv.com
8 | */
9 | class AWSXRayScope implements Scope {
10 |
11 | private final AWSXRayScopeManager scopeManager;
12 | private final AWSXRayScope previousScope;
13 |
14 | private final AWSXRaySpan span;
15 | private final boolean finishOnClose;
16 |
17 | AWSXRayScope(AWSXRayScopeManager scopeManager, AWSXRayScope previousScope, AWSXRaySpan span, boolean finishOnClose) {
18 | this.scopeManager = scopeManager;
19 | this.previousScope = previousScope;
20 | this.span = span;
21 | this.finishOnClose = finishOnClose;
22 | }
23 |
24 | @Override
25 | public void close() {
26 | if (finishOnClose) span.finish();
27 | scopeManager.setCurrentScope(previousScope);
28 | }
29 |
30 | @Override
31 | public AWSXRaySpan span() {
32 | return span;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRayScopeManager.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.AWSXRayRecorder;
4 | import com.amazonaws.xray.entities.Entity;
5 | import io.opentracing.Scope;
6 | import io.opentracing.ScopeManager;
7 | import io.opentracing.Span;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | /**
12 | * Implementation of {@link ScopeManager} when tracing with AWS X-Ray.
13 | * The X-Ray libraries also have their own lifecycle management and
14 | * reference counting for the underlying trace {@link Entity}s, so we
15 | * need to hook in to these to keep OpenTracing and X-Ray in sync.
16 | *
17 | * @author ashley.mercer@skylightipv.com
18 | */
19 | class AWSXRayScopeManager implements ScopeManager {
20 |
21 | private static final Logger log = LoggerFactory.getLogger(AWSXRayScopeManager.class);
22 |
23 | /**
24 | * The {@link AWSXRayRecorder} class keeps track of the current trace
25 | * {@link Entity} on this thread, and we need to keep its view and our
26 | * view of the world in sync.
27 | */
28 | private final AWSXRayRecorder xRayRecorder;
29 |
30 | /**
31 | * X-Ray already keeps track of the current active {@link Entity}, but
32 | * additionally track here the whole {@link Scope} in order to be able
33 | * to recover the previous state on this thread once the current span
34 | * is finished / closed.
35 | */
36 | private final ThreadLocal currentScope;
37 |
38 | /**
39 | * Set the current {@link Scope} back to the given value. All changes to
40 | * the {@link #currentScope} value should pass through this method, since
41 | * it also hooks into the underlying X-Ray classes.
42 | *
43 | * @param scope the new current scope
44 | */
45 | void setCurrentScope(AWSXRayScope scope) {
46 | currentScope.set(scope);
47 | xRayRecorder.setTraceEntity(scope == null ? null : scope.span().getEntity());
48 | }
49 |
50 | AWSXRayScopeManager(AWSXRayRecorder xRayRecorder) {
51 | this.xRayRecorder = xRayRecorder;
52 | this.currentScope = new ThreadLocal<>();
53 | }
54 |
55 | @Override
56 | public AWSXRayScope active() {
57 | return this.currentScope.get();
58 | }
59 |
60 | AWSXRaySpan activeSpan() {
61 | final AWSXRayScope activeScope = this.currentScope.get();
62 | return activeScope == null ? null : activeScope.span();
63 | }
64 |
65 | @Override
66 | public Scope activate(Span span, boolean finishSpanOnClose) {
67 | if (span instanceof AWSXRaySpan) {
68 | final AWSXRayScope oldScope = currentScope.get();
69 | final AWSXRayScope newScope = new AWSXRayScope(this, oldScope, (AWSXRaySpan) span, finishSpanOnClose);
70 | setCurrentScope(newScope);
71 | return newScope;
72 | }
73 | else {
74 | if (span != null) {
75 | log.warn("Cannot activate Span: expected AWSXRaySpan but got type " + span.getClass().getSimpleName());
76 | }
77 | return currentScope.get();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRaySpan.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.entities.Entity;
4 | import com.amazonaws.xray.entities.Segment;
5 | import com.amazonaws.xray.entities.Subsegment;
6 | import io.opentracing.Span;
7 | import io.opentracing.contrib.tag.ExtraTags;
8 | import io.opentracing.log.Fields;
9 | import io.opentracing.tag.Tags;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import java.time.Instant;
14 | import java.util.*;
15 | import java.util.concurrent.ConcurrentHashMap;
16 | import java.util.concurrent.atomic.AtomicBoolean;
17 |
18 | /**
19 | * A {@link Span} in the OpenTracing API corresponds more or less directly
20 | * to a trace {@link Entity} in the X-Ray API, with a couple of major differences:
21 | *
22 | *
23 | * - X-Ray distinguishes between the top-level trace entity, modelled as
24 | * a {@link Segment}, and child entities, modelled as {@link Subsegment}. No
25 | * such distinction exists in OpenTracing.
26 | *
27 | * - OpenTracing allows arbitrary key-value pairs (aka tags) to be stored on
28 | * the trace in an approximately flat namespace; by contrast X-Ray stores such
29 | * data in a hierarchical structure, and uses different areas of the trace for
30 | * different types of data (e.g. HTTP request and response data, versus database
31 | * connection data)
32 | *
33 | * - X-Ray doesn't really have any concept of a "log" as defined in the
34 | * OpenTracing API; distributed logging is better done in AWS by e.g. piping
35 | * log files to CloudWatch. We provide some basic facility to store log
36 | * statements directly on the trace but this isn't advisable (although it can
37 | * be useful for exception / error tracking, since these do show up correctly
38 | * in the X-Ray graphs)
39 | *
40 | *
41 | * @see Span
42 | * @see Segment
43 | * @see Subsegment
44 | * @author ashley.mercer@skylightipv.com
45 | */
46 | class AWSXRaySpan implements Span {
47 |
48 | private static final Logger log = LoggerFactory.getLogger(AWSXRaySpan.class);
49 |
50 | /**
51 | * A map of synonyms from OpenTracing naming conventions to X-Ray. This is
52 | * necessary because X-Ray traces store some information (e.g. HTTP request
53 | * and response data) in different places on the resulting object, but we
54 | * want users to be able to continue to use the standard OpenTracing names.
55 | */
56 | private static final Map TAG_SYNONYMS = new HashMap<>();
57 | static {
58 | TAG_SYNONYMS.put(Tags.DB_INSTANCE.getKey(), "sql.url");
59 | TAG_SYNONYMS.put(Tags.DB_STATEMENT.getKey(), "sql.sanitized_query");
60 | TAG_SYNONYMS.put(Tags.DB_TYPE.getKey(), "sql.database_type");
61 | TAG_SYNONYMS.put(Tags.DB_USER.getKey(), "sql.user");
62 | TAG_SYNONYMS.put(ExtraTags.DB_DRIVER.getKey(), "sql.driver_version");
63 | TAG_SYNONYMS.put(ExtraTags.DB_VERSION.getKey(), "sql.database_version");
64 |
65 | TAG_SYNONYMS.put(Tags.HTTP_METHOD.getKey(), "http.request.method");
66 | TAG_SYNONYMS.put(Tags.HTTP_STATUS.getKey(), "http.response.status");
67 | TAG_SYNONYMS.put(Tags.HTTP_URL.getKey(), "http.request.url");
68 | TAG_SYNONYMS.put(ExtraTags.HTTP_CLIENT_IP.getKey(), "http.request.client_ip");
69 | TAG_SYNONYMS.put(ExtraTags.HTTP_USER_AGENT.getKey(), "http.request.user_agent");
70 | TAG_SYNONYMS.put(ExtraTags.HTTP_CONTENT_LENGTH.getKey(), "http.response.content_length");
71 |
72 | TAG_SYNONYMS.put(ExtraTags.VERSION.getKey(), "service.version");
73 | }
74 |
75 | /**
76 | * Reference to the underlying X-Ray trace {@link Entity}
77 | */
78 | private final Entity entity;
79 |
80 | /**
81 | * @see io.opentracing.SpanContext
82 | */
83 | private final AWSXRaySpanContext context;
84 |
85 | /**
86 | * Keep track of whether finish() has been called yet
87 | */
88 | private final AtomicBoolean isFinished;
89 |
90 | AWSXRaySpan(Entity entity, AWSXRaySpanContext context) {
91 | this.entity = entity;
92 | this.context = context;
93 | this.isFinished = new AtomicBoolean(false);
94 | }
95 |
96 | /**
97 | * @return the underlying X-Ray trace {@link Entity}
98 | */
99 | Entity getEntity() {
100 | return entity;
101 | }
102 |
103 | @Override
104 | public AWSXRaySpanContext context() {
105 | return context;
106 | }
107 |
108 | @Override
109 | public Span setTag(String key, String value) {
110 |
111 | // Match for known tags which are handled differently in X-Ray
112 | if (AWSXRayTags.USER.getKey().equals(key) && entity instanceof Segment) {
113 | ((Segment) entity).setUser(value);
114 | }
115 | else if (AWSXRayTags.ORIGIN.getKey().equals(key) && entity instanceof Segment) {
116 | ((Segment) entity).setOrigin(value);
117 | }
118 | else if (AWSXRayTags.PARENT_ID.getKey().equals(key)) {
119 | entity.setParentId(value);
120 | }
121 | else {
122 | setTagAny(key, value);
123 | }
124 | return this;
125 | }
126 |
127 | @Override
128 | public Span setTag(String key, boolean value) {
129 |
130 | // Match for known tags which are handled differently in X-Ray
131 | if (Tags.ERROR.getKey().equals(key)) {
132 | entity.setError(value);
133 | }
134 | else if (AWSXRayTags.FAULT.getKey().equals(key)) {
135 | entity.setFault(value);
136 | }
137 | else if (AWSXRayTags.THROTTLE.getKey().equals(key)) {
138 | entity.setThrottle(value);
139 | }
140 | else if (AWSXRayTags.IS_SAMPLED.getKey().equals(key) && entity instanceof Segment) {
141 | ((Segment) entity).setSampled(value);
142 | }
143 | else {
144 | setTagAny(key, value);
145 | }
146 | return this;
147 | }
148 |
149 | @Override
150 | public Span setTag(String key, Number value) {
151 | setTagAny(key, value);
152 | return this;
153 | }
154 |
155 | @Override
156 | public Span log(String event) {
157 | return log(Collections.singletonMap(Fields.MESSAGE, event));
158 | }
159 |
160 | @Override
161 | public Span log(long timestampMicroseconds, String event) {
162 | return log(timestampMicroseconds, Collections.singletonMap(Fields.MESSAGE, event));
163 | }
164 |
165 | @Override
166 | public Span log(Map fields) {
167 | return log(Instant.now(), fields);
168 | }
169 |
170 | @Override
171 | public Span log(long timestampMicroseconds, Map fields) {
172 | return log(Instant.ofEpochMilli(timestampMicroseconds / 1000L), fields);
173 | }
174 |
175 | /**
176 | * Log arbitrary data to the X-Ray trace. Since X-Ray doesn't really have its own
177 | * built-in log mechanism (CloudWatch would be a more suitable place for this)
178 | * we just store the provided data in a special "metadata.logs" area.
179 | *
180 | * @param timestamp the timestamp of the provided log
181 | * @param fields arbitrary fields to store against this timestamp
182 | */
183 | private Span log(Instant timestamp, Map fields) {
184 |
185 | // If the provided map contains an exception, use X-Ray's Cause
186 | // object to record the full stack trace
187 | final Object errorObject = fields.get(Fields.ERROR_OBJECT);
188 | if (errorObject instanceof Throwable) {
189 | entity.addException((Throwable) errorObject);
190 | }
191 | else {
192 | entity.putMetadata(AWSXRayMetadataNamespaces.LOG, timestamp.toString(), fields);
193 | }
194 | return this;
195 | }
196 |
197 | @Override
198 | public Span setBaggageItem(String key, String value) {
199 | context.setBaggageItem(key, value);
200 | return this;
201 | }
202 |
203 | @Override
204 | public String getBaggageItem(String key) {
205 | return context.getBaggageItem(key);
206 | }
207 |
208 | @Override
209 | public Span setOperationName(String operationName) {
210 | throw new UnsupportedOperationException("The AWS X-Ray API does not permit segment names to be changed after creation");
211 | }
212 |
213 | /**
214 | * For an arbitrary key and value, store it in the correct place on
215 | * the underlying X-Ray {@link Entity}:
216 | *
217 | *
218 | * - X-Ray traces use different naming conventions from OpenTracing
219 | * so some OpenTracing names are automatically to X-Ray format
220 | * - X-Ray traces have special subsections for certain types of
221 | * data, e.g. HTTP request and response data
222 | * - all other fields are stored in the general-purpose "metadata"
223 | * part of the X-Ray trace
224 | *
225 | *
226 | * @param key the tag (name) for the value to be stored
227 | * @param value the value to be stored
228 | */
229 | private void setTagAny(String key, Object value) {
230 |
231 | // First translate the key from OpenTracing names to X-Ray names
232 | final String awsKey = TAG_SYNONYMS.getOrDefault(key, key);
233 | if (awsKey != null) {
234 |
235 | // OpenTracing keys are '.'-separated by convention
236 | final List allKeyParts = Arrays.asList(awsKey.split("\\."));
237 | final Iterator remainingKeyParts = allKeyParts.iterator();
238 |
239 | // String.split is guaranteed to return at least one element (if the
240 | // separator didn't appear at all) so we're always safe to get the
241 | // first element from this iterator
242 | final String awsKeyPart1 = remainingKeyParts.next();
243 |
244 | // X-Ray Entity uses different Maps to store different types of
245 | // information and the first part of the key will tell us whether to
246 | // use one these Maps or not
247 | if ("annotations".equals(awsKeyPart1)) {
248 | setTagAny(remainingKeyParts, value, entity.getAnnotations());
249 | }
250 | else if ("aws".equals(awsKeyPart1)) {
251 | setTagAny(remainingKeyParts, value, entity.getAws());
252 | }
253 | else if ("http".equals(awsKeyPart1)) {
254 | setTagAny(remainingKeyParts, value, entity.getHttp());
255 | }
256 | // Service-level information is only available on top-level trace Segments
257 | else if ("service".equals(awsKeyPart1) && entity instanceof Segment) {
258 | setTagAny(remainingKeyParts, value, ((Segment) entity).getService());
259 | }
260 | else if ("sql".equals(awsKeyPart1)) {
261 | setTagAny(remainingKeyParts, value, entity.getSql());
262 | }
263 |
264 | // Store everything else in the "metadata" part of the trace
265 | else {
266 | final Map> metadata = entity.getMetadata();
267 |
268 | //
269 | // Figure out which namespace to use in the metadata using the following rules:
270 | //
271 | // "foo" -> "metadata.default.foo"
272 | // "metadata.foo" -> "metadata.default.foo"
273 | // "namespace.foo" -> "metadata.namespace.foo"
274 | // "metadata.namespace.foo" -> "metadata.namespace.foo"
275 | //
276 |
277 | // If the key started with "metadata" chomp it; otherwise assume "metadata"
278 | // implicitly and revert to using the whole of supplied key for namespacing
279 | final Iterator metadataKeyParts = "metadata".equals(awsKeyPart1) && remainingKeyParts.hasNext() ?
280 | remainingKeyParts :
281 | allKeyParts.iterator();
282 |
283 | // Look at the next part of the key: if there are more key parts after this,
284 | // then awsKeyPart2 is the namespace; otherwise assume the default namespace
285 | final String awsKeyPart2 = metadataKeyParts.next();
286 | final String metadataNamespace = metadataKeyParts.hasNext() ?
287 | awsKeyPart2 :
288 | AWSXRayMetadataNamespaces.DEFAULT;
289 |
290 | // If there are remaining key parts (after the second part) then they are the
291 | // nested keys; otherwise we only had a 2-part key (and will have used the
292 | // DEFAULT namespace) so just use the second part as the nested key
293 | final Iterator namespaceKeyParts = metadataKeyParts.hasNext() ?
294 | metadataKeyParts :
295 | Collections.singletonList(awsKeyPart2).iterator();
296 |
297 | final Map targetMap = metadata.computeIfAbsent(metadataNamespace, __ -> new ConcurrentHashMap<>());
298 | setTagAny(namespaceKeyParts, value, targetMap);
299 | }
300 | }
301 | }
302 |
303 | /**
304 | *
305 | * Store an arbitrary value in a nested {@link Map} structure, assuming
306 | * that at each level of nesting we either have a key-value pair, or else
307 | * a key pointing to a nested Map. This mimics the JSON structure used
308 | * by X-Ray to transmit tag and baggage data.
309 | *
310 | *
311 | * For example, given the following existing value for targetMap:
312 | *
313 | *
314 | * {
315 | * "http": {
316 | * "request": {
317 | * "method": "GET",
318 | * "url": "http://www.example.com"
319 | * }
320 | * }
321 | * }
322 | *
323 | *
324 | * and the inputs:
325 | *
326 | *
327 | * keyParts: ["http", "response", "code"],
328 | * value: 200
329 | *
330 | *
331 | * we return the value:
332 | *
333 | *
334 | * {
335 | * "http": {
336 | * "request": {
337 | * "method": "GET",
338 | * "url": "http://www.example.com"
339 | * },
340 | * "response": {
341 | * "code": 200
342 | * }
343 | * }
344 | * }
345 | *
346 | *
347 | * NB this method mutates the underlying value of targetMap so care should
348 | * be taken to only ever pass thread-safe instances (and this method in turn will
349 | * create thread-safe sub-Maps for nested values).
350 | *
351 | * @param remainingKeyParts the list of individual parts of the tag (name) for the value to be stored
352 | * @param value the value to be stored
353 | * @param targetMap the target Map instance
354 | */
355 | private void setTagAny(Iterator remainingKeyParts, Object value, Map targetMap) {
356 |
357 | // The iterator should never be empty here, but just for safety
358 | if (remainingKeyParts.hasNext()) {
359 | final String nextKeyPart = remainingKeyParts.next();
360 |
361 | // The key is further nested so recurse to the next level of the map
362 | if (remainingKeyParts.hasNext()) {
363 |
364 | @SuppressWarnings("unchecked")
365 | final Map targetSubMap = (Map) targetMap.computeIfAbsent(nextKeyPart, __ -> new ConcurrentHashMap<>());
366 | setTagAny(remainingKeyParts, value, targetSubMap);
367 |
368 | // This is the last key, so store at this level
369 | } else {
370 | targetMap.put(nextKeyPart, value);
371 | }
372 | }
373 | }
374 |
375 | @Override
376 | public void finish() {
377 | finish(Instant.now().toEpochMilli() / 1000.0);
378 | }
379 |
380 | @Override
381 | public void finish(long finishMicros) {
382 | finish(finishMicros / 1000.0 / 1000.0);
383 | }
384 |
385 | /**
386 | * Set the end time for this span and close it - this will usually trigger
387 | * sending the trace data back to AWS.
388 | *
389 | * @param finishSeconds timestamp of the end of this span as a number of
390 | * seconds since the UNIX
391 | */
392 | private void finish(double finishSeconds) {
393 | if (isFinished.compareAndSet(false, true)) {
394 | try {
395 | entity.setEndTime(finishSeconds);
396 | entity.close();
397 | } catch (Exception e) {
398 | log.error("Failed to close underlying AWS trace Entity: " + e.getMessage(), e);
399 | }
400 | }
401 | }
402 | }
403 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRaySpanContext.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import io.opentracing.SpanContext;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * In AWS, the span context usually only needs to hold the trace header
10 | * information (current trace ID, parent segment ID, sampling decision) such
11 | * that this can be propagated to child processes.
12 | *
13 | * @author ashley.mercer@skylightipv.com
14 | * @see io.opentracing.SpanContext
15 | * @see com.amazonaws.xray.entities.TraceHeader
16 | */
17 | class AWSXRaySpanContext implements SpanContext {
18 |
19 | private final Map baggage;
20 |
21 | AWSXRaySpanContext(Map baggage) {
22 | this.baggage = new HashMap<>(baggage);
23 | }
24 |
25 | @Override
26 | public Iterable> baggageItems() {
27 | return baggage.entrySet();
28 | }
29 |
30 | Map getBaggage() {
31 | return baggage;
32 | }
33 |
34 | void setBaggageItem(String key, String value) {
35 | baggage.put(key, value);
36 | }
37 |
38 | String getBaggageItem(String key) {
39 | return baggage.get(key);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRayTags.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.entities.Entity;
4 | import com.amazonaws.xray.entities.Segment;
5 | import io.opentracing.tag.BooleanTag;
6 | import io.opentracing.tag.StringTag;
7 |
8 | /**
9 | * Additional known tags which are specific to AWS X-Ray. In general, users
10 | * should prefer the standard OpenTracing tag names and conventions, but it
11 | * can be useful to sometimes take advantage of X-Ray specific functionality.
12 | *
13 | * @author ashley.mercer@skylightipv.com
14 | */
15 | @SuppressWarnings("WeakerAccess")
16 | public final class AWSXRayTags {
17 |
18 | private AWSXRayTags(){}
19 |
20 | /**
21 | * FAULT indicates whether or not a Span ended in a fault
22 | * (non-recoverable exception) state.
23 | *
24 | * @see Entity#isFault()
25 | */
26 | public static final BooleanTag FAULT = new BooleanTag("fault");
27 |
28 | /**
29 | * THROTTLE indicates that the underlying request was throttled,
30 | * usually caused by resource constraints.
31 | *
32 | * @see Entity#isThrottle()
33 | */
34 | public static final BooleanTag THROTTLE = new BooleanTag("throttle");
35 |
36 | /**
37 | * IS_SAMPLED indicates that the current trace segment should be
38 | * subject to sampling by X-Ray.
39 | *
40 | * @see Segment#isSampled()
41 | */
42 | public static final BooleanTag IS_SAMPLED = new BooleanTag("isSampled");
43 |
44 | /**
45 | * USER is the identifier for the user who initiated this request,
46 | * typically a logged-in website user or IAM username. Should only be
47 | * set on top-level trace Spans.
48 | *
49 | * @see Segment#getUser()
50 | */
51 | public static final StringTag USER = new StringTag("user");
52 |
53 | /**
54 | * ORIGIN indicates the type of AWS resource running the application,
55 | * value is typically something like "AWS::EC2::Instance". Should only
56 | * be set on top-level trace Spans.
57 | *
58 | * @see Segment#getOrigin()
59 | */
60 | public static final StringTag ORIGIN = new StringTag("origin");
61 |
62 | /**
63 | * PARENT_ID is the unique identifier for the parent span (as
64 | * distinct from the ID of the whole trace).
65 | *
66 | * @see Entity#getParentId()
67 | */
68 | public static final StringTag PARENT_ID = new StringTag("parentId");
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRayTracer.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.AWSXRayRecorder;
4 | import com.amazonaws.xray.contexts.LambdaSegmentContext;
5 | import com.amazonaws.xray.entities.*;
6 | import io.opentracing.*;
7 | import io.opentracing.propagation.Format;
8 | import io.opentracing.propagation.TextMap;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | import java.time.Instant;
13 | import java.util.Collections;
14 | import java.util.HashMap;
15 | import java.util.Map;
16 | import java.util.concurrent.ConcurrentHashMap;
17 | import java.util.concurrent.atomic.AtomicReference;
18 |
19 | /**
20 | * Top-level OpenTracing {@link Tracer} implementation which is backed
21 | * by the AWS X-Ray client libraries.
22 | *
23 | * @see https://opentracing.io
24 | * @see https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html
25 | * @author ashley.mercer@skylightipv.com
26 | */
27 | @SuppressWarnings("WeakerAccess")
28 | public class AWSXRayTracer implements Tracer {
29 |
30 | private static final Logger log = LoggerFactory.getLogger(AWSXRayTracer.class);
31 |
32 | private final AWSXRayRecorder xRayRecorder;
33 | private final AWSXRayScopeManager scopeManager;
34 |
35 | public AWSXRayTracer(AWSXRayRecorder xRayRecorder) {
36 | this.xRayRecorder = xRayRecorder;
37 | this.scopeManager = new AWSXRayScopeManager(xRayRecorder);
38 | }
39 |
40 | @Override
41 | public ScopeManager scopeManager() {
42 | return scopeManager;
43 | }
44 |
45 | @Override
46 | public Span activeSpan() {
47 | return scopeManager.activeSpan();
48 | }
49 |
50 | @Override
51 | public SpanBuilder buildSpan(String operationName) {
52 | return new AWSXRaySpanBuilderImpl(operationName);
53 | }
54 |
55 | @Override
56 | public void inject(SpanContext spanContext, Format format, C carrier) {
57 | if (format == Format.Builtin.TEXT_MAP || format == Format.Builtin.HTTP_HEADERS) {
58 | final TextMap textMap = (TextMap) carrier;
59 | spanContext.baggageItems().forEach(e -> textMap.put(e.getKey(), e.getValue()));
60 | }
61 | else {
62 | throw new UnsupportedOperationException("Format " + format.toString() + " is not currently supported");
63 | }
64 | }
65 |
66 | @Override
67 | public SpanContext extract(Format format, C carrier) {
68 | if (format == Format.Builtin.TEXT_MAP || format == Format.Builtin.HTTP_HEADERS) {
69 | final TextMap textMap = (TextMap) carrier;
70 | final Map baggage = new HashMap<>();
71 | for (Map.Entry e : textMap) { baggage.put(e.getKey(), e.getValue()); }
72 | return new AWSXRaySpanContext(baggage);
73 | }
74 | else {
75 | throw new UnsupportedOperationException("Format " + format.toString() + " is not currently supported");
76 | }
77 | }
78 |
79 | /**
80 | * AWS-specific {@link io.opentracing.Tracer.SpanBuilder} implementation
81 | */
82 | private final class AWSXRaySpanBuilderImpl implements SpanBuilder {
83 |
84 | private final String operationName;
85 |
86 | private final Map stringTags;
87 | private final Map booleanTags;
88 | private final Map numberTags;
89 |
90 | /**
91 | * AWS X-Ray timestamps are stored a number of seconds since
92 | * the UNIX epoch, with the fractional part giving sub-second
93 | * precision. Defaults to creation time of this builder.
94 | *
95 | * @see #withStartTimestamp(long)
96 | * @see Entity#getStartTime()
97 | */
98 | private final AtomicReference startTimestampEpochSeconds;
99 |
100 | /**
101 | * @see SpanBuilder#ignoreActiveSpan()
102 | */
103 | private final AtomicReference ignoreActiveSpan;
104 |
105 | /**
106 | * Currently only support a single reference to the parent Span (if
107 | * it exists). Other references are not supported.
108 | *
109 | * @see References
110 | */
111 | private final Map references;
112 |
113 | private AWSXRaySpanBuilderImpl(String operationName) {
114 | this.operationName = operationName;
115 |
116 | this.stringTags = new HashMap<>();
117 | this.booleanTags = new HashMap<>();
118 | this.numberTags = new HashMap<>();
119 |
120 | this.startTimestampEpochSeconds = new AtomicReference<>();
121 | this.ignoreActiveSpan = new AtomicReference<>(false);
122 | this.references = new ConcurrentHashMap<>();
123 | }
124 |
125 | @Override
126 | public SpanBuilder asChildOf(SpanContext parent) {
127 | return addReference(References.CHILD_OF, parent);
128 | }
129 |
130 | @Override
131 | public SpanBuilder asChildOf(Span parent) {
132 | if (parent == null) {
133 | return this;
134 | }
135 | else if (parent instanceof AWSXRaySpan) {
136 | return addReference(References.CHILD_OF, new CapturingSpanContext((AWSXRaySpan) parent));
137 | }
138 | else {
139 | return addReference(References.CHILD_OF, parent.context());
140 | }
141 | }
142 |
143 | @Override
144 | public SpanBuilder addReference(String referenceType, SpanContext referencedContext) {
145 | if (references.containsKey(referenceType)) {
146 | log.warn("Replacing reference of type '" + referenceType + "': multiple references of the same type are not supported by X-Ray");
147 | }
148 | references.put(referenceType, referencedContext);
149 | return this;
150 | }
151 |
152 | @Override
153 | public SpanBuilder ignoreActiveSpan() {
154 | ignoreActiveSpan.set(true);
155 | return this;
156 | }
157 |
158 | @Override
159 | public SpanBuilder withTag(String key, String value) {
160 | stringTags.put(key, value);
161 | return this;
162 | }
163 |
164 | @Override
165 | public SpanBuilder withTag(String key, boolean value) {
166 | booleanTags.put(key, value);
167 | return this;
168 | }
169 |
170 | @Override
171 | public SpanBuilder withTag(String key, Number value) {
172 | numberTags.put(key, value);
173 | return this;
174 | }
175 |
176 | @Override
177 | public SpanBuilder withStartTimestamp(long microseconds) {
178 | startTimestampEpochSeconds.set(microseconds / 1000.0 / 1000.0);
179 | return this;
180 | }
181 |
182 | @Override
183 | @Deprecated
184 | public Span startManual() {
185 | return start();
186 | }
187 |
188 | @Override
189 | public Scope startActive(boolean finishSpanOnClose) {
190 | final Span span = start();
191 | return scopeManager.activate(span, finishSpanOnClose);
192 | }
193 |
194 | @Override
195 | public Span start() {
196 |
197 | // X-Ray only supports parent-child relationships between spans
198 | // (OpenTracing allows for other references e.g. FOLLOWS_FROM)
199 | references.forEach((key, value) -> {
200 | if (!References.CHILD_OF.equals(key)) {
201 | log.warn("Ignoring reference of type '" + key + "': references of this type are not supported by X-Ray");
202 | }
203 | });
204 |
205 | // If an explicit CHILD_OF reference is set, this should override
206 | // any (implicit) reference to the current trace entity
207 | final Entity originalTraceEntity = xRayRecorder.getTraceEntity();
208 | final SpanContext explicitParentContext = references.get(References.CHILD_OF);
209 |
210 | final Entity parentEntity;
211 | final Map parentBaggage;
212 |
213 | // Because X-Ray an OpenTracing maintain their references to the
214 | // "current" trace separately, we can be in one of four possible states:
215 | //
216 | // 1. an explicit parent is set, and it has captured a full AWSXRaySpan
217 | // i.e. this is an in-memory Span with a real X-Ray Entity
218 | //
219 | if (explicitParentContext instanceof CapturingSpanContext) {
220 | parentEntity = ((CapturingSpanContext) explicitParentContext).span.getEntity();
221 | parentBaggage = AWSXRayUtils.extract(explicitParentContext.baggageItems());
222 | }
223 |
224 | // 2. an explicit parent is set but it doesn't have an X-Ray Entity
225 | // attached: we can present a FacadeSegment to X-Ray
226 | //
227 | else if (explicitParentContext != null) {
228 |
229 | // If the parent context has a valid AWS trace ID in its baggage
230 | // (e.g. it came from some remote upstream server) then extract
231 | // the trace and parent segment IDs here
232 | TraceHeader traceHeader = null;
233 | for (Map.Entry e : explicitParentContext.baggageItems()) {
234 | if (TraceHeader.HEADER_KEY.equals(e.getKey())) {
235 | traceHeader = TraceHeader.fromString(e.getValue());
236 | }
237 | }
238 | final TraceID traceId = null == traceHeader ? null : traceHeader.getRootTraceId();
239 | final String parentId = null == traceHeader ? null : traceHeader.getParentId();
240 | final TraceHeader.SampleDecision sampleDecision = traceHeader == null ? null : traceHeader.getSampled();
241 |
242 | // NB the default FacadeSegment class throws exceptions but we want
243 | // to allow subsegments to be added and removed (even though both
244 | // of these are ultimately a no-op)
245 | parentEntity = new FacadeSegment(xRayRecorder, traceId, parentId, sampleDecision) {
246 | @Override public void addSubsegment(Subsegment subsegment) {}
247 | @Override public void removeSubsegment(Subsegment subsegment) {}
248 | };
249 | parentBaggage = AWSXRayUtils.extract(explicitParentContext.baggageItems());
250 | }
251 |
252 | // 3. no explicit parent is set, but ignoreActiveSpan has been set so
253 | // make sure the parent Entity is null (i.e. we'll create a new
254 | // Segment in X-Ray terms)
255 | //
256 | else if (ignoreActiveSpan.get()) {
257 | parentEntity = null;
258 | parentBaggage = Collections.emptyMap();
259 | }
260 |
261 | // 4. no explicit parent, and ignoreActiveSpan is not set so create an
262 | // implicit reference to the current trace entity (if it exists:
263 | // if it's null we'll instead end up creating a top-level Segment
264 | // instead)
265 | //
266 | else {
267 | parentEntity = originalTraceEntity;
268 | parentBaggage = Collections.emptyMap();
269 | }
270 |
271 | // X-Ray automatically maintains internal references between Segments and
272 | // Subsegments - rather than trying to replicate that logic here, we cheat
273 | // by (temporarily) overwriting the parent trace Entity, creating the new
274 | // Entity, then setting it back once we're done
275 | xRayRecorder.setTraceEntity(parentEntity);
276 |
277 | // Special case when running in AWS Lambda: the Lambda infrastructure
278 | // creates a top-level trace Segment to which we do not have access, so
279 | // creating another Segment here would be an error. Instead, we need to
280 | // forcibly create a Subsegment.
281 | final boolean isAwsLambda = xRayRecorder.getSegmentContextResolverChain().resolve() instanceof LambdaSegmentContext;
282 |
283 | final Entity childEntity = (xRayRecorder.getTraceEntity() == null && !isAwsLambda) ?
284 | xRayRecorder.beginSegment(operationName) :
285 | xRayRecorder.beginSubsegment(operationName);
286 |
287 | // Set the original trace entity back on AWSXRayRecorder as soon as possible
288 | xRayRecorder.setTraceEntity(originalTraceEntity);
289 |
290 | // AWS X-Ray doesn't support the notion of "not-yet-started" segments
291 | // so set the Entity to be "in progress"
292 | childEntity.setInProgress(true);
293 |
294 | // Default to "now" if an explicit start time wasn't set
295 | startTimestampEpochSeconds.compareAndSet(null, Instant.now().toEpochMilli() / 1000.0);
296 | childEntity.setStartTime(startTimestampEpochSeconds.get());
297 |
298 | // Baggage items should mostly be carried over from the parent Span's
299 | // context (if it exists) to the child Span; however, the TraceHeader
300 | // should be replaced with the new value for the child span
301 | final TraceHeader traceHeader = new TraceHeader(
302 | childEntity.getParentSegment().getTraceId(),
303 | null == parentEntity ? null : parentEntity.getId(),
304 | childEntity.getParentSegment().isSampled() ? TraceHeader.SampleDecision.SAMPLED : TraceHeader.SampleDecision.NOT_SAMPLED
305 | );
306 |
307 | final Map childBaggage = new HashMap<>(parentBaggage);
308 | childBaggage.put(TraceHeader.HEADER_KEY, traceHeader.toString());
309 |
310 | final AWSXRaySpanContext newSpanContext = new AWSXRaySpanContext(childBaggage);
311 |
312 | // Defer to AWSXRaySpan to set tag values since this will handle
313 | // converting to X-Ray's naming conventions and format
314 | final AWSXRaySpan newSpan = new AWSXRaySpan(childEntity, newSpanContext);
315 | stringTags.forEach(newSpan::setTag);
316 | booleanTags.forEach(newSpan::setTag);
317 | numberTags.forEach(newSpan::setTag);
318 |
319 | return newSpan;
320 | }
321 | }
322 |
323 | /**
324 | * Parent-child relationships between Spans are typically only defined in
325 | * terms of the SpanContext (i.e. we only need to know the parent span's
326 | * trace and span ID). However, X-Ray also holds directly object references
327 | * to the underlying Segment and Subsegment instances, so try to capture
328 | * the full AWSXRaySpan instance here if we can.
329 | */
330 | private static final class CapturingSpanContext implements SpanContext {
331 | private final AWSXRaySpan span;
332 |
333 | public CapturingSpanContext(AWSXRaySpan span) {
334 | this.span = span;
335 | }
336 |
337 | @Override
338 | public Iterable> baggageItems() {
339 | return span.context().baggageItems();
340 | }
341 | }
342 | }
343 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/aws/xray/AWSXRayUtils.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import io.opentracing.SpanContext;
4 |
5 | import java.util.Collections;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * @author ashley.mercer@skylightipv.com
11 | */
12 | @SuppressWarnings("WeakerAccess")
13 | public final class AWSXRayUtils {
14 |
15 | private AWSXRayUtils(){}
16 |
17 | /**
18 | * Utility method to copy baggage values from a {@link SpanContext}
19 | * back into a vanilla Map, which is much easier to work with.
20 | *
21 | * @param baggage the view of underlying baggage
22 | * @return a new, unmodifiable Map instance containing all
23 | * of the values copied from the underlying baggage
24 | */
25 | public static Map extract(Iterable> baggage) {
26 | final Map targetMap = new HashMap<>();
27 | baggage.forEach(e -> targetMap.put(e.getKey(), e.getValue()));
28 | return Collections.unmodifiableMap(targetMap);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/io/opentracing/contrib/tag/ExtraTags.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.tag;
2 |
3 | import io.opentracing.tag.IntTag;
4 | import io.opentracing.tag.StringTag;
5 |
6 | /**
7 | * Additional suggested standard tag names that conform to
8 | * the OpenTracing naming patterns.
9 | *
10 | * @author ashley.mercer@skylightipv.com
11 | */
12 | public final class ExtraTags {
13 |
14 | private ExtraTags(){}
15 |
16 | /**
17 | * VERSION records the software version of the current application
18 | */
19 | public static final StringTag VERSION = new StringTag("version");
20 |
21 | /**
22 | * DB_DRIVER records the name and / or version number of the client-side
23 | * database driver
24 | */
25 | public static final StringTag DB_DRIVER = new StringTag("db.driver");
26 |
27 | /**
28 | * DB_VERSION captures the database server-side version String
29 | */
30 | public static final StringTag DB_VERSION = new StringTag("db.version");
31 |
32 | /**
33 | * HTTP_CLIENT_IP records the IP address of the requester, could be captured
34 | * e.g. from the IP packet's 'Source Address' or the X-Forwarded-For header
35 | */
36 | public static final StringTag HTTP_CLIENT_IP = new StringTag("http.client_ip");
37 |
38 | /**
39 | * HTTP_USER_AGENT records the HTTP client's User-Agent header
40 | */
41 | public static final StringTag HTTP_USER_AGENT = new StringTag("http.user_agent");
42 |
43 | /**
44 | * HTTP_CONTENT_LENGTH records the number of bytes returned to the client
45 | * by the server over the course of the request
46 | */
47 | public static final IntTag HTTP_CONTENT_LENGTH = new IntTag("http.content_length");
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/io/opentracing/contrib/aws/xray/AWSXRaySpanBuilderTests.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.AWSXRayRecorder;
4 | import com.amazonaws.xray.AWSXRayRecorderBuilder;
5 | import com.amazonaws.xray.entities.Entity;
6 | import com.amazonaws.xray.entities.Segment;
7 | import com.amazonaws.xray.entities.TraceHeader;
8 | import io.opentracing.Scope;
9 | import io.opentracing.Span;
10 | import io.opentracing.SpanContext;
11 | import org.junit.jupiter.api.DisplayName;
12 | import org.junit.jupiter.api.Test;
13 |
14 | import java.util.Collections;
15 | import java.util.Map;
16 |
17 | import static org.junit.jupiter.api.Assertions.*;
18 |
19 | /**
20 | * @author ashley.mercer@skylightipv.com
21 | */
22 | class AWSXRaySpanBuilderTests extends AWSXRayTestParent {
23 |
24 | private final AWSXRayRecorder recorder = AWSXRayRecorderBuilder.defaultRecorder();
25 | private final AWSXRayTracer tracer = new AWSXRayTracer(recorder);
26 |
27 | @Test
28 | @DisplayName("set span name")
29 | void setSpanName() {
30 | final Span span = tracer
31 | .buildSpan("test-span-name")
32 | .start();
33 |
34 | assertEquals("test-span-name", ((AWSXRaySpan) span).getEntity().getName());
35 | }
36 |
37 | @Test
38 | @DisplayName("set active span")
39 | void setActiveSpan() {
40 | final Scope activeScope = tracer
41 | .buildSpan("test-active-span")
42 | .startActive(true);
43 |
44 | assertEquals(activeScope.span(), tracer.activeSpan());
45 |
46 | activeScope.close();
47 | }
48 |
49 | @Test
50 | @DisplayName("set trace header in baggage")
51 | void setTraceHeaderInBaggage() {
52 | final Scope activeScope = tracer
53 | .buildSpan("test-trace-header")
54 | .startActive(true);
55 |
56 | final String activeTraceHeader = activeScope.span().getBaggageItem(TraceHeader.HEADER_KEY);
57 | assertNotNull(activeTraceHeader);
58 | assertTrue(activeTraceHeader.contains(((AWSXRaySpan) activeScope.span()).getEntity().getTraceId().toString()));
59 |
60 | activeScope.close();
61 | }
62 |
63 | @Test
64 | @DisplayName("set implicit active span as parent")
65 | void setImplicitParentSpan() {
66 | final Scope parentScope = tracer
67 | .buildSpan("parent-span")
68 | .startActive(true);
69 |
70 | final Scope childScope = tracer
71 | .buildSpan("child-span")
72 | .startActive(true);
73 |
74 | final Entity parentEntity = ((AWSXRayScope) parentScope).span().getEntity();
75 | final Entity childEntity = ((AWSXRayScope) childScope).span().getEntity();
76 |
77 | assertFalse(parentEntity.getSubsegments().isEmpty());
78 | assertEquals(parentEntity, childEntity.getParent());
79 | assertEquals(parentEntity.getTraceId(), childEntity.getParent().getTraceId());
80 |
81 | // Check that trace header is correctly set in the child
82 | final String childTraceHeader = childScope.span().getBaggageItem(TraceHeader.HEADER_KEY);
83 | assertNotNull(childTraceHeader);
84 | assertTrue(childTraceHeader.contains(parentEntity.getTraceId().toString()));
85 | assertTrue(childTraceHeader.contains(parentEntity.getId()));
86 |
87 | childScope.close();
88 | parentScope.close();
89 | }
90 |
91 | @Test
92 | @DisplayName("set explicit span as parent")
93 | void setExplicitParentSpan() {
94 |
95 | // NB we *don't* startActive here - assume this Span
96 | // object came from somewhere else in the code
97 | final AWSXRaySpan explicitParentSpan = mockSpan("explicit-parent-span");
98 |
99 | // This implicit parent should be ignored by SpanBuilder
100 | // when we set the explicit parent
101 | final Scope implicitParentScope = tracer
102 | .buildSpan("implicit-parent-span")
103 | .startActive(true);
104 |
105 | final Scope childScope = tracer
106 | .buildSpan("child-span")
107 | .asChildOf(explicitParentSpan)
108 | .startActive(true);
109 |
110 | final Entity explicitParentEntity = explicitParentSpan.getEntity();
111 | final Entity implicitParentEntity = ((AWSXRayScope) implicitParentScope).span().getEntity();
112 | final Entity childEntity = ((AWSXRayScope) childScope).span().getEntity();
113 |
114 | assertFalse(explicitParentEntity.getSubsegments().isEmpty());
115 | assertTrue(implicitParentEntity.getSubsegments().isEmpty());
116 |
117 | assertEquals(explicitParentEntity, childEntity.getParent());
118 | assertNotEquals(explicitParentEntity.getId(), childEntity.getId());
119 |
120 | // Check that trace header is correctly set in the child
121 | final String childTraceHeader = childScope.span().getBaggageItem(TraceHeader.HEADER_KEY);
122 | assertNotNull(childTraceHeader);
123 | assertTrue(childTraceHeader.contains(explicitParentEntity.getTraceId().toString()));
124 | assertTrue(childTraceHeader.contains(explicitParentEntity.getId()));
125 |
126 | childScope.close();
127 | implicitParentScope.close();
128 | }
129 |
130 | @Test
131 | @DisplayName("set explicit span as parent from remote server")
132 | void setExplicitParentSpanFromRemote() {
133 |
134 | // SpanContext can be passed to remote servers using inject() and
135 | // extract(), so assume we read this in from e.g. HTTP headers
136 | final SpanContext remoteContext = new AWSXRaySpanContext(Collections.singletonMap(
137 | TraceHeader.HEADER_KEY,
138 | traceHeader.toString()
139 | ));
140 |
141 | final Scope childScope = tracer
142 | .buildSpan("child-span")
143 | .asChildOf(remoteContext)
144 | .startActive(true);
145 |
146 | final Entity childEntity = ((AWSXRayScope) childScope).span().getEntity();
147 |
148 | assertEquals(childEntity.getParentSegment().getTraceId(), traceHeader.getRootTraceId());
149 | assertEquals(childEntity.getParentSegment().getId(), traceHeader.getParentId());
150 |
151 | // Check that trace header is correctly set in the child
152 | final String childTraceHeader = childScope.span().getBaggageItem(TraceHeader.HEADER_KEY);
153 | assertNotNull(childTraceHeader);
154 |
155 | childScope.close();
156 | }
157 |
158 | @Test
159 | @DisplayName("ignore implicit active span on ignoreActiveSpan")
160 | void ignoreImplicitParentSpan() {
161 | final Scope parentScope = tracer
162 | .buildSpan("parent-span")
163 | .startActive(true);
164 |
165 | final Scope childScope = tracer
166 | .buildSpan("child-span")
167 | .ignoreActiveSpan()
168 | .startActive(true);
169 |
170 | final Entity parentEntity = ((AWSXRayScope) parentScope).span().getEntity();
171 | final Entity childEntity = ((AWSXRayScope) childScope).span().getEntity();
172 |
173 | assertTrue(parentEntity.getSubsegments().isEmpty());
174 | assertNull(childEntity.getParent());
175 | assertNotEquals(parentEntity.getParentSegment().getTraceId(), childEntity.getParentSegment().getTraceId());
176 |
177 | // Check that trace header is correctly set in the child
178 | final String childTraceHeader = childScope.span().getBaggageItem(TraceHeader.HEADER_KEY);
179 | assertNotNull(childTraceHeader);
180 | assertTrue(childTraceHeader.contains(childEntity.getParentSegment().getTraceId().toString()));
181 | assertFalse(childTraceHeader.contains(parentEntity.getParentSegment().getTraceId().toString()));
182 | assertFalse(childTraceHeader.contains(parentEntity.getId()));
183 |
184 | childScope.close();
185 | parentScope.close();
186 | }
187 |
188 | /**
189 | * In systems where the surrounding code is using X-Ray directly, but
190 | * not the OpenTracing API, we should detect if a trace is already in
191 | * progress. For example, in AWS Lambda functions, the lambda server
192 | * creates a top-level trace segment for the whole function call
193 | *
194 | * @see https://docs.aws.amazon.com/xray/latest/devguide/xray-services-lambda.html
195 | */
196 | @Test
197 | @DisplayName("detect a pre-existing X-Ray trace")
198 | void detectPreExisting() {
199 | final Segment parentEntity = awsxRayRecorder.beginSegment("pre-existing-trace");
200 |
201 | final Scope childScope = tracer
202 | .buildSpan("child-of-pre-existing-trace")
203 | .startActive(true);
204 |
205 | final Entity childEntity = ((AWSXRayScope) childScope).span().getEntity();
206 |
207 | assertFalse(parentEntity.getSubsegments().isEmpty());
208 | assertEquals(parentEntity, childEntity.getParent());
209 |
210 | // Check that trace header is correctly set in the child
211 | final String childTraceHeader = childScope.span().getBaggageItem(TraceHeader.HEADER_KEY);
212 | assertNotNull(childTraceHeader);
213 | assertTrue(childTraceHeader.contains(parentEntity.getTraceId().toString()));
214 | assertTrue(childTraceHeader.contains(parentEntity.getId()));
215 |
216 | childScope.close();
217 | }
218 |
219 | @Test
220 | @DisplayName("set tags correctly")
221 | @SuppressWarnings("unchecked")
222 | void setTags() {
223 | final Scope scope = tracer
224 | .buildSpan("test-set-tags")
225 | .withTag("http.request.method", "POST")
226 | .withTag("http.response.status_code", 503)
227 | .withTag("fault", true)
228 | .startActive(true);
229 |
230 | final Entity entity = ((AWSXRayScope) scope).span().getEntity();
231 | assertEquals("POST", ((Map) entity.getHttp().get("request")).get("method"));
232 | assertEquals(503, ((Map) entity.getHttp().get("response")).get("status_code"));
233 | assertTrue(entity.isFault());
234 |
235 | scope.close();
236 | }
237 |
238 | @Test
239 | @DisplayName("set start timestamp correctly")
240 | void setStartTimestamp() {
241 | final Scope scope = tracer
242 | .buildSpan("test-set-start-timestamp")
243 | .withStartTimestamp(1551016321000000L)
244 | .startActive(true);
245 |
246 | final Entity entity = ((AWSXRayScope) scope).span().getEntity();
247 | assertEquals(1551016321.0, entity.getStartTime());
248 |
249 | scope.close();
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/src/test/java/io/opentracing/contrib/aws/xray/AWSXRaySpanTests.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.entities.Segment;
4 | import io.opentracing.log.Fields;
5 | import io.opentracing.tag.Tags;
6 | import org.junit.jupiter.api.DisplayName;
7 | import org.junit.jupiter.api.Test;
8 |
9 | import java.time.Instant;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 | import java.util.function.Function;
13 |
14 | import static org.junit.jupiter.api.Assertions.*;
15 |
16 | /**
17 | * @author ashley.mercer@skylightipv.com
18 | */
19 | class AWSXRaySpanTests extends AWSXRayTestParent {
20 |
21 | @Test
22 | @DisplayName("set ERROR tag on underlying Entity")
23 | void tagError() {
24 | final AWSXRaySpan span = mockSpan("test-tag-is-error");
25 | span.setTag(Tags.ERROR.getKey(), true);
26 |
27 | assertTrue(span.getEntity().isError());
28 | assertFalse(span.getEntity().isFault());
29 | }
30 |
31 | @Test
32 | @DisplayName("set FAULT tag on underlying Entity")
33 | void tagFault() {
34 | final AWSXRaySpan span = mockSpan("test-tag-is-fault");
35 | span.setTag(AWSXRayTags.FAULT.getKey(), true);
36 |
37 | assertFalse(span.getEntity().isError());
38 | assertTrue(span.getEntity().isFault());
39 | }
40 |
41 | @Test
42 | @DisplayName("set THROTTLE tag on underlying Entity")
43 | void tagThrottle() {
44 | final AWSXRaySpan span = mockSpan("test-tag-is-throttle");
45 | span.setTag(AWSXRayTags.THROTTLE.getKey(), true);
46 |
47 | assertFalse(span.getEntity().isFault());
48 | assertTrue(span.getEntity().isThrottle());
49 | }
50 |
51 | @Test
52 | @DisplayName("set IS_SAMPLED tag on underlying Entity")
53 | void tagSampled() {
54 | final AWSXRaySpan span = mockSpan("test-tag-is-sampled");
55 | assertTrue(span.getEntity() instanceof Segment);
56 |
57 | span.setTag(AWSXRayTags.IS_SAMPLED.getKey(), false);
58 | assertFalse(((Segment) span.getEntity()).isSampled());
59 |
60 | span.setTag(AWSXRayTags.IS_SAMPLED.getKey(), true);
61 | assertTrue(((Segment) span.getEntity()).isSampled());
62 | }
63 |
64 | @Test
65 | @DisplayName("set USER tag on underlying Entity")
66 | void tagUser() {
67 | final String expectedUser = "test.user@example.com";
68 |
69 | final AWSXRaySpan span = mockSpan("test-tag-user");
70 | span.setTag(AWSXRayTags.USER.getKey(), expectedUser);
71 |
72 | assertTrue(span.getEntity() instanceof Segment);
73 | assertEquals(expectedUser, ((Segment) span.getEntity()).getUser());
74 | }
75 |
76 | @Test
77 | @DisplayName("set ORIGIN tag on underlying Entity")
78 | void tagOrigin() {
79 | final String expectedOrigin = "AWS::EC2::Instance";
80 |
81 | final AWSXRaySpan span = mockSpan("test-tag-origin");
82 | span.setTag(AWSXRayTags.ORIGIN.getKey(), expectedOrigin);
83 |
84 | assertTrue(span.getEntity() instanceof Segment);
85 | assertEquals(expectedOrigin, ((Segment) span.getEntity()).getOrigin());
86 | }
87 |
88 | @Test
89 | @DisplayName("set PARENT_ID tag on underlying Entity")
90 | void tagParentId() {
91 | final String expectedParentId = "";
92 |
93 | final AWSXRaySpan span = mockSpan("test-tag-parent-id");
94 | span.setTag(AWSXRayTags.PARENT_ID.getKey(), expectedParentId);
95 |
96 | assertEquals(expectedParentId, span.getEntity().getParentId());
97 | }
98 |
99 | @Test
100 | @DisplayName("tag annotations values")
101 | void tagAnnotations() {
102 | testSpecialTags("annotations", s -> s.getEntity().getAnnotations());
103 | }
104 |
105 | @Test
106 | @DisplayName("tag AWS values")
107 | void tagAws() {
108 | testSpecialTags("aws", s -> s.getEntity().getAws());
109 | }
110 |
111 | @Test
112 | @DisplayName("tag HTTP values")
113 | void tagHTTP() {
114 | testSpecialTags("http", s -> s.getEntity().getHttp());
115 | }
116 |
117 | @Test
118 | @DisplayName("tag service values")
119 | void tagService() {
120 | testSpecialTags("service", s -> ((Segment) s.getEntity()).getService());
121 | }
122 |
123 | @Test
124 | @DisplayName("tag SQL values")
125 | void tagSQL() {
126 | testSpecialTags("sql", s -> s.getEntity().getSql());
127 | }
128 |
129 | @Test
130 | @DisplayName("tag metadata with empty key")
131 | void tagMetadataEmptyKey() {
132 | testMetadataTags("", AWSXRayMetadataNamespaces.DEFAULT, "");
133 | }
134 |
135 | @Test
136 | @DisplayName("tag metadata with no prefix, no namespace")
137 | void tagMetadataNoPrefixNoNamespace() {
138 | testMetadataTags("foo", AWSXRayMetadataNamespaces.DEFAULT, "foo");
139 | }
140 | @Test
141 | @DisplayName("tag metadata with no prefix, no namespace, key=metadata")
142 | void tagMetadataNoPrefixNoNamespaceAsNamespace() {
143 | testMetadataTags("metadata", AWSXRayMetadataNamespaces.DEFAULT, "metadata");
144 | }
145 |
146 | @Test
147 | @DisplayName("tag metadata with no namespace")
148 | void tagMetadataNoNamespace() {
149 | testMetadataTags("metadata.foo", AWSXRayMetadataNamespaces.DEFAULT, "foo");
150 | }
151 |
152 | @Test
153 | @DisplayName("tag metadata with no prefix")
154 | void tagMetadataNoPrefix() {
155 | testMetadataTags("bar.foo", "bar", "foo");
156 | }
157 |
158 | @Test
159 | @DisplayName("tag metadata with full name")
160 | void tagMetadataFullName() {
161 | testMetadataTags("metadata.bar.foo", "bar", "foo");
162 | }
163 |
164 | @Test
165 | @DisplayName("tag metadata with nested key")
166 | void tagMetadataNestedKey() {
167 | final AWSXRaySpan span = mockSpan("test-tag-metadata-nested-key");
168 | span.setTag("metadata.default.nested.is_test", true);
169 | span.setTag("metadata.default.nested.counter", 42);
170 | span.setTag("metadata.default.nested.service", "backend");
171 |
172 | @SuppressWarnings("unchecked")
173 | final Map targetMap = (Map) span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.DEFAULT).get("nested");
174 | assertEquals(true, targetMap.get("is_test"));
175 | assertEquals(42, targetMap.get("counter"));
176 | assertEquals("backend", targetMap.get("service"));
177 | }
178 |
179 | private void testSpecialTags(String keyPrefix, Function> getTargetMap) {
180 | final AWSXRaySpan span = mockSpan("test-tag-" + keyPrefix);
181 | span.setTag(keyPrefix + ".is_test", true);
182 | span.setTag(keyPrefix + ".counter", 42);
183 | span.setTag(keyPrefix + ".service", "backend");
184 |
185 | final Map targetMap = getTargetMap.apply(span);
186 | assertEquals(true, targetMap.get("is_test"));
187 | assertEquals(42, targetMap.get("counter"));
188 | assertEquals("backend", targetMap.get("service"));
189 | }
190 |
191 | private void testMetadataTags(String fullKey, String expectedNamespace, String expectedKey) {
192 | final AWSXRaySpan span = mockSpan("test-tag-metadata-" + fullKey.replaceAll("\\.", "-"));
193 | span.setTag(fullKey + "_boolean", true);
194 | span.setTag(fullKey + "_number", 42);
195 | span.setTag(fullKey + "_string", "backend");
196 |
197 | final Map targetMap = span.getEntity().getMetadata().get(expectedNamespace);
198 | assertEquals(true, targetMap.get(expectedKey + "_boolean"));
199 | assertEquals(42, targetMap.get(expectedKey + "_number"));
200 | assertEquals("backend", targetMap.get(expectedKey + "_string"));
201 | }
202 |
203 | private final String LOG_MESSAGE = "This is a log message";
204 | private final Map LOG_OBJECT = new HashMap<>();
205 | {
206 | LOG_OBJECT.put(Fields.MESSAGE, LOG_MESSAGE);
207 | LOG_OBJECT.put(Fields.EVENT, "timeout");
208 | }
209 |
210 | @Test
211 | @DisplayName("log raw event")
212 | void logRawEvent() {
213 | final AWSXRaySpan span = mockSpan("test-log-raw-event");
214 | span.log(LOG_MESSAGE);
215 |
216 | assertNotNull(span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG));
217 | assertEquals(1, span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG).size());
218 | }
219 |
220 | @Test
221 | @DisplayName("log raw event with timestamp")
222 | void logRawEventWithTimestamp() {
223 | final AWSXRaySpan span = mockSpan("test-log-raw-event-with-timestamp");
224 | span.log(Instant.now().toEpochMilli() * 1000, LOG_MESSAGE);
225 |
226 | assertNotNull(span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG));
227 | assertEquals(1, span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG).size());
228 | }
229 |
230 | @Test
231 | @DisplayName("log structured event")
232 | void logStructuredEvent() {
233 | final AWSXRaySpan span = mockSpan("test-log-structured-event");
234 | span.log(LOG_OBJECT);
235 |
236 | assertNotNull(span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG));
237 | assertEquals(1, span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG).size());
238 | }
239 |
240 | @Test
241 | @DisplayName("log structured event with timestamp")
242 | void logStructuredEventWithTimestamp() {
243 | final AWSXRaySpan span = mockSpan("test-log-structured-event");
244 | span.log(Instant.now().toEpochMilli() * 1000, LOG_OBJECT);
245 |
246 | assertNotNull(span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG));
247 | assertEquals(1, span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG).size());
248 | }
249 |
250 | @Test
251 | @DisplayName("log multiple events")
252 | void logMultipleEvents() throws InterruptedException {
253 | final AWSXRaySpan span = mockSpan("test-log-multiple-events");
254 |
255 | // Currently only support millisecond precision for logs, so
256 | // multiple logs can have the same timestamp which fails the test
257 | span.log(LOG_MESSAGE);
258 | Thread.sleep(5);
259 | span.log(LOG_MESSAGE);
260 | Thread.sleep(5);
261 | span.log(LOG_MESSAGE);
262 |
263 | assertEquals(3, span.getEntity().getMetadata().get(AWSXRayMetadataNamespaces.LOG).size());
264 | }
265 |
266 | @Test
267 | @DisplayName("set baggage")
268 | void setBaggage() {
269 | final AWSXRaySpan span = mockSpan("test-set-baggage");
270 | span.setBaggageItem("baggage_key", "value");
271 |
272 | assertNotNull(span.context().getBaggage());
273 | assertFalse(span.context().getBaggage().isEmpty());
274 | assertTrue(span.context().baggageItems().iterator().hasNext());
275 | assertEquals("value", span.getBaggageItem("baggage_key"));
276 | assertEquals("value", span.context().getBaggageItem("baggage_key"));
277 | }
278 |
279 | @Test
280 | @DisplayName("refuse to set operation name after construction")
281 | void setOperationName() {
282 | final AWSXRaySpan span = mockSpan("test-set-operation-name");
283 | assertThrows(Exception.class, () -> span.setOperationName("some-other-name"));
284 | }
285 |
286 | @Test
287 | @DisplayName("close the underlying X-Ray Entity on finish")
288 | void finish() {
289 |
290 | // Fake scope management here by setting the current trace Entity
291 | final AWSXRaySpan span = mockSpan("test-finish");
292 | awsxRayRecorder.setTraceEntity(span.getEntity());
293 | assertTrue(span.getEntity().isInProgress());
294 |
295 | span.finish();
296 |
297 | // X-Ray automatically unsets the current trace Entity on completion
298 | assertFalse(span.getEntity().isInProgress());
299 | assertNull(awsxRayRecorder.getTraceEntity());
300 | }
301 |
302 | @Test
303 | @DisplayName("ignore repeated calls to finish")
304 | void finishMultiple() {
305 |
306 | // Fake scope management here by setting the current trace entity
307 | final AWSXRaySpan span = mockSpan("test-finish");
308 | awsxRayRecorder.setTraceEntity(span.getEntity());
309 | assertTrue(span.getEntity().isInProgress());
310 |
311 | // If the second call to finish() proceeded, X-Ray would throw
312 | // an exception because the underlying Entity has already completed
313 | span.finish();
314 | span.finish();
315 |
316 | // X-Ray automatically unsets the current trace Entity on completion
317 | assertFalse(span.getEntity().isInProgress());
318 | assertNull(awsxRayRecorder.getTraceEntity());
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/src/test/java/io/opentracing/contrib/aws/xray/AWSXRayTestParent.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.AWSXRayRecorder;
4 | import com.amazonaws.xray.AWSXRayRecorderBuilder;
5 | import com.amazonaws.xray.entities.SegmentImpl;
6 | import com.amazonaws.xray.entities.TraceHeader;
7 | import com.amazonaws.xray.entities.TraceID;
8 | import io.opentracing.Tracer;
9 |
10 | import java.util.Collections;
11 |
12 | /**
13 | * @author ashley.mercer@skylightipv.com
14 | */
15 | abstract class AWSXRayTestParent {
16 |
17 | final AWSXRayRecorder awsxRayRecorder = AWSXRayRecorderBuilder.defaultRecorder();
18 |
19 | /**
20 | * Make sure this reference stays as pure {@link Tracer} since we don't
21 | * want to rely on implementation-specific details or return types.
22 | */
23 | final Tracer tracer = new AWSXRayTracer(awsxRayRecorder);
24 |
25 | /**
26 | * A sample {@link com.amazonaws.xray.entities.TraceHeader} value to use
27 | * for testing. NB we use all parts (root, parent, sampling decision) to
28 | * ensure that they all get propagated correctly.
29 | */
30 | final TraceHeader traceHeader = new TraceHeader(
31 | new TraceID(),
32 | "0f15eadda7879f1d",
33 | TraceHeader.SampleDecision.SAMPLED
34 | );
35 |
36 | /**
37 | * @param operationName the operation name
38 | */
39 | AWSXRaySpan mockSpan(String operationName) {
40 | return new AWSXRaySpan(
41 | new SegmentImpl(awsxRayRecorder, operationName),
42 | new AWSXRaySpanContext(Collections.emptyMap()));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/io/opentracing/contrib/aws/xray/AWSXRayTracerTests.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.contrib.aws.xray;
2 |
3 | import com.amazonaws.xray.entities.TraceHeader;
4 | import io.opentracing.Scope;
5 | import io.opentracing.SpanContext;
6 | import io.opentracing.propagation.*;
7 | import org.junit.jupiter.api.DisplayName;
8 | import org.junit.jupiter.api.Test;
9 |
10 | import java.util.Collections;
11 | import java.util.HashMap;
12 |
13 | import static org.junit.jupiter.api.Assertions.*;
14 |
15 | /**
16 | * @author ashley.mercer@skylightipv.com
17 | */
18 | class AWSXRayTracerTests extends AWSXRayTestParent {
19 |
20 | @Test
21 | @DisplayName("store a reference to the current span")
22 | void storeReference() {
23 | final Scope scope = tracer
24 | .buildSpan("simple-span")
25 | .startActive(true);
26 |
27 | assertNotNull(tracer.activeSpan());
28 | assertNotNull(tracer.scopeManager().active().span());
29 |
30 | assertEquals(tracer.activeSpan(), scope.span());
31 | assertEquals(tracer.scopeManager().active().span(), scope.span());
32 |
33 | scope.close();
34 | }
35 |
36 | @Test
37 | @DisplayName("succeed on SpanContext injection (empty)")
38 | void contextInjectEmpty() {
39 | final TextMap textMap = new TextMapAdapter(new HashMap<>());
40 | final SpanContext context = new AWSXRaySpanContext(new HashMap<>());
41 |
42 | tracer.inject(context, Format.Builtin.TEXT_MAP, textMap);
43 | assertFalse(textMap.iterator().hasNext());
44 | }
45 |
46 | @Test
47 | @DisplayName("succeed on SpanContext injection (TraceID)")
48 | void contextInjectTraceId() {
49 | final TextMap textMap = new TextMapAdapter(new HashMap<>());
50 | final SpanContext context = new AWSXRaySpanContext(Collections.singletonMap(
51 | TraceHeader.HEADER_KEY,
52 | traceHeader.toString()
53 | ));
54 |
55 | tracer.inject(context, Format.Builtin.TEXT_MAP, textMap);
56 | assertTrue(textMap.iterator().hasNext());
57 |
58 | final TraceHeader extractedTraceHeader = TraceHeader.fromString(textMap.iterator().next().getValue());
59 | assertEquals(traceHeader.getRootTraceId(), extractedTraceHeader.getRootTraceId());
60 | assertEquals(traceHeader.getParentId(), extractedTraceHeader.getParentId());
61 | assertEquals(traceHeader.getSampled(), extractedTraceHeader.getSampled());
62 | }
63 |
64 | @Test
65 | @DisplayName("succeed on SpanContext extraction (empty)")
66 | void contextExtractEmpty() {
67 | final TextMap textMap = new TextMapAdapter(new HashMap<>());
68 | final SpanContext context = tracer.extract(Format.Builtin.TEXT_MAP, textMap);
69 | assertFalse(context.baggageItems().iterator().hasNext());
70 | }
71 |
72 | @Test
73 | @DisplayName("succeed on SpanContext extraction (TraceID)")
74 | void contextExtractTraceId() {
75 | final TextMap textMap = new TextMapAdapter(Collections.singletonMap(
76 | TraceHeader.HEADER_KEY,
77 | traceHeader.toString()
78 | ));
79 |
80 | final SpanContext context = tracer.extract(Format.Builtin.TEXT_MAP, textMap);
81 | assertTrue(context.baggageItems().iterator().hasNext());
82 |
83 | final TraceHeader extractedTraceHeader = TraceHeader.fromString(context.baggageItems().iterator().next().getValue());
84 | assertEquals(traceHeader.getRootTraceId(), extractedTraceHeader.getRootTraceId());
85 | assertEquals(traceHeader.getParentId(), extractedTraceHeader.getParentId());
86 | assertEquals(traceHeader.getSampled(), extractedTraceHeader.getSampled());
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/test/java/io/opentracing/propagation/TextMapAdapter.java:
--------------------------------------------------------------------------------
1 | package io.opentracing.propagation;
2 |
3 | import java.util.Iterator;
4 | import java.util.Map;
5 |
6 | /**
7 | * @author ashley.mercer@skylightipv.com
8 | */
9 | public class TextMapAdapter implements TextMap {
10 |
11 | private final Map map;
12 |
13 | public TextMapAdapter(Map map) {
14 | this.map = map;
15 | }
16 |
17 | @Override
18 | public Iterator> iterator() {
19 | return map.entrySet().iterator();
20 | }
21 |
22 | @Override
23 | public void put(String key, String value) {
24 | map.put(key, value);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/test/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | [%level] %m%n
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/travis/publish.sh:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2016-2017 The OpenTracing Authors
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 | # in compliance with the License. You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software distributed under the License
10 | # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 | # or implied. See the License for the specific language governing permissions and limitations under
12 | # the License.
13 | #
14 |
15 | set -euo pipefail
16 | set -x
17 |
18 | build_started_by_tag() {
19 | if [ "${TRAVIS_TAG}" == "" ]; then
20 | echo "[Publishing] This build was not started by a tag, publishing snapshot"
21 | return 1
22 | else
23 | echo "[Publishing] This build was started by the tag ${TRAVIS_TAG}, publishing release"
24 | return 0
25 | fi
26 | }
27 |
28 | is_pull_request() {
29 | if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then
30 | echo "[Not Publishing] This is a Pull Request"
31 | return 0
32 | else
33 | echo "[Publishing] This is not a Pull Request"
34 | return 1
35 | fi
36 | }
37 |
38 | is_travis_branch_master_or_release() {
39 | # release to bintray si done from master or release-X.Y maintenance branches
40 | if [[ "${TRAVIS_BRANCH}" = master || "${TRAVIS_BRANCH}" =~ ^release-[0-9]+\.[0-9]+$ ]]; then
41 | echo "[Publishing] Travis branch is master"
42 | return 0
43 | else
44 | echo "[Not Publishing] Travis branch is not master"
45 | return 1
46 | fi
47 | }
48 |
49 | check_travis_branch_equals_travis_tag() {
50 | #Weird comparison comparing branch to tag because when you 'git push --tags'
51 | #the branch somehow becomes the tag value
52 | #github issue: https://github.com/travis-ci/travis-ci/issues/1675
53 | if [ "${TRAVIS_BRANCH}" != "${TRAVIS_TAG}" ]; then
54 | echo "Travis branch does not equal Travis tag, which it should, bailing out."
55 | echo " github issue: https://github.com/travis-ci/travis-ci/issues/1675"
56 | exit 1
57 | else
58 | echo "[Publishing] Branch (${TRAVIS_BRANCH}) same as Tag (${TRAVIS_TAG})"
59 | fi
60 | }
61 |
62 | check_release_tag() {
63 | tag="${TRAVIS_TAG}"
64 | if [[ "$tag" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(\.RC[[:digit:]]+)?$ ]]; then
65 | echo "Build started by version tag $tag. During the release process tags like this"
66 | echo "are created by the 'release' Maven plugin. Nothing to do here."
67 | exit 0
68 | elif [[ ! "$tag" =~ ^release-[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(\.RC[[:digit:]]+)?$ ]]; then
69 | echo "You must specify a tag of the format 'release-0.0.0' or 'release-0.0.0.RC0' to release this project."
70 | echo "The provided tag ${tag} doesn't match that. Aborting."
71 | exit 1
72 | fi
73 | }
74 |
75 | is_release_commit() {
76 | project_version=$(./mvnw help:evaluate -N -Dexpression=project.version|grep -v '\[')
77 | if [[ "$project_version" =~ ^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(\.RC[[:digit:]]+)?$ ]]; then
78 | echo "Build started by release commit $project_version. Will synchronize to maven central."
79 | return 0
80 | else
81 | return 1
82 | fi
83 | }
84 |
85 | release_version() {
86 | echo "${TRAVIS_TAG}" | sed 's/^release-//'
87 | }
88 |
89 | safe_checkout_master() {
90 | # We need to be on a branch for release:perform to be able to create commits,
91 | # and we want that branch to be master or release-X.Y, which has been checked before.
92 | # But we also want to make sure that we build and release exactly the tagged version, so we verify that the remote
93 | # branch is where our tag is.
94 | checkoutBranch=release-`release_version | sed 's/.[[:digit:]]\+$//'`
95 | if ! git ls-remote --exit-code --heads origin "$checkoutBranch" ; then
96 | checkoutBranch=master
97 | fi
98 | git checkout -B "${checkoutBranch}"
99 | git fetch origin "${checkoutBranch}":origin/"${checkoutBranch}"
100 | commit_local_master="$(git show --pretty='format:%H' ${checkoutBranch})"
101 | commit_remote_master="$(git show --pretty='format:%H' origin/${checkoutBranch})"
102 | if [ "$commit_local_master" != "$commit_remote_master" ]; then
103 | echo "${checkoutBranch} on remote 'origin' has commits since the version under release, aborting"
104 | exit 1
105 | fi
106 | }
107 |
108 | #----------------------
109 | # MAIN
110 | #----------------------
111 |
112 | if ! is_pull_request && build_started_by_tag; then
113 | check_travis_branch_equals_travis_tag
114 | check_release_tag
115 | fi
116 |
117 | ./mvnw clean install -nsu
118 |
119 | # If we are on a pull request, our only job is to run tests, which happened above via ./mvnw install
120 | if is_pull_request; then
121 | true
122 | # If we are on master, we will deploy the latest snapshot or release version
123 | # - If a release commit fails to deploy for a transient reason, delete the broken version from bintray and click rebuild
124 | elif is_travis_branch_master_or_release; then
125 | ./mvnw --batch-mode -s ./.settings.xml -Prelease -nsu -DskipTests deploy
126 |
127 | # If the deployment succeeded, sync it to Maven Central. Note: this needs to be done once per project, not module, hence -N
128 | if is_release_commit; then
129 | ./mvnw --batch-mode -s ./.settings.xml -nsu -N io.zipkin.centralsync-maven-plugin:centralsync-maven-plugin:sync
130 | fi
131 |
132 | # If we are on a release tag, the following will update any version references and push a version tag for deployment.
133 | elif build_started_by_tag; then
134 | safe_checkout_master
135 | ./mvnw --batch-mode -s ./.settings.xml -Prelease -nsu -DreleaseVersion="$(release_version)" -Darguments="-DskipTests" release:prepare
136 | fi
137 |
138 |
--------------------------------------------------------------------------------