├── .editorconfig
├── .github
├── maven-cd-settings.xml
├── maven-ci-settings.xml
└── workflows
│ ├── ci-4.x.yml
│ ├── ci-5.x-stable.yml
│ ├── ci-5.x.yml
│ ├── ci-matrix-5.x.yml
│ ├── ci.yml
│ └── deploy.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.md
├── README.adoc
├── pom.xml
├── vertx-opentelemetry
├── pom.xml
└── src
│ ├── main
│ ├── asciidoc
│ │ └── index.adoc
│ ├── java
│ │ ├── examples
│ │ │ └── OpenTelemetryExamples.java
│ │ ├── io
│ │ │ └── vertx
│ │ │ │ └── tracing
│ │ │ │ └── opentelemetry
│ │ │ │ ├── HeadersPropagatorGetter.java
│ │ │ │ ├── HeadersPropagatorSetter.java
│ │ │ │ ├── OpenTelemetryOptions.java
│ │ │ │ ├── OpenTelemetryTracer.java
│ │ │ │ ├── OpenTelemetryTracingFactory.java
│ │ │ │ ├── Operation.java
│ │ │ │ ├── VertxContextStorageProvider.java
│ │ │ │ └── package-info.java
│ │ └── module-info.java
│ └── resources
│ │ └── META-INF
│ │ └── services
│ │ ├── io.opentelemetry.context.ContextStorageProvider
│ │ └── io.vertx.core.spi.VertxServiceProvider
│ └── test
│ └── java
│ └── io
│ └── vertx
│ └── tests
│ └── opentelemetry
│ ├── EventBusTest.java
│ ├── HeadersPropagatorGetterTest.java
│ ├── HeadersPropagatorSetterTest.java
│ ├── OpenTelemetryIntegrationTest.java
│ ├── OpenTelemetryTracingFactoryTest.java
│ └── SqlClientTest.java
├── vertx-opentracing
├── pom.xml
└── src
│ ├── main
│ ├── asciidoc
│ │ └── index.adoc
│ ├── java
│ │ ├── examples
│ │ │ └── OpenTracingExamples.java
│ │ └── io
│ │ │ └── vertx
│ │ │ └── tracing
│ │ │ └── opentracing
│ │ │ ├── OpenTracingOptions.java
│ │ │ ├── OpenTracingTracer.java
│ │ │ ├── OpenTracingTracerFactory.java
│ │ │ ├── OpenTracingUtil.java
│ │ │ └── package-info.java
│ └── resources
│ │ └── META-INF
│ │ └── services
│ │ └── io.vertx.core.spi.VertxServiceProvider
│ └── test
│ └── java
│ └── io
│ └── vertx
│ └── tests
│ └── opentracing
│ ├── EventBusTest.java
│ ├── HttpTest.java
│ ├── OpenTracingTest.java
│ ├── OpenTracingUtilTest.java
│ ├── SqlClientTest.java
│ └── it
│ ├── BasicTracingIT.java
│ ├── CustomTracingIT.java
│ ├── ServerVerticle.java
│ └── jaegercontainer
│ └── JaegerContainerAllIn.java
└── vertx-zipkin
├── pom.xml
└── src
├── main
├── asciidoc
│ └── index.adoc
├── generated
│ └── io
│ │ └── vertx
│ │ └── tracing
│ │ └── zipkin
│ │ ├── HttpSenderOptionsConverter.java
│ │ └── ZipkinTracingOptionsConverter.java
├── java
│ ├── examples
│ │ └── ZipkinTracingExamples.java
│ ├── io
│ │ └── vertx
│ │ │ └── tracing
│ │ │ └── zipkin
│ │ │ ├── HttpSenderOptions.java
│ │ │ ├── VertxSender.java
│ │ │ ├── ZipkinTracer.java
│ │ │ ├── ZipkinTracerFactory.java
│ │ │ ├── ZipkinTracingOptions.java
│ │ │ ├── impl
│ │ │ └── HttpUtils.java
│ │ │ └── package-info.java
│ └── module-info.java
└── resources
│ └── META-INF
│ └── services
│ └── io.vertx.core.spi.VertxServiceProvider
└── test
└── java
└── io
└── vertx
└── tests
└── zipkin
├── EventBusTest.java
├── SqlClientTest.java
├── VertxSenderTest.java
├── ZipkinBaseTest.java
├── ZipkinGenericPropagationTest.java
├── ZipkinHttpClientITTest.java
├── ZipkinHttpServerITTest.java
├── ZipkinHttpTest.java
├── ZipkinTracerUtilTest.java
└── ZipkinTracingOptionsTest.java
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | trim_trailing_whitespace = true
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.github/maven-cd-settings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | false
20 |
21 |
22 |
23 | vertx-snapshots-repository
24 | ${env.VERTX_NEXUS_USERNAME}
25 | ${env.VERTX_NEXUS_PASSWORD}
26 |
27 |
28 |
29 |
30 |
31 | google-mirror
32 |
33 | true
34 |
35 |
36 |
37 | google-maven-central
38 | GCS Maven Central mirror EU
39 | https://maven-central.storage-download.googleapis.com/maven2/
40 |
41 | true
42 |
43 |
44 | false
45 |
46 |
47 |
48 |
49 |
50 | google-maven-central
51 | GCS Maven Central mirror
52 | https://maven-central.storage-download.googleapis.com/maven2/
53 |
54 | true
55 |
56 |
57 | false
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/.github/maven-ci-settings.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | false
20 |
21 |
22 |
23 | google-mirror
24 |
25 | true
26 |
27 |
28 |
29 | google-maven-central
30 | GCS Maven Central mirror EU
31 | https://maven-central.storage-download.googleapis.com/maven2/
32 |
33 | true
34 |
35 |
36 | false
37 |
38 |
39 |
40 |
41 |
42 | google-maven-central
43 | GCS Maven Central mirror
44 | https://maven-central.storage-download.googleapis.com/maven2/
45 |
46 | true
47 |
48 |
49 | false
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/.github/workflows/ci-4.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-tracing (4.x)
2 | on:
3 | schedule:
4 | - cron: '0 4 * * *'
5 | jobs:
6 | CI:
7 | strategy:
8 | matrix:
9 | include:
10 | - os: ubuntu-latest
11 | jdk: 8
12 | - os: ubuntu-latest
13 | jdk: 17
14 | uses: ./.github/workflows/ci.yml
15 | with:
16 | branch: 4.x
17 | jdk: ${{ matrix.jdk }}
18 | os: ${{ matrix.os }}
19 | secrets: inherit
20 | Deploy:
21 | if: ${{ github.repository_owner == 'eclipse-vertx' && (github.event_name == 'push' || github.event_name == 'schedule') }}
22 | needs: CI
23 | uses: ./.github/workflows/deploy.yml
24 | with:
25 | branch: 4.x
26 | jdk: 8
27 | secrets: inherit
28 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x-stable.yml:
--------------------------------------------------------------------------------
1 | name: vertx-tracing (5.x-stable)
2 | on:
3 | push:
4 | branches:
5 | - '5.[0-9]+'
6 | pull_request:
7 | branches:
8 | - '5.[0-9]+'
9 | schedule:
10 | - cron: '0 6 * * *'
11 | jobs:
12 | CI-CD:
13 | uses: ./.github/workflows/ci-matrix-5.x.yml
14 | secrets: inherit
15 | with:
16 | branch: ${{ github.event_name == 'schedule' && vars.VERTX_5_STABLE_BRANCH || github.event.pull_request.head.sha || github.ref_name }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci-5.x.yml:
--------------------------------------------------------------------------------
1 | name: vertx-tracing (5.x)
2 | on:
3 | push:
4 | branches:
5 | - master
6 | pull_request:
7 | branches:
8 | - master
9 | schedule:
10 | - cron: '0 5 * * *'
11 | jobs:
12 | CI-CD:
13 | uses: ./.github/workflows/ci-matrix-5.x.yml
14 | secrets: inherit
15 | with:
16 | branch: ${{ github.event.pull_request.head.sha || github.ref_name }}
17 |
--------------------------------------------------------------------------------
/.github/workflows/ci-matrix-5.x.yml:
--------------------------------------------------------------------------------
1 | name: CI matrix (5.x)
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jobs:
9 | CI:
10 | strategy:
11 | matrix:
12 | include:
13 | - os: ubuntu-latest
14 | jdk: 11
15 | - os: ubuntu-latest
16 | jdk: 21
17 | uses: ./.github/workflows/ci.yml
18 | with:
19 | branch: ${{ inputs.branch }}
20 | jdk: ${{ matrix.jdk }}
21 | os: ${{ matrix.os }}
22 | secrets: inherit
23 | Deploy:
24 | if: ${{ github.repository_owner == 'eclipse-vertx' && (github.event_name == 'push' || github.event_name == 'schedule') }}
25 | needs: CI
26 | uses: ./.github/workflows/deploy.yml
27 | with:
28 | branch: ${{ inputs.branch }}
29 | jdk: 11
30 | secrets: inherit
31 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jdk:
9 | default: 8
10 | type: string
11 | os:
12 | default: ubuntu-latest
13 | type: string
14 | jobs:
15 | Test:
16 | name: Run tests
17 | runs-on: ${{ inputs.os }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | with:
22 | ref: ${{ inputs.branch }}
23 | - name: Install JDK
24 | uses: actions/setup-java@v2
25 | with:
26 | java-version: ${{ inputs.jdk }}
27 | distribution: temurin
28 | - name: Run tests
29 | run: mvn -s .github/maven-ci-settings.xml -q clean verify -B
30 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy
2 | on:
3 | workflow_call:
4 | inputs:
5 | branch:
6 | required: true
7 | type: string
8 | jdk:
9 | default: 8
10 | type: string
11 | jobs:
12 | Deploy:
13 | name: Deploy to OSSRH
14 | runs-on: ubuntu-latest
15 | env:
16 | VERTX_NEXUS_USERNAME: ${{ secrets.VERTX_NEXUS_USERNAME }}
17 | VERTX_NEXUS_PASSWORD: ${{ secrets.VERTX_NEXUS_PASSWORD }}
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | with:
22 | ref: ${{ inputs.branch }}
23 | - name: Install JDK
24 | uses: actions/setup-java@v2
25 | with:
26 | java-version: ${{ inputs.jdk }}
27 | distribution: temurin
28 | - name: Get project version
29 | run: echo "PROJECT_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout | grep -v '\[')" >> $GITHUB_ENV
30 | - name: Maven deploy
31 | if: ${{ endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
32 | run: mvn deploy -s .github/maven-cd-settings.xml -DskipTests -B
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vertx
2 | .DS_Store
3 | .gradle
4 | .idea
5 | .classpath
6 | .project
7 | .settings
8 | .yardoc
9 | .yardopts
10 | build
11 | target
12 | out
13 | *.iml
14 | *.ipr
15 | *.iws
16 | test-output
17 | Scratch.java
18 | ScratchTest.java
19 | test-results
20 | test-tmp
21 | *.class
22 | ScratchPad.java
23 | src/main/resources/ext-js/*.js
24 | src/main/java/io/vertx/java/**/*.java
25 | *.swp
26 |
27 | # VS Code
28 | .factorypath
29 | .vscode
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Eclipse Vert.x Community Contributing Guide
2 |
3 | This document describes a very simple process suitable for most projects in the Vert.x ecosystem. Projects are encouraged to adopt this whether they are hosted in the Vert.x Organization or not.
4 |
5 | The goal of this document is to create a contribution process that:
6 |
7 | * Encourages new contributions.
8 | * Encourages contributors to remain involved.
9 | * Avoids unnecessary processes and bureaucracy whenever possible.
10 | * Creates a transparent decision-making process which makes it clear how contributors can be involved in decision-making.
11 |
12 | ## Vocabulary
13 |
14 | * A **Contributor** is any individual creating or commenting on an issue or pull request.
15 | * A **Committer** is a subset of contributors who have been given write access to the repository.
16 |
17 |
18 | # Logging Issues
19 |
20 | Log an issue for any problem you might have. When in doubt, log an issue, any additional policies about what to include will be provided in the responses. The only exception is security disclosures which should be sent [privately](vertx-enquiries@googlegroups.com).
21 |
22 | Committers may direct you to another repository, ask for additional clarifications, and add appropriate info before the issue is addressed.
23 |
24 | For questions that are not an issue with the code, e.g.: questions related to usage of the project, it is recommended that they are sent to the [community group](https://groups.google.com/forum/#!forum/vertx). This exposes the question to the whole community, which increases the chance of getting faster responses than just from contributors and committers.
25 |
26 | # Contributions
27 |
28 | Any change that would roughly be more than 10 lines (non-trivial change) to resources in this repository must be through pull requests. This applies to all changes to documentation, code, binary files, etc. Even long term committers must use pull requests. Changes less than 10 lines or so (e.g.: correcting typos, small changes to configuration and such-like) can be make directly on a master branch. For a more detailed development process, please consult the article [Development Process](https://github.com/vert-x3/wiki/wiki/Development-Process) in the [wiki](https://github.com/vert-x3/wiki/wiki).
29 |
30 | No pull request can be merged without being reviewed.
31 |
32 | It is expected that all contributors to Eclipse Vert.x organization sign the [Eclipse Contributor Agreement](http://www.eclipse.org/legal/ECA.php). In order to sign the ECA a contributor is required to have an Eclipse Foundation user id and read and digitally sign the following [document](http://www.eclipse.org/contribute/cla). Digitally signing is as simple as read the document and submit the "I agree" button.
33 |
34 | There is an additional "sign off" process for contributions to land. All commits to code in the Eclipse Vert.x organization **MUST** be signed off. This is done when committing from git passing the extra argument `-s` e.g.:
35 |
36 | ```
37 | git commit -s -m "Shave the yak some more"
38 | ```
39 |
40 | For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that contributors in other time zones have time to review. Consideration should also be given to weekends and other holiday periods to ensure active committers all have reasonable time to become involved in the discussion and review process if they wish.
41 |
42 | The default for each contribution is that it is accepted once no committer has an objection. During review committers may also request that a specific contributor who is most versed in a particular area gives a "LGTM" before the PR can be merged.
43 |
44 | For more info, see [here](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git). Once all issues brought by committers are addressed it can be landed by any committer.
45 |
46 | In the case of an objection being raised in a pull request by another committer, all involved committers should seek to arrive at a consensus by way of addressing concerns being expressed by discussion, compromise on the proposed change, or withdrawal of the proposed change.
47 |
48 | If a contribution is controversial and committers cannot agree about how to get it to land or if it should land then it should be escalated to the [committers group](https://groups.google.com/forum/#!forum/vertx-committers). Members of the developers group should regularly discuss pending contributions in order to find a resolution. It is expected that only a small minority of issues be brought to the group for resolution and that discussion and compromise among committers be the default resolution mechanism.
49 |
50 | # Copyright notice headers
51 |
52 | It is important that all source code files have correct copyright notice headers.
53 |
54 | You can opt for the general-purpose form:
55 |
56 | /*
57 | * Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
58 | *
59 | * This program and the accompanying materials are made available under the
60 | * terms of the Eclipse Public License 2.0 which is available at
61 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
62 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
63 | *
64 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
65 | */
66 |
67 | Or you may choose to explicitly mention all contributors in the declaration, like in:
68 |
69 | /*
70 | * Copyright (c) {date} {owner}[ and others]
71 | *
72 | * This program and the accompanying materials are made available under the
73 | * terms of the Eclipse Public License 2.0 which is available at
74 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
75 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
76 | *
77 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
78 | */
79 |
80 | Check https://www.eclipse.org/projects/handbook/#legaldoc
81 |
82 | # Becoming a Contributor
83 |
84 | Becoming a contributor to the project should be an easy step. In order to reduce that barrier new contributors should look for issues tagged as:
85 |
86 | * `Quick Win`
87 | * `Help Wanted`
88 |
89 | These tags should be applied to issues that are relatively simple to fix but not critical to be handled by the current group of committers. Once you pick such an issue to work on a discussion with a committer should happen on the issue itself and the committer should guide you with the mentoring/onboarding process.
90 |
91 |
92 | # Becoming a Committer
93 |
94 | All contributors who land a non-trivial contribution should be on-boarded in a timely manner, and added as a committer, and be given write access to the repository.
95 |
96 | Committers are expected to follow this policy and continue to send pull requests, go through proper review, and have other committers merge their pull requests.
97 |
98 |
99 | # Mentoring / Onboarding
100 |
101 | Committers should help mentor/on-board new contributors. The workflow should roughly follow this items:
102 |
103 | * suggest the new contributor to join the [development group](https://groups.google.com/forum/#!forum/vertx-dev)
104 | * ask for participation on github issue discussions
105 | * suggest to keep in touch with on the dev group on periodic way (e.g.: weekly)
106 | * help finding a good mentor if one is not well versed in a specific area of the project
107 | * suggest progress reporting
108 | * assist with creating an Eclipse user id and ECA sign
109 |
110 |
111 | # Technical Conflict Process
112 |
113 | The Vert.x project uses a "consensus seeking" process for issues that are escalated to the [committers group](https://github.com/orgs/vert-x3/people). The group tries to find a resolution that has no open objections among the members. If a consensus cannot be reached that has no objections then the Project Lead should decide on which approach to take. It is also expected that the majority of decisions made by the group are via a consensus seeking process and that voting is only used as a last-resort.
114 |
115 | Resolution may involve returning the issue to committers with suggestions on how to move forward towards a consensus. It is not expected that a meeting of the group will resolve all issues on its agenda during that meeting and may prefer to continue the discussion happening among the committers.
116 |
117 | Members can be added to the group at any time. Any committer can nominate another committer to the group and the group uses its standard consensus seeking process to evaluate whether or not to add this new member. Members who do not participate consistently at the level of a majority of the other members are expected to resign.
118 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = Tracing for Vert.x 4
2 |
3 | image:https://github.com/eclipse-vertx/vertx-tracing/actions/workflows/ci-5.x.yml/badge.svg["Build Status (5.x)",link="https://github.com/eclipse-vertx/vertx-tracing/actions/workflows/ci-5.x.yml"]
4 | image:https://github.com/eclipse-vertx/vertx-tracing/actions/workflows/ci-4.x.yml/badge.svg["Build Status (4.x)",link="https://github.com/eclipse-vertx/vertx-tracing/actions/workflows/ci-4.x.yml"]
5 | image:https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat["GitHub license",link="http://www.apache.org/licenses/LICENSE-2.0"]
6 |
7 | This is the repository for Vert.x tracing SPI implementations.
8 |
9 | == OpenTelemetry
10 |
11 | Provides integration https://opentelemetry.io/[OpenTelemetry]
12 |
13 | - https://vertx.io/docs/vertx-opentelemetry/java/[website docs]
14 |
15 | == OpenTracing
16 |
17 | Provides integration https://opentracing.io[OpenTracing]
18 |
19 | - https://vertx.io/docs/vertx-opentracing/java/[website docs]
20 |
21 | == Zipkin Brave
22 |
23 | Provides integration with https://github.com/openzipkin/brave[Zipkin Brave]
24 |
25 | - https://vertx.io/docs/vertx-zipkin/java/[website docs]
26 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | 4.0.0
19 |
20 |
21 | io.vertx
22 | vertx5-parent
23 | 12
24 |
25 |
26 | vertx-tracing-parent
27 | 5.1.0-SNAPSHOT
28 | pom
29 |
30 | Vert.x Tracing Parent
31 |
32 |
33 | scm:git:git@github.com:eclipse-vertx/vertx-tracing.git
34 | scm:git:git@github.com:eclipse-vertx/vertx-tracing.git
35 | git@github.com:eclipse-vertx/vertx-tracing.git
36 |
37 |
38 |
39 | 5.9.1
40 | 1.17.6
41 | false
42 |
43 |
44 |
45 |
46 |
47 | io.vertx
48 | vertx-dependencies
49 | ${project.version}
50 | pom
51 | import
52 |
53 |
54 |
55 |
56 |
57 |
58 | io.vertx
59 | vertx-codegen-api
60 | true
61 |
62 |
63 | io.vertx
64 | vertx-codegen-json
65 | true
66 |
67 |
68 | io.vertx
69 | vertx-docgen-api
70 | true
71 |
72 |
73 | io.vertx
74 | vertx-core
75 |
76 |
77 |
78 | io.vertx
79 | vertx-sql-client
80 | test
81 |
82 |
83 | io.vertx
84 | vertx-pg-client
85 | test
86 |
87 |
88 | org.testcontainers
89 | postgresql
90 | ${testcontainers.version}
91 | test
92 |
93 |
94 | org.testcontainers
95 | testcontainers
96 | ${testcontainers.version}
97 | test
98 |
99 |
100 |
101 |
102 |
103 | vertx-zipkin
104 | vertx-opentracing
105 | vertx-opentelemetry
106 |
107 |
108 |
109 |
110 |
111 |
112 | maven-compiler-plugin
113 |
114 |
115 | default-compile
116 |
117 |
118 |
119 | io.vertx
120 | vertx-codegen
121 | processor
122 |
123 |
124 | io.vertx
125 | vertx-docgen-processor
126 | processor
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | maven-assembly-plugin
138 |
139 |
140 | package-docs
141 |
142 | single
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 | 4.0.0
21 |
22 |
23 | io.vertx
24 | vertx-tracing-parent
25 | 5.1.0-SNAPSHOT
26 |
27 |
28 | vertx-opentelemetry
29 |
30 | Vert.x OpenTelemetry
31 |
32 |
33 | 1.46.0
34 | 3.22.0
35 | 4.5.1
36 | 4.2.0
37 |
38 |
39 |
40 |
41 |
42 | io.opentelemetry
43 | opentelemetry-bom
44 | ${opentelemetry.version}
45 | pom
46 | import
47 |
48 |
49 |
50 |
51 |
52 |
53 | io.opentelemetry
54 | opentelemetry-api
55 |
56 |
57 | io.opentelemetry
58 | opentelemetry-sdk
59 |
60 |
61 | io.opentelemetry
62 | opentelemetry-sdk-trace
63 |
64 |
65 |
66 | io.opentelemetry
67 | opentelemetry-sdk-testing
68 | test
69 |
70 |
71 | org.junit.jupiter
72 | junit-jupiter
73 | ${junit.jupiter.version}
74 | test
75 |
76 |
77 | io.vertx
78 | vertx-junit5
79 | test
80 |
81 |
82 | org.assertj
83 | assertj-core
84 | ${assertj.version}
85 | test
86 |
87 |
88 | org.mockito
89 | mockito-junit-jupiter
90 | ${mokito.junit.jupiter.version}
91 | test
92 |
93 |
94 | org.awaitility
95 | awaitility
96 | ${awaitility.version}
97 | test
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/asciidoc/index.adoc:
--------------------------------------------------------------------------------
1 | = Vertx OpenTelemetry
2 |
3 | Vert.x integration with OpenTelemetry.
4 |
5 | [source,$lang]
6 | ----
7 | {@link examples.OpenTelemetryExamples#ex1}
8 | ----
9 |
10 | You can also pass a custom `OpenTelemetry` allowing for greater control
11 | over the configuration.
12 |
13 | [source,$lang]
14 | ----
15 | {@link examples.OpenTelemetryExamples#ex2}
16 | ----
17 |
18 | If you only add this library, it will give you access to OpenTelemetry API with a default `noop` Tracer,
19 | which gives dummy values (all zeroes) for trace and span ids. The OpenTelemetry SDK is needed to get proper values.
20 |
21 | [source,$lang]
22 | ----
23 | {@link examples.OpenTelemetryExamples#ex7}
24 | ----
25 |
26 | [NOTE]
27 | ====
28 | This project provides an OpenTelemetry `ContextStorageProvider` that uses the Vert.x {@link io.vertx.core.Context} when invoked on a Vert.x thread.
29 | Otherwise, it fallbacks to the default storage.
30 |
31 | If several `ContextStorageProvider` implementations are present on the classpath, you can force OpenTelemetry to select the Vert.x one:
32 |
33 | [source]
34 | ----
35 | -Dio.opentelemetry.context.contextStorageProvider=io.vertx.tracing.opentelemetry.VertxContextStorageProvider
36 | ----
37 | ====
38 |
39 | == Tracing policy
40 |
41 | The tracing policy defines the behavior of a component when tracing is enabled:
42 |
43 | - {@link io.vertx.core.tracing.TracingPolicy#PROPAGATE}: the component reports a span in the active trace
44 | - {@link io.vertx.core.tracing.TracingPolicy#ALWAYS}: the component reports a span in the active trace or creates a new active trace
45 | - {@link io.vertx.core.tracing.TracingPolicy#IGNORE}: the component will not be involved in any trace.
46 |
47 | The tracing policy is usually configured in the component options.
48 |
49 | == HTTP tracing
50 |
51 | The Vert.x HTTP server and client reports span around HTTP requests:
52 |
53 | - `name`: the HTTP method
54 | - tags
55 | - `http.method`: the HTTP method
56 | - `http.url`: the request URL
57 | - `http.status_code`: the HTTP status code (as `String`)
58 |
59 | The default HTTP server tracing policy is `ALWAYS`, you can configure the policy with {@link io.vertx.core.http.HttpServerOptions#setTracingPolicy}
60 |
61 | [source,$lang]
62 | ----
63 | {@link examples.OpenTelemetryExamples#ex3}
64 | ----
65 |
66 | The default HTTP client tracing policy is `PROPAGATE`, you can configure the policy with {@link io.vertx.core.http.HttpClientOptions#setTracingPolicy}
67 |
68 | [source,$lang]
69 | ----
70 | {@link examples.OpenTelemetryExamples#ex4}
71 | ----
72 |
73 | == EventBus tracing
74 |
75 | The Vert.x EventBus reports spans around message exchanges.
76 |
77 | The default sending policy is `PROPAGATE`, you can configure the policy with {@link io.vertx.core.eventbus.DeliveryOptions#setTracingPolicy}.
78 |
79 | [source,$lang]
80 | ----
81 | {@link examples.OpenTelemetryExamples#ex6}
82 | ----
83 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/examples/OpenTelemetryExamples.java:
--------------------------------------------------------------------------------
1 | package examples;
2 |
3 | import io.opentelemetry.api.OpenTelemetry;
4 | import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
5 | import io.opentelemetry.context.propagation.ContextPropagators;
6 | import io.opentelemetry.sdk.OpenTelemetrySdk;
7 | import io.opentelemetry.sdk.trace.*;
8 | import io.vertx.core.Vertx;
9 | import io.vertx.core.VertxOptions;
10 | import io.vertx.core.eventbus.DeliveryOptions;
11 | import io.vertx.core.http.HttpClient;
12 | import io.vertx.core.http.HttpClientOptions;
13 | import io.vertx.core.http.HttpServer;
14 | import io.vertx.core.http.HttpServerOptions;
15 | import io.vertx.core.tracing.TracingPolicy;
16 | import io.vertx.docgen.Source;
17 | import io.vertx.tracing.opentelemetry.OpenTelemetryOptions;
18 |
19 | @Source
20 | public class OpenTelemetryExamples {
21 |
22 | public void ex1() {
23 | Vertx vertx = Vertx.vertx(new VertxOptions()
24 | .setTracingOptions(
25 | new OpenTelemetryOptions()
26 | )
27 | );
28 | }
29 |
30 | public void ex2(OpenTelemetry openTelemetry) {
31 | Vertx vertx = Vertx.vertx(new VertxOptions()
32 | .setTracingOptions(
33 | new OpenTelemetryOptions(openTelemetry)
34 | )
35 | );
36 | }
37 |
38 | public void ex3(Vertx vertx) {
39 | HttpServer server = vertx.createHttpServer(new HttpServerOptions()
40 | .setTracingPolicy(TracingPolicy.IGNORE)
41 | );
42 | }
43 |
44 | public void ex4(Vertx vertx) {
45 | HttpClient client = vertx.createHttpClient(new HttpClientOptions()
46 | .setTracingPolicy(TracingPolicy.IGNORE)
47 | );
48 | }
49 |
50 | // public void ex5(Tracer tracer) {
51 | // Span span = tracer.spanBuilder("my-operation")
52 | // .setAttribute("some-key", "some-value")
53 | // .startSpan();
54 | // OpenTelemetryUtil.setSpan(span);
55 | // // Do something, e.g. client request
56 | // span.end();
57 | // }
58 |
59 | public void ex6(Vertx vertx) {
60 | DeliveryOptions options = new DeliveryOptions().setTracingPolicy(TracingPolicy.ALWAYS);
61 | vertx.eventBus().send("the-address", "foo", options);
62 | }
63 |
64 | public void ex7(VertxOptions vertxOptions) {
65 | SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder().build();
66 | OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
67 | .setTracerProvider(sdkTracerProvider)
68 | .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
69 | .buildAndRegisterGlobal();
70 |
71 | vertxOptions.setTracingOptions(new OpenTelemetryOptions(openTelemetry));
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/HeadersPropagatorGetter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentelemetry;
12 |
13 | import io.opentelemetry.context.propagation.TextMapGetter;
14 |
15 | import java.util.HashSet;
16 | import java.util.Map.Entry;
17 | import java.util.Set;
18 |
19 | public final class HeadersPropagatorGetter implements TextMapGetter>> {
20 |
21 | @Override
22 | public Iterable keys(final Iterable> carrier) {
23 | Set keys = new HashSet<>();
24 | for (Entry entry : carrier) {
25 | keys.add(entry.getKey());
26 | }
27 | return keys;
28 | }
29 |
30 | @Override
31 | public String get(final Iterable> carrier, final String key) {
32 | if (carrier == null) {
33 | return null;
34 | }
35 | for (Entry entry : carrier) {
36 | if (entry.getKey().equalsIgnoreCase(key)) {
37 | return entry.getValue();
38 | }
39 | }
40 | return null;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/HeadersPropagatorSetter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentelemetry;
12 |
13 | import io.opentelemetry.context.propagation.TextMapSetter;
14 |
15 | import java.util.function.BiConsumer;
16 |
17 | public final class HeadersPropagatorSetter implements TextMapSetter> {
18 |
19 | @Override
20 | public void set(final BiConsumer carrier, final String key, final String value) {
21 | if (carrier == null) {
22 | return;
23 | }
24 | carrier.accept(key, value);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/OpenTelemetryOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentelemetry;
12 |
13 | import io.opentelemetry.api.GlobalOpenTelemetry;
14 | import io.opentelemetry.api.OpenTelemetry;
15 | import io.vertx.codegen.annotations.DataObject;
16 | import io.vertx.core.json.JsonObject;
17 | import io.vertx.core.spi.tracing.VertxTracer;
18 | import io.vertx.core.tracing.TracingOptions;
19 |
20 | @DataObject
21 | public class OpenTelemetryOptions extends TracingOptions {
22 |
23 | private OpenTelemetry openTelemetry;
24 |
25 | public OpenTelemetryOptions(OpenTelemetry openTelemetry) {
26 | this.openTelemetry = openTelemetry;
27 | }
28 |
29 | public OpenTelemetryOptions() {
30 | }
31 |
32 | public OpenTelemetryOptions(JsonObject json) {
33 | super(json);
34 | }
35 |
36 | public VertxTracer buildTracer() {
37 | if (openTelemetry != null) {
38 | return new OpenTelemetryTracer(openTelemetry);
39 | } else {
40 | return new OpenTelemetryTracer(GlobalOpenTelemetry.get());
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/OpenTelemetryTracer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentelemetry;
12 |
13 | import io.opentelemetry.api.OpenTelemetry;
14 | import io.opentelemetry.api.common.Attributes;
15 | import io.opentelemetry.api.common.AttributesBuilder;
16 | import io.opentelemetry.api.trace.Span;
17 | import io.opentelemetry.api.trace.SpanBuilder;
18 | import io.opentelemetry.api.trace.Tracer;
19 | import io.opentelemetry.context.Scope;
20 | import io.opentelemetry.context.propagation.ContextPropagators;
21 | import io.opentelemetry.context.propagation.TextMapGetter;
22 | import io.opentelemetry.context.propagation.TextMapSetter;
23 | import io.vertx.core.Context;
24 | import io.vertx.core.internal.ContextInternal;
25 | import io.vertx.core.spi.tracing.SpanKind;
26 | import io.vertx.core.spi.tracing.TagExtractor;
27 | import io.vertx.core.spi.tracing.VertxTracer;
28 | import io.vertx.core.tracing.TracingPolicy;
29 | import io.vertx.tracing.opentelemetry.VertxContextStorageProvider.VertxContextStorage;
30 |
31 | import java.util.Map.Entry;
32 | import java.util.function.BiConsumer;
33 |
34 | import static io.vertx.tracing.opentelemetry.OpenTelemetryTracingFactory.ACTIVE_CONTEXT;
35 |
36 | class OpenTelemetryTracer implements VertxTracer {
37 |
38 | private static final TextMapGetter>> getter = new HeadersPropagatorGetter();
39 | private static final TextMapSetter> setter = new HeadersPropagatorSetter();
40 |
41 | private final Tracer tracer;
42 | private final ContextPropagators propagators;
43 |
44 | OpenTelemetryTracer(final OpenTelemetry openTelemetry) {
45 | this.tracer = openTelemetry.getTracer("io.vertx");
46 | this.propagators = openTelemetry.getPropagators();
47 | }
48 |
49 | @Override
50 | public Operation receiveRequest(
51 | final Context context,
52 | final SpanKind kind,
53 | final TracingPolicy policy,
54 | final R request,
55 | final String operation,
56 | final Iterable> headers,
57 | final TagExtractor tagExtractor) {
58 |
59 | if (TracingPolicy.IGNORE.equals(policy)) {
60 | return null;
61 | }
62 |
63 | ContextInternal ctx = (ContextInternal) context;
64 | io.opentelemetry.context.Context otelCtx = ctx.getLocal(ACTIVE_CONTEXT);
65 | if (otelCtx == null) {
66 | otelCtx = io.opentelemetry.context.Context.root();
67 | }
68 |
69 | otelCtx = propagators.getTextMapPropagator().extract(otelCtx, headers, getter);
70 |
71 | // If no span, and policy is PROPAGATE, then don't create the span
72 | if (Span.fromContextOrNull(otelCtx) == null && TracingPolicy.PROPAGATE.equals(policy)) {
73 | return null;
74 | }
75 |
76 | io.opentelemetry.api.trace.SpanKind spanKind = SpanKind.RPC.equals(kind) ? io.opentelemetry.api.trace.SpanKind.SERVER : io.opentelemetry.api.trace.SpanKind.CONSUMER;
77 |
78 | SpanBuilder spanBuilder = tracer
79 | .spanBuilder(operation)
80 | .setParent(otelCtx)
81 | .setSpanKind(spanKind);
82 |
83 | Span span = reportTagsAndStart(spanBuilder, request, tagExtractor, false);
84 | Scope scope = VertxContextStorage.INSTANCE.attach(ctx, span.storeInContext(otelCtx));
85 |
86 | return new Operation(span, scope);
87 | }
88 |
89 | @Override
90 | public void sendResponse(
91 | final Context context,
92 | final R response,
93 | final Operation operation,
94 | final Throwable failure,
95 | final TagExtractor tagExtractor) {
96 | if (operation != null) {
97 | end(operation, response, tagExtractor, failure, false);
98 | }
99 | }
100 |
101 | private static void end(Operation operation, R response, TagExtractor tagExtractor, Throwable failure, boolean client) {
102 | Span span = operation.span();
103 | try {
104 | if (failure != null) {
105 | span.recordException(failure);
106 | }
107 | if (response != null) {
108 | Attributes attributes = processTags(response, tagExtractor, client);
109 | span.setAllAttributes(attributes);
110 | }
111 | span.end();
112 | } finally {
113 | operation.scope().close();
114 | }
115 | }
116 |
117 | @Override
118 | public Operation sendRequest(
119 | final Context context,
120 | final SpanKind kind,
121 | final TracingPolicy policy,
122 | final R request,
123 | final String operation,
124 | final BiConsumer headers,
125 | final TagExtractor tagExtractor) {
126 |
127 | if (TracingPolicy.IGNORE.equals(policy) || request == null) {
128 | return null;
129 | }
130 |
131 | io.opentelemetry.context.Context otelCtx = ((ContextInternal) context).getLocal(ACTIVE_CONTEXT);
132 |
133 | if (otelCtx == null) {
134 | if (!TracingPolicy.ALWAYS.equals(policy)) {
135 | return null;
136 | }
137 | otelCtx = io.opentelemetry.context.Context.root();
138 | }
139 |
140 | io.opentelemetry.api.trace.SpanKind spanKind = SpanKind.RPC.equals(kind) ? io.opentelemetry.api.trace.SpanKind.CLIENT : io.opentelemetry.api.trace.SpanKind.PRODUCER;
141 |
142 | SpanBuilder spanBuilder = tracer.spanBuilder(operation)
143 | .setParent(otelCtx)
144 | .setSpanKind(spanKind);
145 |
146 | Span span = reportTagsAndStart(spanBuilder, request, tagExtractor, true);
147 |
148 | otelCtx = otelCtx.with(span);
149 | propagators.getTextMapPropagator().inject(otelCtx, headers, setter);
150 |
151 | return new Operation(span, Scope.noop());
152 | }
153 |
154 | @Override
155 | public void receiveResponse(
156 | final Context context,
157 | final R response,
158 | final Operation operation,
159 | final Throwable failure,
160 | final TagExtractor tagExtractor) {
161 | if (operation != null) {
162 | end(operation, response, tagExtractor, failure, true);
163 | }
164 | }
165 |
166 | // tags need to be set before start, otherwise any sampler registered won't have access to it
167 | private Span reportTagsAndStart(SpanBuilder span, T obj, TagExtractor tagExtractor, boolean client) {
168 | Attributes attributes = processTags(obj, tagExtractor, client);
169 | span.setAllAttributes(attributes);
170 | return span.startSpan();
171 | }
172 |
173 | private static Attributes processTags(T obj, TagExtractor tagExtractor, boolean client) {
174 | AttributesBuilder builder = Attributes.builder();
175 | int len = tagExtractor.len(obj);
176 | for (int idx = 0; idx < len; idx++) {
177 | builder.put(tagExtractor.name(obj, idx), tagExtractor.value(obj, idx));
178 | }
179 | return builder.build();
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/OpenTelemetryTracingFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentelemetry;
12 |
13 | import io.opentelemetry.context.Context;
14 | import io.vertx.core.json.JsonObject;
15 | import io.vertx.core.spi.VertxTracerFactory;
16 | import io.vertx.core.spi.context.storage.ContextLocal;
17 | import io.vertx.core.spi.tracing.VertxTracer;
18 | import io.vertx.core.tracing.TracingOptions;
19 |
20 | public class OpenTelemetryTracingFactory implements VertxTracerFactory {
21 |
22 | static final ContextLocal ACTIVE_CONTEXT = ContextLocal.registerLocal(Context.class);
23 |
24 | @Override
25 | public VertxTracer, ?> tracer(final TracingOptions options) {
26 | OpenTelemetryOptions openTelemetryOptions;
27 | if (options instanceof OpenTelemetryOptions) {
28 | openTelemetryOptions = (OpenTelemetryOptions) options;
29 | } else {
30 | openTelemetryOptions = new OpenTelemetryOptions(options.toJson());
31 | }
32 | return openTelemetryOptions.buildTracer();
33 | }
34 |
35 | @Override
36 | public TracingOptions newOptions() {
37 | return new OpenTelemetryOptions();
38 | }
39 |
40 | @Override
41 | public TracingOptions newOptions(JsonObject jsonObject) {
42 | return new OpenTelemetryOptions(jsonObject);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/Operation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | package io.vertx.tracing.opentelemetry;
13 |
14 | import io.opentelemetry.api.trace.Span;
15 | import io.opentelemetry.context.Scope;
16 |
17 | import java.util.Objects;
18 |
19 | public final class Operation {
20 |
21 | private final Span span;
22 | private final Scope scope;
23 |
24 | public Operation(Span span, Scope scope) {
25 | this.span = Objects.requireNonNull(span);
26 | this.scope = Objects.requireNonNull(scope);
27 | }
28 |
29 | public Span span() {
30 | return span;
31 | }
32 |
33 | public Scope scope() {
34 | return scope;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/VertxContextStorageProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | package io.vertx.tracing.opentelemetry;
13 |
14 | import io.opentelemetry.context.Context;
15 | import io.opentelemetry.context.ContextStorage;
16 | import io.opentelemetry.context.ContextStorageProvider;
17 | import io.opentelemetry.context.Scope;
18 | import io.vertx.core.internal.ContextInternal;
19 |
20 | import static io.vertx.core.spi.context.storage.AccessMode.CONCURRENT;
21 | import static io.vertx.tracing.opentelemetry.OpenTelemetryTracingFactory.ACTIVE_CONTEXT;
22 |
23 | public class VertxContextStorageProvider implements ContextStorageProvider {
24 |
25 | @Override
26 | public ContextStorage get() {
27 | return VertxContextStorage.INSTANCE;
28 | }
29 |
30 | public enum VertxContextStorage implements ContextStorage {
31 | INSTANCE;
32 |
33 | @Override
34 | public Scope attach(Context toAttach) {
35 | ContextInternal current = ContextInternal.current();
36 | if (current == null) {
37 | return ContextStorage.defaultStorage().attach(toAttach);
38 | }
39 | return attach(current, toAttach);
40 | }
41 |
42 | public Scope attach(io.vertx.core.internal.ContextInternal vertxCtx, Context toAttach) {
43 | Context current = vertxCtx.getLocal(ACTIVE_CONTEXT);
44 |
45 | if (current == toAttach) {
46 | return Scope.noop();
47 | }
48 |
49 | vertxCtx.putLocal(ACTIVE_CONTEXT, CONCURRENT, toAttach);
50 |
51 | if (current == null) {
52 | return () -> vertxCtx.removeLocal(ACTIVE_CONTEXT, CONCURRENT);
53 | }
54 | return () -> vertxCtx.putLocal(ACTIVE_CONTEXT, CONCURRENT, current);
55 | }
56 |
57 | @Override
58 | public Context current() {
59 | ContextInternal vertxCtx = ContextInternal.current();
60 | if (vertxCtx == null) {
61 | return ContextStorage.defaultStorage().current();
62 | }
63 | return vertxCtx.getLocal(ACTIVE_CONTEXT);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/io/vertx/tracing/opentelemetry/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | @io.vertx.codegen.annotations.ModuleGen(name = "vertx-opentelemetry", groupPackage = "io.vertx")
13 | package io.vertx.tracing.opentelemetry;
14 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2024 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | module io.vertx.tracing.opentelemetry {
12 |
13 | requires static io.vertx.docgen;
14 | requires static io.vertx.codegen.api;
15 | requires static io.vertx.codegen.json;
16 |
17 | requires io.opentelemetry.api;
18 | requires io.opentelemetry.context;
19 | requires io.opentelemetry.sdk;
20 | requires io.opentelemetry.sdk.trace;
21 | requires io.vertx.core;
22 |
23 | provides io.opentelemetry.context.ContextStorageProvider with io.vertx.tracing.opentelemetry.VertxContextStorageProvider;
24 | provides io.vertx.core.spi.VertxServiceProvider with io.vertx.tracing.opentelemetry.OpenTelemetryTracingFactory;
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/resources/META-INF/services/io.opentelemetry.context.ContextStorageProvider:
--------------------------------------------------------------------------------
1 | io.vertx.tracing.opentelemetry.VertxContextStorageProvider
2 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider:
--------------------------------------------------------------------------------
1 | io.vertx.tracing.opentelemetry.OpenTelemetryTracingFactory
2 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/test/java/io/vertx/tests/opentelemetry/HeadersPropagatorGetterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentelemetry;
12 |
13 | import io.vertx.tracing.opentelemetry.HeadersPropagatorGetter;
14 | import org.junit.jupiter.api.Test;
15 |
16 | import java.util.AbstractMap.SimpleImmutableEntry;
17 | import java.util.Arrays;
18 | import java.util.Map;
19 |
20 | import static org.assertj.core.api.Assertions.assertThat;
21 |
22 | public class HeadersPropagatorGetterTest {
23 |
24 | @Test
25 | public void shouldGetAllKeys() {
26 |
27 | final HeadersPropagatorGetter getter = new HeadersPropagatorGetter();
28 |
29 | final Iterable keys = getter.keys(Arrays.asList(
30 | new SimpleImmutableEntry<>("a", "1"),
31 | new SimpleImmutableEntry<>("b", "1")
32 | ));
33 |
34 | assertThat(keys).containsAll(Arrays.asList("a", "b"));
35 | }
36 |
37 | @Test
38 | public void shouldReturnNullWhenThereIsNotKeyInCarrier() {
39 |
40 | final HeadersPropagatorGetter getter = new HeadersPropagatorGetter();
41 |
42 | final Iterable> carrier = Arrays.asList(
43 | new SimpleImmutableEntry<>("a", "1"),
44 | new SimpleImmutableEntry<>("b", "1")
45 | );
46 |
47 | assertThat(getter.get(carrier, "c")).isNull();
48 | }
49 |
50 | @Test
51 | public void shouldReturnValueWhenThereIsAKeyInCarrierCaseInsensitive() {
52 |
53 | final HeadersPropagatorGetter getter = new HeadersPropagatorGetter();
54 |
55 | final Iterable> carrier = Arrays.asList(
56 | new SimpleImmutableEntry<>("a", "1"),
57 | new SimpleImmutableEntry<>("b", "2")
58 | );
59 |
60 | assertThat(getter.get(carrier, "A")).isEqualTo("1");
61 | }
62 |
63 | @Test
64 | public void shouldReturnNullWhenCarrierIsNull() {
65 | final HeadersPropagatorGetter getter = new HeadersPropagatorGetter();
66 |
67 | assertThat(getter.get(null, "A")).isNull();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/test/java/io/vertx/tests/opentelemetry/HeadersPropagatorSetterTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentelemetry;
12 |
13 | import io.vertx.tracing.opentelemetry.HeadersPropagatorSetter;
14 | import org.junit.jupiter.api.Test;
15 |
16 | import java.util.concurrent.atomic.AtomicInteger;
17 | import java.util.function.BiConsumer;
18 |
19 | import static org.assertj.core.api.Assertions.assertThat;
20 | import static org.assertj.core.api.Assertions.assertThatNoException;
21 |
22 | public class HeadersPropagatorSetterTest {
23 |
24 | @Test
25 | public void shouldCallConsumerWhenCarrierIsNotNull() {
26 |
27 | final AtomicInteger nCalls = new AtomicInteger(0);
28 | final BiConsumer c = (k, v) -> {
29 | nCalls.incrementAndGet();
30 |
31 | assertThat(k).isEqualTo("k");
32 | assertThat(v).isEqualTo("v");
33 | };
34 |
35 | final HeadersPropagatorSetter setter = new HeadersPropagatorSetter();
36 |
37 | setter.set(c, "k", "v");
38 |
39 | assertThat(nCalls.get()).isEqualTo(1);
40 | }
41 |
42 | @Test
43 | public void shouldNotThrowWhenCarrierIsNull() {
44 | assertThatNoException().isThrownBy(() -> new HeadersPropagatorSetter().set(null, "k", "v"));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/test/java/io/vertx/tests/opentelemetry/OpenTelemetryTracingFactoryTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentelemetry;
12 |
13 | import io.opentelemetry.api.OpenTelemetry;
14 | import io.opentelemetry.api.trace.Span;
15 | import io.opentelemetry.api.trace.Tracer;
16 | import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
17 | import io.opentelemetry.context.Scope;
18 | import io.opentelemetry.context.propagation.ContextPropagators;
19 | import io.vertx.core.Context;
20 | import io.vertx.core.Vertx;
21 | import io.vertx.core.internal.ContextInternal;
22 | import io.vertx.core.spi.tracing.SpanKind;
23 | import io.vertx.core.spi.tracing.TagExtractor;
24 | import io.vertx.core.spi.tracing.VertxTracer;
25 | import io.vertx.core.tracing.TracingPolicy;
26 | import io.vertx.junit5.VertxExtension;
27 | import io.vertx.tracing.opentelemetry.OpenTelemetryOptions;
28 | import io.vertx.tracing.opentelemetry.Operation;
29 | import io.vertx.tracing.opentelemetry.VertxContextStorageProvider.VertxContextStorage;
30 | import org.junit.jupiter.api.Test;
31 | import org.junit.jupiter.api.extension.ExtendWith;
32 |
33 | import java.io.Serializable;
34 | import java.util.AbstractMap.SimpleImmutableEntry;
35 | import java.util.Collections;
36 | import java.util.Map;
37 | import java.util.concurrent.CompletableFuture;
38 | import java.util.concurrent.ExecutionException;
39 |
40 | import static org.assertj.core.api.Assertions.assertThat;
41 | import static org.assertj.core.api.Assertions.assertThatNoException;
42 | import static org.mockito.Mockito.*;
43 |
44 | @ExtendWith(VertxExtension.class)
45 | public class OpenTelemetryTracingFactoryTest {
46 |
47 | @Test
48 | public void receiveRequestShouldNotReturnSpanIfPolicyIsIgnore(final Vertx vertx) {
49 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
50 |
51 | final Operation operation = tracer.receiveRequest(
52 | vertx.getOrCreateContext(),
53 | SpanKind.MESSAGING,
54 | TracingPolicy.IGNORE,
55 | null,
56 | "",
57 | Collections.emptyList(),
58 | TagExtractor.empty()
59 | );
60 |
61 | assertThat(operation).isNull();
62 | }
63 |
64 | @Test
65 | public void receiveRequestShouldNotReturnSpanIfPolicyIsPropagateAndPreviousContextIsNotPresent(final Vertx vertx) {
66 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
67 |
68 | final Operation operation = tracer.receiveRequest(
69 | vertx.getOrCreateContext(),
70 | SpanKind.MESSAGING,
71 | TracingPolicy.PROPAGATE,
72 | null,
73 | "",
74 | Collections.emptyList(),
75 | TagExtractor.empty()
76 | );
77 |
78 | assertThat(operation).isNull();
79 | }
80 |
81 | @Test
82 | public void receiveRequestShouldReturnSpanIfPolicyIsPropagateAndPreviousContextIsPresent(final Vertx vertx) {
83 | VertxTracer tracer = new OpenTelemetryOptions(
84 | OpenTelemetry.propagating(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
85 | ).buildTracer();
86 |
87 | final Iterable> headers = Collections.singletonList(
88 | new SimpleImmutableEntry<>("traceparent", "00-83ebbd06a32c2eaa8d5bf4b060d7cbfa-140cd1a04ab7be4b-01")
89 | );
90 |
91 | final io.vertx.core.Context ctx = vertx.getOrCreateContext();
92 | final Operation operation = tracer.receiveRequest(
93 | ctx,
94 | SpanKind.MESSAGING,
95 | TracingPolicy.PROPAGATE,
96 | null,
97 | "",
98 | headers,
99 | TagExtractor.empty()
100 | );
101 |
102 | assertThat(operation).isNotNull();
103 |
104 | final io.opentelemetry.context.Context tracingContext = VertxContextStorage.INSTANCE.current();
105 | assertThat(tracingContext).isNotNull();
106 | }
107 |
108 | @Test
109 | public void receiveRequestShouldReturnAParentedSpanIfPolicyIsPropagateAndTheOtelContextHasAnOngoingSpan(final Vertx vertx) throws ExecutionException, InterruptedException {
110 | final OpenTelemetry openTelemetry = OpenTelemetry.propagating(
111 | ContextPropagators.create(W3CTraceContextPropagator.getInstance())
112 | );
113 |
114 | final Tracer otelTracer = openTelemetry.getTracer("example-lib");
115 |
116 | final Span parentSpan = otelTracer.spanBuilder("example-span")
117 | .startSpan();
118 |
119 | CompletableFuture futureOperation = new CompletableFuture<>();
120 |
121 | vertx.runOnContext(unused -> {
122 | parentSpan.makeCurrent();
123 |
124 | VertxTracer tracer = new OpenTelemetryOptions(openTelemetry).buildTracer();
125 |
126 | final Operation operation = tracer.receiveRequest(
127 | vertx.getOrCreateContext(),
128 | SpanKind.MESSAGING,
129 | TracingPolicy.PROPAGATE,
130 | null,
131 | "",
132 | Collections.emptyList(),
133 | TagExtractor.empty()
134 | );
135 |
136 | parentSpan.end();
137 |
138 | futureOperation.complete(operation);
139 | });
140 |
141 | Operation operation = futureOperation.get();
142 |
143 | assertThat(operation).isNotNull();
144 | assertThat(operation.span().getSpanContext().getTraceId())
145 | .isEqualTo(parentSpan.getSpanContext().getTraceId());
146 | }
147 |
148 | @Test
149 | public void sendResponseEndsSpan(final Vertx vertx) {
150 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
151 |
152 | Span span = mock(Span.class);
153 | doNothing().when(span).end();
154 | Scope scope = mock(Scope.class);
155 | Operation operation = new Operation(span, scope);
156 |
157 | tracer.sendResponse(
158 | vertx.getOrCreateContext(),
159 | mock(Serializable.class),
160 | operation,
161 | mock(Exception.class),
162 | TagExtractor.empty()
163 | );
164 |
165 | verify(span, times(1)).end();
166 | }
167 |
168 | @Test
169 | public void sendResponseShouldNotThrowExceptionWhenSpanIsNull(final Vertx vertx) {
170 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
171 |
172 | assertThatNoException().isThrownBy(() -> tracer.sendResponse(
173 | vertx.getOrCreateContext(),
174 | mock(Serializable.class),
175 | null,
176 | mock(Exception.class),
177 | TagExtractor.empty()
178 | ));
179 | }
180 |
181 | @Test
182 | public void sendRequestShouldNotReturnSpanIfRequestIsNull(final Vertx vertx) {
183 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
184 |
185 | final Context ctx = vertx.getOrCreateContext();
186 | VertxContextStorage.INSTANCE.attach(io.opentelemetry.context.Context.current());
187 |
188 | final Operation operation = tracer.sendRequest(
189 | ctx,
190 | SpanKind.MESSAGING,
191 | TracingPolicy.PROPAGATE,
192 | null,
193 | "",
194 | (k, v) -> {
195 | },
196 | TagExtractor.empty()
197 | );
198 |
199 | assertThat(operation).isNull();
200 | }
201 |
202 | @Test
203 | public void sendRequestShouldNotReturnSpanIfPolicyIsIgnore(final Vertx vertx) {
204 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
205 |
206 | final Context ctx = vertx.getOrCreateContext();
207 | VertxContextStorage.INSTANCE.attach(io.opentelemetry.context.Context.current());
208 |
209 | final Operation operation = tracer.sendRequest(
210 | ctx,
211 | SpanKind.MESSAGING,
212 | TracingPolicy.IGNORE,
213 | mock(Serializable.class),
214 | "",
215 | (k, v) -> {
216 | },
217 | TagExtractor.empty()
218 | );
219 |
220 | assertThat(operation).isNull();
221 | }
222 |
223 |
224 | @Test
225 | public void sendRequestShouldNotReturnSpanIfPolicyIsPropagateAndPreviousContextIsNotPresent(final Vertx vertx) {
226 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
227 |
228 | final Operation operation = tracer.sendRequest(
229 | vertx.getOrCreateContext(),
230 | SpanKind.MESSAGING,
231 | TracingPolicy.PROPAGATE,
232 | null,
233 | "",
234 | (k, v) -> {
235 | },
236 | TagExtractor.empty()
237 | );
238 |
239 | assertThat(operation).isNull();
240 | }
241 |
242 | @Test
243 | public void sendRequestShouldReturnSpanIfPolicyIsPropagateAndPreviousContextIsPresent(final Vertx vertx) {
244 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
245 |
246 | final Context ctx = vertx.getOrCreateContext();
247 | VertxContextStorage.INSTANCE.attach((ContextInternal) ctx, io.opentelemetry.context.Context.current());
248 |
249 | final Operation operation = tracer.sendRequest(
250 | ctx,
251 | SpanKind.MESSAGING,
252 | TracingPolicy.PROPAGATE,
253 | mock(Serializable.class),
254 | "",
255 | (k, v) -> {
256 | },
257 | TagExtractor.empty()
258 | );
259 |
260 | assertThat(operation).isNotNull();
261 | }
262 |
263 | @Test
264 | public void sendRequestShouldReturnSpanIfPolicyIsAlwaysAndPreviousContextIsNotPresent(final Vertx vertx) {
265 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
266 |
267 | final Context ctx = vertx.getOrCreateContext();
268 |
269 | final Operation operation = tracer.sendRequest(
270 | ctx,
271 | SpanKind.MESSAGING,
272 | TracingPolicy.ALWAYS,
273 | mock(Serializable.class),
274 | "",
275 | (k, v) -> {
276 | },
277 | TagExtractor.empty()
278 | );
279 |
280 | assertThat(operation).isNotNull();
281 | }
282 |
283 | @Test
284 | public void receiveResponseEndsSpan(final Vertx vertx) {
285 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
286 |
287 | Span span = mock(Span.class);
288 | doNothing().when(span).end();
289 | Scope scope = mock(Scope.class);
290 | Operation operation = new Operation(span, scope);
291 |
292 | tracer.receiveResponse(
293 | vertx.getOrCreateContext(),
294 | mock(Serializable.class),
295 | operation,
296 | mock(Exception.class),
297 | TagExtractor.empty()
298 | );
299 |
300 | verify(span, times(1)).end();
301 | }
302 |
303 | @Test
304 | public void receiveResponseShouldNotThrowExceptionWhenSpanIsNull(final Vertx vertx) {
305 | VertxTracer tracer = new OpenTelemetryOptions(OpenTelemetry.noop()).buildTracer();
306 |
307 | assertThatNoException().isThrownBy(() -> tracer.receiveResponse(
308 | vertx.getOrCreateContext(),
309 | mock(Serializable.class),
310 | null,
311 | mock(Exception.class),
312 | TagExtractor.empty()
313 | ));
314 | }
315 | }
316 |
--------------------------------------------------------------------------------
/vertx-opentelemetry/src/test/java/io/vertx/tests/opentelemetry/SqlClientTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentelemetry;
12 |
13 | import io.opentelemetry.api.common.AttributeKey;
14 | import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
15 | import io.opentelemetry.sdk.trace.data.SpanData;
16 | import io.vertx.core.Vertx;
17 | import io.vertx.core.VertxOptions;
18 | import io.vertx.core.http.HttpClient;
19 | import io.vertx.core.http.HttpClientOptions;
20 | import io.vertx.core.http.HttpMethod;
21 | import io.vertx.core.tracing.TracingPolicy;
22 | import io.vertx.junit5.VertxExtension;
23 | import io.vertx.junit5.VertxTestContext;
24 | import io.vertx.pgclient.PgBuilder;
25 | import io.vertx.pgclient.PgConnectOptions;
26 | import io.vertx.sqlclient.Pool;
27 | import io.vertx.sqlclient.Row;
28 | import io.vertx.sqlclient.RowSet;
29 | import io.vertx.sqlclient.Tuple;
30 | import io.vertx.tracing.opentelemetry.OpenTelemetryOptions;
31 | import org.junit.jupiter.api.*;
32 | import org.junit.jupiter.api.extension.ExtendWith;
33 | import org.junit.jupiter.api.extension.RegisterExtension;
34 | import org.testcontainers.containers.PostgreSQLContainer;
35 |
36 | import java.util.List;
37 |
38 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
39 | import static java.util.concurrent.TimeUnit.NANOSECONDS;
40 | import static org.junit.jupiter.api.Assertions.assertEquals;
41 | import static org.junit.jupiter.api.Assertions.assertTrue;
42 |
43 | @ExtendWith(VertxExtension.class)
44 | public class SqlClientTest {
45 |
46 | @RegisterExtension
47 | static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create();
48 |
49 | private static PostgreSQLContainer> server;
50 | private static PgConnectOptions connectOptions;
51 |
52 | private Vertx vertx;
53 | private Pool pool;
54 |
55 | @BeforeAll
56 | public static void startDB() {
57 | server = new PostgreSQLContainer<>("postgres:10")
58 | .withDatabaseName("postgres")
59 | .withUsername("postgres")
60 | .withPassword("postgres");
61 | server.start();
62 | connectOptions = new PgConnectOptions()
63 | .setUser("postgres")
64 | .setPassword("postgres")
65 | .setDatabase("postgres")
66 | .setHost(server.getHost())
67 | .setPort(server.getMappedPort(5432));
68 |
69 | }
70 |
71 | @AfterAll
72 | public static void stopDB() {
73 | server.stop();
74 | }
75 |
76 | @BeforeEach
77 | public void before() {
78 | vertx = Vertx.vertx(new VertxOptions().setTracingOptions(new OpenTelemetryOptions(otelTesting.getOpenTelemetry())));
79 | pool = PgBuilder.pool().connectingTo(connectOptions).using(vertx).build();
80 | }
81 |
82 | @AfterEach
83 | public void after(VertxTestContext context) throws Exception {
84 | vertx.close().onComplete(context.succeedingThenComplete());
85 | }
86 |
87 | @Test
88 | public void testPreparedQuery(VertxTestContext ctx) throws Exception {
89 | long baseDurationInMs = 500;
90 | vertx.createHttpServer().requestHandler(req -> {
91 | pool.preparedQuery("SELECT $1 \"VAL\"")
92 | .execute(Tuple.of("Hello World"))
93 | .onComplete(ar -> {
94 | vertx.setTimer(baseDurationInMs, (__) -> {
95 | if (ar.succeeded()) {
96 | RowSet rows = ar.result();
97 | req.response()
98 | .end();
99 | } else {
100 | req.response()
101 | .setStatusCode(500)
102 | .end();
103 | }
104 | });
105 | });
106 | }).listen(8080).onComplete(ctx.succeeding(srv -> {
107 | HttpClient client = vertx.createHttpClient(new HttpClientOptions().setTracingPolicy(TracingPolicy.ALWAYS));
108 | client.request(HttpMethod.GET, 8080, "localhost", "/").compose(req -> {
109 | return req.send().compose(resp -> resp.body().map(resp));
110 | }).onComplete(ctx.succeeding(resp -> {
111 | ctx.verify(() -> {
112 | assertEquals(200, resp.statusCode());
113 | List spans = otelTesting.getSpans();
114 | assertEquals(3, spans.size());
115 | SpanData requestSpan = spans.get(1);
116 | assertEquals("GET", requestSpan.getName());
117 | assertEquals("GET", requestSpan.getAttributes().get(AttributeKey.stringKey("http.request.method")));
118 | assertEquals("http://localhost:8080/", requestSpan.getAttributes().get(AttributeKey.stringKey("http.url")));
119 | assertEquals("200", requestSpan.getAttributes().get(AttributeKey.stringKey("http.response.status_code")));
120 | assertTrue(MILLISECONDS.convert(requestSpan.getEndEpochNanos() - requestSpan.getStartEpochNanos(), NANOSECONDS) > baseDurationInMs);
121 | SpanData querySpan = spans.get(0);
122 | assertEquals("Query", querySpan.getName());
123 | assertEquals("client", querySpan.getAttributes().get(AttributeKey.stringKey("span.kind")));
124 | assertEquals("SELECT $1 \"VAL\"", querySpan.getAttributes().get(AttributeKey.stringKey("db.query.text")));
125 | assertEquals("postgres", querySpan.getAttributes().get(AttributeKey.stringKey("db.user")));
126 | assertEquals("postgres", querySpan.getAttributes().get(AttributeKey.stringKey("db.namespace")));
127 | assertEquals("postgresql", querySpan.getAttributes().get(AttributeKey.stringKey("db.system")));
128 | assertEquals(querySpan.getParentSpanId(), requestSpan.getSpanId());
129 | assertEquals(querySpan.getTraceId(), requestSpan.getTraceId());
130 | ctx.completeNow();
131 | });
132 | }));
133 | }));
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/vertx-opentracing/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
19 |
20 | 4.0.0
21 |
22 |
23 | io.vertx
24 | vertx-tracing-parent
25 | 5.1.0-SNAPSHOT
26 |
27 |
28 | vertx-opentracing
29 |
30 | Vert.x OpenTracing
31 |
32 |
33 |
34 |
35 | io.jaegertracing
36 | jaeger-client
37 | 1.0.0
38 |
39 |
40 | junit
41 | junit
42 | 4.13.1
43 | test
44 |
45 |
46 | io.vertx
47 | vertx-unit
48 | test
49 |
50 |
51 | io.opentracing
52 | opentracing-mock
53 | 0.33.0
54 | test
55 |
56 |
57 | io.rest-assured
58 | rest-assured
59 | 5.2.0
60 | test
61 |
62 |
63 | io.jaegertracing
64 | jaeger-testcontainers
65 | 0.7.0
66 | test
67 |
68 |
69 | io.grpc
70 | grpc-core
71 |
72 |
73 |
74 |
75 | io.vertx
76 | vertx-junit5
77 |
78 |
79 | org.assertj
80 | assertj-core
81 | 3.22.0
82 | test
83 |
84 |
85 |
86 |
87 |
88 |
89 | org.apache.maven.plugins
90 | maven-failsafe-plugin
91 |
92 |
93 |
94 | integration-test
95 | verify
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | test-traced-service
104 |
105 | const
106 | 1
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/asciidoc/index.adoc:
--------------------------------------------------------------------------------
1 | = Vertx OpenTracing
2 |
3 | Vert.x integrates with OpenTracing thanks to the Jaeger client.
4 |
5 | You can configure Vert.x to use the Jaeger client configured via
6 | https://github.com/jaegertracing/jaeger-client-java/blob/master/jaeger-core/README.md#configuration-via-environment[Environment]
7 |
8 | [source,$lang]
9 | ----
10 | {@link examples.OpenTracingExamples#ex1}
11 | ----
12 |
13 | You can also pass a custom `Tracer` allowing for greater control
14 | over the configuration.
15 |
16 | [source,$lang]
17 | ----
18 | {@link examples.OpenTracingExamples#ex2}
19 | ----
20 |
21 | == Tracing policy
22 |
23 | The tracing policy defines the behavior of a component when tracing is enabled:
24 |
25 | - {@link io.vertx.core.tracing.TracingPolicy#PROPAGATE}: the component reports a span in the active trace
26 | - {@link io.vertx.core.tracing.TracingPolicy#ALWAYS}: the component reports a span in the active trace or creates a new active trace
27 | - {@link io.vertx.core.tracing.TracingPolicy#IGNORE}: the component will not be involved in any trace.
28 |
29 | The tracing policy is usually configured in the component options.
30 |
31 | == HTTP tracing
32 |
33 | The Vert.x HTTP server and client reports span around HTTP requests:
34 |
35 | - `operationName`: the HTTP method
36 | - tags
37 | - `http.method`: the HTTP method
38 | - `http.url`: the request URL
39 | - `http.status_code`: the HTTP status code
40 |
41 | The default HTTP server tracing policy is `ALWAYS`, you can configure the policy with {@link io.vertx.core.http.HttpServerOptions#setTracingPolicy}
42 |
43 | [source,$lang]
44 | ----
45 | {@link examples.OpenTracingExamples#ex3}
46 | ----
47 |
48 | The default HTTP client tracing policy is `PROPAGATE`, you can configure the policy with {@link io.vertx.core.http.HttpClientOptions#setTracingPolicy}
49 |
50 | [source,$lang]
51 | ----
52 | {@link examples.OpenTracingExamples#ex4}
53 | ----
54 |
55 | To initiate a trace for a client call, you need to create it first and make Vert.x
56 | aware of it by using `OpenTracingUtil.setSpan`:
57 |
58 | [source,$lang]
59 | ----
60 | {@link examples.OpenTracingExamples#ex5}
61 | ----
62 |
63 | In an HTTP scenario between two Vert.x services, a span will be created client-side, then
64 | the trace context will be propagated server-side and another span will be added to the trace.
65 |
66 | == EventBus tracing
67 |
68 | The Vert.x EventBus reports spans around message exchanges.
69 |
70 | The default sending policy is `PROPAGATE`, you can configure the policy with {@link io.vertx.core.eventbus.DeliveryOptions#setTracingPolicy}.
71 |
72 | [source,$lang]
73 | ----
74 | {@link examples.OpenTracingExamples#ex6}
75 | ----
76 |
77 | == Obtain current Span
78 |
79 | Vert.x stores current `Span` object in local context.
80 | To obtain it, use method `OpenTracingUtil.getSpan()`.
81 |
82 | This method will work only on Vert.x threads (instances of `VertxThread`).
83 | Obtaining from non-Vert.x thread doesn't work by design, method will return null.
84 |
85 | == Coroutines support
86 |
87 | There is no direct support for coroutines, but it can be achieved with minimal instrumentation.
88 |
89 | There are several steps to achieve this.
90 |
91 | 1. Use `CoroutineVerticle`.
92 | 2. Convert *every route handler* you have to a coroutine.
93 | 3. Use CoroutineContext to store `Tracer` and current `Span` object
94 |
95 |
96 | Example code:
97 |
98 | [source,kotlin]
99 | ----
100 | class TracedVerticle(private val tracer: Tracer): CoroutineVerticle() {
101 | override suspend fun start() {
102 | val router = Router.router(vertx)
103 |
104 | router.route("/hello1")
105 | .method(HttpMethod.GET)
106 | .coroutineHandler { ctx -> // (1)
107 | launch { println("Hello to Console") }
108 | ctx.end("Hello from coroutine handler")
109 | }
110 |
111 | router.route("/hello2")
112 | .method(HttpMethod.GET)
113 | .coroutineHandler(::nonSuspendHandler) // (2)
114 |
115 | vertx.createHttpServer()
116 | .requestHandler(router)
117 | .listen(8080)
118 | .await()
119 | }
120 |
121 | private fun nonSuspendHandler(ctx: RoutingContext) {
122 | ctx.end("Hello from usual handler")
123 | }
124 |
125 | private fun Route.coroutineHandler(handler: Handler): Route = // (3)
126 | this.coroutineHandler(handler::handle)
127 |
128 | private fun Route.coroutineHandler( // (4)
129 | handler: suspend (RoutingContext) -> (Unit)
130 | ): Route = handler { ctx ->
131 | val span: Span = OpenTracingUtil.getSpan() // (5)
132 | launch(ctx.vertx().dispatcher() + SpanElement(tracer, span)) { // (6)
133 | val spanElem = coroutineContext[SpanElement] // (7)
134 | if (spanElem == null) {
135 | handler(ctx)
136 | } else {
137 | val span = spanElem.span
138 | val tracer = spanElem.tracer
139 | val childSpan = span // (8)
140 | try {
141 | withContext(SpanElement(tracer, childSpan)) { handler(ctx) } // (9)
142 | } finally {
143 | // childSpan.finish() // (10)
144 | }
145 | }
146 | // OR create a helper method for further reuse
147 | withContextTraced(coroutineContext) {
148 | try {
149 | handler(ctx)
150 | } catch (t: Throwable) {
151 | ctx.fail(t)
152 | }
153 | }
154 | }
155 | }
156 | }
157 | ----
158 |
159 | 1. Creates a coroutine handler with `coroutineHandler` extension method.
160 | 2. Creates usual async handler, which is then wrapped to a coroutine.
161 | 3. Extension method to convert `Handler` to suspendable function.
162 | 4. Extension method which creates and launches a coroutine on Vert.x EventLoop.
163 | 5. Get current `Span` from Vert.x local context (populated automatically).
164 | 6. Create a wrapper coroutine, add current Span to `CoroutineContext`.
165 | 7. Retrieve a `Span` from coroutine context.
166 | 8. Either reuse `span` or create a new span with `tracer.buildSpan("").asChildOf(span).start()`.
167 | 9. Put a new `Span` to a context.
168 | 10. Finish `childSpan`, if you created a new one.
169 |
170 | Helper code, your implementation may vary:
171 |
172 | [source,kotlin]
173 | ----
174 | /**
175 | * Keeps references to a tracer and current Span inside CoroutineContext
176 | */
177 | class SpanElement(val tracer: Tracer, val span: Span) :
178 | ThreadContextElement,
179 | AbstractCoroutineContextElement(SpanElement) {
180 |
181 | companion object Key : CoroutineContext.Key
182 |
183 | /**
184 | * Will close current [Scope] after continuation's pause.
185 | */
186 | override fun restoreThreadContext(context: CoroutineContext, oldState: Scope) {
187 | oldState.close()
188 | }
189 |
190 | /**
191 | * Will create a new [Scope] after each continuation's resume, scope is activated with provided [span] instance.
192 | */
193 | override fun updateThreadContext(context: CoroutineContext): Scope {
194 | return tracer.activateSpan(span)
195 | }
196 | }
197 |
198 | /**
199 | * Advanced helper method with a few options, also shows how to use MDCContext to pass a Span to a logger.
200 | */
201 | suspend fun withContextTraced(
202 | context: CoroutineContext,
203 | reuseParentSpan: Boolean = true,
204 | block: suspend CoroutineScope.() -> T
205 | ): T {
206 | return coroutineScope {
207 | val spanElem = this.coroutineContext[SpanElement]
208 |
209 | if (spanElem == null) {
210 | logger.warn { "Calling 'withTracer', but no span found in context" }
211 | withContext(context, block)
212 | } else {
213 | val childSpan = if (reuseParentSpan) spanElem.span
214 | else spanElem.tracer.buildSpan("").asChildOf(spanElem.span).start()
215 |
216 | try {
217 | val mdcSpan = mapOf(MDC_SPAN_KEY to childSpan.toString())
218 | withContext(context + SpanElement(spanElem.tracer, childSpan) + MDCContext(mdcSpan), block)
219 | } finally {
220 | if (!reuseParentSpan) childSpan.finish()
221 | }
222 | }
223 | }
224 | }
225 | private const val MDC_SPAN_KEY = "request.span.id"
226 | ----
227 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/java/examples/OpenTracingExamples.java:
--------------------------------------------------------------------------------
1 | package examples;
2 |
3 | import io.opentracing.Span;
4 | import io.opentracing.Tracer;
5 | import io.vertx.core.Vertx;
6 | import io.vertx.core.VertxOptions;
7 | import io.vertx.core.eventbus.DeliveryOptions;
8 | import io.vertx.core.http.HttpClient;
9 | import io.vertx.core.http.HttpClientOptions;
10 | import io.vertx.core.http.HttpServer;
11 | import io.vertx.core.http.HttpServerOptions;
12 | import io.vertx.core.tracing.TracingPolicy;
13 | import io.vertx.docgen.Source;
14 | import io.vertx.tracing.opentracing.OpenTracingOptions;
15 | import io.vertx.tracing.opentracing.OpenTracingTracer;
16 | import io.vertx.tracing.opentracing.OpenTracingUtil;
17 |
18 | @Source
19 | public class OpenTracingExamples {
20 |
21 | public void ex1() {
22 | Vertx vertx = Vertx.vertx(new VertxOptions()
23 | .setTracingOptions(
24 | new OpenTracingOptions()
25 | )
26 | );
27 | }
28 |
29 | public void ex2(Tracer tracer) {
30 | Vertx vertx = Vertx
31 | .builder()
32 | .withTracer(o -> new OpenTracingTracer(false, tracer))
33 | .build();
34 | }
35 |
36 | public void ex3(Vertx vertx) {
37 | HttpServer server = vertx.createHttpServer(new HttpServerOptions()
38 | .setTracingPolicy(TracingPolicy.IGNORE)
39 | );
40 | }
41 |
42 | public void ex4(Vertx vertx) {
43 | HttpClient client = vertx.createHttpClient(new HttpClientOptions()
44 | .setTracingPolicy(TracingPolicy.IGNORE)
45 | );
46 | }
47 |
48 | public void ex5(Tracer tracer) {
49 | Span span = tracer.buildSpan("my-operation")
50 | .withTag("some-key", "some-value")
51 | .start();
52 | OpenTracingUtil.setSpan(span);
53 | // Do something, e.g. client request
54 | span.finish();
55 | }
56 |
57 | public void ex6(Vertx vertx) {
58 | DeliveryOptions options = new DeliveryOptions().setTracingPolicy(TracingPolicy.ALWAYS);
59 | vertx.eventBus().send("the-address", "foo", options);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/java/io/vertx/tracing/opentracing/OpenTracingOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentracing;
12 |
13 | import io.vertx.codegen.annotations.DataObject;
14 | import io.vertx.core.json.JsonObject;
15 | import io.vertx.core.tracing.TracingOptions;
16 |
17 | @DataObject
18 | public class OpenTracingOptions extends TracingOptions {
19 |
20 | public OpenTracingOptions() {
21 | }
22 |
23 | public OpenTracingOptions(OpenTracingOptions other) {
24 | super(other);
25 | }
26 |
27 | public OpenTracingOptions(JsonObject json) {
28 | super(json);
29 | }
30 |
31 | @Override
32 | public OpenTracingOptions copy() {
33 | return new OpenTracingOptions(this);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/java/io/vertx/tracing/opentracing/OpenTracingTracer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentracing;
12 |
13 | import io.jaegertracing.Configuration;
14 | import io.opentracing.Span;
15 | import io.opentracing.SpanContext;
16 | import io.opentracing.Tracer;
17 | import io.opentracing.log.Fields;
18 | import io.opentracing.propagation.Format;
19 | import io.opentracing.propagation.TextMap;
20 | import io.opentracing.tag.Tags;
21 | import io.vertx.core.Context;
22 | import io.vertx.core.internal.ContextInternal;
23 | import io.vertx.core.spi.tracing.SpanKind;
24 | import io.vertx.core.spi.tracing.TagExtractor;
25 | import io.vertx.core.tracing.TracingPolicy;
26 |
27 | import java.util.HashMap;
28 | import java.util.Iterator;
29 | import java.util.Map;
30 | import java.util.function.BiConsumer;
31 |
32 | import static io.vertx.core.spi.context.storage.AccessMode.CONCURRENT;
33 | import static io.vertx.tracing.opentracing.OpenTracingTracerFactory.ACTIVE_SPAN;
34 |
35 | /**
36 | * - https://github.com/opentracing/specification/blob/master/semantic_conventions.md
37 | * - https://github.com/opentracing/specification/blob/master/specification.md
38 | */
39 | public class OpenTracingTracer implements io.vertx.core.spi.tracing.VertxTracer {
40 |
41 | /**
42 | * Instantiate an OpenTracing tracer configured from ENV, e.g {@code JAEGER_SERVICE_NAME}, etc...
43 | */
44 | static Tracer createDefaultTracer() {
45 | Configuration config = Configuration.fromEnv();
46 | return config.getTracerBuilder().build();
47 | }
48 |
49 | private final boolean closeTracer;
50 | private final Tracer tracer;
51 |
52 | /**
53 | * Instantiate a OpenTracing tracer using the specified {@code tracer}.
54 | *
55 | * @param closeTracer close the tracer when necessary
56 | * @param tracer the tracer instance
57 | */
58 | public OpenTracingTracer(boolean closeTracer, Tracer tracer) {
59 | this.closeTracer = closeTracer;
60 | this.tracer = tracer;
61 | }
62 |
63 | @Override
64 | public Span receiveRequest(Context context,
65 | SpanKind kind,
66 | TracingPolicy policy,
67 | R request,
68 | String operation,
69 | Iterable> headers,
70 | TagExtractor tagExtractor) {
71 | if (policy != TracingPolicy.IGNORE) {
72 | SpanContext sc = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMap() {
73 | @Override
74 | public Iterator> iterator() {
75 | return headers.iterator();
76 | }
77 |
78 | @Override
79 | public void put(String key, String value) {
80 | throw new UnsupportedOperationException();
81 | }
82 | });
83 | if (sc != null || policy == TracingPolicy.ALWAYS) {
84 | Span span = tracer.buildSpan(operation)
85 | .ignoreActiveSpan()
86 | .asChildOf(sc)
87 | .withTag(Tags.SPAN_KIND.getKey(), kind == SpanKind.RPC ? Tags.SPAN_KIND_SERVER : Tags.SPAN_KIND_CONSUMER)
88 | .withTag(Tags.COMPONENT.getKey(), "vertx")
89 | .start();
90 | reportTags(span, request, tagExtractor);
91 | ((ContextInternal) context).putLocal(ACTIVE_SPAN, CONCURRENT, span);
92 | return span;
93 | }
94 | }
95 | return null;
96 | }
97 |
98 | @Override
99 | public void sendResponse(
100 | Context context, R response, Span span, Throwable failure, TagExtractor tagExtractor) {
101 | if (span != null) {
102 | ((ContextInternal) context).removeLocal(ACTIVE_SPAN, CONCURRENT);
103 | if (failure != null) {
104 | reportFailure(span, failure);
105 | }
106 | if (response != null) {
107 | reportTags(span, response, tagExtractor);
108 | }
109 | span.finish();
110 | }
111 | }
112 |
113 | @Override
114 | public Span sendRequest(Context context,
115 | SpanKind kind,
116 | TracingPolicy policy,
117 | R request,
118 | String operation,
119 | BiConsumer headers,
120 | TagExtractor tagExtractor) {
121 | if (policy == TracingPolicy.IGNORE) {
122 | return null;
123 | }
124 | Span active = ((ContextInternal)context).getLocal(ACTIVE_SPAN);
125 | if (active != null || policy == TracingPolicy.ALWAYS) {
126 | Span span = tracer
127 | .buildSpan(operation)
128 | .asChildOf(active)
129 | .withTag(Tags.SPAN_KIND.getKey(), kind == SpanKind.RPC ? Tags.SPAN_KIND_CLIENT : Tags.SPAN_KIND_PRODUCER)
130 | .withTag(Tags.COMPONENT.getKey(), "vertx")
131 | .start();
132 | reportTags(span, request, tagExtractor);
133 | if (headers != null) {
134 | tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMap() {
135 | @Override
136 | public Iterator> iterator() {
137 | throw new UnsupportedOperationException();
138 | }
139 |
140 | @Override
141 | public void put(String key, String value) {
142 | headers.accept(key, value);
143 | }
144 | });
145 | }
146 | return span;
147 | }
148 | return null;
149 | }
150 |
151 | @Override
152 | public void receiveResponse(Context context, R response, Span span, Throwable failure,
153 | TagExtractor tagExtractor) {
154 | if (span != null) {
155 | if (failure != null) {
156 | reportFailure(span, failure);
157 | }
158 | if (response != null) {
159 | reportTags(span, response, tagExtractor);
160 | }
161 | span.finish();
162 | }
163 | }
164 |
165 | private void reportTags(Span span, T obj, TagExtractor tagExtractor) {
166 | int len = tagExtractor.len(obj);
167 | for (int idx = 0; idx < len; idx++) {
168 | String name = tagExtractor.name(obj, idx);
169 | String value = tagExtractor.value(obj, idx);
170 | switch (name) {
171 | case "server.address":
172 | case "network.peer.address":
173 | span.setTag("peer.address", value);
174 | break;
175 | case "server.port":
176 | case "network.peer.port":
177 | span.setTag("peer.port", value);
178 | break;
179 | case "messaging.destination.name":
180 | span.setTag("message_bus.destination", value);
181 | break;
182 | case "messaging.system":
183 | span.setTag("message_bus.system", value);
184 | break;
185 | case "messaging.operation":
186 | span.setTag("message_bus.operation", value);
187 | break;
188 | case "db.namespace":
189 | span.setTag("db.instance", value);
190 | break;
191 | case "db.query.text":
192 | case "db.operation.name":
193 | span.setTag("db.statement", value);
194 | break;
195 | case "db.system":
196 | span.setTag("db.type", value);
197 | break;
198 | case "http.request.method":
199 | span.setTag("http.method", value);
200 | break;
201 | case "http.response.status_code":
202 | span.setTag("http.status_code", value);
203 | break;
204 | case "url.full":
205 | span.setTag("http.url", value);
206 | break;
207 | case "url.scheme":
208 | span.setTag("http.scheme", value);
209 | break;
210 | case "url.path":
211 | span.setTag("http.path", value);
212 | break;
213 | case "url.query":
214 | span.setTag("http.query", value);
215 | break;
216 | default:
217 | span.setTag(name, value);
218 | }
219 | }
220 | }
221 |
222 | private void reportFailure(Span span, Throwable failure) {
223 | if (failure != null) {
224 | span.setTag("error", true);
225 | HashMap fields = new HashMap<>();
226 | fields.put(Fields.EVENT, "error");
227 | fields.put(Fields.MESSAGE, failure.getMessage());
228 | fields.put(Fields.ERROR_KIND, "Exception");
229 | fields.put(Fields.ERROR_OBJECT, failure);
230 | span.log(fields);
231 | }
232 | }
233 |
234 | @Override
235 | public void close() {
236 | if (closeTracer && tracer != null) {
237 | tracer.close();
238 | }
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/java/io/vertx/tracing/opentracing/OpenTracingTracerFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentracing;
12 |
13 | import io.opentracing.Span;
14 | import io.opentracing.Tracer;
15 | import io.vertx.core.json.JsonObject;
16 | import io.vertx.core.spi.VertxTracerFactory;
17 | import io.vertx.core.spi.context.storage.ContextLocal;
18 | import io.vertx.core.spi.tracing.VertxTracer;
19 | import io.vertx.core.tracing.TracingOptions;
20 |
21 | public class OpenTracingTracerFactory implements VertxTracerFactory {
22 |
23 | public static final ContextLocal ACTIVE_SPAN = ContextLocal.registerLocal(Span.class);
24 |
25 | private final Tracer tracer;
26 |
27 | public OpenTracingTracerFactory() {
28 | this(null);
29 | }
30 |
31 | public OpenTracingTracerFactory(Tracer tracer) {
32 | this.tracer = tracer;
33 | }
34 |
35 | @Override
36 | public VertxTracer tracer(TracingOptions options) {
37 | if (tracer != null) {
38 | return new OpenTracingTracer(false, tracer);
39 | } else {
40 | return new OpenTracingTracer(true, OpenTracingTracer.createDefaultTracer());
41 | }
42 | }
43 |
44 | @Override
45 | public OpenTracingOptions newOptions() {
46 | return new OpenTracingOptions();
47 | }
48 |
49 | @Override
50 | public OpenTracingOptions newOptions(JsonObject jsonObject) {
51 | return new OpenTracingOptions(jsonObject);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/java/io/vertx/tracing/opentracing/OpenTracingUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.opentracing;
12 |
13 | import io.opentracing.Span;
14 | import io.vertx.core.Context;
15 | import io.vertx.core.internal.ContextInternal;
16 |
17 | import static io.vertx.core.spi.context.storage.AccessMode.CONCURRENT;
18 | import static io.vertx.tracing.opentracing.OpenTracingTracerFactory.ACTIVE_SPAN;
19 |
20 | /**
21 | * OpenTracingContext adds helpers for associating and disassociating spans with the current {@link
22 | * Context}
23 | */
24 | public final class OpenTracingUtil {
25 |
26 | /**
27 | * Get the active span from the current {@link Context}
28 | *
29 | * @return a {@link Span} or null
30 | */
31 | public static Span getSpan() {
32 | ContextInternal c = ContextInternal.current();
33 | return c == null ? null : c.getLocal(ACTIVE_SPAN);
34 | }
35 |
36 | /**
37 | * Set the span as active on the context.
38 | *
39 | * @param span the span to associate with the context.
40 | */
41 | public static void setSpan(Span span) {
42 | if (span != null) {
43 | ContextInternal c = ContextInternal.current();
44 | if (c != null) {
45 | c.putLocal(ACTIVE_SPAN, CONCURRENT, span);
46 | }
47 | }
48 | }
49 |
50 | /**
51 | * Remove any active span on the context.
52 | */
53 | public static void clearContext() {
54 | ContextInternal c = ContextInternal.current();
55 | if (c != null) {
56 | c.removeLocal(ACTIVE_SPAN, CONCURRENT);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/java/io/vertx/tracing/opentracing/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | @io.vertx.codegen.annotations.ModuleGen(name = "vertx-opentracing", groupPackage = "io.vertx")
13 | package io.vertx.tracing.opentracing;
14 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider:
--------------------------------------------------------------------------------
1 | io.vertx.tracing.opentracing.OpenTracingTracerFactory
2 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/EventBusTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | package io.vertx.tests.opentracing;
13 |
14 | import io.opentracing.mock.MockSpan;
15 | import io.opentracing.mock.MockTracer;
16 | import io.vertx.core.*;
17 | import io.vertx.core.eventbus.DeliveryOptions;
18 | import io.vertx.core.http.*;
19 | import io.vertx.core.tracing.TracingPolicy;
20 | import io.vertx.ext.unit.TestContext;
21 | import io.vertx.ext.unit.junit.VertxUnitRunner;
22 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
23 | import org.junit.After;
24 | import org.junit.Before;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 |
28 | import java.util.function.Function;
29 |
30 | @RunWith(VertxUnitRunner.class)
31 | public class EventBusTest {
32 |
33 | private static final String ADDRESS = "the-address";
34 |
35 | private MockTracer tracer;
36 | private Vertx vertx;
37 | private HttpClient client;
38 |
39 | @Before
40 | public void before() {
41 | tracer = new MockTracer();
42 | vertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
43 | client = vertx.createHttpClient(new HttpClientOptions().setDefaultPort(8080));
44 | }
45 |
46 | @After
47 | public void after(TestContext context) {
48 | client.close();
49 | vertx.close().onComplete(context.asyncAssertSuccess());
50 | }
51 |
52 | @Test
53 | public void testEventBusSendPropagate(TestContext ctx) {
54 | testSend(ctx, TracingPolicy.PROPAGATE, 2);
55 | }
56 |
57 | @Test
58 | public void testEventBusSendIgnore(TestContext ctx) {
59 | testSend(ctx, TracingPolicy.IGNORE, 0);
60 | }
61 |
62 | @Test
63 | public void testEventBusSendAlways(TestContext ctx) {
64 | testSend(ctx, TracingPolicy.ALWAYS, 2);
65 | }
66 |
67 | private void testSend(TestContext ctx, TracingPolicy policy, int expected) {
68 | ProducerVerticle producerVerticle = new ProducerVerticle(getHttpServerPolicy(policy), vertx -> {
69 | vertx.eventBus().send(ADDRESS, "ping", new DeliveryOptions().setTracingPolicy(policy));
70 | return Future.succeededFuture();
71 | });
72 | vertx.deployVerticle(producerVerticle).onComplete(ctx.asyncAssertSuccess(d1 -> {
73 | Promise consumerPromise = Promise.promise();
74 | vertx.deployVerticle(new ConsumerVerticle(consumerPromise)).onComplete(ctx.asyncAssertSuccess(d2 -> {
75 | client.request(HttpMethod.GET, "/").onComplete(ctx.asyncAssertSuccess(req -> {
76 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
77 | ctx.assertEquals(200, resp.statusCode());
78 | consumerPromise.future().onComplete(ctx.asyncAssertSuccess(v -> {
79 | int count = 0;
80 | for (MockSpan span : tracer.finishedSpans()) {
81 | String operationName = span.operationName();
82 | if (!operationName.equals("GET")) {
83 | count++;
84 | ctx.assertEquals("send", operationName);
85 | ctx.assertEquals(ADDRESS, span.tags().get("message_bus.destination"));
86 | }
87 | }
88 | ctx.assertEquals(expected, count);
89 | }));
90 | }));
91 | }));
92 | }));
93 | }));
94 | }
95 |
96 | private TracingPolicy getHttpServerPolicy(TracingPolicy policy) {
97 | return policy == TracingPolicy.ALWAYS ? TracingPolicy.IGNORE : TracingPolicy.ALWAYS;
98 | }
99 |
100 | @Test
101 | public void testEventBusPublishProgagate(TestContext ctx) {
102 | testPublish(ctx, TracingPolicy.PROPAGATE, 3);
103 | }
104 |
105 | @Test
106 | public void testEventBusPublishIgnore(TestContext ctx) {
107 | testPublish(ctx, TracingPolicy.IGNORE, 0);
108 | }
109 |
110 | @Test
111 | public void testEventBusPublishAlways(TestContext ctx) {
112 | testPublish(ctx, TracingPolicy.ALWAYS, 3);
113 | }
114 |
115 | private void testPublish(TestContext ctx, TracingPolicy policy, int expected) {
116 | ProducerVerticle producerVerticle = new ProducerVerticle(getHttpServerPolicy(policy), vertx -> {
117 | vertx.eventBus().publish(ADDRESS, "ping", new DeliveryOptions().setTracingPolicy(policy));
118 | return Future.succeededFuture();
119 | });
120 | vertx.deployVerticle(producerVerticle).onComplete(ctx.asyncAssertSuccess(d1 -> {
121 | Promise consumer1Promise = Promise.promise();
122 | Promise consumer2Promise = Promise.promise();
123 | vertx.deployVerticle(new ConsumerVerticle(consumer1Promise)).onComplete(ctx.asyncAssertSuccess(d2 -> {
124 | vertx.deployVerticle(new ConsumerVerticle(consumer2Promise)).onComplete( ctx.asyncAssertSuccess(d3 -> {
125 | client.request(HttpMethod.GET, "/").onComplete(ctx.asyncAssertSuccess(req -> {
126 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
127 | ctx.assertEquals(200, resp.statusCode());
128 | Future.all(consumer1Promise.future(), consumer2Promise.future()).onComplete(ctx.asyncAssertSuccess(v -> {
129 | int count = 0;
130 | for (MockSpan span : tracer.finishedSpans()) {
131 | String operationName = span.operationName();
132 | if (!operationName.equals("GET")) {
133 | count++;
134 | ctx.assertEquals("publish", operationName);
135 | ctx.assertEquals(ADDRESS, span.tags().get("message_bus.destination"));
136 | }
137 | }
138 | ctx.assertEquals(expected, count);
139 | }));
140 | }));
141 | }));
142 | }));
143 | }));
144 | }));
145 | }
146 |
147 | @Test
148 | public void testEventBusRequestReplyPropagate(TestContext ctx) {
149 | testRequestReply(ctx, TracingPolicy.PROPAGATE, false, 2);
150 | }
151 |
152 | @Test
153 | public void testEventBusRequestReplyIgnore(TestContext ctx) {
154 | testRequestReply(ctx, TracingPolicy.IGNORE, false, 0);
155 | }
156 |
157 | @Test
158 | public void testEventBusRequestReplyAlways(TestContext ctx) {
159 | testRequestReply(ctx, TracingPolicy.ALWAYS, false, 2);
160 | }
161 |
162 | @Test
163 | public void testEventBusRequestReplyFailurePropagate(TestContext ctx) {
164 | testRequestReply(ctx, TracingPolicy.PROPAGATE, true, 2);
165 | }
166 |
167 | @Test
168 | public void testEventBusRequestReplyFailureIgnore(TestContext ctx) {
169 | testRequestReply(ctx, TracingPolicy.IGNORE, true, 0);
170 | }
171 |
172 | @Test
173 | public void testEventBusRequestReplyFailureAlways(TestContext ctx) {
174 | testRequestReply(ctx, TracingPolicy.ALWAYS, true, 2);
175 | }
176 |
177 | private void testRequestReply(TestContext ctx, TracingPolicy policy, boolean fail, int expected) {
178 | ProducerVerticle producerVerticle = new ProducerVerticle(getHttpServerPolicy(policy), vertx -> {
179 | Promise promise = Promise.promise();
180 | vertx.eventBus().request(ADDRESS, "ping", new DeliveryOptions().setTracingPolicy(policy)).onComplete(ar -> {
181 | if (ar.failed() == fail) {
182 | vertx.runOnContext(v -> promise.complete());
183 | } else {
184 | vertx.runOnContext(v -> promise.fail("Unexpected"));
185 | }
186 | });
187 | return promise.future();
188 | });
189 | vertx.deployVerticle(producerVerticle).onComplete(ctx.asyncAssertSuccess(d1 -> {
190 | vertx.deployVerticle(new ReplyVerticle(fail)).onComplete(ctx.asyncAssertSuccess(d2 -> {
191 | client.request(HttpMethod.GET, "/").onComplete(ctx.asyncAssertSuccess(req -> {
192 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
193 | ctx.assertEquals(200, resp.statusCode());
194 | int count = 0;
195 | for (MockSpan span : tracer.finishedSpans()) {
196 | String operationName = span.operationName();
197 | if (!operationName.equals("GET")) {
198 | count++;
199 | ctx.assertEquals("send", operationName);
200 | ctx.assertEquals(ADDRESS, span.tags().get("message_bus.destination"));
201 | }
202 | }
203 | ctx.assertEquals(expected, count);
204 | }));
205 | }));
206 | }));
207 | }));
208 | }
209 |
210 | private static class ProducerVerticle extends AbstractVerticle {
211 |
212 | private final TracingPolicy httpServerPolicy;
213 | private final Function> action;
214 |
215 | private ProducerVerticle(TracingPolicy httpServerPolicy, Function> action) {
216 | this.httpServerPolicy = httpServerPolicy;
217 | this.action = action;
218 | }
219 |
220 | @Override
221 | public void start(Promise startPromise) {
222 | vertx.createHttpServer(new HttpServerOptions().setTracingPolicy(httpServerPolicy))
223 | .requestHandler(this::onRequest)
224 | .listen(8080)
225 | .mapEmpty()
226 | .onComplete(startPromise);
227 | }
228 |
229 | private void onRequest(HttpServerRequest request) {
230 | action.apply(vertx).onComplete(ar -> {
231 | if (ar.succeeded()) {
232 | request.response().end();
233 | } else {
234 | ar.cause().printStackTrace();
235 | request.response().setStatusCode(500).end();
236 | }
237 | });
238 | }
239 | }
240 |
241 | private static class ConsumerVerticle extends AbstractVerticle {
242 |
243 | final Promise promise;
244 |
245 | ConsumerVerticle(Promise promise) {
246 | this.promise = promise;
247 | }
248 |
249 | @Override
250 | public void start(Promise startPromise) {
251 | vertx.eventBus().consumer(ADDRESS, msg -> {
252 | vertx.runOnContext(v -> promise.complete());
253 | }).completion().onComplete(startPromise);
254 | }
255 | }
256 |
257 | private static class ReplyVerticle extends AbstractVerticle {
258 |
259 | final boolean fail;
260 |
261 | ReplyVerticle(boolean fail) {
262 | this.fail = fail;
263 | }
264 |
265 | @Override
266 | public void start(Promise startPromise) {
267 | vertx.eventBus().consumer(ADDRESS, msg -> {
268 | if (fail) {
269 | msg.fail(10, "boom");
270 | } else {
271 | msg.reply(msg.body());
272 | }
273 | }).completion().onComplete(startPromise);
274 | }
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/HttpTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2023 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */package io.vertx.tests.opentracing;
11 |
12 | import io.opentracing.mock.MockSpan;
13 | import io.opentracing.mock.MockTracer;
14 | import io.vertx.core.Vertx;
15 | import io.vertx.core.http.HttpClient;
16 | import io.vertx.core.http.HttpClientOptions;
17 | import io.vertx.core.http.HttpServerOptions;
18 | import io.vertx.core.http.RequestOptions;
19 | import io.vertx.core.tracing.TracingPolicy;
20 | import io.vertx.ext.unit.Async;
21 | import io.vertx.ext.unit.TestContext;
22 | import io.vertx.ext.unit.junit.VertxUnitRunner;
23 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
24 | import org.junit.After;
25 | import org.junit.Before;
26 | import org.junit.Test;
27 | import org.junit.runner.RunWith;
28 |
29 | import java.util.List;
30 | import java.util.Map;
31 |
32 | import static org.junit.Assert.assertEquals;
33 |
34 | @RunWith(VertxUnitRunner.class)
35 | public class HttpTest {
36 |
37 | private MockTracer tracer;
38 | private Vertx vertx;
39 |
40 | @Before
41 | public void before() {
42 | tracer = new MockTracer();
43 | vertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
44 | }
45 |
46 | @After
47 | public void after(TestContext context) {
48 | vertx.close().onComplete(context.asyncAssertSuccess());
49 | }
50 |
51 | @Test
52 | public void testClientHttpResponseFailure(TestContext ctx) throws Exception {
53 | Async listenLatch = ctx.async();
54 | vertx.createHttpServer(new HttpServerOptions().setTracingPolicy(TracingPolicy.IGNORE))
55 | .requestHandler(req -> {
56 | req.connection().close();
57 | }).listen(8080, "localhost")
58 | .onComplete(ctx.asyncAssertSuccess(v -> listenLatch.complete()));
59 | listenLatch.awaitSuccess(20_000);
60 | Async closedLatch = ctx.async();
61 | HttpClient client;
62 | client = vertx.createHttpClient(new HttpClientOptions().setDefaultPort(8080).setTracingPolicy(TracingPolicy.ALWAYS));
63 | client.request(new RequestOptions().setPort(8080).setHost("localhost")).onComplete(ctx.asyncAssertSuccess(req -> {
64 | req.send();
65 | req.connection().closeHandler(v -> closedLatch.complete());
66 | }));
67 | closedLatch.awaitSuccess(20_000);
68 | long now = System.currentTimeMillis();
69 | while (tracer.finishedSpans().size() < 1) {
70 | ctx.assertTrue(System.currentTimeMillis() - now < 20_000);
71 | Thread.sleep(10);
72 | }
73 | List spans = tracer.finishedSpans();
74 | assertEquals(1, spans.size());
75 | MockSpan span = spans.get(0);
76 | ctx.assertEquals("GET", span.operationName());
77 | Map tags = span.tags();
78 | assertEquals("http://localhost:8080/", tags.get("http.url"));
79 | assertEquals(true, tags.get("error"));
80 | assertEquals("GET", tags.get("http.method"));
81 | assertEquals("client", tags.get("span.kind"));
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/OpenTracingTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentracing;
12 |
13 | import io.opentracing.mock.MockSpan;
14 | import io.opentracing.mock.MockTracer;
15 | import io.opentracing.propagation.Format;
16 | import io.opentracing.propagation.TextMapAdapter;
17 | import io.opentracing.tag.Tags;
18 | import io.vertx.core.Vertx;
19 | import io.vertx.core.http.HttpClient;
20 | import io.vertx.core.http.HttpClientOptions;
21 | import io.vertx.core.http.HttpMethod;
22 | import io.vertx.core.http.HttpServerOptions;
23 | import io.vertx.core.internal.ContextInternal;
24 | import io.vertx.core.tracing.TracingPolicy;
25 | import io.vertx.ext.unit.Async;
26 | import io.vertx.ext.unit.TestContext;
27 | import io.vertx.ext.unit.junit.VertxUnitRunner;
28 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
29 | import org.junit.After;
30 | import org.junit.Before;
31 | import org.junit.Test;
32 | import org.junit.runner.RunWith;
33 |
34 | import java.net.HttpURLConnection;
35 | import java.net.URL;
36 | import java.util.HashMap;
37 | import java.util.List;
38 | import java.util.Optional;
39 |
40 | import static io.vertx.tracing.opentracing.OpenTracingTracerFactory.ACTIVE_SPAN;
41 | import static org.junit.Assert.assertEquals;
42 |
43 | @RunWith(VertxUnitRunner.class)
44 | public class OpenTracingTest {
45 |
46 | private Vertx vertx;
47 | private MockTracer tracer;
48 |
49 | @Before
50 | public void before() {
51 | tracer = new MockTracer();
52 | vertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
53 | }
54 |
55 | @After
56 | public void after(TestContext ctx) {
57 | vertx.close().onComplete(ctx.asyncAssertSuccess());
58 | }
59 |
60 | List waitUntil(int expected) throws Exception {
61 | long now = System.currentTimeMillis();
62 | while (tracer.finishedSpans().size() < expected && (System.currentTimeMillis() - now) < 10000 ) {
63 | Thread.sleep(10);
64 | }
65 | assertEquals(expected, tracer.finishedSpans().size());
66 | return tracer.finishedSpans();
67 | }
68 |
69 | void assertSingleSpan(List spans) {
70 | long result = spans.stream().map(span -> span.context().traceId()).distinct().count();
71 | assertEquals(1, result);
72 | }
73 |
74 | @Test
75 | public void testHttpServerRequestIgnorePolicy1(TestContext ctx) throws Exception {
76 | testHttpServerRequestPolicy(ctx, new HttpServerOptions().setTracingPolicy(TracingPolicy.IGNORE), true, false);
77 | }
78 |
79 | @Test
80 | public void testHttpServerRequestIgnorePolicy2(TestContext ctx) throws Exception {
81 | testHttpServerRequestPolicy(ctx, new HttpServerOptions().setTracingPolicy(TracingPolicy.IGNORE), false, false);
82 | }
83 |
84 | @Test
85 | public void testHttpServerRequestPropagatePolicy1(TestContext ctx) throws Exception {
86 | testHttpServerRequestPolicy(ctx, new HttpServerOptions().setTracingPolicy(TracingPolicy.PROPAGATE), true, true);
87 | }
88 |
89 | @Test
90 | public void testHttpServerRequestPropagatePolicy2(TestContext ctx) throws Exception {
91 | testHttpServerRequestPolicy(ctx, new HttpServerOptions().setTracingPolicy(TracingPolicy.PROPAGATE), false, false);
92 | }
93 |
94 | @Test
95 | public void testHttpServerRequestSupportPolicy1(TestContext ctx) throws Exception {
96 | testHttpServerRequestPolicy(ctx, new HttpServerOptions().setTracingPolicy(TracingPolicy.ALWAYS), false, true);
97 | }
98 |
99 | @Test
100 | public void testHttpServerRequestSupportPolicy2(TestContext ctx) throws Exception {
101 | testHttpServerRequestPolicy(ctx, new HttpServerOptions().setTracingPolicy(TracingPolicy.ALWAYS), true, true);
102 | }
103 |
104 | private void testHttpServerRequestPolicy(TestContext ctx,
105 | HttpServerOptions options,
106 | boolean createTrace,
107 | boolean expectTrace) throws Exception {
108 | Async listenLatch = ctx.async();
109 | vertx.createHttpServer(options).requestHandler(req -> {
110 | if (expectTrace) {
111 | ctx.assertNotNull(ContextInternal.current().getLocal(ACTIVE_SPAN));
112 | } else {
113 | ctx.assertNull(ContextInternal.current().getLocal(ACTIVE_SPAN));
114 | }
115 | req.response().end();
116 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.countDown()));
117 | listenLatch.awaitSuccess();
118 | sendRequest(createTrace);
119 | if (expectTrace) {
120 | List spans = waitUntil(1);
121 | MockSpan span = spans.get(0);
122 | assertEquals("GET", span.operationName());
123 | assertEquals("GET", span.tags().get("http.method"));
124 | assertEquals("http://localhost:8080/", span.tags().get("http.url"));
125 | assertEquals("200", span.tags().get("http.status_code"));
126 | }
127 | }
128 |
129 | @Test
130 | public void testHttpClientRequestIgnorePolicy1(TestContext ctx) throws Exception {
131 | testHttpClientRequest(ctx, TracingPolicy.IGNORE, true, 1);
132 | }
133 |
134 | @Test
135 | public void testHttpClientRequestIgnorePolicy2(TestContext ctx) throws Exception {
136 | testHttpClientRequest(ctx, TracingPolicy.IGNORE, false, 0);
137 | }
138 |
139 | @Test
140 | public void testHttpClientRequestPropagatePolicy1(TestContext ctx) throws Exception {
141 | testHttpClientRequest(ctx, TracingPolicy.PROPAGATE, true, 3);
142 | }
143 |
144 | @Test
145 | public void testHttpClientRequestPropagatePolicy2(TestContext ctx) throws Exception {
146 | testHttpClientRequest(ctx, TracingPolicy.PROPAGATE, false, 0);
147 | }
148 |
149 | @Test
150 | public void testHttpClientRequestAlwaysPolicy1(TestContext ctx) throws Exception {
151 | testHttpClientRequest(ctx, TracingPolicy.ALWAYS, true, 3);
152 | }
153 |
154 | @Test
155 | public void testHttpClientRequestAlwaysPolicy2(TestContext ctx) throws Exception {
156 | testHttpClientRequest(ctx, TracingPolicy.ALWAYS, false, 2);
157 | }
158 |
159 | private List testHttpClientRequest(TestContext ctx, TracingPolicy policy, boolean createTrace, int expectedTrace) throws Exception {
160 | Async listenLatch = ctx.async(2);
161 | HttpClient c = vertx.createHttpClient(new HttpClientOptions().setTracingPolicy(policy));
162 | vertx.createHttpServer(new HttpServerOptions().setTracingPolicy(TracingPolicy.PROPAGATE)).requestHandler(req -> {
163 | c.request(HttpMethod.GET, 8081, "localhost", "/").onComplete(ctx.asyncAssertSuccess(clientReq -> {
164 | clientReq.send().onComplete(ctx.asyncAssertSuccess(clientResp -> {
165 | req.response().end();
166 | }));
167 | }));
168 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.countDown()));
169 | vertx.createHttpServer(new HttpServerOptions().setTracingPolicy(TracingPolicy.PROPAGATE)).requestHandler(req -> {
170 | req.response().end();
171 | }).listen(8081).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.countDown()));
172 | listenLatch.awaitSuccess();
173 | sendRequest(createTrace);
174 | Thread.sleep(1000);
175 | assertEquals(expectedTrace, tracer.finishedSpans().size());
176 | List spans = tracer.finishedSpans();
177 | if (expectedTrace > 0) {
178 | assertSingleSpan(spans);
179 | Optional opt = spans.stream().filter(span -> {
180 | try {
181 | assertEquals("GET", span.operationName());
182 | assertEquals("GET", span.tags().get("http.method"));
183 | assertEquals(createTrace ? "http://localhost:8080/" : "http://localhost:8081/", span.tags().get("http.url"));
184 | assertEquals("200", span.tags().get("http.status_code"));
185 | return true;
186 | } catch (AssertionError e) {
187 | return false;
188 | }
189 | }).findFirst();
190 | ctx.assertTrue(opt.isPresent());
191 | }
192 | return spans;
193 | }
194 |
195 | private void sendRequest(boolean withTrace) throws Exception {
196 | URL url = new URL("http://localhost:8080");
197 | HttpURLConnection con = (HttpURLConnection) url.openConnection();
198 | con.setRequestMethod("GET");
199 | if (withTrace) {
200 | MockSpan span = tracer.buildSpan("test")
201 | .withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_CLIENT)
202 | .withTag(Tags.COMPONENT.getKey(), "vertx")
203 | .start();
204 | HashMap headers = new HashMap<>();
205 | tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers));
206 | headers.forEach(con::setRequestProperty);
207 | }
208 | assertEquals(200, con.getResponseCode());
209 | }
210 |
211 | @Test
212 | public void testEventBus(TestContext ctx) throws Exception {
213 | Async listenLatch = ctx.async(2);
214 | vertx.createHttpServer().requestHandler(req -> {
215 | vertx.eventBus().request("the-address", "ping").onComplete(ctx.asyncAssertSuccess(resp -> {
216 | req.response().end();
217 | }));
218 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.countDown()));
219 | vertx.eventBus().consumer("the-address", msg -> {
220 | msg.reply("pong");
221 | });
222 | vertx.createHttpServer().requestHandler(req -> {
223 | req.response().end();
224 | }).listen(8081).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.countDown()));
225 | listenLatch.awaitSuccess();
226 | sendRequest(true);
227 | List spans = waitUntil(3);
228 | assertSingleSpan(spans);
229 | MockSpan span = spans.get(0);
230 | assertEquals("send", span.operationName());
231 | assertEquals("the-address", span.tags().get("message_bus.destination"));
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/OpenTracingUtilTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentracing;
12 |
13 | import io.opentracing.Span;
14 | import io.opentracing.mock.MockTracer;
15 | import io.vertx.core.Vertx;
16 | import io.vertx.core.internal.ContextInternal;
17 | import io.vertx.ext.unit.TestContext;
18 | import io.vertx.ext.unit.junit.VertxUnitRunner;
19 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
20 | import io.vertx.tracing.opentracing.OpenTracingUtil;
21 | import org.junit.After;
22 | import org.junit.Before;
23 | import org.junit.Test;
24 | import org.junit.runner.RunWith;
25 |
26 | import static io.vertx.core.spi.context.storage.AccessMode.CONCURRENT;
27 | import static io.vertx.tracing.opentracing.OpenTracingTracerFactory.ACTIVE_SPAN;
28 | import static org.junit.Assert.assertNull;
29 | import static org.junit.Assert.assertSame;
30 |
31 | @RunWith(VertxUnitRunner.class)
32 | public class OpenTracingUtilTest {
33 |
34 | private Vertx vertx;
35 | private MockTracer tracer;
36 |
37 | @Before
38 | public void before() {
39 | tracer = new MockTracer();
40 | vertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
41 | }
42 |
43 | @After
44 | public void after(TestContext ctx) {
45 | vertx.close().onComplete(ctx.asyncAssertSuccess());
46 | }
47 |
48 | @Test
49 | public void getSpan_should_retrieve_a_span_from_the_currentContext(TestContext ctx) {
50 | Span span = tracer.buildSpan("test").start();
51 | vertx.runOnContext(ignored -> {
52 | assertNull(OpenTracingUtil.getSpan());
53 | ContextInternal context = (ContextInternal) Vertx.currentContext();
54 | context.putLocal(ACTIVE_SPAN, CONCURRENT, span);
55 |
56 | assertSame(span, OpenTracingUtil.getSpan());
57 | });
58 | }
59 |
60 | @Test
61 | public void getSpan_should_return_null_when_there_is_no_current_context(TestContext ctx) {
62 | Span span = tracer.buildSpan("test").start();
63 | OpenTracingUtil.setSpan(span);
64 | assertNull(OpenTracingUtil.getSpan());
65 | }
66 |
67 | @Test
68 | public void setSpan_should_put_the_span_on_the_current_context() {
69 | Span span = tracer.buildSpan("test").start();
70 | vertx.runOnContext(ignored -> {
71 | assertNull(OpenTracingUtil.getSpan());
72 | OpenTracingUtil.setSpan(span);
73 |
74 | ContextInternal context = (ContextInternal) Vertx.currentContext();
75 | assertSame(span, context.getLocal(ACTIVE_SPAN));
76 | });
77 | }
78 |
79 | @Test
80 | public void clearContext_should_remove_any_span_from_the_context() {
81 | Span span = tracer.buildSpan("test").start();
82 | vertx.runOnContext(ignored -> {
83 | assertNull(OpenTracingUtil.getSpan());
84 | OpenTracingUtil.setSpan(span);
85 |
86 | OpenTracingUtil.clearContext();
87 | assertNull(OpenTracingUtil.getSpan());
88 | });
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/SqlClientTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.opentracing;
12 |
13 | import io.opentracing.mock.MockSpan;
14 | import io.opentracing.mock.MockTracer;
15 | import io.vertx.core.Vertx;
16 | import io.vertx.core.http.HttpClient;
17 | import io.vertx.core.http.HttpClientOptions;
18 | import io.vertx.core.http.HttpMethod;
19 | import io.vertx.core.tracing.TracingPolicy;
20 | import io.vertx.ext.unit.Async;
21 | import io.vertx.ext.unit.TestContext;
22 | import io.vertx.ext.unit.junit.VertxUnitRunner;
23 | import io.vertx.pgclient.PgBuilder;
24 | import io.vertx.pgclient.PgConnectOptions;
25 | import io.vertx.sqlclient.Pool;
26 | import io.vertx.sqlclient.Row;
27 | import io.vertx.sqlclient.RowSet;
28 | import io.vertx.sqlclient.Tuple;
29 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
30 | import org.junit.*;
31 | import org.junit.runner.RunWith;
32 | import org.testcontainers.containers.PostgreSQLContainer;
33 |
34 | import java.util.List;
35 |
36 | import static java.util.concurrent.TimeUnit.MICROSECONDS;
37 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
38 | import static org.junit.Assert.assertEquals;
39 | import static org.junit.Assert.assertTrue;
40 |
41 | @RunWith(VertxUnitRunner.class)
42 | public class SqlClientTest {
43 |
44 | private static PostgreSQLContainer> server;
45 | private static PgConnectOptions connectOptions;
46 | private Vertx vertx;
47 | private MockTracer tracer;
48 | private Pool pool;
49 |
50 | @BeforeClass
51 | public static void startDB() {
52 | server = new PostgreSQLContainer<>("postgres:10")
53 | .withDatabaseName("postgres")
54 | .withUsername("postgres")
55 | .withPassword("postgres");
56 | server.start();
57 | connectOptions = new PgConnectOptions()
58 | .setUser("postgres")
59 | .setPassword("postgres")
60 | .setDatabase("postgres")
61 | .setHost(server.getHost())
62 | .setPort(server.getMappedPort(5432));
63 |
64 | }
65 |
66 | @AfterClass
67 | public static void stopDB() {
68 | server.stop();
69 | }
70 |
71 | @Before
72 | public void before() {
73 | tracer = new MockTracer();
74 | vertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
75 | pool = PgBuilder.pool().connectingTo(connectOptions).using(vertx).build();
76 | }
77 |
78 | @After
79 | public void after(TestContext ctx) {
80 | vertx.close().onComplete(ctx.asyncAssertSuccess());
81 | }
82 |
83 | List waitUntil(int expected) throws Exception {
84 | long now = System.currentTimeMillis();
85 | while (tracer.finishedSpans().size() < expected && (System.currentTimeMillis() - now) < 10000 ) {
86 | Thread.sleep(10);
87 | }
88 | assertEquals(expected, tracer.finishedSpans().size());
89 | return tracer.finishedSpans();
90 | }
91 |
92 | void assertSingleSpan(List spans) {
93 | long result = spans.stream().map(span -> span.context().traceId()).distinct().count();
94 | assertEquals(1, result);
95 | }
96 |
97 | @Test
98 | public void testPreparedQuery(TestContext ctx) throws Exception {
99 | Async listenLatch = ctx.async();
100 | long baseDurationInMs = 500;
101 | vertx.createHttpServer().requestHandler(req -> {
102 | pool.preparedQuery("SELECT $1 \"VAL\"")
103 | .execute(Tuple.of("Hello World"))
104 | .onComplete(ar -> {
105 | vertx.setTimer(baseDurationInMs, (__) -> {
106 | if (ar.succeeded()) {
107 | RowSet rows = ar.result();
108 | req.response()
109 | .end();
110 | } else {
111 | req.response()
112 | .setStatusCode(500)
113 | .end();
114 | }
115 | });
116 | });
117 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.complete()));
118 | listenLatch.awaitSuccess();
119 | Async responseLatch = ctx.async();
120 | HttpClient client = vertx.createHttpClient(new HttpClientOptions().setTracingPolicy(TracingPolicy.ALWAYS));
121 | client.request(HttpMethod.GET, 8080, "localhost", "/").onComplete(ctx.asyncAssertSuccess(req -> {
122 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
123 | ctx.assertEquals(200, resp.statusCode());
124 | responseLatch.complete();
125 | }));
126 | }));
127 | responseLatch.awaitSuccess();
128 | List spans = waitUntil(3);
129 | MockSpan requestSpan = spans.get(1);
130 | assertEquals("GET", requestSpan.operationName());
131 | assertEquals("GET", requestSpan.tags().get("http.method"));
132 | assertEquals("http://localhost:8080/", requestSpan.tags().get("http.url"));
133 | assertEquals("200", requestSpan.tags().get("http.status_code"));
134 | assertTrue(MILLISECONDS.convert(requestSpan.finishMicros() - requestSpan.startMicros(), MICROSECONDS) > baseDurationInMs);
135 | MockSpan querySpan = spans.get(0);
136 | assertEquals("Query", querySpan.operationName());
137 | assertEquals("client", querySpan.tags().get("span.kind"));
138 | assertEquals("SELECT $1 \"VAL\"", querySpan.tags().get("db.statement"));
139 | assertEquals("sql", querySpan.tags().get("db.type"));
140 | assertEquals("postgres", querySpan.tags().get("db.user"));
141 | assertEquals("postgres", querySpan.tags().get("db.instance"));
142 | assertEquals("postgresql", querySpan.tags().get("db.system"));
143 | assertEquals(querySpan.parentId(), requestSpan.context().spanId());
144 | assertEquals(querySpan.context().traceId(), requestSpan.context().traceId());
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/it/BasicTracingIT.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.opentracing.it;
2 |
3 | import java.time.Duration;
4 |
5 | import io.opentracing.Tracer;
6 | import io.restassured.parsing.Parser;
7 | import io.restassured.response.Response;
8 | import io.vertx.core.Vertx;
9 | import io.vertx.core.http.HttpClient;
10 | import io.vertx.core.http.HttpMethod;
11 | import io.vertx.junit5.Checkpoint;
12 | import io.vertx.junit5.VertxExtension;
13 | import io.vertx.junit5.VertxTestContext;
14 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
15 | import io.vertx.tests.opentracing.it.jaegercontainer.JaegerContainerAllIn;
16 | import org.junit.jupiter.api.AfterAll;
17 | import org.junit.jupiter.api.BeforeAll;
18 | import org.junit.jupiter.api.DisplayName;
19 | import org.junit.jupiter.api.Test;
20 | import org.junit.jupiter.api.extension.ExtendWith;
21 | import org.slf4j.Logger;
22 | import org.slf4j.LoggerFactory;
23 |
24 | import static io.restassured.RestAssured.given;
25 | import static org.awaitility.Awaitility.await;
26 | import static org.hamcrest.Matchers.hasItems;
27 | import static org.hamcrest.Matchers.hasSize;
28 | import static org.assertj.core.api.Assertions.assertThat;
29 |
30 | @ExtendWith(VertxExtension.class)
31 | public class BasicTracingIT {
32 | private static final Logger LOGGER = LoggerFactory.getLogger(BasicTracingIT.class);
33 |
34 | private static Vertx tracedVertx;
35 |
36 | private static Tracer tracer;
37 |
38 | private static final String JAEGER_SERVICE_NAME = "test-traced-service";
39 |
40 | private static final JaegerContainerAllIn JAEGER_ALL_IN_ONE = new JaegerContainerAllIn("quay.io/jaegertracing/all-in-one:latest");
41 |
42 | @BeforeAll
43 | public static void deploy(VertxTestContext context) {
44 |
45 | Checkpoint deployCheck = context.checkpoint(2);
46 | Checkpoint statusCheck = context.checkpoint();
47 | context.verify(() -> {
48 | JAEGER_ALL_IN_ONE.start();
49 | tracer = JAEGER_ALL_IN_ONE.createTracer(JAEGER_SERVICE_NAME);
50 | deployCheck.flag();
51 |
52 | tracedVertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
53 | HttpClient client = tracedVertx.createHttpClient();
54 | tracedVertx.deployVerticle(ServerVerticle.class.getName()).onComplete(context.succeeding(id -> {
55 | client.request(HttpMethod.GET, 8080,"localhost","/health")
56 | .compose(req -> req.send().compose(resp -> resp.end().map(resp.statusCode()))).
57 | onComplete(context.succeeding(sc -> {
58 | context.verify(() ->{
59 | assertThat(sc).isEqualTo(200);
60 | statusCheck.flag();
61 | });
62 | }));
63 | deployCheck.flag();
64 | }));
65 | });
66 | }
67 |
68 | @Test
69 | @DisplayName("simpleTrace")
70 | public void simpleTrace(VertxTestContext context) {
71 | System.out.println("Container name " + JAEGER_SERVICE_NAME);
72 | Checkpoint checkTrace = context.checkpoint();
73 | await().atMost(Duration.ofSeconds(12)).untilAsserted(() -> {
74 | Response response = given()
75 | .get("http://localhost:" + JAEGER_ALL_IN_ONE.getQueryPort() + "/api/traces?service=" + JAEGER_SERVICE_NAME);
76 | response
77 | .then().statusCode(200)
78 | .defaultParser(Parser.JSON)
79 | .body("data", hasSize(1))
80 | .body("data[0].spans", hasSize(1))
81 | .body("data[0].spans.operationName", hasItems("GET"))
82 | .body("data[0].spans.find {" +
83 | " it.operationName == 'GET' }" +
84 | ".tags.collect { \"${it.key}=${it.value}\".toString() }",
85 | hasItems(
86 | "http.status_code=200",
87 | "component=vertx",
88 | "span.kind=server",
89 | "sampler.type=const", // pom.xml JAEGER_SAMPLER_TYPE>const
90 | "http.url=http://localhost:8080/health",
91 | "http.method=GET"));
92 | checkTrace.flag();
93 | });
94 | }
95 |
96 | @AfterAll
97 | public static void cleanUp() {
98 | JAEGER_ALL_IN_ONE.stop();
99 | tracedVertx.close().onComplete(result -> {
100 | if (result.succeeded()) LOGGER.debug("Closing traced vertx OK.");
101 | else LOGGER.error("Closing traced vertx FAILED: " + result.cause());
102 | });
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/it/CustomTracingIT.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.opentracing.it;
2 |
3 | import java.time.Duration;
4 |
5 | import io.opentracing.Span;
6 | import io.opentracing.Tracer;
7 | import io.restassured.parsing.Parser;
8 | import io.restassured.response.Response;
9 | import io.vertx.core.Vertx;
10 | import io.vertx.core.eventbus.DeliveryOptions;
11 | import io.vertx.core.http.HttpClient;
12 | import io.vertx.core.http.HttpClientOptions;
13 | import io.vertx.core.http.HttpClientResponse;
14 | import io.vertx.core.http.HttpMethod;
15 | import io.vertx.core.tracing.TracingPolicy;
16 | import io.vertx.junit5.Checkpoint;
17 | import io.vertx.junit5.VertxExtension;
18 | import io.vertx.junit5.VertxTestContext;
19 | import io.vertx.tracing.opentracing.OpenTracingTracerFactory;
20 | import io.vertx.tracing.opentracing.OpenTracingUtil;
21 | import io.vertx.tests.opentracing.it.jaegercontainer.JaegerContainerAllIn;
22 | import org.junit.jupiter.api.AfterAll;
23 | import org.junit.jupiter.api.BeforeAll;
24 | import org.junit.jupiter.api.BeforeEach;
25 | import org.junit.jupiter.api.DisplayName;
26 | import org.junit.jupiter.api.Test;
27 | import org.junit.jupiter.api.extension.ExtendWith;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | import static io.restassured.RestAssured.given;
32 | import static org.assertj.core.api.Assertions.assertThat;
33 | import static org.awaitility.Awaitility.await;
34 | import static org.hamcrest.Matchers.containsString;
35 | import static org.hamcrest.Matchers.hasSize;
36 | import static org.hamcrest.Matchers.hasToString;
37 | import static org.junit.jupiter.api.Assertions.assertEquals;
38 |
39 | @ExtendWith(VertxExtension.class)
40 | public class CustomTracingIT {
41 | private static final Logger LOGGER = LoggerFactory.getLogger(CustomTracingIT.class);
42 |
43 | private static final JaegerContainerAllIn JAEGER_ALL_IN_ONE = new JaegerContainerAllIn("quay.io/jaegertracing/all-in-one:latest");
44 |
45 | // it has to match with environment variable test-traced-service in pom.xml
46 | private static final String JAEGER_SERVICE_NAME = "test-traced-service";
47 |
48 | private static Vertx tracedVertx;
49 |
50 | private static Tracer tracer;
51 |
52 | @BeforeAll
53 | public static void deploy(VertxTestContext context) {
54 | Checkpoint deployCheck = context.checkpoint(2);
55 | Checkpoint statusCheck = context.checkpoint();
56 | context.verify(() -> {
57 | JAEGER_ALL_IN_ONE.start();
58 | tracer = JAEGER_ALL_IN_ONE.createTracer(JAEGER_SERVICE_NAME);
59 | deployCheck.flag();
60 | tracedVertx = Vertx.builder().withTracer(new OpenTracingTracerFactory(tracer)).build();
61 | HttpClient client = tracedVertx.createHttpClient();
62 | tracedVertx.deployVerticle(ServerVerticle.class.getName()).onComplete(context.succeeding(id -> {
63 | client.request(HttpMethod.GET, 8080,"localhost","/health")
64 | .compose(req -> req.send().compose(resp -> resp.end().map(resp.statusCode()))).
65 | onComplete(context.succeeding(sc -> {
66 | context.verify(() ->{
67 | assertThat(sc).isEqualTo(200);
68 | statusCheck.flag();
69 | });
70 | }));
71 | deployCheck.flag();
72 | }));
73 | });
74 | }
75 |
76 | @AfterAll
77 | public static void cleanUp() {
78 | tracer.close();
79 | JAEGER_ALL_IN_ONE.stop();
80 | tracedVertx.close().onComplete(result -> {
81 | if (result.succeeded()) LOGGER.debug("Closing traced vertx OK.");
82 | else LOGGER.error("Closing traced vertx FAILED: " + result.cause());
83 | });
84 | }
85 |
86 | @BeforeEach
87 | public void httpCall(VertxTestContext context) {
88 | final Checkpoint checkpoint = context.checkpoint();
89 | HttpClient client = tracedVertx.createHttpClient(
90 | new HttpClientOptions()
91 | .setTracingPolicy(TracingPolicy.ALWAYS)
92 | .setDefaultPort(8080)
93 | .setDefaultHost("localhost")
94 | );
95 | // custom span
96 | Span span = tracer.buildSpan("custom-span")
97 | .withTag("custom-key", "custom-value")
98 | .start();
99 | OpenTracingUtil.setSpan(span);
100 | // client GET
101 | client.request(HttpMethod.GET, 8080, "localhost", "/")
102 | .compose(httpClientRequest -> httpClientRequest.send().compose(HttpClientResponse::body)
103 | .onSuccess(body -> {
104 | assertEquals("Hello from the Server", body.toString());
105 | client.close();
106 | }).onFailure(throwable -> LOGGER.error("Error: {}", throwable.getCause().toString())));
107 | span.finish();
108 | // send eventbus-message
109 | DeliveryOptions options = new DeliveryOptions().setTracingPolicy(TracingPolicy.ALWAYS);
110 | tracedVertx.eventBus().send("eventbus-address", "eventbus-message", options);
111 | checkpoint.flag();
112 | }
113 |
114 | @Test
115 | @DisplayName("traceJson")
116 | public void traceJson(VertxTestContext context) {
117 | Checkpoint checkTrace = context.checkpoint();
118 | await().atMost(Duration.ofSeconds(10)).untilAsserted(() -> {
119 | Response response = given()
120 | .get("http://localhost:" + JAEGER_ALL_IN_ONE.getQueryPort() + "/api/traces?service=" + JAEGER_SERVICE_NAME);
121 | response
122 | .then().statusCode(200)
123 | .defaultParser(Parser.JSON)
124 | .body("data", hasSize(4))
125 | .body("data.spans.tags", hasToString(containsString(
126 | "{key=http.status_code, type=string, value=200}")))
127 | // trace contains call to /health
128 | .body("data.spans.tags", hasToString(containsString(
129 | "{key=http.url, type=string, value=http://localhost:8080/health}")))
130 | // call of client
131 | .body("data.spans.tags", hasToString(containsString(
132 | "{key=span.kind, type=string, value=client}")))
133 | // call of server
134 | .body("data.spans.tags", hasToString(containsString(
135 | "{key=span.kind, type=string, value=server}")))
136 |
137 | // contains custom span
138 | .body("data.spans.operationName", hasToString(containsString("custom-span")))
139 | // with custom K,V
140 | .body("data.spans.tags", hasToString(containsString(
141 | "{key=custom-key, type=string, value=custom-value}")))
142 |
143 | // call to eventbus with address
144 | .body("data.spans.tags", hasToString(containsString(
145 | "{key=message_bus.destination, type=string, value=eventbus-address}")))
146 | // log with error
147 | .body("data.spans.logs", hasToString(containsString(
148 | "{key=error.kind, type=string, value=Exception}")))
149 | // with exception message
150 | .body("data.spans.logs", hasToString(containsString(
151 | "{key=message, type=string, value=No handlers for address eventbus-address}")));
152 | checkTrace.flag();
153 | });
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/it/ServerVerticle.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.opentracing.it;
2 |
3 | import io.vertx.core.AbstractVerticle;
4 | import io.vertx.core.Promise;
5 |
6 | public class ServerVerticle extends AbstractVerticle {
7 |
8 | @Override
9 | public void start(Promise startPromise) throws Exception {
10 | vertx.createHttpServer().requestHandler(httpServerRequest -> {
11 | httpServerRequest.response().end("Hello from the Server");
12 | }).listen(8080).onComplete(httpServerAsyncResult -> {
13 | if (httpServerAsyncResult.succeeded()) {
14 | startPromise.complete();
15 | } else {
16 | startPromise.fail(httpServerAsyncResult.cause());
17 | }
18 | });
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/vertx-opentracing/src/test/java/io/vertx/tests/opentracing/it/jaegercontainer/JaegerContainerAllIn.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.opentracing.it.jaegercontainer;
2 |
3 |
4 | import java.util.Collections;
5 | import java.util.Set;
6 |
7 | import io.grpc.ManagedChannel;
8 | import io.grpc.ManagedChannelBuilder;
9 | import io.jaegertracing.api_v2.QueryServiceGrpc;
10 | import io.jaegertracing.internal.JaegerTracer;
11 | import io.jaegertracing.internal.reporters.RemoteReporter;
12 | import io.jaegertracing.internal.samplers.ConstSampler;
13 | import io.jaegertracing.spi.Reporter;
14 | import io.jaegertracing.spi.Sender;
15 | import io.jaegertracing.testcontainers.JaegerAllInOne;
16 | import io.jaegertracing.thrift.internal.senders.HttpSender;
17 | import org.apache.thrift.transport.TTransportException;
18 | import org.testcontainers.containers.GenericContainer;
19 | import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
20 |
21 | public class JaegerContainerAllIn extends GenericContainer {
22 |
23 | public static final int JAEGER_QUERY_PORT = 16686;
24 |
25 | public static final int JAEGER_COLLECTOR_THRIFT_PORT = 14268;
26 |
27 | public static final int JAEGER_COLLECTOR_GRPC_PORT = 14250;
28 |
29 | public static final int JAEGER_ADMIN_PORT = 14269;
30 |
31 | public static final int ZIPKIN_PORT = 9411;
32 |
33 | public JaegerContainerAllIn(String dockerImageName) {
34 | super(dockerImageName);
35 | init();
36 | }
37 |
38 | protected void init() {
39 | waitingFor(new BoundPortHttpWaitStrategy(JAEGER_ADMIN_PORT));
40 | withEnv("COLLECTOR_ZIPKIN_HTTP_PORT",
41 | String.valueOf(ZIPKIN_PORT));
42 | withExposedPorts(JAEGER_ADMIN_PORT,
43 | JAEGER_COLLECTOR_THRIFT_PORT,
44 | JAEGER_COLLECTOR_GRPC_PORT,
45 | JAEGER_QUERY_PORT,
46 | ZIPKIN_PORT);
47 | }
48 |
49 | public int getCollectorThriftPort() {
50 | return getMappedPort(JAEGER_COLLECTOR_THRIFT_PORT);
51 | }
52 |
53 | public int getQueryPort() {
54 | return getMappedPort(JAEGER_QUERY_PORT);
55 | }
56 |
57 | public JaegerTracer createTracer(String serviceName) throws TTransportException {
58 | String endpoint =
59 | String.format("http://localhost:%d/api/traces",
60 | getCollectorThriftPort());
61 | Sender sender = new HttpSender.Builder(endpoint)
62 | .build();
63 | Reporter reporter = new RemoteReporter.Builder()
64 | .withSender(sender)
65 | .build();
66 | JaegerTracer.Builder tracerBuilder = new JaegerTracer.Builder(serviceName)
67 | .withSampler(new ConstSampler(true))
68 | .withReporter(reporter);
69 | return tracerBuilder.build();
70 | }
71 |
72 | public QueryServiceGrpc.QueryServiceBlockingStub createBlockingQueryService() {
73 | ManagedChannel channel =
74 | ManagedChannelBuilder.forTarget(
75 | String.format("localhost:%d",
76 | getQueryPort())).usePlaintext().build();
77 | return QueryServiceGrpc.newBlockingStub(channel);
78 | }
79 |
80 | public static class BoundPortHttpWaitStrategy extends HttpWaitStrategy {
81 | private final int port;
82 |
83 | public BoundPortHttpWaitStrategy(int port) {
84 | this.port = port;
85 | }
86 |
87 | @Override
88 | protected Set getLivenessCheckPorts() {
89 | int mapptedPort = this.waitStrategyTarget.getMappedPort(port);
90 | return Collections.singleton(mapptedPort);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/vertx-zipkin/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | 4.0.0
19 |
20 |
21 | io.vertx
22 | vertx-tracing-parent
23 | 5.1.0-SNAPSHOT
24 |
25 |
26 | vertx-zipkin
27 |
28 | Vert.x Zipkin
29 |
30 |
31 | 6.0.3
32 | 3.4.3
33 |
34 |
35 |
36 |
37 | io.zipkin.brave
38 | brave
39 | ${zipkin.version}
40 |
41 |
42 | io.zipkin.brave
43 | brave-instrumentation-http
44 | ${zipkin.version}
45 |
46 |
47 | io.zipkin.reporter2
48 | zipkin-reporter-brave
49 | ${zipkin-reporter.version}
50 |
51 |
52 |
53 | junit
54 | junit
55 | 4.13.2
56 | test
57 |
58 |
59 | io.vertx
60 | vertx-unit
61 | test
62 |
63 |
64 | io.zipkin.zipkin2
65 | zipkin-junit
66 | 2.24.4
67 | test
68 |
69 |
70 | org.junit.jupiter
71 | junit-jupiter-api
72 | ${junit.jupiter.version}
73 | test
74 |
75 |
76 | io.zipkin.reporter2
77 | zipkin-sender-okhttp3
78 | ${zipkin-reporter.version}
79 | test
80 |
81 |
82 | io.zipkin.brave
83 | brave-tests
84 | ${zipkin.version}
85 | test
86 |
87 |
88 | io.zipkin.brave
89 | brave-instrumentation-http-tests
90 | ${zipkin.version}
91 | test
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/asciidoc/index.adoc:
--------------------------------------------------------------------------------
1 | = Vertx Zipkin Tracing
2 |
3 | Vert.x integrates with Zipkin thanks to the Zipkin Brave client.
4 |
5 | Vert.x uses the ZipKin HTTP sender based using a Vert.x HTTP Client reporting
6 | spans to `http://localhost:9411/api/v2/spans` in JSON format.
7 |
8 | [source,$lang]
9 | ----
10 | {@link examples.ZipkinTracingExamples#ex1}
11 | ----
12 |
13 | The service name is the mandatory Zipkin service name. If you don't set it, then `a-service` will be used instead.
14 |
15 | You can configure the sender to use a specific URL
16 |
17 | [source,$lang]
18 | ----
19 | {@link examples.ZipkinTracingExamples#ex2}
20 | ----
21 |
22 | The default sender uses a single HTTP connection in plain text with compressed bodies.
23 |
24 | You can override the configuration of the HTTP sender with custom `HttpClientOptions`.
25 |
26 | [source,$lang]
27 | ----
28 | {@link examples.ZipkinTracingExamples#ex3}
29 | ----
30 |
31 | Finally you can set a custom ZipKin `Tracing` allowing for greater control
32 | over the configuration.
33 |
34 | [source,$lang]
35 | ----
36 | {@link examples.ZipkinTracingExamples#ex4}
37 | ----
38 |
39 | == Tracing policy
40 |
41 | The tracing policy defines the behavior of a component when tracing is enabled:
42 |
43 | - {@link io.vertx.core.tracing.TracingPolicy#PROPAGATE}: the component reports a span in the active trace
44 | - {@link io.vertx.core.tracing.TracingPolicy#ALWAYS}: the component reports a span in the active trace or creates a new active trace
45 | - {@link io.vertx.core.tracing.TracingPolicy#IGNORE}: the component will not be involved in any trace.
46 |
47 | The tracing policy is usually configured in the component options.
48 |
49 | == HTTP tracing
50 |
51 | The Vert.x HTTP server and client reports span around HTTP requests:
52 |
53 | - `operationName`: the HTTP method
54 | - tags
55 | - `http.method`: the HTTP method
56 | - `http.url`: the request URL
57 | - `http.status_code`: the HTTP status code
58 |
59 | The default HTTP server tracing policy is `ALWAYS`, you can configure the policy with {@link io.vertx.core.http.HttpServerOptions#setTracingPolicy}
60 |
61 | [source,$lang]
62 | ----
63 | {@link examples.ZipkinTracingExamples#ex5}
64 | ----
65 |
66 | The default HTTP client tracing policy is `PROPAGATE`, you can configure the policy with {@link io.vertx.core.http.HttpClientOptions#setTracingPolicy}
67 |
68 | [source,$lang]
69 | ----
70 | {@link examples.ZipkinTracingExamples#ex6}
71 | ----
72 |
73 | == EventBus tracing
74 |
75 | The Vert.x EventBus reports spans around message exchanges.
76 |
77 | The default sending policy is `PROPAGATE`, you can configure the policy with {@link io.vertx.core.eventbus.DeliveryOptions#setTracingPolicy}.
78 |
79 | [source,$lang]
80 | ----
81 | {@link examples.ZipkinTracingExamples#ex7}
82 | ----
83 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/generated/io/vertx/tracing/zipkin/HttpSenderOptionsConverter.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tracing.zipkin;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import io.vertx.core.json.JsonArray;
5 | import java.time.Instant;
6 | import java.time.format.DateTimeFormatter;
7 |
8 | /**
9 | * Converter and mapper for {@link io.vertx.tracing.zipkin.HttpSenderOptions}.
10 | * NOTE: This class has been automatically generated from the {@link io.vertx.tracing.zipkin.HttpSenderOptions} original class using Vert.x codegen.
11 | */
12 | public class HttpSenderOptionsConverter {
13 |
14 | static void fromJson(Iterable> json, HttpSenderOptions obj) {
15 | for (java.util.Map.Entry member : json) {
16 | switch (member.getKey()) {
17 | case "senderEndpoint":
18 | if (member.getValue() instanceof String) {
19 | obj.setSenderEndpoint((String)member.getValue());
20 | }
21 | break;
22 | }
23 | }
24 | }
25 |
26 | static void toJson(HttpSenderOptions obj, JsonObject json) {
27 | toJson(obj, json.getMap());
28 | }
29 |
30 | static void toJson(HttpSenderOptions obj, java.util.Map json) {
31 | if (obj.getSenderEndpoint() != null) {
32 | json.put("senderEndpoint", obj.getSenderEndpoint());
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/generated/io/vertx/tracing/zipkin/ZipkinTracingOptionsConverter.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tracing.zipkin;
2 |
3 | import io.vertx.core.json.JsonObject;
4 | import io.vertx.core.json.JsonArray;
5 | import java.time.Instant;
6 | import java.time.format.DateTimeFormatter;
7 |
8 | /**
9 | * Converter and mapper for {@link io.vertx.tracing.zipkin.ZipkinTracingOptions}.
10 | * NOTE: This class has been automatically generated from the {@link io.vertx.tracing.zipkin.ZipkinTracingOptions} original class using Vert.x codegen.
11 | */
12 | public class ZipkinTracingOptionsConverter {
13 |
14 | static void fromJson(Iterable> json, ZipkinTracingOptions obj) {
15 | for (java.util.Map.Entry member : json) {
16 | switch (member.getKey()) {
17 | case "serviceName":
18 | if (member.getValue() instanceof String) {
19 | obj.setServiceName((String)member.getValue());
20 | }
21 | break;
22 | case "supportsJoin":
23 | if (member.getValue() instanceof Boolean) {
24 | obj.setSupportsJoin((Boolean)member.getValue());
25 | }
26 | break;
27 | case "senderOptions":
28 | if (member.getValue() instanceof JsonObject) {
29 | obj.setSenderOptions(new io.vertx.tracing.zipkin.HttpSenderOptions((io.vertx.core.json.JsonObject)member.getValue()));
30 | }
31 | break;
32 | }
33 | }
34 | }
35 |
36 | static void toJson(ZipkinTracingOptions obj, JsonObject json) {
37 | toJson(obj, json.getMap());
38 | }
39 |
40 | static void toJson(ZipkinTracingOptions obj, java.util.Map json) {
41 | if (obj.getServiceName() != null) {
42 | json.put("serviceName", obj.getServiceName());
43 | }
44 | json.put("supportsJoin", obj.isSupportsJoin());
45 | if (obj.getSenderOptions() != null) {
46 | json.put("senderOptions", obj.getSenderOptions().toJson());
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/examples/ZipkinTracingExamples.java:
--------------------------------------------------------------------------------
1 | package examples;
2 |
3 | import brave.Tracing;
4 | import io.vertx.core.Vertx;
5 | import io.vertx.core.VertxOptions;
6 | import io.vertx.core.eventbus.DeliveryOptions;
7 | import io.vertx.core.http.HttpClient;
8 | import io.vertx.core.http.HttpClientOptions;
9 | import io.vertx.core.http.HttpServer;
10 | import io.vertx.core.http.HttpServerOptions;
11 | import io.vertx.core.net.KeyCertOptions;
12 | import io.vertx.core.tracing.TracingPolicy;
13 | import io.vertx.docgen.Source;
14 | import io.vertx.tracing.zipkin.HttpSenderOptions;
15 | import io.vertx.tracing.zipkin.ZipkinTracingOptions;
16 |
17 | @Source
18 | public class ZipkinTracingExamples {
19 |
20 | public void ex1() {
21 | Vertx vertx = Vertx.vertx(new VertxOptions()
22 | .setTracingOptions(
23 | new ZipkinTracingOptions().setServiceName("A cute service")
24 | )
25 | );
26 | }
27 |
28 | public void ex2(String senderEndpoint) {
29 | Vertx vertx = Vertx.vertx(new VertxOptions()
30 | .setTracingOptions(
31 | new ZipkinTracingOptions()
32 | .setSenderOptions(new HttpSenderOptions().setSenderEndpoint(senderEndpoint))
33 | )
34 | );
35 | }
36 |
37 | public void ex3(String senderEndpoint, KeyCertOptions sslOptions) {
38 | Vertx vertx = Vertx.vertx(new VertxOptions()
39 | .setTracingOptions(
40 | new ZipkinTracingOptions()
41 | .setSenderOptions(new HttpSenderOptions()
42 | .setSenderEndpoint(senderEndpoint)
43 | .setSsl(true)
44 | .setKeyCertOptions(sslOptions))
45 | )
46 | );
47 | }
48 |
49 | public void ex4(Tracing tracing) {
50 | Vertx vertx = Vertx.vertx(new VertxOptions()
51 | .setTracingOptions(
52 | new ZipkinTracingOptions(tracing)
53 | )
54 | );
55 | }
56 |
57 | public void ex5(Vertx vertx) {
58 | HttpServer server = vertx.createHttpServer(new HttpServerOptions()
59 | .setTracingPolicy(TracingPolicy.IGNORE)
60 | );
61 | }
62 |
63 | public void ex6(Vertx vertx) {
64 | HttpClient client = vertx.createHttpClient(new HttpClientOptions()
65 | .setTracingPolicy(TracingPolicy.IGNORE)
66 | );
67 | }
68 |
69 | public void ex7(Vertx vertx) {
70 | DeliveryOptions options = new DeliveryOptions().setTracingPolicy(TracingPolicy.ALWAYS);
71 | vertx.eventBus().send("the-address", "foo", options);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/io/vertx/tracing/zipkin/VertxSender.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.zipkin;
12 |
13 | import io.vertx.core.Vertx;
14 | import io.vertx.core.buffer.Buffer;
15 | import io.vertx.core.http.*;
16 | import io.vertx.core.spi.VertxTracerFactory;
17 | import zipkin2.reporter.BaseHttpSender;
18 | import zipkin2.reporter.Encoding;
19 | import zipkin2.reporter.HttpEndpointSuppliers;
20 |
21 | import java.io.IOException;
22 | import java.util.List;
23 | import java.util.concurrent.TimeUnit;
24 | import java.util.concurrent.TimeoutException;
25 |
26 | /**
27 | * An HTTP sender using Vert.x HttpClient, only JSON encoding is supported.
28 | *
29 | * @author Julien Viet
30 | */
31 | public class VertxSender extends BaseHttpSender {
32 |
33 | private static final CharSequence APPLICATION_JSON = HttpHeaders.createOptimized("application/json");
34 |
35 | private final int messageMaxBytes = 5242880;
36 | private final Vertx vertx;
37 | private final HttpClient client;
38 | private final HttpSenderOptions options;
39 | private final String endpoint;
40 |
41 | public VertxSender(HttpSenderOptions options) {
42 | super(Encoding.JSON, HttpEndpointSuppliers.constantFactory(), options.getSenderEndpoint());
43 | this.options = new HttpSenderOptions(options);
44 | this.endpoint = options.getSenderEndpoint();
45 | this.vertx = Vertx.builder().withTracer(VertxTracerFactory.NOOP).build();
46 | this.client = vertx.createHttpClient(options);
47 | }
48 |
49 | public HttpSenderOptions options() {
50 | return options;
51 | }
52 |
53 | @Override
54 | public Encoding encoding() {
55 | return Encoding.JSON;
56 | }
57 |
58 | @Override
59 | public int messageMaxBytes() {
60 | return messageMaxBytes;
61 | }
62 |
63 | @Override
64 | public int messageSizeInBytes(List encodedSpans) {
65 | int val = 2;
66 | int length = encodedSpans.size();
67 | for (int i = 0; i < length; i++) {
68 | if (i > 0) {
69 | ++val;
70 | }
71 | val += encodedSpans.get(i).length;
72 | }
73 | return val;
74 | }
75 |
76 | @Override
77 | protected RequestOptions newEndpoint(String endpoint) {
78 | RequestOptions options = new RequestOptions()
79 | .setMethod(HttpMethod.POST)
80 | .addHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON);
81 | if (endpoint.startsWith("http://") || endpoint.startsWith("https://")) {
82 | options.setAbsoluteURI(endpoint);
83 | } else {
84 | options.setURI(endpoint);
85 | }
86 | return options;
87 | }
88 |
89 | @Override
90 | protected Buffer newBody(List encodedSpans) {
91 | int capacity = messageSizeInBytes(encodedSpans);
92 | Buffer body = Buffer.buffer(capacity);
93 | body.appendByte((byte) '[');
94 | for (int i = 0; i < encodedSpans.size(); i++) {
95 | if (i > 0) {
96 | body.appendByte((byte) ',');
97 | }
98 | body.appendBytes(encodedSpans.get(i));
99 | }
100 | body.appendByte((byte) ']');
101 | return body;
102 | }
103 |
104 | @Override
105 | protected void postSpans(RequestOptions requestOptions, Buffer body) throws IOException {
106 | try {
107 | client.request(requestOptions)
108 | .compose(req -> req
109 | .send(body)
110 | .compose(HttpClientResponse::body))
111 | .await(20, TimeUnit.SECONDS);
112 | } catch (TimeoutException e) {
113 | throw new IOException(e);
114 | }
115 | }
116 |
117 | @Override
118 | public void doClose() {
119 | client.close();
120 | vertx.close();
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/io/vertx/tracing/zipkin/ZipkinTracerFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.zipkin;
12 |
13 | import brave.Span;
14 | import brave.propagation.TraceContext;
15 | import io.vertx.core.http.HttpServerRequest;
16 | import io.vertx.core.json.JsonObject;
17 | import io.vertx.core.spi.VertxTracerFactory;
18 | import io.vertx.core.spi.context.storage.ContextLocal;
19 | import io.vertx.core.tracing.TracingOptions;
20 |
21 | public class ZipkinTracerFactory implements VertxTracerFactory {
22 |
23 | static final ContextLocal ACTIVE_SPAN = ContextLocal.registerLocal(Span.class);
24 | static final ContextLocal ACTIVE_CONTEXT = ContextLocal.registerLocal(TraceContext.class);
25 | static final ContextLocal ACTIVE_REQUEST = ContextLocal.registerLocal(HttpServerRequest.class);
26 |
27 | @Override
28 | public ZipkinTracer tracer(TracingOptions options) {
29 | ZipkinTracingOptions zipkinOptions;
30 | if (options instanceof ZipkinTracingOptions) {
31 | zipkinOptions = (ZipkinTracingOptions) options;
32 | } else {
33 | zipkinOptions = new ZipkinTracingOptions(options.toJson());
34 | }
35 | return zipkinOptions.buildTracer();
36 | }
37 |
38 | @Override
39 | public TracingOptions newOptions() {
40 | return new ZipkinTracingOptions();
41 | }
42 |
43 | @Override
44 | public TracingOptions newOptions(JsonObject jsonObject) {
45 | return new ZipkinTracingOptions(jsonObject);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/io/vertx/tracing/zipkin/ZipkinTracingOptions.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tracing.zipkin;
12 |
13 | import brave.Tracing;
14 | import brave.http.HttpTracing;
15 | import brave.sampler.Sampler;
16 | import io.vertx.codegen.annotations.DataObject;
17 | import io.vertx.codegen.json.annotations.JsonGen;
18 | import io.vertx.core.json.JsonObject;
19 | import io.vertx.core.tracing.TracingOptions;
20 | import zipkin2.reporter.brave.AsyncZipkinSpanHandler;
21 |
22 | import java.util.Objects;
23 |
24 | @DataObject
25 | @JsonGen(publicConverter = false)
26 | public class ZipkinTracingOptions extends TracingOptions {
27 |
28 | public static final String DEFAULT_SERVICE_NAME = "a-service";
29 | public static final boolean DEFAULT_SUPPORTS_JOIN = true;
30 |
31 | private String serviceName = DEFAULT_SERVICE_NAME;
32 | private Sampler sampler = Sampler.ALWAYS_SAMPLE;
33 | private boolean supportsJoin = DEFAULT_SUPPORTS_JOIN;
34 | private HttpSenderOptions senderOptions = new HttpSenderOptions();
35 | private HttpTracing httpTracing;
36 |
37 | public ZipkinTracingOptions(HttpTracing httpTracing) {
38 | this.httpTracing = httpTracing;
39 | }
40 |
41 | public ZipkinTracingOptions(Tracing tracing) {
42 | this.httpTracing = HttpTracing.newBuilder(tracing).build();
43 | }
44 |
45 | public ZipkinTracingOptions() {
46 | }
47 |
48 | public ZipkinTracingOptions(ZipkinTracingOptions other) {
49 | this.serviceName = other.serviceName;
50 | this.sampler = other.sampler;
51 | this.supportsJoin = other.supportsJoin;
52 | this.senderOptions = other.senderOptions == null ? null : new HttpSenderOptions(other.senderOptions);
53 | this.httpTracing = other.httpTracing == null ? null : other.httpTracing.toBuilder().build();
54 | }
55 |
56 | public ZipkinTracingOptions(JsonObject json) {
57 | super(json);
58 | ZipkinTracingOptionsConverter.fromJson(json, this);
59 | }
60 |
61 | @Override
62 | public ZipkinTracingOptions copy() {
63 | return new ZipkinTracingOptions(this);
64 | }
65 |
66 | /**
67 | * @return the service name
68 | */
69 | public String getServiceName() {
70 | return serviceName;
71 | }
72 |
73 | /**
74 | * Set the service name to use.
75 | *
76 | * @param serviceName the service name
77 | * @return this instance
78 | */
79 | public ZipkinTracingOptions setServiceName(String serviceName) {
80 | Objects.requireNonNull(serviceName, "Service name cannot be null");
81 | this.serviceName = serviceName;
82 | return this;
83 | }
84 |
85 | /**
86 | * @return {@link brave.Tracing.Builder#supportsJoin(boolean)} option value
87 | */
88 | public boolean isSupportsJoin() {
89 | return supportsJoin;
90 | }
91 |
92 | /**
93 | * Configures {@link brave.Tracing.Builder#supportsJoin(boolean)} option.
94 | *
95 | * @param supportsJoin the config value
96 | * @return this instance
97 | */
98 | public ZipkinTracingOptions setSupportsJoin(boolean supportsJoin) {
99 | this.supportsJoin = supportsJoin;
100 | return this;
101 | }
102 |
103 | /**
104 | * @return the sender options
105 | */
106 | public HttpSenderOptions getSenderOptions() {
107 | return senderOptions;
108 | }
109 |
110 | /**
111 | * Set the HTTP sender options to use for reporting spans.
112 | *
113 | * @param senderOptions the options
114 | * @return this instance
115 | */
116 | public ZipkinTracingOptions setSenderOptions(HttpSenderOptions senderOptions) {
117 | this.senderOptions = senderOptions;
118 | return this;
119 | }
120 |
121 | /**
122 | * @return the Zipkin Sampler
123 | */
124 | public Sampler getSampler() {
125 | return sampler;
126 | }
127 |
128 | /**
129 | * Set the Zipkin Sampler.
130 | *
131 | * @param sampler the options
132 | * @return this instance
133 | */
134 | public ZipkinTracingOptions setSampler(Sampler sampler) {
135 | this.sampler = sampler;
136 | return this;
137 | }
138 |
139 | /**
140 | * Build the tracer and return it.
141 | */
142 | public ZipkinTracer buildTracer() {
143 | if (httpTracing != null) {
144 | return new ZipkinTracer(false, httpTracing, null);
145 | } else if (senderOptions != null) {
146 | String localServiceName = serviceName;
147 | VertxSender sender = new VertxSender(senderOptions);
148 | Tracing tracing = Tracing
149 | .newBuilder()
150 | .supportsJoin(supportsJoin)
151 | .localServiceName(localServiceName)
152 | .addSpanHandler(AsyncZipkinSpanHandler.create(sender))
153 | .sampler(sampler)
154 | .build();
155 | return new ZipkinTracer(true, tracing, sender);
156 | } else {
157 | return null;
158 | }
159 | }
160 |
161 | public JsonObject toJson() {
162 | JsonObject json = new JsonObject();
163 | ZipkinTracingOptionsConverter.toJson(this, json);
164 | return json;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/io/vertx/tracing/zipkin/impl/HttpUtils.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tracing.zipkin.impl;
2 |
3 | /**
4 | * Copied over from Vert.x core HttpUtils to avoid exposing internal utilities.
5 | */
6 | public class HttpUtils {
7 |
8 | /**
9 | * Extract the path out of the uri.
10 | */
11 | public static String parsePath(String uri) {
12 | if (uri.isEmpty()) {
13 | return "";
14 | }
15 | int i;
16 | if (uri.charAt(0) == '/') {
17 | i = 0;
18 | } else {
19 | i = uri.indexOf("://");
20 | if (i == -1) {
21 | i = 0;
22 | } else {
23 | i = uri.indexOf('/', i + 3);
24 | if (i == -1) {
25 | // contains no /
26 | return "/";
27 | }
28 | }
29 | }
30 |
31 | int queryStart = uri.indexOf('?', i);
32 | if (queryStart == -1) {
33 | queryStart = uri.length();
34 | if (i == 0) {
35 | return uri;
36 | }
37 | }
38 | return uri.substring(i, queryStart);
39 | }
40 |
41 | private HttpUtils() {
42 | // Utility
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/io/vertx/tracing/zipkin/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | @io.vertx.codegen.annotations.ModuleGen(name = "vertx-zipkin", groupPackage = "io.vertx")
13 | package io.vertx.tracing.zipkin;
14 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2024 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | module io.vertx.tracing.zipkin {
12 |
13 | requires static io.vertx.docgen;
14 | requires static io.vertx.codegen.api;
15 | requires static io.vertx.codegen.json;
16 |
17 | requires brave;
18 | requires brave.http;
19 | requires io.vertx.core;
20 | requires zipkin2.reporter;
21 | requires zipkin2.reporter.brave;
22 |
23 | provides io.vertx.core.spi.VertxServiceProvider with io.vertx.tracing.zipkin.ZipkinTracerFactory;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/main/resources/META-INF/services/io.vertx.core.spi.VertxServiceProvider:
--------------------------------------------------------------------------------
1 | io.vertx.tracing.zipkin.ZipkinTracerFactory
2 |
3 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/EventBusTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | package io.vertx.tests.zipkin;
13 |
14 | import io.vertx.core.*;
15 | import io.vertx.core.eventbus.DeliveryOptions;
16 | import io.vertx.core.http.HttpClientOptions;
17 | import io.vertx.core.http.HttpMethod;
18 | import io.vertx.core.http.HttpServerOptions;
19 | import io.vertx.core.http.HttpServerRequest;
20 | import io.vertx.core.tracing.TracingPolicy;
21 | import io.vertx.ext.unit.Async;
22 | import io.vertx.ext.unit.TestContext;
23 | import org.junit.Test;
24 | import zipkin2.Span;
25 |
26 | import java.util.List;
27 | import java.util.Optional;
28 | import java.util.function.Function;
29 |
30 | import static java.util.stream.Collectors.toList;
31 | import static org.junit.Assert.assertEquals;
32 |
33 | public class EventBusTest extends ZipkinBaseTest {
34 |
35 | private static final String ADDRESS = "the-address";
36 |
37 | @Override
38 | protected HttpClientOptions getHttpClientOptions() {
39 | return new HttpClientOptions().setDefaultPort(8080);
40 | }
41 |
42 | @Test
43 | public void testEventBusSendPropagate(TestContext ctx) throws Exception {
44 | testSend(ctx, TracingPolicy.PROPAGATE, 2);
45 | }
46 |
47 | @Test
48 | public void testEventBusSendIgnore(TestContext ctx) throws Exception {
49 | testSend(ctx, TracingPolicy.IGNORE, 0);
50 | }
51 |
52 | @Test
53 | public void testEventBusSendAlways(TestContext ctx) throws Exception {
54 | testSend(ctx, TracingPolicy.ALWAYS, 2);
55 | }
56 |
57 | private void testSend(TestContext ctx, TracingPolicy policy, int expected) throws Exception {
58 | Async latch = ctx.async();
59 | ProducerVerticle producerVerticle = new ProducerVerticle(getHttpServerPolicy(policy), vertx -> {
60 | vertx.eventBus().send(ADDRESS, "ping", new DeliveryOptions().setTracingPolicy(policy));
61 | return Future.succeededFuture();
62 | });
63 | vertx.deployVerticle(producerVerticle).onComplete(ctx.asyncAssertSuccess(d1 -> {
64 | Promise consumerPromise = Promise.promise();
65 | vertx.deployVerticle(new ConsumerVerticle(consumerPromise)).onComplete(ctx.asyncAssertSuccess(d2 -> {
66 | client.request(HttpMethod.GET, "/").onComplete(ctx.asyncAssertSuccess(req -> {
67 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
68 | ctx.assertEquals(200, resp.statusCode());
69 | consumerPromise.future().onComplete(ctx.asyncAssertSuccess(v -> latch.complete()));
70 | }));
71 | }));
72 | }));
73 | }));
74 | latch.awaitSuccess();
75 | List spans = waitUntilTrace(() -> {
76 | return Optional.of(zipkin.getTraces())
77 | .filter(traces -> !traces.isEmpty())
78 | .map(traces -> traces.get(0))
79 | .map(trace -> trace.stream().filter(span -> !span.tags().containsKey("http.path")).collect(toList()))
80 | .filter(trace -> trace.size() == expected);
81 | });
82 | for (Span span : spans) {
83 | assertEquals("send", span.name());
84 | assertEquals(ADDRESS, span.remoteEndpoint().serviceName());
85 | }
86 | }
87 |
88 | private TracingPolicy getHttpServerPolicy(TracingPolicy policy) {
89 | return policy == TracingPolicy.ALWAYS ? TracingPolicy.IGNORE : TracingPolicy.ALWAYS;
90 | }
91 |
92 | @Test
93 | public void testEventBusPublishProgagate(TestContext ctx) throws Exception {
94 | testPublish(ctx, TracingPolicy.PROPAGATE, 3);
95 | }
96 |
97 | @Test
98 | public void testEventBusPublishIgnore(TestContext ctx) throws Exception {
99 | testPublish(ctx, TracingPolicy.IGNORE, 0);
100 | }
101 |
102 | @Test
103 | public void testEventBusPublishAlways(TestContext ctx) throws Exception {
104 | testPublish(ctx, TracingPolicy.ALWAYS, 3);
105 | }
106 |
107 | private void testPublish(TestContext ctx, TracingPolicy policy, int expected) throws Exception {
108 | Async latch = ctx.async();
109 | ProducerVerticle producerVerticle = new ProducerVerticle(getHttpServerPolicy(policy), vertx -> {
110 | vertx.eventBus().publish(ADDRESS, "ping", new DeliveryOptions().setTracingPolicy(policy));
111 | return Future.succeededFuture();
112 | });
113 | vertx.deployVerticle(producerVerticle).onComplete(ctx.asyncAssertSuccess(d1 -> {
114 | Promise consumer1Promise = Promise.promise();
115 | Promise consumer2Promise = Promise.promise();
116 | vertx.deployVerticle(new ConsumerVerticle(consumer1Promise)).onComplete(ctx.asyncAssertSuccess(d2 -> {
117 | vertx.deployVerticle(new ConsumerVerticle(consumer2Promise)).onComplete(ctx.asyncAssertSuccess(d3 -> {
118 | client.request(HttpMethod.GET, "/").onComplete(ctx.asyncAssertSuccess(req -> {
119 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
120 | ctx.assertEquals(200, resp.statusCode());
121 | Future.all(consumer1Promise.future(), consumer2Promise.future()).onComplete(ctx.asyncAssertSuccess(v -> latch.complete()));
122 | }));
123 | }));
124 | }));
125 | }));
126 | }));
127 | latch.awaitSuccess();
128 | List spans = waitUntilTrace(() -> {
129 | return Optional.of(zipkin.getTraces())
130 | .filter(traces -> !traces.isEmpty())
131 | .map(traces -> traces.get(0))
132 | .map(trace -> trace.stream().filter(span -> !span.tags().containsKey("http.path")).collect(toList()))
133 | .filter(trace -> trace.size() == expected);
134 | });
135 | for (Span span : spans) {
136 | assertEquals("publish", span.name());
137 | assertEquals(ADDRESS, span.remoteEndpoint().serviceName());
138 | }
139 | }
140 |
141 | @Test
142 | public void testEventBusRequestReplyPropagate(TestContext ctx) throws Exception {
143 | testRequestReply(ctx, TracingPolicy.PROPAGATE, false, 2);
144 | }
145 |
146 | @Test
147 | public void testEventBusRequestReplyIgnore(TestContext ctx) throws Exception {
148 | testRequestReply(ctx, TracingPolicy.IGNORE, false, 0);
149 | }
150 |
151 | @Test
152 | public void testEventBusRequestReplyAlways(TestContext ctx) throws Exception {
153 | testRequestReply(ctx, TracingPolicy.ALWAYS, false, 2);
154 | }
155 |
156 | @Test
157 | public void testEventBusRequestReplyFailurePropagate(TestContext ctx) throws Exception {
158 | testRequestReply(ctx, TracingPolicy.PROPAGATE, true, 2);
159 | }
160 |
161 | @Test
162 | public void testEventBusRequestReplyFailureIgnore(TestContext ctx) throws Exception {
163 | testRequestReply(ctx, TracingPolicy.IGNORE, true, 0);
164 | }
165 |
166 | @Test
167 | public void testEventBusRequestReplyFailureAlways(TestContext ctx) throws Exception {
168 | testRequestReply(ctx, TracingPolicy.ALWAYS, true, 2);
169 | }
170 |
171 | private void testRequestReply(TestContext ctx, TracingPolicy policy, boolean fail, int expected) throws Exception {
172 | Async latch = ctx.async();
173 | ProducerVerticle producerVerticle = new ProducerVerticle(getHttpServerPolicy(policy), vertx -> {
174 | Promise promise = Promise.promise();
175 | vertx.eventBus().request(ADDRESS, "ping", new DeliveryOptions().setTracingPolicy(policy)).onComplete(ar -> {
176 | if (ar.failed() == fail) {
177 | vertx.runOnContext(v -> promise.complete());
178 | } else {
179 | vertx.runOnContext(v -> promise.fail("Unexpected"));
180 | }
181 | });
182 | return promise.future();
183 | });
184 | vertx.deployVerticle(producerVerticle).onComplete(ctx.asyncAssertSuccess(d1 -> {
185 | vertx.deployVerticle(new ReplyVerticle(fail)).onComplete(ctx.asyncAssertSuccess(d2 -> {
186 | client.request(HttpMethod.GET, "/").onComplete(ctx.asyncAssertSuccess(req -> {
187 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
188 | ctx.assertEquals(200, resp.statusCode());
189 | latch.complete();
190 | }));
191 | }));
192 | }));
193 | }));
194 | latch.awaitSuccess();
195 | List spans = waitUntilTrace(() -> {
196 | return Optional.of(zipkin.getTraces())
197 | .filter(traces -> !traces.isEmpty())
198 | .map(traces -> traces.get(0))
199 | .map(trace -> trace.stream().filter(span -> !span.tags().containsKey("http.path")).collect(toList()))
200 | .filter(trace -> trace.size() == expected);
201 | });
202 | for (Span span : spans) {
203 | assertEquals("send", span.name());
204 | assertEquals(ADDRESS, span.remoteEndpoint().serviceName());
205 | }
206 | }
207 |
208 | private static class ProducerVerticle extends AbstractVerticle {
209 |
210 | private final TracingPolicy httpServerPolicy;
211 | private final Function> action;
212 |
213 | private ProducerVerticle(TracingPolicy httpServerPolicy, Function> action) {
214 | this.httpServerPolicy = httpServerPolicy;
215 | this.action = action;
216 | }
217 |
218 | @Override
219 | public void start(Promise startPromise) {
220 | vertx.createHttpServer(new HttpServerOptions().setTracingPolicy(httpServerPolicy))
221 | .requestHandler(this::onRequest)
222 | .listen(8080)
223 | .mapEmpty()
224 | .onComplete(startPromise);
225 | }
226 |
227 | private void onRequest(HttpServerRequest request) {
228 | action.apply(vertx).onComplete(ar -> {
229 | if (ar.succeeded()) {
230 | request.response().end();
231 | } else {
232 | ar.cause().printStackTrace();
233 | request.response().setStatusCode(500).end();
234 | }
235 | });
236 | }
237 | }
238 |
239 | private static class ConsumerVerticle extends AbstractVerticle {
240 |
241 | final Promise promise;
242 |
243 | ConsumerVerticle(Promise promise) {
244 | this.promise = promise;
245 | }
246 |
247 | @Override
248 | public void start(Promise startPromise) {
249 | vertx.eventBus().consumer(ADDRESS, msg -> {
250 | vertx.runOnContext(v -> promise.complete());
251 | }).completion().onComplete(startPromise);
252 | }
253 | }
254 |
255 | private static class ReplyVerticle extends AbstractVerticle {
256 |
257 | final boolean fail;
258 |
259 | ReplyVerticle(boolean fail) {
260 | this.fail = fail;
261 | }
262 |
263 | @Override
264 | public void start(Promise startPromise) {
265 | vertx.eventBus().consumer(ADDRESS, msg -> {
266 | if (fail) {
267 | msg.fail(10, "boom");
268 | } else {
269 | msg.reply(msg.body());
270 | }
271 | }).completion().onComplete(startPromise);
272 | }
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/SqlClientTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.zipkin;
12 |
13 | import io.vertx.core.http.HttpClient;
14 | import io.vertx.core.http.HttpClientOptions;
15 | import io.vertx.core.http.HttpMethod;
16 | import io.vertx.core.tracing.TracingPolicy;
17 | import io.vertx.ext.unit.Async;
18 | import io.vertx.ext.unit.TestContext;
19 | import io.vertx.pgclient.PgBuilder;
20 | import io.vertx.pgclient.PgConnectOptions;
21 | import io.vertx.sqlclient.Pool;
22 | import io.vertx.sqlclient.Row;
23 | import io.vertx.sqlclient.RowSet;
24 | import io.vertx.sqlclient.Tuple;
25 | import org.junit.AfterClass;
26 | import org.junit.Before;
27 | import org.junit.BeforeClass;
28 | import org.junit.Test;
29 | import org.testcontainers.containers.PostgreSQLContainer;
30 | import zipkin2.Span;
31 |
32 | import java.net.Inet4Address;
33 | import java.net.InetAddress;
34 | import java.util.List;
35 |
36 | import static java.util.concurrent.TimeUnit.MICROSECONDS;
37 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
38 | import static org.junit.Assert.*;
39 |
40 | public class SqlClientTest extends ZipkinBaseTest {
41 |
42 | private static PostgreSQLContainer> server;
43 | private static PgConnectOptions connectOptions;
44 | private Pool pool;
45 |
46 | @BeforeClass
47 | public static void startDB() throws Exception {
48 | server = new PostgreSQLContainer<>("postgres:10")
49 | .withDatabaseName("postgres")
50 | .withUsername("postgres")
51 | .withPassword("postgres");
52 | server.start();
53 | InetAddress ip = Inet4Address.getByName(server.getHost());
54 | connectOptions = new PgConnectOptions()
55 | .setUser("postgres")
56 | .setPassword("postgres")
57 | .setDatabase("postgres")
58 | .setHost(ip.getHostAddress())
59 | .setPort(server.getMappedPort(5432));
60 |
61 | }
62 |
63 | @AfterClass
64 | public static void stopDB() {
65 | server.stop();
66 | }
67 |
68 | @Before
69 | public void before() {
70 | super.before();
71 | pool = PgBuilder.pool().connectingTo(connectOptions).using(vertx).build();
72 | }
73 |
74 | @Test
75 | public void testPreparedQuery(TestContext ctx) throws Exception {
76 | Async listenLatch = ctx.async();
77 | long baseDurationInMs = 500;
78 | vertx.createHttpServer().requestHandler(req -> {
79 | pool.preparedQuery("SELECT $1 \"VAL\"")
80 | .execute(Tuple.of("Hello World"))
81 | .onComplete(ar -> {
82 | vertx.setTimer(baseDurationInMs, (__) -> {
83 | if (ar.succeeded()) {
84 | RowSet rows = ar.result();
85 | req.response()
86 | .end();
87 | } else {
88 | req.response()
89 | .setStatusCode(500)
90 | .end();
91 | }
92 | });
93 | });
94 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.complete()));
95 | listenLatch.awaitSuccess();
96 | Async responseLatch = ctx.async();
97 | HttpClient client = vertx.createHttpClient(new HttpClientOptions().setTracingPolicy(TracingPolicy.ALWAYS));
98 | client.request(HttpMethod.GET, 8080, "localhost", "/").onComplete(ctx.asyncAssertSuccess(req -> {
99 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
100 | ctx.assertEquals(200, resp.statusCode());
101 | responseLatch.complete();
102 | }));
103 | }));
104 | responseLatch.awaitSuccess();
105 | List trace = assertSingleSpan(waitUntilTrace(3));
106 | assertEquals(3, trace.size());
107 | Span span1 = trace.get(0);
108 | assertEquals(Span.Kind.CLIENT, span1.kind());
109 | assertEquals("my-service-name", span1.localServiceName());
110 | assertEquals("get", span1.name());
111 | assertEquals("GET", span1.tags().get("http.method"));
112 | assertEquals("/", span1.tags().get("http.path"));
113 | assertEquals(8080, span1.remoteEndpoint().portAsInt());
114 | Span span2 = trace.get(1);
115 | assertEquals(Span.Kind.SERVER, span2.kind());
116 | assertEquals("get", span2.name());
117 | assertEquals("GET", span2.tags().get("http.method"));
118 | assertEquals("/", span2.tags().get("http.path"));
119 | assertNotNull(span2.duration());
120 | assertNotNull(span2.timestamp());
121 | assertTrue(MILLISECONDS.convert(span2.durationAsLong(), MICROSECONDS) > baseDurationInMs);
122 | Span span3 = trace.get(2);
123 | assertEquals(Span.Kind.CLIENT, span3.kind());
124 | assertEquals("postgres", span3.remoteServiceName());
125 | assertEquals(connectOptions.getHost(), span3.remoteEndpoint().ipv4());
126 | assertEquals(connectOptions.getPort(), span3.remoteEndpoint().portAsInt());
127 | assertEquals("SELECT $1 \"VAL\"", span3.tags().get("sql.query"));
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/VertxSenderTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.zipkin;
12 |
13 | import io.vertx.core.Vertx;
14 | import io.vertx.core.VertxOptions;
15 | import io.vertx.core.http.HttpClient;
16 | import io.vertx.core.http.HttpClientOptions;
17 | import io.vertx.core.http.HttpMethod;
18 | import io.vertx.core.tracing.TracingPolicy;
19 | import io.vertx.ext.unit.Async;
20 | import io.vertx.ext.unit.TestContext;
21 | import io.vertx.ext.unit.junit.VertxUnitRunner;
22 | import io.vertx.tracing.zipkin.ZipkinTracingOptions;
23 | import org.junit.After;
24 | import org.junit.Before;
25 | import org.junit.Test;
26 | import org.junit.runner.RunWith;
27 | import zipkin2.Span;
28 | import zipkin2.junit.ZipkinRule;
29 |
30 | import java.util.List;
31 |
32 | import static org.junit.Assert.assertEquals;
33 |
34 | @RunWith(VertxUnitRunner.class)
35 | public class VertxSenderTest {
36 |
37 | private Vertx vertx;
38 |
39 | @Before
40 | public void before() {
41 | vertx = Vertx.vertx(new VertxOptions().setTracingOptions(new ZipkinTracingOptions()));
42 | }
43 |
44 | @After
45 | public void after(TestContext ctx) {
46 | vertx.close().onComplete(ctx.asyncAssertSuccess());
47 | }
48 |
49 | @Test
50 | public void testDefaultSenderEndpoint(TestContext ctx) throws Exception {
51 | ZipkinRule zipkin = new ZipkinRule();
52 | zipkin.start(9411);
53 | HttpClient client = vertx.createHttpClient(new HttpClientOptions().setTracingPolicy(TracingPolicy.ALWAYS));
54 | try {
55 | Async listenLatch = ctx.async();
56 | vertx.createHttpServer().requestHandler(req -> {
57 | req.response().end();
58 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.complete()));
59 | listenLatch.awaitSuccess();
60 | Async responseLatch = ctx.async();
61 | client.request(HttpMethod.GET, 8080, "localhost", "/").onComplete(ctx.asyncAssertSuccess(req -> {
62 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
63 | responseLatch.complete();
64 | }));
65 | }));
66 | responseLatch.awaitSuccess();
67 | List trace = ZipkinBaseTest.waitUntilTrace(zipkin, 2);
68 | assertEquals(2, trace.size());
69 | responseLatch.await(10000);
70 | } finally {
71 | client.close();
72 | zipkin.shutdown();
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/ZipkinBaseTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.zipkin;
12 |
13 | import io.vertx.core.Vertx;
14 | import io.vertx.core.VertxOptions;
15 | import io.vertx.core.http.HttpClient;
16 | import io.vertx.core.http.HttpClientOptions;
17 | import io.vertx.core.tracing.TracingPolicy;
18 | import io.vertx.ext.unit.TestContext;
19 | import io.vertx.ext.unit.junit.VertxUnitRunner;
20 | import io.vertx.tracing.zipkin.HttpSenderOptions;
21 | import io.vertx.tracing.zipkin.ZipkinTracingOptions;
22 | import org.junit.After;
23 | import org.junit.Before;
24 | import org.junit.Rule;
25 | import org.junit.runner.RunWith;
26 | import zipkin2.Span;
27 | import zipkin2.junit.ZipkinRule;
28 |
29 | import java.util.ArrayList;
30 | import java.util.LinkedList;
31 | import java.util.List;
32 | import java.util.Optional;
33 | import java.util.concurrent.Callable;
34 |
35 | import static org.junit.Assert.assertEquals;
36 |
37 | @RunWith(VertxUnitRunner.class)
38 | public abstract class ZipkinBaseTest {
39 |
40 | @Rule
41 | public ZipkinRule zipkin = new ZipkinRule();
42 |
43 | protected Vertx vertx;
44 | protected HttpClient client;
45 |
46 | @Before
47 | public void before() {
48 | String url = zipkin.httpUrl() + "/api/v2/spans";
49 | vertx = Vertx.vertx(new VertxOptions().setTracingOptions(
50 | new ZipkinTracingOptions()
51 | .setServiceName("my-service-name")
52 | .setSupportsJoin(false)
53 | .setSenderOptions(new HttpSenderOptions().setSenderEndpoint(url))
54 | ));
55 | client = vertx.createHttpClient(getHttpClientOptions());
56 | }
57 |
58 | protected HttpClientOptions getHttpClientOptions() {
59 | return new HttpClientOptions().setTracingPolicy(TracingPolicy.ALWAYS);
60 | }
61 |
62 | @After
63 | public void after(TestContext ctx) {
64 | client.close();
65 | vertx.close().onComplete(ctx.asyncAssertSuccess());
66 | }
67 |
68 | List waitUntilTrace(int min) throws Exception {
69 | return waitUntilTrace(zipkin, min);
70 | }
71 |
72 | static List waitUntilTrace(ZipkinRule zipkin, int min) throws Exception {
73 | long now = System.currentTimeMillis();
74 | while ((System.currentTimeMillis() - now) < 10000 ) {
75 | List> traces = zipkin.getTraces();
76 | if (traces.size() > 0 && traces.get(0).size() >= min) {
77 | return traces.get(0);
78 | }
79 | Thread.sleep(10);
80 | }
81 | throw new AssertionError("Got traces " + zipkin.getTraces());
82 | }
83 |
84 | static T waitUntilTrace(Callable> pred) throws Exception {
85 | long now = System.currentTimeMillis();
86 | while ((System.currentTimeMillis() - now) < 10000 ) {
87 | Optional opt = pred.call();
88 | if (opt.isPresent()) {
89 | return opt.get();
90 | }
91 | Thread.sleep(10);
92 | }
93 | throw new AssertionError();
94 | }
95 |
96 | List assertSingleSpan(List spans) {
97 | long result = spans.stream().map(Span::traceId).distinct().count();
98 | assertEquals(1, result);
99 |
100 | // Find top
101 | spans = new ArrayList<>(spans);
102 | Span top = spans.stream().filter(span -> span.id().equals(span.traceId())).findFirst().get();
103 | spans.remove(top);
104 | LinkedList sorted = foo(top, spans);
105 | sorted.addFirst(top);
106 | return sorted;
107 | }
108 |
109 | private LinkedList foo(Span top, List others) {
110 | if (others.isEmpty()) {
111 | return new LinkedList<>();
112 | }
113 | Span s = others.stream().filter(span -> span.parentId().equals(top.id())).findFirst().get();
114 | others.remove(s);
115 | LinkedList ret = foo(s, others);
116 | ret.addFirst(s);
117 | return ret;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/ZipkinGenericPropagationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.zipkin;
12 |
13 | import brave.propagation.TraceContext;
14 | import io.vertx.core.Vertx;
15 | import io.vertx.core.http.HttpClient;
16 | import io.vertx.core.http.HttpClientOptions;
17 | import io.vertx.core.http.HttpMethod;
18 | import io.vertx.core.internal.ContextInternal;
19 | import io.vertx.core.internal.VertxInternal;
20 | import io.vertx.core.spi.tracing.SpanKind;
21 | import io.vertx.core.spi.tracing.TagExtractor;
22 | import io.vertx.core.spi.tracing.VertxTracer;
23 | import io.vertx.core.tracing.TracingPolicy;
24 | import io.vertx.ext.unit.Async;
25 | import io.vertx.ext.unit.TestContext;
26 | import io.vertx.tracing.zipkin.ZipkinTracer;
27 | import org.junit.Test;
28 | import zipkin2.Span;
29 |
30 | import java.util.HashMap;
31 | import java.util.List;
32 | import java.util.Map;
33 | import java.util.function.BiConsumer;
34 |
35 | import static org.junit.Assert.assertEquals;
36 |
37 | public class ZipkinGenericPropagationTest extends ZipkinBaseTest {
38 |
39 | @Test
40 | public void serverToClient(TestContext ctx) throws Exception {
41 | Async listenLatch = ctx.async();
42 | vertx.createHttpServer().requestHandler(req -> {
43 | ContextInternal current = (ContextInternal) Vertx.currentContext();
44 | VertxTracer tracer = current.tracer();
45 | TraceContext requestContext = ZipkinTracer.activeContext();
46 | Object request = new Object();
47 | Map headers = new HashMap<>();
48 | Object trace = tracer.sendRequest(current, SpanKind.RPC, TracingPolicy.PROPAGATE, request, "my_op", (BiConsumer) headers::put, TagExtractor.empty());
49 | ctx.assertEquals(requestContext.traceIdString(), headers.get("X-B3-TraceId"));
50 | ctx.assertNotNull(headers.get("X-B3-SpanId"));
51 | ctx.assertEquals(requestContext.spanIdString(), headers.get("X-B3-ParentSpanId"));
52 | current.setTimer(10, id -> {
53 | Object response = new Object();
54 | tracer.receiveResponse(current, response, trace, null, TagExtractor.empty());
55 | req.response().end();
56 | });
57 | }).listen(8080).onComplete(ctx.asyncAssertSuccess(v -> listenLatch.complete()));
58 | listenLatch.awaitSuccess();
59 | Async responseLatch = ctx.async();
60 | HttpClient client = vertx.createHttpClient(new HttpClientOptions().setTracingPolicy(TracingPolicy.ALWAYS));
61 | client.request(HttpMethod.GET, 8080, "localhost", "/").onComplete(ctx.asyncAssertSuccess(req -> {
62 | req.send().onComplete(ctx.asyncAssertSuccess(resp -> {
63 | ctx.assertEquals(200, resp.statusCode());
64 | responseLatch.complete();
65 | }));
66 | }));
67 | responseLatch.awaitSuccess();
68 | List trace = assertSingleSpan(waitUntilTrace(3));
69 | assertEquals(3, trace.size());
70 | Span span1 = trace.get(0);
71 | assertEquals(Span.Kind.CLIENT, span1.kind());
72 | assertEquals("my-service-name", span1.localServiceName());
73 | assertEquals("get", span1.name());
74 | assertEquals("GET", span1.tags().get("http.method"));
75 | assertEquals("/", span1.tags().get("http.path"));
76 | assertEquals(8080, span1.remoteEndpoint().portAsInt());
77 | Span span2 = trace.get(1);
78 | assertEquals(Span.Kind.SERVER, span2.kind());
79 | assertEquals("get", span2.name());
80 | assertEquals("GET", span2.tags().get("http.method"));
81 | assertEquals("/", span2.tags().get("http.path"));
82 | Span span3 = trace.get(2);
83 | assertEquals(Span.Kind.CLIENT, span3.kind());
84 | }
85 |
86 | @Test
87 | public void clientToServer(TestContext ctx) throws Exception {
88 | VertxInternal vertx = (VertxInternal) this.vertx;
89 | ContextInternal current = vertx.getOrCreateContext();
90 | VertxTracer tracer = current.tracer();
91 | Map headers = new HashMap<>();
92 | Object clientTrace = tracer.sendRequest(current, SpanKind.RPC, TracingPolicy.ALWAYS, new Object(), "foo", (BiConsumer) headers::put, TagExtractor.empty());
93 | ContextInternal receiving = vertx.createEventLoopContext();
94 | Async responseLatch = ctx.async();
95 | receiving.runOnContext(v -> {
96 | ContextInternal duplicate = receiving.duplicate();
97 | Object serverTrace = tracer.receiveRequest(duplicate, SpanKind.RPC, TracingPolicy.PROPAGATE, new Object(), "bar", headers.entrySet(), TagExtractor.empty());
98 | tracer.sendResponse(duplicate, new Object(), serverTrace, null, TagExtractor.empty());
99 | responseLatch.complete();
100 | });
101 | responseLatch.await(20_000);
102 | tracer.receiveResponse(current, new Object(), clientTrace, null, TagExtractor.empty());
103 | List trace = assertSingleSpan(waitUntilTrace(2));
104 | Span span1 = trace.get(0);
105 | assertEquals(Span.Kind.CLIENT, span1.kind());
106 | assertEquals("my-service-name", span1.localServiceName());
107 | assertEquals("foo", span1.name());
108 | Span span2 = trace.get(1);
109 | assertEquals(Span.Kind.SERVER, span2.kind());
110 | assertEquals("my-service-name", span2.localServiceName());
111 | assertEquals("bar", span2.name());
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/ZipkinHttpClientITTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.zipkin;
12 |
13 | import brave.propagation.TraceContext;
14 | import brave.test.http.ITHttpAsyncClient;
15 | import io.vertx.core.Future;
16 | import io.vertx.core.Promise;
17 | import io.vertx.core.Vertx;
18 | import io.vertx.core.VertxOptions;
19 | import io.vertx.core.buffer.Buffer;
20 | import io.vertx.core.http.*;
21 | import io.vertx.core.tracing.TracingPolicy;
22 | import io.vertx.tracing.zipkin.ZipkinTracer;
23 | import io.vertx.tracing.zipkin.ZipkinTracingOptions;
24 | import org.junit.jupiter.api.Disabled;
25 | import org.junit.jupiter.api.Test;
26 |
27 | import java.io.IOException;
28 | import java.util.concurrent.TimeUnit;
29 | import java.util.concurrent.TimeoutException;
30 | import java.util.function.BiConsumer;
31 |
32 | import static io.vertx.core.http.HttpMethod.GET;
33 | import static io.vertx.core.http.HttpMethod.OPTIONS;
34 |
35 | public class ZipkinHttpClientITTest extends ITHttpAsyncClient {
36 |
37 | private Vertx vertx;
38 |
39 | @Test
40 | @Disabled
41 | @Override
42 | public void usesParentFromInvocationTime() {
43 | // Does not pass
44 | }
45 |
46 | @Test
47 | @Disabled
48 | @Override
49 | public void customSampler() {
50 | // Does not pass
51 | }
52 |
53 | @Test
54 | @Disabled
55 | @Override
56 | protected void callbackContextIsFromInvocationTime() {
57 | // Does not pass
58 | }
59 |
60 | @Override
61 | protected HttpClient newClient(int port) {
62 | vertx = Vertx.vertx(new VertxOptions().setTracingOptions(new ZipkinTracingOptions(httpTracing)));
63 | return vertx.createHttpClient(new HttpClientOptions()
64 | .setDefaultPort(port)
65 | .setDefaultHost("127.0.0.1")
66 | .setTracingPolicy(TracingPolicy.ALWAYS)
67 | );
68 | }
69 |
70 | @Override
71 | protected void closeClient(HttpClient client) {
72 | if (client != null) {
73 | client.close().await();
74 | }
75 | if (vertx != null) {
76 | vertx.close().await();
77 | }
78 | }
79 |
80 | @Override
81 | protected void get(HttpClient client, String path, BiConsumer callback) {
82 | client.request(GET, path)
83 | .compose(req -> req.send().map(HttpClientResponse::statusCode))
84 | .toCompletionStage().whenComplete(callback);
85 | }
86 |
87 | private Future request(HttpClient client, String pathIncludingQuery, String body, HttpMethod method) {
88 | Promise promise = Promise.promise();
89 | Runnable task = () -> {
90 | if (body == null) {
91 | RequestOptions options = new RequestOptions()
92 | .setURI(pathIncludingQuery)
93 | .setMethod(method)
94 | .setFollowRedirects(true);
95 | client.request(options)
96 | .compose(req -> req.send().compose(resp -> resp.body().mapEmpty()))
97 | .onComplete(promise);
98 | } else {
99 | client.request(HttpMethod.POST, pathIncludingQuery)
100 | .compose(req -> req.send(Buffer.buffer(body)).compose(resp -> resp.body().mapEmpty()))
101 | .onComplete(promise);
102 | }
103 | };
104 | TraceContext traceCtx = currentTraceContext.get();
105 | if (traceCtx != null) {
106 | vertx.runOnContext(v -> {
107 | ZipkinTracer.setTraceContext(traceCtx);
108 | task.run();
109 | });
110 | } else {
111 | task.run();
112 | }
113 | return promise.future();
114 | }
115 |
116 | @Override
117 | protected void get(HttpClient client, String pathIncludingQuery) throws IOException {
118 | try {
119 | request(client, pathIncludingQuery, null, GET).await(10, TimeUnit.SECONDS);
120 | } catch (TimeoutException e) {
121 | throw new IOException(e);
122 | }
123 | }
124 |
125 | @Override
126 | protected void post(HttpClient client, String pathIncludingQuery, String body) throws IOException {
127 | try {
128 | request(client, pathIncludingQuery, body, null).await(10, TimeUnit.SECONDS);
129 | } catch (TimeoutException e) {
130 | throw new IOException(e);
131 | }
132 | }
133 |
134 | @Override
135 | protected void options(HttpClient client, String path) throws IOException {
136 | try {
137 | request(client, path, null, OPTIONS).await(10, TimeUnit.SECONDS);
138 | } catch (TimeoutException e) {
139 | throw new IOException(e);
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/ZipkinHttpServerITTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 | package io.vertx.tests.zipkin;
12 |
13 | import brave.propagation.TraceContext;
14 | import brave.test.http.ITHttpServer;
15 | import io.vertx.core.Handler;
16 | import io.vertx.core.Vertx;
17 | import io.vertx.core.VertxOptions;
18 | import io.vertx.core.http.HttpMethod;
19 | import io.vertx.core.http.HttpServer;
20 | import io.vertx.core.http.HttpServerOptions;
21 | import io.vertx.core.http.HttpServerRequest;
22 | import io.vertx.core.tracing.TracingPolicy;
23 | import io.vertx.tracing.zipkin.ZipkinTracer;
24 | import io.vertx.tracing.zipkin.ZipkinTracingOptions;
25 | import org.junit.jupiter.api.AfterEach;
26 |
27 | import java.io.IOException;
28 |
29 | class ZipkinHttpServerITTest extends ITHttpServer implements Handler {
30 |
31 | private Vertx vertx;
32 | private HttpServer server;
33 | private int port;
34 |
35 | @Override
36 | protected void init() {
37 | vertx = Vertx.vertx(new VertxOptions().setTracingOptions(new ZipkinTracingOptions(httpTracing)));
38 | server = vertx.createHttpServer(new HttpServerOptions().setTracingPolicy(TracingPolicy.ALWAYS))
39 | .requestHandler(this)
40 | .listen(0, "localhost")
41 | .await();
42 | port = server.actualPort();
43 | }
44 |
45 | @Override
46 | public void handle(HttpServerRequest req) {
47 | TraceContext ctx = ZipkinTracer.activeContext();
48 | switch (req.path()) {
49 | case "/foo":
50 | req.response().end("bar");
51 | break;
52 | case "/exception":
53 | req.response().setStatusCode(503).end();
54 | break;
55 | case "/exceptionAsync":
56 | req.endHandler(v -> {
57 | req.response().setStatusCode(503).end();
58 | });
59 | break;
60 | case "/badrequest":
61 | req.response().setStatusCode(400).end();
62 | break;
63 | case "/":
64 | if (req.method() == HttpMethod.OPTIONS) {
65 | req.response().end("bar");
66 | }
67 | break;
68 | case "/async":
69 | if (ZipkinTracer.activeSpan() == null) {
70 | throw new IllegalStateException("couldn't read current span!");
71 | }
72 | req.endHandler(v -> req.response().end("bar"));
73 | break;
74 | case "/items/1":
75 | req.response().end("1");;
76 | break;
77 | case "/items/2":
78 | req.response().end("2");;
79 | break;
80 | case "/child":
81 | httpTracing.tracing().tracer().newChild(ctx).name("child").start().finish();
82 | req.response().end("happy");
83 | break;
84 | default:
85 | req.response().setStatusCode(404).end();
86 | break;
87 | }
88 | }
89 |
90 | @Override
91 | protected String url(String path) {
92 | return "http://127.0.0.1:" + port + path;
93 | }
94 |
95 | @Override
96 | public void httpRoute() {
97 | // Cannot pass because routes are /items/1 and /items/2
98 | }
99 |
100 | @Override
101 | public void httpRoute_nested() {
102 | // Cannot pass because routes are /items/1 and /items/2
103 | }
104 |
105 | @Override
106 | public void httpRoute_async() {
107 | // Cannot pass because routes are /items/1 and /items/2
108 | }
109 |
110 | @Override
111 | protected void spanHandlerSeesError() throws IOException {
112 | // Cannot pass because our Zipkin tracer only reports error when a request is reset
113 | }
114 |
115 | @Override
116 | protected void spanHandlerSeesError_async() throws IOException {
117 | // Cannot pass because our Zipkin tracer only reports error when a request is reset
118 | }
119 |
120 | @Override
121 | protected void setsErrorAndHttpStatusOnUncaughtException() throws IOException {
122 | // Cannot pass because our Zipkin tracer only reports error when a request is reset
123 | }
124 |
125 | @Override
126 | protected void setsErrorAndHttpStatusOnUncaughtException_async() throws IOException {
127 | // Cannot pass because our Zipkin tracer only reports error when a request is reset
128 | }
129 |
130 | @AfterEach
131 | public void stop() throws Exception {
132 | if (server != null) {
133 | server.close().await();
134 | }
135 | if (vertx != null) {
136 | vertx.close().await();
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/ZipkinTracerUtilTest.java:
--------------------------------------------------------------------------------
1 | package io.vertx.tests.zipkin;
2 |
3 | import brave.Span;
4 | import brave.Tracing;
5 | import brave.propagation.B3SingleFormat;
6 | import brave.propagation.TraceContext;
7 | import io.vertx.core.AbstractVerticle;
8 | import io.vertx.core.Promise;
9 | import io.vertx.ext.unit.Async;
10 | import io.vertx.ext.unit.TestContext;
11 | import io.vertx.ext.unit.junit.VertxUnitRunner;
12 | import io.vertx.tracing.zipkin.ZipkinTracer;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 |
16 | import java.util.concurrent.atomic.AtomicReference;
17 |
18 | import static org.junit.Assert.assertEquals;
19 |
20 | @RunWith(VertxUnitRunner.class)
21 | public class ZipkinTracerUtilTest extends ZipkinBaseTest {
22 |
23 | protected static final String ADDRESS = "zipkin.tracer.util.test";
24 |
25 | @Test
26 | public void test(TestContext ctx) throws Exception {
27 | Async latch = ctx.async();
28 | Span span = Tracing.newBuilder().build().tracer().newTrace();
29 | String expectedTraceId = B3SingleFormat.writeB3SingleFormat(span.context());
30 | AtomicReference actualTraceId = new AtomicReference<>();
31 | Promise startSender = Promise.promise();
32 | vertx.deployVerticle(new AbstractVerticle() {
33 | public void start() throws Exception {
34 | vertx.eventBus().consumer(ADDRESS, message -> {
35 | actualTraceId.set(ZipkinTracer.exportTraceId());
36 | latch.complete();
37 | });
38 | startSender.complete();
39 | }
40 | });
41 |
42 | startSender.future().onComplete(res -> {
43 | vertx.deployVerticle(new AbstractVerticle() {
44 | public void start() throws Exception {
45 | ZipkinTracer.importTraceId(expectedTraceId);
46 | vertx.eventBus().send(ADDRESS, true);
47 | }
48 | });
49 | });
50 |
51 | latch.awaitSuccess();
52 |
53 | TraceContext actualContext = B3SingleFormat.parseB3SingleFormat(actualTraceId.get()).context();
54 | assertEquals(span.context().traceId(), actualContext.traceId());
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/vertx-zipkin/src/test/java/io/vertx/tests/zipkin/ZipkinTracingOptionsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
3 | *
4 | * This program and the accompanying materials are made available under the
5 | * terms of the Eclipse Public License 2.0 which is available at
6 | * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7 | * which is available at https://www.apache.org/licenses/LICENSE-2.0.
8 | *
9 | * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10 | */
11 |
12 | package io.vertx.tests.zipkin;
13 |
14 | import io.vertx.core.tracing.TracingOptions;
15 | import io.vertx.tracing.zipkin.*;
16 | import org.junit.Test;
17 |
18 | import static org.junit.Assert.assertEquals;
19 | import static org.junit.Assert.assertNotEquals;
20 | import static org.junit.Assert.assertNotNull;
21 | import static org.junit.Assert.assertTrue;
22 |
23 | public class ZipkinTracingOptionsTest {
24 |
25 | @Test
26 | public void testCopy() {
27 | TracingOptions options = new ZipkinTracingOptions().setServiceName("foo");
28 | TracingOptions copy = options.copy();
29 | assertTrue(copy instanceof ZipkinTracingOptions);
30 | ZipkinTracingOptions other = (ZipkinTracingOptions) copy;
31 | assertEquals("foo", other.getServiceName());
32 | }
33 |
34 | @Test
35 | public void testBuildFromJsonOptions() {
36 | HttpSenderOptions senderOptions = new HttpSenderOptions().setDefaultHost("remote-server");
37 | ZipkinTracingOptions options = new ZipkinTracingOptions().setSenderOptions(senderOptions);
38 | ZipkinTracerFactory factory = new ZipkinTracerFactory();
39 | ZipkinTracer tracer = factory.tracer(new TracingOptions(options.toJson()));
40 | VertxSender sender = tracer.sender();
41 | assertEquals(senderOptions.toJson(), sender.options().toJson());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------