Initial implementation of OpenTelemetry APIs. (#3561)
* Start of tracing prototype * Created initial implementation of azure-core-opentelemetry package * New version of enabling MSVCRT Lib for static configs * Attempt to add OpenTelemetry tests to build * Take a dependency on OpenTelemetry version 1.3 * Added service API level tracing support * API Review feedback
This commit is contained in:
parent
745091d01c
commit
8ca08a1429
4
.vscode/cspell.json
vendored
4
.vscode/cspell.json
vendored
@ -42,9 +42,11 @@
|
||||
"cuse",
|
||||
"CUSEUAP",
|
||||
"DCMAKE",
|
||||
"DDISABLE",
|
||||
"deserializers",
|
||||
"Deserializes",
|
||||
"DFETCH",
|
||||
"DMSVC",
|
||||
"docfx",
|
||||
"DPAPI",
|
||||
"DRUN",
|
||||
@ -81,6 +83,7 @@
|
||||
"ncus",
|
||||
"Niels",
|
||||
"nlohmann",
|
||||
"nostd",
|
||||
"noclean",
|
||||
"NOCLOSE",
|
||||
"NOCRLF",
|
||||
@ -89,6 +92,7 @@
|
||||
"northcentralus",
|
||||
"NTSTATUS",
|
||||
"okhttp",
|
||||
"otel",
|
||||
"PBYTE",
|
||||
"pdbs",
|
||||
"Piotrowski",
|
||||
|
||||
@ -55,21 +55,31 @@ if(MSVC_USE_STATIC_CRT AND MSVC)
|
||||
#
|
||||
# 5. We "replace with empty string" (i.e. remove) first, then add, so that '/MT'
|
||||
# will be present (and present once) even if '/MD' was not.
|
||||
|
||||
message(STATUS "Configuring Static Runtime Library.")
|
||||
if(${CMAKE_CXX_FLAGS} MATCHES ".*/MD.*")
|
||||
string(REGEX REPLACE "/MD" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MT")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_CXX_FLAGS_RELEASE} MATCHES ".*/MD.*")
|
||||
string(REGEX REPLACE "/MD" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
|
||||
|
||||
endif()
|
||||
|
||||
if(${CMAKE_CXX_FLAGS_RELWITHDEBINFO} MATCHES ".*/MD.*")
|
||||
string(REGEX REPLACE "/MD" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MT")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_CXX_FLAGS_MINSIZEREL} MATCHES ".*/MD.*")
|
||||
string(REGEX REPLACE "/MD" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
|
||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MT")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_CXX_FLAGS_DEBUG} MATCHES ".*/MD.*")
|
||||
string(REGEX REPLACE "/MDd" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
|
||||
@ -15,7 +15,13 @@
|
||||
"name": "VCPKG_TARGET_TRIPLET",
|
||||
"value": "x64-windows-static",
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"name": "MSVC_USE_STATIC_CRT",
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -33,6 +39,11 @@
|
||||
"name": "VCPKG_TARGET_TRIPLET",
|
||||
"value": "x64-windows-static",
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"name": "MSVC_USE_STATIC_CRT",
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -51,6 +62,11 @@
|
||||
"name": "VCPKG_TARGET_TRIPLET",
|
||||
"value": "x86-windows-static",
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"name": "MSVC_USE_STATIC_CRT",
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -70,6 +86,11 @@
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
},
|
||||
{
|
||||
"name": "MSVC_USE_STATIC_CRT",
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
},
|
||||
{
|
||||
"name": "BUILD_TRANSPORT_CURL",
|
||||
"value": "True",
|
||||
|
||||
@ -44,15 +44,15 @@
|
||||
"CmakeArgs": " -DBUILD_TRANSPORT_CURL=ON"
|
||||
},
|
||||
"Win32Api_release_curl": {
|
||||
"CmakeArgs": " -DBUILD_TESTING=ON -DBUILD_PERFORMANCE_TESTS=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_TRANSPORT_CURL=ON",
|
||||
"CmakeArgs": " -DBUILD_TESTING=ON -DBUILD_PERFORMANCE_TESTS=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_TRANSPORT_CURL=ON -DMSVC_USE_STATIC_CRT=ON",
|
||||
"BuildArgs": "--parallel 8 --config Release",
|
||||
"PublishMapFiles": "true"
|
||||
},
|
||||
},
|
||||
"Win32Api_debug_tests": {
|
||||
"CmakeArgs": " -DBUILD_TESTING=ON -DBUILD_PERFORMANCE_TESTS=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_TRANSPORT_CURL=ON -DBUILD_TRANSPORT_WINHTTP=ON",
|
||||
"CmakeArgs": " -DBUILD_TESTING=ON -DBUILD_PERFORMANCE_TESTS=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_TRANSPORT_CURL=ON -DBUILD_TRANSPORT_WINHTTP=ON -DMSVC_USE_STATIC_CRT=ON",
|
||||
"BuildArgs": "--parallel 8 --config Debug",
|
||||
"PublishMapFiles": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TargetArchitecture": {
|
||||
"x86": {
|
||||
@ -72,7 +72,6 @@
|
||||
"OSVmImage": "MMS2019",
|
||||
"Pool": "azsdk-pool-mms-win-2019-general",
|
||||
"CMAKE_GENERATOR": "Visual Studio 16 2019",
|
||||
"CmakeArgs": " -DBUILD_TRANSPORT_WINHTTP=ON ",
|
||||
"PublishMapFiles": "true"
|
||||
}
|
||||
},
|
||||
@ -80,11 +79,13 @@
|
||||
"UWP_debug": {
|
||||
"CMAKE_SYSTEM_NAME": "WindowsStore",
|
||||
"CMAKE_SYSTEM_VERSION": "10.0",
|
||||
"CmakeArgs": " -DBUILD_TRANSPORT_WINHTTP=ON -DDISABLE_AZURE_CORE_OPENTELEMETRY=ON ",
|
||||
"BuildArgs": "--parallel 8 --config Debug"
|
||||
},
|
||||
"UWP_release": {
|
||||
"CMAKE_SYSTEM_NAME": "WindowsStore",
|
||||
"CMAKE_SYSTEM_VERSION": "10.0",
|
||||
"CmakeArgs": " -DBUILD_TRANSPORT_WINHTTP=ON -DDISABLE_AZURE_CORE_OPENTELEMETRY=ON ",
|
||||
"BuildArgs": "--parallel 8 --config Release"
|
||||
}
|
||||
},
|
||||
@ -97,12 +98,12 @@
|
||||
},
|
||||
{
|
||||
"StaticConfigs": {
|
||||
"Ubuntu18": {
|
||||
"OSVmImage": "MMSUbuntu18.04",
|
||||
"Pool": "azsdk-pool-mms-ubuntu-1804-general",
|
||||
"VCPKG_DEFAULT_TRIPLET": "x64-linux",
|
||||
"BuildArgs": "-j 10"
|
||||
}
|
||||
"Ubuntu18": {
|
||||
"OSVmImage": "MMSUbuntu18.04",
|
||||
"Pool": "azsdk-pool-mms-ubuntu-1804-general",
|
||||
"VCPKG_DEFAULT_TRIPLET": "x64-linux",
|
||||
"BuildArgs": "-j 10"
|
||||
}
|
||||
},
|
||||
"BuildSettings": {
|
||||
"gpp-5": {
|
||||
|
||||
@ -31,3 +31,4 @@ steps:
|
||||
-Ref $(VcpkgCommit)
|
||||
-VcpkgPath $(VCPKG_INSTALLATION_ROOT)
|
||||
pwsh: true
|
||||
displayName: Clone Vcpkg.
|
||||
|
||||
@ -9,6 +9,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
add_subdirectory(azure-core)
|
||||
if (NOT DISABLE_AZURE_CORE_OPENTELEMETRY)
|
||||
add_subdirectory(azure-core-tracing-opentelemetry)
|
||||
endif()
|
||||
|
||||
if (BUILD_PERFORMANCE_TESTS)
|
||||
add_subdirectory(perf)
|
||||
endif()
|
||||
|
||||
5
sdk/core/azure-core-tracing-opentelemetry/CHANGELOG.md
Normal file
5
sdk/core/azure-core-tracing-opentelemetry/CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Release History
|
||||
|
||||
## 1.0.0-beta.1 (Unreleased)
|
||||
|
||||
- Initial release
|
||||
95
sdk/core/azure-core-tracing-opentelemetry/CMakeLists.txt
Normal file
95
sdk/core/azure-core-tracing-opentelemetry/CMakeLists.txt
Normal file
@ -0,0 +1,95 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# setting CMAKE_TOOLCHAIN_FILE must happen before creating the project
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake-modules")
|
||||
include(AzureVcpkg)
|
||||
az_vcpkg_integrate()
|
||||
|
||||
# Azure core is compatible with CMake 3.12
|
||||
cmake_minimum_required (VERSION 3.12)
|
||||
project(azure-core-tracing-opentelemetry LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
include(AzureVersion)
|
||||
include(AzureCodeCoverage)
|
||||
include(AzureTransportAdapters)
|
||||
include(AzureDoxygen)
|
||||
include(AzureGlobalCompileOptions)
|
||||
include(AzureConfigRTTI)
|
||||
# Add create_map_file function
|
||||
include(CreateMapFile)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if(NOT AZ_ALL_LIBRARIES)
|
||||
find_package(azure-core-cpp "1.5.0" CONFIG QUIET)
|
||||
if(NOT azure-core-cpp_FOUND)
|
||||
find_package(azure-core-cpp "1.5.0" REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
find_package(opentelemetry-cpp "1.3.0" CONFIG REQUIRED)
|
||||
|
||||
set(
|
||||
AZURE_CORE_OPENTELEMETRY_HEADER
|
||||
inc/azure/core/tracing/opentelemetry/opentelemetry.hpp
|
||||
)
|
||||
|
||||
set(
|
||||
AZURE_CORE_OPENTELEMETRY_SOURCE
|
||||
src/opentelemetry.cpp
|
||||
)
|
||||
|
||||
add_library(azure-core-tracing-opentelemetry ${AZURE_CORE_OPENTELEMETRY_HEADER} ${AZURE_CORE_OPENTELEMETRY_SOURCE})
|
||||
|
||||
target_include_directories(
|
||||
azure-core-tracing-opentelemetry
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
# make sure that users can consume the project as a library.
|
||||
add_library(Azure::azure-core-tracing-opentelemetry ALIAS azure-core-tracing-opentelemetry)
|
||||
|
||||
# coverage. Has no effect if BUILD_CODE_COVERAGE is OFF
|
||||
create_code_coverage(core-tracing-opentelemetry azure-core-tracing-opentelemetry azure-core-tracing-opentelemetry-test "tests?/*;samples?/*")
|
||||
|
||||
target_link_libraries(azure-core-tracing-opentelemetry INTERFACE Threads::Threads)
|
||||
|
||||
target_link_libraries(azure-core-tracing-opentelemetry PRIVATE azure-core
|
||||
opentelemetry-cpp::api
|
||||
opentelemetry-cpp::ext
|
||||
opentelemetry-cpp::sdk
|
||||
opentelemetry-cpp::trace)
|
||||
|
||||
|
||||
get_az_version("${CMAKE_CURRENT_SOURCE_DIR}/src/private/package_version.hpp")
|
||||
generate_documentation(azure-core-opentelemetry ${AZ_LIBRARY_VERSION})
|
||||
|
||||
az_vcpkg_export(
|
||||
azure-core-tracing-opentelemetry
|
||||
CORE_TRACING_OPENTELEMETRY
|
||||
"azure/core/tracing/opentelemetry/dll_import_export.hpp"
|
||||
)
|
||||
|
||||
az_rtti_setup(
|
||||
azure-core-tracing-opentelemetry
|
||||
CORE_TRACING_OPENTELEMETRY
|
||||
"azure/core/tracing/opentelemetry/rtti.hpp"
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
# define a symbol that enables some test hooks in code
|
||||
add_compile_definitions(TESTING_BUILD)
|
||||
|
||||
if (NOT AZ_ALL_LIBRARIES)
|
||||
include(AddGoogleTest)
|
||||
enable_testing ()
|
||||
endif()
|
||||
|
||||
add_subdirectory(test/ut)
|
||||
endif()
|
||||
269
sdk/core/azure-core-tracing-opentelemetry/NOTICE.txt
Normal file
269
sdk/core/azure-core-tracing-opentelemetry/NOTICE.txt
Normal file
@ -0,0 +1,269 @@
|
||||
azure-core-tracing-opentelemetry
|
||||
|
||||
NOTICES AND INFORMATION
|
||||
Do Not Translate or Localize
|
||||
|
||||
This software incorporates material from third parties. Microsoft makes certain
|
||||
open source code available at https://3rdpartysource.microsoft.com, or you may
|
||||
send a check or money order for US $5.00, including the product name, the open
|
||||
source component name, and version number, to:
|
||||
|
||||
Source Code Compliance Team
|
||||
Microsoft Corporation
|
||||
One Microsoft Way
|
||||
Redmond, WA 98052
|
||||
USA
|
||||
|
||||
Notwithstanding any other terms, you may reverse engineer this software to the
|
||||
extent required to debug changes to any libraries licensed under the GNU Lesser
|
||||
General Public License.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Azure SDK for C++ uses third-party libraries or other resources that may be
|
||||
distributed under licenses different than the Azure SDK for C++ software.
|
||||
|
||||
In the event that we accidentally failed to list a required notice, please
|
||||
bring it to our attention. Post an issue or email us:
|
||||
|
||||
azcppsdkhelp@microsoft.com
|
||||
|
||||
The attached notices are provided for information only.
|
||||
|
||||
|
||||
License notice for CMake Modules AddGoogleTest
|
||||
-------------------------------------------------------------------
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2017, University of Cincinnati
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Licence notice for opentelemetry-cpp
|
||||
-------------------------------------------------------------------
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
141
sdk/core/azure-core-tracing-opentelemetry/README.md
Normal file
141
sdk/core/azure-core-tracing-opentelemetry/README.md
Normal file
@ -0,0 +1,141 @@
|
||||
# Azure SDK Core Tracing Library for C++
|
||||
|
||||
Azure::Core::Tracing::OpenTelemetry (`azure-core-tracing-opentelemetry`) provides an implementation
|
||||
to enable customers to implement tracing in the Azure SDK for C++ libraries.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Include the package
|
||||
|
||||
The easiest way to acquire the OpenTelemetry library is leveraging vcpkg package manager. See the corresponding [Azure SDK for C++ readme section][azsdk_vcpkg_install].
|
||||
|
||||
To install Azure Core OpenTelemetry package via vcpkg:
|
||||
|
||||
```cmd
|
||||
> vcpkg install azure-core-tracing-opentelemetry-cpp
|
||||
```
|
||||
|
||||
Then, use in your CMake file:
|
||||
|
||||
```CMake
|
||||
find_package(azure-core-tracing-opentelemetry-cpp CONFIG REQUIRED)
|
||||
target_link_libraries(<your project name> PRIVATE Azure::azure-core-opentelemetry)
|
||||
```
|
||||
|
||||
## Key concepts
|
||||
|
||||
The `azure-core-tracing-opentelemetry` package supports enabling tracing for Azure SDK packages, using an [OpenTelemetry](https://opentelemetry.io/) `Tracer`.
|
||||
|
||||
By default, all libraries log with a `NoOpTracer` that takes no action. To enable tracing, you will need to set a global tracer provider following the instructions in the [OpenTelemetry getting started guide](https://opentelemetry-cpp.readthedocs.io/en/latest/api/GettingStarted.html) or the [Enabling Tracing using OpenTelemetry example](#enabling-tracing-using-opentelemetry) below.
|
||||
|
||||
### Span Propagation
|
||||
|
||||
Core Tracing supports both automatic and manual span propagation. Automatic propagation is handled using OpenTelemetry's API and will work well in most scenarios.
|
||||
|
||||
For customers who require manual propagation, all client library operations accept an optional field in the `options` parameter where a tracingContext can
|
||||
be passed in and used as the currently active context. Please see the [Manual Span Propagation example](#manual-span-propagation-using-opentelemetry)
|
||||
below for more details.
|
||||
|
||||
### OpenTelemetry Compatibility
|
||||
|
||||
Most Azure SDKs use [OpenTelemetry](https://opentelemetry.io/) to support tracing. Specifically, we depend on
|
||||
the [azure-core-opentelemetry](https://github.com/open-telemetry/opentelemetry-cpp/blob/main/docs/building-with-vcpkg.md) VCPKG package.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Enabling tracing using OpenTelemetry
|
||||
|
||||
```cpp
|
||||
// Start by creating an OpenTelemetry Provider using the
|
||||
// default OpenTelemetry tracer provider.
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> tracerProvider = std::make_shared<Azure::Core::OpenTelemetry::TracerProvider>();
|
||||
|
||||
// Connect the tracerProvider to the current application context.
|
||||
ApplicationContext().SetTracerProvider(tracerProvider);
|
||||
```
|
||||
|
||||
After this, the SDK API implementations will be able to retrieve the tracer provider and produce tracing events automatically.
|
||||
|
||||
### Enabling tracing using a non-default TracerProvider
|
||||
|
||||
```cpp
|
||||
// Start by creating an OpenTelemetry Provider.
|
||||
auto exporter = std::make_unique<opentelemetry::exporter::memory::InMemorySpanExporter>();
|
||||
m_spanData = exporter->GetData();
|
||||
|
||||
// simple processor
|
||||
auto simple_processor = std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor>(
|
||||
new opentelemetry::sdk::trace::SimpleSpanProcessor(std::move(exporter)));
|
||||
|
||||
auto always_on_sampler = std::unique_ptr<opentelemetry::sdk::trace::AlwaysOnSampler>(
|
||||
new opentelemetry::sdk::trace::AlwaysOnSampler);
|
||||
|
||||
auto resource_attributes = opentelemetry::sdk::resource::ResourceAttributes{
|
||||
{"service.name", "telemetryTest"}, {"service.instance.id", "instance-1"}};
|
||||
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);
|
||||
auto openTelemetryProvider = opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
|
||||
new opentelemetry::sdk::trace::TracerProvider(
|
||||
std::move(simple_processor), resource, std::move(always_on_sampler)));
|
||||
|
||||
// Use the default OpenTelemetry tracer provider.
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> tracerProvider =
|
||||
std::make_shared<Azure::Core::OpenTelemetry::TracerProvider>(openTelemetryProvider);
|
||||
|
||||
// Connect the tracerProvider to the current application context.
|
||||
ApplicationContext().SetTracerProvider(tracerProvider);
|
||||
```
|
||||
|
||||
### Manual Span Propagation using OpenTelemetry
|
||||
|
||||
In Azure Service methods, the `Azure::Context` value passed into the tracer optionally has an associated Span.
|
||||
|
||||
If there is a span associated with the `Azure::Context`, then calling `DiagnosticTracingFactory::CreateSpanFromContext` will
|
||||
cause a new span to be created using the span in the provided `Azure::Context` object as the parent span.
|
||||
|
||||
```cpp
|
||||
auto contextAndSpan
|
||||
= Azure::Core::Tracing::_internal::DiagnosticTracingFactory::CreateSpanFromContext(
|
||||
"HTTP GET#2", context);
|
||||
```
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
You can build and run the tests locally by executing `azure-core-tracing-opentelemetry-test`. Explore the `test` folder to see advanced usage and behavior of the public classes.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you run into issues while using this library, please feel free to [file an issue](https://github.com/Azure/azure-sdk-for-cpp/issues/new).
|
||||
|
||||
### OpenTelemetry Compatibility Errors
|
||||
|
||||
|
||||
> Ideally you'd want to use OpenTelemetry 1.3.0 or higher.
|
||||
|
||||
<!-- ### Community-->
|
||||
|
||||
### Reporting security issues and security bugs
|
||||
|
||||
Security issues and bugs should be reported privately, via email, to the Microsoft Security Response Center (MSRC) <secure@microsoft.com>. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Further information, including the MSRC PGP key, can be found in the [Security TechCenter](https://www.microsoft.com/msrc/faqs-report-an-issue).
|
||||
|
||||
### License
|
||||
|
||||
Azure SDK for C++ is licensed under the [MIT](https://github.com/Azure/azure-sdk-for-cpp/blob/main/LICENSE.txt) license.
|
||||
|
||||
<!-- LINKS -->
|
||||
[azsdk_vcpkg_install]: https://github.com/Azure/azure-sdk-for-cpp#download--install-the-sdk
|
||||
[azure_sdk_for_cpp_contributing]: https://github.com/Azure/azure-sdk-for-cpp/blob/main/CONTRIBUTING.md
|
||||
[azure_sdk_for_cpp_contributing_developer_guide]: https://github.com/Azure/azure-sdk-for-cpp/blob/main/CONTRIBUTING.md#developer-guide
|
||||
[azure_sdk_for_cpp_contributing_pull_requests]: https://github.com/Azure/azure-sdk-for-cpp/blob/main/CONTRIBUTING.md#pull-requests
|
||||
[azure_sdk_cpp_development_guidelines]: https://azure.github.io/azure-sdk/cpp_introduction.html
|
||||
[azure_cli]: https://docs.microsoft.com/cli/azure
|
||||
[azure_pattern_circuit_breaker]: https://docs.microsoft.com/azure/architecture/patterns/circuit-breaker
|
||||
[azure_pattern_retry]: https://docs.microsoft.com/azure/architecture/patterns/retry
|
||||
[azure_portal]: https://portal.azure.com
|
||||
[azure_sub]: https://azure.microsoft.com/free/
|
||||
[c_compiler]: https://visualstudio.microsoft.com/vs/features/cplusplus/
|
||||
[cloud_shell]: https://docs.microsoft.com/azure/cloud-shell/overview
|
||||
[cloud_shell_bash]: https://shell.azure.com/bash
|
||||
|
||||

|
||||
23
sdk/core/azure-core-tracing-opentelemetry/cgmanifest.json
Normal file
23
sdk/core/azure-core-tracing-opentelemetry/cgmanifest.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"Registrations": [
|
||||
{
|
||||
"Component": {
|
||||
"Type": "git",
|
||||
"git": {
|
||||
"RepositoryUrl": "https://github.com/open-telemetry/opentelemetry-cpp",
|
||||
"CommitHash": "da2911cf4458c7068f967c17c65d07eeba449a08"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Component": {
|
||||
"Type": "git",
|
||||
"git": {
|
||||
"RepositoryUrl": "https://github.com/google/googletest",
|
||||
"CommitHash": "703bd9caab50b139428cea1aaff9974ebee5742e"
|
||||
}
|
||||
},
|
||||
"DevelopmentDependency": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief DLL export macro.
|
||||
*/
|
||||
|
||||
// For explanation, see the comment in azure/core/dll_import_export.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @def AZ_CORE_TRACING_OPENTELEMETRY_DLLEXPORT
|
||||
* @brief Applies DLL export attribute, when applicable.
|
||||
* @note See https://docs.microsoft.com/cpp/cpp/dllexport-dllimport?view=msvc-160.
|
||||
*/
|
||||
|
||||
#if defined(AZ_CORE_TRACING_OPENTELEMETRY_DLL) \
|
||||
|| (0 /*@AZ_CORE_TRACING_OPENTELEMETRY_DLL_INSTALLED_AS_PACKAGE@*/)
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_BUILT_AS_DLL 1
|
||||
#else
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_BUILT_AS_DLL 0
|
||||
#endif
|
||||
|
||||
#if AZ_CORE_TRACING_OPENTELEMETRY_BUILT_AS_DLL
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(AZ_CORE_TRACING_OPENTELEMETRY_BEING_BUILT)
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_DLLEXPORT __declspec(dllexport)
|
||||
#else // !defined(AZ_CORE_TRACING_OPENTELEMETRY_BEING_BUILT)
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_DLLEXPORT __declspec(dllimport)
|
||||
#endif // AZ_CORE_TRACING_OPENTELEMETRY_BEING_BUILT
|
||||
#else // !defined(_MSC_VER)
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_DLLEXPORT
|
||||
#endif // _MSC_VER
|
||||
#else // !AZ_CORE_TRACING_OPENTELEMETRY_BUILT_AS_DLL
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_DLLEXPORT
|
||||
#endif // AZ_CORE_TRACING_OPENTELEMETRY_BUILT_AS_DLL
|
||||
|
||||
#undef AZ_CORE_TRACING_OPENTELEMETRY_BUILT_AS_DLL
|
||||
|
||||
/**
|
||||
* @brief Azure SDK abstractions.
|
||||
*
|
||||
*/
|
||||
namespace Azure {
|
||||
/**
|
||||
* @brief Abstractions commonly used by Azure SDK client libraries.
|
||||
*
|
||||
*/
|
||||
namespace Core {
|
||||
/** @brief Azure Tracing Abstractions
|
||||
*/
|
||||
namespace Tracing {
|
||||
/** @brief OpenTelemetry Tracing Abstractions
|
||||
*/
|
||||
namespace OpenTelemetry {
|
||||
}
|
||||
} // namespace Tracing
|
||||
} // namespace Core
|
||||
} // namespace Azure
|
||||
@ -0,0 +1,177 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/tracing/tracing.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// The OpenTelemetry headers generate a couple of warnings on MSVC in the OTel 1.2 package, suppress
|
||||
// the warnings across the includes.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
#include <opentelemetry/common/kv_properties.h>
|
||||
#include <opentelemetry/trace/provider.h>
|
||||
#include <opentelemetry/trace/span.h>
|
||||
#include <opentelemetry/trace/tracer.h>
|
||||
#include <opentelemetry/trace/tracer_provider.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace Azure { namespace Core { namespace Tracing { namespace OpenTelemetry {
|
||||
|
||||
namespace _detail {
|
||||
class OpenTelemetryAttributeSet final : public Azure::Core::Tracing::_internal::AttributeSet,
|
||||
public opentelemetry::common::KeyValueIterable {
|
||||
std::map<std::string, opentelemetry::common::AttributeValue> m_propertySet;
|
||||
|
||||
template <typename T> void AddAttributeToSet(std::string const& attributeName, T value)
|
||||
{
|
||||
m_propertySet.emplace(
|
||||
std::make_pair(attributeName, opentelemetry::common::AttributeValue(value)));
|
||||
}
|
||||
|
||||
public:
|
||||
void AddAttribute(std::string const& attributeName, int32_t value) override
|
||||
{
|
||||
AddAttributeToSet(attributeName, value);
|
||||
}
|
||||
|
||||
void AddAttribute(std::string const& attributeName, int64_t value) override
|
||||
{
|
||||
AddAttributeToSet(attributeName, value);
|
||||
}
|
||||
|
||||
void AddAttribute(std::string const& attributeName, uint64_t value) override
|
||||
{
|
||||
AddAttributeToSet(attributeName, value);
|
||||
}
|
||||
void AddAttribute(std::string const& attributeName, double value) override
|
||||
{
|
||||
AddAttributeToSet(attributeName, value);
|
||||
}
|
||||
|
||||
void AddAttribute(std::string const& attributeName, std::string const& value) override
|
||||
{
|
||||
AddAttributeToSet<std::string const&>(attributeName, value);
|
||||
}
|
||||
void AddAttribute(std::string const& attributeName, const char* value) override
|
||||
{
|
||||
AddAttributeToSet(attributeName, value);
|
||||
}
|
||||
|
||||
void AddAttribute(std::string const& attributeName, bool value) override
|
||||
{
|
||||
AddAttributeToSet(attributeName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over key-value pairs
|
||||
* @param callback a callback to invoke for each key-value. If the callback returns false,
|
||||
* the iteration is aborted.
|
||||
* @return true if every key-value pair was iterated over
|
||||
*/
|
||||
bool ForEachKeyValue(
|
||||
opentelemetry::nostd::function_ref<
|
||||
bool(opentelemetry::nostd::string_view, opentelemetry::common::AttributeValue)>
|
||||
callback) const noexcept override
|
||||
{
|
||||
for (auto& value : m_propertySet)
|
||||
{
|
||||
if (!callback(value.first, value.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of key-value pairs
|
||||
*/
|
||||
size_t size() const noexcept override { return m_propertySet.size(); }
|
||||
|
||||
~OpenTelemetryAttributeSet() {}
|
||||
};
|
||||
/**
|
||||
* @brief Span - represents a span in tracing.
|
||||
*/
|
||||
class OpenTelemetrySpan final : public Azure::Core::Tracing::_internal::Span {
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> m_span;
|
||||
|
||||
public:
|
||||
OpenTelemetrySpan(opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span);
|
||||
|
||||
~OpenTelemetrySpan();
|
||||
|
||||
/**
|
||||
* @brief Signals that the span has now ended.
|
||||
*/
|
||||
virtual void End(Azure::Nullable<Azure::DateTime> endTime) override;
|
||||
|
||||
virtual void AddAttributes(
|
||||
Azure::Core::Tracing::_internal::AttributeSet const& attributeToAdd) override;
|
||||
|
||||
/**
|
||||
* Add an Event to the span. An event is identified by a name and an optional set of
|
||||
* attributes associated with the event.
|
||||
*/
|
||||
virtual void AddEvent(
|
||||
std::string const& eventName,
|
||||
Azure::Core::Tracing::_internal::AttributeSet const& eventAttributes) override;
|
||||
virtual void AddEvent(std::string const& eventName) override;
|
||||
virtual void AddEvent(std::exception const& exception) override;
|
||||
|
||||
virtual void SetStatus(
|
||||
Azure::Core::Tracing::_internal::SpanStatus const& status,
|
||||
std::string const& statusMessage) override;
|
||||
|
||||
opentelemetry::trace::SpanContext GetContext() { return m_span->GetContext(); }
|
||||
};
|
||||
|
||||
class OpenTelemetryTracer final : public Azure::Core::Tracing::_internal::Tracer {
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> m_tracer;
|
||||
|
||||
public:
|
||||
OpenTelemetryTracer(opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> tracer);
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Span> CreateSpan(
|
||||
std::string const& spanName,
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions const& options) const override;
|
||||
|
||||
std::unique_ptr<Azure::Core::Tracing::_internal::AttributeSet> CreateAttributeSet()
|
||||
const override;
|
||||
};
|
||||
} // namespace _detail
|
||||
|
||||
/**
|
||||
* @brief Trace Provider - factory for creating Tracer objects.
|
||||
*
|
||||
* An OpenTelemetryProvider object wraps an opentelemetry-cpp TracerProvider object
|
||||
* and provides an abstraction of the opentelemetry APIs which can be consumed by Azure Core and
|
||||
* other Azure services.
|
||||
*
|
||||
*/
|
||||
class OpenTelemetryProvider final : public Azure::Core::Tracing::TracerProvider {
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> m_tracerProvider;
|
||||
|
||||
public:
|
||||
OpenTelemetryProvider(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> tracerProvider);
|
||||
OpenTelemetryProvider();
|
||||
|
||||
/**
|
||||
* @brief Create a Tracer object
|
||||
*
|
||||
* @param name Name of the tracer object, typically the name of the Service client
|
||||
* (Azure.Storage.Blobs, for example)
|
||||
* @param version Version of the service client.
|
||||
* @return std::shared_ptr<Azure::Core::Tracing::Tracer>
|
||||
*/
|
||||
virtual std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> CreateTracer(
|
||||
std::string const& name,
|
||||
std::string const& version = "") const override;
|
||||
};
|
||||
}}}} // namespace Azure::Core::Tracing::OpenTelemetry
|
||||
@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Run-time type info enable or disable.
|
||||
*
|
||||
* @details When RTTI is enabled, defines a macro `AZ_CORE_RTTI`. When
|
||||
* the macro is not defined, RTTI is disabled.
|
||||
*
|
||||
* @details Each library has this header file. These headers are being configured by
|
||||
* `az_rtti_setup()` CMake macro. CMake install will patch this file during installation, depending
|
||||
* on the build flags.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @def AZ_CORE_TRACING_OPENTELEMETRY_RTTI
|
||||
* @brief A macro indicating whether the code is built with RTTI or not.
|
||||
*
|
||||
* @details `AZ_RTTI` could be defined while building the Azure SDK with CMake, however, after
|
||||
* the build is completed, that information is not preserved for the code that consumes Azure SDK
|
||||
* headers, unless the code that consumes the SDK is the part of the same build process. To address
|
||||
* this issue, CMake install would patch the header it places in the installation directory, so that
|
||||
* condition:
|
||||
* `#if defined(AZ_RTTI) || (0)`
|
||||
* becomes, effectively,
|
||||
* `#if defined(AZ_RTTI) || (0 + 1)`
|
||||
* when the library was built with RTTI support, and will make no changes to the
|
||||
* condition when it was not.
|
||||
*/
|
||||
|
||||
#if defined(AZ_RTTI) || (0 /*@AZ_CORE_TRACING_OPENTELEMETRY_RTTI@*/)
|
||||
#define AZ_CORE_TRACING_OPENTELEMETRY_RTTI
|
||||
#endif
|
||||
189
sdk/core/azure-core-tracing-opentelemetry/src/opentelemetry.cpp
Normal file
189
sdk/core/azure-core-tracing-opentelemetry/src/opentelemetry.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
|
||||
#include "azure/core/tracing/opentelemetry/opentelemetry.hpp"
|
||||
#include <azure/core/nullable.hpp>
|
||||
#include <azure/core/tracing/tracing.hpp>
|
||||
#include <memory>
|
||||
#if defined(_MSC_VER)
|
||||
// The OpenTelemetry headers generate a couple of warnings on MSVC in the OTel 1.2 package, suppress
|
||||
// the warnings across the includes.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
#include <opentelemetry/trace/provider.h>
|
||||
#include <opentelemetry/trace/tracer_provider.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
namespace Azure { namespace Core { namespace Tracing { namespace OpenTelemetry {
|
||||
using namespace Azure::Core::Tracing::_internal;
|
||||
|
||||
OpenTelemetryProvider::OpenTelemetryProvider(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider> tracerProvider)
|
||||
: m_tracerProvider(tracerProvider)
|
||||
{
|
||||
}
|
||||
|
||||
OpenTelemetryProvider::OpenTelemetryProvider()
|
||||
: m_tracerProvider(opentelemetry::trace::Provider::GetTracerProvider())
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> OpenTelemetryProvider::CreateTracer(
|
||||
std::string const& name,
|
||||
std::string const& version) const
|
||||
{
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> returnTracer(
|
||||
m_tracerProvider->GetTracer(name, version));
|
||||
return std::make_shared<Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryTracer>(
|
||||
returnTracer);
|
||||
}
|
||||
namespace _detail {
|
||||
std::unique_ptr<Azure::Core::Tracing::_internal::AttributeSet>
|
||||
OpenTelemetryTracer::CreateAttributeSet() const
|
||||
{
|
||||
return std::make_unique<OpenTelemetryAttributeSet>();
|
||||
}
|
||||
|
||||
OpenTelemetryTracer::OpenTelemetryTracer(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer> tracer)
|
||||
: m_tracer(tracer)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Span> OpenTelemetryTracer::CreateSpan(
|
||||
std::string const& spanName,
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions const& options = {}) const
|
||||
{
|
||||
opentelemetry::trace::StartSpanOptions spanOptions;
|
||||
spanOptions.kind = opentelemetry::trace::SpanKind::kInternal;
|
||||
if (options.Kind == Azure::Core::Tracing::_internal::SpanKind::Client)
|
||||
{
|
||||
spanOptions.kind = opentelemetry::trace::SpanKind::kClient;
|
||||
}
|
||||
else if (options.Kind == SpanKind::Consumer)
|
||||
{
|
||||
spanOptions.kind = opentelemetry::trace::SpanKind::kConsumer;
|
||||
}
|
||||
else if (options.Kind == SpanKind::Producer)
|
||||
{
|
||||
spanOptions.kind = opentelemetry::trace::SpanKind::kProducer;
|
||||
}
|
||||
else if (options.Kind == SpanKind::Server)
|
||||
{
|
||||
spanOptions.kind = opentelemetry::trace::SpanKind::kServer;
|
||||
}
|
||||
else if (options.Kind == SpanKind::Internal)
|
||||
{
|
||||
spanOptions.kind = opentelemetry::trace::SpanKind::kInternal;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown SpanOptions Kind: " + options.Kind.ToString());
|
||||
}
|
||||
|
||||
if (options.ParentSpan)
|
||||
{
|
||||
spanOptions.parent
|
||||
= static_cast<OpenTelemetrySpan*>(options.ParentSpan.get())->GetContext();
|
||||
}
|
||||
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> newSpan;
|
||||
if (options.Attributes)
|
||||
{
|
||||
// Note: We make a huge assumption here: That if you're calling into the OpenTelemetry
|
||||
// version of Azure::Core::Tracing, the Attributes passed in will be an
|
||||
// OpenTelemetryAttributeSet
|
||||
OpenTelemetryAttributeSet* attributes
|
||||
= static_cast<OpenTelemetryAttributeSet*>(options.Attributes.get());
|
||||
newSpan = m_tracer->StartSpan(spanName, *attributes, spanOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
newSpan = m_tracer->StartSpan(spanName, spanOptions);
|
||||
}
|
||||
|
||||
return std::make_shared<Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetrySpan>(
|
||||
newSpan);
|
||||
}
|
||||
|
||||
OpenTelemetrySpan::~OpenTelemetrySpan() {}
|
||||
|
||||
OpenTelemetrySpan::OpenTelemetrySpan(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span)
|
||||
: m_span(span)
|
||||
{
|
||||
}
|
||||
|
||||
void OpenTelemetrySpan::End(Azure::Nullable<Azure::DateTime> endTime)
|
||||
{
|
||||
opentelemetry::trace::EndSpanOptions options;
|
||||
if (endTime)
|
||||
{
|
||||
options.end_steady_time = opentelemetry::common::SteadyTimestamp(
|
||||
std::chrono::steady_clock::time_point(endTime.Value().time_since_epoch()));
|
||||
}
|
||||
m_span->End(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add the set of attributes provided to the current span.
|
||||
*/
|
||||
void OpenTelemetrySpan::AddAttributes(AttributeSet const& attributesToAdd)
|
||||
{
|
||||
// Note: We make a huge assumption here: That if you're calling into the OpenTelemetry
|
||||
// version of Azure::Core::Tracing, the Attributes passed in will be an
|
||||
// OpenTelemetryAttributeSet
|
||||
OpenTelemetryAttributeSet const& attributes
|
||||
= static_cast<OpenTelemetryAttributeSet const&>(attributesToAdd);
|
||||
attributes.ForEachKeyValue(
|
||||
[this](
|
||||
opentelemetry::nostd::string_view name, opentelemetry::common::AttributeValue value) {
|
||||
m_span->SetAttribute(name, value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an Event to the span. An event is identified by a name and an optional set of
|
||||
* attributes associated with the event.
|
||||
*/
|
||||
void OpenTelemetrySpan::AddEvent(
|
||||
std::string const& eventName,
|
||||
AttributeSet const& eventAttributes)
|
||||
{
|
||||
OpenTelemetryAttributeSet const& attributes
|
||||
= static_cast<OpenTelemetryAttributeSet const&>(eventAttributes);
|
||||
|
||||
m_span->AddEvent(eventName, attributes);
|
||||
}
|
||||
|
||||
void OpenTelemetrySpan::AddEvent(std::string const& eventName) { m_span->AddEvent(eventName); }
|
||||
|
||||
void OpenTelemetrySpan::AddEvent(std::exception const& ex) { m_span->AddEvent(ex.what()); }
|
||||
|
||||
void OpenTelemetrySpan::SetStatus(SpanStatus const& status, std::string const& statusMessage)
|
||||
{
|
||||
opentelemetry::trace::StatusCode statusCode = opentelemetry::trace::StatusCode::kUnset;
|
||||
if (status == SpanStatus::Error)
|
||||
{
|
||||
statusCode = opentelemetry::trace::StatusCode::kError;
|
||||
}
|
||||
else if (status == SpanStatus::Ok)
|
||||
{
|
||||
statusCode = opentelemetry::trace::StatusCode::kOk;
|
||||
}
|
||||
else if (status == SpanStatus::Unset)
|
||||
{
|
||||
statusCode = opentelemetry::trace::StatusCode::kUnset;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unknown status code: " + status.ToString());
|
||||
}
|
||||
|
||||
m_span->SetStatus(statusCode, statusMessage);
|
||||
}
|
||||
|
||||
} // namespace _detail
|
||||
}}}} // namespace Azure::Core::Tracing::OpenTelemetry
|
||||
@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Provides version information.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define AZURE_CORE_OPENTELEMETRY_VERSION_MAJOR 1
|
||||
#define AZURE_CORE_OPENTELEMETRY_VERSION_MINOR 0
|
||||
#define AZURE_CORE_OPENTELEMETRY_VERSION_PATCH 0
|
||||
#define AZURE_CORE_OPENTELEMETRY_VERSION_PRERELEASE "beta.1"
|
||||
|
||||
#define AZURE_CORE_OPENTELEMETRY_VERSION_ITOA_HELPER(i) #i
|
||||
#define AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(i) AZURE_CORE_OPENTELEMETRY_VERSION_ITOA_HELPER(i)
|
||||
|
||||
namespace Azure { namespace Core { namespace OpenTelemetry { namespace _detail {
|
||||
/**
|
||||
* @brief Provides version information.
|
||||
*
|
||||
*/
|
||||
class PackageVersion final {
|
||||
public:
|
||||
/// Major numeric identifier.
|
||||
static constexpr int32_t Major = AZURE_CORE_OPENTELEMETRY_VERSION_MAJOR;
|
||||
|
||||
/// Minor numeric identifier.
|
||||
static constexpr int32_t Minor = AZURE_CORE_OPENTELEMETRY_VERSION_MINOR;
|
||||
|
||||
/// Patch numeric identifier.
|
||||
static constexpr int32_t Patch = AZURE_CORE_OPENTELEMETRY_VERSION_PATCH;
|
||||
|
||||
/// Indicates whether the SDK is in a pre-release state.
|
||||
static constexpr bool IsPreRelease
|
||||
= sizeof(AZURE_CORE_OPENTELEMETRY_VERSION_PRERELEASE) != sizeof("");
|
||||
|
||||
/**
|
||||
* @brief The version in string format used for telemetry following the `semver.org` standard
|
||||
* (https://semver.org).
|
||||
*/
|
||||
static constexpr const char* ToString()
|
||||
{
|
||||
return IsPreRelease
|
||||
? AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(AZURE_CORE_OPENTELEMETRY_VERSION_MAJOR) "." AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(
|
||||
AZURE_CORE_OPENTELEMETRY_VERSION_MINOR) "." AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(AZURE_CORE_OPENTELEMETRY_VERSION_PATCH) "-" AZURE_CORE_OPENTELEMETRY_VERSION_PRERELEASE
|
||||
: AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(AZURE_CORE_OPENTELEMETRY_VERSION_MAJOR) "." AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(
|
||||
AZURE_CORE_OPENTELEMETRY_VERSION_MINOR) "." AZURE_CORE_OPENTELEMETRY_VERSION_ITOA(AZURE_CORE_OPENTELEMETRY_VERSION_PATCH);
|
||||
}
|
||||
};
|
||||
}}}} // namespace Azure::Core::OpenTelemetry::_detail
|
||||
|
||||
#undef AZURE_CORE_OPENTELEMETRY_VERSION_ITOA_HELPER
|
||||
#undef AZURE_CORE_OPENTELEMETRY_VERSION_ITOA
|
||||
#undef AZURE_CORE_OPENTELEMETRY_VERSION_MAJOR
|
||||
#undef AZURE_CORE_OPENTELEMETRY_VERSION_MINOR
|
||||
#undef AZURE_CORE_OPENTELEMETRY_VERSION_PATCH
|
||||
#undef AZURE_CORE_OPENTELEMETRY_VERSION_PRERELEASE
|
||||
@ -0,0 +1,59 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
cmake_minimum_required (VERSION 3.13)
|
||||
|
||||
set(azure-core-tracing-opentelemetry-test)
|
||||
|
||||
add_compile_definitions(AZURE_TEST_DATA_PATH="${CMAKE_BINARY_DIR}")
|
||||
|
||||
add_compile_definitions(AZURE_TEST_RECORDING_DIR="${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
project (azure-core-tracing-opentelemetry-test LANGUAGES CXX)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
include(GoogleTest)
|
||||
|
||||
add_executable (
|
||||
azure-core-tracing-opentelemetry-test
|
||||
azure_core_otel_test.cpp
|
||||
azure_core_test.cpp service_support_test.cpp)
|
||||
|
||||
if (MSVC)
|
||||
# Disable warnings:
|
||||
# - C26495: Variable
|
||||
# - 'testing::internal::Mutex::critical_section_'
|
||||
# - 'testing::internal::Mutex::critical_section_init_phase_'
|
||||
# - 'testing::internal::Mutex::owner_thread_id_'
|
||||
# - 'testing::internal::Mutex::type_'
|
||||
# is uninitialized. Always initialize member variables (type.6).
|
||||
# - C26812: The enum type
|
||||
# - 'testing::internal::Mutex::StaticConstructorSelector'
|
||||
# - 'testing::TestPartResult::Type'
|
||||
# is unscoped. Prefer 'enum class' over 'enum' (Enum.3)
|
||||
# - C6326: Google comparisons
|
||||
target_compile_options(azure-core-test PUBLIC /wd26495 /wd26812 /wd6326 /wd28204 /wd28020 /wd6330 /wd4389)
|
||||
endif()
|
||||
|
||||
# Adding private headers from CORE to the tests so we can test the private APIs with no relative paths include.
|
||||
target_include_directories (azure-core-tracing-opentelemetry-test PRIVATE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../src>)
|
||||
|
||||
target_link_libraries(azure-core-tracing-opentelemetry-test PRIVATE azure-core-tracing-opentelemetry
|
||||
azure-core
|
||||
azure-identity
|
||||
opentelemetry-cpp::ostream_span_exporter
|
||||
opentelemetry-cpp::in_memory_span_exporter
|
||||
opentelemetry-cpp::sdk
|
||||
azure-core-test-fw
|
||||
gtest_main)
|
||||
|
||||
create_per_service_target_build(core azure-core-tracing-opentelemetry-test)
|
||||
create_map_file(azure-core-tracing-opentelemetry-test azure-core-tracing-opentelemetry-test.map)
|
||||
|
||||
# gtest_discover_tests will scan the test from azure-core-opentelemetry-test and call add_test
|
||||
# for each test to ctest. This enables `ctest -r` to run specific tests directly.
|
||||
gtest_discover_tests(azure-core-tracing-opentelemetry-test
|
||||
TEST_PREFIX azure-core-opentelemetry.
|
||||
NO_PRETTY_TYPES
|
||||
NO_PRETTY_VALUES)
|
||||
@ -0,0 +1,692 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#define USE_MEMORY_EXPORTER 1
|
||||
#include "azure/core/tracing/opentelemetry/opentelemetry.hpp"
|
||||
#include <azure/core/test/test_base.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// The OpenTelemetry headers generate a couple of warnings on MSVC in the OTel 1.2 package, suppress
|
||||
// the warnings across the includes.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
#include <opentelemetry/exporters/memory/in_memory_span_data.h>
|
||||
#include <opentelemetry/exporters/memory/in_memory_span_exporter.h>
|
||||
#include <opentelemetry/exporters/ostream/span_exporter.h>
|
||||
#include <opentelemetry/sdk/common/global_log_handler.h>
|
||||
#include <opentelemetry/sdk/trace/exporter.h>
|
||||
#include <opentelemetry/sdk/trace/processor.h>
|
||||
#include <opentelemetry/sdk/trace/simple_processor.h>
|
||||
#include <opentelemetry/sdk/trace/tracer_provider.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class OpenTelemetryTests : public Azure::Core::Test::TestBase {
|
||||
private:
|
||||
class CustomLogHandler : public opentelemetry::sdk::common::internal_log::LogHandler {
|
||||
void Handle(
|
||||
opentelemetry::sdk::common::internal_log::LogLevel,
|
||||
const char* file,
|
||||
int line,
|
||||
const char* msg,
|
||||
const opentelemetry::sdk::common::AttributeMap& attributes) noexcept override
|
||||
{
|
||||
GTEST_LOG_(INFO) << "File: " << std::string(file) << " (" << line << "): " << std::string(msg)
|
||||
<< std::endl;
|
||||
if (!attributes.empty())
|
||||
{
|
||||
for (auto& attribute : attributes)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "Attribute " << attribute.first << ": ";
|
||||
switch (attribute.second.index())
|
||||
{
|
||||
case opentelemetry::sdk::common::kTypeBool:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<bool>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeInt:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<int>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeUInt:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<uint32_t>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeInt64:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<int64_t>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeDouble:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<double>(attribute.second);
|
||||
break;
|
||||
|
||||
case opentelemetry::sdk::common::kTypeString:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<std::string>(attribute.second);
|
||||
break;
|
||||
|
||||
case opentelemetry::sdk::common::kTypeSpanBool:
|
||||
case opentelemetry::sdk::common::kTypeSpanInt:
|
||||
case opentelemetry::sdk::common::kTypeSpanUInt:
|
||||
case opentelemetry::sdk::common::kTypeSpanInt64:
|
||||
case opentelemetry::sdk::common::kTypeSpanDouble:
|
||||
case opentelemetry::sdk::common::kTypeSpanString:
|
||||
case opentelemetry::sdk::common::kTypeUInt64:
|
||||
case opentelemetry::sdk::common::kTypeSpanUInt64:
|
||||
case opentelemetry::sdk::common::kTypeSpanByte:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<bool>(attribute.second);
|
||||
GTEST_LOG_(INFO) << "SPAN";
|
||||
break;
|
||||
}
|
||||
GTEST_LOG_(INFO) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
std::shared_ptr<opentelemetry::exporter::memory::InMemorySpanData> m_spanData;
|
||||
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>
|
||||
CreateOpenTelemetryProvider()
|
||||
{
|
||||
#if USE_MEMORY_EXPORTER
|
||||
auto exporter = std::make_unique<opentelemetry::exporter::memory::InMemorySpanExporter>();
|
||||
m_spanData = exporter->GetData();
|
||||
#else
|
||||
// logging exporter
|
||||
auto exporter = std::make_unique<opentelemetry::exporter::trace::OStreamSpanExporter>();
|
||||
#endif
|
||||
|
||||
// simple processor
|
||||
auto simple_processor = std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor>(
|
||||
new opentelemetry::sdk::trace::SimpleSpanProcessor(std::move(exporter)));
|
||||
|
||||
auto always_on_sampler = std::unique_ptr<opentelemetry::sdk::trace::AlwaysOnSampler>(
|
||||
new opentelemetry::sdk::trace::AlwaysOnSampler);
|
||||
|
||||
auto resource_attributes = opentelemetry::sdk::resource::ResourceAttributes{
|
||||
{"service.name", "telemetryTest"}, {"service.instance.id", "instance-1"}};
|
||||
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);
|
||||
// Create using SDK configurations as parameter
|
||||
return opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
|
||||
new opentelemetry::sdk::trace::TracerProvider(
|
||||
std::move(simple_processor), resource, std::move(always_on_sampler)));
|
||||
}
|
||||
|
||||
// Create
|
||||
virtual void SetUp() override
|
||||
{
|
||||
Azure::Core::Test::TestBase::SetUpTestBase(AZURE_TEST_RECORDING_DIR);
|
||||
|
||||
opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::sdk::common::internal_log::LogHandler>(
|
||||
new CustomLogHandler()));
|
||||
opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(
|
||||
opentelemetry::sdk::common::internal_log::LogLevel::Debug);
|
||||
}
|
||||
|
||||
virtual void TearDown() override
|
||||
{
|
||||
// Make sure you call the base classes TearDown method to ensure recordings are made.
|
||||
TestBase::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(OpenTelemetryTests, Basic)
|
||||
{
|
||||
// Simple create an OTel telemetry provider as a static member variable.
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider provider;
|
||||
auto tracer = provider.CreateTracer("TracerName", "1.0");
|
||||
EXPECT_TRUE(tracer);
|
||||
}
|
||||
|
||||
// Create a shared provider using the tracing abstract classes.
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> provider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>();
|
||||
auto tracer = provider->CreateTracer("TracerName", "1.0");
|
||||
EXPECT_TRUE(tracer);
|
||||
}
|
||||
|
||||
// Create a provider using the OpenTelemetry default provider (this will be a "noop" provider).
|
||||
{
|
||||
auto rawTracer(opentelemetry::trace::Provider::GetTracerProvider());
|
||||
|
||||
auto traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(rawTracer);
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
EXPECT_TRUE(tracer);
|
||||
}
|
||||
|
||||
// Create a provider using the OpenTelemetry reference provider (this will be a working provider
|
||||
// using the ostream logger).
|
||||
{
|
||||
auto otelProvider(CreateOpenTelemetryProvider());
|
||||
auto traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
otelProvider);
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, CreateSpanSimple)
|
||||
{
|
||||
// Simple create an OTel telemetry provider as a static member variable.
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider provider;
|
||||
auto tracer = provider.CreateTracer("TracerName", "1.0");
|
||||
EXPECT_TRUE(tracer);
|
||||
auto span = tracer->CreateSpan("My Span");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
|
||||
// Create a provider using the OpenTelemetry reference provider (this will be a working provider
|
||||
// using the ostream logger).
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
{
|
||||
auto span = tracer->CreateSpan("My Span2");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
// Make sure that the span we collected looks right.
|
||||
EXPECT_EQ("My Span2", spans[0]->GetName());
|
||||
EXPECT_EQ(opentelemetry::trace::StatusCode::kUnset, spans[0]->GetStatus());
|
||||
auto spanContext(spans[0]->GetSpanContext());
|
||||
EXPECT_TRUE(spanContext.IsValid());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, TestAttributeSet)
|
||||
{
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet attributeSet;
|
||||
}
|
||||
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet attributeSet;
|
||||
// Add a C style string.
|
||||
attributeSet.AddAttribute("String", "StringValue");
|
||||
|
||||
attributeSet.ForEachKeyValue(
|
||||
[](opentelemetry::nostd::string_view name, opentelemetry::common::AttributeValue value) {
|
||||
EXPECT_EQ(name, "String");
|
||||
EXPECT_EQ(0, strcmp("StringValue", opentelemetry::nostd::get<const char*>(value)));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet attributeSet;
|
||||
attributeSet.AddAttribute("boolTrue", true);
|
||||
attributeSet.AddAttribute("boolFalse", false);
|
||||
|
||||
attributeSet.ForEachKeyValue(
|
||||
[](opentelemetry::nostd::string_view name, opentelemetry::common::AttributeValue value) {
|
||||
if (name == "boolTrue")
|
||||
{
|
||||
EXPECT_TRUE(opentelemetry::nostd::get<bool>(value));
|
||||
}
|
||||
else if (name == "boolFalse")
|
||||
{
|
||||
EXPECT_FALSE(opentelemetry::nostd::get<bool>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet attributeSet;
|
||||
attributeSet.AddAttribute("int1", 1);
|
||||
attributeSet.AddAttribute("pi", 3.1415926);
|
||||
attributeSet.AddAttribute("int64", static_cast<int64_t>(151031ll));
|
||||
attributeSet.AddAttribute("uint64", static_cast<uint64_t>(1ull));
|
||||
attributeSet.AddAttribute("charstring", "char * string.");
|
||||
// Note that the attribute set doesn't take ownership of the input value, so we need to ensure
|
||||
// the lifetime of any std::string values put into the set.
|
||||
std::string stringValue("std::string.");
|
||||
attributeSet.AddAttribute("stdstring", stringValue);
|
||||
|
||||
attributeSet.ForEachKeyValue([](opentelemetry::nostd::string_view name,
|
||||
opentelemetry::common::AttributeValue value) {
|
||||
if (name == "int1")
|
||||
{
|
||||
EXPECT_EQ(1, opentelemetry::nostd::get<int32_t>(value));
|
||||
}
|
||||
else if (name == "pi")
|
||||
{
|
||||
EXPECT_EQ(3.1415926, opentelemetry::nostd::get<double>(value));
|
||||
}
|
||||
else if (name == "int64")
|
||||
{
|
||||
EXPECT_EQ(151031, opentelemetry::nostd::get<int64_t>(value));
|
||||
}
|
||||
else if (name == "uint64")
|
||||
{
|
||||
EXPECT_EQ(1, opentelemetry::nostd::get<uint64_t>(value));
|
||||
}
|
||||
else if (name == "charstring")
|
||||
{
|
||||
const char* cstrVal(opentelemetry::nostd::get<const char*>(value));
|
||||
EXPECT_EQ(0, strcmp(cstrVal, "char * string."));
|
||||
}
|
||||
else if (name == "stdstring")
|
||||
{
|
||||
EXPECT_EQ(
|
||||
"std::string.", opentelemetry::nostd::get<opentelemetry::nostd::string_view>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, CreateSpanWithOptions)
|
||||
{
|
||||
// Simple create an OTel telemetry provider as a static member variable.
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider provider;
|
||||
auto tracer = provider.CreateTracer("TracerName", "1.0");
|
||||
EXPECT_TRUE(tracer);
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
auto span = tracer->CreateSpan("My Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
|
||||
// Create a provider using the OpenTelemetry reference provider (this will be a working provider
|
||||
// using the ostream logger).
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind::Client;
|
||||
auto span = tracer->CreateSpan("Client Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind::Consumer;
|
||||
auto span = tracer->CreateSpan("Consumer Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind::Internal;
|
||||
auto span = tracer->CreateSpan("Internal Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind::Producer;
|
||||
auto span = tracer->CreateSpan("Producer Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind::Server;
|
||||
auto span = tracer->CreateSpan("Server Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind("Bogus");
|
||||
EXPECT_THROW(tracer->CreateSpan("Bogus Span", options), std::runtime_error);
|
||||
}
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(5ul, spans.size());
|
||||
// Make sure that the span we collected looks right.
|
||||
EXPECT_EQ("Client Span", spans[0]->GetName());
|
||||
EXPECT_EQ(opentelemetry::trace::SpanKind::kClient, spans[0]->GetSpanKind());
|
||||
EXPECT_EQ("Consumer Span", spans[1]->GetName());
|
||||
EXPECT_EQ(opentelemetry::trace::SpanKind::kConsumer, spans[1]->GetSpanKind());
|
||||
EXPECT_EQ("Internal Span", spans[2]->GetName());
|
||||
EXPECT_EQ(opentelemetry::trace::SpanKind::kInternal, spans[2]->GetSpanKind());
|
||||
EXPECT_EQ("Producer Span", spans[3]->GetName());
|
||||
EXPECT_EQ(opentelemetry::trace::SpanKind::kProducer, spans[3]->GetSpanKind());
|
||||
EXPECT_EQ("Server Span", spans[4]->GetName());
|
||||
EXPECT_EQ(opentelemetry::trace::SpanKind::kServer, spans[4]->GetSpanKind());
|
||||
}
|
||||
|
||||
{
|
||||
// Create a provider using the OpenTelemetry reference provider (this will be a working provider
|
||||
// using the ostream logger).
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Attributes = std::make_unique<
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet>();
|
||||
options.Attributes->AddAttribute("SimpleStringAttribute", "Simple String");
|
||||
options.Kind = Azure::Core::Tracing::_internal::SpanKind::Client;
|
||||
auto span = tracer->CreateSpan("Client Span", options);
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(static_cast<size_t>(1), spans.size());
|
||||
// Make sure that the span we collected looks right.
|
||||
EXPECT_EQ("Client Span", spans[0]->GetName());
|
||||
EXPECT_EQ(static_cast<size_t>(1), spans[0]->GetAttributes().size());
|
||||
EXPECT_NE(
|
||||
spans[0]->GetAttributes().end(),
|
||||
spans[0]->GetAttributes().find("SimpleStringAttribute"));
|
||||
EXPECT_EQ(
|
||||
"Simple String",
|
||||
opentelemetry::nostd::get<std::string>(
|
||||
spans[0]->GetAttributes().at("SimpleStringAttribute")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, NestSpans)
|
||||
{
|
||||
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("SpanOuter");
|
||||
EXPECT_TRUE(span);
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions so;
|
||||
so.ParentSpan = span;
|
||||
auto span2 = tracer->CreateSpan("SpanInner", so);
|
||||
so.ParentSpan = span2;
|
||||
auto span3 = tracer->CreateSpan("SpanInner2", so);
|
||||
// Span 3's parent is SpanOuter.
|
||||
so.ParentSpan = span;
|
||||
auto span4 = tracer->CreateSpan("SpanInner4", so);
|
||||
span2->End();
|
||||
|
||||
span->End();
|
||||
span4->End();
|
||||
span3->End();
|
||||
}
|
||||
{
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions so;
|
||||
so.ParentSpan = span;
|
||||
auto span5 = tracer->CreateSpan("SequentialInner", so);
|
||||
auto span6 = tracer->CreateSpan("SequentialInner2", so);
|
||||
span5->End();
|
||||
span6->End();
|
||||
}
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(6ul, spans.size());
|
||||
// Make sure that the span we collected looks right.
|
||||
// The spans are ordered in the order they called "End", since a span that hasn't ended cannot
|
||||
// be recorded.
|
||||
EXPECT_EQ("SpanInner", spans[0]->GetName());
|
||||
EXPECT_EQ("SpanOuter", spans[1]->GetName());
|
||||
EXPECT_EQ("SpanInner4", spans[2]->GetName());
|
||||
EXPECT_EQ("SpanInner2", spans[3]->GetName());
|
||||
EXPECT_EQ("SequentialInner", spans[4]->GetName());
|
||||
EXPECT_EQ("SequentialInner2", spans[5]->GetName());
|
||||
EXPECT_FALSE(spans[1]->GetParentSpanId().IsValid()); // Span 1 should be a root span.
|
||||
EXPECT_TRUE(spans[0]->GetParentSpanId().IsValid()); // Span 0 should not be a root span.
|
||||
EXPECT_TRUE(spans[2]->GetParentSpanId().IsValid()); // Span 2 should not be a root span.
|
||||
EXPECT_TRUE(spans[3]->GetParentSpanId().IsValid()); // Span 3 should not be a root span.
|
||||
EXPECT_TRUE(spans[4]->GetParentSpanId().IsValid()); // Span 4 should not be a root span.
|
||||
EXPECT_TRUE(spans[5]->GetParentSpanId().IsValid()); // Span 5 should not be a root span.
|
||||
|
||||
// SpanInner should have SpanOuter as a parent.
|
||||
EXPECT_EQ(spans[0]->GetParentSpanId(), spans[1]->GetSpanId());
|
||||
|
||||
// SpanInner2 should have SpanOuter as a parent.
|
||||
EXPECT_EQ(spans[3]->GetParentSpanId(), spans[0]->GetSpanId());
|
||||
|
||||
// SpanInner4 should have SpanInner2 as a parent.
|
||||
EXPECT_EQ(spans[2]->GetParentSpanId(), spans[1]->GetSpanId());
|
||||
|
||||
// SequentialInner should have SpanOuter as a parent.
|
||||
EXPECT_EQ(spans[4]->GetParentSpanId(), spans[1]->GetSpanId());
|
||||
|
||||
// SequentialInner2 should have SpanOuter as a parent.
|
||||
EXPECT_EQ(spans[5]->GetParentSpanId(), spans[1]->GetSpanId());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, SetStatus)
|
||||
{
|
||||
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("StatusSpan");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Error);
|
||||
span->SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Ok);
|
||||
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
EXPECT_EQ(opentelemetry::trace::StatusCode::kOk, spans[0]->GetStatus());
|
||||
}
|
||||
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("StatusSpan");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Error, "Something went wrong.");
|
||||
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
EXPECT_EQ(opentelemetry::trace::StatusCode::kError, spans[0]->GetStatus());
|
||||
EXPECT_EQ("Something went wrong.", spans[0]->GetDescription());
|
||||
}
|
||||
|
||||
// Set to Unset.
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("StatusSpan");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Unset);
|
||||
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
EXPECT_EQ(opentelemetry::trace::StatusCode::kUnset, spans[0]->GetStatus());
|
||||
}
|
||||
|
||||
// Not set.
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("StatusSpan");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
EXPECT_EQ(opentelemetry::trace::StatusCode::kUnset, spans[0]->GetStatus());
|
||||
}
|
||||
|
||||
// Invalid status.
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("StatusSpan");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
EXPECT_THROW(
|
||||
span->SetStatus(Azure::Core::Tracing::_internal::SpanStatus("Bogus")), std::runtime_error);
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(0ul, spans.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, AddSpanAttributes)
|
||||
{
|
||||
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("AttributeSpan");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet attributeSet;
|
||||
attributeSet.AddAttribute("int1", 1);
|
||||
attributeSet.AddAttribute("pi", 3.1415926);
|
||||
attributeSet.AddAttribute("int64", static_cast<int64_t>(151031ll));
|
||||
attributeSet.AddAttribute("uint64", static_cast<uint64_t>(1ull));
|
||||
attributeSet.AddAttribute("charstring", "char * string.");
|
||||
// Note that the attribute set doesn't take ownership of the input value, so we need to ensure
|
||||
// the lifetime of any std::string values put into the set.
|
||||
std::string stringValue("std::string.");
|
||||
attributeSet.AddAttribute("stdstring", stringValue);
|
||||
span->AddAttributes(attributeSet);
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
// Make sure that the span we collected looks right.
|
||||
EXPECT_EQ("AttributeSpan", spans[0]->GetName());
|
||||
EXPECT_EQ(6ul, spans[0]->GetAttributes().size());
|
||||
EXPECT_NE(spans[0]->GetAttributes().end(), spans[0]->GetAttributes().find("int1"));
|
||||
EXPECT_NE(spans[0]->GetAttributes().end(), spans[0]->GetAttributes().find("pi"));
|
||||
EXPECT_NE(spans[0]->GetAttributes().end(), spans[0]->GetAttributes().find("int64"));
|
||||
EXPECT_NE(spans[0]->GetAttributes().end(), spans[0]->GetAttributes().find("uint64"));
|
||||
EXPECT_NE(spans[0]->GetAttributes().end(), spans[0]->GetAttributes().find("charstring"));
|
||||
EXPECT_NE(spans[0]->GetAttributes().end(), spans[0]->GetAttributes().find("stdstring"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryTests, AddSpanEvents)
|
||||
{
|
||||
{
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> traceProvider
|
||||
= std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
CreateOpenTelemetryProvider());
|
||||
|
||||
auto tracer = traceProvider->CreateTracer("TracerName");
|
||||
auto span = tracer->CreateSpan("SpanWithEvents");
|
||||
EXPECT_TRUE(span);
|
||||
|
||||
span->AddEvent("String Event");
|
||||
span->AddEvent(std::runtime_error("Exception message"));
|
||||
|
||||
{
|
||||
Azure::Core::Tracing::OpenTelemetry::_detail::OpenTelemetryAttributeSet attributeSet;
|
||||
attributeSet.AddAttribute("int1", 1);
|
||||
attributeSet.AddAttribute("pi", 3.1415926);
|
||||
attributeSet.AddAttribute("int64", static_cast<int64_t>(151031ll));
|
||||
attributeSet.AddAttribute("uint64", static_cast<uint64_t>(1ull));
|
||||
attributeSet.AddAttribute("charstring", "char * string.");
|
||||
// Note that the attribute set doesn't take ownership of the input value, so we need to ensure
|
||||
// the lifetime of any std::string values put into the set.
|
||||
std::string stringValue("std::string.");
|
||||
attributeSet.AddAttribute("stdstring", stringValue);
|
||||
span->AddEvent("Event With Attributes", attributeSet);
|
||||
|
||||
span->End();
|
||||
|
||||
// Return the collected spans.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
EXPECT_EQ(3UL, spans[0]->GetEvents().size());
|
||||
|
||||
EXPECT_EQ("String Event", spans[0]->GetEvents()[0].GetName());
|
||||
EXPECT_EQ("Exception message", spans[0]->GetEvents()[1].GetName());
|
||||
EXPECT_EQ("Event With Attributes", spans[0]->GetEvents()[2].GetName());
|
||||
|
||||
const auto& attributes = spans[0]->GetEvents()[2].GetAttributes();
|
||||
|
||||
// Make sure that the span we collected looks right.
|
||||
EXPECT_EQ(6ul, attributes.size());
|
||||
EXPECT_NE(attributes.end(), attributes.find("int1"));
|
||||
EXPECT_NE(attributes.end(), attributes.find("pi"));
|
||||
EXPECT_NE(attributes.end(), attributes.find("int64"));
|
||||
EXPECT_NE(attributes.end(), attributes.find("uint64"));
|
||||
EXPECT_NE(attributes.end(), attributes.find("charstring"));
|
||||
EXPECT_NE(attributes.end(), attributes.find("stdstring"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if defined(AZ_PLATFORM_POSIX)
|
||||
// OpenSSL signals SIGPIPE when trying to clean an HTTPS closed connection.
|
||||
// End users need to decide if SIGPIPE should be ignored or not.
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
auto r = RUN_ALL_TESTS();
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -0,0 +1,601 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#define USE_MEMORY_EXPORTER 1
|
||||
#include "azure/core/internal/tracing/service_tracing.hpp"
|
||||
#include "azure/core/tracing/opentelemetry/opentelemetry.hpp"
|
||||
#include <azure/core/internal/json/json.hpp>
|
||||
#include <azure/core/test/test_base.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// The OpenTelemetry headers generate a couple of warnings on MSVC in the OTel 1.2 package, suppress
|
||||
// the warnings across the includes.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
#pragma warning(disable : 4244)
|
||||
#endif
|
||||
#include <opentelemetry/exporters/memory/in_memory_span_data.h>
|
||||
#include <opentelemetry/exporters/memory/in_memory_span_exporter.h>
|
||||
#include <opentelemetry/exporters/ostream/span_exporter.h>
|
||||
#include <opentelemetry/sdk/common/global_log_handler.h>
|
||||
#include <opentelemetry/sdk/trace/exporter.h>
|
||||
#include <opentelemetry/sdk/trace/processor.h>
|
||||
#include <opentelemetry/sdk/trace/simple_processor.h>
|
||||
#include <opentelemetry/sdk/trace/tracer_provider.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#include <chrono>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class CustomLogHandler : public opentelemetry::sdk::common::internal_log::LogHandler {
|
||||
void Handle(
|
||||
opentelemetry::sdk::common::internal_log::LogLevel,
|
||||
const char* file,
|
||||
int line,
|
||||
const char* msg,
|
||||
const opentelemetry::sdk::common::AttributeMap& attributes) noexcept override
|
||||
{
|
||||
GTEST_LOG_(INFO) << "File: " << std::string(file) << " (" << line << "): " << std::string(msg)
|
||||
<< std::endl;
|
||||
if (!attributes.empty())
|
||||
{
|
||||
for (auto& attribute : attributes)
|
||||
{
|
||||
GTEST_LOG_(INFO) << "Attribute " << attribute.first << ": ";
|
||||
switch (attribute.second.index())
|
||||
{
|
||||
case opentelemetry::sdk::common::kTypeBool:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<bool>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeInt:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<int>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeUInt:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<uint32_t>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeInt64:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<int64_t>(attribute.second);
|
||||
break;
|
||||
case opentelemetry::sdk::common::kTypeDouble:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<double>(attribute.second);
|
||||
break;
|
||||
|
||||
case opentelemetry::sdk::common::kTypeString:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<std::string>(attribute.second);
|
||||
break;
|
||||
|
||||
case opentelemetry::sdk::common::kTypeSpanBool:
|
||||
case opentelemetry::sdk::common::kTypeSpanInt:
|
||||
case opentelemetry::sdk::common::kTypeSpanUInt:
|
||||
case opentelemetry::sdk::common::kTypeSpanInt64:
|
||||
case opentelemetry::sdk::common::kTypeSpanDouble:
|
||||
case opentelemetry::sdk::common::kTypeSpanString:
|
||||
case opentelemetry::sdk::common::kTypeUInt64:
|
||||
case opentelemetry::sdk::common::kTypeSpanUInt64:
|
||||
case opentelemetry::sdk::common::kTypeSpanByte:
|
||||
GTEST_LOG_(INFO) << opentelemetry::nostd::get<bool>(attribute.second);
|
||||
GTEST_LOG_(INFO) << "SPAN";
|
||||
break;
|
||||
}
|
||||
GTEST_LOG_(INFO) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class OpenTelemetryServiceTests : public Azure::Core::Test::TestBase {
|
||||
private:
|
||||
protected:
|
||||
std::shared_ptr<opentelemetry::exporter::memory::InMemorySpanData> m_spanData;
|
||||
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>
|
||||
CreateOpenTelemetryProvider()
|
||||
{
|
||||
#if USE_MEMORY_EXPORTER
|
||||
auto exporter = std::make_unique<opentelemetry::exporter::memory::InMemorySpanExporter>();
|
||||
m_spanData = exporter->GetData();
|
||||
#else
|
||||
// logging exporter
|
||||
auto exporter = std::make_unique<opentelemetry::exporter::trace::OStreamSpanExporter>();
|
||||
#endif
|
||||
|
||||
// simple processor
|
||||
auto simple_processor = std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor>(
|
||||
new opentelemetry::sdk::trace::SimpleSpanProcessor(std::move(exporter)));
|
||||
|
||||
auto always_on_sampler = std::unique_ptr<opentelemetry::sdk::trace::AlwaysOnSampler>(
|
||||
new opentelemetry::sdk::trace::AlwaysOnSampler);
|
||||
|
||||
auto resource_attributes = opentelemetry::sdk::resource::ResourceAttributes{
|
||||
{"service.name", "telemetryTest"}, {"service.instance.id", "instance-1"}};
|
||||
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);
|
||||
// Create using SDK configurations as parameter
|
||||
return opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
|
||||
new opentelemetry::sdk::trace::TracerProvider(
|
||||
std::move(simple_processor), resource, std::move(always_on_sampler)));
|
||||
}
|
||||
|
||||
// Create
|
||||
virtual void SetUp() override
|
||||
{
|
||||
Azure::Core::Test::TestBase::SetUpTestBase(AZURE_TEST_RECORDING_DIR);
|
||||
|
||||
opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogHandler(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::sdk::common::internal_log::LogHandler>(
|
||||
new CustomLogHandler()));
|
||||
opentelemetry::sdk::common::internal_log::GlobalLogHandler::SetLogLevel(
|
||||
opentelemetry::sdk::common::internal_log::LogLevel::Debug);
|
||||
}
|
||||
|
||||
virtual void TearDown() override
|
||||
{
|
||||
// Make sure you call the base classes TearDown method to ensure recordings are made.
|
||||
TestBase::TearDown();
|
||||
}
|
||||
|
||||
bool VerifySpan(
|
||||
std::unique_ptr<opentelemetry::sdk::trace::SpanData> const& span,
|
||||
std::string const& expectedSpanContentsJson)
|
||||
{
|
||||
Azure::Core::Json::_internal::json expectedSpanContents(
|
||||
Azure::Core::Json::_internal::json::parse(expectedSpanContentsJson));
|
||||
EXPECT_EQ(expectedSpanContents["name"].get<std::string>(), span->GetName());
|
||||
if (expectedSpanContents.contains("statusCode"))
|
||||
{
|
||||
std::string expectedStatus = expectedSpanContents["statusCode"].get<std::string>();
|
||||
switch (span->GetStatus())
|
||||
{
|
||||
case opentelemetry::trace::StatusCode::kOk:
|
||||
EXPECT_EQ(expectedStatus, "ok");
|
||||
break;
|
||||
case opentelemetry::trace::StatusCode::kError:
|
||||
EXPECT_EQ(expectedStatus, "error");
|
||||
break;
|
||||
case opentelemetry::trace::StatusCode::kUnset:
|
||||
EXPECT_EQ(expectedStatus, "unset");
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown span status");
|
||||
}
|
||||
}
|
||||
if (expectedSpanContents.contains("kind"))
|
||||
{
|
||||
std::string expectedKind = expectedSpanContents["kind"].get<std::string>();
|
||||
switch (span->GetSpanKind())
|
||||
{
|
||||
case opentelemetry::trace::SpanKind::kClient:
|
||||
EXPECT_EQ(expectedKind, "internal");
|
||||
break;
|
||||
case opentelemetry::trace::SpanKind::kConsumer:
|
||||
EXPECT_EQ(expectedKind, "consumer");
|
||||
break;
|
||||
case opentelemetry::trace::SpanKind::kInternal:
|
||||
EXPECT_EQ(expectedKind, "internal");
|
||||
break;
|
||||
case opentelemetry::trace::SpanKind::kProducer:
|
||||
EXPECT_EQ(expectedKind, "producer");
|
||||
break;
|
||||
case opentelemetry::trace::SpanKind::kServer:
|
||||
EXPECT_EQ(expectedKind, "server");
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown span kind");
|
||||
}
|
||||
}
|
||||
if (expectedSpanContents.contains("attributes"))
|
||||
{
|
||||
auto& expectedAttributes = expectedSpanContents["attributes"];
|
||||
EXPECT_TRUE(expectedAttributes.is_object());
|
||||
auto attributes(span->GetAttributes());
|
||||
|
||||
EXPECT_EQ(expectedAttributes.size(), attributes.size());
|
||||
|
||||
for (const auto& foundAttribute : attributes)
|
||||
{
|
||||
EXPECT_TRUE(expectedAttributes.contains(foundAttribute.first));
|
||||
switch (foundAttribute.second.index())
|
||||
{
|
||||
case opentelemetry::common::kTypeBool: {
|
||||
|
||||
EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_boolean());
|
||||
auto actualVal = opentelemetry::nostd::get<bool>(foundAttribute.second);
|
||||
EXPECT_EQ(expectedAttributes[foundAttribute.first].get<bool>(), actualVal);
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::kTypeCString:
|
||||
case opentelemetry::common::kTypeString: {
|
||||
EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_string());
|
||||
const auto& actualVal = opentelemetry::nostd::get<std::string>(foundAttribute.second);
|
||||
EXPECT_EQ(expectedAttributes[foundAttribute.first].get<std::string>(), actualVal);
|
||||
break;
|
||||
}
|
||||
case opentelemetry::common::kTypeDouble: {
|
||||
|
||||
EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_number());
|
||||
auto actualVal = opentelemetry::nostd::get<double>(foundAttribute.second);
|
||||
EXPECT_EQ(expectedAttributes[foundAttribute.first].get<double>(), actualVal);
|
||||
break;
|
||||
}
|
||||
|
||||
case opentelemetry::common::kTypeInt:
|
||||
case opentelemetry::common::kTypeInt64:
|
||||
EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_number_integer());
|
||||
break;
|
||||
case opentelemetry::common::kTypeSpanBool:
|
||||
case opentelemetry::common::kTypeSpanByte:
|
||||
case opentelemetry::common::kTypeSpanDouble:
|
||||
case opentelemetry::common::kTypeSpanInt:
|
||||
case opentelemetry::common::kTypeSpanInt64:
|
||||
case opentelemetry::common::kTypeSpanString:
|
||||
case opentelemetry::common::kTypeSpanUInt:
|
||||
case opentelemetry::common::kTypeSpanUInt64:
|
||||
EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_array());
|
||||
throw std::runtime_error("Unsupported attribute kind");
|
||||
break;
|
||||
|
||||
case opentelemetry::common::kTypeUInt:
|
||||
case opentelemetry::common::kTypeUInt64:
|
||||
EXPECT_TRUE(expectedAttributes[foundAttribute.first].is_number_unsigned());
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown attribute kind");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// const auto& namespaceVal = opentelemetry::nostd::get<std::string>(azNamespace);
|
||||
}
|
||||
if (expectedSpanContents.contains("library"))
|
||||
{
|
||||
EXPECT_EQ(
|
||||
expectedSpanContents["library"]["name"].get<std::string>(),
|
||||
span->GetInstrumentationLibrary().GetName());
|
||||
EXPECT_EQ(
|
||||
expectedSpanContents["library"]["version"].get<std::string>(),
|
||||
span->GetInstrumentationLibrary().GetVersion());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(OpenTelemetryServiceTests, SimplestTest)
|
||||
{
|
||||
{
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace;
|
||||
}
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service-cpp", "1.0b2");
|
||||
}
|
||||
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service-cpp", "1.0b2");
|
||||
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", {});
|
||||
EXPECT_FALSE(contextAndSpan.first.IsCancelled());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryServiceTests, CreateWithExplicitProvider)
|
||||
{
|
||||
// Create a serviceTrace, set it and retrieve it via a Context object. This verifies that we can
|
||||
// round-trip telemetry providers through a Context (which allows us to hook this up to the
|
||||
// ApplicationContext later).
|
||||
//
|
||||
{
|
||||
auto tracerProvider(CreateOpenTelemetryProvider());
|
||||
auto provider(std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
tracerProvider));
|
||||
|
||||
Azure::Core::Context rootContext;
|
||||
rootContext.SetTracerProvider(provider);
|
||||
EXPECT_EQ(provider, rootContext.GetTracerProvider());
|
||||
}
|
||||
|
||||
{
|
||||
auto tracerProvider(CreateOpenTelemetryProvider());
|
||||
auto provider(std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
tracerProvider));
|
||||
|
||||
// Create a serviceTrace and span using a provider specified in the ClientOptions.
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
clientOptions.Telemetry.TracingProvider = provider;
|
||||
clientOptions.Telemetry.ApplicationId = "MyApplication";
|
||||
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service", "1.0beta-2");
|
||||
|
||||
Azure::Core::Context clientContext;
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", clientContext);
|
||||
EXPECT_FALSE(contextAndSpan.first.IsCancelled());
|
||||
}
|
||||
// Now let's verify what was logged via OpenTelemetry.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
VerifySpan(spans[0], R"(
|
||||
{
|
||||
"name": "My API",
|
||||
"attributes": {
|
||||
"az.namespace": "my-service"
|
||||
},
|
||||
"library": {
|
||||
"name": "my-service",
|
||||
"version": "1.0beta-2"
|
||||
}
|
||||
})");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryServiceTests, CreateWithImplicitProvider)
|
||||
{
|
||||
{
|
||||
auto tracerProvider(CreateOpenTelemetryProvider());
|
||||
auto provider(std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
tracerProvider));
|
||||
|
||||
Azure::Core::Context::ApplicationContext.SetTracerProvider(provider);
|
||||
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
clientOptions.Telemetry.ApplicationId = "MyApplication";
|
||||
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service", "1.0beta-2");
|
||||
|
||||
Azure::Core::Context clientContext;
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", clientContext);
|
||||
EXPECT_FALSE(contextAndSpan.first.IsCancelled());
|
||||
}
|
||||
|
||||
// Now let's verify what was logged via OpenTelemetry.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(1ul, spans.size());
|
||||
|
||||
VerifySpan(spans[0], R"(
|
||||
{
|
||||
"name": "My API",
|
||||
"attributes": {
|
||||
"az.namespace": "my-service"
|
||||
},
|
||||
"library": {
|
||||
"name": "my-service",
|
||||
"version": "1.0beta-2"
|
||||
}
|
||||
})");
|
||||
}
|
||||
|
||||
// Clear the global tracer provider set earlier in the test.
|
||||
Azure::Core::Context::ApplicationContext.SetTracerProvider(nullptr);
|
||||
}
|
||||
|
||||
TEST_F(OpenTelemetryServiceTests, NestSpans)
|
||||
{
|
||||
{
|
||||
auto tracerProvider(CreateOpenTelemetryProvider());
|
||||
auto provider(std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
tracerProvider));
|
||||
|
||||
Azure::Core::Context::ApplicationContext.SetTracerProvider(provider);
|
||||
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
clientOptions.Telemetry.ApplicationId = "MyApplication";
|
||||
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service", "1.0beta-2");
|
||||
|
||||
Azure::Core::Context parentContext;
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", parentContext);
|
||||
EXPECT_FALSE(contextAndSpan.first.IsCancelled());
|
||||
parentContext = contextAndSpan.first;
|
||||
|
||||
{
|
||||
auto innerContextAndSpan = serviceTrace.CreateSpan("Nested API", parentContext);
|
||||
EXPECT_FALSE(innerContextAndSpan.first.IsCancelled());
|
||||
}
|
||||
}
|
||||
// Now let's verify what was logged via OpenTelemetry.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(2ul, spans.size());
|
||||
|
||||
// Because Nested API goes out of scope before My API, it will be logged first in the
|
||||
// tracing spans.
|
||||
{
|
||||
EXPECT_EQ("Nested API", spans[0]->GetName());
|
||||
EXPECT_TRUE(spans[0]->GetParentSpanId().IsValid());
|
||||
// The nested span should have the outer span as a parent.
|
||||
EXPECT_EQ(spans[1]->GetSpanId(), spans[0]->GetParentSpanId());
|
||||
|
||||
const auto& attributes = spans[0]->GetAttributes();
|
||||
EXPECT_EQ(1ul, attributes.size());
|
||||
EXPECT_EQ(
|
||||
"my-service", opentelemetry::nostd::get<std::string>(attributes.at("az.namespace")));
|
||||
}
|
||||
{
|
||||
EXPECT_EQ("My API", spans[1]->GetName());
|
||||
EXPECT_FALSE(spans[1]->GetParentSpanId().IsValid());
|
||||
|
||||
const auto& attributes = spans[1]->GetAttributes();
|
||||
EXPECT_EQ(1ul, attributes.size());
|
||||
EXPECT_EQ(
|
||||
"my-service", opentelemetry::nostd::get<std::string>(attributes.at("az.namespace")));
|
||||
}
|
||||
|
||||
EXPECT_EQ("my-service", spans[0]->GetInstrumentationLibrary().GetName());
|
||||
EXPECT_EQ("my-service", spans[1]->GetInstrumentationLibrary().GetName());
|
||||
EXPECT_EQ("1.0beta-2", spans[0]->GetInstrumentationLibrary().GetVersion());
|
||||
EXPECT_EQ("1.0beta-2", spans[1]->GetInstrumentationLibrary().GetVersion());
|
||||
}
|
||||
}
|
||||
|
||||
// Create a serviceTrace and span using a provider specified in the ClientOptions.
|
||||
class ServiceClientOptions : public Azure::Core::_internal::ClientOptions {
|
||||
public:
|
||||
explicit ServiceClientOptions() : ClientOptions() {}
|
||||
};
|
||||
|
||||
class ServiceClient {
|
||||
private:
|
||||
ServiceClientOptions m_clientOptions;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory m_serviceTrace;
|
||||
|
||||
public:
|
||||
explicit ServiceClient(ServiceClientOptions const& clientOptions = ServiceClientOptions{})
|
||||
: m_serviceTrace(clientOptions, "Azure.Core.OpenTelemetry.Test.Service", "1.0.0.beta-2")
|
||||
{
|
||||
}
|
||||
|
||||
Azure::Response<std::string> GetConfigurationString(
|
||||
std::string const& inputString,
|
||||
Azure::Core::Context const& context = Azure::Core::Context{})
|
||||
{
|
||||
auto contextAndSpan = m_serviceTrace.CreateSpan("GetConfigurationString", context);
|
||||
|
||||
// <Call Into Service via an HTTP pipeline>
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> response
|
||||
= SendHttpRequest(false, contextAndSpan.first);
|
||||
|
||||
// Reflect that the operation was successful.
|
||||
contextAndSpan.second.SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Ok);
|
||||
Azure::Response<std::string> rv(inputString, std::move(response));
|
||||
return rv;
|
||||
// When contextAndSpan.second goes out of scope, it ends the span, which will record it.
|
||||
}
|
||||
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> ActuallySendHttpRequest(
|
||||
Azure::Core::Context const& context)
|
||||
{
|
||||
auto contextAndSpan
|
||||
= Azure::Core::Tracing::_internal::DiagnosticTracingFactory::CreateSpanFromContext(
|
||||
"HTTP GET#2", context);
|
||||
|
||||
return std::make_unique<Azure::Core::Http::RawResponse>(
|
||||
1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK");
|
||||
}
|
||||
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> SendHttpRequest(
|
||||
bool throwException,
|
||||
Azure::Core::Context const& context)
|
||||
{
|
||||
if (throwException)
|
||||
{
|
||||
throw Azure::Core::RequestFailedException("it all goes wrong here.");
|
||||
}
|
||||
|
||||
auto contextAndSpan
|
||||
= Azure::Core::Tracing::_internal::DiagnosticTracingFactory::CreateSpanFromContext(
|
||||
"HTTP GET#1", context);
|
||||
|
||||
std::unique_ptr<Azure::Core::Http::RawResponse> response
|
||||
= ActuallySendHttpRequest(contextAndSpan.first);
|
||||
|
||||
return std::make_unique<Azure::Core::Http::RawResponse>(
|
||||
1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK");
|
||||
}
|
||||
|
||||
Azure::Response<std::string> ApiWhichThrows(
|
||||
std::string const&,
|
||||
Azure::Core::Context const& context = Azure::Core::Context{})
|
||||
{
|
||||
auto contextAndSpan = m_serviceTrace.CreateSpan("ApiWhichThrows", context);
|
||||
|
||||
try
|
||||
{
|
||||
auto rawResponse = SendHttpRequest(false, contextAndSpan.first);
|
||||
return Azure::Response<std::string>("", std::move(rawResponse));
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
// Register that the exception has happened and that the span is now in error.
|
||||
contextAndSpan.second.AddEvent(ex);
|
||||
contextAndSpan.second.SetStatus(Azure::Core::Tracing::_internal::SpanStatus::Error);
|
||||
throw;
|
||||
}
|
||||
|
||||
// When contextAndSpan.second goes out of scope, it ends the span, which will record it.
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(OpenTelemetryServiceTests, ServiceApiImplementation)
|
||||
{
|
||||
{
|
||||
auto tracerProvider(CreateOpenTelemetryProvider());
|
||||
auto provider(std::make_shared<Azure::Core::Tracing::OpenTelemetry::OpenTelemetryProvider>(
|
||||
tracerProvider));
|
||||
|
||||
{
|
||||
// Call a simple API and verify telemetry is generated.
|
||||
{
|
||||
ServiceClientOptions clientOptions;
|
||||
clientOptions.Telemetry.TracingProvider = provider;
|
||||
clientOptions.Telemetry.ApplicationId = "MyApplication";
|
||||
ServiceClient myServiceClient(clientOptions);
|
||||
|
||||
myServiceClient.GetConfigurationString("Fred");
|
||||
}
|
||||
// Now let's verify what was logged via OpenTelemetry.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(3ul, spans.size());
|
||||
|
||||
VerifySpan(spans[0], R"(
|
||||
{
|
||||
"name": "HTTP GET#2",
|
||||
"kind": "internal",
|
||||
"statusCode": "unset",
|
||||
"attributes": {
|
||||
"az.namespace": "Azure.Core.OpenTelemetry.Test.Service"
|
||||
},
|
||||
"library": {
|
||||
"name": "Azure.Core.OpenTelemetry.Test.Service",
|
||||
"version": "1.0.0.beta-2"
|
||||
}
|
||||
})");
|
||||
|
||||
VerifySpan(spans[1], R"(
|
||||
{
|
||||
"name": "HTTP GET#1",
|
||||
"kind": "internal",
|
||||
"statusCode": "unset",
|
||||
"attributes": {
|
||||
"az.namespace": "Azure.Core.OpenTelemetry.Test.Service"
|
||||
},
|
||||
"library": {
|
||||
"name": "Azure.Core.OpenTelemetry.Test.Service",
|
||||
"version": "1.0.0.beta-2"
|
||||
}
|
||||
})");
|
||||
|
||||
VerifySpan(spans[2], R"(
|
||||
{
|
||||
"name": "GetConfigurationString",
|
||||
"kind": "internal",
|
||||
"statusCode": "ok",
|
||||
"attributes": {
|
||||
"az.namespace": "Azure.Core.OpenTelemetry.Test.Service"
|
||||
},
|
||||
"library": {
|
||||
"name": "Azure.Core.OpenTelemetry.Test.Service",
|
||||
"version": "1.0.0.beta-2"
|
||||
}
|
||||
})");
|
||||
}
|
||||
}
|
||||
// Call into the fake service client ensuring that no telemetry is generated.
|
||||
{
|
||||
// Call a simple API and verify no telemetry is generated.
|
||||
{
|
||||
ServiceClient myServiceClient;
|
||||
|
||||
myServiceClient.GetConfigurationString("George");
|
||||
}
|
||||
// Now let's verify what was logged via OpenTelemetry.
|
||||
auto spans = m_spanData->GetSpans();
|
||||
EXPECT_EQ(0ul, spans.size());
|
||||
}
|
||||
}
|
||||
18
sdk/core/azure-core-tracing-opentelemetry/vcpkg.json
Normal file
18
sdk/core/azure-core-tracing-opentelemetry/vcpkg.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "azure-core-tracing-opentelemetry-cpp",
|
||||
"version-string": "1.0.0-beta.1",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "opentelemetry-cpp",
|
||||
"platform": "!uwp"
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake-config",
|
||||
"host": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(Threads)
|
||||
|
||||
find_dependency(opentelemetry-cpp@CURL_MIN_REQUIRED_VERSION@)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/azure-core-opentelemetry-cppTargets.cmake")
|
||||
|
||||
check_required_components("azure-core-opentelemetry-cpp")
|
||||
@ -0,0 +1,22 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO Azure/azure-sdk-for-cpp
|
||||
REF azure-core_coretracing@AZ_LIBRARY_VERSION@
|
||||
SHA512 0
|
||||
)
|
||||
|
||||
vcpkg_cmake_configure(
|
||||
SOURCE_PATH ${SOURCE_PATH}/sdk/core/azure-core/
|
||||
OPTIONS
|
||||
${FEATURE_OPTIONS}
|
||||
-DWARNINGS_AS_ERRORS=OFF
|
||||
)
|
||||
|
||||
vcpkg_cmake_install()
|
||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
|
||||
vcpkg_cmake_config_fixup()
|
||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share")
|
||||
vcpkg_copy_pdbs()
|
||||
26
sdk/core/azure-core-tracing-opentelemetry/vcpkg/vcpkg.json
Normal file
26
sdk/core/azure-core-tracing-opentelemetry/vcpkg/vcpkg.json
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
{
|
||||
"name": "azure-core-opentelemetry-cpp",
|
||||
"version-semver": "@AZ_LIBRARY_VERSION@",
|
||||
"description": [
|
||||
"Microsoft Azure Core OpenTelemetry SDK for C++",
|
||||
"This library provides support for modern Azure SDK client libraries written in C++ to leverage OpenTelemetry APIs."
|
||||
],
|
||||
"homepage": "https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/core/azure-core-opentelemetry",
|
||||
"license": "MIT",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "vcpkg-cmake-config",
|
||||
"host": true
|
||||
},
|
||||
{
|
||||
"name": "opentelemetry-cpp"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
## 1.7.0-beta.1 (Unreleased)
|
||||
|
||||
### Features Added
|
||||
Added implementation for Distributed Tracing.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
|
||||
@ -77,6 +77,7 @@ set(
|
||||
inc/azure/core/internal/environment.hpp
|
||||
inc/azure/core/internal/extendable_enumeration.hpp
|
||||
inc/azure/core/internal/strings.hpp
|
||||
inc/azure/core/internal/tracing/service_tracing.hpp
|
||||
inc/azure/core/io/body_stream.hpp
|
||||
inc/azure/core/azure_assert.hpp
|
||||
inc/azure/core/base64.hpp
|
||||
@ -95,10 +96,10 @@ set(
|
||||
inc/azure/core/platform.hpp
|
||||
inc/azure/core/response.hpp
|
||||
inc/azure/core/rtti.hpp
|
||||
inc/azure/core/tracing/tracing.hpp
|
||||
inc/azure/core/url.hpp
|
||||
inc/azure/core/uuid.hpp
|
||||
inc/azure/core.hpp
|
||||
)
|
||||
inc/azure/core.hpp)
|
||||
|
||||
set(
|
||||
AZURE_CORE_SOURCE
|
||||
@ -132,7 +133,7 @@ set(
|
||||
src/operation_status.cpp
|
||||
src/strings.cpp
|
||||
src/uuid.cpp
|
||||
)
|
||||
src/tracing/tracing.cpp)
|
||||
|
||||
add_library(azure-core ${AZURE_CORE_HEADER} ${AZURE_CORE_SOURCE})
|
||||
|
||||
|
||||
@ -50,3 +50,6 @@
|
||||
|
||||
// azure/core/io
|
||||
#include "azure/core/io/body_stream.hpp"
|
||||
|
||||
// azure/core/tracing
|
||||
#include "azure/core/tracing/tracing.hpp"
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
#include "azure/core/datetime.hpp"
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
#include "azure/core/rtti.hpp"
|
||||
|
||||
#include "azure/core/tracing/tracing.hpp"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
@ -76,6 +76,7 @@ namespace Azure { namespace Core {
|
||||
{
|
||||
std::shared_ptr<ContextSharedState> Parent;
|
||||
std::atomic<DateTime::rep> Deadline;
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> TraceProvider;
|
||||
Context::Key Key;
|
||||
std::shared_ptr<void> Value;
|
||||
#if defined(AZ_CORE_RTTI)
|
||||
@ -248,6 +249,22 @@ namespace Azure { namespace Core {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the tracer provider for the current context.
|
||||
*/
|
||||
std::shared_ptr<Tracing::TracerProvider> GetTracerProvider()
|
||||
{
|
||||
return m_contextSharedState->TraceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the tracer provider for the current context.
|
||||
*/
|
||||
void SetTracerProvider(std::shared_ptr<Tracing::TracerProvider> tracerProvider)
|
||||
{
|
||||
m_contextSharedState->TraceProvider = tracerProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The application context (root).
|
||||
*
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "azure/core/dll_import_export.hpp"
|
||||
#include "azure/core/http/http.hpp"
|
||||
#include "azure/core/http/transport.hpp"
|
||||
#include "azure/core/tracing/tracing.hpp"
|
||||
#include "azure/core/uuid.hpp"
|
||||
|
||||
#include <chrono>
|
||||
@ -56,6 +57,13 @@ namespace Azure { namespace Core { namespace Http { namespace Policies {
|
||||
*
|
||||
*/
|
||||
std::string ApplicationId;
|
||||
|
||||
/**
|
||||
* @brief Specifies the default distributed tracing provider to use for this client. By default,
|
||||
* this will be the tracing provider specified in the application context.
|
||||
*/
|
||||
std::shared_ptr<Azure::Core::Tracing::TracerProvider> TracingProvider{
|
||||
Context::ApplicationContext.GetTracerProvider()};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file
|
||||
|
||||
@ -0,0 +1,246 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/context.hpp"
|
||||
#include "azure/core/internal/client_options.hpp"
|
||||
#include "azure/core/tracing/tracing.hpp"
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Helper classes to enable service client distributed tracing implementations.
|
||||
*
|
||||
* @remark See #policy.hpp
|
||||
*/
|
||||
namespace Azure { namespace Core { namespace Tracing { namespace _internal {
|
||||
|
||||
/**
|
||||
* @brief RAII Helper class for Azure::Core::Tracing::Span objects.
|
||||
*
|
||||
* @details The ServiceSpan object is an RAII helper object used to manage Span objects.
|
||||
*
|
||||
* Before a Span is registered with OpenTelemetry, the span needs to have called the
|
||||
* "Azure::Core::Tracing::Span::End" method. The ServiceSpan method wraps an
|
||||
* Azure::Core::Tracing::Span object and ensures that the "End" method is called in the destructor
|
||||
* for the span.
|
||||
*/
|
||||
class ServiceSpan final : public Span {
|
||||
private:
|
||||
std::shared_ptr<Span> m_span;
|
||||
|
||||
friend class DiagnosticTracingFactory;
|
||||
ServiceSpan() = default;
|
||||
explicit ServiceSpan(std::shared_ptr<Span> span) : m_span(span) {}
|
||||
|
||||
ServiceSpan(const ServiceSpan&) = delete;
|
||||
ServiceSpan& operator=(ServiceSpan const&) = delete;
|
||||
|
||||
ServiceSpan& operator=(ServiceSpan&&) noexcept = default;
|
||||
|
||||
public:
|
||||
ServiceSpan(ServiceSpan&& that) = default;
|
||||
|
||||
~ServiceSpan()
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->End();
|
||||
}
|
||||
}
|
||||
|
||||
void End(Azure::Nullable<Azure::DateTime> = Azure::Nullable<Azure::DateTime>{}) override
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->End();
|
||||
}
|
||||
}
|
||||
void SetStatus(
|
||||
Azure::Core::Tracing::_internal::SpanStatus const& status,
|
||||
std::string const& description = "") override
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->SetStatus(status, description);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Adds a set of attributes to the span.
|
||||
*
|
||||
* @param attributeToAdd Attributes to be added to the span.
|
||||
*/
|
||||
virtual void AddAttributes(AttributeSet const& attributeToAdd) override
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->AddAttributes(attributeToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an event to the span.
|
||||
*
|
||||
* Add an Event to the span. An event is identified by a name and an optional set of attributes
|
||||
* associated with the event.
|
||||
*
|
||||
* @param eventName Name of the event to add.
|
||||
* @param eventAttributes Attributes associated with the event.
|
||||
*/
|
||||
virtual void AddEvent(std::string const& eventName, AttributeSet const& eventAttributes)
|
||||
override
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->AddEvent(eventName, eventAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an event to the span.
|
||||
*
|
||||
* Add an Event to the span. An event is identified by a name
|
||||
*
|
||||
* @param eventName Name of the event to add.
|
||||
*/
|
||||
virtual void AddEvent(std::string const& eventName) override
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->AddEvent(eventName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Records an exception occurring in the span.
|
||||
*
|
||||
* @param exception Exception which has occurred.
|
||||
*/
|
||||
virtual void AddEvent(std::exception const& exception) override
|
||||
{
|
||||
if (m_span)
|
||||
{
|
||||
m_span->AddEvent(exception);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper class to enable distributed tracing for the service.
|
||||
*
|
||||
* @details Each service implementation SHOULD have a member variable which aids in managing
|
||||
* the distributed tracing for the service.
|
||||
*/
|
||||
class DiagnosticTracingFactory final {
|
||||
private:
|
||||
std::string m_serviceName;
|
||||
std::string m_serviceVersion;
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> m_serviceTracer;
|
||||
|
||||
/** @brief The key used to retrieve the span and tracer associated with a context object.
|
||||
*
|
||||
* The value stored in the context with this key is a `std::pair<std::shared_ptr<Span>,
|
||||
* std::shared_ptr<Tracer>>`.
|
||||
*
|
||||
* A caller can use the Span and Tracer to create a new span associated with the current
|
||||
* context span.
|
||||
*/
|
||||
static Azure::Core::Context::Key ContextSpanKey;
|
||||
static Azure::Core::Context::Key TracingFactoryContextKey;
|
||||
// using TracingContext = std::pair<std::shared_ptr<Span>, std::shared_ptr<Tracer>>;
|
||||
using TracingContext = std::shared_ptr<Span>;
|
||||
|
||||
static DiagnosticTracingFactory* DiagnosticFactoryFromContext(
|
||||
Azure::Core::Context const& context);
|
||||
|
||||
static Azure::Nullable<TracingContext> TracingContextFromContext(
|
||||
Azure::Core::Context const& context);
|
||||
|
||||
public:
|
||||
DiagnosticTracingFactory(
|
||||
Azure::Core::_internal::ClientOptions const& options,
|
||||
std::string serviceName,
|
||||
std::string serviceVersion)
|
||||
: m_serviceName(serviceName), m_serviceVersion(serviceVersion),
|
||||
m_serviceTracer(
|
||||
options.Telemetry.TracingProvider
|
||||
? options.Telemetry.TracingProvider->CreateTracer(serviceName, serviceVersion)
|
||||
: nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DiagnosticTracingFactory() = default;
|
||||
|
||||
/** @brief A ContextAndSpan provides an updated Context object and a new span object
|
||||
* which can be used to add events and attributes to the span.
|
||||
*/
|
||||
using ContextAndSpan = std::pair<Azure::Core::Context, ServiceSpan>;
|
||||
|
||||
ContextAndSpan CreateSpan(
|
||||
std::string const& spanName,
|
||||
Azure::Core::Context const& clientContext);
|
||||
|
||||
static ContextAndSpan CreateSpanFromContext(
|
||||
std::string const& spanName,
|
||||
Azure::Core::Context const& clientContext);
|
||||
|
||||
std::unique_ptr<Azure::Core::Tracing::_internal::AttributeSet> CreateAttributeSet();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Attributes emitted as a part of distributed tracing spans.
|
||||
*
|
||||
* List taken from here:
|
||||
* https://github.com/Azure/azure-sdk/blob/main/docs/tracing/distributed-tracing-conventions.yml
|
||||
*
|
||||
*/
|
||||
class TracingAttributes
|
||||
: public Azure::Core::_internal::ExtendableEnumeration<TracingAttributes> {
|
||||
public:
|
||||
explicit TracingAttributes(std::string const& that) : ExtendableEnumeration(that) {}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* [Namespace](https://docs.microsoft.com/azure/azure-resource-manager/management/azure-services-resource-providers)
|
||||
* of Azure service request is made against.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes AzNamespace;
|
||||
|
||||
/**
|
||||
* @brief HTTP request method.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes HttpMethod;
|
||||
|
||||
/**
|
||||
* @brief Full HTTP request URL in the form `scheme://host[:port]/path?query[#fragment]`.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes HttpUrl;
|
||||
|
||||
/**
|
||||
* @brief [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6).
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes HttpStatusCode;
|
||||
|
||||
/**
|
||||
* @brief Value of the [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3)
|
||||
* header sent by the client.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes HttpUserAgent;
|
||||
|
||||
/** @brief Value of the[x - ms - client - request - id] header(or other request - id header,
|
||||
* depending on the service) sent by the client.
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes RequestId;
|
||||
|
||||
/** @brief Value of the [x-ms-request-id] header (or other request-id header, depending on the
|
||||
* service) sent by the server in response.
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static TracingAttributes ServiceRequestId;
|
||||
};
|
||||
|
||||
}}}} // namespace Azure::Core::Tracing::_internal
|
||||
289
sdk/core/azure-core/inc/azure/core/tracing/tracing.hpp
Normal file
289
sdk/core/azure-core/inc/azure/core/tracing/tracing.hpp
Normal file
@ -0,0 +1,289 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Handling log messages from Azure SDK.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "azure/core/datetime.hpp"
|
||||
#include "azure/core/internal/extendable_enumeration.hpp"
|
||||
#include "azure/core/nullable.hpp"
|
||||
#include "azure/core/url.hpp"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Azure { namespace Core { namespace Tracing {
|
||||
|
||||
namespace _internal {
|
||||
|
||||
/** The set of attributes to be applied to a Span or Event.
|
||||
*
|
||||
* @details
|
||||
* An AttributeSet represents a set of attributes to be added to a span or
|
||||
* event.
|
||||
*
|
||||
* @note Note that AttributeSets do *NOT* take a copy of their input values,
|
||||
* it is the responsibility of the caller to ensure that the object remains
|
||||
* valid between when it is added to the AttributeSet and when it is added to
|
||||
* a span or event.
|
||||
*
|
||||
* OpenTelemetry property bags can hold:
|
||||
* - bool
|
||||
* - int32_t
|
||||
* - int64_t
|
||||
* - uint64_t
|
||||
* - double
|
||||
* - const char *
|
||||
* - std::string/std::string_view *** Not Implemented
|
||||
* - std::span<const bool> *** Not Implemented
|
||||
* - std::span<const int32_t> *** Not Implemented
|
||||
* - std::span<const int64_t> *** Not Implemented
|
||||
* - std::span<const uint32_t> *** Not Implemented
|
||||
* - std::span<const double> *** Not Implemented
|
||||
* - std::span<std::string/std::string_view> *** Not Implemented
|
||||
* - uint64_t (not fully supported) *** Not Implemented
|
||||
* - std::span<uint64_t> (not fully supported) *** Not Implemented
|
||||
* - std::span<const uint8_t> (not fully supported) *** Not Implemented.
|
||||
*
|
||||
*/
|
||||
class AttributeSet {
|
||||
public:
|
||||
/**
|
||||
* @brief Adds a Boolean attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, bool value) = 0;
|
||||
/**
|
||||
* @brief Adds a 32bit integer attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, int32_t value) = 0;
|
||||
/**
|
||||
* @brief Adds a 64bit integer attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, int64_t value) = 0;
|
||||
/**
|
||||
* @brief Adds an unsigned 64bit integer attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, uint64_t value) = 0;
|
||||
/**
|
||||
* @brief Adds a 64bit floating point attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, double value) = 0;
|
||||
/**
|
||||
* @brief Adds a C style string attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, const char* value) = 0;
|
||||
/**
|
||||
* @brief Adds a C++ string attribute to the attribute set.
|
||||
*
|
||||
* @param attributeName Name of attribute to add.
|
||||
* @param value Value of attribute.
|
||||
*/
|
||||
virtual void AddAttribute(std::string const& attributeName, std::string const& value) = 0;
|
||||
|
||||
/**
|
||||
* @brief destroys an AttributeSet - virtual destructor to enable base class users to destroy
|
||||
* derived classes.
|
||||
*/
|
||||
virtual ~AttributeSet(){};
|
||||
};
|
||||
|
||||
/** @brief The Type of Span.
|
||||
*/
|
||||
class SpanKind final : public Azure::Core::_internal::ExtendableEnumeration<SpanKind> {
|
||||
public:
|
||||
explicit SpanKind(std::string const& kind) : ExtendableEnumeration(kind) {}
|
||||
SpanKind() = default;
|
||||
|
||||
/**
|
||||
* @brief Represents an "Internal" operation.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanKind Internal;
|
||||
/**
|
||||
* @brief Represents a request to a remote service.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanKind Client;
|
||||
/**
|
||||
* @brief Represents a span covering the server side handling of an API call.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanKind Server;
|
||||
/**
|
||||
* @brief Represents the initiator of an asynchronous request.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanKind Producer;
|
||||
/**
|
||||
* @brief Represents a span which describes a child of a producer request.
|
||||
*
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanKind Consumer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the status of a span.
|
||||
*/
|
||||
class SpanStatus final : public Azure::Core::_internal::ExtendableEnumeration<SpanStatus> {
|
||||
|
||||
public:
|
||||
explicit SpanStatus(std::string const& status) : ExtendableEnumeration(status) {}
|
||||
SpanStatus() = default;
|
||||
|
||||
/**
|
||||
* @brief The default status of a span.
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanStatus Unset;
|
||||
/**
|
||||
* @brief The operation has completed successfully.
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanStatus Ok;
|
||||
/**
|
||||
* @brief The operation contains an error.
|
||||
*/
|
||||
AZ_CORE_DLLEXPORT const static SpanStatus Error;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Span - represents a span in tracing.
|
||||
*/
|
||||
class Span {
|
||||
public:
|
||||
/**
|
||||
* @brief Signals that the span has now ended.
|
||||
*/
|
||||
virtual void End(Azure::Nullable<Azure::DateTime> endTime = {}) = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds a set of attributes to the span.
|
||||
*
|
||||
* @param attributeToAdd Attributes to be added to the span.
|
||||
*/
|
||||
virtual void AddAttributes(AttributeSet const& attributeToAdd) = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds an event to the span.
|
||||
*
|
||||
* Add an Event to the span. An event is identified by a name and an optional set of
|
||||
* attributes associated with the event.
|
||||
*
|
||||
* @param eventName Name of the event to add.
|
||||
* @param eventAttributes Attributes associated with the event.
|
||||
*/
|
||||
virtual void AddEvent(std::string const& eventName, AttributeSet const& eventAttributes) = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds an event to the span.
|
||||
*
|
||||
* Add an Event to the span. An event is identified by a name
|
||||
*
|
||||
* @param eventName Name of the event to add.
|
||||
*/
|
||||
virtual void AddEvent(std::string const& eventName) = 0;
|
||||
/**
|
||||
* @brief Records an exception occurring in the span.
|
||||
*
|
||||
* @param exception Exception which has occurred.
|
||||
*/
|
||||
virtual void AddEvent(std::exception const& exception) = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the Status of the span
|
||||
*
|
||||
* @param status Updated status of the span.
|
||||
* @param description A description associated with the Status.
|
||||
*/
|
||||
virtual void SetStatus(SpanStatus const& status, std::string const& description = "") = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Options used while creating a span.
|
||||
*
|
||||
*/
|
||||
struct CreateSpanOptions final
|
||||
{
|
||||
/**
|
||||
* @brief The kind of span to be created.
|
||||
*
|
||||
*/
|
||||
SpanKind Kind{SpanKind::Internal};
|
||||
/**
|
||||
* @brief Attributes associated with the span.
|
||||
*
|
||||
*/
|
||||
std::unique_ptr<AttributeSet> Attributes;
|
||||
|
||||
/**
|
||||
* @brief Parent for the newly created span.
|
||||
*/
|
||||
std::shared_ptr<Span> ParentSpan;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Tracer - factory for creating span objects.
|
||||
*
|
||||
*/
|
||||
class Tracer {
|
||||
public:
|
||||
/**
|
||||
* @brief Create new Span object.
|
||||
*
|
||||
* @details Creates a new span object.
|
||||
*
|
||||
* @note There is no concept of a "current" span, each span created is a top level span,
|
||||
* unless the CreateSpanOptions has ParentSpan member, in which case the ParentSpan member
|
||||
* will be the parent of the newly created span.
|
||||
*
|
||||
* @param spanName The name of the span to create.
|
||||
* @param options Options to be used when creating the span.
|
||||
* @return std::shared_ptr<Azure::Core::Tracing::Span> Newly created span.
|
||||
*/
|
||||
virtual std::shared_ptr<Span> CreateSpan(
|
||||
std::string const& spanName,
|
||||
CreateSpanOptions const& options = {}) const = 0;
|
||||
|
||||
virtual std::unique_ptr<AttributeSet> CreateAttributeSet() const = 0;
|
||||
};
|
||||
} // namespace _internal
|
||||
|
||||
/**
|
||||
* @brief Trace Provider - factory for creating Tracer objects.
|
||||
*/
|
||||
class TracerProvider {
|
||||
public:
|
||||
/**
|
||||
* @brief Create a Tracer object
|
||||
*
|
||||
* @param name Name of the tracer object, typically the name of the Service client
|
||||
* (Azure.Storage.Blobs, for example)
|
||||
* @param version Version of the service client.
|
||||
* @return std::shared_ptr<Azure::Core::Tracing::Tracer>
|
||||
*/
|
||||
virtual std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> CreateTracer(
|
||||
std::string const& name,
|
||||
std::string const& version = "") const = 0;
|
||||
};
|
||||
}}} // namespace Azure::Core::Tracing
|
||||
115
sdk/core/azure-core/src/tracing/tracing.cpp
Normal file
115
sdk/core/azure-core/src/tracing/tracing.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "azure/core/tracing/tracing.hpp"
|
||||
#include "azure/core/internal/tracing/service_tracing.hpp"
|
||||
|
||||
namespace Azure { namespace Core { namespace Tracing { namespace _internal {
|
||||
|
||||
const SpanKind SpanKind::Internal("Internal");
|
||||
const SpanKind SpanKind::Client("Client");
|
||||
const SpanKind SpanKind::Consumer("Consumer");
|
||||
const SpanKind SpanKind::Producer("Producer");
|
||||
const SpanKind SpanKind::Server("Server");
|
||||
|
||||
const SpanStatus SpanStatus::Unset("Unset");
|
||||
const SpanStatus SpanStatus::Ok("Ok");
|
||||
const SpanStatus SpanStatus::Error("Error");
|
||||
|
||||
const TracingAttributes TracingAttributes::AzNamespace("az.namespace");
|
||||
|
||||
DiagnosticTracingFactory::ContextAndSpan DiagnosticTracingFactory::CreateSpan(
|
||||
std::string const& methodName,
|
||||
Azure::Core::Context const& context)
|
||||
{
|
||||
CreateSpanOptions createOptions;
|
||||
if (m_serviceTracer)
|
||||
{
|
||||
Azure::Core::Context contextToUse = context;
|
||||
|
||||
// Ensure that the factory is available in the context chain.
|
||||
DiagnosticTracingFactory* tracingFactoryFromContext;
|
||||
if (!context.TryGetValue(TracingFactoryContextKey, tracingFactoryFromContext))
|
||||
{
|
||||
contextToUse = context.WithValue(TracingFactoryContextKey, this);
|
||||
}
|
||||
|
||||
TracingContext traceContext;
|
||||
// Find a span in the context hierarchy.
|
||||
if (contextToUse.TryGetValue(ContextSpanKey, traceContext))
|
||||
{
|
||||
createOptions.ParentSpan = traceContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Please note: Not specifically needed, but make sure that this is a root level
|
||||
// span if there is no parent span in the context
|
||||
createOptions.ParentSpan = nullptr;
|
||||
}
|
||||
createOptions.Attributes = m_serviceTracer->CreateAttributeSet();
|
||||
createOptions.Attributes->AddAttribute(
|
||||
TracingAttributes::AzNamespace.ToString(), m_serviceName);
|
||||
|
||||
std::shared_ptr<Span> newSpan(m_serviceTracer->CreateSpan(methodName, createOptions));
|
||||
TracingContext tracingContext = newSpan;
|
||||
Azure::Core::Context newContext = contextToUse.WithValue(ContextSpanKey, tracingContext);
|
||||
ServiceSpan newServiceSpan(newSpan);
|
||||
return std::make_pair<Azure::Core::Context, ServiceSpan>(
|
||||
std::move(newContext), std::move(newServiceSpan));
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(context, ServiceSpan{});
|
||||
}
|
||||
}
|
||||
DiagnosticTracingFactory::ContextAndSpan DiagnosticTracingFactory::CreateSpanFromContext(
|
||||
std::string const& spanName,
|
||||
Azure::Core::Context const& context)
|
||||
{
|
||||
DiagnosticTracingFactory* tracingFactory
|
||||
= DiagnosticTracingFactory::DiagnosticFactoryFromContext(context);
|
||||
if (tracingFactory)
|
||||
{
|
||||
return tracingFactory->CreateSpan(spanName, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(context, ServiceSpan{});
|
||||
}
|
||||
}
|
||||
|
||||
Azure::Nullable<DiagnosticTracingFactory::TracingContext>
|
||||
DiagnosticTracingFactory::TracingContextFromContext(Azure::Core::Context const& context)
|
||||
{
|
||||
TracingContext traceContext;
|
||||
if (context.TryGetValue(ContextSpanKey, traceContext))
|
||||
{
|
||||
return traceContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Azure::Nullable<TracingContext>{};
|
||||
}
|
||||
}
|
||||
|
||||
DiagnosticTracingFactory* DiagnosticTracingFactory::DiagnosticFactoryFromContext(
|
||||
Azure::Core::Context const& context)
|
||||
{
|
||||
DiagnosticTracingFactory* factory;
|
||||
if (context.TryGetValue(TracingFactoryContextKey, factory))
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Azure::Core::Tracing::_internal::AttributeSet>
|
||||
DiagnosticTracingFactory::CreateAttributeSet()
|
||||
{
|
||||
return m_serviceTracer->CreateAttributeSet();
|
||||
}
|
||||
|
||||
Azure::Core::Context::Key DiagnosticTracingFactory::ContextSpanKey;
|
||||
Azure::Core::Context::Key DiagnosticTracingFactory::TracingFactoryContextKey;
|
||||
|
||||
}}}} // namespace Azure::Core::Tracing::_internal
|
||||
@ -49,6 +49,7 @@ add_executable (
|
||||
environment_log_level_listener_test.cpp
|
||||
extendable_enumeration_test.cpp
|
||||
etag_test.cpp
|
||||
exception_test.cpp
|
||||
http_test.cpp
|
||||
http_test.hpp
|
||||
http_method_test.cpp
|
||||
@ -68,6 +69,7 @@ add_executable (
|
||||
request_id_policy_test.cpp
|
||||
response_t_test.cpp
|
||||
retry_policy_test.cpp
|
||||
service_tracing_test.cpp
|
||||
sha_test.cpp
|
||||
simplified_header_test.cpp
|
||||
string_test.cpp
|
||||
@ -77,7 +79,6 @@ add_executable (
|
||||
transport_adapter_implementation_test.cpp
|
||||
url_test.cpp
|
||||
uuid_test.cpp
|
||||
exception_test.cpp
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <azure/core/context.hpp>
|
||||
#include <azure/core/tracing/tracing.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
@ -30,7 +31,7 @@ TEST(Context, BasicBool)
|
||||
|
||||
// New context from previous
|
||||
auto c2 = context.WithValue(key, true);
|
||||
bool value;
|
||||
bool value{};
|
||||
EXPECT_TRUE(c2.TryGetValue<bool>(key, value));
|
||||
EXPECT_TRUE(value == true);
|
||||
|
||||
@ -513,3 +514,26 @@ TEST(Context, KeyTypePairPrecondition)
|
||||
EXPECT_TRUE(c3.TryGetValue<std::string>(key, strValue));
|
||||
EXPECT_TRUE(strValue == s);
|
||||
}
|
||||
|
||||
TEST(Context, SetTracingProvider)
|
||||
{
|
||||
class TestTracingProvider final : public Azure::Core::Tracing::TracerProvider {
|
||||
public:
|
||||
TestTracingProvider() : TracerProvider() {}
|
||||
~TestTracingProvider() {}
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> CreateTracer(
|
||||
std::string const&,
|
||||
std::string const&) const override
|
||||
{
|
||||
throw std::runtime_error("Not implemented");
|
||||
};
|
||||
};
|
||||
|
||||
Context context;
|
||||
context.SetTracerProvider(nullptr);
|
||||
|
||||
// Verify we can round trip a tracing provider through the context.
|
||||
auto testProvider = std::make_shared<TestTracingProvider>();
|
||||
context.SetTracerProvider(testProvider);
|
||||
EXPECT_EQ(testProvider, context.GetTracerProvider());
|
||||
}
|
||||
|
||||
146
sdk/core/azure-core/test/ut/service_tracing_test.cpp
Normal file
146
sdk/core/azure-core/test/ut/service_tracing_test.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/core/tracing/tracing.hpp"
|
||||
#include <azure/core/internal/tracing/service_tracing.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace Azure::Core;
|
||||
using namespace Azure::Core::Tracing;
|
||||
using namespace Azure::Core::Tracing::_internal;
|
||||
|
||||
TEST(DiagnosticTracingFactory, ServiceTraceEnums)
|
||||
{
|
||||
// Exercise the SpanKind and SpanStatus constructors from the distributed tracing header.
|
||||
{
|
||||
SpanKind spanKind = Azure::Core::Tracing::_internal::SpanKind::Client;
|
||||
spanKind = SpanKind::Consumer;
|
||||
spanKind = SpanKind::Internal;
|
||||
spanKind = SpanKind::Producer;
|
||||
spanKind = Azure::Core::Tracing::_internal::SpanKind::Server;
|
||||
std::string kindValue = spanKind.ToString();
|
||||
}
|
||||
{
|
||||
SpanStatus spanStatus = SpanStatus::Unset;
|
||||
spanStatus = SpanStatus::Error;
|
||||
spanStatus = SpanStatus::Ok;
|
||||
std::string statusValue = spanStatus.ToString();
|
||||
}
|
||||
Azure::Core::Tracing::_internal::CreateSpanOptions options;
|
||||
options.Kind = SpanKind::Internal;
|
||||
|
||||
std::string tracingAttributeName = TracingAttributes::AzNamespace.ToString();
|
||||
}
|
||||
|
||||
TEST(DiagnosticTracingFactory, SimpleServiceSpanTests)
|
||||
{
|
||||
{
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace;
|
||||
}
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service-cpp", "1.0b2");
|
||||
}
|
||||
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service-cpp", "1.0b2");
|
||||
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", {});
|
||||
EXPECT_FALSE(contextAndSpan.first.IsCancelled());
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy service tracing class.
|
||||
class TestSpan final : public Azure::Core::Tracing::_internal::Span {
|
||||
public:
|
||||
TestSpan() : Azure::Core::Tracing::_internal::Span() {}
|
||||
|
||||
// Inherited via Span
|
||||
virtual void AddAttributes(AttributeSet const&) override {}
|
||||
virtual void AddEvent(std::string const&, AttributeSet const&) override {}
|
||||
virtual void AddEvent(std::string const&) override {}
|
||||
virtual void AddEvent(std::exception const&) override {}
|
||||
virtual void SetStatus(SpanStatus const&, std::string const&) override {}
|
||||
|
||||
// Inherited via Span
|
||||
virtual void End(Azure::Nullable<Azure::DateTime>) override {}
|
||||
};
|
||||
|
||||
class TestAttributeSet : public Azure::Core::Tracing::_internal::AttributeSet {
|
||||
public:
|
||||
TestAttributeSet() : Azure::Core::Tracing::_internal::AttributeSet() {}
|
||||
|
||||
// Inherited via AttributeSet
|
||||
virtual void AddAttribute(std::string const&, bool) override {}
|
||||
virtual void AddAttribute(std::string const&, int32_t) override {}
|
||||
virtual void AddAttribute(std::string const&, int64_t) override {}
|
||||
virtual void AddAttribute(std::string const&, uint64_t) override {}
|
||||
virtual void AddAttribute(std::string const&, double) override {}
|
||||
virtual void AddAttribute(std::string const&, const char*) override {}
|
||||
virtual void AddAttribute(std::string const&, std::string const&) override {}
|
||||
};
|
||||
class TestTracer final : public Azure::Core::Tracing::_internal::Tracer {
|
||||
public:
|
||||
TestTracer(std::string const&, std::string const&) : Azure::Core::Tracing::_internal::Tracer() {}
|
||||
std::shared_ptr<Span> CreateSpan(std::string const&, CreateSpanOptions const&) const override
|
||||
{
|
||||
return std::make_shared<TestSpan>();
|
||||
}
|
||||
|
||||
std::unique_ptr<AttributeSet> CreateAttributeSet() const override
|
||||
{
|
||||
return std::make_unique<TestAttributeSet>();
|
||||
};
|
||||
};
|
||||
|
||||
class TestTracingProvider final : public Azure::Core::Tracing::TracerProvider {
|
||||
public:
|
||||
TestTracingProvider() : TracerProvider() {}
|
||||
~TestTracingProvider() {}
|
||||
std::shared_ptr<Azure::Core::Tracing::_internal::Tracer> CreateTracer(
|
||||
std::string const& serviceName,
|
||||
std::string const& serviceVersion) const override
|
||||
{
|
||||
return std::make_shared<TestTracer>(serviceName, serviceVersion);
|
||||
};
|
||||
};
|
||||
|
||||
TEST(DiagnosticTracingFactory, BasicServiceSpanTests)
|
||||
{
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service-cpp", "1.0b2");
|
||||
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", {});
|
||||
auto span = std::move(contextAndSpan.second);
|
||||
|
||||
span.End();
|
||||
span.AddEvent("New Event");
|
||||
span.AddEvent(std::runtime_error("Exception"));
|
||||
span.SetStatus(SpanStatus::Error);
|
||||
}
|
||||
{
|
||||
Azure::Core::_internal::ClientOptions clientOptions;
|
||||
auto testTracer = std::make_shared<TestTracingProvider>();
|
||||
clientOptions.Telemetry.TracingProvider = testTracer;
|
||||
Azure::Core::Tracing::_internal::DiagnosticTracingFactory serviceTrace(
|
||||
clientOptions, "my-service-cpp", "1.0b2");
|
||||
|
||||
auto contextAndSpan = serviceTrace.CreateSpan("My API", {});
|
||||
auto span = std::move(contextAndSpan.second);
|
||||
|
||||
span.End();
|
||||
span.AddEvent("New Event");
|
||||
span.AddEvent(std::runtime_error("Exception"));
|
||||
std::unique_ptr<Azure::Core::Tracing::_internal::AttributeSet> attributeSet
|
||||
= serviceTrace.CreateAttributeSet();
|
||||
attributeSet->AddAttribute("Joe", "Joe'sValue");
|
||||
span.AddEvent("AttributeEvent", *attributeSet);
|
||||
span.AddAttributes(*attributeSet);
|
||||
span.SetStatus(SpanStatus::Error);
|
||||
}
|
||||
}
|
||||
@ -3,12 +3,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <azure/core/internal/client_options.hpp>
|
||||
#include <azure/core/internal/tracing/service_tracing.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Template {
|
||||
|
||||
struct TemplateClientOptions : public Azure::Core::_internal::ClientOptions
|
||||
{
|
||||
};
|
||||
class TemplateClient final {
|
||||
|
||||
public:
|
||||
TemplateClient(TemplateClientOptions options = TemplateClientOptions());
|
||||
std::string ClientVersion() const;
|
||||
int GetValue(int key) const;
|
||||
};
|
||||
|
||||
@ -12,6 +12,8 @@ using namespace Azure::Template::_detail;
|
||||
|
||||
std::string TemplateClient::ClientVersion() const { return PackageVersion::ToString(); }
|
||||
|
||||
TemplateClient::TemplateClient(TemplateClientOptions) {}
|
||||
|
||||
int TemplateClient::GetValue(int key) const
|
||||
{
|
||||
if (key < 0)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "azure-sdk-for-cpp",
|
||||
"version": "1.5.0",
|
||||
"builtin-baseline": "f0aa678b7471497f1adedcc99f40e1599ad22f69",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "curl"
|
||||
@ -11,6 +12,11 @@
|
||||
},
|
||||
{
|
||||
"name": "openssl"
|
||||
},
|
||||
{
|
||||
"name": "opentelemetry-cpp",
|
||||
"platform": "!uwp",
|
||||
"version>=": "1.3.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user