├── .bazelrc ├── .bazelversion ├── .clang-format ├── .github └── workflows │ └── main.yml ├── .gitignore ├── BUILD ├── CMakeLists.txt ├── LICENSE ├── README.md ├── SECURITY.md ├── WORKSPACE ├── bazel ├── BUILD ├── coverage │ ├── BUILD │ └── collect_cc_coverage.sh ├── fmtlib.BUILD ├── httplib.BUILD ├── repositories.bzl └── spdlog.BUILD ├── cmake ├── cpp2sky-config.cmake.in ├── cpp2sky.pc.in ├── fmtlib.cmake ├── grpc.cmake ├── httplib.cmake ├── project-import-cmake │ ├── CMakeLists.txt │ ├── sample.cc │ └── sample_client.cc ├── project-import-pkgconfig │ ├── CMakeLists.txt │ ├── sample.cc │ └── sample_client.cc ├── proto2cpp.cmake ├── spdlog.cmake └── util.cmake ├── coverage.sh ├── cpp2sky ├── BUILD ├── assert.h ├── config.proto ├── exception.h ├── internal │ ├── BUILD │ ├── async_client.h │ ├── matcher.h │ └── random_generator.h ├── propagation.h ├── time.h ├── trace_log.h ├── tracer.h ├── tracing_context.h └── well_known_names.h ├── docs └── README.md ├── example ├── BUILD ├── sample.cc └── sample_client.cc ├── source ├── BUILD ├── grpc_async_client_impl.cc ├── grpc_async_client_impl.h ├── matchers │ ├── BUILD │ ├── suffix_matcher.cc │ └── suffix_matcher.h ├── propagation_impl.cc ├── propagation_impl.h ├── tracer_impl.cc ├── tracer_impl.h ├── tracing_context_impl.cc ├── tracing_context_impl.h └── utils │ ├── BUILD │ ├── base64.h │ ├── buffer.h │ ├── random_generator.cc │ ├── random_generator.h │ └── timer.h └── test ├── BUILD ├── buffer_test.cc ├── e2e ├── BUILD ├── bridge.py ├── consumer.cc ├── data │ └── all_expected.yaml ├── docker │ ├── Dockerfile.bridge │ ├── Dockerfile.consumer │ ├── Dockerfile.provider │ ├── Dockerfile.tool │ └── docker-compose.yml ├── main.py ├── provider.cc └── requirements.txt ├── grpc_async_client_test.cc ├── matcher_test.cc ├── mocks.h ├── propagation_test.cc ├── tracer_test.cc └── tracing_context_test.cc /.bazelrc: -------------------------------------------------------------------------------- 1 | build --enable_platform_specific_config 2 | build --features=-supports_dynamic_linker 3 | build --copt="-Wall" 4 | build --copt="-Werror" 5 | build:windows --copt=/wd4716 6 | 7 | # Pass PATH variable from the environment 8 | build --action_env=PATH 9 | 10 | # Common flags for Clang 11 | build:clang --action_env=BAZEL_COMPILER=clang 12 | build:clang --action_env=CC=clang --action_env=CXX=clang++ 13 | build:clang --linkopt=-fuse-ld=lld 14 | 15 | # Coverage options 16 | coverage --config=coverage 17 | coverage --build_tests_only 18 | build:coverage --config=clang 19 | build:coverage --action_env=BAZEL_USE_LLVM_NATIVE_COVERAGE=1 20 | build:coverage --action_env=GCOV=llvm-profdata 21 | build:coverage --combined_report=lcov 22 | build:coverage --experimental_use_llvm_covmap 23 | build:coverage --experimental_generate_llvm_lcov 24 | build:coverage --collect_code_coverage 25 | build:coverage --instrumentation_filter="//source[/:],//cpp2sky[/:]" 26 | build:coverage --coverage_support=@cpp2sky//bazel/coverage:coverage_support 27 | build:coverage --test_env=CC_CODE_COVERAGE_SCRIPT=external/cpp2sky/bazel/coverage/collect_cc_coverage.sh 28 | build:coverage --strategy=TestRunner=local 29 | build:coverage --strategy=CoverageReport=local 30 | build:coverage --experimental_use_llvm_covmap 31 | build:coverage --collect_code_coverage 32 | build:coverage --test_tag_filters=-nocoverage 33 | 34 | try-import %workspace%/user.bazelrc 35 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 4.0.0 2 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: cpp2sky test 4 | 5 | on: 6 | push: 7 | branches: [ main ] 8 | pull_request: 9 | branches: [ main ] 10 | 11 | jobs: 12 | format: 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Setup clang-format 17 | run: | 18 | git clone https://github.com/Sarcasm/run-clang-format.git 19 | - name: Run clang-format 20 | run: find ./ -iname "*.h" -o -iname "*.cc" | xargs ./run-clang-format/run-clang-format.py 21 | 22 | unit-test: 23 | runs-on: ubuntu-20.04 24 | steps: 25 | - run: | 26 | echo "/opt/llvm/bin" >> $GITHUB_PATH 27 | - uses: actions/checkout@v3 28 | - name: Run bazel test with GCC c++11 29 | run: | 30 | bazel test --test_output=all --cxxopt=-std=c++0x //... 31 | - name: Run bazel test with GCC c++17 32 | run: | 33 | bazel test --test_output=all --cxxopt=-std=c++17 //... 34 | - name: Run bazel test with CLANG c++11 35 | run: | 36 | bazel test --test_output=all -c dbg --config=clang --cxxopt=-std=c++0x //... 37 | - name: Run bazel test with CLANG c++17 38 | run: | 39 | bazel test --test_output=all -c opt --config=clang --cxxopt=-std=c++17 //... 40 | - name: Install cmake dependencies and run cmake compile 41 | run: | 42 | sudo apt update 43 | sudo apt -y install cmake 44 | sudo git clone -b v9.1.0 https://github.com/apache/skywalking-data-collect-protocol.git ./3rdparty/skywalking-data-collect-protocol 45 | sudo git clone -b v1.46.6 https://github.com/grpc/grpc.git --recursive 46 | sudo cmake -S ./grpc -B ./grpc/build 47 | sudo cmake --build ./grpc/build --parallel 8 --target install 48 | sudo cmake -S . -B ./build 49 | sudo cmake --build ./build 50 | - name: Install lcov and genhtml and link llvm 51 | run: | 52 | sudo apt update 53 | sudo apt -y install lcov 54 | sudo ln -s /usr/bin/llvm-profdata-11 /usr/bin/llvm-profdata 55 | sudo ln -s /usr/bin/llvm-cov-11 /usr/bin/llvm-cov 56 | - name: Run coverage test 57 | run: | 58 | ./coverage.sh 59 | 60 | e2e-test: 61 | runs-on: ubuntu-20.04 62 | steps: 63 | - uses: actions/checkout@v3 64 | - name: Prepare service container 65 | run: | 66 | docker compose -f test/e2e/docker/docker-compose.yml up -d 67 | - name: Run e2e 68 | run: | 69 | sleep 10 70 | pip3 install --upgrade pip 71 | pip3 install setuptools 72 | pip3 install -r test/e2e/requirements.txt 73 | python3 test/e2e/main.py --expected_file=test/e2e/data/all_expected.yaml --max_retry_times=3 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # user's bazel configuration files 35 | user.bazelrc 36 | 37 | # bazel artifacts 38 | /bazel-bin 39 | /bazel-out 40 | /bazel-cpp2sky 41 | /bazel-testlogs 42 | 43 | # editor settings 44 | .vscode/ 45 | 46 | .clangd/ 47 | compile_commands.json 48 | 49 | coverage_report 50 | 51 | ### Automatically added by Hedron's Bazel Compile Commands Extractor: https://github.com/hedronvision/bazel-compile-commands-extractor 52 | # Ignore the `external` link (that is added by `bazel-compile-commands-extractor`). The link differs between macOS/Linux and Windows, so it shouldn't be checked in. The pattern must not end with a trailing `/` because it's a symlink on macOS/Linux. 53 | /external 54 | # Ignore links to Bazel's output. The pattern needs the `*` because people can change the name of the directory into which your repository is cloned (changing the `bazel-` symlink), and must not end with a trailing `/` because it's a symlink on macOS/Linux. 55 | /bazel-* 56 | # Ignore generated output. Although valuable (after all, the primary purpose of `bazel-compile-commands-extractor` is to produce `compile_commands.json`!), it should not be checked in. 57 | /compile_commands.json 58 | # Ignore the directory in which `clangd` stores its local index. 59 | /.cache/ 60 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") 2 | 3 | refresh_compile_commands( 4 | name = "refresh_compile_commands", 5 | 6 | # Specify the targets of interest. 7 | # For example, specify a dict of targets and any flags required to build. 8 | targets = { 9 | "//cpp2sky/...": "", 10 | "//source/...": "", 11 | "//test/...": "", 12 | "//example/...": "", 13 | }, 14 | ) 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | if(POLICY CMP0091) 4 | cmake_policy(SET CMP0091 NEW) # recognize CMAKE_MSVC_RUNTIME_LIBRARY 5 | endif() 6 | 7 | project(cpp2sky 8 | VERSION 0.5.1 9 | DESCRIPTION "Distributed tracing and monitor SDK in CPP for Apache SkyWalking APM" 10 | HOMEPAGE_URL "https://github.com/SkyAPM/cpp2sky" 11 | ) 12 | 13 | option(OVERRIDE_CXX_STANDARD_FLAGS "Force building with -std=c++11 even if the CXXFLAGS are configured differently" ON) 14 | option(SPDLOG_FETCHCONTENT "Using spdlog FetchContent to build" ON) 15 | option(FMTLIB_FETCHCONTENT "Using fmt FetchContent to build" ON) 16 | option(HTTPLIB_FETCHCONTENT "Using httplib FetchContent to build" ON) 17 | option(CPP2SKY_INSTALL "Generate the install target." OFF) 18 | option(GENERATE_CPP2SKY_PKGCONFIG "Generate and install pkg-config files for UNIX" OFF) 19 | 20 | 21 | if(OVERRIDE_CXX_STANDARD_FLAGS) 22 | set(CMAKE_CXX_STANDARD 11) 23 | set(CMAKE_CXX_EXTENSIONS OFF) 24 | endif() 25 | 26 | include(GNUInstallDirs) 27 | set(CMAKE_THREAD_PREFER_PTHREAD TRUE) 28 | find_package(Threads) 29 | 30 | 31 | file(GLOB_RECURSE SOURCE_CC_FILES "source/*.cc") 32 | file(GLOB_RECURSE SOURCE_HDR_FILES "source/*.h") 33 | add_library(${PROJECT_NAME} STATIC ${SOURCE_CC_FILES} ${SOURCE_HDR_FILES}) 34 | target_include_directories(${PROJECT_NAME} PUBLIC 35 | $ 36 | ) 37 | 38 | add_executable(example-sample "example/sample.cc") 39 | add_executable(example-sample-client "example/sample_client.cc") 40 | target_link_libraries(example-sample ${PROJECT_NAME}) 41 | target_link_libraries(example-sample-client ${PROJECT_NAME}) 42 | 43 | 44 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 45 | include(fmtlib) 46 | include(spdlog) 47 | include(httplib) 48 | include(grpc) 49 | include(proto2cpp) 50 | 51 | target_link_libraries(${PROJECT_NAME} 52 | PUBLIC $ 53 | ${_REFLECTION} 54 | ${_GRPC_GRPCPP} 55 | ${_PROTOBUF_LIBPROTOBUF} 56 | fmt 57 | spdlog 58 | $<$:httplib> 59 | ) 60 | 61 | 62 | if(CPP2SKY_INSTALL) 63 | include(CMakePackageConfigHelpers) 64 | # Install the static library. 65 | install( 66 | TARGETS ${PROJECT_NAME} 67 | EXPORT ${PROJECT_NAME}-targets 68 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 69 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 70 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 71 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 72 | ) 73 | 74 | # Install export 75 | install( 76 | EXPORT ${PROJECT_NAME}-targets 77 | NAMESPACE ${PROJECT_NAME}:: 78 | FILE ${PROJECT_NAME}-targets.cmake 79 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 80 | ) 81 | 82 | # Install the project include 83 | install(DIRECTORY cpp2sky 84 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 85 | FILES_MATCHING 86 | PATTERN "*.h" 87 | PATTERN "BUILD" EXCLUDE 88 | PATTERN "*.proto" EXCLUDE 89 | ) 90 | 91 | # Install *.cmake.in 92 | configure_package_config_file( 93 | ${PROJECT_SOURCE_DIR}/cmake/cpp2sky-config.cmake.in 94 | ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake 95 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 96 | ) 97 | 98 | install( 99 | FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake 100 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 101 | ) 102 | endif() 103 | 104 | 105 | if(GENERATE_CPP2SKY_PKGCONFIG) 106 | # see https://github.com/jupp0r/prometheus-cpp/issues/587 107 | if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") 108 | set(CPP2SKY_PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") 109 | else() 110 | set(CPP2SKY_PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") 111 | endif() 112 | 113 | if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") 114 | set(CPP2SKY_PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") 115 | else() 116 | set(CPP2SKY_PKGCONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") 117 | endif() 118 | configure_file( 119 | ${PROJECT_SOURCE_DIR}/cmake/cpp2sky.pc.in 120 | ${PROJECT_BINARY_DIR}/cpp2sky.pc 121 | @ONLY 122 | ) 123 | 124 | # Install the pkgconfig 125 | install( 126 | FILES ${PROJECT_BINARY_DIR}/cpp2sky.pc 127 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" 128 | ) 129 | 130 | endif() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp2sky 2 | 3 | ![cpp2sky test](https://github.com/SkyAPM/cpp2sky/workflows/cpp2sky%20test/badge.svg) 4 | 5 | Distributed tracing and monitor SDK in CPP for Apache SkyWalking APM. This SDK is compatible with C++ 17, C++ 14, and C++ 11. 6 | 7 | ## Build 8 | 9 | #### Bazel 10 | 11 | Download cpp2sky tarball with specified version. 12 | 13 | ``` 14 | http_archive( 15 | name = "com_github_skyapm_cpp2sky", 16 | sha256 = , 17 | urls = ["https://github.com/skyAPM/cpp2sky/archive/.tar.gz"], 18 | ) 19 | ``` 20 | 21 | Add interface definition and library to your project. 22 | 23 | ``` 24 | cc_binary( 25 | name = "example", 26 | srcs = ["example.cc"], 27 | deps = [ 28 | "@com_github_skyapm_cpp2sky//cpp2sky:cpp2sky_interface", 29 | "@com_github_skyapm_cpp2sky//source:cpp2sky_lib" 30 | ], 31 | ) 32 | ``` 33 | 34 | #### Cmake 35 | 36 | You can compile this project, according to the following steps: 37 | ``` 38 | step 01: git clone git@github.com:SkyAPM/cpp2sky.git 39 | step 02: git clone -b v9.1.0 https://github.com/apache/skywalking-data-collect-protocol.git ./3rdparty/skywalking-data-collect-protocol 40 | step 03: git clone -b v1.46.6 https://github.com/grpc/grpc.git --recursive 41 | step 04: cmake -S ./grpc -B ./grpc/build && cmake --build ./grpc/build --parallel 8 --target install 42 | step 05: cmake -S . -B ./build && cmake --build ./build 43 | ``` 44 | 45 | You can also use find_package to get target libary in your project. Like this: 46 | ``` 47 | find_package(cpp2sky CONFIG REQUIRED) 48 | target_link_libraries(${PROJECT_NAME} cpp2sky::cpp2sky proto_lib) 49 | ``` 50 | Of course, if OS is similar to Unix, you can also use pkgconfig to build the project. Like this: 51 | ``` 52 | find_package(PkgConfig REQUIRED) 53 | pkg_check_modules(CPP2SKY_PKG REQUIRED cpp2sky) 54 | ``` 55 | 56 | Note: 57 | - If you want to build this project over c11, you must update grpc version(current version:v1.46.6). 58 | - Only test cmake using Centos and Ubuntu. 59 | 60 | #### Develop 61 | 62 | Generate `compile_commands.json` for this repo by `bazel run :refresh_compile_commands`. Thank https://github.com/hedronvision/bazel-compile-commands-extractor for it provide the great script/tool to make this so easy! 63 | 64 | #### Docs 65 | 66 | cpp2sky configration is based on protobuf, and docs are generated by [protodoc](https://github.com/etcd-io/protodoc). If you have any API change, you should run below. 67 | 68 | ``` 69 | protodoc --directory=./cpp2sky --parse="message" --languages="C++" --title=cpp2sky config --output=docs/README.md 70 | ``` 71 | 72 | ## Basic usage 73 | 74 | #### Config 75 | 76 | cpp2sky provides simple configuration for tracer. API docs are available at `docs/README.md`. 77 | The detail information is described in [official protobuf definition](https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent/Tracing.proto#L57-L67). 78 | 79 | ```cpp 80 | #include 81 | 82 | int main() { 83 | using namespace cpp2sky; 84 | 85 | static const std::string service_name = "service_name"; 86 | static const std::string instance_name = "instance_name"; 87 | static const std::string oap_addr = "oap:12800"; 88 | static const std::string token = "token"; 89 | 90 | TracerConfig tracer_config; 91 | 92 | config.set_instance_name(instance_name); 93 | config.set_service_name(service_name); 94 | config.set_address(oap_addr); 95 | config.set_token(token); 96 | } 97 | ``` 98 | 99 | #### Create tracer 100 | 101 | After you constructed config, then setup tracer. Tracer supports gRPC reporter only, also TLS adopted gRPC reporter isn't available now. 102 | TLS adoption and REST tracer will be supported in the future. 103 | 104 | ```cpp 105 | TracerConfig tracer_config; 106 | 107 | // Setup 108 | 109 | TracerPtr tracer = createInsecureGrpcTracer(tracer_config); 110 | ``` 111 | 112 | #### Fetch propagated span 113 | 114 | cpp2sky supports only HTTP tracer now. 115 | Tracing span will be delivered from `sw8` and `sw8-x` HTTP headers. For more detail, please visit [here](https://github.com/apache/skywalking/blob/08781b41a8255bcceebb3287364c81745a04bec6/docs/en/protocols/Skywalking-Cross-Process-Propagation-Headers-Protocol-v3.md) 116 | Then, you can create propagated span object by decoding these items. 117 | 118 | ```cpp 119 | SpanContextSharedPtr parent_span = createSpanContext(parent); 120 | ``` 121 | 122 | #### Create span 123 | 124 | First, you must create tracing context that holds all spans, then crete initial entry span. 125 | 126 | ```cpp 127 | TracingContextSharedPtr tracing_context = tracer->newContext(); 128 | TracingSpanSharedPtr tracing_span = tracing_context->createEntrySpan(); 129 | ``` 130 | 131 | After that, you can create another span to trace another workload, such as RPC to other services. 132 | Note that you must have parent span to create secondary span. It will construct parent-child relation when analysis. 133 | 134 | ```cpp 135 | TracingSpanSharedPtr current_span = tracing_context->createExitSpan(current_span); 136 | ``` 137 | 138 | Alternative approach is RAII based one. It is used like below, 139 | 140 | ```cpp 141 | { 142 | StartEntrySpan entry_span(tracing_context, "sample_op1"); 143 | { 144 | StartExitSpan exit_span(tracing_context, entry_span.get(), "sample_op2"); 145 | 146 | // something... 147 | } 148 | } 149 | ``` 150 | 151 | #### Send segment to OAP 152 | 153 | Note that TracingContext is unique pointer. So when you'd like to send data, you must move it and don't refer after sending, 154 | to avoid undefined behavior. 155 | 156 | ```cpp 157 | TracingContextSharedPtr tracing_context = tracer->newContext(); 158 | TracingSpanSharedPtr tracing_span = tracing_context->createEntrySpan(); 159 | 160 | tracing_span->startSpan("sample_workload"); 161 | tracing_span->endSpan(); 162 | 163 | tracer->report(std::move(tracing_context)); 164 | ``` 165 | 166 | #### Skywalking CDS 167 | 168 | C++ agent implements Skywalking CDS feature it allows to change bootstrap config dynamically from the response of sync request, invoked from this periodically. 169 | Dynamically configurable values are described in description of properties on `docs/README.md'. 170 | 171 | ```cpp 172 | TracerConfig config; 173 | // If you set this value as zero, CDS request won't occur. 174 | config.set_cds_request_interval(5); // CDS request interval should be 5sec 175 | ``` 176 | 177 | If you are using Consul KVS as backend, we could put configuration value through HTTP request. 178 | 179 | ```yaml 180 | configurations: 181 | service_name: 182 | ignore_suffix: '/ignore, /hoge' 183 | ``` 184 | 185 | After setup configurations, try to put values with 186 | 187 | ``` 188 | curl --request PUT --data-binary "@./config.yaml" http://localhost:8500/v1/kv/configuration-discovery.default.agentConfigurations 189 | ``` 190 | 191 | ## Trace and Log integration 192 | 193 | cpp2sky implements to output logs which is the key to integrate with actual tracing context. 194 | 195 | #### Supported Logger 196 | 197 | - [spdlog](https://github.com/gabime/spdlog) 198 | 199 | ```cpp 200 | #include 201 | #include 202 | 203 | int main() { 204 | auto logger = spdlog::default_logger(); 205 | // set_pattern must be called. 206 | logger->set_pattern(logFormat()); 207 | 208 | // It will generate log message as follows. 209 | // 210 | // {"level": "warning", "msg": "sample", "SW_CTX": ["service","instance","trace_id","segment_id","span_id"]} 211 | // 212 | logger->warn(tracing_context->logMessage("sample")); 213 | } 214 | ``` 215 | 216 | ## Security 217 | 218 | If you've found any security issues, please read [Security Reporting Process](https://github.com/SkyAPM/cpp2sky/blob/main/SECURITY.md) and take described steps. 219 | 220 | ## LICENSE 221 | 222 | Apache 2.0 License. See [LICENSE](https://github.com/SkyAPM/cpp2sky/blob/main/LICENSE) for more detail. 223 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Reporting Process 2 | 3 | If you've found a security vulnerability on cpp2sky, please report the description and details to skyapm-security@googlegroups.com. After that we will acknowledge about your report then send confiramation email. 4 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "cpp2sky") 2 | 3 | load("//bazel:repositories.bzl", "cpp2sky_dependencies") 4 | 5 | cpp2sky_dependencies() 6 | 7 | load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") 8 | 9 | rules_proto_dependencies() 10 | 11 | rules_proto_toolchains() 12 | 13 | load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") 14 | 15 | grpc_deps() 16 | 17 | load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") 18 | 19 | grpc_extra_deps() 20 | 21 | load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") 22 | 23 | hedron_compile_commands_setup() 24 | -------------------------------------------------------------------------------- /bazel/BUILD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkyAPM/cpp2sky/d8b7b81a585c0938667082e00838053a29ecad6b/bazel/BUILD -------------------------------------------------------------------------------- /bazel/coverage/BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) # Apache 2 2 | 3 | filegroup( 4 | name = "coverage_support", 5 | srcs = ["collect_cc_coverage.sh"], 6 | ) 7 | -------------------------------------------------------------------------------- /bazel/coverage/collect_cc_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | # This is fork from https://raw.githubusercontent.com/bazelbuild/bazel/master/tools/test/collect_cc_coverage.sh 3 | 4 | # Copyright 2016 The Bazel Authors. All rights reserved. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # This script collects code coverage data for C++ sources, after the tests 19 | # were executed. 20 | # 21 | # Bazel C++ code coverage collection support is poor and limited. There is 22 | # an ongoing effort to improve this (tracking issue #1118). 23 | # 24 | # Bazel uses the lcov tool for gathering coverage data. There is also 25 | # an experimental support for clang llvm coverage, which uses the .profraw 26 | # data files to compute the coverage report. 27 | # 28 | # This script assumes the following environment variables are set: 29 | # - COVERAGE_DIR Directory containing metadata files needed for 30 | # coverage collection (e.g. gcda files, profraw). 31 | # - COVERAGE_MANIFEST Location of the instrumented file manifest. 32 | # - COVERAGE_GCOV_PATH Location of gcov. This is set by the TestRunner. 33 | # - COVERAGE_GCOV_OPTIONS Additional options to pass to gcov. 34 | # - ROOT Location from where the code coverage collection 35 | # was invoked. 36 | # 37 | # The script looks in $COVERAGE_DIR for the C++ metadata coverage files (either 38 | # gcda or profraw) and uses either lcov or gcov to get the coverage data. 39 | # The coverage data is placed in $COVERAGE_OUTPUT_FILE. 40 | 41 | # Checks if clang llvm coverage should be used instead of lcov. 42 | function uses_llvm() { 43 | if stat "${COVERAGE_DIR}"/*.profraw >/dev/null 2>&1; then 44 | return 0 45 | fi 46 | return 1 47 | } 48 | 49 | # Returns 0 if gcov must be used, 1 otherwise. 50 | function uses_gcov() { 51 | [[ "$GCOV_COVERAGE" -eq "1" ]] && return 0 52 | return 1 53 | } 54 | 55 | function init_gcov() { 56 | # Symlink the gcov tool such with a link called gcov. Clang comes with a tool 57 | # called llvm-cov, which behaves like gcov if symlinked in this way (otherwise 58 | # we would need to invoke it with "llvm-cov gcov"). 59 | # For more details see https://llvm.org/docs/CommandGuide/llvm-cov.html. 60 | GCOV="${COVERAGE_DIR}/gcov" 61 | if [ ! -f "${COVERAGE_GCOV_PATH}" ]; then 62 | echo "GCov does not exist at the given path: '${COVERAGE_GCOV_PATH}'" 63 | exit 1 64 | fi 65 | # When using a tool from a toolchain COVERAGE_GCOV_PATH will be a relative 66 | # path. To make it work on different working directories it's required to 67 | # convert the path to an absolute one. 68 | COVERAGE_GCOV_PATH_ABS="$(cd "${COVERAGE_GCOV_PATH%/*}" && pwd)/${COVERAGE_GCOV_PATH##*/}" 69 | ln -s "${COVERAGE_GCOV_PATH_ABS}" "${GCOV}" 70 | } 71 | 72 | # Computes code coverage data using the clang generated metadata found under 73 | # $COVERAGE_DIR. 74 | # Writes the collected coverage into the given output file. 75 | function llvm_coverage_lcov() { 76 | local output_file="${1}"; shift 77 | export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw" 78 | "${COVERAGE_GCOV_PATH}" merge -output "${output_file}.data" \ 79 | "${COVERAGE_DIR}"/*.profraw 80 | 81 | local object_param="" 82 | while read -r line; do 83 | if [[ ${line: -24} == "runtime_objects_list.txt" ]]; then 84 | while read -r line_runtime_object; do 85 | if [[ ${line_runtime_object} == *"absl"* ]]; then 86 | continue 87 | fi 88 | object_param+=" -object ${RUNFILES_DIR}/${TEST_WORKSPACE}/${line_runtime_object}" 89 | done < "${line}" 90 | fi 91 | done < "${COVERAGE_MANIFEST}" 92 | 93 | "${LLVM_COV}" export -instr-profile "${output_file}.data" -format=lcov \ 94 | -ignore-filename-regex='.*external/.+' \ 95 | -ignore-filename-regex='/tmp/.+' \ 96 | ${object_param} | sed 's#/proc/self/cwd/##' > "${output_file}" 97 | } 98 | 99 | function llvm_coverage_profdata() { 100 | local output_file="${1}"; shift 101 | export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw" 102 | "${COVERAGE_GCOV_PATH}" merge -output "${output_file}" \ 103 | "${COVERAGE_DIR}"/*.profraw 104 | } 105 | 106 | # Generates a code coverage report in gcov intermediate text format by invoking 107 | # gcov and using the profile data (.gcda) and notes (.gcno) files. 108 | # 109 | # The profile data files are expected to be found under $COVERAGE_DIR. 110 | # The notes file are expected to be found under $ROOT. 111 | # 112 | # - output_file The location of the file where the generated code coverage 113 | # report is written. 114 | function gcov_coverage() { 115 | local output_file="${1}"; shift 116 | 117 | # We'll save the standard output of each the gcov command in this log. 118 | local gcov_log="$output_file.gcov.log" 119 | 120 | # Copy .gcno files next to their corresponding .gcda files in $COVERAGE_DIR 121 | # because gcov expects them to be in the same directory. 122 | while read -r line; do 123 | if [[ ${line: -4} == "gcno" ]]; then 124 | gcno_path=${line} 125 | local gcda="${COVERAGE_DIR}/$(dirname ${gcno_path})/$(basename ${gcno_path} .gcno).gcda" 126 | # If the gcda file was not found we skip generating coverage from the gcno 127 | # file. 128 | if [[ -f "$gcda" ]]; then 129 | # gcov expects both gcno and gcda files to be in the same directory. 130 | # We overcome this by copying the gcno to $COVERAGE_DIR where the gcda 131 | # files are expected to be. 132 | if [ ! -f "${COVERAGE_DIR}/${gcno_path}" ]; then 133 | mkdir -p "${COVERAGE_DIR}/$(dirname ${gcno_path})" 134 | cp "$ROOT/${gcno_path}" "${COVERAGE_DIR}/${gcno_path}" 135 | fi 136 | # Invoke gcov to generate a code coverage report with the flags: 137 | # -i Output gcov file in an intermediate text format. 138 | # The output is a single .gcov file per .gcda file. 139 | # No source code is required. 140 | # -o directory The directory containing the .gcno and 141 | # .gcda data files. 142 | # "${gcda"} The input file name. gcov is looking for data files 143 | # named after the input filename without its extension. 144 | # gcov produces files called .gcov in the current 145 | # directory. These contain the coverage information of the source file 146 | # they correspond to. One .gcov file is produced for each source 147 | # (or header) file containing code which was compiled to produce the 148 | # .gcda files. 149 | # Don't generate branch coverage (-b) because of a gcov issue that 150 | # segfaults when both -i and -b are used (see 151 | # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84879). 152 | "${GCOV}" -i $COVERAGE_GCOV_OPTIONS -o "$(dirname ${gcda})" "${gcda}" 153 | 154 | # Extract gcov's version: the output of `gcov --version` contains the 155 | # version as a set of major-minor-patch numbers, of which we extract 156 | # the major version. 157 | gcov_major_version=$("${GCOV}" --version | sed -n -E -e 's/^.*\s([0-9]+)\.[0-9]+\.[0-9]+\s?.*$/\1/p') 158 | 159 | # Check the gcov version so we can process the data correctly 160 | if [[ $gcov_major_version -ge 9 ]]; then 161 | # gcov 9 or higher use a JSON based format for their coverage reports. 162 | # The output is generated into multiple files: "$(basename ${gcda}).gcov.json.gz" 163 | # Concatenating JSON documents does not yield a valid document, so they are moved individually 164 | mv -- *.gcov.json.gz "$(dirname "$output_file")" 165 | else 166 | # Append all .gcov files in the current directory to the output file. 167 | cat -- *.gcov >> "$output_file" 168 | # Delete the .gcov files. 169 | rm -- *.gcov 170 | fi 171 | fi 172 | fi 173 | done < "${COVERAGE_MANIFEST}" 174 | } 175 | 176 | function main() { 177 | init_gcov 178 | 179 | # If llvm code coverage is used, we output the raw code coverage report in 180 | # the $COVERAGE_OUTPUT_FILE. This report will not be converted to any other 181 | # format by LcovMerger. 182 | # TODO(#5881): Convert profdata reports to lcov. 183 | if uses_llvm; then 184 | if [[ "${GENERATE_LLVM_LCOV}" == "1" ]]; then 185 | BAZEL_CC_COVERAGE_TOOL="LLVM_LCOV" 186 | else 187 | BAZEL_CC_COVERAGE_TOOL="PROFDATA" 188 | fi 189 | fi 190 | 191 | # When using either gcov or lcov, have an output file specific to the test 192 | # and format used. For lcov we generate a ".dat" output file and for gcov 193 | # a ".gcov" output file. It is important that these files are generated under 194 | # COVERAGE_DIR. 195 | # When this script is invoked by tools/test/collect_coverage.sh either of 196 | # these two coverage reports will be picked up by LcovMerger and their 197 | # content will be converted and/or merged with other reports to an lcov 198 | # format, generating the final code coverage report. 199 | case "$BAZEL_CC_COVERAGE_TOOL" in 200 | ("GCOV") gcov_coverage "$COVERAGE_DIR/_cc_coverage.gcov" ;; 201 | ("PROFDATA") llvm_coverage_profdata "$COVERAGE_DIR/_cc_coverage.profdata" ;; 202 | ("LLVM_LCOV") llvm_coverage_lcov "$COVERAGE_DIR/_cc_coverage.dat" ;; 203 | (*) echo "Coverage tool $BAZEL_CC_COVERAGE_TOOL not supported" \ 204 | && exit 1 205 | esac 206 | } 207 | 208 | main 209 | -------------------------------------------------------------------------------- /bazel/fmtlib.BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | licenses(["notice"]) # Apache 2 4 | 5 | cc_library( 6 | name = "fmtlib", 7 | hdrs = glob([ 8 | "include/fmt/*.h", 9 | ]), 10 | defines = ["FMT_HEADER_ONLY"], 11 | includes = ["include"], 12 | visibility = ["//visibility:public"], 13 | ) 14 | -------------------------------------------------------------------------------- /bazel/httplib.BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "httplib", 5 | hdrs = ["httplib.h"], 6 | visibility = ["//visibility:public"], 7 | ) 8 | -------------------------------------------------------------------------------- /bazel/repositories.bzl: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | def cpp2sky_dependencies(): 4 | rules_proto() 5 | skywalking_data_collect_protocol() 6 | com_github_grpc_grpc() 7 | com_google_googletest() 8 | com_google_protobuf() 9 | com_github_httplib() 10 | com_github_fmtlib_fmt() 11 | com_google_abseil() 12 | com_github_gabime_spdlog() 13 | hedron_compile_commands() 14 | 15 | def skywalking_data_collect_protocol(): 16 | http_archive( 17 | name = "skywalking_data_collect_protocol", 18 | sha256 = "4cf7cf84a9478a09429a7fbc6ad1e1b10c70eb54999438a36eacaf539a39d343", 19 | urls = [ 20 | "https://github.com/apache/skywalking-data-collect-protocol/archive/v9.1.0.tar.gz", 21 | ], 22 | strip_prefix = "skywalking-data-collect-protocol-9.1.0", 23 | ) 24 | 25 | def com_github_grpc_grpc(): 26 | http_archive( 27 | name = "com_github_grpc_grpc", 28 | sha256 = "1ccc2056b68b81ada8df61310e03dfa0541c34821fd711654d0590a7321db9c8", 29 | urls = ["https://github.com/grpc/grpc/archive/a3ae8e00a2c5553c806e83fae83e33f0198913f0.tar.gz"], 30 | strip_prefix = "grpc-a3ae8e00a2c5553c806e83fae83e33f0198913f0", 31 | ) 32 | 33 | def rules_proto(): 34 | http_archive( 35 | name = "rules_proto", 36 | sha256 = "66bfdf8782796239d3875d37e7de19b1d94301e8972b3cbd2446b332429b4df1", 37 | strip_prefix = "rules_proto-4.0.0", 38 | urls = [ 39 | "https://github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz", 40 | ], 41 | ) 42 | 43 | def com_google_googletest(): 44 | http_archive( 45 | name = "com_google_googletest", 46 | sha256 = "7897bfaa5ad39a479177cfb5c3ce010184dbaee22a7c3727b212282871918751", 47 | strip_prefix = "googletest-a4ab0abb93620ce26efad9de9296b73b16e88588", 48 | urls = ["https://github.com/google/googletest/archive/a4ab0abb93620ce26efad9de9296b73b16e88588.tar.gz"], 49 | ) 50 | 51 | def com_google_protobuf(): 52 | http_archive( 53 | name = "com_google_protobuf", 54 | sha256 = "89ac31a93832e204db6d73b1e80f39f142d5747b290f17340adce5be5b122f94", 55 | strip_prefix = "protobuf-3.19.4", 56 | urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v3.19.4/protobuf-cpp-3.19.4.tar.gz"], 57 | ) 58 | 59 | def com_github_httplib(): 60 | http_archive( 61 | name = "com_github_httplib", 62 | sha256 = "0e424f92b607fc9245c144dada85c2e97bc6cc5938c0c69a598a5b2a5c1ab98a", 63 | strip_prefix = "cpp-httplib-0.7.15", 64 | build_file = "//bazel:httplib.BUILD", 65 | urls = ["https://github.com/yhirose/cpp-httplib/archive/v0.7.15.tar.gz"], 66 | ) 67 | 68 | def com_github_fmtlib_fmt(): 69 | http_archive( 70 | name = "com_github_fmtlib_fmt", 71 | sha256 = "23778bad8edba12d76e4075da06db591f3b0e3c6c04928ced4a7282ca3400e5d", 72 | strip_prefix = "fmt-8.1.1", 73 | build_file = "//bazel:fmtlib.BUILD", 74 | urls = ["https://github.com/fmtlib/fmt/releases/download/8.1.1/fmt-8.1.1.zip"], 75 | ) 76 | 77 | def com_github_gabime_spdlog(): 78 | http_archive( 79 | name = "com_github_gabime_spdlog", 80 | sha256 = "6fff9215f5cb81760be4cc16d033526d1080427d236e86d70bb02994f85e3d38", 81 | strip_prefix = "spdlog-1.9.2", 82 | build_file = "//bazel:spdlog.BUILD", 83 | urls = ["https://github.com/gabime/spdlog/archive/v1.9.2.tar.gz"], 84 | ) 85 | 86 | def com_google_abseil(): 87 | http_archive( 88 | name = "com_google_absl", 89 | sha256 = "5ca73792af71ab962ee81cdf575f79480704b8fb87e16ca8f1dc1e9b6822611e", 90 | strip_prefix = "abseil-cpp-6f43f5bb398b6685575b36874e36cf1695734df1", 91 | urls = ["https://github.com/abseil/abseil-cpp/archive/6f43f5bb398b6685575b36874e36cf1695734df1.tar.gz"], 92 | ) 93 | 94 | def hedron_compile_commands(): 95 | # Hedron's Compile Commands Extractor for Bazel 96 | # https://github.com/hedronvision/bazel-compile-commands-extractor 97 | http_archive( 98 | name = "hedron_compile_commands", 99 | url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/dc36e462a2468bd79843fe5176542883b8ce4abe.tar.gz", 100 | sha256 = "d63c1573eb1daa4580155a1f0445992878f4aa8c34eb165936b69504a8407662", 101 | strip_prefix = "bazel-compile-commands-extractor-dc36e462a2468bd79843fe5176542883b8ce4abe", 102 | ) 103 | -------------------------------------------------------------------------------- /bazel/spdlog.BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | licenses(["notice"]) # Apache 2 4 | 5 | cc_library( 6 | name = "spdlog", 7 | hdrs = glob([ 8 | "include/**/*.h", 9 | ]), 10 | defines = ["SPDLOG_FMT_EXTERNAL"], 11 | includes = ["include"], 12 | visibility = ["//visibility:public"], 13 | deps = ["@com_github_fmtlib_fmt//:fmtlib"], 14 | ) 15 | -------------------------------------------------------------------------------- /cmake/cpp2sky-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | include(CMakeFindDependencyMacro) 3 | 4 | set(CMAKE_THREAD_PREFER_PTHREAD TRUE) 5 | find_dependency(Threads) 6 | unset(CMAKE_THREAD_PREFER_PTHREAD) 7 | 8 | find_dependency(fmt) 9 | find_dependency(spdlog) 10 | find_dependency(httplib) 11 | find_dependency(Protobuf) 12 | find_dependency(gRPC) 13 | 14 | 15 | include(${CMAKE_CURRENT_LIST_DIR}/cpp2sky-targets.cmake) 16 | -------------------------------------------------------------------------------- /cmake/cpp2sky.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=@CMAKE_INSTALL_PREFIX@ 3 | libdir=@CPP2SKY_PKGCONFIG_LIBDIR@ 4 | includedir=@CPP2SKY_PKGCONFIG_INCLUDEDIR@ 5 | 6 | Name: cpp2sky 7 | Description: Distributed tracing and monitor SDK in CPP for Apache SkyWalking APM 8 | Version: @CPP2SKY_VERSION@ 9 | Libs: -L${libdir} -lcpp2sky 10 | Cflags: -I${includedir}/skywalking-protocol 11 | -------------------------------------------------------------------------------- /cmake/fmtlib.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | if(MSVC) 4 | add_definitions(-D_WIN32_WINNT=0x600) 5 | endif() 6 | 7 | find_package(Threads REQUIRED) 8 | 9 | if(FMTLIB_AS_SUBMODULE) 10 | # using submodule in case of git clone timeout 11 | if(CPP2SKY_INSTALL) 12 | set(FMT_INSTALL ON) 13 | endif(CPP2SKY_INSTALL) 14 | add_subdirectory(3rdparty/fmt ${CMAKE_CURRENT_BINARY_DIR}/fmt) 15 | message(STATUS "Using fmt via add_subdirectory.") 16 | elseif(FMTLIB_FETCHCONTENT) 17 | # using FetchContent to install spdlog 18 | include(FetchContent) 19 | if(${CMAKE_VERSION} VERSION_LESS 3.14) 20 | include(add_FetchContent_MakeAvailable.cmake) 21 | endif() 22 | 23 | FetchContent_Declare( 24 | fmtlib 25 | URL https://github.com/fmtlib/fmt/releases/download/8.1.1/fmt-8.1.1.zip 26 | URL_HASH SHA256=23778bad8edba12d76e4075da06db591f3b0e3c6c04928ced4a7282ca3400e5d 27 | ) 28 | FetchContent_MakeAvailable(fmtlib) 29 | else() 30 | find_package(fmt CONFIG REQUIRED) 31 | message(STATUS "Using fmt by find_package") 32 | endif() 33 | -------------------------------------------------------------------------------- /cmake/grpc.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2018 gRPC authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # cmake build file for C++ route_guide example. 16 | # Assumes protobuf and gRPC have been installed using cmake. 17 | # See cmake_externalproject/CMakeLists.txt for all-in-one cmake build 18 | # that automatically builds all the dependencies before building route_guide. 19 | 20 | cmake_minimum_required(VERSION 3.14) 21 | 22 | if(MSVC) 23 | add_definitions(-D_WIN32_WINNT=0x600) 24 | endif() 25 | 26 | find_package(Threads REQUIRED) 27 | 28 | if(GRPC_AS_SUBMODULE) 29 | # One way to build a projects that uses gRPC is to just include the 30 | # entire gRPC project tree via "add_subdirectory". 31 | # This approach is very simple to use, but the are some potential 32 | # disadvantages: 33 | # * it includes gRPC's CMakeLists.txt directly into your build script 34 | # without and that can make gRPC's internal setting interfere with your 35 | # own build. 36 | # * depending on what's installed on your system, the contents of submodules 37 | # in gRPC's third_party/* might need to be available (and there might be 38 | # additional prerequisites required to build them). Consider using 39 | # the gRPC_*_PROVIDER options to fine-tune the expected behavior. 40 | # 41 | # A more robust approach to add dependency on gRPC is using 42 | # cmake's ExternalProject_Add (see cmake_externalproject/CMakeLists.txt). 43 | 44 | # Include the gRPC's cmake build (normally grpc source code would live 45 | # in a git submodule called "third_party/grpc", but this example lives in 46 | # the same repository as gRPC sources, so we just look a few directories up) 47 | if(NOT GRPC_ROOT_DIR) 48 | set(GRPC_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/grpc) 49 | endif() 50 | add_subdirectory(${GRPC_ROOT_DIR} 3rdparty/grpc) 51 | message(STATUS "Using gRPC via add_subdirectory.") 52 | # After using add_subdirectory, we can now use the grpc targets directly from 53 | # this build. 54 | set(_PROTOBUF_LIBPROTOBUF libprotobuf) 55 | set(_REFLECTION grpc++_reflection) 56 | if(CMAKE_CROSSCOMPILING) 57 | find_program(_PROTOBUF_PROTOC protoc) 58 | else() 59 | set(_PROTOBUF_PROTOC $) 60 | endif() 61 | set(_GRPC_GRPCPP grpc++) 62 | if(CMAKE_CROSSCOMPILING) 63 | find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) 64 | else() 65 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 66 | endif() 67 | elseif(GRPC_FETCHCONTENT) 68 | # Another way is to use CMake's FetchContent module to clone gRPC at 69 | # configure time. This makes gRPC's source code available to your project, 70 | # similar to a git submodule. 71 | message(STATUS "Using gRPC via add_subdirectory (FetchContent).") 72 | include(FetchContent) 73 | FetchContent_Declare( 74 | grpc 75 | URL https://github.com/grpc/grpc/archive/a3ae8e00a2c5553c806e83fae83e33f0198913f0.tar.gz 76 | URL_HASH SHA256=1ccc2056b68b81ada8df61310e03dfa0541c34821fd711654d0590a7321db9c8 77 | ) 78 | FetchContent_MakeAvailable(grpc) 79 | 80 | # Since FetchContent uses add_subdirectory under the hood, we can use 81 | # the grpc targets directly from this build. 82 | set(_PROTOBUF_LIBPROTOBUF libprotobuf) 83 | set(_REFLECTION grpc++_reflection) 84 | set(_PROTOBUF_PROTOC $) 85 | set(_GRPC_GRPCPP grpc++) 86 | if(CMAKE_CROSSCOMPILING) 87 | find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) 88 | else() 89 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 90 | endif() 91 | else() 92 | # This branch assumes that gRPC and all its dependencies are already installed 93 | # on this system, so they can be located by find_package(). 94 | message(STATUS "gRPC and all its dependencies should be able to located by find_package().") 95 | 96 | # Find Protobuf installation 97 | # Looks for protobuf-config.cmake file installed by Protobuf's cmake installation. 98 | option(protobuf_MODULE_COMPATIBLE TRUE) 99 | find_package(Protobuf CONFIG REQUIRED) 100 | message(STATUS "Using protobuf ${Protobuf_VERSION}") 101 | 102 | set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) 103 | set(_REFLECTION gRPC::grpc++_reflection) 104 | 105 | message(STATUS "CMAKE_CROSSCOMPILING: ${CMAKE_CROSSCOMPILING}") 106 | 107 | if(CMAKE_CROSSCOMPILING) 108 | find_program(_PROTOBUF_PROTOC protoc) 109 | else() 110 | set(_PROTOBUF_PROTOC $) 111 | endif() 112 | 113 | # Find gRPC installation 114 | # Looks for gRPCConfig.cmake file installed by gRPC's cmake installation. 115 | find_package(gRPC CONFIG REQUIRED) 116 | message(STATUS "Using gRPC ${gRPC_VERSION}") 117 | 118 | set(_GRPC_GRPCPP gRPC::grpc++) 119 | if(CMAKE_CROSSCOMPILING) 120 | find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) 121 | else() 122 | set(_GRPC_CPP_PLUGIN_EXECUTABLE $) 123 | endif() 124 | 125 | endif() -------------------------------------------------------------------------------- /cmake/httplib.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | if(MSVC) 4 | add_definitions(-D_WIN32_WINNT=0x600) 5 | endif() 6 | 7 | find_package(Threads REQUIRED) 8 | 9 | if(HTTPLIB_AS_SUBMODULE) 10 | # using submodule in case of git clone timeout 11 | add_subdirectory(3rdparty/httplib ${CMAKE_CURRENT_BINARY_DIR}/httplib) 12 | message(STATUS "Using httplib via add_subdirectory.") 13 | elseif(HTTPLIB_FETCHCONTENT) 14 | # using FetchContent to install spdlog 15 | include(FetchContent) 16 | if(${CMAKE_VERSION} VERSION_LESS 3.14) 17 | include(add_FetchContent_MakeAvailable.cmake) 18 | endif() 19 | 20 | set(HTTPLIB_GIT_TAG v0.7.15) 21 | set(HTTPLIB_GIT_URL https://github.com/yhirose/cpp-httplib.git) 22 | 23 | FetchContent_Declare( 24 | httplib 25 | GIT_REPOSITORY ${HTTPLIB_GIT_URL} 26 | GIT_TAG ${HTTPLIB_GIT_TAG} 27 | ) 28 | 29 | FetchContent_MakeAvailable(httplib) 30 | else() 31 | find_package(httplib CONFIG REQUIRED) 32 | message(STATUS "Using httplib by find_package") 33 | endif() 34 | -------------------------------------------------------------------------------- /cmake/project-import-cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | if(POLICY CMP0091) 3 | cmake_policy(SET CMP0091 NEW) # recognize CMAKE_MSVC_RUNTIME_LIBRARY 4 | endif() 5 | 6 | project(cpp2sky-import-cmake) 7 | 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_EXTENSIONS OFF) 10 | 11 | find_package(cpp2sky CONFIG REQUIRED) 12 | 13 | add_executable(example-sample sample.cc) 14 | target_link_libraries(example-sample PUBLIC 15 | cpp2sky::cpp2sky 16 | proto_lib 17 | ) 18 | 19 | add_executable(example-sample-client sample_client.cc) 20 | target_link_libraries(example-sample-client PUBLIC 21 | cpp2sky::cpp2sky 22 | proto_lib 23 | ) 24 | -------------------------------------------------------------------------------- /cmake/project-import-cmake/sample.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("node_0"); 30 | config.set_service_name("mesh"); 31 | config.set_address("0.0.0.0:11800"); 32 | config.set_cds_request_interval(5); 33 | } 34 | 35 | int main() { 36 | init(); 37 | 38 | httplib::Server svr; 39 | // 1. Create tracer object to send span data to OAP. 40 | auto tracer = createInsecureGrpcTracer(config); 41 | 42 | svr.Get("/ping", [&](const httplib::Request& req, httplib::Response& res) { 43 | std::string context = req.get_header_value(kPropagationHeader.data()); 44 | 45 | TracingContextSharedPtr tracing_context; 46 | 47 | if (!context.empty()) { 48 | // 2. Create tracing context with propagated information. 49 | tracing_context = tracer->newContext(createSpanContext(context)); 50 | } 51 | 52 | { 53 | // 3. Create entry span. 54 | StartEntrySpan current_span(tracing_context, "sample_op3"); 55 | 56 | /** 57 | * something.... 58 | */ 59 | } 60 | 61 | // 4. Send span data 62 | if (tracing_context != nullptr) { 63 | tracer->report(std::move(tracing_context)); 64 | } 65 | }); 66 | 67 | svr.listen("0.0.0.0", 8081); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /cmake/project-import-cmake/sample_client.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("client_0"); 30 | config.set_service_name(""); 31 | config.set_address("0.0.0.0:11800"); 32 | } 33 | 34 | int main() { 35 | init(); 36 | 37 | // 1. Create tracer object to send span data to OAP. 38 | auto tracer = createInsecureGrpcTracer(config); 39 | 40 | // 2. Create tracing context 41 | auto tracing_context = tracer->newContext(); 42 | 43 | /** 44 | * 3. Create entry span it traces RPC call. 45 | * Span lifetime is managed by RAII. So user don't have to call startSpan and 46 | * endSpan explicitly. But it provides basic approach that doesn't use RAII. 47 | * 48 | * example: 49 | * 50 | * auto current_span = tracing_context->createEntrySpan(); 51 | * current_span->startSpan("sample_op1"); 52 | * 53 | * auto current_span2 = tracing_context->createExitSpan(); 54 | * current_span2->startSpan("sample_op2"); 55 | * 56 | * httplib::Client cli("remote", 8082); 57 | * httplib::Headers headers = { 58 | * {kPropagationHeader.data(), 59 | * tracing_context->createSW8HeaderValue("remote:8082")}}; 60 | * 61 | * auto res = cli.Get("/ping", headers); 62 | * 63 | * current_span2->endSpan(); 64 | * current_span->endSpan(); 65 | * 66 | */ 67 | { 68 | StartEntrySpan entry_span(tracing_context, "sample_op1"); 69 | 70 | { 71 | std::string target_address = "127.0.0.1:8081"; 72 | StartExitSpan exit_span(tracing_context, entry_span.get(), "sample_op2"); 73 | exit_span.get()->setPeer(target_address); 74 | 75 | httplib::Client cli("127.0.0.1", 8081); 76 | httplib::Headers headers = { 77 | {kPropagationHeader.data(), 78 | *tracing_context->createSW8HeaderValue(target_address)}}; 79 | 80 | auto res = cli.Get("/ping", headers); 81 | } 82 | } 83 | 84 | tracer->report(std::move(tracing_context)); 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /cmake/project-import-pkgconfig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | if(POLICY CMP0091) 3 | cmake_policy(SET CMP0091 NEW) # recognize CMAKE_MSVC_RUNTIME_LIBRARY 4 | endif() 5 | 6 | project(cpp2sky-import-pkgconfig) 7 | 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_EXTENSIONS OFF) 10 | 11 | find_package(PkgConfig REQUIRED) 12 | pkg_check_modules(CPP2SKY_PKG REQUIRED cpp2sky) 13 | 14 | find_package(fmt) 15 | find_package(spdlog) 16 | find_package(httplib) 17 | 18 | find_package(Protobuf CONFIG REQUIRED) 19 | set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) 20 | set(_REFLECTION gRPC::grpc++_reflection) 21 | find_package(gRPC CONFIG REQUIRED) 22 | set(_GRPC_GRPCPP gRPC::grpc++) 23 | 24 | 25 | add_executable(example-sample sample.cc) 26 | target_link_libraries(example-sample PRIVATE 27 | ${CPP2SKY_PKG_LIBRARIES} 28 | proto_lib 29 | ${_REFLECTION} 30 | ${_GRPC_GRPCPP} 31 | ${_PROTOBUF_LIBPROTOBUF} 32 | fmt::fmt 33 | spdlog::spdlog 34 | httplib::httplib 35 | ) 36 | 37 | add_executable(example-sample-client sample_client.cc) 38 | target_link_libraries(example-sample-client PUBLIC 39 | ${CPP2SKY_PKG_LIBRARIES} 40 | proto_lib 41 | ${_REFLECTION} 42 | ${_GRPC_GRPCPP} 43 | ${_PROTOBUF_LIBPROTOBUF} 44 | fmt::fmt 45 | spdlog::spdlog 46 | httplib::httplib 47 | ) 48 | -------------------------------------------------------------------------------- /cmake/project-import-pkgconfig/sample.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("node_0"); 30 | config.set_service_name("mesh"); 31 | config.set_address("0.0.0.0:11800"); 32 | config.set_cds_request_interval(5); 33 | } 34 | 35 | int main() { 36 | init(); 37 | 38 | httplib::Server svr; 39 | // 1. Create tracer object to send span data to OAP. 40 | auto tracer = createInsecureGrpcTracer(config); 41 | 42 | svr.Get("/ping", [&](const httplib::Request& req, httplib::Response& res) { 43 | std::string context = req.get_header_value(kPropagationHeader.data()); 44 | 45 | TracingContextSharedPtr tracing_context; 46 | 47 | if (!context.empty()) { 48 | // 2. Create tracing context with propagated information. 49 | tracing_context = tracer->newContext(createSpanContext(context)); 50 | } 51 | 52 | { 53 | // 3. Create entry span. 54 | StartEntrySpan current_span(tracing_context, "sample_op3"); 55 | 56 | /** 57 | * something.... 58 | */ 59 | } 60 | 61 | // 4. Send span data 62 | if (tracing_context != nullptr) { 63 | tracer->report(std::move(tracing_context)); 64 | } 65 | }); 66 | 67 | svr.listen("0.0.0.0", 8081); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /cmake/project-import-pkgconfig/sample_client.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("client_0"); 30 | config.set_service_name(""); 31 | config.set_address("0.0.0.0:11800"); 32 | } 33 | 34 | int main() { 35 | init(); 36 | 37 | // 1. Create tracer object to send span data to OAP. 38 | auto tracer = createInsecureGrpcTracer(config); 39 | 40 | // 2. Create tracing context 41 | auto tracing_context = tracer->newContext(); 42 | 43 | /** 44 | * 3. Create entry span it traces RPC call. 45 | * Span lifetime is managed by RAII. So user don't have to call startSpan and 46 | * endSpan explicitly. But it provides basic approach that doesn't use RAII. 47 | * 48 | * example: 49 | * 50 | * auto current_span = tracing_context->createEntrySpan(); 51 | * current_span->startSpan("sample_op1"); 52 | * 53 | * auto current_span2 = tracing_context->createExitSpan(); 54 | * current_span2->startSpan("sample_op2"); 55 | * 56 | * httplib::Client cli("remote", 8082); 57 | * httplib::Headers headers = { 58 | * {kPropagationHeader.data(), 59 | * tracing_context->createSW8HeaderValue("remote:8082")}}; 60 | * 61 | * auto res = cli.Get("/ping", headers); 62 | * 63 | * current_span2->endSpan(); 64 | * current_span->endSpan(); 65 | * 66 | */ 67 | { 68 | StartEntrySpan entry_span(tracing_context, "sample_op1"); 69 | 70 | { 71 | std::string target_address = "127.0.0.1:8081"; 72 | StartExitSpan exit_span(tracing_context, entry_span.get(), "sample_op2"); 73 | exit_span.get()->setPeer(target_address); 74 | 75 | httplib::Client cli("127.0.0.1", 8081); 76 | httplib::Headers headers = { 77 | {kPropagationHeader.data(), 78 | *tracing_context->createSW8HeaderValue(target_address)}}; 79 | 80 | auto res = cli.Get("/ping", headers); 81 | } 82 | } 83 | 84 | tracer->report(std::move(tracing_context)); 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /cmake/proto2cpp.cmake: -------------------------------------------------------------------------------- 1 | #[[ 2 | function PROTOBUF_GENERATE_CPP demo: 3 | 4 | # case 01: 5 | ROOT_DIR = ${CMAKE_SOURCE_DIR}/3rdparty/skywalking-data-collect-protocol 6 | NEED_GRPC_SERVICE = ON 7 | ARGN = language-agent/*.proto; 8 | 9 | # case 02: 10 | ROOT_DIR = ${CMAKE_SOURCE_DIR}/3rdparty/skywalking-data-collect-protocol 11 | NEED_GRPC_SERVICE = OFF 12 | ARGN = common/*.proto; 13 | ]] 14 | function(PROTOBUF_GENERATE_CPP SRCS HDRS ROOT_DIR NEED_GRPC_SERVICE) 15 | if(NOT ARGN) 16 | message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files") 17 | return() 18 | endif() 19 | 20 | set(${SRCS}) 21 | set(${HDRS}) 22 | 23 | foreach(FIL ${ARGN}) 24 | get_filename_component(FIL_WE ${FIL} NAME_WE) 25 | get_filename_component(FIL_DIR ${FIL} PATH) 26 | 27 | list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.pb.cc") 28 | list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.pb.h") 29 | 30 | add_custom_command( 31 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.pb.cc" 32 | "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.pb.h" 33 | COMMAND ${_PROTOBUF_PROTOC} 34 | ARGS ${FIL} 35 | --cpp_out ${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol 36 | -I ${ROOT_DIR} 37 | COMMENT "Running Protocol Buffer Compiler on ${FIL}" 38 | VERBATIM 39 | ) 40 | 41 | if(NEED_GRPC_SERVICE) 42 | list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.grpc.pb.cc") 43 | list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.grpc.pb.h") 44 | 45 | add_custom_command( 46 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.grpc.pb.cc" 47 | "${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol/${FIL_DIR}/${FIL_WE}.grpc.pb.h" 48 | COMMAND ${_PROTOBUF_PROTOC} 49 | ARGS ${FIL} 50 | --grpc_out ${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol 51 | --plugin=protoc-gen-grpc=${_GRPC_CPP_PLUGIN_EXECUTABLE} 52 | -I ${ROOT_DIR} 53 | COMMENT "Running C++ protocol service compiler on ${msg}" 54 | VERBATIM 55 | ) 56 | endif(NEED_GRPC_SERVICE) 57 | 58 | endforeach() 59 | 60 | set_source_files_properties(${TMP_SRCS} ${TMP_HDRS} PROPERTIES GENERATED TRUE) 61 | set(${SRCS} ${${SRCS}} PARENT_SCOPE) 62 | set(${HDRS} ${${HDRS}} PARENT_SCOPE) 63 | endfunction() 64 | 65 | # BaseRootDir 66 | SET(SKYWALKING_PROTOCOL_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol) 67 | if(EXISTS ${SKYWALKING_PROTOCOL_OUTPUT_DIR} AND IS_DIRECTORY ${SKYWALKING_PROTOCOL_OUTPUT_DIR}) 68 | SET(PROTO_META_BASE_DIR ${SKYWALKING_PROTOCOL_OUTPUT_DIR}) 69 | else() 70 | file(MAKE_DIRECTORY ${SKYWALKING_PROTOCOL_OUTPUT_DIR}) 71 | SET(PROTO_META_BASE_DIR ${SKYWALKING_PROTOCOL_OUTPUT_DIR}) 72 | endif() 73 | 74 | # compile skywalking-data-collect-protocol/*.proto 75 | set(NEED_GRPC_SERVICE ON) 76 | set(PROTOC_FILES language-agent/Tracing.proto language-agent/ConfigurationDiscoveryService.proto) 77 | set(PROTOC_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/skywalking-data-collect-protocol") 78 | PROTOBUF_GENERATE_CPP(SERVICE_PROTO_SRCS SERVICE_PROTO_HDRS ${PROTOC_BASE_DIR} ${NEED_GRPC_SERVICE} ${PROTOC_FILES}) 79 | 80 | set(NEED_GRPC_SERVICE OFF) 81 | set(PROTOC_FILES common/Common.proto) 82 | set(PROTOC_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/skywalking-data-collect-protocol") 83 | PROTOBUF_GENERATE_CPP(SDC_PROTO_SRCS SDC_PROTO_HDRS ${PROTOC_BASE_DIR} ${NEED_GRPC_SERVICE} ${PROTOC_FILES}) 84 | 85 | 86 | # compile config.proto 87 | set(NEED_GRPC_SERVICE OFF) 88 | set(PROTOC_FILES cpp2sky/config.proto) 89 | set(PROTOC_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 90 | PROTOBUF_GENERATE_CPP(CONFIG_PROTO_SRCS CONFIG_PROTO_HDRS ${PROTOC_BASE_DIR} ${NEED_GRPC_SERVICE} ${PROTOC_FILES}) 91 | 92 | add_library(proto_lib STATIC 93 | ${SDC_PROTO_SRCS} 94 | ${SDC_PROTO_HDRS} 95 | ${CONFIG_PROTO_SRCS} 96 | ${CONFIG_PROTO_HDRS} 97 | ${SERVICE_PROTO_SRCS} 98 | ${SERVICE_PROTO_HDRS} 99 | ) 100 | target_include_directories(proto_lib PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol) 101 | 102 | install(TARGETS proto_lib 103 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 104 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 105 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 106 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 107 | ) 108 | 109 | install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/skywalking-protocol 110 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 111 | FILES_MATCHING 112 | PATTERN "*.h" 113 | PATTERN "*.cc" EXCLUDE) 114 | -------------------------------------------------------------------------------- /cmake/spdlog.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | if(MSVC) 4 | add_definitions(-D_WIN32_WINNT=0x600) 5 | endif() 6 | 7 | find_package(Threads REQUIRED) 8 | 9 | if(SPDLOG_AS_SUBMODULE) 10 | # using submodule in case of git clone timeout 11 | if(CPP2SKY_INSTALL) 12 | set(SPDLOG_MASTER_PROJECT ON) 13 | endif(CPP2SKY_INSTALL) 14 | add_subdirectory(3rdparty/spdlog ${CMAKE_CURRENT_BINARY_DIR}/spdlog) 15 | message(STATUS "Using spdlog via add_subdirectory.") 16 | elseif(SPDLOG_FETCHCONTENT) 17 | # using FetchContent to install spdlog 18 | include(FetchContent) 19 | if(${CMAKE_VERSION} VERSION_LESS 3.14) 20 | include(add_FetchContent_MakeAvailable.cmake) 21 | endif() 22 | 23 | set(SPDLOG_GIT_TAG v1.9.2) 24 | set(SPDLOG_GIT_URL https://github.com/gabime/spdlog.git) 25 | 26 | FetchContent_Declare( 27 | spdlog 28 | GIT_REPOSITORY ${SPDLOG_GIT_URL} 29 | GIT_TAG ${SPDLOG_GIT_TAG} 30 | ) 31 | 32 | FetchContent_MakeAvailable(spdlog) 33 | else() 34 | find_package(spdlog CONFIG REQUIRED) 35 | message(STATUS "Using spdlog by find_package") 36 | endif() 37 | -------------------------------------------------------------------------------- /cmake/util.cmake: -------------------------------------------------------------------------------- 1 | # Campatible with cmake 3.11 and above. 2 | macro(FetchContent_MakeAvailable NAME) 3 | FetchContent_GetProperties(${NAME}) 4 | if(NOT ${NAME}_POPULATED) 5 | FetchContent_Populate(${NAME}) 6 | add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) 7 | endif() 8 | endmacro()macro(FetchContent_MakeAvailable NAME) 9 | FetchContent_GetProperties(${NAME}) 10 | if(NOT ${NAME}_POPULATED) 11 | FetchContent_Populate(${NAME}) 12 | add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) 13 | endif() 14 | endmacro() -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | [[ -z "${SRCDIR}" ]] && SRCDIR="${PWD}" 6 | 7 | OUTPUT_DIR="./coverage_report/" 8 | DATA_DIR="${SRCDIR}/bazel-testlogs/" 9 | PROJECT=$(basename "${SRCDIR}") 10 | 11 | # This is the target that will be run to generate coverage data. It can be overridden 12 | # by consumer projects that want to run coverage on a different/combined target. 13 | # Command-line arguments take precedence over ${COVERAGE_TARGET}. 14 | if [[ $# -gt 0 ]]; then 15 | COVERAGE_TARGETS=$* 16 | elif [[ -n "${COVERAGE_TARGET}" ]]; then 17 | COVERAGE_TARGETS=${COVERAGE_TARGET} 18 | else 19 | COVERAGE_TARGETS=//test/... 20 | fi 21 | 22 | echo "Starting gen_coverage.sh..." 23 | echo " PWD=$(pwd)" 24 | echo " OUTPUT_DIR=${OUTPUT_DIR}" 25 | echo " DATA_DIR=${DATA_DIR}" 26 | echo " TARGETS=${COVERAGE_TARGETS}" 27 | 28 | echo "Generating coverage data..." 29 | bazel coverage ${COVERAGE_TARGETS} --test_output=errors 30 | 31 | rm -rf ${OUTPUT_DIR} 32 | mkdir -p ${OUTPUT_DIR} 33 | 34 | COVERAGE_DATA="${OUTPUT_DIR}/coverage.dat" 35 | cp bazel-out/_coverage/_coverage_report.dat "${COVERAGE_DATA}" 36 | 37 | echo "Generating report..." 38 | 39 | genhtml --title ${PROJECT} --ignore-errors "source" ${COVERAGE_DATA} -o "${OUTPUT_DIR}" 40 | tar -zcf ${PROJECT}_coverage.tar.gz ${OUTPUT_DIR} 41 | mv ${PROJECT}_coverage.tar.gz ${OUTPUT_DIR} 42 | 43 | echo "HTML coverage report is in ${OUTPUT_DIR}/index.html" 44 | echo "All coverage report files are in ${OUTPUT_DIR}/${PROJECT}_coverage.tar.gz" 45 | -------------------------------------------------------------------------------- /cpp2sky/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library", "cc_proto_library") 2 | load("@rules_proto//proto:defs.bzl", "proto_library") 3 | 4 | licenses(["notice"]) # Apache 2 5 | 6 | proto_library( 7 | name = "config_proto_lib", 8 | srcs = ["config.proto"], 9 | ) 10 | 11 | cc_proto_library( 12 | name = "config_cc_proto", 13 | visibility = ["//visibility:public"], 14 | deps = [":config_proto_lib"], 15 | ) 16 | 17 | cc_library( 18 | name = "cpp2sky_interface", 19 | hdrs = [ 20 | "assert.h", 21 | "exception.h", 22 | "propagation.h", 23 | "time.h", 24 | "trace_log.h", 25 | "tracer.h", 26 | "tracing_context.h", 27 | "well_known_names.h", 28 | ], 29 | visibility = ["//visibility:public"], 30 | deps = [ 31 | ":config_cc_proto", 32 | ], 33 | ) 34 | 35 | cc_library( 36 | name = "cpp2sky_data_interface", 37 | hdrs = [ 38 | "assert.h", 39 | "exception.h", 40 | "propagation.h", 41 | "time.h", 42 | "tracing_context.h", 43 | "well_known_names.h", 44 | ], 45 | visibility = ["//visibility:public"], 46 | deps = [ 47 | ":config_cc_proto", 48 | "@com_google_absl//absl/memory", 49 | "@com_google_absl//absl/strings", 50 | "@com_google_absl//absl/types:optional", 51 | ], 52 | ) 53 | -------------------------------------------------------------------------------- /cpp2sky/assert.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | namespace cpp2sky { 18 | 19 | template 20 | class FalseValue { 21 | static constexpr bool value = false; 22 | }; 23 | 24 | #define CPP2SKY_STATIC_ASSERT(T, m) static_assert(FalseValue::value, m) 25 | 26 | } // namespace cpp2sky 27 | -------------------------------------------------------------------------------- /cpp2sky/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package cpp2sky; 4 | 5 | enum Protocol { 6 | GRPC = 0; 7 | REST = 1; 8 | } 9 | 10 | message TracerConfig { 11 | // Tracer protocol. 12 | // | Static config only 13 | Protocol protocol = 1; 14 | 15 | // Service name 16 | // | Static config only 17 | string service_name = 2; 18 | 19 | // Instance name 20 | // | Static config only 21 | string instance_name = 3; 22 | 23 | // OAP address. 24 | // | Static config only 25 | string address = 4; 26 | 27 | // OAP token. 28 | // | Static config only 29 | string token = 5; 30 | 31 | // The size of buffer it stores pending messages. 32 | // | Static config only 33 | uint32 delayed_buffer_size = 6; 34 | 35 | // If the operation name of the first span is included in this set, 36 | // this segment should be ignored. 37 | // | This value can be changed with SkyWalking CDS. 38 | repeated string ignore_operation_name_suffix = 8; 39 | 40 | // CDS sync request interval. If this value is zero, CDS feature will be disabled. 41 | // | Static config only 42 | uint32 cds_request_interval = 7; 43 | } -------------------------------------------------------------------------------- /cpp2sky/exception.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace cpp2sky { 22 | 23 | class TracerException : public std::runtime_error { 24 | public: 25 | TracerException(const std::string& message) : std::runtime_error(message) {} 26 | }; 27 | 28 | } // namespace cpp2sky 29 | -------------------------------------------------------------------------------- /cpp2sky/internal/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library", "cc_proto_library") 2 | load("@rules_proto//proto:defs.bzl", "proto_library") 3 | 4 | licenses(["notice"]) # Apache 2 5 | 6 | cc_library( 7 | name = "async_client_interface", 8 | hdrs = [ 9 | "async_client.h", 10 | ], 11 | visibility = ["//visibility:public"], 12 | deps = [ 13 | "@com_github_grpc_grpc//:grpc++", 14 | "@skywalking_data_collect_protocol//language-agent:tracing_protocol_cc_grpc", 15 | ], 16 | ) 17 | 18 | cc_library( 19 | name = "random_generator_interface", 20 | hdrs = [ 21 | "random_generator.h", 22 | ], 23 | visibility = ["//visibility:public"], 24 | ) 25 | 26 | cc_library( 27 | name = "matcher_interface", 28 | hdrs = ["matcher.h"], 29 | visibility = ["//visibility:public"], 30 | ) 31 | -------------------------------------------------------------------------------- /cpp2sky/internal/async_client.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | 20 | #include "google/protobuf/message.h" 21 | #include "grpcpp/generic/generic_stub.h" 22 | #include "grpcpp/grpcpp.h" 23 | #include "language-agent/Tracing.pb.h" 24 | 25 | namespace cpp2sky { 26 | 27 | /** 28 | * Template base class for gRPC async client. 29 | */ 30 | template 31 | class AsyncClient { 32 | public: 33 | virtual ~AsyncClient() = default; 34 | 35 | /** 36 | * Send the specified protobuf message. 37 | */ 38 | virtual void sendMessage(RequestType message) = 0; 39 | 40 | /** 41 | * Reset the client. This should be called when the client is no longer 42 | * needed. 43 | */ 44 | virtual void resetClient() = 0; 45 | }; 46 | 47 | template 48 | using AsyncClientPtr = std::unique_ptr>; 49 | 50 | /** 51 | * Template base class for gRPC async stream. The stream is used to represent 52 | * a single gRPC stream/request. 53 | */ 54 | template 55 | class AsyncStream { 56 | public: 57 | virtual ~AsyncStream() = default; 58 | 59 | /** 60 | * Send the specified protobuf message. 61 | */ 62 | virtual void sendMessage(RequestType message) = 0; 63 | }; 64 | 65 | template 66 | using AsyncStreamPtr = std::unique_ptr>; 67 | 68 | /** 69 | * Tag for async operation. The callback should be called when the operation is 70 | * done. 71 | */ 72 | struct AsyncEventTag { 73 | std::function callback; 74 | }; 75 | using AsyncEventTagPtr = std::unique_ptr; 76 | 77 | using GrpcClientContextPtr = std::unique_ptr; 78 | using GrpcCompletionQueue = grpc::CompletionQueue; 79 | 80 | /** 81 | * Factory for creating async stream. 82 | */ 83 | template 84 | class AsyncStreamFactory { 85 | public: 86 | virtual ~AsyncStreamFactory() = default; 87 | 88 | using StreamPtr = AsyncStreamPtr; 89 | using GrpcStub = grpc::TemplatedGenericStub; 90 | 91 | virtual StreamPtr createStream(GrpcClientContextPtr client_ctx, 92 | GrpcStub& stub, GrpcCompletionQueue& cq, 93 | AsyncEventTag& basic_event_tag, 94 | AsyncEventTag& write_event_tag) = 0; 95 | }; 96 | 97 | template 98 | using AsyncStreamFactoryPtr = 99 | std::unique_ptr>; 100 | 101 | using TraceRequestType = skywalking::v3::SegmentObject; 102 | using TraceResponseType = skywalking::v3::Commands; 103 | 104 | using TraceAsyncStream = AsyncStream; 105 | using TraceAsyncStreamPtr = AsyncStreamPtr; 106 | 107 | using TraceAsyncStreamFactory = 108 | AsyncStreamFactory; 109 | using TraceAsyncStreamFactoryPtr = 110 | AsyncStreamFactoryPtr; 111 | 112 | using TraceAsyncClient = AsyncClient; 113 | using TraceAsyncClientPtr = std::unique_ptr; 114 | 115 | } // namespace cpp2sky 116 | -------------------------------------------------------------------------------- /cpp2sky/internal/matcher.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "absl/strings/string_view.h" 20 | 21 | namespace cpp2sky { 22 | 23 | class Matcher { 24 | public: 25 | virtual ~Matcher() = default; 26 | 27 | /** 28 | * Check whether to match rule. 29 | */ 30 | virtual bool match(absl::string_view target) = 0; 31 | }; 32 | 33 | using MatcherPtr = std::unique_ptr; 34 | 35 | } // namespace cpp2sky 36 | -------------------------------------------------------------------------------- /cpp2sky/internal/random_generator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | namespace cpp2sky { 20 | 21 | class RandomGenerator { 22 | public: 23 | virtual ~RandomGenerator() = default; 24 | 25 | /** 26 | * Get uuid 27 | */ 28 | virtual std::string uuid() = 0; 29 | }; 30 | 31 | } // namespace cpp2sky 32 | -------------------------------------------------------------------------------- /cpp2sky/propagation.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "absl/strings/string_view.h" 20 | 21 | namespace cpp2sky { 22 | 23 | class SpanContext { 24 | public: 25 | virtual ~SpanContext() = default; 26 | 27 | /** 28 | * Get the status of sample on SKyWalking. 29 | * It indicates whether current context should send or not. 30 | */ 31 | virtual bool sample() const = 0; 32 | 33 | /** 34 | * Get parent's trace ID. This value must be unique globally. 35 | */ 36 | virtual const std::string& traceId() const = 0; 37 | 38 | /** 39 | * Get trace parent's segment ID. This value must be unique globally. 40 | */ 41 | virtual const std::string& traceSegmentId() const = 0; 42 | 43 | /** 44 | * Get parent's span ID. This span id points to the parent span in parent 45 | * trace segment. 46 | */ 47 | virtual int32_t spanId() const = 0; 48 | 49 | /** 50 | * Get parent's service name. 51 | */ 52 | virtual const std::string& service() const = 0; 53 | 54 | /** 55 | * Get parent's service instance name. 56 | */ 57 | virtual const std::string& serviceInstance() const = 0; 58 | 59 | /** 60 | * Get endpoint. Operation Name of the first entry span in the parent 61 | * segment. 62 | */ 63 | virtual const std::string& endpoint() const = 0; 64 | 65 | /** 66 | * Get target address. The network address used at client side to access 67 | * this target service. 68 | */ 69 | virtual const std::string& targetAddress() const = 0; 70 | }; 71 | 72 | using SpanContextSharedPtr = std::shared_ptr; 73 | 74 | enum class TracingMode { 75 | Default, 76 | // It represents all spans generated in this context should skip 77 | // analysis. 78 | Skip 79 | }; 80 | 81 | class SpanContextExtension { 82 | public: 83 | virtual ~SpanContextExtension() = default; 84 | 85 | virtual TracingMode tracingMode() const = 0; 86 | }; 87 | 88 | using SpanContextExtensionSharedPtr = std::shared_ptr; 89 | 90 | SpanContextSharedPtr createSpanContext(absl::string_view ctx); 91 | 92 | SpanContextExtensionSharedPtr createSpanContextExtension(absl::string_view ctx); 93 | 94 | } // namespace cpp2sky 95 | -------------------------------------------------------------------------------- /cpp2sky/time.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "cpp2sky/assert.h" 20 | 21 | namespace cpp2sky { 22 | 23 | using SystemTime = std::chrono::system_clock::time_point; 24 | using SteadyTime = std::chrono::steady_clock::time_point; 25 | 26 | template 27 | class TimePoint { 28 | CPP2SKY_STATIC_ASSERT(T, "Invalid time type"); 29 | }; 30 | 31 | template <> 32 | class TimePoint { 33 | public: 34 | TimePoint(SystemTime point) : point_(point) {} 35 | TimePoint() { point_ = std::chrono::system_clock::now(); } 36 | 37 | // Fetch timestamp with millisecond resolution 38 | int64_t fetch() { 39 | return std::chrono::duration_cast( 40 | point_.time_since_epoch()) 41 | .count(); 42 | } 43 | 44 | private: 45 | SystemTime point_; 46 | }; 47 | 48 | template <> 49 | class TimePoint { 50 | public: 51 | TimePoint(SteadyTime point) : point_(point) {} 52 | TimePoint() { point_ = std::chrono::steady_clock::now(); } 53 | 54 | // Fetch timestamp with millisecond resolution 55 | int64_t fetch() { 56 | return std::chrono::duration_cast( 57 | point_.time_since_epoch()) 58 | .count(); 59 | } 60 | 61 | private: 62 | SteadyTime point_; 63 | }; 64 | 65 | } // namespace cpp2sky 66 | -------------------------------------------------------------------------------- /cpp2sky/trace_log.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include 20 | 21 | #include "absl/strings/string_view.h" 22 | 23 | namespace cpp2sky { 24 | 25 | static constexpr absl::string_view SPDLOG_LOG_FORMAT = 26 | "{\"level\": \"%^%l%$\", \"msg\": \"%v"; 27 | 28 | template 29 | std::string logFormat() { 30 | static_assert(std::is_same::value, 31 | "non-supported logger type"); 32 | return SPDLOG_LOG_FORMAT.data(); 33 | } 34 | 35 | } // namespace cpp2sky 36 | -------------------------------------------------------------------------------- /cpp2sky/tracer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "cpp2sky/config.pb.h" 20 | #include "cpp2sky/propagation.h" 21 | #include "cpp2sky/tracing_context.h" 22 | 23 | namespace cpp2sky { 24 | 25 | class Tracer { 26 | public: 27 | virtual ~Tracer() = default; 28 | 29 | /** 30 | * Start new segment. It will be called per request, for example. 31 | */ 32 | virtual TracingContextSharedPtr newContext() = 0; 33 | virtual TracingContextSharedPtr newContext(SpanContextSharedPtr span) = 0; 34 | 35 | /** 36 | * Send SegmentContext to the collector. 37 | */ 38 | virtual bool report(TracingContextSharedPtr obj) = 0; 39 | }; 40 | 41 | using TracerPtr = std::unique_ptr; 42 | 43 | TracerPtr createInsecureGrpcTracer(const TracerConfig& cfg); 44 | 45 | } // namespace cpp2sky 46 | -------------------------------------------------------------------------------- /cpp2sky/tracing_context.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/memory/memory.h" 21 | #include "absl/strings/string_view.h" 22 | #include "absl/types/optional.h" 23 | #include "cpp2sky/config.pb.h" 24 | #include "cpp2sky/propagation.h" 25 | #include "cpp2sky/time.h" 26 | #include "language-agent/Tracing.pb.h" 27 | 28 | namespace cpp2sky { 29 | 30 | class TracingSpan { 31 | public: 32 | virtual ~TracingSpan() = default; 33 | 34 | /** 35 | * Generate Apache SkyWalking native span object from current segment span. 36 | */ 37 | virtual skywalking::v3::SpanObject createSpanObject() = 0; 38 | 39 | /** 40 | * Get span ID. 41 | */ 42 | virtual int32_t spanId() const = 0; 43 | 44 | /** 45 | * Get parent span ID. 46 | */ 47 | virtual int32_t parentSpanId() const = 0; 48 | 49 | /** 50 | * Get start time. 51 | */ 52 | virtual int64_t startTime() const = 0; 53 | 54 | /** 55 | * Get end time. 56 | */ 57 | virtual int64_t endTime() const = 0; 58 | 59 | /** 60 | * Get peer address. 61 | */ 62 | virtual absl::string_view peer() const = 0; 63 | 64 | /** 65 | * Get span type. 66 | */ 67 | virtual skywalking::v3::SpanType spanType() const = 0; 68 | 69 | /** 70 | * Get span layer. 71 | */ 72 | virtual skywalking::v3::SpanLayer spanLayer() const = 0; 73 | 74 | /** 75 | * Get error occurred or not. 76 | */ 77 | virtual bool errorStatus() const = 0; 78 | 79 | /** 80 | * Enable to skip analysis or not. 81 | */ 82 | virtual bool skipAnalysis() const = 0; 83 | 84 | /** 85 | * Get component ID. 86 | */ 87 | virtual int32_t componentId() const = 0; 88 | 89 | /** 90 | * Get operation name. 91 | */ 92 | virtual absl::string_view operationName() const = 0; 93 | 94 | /** 95 | * Set parent span ID of this span. 96 | */ 97 | virtual void setParentSpanId(int32_t span_id) = 0; 98 | 99 | /** 100 | * Set start time to calculate execution time. 101 | */ 102 | virtual void startSpan(absl::string_view operation_name) = 0; 103 | virtual void startSpan(absl::string_view operation_name, 104 | TimePoint current_time) = 0; 105 | virtual void startSpan(absl::string_view operation_name, 106 | TimePoint current_time) = 0; 107 | 108 | /** 109 | * Set end time to calculate execution time. 110 | */ 111 | virtual void endSpan() = 0; 112 | virtual void endSpan(TimePoint current_time) = 0; 113 | virtual void endSpan(TimePoint current_time) = 0; 114 | 115 | /** 116 | * Set peer address for this span (lvalue) 117 | */ 118 | virtual void setPeer(absl::string_view remote_address) = 0; 119 | 120 | /** 121 | * Set span type. Entry or Exit. Entry span means origin span which doesn't 122 | * have parent span, like root node of span tree. Exit span has opposite 123 | * meaning, like leaf node of span tree. 124 | */ 125 | virtual void setSpanType(skywalking::v3::SpanType type) = 0; 126 | 127 | /** 128 | * Set span layer. It supports only HTTP request tracing currently. 129 | */ 130 | virtual void setSpanLayer(skywalking::v3::SpanLayer layer) = 0; 131 | 132 | /** 133 | * If error had caused on this span, This should be called. 134 | */ 135 | virtual void setErrorStatus() = 0; 136 | 137 | /** 138 | * Determine whether to skip the analysis of this span. If we'd like to skip 139 | * analysis, this should be called. 140 | */ 141 | virtual void setSkipAnalysis() = 0; 142 | 143 | /** 144 | * Set tag to current span. 145 | */ 146 | virtual void addTag(absl::string_view key, absl::string_view value) = 0; 147 | 148 | /** 149 | * Add log related with current span. 150 | */ 151 | virtual void addLog(absl::string_view key, absl::string_view value) = 0; 152 | virtual void addLog(absl::string_view key, absl::string_view value, 153 | TimePoint current_time) = 0; 154 | virtual void addLog(absl::string_view key, absl::string_view value, 155 | TimePoint current_time) = 0; 156 | 157 | /** 158 | * Set component ID. 159 | */ 160 | virtual void setComponentId(int32_t component_id) = 0; 161 | 162 | /** 163 | * Set operation name. 164 | */ 165 | virtual void setOperationName(absl::string_view operation_name) = 0; 166 | 167 | /** 168 | * Add parent segment reference to current span. 169 | */ 170 | virtual void addSegmentRef(const SpanContext& span_context) = 0; 171 | 172 | /** 173 | * This span had finished or not. 174 | */ 175 | virtual bool finished() const = 0; 176 | }; 177 | 178 | using TracingSpanSharedPtr = std::shared_ptr; 179 | 180 | class TracingContext { 181 | public: 182 | virtual ~TracingContext() = default; 183 | 184 | /** 185 | * Get trace ID. This value must be unique globally. 186 | */ 187 | virtual const std::string& traceId() const = 0; 188 | 189 | /** 190 | * Get trace segment ID. This value must be unique globally. 191 | */ 192 | virtual const std::string& traceSegmentId() const = 0; 193 | 194 | /** 195 | * Get service name. 196 | */ 197 | virtual const std::string& service() const = 0; 198 | 199 | /** 200 | * Get service instance name. 201 | */ 202 | virtual const std::string& serviceInstance() const = 0; 203 | 204 | /** 205 | * Get spans generated by this segment context. 206 | */ 207 | virtual const std::list& spans() const = 0; 208 | 209 | /** 210 | * Get span context which generated this segment context as parent. 211 | */ 212 | virtual SpanContextSharedPtr parentSpanContext() const = 0; 213 | 214 | /** 215 | * Get span context extension which generated this segment context. 216 | */ 217 | virtual SpanContextExtensionSharedPtr parentSpanContextExtension() const = 0; 218 | 219 | /** 220 | * Generate a segment span related with this segment context. 221 | * @param parent_span Parent span which is extracted from caller. 222 | */ 223 | virtual TracingSpanSharedPtr createExitSpan( 224 | TracingSpanSharedPtr parent_span) = 0; 225 | 226 | /** 227 | * Generate root segment span, called once per workload. 228 | */ 229 | virtual TracingSpanSharedPtr createEntrySpan() = 0; 230 | 231 | /** 232 | * Generate sw8 value to send SegmentRef. 233 | * @param target_address Target address to send request. For more detail: 234 | * https://github.com/apache/skywalking-data-collect-protocol/blob/master/language-agent/Tracing.proto#L97-L101 235 | */ 236 | virtual absl::optional createSW8HeaderValue( 237 | const absl::string_view target_address) = 0; 238 | 239 | /** 240 | * Generate Apache SkyWalking native segment object. This method **SHOULD** 241 | * only be called once. 242 | */ 243 | virtual skywalking::v3::SegmentObject createSegmentObject() = 0; 244 | 245 | /** 246 | * If called, all spans belongs to this segment will be skipped analysis. 247 | */ 248 | virtual void setSkipAnalysis() = 0; 249 | 250 | /** 251 | * Whether belonging span can be skipped analysis or not. 252 | */ 253 | virtual bool skipAnalysis() = 0; 254 | 255 | /** 256 | * Determine whether to send this segment or not. 257 | */ 258 | virtual bool readyToSend() = 0; 259 | 260 | /** 261 | * Get log message. Output value of this function is based on default cpp2sky 262 | * logging format following with any format extracted with 263 | * cpp2sky::logFormat(). 264 | */ 265 | virtual std::string logMessage(absl::string_view message) const = 0; 266 | }; 267 | 268 | using TracingContextSharedPtr = std::shared_ptr; 269 | 270 | /** 271 | * RAII based span creation. It acquired then create new span with required 272 | * properties. The span wiil be closed and set end time when called destructor. 273 | */ 274 | class StartEntrySpan { 275 | public: 276 | StartEntrySpan(TracingContextSharedPtr tracing_context, 277 | absl::string_view operation_name) 278 | : span_(tracing_context->createEntrySpan()) { 279 | span_->startSpan(operation_name.data()); 280 | } 281 | 282 | ~StartEntrySpan() { 283 | // Span won't be released because the entity is holded by TracingContext. 284 | span_->endSpan(); 285 | } 286 | 287 | TracingSpanSharedPtr get() { return span_; } 288 | 289 | private: 290 | TracingSpanSharedPtr span_; 291 | }; 292 | 293 | class StartExitSpan { 294 | public: 295 | StartExitSpan(TracingContextSharedPtr tracing_context, 296 | TracingSpanSharedPtr parent_span, 297 | absl::string_view operation_name) 298 | : span_(tracing_context->createExitSpan(parent_span)) { 299 | span_->startSpan(operation_name.data()); 300 | } 301 | 302 | ~StartExitSpan() { 303 | // Span won't be released because the entity is holded by TracingContext. 304 | span_->endSpan(); 305 | } 306 | 307 | TracingSpanSharedPtr get() { return span_; } 308 | 309 | private: 310 | TracingSpanSharedPtr span_; 311 | }; 312 | 313 | } // namespace cpp2sky 314 | -------------------------------------------------------------------------------- /cpp2sky/well_known_names.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include "absl/strings/string_view.h" 18 | 19 | namespace cpp2sky { 20 | 21 | static constexpr absl::string_view kPropagationHeader = "sw8"; 22 | static constexpr absl::string_view kPropagationExtensionHeader = "sw8-x"; 23 | 24 | } // namespace cpp2sky 25 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ### cpp2sky 2 | 3 | 4 | ##### message `TracerConfig` (cpp2sky/config.proto) 5 | 6 | | Field | Description | Type | C++ | 7 | | ----- | ----------- | ---- | --- | 8 | | protocol | Tracer protocol. | Static config only | Protocol | | 9 | | service_name | Service name | Static config only | string | string | 10 | | instance_name | Instance name | Static config only | string | string | 11 | | address | OAP address. | Static config only | string | string | 12 | | token | OAP token. | Static config only | string | string | 13 | | delayed_buffer_size | The size of buffer it stores pending messages. | Static config only | uint32 | uint32 | 14 | | ignore_operation_name_suffix | If the operation name of the first span is included in this set, this segment should be ignored. | This value can be changed with SkyWalking CDS. | (slice of) string | (slice of) string | 15 | | cds_request_interval | CDS sync request interval. If this value is zero, CDS feature will be disabled. | Static config only | uint32 | uint32 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary") 2 | 3 | cc_binary( 4 | name = "sample", 5 | srcs = ["sample.cc"], 6 | deps = [ 7 | "//cpp2sky:cpp2sky_interface", 8 | "//source:cpp2sky_lib", 9 | "@com_github_httplib//:httplib", 10 | ], 11 | ) 12 | 13 | cc_binary( 14 | name = "sample_client", 15 | srcs = ["sample_client.cc"], 16 | deps = [ 17 | "//cpp2sky:cpp2sky_interface", 18 | "//source:cpp2sky_lib", 19 | "@com_github_httplib//:httplib", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /example/sample.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("node_0"); 30 | config.set_service_name("mesh"); 31 | config.set_address("0.0.0.0:11800"); 32 | config.set_cds_request_interval(5); 33 | } 34 | 35 | int main() { 36 | init(); 37 | 38 | httplib::Server svr; 39 | // 1. Create tracer object to send span data to OAP. 40 | auto tracer = createInsecureGrpcTracer(config); 41 | 42 | svr.Get("/ping", [&](const httplib::Request& req, httplib::Response& res) { 43 | std::string context = req.get_header_value(kPropagationHeader.data()); 44 | 45 | TracingContextSharedPtr tracing_context; 46 | 47 | if (!context.empty()) { 48 | // 2. Create tracing context with propagated information. 49 | tracing_context = tracer->newContext(createSpanContext(context)); 50 | } 51 | 52 | { 53 | // 3. Create entry span. 54 | StartEntrySpan current_span(tracing_context, "sample_op3"); 55 | 56 | /** 57 | * something.... 58 | */ 59 | } 60 | 61 | // 4. Send span data 62 | if (tracing_context != nullptr) { 63 | tracer->report(std::move(tracing_context)); 64 | } 65 | }); 66 | 67 | svr.listen("0.0.0.0", 8081); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /example/sample_client.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("client_0"); 30 | config.set_service_name(""); 31 | config.set_address("0.0.0.0:11800"); 32 | } 33 | 34 | int main() { 35 | init(); 36 | 37 | // 1. Create tracer object to send span data to OAP. 38 | auto tracer = createInsecureGrpcTracer(config); 39 | 40 | // 2. Create tracing context 41 | auto tracing_context = tracer->newContext(); 42 | 43 | /** 44 | * 3. Create entry span it traces RPC call. 45 | * Span lifetime is managed by RAII. So user don't have to call startSpan and 46 | * endSpan explicitly. But it provides basic approach that doesn't use RAII. 47 | * 48 | * example: 49 | * 50 | * auto current_span = tracing_context->createEntrySpan(); 51 | * current_span->startSpan("sample_op1"); 52 | * 53 | * auto current_span2 = tracing_context->createExitSpan(); 54 | * current_span2->startSpan("sample_op2"); 55 | * 56 | * httplib::Client cli("remote", 8082); 57 | * httplib::Headers headers = { 58 | * {kPropagationHeader.data(), 59 | * tracing_context->createSW8HeaderValue("remote:8082")}}; 60 | * 61 | * auto res = cli.Get("/ping", headers); 62 | * 63 | * current_span2->endSpan(); 64 | * current_span->endSpan(); 65 | * 66 | */ 67 | { 68 | StartEntrySpan entry_span(tracing_context, "sample_op1"); 69 | 70 | { 71 | std::string target_address = "127.0.0.1:8081"; 72 | StartExitSpan exit_span(tracing_context, entry_span.get(), "sample_op2"); 73 | exit_span.get()->setPeer(target_address); 74 | 75 | httplib::Client cli("127.0.0.1", 8081); 76 | httplib::Headers headers = { 77 | {kPropagationHeader.data(), 78 | *tracing_context->createSW8HeaderValue(target_address)}}; 79 | 80 | auto res = cli.Get("/ping", headers); 81 | } 82 | } 83 | 84 | tracer->report(std::move(tracing_context)); 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /source/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "cpp2sky_lib", 5 | srcs = [ 6 | "grpc_async_client_impl.cc", 7 | "tracer_impl.cc", 8 | ], 9 | hdrs = [ 10 | "grpc_async_client_impl.h", 11 | "tracer_impl.h", 12 | ], 13 | visibility = ["//visibility:public"], 14 | deps = [ 15 | ":cpp2sky_data_lib", 16 | "//cpp2sky:config_cc_proto", 17 | "//cpp2sky:cpp2sky_data_interface", 18 | "//cpp2sky:cpp2sky_interface", 19 | "//cpp2sky/internal:async_client_interface", 20 | "//cpp2sky/internal:matcher_interface", 21 | "//source/matchers:suffix_matcher_lib", 22 | "//source/utils:util_lib", 23 | "@com_github_gabime_spdlog//:spdlog", 24 | "@com_github_grpc_grpc//:grpc++", 25 | "@com_google_absl//absl/strings", 26 | "@skywalking_data_collect_protocol//language-agent:configuration_discovery_service_cc_grpc", 27 | "@skywalking_data_collect_protocol//language-agent:configuration_discovery_service_cc_proto", 28 | "@skywalking_data_collect_protocol//language-agent:tracing_protocol_cc_grpc", 29 | "@skywalking_data_collect_protocol//language-agent:tracing_protocol_cc_proto", 30 | ], 31 | ) 32 | 33 | cc_library( 34 | name = "cpp2sky_data_lib", 35 | srcs = [ 36 | "propagation_impl.cc", 37 | "tracing_context_impl.cc", 38 | ], 39 | hdrs = [ 40 | "propagation_impl.h", 41 | "tracing_context_impl.h", 42 | ], 43 | visibility = ["//visibility:public"], 44 | deps = [ 45 | "//cpp2sky:config_cc_proto", 46 | "//cpp2sky:cpp2sky_data_interface", 47 | "//source/utils:util_lib", 48 | "@com_google_absl//absl/memory", 49 | "@skywalking_data_collect_protocol//language-agent:tracing_protocol_cc_proto", 50 | ], 51 | ) 52 | -------------------------------------------------------------------------------- /source/grpc_async_client_impl.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "source/grpc_async_client_impl.h" 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "absl/strings/string_view.h" 24 | #include "cpp2sky/exception.h" 25 | #include "cpp2sky/internal/async_client.h" 26 | #include "spdlog/spdlog.h" 27 | 28 | namespace cpp2sky { 29 | 30 | namespace { 31 | 32 | static constexpr uint32_t MaxPendingMessagesSize = 1024; 33 | 34 | static std::string AuthenticationKey = "authentication"; 35 | 36 | static std::string TraceCollectMethod = "/TraceSegmentReportService/collect"; 37 | 38 | } // namespace 39 | 40 | using namespace spdlog; 41 | 42 | void EventLoopThread::gogo() { 43 | while (true) { 44 | void* got_tag{nullptr}; 45 | bool ok{false}; 46 | 47 | // true if got an event from the queue or false 48 | // if the queue is fully drained and is shutdown. 49 | const bool status = cq_.Next(&got_tag, &ok); 50 | if (!status) { 51 | assert(got_tag == nullptr); 52 | assert(!ok); 53 | info("[Reporter] Completion queue is drained and is shutdown."); 54 | break; 55 | } 56 | 57 | assert(got_tag != nullptr); 58 | 59 | // The lifetime of the tag is managed by the caller. 60 | auto* tag = static_cast(got_tag); 61 | tag->callback(ok); 62 | } 63 | } 64 | 65 | TraceAsyncStreamImpl::TraceAsyncStreamImpl(GrpcClientContextPtr client_ctx, 66 | TraceGrpcStub& stub, 67 | GrpcCompletionQueue& cq, 68 | AsyncEventTag& basic_event_tag, 69 | AsyncEventTag& write_event_tag) 70 | : client_ctx_(std::move(client_ctx)), 71 | basic_event_tag_(basic_event_tag), 72 | write_event_tag_(write_event_tag) { 73 | if (client_ctx_ == nullptr) { 74 | client_ctx_.reset(new grpc::ClientContext()); 75 | } 76 | 77 | request_writer_ = 78 | stub.PrepareCall(client_ctx_.get(), TraceCollectMethod, &cq); 79 | request_writer_->StartCall(reinterpret_cast(&basic_event_tag_)); 80 | } 81 | 82 | void TraceAsyncStreamImpl::sendMessage(TraceRequestType message) { 83 | request_writer_->Write(message, reinterpret_cast(&write_event_tag_)); 84 | } 85 | 86 | TraceAsyncStreamPtr TraceAsyncStreamFactoryImpl::createStream( 87 | GrpcClientContextPtr client_ctx, TraceGrpcStub& stub, 88 | GrpcCompletionQueue& cq, AsyncEventTag& basic_event_tag, 89 | AsyncEventTag& write_event_tag) { 90 | return TraceAsyncStreamPtr{new TraceAsyncStreamImpl( 91 | std::move(client_ctx), stub, cq, basic_event_tag, write_event_tag)}; 92 | } 93 | 94 | std::unique_ptr TraceAsyncClientImpl::createClient( 95 | const std::string& address, const std::string& token, 96 | TraceAsyncStreamFactoryPtr factory, CredentialsSharedPtr cred) { 97 | return std::unique_ptr{new TraceAsyncClientImpl( 98 | address, token, std::move(factory), std::move(cred))}; 99 | } 100 | 101 | TraceAsyncClientImpl::TraceAsyncClientImpl(const std::string& address, 102 | const std::string& token, 103 | TraceAsyncStreamFactoryPtr factory, 104 | CredentialsSharedPtr cred) 105 | : token_(token), 106 | stream_factory_(std::move(factory)), 107 | stub_(grpc::CreateChannel(address, cred)) { 108 | basic_event_tag_.callback = [this](bool ok) { 109 | if (client_reset_) { 110 | return; 111 | } 112 | 113 | if (ok) { 114 | trace("[Reporter] Stream event success.", fmt::ptr(this)); 115 | 116 | // Mark event loop as idle because the previous Write() or 117 | // other operations are successful. 118 | markEventLoopIdle(); 119 | 120 | sendMessageOnce(); 121 | return; 122 | } else { 123 | trace("[Reporter] Stream event failure.", fmt::ptr(this)); 124 | 125 | // Do not mark event loop as idle because the previous Write() 126 | // or other operations are failed. The event loop should keep 127 | // running to process the re-creation of the stream. 128 | assert(event_loop_idle_.load() == false); 129 | // Reset stream and try to create a new one. 130 | startStream(); 131 | } 132 | }; 133 | 134 | write_event_tag_.callback = [this](bool ok) { 135 | if (ok) { 136 | trace("[Reporter] Stream {} message sending success.", fmt::ptr(this)); 137 | messages_sent_++; 138 | } else { 139 | trace("[Reporter] Stream {} message sending failure.", fmt::ptr(this)); 140 | messages_dropped_++; 141 | } 142 | // Delegate the event to basic_event_tag_ to trigger the next task or 143 | // reset the stream. 144 | basic_event_tag_.callback(ok); 145 | }; 146 | 147 | // If the factory is not provided, use the default one. 148 | if (stream_factory_ == nullptr) { 149 | stream_factory_.reset(new TraceAsyncStreamFactoryImpl()); 150 | } 151 | 152 | startStream(); 153 | } 154 | 155 | void TraceAsyncClientImpl::sendMessageOnce() { 156 | bool expect_idle = true; 157 | if (event_loop_idle_.compare_exchange_strong(expect_idle, false)) { 158 | assert(active_stream_ != nullptr); 159 | 160 | auto opt_message = message_buffer_.pop_front(); 161 | if (!opt_message.has_value()) { 162 | // No message to send, mark event loop as idle. 163 | markEventLoopIdle(); 164 | return; 165 | } 166 | 167 | active_stream_->sendMessage(std::move(opt_message).value()); 168 | } 169 | } 170 | 171 | void TraceAsyncClientImpl::startStream() { 172 | if (active_stream_ != nullptr) { 173 | resetStream(); // Reset stream before creating a new one. 174 | } 175 | 176 | // Create the unique client context for the new stream. 177 | // Each stream should have its own context. 178 | auto client_ctx = GrpcClientContextPtr{new grpc::ClientContext()}; 179 | if (!token_.empty()) { 180 | client_ctx->AddMetadata(AuthenticationKey, token_); 181 | } 182 | 183 | active_stream_ = stream_factory_->createStream( 184 | std::move(client_ctx), stub_, event_loop_.cq_, basic_event_tag_, 185 | write_event_tag_); 186 | 187 | info("[Reporter] Stream {} has created.", fmt::ptr(active_stream_.get())); 188 | } 189 | 190 | void TraceAsyncClientImpl::resetStream() { 191 | info("[Reporter] Stream {} has deleted.", fmt::ptr(active_stream_.get())); 192 | active_stream_.reset(); 193 | } 194 | 195 | void TraceAsyncClientImpl::sendMessage(TraceRequestType message) { 196 | messages_total_++; 197 | 198 | const size_t pending = message_buffer_.size(); 199 | if (pending > MaxPendingMessagesSize) { 200 | info("[Reporter] pending message overflow and drop message"); 201 | messages_dropped_++; 202 | return; 203 | } 204 | message_buffer_.push_back(std::move(message)); 205 | 206 | sendMessageOnce(); 207 | } 208 | 209 | } // namespace cpp2sky 210 | -------------------------------------------------------------------------------- /source/grpc_async_client_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "cpp2sky/config.pb.h" 25 | #include "cpp2sky/internal/async_client.h" 26 | #include "language-agent/Tracing.grpc.pb.h" 27 | #include "source/utils/buffer.h" 28 | 29 | namespace cpp2sky { 30 | 31 | using CredentialsSharedPtr = std::shared_ptr; 32 | 33 | using TraceGrpcStub = 34 | grpc::TemplatedGenericStub; 35 | using TraceReaderWriter = 36 | grpc::ClientAsyncReaderWriter; 37 | using TraceReaderWriterPtr = std::unique_ptr; 38 | 39 | class EventLoopThread { 40 | public: 41 | EventLoopThread() : thread_([this] { this->gogo(); }) {} 42 | ~EventLoopThread() { exit(); } 43 | 44 | grpc::CompletionQueue cq_; 45 | 46 | void exit() { 47 | if (!exited_) { 48 | exited_ = true; 49 | cq_.Shutdown(); 50 | thread_.join(); 51 | } 52 | } 53 | 54 | private: 55 | bool exited_{false}; 56 | std::thread thread_; 57 | 58 | void gogo(); 59 | }; 60 | 61 | class TraceAsyncStreamImpl : public TraceAsyncStream { 62 | public: 63 | TraceAsyncStreamImpl(GrpcClientContextPtr client_ctx, TraceGrpcStub& stub, 64 | GrpcCompletionQueue& cq, AsyncEventTag& basic_event_tag, 65 | AsyncEventTag& write_event_tag); 66 | 67 | // AsyncStream 68 | void sendMessage(TraceRequestType message) override; 69 | 70 | private: 71 | GrpcClientContextPtr client_ctx_; 72 | TraceReaderWriterPtr request_writer_; 73 | 74 | AsyncEventTag& basic_event_tag_; 75 | AsyncEventTag& write_event_tag_; 76 | }; 77 | 78 | class TraceAsyncStreamFactoryImpl : public TraceAsyncStreamFactory { 79 | public: 80 | TraceAsyncStreamFactoryImpl() = default; 81 | 82 | TraceAsyncStreamPtr createStream(GrpcClientContextPtr client_ctx, 83 | GrpcStub& stub, GrpcCompletionQueue& cq, 84 | AsyncEventTag& basic_event_tag, 85 | AsyncEventTag& write_event_tag) override; 86 | }; 87 | 88 | class TraceAsyncClientImpl : public TraceAsyncClient { 89 | public: 90 | /** 91 | * Create a new GrpcAsyncSegmentReporterClient. 92 | * 93 | * @param address The address of the server. 94 | * @param token The optional token used to authenticate the client. 95 | * If non-empty token is provided, the client will send the token 96 | * to the server in the metadata. 97 | * @param cred The credentials for creating the channel. 98 | * @param factory The factory function to create the stream from the 99 | * request writer and event tags. In most cases, the default factory 100 | * should be used. 101 | */ 102 | static std::unique_ptr createClient( 103 | const std::string& address, const std::string& token, 104 | TraceAsyncStreamFactoryPtr factory = nullptr, 105 | CredentialsSharedPtr cred = grpc::InsecureChannelCredentials()); 106 | 107 | ~TraceAsyncClientImpl() override { 108 | if (!client_reset_) { 109 | resetClient(); 110 | } 111 | } 112 | 113 | // AsyncClient 114 | void sendMessage(TraceRequestType message) override; 115 | void resetClient() override { 116 | // After this is called, no more events will be processed. 117 | client_reset_ = true; 118 | message_buffer_.clear(); 119 | event_loop_.exit(); 120 | resetStream(); 121 | } 122 | 123 | protected: 124 | TraceAsyncClientImpl( 125 | const std::string& address, const std::string& token, 126 | TraceAsyncStreamFactoryPtr factory = nullptr, 127 | CredentialsSharedPtr cred = grpc::InsecureChannelCredentials()); 128 | 129 | // Start or re-create the stream that used to send messages. 130 | void startStream(); 131 | void resetStream(); 132 | void markEventLoopIdle() { event_loop_idle_.store(true); } 133 | void sendMessageOnce(); 134 | 135 | const std::string token_; 136 | TraceAsyncStreamFactoryPtr stream_factory_; 137 | TraceGrpcStub stub_; 138 | 139 | // This may be operated by multiple threads. 140 | std::atomic messages_total_{0}; 141 | std::atomic messages_dropped_{0}; 142 | std::atomic messages_sent_{0}; 143 | 144 | EventLoopThread event_loop_; 145 | std::atomic client_reset_{false}; 146 | 147 | ValueBuffer message_buffer_; 148 | 149 | AsyncEventTag basic_event_tag_; 150 | AsyncEventTag write_event_tag_; 151 | 152 | // The Write() of the stream could only be called once at a time 153 | // until the previous Write() is finished (callback is called). 154 | // Considering the complexity and the thread safety, we make sure 155 | // that all operations on the stream are done one by one. 156 | // This flag is used to indicate whether the event loop is idle 157 | // before we perform the next operation on the stream. 158 | // 159 | // Initially the value is false because the event loop will be 160 | // occupied by the first operation (startStream). 161 | std::atomic event_loop_idle_{false}; 162 | 163 | TraceAsyncStreamPtr active_stream_; 164 | }; 165 | 166 | } // namespace cpp2sky 167 | -------------------------------------------------------------------------------- /source/matchers/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "suffix_matcher_lib", 5 | srcs = ["suffix_matcher.cc"], 6 | hdrs = ["suffix_matcher.h"], 7 | visibility = ["//visibility:public"], 8 | deps = [ 9 | "//cpp2sky/internal:matcher_interface", 10 | "@com_google_absl//absl/strings", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /source/matchers/suffix_matcher.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "suffix_matcher.h" 16 | 17 | #include "absl/strings/match.h" 18 | 19 | namespace cpp2sky { 20 | 21 | bool SuffixMatcher::match(absl::string_view target) { 22 | for (const auto& ignore_suffix : target_suffixes_) { 23 | if (absl::EndsWith(target, ignore_suffix)) { 24 | return true; 25 | } 26 | } 27 | 28 | return false; 29 | } 30 | 31 | } // namespace cpp2sky 32 | -------------------------------------------------------------------------------- /source/matchers/suffix_matcher.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "cpp2sky/internal/matcher.h" 20 | 21 | namespace cpp2sky { 22 | 23 | class SuffixMatcher : public Matcher { 24 | public: 25 | explicit SuffixMatcher(std::vector&& target_suffixes) 26 | : target_suffixes_(std::move(target_suffixes)) {} 27 | 28 | bool match(absl::string_view target) override; 29 | 30 | private: 31 | std::vector target_suffixes_; 32 | }; 33 | 34 | } // namespace cpp2sky 35 | -------------------------------------------------------------------------------- /source/propagation_impl.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "source/propagation_impl.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/memory/memory.h" 21 | #include "absl/strings/numbers.h" 22 | #include "absl/strings/string_view.h" 23 | #include "cpp2sky/exception.h" 24 | #include "source/utils/base64.h" 25 | 26 | namespace cpp2sky { 27 | 28 | namespace { 29 | static constexpr size_t EXPECTED_FIELD_COUNT = 8; 30 | 31 | // TODO(shikugawa): This value specifies the number of values on `sw8-x` header. 32 | // This value should be extensible from user config to deliver arbitary 33 | // information as SpanContext. 34 | static constexpr size_t EXPECTED_EXTENSION_FIELD_COUNT = 1; 35 | } // namespace 36 | 37 | SpanContextImpl::SpanContextImpl(absl::string_view header_value) { 38 | std::array fields; 39 | size_t current_field_idx = 0; 40 | std::string value; 41 | 42 | for (size_t i = 0; i < header_value.size(); ++i) { 43 | if (current_field_idx >= EXPECTED_FIELD_COUNT) { 44 | throw TracerException( 45 | "Invalid span context format. It must have 8 fields."); 46 | } 47 | if (header_value[i] == '-') { 48 | fields[current_field_idx] = value; 49 | value.clear(); 50 | ++current_field_idx; 51 | continue; 52 | } 53 | value += header_value[i]; 54 | } 55 | fields[current_field_idx] = value; 56 | 57 | if (current_field_idx != EXPECTED_FIELD_COUNT - 1) { 58 | throw TracerException( 59 | "Invalid span context format. It must have 8 fields."); 60 | } 61 | 62 | if (fields[0] != "0" && fields[0] != "1") { 63 | throw TracerException( 64 | "Invalid span context format. Sample field must be 0 or 1."); 65 | } 66 | 67 | // Sampling is always true 68 | sample_ = true; 69 | trace_id_ = Base64::decodeWithoutPadding(absl::string_view(fields[1])); 70 | trace_segment_id_ = 71 | Base64::decodeWithoutPadding(absl::string_view(fields[2])); 72 | 73 | if (!absl::SimpleAtoi(fields[3], &span_id_)) { 74 | throw TracerException( 75 | "Invalid span id format. Span id field must be integer number."); 76 | } 77 | 78 | service_ = Base64::decodeWithoutPadding(absl::string_view(fields[4])); 79 | service_instance_ = 80 | Base64::decodeWithoutPadding(absl::string_view(fields[5])); 81 | endpoint_ = Base64::decodeWithoutPadding(absl::string_view(fields[6])); 82 | target_address_ = Base64::decodeWithoutPadding(absl::string_view(fields[7])); 83 | } 84 | 85 | SpanContextExtensionImpl::SpanContextExtensionImpl( 86 | absl::string_view header_value) { 87 | std::array fields; 88 | size_t current_field_idx = 0; 89 | std::string value; 90 | 91 | for (size_t i = 0; i < header_value.size(); ++i) { 92 | if (current_field_idx >= EXPECTED_EXTENSION_FIELD_COUNT) { 93 | throw TracerException( 94 | "Invalid span context format. It must have 1 fields."); 95 | } 96 | if (header_value[i] == '-') { 97 | fields[current_field_idx] = value; 98 | value.clear(); 99 | ++current_field_idx; 100 | continue; 101 | } 102 | value += header_value[i]; 103 | } 104 | fields[current_field_idx] = value; 105 | 106 | if (current_field_idx != EXPECTED_EXTENSION_FIELD_COUNT - 1) { 107 | throw TracerException( 108 | "Invalid span context format. It must have 1 fields."); 109 | } 110 | 111 | if (fields[0] != "0" && fields[0] != "1") { 112 | throw TracerException( 113 | "Invalid span context format. tracing mode field must be 0 or 1."); 114 | } 115 | 116 | if (fields[0] == "1") { 117 | tracing_mode_ = TracingMode::Skip; 118 | } 119 | } 120 | 121 | SpanContextSharedPtr createSpanContext(absl::string_view ctx) { 122 | return std::make_shared(ctx); 123 | } 124 | 125 | SpanContextExtensionSharedPtr createSpanContextExtension( 126 | absl::string_view ctx) { 127 | return std::make_shared(ctx); 128 | } 129 | 130 | } // namespace cpp2sky 131 | -------------------------------------------------------------------------------- /source/propagation_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/strings/string_view.h" 21 | #include "cpp2sky/propagation.h" 22 | 23 | namespace cpp2sky { 24 | 25 | class SpanContextImpl : public SpanContext { 26 | public: 27 | SpanContextImpl(absl::string_view header_value); 28 | 29 | bool sample() const override { return sample_; } 30 | const std::string& traceId() const override { return trace_id_; } 31 | const std::string& traceSegmentId() const override { 32 | return trace_segment_id_; 33 | } 34 | int32_t spanId() const override { return span_id_; } 35 | const std::string& service() const override { return service_; } 36 | const std::string& serviceInstance() const override { 37 | return service_instance_; 38 | } 39 | const std::string& endpoint() const override { return endpoint_; } 40 | const std::string& targetAddress() const override { return target_address_; } 41 | 42 | private: 43 | // Based on 44 | // https://github.com/apache/skywalking/blob/master/docs/en/protocols/Skywalking-Cross-Process-Propagation-Headers-Protocol-v3.md 45 | bool sample_{true}; 46 | std::string trace_id_; 47 | std::string trace_segment_id_; 48 | int32_t span_id_{}; 49 | std::string service_; 50 | std::string service_instance_; 51 | std::string endpoint_; 52 | std::string target_address_; 53 | }; 54 | 55 | class SpanContextExtensionImpl : public SpanContextExtension { 56 | public: 57 | SpanContextExtensionImpl(absl::string_view header_value); 58 | 59 | TracingMode tracingMode() const override { return tracing_mode_; } 60 | 61 | private: 62 | TracingMode tracing_mode_ = TracingMode::Default; 63 | }; 64 | 65 | } // namespace cpp2sky 66 | -------------------------------------------------------------------------------- /source/tracer_impl.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "source/tracer_impl.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "cpp2sky/exception.h" 21 | #include "language-agent/ConfigurationDiscoveryService.pb.h" 22 | #include "matchers/suffix_matcher.h" 23 | #include "source/grpc_async_client_impl.h" 24 | #include "spdlog/spdlog.h" 25 | 26 | namespace cpp2sky { 27 | 28 | using namespace spdlog; 29 | 30 | TracerImpl::TracerImpl(const TracerConfig& config, CredentialsSharedPtr cred) 31 | : segment_factory_(config) { 32 | init(config, cred); 33 | } 34 | 35 | TracerImpl::TracerImpl(const TracerConfig& config, 36 | TraceAsyncClientPtr async_client) 37 | : async_client_(std::move(async_client)), segment_factory_(config) { 38 | init(config, nullptr); 39 | } 40 | 41 | TracerImpl::~TracerImpl() { 42 | // Stop the reporter client. 43 | async_client_->resetClient(); 44 | } 45 | 46 | TracingContextSharedPtr TracerImpl::newContext() { 47 | return segment_factory_.create(); 48 | } 49 | 50 | TracingContextSharedPtr TracerImpl::newContext(SpanContextSharedPtr span) { 51 | return segment_factory_.create(span); 52 | } 53 | 54 | bool TracerImpl::report(TracingContextSharedPtr ctx) { 55 | if (!ctx || !ctx->readyToSend()) { 56 | return false; 57 | } 58 | 59 | if (!ctx->spans().empty()) { 60 | if (ignore_matcher_->match(ctx->spans().front()->operationName())) { 61 | return false; 62 | } 63 | } 64 | 65 | async_client_->sendMessage(ctx->createSegmentObject()); 66 | return true; 67 | } 68 | 69 | void TracerImpl::init(const TracerConfig& config, CredentialsSharedPtr cred) { 70 | spdlog::set_level(spdlog::level::warn); 71 | 72 | if (async_client_ == nullptr) { 73 | if (config.protocol() != Protocol::GRPC) { 74 | throw TracerException("Only GRPC is supported."); 75 | } 76 | async_client_ = TraceAsyncClientImpl::createClient( 77 | config.address(), config.token(), nullptr, std::move(cred)); 78 | } 79 | 80 | ignore_matcher_.reset(new SuffixMatcher( 81 | std::vector(config.ignore_operation_name_suffix().begin(), 82 | config.ignore_operation_name_suffix().end()))); 83 | } 84 | 85 | TracerPtr createInsecureGrpcTracer(const TracerConfig& cfg) { 86 | return TracerPtr{new TracerImpl(cfg, grpc::InsecureChannelCredentials())}; 87 | } 88 | 89 | } // namespace cpp2sky 90 | -------------------------------------------------------------------------------- /source/tracer_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "cpp2sky/internal/async_client.h" 20 | #include "cpp2sky/internal/matcher.h" 21 | #include "cpp2sky/tracer.h" 22 | #include "language-agent/ConfigurationDiscoveryService.pb.h" 23 | #include "source/grpc_async_client_impl.h" 24 | #include "source/tracing_context_impl.h" 25 | #include "source/utils/timer.h" 26 | 27 | namespace cpp2sky { 28 | 29 | using TracerRequestType = skywalking::v3::SegmentObject; 30 | using TracerResponseType = skywalking::v3::Commands; 31 | 32 | using CdsRequest = skywalking::v3::ConfigurationSyncRequest; 33 | using CdsResponse = skywalking::v3::Commands; 34 | 35 | class TracerImpl : public Tracer { 36 | public: 37 | TracerImpl(const TracerConfig& config, CredentialsSharedPtr credentials); 38 | TracerImpl(const TracerConfig& config, TraceAsyncClientPtr async_client); 39 | ~TracerImpl(); 40 | 41 | TracingContextSharedPtr newContext() override; 42 | TracingContextSharedPtr newContext(SpanContextSharedPtr span) override; 43 | 44 | bool report(TracingContextSharedPtr ctx) override; 45 | 46 | private: 47 | void init(const TracerConfig& config, CredentialsSharedPtr cred); 48 | 49 | TraceAsyncClientPtr async_client_; 50 | TracingContextFactory segment_factory_; 51 | MatcherPtr ignore_matcher_; 52 | }; 53 | 54 | } // namespace cpp2sky 55 | -------------------------------------------------------------------------------- /source/tracing_context_impl.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "source/tracing_context_impl.h" 16 | 17 | #include 18 | 19 | #include "absl/strings/string_view.h" 20 | #include "cpp2sky/exception.h" 21 | #include "cpp2sky/time.h" 22 | #include "language-agent/Tracing.pb.h" 23 | #include "source/utils/base64.h" 24 | #include "source/utils/random_generator.h" 25 | 26 | namespace cpp2sky { 27 | 28 | TracingSpanImpl::TracingSpanImpl(int32_t span_id, 29 | TracingContextImpl& parent_tracing_context) 30 | : span_store_( 31 | *parent_tracing_context.segment_store_.mutable_spans()->Add()) { 32 | span_store_.set_spanid(span_id); 33 | // Default component id for historical reason. 34 | span_store_.set_componentid(9000); 35 | } 36 | 37 | void TracingSpanImpl::addLogImpl(absl::string_view key, absl::string_view value, 38 | int64_t timestamp) { 39 | assert(!finished_); 40 | auto* l = span_store_.add_logs(); 41 | l->set_time(timestamp); 42 | auto* e = l->add_data(); 43 | e->set_key(std::string(key)); 44 | e->set_value(std::string(value)); 45 | } 46 | 47 | void TracingSpanImpl::addLog(absl::string_view key, absl::string_view value) { 48 | addLogImpl(key, value, TimePoint().fetch()); 49 | } 50 | 51 | void TracingSpanImpl::addLog(absl::string_view key, absl::string_view value, 52 | TimePoint current_time) { 53 | addLogImpl(key, value, current_time.fetch()); 54 | } 55 | 56 | void TracingSpanImpl::addLog(absl::string_view key, absl::string_view value, 57 | TimePoint current_time) { 58 | addLogImpl(key, value, current_time.fetch()); 59 | } 60 | 61 | void TracingSpanImpl::addTag(absl::string_view key, absl::string_view value) { 62 | assert(!finished_); 63 | auto* kv = span_store_.add_tags(); 64 | kv->set_key(std::string(key)); 65 | kv->set_value(std::string(value)); 66 | } 67 | 68 | void TracingSpanImpl::startSpanImpl(absl::string_view operation_name, 69 | int64_t timestamp) { 70 | span_store_.set_operationname(std::string(operation_name)); 71 | span_store_.set_starttime(timestamp); 72 | } 73 | 74 | void TracingSpanImpl::startSpan(absl::string_view operation_name) { 75 | startSpanImpl(operation_name, TimePoint().fetch()); 76 | } 77 | 78 | void TracingSpanImpl::startSpan(absl::string_view operation_name, 79 | TimePoint current_time) { 80 | startSpanImpl(operation_name, current_time.fetch()); 81 | } 82 | 83 | void TracingSpanImpl::startSpan(absl::string_view operation_name, 84 | TimePoint current_time) { 85 | startSpanImpl(operation_name, current_time.fetch()); 86 | } 87 | 88 | void TracingSpanImpl::endSpanImpl(int64_t timestamp) { 89 | assert(!finished_); 90 | span_store_.set_endtime(timestamp); 91 | finished_ = true; 92 | } 93 | 94 | void TracingSpanImpl::endSpan() { 95 | endSpanImpl(TimePoint().fetch()); 96 | } 97 | 98 | void TracingSpanImpl::endSpan(TimePoint current_time) { 99 | endSpanImpl(current_time.fetch()); 100 | } 101 | 102 | void TracingSpanImpl::endSpan(TimePoint current_time) { 103 | endSpanImpl(current_time.fetch()); 104 | } 105 | 106 | void TracingSpanImpl::setComponentId(int32_t component_id) { 107 | assert(!finished_); 108 | 109 | // Component ID is reserved on Skywalking spec. 110 | // For more details here: 111 | // https://github.com/apache/skywalking/blob/master/docs/en/guides/Component-library-settings.md 112 | span_store_.set_componentid(component_id); 113 | } 114 | 115 | void TracingSpanImpl::setOperationName(absl::string_view name) { 116 | assert(!finished_); 117 | span_store_.set_operationname(std::string(name)); 118 | } 119 | 120 | void TracingSpanImpl::addSegmentRef(const SpanContext& span_context) { 121 | // TODO(shikugawa): cpp2sky only supports cross process propagation right now. 122 | // So It is correct to specify this. 123 | auto* entry = span_store_.add_refs(); 124 | 125 | entry->set_reftype(skywalking::v3::RefType::CrossProcess); 126 | entry->set_traceid(span_context.traceId()); 127 | entry->set_parenttracesegmentid(span_context.traceSegmentId()); 128 | entry->set_parentservice(span_context.service()); 129 | entry->set_parentserviceinstance(span_context.serviceInstance()); 130 | entry->set_parentspanid(span_context.spanId()); 131 | entry->set_parentendpoint(span_context.endpoint()); 132 | entry->set_networkaddressusedatpeer(span_context.targetAddress()); 133 | } 134 | 135 | TracingContextImpl::TracingContextImpl( 136 | const std::string& service_name, const std::string& instance_name, 137 | SpanContextSharedPtr parent_span_context, 138 | SpanContextExtensionSharedPtr parent_ext_span_context, 139 | RandomGenerator& random) 140 | : parent_span_context_(std::move(parent_span_context)), 141 | parent_ext_span_context_(std::move(parent_ext_span_context)) { 142 | segment_store_.set_traceid( 143 | parent_span_context_ ? parent_span_context_->traceId() : random.uuid()); 144 | segment_store_.set_tracesegmentid(random.uuid()); 145 | segment_store_.set_service(service_name); 146 | segment_store_.set_serviceinstance(instance_name); 147 | } 148 | 149 | TracingContextImpl::TracingContextImpl(const std::string& service_name, 150 | const std::string& instance_name, 151 | RandomGenerator& random) 152 | : TracingContextImpl(service_name, instance_name, nullptr, nullptr, 153 | random) {} 154 | 155 | TracingContextImpl::TracingContextImpl(const std::string& service_name, 156 | const std::string& instance_name, 157 | SpanContextSharedPtr parent_span_context, 158 | RandomGenerator& random) 159 | : TracingContextImpl(service_name, instance_name, 160 | std::move(parent_span_context), nullptr, random) {} 161 | 162 | TracingSpanSharedPtr TracingContextImpl::createExitSpan( 163 | TracingSpanSharedPtr parent_span) { 164 | auto current_span = createSpan(); 165 | current_span->setParentSpanId(parent_span->spanId()); 166 | current_span->setSpanType(skywalking::v3::SpanType::Exit); 167 | return current_span; 168 | } 169 | 170 | TracingSpanSharedPtr TracingContextImpl::createEntrySpan() { 171 | if (!spans_.empty()) { 172 | return nullptr; 173 | } 174 | 175 | auto current_span = createSpan(); 176 | current_span->setParentSpanId(-1); 177 | current_span->setSpanType(skywalking::v3::SpanType::Entry); 178 | 179 | if (parent_span_context_ != nullptr) { 180 | current_span->addSegmentRef(*parent_span_context_); 181 | } 182 | 183 | return current_span; 184 | } 185 | 186 | absl::optional TracingContextImpl::createSW8HeaderValue( 187 | const absl::string_view target_address) { 188 | auto target_span = spans_.back(); 189 | if (target_span->spanType() != skywalking::v3::SpanType::Exit) { 190 | return absl::nullopt; 191 | } 192 | return encodeSpan(target_span, target_address); 193 | } 194 | 195 | std::string TracingContextImpl::encodeSpan( 196 | TracingSpanSharedPtr parent_span, const absl::string_view target_address) { 197 | assert(parent_span); 198 | std::string header_value; 199 | 200 | auto parent_spanid = std::to_string(parent_span->spanId()); 201 | auto endpoint = spans_.front()->operationName(); 202 | 203 | // always send to OAP 204 | header_value += "1-"; 205 | header_value += Base64::encode(segment_store_.traceid()) + "-"; 206 | header_value += Base64::encode(segment_store_.tracesegmentid()) + "-"; 207 | header_value += parent_spanid + "-"; 208 | header_value += Base64::encode(segment_store_.service()) + "-"; 209 | header_value += Base64::encode(segment_store_.serviceinstance()) + "-"; 210 | header_value += Base64::encode(endpoint.data(), endpoint.size()) + "-"; 211 | header_value += 212 | Base64::encode(target_address.data(), target_address.length()); 213 | 214 | return header_value; 215 | } 216 | 217 | TracingSpanSharedPtr TracingContextImpl::createSpan() { 218 | auto current_span = std::make_shared(spans_.size(), *this); 219 | 220 | // It supports only HTTP request tracing. 221 | current_span->setSpanLayer(skywalking::v3::SpanLayer::Http); 222 | if (should_skip_analysis_) { 223 | current_span->setSkipAnalysis(); 224 | } 225 | 226 | spans_.push_back(current_span); 227 | return current_span; 228 | } 229 | 230 | skywalking::v3::SegmentObject TracingContextImpl::createSegmentObject() { 231 | spans_.clear(); 232 | return std::move(segment_store_); 233 | } 234 | 235 | bool TracingContextImpl::readyToSend() { 236 | for (const auto& span : spans_) { 237 | if (!span->finished()) { 238 | return false; 239 | } 240 | } 241 | return true; 242 | } 243 | 244 | std::string TracingContextImpl::logMessage(absl::string_view message) const { 245 | std::string output = message.data(); 246 | output += "\", \"SW_CTX\": ["; 247 | output += "\"" + segment_store_.service() + "\","; 248 | output += "\"" + segment_store_.serviceinstance() + "\","; 249 | output += "\"" + segment_store_.traceid() + "\","; 250 | output += "\"" + segment_store_.tracesegmentid() + "\","; 251 | 252 | if (!spans_.empty()) { 253 | output += "\"" + std::to_string(spans_.back()->spanId()) + "\"]}"; 254 | } else { 255 | output += "\"-1\"]}"; 256 | } 257 | 258 | return output; 259 | } 260 | 261 | TracingContextFactory::TracingContextFactory(const TracerConfig& config) 262 | : service_name_(config.service_name()), 263 | instance_name_(config.instance_name()) {} 264 | 265 | TracingContextSharedPtr TracingContextFactory::create() { 266 | return std::make_shared(service_name_, instance_name_, 267 | random_generator_); 268 | } 269 | 270 | TracingContextSharedPtr TracingContextFactory::create( 271 | SpanContextSharedPtr span_context) { 272 | return std::make_shared(service_name_, instance_name_, 273 | span_context, random_generator_); 274 | } 275 | 276 | TracingContextSharedPtr TracingContextFactory::create( 277 | SpanContextSharedPtr span_context, 278 | SpanContextExtensionSharedPtr ext_span_context) { 279 | auto context = std::make_shared( 280 | service_name_, instance_name_, span_context, ext_span_context, 281 | random_generator_); 282 | if (ext_span_context->tracingMode() == TracingMode::Skip) { 283 | context->setSkipAnalysis(); 284 | } 285 | return context; 286 | } 287 | 288 | } // namespace cpp2sky 289 | -------------------------------------------------------------------------------- /source/tracing_context_impl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include "absl/strings/string_view.h" 18 | #include "cpp2sky/config.pb.h" 19 | #include "cpp2sky/propagation.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "source/utils/random_generator.h" 22 | 23 | namespace cpp2sky { 24 | 25 | class TracingContextImpl; 26 | 27 | class TracingSpanImpl : public TracingSpan { 28 | public: 29 | TracingSpanImpl(int32_t span_id, TracingContextImpl& parent_tracing_context); 30 | 31 | skywalking::v3::SpanObject createSpanObject() override { 32 | // Create an copy of current span object. This is only be used for test for 33 | // now. 34 | return span_store_; 35 | } 36 | int32_t spanId() const override { return span_store_.spanid(); } 37 | int32_t parentSpanId() const override { return span_store_.parentspanid(); } 38 | int64_t startTime() const override { return span_store_.starttime(); } 39 | int64_t endTime() const override { return span_store_.endtime(); } 40 | absl::string_view peer() const override { return span_store_.peer(); } 41 | skywalking::v3::SpanType spanType() const override { 42 | return span_store_.spantype(); 43 | } 44 | skywalking::v3::SpanLayer spanLayer() const override { 45 | return span_store_.spanlayer(); 46 | } 47 | bool errorStatus() const override { return span_store_.iserror(); } 48 | bool skipAnalysis() const override { return span_store_.skipanalysis(); } 49 | int32_t componentId() const override { return span_store_.componentid(); } 50 | absl::string_view operationName() const override { 51 | return span_store_.operationname(); 52 | } 53 | 54 | void setParentSpanId(int32_t span_id) override { 55 | assert(!finished_); 56 | span_store_.set_parentspanid(span_id); 57 | } 58 | void startSpan(absl::string_view operation_name) override; 59 | void startSpan(absl::string_view operation_name, 60 | TimePoint current_time) override; 61 | void startSpan(absl::string_view operation_name, 62 | TimePoint current_time) override; 63 | void endSpan() override; 64 | void endSpan(TimePoint current_time) override; 65 | void endSpan(TimePoint current_time) override; 66 | void setPeer(absl::string_view remote_address) override { 67 | assert(!finished_); 68 | span_store_.set_peer(std::string(remote_address)); 69 | } 70 | void setSpanType(skywalking::v3::SpanType type) override { 71 | span_store_.set_spantype(type); 72 | } 73 | void setSpanLayer(skywalking::v3::SpanLayer layer) override { 74 | span_store_.set_spanlayer(layer); 75 | } 76 | void setErrorStatus() override { span_store_.set_iserror(true); } 77 | void setSkipAnalysis() override { span_store_.set_skipanalysis(true); } 78 | void addTag(absl::string_view key, absl::string_view value) override; 79 | void addLog(absl::string_view key, absl::string_view value) override; 80 | void addLog(absl::string_view key, absl::string_view value, 81 | TimePoint current_time) override; 82 | void addLog(absl::string_view key, absl::string_view value, 83 | TimePoint current_time) override; 84 | void setComponentId(int32_t component_id) override; 85 | void setOperationName(absl::string_view name) override; 86 | void addSegmentRef(const SpanContext& span_context) override; 87 | bool finished() const override { return finished_; } 88 | 89 | void addLogImpl(absl::string_view key, absl::string_view value, 90 | int64_t timestamp); 91 | void startSpanImpl(absl::string_view operation_name, int64_t timestamp); 92 | void endSpanImpl(int64_t timestamp); 93 | 94 | private: 95 | bool finished_ = false; 96 | 97 | // Parent segment owns all span objects and we only keep a ref in the tracing 98 | // span. 99 | skywalking::v3::SpanObject& span_store_; 100 | }; 101 | 102 | class TracingContextImpl : public TracingContext { 103 | public: 104 | // This constructor is called when there is no parent SpanContext. 105 | TracingContextImpl(const std::string& service_name, 106 | const std::string& instance_name, RandomGenerator& random); 107 | TracingContextImpl(const std::string& service_name, 108 | const std::string& instance_name, 109 | SpanContextSharedPtr parent_span_context, 110 | RandomGenerator& random); 111 | TracingContextImpl(const std::string& service_name, 112 | const std::string& instance_name, 113 | SpanContextSharedPtr parent_span_context, 114 | SpanContextExtensionSharedPtr parent_ext_span_context, 115 | RandomGenerator& random); 116 | 117 | const std::string& traceId() const override { 118 | return segment_store_.traceid(); 119 | } 120 | const std::string& traceSegmentId() const override { 121 | return segment_store_.tracesegmentid(); 122 | } 123 | const std::string& service() const override { 124 | return segment_store_.service(); 125 | } 126 | const std::string& serviceInstance() const override { 127 | return segment_store_.serviceinstance(); 128 | } 129 | const std::list& spans() const override { 130 | return spans_; 131 | } 132 | SpanContextSharedPtr parentSpanContext() const override { 133 | return parent_span_context_; 134 | } 135 | SpanContextExtensionSharedPtr parentSpanContextExtension() const override { 136 | return parent_ext_span_context_; 137 | } 138 | 139 | TracingSpanSharedPtr createExitSpan( 140 | TracingSpanSharedPtr parent_span) override; 141 | 142 | TracingSpanSharedPtr createEntrySpan() override; 143 | absl::optional createSW8HeaderValue( 144 | const absl::string_view target_address) override; 145 | skywalking::v3::SegmentObject createSegmentObject() override; 146 | void setSkipAnalysis() override { should_skip_analysis_ = true; } 147 | bool skipAnalysis() override { return should_skip_analysis_; } 148 | bool readyToSend() override; 149 | std::string logMessage(absl::string_view message) const override; 150 | 151 | private: 152 | friend class TracingSpanImpl; 153 | 154 | std::string encodeSpan(TracingSpanSharedPtr parent_span, 155 | const absl::string_view target_address); 156 | TracingSpanSharedPtr createSpan(); 157 | 158 | SpanContextSharedPtr parent_span_context_; 159 | SpanContextExtensionSharedPtr parent_ext_span_context_; 160 | 161 | std::list spans_; 162 | 163 | skywalking::v3::SegmentObject segment_store_; 164 | 165 | bool should_skip_analysis_ = false; 166 | }; 167 | 168 | class TracingContextFactory { 169 | public: 170 | TracingContextFactory(const TracerConfig& config); 171 | 172 | TracingContextSharedPtr create(); 173 | TracingContextSharedPtr create(SpanContextSharedPtr span_context); 174 | TracingContextSharedPtr create( 175 | SpanContextSharedPtr span_context, 176 | SpanContextExtensionSharedPtr ext_span_context); 177 | 178 | private: 179 | std::string service_name_; 180 | std::string instance_name_; 181 | RandomGeneratorImpl random_generator_; 182 | }; 183 | 184 | } // namespace cpp2sky 185 | -------------------------------------------------------------------------------- /source/utils/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "util_lib", 5 | srcs = [ 6 | "random_generator.cc", 7 | ], 8 | hdrs = [ 9 | "base64.h", 10 | "buffer.h", 11 | "random_generator.h", 12 | "timer.h", 13 | ], 14 | visibility = ["//visibility:public"], 15 | deps = [ 16 | "//cpp2sky/internal:random_generator_interface", 17 | "@com_google_absl//absl/strings", 18 | ], 19 | ) 20 | -------------------------------------------------------------------------------- /source/utils/base64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // From 16 | // https://github.com/envoyproxy/envoy/blob/master/source/common/common/base64.{h,cc} 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace cpp2sky { 25 | 26 | class Base64 { 27 | public: 28 | static std::string encode(const char* input, uint64_t length, 29 | bool add_padding); 30 | static std::string encode(const char* input, uint64_t length) { 31 | return encode(input, length, true); 32 | } 33 | static std::string encode(absl::string_view input) { 34 | return encode(input.data(), input.size()); 35 | } 36 | static std::string decodeWithoutPadding(absl::string_view input); 37 | }; 38 | 39 | // clang-format off 40 | constexpr char CHAR_TABLE[] = 41 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 42 | 43 | constexpr unsigned char REVERSE_LOOKUP_TABLE[256] = { 44 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 45 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 46 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 47 | 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 48 | 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49 | 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 50 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 51 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 52 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 53 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 54 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}; 55 | // clang-format on 56 | 57 | inline bool decodeBase(const uint8_t cur_char, uint64_t pos, std::string& ret, 58 | const unsigned char* const reverse_lookup_table) { 59 | const unsigned char c = reverse_lookup_table[static_cast(cur_char)]; 60 | if (c == 64) { 61 | // Invalid character 62 | return false; 63 | } 64 | 65 | switch (pos % 4) { 66 | case 0: 67 | ret.push_back(c << 2); 68 | break; 69 | case 1: 70 | ret.back() |= c >> 4; 71 | ret.push_back(c << 4); 72 | break; 73 | case 2: 74 | ret.back() |= c >> 2; 75 | ret.push_back(c << 6); 76 | break; 77 | case 3: 78 | ret.back() |= c; 79 | break; 80 | } 81 | return true; 82 | } 83 | 84 | inline bool decodeLast(const uint8_t cur_char, uint64_t pos, std::string& ret, 85 | const unsigned char* const reverse_lookup_table) { 86 | const unsigned char c = reverse_lookup_table[static_cast(cur_char)]; 87 | if (c == 64) { 88 | // Invalid character 89 | return false; 90 | } 91 | 92 | switch (pos % 4) { 93 | case 0: 94 | return false; 95 | case 1: 96 | ret.back() |= c >> 4; 97 | return (c & 0b1111) == 0; 98 | case 2: 99 | ret.back() |= c >> 2; 100 | return (c & 0b11) == 0; 101 | case 3: 102 | ret.back() |= c; 103 | break; 104 | } 105 | return true; 106 | } 107 | 108 | inline void encodeBase(const uint8_t cur_char, uint64_t pos, uint8_t& next_c, 109 | std::string& ret, const char* const char_table) { 110 | switch (pos % 3) { 111 | case 0: 112 | ret.push_back(char_table[cur_char >> 2]); 113 | next_c = (cur_char & 0x03) << 4; 114 | break; 115 | case 1: 116 | ret.push_back(char_table[next_c | (cur_char >> 4)]); 117 | next_c = (cur_char & 0x0f) << 2; 118 | break; 119 | case 2: 120 | ret.push_back(char_table[next_c | (cur_char >> 6)]); 121 | ret.push_back(char_table[cur_char & 0x3f]); 122 | next_c = 0; 123 | break; 124 | } 125 | } 126 | 127 | inline void encodeLast(uint64_t pos, uint8_t last_char, std::string& ret, 128 | const char* const char_table, bool add_padding) { 129 | switch (pos % 3) { 130 | case 1: 131 | ret.push_back(char_table[last_char]); 132 | if (add_padding) { 133 | ret.push_back('='); 134 | ret.push_back('='); 135 | } 136 | break; 137 | case 2: 138 | ret.push_back(char_table[last_char]); 139 | if (add_padding) { 140 | ret.push_back('='); 141 | } 142 | break; 143 | default: 144 | break; 145 | } 146 | } 147 | 148 | inline std::string Base64::encode(const char* input, uint64_t length, 149 | bool add_padding) { 150 | uint64_t output_length = (length + 2) / 3 * 4; 151 | std::string ret; 152 | ret.reserve(output_length); 153 | 154 | uint64_t pos = 0; 155 | uint8_t next_c = 0; 156 | 157 | for (uint64_t i = 0; i < length; ++i) { 158 | encodeBase(input[i], pos++, next_c, ret, CHAR_TABLE); 159 | } 160 | 161 | encodeLast(pos, next_c, ret, CHAR_TABLE, add_padding); 162 | 163 | return ret; 164 | } 165 | 166 | inline std::string Base64::decodeWithoutPadding(absl::string_view input) { 167 | if (input.empty()) { 168 | return ""; 169 | } 170 | 171 | // At most last two chars can be '='. 172 | size_t n = input.length(); 173 | if (input[n - 1] == '=') { 174 | n--; 175 | if (n > 0 && input[n - 1] == '=') { 176 | n--; 177 | } 178 | } 179 | // Last position before "valid" padding character. 180 | uint64_t last = n - 1; 181 | // Determine output length. 182 | size_t max_length = (n + 3) / 4 * 3; 183 | if (n % 4 == 3) { 184 | max_length -= 1; 185 | } 186 | if (n % 4 == 2) { 187 | max_length -= 2; 188 | } 189 | 190 | std::string ret; 191 | ret.reserve(max_length); 192 | for (uint64_t i = 0; i < last; ++i) { 193 | if (!decodeBase(input[i], i, ret, REVERSE_LOOKUP_TABLE)) { 194 | return ""; 195 | } 196 | } 197 | 198 | if (!decodeLast(input[last], last, ret, REVERSE_LOOKUP_TABLE)) { 199 | return ""; 200 | } 201 | 202 | assert(ret.size() == max_length); 203 | return ret; 204 | } 205 | 206 | } // namespace cpp2sky 207 | -------------------------------------------------------------------------------- /source/utils/buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/types/optional.h" 21 | 22 | namespace cpp2sky { 23 | 24 | template 25 | class ValueBuffer { 26 | public: 27 | ValueBuffer() = default; 28 | 29 | absl::optional pop_front() { 30 | std::unique_lock lock(mux_); 31 | if (buf_.empty()) { 32 | return absl::nullopt; 33 | } 34 | auto result = std::move(buf_.front()); 35 | buf_.pop_front(); 36 | return result; 37 | } 38 | 39 | /** 40 | * Insert new value. 41 | */ 42 | void push_back(Value value) { 43 | std::unique_lock lock(mux_); 44 | buf_.emplace_back(std::move(value)); 45 | } 46 | 47 | /** 48 | * Check whether buffer is empty or not. 49 | */ 50 | bool empty() const { 51 | std::unique_lock lock(mux_); 52 | return buf_.empty(); 53 | } 54 | 55 | /** 56 | * Get item count 57 | */ 58 | size_t size() const { 59 | std::unique_lock lock(mux_); 60 | return buf_.size(); 61 | } 62 | 63 | /** 64 | * Clear buffer 65 | */ 66 | void clear() { 67 | std::unique_lock lock(mux_); 68 | buf_.clear(); 69 | } 70 | 71 | private: 72 | std::deque buf_; 73 | mutable std::mutex mux_; 74 | }; 75 | 76 | } // namespace cpp2sky 77 | -------------------------------------------------------------------------------- /source/utils/random_generator.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // From 16 | // https://github.com/envoyproxy/envoy/blob/master/source/common/common/random_generator.{h,cc} 17 | 18 | #include "source/utils/random_generator.h" 19 | 20 | namespace cpp2sky { 21 | 22 | std::string RandomGeneratorImpl::uuid() { 23 | static thread_local char buffered[2048]; 24 | static thread_local size_t buffered_idx = sizeof(buffered); 25 | 26 | if (buffered_idx + 16 > sizeof(buffered)) { 27 | // TODO(shikugawa): Self-implemented random number generator is used right 28 | // now. RAND_bytes on OpenSSL is used on Envoy's RandomGenerator. 29 | randomBuffer(buffered, sizeof(buffered)); 30 | buffered_idx = 0; 31 | } 32 | 33 | // Consume 16 bytes from the buffer. 34 | assert(buffered_idx + 16 <= sizeof(buffered)); 35 | char* rand = &buffered[buffered_idx]; 36 | buffered_idx += 16; 37 | 38 | // Create UUID from Truly Random or Pseudo-Random Numbers. 39 | // See: https://tools.ietf.org/html/rfc4122#section-4.4 40 | rand[6] = (rand[6] & 0x0f) | 0x40; // UUID version 4 (random) 41 | rand[8] = (rand[8] & 0x3f) | 0x80; // UUID variant 1 (RFC4122) 42 | 43 | // Convert UUID to a string representation, e.g. 44 | // a121e9e1-feae-4136-9e0e-6fac343d56c9. 45 | static const char* const hex = "0123456789abcdef"; 46 | char uuid[UUID_LENGTH]; 47 | 48 | for (uint8_t i = 0; i < 4; i++) { 49 | const uint8_t d = rand[i]; 50 | uuid[2 * i] = hex[d >> 4]; 51 | uuid[2 * i + 1] = hex[d & 0x0f]; 52 | } 53 | 54 | uuid[8] = '-'; 55 | 56 | for (uint8_t i = 4; i < 6; i++) { 57 | const uint8_t d = rand[i]; 58 | uuid[2 * i + 1] = hex[d >> 4]; 59 | uuid[2 * i + 2] = hex[d & 0x0f]; 60 | } 61 | 62 | uuid[13] = '-'; 63 | 64 | for (uint8_t i = 6; i < 8; i++) { 65 | const uint8_t d = rand[i]; 66 | uuid[2 * i + 2] = hex[d >> 4]; 67 | uuid[2 * i + 3] = hex[d & 0x0f]; 68 | } 69 | 70 | uuid[18] = '-'; 71 | 72 | for (uint8_t i = 8; i < 10; i++) { 73 | const uint8_t d = rand[i]; 74 | uuid[2 * i + 3] = hex[d >> 4]; 75 | uuid[2 * i + 4] = hex[d & 0x0f]; 76 | } 77 | 78 | uuid[23] = '-'; 79 | 80 | for (uint8_t i = 10; i < 16; i++) { 81 | const uint8_t d = rand[i]; 82 | uuid[2 * i + 4] = hex[d >> 4]; 83 | uuid[2 * i + 5] = hex[d & 0x0f]; 84 | } 85 | 86 | return std::string(uuid, UUID_LENGTH); 87 | } 88 | 89 | void RandomGeneratorImpl::randomBuffer(char* ch, size_t len) { 90 | std::random_device engine; 91 | std::uniform_int_distribution dist(0, CHARS.size() - 1); 92 | for (size_t i = 0; i < len; ++i) { 93 | ch[i] = CHARS[dist(engine)]; 94 | } 95 | } 96 | 97 | } // namespace cpp2sky 98 | -------------------------------------------------------------------------------- /source/utils/random_generator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // From 16 | // https://github.com/envoyproxy/envoy/blob/master/source/common/common/random_generator.{h,cc} 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "absl/strings/string_view.h" 25 | #include "cpp2sky/internal/random_generator.h" 26 | 27 | namespace cpp2sky { 28 | 29 | namespace { 30 | static constexpr size_t UUID_LENGTH = 36; 31 | static constexpr absl::string_view CHARS = 32 | "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"; 33 | } // namespace 34 | 35 | class RandomGeneratorImpl : public RandomGenerator { 36 | public: 37 | std::string uuid(); 38 | 39 | private: 40 | void randomBuffer(char* ch, size_t len); 41 | }; 42 | 43 | } // namespace cpp2sky 44 | -------------------------------------------------------------------------------- /source/utils/timer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | 19 | #include "cpp2sky/time.h" 20 | 21 | namespace cpp2sky { 22 | class Timer { 23 | public: 24 | Timer(int64_t interval_sec) { 25 | interval_ = interval_sec * 1000; 26 | prev_time_ = TimePoint().fetch(); 27 | } 28 | 29 | bool check() { 30 | const auto current_time = TimePoint().fetch(); 31 | bool result = current_time - prev_time_ > interval_; 32 | if (result) { 33 | prev_time_ = current_time; 34 | } 35 | 36 | return result; 37 | } 38 | 39 | private: 40 | int64_t prev_time_; 41 | int64_t interval_; 42 | }; 43 | } // namespace cpp2sky 44 | -------------------------------------------------------------------------------- /test/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") 2 | 3 | cc_library( 4 | name = "mocks", 5 | hdrs = [ 6 | "mocks.h", 7 | ], 8 | deps = [ 9 | "//cpp2sky/internal:async_client_interface", 10 | "//cpp2sky/internal:random_generator_interface", 11 | "@com_google_googletest//:gtest_main", 12 | "@skywalking_data_collect_protocol//language-agent:tracing_protocol_cc_proto", 13 | ], 14 | ) 15 | 16 | cc_test( 17 | name = "propagation_test", 18 | srcs = [ 19 | "propagation_test.cc", 20 | ], 21 | visibility = ["//visibility:public"], 22 | deps = [ 23 | "//source:cpp2sky_lib", 24 | "@com_google_googletest//:gtest_main", 25 | ], 26 | ) 27 | 28 | cc_test( 29 | name = "tracing_context_test", 30 | srcs = [ 31 | "tracing_context_test.cc", 32 | ], 33 | visibility = ["//visibility:public"], 34 | deps = [ 35 | ":mocks", 36 | "//source:cpp2sky_lib", 37 | "@com_google_googletest//:gtest_main", 38 | "@com_google_protobuf//:protobuf", 39 | ], 40 | ) 41 | 42 | cc_test( 43 | name = "grpc_async_client_test", 44 | srcs = [ 45 | "grpc_async_client_test.cc", 46 | ], 47 | visibility = ["//visibility:public"], 48 | deps = [ 49 | ":mocks", 50 | "//source:cpp2sky_lib", 51 | "@com_google_absl//absl/memory", 52 | "@com_google_googletest//:gtest_main", 53 | ], 54 | ) 55 | 56 | cc_test( 57 | name = "buffer_test", 58 | srcs = [ 59 | "buffer_test.cc", 60 | ], 61 | visibility = ["//visibility:public"], 62 | deps = [ 63 | ":mocks", 64 | "//source:cpp2sky_lib", 65 | "@com_google_absl//absl/memory", 66 | "@com_google_googletest//:gtest_main", 67 | ], 68 | ) 69 | 70 | cc_test( 71 | name = "tracer_test", 72 | srcs = [ 73 | "tracer_test.cc", 74 | ], 75 | visibility = ["//visibility:public"], 76 | deps = [ 77 | ":mocks", 78 | "//source:cpp2sky_lib", 79 | "@com_google_googletest//:gtest_main", 80 | ], 81 | ) 82 | 83 | cc_test( 84 | name = "matcher_test", 85 | srcs = [ 86 | "matcher_test.cc", 87 | ], 88 | visibility = ["//visibility:public"], 89 | deps = [ 90 | ":mocks", 91 | "//source:cpp2sky_lib", 92 | "@com_google_googletest//:gtest_main", 93 | ], 94 | ) 95 | -------------------------------------------------------------------------------- /test/buffer_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include "source/utils/buffer.h" 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/memory/memory.h" 21 | 22 | namespace cpp2sky { 23 | 24 | TEST(BufferTest, Basic) { 25 | ValueBuffer buffer; 26 | EXPECT_TRUE(buffer.empty()); 27 | EXPECT_EQ(buffer.size(), 0); 28 | 29 | buffer.push_back(1); 30 | EXPECT_FALSE(buffer.empty()); 31 | EXPECT_EQ(buffer.size(), 1); 32 | 33 | buffer.push_back(2); 34 | EXPECT_FALSE(buffer.empty()); 35 | EXPECT_EQ(buffer.size(), 2); 36 | 37 | auto value = buffer.pop_front(); 38 | EXPECT_TRUE(value.has_value()); 39 | EXPECT_EQ(value.value(), 1); 40 | EXPECT_FALSE(buffer.empty()); 41 | EXPECT_EQ(buffer.size(), 1); 42 | 43 | value = buffer.pop_front(); 44 | EXPECT_TRUE(value.has_value()); 45 | EXPECT_EQ(value.value(), 2); 46 | EXPECT_TRUE(buffer.empty()); 47 | EXPECT_EQ(buffer.size(), 0); 48 | 49 | value = buffer.pop_front(); 50 | EXPECT_FALSE(value.has_value()); 51 | EXPECT_TRUE(buffer.empty()); 52 | EXPECT_EQ(buffer.size(), 0); 53 | } 54 | 55 | } // namespace cpp2sky 56 | -------------------------------------------------------------------------------- /test/e2e/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary") 2 | 3 | cc_binary( 4 | name = "e2e_consumer", 5 | srcs = ["consumer.cc"], 6 | deps = [ 7 | "//cpp2sky:cpp2sky_interface", 8 | "//source:cpp2sky_lib", 9 | "@com_github_httplib//:httplib", 10 | ], 11 | ) 12 | 13 | cc_binary( 14 | name = "e2e_provider", 15 | srcs = ["provider.cc"], 16 | deps = [ 17 | "//cpp2sky:cpp2sky_interface", 18 | "//source:cpp2sky_lib", 19 | "@com_github_httplib//:httplib", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /test/e2e/bridge.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from skywalking import agent, config 4 | from skywalking.decorators import runnable 5 | 6 | if __name__ == '__main__': 7 | config.init() 8 | agent.start() 9 | 10 | from flask import Flask, Response 11 | 12 | app = Flask(__name__) 13 | 14 | @app.route("/users", methods=["POST", "GET"]) 15 | def application(): 16 | res = requests.get("http://provider:8081/pong2") 17 | return Response(status=res.status_code) 18 | 19 | PORT = 8082 20 | app.run(host='0.0.0.0', port=PORT, debug=True) 21 | -------------------------------------------------------------------------------- /test/e2e/consumer.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | 17 | #include "cpp2sky/propagation.h" 18 | #include "cpp2sky/tracer.h" 19 | #include "cpp2sky/tracing_context.h" 20 | #include "cpp2sky/well_known_names.h" 21 | #include "httplib.h" 22 | 23 | using namespace cpp2sky; 24 | 25 | TracerConfig config; 26 | 27 | void init() { 28 | config.set_instance_name("node_0"); 29 | config.set_service_name("consumer"); 30 | config.set_address("collector:19876"); 31 | } 32 | 33 | int main() { 34 | init(); 35 | 36 | httplib::Server svr; 37 | auto tracer = createInsecureGrpcTracer(config); 38 | 39 | // C++ 40 | svr.Get("/ping", [&](const httplib::Request& req, httplib::Response& res) { 41 | auto tracing_context = tracer->newContext(); 42 | 43 | { 44 | StartEntrySpan entry_span(tracing_context, "/ping"); 45 | 46 | { 47 | std::string target_address = "provider:8081"; 48 | 49 | StartExitSpan exit_span(tracing_context, entry_span.get(), "/pong"); 50 | exit_span.get()->setPeer(target_address); 51 | 52 | httplib::Client cli("provider", 8081); 53 | httplib::Headers headers = { 54 | {kPropagationHeader.data(), 55 | *tracing_context->createSW8HeaderValue(target_address)}}; 56 | auto res = cli.Get("/pong", headers); 57 | } 58 | } 59 | 60 | tracer->report(std::move(tracing_context)); 61 | }); 62 | 63 | // Python 64 | svr.Get("/ping2", [&](const httplib::Request& req, httplib::Response& res) { 65 | auto tracing_context = tracer->newContext(); 66 | 67 | { 68 | StartEntrySpan entry_span(tracing_context, "/ping2"); 69 | 70 | { 71 | std::string target_address = "bridge:8082"; 72 | 73 | StartExitSpan exit_span(tracing_context, entry_span.get(), "/users"); 74 | exit_span.get()->setPeer(target_address); 75 | 76 | httplib::Client cli("bridge", 8082); 77 | httplib::Headers headers = { 78 | {kPropagationHeader.data(), 79 | *tracing_context->createSW8HeaderValue(target_address)}}; 80 | auto res = cli.Get("/users", headers); 81 | } 82 | } 83 | 84 | tracer->report(std::move(tracing_context)); 85 | }); 86 | 87 | svr.listen("0.0.0.0", 8080); 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /test/e2e/data/all_expected.yaml: -------------------------------------------------------------------------------- 1 | segmentItems: 2 | - segmentSize: 2 3 | segments: 4 | # Segment from provider in [consumer -> provider] case. 5 | - segmentId: not null 6 | spans: 7 | - componentId: 9000 8 | endTime: gt 0 9 | isError: false 10 | operationName: /pong 11 | parentSpanId: -1 12 | peer: '' 13 | refs: 14 | - networkAddress: provider:8081 15 | parentEndpoint: /ping 16 | parentService: consumer 17 | parentServiceInstance: node_0 18 | parentSpanId: 1 19 | parentTraceSegmentId: not null 20 | refType: CrossProcess 21 | traceId: not null 22 | skipAnalysis: false 23 | spanId: 0 24 | spanLayer: Http 25 | spanType: Entry 26 | startTime: gt 0 27 | # Segment from provider in [consumer -> bridge -> provider] case. 28 | - segmentId: not null 29 | spans: 30 | - componentId: 9000 31 | endTime: gt 0 32 | isError: false 33 | operationName: /pong2 34 | parentSpanId: -1 35 | peer: '' 36 | refs: 37 | - networkAddress: provider:8081 38 | parentEndpoint: /pong2 39 | parentService: bridge 40 | parentServiceInstance: not null 41 | parentSpanId: 1 42 | parentTraceSegmentId: not null 43 | refType: CrossProcess 44 | traceId: not null 45 | skipAnalysis: false 46 | spanId: 0 47 | spanLayer: Http 48 | spanType: Entry 49 | startTime: gt 0 50 | serviceName: provider 51 | - segmentSize: 1 52 | segments: 53 | # Segment from bridge in [consumer -> bridge -> provider] case. 54 | - segmentId: not null 55 | spans: 56 | - componentId: 7002 57 | endTime: gt 0 58 | isError: false 59 | operationName: /pong2 60 | parentSpanId: 0 61 | peer: provider:8081 62 | skipAnalysis: false 63 | spanId: 1 64 | spanLayer: Http 65 | spanType: Exit 66 | startTime: gt 0 67 | tags: 68 | - key: http.method 69 | value: GET 70 | - key: http.url 71 | value: http://provider:8081/pong2 72 | - key: http.status_code 73 | value: '200' 74 | - componentId: 7001 75 | endTime: gt 0 76 | isError: false 77 | operationName: /users 78 | parentSpanId: -1 79 | peer: not null 80 | refs: 81 | - networkAddress: bridge:8082 82 | parentEndpoint: /ping2 83 | parentService: consumer 84 | parentServiceInstance: node_0 85 | parentSpanId: 1 86 | parentTraceSegmentId: not null 87 | refType: CrossProcess 88 | traceId: not null 89 | skipAnalysis: false 90 | spanId: 0 91 | spanLayer: Http 92 | spanType: Entry 93 | startTime: gt 0 94 | tags: 95 | - key: http.method 96 | value: GET 97 | - key: http.url 98 | value: http://bridge:8082/users 99 | - key: http.status_code 100 | value: '200' 101 | serviceName: bridge 102 | - segmentSize: 2 103 | segments: 104 | # Segment from consumer [consumer -> provider] case. 105 | - segmentId: not null 106 | spans: 107 | - componentId: 9000 108 | endTime: gt 0 109 | isError: false 110 | operationName: /ping 111 | parentSpanId: -1 112 | peer: '' 113 | skipAnalysis: false 114 | spanId: 0 115 | spanLayer: Http 116 | spanType: Entry 117 | startTime: gt 0 118 | - componentId: 9000 119 | endTime: gt 0 120 | isError: false 121 | operationName: /pong 122 | parentSpanId: 0 123 | peer: provider:8081 124 | skipAnalysis: false 125 | spanId: 1 126 | spanLayer: Http 127 | spanType: Exit 128 | startTime: gt 0 129 | # Segment from consumer [consumer -> bridge -> provider] case. 130 | - segmentId: not null 131 | spans: 132 | - componentId: 9000 133 | endTime: gt 0 134 | isError: false 135 | operationName: /ping2 136 | parentSpanId: -1 137 | peer: '' 138 | skipAnalysis: false 139 | spanId: 0 140 | spanLayer: Http 141 | spanType: Entry 142 | startTime: gt 0 143 | - componentId: 9000 144 | endTime: gt 0 145 | isError: false 146 | operationName: /users 147 | parentSpanId: 0 148 | peer: bridge:8082 149 | skipAnalysis: false 150 | spanId: 1 151 | spanLayer: Http 152 | spanType: Exit 153 | startTime: gt 0 154 | serviceName: consumer -------------------------------------------------------------------------------- /test/e2e/docker/Dockerfile.bridge: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | COPY . /tmp 3 | WORKDIR tmp 4 | RUN pip3 install -r test/e2e/requirements.txt 5 | -------------------------------------------------------------------------------- /test/e2e/docker/Dockerfile.consumer: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | RUN apt-get update && apt install -y wget build-essential clang 3 | RUN wget -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 4 | RUN chmod +x /usr/local/bin/bazel 5 | COPY . /tmp 6 | WORKDIR tmp 7 | RUN bazel build //test/e2e:e2e_consumer 8 | -------------------------------------------------------------------------------- /test/e2e/docker/Dockerfile.provider: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | RUN apt-get update && apt install -y wget build-essential clang 3 | RUN wget -O /usr/local/bin/bazel https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64 4 | RUN chmod +x /usr/local/bin/bazel 5 | COPY . /tmp 6 | WORKDIR tmp 7 | RUN bazel build //test/e2e:e2e_provider 8 | -------------------------------------------------------------------------------- /test/e2e/docker/Dockerfile.tool: -------------------------------------------------------------------------------- 1 | FROM openjdk:8 2 | 3 | WORKDIR /tests 4 | 5 | ARG COMMIT_HASH=b6efe6af0a5499502b8cf8b76c7351e3f172a616 6 | 7 | ADD https://github.com/apache/skywalking-agent-test-tool/archive/${COMMIT_HASH}.tar.gz . 8 | 9 | RUN tar -xf ${COMMIT_HASH}.tar.gz --strip 1 10 | 11 | RUN rm ${COMMIT_HASH}.tar.gz 12 | 13 | RUN ./mvnw -B -DskipTests package 14 | 15 | FROM openjdk:8 16 | 17 | EXPOSE 19876 12800 18 | 19 | WORKDIR /tests 20 | 21 | COPY --from=0 /tests/dist/skywalking-mock-collector.tar.gz /tests 22 | 23 | RUN tar -xf skywalking-mock-collector.tar.gz --strip 1 24 | 25 | RUN chmod +x bin/collector-startup.sh 26 | 27 | ENTRYPOINT bin/collector-startup.sh 28 | -------------------------------------------------------------------------------- /test/e2e/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | collector: 4 | build: 5 | context: ../../.. 6 | dockerfile: ./test/e2e/docker/Dockerfile.tool 7 | ports: 8 | - 19876:19876 9 | - 12800:12800 10 | 11 | consumer: 12 | build: 13 | context: ../../.. 14 | dockerfile: ./test/e2e/docker/Dockerfile.consumer 15 | ports: 16 | - 8080:8080 17 | command: sh -c "GRPC_VERBOSITY=debug /tmp/bazel-bin/test/e2e/e2e_consumer" 18 | depends_on: 19 | - collector 20 | - bridge 21 | - provider 22 | 23 | bridge: 24 | build: 25 | context: ../../.. 26 | dockerfile: ./test/e2e/docker/Dockerfile.bridge 27 | ports: 28 | - 8082:8082 29 | environment: 30 | SW_AGENT_NAME: bridge 31 | SW_AGENT_COLLECTOR_BACKEND_SERVICES: "collector:19876" 32 | SW_AGENT_LOGGING_LEVEL: DEBUG 33 | SW_FLASK_COLLECT_HTTP_PARAMS: "True" 34 | SW_AGENT_LOG_REPORTER_ACTIVE: "False" 35 | SW_AGENT_PROFILE_ACTIVE: "False" 36 | command: sh -c "sleep 10 && python3 test/e2e/bridge.py" 37 | depends_on: 38 | - collector 39 | - provider 40 | 41 | provider: 42 | build: 43 | context: ../../.. 44 | dockerfile: ./test/e2e/docker/Dockerfile.provider 45 | ports: 46 | - 8081:8081 47 | command: sh -c "GRPC_VERBOSITY=debug /tmp/bazel-bin/test/e2e/e2e_provider" 48 | depends_on: 49 | - collector 50 | -------------------------------------------------------------------------------- /test/e2e/main.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import os 3 | import sys 4 | import time 5 | from difflib import Differ 6 | from os.path import dirname 7 | 8 | import argparse 9 | import yaml 10 | import requests 11 | import time 12 | from requests import Response 13 | 14 | try: 15 | from yaml import CSafeLoader as Loader 16 | except ImportError: 17 | from yaml import SafeLoader as Loader 18 | 19 | def validate(expected_file_name): 20 | with open(expected_file_name) as expected_data_file: 21 | expected_data = os.linesep.join(expected_data_file.readlines()) 22 | 23 | response = requests.post(url='http://0.0.0.0:12800/dataValidate', data=expected_data) 24 | 25 | if response.status_code != 200: 26 | print('validate result: ') 27 | print(response.content) 28 | 29 | res = requests.get('http://0.0.0.0:12800/receiveData') 30 | actual_data = yaml.dump(yaml.load(res.content, Loader=Loader)) 31 | 32 | print('actual data: ') 33 | print(actual_data) 34 | 35 | differ = Differ() 36 | diff_list = list(differ.compare( 37 | actual_data.splitlines(keepends=True), 38 | yaml.dump(yaml.load(expected_data, Loader=Loader)).splitlines(keepends=True) 39 | )) 40 | 41 | print('diff list: ') 42 | sys.stdout.writelines(diff_list) 43 | 44 | return response 45 | 46 | if __name__ == "__main__": 47 | parser = argparse.ArgumentParser() 48 | parser.add_argument('--expected_file', help='File name which includes expected reported value') 49 | parser.add_argument('--max_retry_times', help='Max retry times', type=int) 50 | 51 | args = parser.parse_args() 52 | 53 | retry_times = 0 54 | while True: 55 | if retry_times > args.max_retry_times: 56 | raise RuntimeError("Max retry times exceeded") 57 | 58 | # Test two hops: consumer -> provider by /ping 59 | try: 60 | requests.get('http://0.0.0.0:8080/ping', timeout=5) 61 | except Exception as e: 62 | print(e) 63 | retry_times += 1 64 | time.sleep(2) 65 | continue 66 | 67 | # Test three hops: consumer -> bridge -> provider by /ping2 68 | retry_times = 0 69 | try: 70 | requests.get('http://0.0.0.0:8080/ping2', timeout=5) 71 | except Exception as e: 72 | print(e) 73 | retry_times += 1 74 | time.sleep(2) 75 | continue 76 | 77 | res = validate(args.expected_file) 78 | assert res.status_code == 200 79 | break 80 | -------------------------------------------------------------------------------- /test/e2e/provider.cc: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2020 SkyAPM 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | #include 17 | 18 | #include "cpp2sky/propagation.h" 19 | #include "cpp2sky/tracer.h" 20 | #include "cpp2sky/tracing_context.h" 21 | #include "cpp2sky/well_known_names.h" 22 | #include "httplib.h" 23 | 24 | using namespace cpp2sky; 25 | 26 | TracerConfig config; 27 | 28 | void init() { 29 | config.set_instance_name("node_0"); 30 | config.set_service_name("provider"); 31 | config.set_address("collector:19876"); 32 | } 33 | 34 | int main() { 35 | init(); 36 | 37 | httplib::Server svr; 38 | auto tracer = createInsecureGrpcTracer(config); 39 | 40 | svr.Get("/pong", [&](const httplib::Request& req, httplib::Response& res) { 41 | auto parent = req.get_header_value(kPropagationHeader.data()); 42 | auto tracing_context = tracer->newContext(createSpanContext(parent)); 43 | 44 | { StartEntrySpan entry_span(tracing_context, "/pong"); } 45 | 46 | tracer->report(std::move(tracing_context)); 47 | }); 48 | 49 | svr.Get("/pong2", [&](const httplib::Request& req, httplib::Response& res) { 50 | auto parent = req.get_header_value(kPropagationHeader.data()); 51 | auto tracing_context = tracer->newContext(createSpanContext(parent)); 52 | 53 | { StartEntrySpan entry_span(tracing_context, "/pong2"); } 54 | 55 | tracer->report(std::move(tracing_context)); 56 | }); 57 | 58 | svr.listen("0.0.0.0", 8081); 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /test/e2e/requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==5.3.1 2 | requests==2.26.0 3 | apache-skywalking==0.8.0 4 | Flask==2.0.2 5 | Werkzeug==2.2.2 -------------------------------------------------------------------------------- /test/grpc_async_client_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "absl/memory/memory.h" 23 | #include "cpp2sky/internal/async_client.h" 24 | #include "language-agent/Tracing.pb.h" 25 | #include "source/grpc_async_client_impl.h" 26 | #include "test/mocks.h" 27 | 28 | namespace cpp2sky { 29 | 30 | using testing::_; 31 | 32 | struct TestStats { 33 | TestStats(uint64_t total, uint64_t dropped, uint64_t sent) 34 | : total_(total), dropped_(dropped), sent_(sent) { 35 | pending_ = total_ - dropped_ - sent_; 36 | } 37 | 38 | uint64_t total_{}; 39 | uint64_t dropped_{}; 40 | uint64_t sent_{}; 41 | uint64_t pending_{}; 42 | }; 43 | 44 | class TestTraceAsyncClient : public TraceAsyncClientImpl { 45 | public: 46 | TestTraceAsyncClient(const std::string& address, const std::string& token, 47 | TraceAsyncStreamFactoryPtr stream_factory, 48 | CredentialsSharedPtr credentials) 49 | : TraceAsyncClientImpl(address, token, std::move(stream_factory), 50 | std::move(credentials)) {} 51 | 52 | TestStats getTestStats() const { 53 | TestStats stats(messages_total_.load(), messages_dropped_.load(), 54 | messages_sent_.load()); 55 | return stats; 56 | } 57 | 58 | void notifyWriteEvent(bool success) { write_event_tag_.callback(success); } 59 | void notifyStartEvent(bool success) { basic_event_tag_.callback(success); } 60 | 61 | uint64_t bufferSize() const { return message_buffer_.size(); } 62 | }; 63 | 64 | class TestTraceAsyncStreamFactory : public TraceAsyncStreamFactory { 65 | public: 66 | TestTraceAsyncStreamFactory(std::shared_ptr mock_stream) 67 | : mock_stream_(mock_stream) {} 68 | 69 | class TestTraceAsyncStream : public TraceAsyncStream { 70 | public: 71 | TestTraceAsyncStream(std::shared_ptr mock_stream) 72 | : mock_stream_(mock_stream) {} 73 | void sendMessage(TraceRequestType message) override { 74 | mock_stream_->sendMessage(std::move(message)); 75 | } 76 | std::shared_ptr mock_stream_; 77 | }; 78 | 79 | TraceAsyncStreamPtr createStream(GrpcClientContextPtr, GrpcStub&, 80 | GrpcCompletionQueue&, AsyncEventTag&, 81 | AsyncEventTag&) override { 82 | return TraceAsyncStreamPtr{new TestTraceAsyncStream(mock_stream_)}; 83 | } 84 | 85 | std::shared_ptr mock_stream_; 86 | }; 87 | 88 | class TraceAsyncClientImplTest : public testing::Test { 89 | public: 90 | TraceAsyncClientImplTest() { 91 | client_.reset(new TestTraceAsyncClient( 92 | address_, token_, 93 | TraceAsyncStreamFactoryPtr{ 94 | new TestTraceAsyncStreamFactory(mock_stream_)}, 95 | grpc::InsecureChannelCredentials())); 96 | } 97 | 98 | ~TraceAsyncClientImplTest() { 99 | client_->resetClient(); 100 | client_.reset(); 101 | } 102 | 103 | protected: 104 | std::string address_{"localhost:50051"}; 105 | std::string token_{"token"}; 106 | 107 | std::shared_ptr mock_stream_ = 108 | std::make_shared(); 109 | 110 | std::unique_ptr client_; 111 | }; 112 | 113 | TEST_F(TraceAsyncClientImplTest, SendMessageTest) { 114 | skywalking::v3::SegmentObject fake_message; 115 | EXPECT_CALL(*mock_stream_, sendMessage(_)).Times(0); 116 | client_->sendMessage(fake_message); 117 | 118 | auto stats = client_->getTestStats(); 119 | EXPECT_EQ(stats.total_, 1); 120 | EXPECT_EQ(stats.dropped_, 0); 121 | EXPECT_EQ(stats.sent_, 0); 122 | EXPECT_EQ(stats.pending_, 1); 123 | EXPECT_EQ(client_->bufferSize(), 1); 124 | 125 | client_->notifyStartEvent(false); 126 | 127 | sleep(1); // wait for the event loop to process the event. 128 | 129 | // The stream is not ready, the message still in the buffer. 130 | stats = client_->getTestStats(); 131 | EXPECT_EQ(stats.total_, 1); 132 | EXPECT_EQ(stats.dropped_, 0); 133 | EXPECT_EQ(stats.sent_, 0); 134 | EXPECT_EQ(stats.pending_, 1); 135 | EXPECT_EQ(client_->bufferSize(), 1); 136 | 137 | EXPECT_CALL(*mock_stream_, sendMessage(_)); 138 | client_->notifyStartEvent(true); 139 | sleep(1); // wait for the event loop to process the event. 140 | 141 | // The stream is ready, the message is popped and sent. 142 | // But before the collback is called, the stats is not updated. 143 | 144 | stats = client_->getTestStats(); 145 | EXPECT_EQ(stats.total_, 1); 146 | EXPECT_EQ(stats.dropped_, 0); 147 | EXPECT_EQ(stats.sent_, 0); 148 | EXPECT_EQ(stats.pending_, 1); 149 | EXPECT_EQ(client_->bufferSize(), 0); 150 | 151 | client_->notifyWriteEvent(true); 152 | sleep(1); // wait for the event loop to process the event. 153 | 154 | // The message is sent successfully. 155 | stats = client_->getTestStats(); 156 | EXPECT_EQ(stats.total_, 1); 157 | EXPECT_EQ(stats.dropped_, 0); 158 | EXPECT_EQ(stats.sent_, 1); 159 | EXPECT_EQ(stats.pending_, 0); 160 | EXPECT_EQ(client_->bufferSize(), 0); 161 | 162 | // Send another message. This time the stream is ready and 163 | // previous message is sent successfully. So the new message 164 | // should be sent immediately. 165 | EXPECT_CALL(*mock_stream_, sendMessage(_)); 166 | client_->sendMessage(fake_message); 167 | sleep(1); // wait for the event loop to process the event. 168 | 169 | stats = client_->getTestStats(); 170 | EXPECT_EQ(stats.total_, 2); 171 | EXPECT_EQ(stats.dropped_, 0); 172 | EXPECT_EQ(stats.sent_, 1); 173 | EXPECT_EQ(stats.pending_, 1); 174 | 175 | client_->notifyWriteEvent(true); 176 | sleep(1); // wait for the event loop to process the event. 177 | 178 | stats = client_->getTestStats(); 179 | EXPECT_EQ(stats.total_, 2); 180 | EXPECT_EQ(stats.dropped_, 0); 181 | EXPECT_EQ(stats.sent_, 2); 182 | EXPECT_EQ(stats.pending_, 0); 183 | } 184 | 185 | } // namespace cpp2sky 186 | -------------------------------------------------------------------------------- /test/matcher_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "cpp2sky/config.pb.h" 19 | #include "mocks.h" 20 | #include "source/matchers/suffix_matcher.h" 21 | 22 | namespace cpp2sky { 23 | 24 | TEST(MatcherTest, Success) { 25 | SuffixMatcher matcher(std::vector{"/ignore"}); 26 | EXPECT_TRUE(matcher.match("/istio/ignore")); 27 | EXPECT_FALSE(matcher.match("/istio")); 28 | } 29 | 30 | } // namespace cpp2sky 31 | -------------------------------------------------------------------------------- /test/mocks.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include 18 | #include 19 | 20 | #include "cpp2sky/internal/async_client.h" 21 | #include "cpp2sky/internal/random_generator.h" 22 | 23 | using testing::_; 24 | using testing::Return; 25 | 26 | namespace cpp2sky { 27 | 28 | class MockRandomGenerator : public RandomGenerator { 29 | public: 30 | MockRandomGenerator() { ON_CALL(*this, uuid).WillByDefault(Return("uuid")); } 31 | MOCK_METHOD(std::string, uuid, ()); 32 | }; 33 | 34 | class MockTraceAsyncStream : public TraceAsyncStream { 35 | public: 36 | MOCK_METHOD(void, sendMessage, (TraceRequestType)); 37 | }; 38 | 39 | class MockTraceAsyncClient : public TraceAsyncClient { 40 | public: 41 | MOCK_METHOD(void, sendMessage, (TraceRequestType)); 42 | MOCK_METHOD(void, resetClient, ()); 43 | }; 44 | 45 | } // namespace cpp2sky 46 | -------------------------------------------------------------------------------- /test/propagation_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | 18 | #include "absl/strings/string_view.h" 19 | #include "cpp2sky/exception.h" 20 | #include "source/propagation_impl.h" 21 | 22 | namespace cpp2sky { 23 | 24 | static constexpr absl::string_view sample = 25 | "1-MQ==-NQ==-3-bWVzaA==-aW5zdGFuY2U=-L2FwaS92MS9oZWFsdGg=-" 26 | "ZXhhbXBsZS5jb206ODA4MA=="; 27 | 28 | static constexpr absl::string_view less_field = 29 | "1-MQ==-NQ==-3-bWVzaA==-aW5zdGFuY2U=-L2FwaS92MS9oZWFsdGg="; 30 | 31 | static constexpr absl::string_view more_field = 32 | "1-MQ==-NQ==-3-bWVzaA==-aW5zdGFuY2U=-L2FwaS92MS9oZWFsdGg=-" 33 | "ZXhhbXBsZS5jb206ODA4MA==-hogehoge"; 34 | 35 | static constexpr absl::string_view invalid_sample = 36 | "3-MQ==-NQ==-3-bWVzaA==-aW5zdGFuY2U=-L2FwaS92MS9oZWFsdGg=-" 37 | "ZXhhbXBsZS5jb206ODA4MA=="; 38 | 39 | static constexpr absl::string_view invalid_span_id = 40 | "1-MQ==-NQ==-abc-bWVzaA==-aW5zdGFuY2U=-L2FwaS92MS9oZWFsdGg=-" 41 | "ZXhhbXBsZS5jb206ODA4MA=="; 42 | 43 | TEST(TestSpanContext, Basic) { 44 | auto data = std::string(sample.data()); 45 | SpanContextImpl sc(data); 46 | EXPECT_TRUE(sc.sample()); 47 | EXPECT_EQ(sc.traceId(), "1"); 48 | EXPECT_EQ(sc.traceSegmentId(), "5"); 49 | EXPECT_EQ(sc.spanId(), 3); 50 | EXPECT_EQ(sc.service(), "mesh"); 51 | EXPECT_EQ(sc.serviceInstance(), "instance"); 52 | EXPECT_EQ(sc.endpoint(), "/api/v1/health"); 53 | EXPECT_EQ(sc.targetAddress(), "example.com:8080"); 54 | } 55 | 56 | TEST(TestSpanContext, MalformedSpanContext) { 57 | { 58 | auto data = std::string(less_field.data()); 59 | EXPECT_THROW(SpanContextImpl{data}, TracerException); 60 | } 61 | { 62 | auto data = std::string(more_field.data()); 63 | EXPECT_THROW(SpanContextImpl{data}, TracerException); 64 | } 65 | { 66 | auto data = std::string(invalid_sample.data()); 67 | EXPECT_THROW(SpanContextImpl{data}, TracerException); 68 | } 69 | { EXPECT_THROW(SpanContextImpl{invalid_span_id}, TracerException); } 70 | } 71 | 72 | } // namespace cpp2sky 73 | -------------------------------------------------------------------------------- /test/tracer_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2021 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "cpp2sky/config.pb.h" 20 | #include "cpp2sky/internal/async_client.h" 21 | #include "mocks.h" 22 | #include "source/tracer_impl.h" 23 | 24 | namespace cpp2sky { 25 | 26 | TEST(TracerTest, MatchedOpShouldIgnored) { 27 | TracerConfig config; 28 | *config.add_ignore_operation_name_suffix() = "/ignored"; 29 | 30 | TracerImpl tracer(config, TraceAsyncClientPtr{ 31 | new testing::NiceMock()}); 32 | auto context = tracer.newContext(); 33 | auto span = context->createEntrySpan(); 34 | 35 | span->startSpan("/hoge/ignored"); 36 | span->endSpan(); 37 | 38 | EXPECT_FALSE(tracer.report(context)); 39 | } 40 | 41 | TEST(TracerTest, NotClosedSpanExists) { 42 | TracerConfig config; 43 | 44 | TracerImpl tracer(config, TraceAsyncClientPtr{ 45 | new testing::NiceMock()}); 46 | auto context = tracer.newContext(); 47 | auto span = context->createEntrySpan(); 48 | 49 | span->startSpan("/hoge"); 50 | 51 | EXPECT_FALSE(tracer.report(context)); 52 | } 53 | 54 | TEST(TracerTest, Success) { 55 | TracerConfig config; 56 | 57 | auto mock_reporter = std::unique_ptr{ 58 | new testing::NiceMock()}; 59 | EXPECT_CALL(*mock_reporter, sendMessage(_)); 60 | 61 | TracerImpl tracer(config, std::move(mock_reporter)); 62 | auto context = tracer.newContext(); 63 | auto span = context->createEntrySpan(); 64 | 65 | span->startSpan("/hoge"); 66 | span->endSpan(); 67 | 68 | EXPECT_TRUE(tracer.report(context)); 69 | } 70 | 71 | } // namespace cpp2sky 72 | -------------------------------------------------------------------------------- /test/tracing_context_test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 SkyAPM 2 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include "absl/strings/string_view.h" 23 | #include "external/skywalking_data_collect_protocol/language-agent/Tracing.pb.h" 24 | #include "mocks.h" 25 | #include "source/propagation_impl.h" 26 | #include "source/tracing_context_impl.h" 27 | 28 | using google::protobuf::util::JsonStringToMessage; 29 | using testing::NiceMock; 30 | using testing::Return; 31 | 32 | namespace cpp2sky { 33 | 34 | static constexpr absl::string_view sample_ctx = 35 | "1-MQ==-NQ==-3-bWVzaA==-aW5zdGFuY2U=-L2FwaS92MS9oZWFsdGg=-" 36 | "ZXhhbXBsZS5jb206ODA4MA=="; 37 | 38 | class TracingContextTest : public testing::Test { 39 | public: 40 | TracingContextTest() { 41 | config_.set_service_name(service_name_); 42 | config_.set_instance_name(instance_name_); 43 | 44 | span_ctx_ = std::make_shared(sample_ctx); 45 | span_ext_ctx_ = std::make_shared("1"); 46 | 47 | factory_.reset(new TracingContextFactory(config_)); 48 | } 49 | 50 | protected: 51 | NiceMock random_; 52 | std::string service_name_ = "mesh"; 53 | std::string instance_name_ = "service_0"; 54 | TracerConfig config_; 55 | SpanContextSharedPtr span_ctx_; 56 | SpanContextExtensionSharedPtr span_ext_ctx_; 57 | std::unique_ptr factory_; 58 | }; 59 | 60 | TEST_F(TracingContextTest, BasicTest) { 61 | auto sc = factory_->create(); 62 | EXPECT_EQ(sc->service(), "mesh"); 63 | EXPECT_EQ(sc->serviceInstance(), "service_0"); 64 | 65 | // No parent span 66 | auto span = sc->createEntrySpan(); 67 | EXPECT_EQ(sc->spans().size(), 1); 68 | EXPECT_EQ(span->spanId(), 0); 69 | 70 | auto t1 = TimePoint( 71 | SystemTime(std::chrono::duration(100))); 72 | auto t2 = TimePoint( 73 | SystemTime(std::chrono::duration(200))); 74 | 75 | span->startSpan("sample1", t1); 76 | span->setPeer("localhost:9000"); 77 | span->endSpan(t2); 78 | 79 | std::string json = R"EOF( 80 | { 81 | "spanId": "0", 82 | "parentSpanId": "-1", 83 | "startTime": "100", 84 | "endTime": "200", 85 | "peer": "localhost:9000", 86 | "spanType": "Entry", 87 | "spanLayer": "Http", 88 | "componentId": "9000", 89 | "operationName": "sample1", 90 | "skipAnalysis": "false", 91 | } 92 | )EOF"; 93 | skywalking::v3::SpanObject expected_obj; 94 | JsonStringToMessage(json, &expected_obj); 95 | EXPECT_EQ(expected_obj.DebugString(), span->createSpanObject().DebugString()); 96 | 97 | // With parent span 98 | auto span_child = sc->createExitSpan(std::move(span)); 99 | EXPECT_EQ(sc->spans().size(), 2); 100 | EXPECT_EQ(span_child->spanId(), 1); 101 | 102 | t1 = TimePoint( 103 | SystemTime(std::chrono::duration(100))); 104 | t2 = TimePoint( 105 | SystemTime(std::chrono::duration(200))); 106 | 107 | span_child->startSpan("sample1", t1); 108 | span_child->setPeer("localhost:9000"); 109 | span_child->endSpan(t2); 110 | 111 | std::string json2 = R"EOF( 112 | { 113 | "spanId": "1", 114 | "parentSpanId": "0", 115 | "startTime": "100", 116 | "endTime": "200", 117 | "peer": "localhost:9000", 118 | "spanType": "Exit", 119 | "spanLayer": "Http", 120 | "componentId": "9000", 121 | "operationName": "sample1", 122 | "skipAnalysis": "false", 123 | } 124 | )EOF"; 125 | skywalking::v3::SpanObject expected_obj2; 126 | JsonStringToMessage(json2, &expected_obj2); 127 | EXPECT_EQ(expected_obj2.DebugString(), 128 | span_child->createSpanObject().DebugString()); 129 | } 130 | 131 | TEST_F(TracingContextTest, ChildSegmentContext) { 132 | auto sc = factory_->create(span_ctx_); 133 | EXPECT_EQ(sc->service(), "mesh"); 134 | EXPECT_EQ(sc->serviceInstance(), "service_0"); 135 | 136 | // No parent span 137 | auto span = sc->createEntrySpan(); 138 | EXPECT_EQ(sc->spans().size(), 1); 139 | EXPECT_EQ(span->spanId(), 0); 140 | 141 | auto t1 = TimePoint( 142 | SystemTime(std::chrono::duration(100))); 143 | auto t2 = TimePoint( 144 | SystemTime(std::chrono::duration(200))); 145 | 146 | span->startSpan("sample1", t1); 147 | span->setPeer("localhost:9000"); 148 | span->setOperationName("sample11"); 149 | span->endSpan(t2); 150 | 151 | std::string json = R"EOF( 152 | { 153 | "spanId": "0", 154 | "parentSpanId": "-1", 155 | "startTime": "100", 156 | "endTime": "200", 157 | "refs": { 158 | "refType": "CrossProcess", 159 | "traceId": "1", 160 | "parentTraceSegmentId": "5", 161 | "parentSpanId": 3, 162 | "parentService": "mesh", 163 | "parentServiceInstance": "instance", 164 | "parentEndpoint": "/api/v1/health", 165 | "networkAddressUsedAtPeer": "example.com:8080" 166 | }, 167 | "peer": "localhost:9000", 168 | "spanType": "Entry", 169 | "spanLayer": "Http", 170 | "componentId": "9000", 171 | "skipAnalysis": "false", 172 | "operationName": "sample11", 173 | } 174 | )EOF"; 175 | skywalking::v3::SpanObject expected_obj; 176 | JsonStringToMessage(json, &expected_obj); 177 | EXPECT_EQ(expected_obj.DebugString(), span->createSpanObject().DebugString()); 178 | 179 | // With parent span 180 | auto span_child = sc->createExitSpan(std::move(span)); 181 | EXPECT_EQ(sc->spans().size(), 2); 182 | EXPECT_EQ(span_child->spanId(), 1); 183 | 184 | t1 = TimePoint( 185 | SystemTime(std::chrono::duration(100))); 186 | t2 = TimePoint( 187 | SystemTime(std::chrono::duration(200))); 188 | 189 | span_child->startSpan("sample1", t1); 190 | 191 | span_child->setPeer("localhost:9000"); 192 | span_child->addTag("category", "database"); 193 | 194 | absl::string_view key = "method"; 195 | absl::string_view value = "GETxxxx"; 196 | value.remove_suffix(4); 197 | span_child->addTag(key, value); 198 | 199 | std::string log_key = "service_0"; 200 | std::string log_value = "error"; 201 | auto t3 = TimePoint( 202 | SystemTime(std::chrono::duration(300))); 203 | span_child->addLog(log_key, log_value, t3); 204 | 205 | absl::string_view log_key2 = "service_1"; 206 | absl::string_view log_value2 = "succeeded\x01\x03"; 207 | log_value2.remove_suffix(2); 208 | 209 | auto t4 = TimePoint( 210 | SystemTime(std::chrono::duration(300))); 211 | span_child->addLog(log_key2, log_value2, t4); 212 | 213 | span_child->endSpan(t2); 214 | 215 | std::string json2 = R"EOF( 216 | { 217 | "spanId": "1", 218 | "parentSpanId": "0", 219 | "startTime": "100", 220 | "endTime": "200", 221 | "peer": "localhost:9000", 222 | "spanType": "Exit", 223 | "spanLayer": "Http", 224 | "componentId": "9000", 225 | "skipAnalysis": "false", 226 | "tags": [ 227 | { 228 | "key": "category", 229 | "value": "database" 230 | }, 231 | { 232 | "key": "method", 233 | "value": "GET" 234 | } 235 | ], 236 | "logs": [ 237 | { 238 | "time": "300", 239 | "data": { 240 | "key": "service_0", 241 | "value": "error" 242 | } 243 | }, 244 | { 245 | "time": "300", 246 | "data": { 247 | "key": "service_1", 248 | "value": "succeeded" 249 | } 250 | } 251 | ], 252 | "operationName": "sample1", 253 | } 254 | )EOF"; 255 | skywalking::v3::SpanObject expected_obj2; 256 | JsonStringToMessage(json2, &expected_obj2); 257 | EXPECT_EQ(expected_obj2.DebugString(), 258 | span_child->createSpanObject().DebugString()); 259 | } 260 | 261 | TEST_F(TracingContextTest, SkipAnalysisSegment) { 262 | auto sc = factory_->create(span_ctx_, span_ext_ctx_); 263 | EXPECT_TRUE(sc->skipAnalysis()); 264 | 265 | // No parent span 266 | auto span = sc->createEntrySpan(); 267 | EXPECT_EQ(sc->spans().size(), 1); 268 | EXPECT_EQ(span->spanId(), 0); 269 | 270 | auto t1 = TimePoint( 271 | SystemTime(std::chrono::duration(100))); 272 | auto t2 = TimePoint( 273 | SystemTime(std::chrono::duration(200))); 274 | 275 | span->startSpan("sample1", t1); 276 | span->setPeer("localhost:9000"); 277 | span->endSpan(t2); 278 | 279 | std::string json = R"EOF( 280 | { 281 | "spanId": "0", 282 | "parentSpanId": "-1", 283 | "startTime": "100", 284 | "endTime": "200", 285 | "peer": "localhost:9000", 286 | "spanType": "Entry", 287 | "spanLayer": "Http", 288 | "componentId": "9000", 289 | "operationName": "sample1", 290 | "skipAnalysis": "true", 291 | "refs": { 292 | "refType": "CrossProcess", 293 | "traceId": "1", 294 | "parentTraceSegmentId": "5", 295 | "parentSpanId": 3, 296 | "parentService": "mesh", 297 | "parentServiceInstance": "instance", 298 | "parentEndpoint": "/api/v1/health", 299 | "networkAddressUsedAtPeer": "example.com:8080" 300 | } 301 | } 302 | )EOF"; 303 | skywalking::v3::SpanObject expected_obj; 304 | JsonStringToMessage(json, &expected_obj); 305 | EXPECT_EQ(expected_obj.DebugString(), span->createSpanObject().DebugString()); 306 | } 307 | 308 | TEST_F(TracingContextTest, SW8CreateTest) { 309 | TracingContextImpl sc(config_.service_name(), config_.instance_name(), 310 | span_ctx_, span_ext_ctx_, random_); 311 | EXPECT_EQ(sc.service(), "mesh"); 312 | EXPECT_EQ(sc.serviceInstance(), "service_0"); 313 | 314 | auto span = sc.createEntrySpan(); 315 | EXPECT_EQ(sc.spans().size(), 1); 316 | EXPECT_EQ(span->spanId(), 0); 317 | span->startSpan("sample1"); 318 | span->endSpan(); 319 | 320 | std::string target_address("10.0.0.1:443"); 321 | 322 | // Entry span should be rejected as propagation context 323 | EXPECT_FALSE(sc.createSW8HeaderValue(target_address).has_value()); 324 | 325 | auto span2 = sc.createExitSpan(span); 326 | 327 | EXPECT_EQ(sc.spans().size(), 2); 328 | EXPECT_EQ(span2->spanId(), 1); 329 | span2->startSpan("sample2"); 330 | span2->endSpan(); 331 | 332 | std::string expect_sw8( 333 | "1-MQ==-dXVpZA==-1-bWVzaA==-c2VydmljZV8w-c2FtcGxlMQ==-" 334 | "MTAuMC4wLjE6NDQz"); 335 | 336 | EXPECT_EQ(expect_sw8, *sc.createSW8HeaderValue(target_address)); 337 | 338 | std::vector target_address_based_vector; 339 | target_address_based_vector.reserve(target_address.size() * 2); 340 | 341 | target_address_based_vector = {'1', '0', '.', '0', '.', '0', 342 | '.', '1', ':', '4', '4', '3'}; 343 | 344 | absl::string_view target_address_based_vector_view{ 345 | target_address_based_vector.data(), target_address_based_vector.size()}; 346 | 347 | EXPECT_EQ(target_address_based_vector.size(), target_address.size()); 348 | EXPECT_EQ(expect_sw8, 349 | *sc.createSW8HeaderValue(target_address_based_vector_view)); 350 | 351 | // Make sure that the end of target_address_based_vector_view is not '\0'. We 352 | // reserve enough memory for target_address_based_vector, so push back will 353 | // not cause content to be re-allocated. 354 | target_address_based_vector.push_back('x'); 355 | EXPECT_EQ(expect_sw8, 356 | *sc.createSW8HeaderValue(target_address_based_vector_view)); 357 | } 358 | 359 | TEST_F(TracingContextTest, ReadyToSendTest) { 360 | auto sc = factory_->create(); 361 | 362 | // No parent span 363 | auto span = sc->createEntrySpan(); 364 | EXPECT_EQ(sc->spans().size(), 1); 365 | EXPECT_EQ(span->spanId(), 0); 366 | 367 | auto t1 = TimePoint( 368 | SystemTime(std::chrono::duration(100))); 369 | auto t2 = TimePoint( 370 | SystemTime(std::chrono::duration(200))); 371 | 372 | span->startSpan("sample1", t1); 373 | span->setPeer("localhost:9000"); 374 | span->endSpan(t2); 375 | 376 | EXPECT_TRUE(sc->readyToSend()); 377 | 378 | auto span2 = sc->createExitSpan(span); 379 | auto t3 = TimePoint( 380 | SystemTime(std::chrono::duration(300))); 381 | 382 | span->startSpan("sample1", t3); 383 | 384 | EXPECT_FALSE(sc->readyToSend()); 385 | } 386 | 387 | TEST_F(TracingContextTest, TraceLogTest) { 388 | TracingContextImpl sc(config_.service_name(), config_.instance_name(), 389 | span_ctx_, span_ext_ctx_, random_); 390 | EXPECT_EQ( 391 | "test\", \"SW_CTX\": [\"mesh\",\"service_0\",\"1\",\"uuid\",\"-1\"]}", 392 | sc.logMessage("test")); 393 | } 394 | 395 | } // namespace cpp2sky 396 | --------------------------------------------------------------------------------