├── .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 | --------------------------------------------------------------------------------