diff --git a/.github/.codecov.yaml b/.github/.codecov.yaml index 3389f63841..c97b4e92dd 100644 --- a/.github/.codecov.yaml +++ b/.github/.codecov.yaml @@ -6,6 +6,8 @@ coverage: precision: 2 round: down range: "80...100" + status: + patch: off parsers: gcov: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4fab212f3..8f8cd06a70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: 'true' - name: setup run: | sudo ./ci/setup_cmake.sh @@ -21,11 +23,33 @@ jobs: - name: run prometheus exporter tests run: ./ci/do_ci.sh cmake.exporter.prometheus.test + cmake_gcc_48_test: + name: CMake gcc 4.8 + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + - name: setup + run: | + sudo ./ci/setup_ci_environment.sh + sudo ./ci/install_gcc48.sh + - name: setup cmake + run: | + sudo CC=/usr/bin/gcc-4.8 CXX=/usr/bin/g++-4.8 ./ci/setup_cmake.sh + - name: run tests + run: ./ci/do_ci.sh cmake.legacy.test + env: + CC: /usr/bin/gcc-4.8 + CXX: /usr/bin/g++-4.8 + cmake_test_cxx20: name: CMake C++20 test runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 + with: + submodules: 'true' - name: setup run: | sudo ./ci/setup_ci_environment.sh @@ -33,11 +57,73 @@ jobs: - name: run tests run: ./ci/do_ci.sh cmake.c++20.test + cmake_posix: + name: Build on ${{ matrix.os }} ${{ matrix.config }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + config: [release] + os: [ubuntu-18.04, ubuntu-20.04, macos-latest] + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + - name: build + run: tools/build.sh ${{ matrix.config }} + + # TODO #1: add vs2015 + # TODO #2: consolidate cmake_[vs2015|vs2017|vs2019] into one matrix build + cmake_windows_vs2017: + name: Build on Windows ${{ matrix.arch }}-${{ matrix.config }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + arch: [x64] + config: [release] + os: [windows-2016] + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: 'true' + - name: Setup build tools + run: | + cd "$Env:GITHUB_WORKSPACE" + .\tools\setup-buildtools.cmd + - name: Build + run: | + cd "$Env:GITHUB_WORKSPACE" + .\tools\build-vs2017.cmd ${{ matrix.config }} + + cmake_windows_vs2019: + name: Build on Windows ${{ matrix.arch }}-${{ matrix.config }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + arch: [x64] + config: [release] + os: [windows-2019] + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: 'true' + - name: Setup build tools + run: | + cd "$Env:GITHUB_WORKSPACE" + .\tools\setup-buildtools.cmd + - name: Build + run: | + cd "$Env:GITHUB_WORKSPACE" + .\tools\build-vs2017.cmd ${{ matrix.config }} + plugin_test: name: Plugin -> CMake runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: 'true' - name: setup run: | sudo ./ci/setup_cmake.sh @@ -125,11 +211,21 @@ jobs: - name: run tests run: ./ci/do_ci.sh bazel.tsan + bazel_osx: + name: Bazel on MacOS + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: run tests + run: ./ci/do_ci.sh bazel.test + benchmark: name: Benchmark runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + submodules: 'true' - name: setup run: | sudo ./ci/setup_cmake.sh @@ -155,19 +251,13 @@ jobs: - name: run tests run: ./ci/do_ci.sh format - osx_test: - name: Bazel on MacOS - runs-on: macos-latest - steps: - - uses: actions/checkout@v2 - - name: run tests - run: ./ci/do_ci.sh bazel.test - windows: name: CMake -> exporter proto runs-on: windows-2019 steps: - uses: actions/checkout@v2 + with: + submodules: 'true' - name: setup run: | ./ci/setup_windows_cmake.ps1 @@ -193,6 +283,8 @@ jobs: runs-on: windows-2019 steps: - uses: actions/checkout@v2 + with: + submodules: 'true' - name: setup run: | ./ci/setup_windows_cmake.ps1 @@ -215,3 +307,14 @@ jobs: uses: codecov/codecov-action@v1 with: file: /home/runner/build/coverage.info + + docfx_check: + name: DocFX check + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: install docfx + run: choco install docfx -y + - name: run ./ci/docfx.cmd + shell: cmd + run: ./ci/docfx.cmd diff --git a/.gitignore b/.gitignore index ae5b7926b2..37d1350329 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,11 @@ # Bazel files /bazel-* + +# Output directories +/out +/out.* +# Indicator that the tools were deployed +.buildtools +# Tools module +/tools/vcpkg diff --git a/.gitmodules b/.gitmodules index 94dac0ba74..005d9cd1e0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,22 @@ [submodule "third_party/prometheus-cpp"] path = third_party/prometheus-cpp url = https://github.com/jupp0r/prometheus-cpp.git + +[submodule "tools/vcpkg"] + path = tools/vcpkg + url = https://github.com/Microsoft/vcpkg + branch = master + +[submodule "third_party/ms-gsl"] + path = "third_party/ms-gsl" + url = https://github.com/microsoft/GSL + branch = master + +[submodule "third_party/googletest"] + path = third_party/googletest + url = https://github.com/google/googletest + branch = master + +[submodule "third_party/benchmark"] + path = third_party/benchmark + url = https://github.com/google/benchmark diff --git a/CMakeLists.txt b/CMakeLists.txt index 78ff1b5c89..313657d5fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,18 +10,62 @@ if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 11) endif() +option(WITH_STL "Whether to use Standard Library for C++latest features" OFF) + +option(WITH_ABSEIL "Whether to use Abseil for C++latest features" OFF) + +if(WITH_ABSEIL) + add_definitions(-DHAVE_ABSEIL) + find_package(absl CONFIG REQUIRED) + + # Abseil headers-only lib is needed for absl::variant to work vs2015. + # `nostd::mpark::variant` is not compiling in vs2015. + set(CORE_RUNTIME_LIBS absl::any absl::base absl::bits absl::city) + + # target_link_libraries(main PRIVATE absl::any absl::base absl::bits + # absl::city) +endif() + +if(WITH_STL) + + # Require at least C++17. C++20 is needed to avoid gsl::span + add_definitions(-DHAVE_CPP_STDLIB -DHAVE_GSL) + + # Ask for 20, but may get C++17 + set(CMAKE_CXX_STANDARD 20) + + # Guidelines Support Library path. Used if we are not on not get C++20. + # + # TODO: respect WITH_ABSEIL as alternate implementation of std::span + set(GSL_DIR third_party/ms-gsl) + include_directories(${GSL_DIR}/include) + + # Optimize for speed to reduce the hops + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS_SPEED "/O2") + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} /Zc:__cplusplus ${CMAKE_CXX_FLAGS_SPEED}") + endif() + +endif() + option(WITH_OTPROTOCOL "Whether to include the OpenTelemetry Protocol in the SDK" OFF) option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" OFF) +option(WITH_TESTS "Whether to enable tests" ON) +option(WITH_EXAMPLES "Whether to build examples" ON) + set(WITH_PROTOBUF OFF) if(WITH_OTPROTOCOL) set(WITH_PROTOBUF ON) endif() -include(CTest) +if(WITH_TESTS) + include(CTest) +endif() find_package(Threads) @@ -58,5 +102,7 @@ include_directories(sdk) add_subdirectory(sdk) include_directories(.) add_subdirectory(exporters) -add_subdirectory(examples) +if(WITH_EXAMPLES) + add_subdirectory(examples) +endif() add_subdirectory(ext) diff --git a/README.md b/README.md index 4cc0df677b..2c17a0cd9c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,17 @@ The C++ [OpenTelemetry](https://opentelemetry.io/) client. +## Supported C++ Versions + +Code shipped from this repository generally supports the following versions of +C++ standards: + +* ISO/IEC 14882:2011 (C++11, C++0x) +* ISO/IEC 14882:2014 (C++14, C++1y) +* ISO/IEC 14882:2017 (C++17, C++1z) + +Any exceptions to this are noted in the individual `README.md` files. + ## Installation TBD @@ -19,7 +30,11 @@ TBD See [CONTRIBUTING.md](CONTRIBUTING.md) -We meet weekly on Mondays at 3:00PM PT. Check the [OpenTelemetry community calendar](https://calendar.google.com/calendar/embed?src=google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com) for specific dates. +We meet weekly, and the time of the meeting alternates between Monday at 15:00 +PT and Wednesday at 10:00 PT. The meeting is subject to change depending on +contributors' availability. Check the [OpenTelemetry community +calendar](https://calendar.google.com/calendar/embed?src=google.com_b79e3e90j7bbsa2n2p5an5lf60%40group.calendar.google.com) +for specific dates. Meetings take place via [Zoom video conference](https://zoom.us/j/8203130519). @@ -27,9 +42,11 @@ Meeting notes are available as a public [Google doc](https://docs.google.com/doc Approvers ([@open-telemetry/cpp-approvers](https://github.com/orgs/open-telemetry/teams/cpp-approvers)): -- [Max Golovanov](https://github.com/maxgolov), Microsoft +- [Lalit Kumar Bhasin](https://github.com/lalitb), Microsoft - [Johannes Tax](https://github.com/pyohannes), New Relic +- [Max Golovanov](https://github.com/maxgolov), Microsoft - [Ryan Burn](https://github.com/rnburn), Lightstep +- [Tom Tan](https://github.com/ThomsonTan), Microsoft *Find more about the approver role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#approver).* @@ -38,6 +55,10 @@ Maintainers ([@open-telemetry/cpp-maintainers](https://github.com/orgs/open-tele - [Emil Mikulic](https://github.com/g-easy), Google - [Reiley Yang](https://github.com/reyang), Microsoft +Triagers ([@open-telemetry/cpp-triagers](https://github.com/orgs/open-telemetry/teams/cpp-triagers)): + +- [Jodee Varney](https://github.com/jodeev), New Relic + *Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#maintainer).* ## Release Schedule diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index e446300a03..2f6eed51fb 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -12,3 +12,9 @@ install( if(BUILD_TESTING) add_subdirectory(test) endif() + +if(WITH_STL) + message("Building with standard library types...") +else() + message("Building with nostd types...") +endif() diff --git a/api/include/opentelemetry/common/attribute_value.h b/api/include/opentelemetry/common/attribute_value.h index e68b6d8bb1..3df800e187 100644 --- a/api/include/opentelemetry/common/attribute_value.h +++ b/api/include/opentelemetry/common/attribute_value.h @@ -10,20 +10,27 @@ OPENTELEMETRY_BEGIN_NAMESPACE namespace common { -using AttributeValue = nostd::variant, - nostd::span, - nostd::span, - nostd::span, - nostd::span, - nostd::span, - nostd::span>; +using AttributeValue = + nostd::variant, +#endif + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span>; enum AttributeType { @@ -35,7 +42,9 @@ enum AttributeType TYPE_DOUBLE, TYPE_STRING, TYPE_CSTRING, - // TYPE_SPAN_BYTE, // TODO: not part of OT spec yet +#if HAVE_SPAN_BYTE + TYPE_SPAN_BYTE, +#endif TYPE_SPAN_BOOL, TYPE_SPAN_INT, TYPE_SPAN_INT64, diff --git a/api/include/opentelemetry/trace/key_value_iterable.h b/api/include/opentelemetry/common/key_value_iterable.h similarity index 68% rename from api/include/opentelemetry/trace/key_value_iterable.h rename to api/include/opentelemetry/common/key_value_iterable.h index 8b1f2e1124..0742082e56 100644 --- a/api/include/opentelemetry/trace/key_value_iterable.h +++ b/api/include/opentelemetry/common/key_value_iterable.h @@ -5,7 +5,7 @@ #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE -namespace trace +namespace common { /** * Supports internal iteration over a collection of key-value pairs. @@ -30,5 +30,23 @@ class KeyValueIterable */ virtual size_t size() const noexcept = 0; }; -} // namespace trace + +// +// NULL object pattern empty iterable. +// +class NullKeyValueIterable : public KeyValueIterable +{ +public: + NullKeyValueIterable(){}; + + virtual bool ForEachKeyValue( + nostd::function_ref) const noexcept + { + return true; + }; + + virtual size_t size() const noexcept { return 0; } +}; + +} // namespace common OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/key_value_iterable_view.h b/api/include/opentelemetry/common/key_value_iterable_view.h similarity index 88% rename from api/include/opentelemetry/trace/key_value_iterable_view.h rename to api/include/opentelemetry/common/key_value_iterable_view.h index 2daf83c713..6aa300d933 100644 --- a/api/include/opentelemetry/trace/key_value_iterable_view.h +++ b/api/include/opentelemetry/common/key_value_iterable_view.h @@ -4,12 +4,12 @@ #include #include +#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/nostd/utility.h" -#include "opentelemetry/trace/key_value_iterable.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE -namespace trace +namespace common { namespace detail { @@ -33,7 +33,6 @@ struct is_key_value_iterable template class KeyValueIterableView final : public KeyValueIterable { - static_assert(detail::is_key_value_iterable::value, "Must be a key-value iterable"); public: explicit KeyValueIterableView(const T &container) noexcept : container_{&container} {} @@ -60,5 +59,5 @@ class KeyValueIterableView final : public KeyValueIterable private: const T *container_; }; -} // namespace trace +} // namespace common OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/common/spin_lock_mutex.h b/api/include/opentelemetry/common/spin_lock_mutex.h new file mode 100644 index 0000000000..cd911e47ca --- /dev/null +++ b/api/include/opentelemetry/common/spin_lock_mutex.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace common +{ + +/** + * A Mutex which uses atomic flags and spin-locks instead of halting threads. + * + * This class implements the `BasicLockable` specification: + * https://en.cppreference.com/w/cpp/named_req/BasicLockable + */ +class SpinLockMutex +{ +public: + SpinLockMutex() noexcept {} + ~SpinLockMutex() noexcept = default; + SpinLockMutex(const SpinLockMutex &) = delete; + SpinLockMutex &operator=(const SpinLockMutex &) = delete; + SpinLockMutex &operator=(const SpinLockMutex &) volatile = delete; + + /** + * Blocks until a lock can be obtained for the current thread. + * + * This mutex will spin the current CPU waiting for the lock to be available. This can have + * decent performance in scenarios where there is low lock contention and lock-holders acheive + * their work quickly. It degrades in scenarios where locked tasks take a long time. + */ + void lock() noexcept + { + /* Note: We expect code protected by this lock to be "fast", i.e. we do NOT incrementally + * back-off and wait/notify here, we just loop until we have access, then try again. + * + * This has the downside that we could be spinning a long time if the exporter is slow. + * Note: in C++20x we could use `.wait` to make this slightly better. This should move to + * an exponential-back-off / wait strategy. + */ + while (flag_.test_and_set(std::memory_order_acquire)) + /** TODO - We should immmediately yield if the machine is single processor. */ + ; + } + /** Releases the lock held by the execution agent. Throws no exceptions. */ + void unlock() noexcept { flag_.clear(std::memory_order_release); } + +private: + std::atomic_flag flag_{ATOMIC_FLAG_INIT}; +}; + +} // namespace common +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/context/context.h b/api/include/opentelemetry/context/context.h index a1379cdc64..ac2f9c0b23 100644 --- a/api/include/opentelemetry/context/context.h +++ b/api/include/opentelemetry/context/context.h @@ -1,5 +1,6 @@ #pragma once +#include #include "opentelemetry/context/context_value.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" @@ -38,8 +39,8 @@ class Context template Context SetValues(T &values) noexcept { - Context context = Context(values); - nostd::shared_ptr &last = context.head_; + Context context = Context(values); + nostd::shared_ptr last = context.head_; while (last->next_ != nullptr) { last = last->next_; @@ -65,7 +66,7 @@ class Context { if (key.size() == data->key_length_) { - if (memcmp(key.data(), data->key_, data->key_length_) == 0) + if (std::memcmp(key.data(), data->key_, data->key_length_) == 0) { return data->value_; } @@ -81,7 +82,7 @@ class Context { if (key.size() == data->key_length_) { - if (memcmp(key.data(), data->key_, data->key_length_) == 0) + if (std::memcmp(key.data(), data->key_, data->key_length_) == 0) { return true; } @@ -97,10 +98,10 @@ class Context class DataList { public: - nostd::shared_ptr next_; - char *key_; + nostd::shared_ptr next_; + size_t key_length_; ContextValue value_; @@ -109,7 +110,7 @@ class Context // Builds a data list off of a key and value iterable and returns the head template - DataList(const T &keys_and_vals) : key_{nullptr} + DataList(const T &keys_and_vals) : key_{nullptr}, next_(nostd::shared_ptr{nullptr}) { bool first = true; auto *node = this; diff --git a/api/include/opentelemetry/context/runtime_context.h b/api/include/opentelemetry/context/runtime_context.h index cc4b3bb485..b2c65a0fe3 100644 --- a/api/include/opentelemetry/context/runtime_context.h +++ b/api/include/opentelemetry/context/runtime_context.h @@ -15,7 +15,7 @@ class Token bool operator==(const Context &other) noexcept { return context_ == other; } private: - friend class RuntimeContext; + friend class RuntimeContextStorage; // The ContextDetacher object automatically attempts to detach // the Token when all copies of the Token are out of scope. @@ -45,28 +45,64 @@ class Token nostd::shared_ptr detacher_; }; -// Provides a wrapper for propagating the context object globally. In order -// to use either the threadlocal_context.h file must be included or another -// implementation which must be derived from the RuntimeContext can be -// provided. +/** + * RuntimeContextStorage is used by RuntimeContext to store Context frames. + * + * Custom context management strategies can be implemented by deriving from + * this class and passing an initialized RuntimeContextStorage object to + * RuntimeContext::SetRuntimeContextStorage. + */ +class RuntimeContextStorage +{ +public: + /** + * Return the current context. + * @return the current context + */ + virtual Context GetCurrent() noexcept = 0; + + /** + * Set the current context. + * @param the new current context + */ + virtual Token Attach(Context context) noexcept = 0; + + /** + * Detach the context related to the given token. + * @param token a token related to a context + * @return true if the context could be detached + */ + virtual bool Detach(Token &token) noexcept = 0; + +protected: + Token CreateToken(Context context) noexcept { return Token(context); } +}; + +/** + * Construct and return the default RuntimeContextStorage + * @return a ThreadLocalContextStorage + */ +static RuntimeContextStorage *GetDefaultStorage() noexcept; + +// Provides a wrapper for propagating the context object globally. +// +// By default, a thread-local runtime context storage is used. class RuntimeContext { public: // Return the current context. - static Context GetCurrent() noexcept { return context_handler_->InternalGetCurrent(); } + static Context GetCurrent() noexcept { return GetRuntimeContextStorage()->GetCurrent(); } // Sets the current 'Context' object. Returns a token // that can be used to reset to the previous Context. static Token Attach(Context context) noexcept { - return context_handler_->InternalAttach(context); + return GetRuntimeContextStorage()->Attach(context); } // Resets the context to a previous value stored in the // passed in token. Returns true if successful, false otherwise - static bool Detach(Token &token) noexcept { return context_handler_->InternalDetach(token); } - - static RuntimeContext *context_handler_; + static bool Detach(Token &token) noexcept { return GetRuntimeContextStorage()->Detach(token); } // Sets the Key and Value into the passed in context or if a context is not // passed in, the RuntimeContext. @@ -108,15 +144,31 @@ class RuntimeContext return temp_context.GetValue(key); } -protected: - // Provides a token with the passed in context - Token CreateToken(Context context) noexcept { return Token(context); } - - virtual Context InternalGetCurrent() noexcept = 0; + /** + * Provide a custom runtime context storage. + * + * This provides a possibility to override the default thread-local runtime + * context storage. This has to be set before any spans are created by the + * application, otherwise the behavior is undefined. + * + * @param storage a custom runtime context storage + */ + static void SetRuntimeContextStorage(nostd::shared_ptr storage) noexcept + { + GetStorage() = storage; + } - virtual Token InternalAttach(Context context) noexcept = 0; +private: + static nostd::shared_ptr GetRuntimeContextStorage() noexcept + { + return GetStorage(); + } - virtual bool InternalDetach(Token &token) noexcept = 0; + static nostd::shared_ptr &GetStorage() noexcept + { + static nostd::shared_ptr context(GetDefaultStorage()); + return context; + } }; inline Token::ContextDetacher::~ContextDetacher() @@ -125,5 +177,115 @@ inline Token::ContextDetacher::~ContextDetacher() token.context_ = context_; context::RuntimeContext::Detach(token); } + +// The ThreadLocalContextStorage class is a derived class from +// RuntimeContextStorage and provides a wrapper for propogating context through +// cpp thread locally. This file must be included to use the RuntimeContext +// class if another implementation has not been registered. +class ThreadLocalContextStorage : public RuntimeContextStorage +{ +public: + ThreadLocalContextStorage() noexcept = default; + + // Return the current context. + Context GetCurrent() noexcept override { return GetStack().Top(); } + + // Resets the context to a previous value stored in the + // passed in token. Returns true if successful, false otherwise + bool Detach(Token &token) noexcept override + { + if (!(token == GetStack().Top())) + { + return false; + } + GetStack().Pop(); + return true; + } + + // Sets the current 'Context' object. Returns a token + // that can be used to reset to the previous Context. + Token Attach(Context context) noexcept override + { + GetStack().Push(context); + Token old_context = CreateToken(context); + return old_context; + } + +private: + // A nested class to store the attached contexts in a stack. + class Stack + { + friend class ThreadLocalContextStorage; + + Stack() noexcept : size_(0), capacity_(0), base_(nullptr){}; + + // Pops the top Context off the stack and returns it. + Context Pop() noexcept + { + if (size_ == 0) + { + return Context(); + } + size_ -= 1; + return base_[size_]; + } + + // Returns the Context at the top of the stack. + Context Top() const noexcept + { + if (size_ == 0) + { + return Context(); + } + return base_[size_ - 1]; + } + + // Pushes the passed in context pointer to the top of the stack + // and resizes if necessary. + void Push(Context context) noexcept + { + size_++; + if (size_ > capacity_) + { + Resize(size_ * 2); + } + base_[size_ - 1] = context; + } + + // Reallocates the storage array to the pass in new capacity size. + void Resize(size_t new_capacity) noexcept + { + size_t old_size = size_ - 1; + if (new_capacity == 0) + { + new_capacity = 2; + } + Context *temp = new Context[new_capacity]; + if (base_ != nullptr) + { + std::copy(base_, base_ + old_size, temp); + delete[] base_; + } + base_ = temp; + } + + ~Stack() noexcept { delete[] base_; } + + size_t size_; + size_t capacity_; + Context *base_; + }; + + Stack &GetStack() + { + static thread_local Stack stack_ = Stack(); + return stack_; + } +}; + +static RuntimeContextStorage *GetDefaultStorage() noexcept +{ + return new ThreadLocalContextStorage(); +} } // namespace context OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/context/threadlocal_context.h b/api/include/opentelemetry/context/threadlocal_context.h deleted file mode 100644 index febd89e63a..0000000000 --- a/api/include/opentelemetry/context/threadlocal_context.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "opentelemetry/context/context.h" -#include "opentelemetry/context/runtime_context.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace context -{ - -// The ThreadLocalContext class is a derived class from RuntimeContext and -// provides a wrapper for propogating context through cpp thread locally. -// This file must be included to use the RuntimeContext class if another -// implementation has not been registered. -class ThreadLocalContext : public RuntimeContext -{ -public: - ThreadLocalContext() noexcept = default; - - // Return the current context. - Context InternalGetCurrent() noexcept override { return stack_.Top(); } - - // Resets the context to a previous value stored in the - // passed in token. Returns true if successful, false otherwise - bool InternalDetach(Token &token) noexcept override - { - if (!(token == stack_.Top())) - { - return false; - } - stack_.Pop(); - return true; - } - - // Sets the current 'Context' object. Returns a token - // that can be used to reset to the previous Context. - Token InternalAttach(Context context) noexcept override - { - stack_.Push(context); - Token old_context = CreateToken(context); - return old_context; - } - -private: - // A nested class to store the attached contexts in a stack. - class Stack - { - friend class ThreadLocalContext; - - Stack() noexcept : size_(0), capacity_(0), base_(nullptr){}; - - // Pops the top Context off the stack and returns it. - Context Pop() noexcept - { - if (size_ <= 0) - { - return Context(); - } - int index = size_ - 1; - size_--; - return base_[index]; - } - - // Returns the Context at the top of the stack. - Context Top() const noexcept - { - if (size_ <= 0) - { - return Context(); - } - return base_[size_ - 1]; - } - - // Pushes the passed in context pointer to the top of the stack - // and resizes if necessary. - void Push(Context context) noexcept - { - size_++; - if (size_ > capacity_) - { - Resize(size_ * 2); - } - base_[size_ - 1] = context; - } - - // Reallocates the storage array to the pass in new capacity size. - void Resize(int new_capacity) noexcept - { - int old_size = size_ - 1; - if (new_capacity == 0) - { - new_capacity = 2; - } - Context *temp = new Context[new_capacity]; - if (base_ != nullptr) - { - std::copy(base_, base_ + old_size, temp); - delete[] base_; - } - base_ = temp; - } - - ~Stack() noexcept { delete[] base_; } - - size_t size_; - size_t capacity_; - Context *base_; - }; - - static thread_local Stack stack_; -}; -thread_local ThreadLocalContext::Stack ThreadLocalContext::stack_ = ThreadLocalContext::Stack(); - -// Registers the ThreadLocalContext as the context handler for the RuntimeContext -RuntimeContext *RuntimeContext::context_handler_ = new ThreadLocalContext(); -} // namespace context -OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/metrics/async_instruments.h b/api/include/opentelemetry/metrics/async_instruments.h index bfe43015f3..24eab3467e 100644 --- a/api/include/opentelemetry/metrics/async_instruments.h +++ b/api/include/opentelemetry/metrics/async_instruments.h @@ -28,7 +28,7 @@ class ValueObserver : virtual public AsynchronousInstrument * @param value is the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void observe(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void observe(T value, const common::KeyValueIterable &labels) override = 0; /** * Captures data by activating the callback function associated with the @@ -55,7 +55,7 @@ class SumObserver : virtual public AsynchronousInstrument void (*callback)(ObserverResult)) {} - virtual void observe(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void observe(T value, const common::KeyValueIterable &labels) override = 0; virtual void run() override = 0; }; @@ -74,7 +74,7 @@ class UpDownSumObserver : virtual public AsynchronousInstrument void (*callback)(ObserverResult)) {} - virtual void observe(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void observe(T value, const common::KeyValueIterable &labels) override = 0; virtual void run() override = 0; }; diff --git a/api/include/opentelemetry/metrics/instrument.h b/api/include/opentelemetry/metrics/instrument.h index 2a7ac8b6d2..45d8339481 100644 --- a/api/include/opentelemetry/metrics/instrument.h +++ b/api/include/opentelemetry/metrics/instrument.h @@ -2,9 +2,9 @@ #include #include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable_view.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/trace/key_value_iterable_view.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace metrics @@ -135,7 +135,7 @@ class SynchronousInstrument : virtual public Instrument * @return a Bound Instrument */ virtual nostd::shared_ptr> bind( - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { return nostd::shared_ptr>(); } @@ -152,7 +152,7 @@ class SynchronousInstrument : virtual public Instrument * @param value is the numerical representation of the metric being captured * @return void */ - virtual void update(T value, const trace::KeyValueIterable &labels) = 0; + virtual void update(T value, const common::KeyValueIterable &labels) = 0; }; template @@ -181,7 +181,7 @@ class AsynchronousInstrument : virtual public Instrument * @param labels is the numerical representation of the metric being captured * @return none */ - virtual void observe(T value, const trace::KeyValueIterable &labels) = 0; + virtual void observe(T value, const common::KeyValueIterable &labels) = 0; /** * Captures data by activating the callback function associated with the diff --git a/api/include/opentelemetry/metrics/meter.h b/api/include/opentelemetry/metrics/meter.h index c58c489f2a..3d8c2e037d 100644 --- a/api/include/opentelemetry/metrics/meter.h +++ b/api/include/opentelemetry/metrics/meter.h @@ -263,19 +263,19 @@ class Meter * @param values a span of values to record to the instruments in the corresponding * position in the instruments span. */ - virtual void RecordShortBatch(const trace::KeyValueIterable &labels, + virtual void RecordShortBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept = 0; - virtual void RecordIntBatch(const trace::KeyValueIterable &labels, + virtual void RecordIntBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept = 0; - virtual void RecordFloatBatch(const trace::KeyValueIterable &labels, + virtual void RecordFloatBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept = 0; - virtual void RecordDoubleBatch(const trace::KeyValueIterable &labels, + virtual void RecordDoubleBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept = 0; }; diff --git a/api/include/opentelemetry/metrics/noop.h b/api/include/opentelemetry/metrics/noop.h index c5569fd4e9..552e125dd0 100644 --- a/api/include/opentelemetry/metrics/noop.h +++ b/api/include/opentelemetry/metrics/noop.h @@ -15,6 +15,9 @@ #include +// TODO: Create generic short pattern for opentelemetry::common and opentelemetry::trace and others +// as necessary + OPENTELEMETRY_BEGIN_NAMESPACE namespace metrics { @@ -38,7 +41,7 @@ class NoopValueObserver : public ValueObserver virtual nostd::string_view GetUnits() override { return nostd::string_view(""); } - virtual void observe(T value, const trace::KeyValueIterable &labels) override {} + virtual void observe(T value, const common::KeyValueIterable &labels) override {} virtual void run() override {} @@ -65,7 +68,7 @@ class NoopSumObserver : public SumObserver virtual nostd::string_view GetUnits() override { return nostd::string_view(""); } - virtual void observe(T value, const trace::KeyValueIterable &labels) override {} + virtual void observe(T value, const common::KeyValueIterable &labels) override {} virtual void run() override {} @@ -92,7 +95,7 @@ class NoopUpDownSumObserver : public UpDownSumObserver virtual nostd::string_view GetUnits() override { return nostd::string_view(""); } - virtual void observe(T value, const trace::KeyValueIterable &labels) override {} + virtual void observe(T value, const common::KeyValueIterable &labels) override {} virtual void run() override {} @@ -138,14 +141,15 @@ class NoopCounter : public Counter bool /*enabled*/) {} - nostd::shared_ptr> bindNoopCounter(const trace::KeyValueIterable & /*labels*/) + nostd::shared_ptr> bindNoopCounter( + const common::KeyValueIterable & /*labels*/) { return nostd::shared_ptr>(new BoundNoopCounter()); } - virtual void add(T value, const trace::KeyValueIterable & /*labels*/) override {} + virtual void add(T value, const common::KeyValueIterable & /*labels*/) override {} - virtual void update(T value, const trace::KeyValueIterable & /*labels*/) override {} + virtual void update(T value, const common::KeyValueIterable & /*labels*/) override {} virtual bool IsEnabled() override { return false; } @@ -198,14 +202,14 @@ class NoopUpDownCounter : public UpDownCounter {} nostd::shared_ptr> bindNoopUpDownCounter( - const trace::KeyValueIterable & /*labels*/) + const common::KeyValueIterable & /*labels*/) { return nostd::shared_ptr>(new BoundNoopUpDownCounter()); } - virtual void add(T value, const trace::KeyValueIterable & /*labels*/) override {} + virtual void add(T value, const common::KeyValueIterable & /*labels*/) override {} - virtual void update(T value, const trace::KeyValueIterable & /*labels*/) override {} + virtual void update(T value, const common::KeyValueIterable & /*labels*/) override {} virtual bool IsEnabled() override { return false; } @@ -258,14 +262,14 @@ class NoopValueRecorder : public ValueRecorder {} nostd::shared_ptr> bindNoopValueRecorder( - const trace::KeyValueIterable & /*labels*/) + const common::KeyValueIterable & /*labels*/) { return nostd::shared_ptr>(new BoundNoopValueRecorder()); } - virtual void record(T value, const trace::KeyValueIterable & /*labels*/) override {} + virtual void record(T value, const common::KeyValueIterable & /*labels*/) override {} - virtual void update(T value, const trace::KeyValueIterable & /*labels*/) override {} + virtual void update(T value, const common::KeyValueIterable & /*labels*/) override {} virtual bool IsEnabled() override { return false; } @@ -296,36 +300,36 @@ class NoopMeter : public Meter * @param enabled a boolean that turns the metric instrument on and off. * @return */ - opentelemetry::nostd::shared_ptr> NewShortCounter(nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewShortCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopCounter(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewIntCounter(nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewIntCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{new NoopCounter(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewFloatCounter(nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewFloatCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopCounter(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewDoubleCounter(nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewDoubleCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopCounter(name, description, unit, enabled)}; @@ -341,41 +345,37 @@ class NoopMeter : public Meter * @param enabled a boolean that turns the metric instrument on and off. * @return */ - opentelemetry::nostd::shared_ptr> NewShortUpDownCounter( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewShortUpDownCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopUpDownCounter(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewIntUpDownCounter( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewIntUpDownCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopUpDownCounter(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewFloatUpDownCounter( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewFloatUpDownCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopUpDownCounter(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewDoubleUpDownCounter( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewDoubleUpDownCounter(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopUpDownCounter(name, description, unit, enabled)}; @@ -391,41 +391,37 @@ class NoopMeter : public Meter * @param enabled a boolean that turns the metric instrument on and off. * @return */ - opentelemetry::nostd::shared_ptr> NewShortValueRecorder( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewShortValueRecorder(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopValueRecorder(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewIntValueRecorder( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewIntValueRecorder(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopValueRecorder(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewFloatValueRecorder( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewFloatValueRecorder(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopValueRecorder(name, description, unit, enabled)}; } - opentelemetry::nostd::shared_ptr> NewDoubleValueRecorder( - nostd::string_view name, - nostd::string_view description, - nostd::string_view unit, - const bool enabled) override + nostd::shared_ptr> NewDoubleValueRecorder(nostd::string_view name, + nostd::string_view description, + nostd::string_view unit, + const bool enabled) override { return nostd::shared_ptr>{ new NoopValueRecorder(name, description, unit, enabled)}; @@ -441,7 +437,7 @@ class NoopMeter : public Meter * @param enabled a boolean that turns the metric instrument on and off. * @return */ - opentelemetry::nostd::shared_ptr> NewShortSumObserver( + nostd::shared_ptr> NewShortSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -452,7 +448,7 @@ class NoopMeter : public Meter new NoopSumObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewIntSumObserver( + nostd::shared_ptr> NewIntSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -463,7 +459,7 @@ class NoopMeter : public Meter new NoopSumObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewFloatSumObserver( + nostd::shared_ptr> NewFloatSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -474,7 +470,7 @@ class NoopMeter : public Meter new NoopSumObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewDoubleSumObserver( + nostd::shared_ptr> NewDoubleSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -495,7 +491,7 @@ class NoopMeter : public Meter * @param enabled a boolean that turns the metric instrument on and off. * @return */ - opentelemetry::nostd::shared_ptr> NewShortUpDownSumObserver( + nostd::shared_ptr> NewShortUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -506,7 +502,7 @@ class NoopMeter : public Meter new NoopUpDownSumObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewIntUpDownSumObserver( + nostd::shared_ptr> NewIntUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -517,7 +513,7 @@ class NoopMeter : public Meter new NoopUpDownSumObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewFloatUpDownSumObserver( + nostd::shared_ptr> NewFloatUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -528,7 +524,7 @@ class NoopMeter : public Meter new NoopUpDownSumObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewDoubleUpDownSumObserver( + nostd::shared_ptr> NewDoubleUpDownSumObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -549,7 +545,7 @@ class NoopMeter : public Meter * @param enabled a boolean that turns the metric instrument on and off. * @return */ - opentelemetry::nostd::shared_ptr> NewShortValueObserver( + nostd::shared_ptr> NewShortValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -560,7 +556,7 @@ class NoopMeter : public Meter new NoopValueObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewIntValueObserver( + nostd::shared_ptr> NewIntValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -571,7 +567,7 @@ class NoopMeter : public Meter new NoopValueObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewFloatValueObserver( + nostd::shared_ptr> NewFloatValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -582,7 +578,7 @@ class NoopMeter : public Meter new NoopValueObserver(name, description, unit, enabled, callback)}; } - opentelemetry::nostd::shared_ptr> NewDoubleValueObserver( + nostd::shared_ptr> NewDoubleValueObserver( nostd::string_view name, nostd::string_view description, nostd::string_view unit, @@ -602,28 +598,28 @@ class NoopMeter : public Meter * @param instrs the instruments to record to. * @param values the value to record to those instruments. */ - void RecordShortBatch(const trace::KeyValueIterable &labels, + void RecordShortBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override { // No-op } - void RecordIntBatch(const trace::KeyValueIterable &labels, + void RecordIntBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override { // No-op } - void RecordFloatBatch(const trace::KeyValueIterable &labels, + void RecordFloatBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override { // No-op } - void RecordDoubleBatch(const trace::KeyValueIterable &labels, + void RecordDoubleBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override { diff --git a/api/include/opentelemetry/metrics/observer_result.h b/api/include/opentelemetry/metrics/observer_result.h index a16b5a3815..c7733c75e4 100644 --- a/api/include/opentelemetry/metrics/observer_result.h +++ b/api/include/opentelemetry/metrics/observer_result.h @@ -23,7 +23,7 @@ class ObserverResult ObserverResult(AsynchronousInstrument *instrument) : instrument_(instrument) {} - virtual void observe(T value, const trace::KeyValueIterable &labels) + virtual void observe(T value, const common::KeyValueIterable &labels) { instrument_->observe(value, labels); } diff --git a/api/include/opentelemetry/metrics/provider.h b/api/include/opentelemetry/metrics/provider.h index 8559562d8d..88b97fdf5f 100644 --- a/api/include/opentelemetry/metrics/provider.h +++ b/api/include/opentelemetry/metrics/provider.h @@ -1,7 +1,8 @@ #pragma once -#include +#include +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/metrics/meter_provider.h" #include "opentelemetry/metrics/noop.h" #include "opentelemetry/nostd/shared_ptr.h" @@ -23,12 +24,8 @@ class Provider */ static nostd::shared_ptr GetMeterProvider() noexcept { - while (GetLock().test_and_set(std::memory_order_acquire)) - ; - auto provider = nostd::shared_ptr(GetProvider()); - GetLock().clear(std::memory_order_release); - - return provider; + std::lock_guard guard(GetLock()); + return nostd::shared_ptr(GetProvider()); } /** @@ -36,10 +33,8 @@ class Provider */ static void SetMeterProvider(nostd::shared_ptr tp) noexcept { - while (GetLock().test_and_set(std::memory_order_acquire)) - ; + std::lock_guard guard(GetLock()); GetProvider() = tp; - GetLock().clear(std::memory_order_release); } private: @@ -49,9 +44,9 @@ class Provider return provider; } - static std::atomic_flag &GetLock() noexcept + static common::SpinLockMutex &GetLock() noexcept { - static std::atomic_flag lock = ATOMIC_FLAG_INIT; + static common::SpinLockMutex lock; return lock; } }; diff --git a/api/include/opentelemetry/metrics/sync_instruments.h b/api/include/opentelemetry/metrics/sync_instruments.h index 8823d83e4d..c5ab128074 100644 --- a/api/include/opentelemetry/metrics/sync_instruments.h +++ b/api/include/opentelemetry/metrics/sync_instruments.h @@ -48,7 +48,7 @@ class Counter : virtual public SynchronousInstrument * @param labels the set of labels, as key-value pairs. * @return a BoundIntCounter tied to the specified labels */ - virtual nostd::shared_ptr> bindCounter(const trace::KeyValueIterable &labels) + virtual nostd::shared_ptr> bindCounter(const common::KeyValueIterable &labels) { return nostd::shared_ptr>(); } @@ -61,9 +61,9 @@ class Counter : virtual public SynchronousInstrument * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void add(T value, const trace::KeyValueIterable &labels) = 0; + virtual void add(T value, const common::KeyValueIterable &labels) = 0; - virtual void update(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void update(T value, const common::KeyValueIterable &labels) override = 0; }; template @@ -101,7 +101,7 @@ class UpDownCounter : virtual public SynchronousInstrument bool enabled); virtual nostd::shared_ptr> bindUpDownCounter( - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { return nostd::shared_ptr>(); } @@ -114,9 +114,9 @@ class UpDownCounter : virtual public SynchronousInstrument * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void add(T value, const trace::KeyValueIterable &labels) = 0; + virtual void add(T value, const common::KeyValueIterable &labels) = 0; - virtual void update(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void update(T value, const common::KeyValueIterable &labels) override = 0; }; template @@ -154,7 +154,7 @@ class ValueRecorder : virtual public SynchronousInstrument bool enabled); virtual nostd::shared_ptr> bindValueRecorder( - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { return nostd::shared_ptr>(); } @@ -167,9 +167,9 @@ class ValueRecorder : virtual public SynchronousInstrument * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void record(T value, const trace::KeyValueIterable &labels) = 0; + virtual void record(T value, const common::KeyValueIterable &labels) = 0; - virtual void update(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void update(T value, const common::KeyValueIterable &labels) override = 0; }; } // namespace metrics diff --git a/api/include/opentelemetry/nostd/function_ref.h b/api/include/opentelemetry/nostd/function_ref.h index de61c7040a..508cc53029 100644 --- a/api/include/opentelemetry/nostd/function_ref.h +++ b/api/include/opentelemetry/nostd/function_ref.h @@ -1,3 +1,17 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + #pragma once #include diff --git a/api/include/opentelemetry/nostd/mpark/variant.h b/api/include/opentelemetry/nostd/mpark/variant.h new file mode 100644 index 0000000000..097bb57355 --- /dev/null +++ b/api/include/opentelemetry/nostd/mpark/variant.h @@ -0,0 +1,1281 @@ +// MPark.Variant +// +// Copyright Michael Park, 2015-2017 +// Copyright OpenTelemetry Authors, 2020 +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file third_party/boost/LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/detail/all.h" +#include "opentelemetry/nostd/detail/dependent_type.h" +#include "opentelemetry/nostd/detail/find_index.h" +#include "opentelemetry/nostd/detail/functional.h" +#include "opentelemetry/nostd/detail/recursive_union.h" +#include "opentelemetry/nostd/detail/trait.h" +#include "opentelemetry/nostd/detail/type_pack_element.h" +#include "opentelemetry/nostd/detail/variant_alternative.h" +#include "opentelemetry/nostd/detail/variant_fwd.h" +#include "opentelemetry/nostd/detail/variant_size.h" +#include "opentelemetry/nostd/type_traits.h" +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/version.h" + +#define AUTO_RETURN(...) \ + ->decay_t { return __VA_ARGS__; } + +#define AUTO_REFREF_RETURN(...) \ + ->decltype((__VA_ARGS__)) \ + { \ + static_assert(std::is_reference::value, ""); \ + return __VA_ARGS__; \ + } + +#define DECLTYPE_AUTO_RETURN(...) \ + ->decltype(__VA_ARGS__) { return __VA_ARGS__; } + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +constexpr std::size_t variant_npos = static_cast(-1); + +class bad_variant_access : public std::exception +{ +public: + virtual const char *what() const noexcept override { return "bad_variant_access"; } +}; + +[[noreturn]] inline void throw_bad_variant_access() +{ +#if __EXCEPTIONS + throw bad_variant_access{}; +#else + std::terminate(); +#endif +} + +namespace detail +{ +namespace access +{ +struct base +{ + template + inline static constexpr auto get_alt(V &&v) +#ifdef _MSC_VER + AUTO_REFREF_RETURN(recursive_union::get_alt(std::forward(v).data_, in_place_index_t{})) +#else + AUTO_REFREF_RETURN(recursive_union::get_alt(data(std::forward(v)), in_place_index_t{})) +#endif +}; + +struct variant +{ + template + inline static constexpr auto get_alt(V &&v) + AUTO_REFREF_RETURN(base::get_alt(std::forward(v).impl_)) +}; +} // namespace access +} // namespace detail + +namespace detail +{ +namespace visitation +{ + +struct base +{ + template + using dispatch_result_t = decltype( + nostd::invoke(std::declval(), access::base::get_alt<0>(std::declval())...)); + + template + struct expected + { + template + inline static constexpr bool but_got() + { + return std::is_same::value; + } + }; + + template + struct visit_return_type_check + { + static_assert(expected::template but_got(), + "`visit` requires the visitor to have a single return type"); + + template + inline static constexpr auto invoke(Visitor &&visitor, Alts &&... alts) + DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), + std::forward(alts)...)) + }; + + template + inline static constexpr const T &at(const T &elem) noexcept + { + return elem; + } + + template + inline static constexpr const remove_all_extents_t &at(const std::array &elems, + std::size_t i, + Is... is) noexcept + { + return at(elems[i], is...); + } + + template + inline static constexpr std::array, sizeof...(Fs) + 1> make_farray(F &&f, Fs &&... fs) + { + return {{std::forward(f), std::forward(fs)...}}; + } + + template + struct make_fmatrix_impl + { + + template + inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) + { + using Expected = dispatch_result_t; + using Actual = decltype( + nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); + return visit_return_type_check::invoke( + std::forward(f), access::base::get_alt(std::forward(vs))...); + } + + template + struct impl; + + template + struct impl> + { + inline constexpr auto operator()() const AUTO_RETURN(&dispatch) + }; + + template + struct impl, Ls...> + { + inline constexpr auto operator()() const + AUTO_RETURN(make_farray(impl, Ls...>{}()...)) + }; + }; + + template + inline static constexpr auto make_fmatrix() AUTO_RETURN( + typename make_fmatrix_impl:: + template impl, make_index_sequence::size()>...>{}()) + + template + struct make_fdiagonal_impl + { + template + inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) + { + using Expected = dispatch_result_t; + using Actual = decltype( + nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); + return visit_return_type_check::invoke( + std::forward(f), access::base::get_alt(std::forward(vs))...); + } + + template + inline static constexpr auto impl(index_sequence) + AUTO_RETURN(make_farray(&dispatch...)) + }; + + template + inline static constexpr auto make_fdiagonal() + -> decltype(make_fdiagonal_impl::impl(make_index_sequence::size()>{})) + { + static_assert(all<(decay_t::size() == decay_t::size())...>::value, + "all of the variants must be the same size."); + return make_fdiagonal_impl::impl(make_index_sequence::size()>{}); + } +}; + +#if !defined(_MSC_VER) || _MSC_VER >= 1910 +template +using fmatrix_t = decltype(base::make_fmatrix()); + +template +struct fmatrix +{ + static constexpr fmatrix_t value = base::make_fmatrix(); +}; + +template +constexpr fmatrix_t fmatrix::value; + +template +using fdiagonal_t = decltype(base::make_fdiagonal()); + +template +struct fdiagonal +{ + static constexpr fdiagonal_t value = base::make_fdiagonal(); +}; + +template +constexpr fdiagonal_t fdiagonal::value; +#endif + +struct alt +{ + template + inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( + base::at(base::make_fmatrix(vs)))...>(), + vs.index()...)(std::forward(visitor), as_base(std::forward(vs))...)) + + template + inline static constexpr auto visit_alt_at(std::size_t index, Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN(base::at( + base::make_fdiagonal(vs)))...>(), + index)(std::forward(visitor), as_base(std::forward(vs))...)) +}; + +struct variant +{ +private: + template + struct visitor + { + template + inline static constexpr bool does_not_handle() + { + return nostd::is_invocable::value; + } + }; + + template + struct visit_exhaustiveness_check + { + static_assert(visitor::template does_not_handle(), + "`visit` requires the visitor to be exhaustive."); + + inline static constexpr auto invoke(Visitor &&visitor, Values &&... values) + DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), + std::forward(values)...)) + }; + + template + struct value_visitor + { + Visitor &&visitor_; + + template + inline constexpr auto operator()(Alts &&... alts) const DECLTYPE_AUTO_RETURN( + visit_exhaustiveness_check(alts).value))...>::invoke( + std::forward(visitor_), + std::forward(alts).value...)) + }; + + template + inline static constexpr auto make_value_visitor(Visitor &&visitor) + AUTO_RETURN(value_visitor{std::forward(visitor)}) + + public + : template + inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN(alt::visit_alt(std::forward(visitor), + std::forward(vs).impl_...)) + + template + inline static constexpr auto visit_alt_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(alt::visit_alt_at(index, + std::forward(visitor), + std::forward(vs).impl_...)) + + template + inline static constexpr auto visit_value(Visitor &&visitor, Vs &&... vs) + DECLTYPE_AUTO_RETURN( + visit_alt(make_value_visitor(std::forward(visitor)), + std::forward(vs)...)) + + template + inline static constexpr auto visit_value_at(std::size_t index, + Visitor &&visitor, + Vs &&... vs) + DECLTYPE_AUTO_RETURN(visit_alt_at( + index, + make_value_visitor(std::forward(visitor)), + std::forward(vs)...)) +}; +} // namespace visitation + +template +using index_t = typename std::conditional< + sizeof...(Ts) < (std::numeric_limits::max)(), + unsigned char, + typename std::conditional::max)(), + unsigned short, + unsigned int>::type>::type; + +template +class base +{ +public: + inline explicit constexpr base(valueless_t tag) noexcept + : data_(tag), index_(static_cast>(-1)) + {} + + template + inline explicit constexpr base(in_place_index_t, Args &&... args) + : data_(in_place_index_t{}, std::forward(args)...), index_(I) + {} + + inline constexpr bool valueless_by_exception() const noexcept + { + return index_ == static_cast>(-1); + } + + inline constexpr std::size_t index() const noexcept + { + return valueless_by_exception() ? variant_npos : index_; + } + +protected: + using data_t = recursive_union; + + friend inline constexpr base &as_base(base &b) { return b; } + friend inline constexpr const base &as_base(const base &b) { return b; } + friend inline constexpr base &&as_base(base &&b) { return std::move(b); } + friend inline constexpr const base &&as_base(const base &&b) { return std::move(b); } + + friend inline constexpr data_t &data(base &b) { return b.data_; } + friend inline constexpr const data_t &data(const base &b) { return b.data_; } + friend inline constexpr data_t &&data(base &&b) { return std::move(b).data_; } + friend inline constexpr const data_t &&data(const base &&b) { return std::move(b).data_; } + + inline static constexpr std::size_t size() { return sizeof...(Ts); } + + data_t data_; + index_t index_; + + friend struct access::base; + friend struct visitation::base; +}; + +struct dtor +{ +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4100) +#endif + template + inline void operator()(Alt &alt) const noexcept + { + alt.~Alt(); + } +#ifdef _MSC_VER +# pragma warning(pop) +#endif +}; + +template +class destructor; + +#define OPENTELEMETRY_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ + template \ + class destructor, destructible_trait> : public base \ + { \ + using super = base; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + destructor(const destructor &) = default; \ + destructor(destructor &&) = default; \ + definition destructor &operator=(const destructor &) = default; \ + destructor &operator=(destructor &&) = default; \ + \ + protected: \ + destroy \ + } + +OPENTELEMETRY_VARIANT_DESTRUCTOR( + Trait::TriviallyAvailable, ~destructor() = default; + , inline void destroy() noexcept { this->index_ = static_cast>(-1); }); + +OPENTELEMETRY_VARIANT_DESTRUCTOR( + Trait::Available, + ~destructor() { destroy(); }, + inline void destroy() noexcept { + if (!this->valueless_by_exception()) + { + visitation::alt::visit_alt(dtor{}, *this); + } + this->index_ = static_cast>(-1); + }); + +OPENTELEMETRY_VARIANT_DESTRUCTOR(Trait::Unavailable, ~destructor() = delete; + , inline void destroy() noexcept = delete;); + +#undef OPENTELEMETRY_VARIANT_DESTRUCTOR + +template +class constructor : public destructor +{ + using super = destructor; + +public: + using super::super; + using super::operator=; + +protected: + struct ctor + { + template + inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const + { + constructor::construct_alt(lhs_alt, std::forward(rhs_alt).value); + } + }; + + template + inline static T &construct_alt(alt &a, Args &&... args) + { + auto *result = ::new (static_cast(std::addressof(a))) + alt(in_place_t{}, std::forward(args)...); + return result->value; + } + + template + inline static void generic_construct(constructor &lhs, Rhs &&rhs) + { + lhs.destroy(); + if (!rhs.valueless_by_exception()) + { + visitation::alt::visit_alt_at(rhs.index(), ctor{}, lhs, std::forward(rhs)); + lhs.index_ = rhs.index_; + } + } +}; + +template +class move_constructor; + +#define OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ + template \ + class move_constructor, move_constructible_trait> \ + : public constructor> \ + { \ + using super = constructor>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + move_constructor(const move_constructor &) = default; \ + definition ~move_constructor() = default; \ + move_constructor &operator=(const move_constructor &) = default; \ + move_constructor &operator=(move_constructor &&) = default; \ + } + +OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::TriviallyAvailable, + move_constructor(move_constructor &&that) = default;); + +OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR( + Trait::Available, + move_constructor(move_constructor &&that) noexcept( + all::value...>::value) + : move_constructor(valueless_t{}) { this->generic_construct(*this, std::move(that)); }); + +OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::Unavailable, + move_constructor(move_constructor &&) = delete;); + +#undef OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR + +template +class copy_constructor; + +#define OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ + template \ + class copy_constructor, copy_constructible_trait> \ + : public move_constructor> \ + { \ + using super = move_constructor>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + definition copy_constructor(copy_constructor &&) = default; \ + ~copy_constructor() = default; \ + copy_constructor &operator=(const copy_constructor &) = default; \ + copy_constructor &operator=(copy_constructor &&) = default; \ + } + +OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::TriviallyAvailable, + copy_constructor(const copy_constructor &that) = default;); + +OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR( + Trait::Available, copy_constructor(const copy_constructor &that) + : copy_constructor(valueless_t{}) { this->generic_construct(*this, that); }); + +OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::Unavailable, + copy_constructor(const copy_constructor &) = delete;); + +#undef OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR + +template +class assignment : public copy_constructor +{ + using super = copy_constructor; + +public: + using super::super; + using super::operator=; + + template + inline /* auto & */ auto emplace(Args &&... args) + -> decltype(this->construct_alt(access::base::get_alt(*this), std::forward(args)...)) + { + this->destroy(); + auto &result = + this->construct_alt(access::base::get_alt(*this), std::forward(args)...); + this->index_ = I; + return result; + } + +protected: + template + struct assigner + { + template + inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const + { + self->assign_alt(this_alt, std::forward(that_alt).value); + } + assignment *self; + }; + + template + inline void assign_alt(alt &a, Arg &&arg) + { + if (this->index() == I) + { +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4244) +#endif + a.value = std::forward(arg); +#ifdef _MSC_VER +# pragma warning(pop) +#endif + } + else + { + struct + { + void operator()(std::true_type) const { this_->emplace(std::forward(arg_)); } + void operator()(std::false_type) const { this_->emplace(T(std::forward(arg_))); } + assignment *this_; + Arg &&arg_; + } impl{this, std::forward(arg)}; + impl(bool_constant < std::is_nothrow_constructible::value || + !std::is_nothrow_move_constructible::value > {}); + } + } + + template + inline void generic_assign(That &&that) + { + if (this->valueless_by_exception() && that.valueless_by_exception()) + { + // do nothing. + } + else if (that.valueless_by_exception()) + { + this->destroy(); + } + else + { + visitation::alt::visit_alt_at(that.index(), assigner{this}, *this, + std::forward(that)); + } + } +}; + +template +class move_assignment; + +#define OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ + template \ + class move_assignment, move_assignable_trait> : public assignment> \ + { \ + using super = assignment>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + move_assignment(const move_assignment &) = default; \ + move_assignment(move_assignment &&) = default; \ + ~move_assignment() = default; \ + move_assignment &operator=(const move_assignment &) = default; \ + definition \ + } + +OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( + Trait::TriviallyAvailable, move_assignment &operator=(move_assignment &&that) = default;); + +OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( + Trait::Available, + move_assignment & + operator=(move_assignment &&that) noexcept( + all<(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value)...>::value) { + this->generic_assign(std::move(that)); + return *this; + }); + +OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(Trait::Unavailable, + move_assignment &operator=(move_assignment &&) = delete;); + +#undef OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT + +template +class copy_assignment; + +#define OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ + template \ + class copy_assignment, copy_assignable_trait> \ + : public move_assignment> \ + { \ + using super = move_assignment>; \ + \ + public: \ + using super::super; \ + using super::operator=; \ + \ + copy_assignment(const copy_assignment &) = default; \ + copy_assignment(copy_assignment &&) = default; \ + ~copy_assignment() = default; \ + definition copy_assignment &operator=(copy_assignment &&) = default; \ + } + +OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( + Trait::TriviallyAvailable, copy_assignment &operator=(const copy_assignment &that) = default;); + +OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( + Trait::Available, + copy_assignment & + operator=(const copy_assignment &that) { + this->generic_assign(that); + return *this; + }); + +OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( + Trait::Unavailable, copy_assignment &operator=(const copy_assignment &) = delete;); + +#undef OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT +template +class impl : public copy_assignment> +{ + using super = copy_assignment>; + +public: + using super::super; + using super::operator=; + + impl(const impl &) = default; + impl(impl &&) = default; + ~impl() = default; + impl &operator=(const impl &) = default; + impl &operator=(impl &&) = default; + + template + inline void assign(Arg &&arg) + { + this->assign_alt(access::base::get_alt(*this), std::forward(arg)); + } + + inline void swap(impl &that) + { + if (this->valueless_by_exception() && that.valueless_by_exception()) + { + // do nothing. + } + else if (this->index() == that.index()) + { + visitation::alt::visit_alt_at(this->index(), swapper{}, *this, that); + } + else + { + impl *lhs = this; + impl *rhs = std::addressof(that); + if (lhs->move_nothrow() && !rhs->move_nothrow()) + { + std::swap(lhs, rhs); + } + impl tmp(std::move(*rhs)); +#if __EXCEPTIONS + // EXTENSION: When the move construction of `lhs` into `rhs` throws + // and `tmp` is nothrow move constructible then we move `tmp` back + // into `rhs` and provide the strong exception safety guarantee. + try + { + this->generic_construct(*rhs, std::move(*lhs)); + } + catch (...) + { + if (tmp.move_nothrow()) + { + this->generic_construct(*rhs, std::move(tmp)); + } + throw; + } +#else + this->generic_construct(*rhs, std::move(*lhs)); +#endif + this->generic_construct(*lhs, std::move(tmp)); + } + } + +private: + struct swapper + { + template + inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const + { + using std::swap; + swap(this_alt.value, that_alt.value); + } + }; + + inline constexpr bool move_nothrow() const + { + return this->valueless_by_exception() || + std::array{ + {std::is_nothrow_move_constructible::value...}}[this->index()]; + } +}; + +template +struct is_non_narrowing_convertible +{ + template + static std::true_type test(T(&&)[1]); + + template + static auto impl(int) -> decltype(test({std::declval()})); + + template + static auto impl(...) -> std::false_type; + + static constexpr bool value = decltype(impl(0))::value; +}; + +template ::value, + typename = void> +struct overload_leaf +{}; + +template +struct overload_leaf +{ + using impl = size_constant (*)(T); + operator impl() const { return nullptr; }; +}; + +template +struct overload_leaf, bool>::value + ? std::is_same, bool>::value + : +#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5 + is_non_narrowing_convertible::value +#else + std::is_convertible::value +#endif + >> +{ + using impl = size_constant (*)(T); + operator impl() const { return nullptr; }; +}; + +template +struct overload_impl +{ +private: + template + struct impl; + + template + struct impl> : overload_leaf... + {}; + +public: + using type = impl>; +}; + +template +using overload = typename overload_impl::type; + +template +using best_match = invoke_result_t, Arg>; + +template +struct is_in_place_index : std::false_type +{}; + +template +struct is_in_place_index> : std::true_type +{}; + +template +struct is_in_place_type : std::false_type +{}; + +template +struct is_in_place_type> : std::true_type +{}; +} // namespace detail + +template +class variant +{ + static_assert(0 < sizeof...(Ts), "variant must consist of at least one alternative."); + + static_assert(detail::all::value...>::value, + "variant can not have an array type as an alternative."); + + static_assert(detail::all::value...>::value, + "variant can not have a reference type as an alternative."); + + static_assert(detail::all::value...>::value, + "variant can not have a void type as an alternative."); + +public: + template , + enable_if_t::value, int> = 0> + inline constexpr variant() noexcept(std::is_nothrow_default_constructible::value) + : impl_(in_place_index_t<0>{}) + {} + + variant(const variant &) = default; + variant(variant &&) = default; + + template , + enable_if_t::value, int> = 0, + enable_if_t::value, int> = 0, + enable_if_t::value, int> = 0, + std::size_t I = detail::best_match::value, + typename T = detail::type_pack_element_t, + enable_if_t::value, int> = 0> + inline constexpr variant(Arg &&arg) noexcept(std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, std::forward(arg)) + {} + + template , + enable_if_t::value, int> = 0> + inline explicit constexpr variant(in_place_index_t, Args &&... args) noexcept( + std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, std::forward(args)...) + {} + + template < + std::size_t I, + typename Up, + typename... Args, + typename T = detail::type_pack_element_t, + enable_if_t &, Args...>::value, int> = 0> + inline explicit constexpr variant( + in_place_index_t, + std::initializer_list il, + Args &&... args) noexcept(std::is_nothrow_constructible &, + Args...>::value) + : impl_(in_place_index_t{}, il, std::forward(args)...) + {} + + template ::value, + enable_if_t::value, int> = 0> + inline explicit constexpr variant(in_place_type_t, Args &&... args) noexcept( + std::is_nothrow_constructible::value) + : impl_(in_place_index_t{}, std::forward(args)...) + {} + + template < + typename T, + typename Up, + typename... Args, + std::size_t I = detail::find_index_sfinae::value, + enable_if_t &, Args...>::value, int> = 0> + inline explicit constexpr variant( + in_place_type_t, + std::initializer_list il, + Args &&... args) noexcept(std::is_nothrow_constructible &, + Args...>::value) + : impl_(in_place_index_t{}, il, std::forward(args)...) + {} + + ~variant() = default; + + variant &operator=(const variant &) = default; + variant &operator=(variant &&) = default; + + template < + typename Arg, + enable_if_t, variant>::value, int> = 0, + std::size_t I = detail::best_match::value, + typename T = detail::type_pack_element_t, + enable_if_t<(std::is_assignable::value && std::is_constructible::value), + int> = 0> + inline variant &operator=(Arg &&arg) noexcept((std::is_nothrow_assignable::value && + std::is_nothrow_constructible::value)) + { + impl_.template assign(std::forward(arg)); + return *this; + } + + template , + enable_if_t::value, int> = 0> + inline T &emplace(Args &&... args) + { + return impl_.template emplace(std::forward(args)...); + } + + template < + std::size_t I, + typename Up, + typename... Args, + typename T = detail::type_pack_element_t, + enable_if_t &, Args...>::value, int> = 0> + inline T &emplace(std::initializer_list il, Args &&... args) + { + return impl_.template emplace(il, std::forward(args)...); + } + + template ::value, + enable_if_t::value, int> = 0> + inline T &emplace(Args &&... args) + { + return impl_.template emplace(std::forward(args)...); + } + + template < + typename T, + typename Up, + typename... Args, + std::size_t I = detail::find_index_sfinae::value, + enable_if_t &, Args...>::value, int> = 0> + inline T &emplace(std::initializer_list il, Args &&... args) + { + return impl_.template emplace(il, std::forward(args)...); + } + + inline constexpr bool valueless_by_exception() const noexcept + { + return impl_.valueless_by_exception(); + } + + inline constexpr std::size_t index() const noexcept { return impl_.index(); } + + template , Dummy>::value && + detail::dependent_type, Dummy>::value)...>::value, + int> = 0> + inline void swap(variant &that) noexcept( + detail::all<(std::is_nothrow_move_constructible::value && + is_nothrow_swappable::value)...>::value) + { + impl_.swap(that.impl_); + } + +private: + detail::impl impl_; + + friend struct detail::access::variant; + friend struct detail::visitation::variant; +}; + +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return v.index() == I; +} + +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return holds_alternative::value>(v); +} + +namespace detail +{ +template +struct generic_get_impl +{ + constexpr generic_get_impl(int) noexcept {} + + constexpr auto operator()(V &&v) const + AUTO_REFREF_RETURN(access::variant::get_alt(std::forward(v)).value) +}; + +template +inline constexpr auto generic_get(V &&v) AUTO_REFREF_RETURN(generic_get_impl( + holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))(std::forward(v))) +} // namespace detail + +template +inline constexpr variant_alternative_t> &get(variant &v) +{ + return detail::generic_get(v); +} + +template +inline constexpr variant_alternative_t> &&get(variant &&v) +{ + return detail::generic_get(std::move(v)); +} + +template +inline constexpr const variant_alternative_t> &get(const variant &v) +{ + return detail::generic_get(v); +} + +template +inline constexpr const variant_alternative_t> &&get(const variant &&v) +{ + return detail::generic_get(std::move(v)); +} + +template +inline constexpr T &get(variant &v) +{ + return get::value>(v); +} + +template +inline constexpr T &&get(variant &&v) +{ + return get::value>(std::move(v)); +} + +template +inline constexpr const T &get(const variant &v) +{ + return get::value>(v); +} + +template +inline constexpr const T &&get(const variant &&v) +{ + return get::value>(std::move(v)); +} + +namespace detail +{ + +template +inline constexpr /* auto * */ auto generic_get_if(V *v) noexcept AUTO_RETURN( + v &&holds_alternative(*v) ? std::addressof(access::variant::get_alt(*v).value) : nullptr) + +} // namespace detail + +template +inline constexpr add_pointer_t>> get_if( + variant *v) noexcept +{ + return detail::generic_get_if(v); +} + +template +inline constexpr add_pointer_t>> get_if( + const variant *v) noexcept +{ + return detail::generic_get_if(v); +} + +template +inline constexpr add_pointer_t get_if(variant *v) noexcept +{ + return get_if::value>(v); +} + +template +inline constexpr add_pointer_t get_if(const variant *v) noexcept +{ + return get_if::value>(v); +} + +namespace detail +{ +template +struct convert_to_bool +{ + template + inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const + { + static_assert(std::is_convertible, bool>::value, + "relational operators must return a type" + " implicitly convertible to bool"); + return nostd::invoke(RelOp{}, std::forward(lhs), std::forward(rhs)); + } +}; +} // namespace detail + +template +inline constexpr bool operator==(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using equal_to = detail::convert_to_bool; + return lhs.index() == rhs.index() && (lhs.valueless_by_exception() || + variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); +} + +template +inline constexpr bool operator!=(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using not_equal_to = detail::convert_to_bool; + return lhs.index() != rhs.index() || + (!lhs.valueless_by_exception() && + variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); +} + +template +inline constexpr bool operator<(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using less = detail::convert_to_bool; + return !rhs.valueless_by_exception() && + (lhs.valueless_by_exception() || lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); +} + +template +inline constexpr bool operator>(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using greater = detail::convert_to_bool; + return !lhs.valueless_by_exception() && + (rhs.valueless_by_exception() || lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); +} + +template +inline constexpr bool operator<=(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using less_equal = detail::convert_to_bool; + return lhs.valueless_by_exception() || + (!rhs.valueless_by_exception() && + (lhs.index() < rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); +} + +template +inline constexpr bool operator>=(const variant &lhs, const variant &rhs) +{ + using detail::visitation::variant; + using greater_equal = detail::convert_to_bool; + return rhs.valueless_by_exception() || + (!lhs.valueless_by_exception() && + (lhs.index() > rhs.index() || + (lhs.index() == rhs.index() && + variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs)))); +} + +struct monostate +{}; + +inline constexpr bool operator<(monostate, monostate) noexcept +{ + return false; +} + +inline constexpr bool operator>(monostate, monostate) noexcept +{ + return false; +} + +inline constexpr bool operator<=(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator>=(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator==(monostate, monostate) noexcept +{ + return true; +} + +inline constexpr bool operator!=(monostate, monostate) noexcept +{ + return false; +} + +namespace detail +{ + +template +inline constexpr bool all_of_impl(const std::array &bs, std::size_t idx) +{ + return idx >= N || (bs[idx] && all_of_impl(bs, idx + 1)); +} + +template +inline constexpr bool all_of(const std::array &bs) +{ + return all_of_impl(bs, 0); +} + +} // namespace detail + +template +inline constexpr auto visit(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( + (detail::all_of(std::array{{!vs.valueless_by_exception()...}}) + ? (void)0 + : throw_bad_variant_access()), + detail::visitation::variant::visit_value(std::forward(visitor), + std::forward(vs)...)) template +inline auto swap(variant &lhs, variant &rhs) noexcept(noexcept(lhs.swap(rhs))) + -> decltype(lhs.swap(rhs)) +{ + lhs.swap(rhs); +} +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE + +#undef AUTO_RETURN + +#undef AUTO_REFREF_RETURN + +#undef DECLTYPE_AUTO_RETURN diff --git a/api/include/opentelemetry/nostd/shared_ptr.h b/api/include/opentelemetry/nostd/shared_ptr.h index 7d84b6f408..e7c2a464d2 100644 --- a/api/include/opentelemetry/nostd/shared_ptr.h +++ b/api/include/opentelemetry/nostd/shared_ptr.h @@ -1,8 +1,26 @@ -#pragma once +// Copyright 2020, OpenTelemetry Authors +// +// 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. -#include +#pragma once +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/shared_ptr.h" +#else +# include +# include +# include -#include "opentelemetry/version.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -180,3 +198,4 @@ inline bool operator!=(std::nullptr_t, const shared_ptr &rhs) noexcept } } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/span.h b/api/include/opentelemetry/nostd/span.h index f28a80cbbf..5b95010edd 100644 --- a/api/include/opentelemetry/nostd/span.h +++ b/api/include/opentelemetry/nostd/span.h @@ -1,14 +1,31 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + #pragma once -#include -#include -#include -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/span.h" +#else +# include +# include +# include +# include +# include +# include -#include "opentelemetry/nostd/utility.h" -#include "opentelemetry/version.h" +# include "opentelemetry/nostd/utility.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -238,3 +255,4 @@ class span }; } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/string_view.h b/api/include/opentelemetry/nostd/string_view.h index 315d038b3f..4a5bdefa81 100644 --- a/api/include/opentelemetry/nostd/string_view.h +++ b/api/include/opentelemetry/nostd/string_view.h @@ -1,13 +1,30 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + #pragma once -#include -#include -#include -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/string_view.h" +#else +# include +# include +# include +# include +# include +# include -#include "opentelemetry/version.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -58,11 +75,11 @@ class string_view { if (pos > length_) { -#if __EXCEPTIONS +# if __EXCEPTIONS throw std::out_of_range{"opentelemetry::nostd::string_view"}; -#else +# else std::terminate(); -#endif +# endif } n = (std::min)(n, length_ - pos); return string_view(data_ + pos, n); @@ -118,7 +135,12 @@ class string_view inline bool operator==(string_view lhs, string_view rhs) noexcept { return lhs.length() == rhs.length() && +# if _MSC_VER == 1900 + // Avoid SCL error in Visual Studio 2015 + (std::memcmp(lhs.data(), rhs.data(), lhs.length()) == 0); +# else std::equal(lhs.data(), lhs.data() + lhs.length(), rhs.data()); +# endif } inline bool operator==(string_view lhs, const std::string &rhs) noexcept @@ -172,3 +194,19 @@ inline std::ostream &operator<<(std::ostream &os, string_view s) } } // namespace nostd OPENTELEMETRY_END_NAMESPACE + +namespace std +{ +template <> +struct hash +{ + std::size_t operator()(const OPENTELEMETRY_NAMESPACE::nostd::string_view &k) const + { + // TODO: for C++17 that has native support for std::basic_string_view it would + // be more performance-efficient to provide a zero-copy hash. + auto s = std::string(k.data(), k.size()); + return std::hash{}(s); + } +}; +} // namespace std +#endif diff --git a/api/include/opentelemetry/nostd/type_traits.h b/api/include/opentelemetry/nostd/type_traits.h index a42f440df8..c1e4cfdf02 100644 --- a/api/include/opentelemetry/nostd/type_traits.h +++ b/api/include/opentelemetry/nostd/type_traits.h @@ -1,11 +1,28 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + #pragma once -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/type_traits.h" +#else +# include +# include -#include "opentelemetry/config.h" -#include "opentelemetry/nostd/detail/void.h" -#include "opentelemetry/version.h" +# include "opentelemetry/config.h" +# include "opentelemetry/nostd/detail/void.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -116,12 +133,12 @@ using is_nothrow_swappable = detail::swappable::is_nothrow_swappable struct is_trivially_copy_constructible { @@ -145,6 +162,7 @@ struct is_trivially_move_assignable { static constexpr bool value = __is_trivial(T); }; -#endif +# endif } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/unique_ptr.h b/api/include/opentelemetry/nostd/unique_ptr.h index fc9a36aa7b..b0418891d0 100644 --- a/api/include/opentelemetry/nostd/unique_ptr.h +++ b/api/include/opentelemetry/nostd/unique_ptr.h @@ -1,11 +1,28 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + #pragma once -#include -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/unique_ptr.h" +#else +# include +# include +# include +# include -#include "opentelemetry/version.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -166,3 +183,4 @@ bool operator!=(std::nullptr_t, const unique_ptr &rhs) noexcept } } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/utility.h b/api/include/opentelemetry/nostd/utility.h index d7fa3bfc19..ae9197e714 100644 --- a/api/include/opentelemetry/nostd/utility.h +++ b/api/include/opentelemetry/nostd/utility.h @@ -1,12 +1,30 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + #pragma once -#include -#include -#include +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/utility.h" +#else + +# include +# include +# include -#include "opentelemetry/nostd/detail/decay.h" -#include "opentelemetry/nostd/detail/invoke.h" -#include "opentelemetry/version.h" +# include "opentelemetry/nostd/detail/decay.h" +# include "opentelemetry/nostd/detail/invoke.h" +# include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd @@ -146,3 +164,4 @@ struct in_place_type_t }; } // namespace nostd OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/api/include/opentelemetry/nostd/variant.h b/api/include/opentelemetry/nostd/variant.h index 097bb57355..37281e1db6 100644 --- a/api/include/opentelemetry/nostd/variant.h +++ b/api/include/opentelemetry/nostd/variant.h @@ -1,1281 +1,33 @@ -// MPark.Variant +// Copyright 2020, OpenTelemetry Authors // -// Copyright Michael Park, 2015-2017 -// Copyright OpenTelemetry Authors, 2020 +// 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 // -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file third_party/boost/LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +// 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. #pragma once -#include -#include -#include - -#include "opentelemetry/nostd/detail/all.h" -#include "opentelemetry/nostd/detail/dependent_type.h" -#include "opentelemetry/nostd/detail/find_index.h" -#include "opentelemetry/nostd/detail/functional.h" -#include "opentelemetry/nostd/detail/recursive_union.h" -#include "opentelemetry/nostd/detail/trait.h" -#include "opentelemetry/nostd/detail/type_pack_element.h" -#include "opentelemetry/nostd/detail/variant_alternative.h" -#include "opentelemetry/nostd/detail/variant_fwd.h" -#include "opentelemetry/nostd/detail/variant_size.h" -#include "opentelemetry/nostd/type_traits.h" -#include "opentelemetry/nostd/utility.h" -#include "opentelemetry/version.h" - -#define AUTO_RETURN(...) \ - ->decay_t { return __VA_ARGS__; } - -#define AUTO_REFREF_RETURN(...) \ - ->decltype((__VA_ARGS__)) \ - { \ - static_assert(std::is_reference::value, ""); \ - return __VA_ARGS__; \ - } - -#define DECLTYPE_AUTO_RETURN(...) \ - ->decltype(__VA_ARGS__) { return __VA_ARGS__; } - +#ifdef HAVE_CPP_STDLIB +# include "opentelemetry/std/variant.h" +#elif defined(HAVE_ABSEIL) +// TODO: prefer `absl::variant` over `nostd::variant` since the latter does not compile with vs2015 +# include "absl/types/variant.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace nostd { -constexpr std::size_t variant_npos = static_cast(-1); - -class bad_variant_access : public std::exception -{ -public: - virtual const char *what() const noexcept override { return "bad_variant_access"; } -}; - -[[noreturn]] inline void throw_bad_variant_access() -{ -#if __EXCEPTIONS - throw bad_variant_access{}; -#else - std::terminate(); -#endif -} - -namespace detail -{ -namespace access -{ -struct base -{ - template - inline static constexpr auto get_alt(V &&v) -#ifdef _MSC_VER - AUTO_REFREF_RETURN(recursive_union::get_alt(std::forward(v).data_, in_place_index_t{})) -#else - AUTO_REFREF_RETURN(recursive_union::get_alt(data(std::forward(v)), in_place_index_t{})) -#endif -}; - -struct variant -{ - template - inline static constexpr auto get_alt(V &&v) - AUTO_REFREF_RETURN(base::get_alt(std::forward(v).impl_)) -}; -} // namespace access -} // namespace detail - -namespace detail -{ -namespace visitation -{ - -struct base -{ - template - using dispatch_result_t = decltype( - nostd::invoke(std::declval(), access::base::get_alt<0>(std::declval())...)); - - template - struct expected - { - template - inline static constexpr bool but_got() - { - return std::is_same::value; - } - }; - - template - struct visit_return_type_check - { - static_assert(expected::template but_got(), - "`visit` requires the visitor to have a single return type"); - - template - inline static constexpr auto invoke(Visitor &&visitor, Alts &&... alts) - DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), - std::forward(alts)...)) - }; - - template - inline static constexpr const T &at(const T &elem) noexcept - { - return elem; - } - - template - inline static constexpr const remove_all_extents_t &at(const std::array &elems, - std::size_t i, - Is... is) noexcept - { - return at(elems[i], is...); - } - - template - inline static constexpr std::array, sizeof...(Fs) + 1> make_farray(F &&f, Fs &&... fs) - { - return {{std::forward(f), std::forward(fs)...}}; - } - - template - struct make_fmatrix_impl - { - - template - inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) - { - using Expected = dispatch_result_t; - using Actual = decltype( - nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); - return visit_return_type_check::invoke( - std::forward(f), access::base::get_alt(std::forward(vs))...); - } - - template - struct impl; - - template - struct impl> - { - inline constexpr auto operator()() const AUTO_RETURN(&dispatch) - }; - - template - struct impl, Ls...> - { - inline constexpr auto operator()() const - AUTO_RETURN(make_farray(impl, Ls...>{}()...)) - }; - }; - - template - inline static constexpr auto make_fmatrix() AUTO_RETURN( - typename make_fmatrix_impl:: - template impl, make_index_sequence::size()>...>{}()) - - template - struct make_fdiagonal_impl - { - template - inline static constexpr dispatch_result_t dispatch(F &&f, Vs &&... vs) - { - using Expected = dispatch_result_t; - using Actual = decltype( - nostd::invoke(std::forward(f), access::base::get_alt(std::forward(vs))...)); - return visit_return_type_check::invoke( - std::forward(f), access::base::get_alt(std::forward(vs))...); - } - - template - inline static constexpr auto impl(index_sequence) - AUTO_RETURN(make_farray(&dispatch...)) - }; - - template - inline static constexpr auto make_fdiagonal() - -> decltype(make_fdiagonal_impl::impl(make_index_sequence::size()>{})) - { - static_assert(all<(decay_t::size() == decay_t::size())...>::value, - "all of the variants must be the same size."); - return make_fdiagonal_impl::impl(make_index_sequence::size()>{}); - } -}; - -#if !defined(_MSC_VER) || _MSC_VER >= 1910 -template -using fmatrix_t = decltype(base::make_fmatrix()); - -template -struct fmatrix -{ - static constexpr fmatrix_t value = base::make_fmatrix(); -}; - -template -constexpr fmatrix_t fmatrix::value; - -template -using fdiagonal_t = decltype(base::make_fdiagonal()); - -template -struct fdiagonal -{ - static constexpr fdiagonal_t value = base::make_fdiagonal(); -}; - -template -constexpr fdiagonal_t fdiagonal::value; -#endif - -struct alt -{ - template - inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( - base::at(base::make_fmatrix(vs)))...>(), - vs.index()...)(std::forward(visitor), as_base(std::forward(vs))...)) - - template - inline static constexpr auto visit_alt_at(std::size_t index, Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN(base::at( - base::make_fdiagonal(vs)))...>(), - index)(std::forward(visitor), as_base(std::forward(vs))...)) -}; - -struct variant -{ -private: - template - struct visitor - { - template - inline static constexpr bool does_not_handle() - { - return nostd::is_invocable::value; - } - }; - - template - struct visit_exhaustiveness_check - { - static_assert(visitor::template does_not_handle(), - "`visit` requires the visitor to be exhaustive."); - - inline static constexpr auto invoke(Visitor &&visitor, Values &&... values) - DECLTYPE_AUTO_RETURN(nostd::invoke(std::forward(visitor), - std::forward(values)...)) - }; - - template - struct value_visitor - { - Visitor &&visitor_; - - template - inline constexpr auto operator()(Alts &&... alts) const DECLTYPE_AUTO_RETURN( - visit_exhaustiveness_check(alts).value))...>::invoke( - std::forward(visitor_), - std::forward(alts).value...)) - }; - - template - inline static constexpr auto make_value_visitor(Visitor &&visitor) - AUTO_RETURN(value_visitor{std::forward(visitor)}) - - public - : template - inline static constexpr auto visit_alt(Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN(alt::visit_alt(std::forward(visitor), - std::forward(vs).impl_...)) - - template - inline static constexpr auto visit_alt_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(alt::visit_alt_at(index, - std::forward(visitor), - std::forward(vs).impl_...)) - - template - inline static constexpr auto visit_value(Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN( - visit_alt(make_value_visitor(std::forward(visitor)), - std::forward(vs)...)) - - template - inline static constexpr auto visit_value_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(visit_alt_at( - index, - make_value_visitor(std::forward(visitor)), - std::forward(vs)...)) -}; -} // namespace visitation - -template -using index_t = typename std::conditional< - sizeof...(Ts) < (std::numeric_limits::max)(), - unsigned char, - typename std::conditional::max)(), - unsigned short, - unsigned int>::type>::type; - -template -class base -{ -public: - inline explicit constexpr base(valueless_t tag) noexcept - : data_(tag), index_(static_cast>(-1)) - {} - - template - inline explicit constexpr base(in_place_index_t, Args &&... args) - : data_(in_place_index_t{}, std::forward(args)...), index_(I) - {} - - inline constexpr bool valueless_by_exception() const noexcept - { - return index_ == static_cast>(-1); - } - - inline constexpr std::size_t index() const noexcept - { - return valueless_by_exception() ? variant_npos : index_; - } - -protected: - using data_t = recursive_union; - - friend inline constexpr base &as_base(base &b) { return b; } - friend inline constexpr const base &as_base(const base &b) { return b; } - friend inline constexpr base &&as_base(base &&b) { return std::move(b); } - friend inline constexpr const base &&as_base(const base &&b) { return std::move(b); } - - friend inline constexpr data_t &data(base &b) { return b.data_; } - friend inline constexpr const data_t &data(const base &b) { return b.data_; } - friend inline constexpr data_t &&data(base &&b) { return std::move(b).data_; } - friend inline constexpr const data_t &&data(const base &&b) { return std::move(b).data_; } - - inline static constexpr std::size_t size() { return sizeof...(Ts); } - - data_t data_; - index_t index_; - - friend struct access::base; - friend struct visitation::base; -}; - -struct dtor -{ -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4100) -#endif - template - inline void operator()(Alt &alt) const noexcept - { - alt.~Alt(); - } -#ifdef _MSC_VER -# pragma warning(pop) -#endif -}; - -template -class destructor; - -#define OPENTELEMETRY_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ - template \ - class destructor, destructible_trait> : public base \ - { \ - using super = base; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - destructor(const destructor &) = default; \ - destructor(destructor &&) = default; \ - definition destructor &operator=(const destructor &) = default; \ - destructor &operator=(destructor &&) = default; \ - \ - protected: \ - destroy \ - } - -OPENTELEMETRY_VARIANT_DESTRUCTOR( - Trait::TriviallyAvailable, ~destructor() = default; - , inline void destroy() noexcept { this->index_ = static_cast>(-1); }); - -OPENTELEMETRY_VARIANT_DESTRUCTOR( - Trait::Available, - ~destructor() { destroy(); }, - inline void destroy() noexcept { - if (!this->valueless_by_exception()) - { - visitation::alt::visit_alt(dtor{}, *this); - } - this->index_ = static_cast>(-1); - }); - -OPENTELEMETRY_VARIANT_DESTRUCTOR(Trait::Unavailable, ~destructor() = delete; - , inline void destroy() noexcept = delete;); - -#undef OPENTELEMETRY_VARIANT_DESTRUCTOR - -template -class constructor : public destructor -{ - using super = destructor; - -public: - using super::super; - using super::operator=; - -protected: - struct ctor - { - template - inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const - { - constructor::construct_alt(lhs_alt, std::forward(rhs_alt).value); - } - }; - - template - inline static T &construct_alt(alt &a, Args &&... args) - { - auto *result = ::new (static_cast(std::addressof(a))) - alt(in_place_t{}, std::forward(args)...); - return result->value; - } - - template - inline static void generic_construct(constructor &lhs, Rhs &&rhs) - { - lhs.destroy(); - if (!rhs.valueless_by_exception()) - { - visitation::alt::visit_alt_at(rhs.index(), ctor{}, lhs, std::forward(rhs)); - lhs.index_ = rhs.index_; - } - } -}; - -template -class move_constructor; - -#define OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ - template \ - class move_constructor, move_constructible_trait> \ - : public constructor> \ - { \ - using super = constructor>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - move_constructor(const move_constructor &) = default; \ - definition ~move_constructor() = default; \ - move_constructor &operator=(const move_constructor &) = default; \ - move_constructor &operator=(move_constructor &&) = default; \ - } - -OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::TriviallyAvailable, - move_constructor(move_constructor &&that) = default;); - -OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR( - Trait::Available, - move_constructor(move_constructor &&that) noexcept( - all::value...>::value) - : move_constructor(valueless_t{}) { this->generic_construct(*this, std::move(that)); }); - -OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR(Trait::Unavailable, - move_constructor(move_constructor &&) = delete;); - -#undef OPENTELEMETRY_VARIANT_MOVE_CONSTRUCTOR - -template -class copy_constructor; - -#define OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ - template \ - class copy_constructor, copy_constructible_trait> \ - : public move_constructor> \ - { \ - using super = move_constructor>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - definition copy_constructor(copy_constructor &&) = default; \ - ~copy_constructor() = default; \ - copy_constructor &operator=(const copy_constructor &) = default; \ - copy_constructor &operator=(copy_constructor &&) = default; \ - } - -OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::TriviallyAvailable, - copy_constructor(const copy_constructor &that) = default;); - -OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR( - Trait::Available, copy_constructor(const copy_constructor &that) - : copy_constructor(valueless_t{}) { this->generic_construct(*this, that); }); - -OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR(Trait::Unavailable, - copy_constructor(const copy_constructor &) = delete;); - -#undef OPENTELEMETRY_VARIANT_COPY_CONSTRUCTOR - -template -class assignment : public copy_constructor -{ - using super = copy_constructor; - -public: - using super::super; - using super::operator=; - - template - inline /* auto & */ auto emplace(Args &&... args) - -> decltype(this->construct_alt(access::base::get_alt(*this), std::forward(args)...)) - { - this->destroy(); - auto &result = - this->construct_alt(access::base::get_alt(*this), std::forward(args)...); - this->index_ = I; - return result; - } - -protected: - template - struct assigner - { - template - inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const - { - self->assign_alt(this_alt, std::forward(that_alt).value); - } - assignment *self; - }; - - template - inline void assign_alt(alt &a, Arg &&arg) - { - if (this->index() == I) - { -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4244) -#endif - a.value = std::forward(arg); -#ifdef _MSC_VER -# pragma warning(pop) -#endif - } - else - { - struct - { - void operator()(std::true_type) const { this_->emplace(std::forward(arg_)); } - void operator()(std::false_type) const { this_->emplace(T(std::forward(arg_))); } - assignment *this_; - Arg &&arg_; - } impl{this, std::forward(arg)}; - impl(bool_constant < std::is_nothrow_constructible::value || - !std::is_nothrow_move_constructible::value > {}); - } - } - - template - inline void generic_assign(That &&that) - { - if (this->valueless_by_exception() && that.valueless_by_exception()) - { - // do nothing. - } - else if (that.valueless_by_exception()) - { - this->destroy(); - } - else - { - visitation::alt::visit_alt_at(that.index(), assigner{this}, *this, - std::forward(that)); - } - } -}; - -template -class move_assignment; - -#define OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ - template \ - class move_assignment, move_assignable_trait> : public assignment> \ - { \ - using super = assignment>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - move_assignment(const move_assignment &) = default; \ - move_assignment(move_assignment &&) = default; \ - ~move_assignment() = default; \ - move_assignment &operator=(const move_assignment &) = default; \ - definition \ - } - -OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( - Trait::TriviallyAvailable, move_assignment &operator=(move_assignment &&that) = default;); - -OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT( - Trait::Available, - move_assignment & - operator=(move_assignment &&that) noexcept( - all<(std::is_nothrow_move_constructible::value && - std::is_nothrow_move_assignable::value)...>::value) { - this->generic_assign(std::move(that)); - return *this; - }); - -OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT(Trait::Unavailable, - move_assignment &operator=(move_assignment &&) = delete;); - -#undef OPENTELEMETRY_VARIANT_MOVE_ASSIGNMENT - -template -class copy_assignment; - -#define OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ - template \ - class copy_assignment, copy_assignable_trait> \ - : public move_assignment> \ - { \ - using super = move_assignment>; \ - \ - public: \ - using super::super; \ - using super::operator=; \ - \ - copy_assignment(const copy_assignment &) = default; \ - copy_assignment(copy_assignment &&) = default; \ - ~copy_assignment() = default; \ - definition copy_assignment &operator=(copy_assignment &&) = default; \ - } - -OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( - Trait::TriviallyAvailable, copy_assignment &operator=(const copy_assignment &that) = default;); - -OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( - Trait::Available, - copy_assignment & - operator=(const copy_assignment &that) { - this->generic_assign(that); - return *this; - }); - -OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT( - Trait::Unavailable, copy_assignment &operator=(const copy_assignment &) = delete;); - -#undef OPENTELEMETRY_VARIANT_COPY_ASSIGNMENT -template -class impl : public copy_assignment> -{ - using super = copy_assignment>; - -public: - using super::super; - using super::operator=; - - impl(const impl &) = default; - impl(impl &&) = default; - ~impl() = default; - impl &operator=(const impl &) = default; - impl &operator=(impl &&) = default; - - template - inline void assign(Arg &&arg) - { - this->assign_alt(access::base::get_alt(*this), std::forward(arg)); - } - - inline void swap(impl &that) - { - if (this->valueless_by_exception() && that.valueless_by_exception()) - { - // do nothing. - } - else if (this->index() == that.index()) - { - visitation::alt::visit_alt_at(this->index(), swapper{}, *this, that); - } - else - { - impl *lhs = this; - impl *rhs = std::addressof(that); - if (lhs->move_nothrow() && !rhs->move_nothrow()) - { - std::swap(lhs, rhs); - } - impl tmp(std::move(*rhs)); -#if __EXCEPTIONS - // EXTENSION: When the move construction of `lhs` into `rhs` throws - // and `tmp` is nothrow move constructible then we move `tmp` back - // into `rhs` and provide the strong exception safety guarantee. - try - { - this->generic_construct(*rhs, std::move(*lhs)); - } - catch (...) - { - if (tmp.move_nothrow()) - { - this->generic_construct(*rhs, std::move(tmp)); - } - throw; - } -#else - this->generic_construct(*rhs, std::move(*lhs)); -#endif - this->generic_construct(*lhs, std::move(tmp)); - } - } - -private: - struct swapper - { - template - inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const - { - using std::swap; - swap(this_alt.value, that_alt.value); - } - }; - - inline constexpr bool move_nothrow() const - { - return this->valueless_by_exception() || - std::array{ - {std::is_nothrow_move_constructible::value...}}[this->index()]; - } -}; - -template -struct is_non_narrowing_convertible -{ - template - static std::true_type test(T(&&)[1]); - - template - static auto impl(int) -> decltype(test({std::declval()})); - - template - static auto impl(...) -> std::false_type; - - static constexpr bool value = decltype(impl(0))::value; -}; - -template ::value, - typename = void> -struct overload_leaf -{}; - -template -struct overload_leaf -{ - using impl = size_constant (*)(T); - operator impl() const { return nullptr; }; -}; - -template -struct overload_leaf, bool>::value - ? std::is_same, bool>::value - : -#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 5 - is_non_narrowing_convertible::value -#else - std::is_convertible::value -#endif - >> -{ - using impl = size_constant (*)(T); - operator impl() const { return nullptr; }; -}; - -template -struct overload_impl -{ -private: - template - struct impl; - - template - struct impl> : overload_leaf... - {}; - -public: - using type = impl>; -}; - -template -using overload = typename overload_impl::type; - -template -using best_match = invoke_result_t, Arg>; - -template -struct is_in_place_index : std::false_type -{}; - -template -struct is_in_place_index> : std::true_type -{}; - -template -struct is_in_place_type : std::false_type -{}; - -template -struct is_in_place_type> : std::true_type -{}; -} // namespace detail - -template -class variant -{ - static_assert(0 < sizeof...(Ts), "variant must consist of at least one alternative."); - - static_assert(detail::all::value...>::value, - "variant can not have an array type as an alternative."); - - static_assert(detail::all::value...>::value, - "variant can not have a reference type as an alternative."); - - static_assert(detail::all::value...>::value, - "variant can not have a void type as an alternative."); - -public: - template , - enable_if_t::value, int> = 0> - inline constexpr variant() noexcept(std::is_nothrow_default_constructible::value) - : impl_(in_place_index_t<0>{}) - {} - - variant(const variant &) = default; - variant(variant &&) = default; - - template , - enable_if_t::value, int> = 0, - enable_if_t::value, int> = 0, - enable_if_t::value, int> = 0, - std::size_t I = detail::best_match::value, - typename T = detail::type_pack_element_t, - enable_if_t::value, int> = 0> - inline constexpr variant(Arg &&arg) noexcept(std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, std::forward(arg)) - {} - - template , - enable_if_t::value, int> = 0> - inline explicit constexpr variant(in_place_index_t, Args &&... args) noexcept( - std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, std::forward(args)...) - {} - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = detail::type_pack_element_t, - enable_if_t &, Args...>::value, int> = 0> - inline explicit constexpr variant( - in_place_index_t, - std::initializer_list il, - Args &&... args) noexcept(std::is_nothrow_constructible &, - Args...>::value) - : impl_(in_place_index_t{}, il, std::forward(args)...) - {} - - template ::value, - enable_if_t::value, int> = 0> - inline explicit constexpr variant(in_place_type_t, Args &&... args) noexcept( - std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, std::forward(args)...) - {} - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - enable_if_t &, Args...>::value, int> = 0> - inline explicit constexpr variant( - in_place_type_t, - std::initializer_list il, - Args &&... args) noexcept(std::is_nothrow_constructible &, - Args...>::value) - : impl_(in_place_index_t{}, il, std::forward(args)...) - {} - - ~variant() = default; - - variant &operator=(const variant &) = default; - variant &operator=(variant &&) = default; - - template < - typename Arg, - enable_if_t, variant>::value, int> = 0, - std::size_t I = detail::best_match::value, - typename T = detail::type_pack_element_t, - enable_if_t<(std::is_assignable::value && std::is_constructible::value), - int> = 0> - inline variant &operator=(Arg &&arg) noexcept((std::is_nothrow_assignable::value && - std::is_nothrow_constructible::value)) - { - impl_.template assign(std::forward(arg)); - return *this; - } - - template , - enable_if_t::value, int> = 0> - inline T &emplace(Args &&... args) - { - return impl_.template emplace(std::forward(args)...); - } - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = detail::type_pack_element_t, - enable_if_t &, Args...>::value, int> = 0> - inline T &emplace(std::initializer_list il, Args &&... args) - { - return impl_.template emplace(il, std::forward(args)...); - } - - template ::value, - enable_if_t::value, int> = 0> - inline T &emplace(Args &&... args) - { - return impl_.template emplace(std::forward(args)...); - } - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - enable_if_t &, Args...>::value, int> = 0> - inline T &emplace(std::initializer_list il, Args &&... args) - { - return impl_.template emplace(il, std::forward(args)...); - } - - inline constexpr bool valueless_by_exception() const noexcept - { - return impl_.valueless_by_exception(); - } - - inline constexpr std::size_t index() const noexcept { return impl_.index(); } - - template , Dummy>::value && - detail::dependent_type, Dummy>::value)...>::value, - int> = 0> - inline void swap(variant &that) noexcept( - detail::all<(std::is_nothrow_move_constructible::value && - is_nothrow_swappable::value)...>::value) - { - impl_.swap(that.impl_); - } - -private: - detail::impl impl_; - - friend struct detail::access::variant; - friend struct detail::visitation::variant; -}; - -template -inline constexpr bool holds_alternative(const variant &v) noexcept -{ - return v.index() == I; -} - -template -inline constexpr bool holds_alternative(const variant &v) noexcept -{ - return holds_alternative::value>(v); -} - -namespace detail -{ -template -struct generic_get_impl -{ - constexpr generic_get_impl(int) noexcept {} - - constexpr auto operator()(V &&v) const - AUTO_REFREF_RETURN(access::variant::get_alt(std::forward(v)).value) -}; - -template -inline constexpr auto generic_get(V &&v) AUTO_REFREF_RETURN(generic_get_impl( - holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))(std::forward(v))) -} // namespace detail - -template -inline constexpr variant_alternative_t> &get(variant &v) -{ - return detail::generic_get(v); -} - -template -inline constexpr variant_alternative_t> &&get(variant &&v) -{ - return detail::generic_get(std::move(v)); -} - -template -inline constexpr const variant_alternative_t> &get(const variant &v) -{ - return detail::generic_get(v); -} - -template -inline constexpr const variant_alternative_t> &&get(const variant &&v) -{ - return detail::generic_get(std::move(v)); -} - -template -inline constexpr T &get(variant &v) -{ - return get::value>(v); -} - -template -inline constexpr T &&get(variant &&v) -{ - return get::value>(std::move(v)); -} - -template -inline constexpr const T &get(const variant &v) -{ - return get::value>(v); -} - -template -inline constexpr const T &&get(const variant &&v) -{ - return get::value>(std::move(v)); -} - -namespace detail -{ - -template -inline constexpr /* auto * */ auto generic_get_if(V *v) noexcept AUTO_RETURN( - v &&holds_alternative(*v) ? std::addressof(access::variant::get_alt(*v).value) : nullptr) - -} // namespace detail - -template -inline constexpr add_pointer_t>> get_if( - variant *v) noexcept -{ - return detail::generic_get_if(v); -} - -template -inline constexpr add_pointer_t>> get_if( - const variant *v) noexcept -{ - return detail::generic_get_if(v); -} - -template -inline constexpr add_pointer_t get_if(variant *v) noexcept -{ - return get_if::value>(v); -} - -template -inline constexpr add_pointer_t get_if(const variant *v) noexcept -{ - return get_if::value>(v); -} - -namespace detail -{ -template -struct convert_to_bool -{ - template - inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const - { - static_assert(std::is_convertible, bool>::value, - "relational operators must return a type" - " implicitly convertible to bool"); - return nostd::invoke(RelOp{}, std::forward(lhs), std::forward(rhs)); - } -}; -} // namespace detail - -template -inline constexpr bool operator==(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using equal_to = detail::convert_to_bool; - return lhs.index() == rhs.index() && (lhs.valueless_by_exception() || - variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); -} - -template -inline constexpr bool operator!=(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using not_equal_to = detail::convert_to_bool; - return lhs.index() != rhs.index() || - (!lhs.valueless_by_exception() && - variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); -} - -template -inline constexpr bool operator<(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using less = detail::convert_to_bool; - return !rhs.valueless_by_exception() && - (lhs.valueless_by_exception() || lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); -} - -template -inline constexpr bool operator>(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using greater = detail::convert_to_bool; - return !lhs.valueless_by_exception() && - (rhs.valueless_by_exception() || lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); -} - -template -inline constexpr bool operator<=(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using less_equal = detail::convert_to_bool; - return lhs.valueless_by_exception() || - (!rhs.valueless_by_exception() && - (lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); -} - -template -inline constexpr bool operator>=(const variant &lhs, const variant &rhs) -{ - using detail::visitation::variant; - using greater_equal = detail::convert_to_bool; - return rhs.valueless_by_exception() || - (!lhs.valueless_by_exception() && - (lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs)))); -} - -struct monostate -{}; - -inline constexpr bool operator<(monostate, monostate) noexcept -{ - return false; -} - -inline constexpr bool operator>(monostate, monostate) noexcept -{ - return false; -} - -inline constexpr bool operator<=(monostate, monostate) noexcept -{ - return true; -} - -inline constexpr bool operator>=(monostate, monostate) noexcept -{ - return true; -} - -inline constexpr bool operator==(monostate, monostate) noexcept -{ - return true; -} - -inline constexpr bool operator!=(monostate, monostate) noexcept -{ - return false; -} - -namespace detail -{ - -template -inline constexpr bool all_of_impl(const std::array &bs, std::size_t idx) -{ - return idx >= N || (bs[idx] && all_of_impl(bs, idx + 1)); -} - -template -inline constexpr bool all_of(const std::array &bs) -{ - return all_of_impl(bs, 0); -} - -} // namespace detail - -template -inline constexpr auto visit(Visitor &&visitor, Vs &&... vs) DECLTYPE_AUTO_RETURN( - (detail::all_of(std::array{{!vs.valueless_by_exception()...}}) - ? (void)0 - : throw_bad_variant_access()), - detail::visitation::variant::visit_value(std::forward(visitor), - std::forward(vs)...)) template -inline auto swap(variant &lhs, variant &rhs) noexcept(noexcept(lhs.swap(rhs))) - -> decltype(lhs.swap(rhs)) -{ - lhs.swap(rhs); -} +using absl::get; +using absl::holds_alternative; +using absl::variant; +using absl::visit; } // namespace nostd OPENTELEMETRY_END_NAMESPACE - -#undef AUTO_RETURN - -#undef AUTO_REFREF_RETURN - -#undef DECLTYPE_AUTO_RETURN +#else +# include "opentelemetry/nostd/mpark/variant.h" +#endif diff --git a/api/include/opentelemetry/plugin/tracer.h b/api/include/opentelemetry/plugin/tracer.h index a1a15e6650..ffd865031f 100644 --- a/api/include/opentelemetry/plugin/tracer.h +++ b/api/include/opentelemetry/plugin/tracer.h @@ -2,6 +2,7 @@ #include +#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/plugin/detail/dynamic_library_handle.h" #include "opentelemetry/plugin/detail/tracer_handle.h" #include "opentelemetry/trace/tracer.h" @@ -32,7 +33,7 @@ class Span final : public trace::Span void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace::KeyValueIterable &attributes) noexcept override + const common::KeyValueIterable &attributes) noexcept override { span_->AddEvent(name, timestamp, attributes); } @@ -50,8 +51,6 @@ class Span final : public trace::Span trace::SpanContext GetContext() const noexcept override { return span_->GetContext(); } - void SetToken(nostd::unique_ptr &&token) noexcept override {} - private: std::shared_ptr tracer_; nostd::shared_ptr span_; @@ -68,7 +67,7 @@ class Tracer final : public trace::Tracer, public std::enable_shared_from_this StartSpan( nostd::string_view name, - const trace::KeyValueIterable &attributes, + const common::KeyValueIterable &attributes, const trace::StartSpanOptions &options = {}) noexcept override { auto span = tracer_handle_->tracer().StartSpan(name, attributes, options); diff --git a/api/include/opentelemetry/std/shared_ptr.h b/api/include/opentelemetry/std/shared_ptr.h new file mode 100644 index 0000000000..8969f2ced5 --- /dev/null +++ b/api/include/opentelemetry/std/shared_ptr.h @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::shared_ptr +template +using shared_ptr = std::shared_ptr<_Types...>; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/span.h b/api/include/opentelemetry/std/span.h new file mode 100644 index 0000000000..da9fcb48a3 --- /dev/null +++ b/api/include/opentelemetry/std/span.h @@ -0,0 +1,76 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +// Standard library implementation requires at least C++17 compiler. +// Older C++14 compilers may provide support for __has_include as a +// conforming extension. +#if defined __has_include +# if __has_include() // Check for __cpp_{feature} +# include +# if defined(__cpp_lib_span) +# define HAVE_SPAN +# endif +# endif +# if __has_include() && !defined(HAVE_SPAN) // Check for span +# define HAVE_SPAN +# endif +# if !__has_include() // Check for string_view +# error \ + "STL library does not support std::span. Possible solution:" \ + " - #undef HAVE_CPP_STDLIB // to use OpenTelemetry nostd::string_view" +# endif +#endif + +#if !defined(HAVE_SPAN) +# if defined(HAVE_GSL) +# include +// Guidelines Support Library provides an implementation of std::span +# include +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +template +using span = gsl::span; +} +OPENTELEMETRY_END_NAMESPACE +# else +// No span implementation provided. +# error \ + "STL library does not support std::span. Possible solutions:" \ + " - #undef HAVE_CPP_STDLIB // to use OpenTelemetry nostd::span .. or " \ + " - #define HAVE_GSL // to use gsl::span " +# endif + +#else // HAVE_SPAN +// Using std::span (https://wg21.link/P0122R7) from Standard Library available in C++20 : +// - GCC libstdc++ 10+ +// - Clang libc++ 7 +// - MSVC Standard Library 19.26* +// - Apple Clang 10.0.0* +# include +# include +OPENTELEMETRY_BEGIN_NAMESPACE +namespace nostd +{ +constexpr std::size_t dynamic_extent = std::numeric_limits::max(); + +template +using span = std::span; +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE +#endif // of HAVE_SPAN diff --git a/api/include/opentelemetry/std/string_view.h b/api/include/opentelemetry/std/string_view.h new file mode 100644 index 0000000000..755b4387a1 --- /dev/null +++ b/api/include/opentelemetry/std/string_view.h @@ -0,0 +1,36 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +#include "opentelemetry/std/utility.h" + +#include +#include +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::string_view +using string_view = std::string_view; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/type_traits.h b/api/include/opentelemetry/std/type_traits.h new file mode 100644 index 0000000000..1fd6ef7f88 --- /dev/null +++ b/api/include/opentelemetry/std/type_traits.h @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::enable_if_t<...> +template +using enable_if_t = typename std::enable_if::type; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/unique_ptr.h b/api/include/opentelemetry/std/unique_ptr.h new file mode 100644 index 0000000000..7877d2d854 --- /dev/null +++ b/api/include/opentelemetry/std/unique_ptr.h @@ -0,0 +1,31 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::unique_ptr +template +using unique_ptr = std::unique_ptr<_Types...>; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/utility.h b/api/include/opentelemetry/std/utility.h new file mode 100644 index 0000000000..a0d73c9fe9 --- /dev/null +++ b/api/include/opentelemetry/std/utility.h @@ -0,0 +1,80 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// +// Backport of std::data +// +// See https://en.cppreference.com/w/cpp/iterator/data +// +template +auto data(C &c) noexcept(noexcept(c.data())) -> decltype(c.data()) +{ + return c.data(); +} + +template +auto data(const C &c) noexcept(noexcept(c.data())) -> decltype(c.data()) +{ + return c.data(); +} + +template +T *data(T (&array)[N]) noexcept +{ + return array; +} + +template +const E *data(std::initializer_list list) noexcept +{ + return list.begin(); +} + +// +// Backport of std::size +// +// See https://en.cppreference.com/w/cpp/iterator/size +// +template +auto size(const C &c) noexcept(noexcept(c.size())) -> decltype(c.size()) +{ + return c.size(); +} + +template +std::size_t size(T (&array)[N]) noexcept +{ + return N; +} + +template +using make_index_sequence = std::make_index_sequence; + +template +using index_sequence = std::index_sequence; + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/std/variant.h b/api/include/opentelemetry/std/variant.h new file mode 100644 index 0000000000..aca8d4de5f --- /dev/null +++ b/api/include/opentelemetry/std/variant.h @@ -0,0 +1,232 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/version.h" + +#include +#include +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +// Standard Type aliases in nostd namespace +namespace nostd +{ + +// nostd::variant<...> +template +using variant = std::variant<_Types...>; + +#if defined(__APPLE__) && defined(_LIBCPP_USE_AVAILABILITY_APPLE) +// Apple Platforms provide std::bad_variant_access only in newer versions of OS. +// To keep API compatible with any version of OS - we are providing our own +// implementation of nostd::bad_variant_access exception. +# if __EXCEPTIONS + +// nostd::bad_variant_access +class bad_variant_access : public std::exception +{ +public: + virtual const char *what() const noexcept override { return "bad_variant_access"; } +}; + +[[noreturn]] inline void throw_bad_variant_access() +{ + throw bad_variant_access{}; +} +# endif + +# if __EXCEPTIONS +# define THROW_BAD_VARIANT_ACCESS throw_bad_variant_access() +# else +# define THROW_BAD_VARIANT_ACCESS std::terminate() +# endif + +// +// nostd::get<...> for Apple Clang +// +template +constexpr auto get_type = [](auto &&t) constexpr -> decltype(auto) +{ + auto v = t; + auto result = std::get_if(&v); // TODO: optimize with std::forward(t) if t is not rvalue + if (result) + { + return *result; + } + THROW_BAD_VARIANT_ACCESS; + return *result; +}; + +template +constexpr auto get_index = [](auto &&t) constexpr -> decltype(auto) +{ + auto v = t; + auto result = std::get_if(&v); // TODO: optimize with std::forward(t) if t is not rvalue + if (result) + { + return *result; + } + THROW_BAD_VARIANT_ACCESS; + return *result; +}; + +template +constexpr std::variant_alternative_t> &get(std::variant &v) +{ + return get_index(v); +}; + +template +constexpr std::variant_alternative_t> &&get(std::variant &&v) +{ + return get_index(std::forward(v)); +}; + +template +constexpr const std::variant_alternative_t> &get( + const std::variant &v) +{ + return get_index(v); +}; + +template +constexpr const std::variant_alternative_t> &&get( + const std::variant &&v) +{ + return get_index(std::forward(v)); +}; + +template +constexpr T &get(std::variant &v) +{ + return get_type(v); +}; + +template +constexpr T /*&&*/ get(std::variant &&v) +{ + return get_type(v); +}; + +template +constexpr const T &get(const std::variant &v) +{ + return get_type(v); +}; + +template +constexpr const T &&get(const std::variant &&v) +{ + return get_type(std::forward(v)); +}; + +template +constexpr auto visit(_Callable &&_Obj, _Variants &&... _Args) +{ + // Ref: + // https://stackoverflow.com/questions/52310835/xcode-10-call-to-unavailable-function-stdvisit + return std::__variant_detail::__visitation::__variant::__visit_value(_Obj, _Args...); +}; + +#else + +template +constexpr std::variant_alternative_t> &get(std::variant &v) +{ + return std::get(v); +}; + +template +constexpr std::variant_alternative_t> &&get(std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr const std::variant_alternative_t> &get( + const std::variant &v) +{ + return std::get(v); +}; + +template +constexpr const std::variant_alternative_t> &&get( + const std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr T &get(std::variant &v) +{ + return std::get(v); +}; + +template +constexpr T &&get(std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr const T &get(const std::variant &v) +{ + return std::get(v); +}; + +template +constexpr const T &&get(const std::variant &&v) +{ + return std::get(std::forward(v)); +}; + +template +constexpr auto visit(_Callable &&_Obj, _Variants &&... _Args) +{ + return std::visit<_Callable, _Variants...>(static_cast<_Callable &&>(_Obj), + static_cast<_Variants &&>(_Args)...); +}; + +#endif + +/* +# if _HAS_CXX20 +template +constexpr _Ret visit(_Callable &&_Obj, _Variants &&... _Args) +{ + return std::visit<_Ret, _Callable, _Variants...>( + static_cast<_Callable &&>(_Obj), + static_cast<_Variants &&>(_Args)...); +}; +# endif +*/ + +// nostd::holds_alternative +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return v.index() == I; +} + +template +inline constexpr bool holds_alternative(const variant &v) noexcept +{ + return std::holds_alternative(v); +} + +} // namespace nostd +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/default_span.h b/api/include/opentelemetry/trace/default_span.h index ec58437ec2..15d5f4bf84 100644 --- a/api/include/opentelemetry/trace/default_span.h +++ b/api/include/opentelemetry/trace/default_span.h @@ -25,10 +25,10 @@ class DefaultSpan : public Span void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const KeyValueIterable &attributes) noexcept + const common::KeyValueIterable &attributes) noexcept {} - void AddEvent(nostd::string_view name, const KeyValueIterable &attributes) noexcept + void AddEvent(nostd::string_view name, const common::KeyValueIterable &attributes) noexcept { this->AddEvent(name, std::chrono::system_clock::now(), attributes); } @@ -41,10 +41,6 @@ class DefaultSpan : public Span nostd::string_view ToString() { return "DefaultSpan"; } - void SetToken(nostd::unique_ptr &&default_token) noexcept {} - - DefaultSpan() = default; - DefaultSpan(SpanContext span_context) : span_context_(span_context) {} // movable and copiable diff --git a/api/include/opentelemetry/trace/default_tracer.h b/api/include/opentelemetry/trace/default_tracer.h index f6b24aede0..384c0115a1 100644 --- a/api/include/opentelemetry/trace/default_tracer.h +++ b/api/include/opentelemetry/trace/default_tracer.h @@ -21,7 +21,7 @@ class DefaultTracer : public Tracer * key will be overwritten. */ nostd::unique_ptr StartSpan(nostd::string_view name, - const KeyValueIterable &attributes, + const common::KeyValueIterable &attributes, const StartSpanOptions &options = {}) override noexcept { return nostd::unique_ptr(new DefaultSpan::GetInvalid()); diff --git a/api/include/opentelemetry/trace/noop.h b/api/include/opentelemetry/trace/noop.h index afb42c1661..2997e9579f 100644 --- a/api/include/opentelemetry/trace/noop.h +++ b/api/include/opentelemetry/trace/noop.h @@ -24,7 +24,9 @@ namespace trace class NoopSpan final : public Span { public: - explicit NoopSpan(const std::shared_ptr &tracer) noexcept : tracer_{tracer} {} + explicit NoopSpan(const std::shared_ptr &tracer) noexcept + : tracer_{tracer}, span_context_{SpanContext::GetInvalid()} + {} void SetAttribute(nostd::string_view /*key*/, const common::AttributeValue & /*value*/) noexcept override @@ -37,7 +39,7 @@ class NoopSpan final : public Span void AddEvent(nostd::string_view /*name*/, core::SystemTimestamp /*timestamp*/, - const trace::KeyValueIterable & /*attributes*/) noexcept override + const common::KeyValueIterable & /*attributes*/) noexcept override {} void SetStatus(CanonicalCode /*code*/, nostd::string_view /*description*/) noexcept override {} @@ -50,8 +52,6 @@ class NoopSpan final : public Span SpanContext GetContext() const noexcept override { return span_context_; } - void SetToken(nostd::unique_ptr && /* token */) noexcept override {} - private: std::shared_ptr tracer_; SpanContext span_context_; @@ -65,10 +65,15 @@ class NoopTracer final : public Tracer, public std::enable_shared_from_this StartSpan(nostd::string_view /*name*/, - const KeyValueIterable & /*attributes*/, + const common::KeyValueIterable & /*attributes*/, const StartSpanOptions & /*options*/) noexcept override { - return nostd::shared_ptr{new (std::nothrow) NoopSpan{this->shared_from_this()}}; + // Don't allocate a no-op span for every StartSpan call, but use a static + // singleton for this case. + static nostd::shared_ptr noop_span( + new trace_api::NoopSpan{this->shared_from_this()}); + + return noop_span; } void ForceFlushWithMicroseconds(uint64_t /*timeout*/) noexcept override {} diff --git a/api/include/opentelemetry/trace/propagation/http_trace_context.h b/api/include/opentelemetry/trace/propagation/http_trace_context.h index 259bbcbc3b..d4b249970b 100644 --- a/api/include/opentelemetry/trace/propagation/http_trace_context.h +++ b/api/include/opentelemetry/trace/propagation/http_trace_context.h @@ -16,13 +16,13 @@ #include #include #include +#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/context/context.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/trace/default_span.h" -#include "opentelemetry/trace/key_value_iterable.h" #include "opentelemetry/trace/propagation/http_text_format.h" #include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span_context.h" @@ -92,7 +92,7 @@ class HttpTraceContext : public HTTPTextFormat { return nostd::get>(span).get()->GetContext(); } - return SpanContext(); + return SpanContext::GetInvalid(); } static TraceId GenerateTraceIdFromString(nostd::string_view trace_id) diff --git a/api/include/opentelemetry/trace/provider.h b/api/include/opentelemetry/trace/provider.h index 0cfed6d165..c78c4ad90c 100644 --- a/api/include/opentelemetry/trace/provider.h +++ b/api/include/opentelemetry/trace/provider.h @@ -1,7 +1,8 @@ #pragma once -#include +#include +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/trace/noop.h" #include "opentelemetry/trace/tracer_provider.h" @@ -23,12 +24,8 @@ class Provider */ static nostd::shared_ptr GetTracerProvider() noexcept { - while (GetLock().test_and_set(std::memory_order_acquire)) - ; - auto provider = nostd::shared_ptr(GetProvider()); - GetLock().clear(std::memory_order_release); - - return provider; + std::lock_guard guard(GetLock()); + return nostd::shared_ptr(GetProvider()); } /** @@ -36,10 +33,8 @@ class Provider */ static void SetTracerProvider(nostd::shared_ptr tp) noexcept { - while (GetLock().test_and_set(std::memory_order_acquire)) - ; + std::lock_guard guard(GetLock()); GetProvider() = tp; - GetLock().clear(std::memory_order_release); } private: @@ -49,9 +44,9 @@ class Provider return provider; } - static std::atomic_flag &GetLock() noexcept + static common::SpinLockMutex &GetLock() noexcept { - static std::atomic_flag lock = ATOMIC_FLAG_INIT; + static common::SpinLockMutex lock; return lock; } }; diff --git a/api/include/opentelemetry/trace/scope.h b/api/include/opentelemetry/trace/scope.h new file mode 100644 index 0000000000..594e095d3c --- /dev/null +++ b/api/include/opentelemetry/trace/scope.h @@ -0,0 +1,48 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include "opentelemetry/context/runtime_context.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +/** + * Controls how long a span is active. + * + * On creation of the Scope object, the given span is set to the currently + * active span. On destruction, the given span is ended and the previously + * active span will be the currently active span again. + */ +class Scope final +{ +public: + /** + * Initialize a new scope. + * @param span the given span will be set as the currently active span. + */ + Scope(nostd::shared_ptr &span) noexcept + : token_(context::Token(context::RuntimeContext::Attach( + context::RuntimeContext::GetCurrent().SetValue(SpanKey, span)))) + {} + +private: + context::Token token_; +}; + +} // namespace trace +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index 571a7aa2b8..7235e96ae1 100644 --- a/api/include/opentelemetry/trace/span.h +++ b/api/include/opentelemetry/trace/span.h @@ -3,22 +3,20 @@ #include #include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable_view.h" #include "opentelemetry/core/timestamp.h" +#include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/type_traits.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/trace/canonical_code.h" -#include "opentelemetry/trace/key_value_iterable_view.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/version.h" constexpr char SpanKey[] = "span_key"; OPENTELEMETRY_BEGIN_NAMESPACE -namespace context -{ -class Token; -} namespace trace { enum class SpanKind @@ -47,8 +45,13 @@ struct StartSpanOptions core::SystemTimestamp start_system_time; core::SteadyTimestamp start_steady_time; + // Explicitely set the parent of a Span. + // + // This defaults to an invalid span context. In this case, the Span is + // automatically parented to the currently active span. + SpanContext parent = SpanContext::GetInvalid(); + // TODO: - // Span(Context?) parent; // SpanContext remote_parent; // Links SpanKind kind = SpanKind::kInternal; @@ -99,25 +102,28 @@ class Span // Adds an event to the Span, with a custom timestamp, and attributes. virtual void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const KeyValueIterable &attributes) noexcept = 0; + const common::KeyValueIterable &attributes) noexcept = 0; - virtual void AddEvent(nostd::string_view name, const KeyValueIterable &attributes) noexcept + virtual void AddEvent(nostd::string_view name, + const common::KeyValueIterable &attributes) noexcept { this->AddEvent(name, std::chrono::system_clock::now(), attributes); } - template ::value> * = nullptr> + template ::value> * = nullptr> void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, const T &attributes) noexcept { - this->AddEvent(name, timestamp, KeyValueIterableView{attributes}); + this->AddEvent(name, timestamp, common::KeyValueIterableView{attributes}); } - template ::value> * = nullptr> + template ::value> * = nullptr> void AddEvent(nostd::string_view name, const T &attributes) noexcept { - this->AddEvent(name, KeyValueIterableView{attributes}); + this->AddEvent(name, common::KeyValueIterableView{attributes}); } void AddEvent(nostd::string_view name, @@ -162,8 +168,15 @@ class Span // Returns true if this Span is recording tracing events (e.g. SetAttribute, // AddEvent). virtual bool IsRecording() const noexcept = 0; - - virtual void SetToken(nostd::unique_ptr &&token) noexcept = 0; }; + +template +nostd::shared_ptr to_span_ptr(TracerType *objPtr, + nostd::string_view name, + const trace::StartSpanOptions &options) +{ + return nostd::shared_ptr{new (std::nothrow) SpanType{*objPtr, name, options}}; +} + } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/span_context.h b/api/include/opentelemetry/trace/span_context.h index 6228bf215c..6add265eed 100644 --- a/api/include/opentelemetry/trace/span_context.h +++ b/api/include/opentelemetry/trace/span_context.h @@ -33,10 +33,6 @@ namespace trace_api = opentelemetry::trace; class SpanContext final { public: - // An invalid SpanContext. - SpanContext() noexcept - : trace_flags_(trace::TraceFlags((uint8_t) false)), remote_parent_(false){}; - /* A temporary constructor for an invalid SpanContext. * Trace id and span id are set to invalid (all zeros). * @@ -71,31 +67,9 @@ class SpanContext final remote_parent_(has_remote_parent) {} - SpanContext(SpanContext &&ctx) - : trace_id_(ctx.trace_id()), span_id_(ctx.span_id()), trace_flags_(ctx.trace_flags()) - {} + SpanContext(const SpanContext &ctx) = default; - SpanContext(const SpanContext &ctx) - : trace_id_(ctx.trace_id()), span_id_(ctx.span_id()), trace_flags_(ctx.trace_flags()) - {} - // - // SpanContext &operator=(const SpanContext &ctx) - // { - // SpanContext *spn_ctx = - // new SpanContext(ctx.trace_id(), ctx.span_id(), ctx.trace_flags(), - // ctx.HasRemoteParent()); - // this = spn_ctx; - // return *this; - // }; - // - // SpanContext &operator=(SpanContext &&ctx) - // { - // SpanContext *spn_ctx = - // new SpanContext(ctx.trace_id(), ctx.span_id(), ctx.trace_flags(), - // ctx.HasRemoteParent()); - // this = spn_ctx; - // return *this; - // }; + SpanContext &operator=(const SpanContext &ctx) = default; bool operator==(const SpanContext &that) const noexcept { @@ -110,10 +84,10 @@ class SpanContext final bool IsSampled() const noexcept { return trace_flags_.IsSampled(); } private: - const trace_api::TraceId trace_id_; - const trace_api::SpanId span_id_; - const trace_api::TraceFlags trace_flags_; - const bool remote_parent_ = false; + trace_api::TraceId trace_id_; + trace_api::SpanId span_id_; + trace_api::TraceFlags trace_flags_; + bool remote_parent_ = false; }; } // namespace trace OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/tracer.h b/api/include/opentelemetry/trace/tracer.h index 68403fa1de..9ca8b94b8c 100644 --- a/api/include/opentelemetry/trace/tracer.h +++ b/api/include/opentelemetry/trace/tracer.h @@ -3,6 +3,8 @@ #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/scope.h" #include "opentelemetry/trace/span.h" #include "opentelemetry/version.h" @@ -30,7 +32,7 @@ class Tracer * key will be overwritten. */ virtual nostd::shared_ptr StartSpan(nostd::string_view name, - const KeyValueIterable &attributes, + const common::KeyValueIterable &attributes, const StartSpanOptions &options = {}) noexcept = 0; nostd::shared_ptr StartSpan(nostd::string_view name, @@ -39,12 +41,13 @@ class Tracer return this->StartSpan(name, {}, options); } - template ::value> * = nullptr> + template ::value> * = nullptr> nostd::shared_ptr StartSpan(nostd::string_view name, const T &attributes, const StartSpanOptions &options = {}) noexcept { - return this->StartSpan(name, KeyValueIterableView(attributes), options); + return this->StartSpan(name, common::KeyValueIterableView(attributes), options); } nostd::shared_ptr StartSpan( @@ -58,6 +61,35 @@ class Tracer options); } + /** + * Set the active span. The span will remain active until the returned Scope + * object is destroyed. + * @param span the span that should be set as the new active span. + * @return a Scope that controls how long the span will be active. + */ + nostd::unique_ptr WithActiveSpan(nostd::shared_ptr &span) noexcept + { + return nostd::unique_ptr(new Scope{span}); + } + + /** + * Get the currently active span. + * @return the currently active span, or an invalid default span if no span + * is active. + */ + nostd::shared_ptr GetCurrentSpan() noexcept + { + context::ContextValue active_span = context::RuntimeContext::GetValue(SpanKey); + if (nostd::holds_alternative>(active_span)) + { + return nostd::get>(active_span); + } + else + { + return nostd::shared_ptr(new DefaultSpan(SpanContext::GetInvalid())); + } + } + /** * Force any buffered spans to flush. * @param timeout to complete the flush diff --git a/api/include/opentelemetry/version.h b/api/include/opentelemetry/version.h index 1bbe71a217..aa0d16b0cc 100644 --- a/api/include/opentelemetry/version.h +++ b/api/include/opentelemetry/version.h @@ -12,4 +12,7 @@ #define OPENTELEMETRY_END_NAMESPACE \ }} + +#define OPENTELEMETRY_NAMESPACE opentelemetry :: OPENTELEMETRY_CONCAT(v, OPENTELEMETRY_ABI_VERSION_NO) + // clang-format on diff --git a/api/test/context/CMakeLists.txt b/api/test/context/CMakeLists.txt index 38d48ef0e8..2c9da11191 100644 --- a/api/test/context/CMakeLists.txt +++ b/api/test/context/CMakeLists.txt @@ -1,8 +1,12 @@ include(GoogleTest) -foreach(testname context_test) +foreach(testname context_test runtime_context_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) - gtest_add_tests(TARGET ${testname} TEST_PREFIX context. TEST_LIST ${testname}) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX context. + TEST_LIST ${testname}) endforeach() diff --git a/api/test/context/context_test.cc b/api/test/context/context_test.cc index f9a6dfbfd4..66b7f34c45 100644 --- a/api/test/context/context_test.cc +++ b/api/test/context/context_test.cc @@ -78,14 +78,19 @@ TEST(ContextTest, ContextInheritance) { using M = std::map; - M m1 = {{"test_key", (int64_t)123}, {"foo_key", (int64_t)456}}; - M m2 = {{"other_key", (int64_t)789}}; + M m1 = {{"test_key", (int64_t)123}, {"foo_key", (int64_t)321}}; + M m2 = {{"other_key", (int64_t)789}, {"another_key", (int64_t)987}}; context::Context test_context = context::Context(m1); context::Context foo_context = test_context.SetValues(m2); EXPECT_EQ(nostd::get(foo_context.GetValue("test_key")), 123); - EXPECT_EQ(nostd::get(foo_context.GetValue("foo_key")), 456); + EXPECT_EQ(nostd::get(foo_context.GetValue("foo_key")), 321); + EXPECT_EQ(nostd::get(foo_context.GetValue("other_key")), 789); + EXPECT_EQ(nostd::get(foo_context.GetValue("another_key")), 987); + + EXPECT_EQ(nostd::get(test_context.GetValue("other_key")), 0); + EXPECT_EQ(nostd::get(test_context.GetValue("another_key")), 0); } // Tests that copying a context copies the key value pairs as expected. diff --git a/api/test/context/runtime_context_test.cc b/api/test/context/runtime_context_test.cc index f9e229d57c..7924294185 100644 --- a/api/test/context/runtime_context_test.cc +++ b/api/test/context/runtime_context_test.cc @@ -1,5 +1,5 @@ +#include "opentelemetry/context/runtime_context.h" #include "opentelemetry/context/context.h" -#include "opentelemetry/context/threadlocal_context.h" #include diff --git a/api/test/core/CMakeLists.txt b/api/test/core/CMakeLists.txt index 633759a9d5..0d59831bb4 100644 --- a/api/test/core/CMakeLists.txt +++ b/api/test/core/CMakeLists.txt @@ -1,7 +1,10 @@ include(GoogleTest) add_executable(timestamp_test timestamp_test.cc) -target_link_libraries(timestamp_test ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) -gtest_add_tests(TARGET timestamp_test TEST_PREFIX trace. TEST_LIST - timestamp_test) +target_link_libraries( + timestamp_test ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) +gtest_add_tests( + TARGET timestamp_test + TEST_PREFIX trace. + TEST_LIST timestamp_test) diff --git a/api/test/metrics/CMakeLists.txt b/api/test/metrics/CMakeLists.txt index c9d2083d5a..f20a46c094 100644 --- a/api/test/metrics/CMakeLists.txt +++ b/api/test/metrics/CMakeLists.txt @@ -1,6 +1,10 @@ foreach(testname noop_instrument_test meter_provider_test noop_metrics_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) - gtest_add_tests(TARGET ${testname} TEST_PREFIX metrics. TEST_LIST ${testname}) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX metrics. + TEST_LIST ${testname}) endforeach() diff --git a/api/test/metrics/noop_instrument_test.cc b/api/test/metrics/noop_instrument_test.cc index 5e53599f50..c6bb8c7b81 100644 --- a/api/test/metrics/noop_instrument_test.cc +++ b/api/test/metrics/noop_instrument_test.cc @@ -11,7 +11,7 @@ namespace metrics void noopIntCallback(ObserverResult result) { std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; result.observe(1, labelkv); result.observe(-1, labelkv); } @@ -19,7 +19,7 @@ void noopIntCallback(ObserverResult result) void noopDoubleCallback(ObserverResult result) { std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; result.observe(1.5, labelkv); result.observe(-1.5, labelkv); } @@ -31,7 +31,7 @@ TEST(ValueObserver, Observe) NoopValueObserver beta("test", "none", "unitless", true, &noopDoubleCallback); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.observe(1, labelkv); beta.observe(1.5, labelkv); @@ -44,7 +44,7 @@ TEST(SumObserver, DefaultConstruction) NoopSumObserver beta("test", "none", "unitless", true, &noopDoubleCallback); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.observe(1, labelkv); beta.observe(1.5, labelkv); @@ -57,7 +57,7 @@ TEST(UpDownSumObserver, DefaultConstruction) NoopUpDownSumObserver beta("test", "none", "unitless", true, &noopDoubleCallback); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.observe(1, labelkv); beta.observe(1.0, labelkv); @@ -71,7 +71,7 @@ TEST(Counter, DefaultConstruction) NoopCounter beta("other", "none", "unitless", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.bind(labelkv); @@ -88,7 +88,7 @@ TEST(Counter, Add) NoopCounter beta("other", "none", "unitless", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.add(1, labelkv); beta.add(1.5, labelkv); @@ -109,7 +109,7 @@ TEST(UpDownCounter, DefaultConstruction) NoopUpDownCounter beta("other", "none", "unitless", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.bind(labelkv); @@ -126,7 +126,7 @@ TEST(UpDownCounter, Add) NoopUpDownCounter beta("other", "none", "unitless", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.add(1, labelkv); beta.add(1.5, labelkv); @@ -149,7 +149,7 @@ TEST(ValueRecorder, DefaultConstruction) NoopValueRecorder beta("other", "none", "unitless", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.bind(labelkv); @@ -166,7 +166,7 @@ TEST(ValueRecorder, Record) NoopValueRecorder beta("other", "none", "unitless", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.record(1, labelkv); beta.record(1.5, labelkv); diff --git a/api/test/metrics/noop_metrics_test.cc b/api/test/metrics/noop_metrics_test.cc index 85533e070e..8e43f78180 100644 --- a/api/test/metrics/noop_metrics_test.cc +++ b/api/test/metrics/noop_metrics_test.cc @@ -4,6 +4,7 @@ #include "opentelemetry/metrics/observer_result.h" #include "opentelemetry/metrics/sync_instruments.h" +#include #include OPENTELEMETRY_BEGIN_NAMESPACE @@ -11,12 +12,10 @@ OPENTELEMETRY_BEGIN_NAMESPACE using opentelemetry::metrics::Meter; using opentelemetry::metrics::NoopMeter; -namespace metrics_api = opentelemetry::metrics; - void Callback(opentelemetry::metrics::ObserverResult result) { std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; result.observe(1, labelkv); } @@ -42,7 +41,7 @@ TEST(NoopMeter, RecordBatch) std::unique_ptr m{std::unique_ptr(new NoopMeter{})}; std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; auto s = m->NewShortCounter("Test short counter", "For testing", "Unitless", true); diff --git a/api/test/nostd/BUILD b/api/test/nostd/BUILD index 35306e6a3c..c42d2b9255 100644 --- a/api/test/nostd/BUILD +++ b/api/test/nostd/BUILD @@ -16,6 +16,7 @@ cc_test( ], deps = [ "//api", + "@com_github_google_benchmark//:benchmark", "@com_google_googletest//:gtest_main", ], ) @@ -60,6 +61,7 @@ cc_test( ], deps = [ "//api", + "@com_github_google_benchmark//:benchmark", "@com_google_googletest//:gtest_main", ], ) @@ -71,6 +73,7 @@ cc_test( ], deps = [ "//api", + "@com_github_google_benchmark//:benchmark", "@com_google_googletest//:gtest_main", ], ) diff --git a/api/test/nostd/CMakeLists.txt b/api/test/nostd/CMakeLists.txt index fdbf8e7b07..096da4b48b 100644 --- a/api/test/nostd/CMakeLists.txt +++ b/api/test/nostd/CMakeLists.txt @@ -3,7 +3,11 @@ include(GoogleTest) foreach(testname function_ref_test string_view_test unique_ptr_test utility_test span_test shared_ptr_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) - gtest_add_tests(TARGET ${testname} TEST_PREFIX nostd. TEST_LIST ${testname}) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX nostd. + TEST_LIST ${testname}) endforeach() diff --git a/api/test/nostd/shared_ptr_test.cc b/api/test/nostd/shared_ptr_test.cc index 46151d88c4..ffc9816f5b 100644 --- a/api/test/nostd/shared_ptr_test.cc +++ b/api/test/nostd/shared_ptr_test.cc @@ -1,6 +1,8 @@ #include "opentelemetry/nostd/shared_ptr.h" +#include #include +#include using opentelemetry::nostd::shared_ptr; @@ -157,11 +159,11 @@ TEST(SharedPtrTest, Comparison) EXPECT_EQ(nullptr, ptr3); } -TEST(SharedPtrTest, Sort) +static void SharedPtrTest_Sort(size_t size = 10) { std::vector> nums; - for (int i = 10; i > 0; i--) + for (int i = size; i > 0; i--) { nums.push_back(shared_ptr(new int(i))); } @@ -177,3 +179,26 @@ TEST(SharedPtrTest, Sort) EXPECT_EQ(nums, nums2); } + +TEST(SharedPtrTest, Sort) +{ + SharedPtrTest_Sort(); +} + +static void SharedPtrTestSort(benchmark::State &state) +{ + for (auto _ : state) + { + SharedPtrTest_Sort(10000); + } +} +BENCHMARK(SharedPtrTestSort); + +TEST(SharedPtr, PerfTests) +{ + // Run all benchmarks + int argc = 0; + const char *argv[] = {""}; + ::benchmark::Initialize(&argc, (char **)(argv)); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/api/test/nostd/span_test.cc b/api/test/nostd/span_test.cc index 44e7d49a15..6d484bccad 100644 --- a/api/test/nostd/span_test.cc +++ b/api/test/nostd/span_test.cc @@ -1,5 +1,8 @@ #include "opentelemetry/nostd/span.h" +#include +#include + #include #include #include @@ -56,7 +59,10 @@ TEST(SpanTest, PointerCountConstruction) EXPECT_EQ(s2.data(), array.data()); EXPECT_EQ(s2.size(), array.size()); +#ifndef HAVE_CPP_STDLIB + /* This test is not supposed to fail with STL. Why is this invalid construct? */ EXPECT_DEATH((span{array.data(), array.size()}), ".*"); +#endif } TEST(SpanTest, RangeConstruction) @@ -71,7 +77,10 @@ TEST(SpanTest, RangeConstruction) EXPECT_EQ(s2.data(), array); EXPECT_EQ(s2.size(), 3); +#ifndef HAVE_CPP_STDLIB + /* This test is not supposed to fail with STL. Why is this invalid construct? */ EXPECT_DEATH((span{std::begin(array), std::end(array)}), ".*"); +#endif } TEST(SpanTest, ArrayConstruction) @@ -106,10 +115,15 @@ TEST(SpanTest, ContainerConstruction) EXPECT_EQ(s1.data(), v.data()); EXPECT_EQ(s1.size(), v.size()); - span s2{v}; + span s2{v.data(), 3}; + EXPECT_EQ(s2.data(), v.data()); EXPECT_EQ(s2.size(), v.size()); - EXPECT_DEATH((span{v}), ".*"); + +#ifndef HAVE_CPP_STDLIB + /* This test is not supposed to fail with STL. Why is this invalid construct? */ + EXPECT_DEATH((span{v.data(), 3}), ".*"); +#endif EXPECT_FALSE((std::is_constructible, std::vector>::value)); EXPECT_FALSE((std::is_constructible, std::list>::value)); @@ -173,3 +187,60 @@ TEST(SpanTest, Iteration) EXPECT_EQ(std::distance(s2.begin(), s2.end()), array.size()); EXPECT_TRUE(std::equal(s2.begin(), s2.end(), array.begin())); } + +static void SpanIterator(benchmark::State &state) +{ + constexpr size_t aSize = 100000; + std::array array{}; + for (size_t i = 0; i < aSize; i++) + { + array[i] = i; + } + span s1{array.data(), array.size()}; + for (auto _ : state) + { + size_t i = 0; + for (auto item : s1) + { + EXPECT_EQ(item, i); + i++; + } + } +} +BENCHMARK(SpanIterator); + +static void SpanConstructor(benchmark::State &state) +{ + constexpr size_t aSize = 1000000; + auto *aInt16 = new std::array{}; + auto *aInt32 = new std::array{}; + auto *aInt64 = new std::array{}; + for (size_t i = 0; i < aSize; i++) + { + (*aInt16)[i] = i; + (*aInt32)[i] = i; + (*aInt64)[i] = i; + } + size_t j = aSize; + for (auto _ : state) + { + benchmark::DoNotOptimize([&] { + span s1{aInt16->data(), aInt16->size()}; + span s2{aInt32->data(), aInt32->size()}; + span s3{aInt64->data(), aInt64->size()}; + }); + } + delete aInt16; + delete aInt32; + delete aInt64; +} +BENCHMARK(SpanConstructor); + +TEST(SpanTest, PerfTests) +{ + // Run all benchmarks + int argc = 0; + const char *argv[] = {""}; + ::benchmark::Initialize(&argc, (char **)(argv)); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/api/test/nostd/string_view_test.cc b/api/test/nostd/string_view_test.cc index 364410a312..01cbd29ce6 100644 --- a/api/test/nostd/string_view_test.cc +++ b/api/test/nostd/string_view_test.cc @@ -2,7 +2,10 @@ #include -#include "map" +#include +#include +#include +#include using opentelemetry::nostd::string_view; @@ -68,7 +71,7 @@ TEST(StringViewTest, SubstrPortion) TEST(StringViewTest, SubstrOutOfRange) { string_view s = "abc123"; -#if __EXCEPTIONS +#if __EXCEPTIONS || defined(HAVE_STDLIB_CPP) EXPECT_THROW(s.substr(10), std::out_of_range); #else EXPECT_DEATH({ s.substr(10); }, ""); @@ -102,3 +105,111 @@ TEST(StringViewTest, MapKeyOrdering) i++; } } + +static void StringViewSubStr(benchmark::State &state) +{ + std::string s = + "Hello OpenTelemetry nostd::string_view implementation! Feel free to evaluate my " + "performance."; + for (auto _ : state) + { + string_view sv = s; + auto oneSv = sv.substr(0, 5); + auto twoSv = sv.substr(6, 5); + auto threeSv = sv.substr(12, 5); + auto fourSv = sv.substr(18, 11); + auto fiveSv = sv.substr(30, 5); + benchmark::DoNotOptimize(oneSv); + benchmark::DoNotOptimize(twoSv); + benchmark::DoNotOptimize(threeSv); + benchmark::DoNotOptimize(fourSv); + benchmark::DoNotOptimize(fiveSv); + } +} +BENCHMARK(StringViewSubStr); + +static void StringViewMaps(benchmark::State &state) +{ + std::map m; + std::string txt; + size_t i = 0; + for (auto _ : state) + { + i %= 200; // up to 200 key-value pairs in this collection + m[string_view(std::to_string(i))] = string_view(std::to_string(i)); + i += 1; + }; +} +BENCHMARK(StringViewMaps); + +static void StringViewFromCString(benchmark::State &state) +{ + std::string s = + "Hello OpenTelemetry nostd::string_view implementation! Feel free to evaluate my " + "performance."; + for (auto _ : state) + { + string_view sv(s.c_str()); + // scan thru string_view + for (const auto &c : sv) + ; + }; +} +BENCHMARK(StringViewFromCString); + +static void StringViewToString(benchmark::State &state) +{ + string_view s = + "Hello OpenTelemetry nostd::string_view implementation! Feel free to evaluate my " + "performance."; + std::string txt; + for (auto _ : state) + { + txt = std::string(s.data(), s.length()); + }; +} +BENCHMARK(StringViewToString); + +static void StringViewExplode(benchmark::State &state) +{ + for (auto _ : state) + { + string_view sv = + "Hello OpenTelemetry nostd::string_view implementation! Feel free to evaluate my " + "performance."; + std::string s; + for (size_t i = 0; i < 5; i++) + { + s += std::string(sv.data(), sv.length()); + sv = s; + string_view sv2 = s; + if (sv == sv2) + ; // FIXME: Warning C4390 ';' : empty controlled statement found; is this the intent ? + } + }; +} +BENCHMARK(StringViewExplode); + +static void StringViewVector(benchmark::State &state) +{ + std::vector v; + for (auto _ : state) + { + string_view sv = + "Hello OpenTelemetry nostd::string_view implementation! Feel free to evaluate my " + "performance."; + v.push_back(sv); + if (v.size() > 10000) + v.clear(); + } +} +BENCHMARK(StringViewVector); + +TEST(StringView, PerfTests) +{ + // Run all benchmarks + int argc = 0; + const char *argv[] = {""}; + ::benchmark::Initialize(&argc, (char **)(argv)); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/api/test/nostd/utility_test.cc b/api/test/nostd/utility_test.cc index ee38f3f474..edf5cd3f9e 100644 --- a/api/test/nostd/utility_test.cc +++ b/api/test/nostd/utility_test.cc @@ -1,5 +1,6 @@ #include "opentelemetry/nostd/utility.h" +#include #include #include @@ -20,7 +21,8 @@ TEST(UtilityTest, Data) std::vector v = {1, 2, 3}; int array[3] = {1, 2, 3}; std::initializer_list list{1, 2, 3}; - int x; + int x = 0; + std::ignore = x; EXPECT_EQ(opentelemetry::nostd::data(v), v.data()); EXPECT_EQ(opentelemetry::nostd::data(array), array); @@ -32,7 +34,8 @@ TEST(UtilityTest, Size) { std::vector v = {1, 2, 3}; int array[3] = {1, 2, 3}; - int x; + int x = 0; + std::ignore = x; EXPECT_EQ(opentelemetry::nostd::size(v), v.size()); EXPECT_EQ(opentelemetry::nostd::size(array), 3); diff --git a/api/test/plugin/CMakeLists.txt b/api/test/plugin/CMakeLists.txt index d17c300794..29a75b6b84 100644 --- a/api/test/plugin/CMakeLists.txt +++ b/api/test/plugin/CMakeLists.txt @@ -1,8 +1,11 @@ include(GoogleTest) add_executable(dynamic_load_test dynamic_load_test.cc) -target_link_libraries(dynamic_load_test ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) +target_link_libraries( + dynamic_load_test ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) target_link_libraries(dynamic_load_test ${CMAKE_DL_LIBS}) -gtest_add_tests(TARGET dynamic_load_test TEST_PREFIX plugin. TEST_LIST - dynamic_load_test) +gtest_add_tests( + TARGET dynamic_load_test + TEST_PREFIX plugin. + TEST_LIST dynamic_load_test) diff --git a/api/test/plugin/dynamic_load_test.cc b/api/test/plugin/dynamic_load_test.cc index 2e630f6649..3e0e45ecc1 100644 --- a/api/test/plugin/dynamic_load_test.cc +++ b/api/test/plugin/dynamic_load_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/plugin/dynamic_load.h" -#include "opentelemetry/context/threadlocal_context.h" #include diff --git a/api/test/trace/BUILD b/api/test/trace/BUILD index b77ae1e2fe..0a313a14fe 100644 --- a/api/test/trace/BUILD +++ b/api/test/trace/BUILD @@ -104,3 +104,25 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "scope_test", + srcs = [ + "scope_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) + +cc_test( + name = "tracer_test", + srcs = [ + "tracer_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/api/test/trace/CMakeLists.txt b/api/test/trace/CMakeLists.txt index 558ce2d429..6c69883b2c 100644 --- a/api/test/trace/CMakeLists.txt +++ b/api/test/trace/CMakeLists.txt @@ -6,11 +6,17 @@ foreach( trace_id_test trace_flags_test span_context_test - noop_test) - add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) - gtest_add_tests(TARGET ${testname} TEST_PREFIX trace. TEST_LIST ${testname}) + scope_test + noop_test + tracer_test) + add_executable(api_${testname} "${testname}.cc") + target_link_libraries( + api_${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET api_${testname} + TEST_PREFIX trace. + TEST_LIST api_${testname}) endforeach() add_executable(span_id_benchmark span_id_benchmark.cc) diff --git a/api/test/trace/key_value_iterable_view_test.cc b/api/test/trace/key_value_iterable_view_test.cc index 9c262eb3f1..9fba29734f 100644 --- a/api/test/trace/key_value_iterable_view_test.cc +++ b/api/test/trace/key_value_iterable_view_test.cc @@ -1,12 +1,12 @@ -#include "opentelemetry/trace/key_value_iterable_view.h" - -#include +#include "opentelemetry/common/key_value_iterable_view.h" #include +#include +#include "opentelemetry/nostd/type_traits.h" using namespace opentelemetry; -static int TakeKeyValues(const trace::KeyValueIterable &iterable) +static int TakeKeyValues(const common::KeyValueIterable &iterable) { std::map result; int count = 0; @@ -17,27 +17,27 @@ static int TakeKeyValues(const trace::KeyValueIterable &iterable) return count; } -template ::value> * = nullptr> +template ::value> * = nullptr> static int TakeKeyValues(const T &iterable) { - return TakeKeyValues(trace::KeyValueIterableView{iterable}); + return TakeKeyValues(common::KeyValueIterableView{iterable}); } TEST(KeyValueIterableViewTest, is_key_value_iterable) { using M1 = std::map; - EXPECT_TRUE(bool{trace::detail::is_key_value_iterable::value}); + EXPECT_TRUE(bool{common::detail::is_key_value_iterable::value}); using M2 = std::map; - EXPECT_TRUE(bool{trace::detail::is_key_value_iterable::value}); + EXPECT_TRUE(bool{common::detail::is_key_value_iterable::value}); using M3 = std::map; - EXPECT_TRUE(bool{trace::detail::is_key_value_iterable::value}); + EXPECT_TRUE(bool{common::detail::is_key_value_iterable::value}); struct A {}; using M4 = std::map; - EXPECT_FALSE(bool{trace::detail::is_key_value_iterable::value}); + EXPECT_FALSE(bool{common::detail::is_key_value_iterable::value}); } TEST(KeyValueIterableViewTest, ForEachKeyValue) @@ -53,7 +53,7 @@ TEST(KeyValueIterableViewTest, ForEachKeyValueWithExit) { using M = std::map; M m1 = {{"abc", "123"}, {"xyz", "456"}}; - trace::KeyValueIterableView iterable{m1}; + common::KeyValueIterableView iterable{m1}; int count = 0; auto exit = iterable.ForEachKeyValue([&count](nostd::string_view /*key*/, common::AttributeValue /*value*/) noexcept { diff --git a/api/test/trace/noop_test.cc b/api/test/trace/noop_test.cc index 989f2acddc..4a069430ef 100644 --- a/api/test/trace/noop_test.cc +++ b/api/test/trace/noop_test.cc @@ -7,7 +7,6 @@ #include -using opentelemetry::context::Token; using opentelemetry::core::SystemTimestamp; using opentelemetry::trace::NoopTracer; using opentelemetry::trace::SpanContext; @@ -42,7 +41,5 @@ TEST(NoopTest, UseNoopTracers) SystemTimestamp t1; s1->AddEvent("test_time_stamp", t1); - s1->SetToken(opentelemetry::nostd::unique_ptr(nullptr)); - s1->GetContext(); -} \ No newline at end of file +} diff --git a/api/test/trace/propagation/CMakeLists.txt b/api/test/trace/propagation/CMakeLists.txt index f2a9815426..8f13cd68de 100644 --- a/api/test/trace/propagation/CMakeLists.txt +++ b/api/test/trace/propagation/CMakeLists.txt @@ -1,6 +1,10 @@ foreach(testname http_text_format_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) - gtest_add_tests(TARGET ${testname} TEST_PREFIX trace. TEST_LIST ${testname}) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX trace. + TEST_LIST ${testname}) endforeach() diff --git a/api/test/trace/propagation/http_text_format_test.cc b/api/test/trace/propagation/http_text_format_test.cc index c6262a1606..bbdba64d36 100644 --- a/api/test/trace/propagation/http_text_format_test.cc +++ b/api/test/trace/propagation/http_text_format_test.cc @@ -66,8 +66,9 @@ TEST(HTTPTextFormatTest, NoSendEmptyTraceState) // If the trace state is empty, do not set the header. const std::map carrier = { {"traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-0102030405060708-01"}}; - context::Context ctx1 = - context::Context("current-span", nostd::shared_ptr(new trace::DefaultSpan())); + context::Context ctx1 = context::Context{ + "current-span", + nostd::shared_ptr(new trace::DefaultSpan(trace::SpanContext::GetInvalid()))}; context::Context ctx2 = format.Extract(Getter, carrier, ctx1); std::map c2 = {}; format.Inject(Setter, c2, ctx2); diff --git a/api/test/trace/scope_test.cc b/api/test/trace/scope_test.cc new file mode 100644 index 0000000000..7c68d94abe --- /dev/null +++ b/api/test/trace/scope_test.cc @@ -0,0 +1,47 @@ +#include "opentelemetry/trace/scope.h" +#include "opentelemetry/context/context.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/noop.h" + +#include + +using opentelemetry::trace::NoopSpan; +using opentelemetry::trace::Scope; +using opentelemetry::trace::Span; +namespace nostd = opentelemetry::nostd; +namespace context = opentelemetry::context; + +TEST(ScopeTest, Construct) +{ + nostd::shared_ptr span(new NoopSpan(nullptr)); + Scope scope(span); + + context::ContextValue active_span_value = context::RuntimeContext::GetValue(SpanKey); + ASSERT_TRUE(nostd::holds_alternative>(active_span_value)); + + auto active_span = nostd::get>(active_span_value); + ASSERT_EQ(active_span, span); +} + +TEST(ScopeTest, Destruct) +{ + nostd::shared_ptr span(new NoopSpan(nullptr)); + Scope scope(span); + + { + nostd::shared_ptr span_nested(new NoopSpan(nullptr)); + Scope scope_nested(span_nested); + + context::ContextValue active_span_value = context::RuntimeContext::GetValue(SpanKey); + ASSERT_TRUE(nostd::holds_alternative>(active_span_value)); + + auto active_span = nostd::get>(active_span_value); + ASSERT_EQ(active_span, span_nested); + } + + context::ContextValue active_span_value = context::RuntimeContext::GetValue(SpanKey); + ASSERT_TRUE(nostd::holds_alternative>(active_span_value)); + + auto active_span = nostd::get>(active_span_value); + ASSERT_EQ(active_span, span); +} diff --git a/api/test/trace/span_context_test.cc b/api/test/trace/span_context_test.cc index b364348ccd..1a4b7c62a2 100644 --- a/api/test/trace/span_context_test.cc +++ b/api/test/trace/span_context_test.cc @@ -42,7 +42,7 @@ TEST(SpanContextTest, TraceFlags) // Test that SpanContext is invalid TEST(SpanContextTest, Invalid) { - SpanContext s1(false, false); + SpanContext s1 = SpanContext::GetInvalid(); EXPECT_FALSE(s1.IsValid()); // Test that trace id and span id are invalid diff --git a/api/test/trace/tracer_test.cc b/api/test/trace/tracer_test.cc new file mode 100644 index 0000000000..b97a1b285e --- /dev/null +++ b/api/test/trace/tracer_test.cc @@ -0,0 +1,35 @@ +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/trace/noop.h" +#include "opentelemetry/trace/scope.h" + +#include + +namespace trace_api = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; +namespace context = opentelemetry::context; + +TEST(TracerTest, GetCurrentSpan) +{ + std::unique_ptr tracer(new trace_api::NoopTracer()); + nostd::shared_ptr span_first(new trace_api::NoopSpan(nullptr)); + nostd::shared_ptr span_second(new trace_api::NoopSpan(nullptr)); + + auto current = tracer->GetCurrentSpan(); + ASSERT_FALSE(current->GetContext().IsValid()); + + auto scope_first = tracer->WithActiveSpan(span_first); + current = tracer->GetCurrentSpan(); + ASSERT_EQ(current, span_first); + + auto scope_second = tracer->WithActiveSpan(span_second); + current = tracer->GetCurrentSpan(); + ASSERT_EQ(current, span_second); + + scope_second.reset(nullptr); + current = tracer->GetCurrentSpan(); + ASSERT_EQ(current, span_first); + + scope_first.reset(nullptr); + current = tracer->GetCurrentSpan(); + ASSERT_FALSE(current->GetContext().IsValid()); +} diff --git a/ci/do_ci.sh b/ci/do_ci.sh index e97686b0b7..5f64601521 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -30,6 +30,16 @@ elif [[ "$1" == "cmake.c++20.test" ]]; then make make test exit 0 +elif [[ "$1" == "cmake.legacy.test" ]]; then + cd "${BUILD_DIR}" + rm -rf * + cmake -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="-Werror" \ + -DCMAKE_CXX_STANDARD=11 \ + "${SRC_DIR}" + make + make test + exit 0 elif [[ "$1" == "cmake.exporter.otprotocol.test" ]]; then cd "${BUILD_DIR}" rm -rf * @@ -125,7 +135,7 @@ elif [[ "$1" == "bazel.tsan" ]]; then exit 0 elif [[ "$1" == "bazel.valgrind" ]]; then bazel build $BAZEL_OPTIONS //... - bazel test --run_under="/usr/bin/valgrind --leak-check=full --error-exitcode=1" $BAZEL_TEST_OPTIONS //... + bazel test --run_under="/usr/bin/valgrind --leak-check=full --error-exitcode=1 --suppressions=\"${SRC_DIR}/ci/valgrind-suppressions\"" $BAZEL_TEST_OPTIONS //... exit 0 elif [[ "$1" == "benchmark" ]]; then [ -z "${BENCHMARK_DIR}" ] && export BENCHMARK_DIR=$HOME/benchmark @@ -146,6 +156,7 @@ elif [[ "$1" == "format" ]]; then if [[ ! -z "$CHANGED" ]]; then echo "The following files have changes:" echo "$CHANGED" + git diff exit 1 fi exit 0 diff --git a/ci/docfx.cmd b/ci/docfx.cmd new file mode 100644 index 0000000000..46582ac980 --- /dev/null +++ b/ci/docfx.cmd @@ -0,0 +1,18 @@ +SETLOCAL ENABLEEXTENSIONS + +type ci\docfx.json > docfx.json +type ci\toc.yml > toc.yml +docfx build docfx.json > docfx.log +DEL docfx.json 2> NUL +DEL toc.yml 2> NUL +@IF NOT %ERRORLEVEL% == 0 ( + type docfx.log + ECHO Error: docfx build failed. 1>&2 + EXIT /B %ERRORLEVEL% +) +@type docfx.log +@type docfx.log | findstr /C:"Build succeeded." +@IF NOT %ERRORLEVEL% == 0 ( + ECHO Error: you have introduced build warnings. 1>&2 + EXIT /B %ERRORLEVEL% +) diff --git a/ci/docfx.json b/ci/docfx.json new file mode 100644 index 0000000000..9de16e8f9b --- /dev/null +++ b/ci/docfx.json @@ -0,0 +1,26 @@ +{ + "build": { + "content": [ + { + "files": [ + "**.md", + "toc.yml" + ] + } + ], + "resource": [ + { + "files": [ + + ] + } + ], + "dest": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": true, + "disableGitFeatures": true + } +} diff --git a/ci/toc.yml b/ci/toc.yml new file mode 100644 index 0000000000..64357acfca --- /dev/null +++ b/ci/toc.yml @@ -0,0 +1,2 @@ +- name: OpenTelemetry CPP + href: ./README.md diff --git a/ci/valgrind-suppressions b/ci/valgrind-suppressions new file mode 100644 index 0000000000..28741e7a17 --- /dev/null +++ b/ci/valgrind-suppressions @@ -0,0 +1,15 @@ +{ + + Memcheck:Leak + ... + fun:grpc_shutdown + ... +} + +{ + + Memcheck:Leak + ... + fun:grpc_init + ... +} diff --git a/docs/abi-compatibility.md b/docs/abi-compatibility.md new file mode 100644 index 0000000000..53936de0bd --- /dev/null +++ b/docs/abi-compatibility.md @@ -0,0 +1,35 @@ +# Windows + +## Visual Studio 2015, 2017, 2019 + +The Microsoft C++ (MSVC) compiler toolsets in Visual Studio 2013 and earlier don't guarantee binary compatibility across versions. You can't link object files, static libraries, dynamic libraries, and executables built by different versions. The ABIs, object formats, and runtime libraries are incompatible. + +We've changed this behavior in Visual Studio 2015, 2017, and 2019. The runtime libraries and apps compiled by any of these versions of the compiler are binary-compatible. It's reflected in the C++ toolset major number, which is 14 for all three versions. (The toolset version is v140 for Visual Studio 2015, v141 for 2017, and v142 for 2019). Say you have third-party libraries built by Visual Studio 2015. You can still use them in an application built by Visual Studio 2017 or 2019. There's no need to recompile with a matching toolset. The latest version of the Microsoft Visual C++ Redistributable package (the Redistributable) works for all of them. + +There are three important restrictions on binary compatibility: + +- You can mix binaries built by different versions of the toolset. However, you must use a toolset at least as recent as the most recent binary to link your app. Here's an example: you can link an app compiled using the 2017 toolset to a static library compiled using 2019, if they're linked using the 2019 toolset. + +- The Redistributable your app uses has a similar binary-compatibility restriction. When you mix binaries built by different supported versions of the toolset, the Redistributable version must be at least as new as the latest toolset used by any app component. + +- Static libraries or object files compiled using the /GL (Whole program optimization) compiler switch aren't binary-compatible across versions. All object files and libraries compiled using /GL must use exactly the same toolset for the compile and the final link. + +[Reference](https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=vs-2019) + +## Breaking Compatibility + +### Exceptions + +- [__CxxFrameHandler4](https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/) - static library built with Visual Studio 2019 wont link to executable compiled with Visual Studio 2017 + +### Release vs Debug libraries on Windows + +- [\_SECURE_SCL](https://docs.microsoft.com/en-us/cpp/standard-library/secure-scl?view=vs-2019) - checked iterators (old) + +- [\_ITERATOR_DEBUG_LEVEL](https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=vs-2019) - checked iterators (new) + +### Spectre Mitigation + +- [Runtime Libraries for Spectre Mitigation](https://docs.microsoft.com/en-us/cpp/build/reference/qspectre?view=vs-2019) + +- [New Spectre Mitigations in Visual Studio 2019](https://devblogs.microsoft.com/cppblog/more-spectre-mitigations-in-msvc/) diff --git a/docs/building-with-stdlib.md b/docs/building-with-stdlib.md new file mode 100644 index 0000000000..4031be90d2 --- /dev/null +++ b/docs/building-with-stdlib.md @@ -0,0 +1,102 @@ +# Building with Standard C++ Library + +Standard Library build flavor works best for statically linking the SDK in a +process (environment where ABI compat is not a requirement), or for +"header-only" implementation of SDK. + +Proposed approach cannot be employed for shared libs in environments where +ABI compatibility is required: + +- OpenTelemetry SDK binary compiled with compiler A + STL B +- may not be ABI compatible with the main executable compiled with compiler + C + STL D on a same OS. +Thus, this approach works best only for statically lined / header only use. + +## Motivation + +In certain scenarios it may be of benefit to compile the OpenTelemetry SDK from +source using standard library container classes (`std::map`, `std::string_view`, +`std::span`, `std::variant`) instead of OpenTelemetry `nostd::` analogs (which +were backported to C++11 and designed as ABI-stable). This PR also can be used +as a foundation for alternate approach - to also allow bindings to [abseil](https://github.com/abseil/abseil-cpp) +classes. That is in environments that would rather prefer `Abseil` classes +over the standard lib or `nostd`. + +The approach is to provide totally opaque (from SDK code / SDK developer +perspective) mapping / aliasing from `nostd::` classes back to their `std::` +counterparts, and that is only done in environments where this is possible. + +We continue fully supporting both models and run CI for both. + +## Pros and Cons + +### Using trusted/certified Standard Library + +Using standard library implementation classes in customer code and on API +surface instead of `nostd` classes. Certain environments would prefer to have +**standard** instead of non-standard classes due to security, performance, +stability and debug'ability considerations. For example, Visual Studio IDE +provides 1st class Debug experience for Standard containers, plus additional +runtime-checks for Debug builds that use Standard containers. + +### Minimizing binary size + +No need to marshal types from standard to `nostd`, then back to standard +library means less code involved and less memcpy. We use Standard classes +used elsewhere in the app. We can subsequently improve the process by avoiding +`KeyValueIterable` transform (and, thus, unnecessary memcpy) when we know +that the incoming types does not need to be 'transferred' across ABI boundary. +This is the case for statically linked executables and when SDK is implemented +as 'header-only' library. + +### Avoiding unnecessary extra memcpy (perf improvements) + +No need to transform from 'native' standard library types, e.g. `std::map` via +`KeyValueIterable` means we can bypass that transformation process, if and when +we know that the ABI compatibility is not a requirement in certain environment. +ETA perf improvement is 1.5%-3% better perf since an extra transform, memcpy, +iteration for-each key-value is avoided. + +### ABI stability + +Obviously this approach does not work in environment where ABI stability +guarantee must be provided, e.g. for dynamically loadable OpenTelemetry SDK +module and plugins for products such as NGINX, Envoy, etc. + +## Scope of changes needed to implement the feature + +### Separate flavors of SDK build + +Supported build flavors: + +- `nostd` - OpenTelemetry backport of classes for C++11. Not using STD Lib. +- `stl` - Standard Library. Best be compiled with C++20 compaitlbe compiler. + C++17 may be used with additional dependencies, e.g. MS-GSL or Abseil. +- `absl` - TODO: this should allow using Abseil C++ lirary only (no MS-GSL). + +### Test setup + +Tests to validate that all OpenTelemetry functionality is working the same +identical way irrespective of what C++ runtime / STL library it is compiled +with. + +### Implementation Details + +Feature allows to alias from `nostd::` to `std::` classes for C++17 and above. + +Consistent handling of `std::variant` across various OS: + +- backport of a few missing variant features, e.g. `std::get` and `std::visit` + for older version of Mac OS X. Patches that enable proper + handling of `std::visit` and `std::variant` irrespective of OS version + to resolve [this quirk](https://stackoverflow.com/questions/52310835/xcode-10-call-to-unavailable-function-stdvisit). + +- ability to optionally borrow implementation of C++20 `gsl::span` from + [Microsoft Guidelines Support Library](https://github.com/microsoft/GSL). + This is not necessary for C++20 and above compilers. + +- ability to use Abseil `absl::variant` for Visual Studio 2015 + +- set of tools to locally build the SDK with both options across various OS + and compilers, including vs2015 (C++11), vs2017 (C++17), vs2019 (C++20), + ubuntu-18.xx (C++17), ubuntu-20.xx/gcc-9 (C++20), Mac OS X (C++17 and above). diff --git a/docs/cpp-metrics-api-design.md b/docs/cpp-metrics-api-design.md index fc58ed7ea7..8279bfb4a3 100644 --- a/docs/cpp-metrics-api-design.md +++ b/docs/cpp-metrics-api-design.md @@ -248,7 +248,7 @@ Metric instruments are created through instances of the `Meter` class and each t * Monotonicity: A monotonic instrument is an additive instrument, where the progression of each sum is non-decreasing. Monotonic instruments are useful for monitoring rate information. The following instrument types will be supported: -![Metric Instrument Table](../images/MetricInstrumentsTable.png) + ### Metric Event Data Model diff --git a/docs/cpp-metrics-sdk-design.md b/docs/cpp-metrics-sdk-design.md index 583771bdac..586372fc2b 100644 --- a/docs/cpp-metrics-sdk-design.md +++ b/docs/cpp-metrics-sdk-design.md @@ -13,7 +13,7 @@ ## SDK Data Path Diagram -![Data Path Diagram](../images/DataPath.png) + This is the control path our implementation of the metrics SDK will follow. There are five main components: The controller, accumulator, aggregators, processor, and exporter. Each of these components will be further elaborated on. @@ -351,7 +351,7 @@ The only addition to the SDK metric instrument classes from their API counterpar Note: these requirements come from a specification currently under development. Changes and feedback are in [PR #347](https://github.com/open-telemetry/opentelemetry-specification/pull/347) and the current document is linked [here](https://github.com/open-telemetry/opentelemetry-specification/blob/64bbb0c611d849b90916005d7714fa2a7132d0bf/specification/metrics/sdk.md). -![Data Path Diagram](../images/DataPath.png) + ## **Accumulator** diff --git a/docs/cpp-ostream-exporter-design.md b/docs/cpp-ostream-exporter-design.md index 45a0de10b4..864a7d9c9b 100644 --- a/docs/cpp-ostream-exporter-design.md +++ b/docs/cpp-ostream-exporter-design.md @@ -35,7 +35,7 @@ The goal of the interface is to minimize burden of implementation for protocol-d The SpanExporter is called through the SpanProcessor, which passes finished spans to the configured SpanExporter, as soon as they are finished. The SpanProcessor also shutdown the exporter by the Shutdown function within the SpanProcessor. -![SDK Data Path](./images/SpanDataPath.png) + The specification states: exporter must support two functions: Export and Shutdown. @@ -127,7 +127,7 @@ The MetricsExporter has the same requirements as the SpanExporter. The exporter Exports a batch of telemetry data. Protocol exporters that will implement this function are typically expected to serialize and transmit the data to the destination. -![SDK Data Path](./images/DataPath.png) + ### `Export(batch of Records)` diff --git a/examples/batch/CMakeLists.txt b/examples/batch/CMakeLists.txt index d1146d59b3..41654aeb93 100644 --- a/examples/batch/CMakeLists.txt +++ b/examples/batch/CMakeLists.txt @@ -2,5 +2,6 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/ostream/include) add_executable(batch_span_processor_example main.cc) -target_link_libraries(batch_span_processor_example ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_exporter_ostream_span opentelemetry_trace) +target_link_libraries( + batch_span_processor_example ${CMAKE_THREAD_LIBS_INIT} ${CORE_RUNTIME_LIBS} + opentelemetry_exporter_ostream_span opentelemetry_trace) diff --git a/examples/batch/main.cc b/examples/batch/main.cc index e159984ddf..7d7fa57f36 100644 --- a/examples/batch/main.cc +++ b/examples/batch/main.cc @@ -4,8 +4,6 @@ #include "opentelemetry/exporters/ostream/span_exporter.h" #include "opentelemetry/sdk/trace/batch_span_processor.h" -#include "opentelemetry/context/threadlocal_context.h" - #include #include diff --git a/examples/metrics_simple/CMakeLists.txt b/examples/metrics_simple/CMakeLists.txt index 4269318871..add3a153ce 100644 --- a/examples/metrics_simple/CMakeLists.txt +++ b/examples/metrics_simple/CMakeLists.txt @@ -3,4 +3,4 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/ostream/include) add_executable(simple_metrics main.cc) target_link_libraries( simple_metrics ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics - opentelemetry_exporter_ostream_metrics) + ${CORE_RUNTIME_LIBS} opentelemetry_exporter_ostream_metrics) diff --git a/examples/metrics_simple/README.md b/examples/metrics_simple/README.md index 7581dfb2f8..f840fa1e89 100644 --- a/examples/metrics_simple/README.md +++ b/examples/metrics_simple/README.md @@ -33,7 +33,7 @@ shared_ptr processor = shared_ptr(new Ungrou // Observer callback function void SumObserverCallback(metrics_api::ObserverResult result){ std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; result.observe(1,labelkv); } @@ -43,7 +43,7 @@ auto obs= meter->NewIntSumObserver("Counter","none", "none", true, &SumObserverC // Create a label set which annotates metric values std::map labels = {{"key", "value"}}; -auto labelkv = trace::KeyValueIterableView{labels}; +auto labelkv = common::KeyValueIterableView{labels}; // Capture data from instruments. Note that the asynchronous instrument is updates // automatically though its callback at the collection interval. Additional measurments diff --git a/examples/metrics_simple/main.cc b/examples/metrics_simple/main.cc index 4f0637371d..ca8bb2bda3 100644 --- a/examples/metrics_simple/main.cc +++ b/examples/metrics_simple/main.cc @@ -7,7 +7,6 @@ namespace sdkmetrics = opentelemetry::sdk::metrics; namespace nostd = opentelemetry::nostd; -namespace trace = opentelemetry::trace; int main() { @@ -33,7 +32,7 @@ int main() // Create a labelset std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; // Create arrays of instrument and values to add to them metrics_api::SynchronousInstrument *iinstr_arr[] = {intupdowncounter.get(), diff --git a/examples/otlp/CMakeLists.txt b/examples/otlp/CMakeLists.txt index 23dc75fe6d..02eaa2c49d 100644 --- a/examples/otlp/CMakeLists.txt +++ b/examples/otlp/CMakeLists.txt @@ -4,8 +4,9 @@ include_directories(${CMAKE_SOURCE_DIR}/exporters/otlp/include) add_library(otlp_foo_library foo_library/foo_library.cc) target_link_libraries(otlp_foo_library ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_api) + ${CORE_RUNTIME_LIBS} opentelemetry_api) add_executable(example_otlp main.cc) -target_link_libraries(example_otlp ${CMAKE_THREAD_LIBS_INIT} otlp_foo_library - opentelemetry_trace opentelemetry_exporter_otprotocol) +target_link_libraries( + example_otlp ${CMAKE_THREAD_LIBS_INIT} ${CORE_RUNTIME_LIBS} otlp_foo_library + opentelemetry_trace opentelemetry_exporter_otprotocol) diff --git a/examples/otlp/foo_library/foo_library.cc b/examples/otlp/foo_library/foo_library.cc index fb79781635..fdbf5b892b 100644 --- a/examples/otlp/foo_library/foo_library.cc +++ b/examples/otlp/foo_library/foo_library.cc @@ -1,4 +1,3 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/trace/provider.h" namespace trace = opentelemetry::trace; diff --git a/examples/plugin/load/main.cc b/examples/plugin/load/main.cc index 5348838942..3c6799fed0 100644 --- a/examples/plugin/load/main.cc +++ b/examples/plugin/load/main.cc @@ -1,4 +1,3 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/plugin/dynamic_load.h" #include diff --git a/examples/plugin/plugin/tracer.cc b/examples/plugin/plugin/tracer.cc index 8226fc1ce0..0b6fb1b903 100644 --- a/examples/plugin/plugin/tracer.cc +++ b/examples/plugin/plugin/tracer.cc @@ -1,5 +1,4 @@ #include "tracer.h" -#include "opentelemetry/context/runtime_context.h" #include "opentelemetry/nostd/unique_ptr.h" #include @@ -18,9 +17,9 @@ class Span final : public trace::Span public: Span(std::shared_ptr &&tracer, nostd::string_view name, - const opentelemetry::trace::KeyValueIterable & /*attributes*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, const trace::StartSpanOptions & /*options*/) noexcept - : tracer_{std::move(tracer)}, name_{name} + : tracer_{std::move(tracer)}, name_{name}, span_context_{trace::SpanContext::GetInvalid()} { std::cout << "StartSpan: " << name << "\n"; } @@ -39,7 +38,7 @@ class Span final : public trace::Span void AddEvent(nostd::string_view /*name*/, core::SystemTimestamp /*timestamp*/, - const trace::KeyValueIterable & /*attributes*/) noexcept override + const common::KeyValueIterable & /*attributes*/) noexcept override {} void SetStatus(trace::CanonicalCode /*code*/, @@ -54,8 +53,6 @@ class Span final : public trace::Span trace::SpanContext GetContext() const noexcept override { return span_context_; } - void SetToken(nostd::unique_ptr &&token) noexcept override {} - private: std::shared_ptr tracer_; std::string name_; @@ -67,7 +64,7 @@ Tracer::Tracer(nostd::string_view /*output*/) {} nostd::shared_ptr Tracer::StartSpan( nostd::string_view name, - const opentelemetry::trace::KeyValueIterable &attributes, + const opentelemetry::common::KeyValueIterable &attributes, const trace::StartSpanOptions &options) noexcept { return nostd::shared_ptr{ diff --git a/examples/plugin/plugin/tracer.h b/examples/plugin/plugin/tracer.h index c50b5f458b..7d6048c88d 100644 --- a/examples/plugin/plugin/tracer.h +++ b/examples/plugin/plugin/tracer.h @@ -13,7 +13,7 @@ class Tracer final : public opentelemetry::trace::Tracer, // opentelemetry::trace::Tracer opentelemetry::nostd::shared_ptr StartSpan( opentelemetry::nostd::string_view name, - const opentelemetry::trace::KeyValueIterable & /*attributes*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/, const opentelemetry::trace::StartSpanOptions & /*options */) noexcept override; void ForceFlushWithMicroseconds(uint64_t /*timeout*/) noexcept override {} diff --git a/examples/simple/foo_library/foo_library.cc b/examples/simple/foo_library/foo_library.cc index 0990b85ced..73077f135e 100644 --- a/examples/simple/foo_library/foo_library.cc +++ b/examples/simple/foo_library/foo_library.cc @@ -13,21 +13,30 @@ nostd::shared_ptr get_tracer() void f1() { - auto span = get_tracer()->StartSpan("f1"); + auto span = get_tracer()->StartSpan("f1"); + auto scope = get_tracer()->WithActiveSpan(span); + + span->End(); } void f2() { - auto span = get_tracer()->StartSpan("f2"); + auto span = get_tracer()->StartSpan("f2"); + auto scope = get_tracer()->WithActiveSpan(span); f1(); f1(); + + span->End(); } } // namespace void foo_library() { - auto span = get_tracer()->StartSpan("library"); + auto span = get_tracer()->StartSpan("library"); + auto scope = get_tracer()->WithActiveSpan(span); f2(); + + span->End(); } diff --git a/examples/simple/main.cc b/examples/simple/main.cc index 572c3be1a7..485d31c2ec 100644 --- a/examples/simple/main.cc +++ b/examples/simple/main.cc @@ -1,4 +1,3 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/tracer_provider.h" #include "opentelemetry/trace/provider.h" diff --git a/examples/zpages/zpages_example.cc b/examples/zpages/zpages_example.cc index f89f8f95d9..72ca3fda75 100644 --- a/examples/zpages/zpages_example.cc +++ b/examples/zpages/zpages_example.cc @@ -5,7 +5,6 @@ #include #include #include -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/ext/zpages/zpages.h" // Required file include for zpages diff --git a/exporters/CMakeLists.txt b/exporters/CMakeLists.txt index 227c0f7f1f..fdb9776473 100644 --- a/exporters/CMakeLists.txt +++ b/exporters/CMakeLists.txt @@ -3,6 +3,7 @@ if(WITH_OTPROTOCOL) endif() add_subdirectory(ostream) +add_subdirectory(memory) if(WITH_PROMETHEUS) add_subdirectory(prometheus) diff --git a/exporters/memory/BUILD b/exporters/memory/BUILD new file mode 100644 index 0000000000..a3ab2d1532 --- /dev/null +++ b/exporters/memory/BUILD @@ -0,0 +1,41 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "in_memory_span_data", + hdrs = [ + "include/opentelemetry/exporters/memory/in_memory_span_data.h", + ], + strip_include_prefix = "include", + deps = [ + "//api", + "//sdk/src/trace", + ], +) + +cc_test( + name = "in_memory_span_data_test", + deps = [ + ":in_memory_span_data", + "@com_google_googletest//:gtest_main", + ], +) + +cc_library( + name = "in_memory_span_exporter", + hdrs = [ + "include/opentelemetry/exporters/memory/in_memory_span_exporter.h", + ], + strip_include_prefix = "include", + deps = [ + ":in_memory_span_data", + "//sdk/src/trace", + ], +) + +cc_test( + name = "in_memory_span_exporter_test", + deps = [ + ":in_memory_span_exporter", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/exporters/memory/CMakeLists.txt b/exporters/memory/CMakeLists.txt new file mode 100644 index 0000000000..2739596fd5 --- /dev/null +++ b/exporters/memory/CMakeLists.txt @@ -0,0 +1,28 @@ +include_directories(include) + +add_library(opentelemetry_exporter_in_memory INTERFACE) + +target_include_directories(opentelemetry_exporter_in_memory INTERFACE include/) + +if(BUILD_TESTING) + add_executable(in_memory_span_data_test test/in_memory_span_data_test.cc) + add_executable(in_memory_span_exporter_test + test/in_memory_span_exporter_test.cc) + + target_link_libraries( + in_memory_span_data_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_exporter_in_memory) + + target_link_libraries( + in_memory_span_exporter_test ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_exporter_in_memory) + + gtest_add_tests( + TARGET in_memory_span_data_test + TEST_PREFIX exporter. + TEST_LIST in_memory_span_data_test) + gtest_add_tests( + TARGET in_memory_span_exporter_test + TEST_PREFIX exporter. + TEST_LIST in_memory_span_exporter_test) +endif() diff --git a/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h new file mode 100644 index 0000000000..4e7e7c4f71 --- /dev/null +++ b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h @@ -0,0 +1,68 @@ +#pragma once + +#include "opentelemetry/sdk/common/circular_buffer.h" +#include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/sdk/trace/span_data.h" +#include "vector" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace memory +{ +/** + * A wrapper class holding in memory exporter data + */ +class InMemorySpanData final +{ +public: + /** + * @param buffer_size a required value that sets the size of the CircularBuffer + */ + InMemorySpanData(size_t buffer_size) : spans_received_(buffer_size) {} + + /** + * @param data a required unique pointer to the data to add to the CircularBuffer + */ + void Add(std::unique_ptr data) noexcept + { + std::unique_ptr span_data( + static_cast(data.release())); + spans_received_.Add(span_data); + } + + /** + * @return Returns a vector of unique pointers containing all the span data in the + * CircularBuffer. This operation will empty the Buffer, which is why the data + * is returned as unique pointers + */ + std::vector> GetSpans() noexcept + { + std::vector> res; + + // Pointer swap is required because the Consume function requires that the + // AtomicUniquePointer be set to null + spans_received_.Consume( + spans_received_.size(), [&](opentelemetry::sdk::common::CircularBufferRange< + opentelemetry::sdk::common::AtomicUniquePtr< + opentelemetry::sdk::trace::SpanData>> range) noexcept { + range.ForEach([&]( + opentelemetry::sdk::common::AtomicUniquePtr & + ptr) noexcept { + std::unique_ptr swap_ptr = + std::unique_ptr(nullptr); + ptr.Swap(swap_ptr); + res.push_back(std::unique_ptr(swap_ptr.release())); + return true; + }); + }); + + return res; + } + +private: + opentelemetry::sdk::common::CircularBuffer spans_received_; +}; +} // namespace memory +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h new file mode 100644 index 0000000000..81e90c2c0d --- /dev/null +++ b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h @@ -0,0 +1,75 @@ +#pragma once +#include "opentelemetry/exporters/memory/in_memory_span_data.h" +#include "opentelemetry/sdk/trace/exporter.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace memory +{ +const size_t MAX_BUFFER_SIZE = 100; + +/** + * A in memory exporter that switches a flag once a valid recordable was received + * and keeps track of all received spans in memory. + */ +class InMemorySpanExporter final : public opentelemetry::sdk::trace::SpanExporter +{ +public: + /** + * @param buffer_size an optional value that sets the size of the InMemorySpanData + */ + InMemorySpanExporter(size_t buffer_size = MAX_BUFFER_SIZE) + : data_(new opentelemetry::exporter::memory::InMemorySpanData(buffer_size)) + {} + + /** + * @return Returns a unique pointer to an empty recordable object + */ + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new sdk::trace::SpanData()); + } + + /** + * @param recordables a required span containing unique pointers to the data + * to add to the InMemorySpanData + * @return Returns the result of the operation + */ + sdk::trace::ExportResult Export( + const nostd::span> &recordables) noexcept override + { + for (auto &recordable : recordables) + { + auto span = std::unique_ptr( + dynamic_cast(recordable.release())); + if (span != nullptr) + { + data_->Add(std::move(span)); + } + } + + return sdk::trace::ExportResult::kSuccess; + } + + /** + * @param timeout an optional value containing the timeout of the exporter + * note: passing custom timeout values is not currently supported for this exporter + */ + void Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override{}; + + /** + * @return Returns a shared pointer to this exporters InMemorySpanData + */ + std::shared_ptr GetData() noexcept + { + return data_; + } + +private: + std::shared_ptr data_; +}; +} // namespace memory +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/memory/test/in_memory_span_data_test.cc b/exporters/memory/test/in_memory_span_data_test.cc new file mode 100644 index 0000000000..2b68d18515 --- /dev/null +++ b/exporters/memory/test/in_memory_span_data_test.cc @@ -0,0 +1,25 @@ +#include "opentelemetry/exporters/memory/in_memory_span_data.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/trace/span_data.h" + +#include + +using opentelemetry::exporter::memory::InMemorySpanData; +using opentelemetry::sdk::trace::Recordable; +using opentelemetry::sdk::trace::SpanData; + +TEST(InMemorySpanData, AddRecordable) +{ + InMemorySpanData data(100); + + ASSERT_EQ(0, data.GetSpans().size()); + + std::unique_ptr spandata(new SpanData()); + + data.Add(std::move(spandata)); + + // Consumes all spans in exporter + ASSERT_EQ(1, data.GetSpans().size()); + + ASSERT_EQ(0, data.GetSpans().size()); +} diff --git a/exporters/memory/test/in_memory_span_exporter_test.cc b/exporters/memory/test/in_memory_span_exporter_test.cc new file mode 100644 index 0000000000..ba94be27dc --- /dev/null +++ b/exporters/memory/test/in_memory_span_exporter_test.cc @@ -0,0 +1,26 @@ +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/trace/span_data.h" + +#include + +using opentelemetry::exporter::memory::InMemorySpanExporter; +using opentelemetry::sdk::trace::Recordable; +using opentelemetry::sdk::trace::SpanData; + +TEST(InMemorySpanExporter, ExportBatch) +{ + InMemorySpanExporter exporter; + + ASSERT_EQ(0, exporter.GetData().get()->GetSpans().size()); + + std::unique_ptr spandata(new SpanData()); + opentelemetry::nostd::span> batch(&spandata, 1); + + exporter.Export(batch); + + ASSERT_EQ(1, exporter.GetData().get()->GetSpans().size()); + + // Consumes all spans in exporter + ASSERT_EQ(0, exporter.GetData().get()->GetSpans().size()); +} diff --git a/exporters/ostream/CMakeLists.txt b/exporters/ostream/CMakeLists.txt index 4818dfd5e2..c307de7244 100644 --- a/exporters/ostream/CMakeLists.txt +++ b/exporters/ostream/CMakeLists.txt @@ -9,14 +9,18 @@ if(BUILD_TESTING) target_link_libraries( ostream_span_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_exporter_ostream_span) + ${CORE_RUNTIME_LIBS} opentelemetry_exporter_ostream_span) target_link_libraries( ostream_metrics_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_exporter_ostream_metrics) + ${CORE_RUNTIME_LIBS} opentelemetry_exporter_ostream_metrics) - gtest_add_tests(TARGET ostream_metrics_test TEST_PREFIX exporter. TEST_LIST - ostream_metrics_test) - gtest_add_tests(TARGET ostream_span_test TEST_PREFIX exporter. TEST_LIST - ostream_span_test) + gtest_add_tests( + TARGET ostream_metrics_test + TEST_PREFIX exporter. + TEST_LIST ostream_metrics_test) + gtest_add_tests( + TARGET ostream_span_test + TEST_PREFIX exporter. + TEST_LIST ostream_span_test) endif() # BUILD_TESTING diff --git a/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h b/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h index 312aecb6ea..3bb228b847 100644 --- a/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h +++ b/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "opentelemetry/sdk/metrics/aggregator/exact_aggregator.h" #include "opentelemetry/sdk/metrics/aggregator/gauge_aggregator.h" #include "opentelemetry/sdk/metrics/aggregator/histogram_aggregator.h" @@ -82,9 +83,9 @@ class OStreamMetricsExporter final : public sdkmetrics::MetricsExporter } else { - auto vec = agg->get_checkpoint(); - int size = vec.size(); - int i = 1; + auto vec = agg->get_checkpoint(); + size_t size = vec.size(); + size_t i = 1; sout_ << "\n values : " << '['; @@ -104,12 +105,12 @@ class OStreamMetricsExporter final : public sdkmetrics::MetricsExporter auto boundaries = agg->get_boundaries(); auto counts = agg->get_counts(); - int boundaries_size = boundaries.size(); - int counts_size = counts.size(); + size_t boundaries_size = boundaries.size(); + size_t counts_size = counts.size(); sout_ << "\n buckets : " << '['; - for (int i = 0; i < boundaries_size; i++) + for (size_t i = 0; i < boundaries_size; i++) { sout_ << boundaries[i]; @@ -119,7 +120,7 @@ class OStreamMetricsExporter final : public sdkmetrics::MetricsExporter sout_ << ']'; sout_ << "\n counts : " << '['; - for (int i = 0; i < counts_size; i++) + for (size_t i = 0; i < counts_size; i++) { sout_ << counts[i]; @@ -134,12 +135,12 @@ class OStreamMetricsExporter final : public sdkmetrics::MetricsExporter auto boundaries = agg->get_boundaries(); auto counts = agg->get_counts(); - int boundaries_size = boundaries.size(); - int counts_size = counts.size(); + size_t boundaries_size = boundaries.size(); + size_t counts_size = counts.size(); sout_ << "\n buckets : " << '['; - for (int i = 0; i < boundaries_size; i++) + for (size_t i = 0; i < boundaries_size; i++) { sout_ << boundaries[i]; @@ -149,7 +150,7 @@ class OStreamMetricsExporter final : public sdkmetrics::MetricsExporter sout_ << ']'; sout_ << "\n counts : " << '['; - for (int i = 0; i < counts_size; i++) + for (size_t i = 0; i < counts_size; i++) { sout_ << counts[i]; diff --git a/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h b/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h index e7dd1ed42a..2b8add09ae 100644 --- a/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h +++ b/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h @@ -62,19 +62,24 @@ class OStreamSpanExporter final : public sdktrace::SpanExporter {16, "UNAUTHENTICATED"}}; /* - print_array and print_value are used to print out the value of an attribute within a vector. + print_value is used to print out the value of an attribute within a vector. These values are held in a variant which makes the process of printing them much more complicated. */ template - void print_array(sdktrace::SpanDataAttributeValue &value) + void print_value(const T &item) + { + sout_ << item; + } + + template + void print_value(const std::vector &vec) { sout_ << '['; - auto s = nostd::get>(value); size_t i = 1; - size_t sz = s.size(); - for (auto v : s) + size_t sz = vec.size(); + for (auto v : vec) { sout_ << v; if (i != sz) @@ -84,70 +89,30 @@ class OStreamSpanExporter final : public sdktrace::SpanExporter sout_ << ']'; } - void print_value(sdktrace::SpanDataAttributeValue &value) + class SpanDataAttributeValueVisitor { - if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative(value)) - { - sout_ << nostd::get(value); - } - else if (nostd::holds_alternative>(value)) - { - print_array(value); - } - else if (nostd::holds_alternative>(value)) - { - print_array(value); - } - else if (nostd::holds_alternative>(value)) - { - print_array(value); - } - else if (nostd::holds_alternative>(value)) - { - print_array(value); - } - else if (nostd::holds_alternative>(value)) - { - print_array(value); - } - else if (nostd::holds_alternative>(value)) - { - print_array(value); - } - else if (nostd::holds_alternative>(value)) + public: + SpanDataAttributeValueVisitor(OStreamSpanExporter &exporter) : exporter_(exporter) {} + + template + void operator()(T &&arg) { - print_array(value); + exporter_.print_value(arg); } + + private: + OStreamSpanExporter &exporter_; + }; + + void print_value(sdktrace::SpanDataAttributeValue &value) + { + nostd::visit(SpanDataAttributeValueVisitor(*this), value); } void printAttributes(std::unordered_map map) { - int size = map.size(); - int i = 1; + size_t size = map.size(); + size_t i = 1; for (auto kv : map) { sout_ << kv.first << ": "; diff --git a/exporters/ostream/test/ostream_span_test.cc b/exporters/ostream/test/ostream_span_test.cc index 5dfecde265..e1f0c6b937 100644 --- a/exporters/ostream/test/ostream_span_test.cc +++ b/exporters/ostream/test/ostream_span_test.cc @@ -1,4 +1,3 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/sdk/trace/recordable.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" diff --git a/exporters/otlp/CMakeLists.txt b/exporters/otlp/CMakeLists.txt index 27ee7de956..d1bc535763 100644 --- a/exporters/otlp/CMakeLists.txt +++ b/exporters/otlp/CMakeLists.txt @@ -9,6 +9,8 @@ if(BUILD_TESTING) target_link_libraries( recordable_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_exporter_otprotocol protobuf::libprotobuf) - gtest_add_tests(TARGET recordable_test TEST_PREFIX exporter. TEST_LIST - recordable_test) + gtest_add_tests( + TARGET recordable_test + TEST_PREFIX exporter. + TEST_LIST recordable_test) endif() # BUILD_TESTING diff --git a/exporters/otlp/README.md b/exporters/otlp/README.md index debbbd5db9..05915d7b27 100644 --- a/exporters/otlp/README.md +++ b/exporters/otlp/README.md @@ -8,7 +8,7 @@ For a full list of backends supported by the Collector, see the [main Collector ## Configuration -The OTLP exporter offers some configuration options. To configure the exporter, create an `OtlpExporterOptions` struct (defined in [exporter.h](include/opentelemetry/exporters/otlp/otlp_exporter.h)), set the options inside, and pass the struct to the `OtlpExporter` constructor, like so: +The OTLP exporter offers some configuration options. To configure the exporter, create an `OtlpExporterOptions` struct (defined in [exporter.h](https://github.com/open-telemetry/opentelemetry-cpp/blob/master/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_exporter.h)), set the options inside, and pass the struct to the `OtlpExporter` constructor, like so: ``` OtlpExporterOptions options; @@ -24,4 +24,4 @@ auto exporter = std::unique_ptr(new otlp::OtlpExporter(o ## Example -For a complete example demonstrating how to use the OTLP exporter, see [examples/otlp](../../examples/otlp). +For a complete example demonstrating how to use the OTLP exporter, see [examples/otlp](https://github.com/open-telemetry/opentelemetry-cpp/blob/master/examples/otlp/). diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/recordable.h b/exporters/otlp/include/opentelemetry/exporters/otlp/recordable.h index 5dc4de02f0..3e1c14c208 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/recordable.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/recordable.h @@ -23,10 +23,10 @@ class Recordable final : public sdk::trace::Recordable void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace::KeyValueIterable &attributes) noexcept override; + const common::KeyValueIterable &attributes) noexcept override; void AddLink(opentelemetry::trace::SpanContext span_context, - const trace::KeyValueIterable &attributes) noexcept override; + const common::KeyValueIterable &attributes) noexcept override; void SetStatus(trace::CanonicalCode code, nostd::string_view description) noexcept override; diff --git a/exporters/otlp/src/recordable.cc b/exporters/otlp/src/recordable.cc index 4d19e3ecd1..a48f6a95f8 100644 --- a/exporters/otlp/src/recordable.cc +++ b/exporters/otlp/src/recordable.cc @@ -120,7 +120,7 @@ void Recordable::SetAttribute(nostd::string_view key, void Recordable::AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace::KeyValueIterable &attributes) noexcept + const common::KeyValueIterable &attributes) noexcept { auto *event = span_.add_events(); event->set_name(name.data(), name.size()); @@ -133,7 +133,7 @@ void Recordable::AddEvent(nostd::string_view name, } void Recordable::AddLink(opentelemetry::trace::SpanContext span_context, - const trace::KeyValueIterable &attributes) noexcept + const common::KeyValueIterable &attributes) noexcept { auto *link = span_.add_links(); attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { diff --git a/exporters/otlp/test/otlp_exporter_benchmark.cc b/exporters/otlp/test/otlp_exporter_benchmark.cc index 30f9046e34..0accff0bf4 100644 --- a/exporters/otlp/test/otlp_exporter_benchmark.cc +++ b/exporters/otlp/test/otlp_exporter_benchmark.cc @@ -1,4 +1,3 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/exporters/otlp/otlp_exporter.h" #include "opentelemetry/exporters/otlp/recordable.h" diff --git a/exporters/otlp/test/otlp_exporter_test.cc b/exporters/otlp/test/otlp_exporter_test.cc index a79046f1d3..a221365179 100644 --- a/exporters/otlp/test/otlp_exporter_test.cc +++ b/exporters/otlp/test/otlp_exporter_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/exporters/otlp/otlp_exporter.h" -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/proto/collector/trace/v1/trace_service_mock.grpc.pb.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/tracer_provider.h" diff --git a/exporters/otlp/test/recordable_test.cc b/exporters/otlp/test/recordable_test.cc index a349c1f50b..665fcc0003 100644 --- a/exporters/otlp/test/recordable_test.cc +++ b/exporters/otlp/test/recordable_test.cc @@ -1,6 +1,5 @@ #include "opentelemetry/exporters/otlp/recordable.h" #include -#include "opentelemetry/context/threadlocal_context.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -106,7 +105,7 @@ TEST(Recordable, AddEventWithAttributes) {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; rec.AddEvent("Test Event", std::chrono::system_clock::now(), - trace::KeyValueIterableView>(attributes)); + common::KeyValueIterableView>(attributes)); for (int i = 0; i < kNumAttributes; i++) { @@ -125,7 +124,7 @@ TEST(Recordable, AddLink) {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; rec.AddLink(trace::SpanContext(false, false), - trace::KeyValueIterableView>(attributes)); + common::KeyValueIterableView>(attributes)); for (int i = 0; i < kNumAttributes; i++) { diff --git a/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h b/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h index 71aad4d59b..93184edc02 100644 --- a/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h +++ b/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h @@ -45,7 +45,7 @@ class PrometheusCollector : public prometheus_client::Collectable * This constructor initializes the collection for metrics to export * in this class with default capacity */ - explicit PrometheusCollector(int max_collection_size = 2048); + explicit PrometheusCollector(size_t max_collection_size = 2048); /** * Collects all metrics data from metricsToCollect collection. @@ -88,7 +88,7 @@ class PrometheusCollector : public prometheus_client::Collectable /** * Maximum size of the metricsToCollect collection. */ - int max_collection_size_; + size_t max_collection_size_; /* * Lock when operating the metricsToCollect collection diff --git a/exporters/prometheus/src/prometheus_collector.cc b/exporters/prometheus/src/prometheus_collector.cc index cc7256c925..b0c8330135 100644 --- a/exporters/prometheus/src/prometheus_collector.cc +++ b/exporters/prometheus/src/prometheus_collector.cc @@ -29,7 +29,7 @@ namespace prometheus * This constructor initializes the collection for metrics to export * in this class with default capacity */ -PrometheusCollector::PrometheusCollector(int max_collection_size) +PrometheusCollector::PrometheusCollector(size_t max_collection_size) : max_collection_size_(max_collection_size) { metrics_to_collect_ = diff --git a/exporters/prometheus/src/prometheus_exporter_utils.cc b/exporters/prometheus/src/prometheus_exporter_utils.cc index de2a233fa8..208514af39 100644 --- a/exporters/prometheus/src/prometheus_exporter_utils.cc +++ b/exporters/prometheus/src/prometheus_exporter_utils.cc @@ -284,7 +284,7 @@ void PrometheusExporterUtils::SetMetricBasic(prometheus_client::ClientMetric &me if (!label_pairs.empty()) { metric.label.resize(label_pairs.size()); - for (int i = 0; i < label_pairs.size(); ++i) + for (size_t i = 0; i < label_pairs.size(); ++i) { auto origin_name = label_pairs[i].first; auto sanitized = SanitizeNames(origin_name); @@ -396,7 +396,7 @@ void PrometheusExporterUtils::SetValue(std::vector values, metric->histogram.sample_count = values[1]; int cumulative = 0; std::vector buckets; - for (int i = 0; i < boundaries.size() + 1; i++) + for (size_t i = 0; i < boundaries.size() + 1; i++) { prometheus_client::ClientMetric::Bucket bucket; cumulative += counts[i]; @@ -444,7 +444,7 @@ void PrometheusExporterUtils::SetValue(std::vector values, if (do_quantile) { std::vector prometheus_quantiles; - for (int i = 0; i < quantiles.size(); i++) + for (size_t i = 0; i < quantiles.size(); i++) { prometheus_client::ClientMetric::Quantile quantile; quantile.quantile = quantile_points[i]; diff --git a/exporters/prometheus/test/CMakeLists.txt b/exporters/prometheus/test/CMakeLists.txt index c57f900347..4168c6d3e4 100644 --- a/exporters/prometheus/test/CMakeLists.txt +++ b/exporters/prometheus/test/CMakeLists.txt @@ -3,6 +3,8 @@ foreach(testname prometheus_collector_test prometheus_exporter_utils_test) target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} prometheus_exporter prometheus-cpp::pull) - gtest_add_tests(TARGET ${testname} TEST_PREFIX exporter. TEST_LIST - ${testname}) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX exporter. + TEST_LIST ${testname}) endforeach() diff --git a/exporters/prometheus/test/prometheus_collector_test.cc b/exporters/prometheus/test/prometheus_collector_test.cc index 444302fa44..64ff9f3dce 100644 --- a/exporters/prometheus/test/prometheus_collector_test.cc +++ b/exporters/prometheus/test/prometheus_collector_test.cc @@ -215,7 +215,7 @@ TEST(PrometheusCollector, AddMetricDataWithCounterRecordsSuccessfully) auto after_agg = nostd::get>>(after_agg_var); ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size()); - for (int i = 0; i < before_agg->get_checkpoint().size(); i++) + for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++) { ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]); } @@ -267,7 +267,7 @@ TEST(PrometheusCollector, AddMetricDataWithMinMaxSumCountRecordsSuccessfully) auto after_agg = nostd::get>>(after_agg_var); ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size()); - for (int i = 0; i < before_agg->get_checkpoint().size(); i++) + for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++) { ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]); } @@ -319,7 +319,7 @@ TEST(PrometheusCollector, AddMetricDataWithGaugeRecordsSuccessfully) auto after_agg = nostd::get>>(after_agg_var); ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size()); - for (int i = 0; i < before_agg->get_checkpoint().size(); i++) + for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++) { ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]); } @@ -372,15 +372,15 @@ TEST(PrometheusCollector, AddMetricDataWithSketchRecordsSuccessfully) auto after_agg = nostd::get>>(after_agg_var); ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size()); - for (int i = 0; i < before_agg->get_checkpoint().size(); i++) + for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++) { ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]); } - for (int i = 0; i < before_agg->get_boundaries().size(); i++) + for (size_t i = 0; i < before_agg->get_boundaries().size(); i++) { ASSERT_EQ(before_agg->get_boundaries()[i], after_agg->get_boundaries()[i]); } - for (int i = 0; i < before_agg->get_counts().size(); i++) + for (size_t i = 0; i < before_agg->get_counts().size(); i++) { ASSERT_EQ(before_agg->get_counts()[i], after_agg->get_counts()[i]); } @@ -433,15 +433,15 @@ TEST(PrometheusCollector, AddMetricDataWithHistogramRecordsSuccessfully) auto after_agg = nostd::get>>(after_agg_var); ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size()); - for (int i = 0; i < before_agg->get_checkpoint().size(); i++) + for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++) { ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]); } - for (int i = 0; i < before_agg->get_boundaries().size(); i++) + for (size_t i = 0; i < before_agg->get_boundaries().size(); i++) { ASSERT_EQ(before_agg->get_boundaries()[i], after_agg->get_boundaries()[i]); } - for (int i = 0; i < before_agg->get_counts().size(); i++) + for (size_t i = 0; i < before_agg->get_counts().size(); i++) { ASSERT_EQ(before_agg->get_counts()[i], after_agg->get_counts()[i]); } @@ -512,7 +512,7 @@ TEST(PrometheusCollector, AddMetricDataWithExactRecordsSuccessfully) else { ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size()); - for (int i = 0; i < before_agg->get_checkpoint().size(); i++) + for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++) { ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]); } diff --git a/exporters/prometheus/test/prometheus_exporter_utils_test.cc b/exporters/prometheus/test/prometheus_exporter_utils_test.cc index ed8f921265..6352faeda5 100644 --- a/exporters/prometheus/test/prometheus_exporter_utils_test.cc +++ b/exporters/prometheus/test/prometheus_exporter_utils_test.cc @@ -87,7 +87,7 @@ void assert_histogram(prometheus_client::MetricFamily &metric, { int cumulative_count = 0; auto buckets = metric.metric[0].histogram.bucket; - for (int i = 0; i < buckets.size(); i++) + for (size_t i = 0; i < buckets.size(); i++) { auto bucket = buckets[i]; if (i != buckets.size() - 1) diff --git a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h index 1c7595aae6..4d937bd8b7 100644 --- a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h +++ b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h @@ -17,7 +17,9 @@ using opentelemetry::sdk::trace::AttributeConverter; using opentelemetry::sdk::trace::SpanDataAttributeValue; using opentelemetry::sdk::trace::SpanDataEvent; -namespace trace_api = opentelemetry::trace; + +// TODO: Create generic short pattern for opentelemetry::common and opentelemetry::trace and others +// as necessary OPENTELEMETRY_BEGIN_NAMESPACE namespace ext @@ -137,7 +139,8 @@ class ThreadsafeSpanData final : public opentelemetry::sdk::trace::Recordable attributes_[std::string(key)] = nostd::visit(converter_, value); } - void SetStatus(trace_api::CanonicalCode code, nostd::string_view description) noexcept override + void SetStatus(opentelemetry::trace::CanonicalCode code, + nostd::string_view description) noexcept override { std::lock_guard lock(mutex_); status_code_ = code; @@ -162,10 +165,10 @@ class ThreadsafeSpanData final : public opentelemetry::sdk::trace::Recordable duration_ = duration; } - void AddLink( - opentelemetry::trace::SpanContext span_context, - const trace_api::KeyValueIterable &attributes = - trace_api::KeyValueIterableView>({})) noexcept override + void AddLink(opentelemetry::trace::SpanContext span_context, + const opentelemetry::common::KeyValueIterable &attributes = + opentelemetry::common::KeyValueIterableView>( + {})) noexcept override { std::lock_guard lock(mutex_); (void)span_context; @@ -175,8 +178,9 @@ class ThreadsafeSpanData final : public opentelemetry::sdk::trace::Recordable void AddEvent( nostd::string_view name, core::SystemTimestamp timestamp = core::SystemTimestamp(std::chrono::system_clock::now()), - const trace_api::KeyValueIterable &attributes = - trace_api::KeyValueIterableView>({})) noexcept override + const opentelemetry::common::KeyValueIterable &attributes = + opentelemetry::common::KeyValueIterableView>( + {})) noexcept override { std::lock_guard lock(mutex_); events_.push_back(SpanDataEvent(std::string(name), timestamp, attributes)); diff --git a/ext/test/zpages/CMakeLists.txt b/ext/test/zpages/CMakeLists.txt index 34c3637316..c7d0d837c8 100644 --- a/ext/test/zpages/CMakeLists.txt +++ b/ext/test/zpages/CMakeLists.txt @@ -4,5 +4,8 @@ foreach(testname tracez_processor_test tracez_data_aggregator_test target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_zpages) - gtest_add_tests(TARGET ${testname} TEST_PREFIX ext. TEST_LIST ${testname}) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX ext. + TEST_LIST ${testname}) endforeach() diff --git a/ext/test/zpages/threadsafe_span_data_test.cc b/ext/test/zpages/threadsafe_span_data_test.cc index 12ad3e5a45..5a4791d527 100644 --- a/ext/test/zpages/threadsafe_span_data_test.cc +++ b/ext/test/zpages/threadsafe_span_data_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/ext/zpages/threadsafe_span_data.h" -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_id.h" diff --git a/ext/test/zpages/tracez_data_aggregator_test.cc b/ext/test/zpages/tracez_data_aggregator_test.cc index 035b2d8f26..78812e22b3 100644 --- a/ext/test/zpages/tracez_data_aggregator_test.cc +++ b/ext/test/zpages/tracez_data_aggregator_test.cc @@ -2,7 +2,6 @@ #include -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/ext/zpages/tracez_processor.h" #include "opentelemetry/sdk/trace/recordable.h" #include "opentelemetry/sdk/trace/tracer.h" @@ -51,8 +50,8 @@ class TracezDataAggregatorTest : public ::testing::Test void VerifySpanCountsInTracezData( const std::string &span_name, const TracezData &aggregated_data, - unsigned int running_span_count, - unsigned int error_span_count, + size_t running_span_count, + size_t error_span_count, std::array completed_span_count_per_latency_bucket) { // Asserts are needed to check the size of the container because they may need @@ -61,14 +60,14 @@ void VerifySpanCountsInTracezData( << " Count of running spans incorrect for " << span_name << "\n"; EXPECT_EQ(aggregated_data.sample_running_spans.size(), - std::min(running_span_count, kMaxNumberOfSampleSpans)) + std::min(running_span_count, kMaxNumberOfSampleSpans)) << " Size of sample running spans incorrect for " << span_name << "\n"; EXPECT_EQ(aggregated_data.error_span_count, error_span_count) << " Count of error spans incorrect for " << span_name << "\n"; EXPECT_EQ(aggregated_data.sample_error_spans.size(), - std::min(error_span_count, kMaxNumberOfSampleSpans)) + std::min(error_span_count, kMaxNumberOfSampleSpans)) << " Count of running spans incorrect for " << span_name << "\n"; for (unsigned int boundary = 0; boundary < kLatencyBoundaries.size(); boundary++) @@ -78,8 +77,8 @@ void VerifySpanCountsInTracezData( << " Count of completed spans in latency boundary " << boundary << " incorrect for " << span_name << "\n"; EXPECT_EQ(aggregated_data.sample_latency_spans[boundary].size(), - std::min(completed_span_count_per_latency_bucket[boundary], - kMaxNumberOfSampleSpans)) + std::min(completed_span_count_per_latency_bucket[boundary], + kMaxNumberOfSampleSpans)) << " Count of sample completed spans in latency boundary " << boundary << " incorrect for " << span_name << "\n"; } diff --git a/ext/test/zpages/tracez_processor_test.cc b/ext/test/zpages/tracez_processor_test.cc index 7b4c633926..e0cd4f96ba 100644 --- a/ext/test/zpages/tracez_processor_test.cc +++ b/ext/test/zpages/tracez_processor_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/ext/zpages/tracez_processor.h" -#include "opentelemetry/context/threadlocal_context.h" #include @@ -49,14 +48,14 @@ void UpdateSpans(std::shared_ptr &processor, */ bool ContainsNames(const std::vector &names, std::unordered_set &running, - unsigned int name_start = 0, - unsigned int name_end = 0, + size_t name_start = 0, + size_t name_end = 0, bool one_to_one_correspondence = false) { if (name_end == 0) name_end = names.size(); - unsigned int num_names = name_end - name_start; + size_t num_names = name_end - name_start; if (num_names > running.size() || // More names than spans, can't have all names (one_to_one_correspondence && num_names != running.size())) @@ -97,15 +96,15 @@ bool ContainsNames(const std::vector &names, */ bool ContainsNames(const std::vector &names, std::vector> &completed, - unsigned int name_start = 0, - unsigned int name_end = 0, + size_t name_start = 0, + size_t name_end = 0, bool one_to_one_correspondence = false) { if (name_end == 0) name_end = names.size(); - unsigned int num_names = name_end - name_start; + size_t num_names = name_end - name_start; if (num_names > completed.size() || (one_to_one_correspondence && num_names != completed.size())) { diff --git a/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h b/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h index 1fdfee618c..d93d9b72b2 100644 --- a/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h +++ b/sdk/include/opentelemetry/sdk/common/atomic_unique_ptr.h @@ -34,7 +34,7 @@ class AtomicUniquePtr /** * @return true if the pointer is null */ - bool IsNull() const noexcept { return ptr_ == nullptr; } + bool IsNull() const noexcept { return ptr_.load() == nullptr; } /** * Atomically swap the pointer only if it's null. diff --git a/sdk/include/opentelemetry/sdk/common/empty_attributes.h b/sdk/include/opentelemetry/sdk/common/empty_attributes.h index b4c69abe7d..743b85e7c4 100644 --- a/sdk/include/opentelemetry/sdk/common/empty_attributes.h +++ b/sdk/include/opentelemetry/sdk/common/empty_attributes.h @@ -1,24 +1,35 @@ -#include "opentelemetry/trace/key_value_iterable_view.h" +#include "opentelemetry/common/key_value_iterable_view.h" +#include #include +#include +#include OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { + +#if 0 /** * Maintain a static empty array of pairs that represents empty (default) attributes. * This helps to avoid constructing a new empty container every time a call is made * with default attributes. */ -static const opentelemetry::trace::KeyValueIterableView, 0>> +static const opentelemetry::common::KeyValueIterableView, 0>> &GetEmptyAttributes() noexcept { static const std::array, 0> array{}; - static const opentelemetry::trace::KeyValueIterableView< + static const opentelemetry::common::KeyValueIterableView< std::array, 0>> kEmptyAttributes(array); return kEmptyAttributes; } +#else +static const opentelemetry::common::NullKeyValueIterable GetEmptyAttributes() noexcept +{ + return opentelemetry::common::NullKeyValueIterable(); +} +#endif } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h new file mode 100644 index 0000000000..73e54f0fe2 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include +#include "opentelemetry/nostd/function_ref.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/version.h" + +/* + Usage Example + + struct SimpleReponseHandler: public ResponseHandler { + void OnResponse(Response& res) noexcept override + { + if (res.IsSuccess()) { + res.GetNextHeader([](nostd::string_view name, std::string value) -> bool { + std::cout << "Header Name:" << name << " Header Value:"<< value ; + return true; + }); + .. process response body + } + } + + void OnError(nostd::string_view err) noexcept override + { + std::cout << " Error:" << err; + } + }; + + SessionManager sessionManager; // implementer can provide singleton implementation for it + auto session = sessionManager.createSession("localhost", 8000); + auto request = session->CreateRequest(); + request->AddHeader(..); + SimpleResponseHandler res_handler; + session->SendRequest(res_handler); + session->FinishSession() // optionally in the end + ...shutdown + sessionManager.FinishAllSessions() +*/ + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace http +{ + +enum class Method +{ + Get, + Post, + Put, + Options, + Head, + Patch, + Delete +}; + +enum class SessionState +{ + Created, // session object is created + Ongoing, // session is ongoing + Finished, // session is finished ( this needs to be the final state ) + Queued, // http request is queued + TimedOut, // Request timedout, no response received + Aborted, // http request aborted due to local error, + Cancelled, // http request cancelled, possibly due to session->CancelSession(); + SendingFailed, // http request sending failed + NetworkError, // network error + SSLHandshakeFailed, // ssl handshake failed + ReadError, // error while reading response + WriteError // error while writing rquest +}; + +using Byte = uint8_t; +using StatusCode = uint16_t; +using Body = std::vector; +using SSLCertificate = std::vector; + +class Request +{ +public: + virtual void SetMethod(Method method) noexcept = 0; + + virtual void SetUri(nostd::string_view uri) noexcept = 0; + + virtual void SetBody(Body &body) noexcept = 0; + + virtual void AddHeader(nostd::string_view name, nostd::string_view value) noexcept = 0; + + virtual void ReplaceHeader(nostd::string_view name, nostd::string_view value) noexcept = 0; + + virtual void SetTimeoutMs(std::chrono::milliseconds timeout_ms) noexcept = 0; + + virtual ~Request() = default; +}; + +class Response +{ +public: + virtual const Body &GetBody() const noexcept = 0; + + virtual bool ForEachHeader( + nostd::function_ref callable) const + noexcept = 0; + + virtual bool ForEachHeader( + const nostd::string_view &key, + nostd::function_ref callable) const + noexcept = 0; + + virtual StatusCode GetStatusCode() const noexcept = 0; + + virtual ~Response() = default; +}; + +class EventHandler +{ +public: + virtual void OnResponse(Response &) noexcept = 0; + + virtual void OnError(SessionState, nostd::string_view) noexcept = 0; + + virtual void OnConnecting(const SSLCertificate &) noexcept {} + + virtual ~EventHandler() = default; +}; + +class Session +{ +public: + virtual std::shared_ptr CreateRequest() noexcept = 0; + + virtual void SendRequest(EventHandler &) noexcept = 0; + + virtual bool IsSessionActive() noexcept = 0; + + virtual bool CancelSession() noexcept = 0; + + virtual bool FinishSession() noexcept = 0; + + virtual ~Session() = default; +}; + +class SessionManager +{ +public: + virtual std::shared_ptr CreateSession(nostd::string_view host, + uint16_t port = 80) noexcept = 0; + + virtual bool CancelAllSessions() noexcept = 0; + + virtual bool FinishAllSessions() noexcept = 0; + + virtual ~SessionManager() = default; +}; + +} // namespace http +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregator/exact_aggregator.h b/sdk/include/opentelemetry/sdk/metrics/aggregator/exact_aggregator.h index f1a5242c48..b34bfd0643 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregator/exact_aggregator.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregator/exact_aggregator.h @@ -145,8 +145,8 @@ class ExactAggregator : public Aggregator } else { - float position = float(this->checkpoint_.size() - 1) * q; - int ceiling = ceil(position); + float position = float(float(this->checkpoint_.size() - 1) * q); + int ceiling = int(ceil(position)); return this->checkpoint_[ceiling]; } } diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregator/histogram_aggregator.h b/sdk/include/opentelemetry/sdk/metrics/aggregator/histogram_aggregator.h index 07b05c247c..785ae2733d 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregator/histogram_aggregator.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregator/histogram_aggregator.h @@ -63,8 +63,8 @@ class HistogramAggregator final : public Aggregator void update(T val) override { this->mu_.lock(); - this->updated_ = true; - int bucketID = boundaries_.size(); + this->updated_ = true; + size_t bucketID = boundaries_.size(); for (size_t i = 0; i < boundaries_.size(); i++) { if (val < boundaries_[i]) // concurrent read is thread-safe diff --git a/sdk/include/opentelemetry/sdk/metrics/aggregator/sketch_aggregator.h b/sdk/include/opentelemetry/sdk/metrics/aggregator/sketch_aggregator.h index bc29868320..9f0545d993 100644 --- a/sdk/include/opentelemetry/sdk/metrics/aggregator/sketch_aggregator.h +++ b/sdk/include/opentelemetry/sdk/metrics/aggregator/sketch_aggregator.h @@ -72,7 +72,7 @@ class SketchAggregator final : public Aggregator } else { - idx = ceil(log(val) / log(gamma)); + idx = static_cast(ceil(log(val) / log(gamma))); } if (raw_.find(idx) != raw_.end()) { @@ -118,7 +118,7 @@ class SketchAggregator final : public Aggregator idx = iter->first; count += iter->second; } - return round(2 * pow(gamma, idx) / (gamma + 1)); + return static_cast(round(2 * pow(gamma, idx) / (gamma + 1))); } /** diff --git a/sdk/include/opentelemetry/sdk/metrics/async_instruments.h b/sdk/include/opentelemetry/sdk/metrics/async_instruments.h index 9eccb86466..69a723bb62 100644 --- a/sdk/include/opentelemetry/sdk/metrics/async_instruments.h +++ b/sdk/include/opentelemetry/sdk/metrics/async_instruments.h @@ -46,7 +46,7 @@ class ValueObserver : public AsynchronousInstrument, virtual public metrics_a * @param value is the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void observe(T value, const trace::KeyValueIterable &labels) override + virtual void observe(T value, const common::KeyValueIterable &labels) override { this->mu_.lock(); std::string labelset = KvToString(labels); @@ -121,7 +121,7 @@ class SumObserver : public AsynchronousInstrument, virtual public metrics_api * @param value is the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void observe(T value, const trace::KeyValueIterable &labels) override + virtual void observe(T value, const common::KeyValueIterable &labels) override { this->mu_.lock(); std::string labelset = KvToString(labels); @@ -219,7 +219,7 @@ class UpDownSumObserver : public AsynchronousInstrument, * @param value is the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void observe(T value, const trace::KeyValueIterable &labels) override + virtual void observe(T value, const common::KeyValueIterable &labels) override { this->mu_.lock(); std::string labelset = KvToString(labels); diff --git a/sdk/include/opentelemetry/sdk/metrics/controller.h b/sdk/include/opentelemetry/sdk/metrics/controller.h index 8a99012670..c36d789e72 100644 --- a/sdk/include/opentelemetry/sdk/metrics/controller.h +++ b/sdk/include/opentelemetry/sdk/metrics/controller.h @@ -14,7 +14,6 @@ #include "opentelemetry/version.h" namespace metrics_api = opentelemetry::metrics; -namespace trace_api = opentelemetry::trace; OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk diff --git a/sdk/include/opentelemetry/sdk/metrics/instrument.h b/sdk/include/opentelemetry/sdk/metrics/instrument.h index 802e201bc6..41257541f3 100644 --- a/sdk/include/opentelemetry/sdk/metrics/instrument.h +++ b/sdk/include/opentelemetry/sdk/metrics/instrument.h @@ -13,7 +13,6 @@ #include "opentelemetry/version.h" namespace metrics_api = opentelemetry::metrics; -namespace trace_api = opentelemetry::trace; OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -174,13 +173,13 @@ class SynchronousInstrument : public Instrument, * @return a Bound Instrument */ virtual nostd::shared_ptr> bind( - const trace::KeyValueIterable &labels) override + const common::KeyValueIterable &labels) override { return nostd::shared_ptr>(); } // This function is necessary for batch recording and should NOT be called by the user - virtual void update(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void update(T value, const common::KeyValueIterable &labels) override = 0; /** * Checkpoints instruments and returns a set of records which are ready for processing. @@ -221,7 +220,7 @@ class AsynchronousInstrument : public Instrument, * @param labels is the numerical representation of the metric being captured * @return none */ - virtual void observe(T value, const trace::KeyValueIterable &labels) override = 0; + virtual void observe(T value, const common::KeyValueIterable &labels) override = 0; virtual std::vector GetRecords() = 0; @@ -236,7 +235,7 @@ class AsynchronousInstrument : public Instrument, virtual void run() override = 0; }; -// Helper functions for turning a trace::KeyValueIterable into a string +// Helper functions for turning a common::KeyValueIterable into a string inline void print_value(std::stringstream &ss, common::AttributeValue &value, bool jsonTypes = false) @@ -271,7 +270,7 @@ inline std::string mapToString(const std::map &conv) return ss.str(); } -inline std::string KvToString(const trace::KeyValueIterable &kv) noexcept +inline std::string KvToString(const common::KeyValueIterable &kv) noexcept { std::stringstream ss; ss << "{"; diff --git a/sdk/include/opentelemetry/sdk/metrics/meter.h b/sdk/include/opentelemetry/sdk/metrics/meter.h index 001866dd99..2f2592a858 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter.h @@ -253,19 +253,19 @@ class Meter : public metrics_api::Meter * @param values a span of pairs where the first element of the pair is a metric instrument * to record to, and the second element is the value to update that instrument with. */ - void RecordShortBatch(const trace::KeyValueIterable &labels, + void RecordShortBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override; - void RecordIntBatch(const trace::KeyValueIterable &labels, + void RecordIntBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override; - void RecordFloatBatch(const trace::KeyValueIterable &labels, + void RecordFloatBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override; - void RecordDoubleBatch(const trace::KeyValueIterable &labels, + void RecordDoubleBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept override; diff --git a/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h b/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h index aab0c4e86f..14d15ccf22 100644 --- a/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h +++ b/sdk/include/opentelemetry/sdk/metrics/sync_instruments.h @@ -90,7 +90,7 @@ class Counter final : public SynchronousInstrument, public metrics_api::Count */ virtual nostd::shared_ptr> bindCounter( - const trace::KeyValueIterable &labels) override + const common::KeyValueIterable &labels) override { this->mu_.lock(); std::string labelset = KvToString(labels); @@ -119,7 +119,7 @@ class Counter final : public SynchronousInstrument, public metrics_api::Count * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - virtual void add(T value, const trace::KeyValueIterable &labels) override + virtual void add(T value, const common::KeyValueIterable &labels) override { if (value < 0) { @@ -163,7 +163,7 @@ class Counter final : public SynchronousInstrument, public metrics_api::Count return ret; } - virtual void update(T val, const trace::KeyValueIterable &labels) override { add(val, labels); } + virtual void update(T val, const common::KeyValueIterable &labels) override { add(val, labels); } // A collection of the bound instruments created by this unbound instrument identified by their // labels. @@ -228,7 +228,7 @@ class UpDownCounter final : public SynchronousInstrument, public metrics_api: * @return a BoundIntCounter tied to the specified labels */ nostd::shared_ptr> bindUpDownCounter( - const trace::KeyValueIterable &labels) override + const common::KeyValueIterable &labels) override { this->mu_.lock(); std::string labelset = KvToString(labels); @@ -257,7 +257,7 @@ class UpDownCounter final : public SynchronousInstrument, public metrics_api: * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - void add(T value, const trace::KeyValueIterable &labels) override + void add(T value, const common::KeyValueIterable &labels) override { auto sp = bindUpDownCounter(labels); sp->update(value); @@ -290,7 +290,7 @@ class UpDownCounter final : public SynchronousInstrument, public metrics_api: return ret; } - virtual void update(T val, const trace::KeyValueIterable &labels) override { add(val, labels); } + virtual void update(T val, const common::KeyValueIterable &labels) override { add(val, labels); } std::unordered_map>> boundInstruments_; @@ -354,7 +354,7 @@ class ValueRecorder final : public SynchronousInstrument, public metrics_api: * @return a BoundIntCounter tied to the specified labels */ nostd::shared_ptr> bindValueRecorder( - const trace::KeyValueIterable &labels) override + const common::KeyValueIterable &labels) override { this->mu_.lock(); std::string labelset = KvToString(labels); @@ -383,7 +383,7 @@ class ValueRecorder final : public SynchronousInstrument, public metrics_api: * @param value the numerical representation of the metric being captured * @param labels the set of labels, as key-value pairs */ - void record(T value, const trace::KeyValueIterable &labels) override + void record(T value, const common::KeyValueIterable &labels) override { auto sp = bindValueRecorder(labels); sp->update(value); @@ -416,7 +416,7 @@ class ValueRecorder final : public SynchronousInstrument, public metrics_api: return ret; } - virtual void update(T value, const trace::KeyValueIterable &labels) override + virtual void update(T value, const common::KeyValueIterable &labels) override { record(value, labels); } diff --git a/sdk/include/opentelemetry/sdk/trace/attribute_utils.h b/sdk/include/opentelemetry/sdk/trace/attribute_utils.h index b836afbaee..0fdb40a413 100644 --- a/sdk/include/opentelemetry/sdk/trace/attribute_utils.h +++ b/sdk/include/opentelemetry/sdk/trace/attribute_utils.h @@ -1,9 +1,10 @@ #pragma once +#include #include #include #include "opentelemetry/common/attribute_value.h" -#include "opentelemetry/trace/key_value_iterable_view.h" +#include "opentelemetry/common/key_value_iterable_view.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -21,6 +22,9 @@ using SpanDataAttributeValue = nostd::variant, +#endif std::vector, std::vector, std::vector, @@ -37,17 +41,23 @@ struct AttributeConverter SpanDataAttributeValue operator()(bool v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(int32_t v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(uint32_t v) { return SpanDataAttributeValue(v); } - /*SpanDataAttributeValue operator()(int v) - { - return SpanDataAttributeValue(static_cast(v)); - }*/ SpanDataAttributeValue operator()(int64_t v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(uint64_t v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(double v) { return SpanDataAttributeValue(v); } SpanDataAttributeValue operator()(nostd::string_view v) { - return SpanDataAttributeValue(std::string(v)); + return SpanDataAttributeValue(std::string(v.data(), v.size())); + } + SpanDataAttributeValue operator()(const char *s) + { + return SpanDataAttributeValue(std::string(s)); } +#ifdef HAVE_SPAN_BYTE + SpanDataAttributeValue operator()(nostd::span v) + { + return convertSpan(v); + } +#endif SpanDataAttributeValue operator()(nostd::span v) { return convertSpan(v); } SpanDataAttributeValue operator()(nostd::span v) { @@ -74,12 +84,7 @@ struct AttributeConverter template SpanDataAttributeValue convertSpan(nostd::span vals) { - std::vector copy; - for (auto &val : vals) - { - copy.push_back(T(val)); - } - + const std::vector copy(vals.begin(), vals.end()); return SpanDataAttributeValue(std::move(copy)); } }; @@ -94,7 +99,7 @@ class AttributeMap AttributeMap(){}; // Contruct attribute map and populate with attributes - AttributeMap(const opentelemetry::trace::KeyValueIterable &attributes) + AttributeMap(const opentelemetry::common::KeyValueIterable &attributes) { attributes.ForEachKeyValue([&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept { diff --git a/sdk/include/opentelemetry/sdk/trace/exporter.h b/sdk/include/opentelemetry/sdk/trace/exporter.h index ec5473edb8..d7eb6f532a 100644 --- a/sdk/include/opentelemetry/sdk/trace/exporter.h +++ b/sdk/include/opentelemetry/sdk/trace/exporter.h @@ -39,6 +39,8 @@ class SpanExporter * custom recordables or use the default SpanData recordable provided by the * SDK. * @return a newly initialized Recordable object + * + * Note: This method must be callable from multiple threads. */ virtual std::unique_ptr MakeRecordable() noexcept = 0; diff --git a/sdk/include/opentelemetry/sdk/trace/processor.h b/sdk/include/opentelemetry/sdk/trace/processor.h index 04f9f8ab9d..ebd37f6218 100644 --- a/sdk/include/opentelemetry/sdk/trace/processor.h +++ b/sdk/include/opentelemetry/sdk/trace/processor.h @@ -24,6 +24,8 @@ class SpanProcessor * Create a span recordable. This requests a new span recordable from the * associated exporter. * @return a newly initialized recordable + * + * Note: This method must be callable from multiple threads. */ virtual std::unique_ptr MakeRecordable() noexcept = 0; diff --git a/sdk/include/opentelemetry/sdk/trace/recordable.h b/sdk/include/opentelemetry/sdk/trace/recordable.h index 68124db0e6..95c89c2ce5 100644 --- a/sdk/include/opentelemetry/sdk/trace/recordable.h +++ b/sdk/include/opentelemetry/sdk/trace/recordable.h @@ -1,11 +1,11 @@ #pragma once #include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/core/timestamp.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/empty_attributes.h" #include "opentelemetry/trace/canonical_code.h" -#include "opentelemetry/trace/key_value_iterable.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_id.h" @@ -13,10 +13,11 @@ #include +// TODO: Create generic short pattern for opentelemetry::common and opentelemetry::trace + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { -namespace trace_api = opentelemetry::trace; namespace trace { /** @@ -55,7 +56,7 @@ class Recordable */ virtual void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace_api::KeyValueIterable &attributes) noexcept = 0; + const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; /** * Add an event to a span with default timestamp and attributes. @@ -83,7 +84,7 @@ class Recordable * @param attributes the attributes associated with the link */ virtual void AddLink(opentelemetry::trace::SpanContext span_context, - const trace_api::KeyValueIterable &attributes) noexcept = 0; + const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; /** * Add a link to a span with default (empty) attributes. @@ -99,7 +100,7 @@ class Recordable * @param code the status code * @param description a description of the status */ - virtual void SetStatus(trace_api::CanonicalCode code, + virtual void SetStatus(opentelemetry::trace::CanonicalCode code, nostd::string_view description) noexcept = 0; /** diff --git a/sdk/include/opentelemetry/sdk/trace/sampler.h b/sdk/include/opentelemetry/sdk/trace/sampler.h index 2308ae7080..dc084dca22 100644 --- a/sdk/include/opentelemetry/sdk/trace/sampler.h +++ b/sdk/include/opentelemetry/sdk/trace/sampler.h @@ -24,9 +24,9 @@ enum class Decision { // IsRecording() == false, span will not be recorded and all events and attributes will be // dropped. - NOT_RECORD, + DROP, // IsRecording() == true, but Sampled flag MUST NOT be set. - RECORD, + RECORD_ONLY, // IsRecording() == true AND Sampled flag` MUST be set. RECORD_AND_SAMPLE }; @@ -53,10 +53,10 @@ class Sampler /** * Called during Span creation to make a sampling decision. * - * @param parent_context a const pointer of the SpanContext of a parent Span. - * null if this is a root span. + * @param parent_context a const reference to the SpanContext of a parent Span. + * An invalid SpanContext if this is a root span. * @param trace_id the TraceId for the new Span. This will be identical to that in - * the parentContext, unless this is a root span. + * the parentContext, unless this is a root span. * @param name the name of the new Span. * @param spanKind the trace_api::SpanKind of the Span. * @param attributes list of AttributeValue with their keys. @@ -65,11 +65,12 @@ class Sampler * @since 0.1.0 */ - virtual SamplingResult ShouldSample(const trace_api::SpanContext *parent_context, - trace_api::TraceId trace_id, - nostd::string_view name, - trace_api::SpanKind span_kind, - const trace_api::KeyValueIterable &attributes) noexcept = 0; + virtual SamplingResult ShouldSample( + const trace_api::SpanContext &parent_context, + trace_api::TraceId trace_id, + nostd::string_view name, + trace_api::SpanKind span_kind, + const opentelemetry::common::KeyValueIterable &attributes) noexcept = 0; /** * Returns the sampler name or short description with the configuration. diff --git a/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h b/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h index 894b8ed598..7fd862be89 100644 --- a/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h +++ b/sdk/include/opentelemetry/sdk/trace/samplers/always_off.h @@ -10,22 +10,23 @@ namespace trace namespace trace_api = opentelemetry::trace; /** - * The always off sampler always returns NOT_RECORD, effectively disabling + * The always off sampler always returns DROP, effectively disabling * tracing functionality. */ class AlwaysOffSampler : public Sampler { public: /** - * @return Returns NOT_RECORD always + * @return Returns DROP always */ - SamplingResult ShouldSample(const trace_api::SpanContext * /*parent_context*/, - trace_api::TraceId /*trace_id*/, - nostd::string_view /*name*/, - trace_api::SpanKind /*span_kind*/, - const trace_api::KeyValueIterable & /*attributes*/) noexcept override + SamplingResult ShouldSample( + const trace_api::SpanContext & /*parent_context*/, + trace_api::TraceId /*trace_id*/, + nostd::string_view /*name*/, + trace_api::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/) noexcept override { - return {Decision::NOT_RECORD, nullptr}; + return {Decision::DROP, nullptr}; } /** diff --git a/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h b/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h index 0137a6b181..0a75eb1e00 100644 --- a/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h +++ b/sdk/include/opentelemetry/sdk/trace/samplers/always_on.h @@ -19,11 +19,11 @@ class AlwaysOnSampler : public Sampler * @return Always return Decision RECORD_AND_SAMPLE */ inline SamplingResult ShouldSample( - const trace_api::SpanContext * /*parent_context*/, + const trace_api::SpanContext & /*parent_context*/, trace_api::TraceId /*trace_id*/, nostd::string_view /*name*/, trace_api::SpanKind /*span_kind*/, - const trace_api::KeyValueIterable & /*attributes*/) noexcept override + const opentelemetry::common::KeyValueIterable & /*attributes*/) noexcept override { return {Decision::RECORD_AND_SAMPLE, nullptr}; } diff --git a/sdk/include/opentelemetry/sdk/trace/samplers/parent_or_else.h b/sdk/include/opentelemetry/sdk/trace/samplers/parent.h similarity index 50% rename from sdk/include/opentelemetry/sdk/trace/samplers/parent_or_else.h rename to sdk/include/opentelemetry/sdk/trace/samplers/parent.h index ce16f7e9c6..91337bd497 100644 --- a/sdk/include/opentelemetry/sdk/trace/samplers/parent_or_else.h +++ b/sdk/include/opentelemetry/sdk/trace/samplers/parent.h @@ -11,25 +11,26 @@ namespace trace namespace trace_api = opentelemetry::trace; /** - * The parent or else sampler is a composite sampler. ParentOrElse(delegateSampler) either respects + * The ParentBased sampler is a composite sampler. ParentBased(delegateSampler) either respects * the parent span's sampling decision or delegates to delegateSampler for root spans. */ -class ParentOrElseSampler : public Sampler +class ParentBasedSampler : public Sampler { public: - explicit ParentOrElseSampler(std::shared_ptr delegate_sampler) noexcept; + explicit ParentBasedSampler(std::shared_ptr delegate_sampler) noexcept; /** The decision either respects the parent span's sampling decision or delegates to * delegateSampler for root spans - * @return Returns NOT_RECORD always + * @return Returns DROP always */ - SamplingResult ShouldSample(const trace_api::SpanContext *parent_context, - trace_api::TraceId trace_id, - nostd::string_view name, - trace_api::SpanKind span_kind, - const trace_api::KeyValueIterable &attributes) noexcept override; + SamplingResult ShouldSample( + const trace_api::SpanContext &parent_context, + trace_api::TraceId trace_id, + nostd::string_view name, + trace_api::SpanKind span_kind, + const opentelemetry::common::KeyValueIterable &attributes) noexcept override; /** - * @return Description MUST be ParentOrElse{delegate_sampler_.getDescription()} + * @return Description MUST be ParentBased{delegate_sampler_.getDescription()} */ nostd::string_view GetDescription() const noexcept override; diff --git a/sdk/include/opentelemetry/sdk/trace/samplers/probability.h b/sdk/include/opentelemetry/sdk/trace/samplers/probability.h deleted file mode 100644 index 9ad8e7f0ea..0000000000 --- a/sdk/include/opentelemetry/sdk/trace/samplers/probability.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "opentelemetry/sdk/trace/sampler.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace trace -{ -namespace trace_api = opentelemetry::trace; -/** - * The probability sampler, based on it's configuration, should either defer the - * decision to sample to it's parent, or compute and return a decision based on - * the provided trace_id and probability. - */ -class ProbabilitySampler : public Sampler -{ -public: - /** - * @param probability a required value, 1.0 >= probability >= 0.0, that given any - * random trace_id, ShouldSample will return RECORD_AND_SAMPLE - * @throws invalid_argument if probability is out of bounds [0.0, 1.0] - */ - explicit ProbabilitySampler(double probability); - - /** - * @return Returns either RECORD_AND_SAMPLE or NOT_RECORD based on current - * sampler configuration and provided parent_context / tracer_id. tracer_id - * is used as a pseudorandom value in conjunction with the predefined - * threshold to determine whether this trace should be sampled - */ - SamplingResult ShouldSample(const trace_api::SpanContext *parent_context, - trace_api::TraceId trace_id, - nostd::string_view /*name*/, - trace_api::SpanKind /*span_kind*/, - const trace_api::KeyValueIterable & /*attributes*/) noexcept override; - - /** - * @return Description MUST be ProbabilitySampler{0.000100} - */ - nostd::string_view GetDescription() const noexcept override; - -private: - std::string description_; - const uint64_t threshold_; -}; -} // namespace trace -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h b/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h new file mode 100644 index 0000000000..80be8ae80d --- /dev/null +++ b/sdk/include/opentelemetry/sdk/trace/samplers/trace_id_ratio.h @@ -0,0 +1,50 @@ +#pragma once + +#include "opentelemetry/sdk/trace/sampler.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +namespace trace_api = opentelemetry::trace; +/** + * The TraceIdRatioBased sampler computes and returns a decision based on the + * provided trace_id and the configured ratio. + */ +class TraceIdRatioBasedSampler : public Sampler +{ +public: + /** + * @param ratio a required value, 1.0 >= ratio >= 0.0. If the given trace_id + * falls into a given ratio of all possible trace_id values, ShouldSample will + * return RECORD_AND_SAMPLE. + * @throws invalid_argument if ratio is out of bounds [0.0, 1.0] + */ + explicit TraceIdRatioBasedSampler(double ratio); + + /** + * @return Returns either RECORD_AND_SAMPLE or DROP based on current + * sampler configuration and provided trace_id and ratio. trace_id + * is used as a pseudorandom value in conjunction with the predefined + * ratio to determine whether this trace should be sampled + */ + SamplingResult ShouldSample( + const trace_api::SpanContext & /*parent_context*/, + trace_api::TraceId trace_id, + nostd::string_view /*name*/, + trace_api::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/) noexcept override; + + /** + * @return Description MUST be TraceIdRatioBasedSampler{0.000100} + */ + nostd::string_view GetDescription() const noexcept override; + +private: + std::string description_; + const uint64_t threshold_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/simple_processor.h b/sdk/include/opentelemetry/sdk/trace/simple_processor.h index c9cc617fa6..e10d8fa515 100644 --- a/sdk/include/opentelemetry/sdk/trace/simple_processor.h +++ b/sdk/include/opentelemetry/sdk/trace/simple_processor.h @@ -1,5 +1,9 @@ #pragma once +#include +#include + +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/sdk/trace/exporter.h" #include "opentelemetry/sdk/trace/processor.h" @@ -13,6 +17,9 @@ namespace trace * SpanExporter, as soon as they are finished. * * OnEnd and ForceFlush are no-ops. + * + * All calls to the configured SpanExporter are synchronized using a + * spin-lock on an atomic_flag. */ class SimpleSpanProcessor : public SpanProcessor { @@ -35,6 +42,7 @@ class SimpleSpanProcessor : public SpanProcessor void OnEnd(std::unique_ptr &&span) noexcept override { nostd::span> batch(&span, 1); + const std::lock_guard locked(lock_); if (exporter_->Export(batch) == ExportResult::kFailure) { /* Once it is defined how the SDK does logging, an error should be @@ -48,11 +56,17 @@ class SimpleSpanProcessor : public SpanProcessor void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override { - exporter_->Shutdown(timeout); + // We only call shutdown ONCE. + if (!shutdown_latch_.test_and_set(std::memory_order_acquire)) + { + exporter_->Shutdown(timeout); + } } private: std::unique_ptr exporter_; + opentelemetry::common::SpinLockMutex lock_; + std::atomic_flag shutdown_latch_{ATOMIC_FLAG_INIT}; }; } // namespace trace } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/span_data.h b/sdk/include/opentelemetry/sdk/trace/span_data.h index b745a3f33f..8787f5fce2 100644 --- a/sdk/include/opentelemetry/sdk/trace/span_data.h +++ b/sdk/include/opentelemetry/sdk/trace/span_data.h @@ -12,6 +12,8 @@ #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_id.h" +#include + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -25,7 +27,7 @@ class SpanDataEvent public: SpanDataEvent(std::string name, core::SystemTimestamp timestamp, - const trace_api::KeyValueIterable &attributes) + const opentelemetry::common::KeyValueIterable &attributes) : name_(name), timestamp_(timestamp), attribute_map_(attributes) {} @@ -64,7 +66,7 @@ class SpanDataLink { public: SpanDataLink(opentelemetry::trace::SpanContext span_context, - const trace_api::KeyValueIterable &attributes) + const opentelemetry::common::KeyValueIterable &attributes) : span_context_(span_context), attribute_map_(attributes) {} @@ -174,26 +176,30 @@ class SpanData final : public Recordable void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace_api::KeyValueIterable &attributes) noexcept override + const opentelemetry::common::KeyValueIterable &attributes) noexcept override { SpanDataEvent event(std::string(name), timestamp, attributes); events_.push_back(event); } void AddLink(opentelemetry::trace::SpanContext span_context, - const trace_api::KeyValueIterable &attributes) noexcept override + const opentelemetry::common::KeyValueIterable &attributes) noexcept override { SpanDataLink link(span_context, attributes); links_.push_back(link); } - void SetStatus(trace_api::CanonicalCode code, nostd::string_view description) noexcept override + void SetStatus(opentelemetry::trace::CanonicalCode code, + nostd::string_view description) noexcept override { status_code_ = code; status_desc_ = std::string(description); } - void SetName(nostd::string_view name) noexcept override { name_ = std::string(name); } + void SetName(nostd::string_view name) noexcept override + { + name_ = std::string(name.data(), name.length()); + } void SetStartTime(opentelemetry::core::SystemTimestamp start_time) noexcept override { diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index bb28842c75..42d16ce219 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -46,7 +46,7 @@ class Tracer final : public trace_api::Tracer, public std::enable_shared_from_th nostd::shared_ptr StartSpan( nostd::string_view name, - const trace_api::KeyValueIterable &attributes, + const opentelemetry::common::KeyValueIterable &attributes, const trace_api::StartSpanOptions &options = {}) noexcept override; void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override; diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index 6581ad46be..abffd0ca47 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -54,7 +54,12 @@ class TracerProvider final : public opentelemetry::trace::TracerProvider private: opentelemetry::sdk::AtomicSharedPtr processor_; +#if 0 + // TODO: pros and cons of exposing this as `std::shared_ptr` vs keeping `nostd::shared_ptr` ? std::shared_ptr tracer_; +#else + opentelemetry::nostd::shared_ptr tracer_; +#endif const std::shared_ptr sampler_; }; } // namespace trace diff --git a/sdk/src/common/BUILD b/sdk/src/common/BUILD index a8981b4d55..b0724c3810 100644 --- a/sdk/src/common/BUILD +++ b/sdk/src/common/BUILD @@ -16,7 +16,10 @@ package(default_visibility = ["//visibility:public"]) cc_library( name = "random", - srcs = ["random.cc"], + srcs = [ + "core.cc", + "random.cc", + ], hdrs = [ "fast_random_number_generator.h", "random.h", diff --git a/sdk/src/common/CMakeLists.txt b/sdk/src/common/CMakeLists.txt index 042a857539..4a47500a73 100644 --- a/sdk/src/common/CMakeLists.txt +++ b/sdk/src/common/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMMON_SRCS random.cc) +set(COMMON_SRCS random.cc core.cc) if(WIN32) list(APPEND COMMON_SRCS platform/fork_windows.cc) else() @@ -6,4 +6,5 @@ else() endif() add_library(opentelemetry_common ${COMMON_SRCS}) -target_link_libraries(opentelemetry_common Threads::Threads) +target_link_libraries(opentelemetry_common Threads::Threads + ${CORE_RUNTIME_LIBS}) diff --git a/sdk/src/common/core.cc b/sdk/src/common/core.cc new file mode 100644 index 0000000000..a8db9a0448 --- /dev/null +++ b/sdk/src/common/core.cc @@ -0,0 +1,12 @@ +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/version.h" + +#if defined(HAVE_ABSEIL) +namespace absl +{ +namespace variant_internal +{ +void __cdecl ThrowBadVariantAccess(){/* TODO: std::terminate or re-throw? */}; +} +} // namespace absl +#endif diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index f856481a5e..29a769628f 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -543,41 +543,41 @@ nostd::shared_ptr> Meter::NewDoubleValueObser return nostd::shared_ptr>(ptr); } -void Meter::RecordShortBatch(const trace::KeyValueIterable &labels, +void Meter::RecordShortBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept { - for (int i = 0; i < instruments.size(); ++i) + for (size_t i = 0; i < instruments.size(); ++i) { instruments[i]->update(values[i], labels); } } -void Meter::RecordIntBatch(const trace::KeyValueIterable &labels, +void Meter::RecordIntBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept { - for (int i = 0; i < instruments.size(); ++i) + for (size_t i = 0; i < instruments.size(); ++i) { instruments[i]->update(values[i], labels); } } -void Meter::RecordFloatBatch(const trace::KeyValueIterable &labels, +void Meter::RecordFloatBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept { - for (int i = 0; i < instruments.size(); ++i) + for (size_t i = 0; i < instruments.size(); ++i) { instruments[i]->update(values[i], labels); } } -void Meter::RecordDoubleBatch(const trace::KeyValueIterable &labels, +void Meter::RecordDoubleBatch(const common::KeyValueIterable &labels, nostd::span *> instruments, nostd::span values) noexcept { - for (int i = 0; i < instruments.size(); ++i) + for (size_t i = 0; i < instruments.size(); ++i) { instruments[i]->update(values[i], labels); } @@ -738,7 +738,7 @@ bool Meter::IsValidName(nostd::string_view name) return false; else { - for (int i = 0; i < name.size(); ++i) + for (size_t i = 0; i < name.size(); ++i) { if (!isalnum(name[i]) && name[i] != '_' && name[i] != '.' && name[i] != '-') return false; @@ -761,4 +761,4 @@ bool Meter::NameAlreadyUsed(nostd::string_view name) } } // namespace metrics } // namespace sdk -OPENTELEMETRY_END_NAMESPACE \ No newline at end of file +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/CMakeLists.txt b/sdk/src/trace/CMakeLists.txt index 9f9e06f45c..97f4f3d3b3 100644 --- a/sdk/src/trace/CMakeLists.txt +++ b/sdk/src/trace/CMakeLists.txt @@ -1,6 +1,7 @@ add_library( opentelemetry_trace tracer_provider.cc tracer.cc span.cc batch_span_processor.cc - samplers/parent_or_else.cc samplers/probability.cc) + samplers/parent.cc samplers/trace_id_ratio.cc) -target_link_libraries(opentelemetry_trace opentelemetry_common) +target_link_libraries(opentelemetry_trace opentelemetry_common + ${CORE_RUNTIME_LIBS}) diff --git a/sdk/src/trace/samplers/parent_or_else.cc b/sdk/src/trace/samplers/parent.cc similarity index 50% rename from sdk/src/trace/samplers/parent_or_else.cc rename to sdk/src/trace/samplers/parent.cc index 29026a0167..5385b3543e 100644 --- a/sdk/src/trace/samplers/parent_or_else.cc +++ b/sdk/src/trace/samplers/parent.cc @@ -1,38 +1,38 @@ -#include "opentelemetry/sdk/trace/samplers/parent_or_else.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { namespace trace { -ParentOrElseSampler::ParentOrElseSampler(std::shared_ptr delegate_sampler) noexcept +ParentBasedSampler::ParentBasedSampler(std::shared_ptr delegate_sampler) noexcept : delegate_sampler_(delegate_sampler), - description_("ParentOrElse{" + std::string{delegate_sampler->GetDescription()} + "}") + description_("ParentBased{" + std::string{delegate_sampler->GetDescription()} + "}") {} -SamplingResult ParentOrElseSampler::ShouldSample( - const trace_api::SpanContext *parent_context, +SamplingResult ParentBasedSampler::ShouldSample( + const trace_api::SpanContext &parent_context, trace_api::TraceId trace_id, nostd::string_view name, trace_api::SpanKind span_kind, - const trace_api::KeyValueIterable &attributes) noexcept + const opentelemetry::common::KeyValueIterable &attributes) noexcept { - if (parent_context == nullptr) + if (!parent_context.IsValid()) { // If no parent (root span) exists returns the result of the delegateSampler return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes); } // If parent exists: - if (parent_context->IsSampled()) + if (parent_context.IsSampled()) { return {Decision::RECORD_AND_SAMPLE, nullptr}; } - return {Decision::NOT_RECORD, nullptr}; + return {Decision::DROP, nullptr}; } -nostd::string_view ParentOrElseSampler::GetDescription() const noexcept +nostd::string_view ParentBasedSampler::GetDescription() const noexcept { return description_; } diff --git a/sdk/src/trace/samplers/probability.cc b/sdk/src/trace/samplers/trace_id_ratio.cc similarity index 59% rename from sdk/src/trace/samplers/probability.cc rename to sdk/src/trace/samplers/trace_id_ratio.cc index 0fd45644e4..f20246cb55 100644 --- a/sdk/src/trace/samplers/probability.cc +++ b/sdk/src/trace/samplers/trace_id_ratio.cc @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "opentelemetry/sdk/trace/samplers/probability.h" +#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h" #include #include @@ -24,26 +24,26 @@ namespace trace_api = opentelemetry::trace; namespace { /** - * Converts a probability in [0, 1] to a threshold in [0, UINT64_MAX] + * Converts a ratio in [0, 1] to a threshold in [0, UINT64_MAX] * - * @param probability a required value top be converted to uint64_t. is - * bounded by 1 >= probability >= 0. - * @return Returns threshold value computed after converting probability to + * @param ratio a required value top be converted to uint64_t. is + * bounded by 1 >= ratio >= 0. + * @return Returns threshold value computed after converting ratio to * uint64_t datatype */ -uint64_t CalculateThreshold(double probability) noexcept +uint64_t CalculateThreshold(double ratio) noexcept { - if (probability <= 0.0) + if (ratio <= 0.0) return 0; - if (probability >= 1.0) + if (ratio >= 1.0) return UINT64_MAX; - // We can't directly return probability * UINT64_MAX. + // We can't directly return ratio * UINT64_MAX. // // UINT64_MAX is (2^64)-1, but as a double rounds up to 2^64. // For probabilities >= 1-(2^-54), the product wraps to zero! // Instead, calculate the high and low 32 bits separately. - const double product = UINT32_MAX * probability; + const double product = UINT32_MAX * ratio; double hi_bits, lo_bits = ldexp(modf(product, &hi_bits), 32) + product; return (static_cast(hi_bits) << 32) + static_cast(lo_bits); } @@ -62,9 +62,9 @@ uint64_t CalculateThresholdFromBuffer(const trace_api::TraceId &trace_id) noexce uint64_t res = 0; std::memcpy(&res, &trace_id, 8); - double probability = (double)res / UINT64_MAX; + double ratio = (double)res / UINT64_MAX; - return CalculateThreshold(probability); + return CalculateThreshold(ratio); } } // namespace @@ -73,47 +73,35 @@ namespace sdk { namespace trace { -ProbabilitySampler::ProbabilitySampler(double probability) - : threshold_(CalculateThreshold(probability)) +TraceIdRatioBasedSampler::TraceIdRatioBasedSampler(double ratio) + : threshold_(CalculateThreshold(ratio)) { - if (probability > 1.0) - probability = 1.0; - if (probability < 0.0) - probability = 0.0; - description_ = "ProbabilitySampler{" + std::to_string(probability) + "}"; + if (ratio > 1.0) + ratio = 1.0; + if (ratio < 0.0) + ratio = 0.0; + description_ = "TraceIdRatioBasedSampler{" + std::to_string(ratio) + "}"; } -SamplingResult ProbabilitySampler::ShouldSample( - const trace_api::SpanContext *parent_context, +SamplingResult TraceIdRatioBasedSampler::ShouldSample( + const trace_api::SpanContext & /*parent_context*/, trace_api::TraceId trace_id, nostd::string_view /*name*/, trace_api::SpanKind /*span_kind*/, - const trace_api::KeyValueIterable & /*attributes*/) noexcept + const opentelemetry::common::KeyValueIterable & /*attributes*/) noexcept { - if (parent_context && !parent_context->HasRemoteParent()) - { - if (parent_context->IsSampled()) - { - return {Decision::RECORD_AND_SAMPLE, nullptr}; - } - else - { - return {Decision::NOT_RECORD, nullptr}; - } - } - if (threshold_ == 0) - return {Decision::NOT_RECORD, nullptr}; + return {Decision::DROP, nullptr}; if (CalculateThresholdFromBuffer(trace_id) <= threshold_) { return {Decision::RECORD_AND_SAMPLE, nullptr}; } - return {Decision::NOT_RECORD, nullptr}; + return {Decision::DROP, nullptr}; } -nostd::string_view ProbabilitySampler::GetDescription() const noexcept +nostd::string_view TraceIdRatioBasedSampler::GetDescription() const noexcept { return description_; } diff --git a/sdk/src/trace/span.cc b/sdk/src/trace/span.cc index c27c071b62..417051e464 100644 --- a/sdk/src/trace/span.cc +++ b/sdk/src/trace/span.cc @@ -60,18 +60,15 @@ trace_api::SpanId GenerateRandomSpanId() Span::Span(std::shared_ptr &&tracer, std::shared_ptr processor, nostd::string_view name, - const trace_api::KeyValueIterable &attributes, + const opentelemetry::common::KeyValueIterable &attributes, const trace_api::StartSpanOptions &options, const trace_api::SpanContext &parent_span_context) noexcept : tracer_{std::move(tracer)}, processor_{processor}, recordable_{processor_->MakeRecordable()}, start_steady_time{options.start_steady_time}, - has_ended_{false}, - token_{nullptr} - + has_ended_{false} { - (void)options; if (recordable_ == nullptr) { return; @@ -121,25 +118,38 @@ void Span::SetAttribute(nostd::string_view key, void Span::AddEvent(nostd::string_view name) noexcept { - (void)name; + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->AddEvent(name); } void Span::AddEvent(nostd::string_view name, core::SystemTimestamp timestamp) noexcept { - (void)name; - (void)timestamp; + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->AddEvent(name, timestamp); } void Span::AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace_api::KeyValueIterable &attributes) noexcept + const opentelemetry::common::KeyValueIterable &attributes) noexcept { - (void)name; - (void)timestamp; - (void)attributes; + std::lock_guard lock_guard{mu_}; + if (recordable_ == nullptr) + { + return; + } + recordable_->AddEvent(name, timestamp, attributes); } -void Span::SetStatus(trace_api::CanonicalCode code, nostd::string_view description) noexcept +void Span::SetStatus(opentelemetry::trace::CanonicalCode code, + nostd::string_view description) noexcept { std::lock_guard lock_guard{mu_}; if (recordable_ == nullptr) @@ -169,12 +179,6 @@ void Span::End(const trace_api::EndSpanOptions &options) noexcept } has_ended_ = true; - if (token_ != nullptr) - { - context::RuntimeContext::Detach(*token_); - token_.reset(); - } - if (recordable_ == nullptr) { return; @@ -193,12 +197,6 @@ bool Span::IsRecording() const noexcept std::lock_guard lock_guard{mu_}; return recordable_ != nullptr; } - -void Span::SetToken(nostd::unique_ptr &&token) noexcept -{ - token_ = std::move(token); -} - } // namespace trace } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/span.h b/sdk/src/trace/span.h index 03323d6b13..f11711e2aa 100644 --- a/sdk/src/trace/span.h +++ b/sdk/src/trace/span.h @@ -18,7 +18,7 @@ class Span final : public trace_api::Span explicit Span(std::shared_ptr &&tracer, std::shared_ptr processor, nostd::string_view name, - const trace_api::KeyValueIterable &attributes, + const opentelemetry::common::KeyValueIterable &attributes, const trace_api::StartSpanOptions &options, const trace_api::SpanContext &parent_span_context) noexcept; @@ -33,7 +33,7 @@ class Span final : public trace_api::Span void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp, - const trace_api::KeyValueIterable &attributes) noexcept override; + const opentelemetry::common::KeyValueIterable &attributes) noexcept override; void SetStatus(trace_api::CanonicalCode code, nostd::string_view description) noexcept override; @@ -45,8 +45,6 @@ class Span final : public trace_api::Span trace_api::SpanContext GetContext() const noexcept override { return *span_context_.get(); } - void SetToken(nostd::unique_ptr &&token) noexcept override; - private: std::shared_ptr tracer_; std::shared_ptr processor_; @@ -55,7 +53,6 @@ class Span final : public trace_api::Span opentelemetry::core::SteadyTimestamp start_steady_time; std::unique_ptr span_context_; bool has_ended_; - nostd::unique_ptr token_; }; } // namespace trace } // namespace sdk diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index f7b2d38033..db88cc2b46 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -30,44 +30,49 @@ std::shared_ptr Tracer::GetSampler() const noexcept return sampler_; } -// Helper function to extract the current span context from the runtime context. -// Returns an invalid span context if the runtime context doesn't contain a span. -trace_api::SpanContext GetCurrentSpanContext() +trace_api::SpanContext GetCurrentSpanContext(const trace_api::SpanContext &explicit_parent) { - context::ContextValue curr_span_context = context::RuntimeContext::GetValue(SpanKey); + // Use the explicit parent, if it's valid. + if (explicit_parent.IsValid()) + { + return explicit_parent; + } + + // Use the currently active span, if there's one. + auto curr_span_context = context::RuntimeContext::GetValue(SpanKey); if (nostd::holds_alternative>(curr_span_context)) { auto curr_span = nostd::get>(curr_span_context); return curr_span->GetContext(); } - return trace_api::SpanContext(); + + // Otherwise return an invalid SpanContext. + return trace_api::SpanContext::GetInvalid(); } nostd::shared_ptr Tracer::StartSpan( nostd::string_view name, - const trace_api::KeyValueIterable &attributes, + const opentelemetry::common::KeyValueIterable &attributes, const trace_api::StartSpanOptions &options) noexcept { - // TODO: replace nullptr with parent context in span context + trace_api::SpanContext parent = GetCurrentSpanContext(options.parent); + auto sampling_result = - sampler_->ShouldSample(nullptr, trace_api::TraceId(), name, options.kind, attributes); - if (sampling_result.decision == Decision::NOT_RECORD) + sampler_->ShouldSample(parent, parent.trace_id(), name, options.kind, attributes); + if (sampling_result.decision == Decision::DROP) { - auto span = nostd::shared_ptr{ - new (std::nothrow) trace_api::NoopSpan{this->shared_from_this()}}; + // Don't allocate a no-op span for every DROP decision, but use a static + // singleton for this case. + static nostd::shared_ptr noop_span( + new trace_api::NoopSpan{this->shared_from_this()}); - return span; + return noop_span; } else { - auto span = nostd::shared_ptr{ - new (std::nothrow) Span{this->shared_from_this(), processor_.load(), name, attributes, - options, GetCurrentSpanContext()}}; - - span->SetToken( - nostd::unique_ptr(new context::Token(context::RuntimeContext::Attach( - context::RuntimeContext::GetCurrent().SetValue(SpanKey, span))))); + auto span = nostd::shared_ptr{new (std::nothrow) Span{ + this->shared_from_this(), processor_.load(), name, attributes, options, parent}}; // if the attributes is not nullptr, add attributes to the span. if (sampling_result.attributes) diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 8f05436b4c..e2dd5f69e0 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -14,7 +14,8 @@ opentelemetry::nostd::shared_ptr TracerProvider::G nostd::string_view library_name, nostd::string_view library_version) noexcept { - return opentelemetry::nostd::shared_ptr(tracer_); + // TODO: do we have to transform this at all, if we can keep it nostd::shared_ptr at source? + return tracer_; } void TracerProvider::SetProcessor(std::shared_ptr processor) noexcept diff --git a/sdk/test/common/CMakeLists.txt b/sdk/test/common/CMakeLists.txt index 7a181c90c4..f1028bf6d8 100644 --- a/sdk/test/common/CMakeLists.txt +++ b/sdk/test/common/CMakeLists.txt @@ -4,8 +4,11 @@ foreach(testname add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - opentelemetry_common opentelemetry_trace) - gtest_add_tests(TARGET ${testname} TEST_PREFIX trace. TEST_LIST ${testname}) + ${CORE_RUNTIME_LIBS} opentelemetry_common opentelemetry_trace) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX trace. + TEST_LIST ${testname}) endforeach() add_executable(random_fork_test random_fork_test.cc) @@ -13,9 +16,11 @@ target_link_libraries(random_fork_test opentelemetry_common) add_test(random_fork_test random_fork_test) add_executable(random_benchmark random_benchmark.cc) -target_link_libraries(random_benchmark benchmark::benchmark - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) +target_link_libraries( + random_benchmark benchmark::benchmark ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_common) add_executable(circular_buffer_benchmark circular_buffer_benchmark.cc) -target_link_libraries(circular_buffer_benchmark benchmark::benchmark - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) +target_link_libraries( + circular_buffer_benchmark benchmark::benchmark ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) diff --git a/sdk/test/common/circular_buffer_benchmark.cc b/sdk/test/common/circular_buffer_benchmark.cc index cec8c95f5f..cb6eba6482 100644 --- a/sdk/test/common/circular_buffer_benchmark.cc +++ b/sdk/test/common/circular_buffer_benchmark.cc @@ -102,8 +102,8 @@ static void RunSimulation(Buffer &buffer, int num_threads, int n) noexcept static void BM_BaselineBuffer(benchmark::State &state) { const size_t max_elements = 500; - auto num_threads = state.range(0); - const int n = N / num_threads; + const int num_threads = static_cast(state.range(0)); + const int n = static_cast(N / num_threads); BaselineCircularBuffer buffer{max_elements}; for (auto _ : state) { @@ -116,8 +116,8 @@ BENCHMARK(BM_BaselineBuffer)->Arg(1)->Arg(2)->Arg(4); static void BM_LockFreeBuffer(benchmark::State &state) { const size_t max_elements = 500; - auto num_threads = state.range(0); - const int n = N / num_threads; + const int num_threads = static_cast(state.range(0)); + const int n = static_cast(N / num_threads); CircularBuffer buffer{max_elements}; for (auto _ : state) { diff --git a/sdk/test/common/circular_buffer_test.cc b/sdk/test/common/circular_buffer_test.cc index 2fd9dc4dcc..404e8a0325 100644 --- a/sdk/test/common/circular_buffer_test.cc +++ b/sdk/test/common/circular_buffer_test.cc @@ -1,5 +1,6 @@ #include "opentelemetry/sdk/common/circular_buffer.h" +#include #include #include #include diff --git a/sdk/test/metrics/CMakeLists.txt b/sdk/test/metrics/CMakeLists.txt index e518dbdd1e..dbab882a46 100644 --- a/sdk/test/metrics/CMakeLists.txt +++ b/sdk/test/metrics/CMakeLists.txt @@ -11,7 +11,11 @@ foreach( metric_instrument_test controller_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics) - gtest_add_tests(TARGET ${testname} TEST_PREFIX metrics. TEST_LIST ${testname}) + target_link_libraries( + ${testname} ${GTEST_BOTH_LIBRARIES} ${CORE_RUNTIME_LIBS} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_metrics) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX metrics. + TEST_LIST ${testname}) endforeach() diff --git a/sdk/test/metrics/controller_test.cc b/sdk/test/metrics/controller_test.cc index 0dd14b7649..dad5012f9f 100644 --- a/sdk/test/metrics/controller_test.cc +++ b/sdk/test/metrics/controller_test.cc @@ -34,7 +34,7 @@ TEST(Controller, Constructor) auto instr = meter->NewIntCounter("test", "none", "none", true); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha.start(); diff --git a/sdk/test/metrics/meter_test.cc b/sdk/test/metrics/meter_test.cc index b3cdc0ce39..b47b0d418a 100644 --- a/sdk/test/metrics/meter_test.cc +++ b/sdk/test/metrics/meter_test.cc @@ -69,7 +69,7 @@ TEST(Meter, CollectSyncInstruments) auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; counter->add(1, labelkv); @@ -101,7 +101,7 @@ TEST(Meter, CollectDeletedSync) ASSERT_EQ(m.Collect().size(), 0); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; { auto counter = m.NewShortCounter("Test-counter", "For testing", "Unitless", true); counter->add(1, labelkv); @@ -118,7 +118,7 @@ TEST(Meter, CollectDeletedSync) void Callback(opentelemetry::metrics::ObserverResult result) { std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; result.observe(1, labelkv); } @@ -134,7 +134,7 @@ TEST(Meter, CollectAsyncInstruments) m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &ShortCallback); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; sumobs->observe(1, labelkv); @@ -166,7 +166,7 @@ TEST(Meter, CollectDeletedAsync) ASSERT_EQ(m.Collect().size(), 0); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; { auto sumobs = m.NewShortSumObserver("Test-counter", "For testing", "Unitless", true, &Callback); sumobs->observe(1, labelkv); @@ -191,7 +191,7 @@ TEST(Meter, RecordBatch) auto dcounter = m.NewDoubleCounter("Test-dcounter", "For testing", "Unitless", true); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; metrics_api::SynchronousInstrument *sinstr_arr[] = {scounter.get()}; short svalues_arr[] = {1}; @@ -246,7 +246,7 @@ TEST(Meter, DisableCollectSync) { Meter m("Test"); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; auto c = m.NewShortCounter("c", "", "", false); c->add(1, labelkv); ASSERT_EQ(m.Collect().size(), 0); @@ -256,7 +256,7 @@ TEST(Meter, DisableCollectAsync) { Meter m("Test"); std::map labels = {{"Key", "Value"}}; - auto labelkv = opentelemetry::trace::KeyValueIterableView{labels}; + auto labelkv = opentelemetry::common::KeyValueIterableView{labels}; auto c = m.NewShortValueObserver("c", "", "", false, &ShortCallback); c->observe(1, labelkv); ASSERT_EQ(m.Collect().size(), 0); diff --git a/sdk/test/metrics/metric_instrument_test.cc b/sdk/test/metrics/metric_instrument_test.cc index 28ca482375..9fd1c5c5a7 100644 --- a/sdk/test/metrics/metric_instrument_test.cc +++ b/sdk/test/metrics/metric_instrument_test.cc @@ -20,7 +20,7 @@ namespace metrics void ObserverConstructorCallback(metrics_api::ObserverResult result) { std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; result.observe(1, labelkv); } @@ -31,7 +31,7 @@ TEST(ApiSdkConversion, async) new ValueObserver("ankit", "none", "unitles", true, &ObserverConstructorCallback)); std::map labels = {{"key587", "value264"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; alpha->observe(123456, labelkv); EXPECT_EQ(dynamic_cast *>(alpha.get())->GetRecords()[0].GetLabels(), @@ -50,7 +50,7 @@ TEST(IntValueObserver, InstrumentFunctions) ValueObserver alpha("enabled", "no description", "unitless", true, &ObserverConstructorCallback); std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; EXPECT_EQ(alpha.GetName(), "enabled"); EXPECT_EQ(alpha.GetDescription(), "no description"); @@ -64,7 +64,7 @@ TEST(IntValueObserver, InstrumentFunctions) void ObserverCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -74,7 +74,7 @@ void ObserverCallback(std::shared_ptr> in, void NegObserverCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -89,8 +89,8 @@ TEST(IntValueObserver, StressObserve) std::map labels = {{"key", "value"}}; std::map labels1 = {{"key1", "value1"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; std::thread first(ObserverCallback, alpha, 25, labelkv); // spawn new threads that call the callback @@ -116,7 +116,7 @@ TEST(IntValueObserver, StressObserve) void SumObserverCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -131,8 +131,8 @@ TEST(IntSumObserver, StressObserve) std::map labels = {{"key", "value"}}; std::map labels1 = {{"key1", "value1"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; std::thread first(SumObserverCallback, alpha, 100000, labelkv); std::thread second(SumObserverCallback, alpha, 100000, labelkv); @@ -148,7 +148,7 @@ TEST(IntSumObserver, StressObserve) void UpDownSumObserverCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -158,7 +158,7 @@ void UpDownSumObserverCallback(std::shared_ptr> in, void NegUpDownSumObserverCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -173,8 +173,8 @@ TEST(IntUpDownObserver, StressAdd) std::map labels = {{"key", "value"}}; std::map labels1 = {{"key1", "value1"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; std::thread first(UpDownSumObserverCallback, alpha, 12340, labelkv); // spawn new threads that call the callback @@ -216,10 +216,10 @@ TEST(Counter, Binding) std::map labels2 = {{"key2", "value2"}, {"key3", "value3"}}; std::map labels3 = {{"key3", "value3"}, {"key2", "value2"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; - auto labelkv2 = trace::KeyValueIterableView{labels2}; - auto labelkv3 = trace::KeyValueIterableView{labels3}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; + auto labelkv2 = common::KeyValueIterableView{labels2}; + auto labelkv3 = common::KeyValueIterableView{labels3}; auto beta = alpha.bindCounter(labelkv); auto gamma = alpha.bindCounter(labelkv1); @@ -246,7 +246,7 @@ TEST(Counter, getAggsandnewupdate) std::map labels = {{"key3", "value3"}, {"key2", "value2"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; auto beta = alpha.bindCounter(labelkv); beta->add(1); beta->unbind(); @@ -263,7 +263,7 @@ TEST(Counter, getAggsandnewupdate) void CounterCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -278,8 +278,8 @@ TEST(Counter, StressAdd) std::map labels = {{"key", "value"}}; std::map labels1 = {{"key1", "value1"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; std::thread first(CounterCallback, alpha, 1000, labelkv); std::thread second(CounterCallback, alpha, 1000, labelkv); @@ -301,7 +301,7 @@ TEST(Counter, StressAdd) void UpDownCounterCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -311,7 +311,7 @@ void UpDownCounterCallback(std::shared_ptr> in, void NegUpDownCounterCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -326,8 +326,8 @@ TEST(IntUpDownCounter, StressAdd) std::map labels = {{"key", "value"}}; std::map labels1 = {{"key1", "value1"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; std::thread first(UpDownCounterCallback, alpha, 12340, labelkv); // spawn new threads that call the callback @@ -354,7 +354,7 @@ TEST(IntUpDownCounter, StressAdd) void RecorderCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -364,7 +364,7 @@ void RecorderCallback(std::shared_ptr> in, void NegRecorderCallback(std::shared_ptr> in, int freq, - const trace::KeyValueIterable &labels) + const common::KeyValueIterable &labels) { for (int i = 0; i < freq; i++) { @@ -379,8 +379,8 @@ TEST(IntValueRecorder, StressRecord) std::map labels = {{"key", "value"}}; std::map labels1 = {{"key1", "value1"}}; - auto labelkv = trace::KeyValueIterableView{labels}; - auto labelkv1 = trace::KeyValueIterableView{labels1}; + auto labelkv = common::KeyValueIterableView{labels}; + auto labelkv1 = common::KeyValueIterableView{labels1}; std::thread first(RecorderCallback, alpha, 25, labelkv); // spawn new threads that call the callback @@ -445,7 +445,7 @@ TEST(Instruments, NoUpdateNoRecord) std::map labels = {{"key", "value"}}; - auto labelkv = trace::KeyValueIterableView{labels}; + auto labelkv = common::KeyValueIterableView{labels}; EXPECT_EQ(alpha.GetRecords().size(), 0); alpha.add(1, labelkv); diff --git a/sdk/test/metrics/sketch_aggregator_test.cc b/sdk/test/metrics/sketch_aggregator_test.cc index 8b4800952f..7045fe393a 100644 --- a/sdk/test/metrics/sketch_aggregator_test.cc +++ b/sdk/test/metrics/sketch_aggregator_test.cc @@ -58,7 +58,7 @@ TEST(Sketch, NormalValues) EXPECT_EQ(alpha.get_counts(), correct); std::vector captured_bounds = alpha.get_boundaries(); - for (int i = 0; i < captured_bounds.size(); i++) + for (size_t i = 0; i < captured_bounds.size(); i++) { captured_bounds[i] = round(captured_bounds[i]); } diff --git a/sdk/test/trace/BUILD b/sdk/test/trace/BUILD index f257ff1f1f..a4f0e9d1ae 100644 --- a/sdk/test/trace/BUILD +++ b/sdk/test/trace/BUILD @@ -28,6 +28,7 @@ cc_test( "simple_processor_test.cc", ], deps = [ + "//exporters/memory:in_memory_span_exporter", "//sdk/src/trace", "@com_google_googletest//:gtest_main", ], @@ -50,6 +51,7 @@ cc_test( "tracer_test.cc", ], deps = [ + "//exporters/memory:in_memory_span_exporter", "//sdk/src/trace", "@com_google_googletest//:gtest_main", ], @@ -78,9 +80,9 @@ cc_test( ) cc_test( - name = "parent_or_else_sampler_test", + name = "parent_sampler_test", srcs = [ - "parent_or_else_sampler_test.cc", + "parent_sampler_test.cc", ], deps = [ "//sdk/src/trace", @@ -89,9 +91,9 @@ cc_test( ) cc_test( - name = "probability_sampler_test", + name = "trace_id_ratio_sampler_test", srcs = [ - "probability_sampler_test.cc", + "trace_id_ratio_sampler_test.cc", ], deps = [ "//sdk/src/common:random", @@ -114,5 +116,8 @@ cc_test( otel_cc_benchmark( name = "sampler_benchmark", srcs = ["sampler_benchmark.cc"], - deps = ["//sdk/src/trace"], + deps = [ + "//exporters/memory:in_memory_span_exporter", + "//sdk/src/trace", + ], ) diff --git a/sdk/test/trace/CMakeLists.txt b/sdk/test/trace/CMakeLists.txt index c72cd7fe77..1798386884 100644 --- a/sdk/test/trace/CMakeLists.txt +++ b/sdk/test/trace/CMakeLists.txt @@ -6,16 +6,26 @@ foreach( tracer_test always_off_sampler_test always_on_sampler_test - parent_or_else_sampler_test - probability_sampler_test + parent_sampler_test + trace_id_ratio_sampler_test batch_span_processor_test attribute_utils_test) add_executable(${testname} "${testname}.cc") - target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_trace) - gtest_add_tests(TARGET ${testname} TEST_PREFIX trace. TEST_LIST ${testname}) + target_link_libraries( + ${testname} + ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${CORE_RUNTIME_LIBS} + opentelemetry_common + opentelemetry_trace + opentelemetry_exporter_in_memory) + gtest_add_tests( + TARGET ${testname} + TEST_PREFIX trace. + TEST_LIST ${testname}) endforeach() add_executable(sampler_benchmark sampler_benchmark.cc) -target_link_libraries(sampler_benchmark benchmark::benchmark - ${CMAKE_THREAD_LIBS_INIT} opentelemetry_trace) +target_link_libraries( + sampler_benchmark benchmark::benchmark ${CMAKE_THREAD_LIBS_INIT} + ${CORE_RUNTIME_LIBS} opentelemetry_trace opentelemetry_exporter_in_memory) diff --git a/sdk/test/trace/always_off_sampler_test.cc b/sdk/test/trace/always_off_sampler_test.cc index f987185c2b..023cc45316 100644 --- a/sdk/test/trace/always_off_sampler_test.cc +++ b/sdk/test/trace/always_off_sampler_test.cc @@ -1,10 +1,10 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/sdk/trace/samplers/always_off.h" #include using opentelemetry::sdk::trace::AlwaysOffSampler; using opentelemetry::sdk::trace::Decision; +using opentelemetry::trace::SpanContext; TEST(AlwaysOffSampler, ShouldSample) { @@ -15,11 +15,12 @@ TEST(AlwaysOffSampler, ShouldSample) using M = std::map; M m1 = {{}}; - opentelemetry::trace::KeyValueIterableView view{m1}; + opentelemetry::common::KeyValueIterableView view{m1}; - auto sampling_result = sampler.ShouldSample(nullptr, trace_id, "", span_kind, view); + auto sampling_result = + sampler.ShouldSample(SpanContext::GetInvalid(), trace_id, "", span_kind, view); - ASSERT_EQ(Decision::NOT_RECORD, sampling_result.decision); + ASSERT_EQ(Decision::DROP, sampling_result.decision); ASSERT_EQ(nullptr, sampling_result.attributes); } diff --git a/sdk/test/trace/always_on_sampler_test.cc b/sdk/test/trace/always_on_sampler_test.cc index 5f6443c096..31d3a2df01 100644 --- a/sdk/test/trace/always_on_sampler_test.cc +++ b/sdk/test/trace/always_on_sampler_test.cc @@ -1,4 +1,3 @@ -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" @@ -7,6 +6,7 @@ using namespace opentelemetry::sdk::trace; using namespace opentelemetry::nostd; +using opentelemetry::trace::SpanContext; TEST(AlwaysOnSampler, ShouldSample) { @@ -21,16 +21,18 @@ TEST(AlwaysOnSampler, ShouldSample) // Test with invalid (empty) trace id and empty parent context auto sampling_result = sampler.ShouldSample( - nullptr, trace_id_invalid, "invalid trace id test", trace_api::SpanKind::kServer, - trace_api::KeyValueIterableView>(key_value_container)); + SpanContext::GetInvalid(), trace_id_invalid, "invalid trace id test", + trace_api::SpanKind::kServer, + opentelemetry::common::KeyValueIterableView>(key_value_container)); ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); ASSERT_EQ(nullptr, sampling_result.attributes); // Test with a valid trace id and empty parent context sampling_result = sampler.ShouldSample( - nullptr, trace_id_valid, "valid trace id test", trace_api::SpanKind::kServer, - trace_api::KeyValueIterableView>(key_value_container)); + SpanContext::GetInvalid(), trace_id_valid, "valid trace id test", + trace_api::SpanKind::kServer, + opentelemetry::common::KeyValueIterableView>(key_value_container)); ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); ASSERT_EQ(nullptr, sampling_result.attributes); diff --git a/sdk/test/trace/attribute_utils_test.cc b/sdk/test/trace/attribute_utils_test.cc index a042fcd7d9..7a3a753b29 100644 --- a/sdk/test/trace/attribute_utils_test.cc +++ b/sdk/test/trace/attribute_utils_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/sdk/trace/attribute_utils.h" -#include "opentelemetry/context/threadlocal_context.h" #include @@ -17,7 +16,7 @@ TEST(AttributeMapTest, AttributesConstruction) std::map attributes = { {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}}; - opentelemetry::trace::KeyValueIterableView> iterable(attributes); + opentelemetry::common::KeyValueIterableView> iterable(attributes); opentelemetry::sdk::trace::AttributeMap map(iterable); for (int i = 0; i < kNumAttributes; i++) diff --git a/sdk/test/trace/batch_span_processor_test.cc b/sdk/test/trace/batch_span_processor_test.cc index d240d06b81..8e9e43c735 100644 --- a/sdk/test/trace/batch_span_processor_test.cc +++ b/sdk/test/trace/batch_span_processor_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/sdk/trace/batch_span_processor.h" -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/sdk/trace/span_data.h" #include "opentelemetry/sdk/trace/tracer.h" diff --git a/sdk/test/trace/parent_or_else_sampler_test.cc b/sdk/test/trace/parent_or_else_sampler_test.cc deleted file mode 100644 index 7e376fd91e..0000000000 --- a/sdk/test/trace/parent_or_else_sampler_test.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include "opentelemetry/context/threadlocal_context.h" -#include "opentelemetry/sdk/trace/samplers/always_off.h" -#include "opentelemetry/sdk/trace/samplers/always_on.h" -#include "opentelemetry/sdk/trace/samplers/parent_or_else.h" - -using opentelemetry::sdk::trace::AlwaysOffSampler; -using opentelemetry::sdk::trace::AlwaysOnSampler; -using opentelemetry::sdk::trace::Decision; -using opentelemetry::sdk::trace::ParentOrElseSampler; -using opentelemetry::trace::SpanContext; - -TEST(ParentOrElseSampler, ShouldSample) -{ - ParentOrElseSampler sampler_off(std::make_shared()); - ParentOrElseSampler sampler_on(std::make_shared()); - - // Set up parameters - opentelemetry::trace::TraceId trace_id; - opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; - using M = std::map; - M m1 = {{}}; - opentelemetry::trace::KeyValueIterableView view{m1}; - SpanContext parent_context_sampled(true, false); - SpanContext parent_context_nonsampled(false, false); - - // Case 1: Parent doesn't exist. Return result of delegateSampler() - auto sampling_result = sampler_off.ShouldSample(nullptr, trace_id, "", span_kind, view); - auto sampling_result2 = sampler_on.ShouldSample(nullptr, trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::NOT_RECORD, sampling_result.decision); - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result2.decision); - - // Case 2: Parent exists and SampledFlag is true - auto sampling_result3 = - sampler_off.ShouldSample(&parent_context_sampled, trace_id, "", span_kind, view); - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result3.decision); - - // Case 3: Parent exists and SampledFlag is false - auto sampling_result4 = - sampler_on.ShouldSample(&parent_context_nonsampled, trace_id, "", span_kind, view); - ASSERT_EQ(Decision::NOT_RECORD, sampling_result4.decision); -} - -TEST(ParentOrElseSampler, GetDescription) -{ - ParentOrElseSampler sampler(std::make_shared()); - ASSERT_EQ("ParentOrElse{AlwaysOffSampler}", sampler.GetDescription()); - ParentOrElseSampler sampler2(std::make_shared()); - ASSERT_EQ("ParentOrElse{AlwaysOnSampler}", sampler2.GetDescription()); -} diff --git a/sdk/test/trace/parent_sampler_test.cc b/sdk/test/trace/parent_sampler_test.cc new file mode 100644 index 0000000000..59eae3b293 --- /dev/null +++ b/sdk/test/trace/parent_sampler_test.cc @@ -0,0 +1,58 @@ +#include +#include +#include "opentelemetry/sdk/trace/samplers/always_off.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" + +using opentelemetry::sdk::trace::AlwaysOffSampler; +using opentelemetry::sdk::trace::AlwaysOnSampler; +using opentelemetry::sdk::trace::Decision; +using opentelemetry::sdk::trace::ParentBasedSampler; +namespace trace_api = opentelemetry::trace; + +TEST(ParentBasedSampler, ShouldSample) +{ + ParentBasedSampler sampler_off(std::make_shared()); + ParentBasedSampler sampler_on(std::make_shared()); + + // Set up parameters + uint8_t trace_id_buffer[trace_api::TraceId::kSize] = {1}; + trace_api::TraceId trace_id{trace_id_buffer}; + uint8_t span_id_buffer[trace_api::SpanId::kSize] = {1}; + trace_api::SpanId span_id{span_id_buffer}; + + opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; + using M = std::map; + M m1 = {{}}; + opentelemetry::common::KeyValueIterableView view{m1}; + trace_api::SpanContext parent_context_sampled(trace_id, span_id, trace_api::TraceFlags{1}, false); + trace_api::SpanContext parent_context_nonsampled(trace_id, span_id, trace_api::TraceFlags{0}, + false); + + // Case 1: Parent doesn't exist. Return result of delegateSampler() + auto sampling_result = + sampler_off.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id, "", span_kind, view); + auto sampling_result2 = + sampler_on.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result2.decision); + + // Case 2: Parent exists and SampledFlag is true + auto sampling_result3 = + sampler_off.ShouldSample(parent_context_sampled, trace_id, "", span_kind, view); + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result3.decision); + + // Case 3: Parent exists and SampledFlag is false + auto sampling_result4 = + sampler_on.ShouldSample(parent_context_nonsampled, trace_id, "", span_kind, view); + ASSERT_EQ(Decision::DROP, sampling_result4.decision); +} + +TEST(ParentBasedSampler, GetDescription) +{ + ParentBasedSampler sampler(std::make_shared()); + ASSERT_EQ("ParentBased{AlwaysOffSampler}", sampler.GetDescription()); + ParentBasedSampler sampler2(std::make_shared()); + ASSERT_EQ("ParentBased{AlwaysOnSampler}", sampler2.GetDescription()); +} diff --git a/sdk/test/trace/probability_sampler_test.cc b/sdk/test/trace/probability_sampler_test.cc deleted file mode 100644 index cd4815aa46..0000000000 --- a/sdk/test/trace/probability_sampler_test.cc +++ /dev/null @@ -1,221 +0,0 @@ -#include "opentelemetry/context/threadlocal_context.h" -#include "opentelemetry/sdk/trace/samplers/probability.h" -#include "src/common/random.h" - -#include -#include -#include - -using opentelemetry::sdk::common::Random; -using opentelemetry::sdk::trace::Decision; -using opentelemetry::sdk::trace::ProbabilitySampler; -using opentelemetry::trace::SpanContext; - -namespace -{ -/* - * Helper function for running probability sampler tests. - * Given a span context, sampler, and number of iterations this function - * will return the number of RECORD_AND_SAMPLE decision based on randomly - * generated traces. - * - * @param context a required valid span context - * @param sampler a required valid sampler - * @param iterations a requried number specifying the number of times to - * generate a random trace_id and check if it should sample using the provided - * provider and context - */ -int RunShouldSampleCountDecision(SpanContext &context, ProbabilitySampler &sampler, int iterations) -{ - int actual_count = 0; - - opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; - - using M = std::map; - M m1 = {{}}; - opentelemetry::trace::KeyValueIterableView view{m1}; - - for (int i = 0; i < iterations; ++i) - { - uint8_t buf[16] = {0}; - Random::GenerateRandomBuffer(buf); - - opentelemetry::trace::TraceId trace_id(buf); - - auto result = sampler.ShouldSample(&context, trace_id, "", span_kind, view); - if (result.decision == Decision::RECORD_AND_SAMPLE) - { - ++actual_count; - } - } - - return actual_count; -} -} // namespace - -TEST(ProbabilitySampler, ShouldSampleWithoutContext) -{ - opentelemetry::trace::TraceId invalid_trace_id; - - opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; - - using M = std::map; - M m1 = {{}}; - opentelemetry::trace::KeyValueIterableView view{m1}; - - ProbabilitySampler s1(0.01); - - auto sampling_result = s1.ShouldSample(nullptr, invalid_trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - constexpr uint8_t buf[] = {0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0}; - opentelemetry::trace::TraceId valid_trace_id(buf); - - sampling_result = s1.ShouldSample(nullptr, valid_trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::NOT_RECORD, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - ProbabilitySampler s2(0.50000001); - - sampling_result = s2.ShouldSample(nullptr, valid_trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - ProbabilitySampler s3(0.49999999); - - sampling_result = s3.ShouldSample(nullptr, valid_trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::NOT_RECORD, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - ProbabilitySampler s4(0.50000000); - - sampling_result = s4.ShouldSample(nullptr, valid_trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); -} - -TEST(ProbabilitySampler, ShouldSampleWithContext) -{ - opentelemetry::trace::TraceId trace_id; - opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; - SpanContext c1(false, false); - SpanContext c2(true, false); - SpanContext c3(false, true); - SpanContext c4(true, true); - - using M = std::map; - M m1 = {{}}; - opentelemetry::trace::KeyValueIterableView view{m1}; - - ProbabilitySampler s1(0.01); - - auto sampling_result = s1.ShouldSample(&c1, trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::NOT_RECORD, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - sampling_result = s1.ShouldSample(&c2, trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - sampling_result = s1.ShouldSample(&c3, trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); - - sampling_result = s1.ShouldSample(&c4, trace_id, "", span_kind, view); - - ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); - ASSERT_EQ(nullptr, sampling_result.attributes); -} - -TEST(ProbabilitySampler, ProbabilitySamplerHalf) -{ - double probability = 0.5; - int iterations = 100000, expected_count = iterations * probability, variance = iterations * 0.01; - - SpanContext c(true, true); - ProbabilitySampler s(probability); - - int actual_count = RunShouldSampleCountDecision(c, s, iterations); - - ASSERT_TRUE(actual_count < (expected_count + variance)); - ASSERT_TRUE(actual_count > (expected_count - variance)); -} - -TEST(ProbabilitySampler, ProbabilitySamplerOnePercent) -{ - double probability = 0.01; - int iterations = 100000, expected_count = iterations * probability, variance = iterations * 0.01; - - SpanContext c(true, true); - ProbabilitySampler s(probability); - - int actual_count = RunShouldSampleCountDecision(c, s, iterations); - - ASSERT_TRUE(actual_count < (expected_count + variance)); - ASSERT_TRUE(actual_count > (expected_count - variance)); -} - -TEST(ProbabilitySampler, ProbabilitySamplerAll) -{ - double probability = 1.0; - int iterations = 100000, expected_count = iterations * probability; - - SpanContext c(true, true); - ProbabilitySampler s(probability); - - int actual_count = RunShouldSampleCountDecision(c, s, iterations); - - ASSERT_EQ(actual_count, expected_count); -} - -TEST(ProbabilitySampler, ProbabilitySamplerNone) -{ - double probability = 0.0; - int iterations = 100000, expected_count = iterations * probability; - - SpanContext c(true, true); - ProbabilitySampler s(probability); - - int actual_count = RunShouldSampleCountDecision(c, s, iterations); - - ASSERT_EQ(actual_count, expected_count); -} - -TEST(ProbabilitySampler, GetDescription) -{ - ProbabilitySampler s1(0.01); - ASSERT_EQ("ProbabilitySampler{0.010000}", s1.GetDescription()); - - ProbabilitySampler s2(0.00); - ASSERT_EQ("ProbabilitySampler{0.000000}", s2.GetDescription()); - - ProbabilitySampler s3(1.00); - ASSERT_EQ("ProbabilitySampler{1.000000}", s3.GetDescription()); - - ProbabilitySampler s4(0.102030405); - ASSERT_EQ("ProbabilitySampler{0.102030}", s4.GetDescription()); - - ProbabilitySampler s5(3.00); - ASSERT_EQ("ProbabilitySampler{1.000000}", s5.GetDescription()); - - ProbabilitySampler s6(-3.00); - ASSERT_EQ("ProbabilitySampler{0.000000}", s6.GetDescription()); - - ProbabilitySampler s7(1.00000000001); - ASSERT_EQ("ProbabilitySampler{1.000000}", s7.GetDescription()); - - ProbabilitySampler s8(-1.00000000001); - ASSERT_EQ("ProbabilitySampler{0.000000}", s8.GetDescription()); - - ProbabilitySampler s9(0.50); - ASSERT_EQ("ProbabilitySampler{0.500000}", s9.GetDescription()); -} diff --git a/sdk/test/trace/sampler_benchmark.cc b/sdk/test/trace/sampler_benchmark.cc index 2068846a00..f8d59a315b 100644 --- a/sdk/test/trace/sampler_benchmark.cc +++ b/sdk/test/trace/sampler_benchmark.cc @@ -1,9 +1,9 @@ -#include "opentelemetry/context/threadlocal_context.h" +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" #include "opentelemetry/sdk/trace/sampler.h" #include "opentelemetry/sdk/trace/samplers/always_off.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" -#include "opentelemetry/sdk/trace/samplers/parent_or_else.h" -#include "opentelemetry/sdk/trace/samplers/probability.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" +#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" #include "opentelemetry/sdk/trace/tracer.h" @@ -13,46 +13,9 @@ #include using namespace opentelemetry::sdk::trace; -namespace nostd = opentelemetry::nostd; -namespace common = opentelemetry::common; +using opentelemetry::exporter::memory::InMemorySpanExporter; using opentelemetry::trace::SpanContext; -/** - * A mock exporter that switches a flag once a valid recordable was received. - */ -class MockSpanExporter final : public SpanExporter -{ -public: - MockSpanExporter(std::shared_ptr>> spans_received) noexcept - : spans_received_(spans_received) - {} - - std::unique_ptr MakeRecordable() noexcept override - { - return std::unique_ptr(new SpanData); - } - - ExportResult Export(const nostd::span> &recordables) noexcept override - { - for (auto &recordable : recordables) - { - auto span = std::unique_ptr(static_cast(recordable.release())); - if (span != nullptr) - { - spans_received_->push_back(std::move(span)); - } - } - - return ExportResult::kSuccess; - } - - void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override - {} - -private: - std::shared_ptr>> spans_received_; -}; - namespace { // Sampler constructor used as a baseline to compare with other samplers @@ -75,23 +38,23 @@ void BM_AlwaysOnSamplerConstruction(benchmark::State &state) } BENCHMARK(BM_AlwaysOnSamplerConstruction); -void BM_ParentOrElseSamplerConstruction(benchmark::State &state) +void BM_ParentBasedSamplerConstruction(benchmark::State &state) { while (state.KeepRunning()) { - benchmark::DoNotOptimize(ParentOrElseSampler(std::make_shared())); + benchmark::DoNotOptimize(ParentBasedSampler(std::make_shared())); } } -BENCHMARK(BM_ParentOrElseSamplerConstruction); +BENCHMARK(BM_ParentBasedSamplerConstruction); -void BM_ProbabilitySamplerConstruction(benchmark::State &state) +void BM_TraceIdRatioBasedSamplerConstruction(benchmark::State &state) { while (state.KeepRunning()) { - benchmark::DoNotOptimize(ProbabilitySampler(0.01)); + benchmark::DoNotOptimize(TraceIdRatioBasedSampler(0.01)); } } -BENCHMARK(BM_ProbabilitySamplerConstruction); +BENCHMARK(BM_TraceIdRatioBasedSamplerConstruction); // Sampler Helper Function void BenchmarkShouldSampler(Sampler &sampler, benchmark::State &state) @@ -101,11 +64,12 @@ void BenchmarkShouldSampler(Sampler &sampler, benchmark::State &state) using M = std::map; M m1 = {{}}; - opentelemetry::trace::KeyValueIterableView view{m1}; + opentelemetry::common::KeyValueIterableView view{m1}; while (state.KeepRunning()) { - benchmark::DoNotOptimize(sampler.ShouldSample(nullptr, trace_id, "", span_kind, view)); + auto invalid_ctx = SpanContext::GetInvalid(); + benchmark::DoNotOptimize(sampler.ShouldSample(invalid_ctx, trace_id, "", span_kind, view)); } } @@ -127,29 +91,26 @@ void BM_AlwaysOnSamplerShouldSample(benchmark::State &state) } BENCHMARK(BM_AlwaysOnSamplerShouldSample); -void BM_ParentOrElseSamplerShouldSample(benchmark::State &state) +void BM_ParentBasedSamplerShouldSample(benchmark::State &state) { - ParentOrElseSampler sampler(std::make_shared()); + ParentBasedSampler sampler(std::make_shared()); BenchmarkShouldSampler(sampler, state); } -BENCHMARK(BM_ParentOrElseSamplerShouldSample); +BENCHMARK(BM_ParentBasedSamplerShouldSample); -void BM_ProbabilitySamplerShouldSample(benchmark::State &state) +void BM_TraceIdRatioBasedSamplerShouldSample(benchmark::State &state) { - ProbabilitySampler sampler(0.01); + TraceIdRatioBasedSampler sampler(0.01); BenchmarkShouldSampler(sampler, state); } -BENCHMARK(BM_ProbabilitySamplerShouldSample); +BENCHMARK(BM_TraceIdRatioBasedSamplerShouldSample); // Sampler Helper Function void BenchmarkSpanCreation(std::shared_ptr sampler, benchmark::State &state) { - std::shared_ptr>> spans_received( - new std::vector>); - - std::unique_ptr exporter(new MockSpanExporter(spans_received)); + std::unique_ptr exporter(new InMemorySpanExporter()); auto processor = std::make_shared(std::move(exporter)); auto tracer = std::shared_ptr(new Tracer(processor, sampler)); diff --git a/sdk/test/trace/simple_processor_test.cc b/sdk/test/trace/simple_processor_test.cc index b1da619e64..7b5db6e1eb 100644 --- a/sdk/test/trace/simple_processor_test.cc +++ b/sdk/test/trace/simple_processor_test.cc @@ -1,67 +1,68 @@ #include "opentelemetry/sdk/trace/simple_processor.h" -#include "opentelemetry/context/threadlocal_context.h" +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" #include "opentelemetry/nostd/span.h" +#include "opentelemetry/sdk/trace/exporter.h" #include "opentelemetry/sdk/trace/span_data.h" #include using namespace opentelemetry::sdk::trace; +using opentelemetry::exporter::memory::InMemorySpanData; +using opentelemetry::exporter::memory::InMemorySpanExporter; -/** - * A mock exporter that switches a flag once a valid recordable was received. - */ -class MockSpanExporter final : public SpanExporter +TEST(SimpleProcessor, ToInMemorySpanExporter) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + SimpleSpanProcessor processor(std::move(exporter)); + + auto recordable = processor.MakeRecordable(); + + processor.OnStart(*recordable); + + ASSERT_EQ(0, span_data->GetSpans().size()); + + processor.OnEnd(std::move(recordable)); + + ASSERT_EQ(1, span_data->GetSpans().size()); + + processor.Shutdown(); +} + +// An exporter that does nothing but record (and give back ) the # of times Shutdown was called. +class RecordShutdownExporter final : public SpanExporter { public: - MockSpanExporter(std::shared_ptr span_received, - std::shared_ptr shutdown_called) noexcept - : span_received_(span_received), shutdown_called_(shutdown_called) - {} + RecordShutdownExporter(int *shutdown_counter) : shutdown_counter_(shutdown_counter) {} std::unique_ptr MakeRecordable() noexcept override { - return std::unique_ptr(new SpanData); + return std::unique_ptr(new SpanData()); } ExportResult Export( - const opentelemetry::nostd::span> &spans) noexcept override + const opentelemetry::nostd::span> &recordables) noexcept override { - for (auto &span : spans) - { - if (span != nullptr) - { - *span_received_ = true; - } - } - return ExportResult::kSuccess; } void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override { - *shutdown_called_ = true; + *shutdown_counter_ += 1; } private: - std::shared_ptr span_received_; - std::shared_ptr shutdown_called_; + int *shutdown_counter_; }; -TEST(SimpleSpanProcessor, ToMockSpanExporter) +TEST(SimpleSpanProcessor, ShutdownCalledOnce) { - std::shared_ptr span_received(new bool(false)); - std::shared_ptr shutdown_called(new bool(false)); - std::unique_ptr exporter(new MockSpanExporter(span_received, shutdown_called)); + int shutdowns = 0; + std::unique_ptr exporter(new RecordShutdownExporter(&shutdowns)); SimpleSpanProcessor processor(std::move(exporter)); - - auto recordable = processor.MakeRecordable(); - - processor.OnStart(*recordable); - ASSERT_FALSE(*span_received); - - processor.OnEnd(std::move(recordable)); - ASSERT_TRUE(*span_received); - + EXPECT_EQ(0, shutdowns); + processor.Shutdown(); + EXPECT_EQ(1, shutdowns); processor.Shutdown(); - ASSERT_TRUE(*shutdown_called); + EXPECT_EQ(1, shutdowns); } diff --git a/sdk/test/trace/span_data_test.cc b/sdk/test/trace/span_data_test.cc index a235d53938..36db80904c 100644 --- a/sdk/test/trace/span_data_test.cc +++ b/sdk/test/trace/span_data_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/sdk/trace/span_data.h" -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_id.h" @@ -66,7 +65,7 @@ TEST(SpanData, EventAttributes) data.AddEvent( "Test Event", std::chrono::system_clock::now(), - opentelemetry::trace::KeyValueIterableView>(attributes)); + opentelemetry::common::KeyValueIterableView>(attributes)); for (int i = 0; i < kNumAttributes; i++) { @@ -87,7 +86,7 @@ TEST(SpanData, Links) data.AddLink( opentelemetry::trace::SpanContext(false, false), - opentelemetry::trace::KeyValueIterableView>(attributes)); + opentelemetry::common::KeyValueIterableView>(attributes)); for (int i = 0; i < kNumAttributes; i++) { diff --git a/sdk/test/trace/trace_id_ratio_sampler_test.cc b/sdk/test/trace/trace_id_ratio_sampler_test.cc new file mode 100644 index 0000000000..f60c002804 --- /dev/null +++ b/sdk/test/trace/trace_id_ratio_sampler_test.cc @@ -0,0 +1,238 @@ +#include "opentelemetry/sdk/trace/samplers/trace_id_ratio.h" +#include "src/common/random.h" + +#include +#include +#include + +using opentelemetry::sdk::common::Random; +using opentelemetry::sdk::trace::Decision; +using opentelemetry::sdk::trace::TraceIdRatioBasedSampler; +namespace trace_api = opentelemetry::trace; + +namespace +{ +/* + * Helper function for running TraceIdBased sampler tests. + * Given a span context, sampler, and number of iterations this function + * will return the number of RECORD_AND_SAMPLE decision based on randomly + * generated traces. + * + * @param context a required valid span context + * @param sampler a required valid sampler + * @param iterations a requried number specifying the number of times to + * generate a random trace_id and check if it should sample using the provided + * provider and context + */ +int RunShouldSampleCountDecision(trace_api::SpanContext &context, + TraceIdRatioBasedSampler &sampler, + int iterations) +{ + int actual_count = 0; + + opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; + + using M = std::map; + M m1 = {{}}; + opentelemetry::common::KeyValueIterableView view{m1}; + + for (int i = 0; i < iterations; ++i) + { + uint8_t buf[16] = {0}; + Random::GenerateRandomBuffer(buf); + + opentelemetry::trace::TraceId trace_id(buf); + + auto result = sampler.ShouldSample(context, trace_id, "", span_kind, view); + if (result.decision == Decision::RECORD_AND_SAMPLE) + { + ++actual_count; + } + } + + return actual_count; +} +} // namespace + +TEST(TraceIdRatioBasedSampler, ShouldSampleWithoutContext) +{ + opentelemetry::trace::TraceId invalid_trace_id; + + opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; + + using M = std::map; + M m1 = {{}}; + opentelemetry::common::KeyValueIterableView view{m1}; + + TraceIdRatioBasedSampler s1(0.01); + + auto sampling_result = + s1.ShouldSample(trace_api::SpanContext::GetInvalid(), invalid_trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + constexpr uint8_t buf[] = {0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0}; + opentelemetry::trace::TraceId valid_trace_id(buf); + + sampling_result = + s1.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + TraceIdRatioBasedSampler s2(0.50000001); + + sampling_result = + s2.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + TraceIdRatioBasedSampler s3(0.49999999); + + sampling_result = + s3.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::DROP, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + TraceIdRatioBasedSampler s4(0.50000000); + + sampling_result = + s4.ShouldSample(trace_api::SpanContext::GetInvalid(), valid_trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); +} + +TEST(TraceIdRatioBasedSampler, ShouldSampleWithContext) +{ + + uint8_t trace_id_buffer[trace_api::TraceId::kSize] = {1}; + trace_api::TraceId trace_id{trace_id_buffer}; + uint8_t span_id_buffer[trace_api::SpanId::kSize] = {1}; + trace_api::SpanId span_id{span_id_buffer}; + + opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal; + trace_api::SpanContext c1(trace_id, span_id, trace_api::TraceFlags{0}, false); + trace_api::SpanContext c2(trace_id, span_id, trace_api::TraceFlags{1}, false); + trace_api::SpanContext c3(trace_id, span_id, trace_api::TraceFlags{0}, true); + trace_api::SpanContext c4(trace_id, span_id, trace_api::TraceFlags{1}, true); + + using M = std::map; + M m1 = {{}}; + opentelemetry::common::KeyValueIterableView view{m1}; + + TraceIdRatioBasedSampler s1(0.01); + + auto sampling_result = s1.ShouldSample(c1, trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + sampling_result = s1.ShouldSample(c2, trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + sampling_result = s1.ShouldSample(c3, trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); + + sampling_result = s1.ShouldSample(c4, trace_id, "", span_kind, view); + + ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result.decision); + ASSERT_EQ(nullptr, sampling_result.attributes); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerHalf) +{ + double ratio = 0.5; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + int variance = static_cast(iterations * 0.01); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_TRUE(actual_count < (expected_count + variance)); + ASSERT_TRUE(actual_count > (expected_count - variance)); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerOnePercent) +{ + double ratio = 0.01; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + int variance = static_cast(iterations * 0.01); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_TRUE(actual_count < (expected_count + variance)); + ASSERT_TRUE(actual_count > (expected_count - variance)); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerAll) +{ + double ratio = 1.0; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_EQ(actual_count, expected_count); +} + +TEST(TraceIdRatioBasedSampler, TraceIdRatioBasedSamplerNone) +{ + double ratio = 0.0; + int iterations = 100000; + int expected_count = static_cast(iterations * ratio); + + trace_api::SpanContext c(true, true); + TraceIdRatioBasedSampler s(ratio); + + int actual_count = RunShouldSampleCountDecision(c, s, iterations); + + ASSERT_EQ(actual_count, expected_count); +} + +TEST(TraceIdRatioBasedSampler, GetDescription) +{ + TraceIdRatioBasedSampler s1(0.01); + ASSERT_EQ("TraceIdRatioBasedSampler{0.010000}", s1.GetDescription()); + + TraceIdRatioBasedSampler s2(0.00); + ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s2.GetDescription()); + + TraceIdRatioBasedSampler s3(1.00); + ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s3.GetDescription()); + + TraceIdRatioBasedSampler s4(0.102030405); + ASSERT_EQ("TraceIdRatioBasedSampler{0.102030}", s4.GetDescription()); + + TraceIdRatioBasedSampler s5(3.00); + ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s5.GetDescription()); + + TraceIdRatioBasedSampler s6(-3.00); + ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s6.GetDescription()); + + TraceIdRatioBasedSampler s7(1.00000000001); + ASSERT_EQ("TraceIdRatioBasedSampler{1.000000}", s7.GetDescription()); + + TraceIdRatioBasedSampler s8(-1.00000000001); + ASSERT_EQ("TraceIdRatioBasedSampler{0.000000}", s8.GetDescription()); + + TraceIdRatioBasedSampler s9(0.50); + ASSERT_EQ("TraceIdRatioBasedSampler{0.500000}", s9.GetDescription()); +} diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index 5840e3f9e0..dca3bc3607 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -1,5 +1,4 @@ #include "opentelemetry/sdk/trace/tracer_provider.h" -#include "opentelemetry/context/threadlocal_context.h" #include "opentelemetry/sdk/trace/samplers/always_off.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" #include "opentelemetry/sdk/trace/simple_processor.h" diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index 891cce22d4..cc21a15b2f 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -1,8 +1,8 @@ #include "opentelemetry/sdk/trace/tracer.h" -#include "opentelemetry/context/threadlocal_context.h" +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" #include "opentelemetry/sdk/trace/samplers/always_off.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" -#include "opentelemetry/sdk/trace/samplers/parent_or_else.h" +#include "opentelemetry/sdk/trace/samplers/parent.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" @@ -11,10 +11,10 @@ using namespace opentelemetry::sdk::trace; using opentelemetry::core::SteadyTimestamp; using opentelemetry::core::SystemTimestamp; -namespace nostd = opentelemetry::nostd; -namespace common = opentelemetry::common; -namespace context = opentelemetry::context; -namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; +namespace common = opentelemetry::common; +using opentelemetry::exporter::memory::InMemorySpanData; +using opentelemetry::exporter::memory::InMemorySpanExporter; using opentelemetry::trace::SpanContext; /** @@ -23,11 +23,12 @@ using opentelemetry::trace::SpanContext; class MockSampler final : public Sampler { public: - SamplingResult ShouldSample(const SpanContext * /*parent_context*/, - trace_api::TraceId /*trace_id*/, - nostd::string_view /*name*/, - trace_api::SpanKind /*span_kind*/, - const trace_api::KeyValueIterable & /*attributes*/) noexcept override + SamplingResult ShouldSample( + const SpanContext & /*parent_context*/, + trace_api::TraceId /*trace_id*/, + nostd::string_view /*name*/, + trace_api::SpanKind /*span_kind*/, + const opentelemetry::common::KeyValueIterable & /*attributes*/) noexcept override { // Return two pairs of attributes. These attributes should be added to the // span attributes @@ -40,130 +41,94 @@ class MockSampler final : public Sampler nostd::string_view GetDescription() const noexcept override { return "MockSampler"; } }; -/** - * A mock exporter that switches a flag once a valid recordable was received. - */ -class MockSpanExporter final : public SpanExporter -{ -public: - MockSpanExporter(std::shared_ptr>> spans_received) noexcept - : spans_received_(spans_received) - {} - - std::unique_ptr MakeRecordable() noexcept override - { - return std::unique_ptr(new SpanData); - } - - ExportResult Export(const nostd::span> &recordables) noexcept override - { - for (auto &recordable : recordables) - { - auto span = std::unique_ptr(static_cast(recordable.release())); - if (span != nullptr) - { - spans_received_->push_back(std::move(span)); - } - } - - return ExportResult::kSuccess; - } - - void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override - {} - -private: - std::shared_ptr>> spans_received_; -}; - namespace { std::shared_ptr initTracer( - std::shared_ptr>> &received) + std::unique_ptr &&exporter) { - std::unique_ptr exporter(new MockSpanExporter(received)); auto processor = std::make_shared(std::move(exporter)); return std::shared_ptr(new Tracer(processor)); } std::shared_ptr initTracer( - std::shared_ptr>> &received, + std::unique_ptr &&exporter, std::shared_ptr sampler) { - std::unique_ptr exporter(new MockSpanExporter(received)); auto processor = std::make_shared(std::move(exporter)); return std::shared_ptr(new Tracer(processor, sampler)); } } // namespace -TEST(Tracer, ToMockSpanExporter) +TEST(Tracer, ToInMemorySpanExporter) { - std::shared_ptr>> spans_received( - new std::vector>); - auto tracer = initTracer(spans_received); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); auto span_first = tracer->StartSpan("span 1"); + auto scope_first = tracer->WithActiveSpan(span_first); auto span_second = tracer->StartSpan("span 2"); - ASSERT_EQ(0, spans_received->size()); + ASSERT_EQ(0, span_data->GetSpans().size()); span_second->End(); - ASSERT_EQ(1, spans_received->size()); - ASSERT_EQ("span 2", spans_received->at(0)->GetName()); - EXPECT_TRUE(spans_received->at(0)->GetTraceId().IsValid()); - EXPECT_TRUE(spans_received->at(0)->GetSpanId().IsValid()); - EXPECT_TRUE(spans_received->at(0)->GetParentSpanId().IsValid()); + auto span2 = span_data->GetSpans(); + ASSERT_EQ(1, span2.size()); + ASSERT_EQ("span 2", span2.at(0)->GetName()); + EXPECT_TRUE(span2.at(0)->GetTraceId().IsValid()); + EXPECT_TRUE(span2.at(0)->GetSpanId().IsValid()); + EXPECT_TRUE(span2.at(0)->GetParentSpanId().IsValid()); span_first->End(); - ASSERT_EQ(2, spans_received->size()); + + auto span1 = span_data->GetSpans(); + ASSERT_EQ(1, span1.size()); + ASSERT_EQ("span 1", span1.at(0)->GetName()); + EXPECT_TRUE(span1.at(0)->GetTraceId().IsValid()); + EXPECT_TRUE(span1.at(0)->GetSpanId().IsValid()); + EXPECT_FALSE(span1.at(0)->GetParentSpanId().IsValid()); // Verify trace and parent span id propagation - EXPECT_EQ(span_second->GetContext().trace_id(), span_first->GetContext().trace_id()); - EXPECT_EQ(spans_received->at(0)->GetTraceId(), spans_received->at(1)->GetTraceId()); - EXPECT_EQ(spans_received->at(0)->GetParentSpanId(), spans_received->at(1)->GetSpanId()); - - ASSERT_EQ("span 1", spans_received->at(1)->GetName()); - EXPECT_TRUE(spans_received->at(1)->GetTraceId().IsValid()); - EXPECT_TRUE(spans_received->at(1)->GetSpanId().IsValid()); - EXPECT_FALSE(spans_received->at(1)->GetParentSpanId().IsValid()); + EXPECT_EQ(span1.at(0)->GetTraceId(), span2.at(0)->GetTraceId()); + EXPECT_EQ(span2.at(0)->GetParentSpanId(), span1.at(0)->GetSpanId()); } TEST(Tracer, StartSpanSampleOn) { - // create a tracer with default AlwaysOn sampler. - std::shared_ptr>> spans_received( - new std::vector>); - auto tracer_on = initTracer(spans_received); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_on = initTracer(std::move(exporter)); tracer_on->StartSpan("span 1")->End(); - ASSERT_EQ(1, spans_received->size()); - auto &span_data = spans_received->at(0); - ASSERT_LT(std::chrono::nanoseconds(0), span_data->GetStartTime().time_since_epoch()); - ASSERT_LT(std::chrono::nanoseconds(0), span_data->GetDuration()); + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &cur_span_data = spans.at(0); + ASSERT_LT(std::chrono::nanoseconds(0), cur_span_data->GetStartTime().time_since_epoch()); + ASSERT_LT(std::chrono::nanoseconds(0), cur_span_data->GetDuration()); } TEST(Tracer, StartSpanSampleOff) { - std::shared_ptr>> spans_received( - new std::vector>); - // create a tracer with a custom AlwaysOff sampler. - auto tracer_off = initTracer(spans_received, std::make_shared()); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_off = initTracer(std::move(exporter), std::make_shared()); // This span will not be recorded. tracer_off->StartSpan("span 2")->End(); // The span doesn't write any span data because the sampling decision is alway - // NOT_RECORD. - ASSERT_EQ(0, spans_received->size()); + // DROP. + ASSERT_EQ(0, span_data->GetSpans().size()); } TEST(Tracer, StartSpanWithOptionsTime) { - std::shared_ptr>> spans_received( - new std::vector>); - auto tracer = initTracer(spans_received); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); opentelemetry::trace::StartSpanOptions start; start.start_system_time = SystemTimestamp(std::chrono::nanoseconds(300)); @@ -174,20 +139,19 @@ TEST(Tracer, StartSpanWithOptionsTime) tracer->StartSpan("span 1", start)->End(end); - ASSERT_EQ(1, spans_received->size()); + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); - auto &span_data = spans_received->at(0); - ASSERT_EQ(std::chrono::nanoseconds(300), span_data->GetStartTime().time_since_epoch()); - ASSERT_EQ(std::chrono::nanoseconds(30), span_data->GetDuration()); + auto &cur_span_data = spans.at(0); + ASSERT_EQ(std::chrono::nanoseconds(300), cur_span_data->GetStartTime().time_since_epoch()); + ASSERT_EQ(std::chrono::nanoseconds(30), cur_span_data->GetDuration()); } TEST(Tracer, StartSpanWithAttributes) { - - std::shared_ptr>> spans_received( - new std::vector>); - // The default tracer has empty sampling result attribute - auto tracer = initTracer(spans_received); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); // Start a span with all supported scalar attribute types. tracer @@ -203,8 +167,6 @@ TEST(Tracer, StartSpanWithAttributes) {"attr9", "string"}}) ->End(); - ASSERT_EQ(1, spans_received->size()); - // Start a span with all supported array attribute types. int listInt[] = {1, 2, 3}; unsigned int listUInt[] = {1, 2, 3}; @@ -228,47 +190,48 @@ TEST(Tracer, StartSpanWithAttributes) tracer->StartSpan("span 2", m)->End(); - ASSERT_EQ(2, spans_received->size()); - - auto &span_data = spans_received->at(0); - ASSERT_EQ(9, span_data->GetAttributes().size()); - ASSERT_EQ(314159, nostd::get(span_data->GetAttributes().at("attr1"))); - ASSERT_EQ(false, nostd::get(span_data->GetAttributes().at("attr2"))); - ASSERT_EQ(314159, nostd::get(span_data->GetAttributes().at("attr3"))); - ASSERT_EQ(-20, nostd::get(span_data->GetAttributes().at("attr4"))); - ASSERT_EQ(20, nostd::get(span_data->GetAttributes().at("attr5"))); - ASSERT_EQ(-20, nostd::get(span_data->GetAttributes().at("attr6"))); - ASSERT_EQ(20, nostd::get(span_data->GetAttributes().at("attr7"))); - ASSERT_EQ(3.1, nostd::get(span_data->GetAttributes().at("attr8"))); - ASSERT_EQ("string", nostd::get(span_data->GetAttributes().at("attr9"))); - - auto &span_data2 = spans_received->at(1); - ASSERT_EQ(9, span_data2->GetAttributes().size()); - ASSERT_EQ(std::vector({1, 2, 3}), - nostd::get>(span_data2->GetAttributes().at("attr1"))); - ASSERT_EQ(std::vector({1, 2, 3}), - nostd::get>(span_data2->GetAttributes().at("attr2"))); + auto spans = span_data->GetSpans(); + ASSERT_EQ(2, spans.size()); + + auto &cur_span_data = spans.at(0); + ASSERT_EQ(9, cur_span_data->GetAttributes().size()); + ASSERT_EQ(314159, nostd::get(cur_span_data->GetAttributes().at("attr1"))); + ASSERT_EQ(false, nostd::get(cur_span_data->GetAttributes().at("attr2"))); + ASSERT_EQ(314159, nostd::get(cur_span_data->GetAttributes().at("attr3"))); + ASSERT_EQ(-20, nostd::get(cur_span_data->GetAttributes().at("attr4"))); + ASSERT_EQ(20, nostd::get(cur_span_data->GetAttributes().at("attr5"))); + ASSERT_EQ(-20, nostd::get(cur_span_data->GetAttributes().at("attr6"))); + ASSERT_EQ(20, nostd::get(cur_span_data->GetAttributes().at("attr7"))); + ASSERT_EQ(3.1, nostd::get(cur_span_data->GetAttributes().at("attr8"))); + ASSERT_EQ("string", nostd::get(cur_span_data->GetAttributes().at("attr9"))); + + auto &cur_span_data2 = spans.at(1); + ASSERT_EQ(9, cur_span_data2->GetAttributes().size()); + ASSERT_EQ(std::vector({1, 2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr1"))); + ASSERT_EQ(std::vector({1, 2, 3}), + nostd::get>(cur_span_data2->GetAttributes().at("attr2"))); ASSERT_EQ(std::vector({1, -2, 3}), - nostd::get>(span_data2->GetAttributes().at("attr3"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr3"))); ASSERT_EQ(std::vector({1, 2, 3}), - nostd::get>(span_data2->GetAttributes().at("attr4"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr4"))); ASSERT_EQ(std::vector({1, -2, 3}), - nostd::get>(span_data2->GetAttributes().at("attr5"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr5"))); ASSERT_EQ(std::vector({1, 2, 3}), - nostd::get>(span_data2->GetAttributes().at("attr6"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr6"))); ASSERT_EQ(std::vector({1.1, 2.1, 3.1}), - nostd::get>(span_data2->GetAttributes().at("attr7"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr7"))); ASSERT_EQ(std::vector({true, false}), - nostd::get>(span_data2->GetAttributes().at("attr8"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr8"))); ASSERT_EQ(std::vector({"a", "b"}), - nostd::get>(span_data2->GetAttributes().at("attr9"))); + nostd::get>(cur_span_data2->GetAttributes().at("attr9"))); } TEST(Tracer, StartSpanWithAttributesCopy) { - std::shared_ptr>> spans_received( - new std::vector>); - auto tracer = initTracer(spans_received); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); { std::unique_ptr> numbers(new std::vector); @@ -289,18 +252,19 @@ TEST(Tracer, StartSpanWithAttributesCopy) ->End(); } - ASSERT_EQ(1, spans_received->size()); + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); - auto &span_data = spans_received->at(0); - ASSERT_EQ(2, span_data->GetAttributes().size()); + auto &cur_span_data = spans.at(0); + ASSERT_EQ(2, cur_span_data->GetAttributes().size()); - auto numbers = nostd::get>(span_data->GetAttributes().at("attr1")); + auto numbers = nostd::get>(cur_span_data->GetAttributes().at("attr1")); ASSERT_EQ(3, numbers.size()); ASSERT_EQ(1, numbers[0]); ASSERT_EQ(2, numbers[1]); ASSERT_EQ(3, numbers[2]); - auto strings = nostd::get>(span_data->GetAttributes().at("attr2")); + auto strings = nostd::get>(cur_span_data->GetAttributes().at("attr2")); ASSERT_EQ(3, strings.size()); ASSERT_EQ("a", strings[0]); ASSERT_EQ("b", strings[1]); @@ -327,26 +291,52 @@ TEST(Tracer, GetSampler) TEST(Tracer, SpanSetAttribute) { - std::shared_ptr>> spans_received( - new std::vector>); - auto tracer = initTracer(spans_received); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); auto span = tracer->StartSpan("span 1"); span->SetAttribute("abc", 3.1); span->End(); - ASSERT_EQ(1, spans_received->size()); - auto &span_data = spans_received->at(0); - ASSERT_EQ(3.1, nostd::get(span_data->GetAttributes().at("abc"))); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + auto &cur_span_data = spans.at(0); + ASSERT_EQ(3.1, nostd::get(cur_span_data->GetAttributes().at("abc"))); } -TEST(Tracer, TestAlwaysOnSampler) +TEST(Tracer, SpanSetEvents) { - std::shared_ptr>> spans_received( - new std::vector>); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); - auto tracer_on = initTracer(spans_received); + auto span = tracer->StartSpan("span 1"); + span->AddEvent("event 1"); + span->AddEvent("event 2", std::chrono::system_clock::now()); + span->AddEvent("event 3", std::chrono::system_clock::now(), {{"attr1", 1}}); + span->End(); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + + auto &span_data_events = spans.at(0)->GetEvents(); + ASSERT_EQ(3, span_data_events.size()); + ASSERT_EQ("event 1", span_data_events[0].GetName()); + ASSERT_EQ("event 2", span_data_events[1].GetName()); + ASSERT_EQ("event 3", span_data_events[2].GetName()); + ASSERT_EQ(0, span_data_events[0].GetAttributes().size()); + ASSERT_EQ(0, span_data_events[1].GetAttributes().size()); + ASSERT_EQ(1, span_data_events[2].GetAttributes().size()); +} + +TEST(Tracer, TestAlwaysOnSampler) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_on = initTracer(std::move(exporter)); // Testing AlwaysOn sampler. // Create two spans for each tracer. Check the exported result. @@ -354,16 +344,18 @@ TEST(Tracer, TestAlwaysOnSampler) auto span_on_2 = tracer_on->StartSpan("span 2"); span_on_2->End(); span_on_1->End(); - ASSERT_EQ(2, spans_received->size()); - ASSERT_EQ("span 2", spans_received->at(0)->GetName()); // span 2 ends first. - ASSERT_EQ("span 1", spans_received->at(1)->GetName()); + + auto spans = span_data->GetSpans(); + ASSERT_EQ(2, spans.size()); + ASSERT_EQ("span 2", spans.at(0)->GetName()); // span 2 ends first. + ASSERT_EQ("span 1", spans.at(1)->GetName()); } TEST(Tracer, TestAlwaysOffSampler) { - std::shared_ptr>> spans_received_off( - new std::vector>); - auto tracer_off = initTracer(spans_received_off, std::make_shared()); + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer_off = initTracer(std::move(exporter), std::make_shared()); auto span_off_1 = tracer_off->StartSpan("span 1"); auto span_off_2 = tracer_off->StartSpan("span 2"); @@ -373,19 +365,18 @@ TEST(Tracer, TestAlwaysOffSampler) span_off_1->End(); // The tracer export nothing with an AlwaysOff sampler - ASSERT_EQ(0, spans_received_off->size()); + ASSERT_EQ(0, span_data->GetSpans().size()); } -TEST(Tracer, TestParentOrElseSampler) +TEST(Tracer, TestParentBasedSampler) { - std::shared_ptr>> spans_received_parent_on( - new std::vector>); - // Current ShouldSample always pass an empty ParentContext, // so this sampler will work as an AlwaysOnSampler. + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data_parent_on = exporter->GetData(); auto tracer_parent_on = - initTracer(spans_received_parent_on, - std::make_shared(std::make_shared())); + initTracer(std::move(exporter), + std::make_shared(std::make_shared())); auto span_parent_on_1 = tracer_parent_on->StartSpan("span 1"); auto span_parent_on_2 = tracer_parent_on->StartSpan("span 2"); @@ -394,18 +385,19 @@ TEST(Tracer, TestParentOrElseSampler) span_parent_on_2->End(); span_parent_on_1->End(); - ASSERT_EQ(2, spans_received_parent_on->size()); - ASSERT_EQ("span 2", spans_received_parent_on->at(0)->GetName()); - ASSERT_EQ("span 1", spans_received_parent_on->at(1)->GetName()); - std::shared_ptr>> spans_received_parent_off( - new std::vector>); + auto spans = span_data_parent_on->GetSpans(); + ASSERT_EQ(2, spans.size()); + ASSERT_EQ("span 2", spans.at(0)->GetName()); + ASSERT_EQ("span 1", spans.at(1)->GetName()); // Current ShouldSample always pass an empty ParentContext, // so this sampler will work as an AlwaysOnSampler. + std::unique_ptr exporter2(new InMemorySpanExporter()); + std::shared_ptr span_data_parent_off = exporter2->GetData(); auto tracer_parent_off = - initTracer(spans_received_parent_off, - std::make_shared(std::make_shared())); + initTracer(std::move(exporter2), + std::make_shared(std::make_shared())); auto span_parent_off_1 = tracer_parent_off->StartSpan("span 1"); auto span_parent_off_2 = tracer_parent_off->StartSpan("span 2"); @@ -414,30 +406,75 @@ TEST(Tracer, TestParentOrElseSampler) span_parent_off_1->End(); span_parent_off_2->End(); - ASSERT_EQ(0, spans_received_parent_off->size()); + ASSERT_EQ(0, span_data_parent_off->GetSpans().size()); } -TEST(Tracer, StartSpanUpdatesRuntimeContext) +TEST(Tracer, WithActiveSpan) { + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto spans = span_data.get()->GetSpans(); - std::shared_ptr>> spans_received( - new std::vector>); - auto tracer = initTracer(spans_received); + ASSERT_EQ(0, spans.size()); - auto span_first = tracer->StartSpan("span 1"); - auto span_second = tracer->StartSpan("span 2"); + { + auto span_first = tracer->StartSpan("span 1"); + auto scope_first = tracer->WithActiveSpan(span_first); - EXPECT_EQ(0, spans_received->size()); + { + auto span_second = tracer->StartSpan("span 2"); + auto scope_second = tracer->WithActiveSpan(span_second); - nostd::get>( - context::RuntimeContext::GetCurrent().GetValue(SpanKey)) - ->End(); - EXPECT_EQ(1, spans_received->size()); - EXPECT_EQ("span 2", spans_received->at(0)->GetName()); + spans = span_data->GetSpans(); + ASSERT_EQ(0, spans.size()); - nostd::get>( - context::RuntimeContext::GetCurrent().GetValue(SpanKey)) - ->End(); - EXPECT_EQ(2, spans_received->size()); - EXPECT_EQ("span 1", spans_received->at(1)->GetName()); + span_second->End(); + } + + spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + EXPECT_EQ("span 2", spans.at(0)->GetName()); + + span_first->End(); + } + + spans = span_data->GetSpans(); + ASSERT_EQ(1, spans.size()); + EXPECT_EQ("span 1", spans.at(0)->GetName()); +} + +TEST(Tracer, ExpectParent) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto spans = span_data.get()->GetSpans(); + + ASSERT_EQ(0, spans.size()); + + auto span_first = tracer->StartSpan("span 1"); + + trace_api::StartSpanOptions options; + options.parent = span_first->GetContext(); + auto span_second = tracer->StartSpan("span 2", options); + + options.parent = span_second->GetContext(); + auto span_third = tracer->StartSpan("span 3", options); + + span_third->End(); + span_second->End(); + span_first->End(); + + spans = span_data->GetSpans(); + ASSERT_EQ(3, spans.size()); + auto spandata_first = std::move(spans.at(2)); + auto spandata_second = std::move(spans.at(1)); + auto spandata_third = std::move(spans.at(0)); + EXPECT_EQ("span 1", spandata_first->GetName()); + EXPECT_EQ("span 2", spandata_second->GetName()); + EXPECT_EQ("span 3", spandata_third->GetName()); + + EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId()); + EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId()); } diff --git a/third_party/benchmark b/third_party/benchmark new file mode 160000 index 0000000000..4475ff6b8a --- /dev/null +++ b/third_party/benchmark @@ -0,0 +1 @@ +Subproject commit 4475ff6b8a7a4077d7492b76ef5278a3dc53a2e4 diff --git a/third_party/googletest b/third_party/googletest new file mode 160000 index 0000000000..13a433a94d --- /dev/null +++ b/third_party/googletest @@ -0,0 +1 @@ +Subproject commit 13a433a94dd9c7e55907d7a9b75f44ff82f309eb diff --git a/third_party/ms-gsl b/third_party/ms-gsl new file mode 160000 index 0000000000..63379b7935 --- /dev/null +++ b/third_party/ms-gsl @@ -0,0 +1 @@ +Subproject commit 63379b7935e41b19a006227910d03dd037a7aa6c diff --git a/tools/build-all.sh b/tools/build-all.sh new file mode 100755 index 0000000000..8ebedd8983 --- /dev/null +++ b/tools/build-all.sh @@ -0,0 +1,6 @@ +#!/bin/sh +cd .. +mkdir -p out +cd out +cmake .. +make diff --git a/tools/build-benchmark.cmd b/tools/build-benchmark.cmd new file mode 100644 index 0000000000..39b9f180d9 --- /dev/null +++ b/tools/build-benchmark.cmd @@ -0,0 +1,35 @@ +@echo off +set VS_TOOLS_VERSION=vs2019 +set CMAKE_GEN="Visual Studio 16 2019" +echo Building Google Benchmark (test only dependency)... +@setlocal ENABLEEXTENSIONS + +echo Auto-detecting Visual Studio version... +call "%~dp0\vcvars.cmd" + +pushd "%~dp0\.." +set "ROOT=%CD%" + +set MAXCPUCOUNT=%NUMBER_OF_PROCESSORS% +set platform= + +if not exist "%ROOT%\third_party\benchmark\" ( + echo "Google Benchmark library is not available, skipping benchmark build." + call skip_the_build +) + +cd "%ROOT%\third_party\benchmark\" +set "GOOGLETEST_PATH=%ROOT%\third_party\googletest" +if not exist "build" ( + mkdir build +) +cd build + +REM By default we generate the project for the older Visual Studio 2017 even if we have newer version installed +cmake ../ -G %CMAKE_GEN% -Ax64 -DBENCHMARK_ENABLE_TESTING=OFF +set SOLUTION=%ROOT%\third_party\benchmark\build\benchmark.sln +msbuild %SOLUTION% /maxcpucount:%MAXCPUCOUNT% /p:Configuration=Debug /p:Platform=x64 +msbuild %SOLUTION% /maxcpucount:%MAXCPUCOUNT% /p:Configuration=Release /p:Platform=x64 +popd + +:skip_the_build diff --git a/tools/build-gtest.sh b/tools/build-gtest.sh new file mode 100755 index 0000000000..d2573b885a --- /dev/null +++ b/tools/build-gtest.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +if [ "$1" == "ios" ]; then + IOS_BUILD="YES" + # Skip building tests on iOS as there is no way to run them + BUILD_TESTS="OFF" +else + IOS_BUILD="NO" + BUILD_TESTS="ON" +fi + +pushd `dirname $0`/.. +cd third_party/googletest +set -evx +env | sort +rm -rf build +mkdir -p build || true +cd build +cmake -Dgtest_build_samples=OFF \ + -Dgmock_build_samples=OFF \ + -Dgtest_build_tests=OFF \ + -Dgmock_build_tests=OFF \ + -DCMAKE_CXX_FLAGS="-fPIC $CXX_FLAGS" \ + -DBUILD_IOS=$IOS_BUILD \ + -DCMAKE_CXX_STANDARD=17 \ + .. +make +make install +popd diff --git a/tools/build-vs2015.cmd b/tools/build-vs2015.cmd new file mode 100644 index 0000000000..a80ca66409 --- /dev/null +++ b/tools/build-vs2015.cmd @@ -0,0 +1,6 @@ +set "VS_TOOLS_VERSION=vs2015" +REM Ref. https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2014%202015.html +set "CMAKE_GEN=Visual Studio 14 2015 Win64" +cd %~dp0 +call setup-buildtools.cmd +call build.cmd -DWITH_ABSEIL:BOOL=ON diff --git a/tools/build-vs2017.cmd b/tools/build-vs2017.cmd new file mode 100644 index 0000000000..be4322892c --- /dev/null +++ b/tools/build-vs2017.cmd @@ -0,0 +1,5 @@ +set "VS_TOOLS_VERSION=vs2017" +set "CMAKE_GEN=Visual Studio 15 2017 Win64" +cd %~dp0 +call setup-buildtools.cmd +call build.cmd diff --git a/tools/build-vs2019.cmd b/tools/build-vs2019.cmd new file mode 100644 index 0000000000..554c291a31 --- /dev/null +++ b/tools/build-vs2019.cmd @@ -0,0 +1,5 @@ +set "VS_TOOLS_VERSION=vs2019" +set "CMAKE_GEN=Visual Studio 16 2019" +cd %~dp0 +call setup-buildtools.cmd +call build.cmd diff --git a/tools/build.cmd b/tools/build.cmd new file mode 100644 index 0000000000..e49bc14289 --- /dev/null +++ b/tools/build.cmd @@ -0,0 +1,74 @@ +@echo off +REM Currently we require Visual Studio 2019 for C++20 build targeting Release/x64. +REM +REM TODO: allow specifying compiler version as argument. +REM +REM Supported versions for nostd build: +REM - vs2015 (C++11) +REM - vs2017 (C++14) +REM - vs2019 (C++20) +REM +REM Supported versions for STL build: +REM - vs2017 (C++14) +REM - vs2019 (C++20) +REM + +if "%VS_TOOLS_VERSION%" == "" set "VS_TOOLS_VERSION=vs2019" +if "%CMAKE_GEN%" == "" set "CMAKE_GEN=Visual Studio 16 2019" + +pushd %~dp0 +setlocal enableextensions +setlocal enabledelayedexpansion +set "ROOT=%~dp0\.." + +REM Use preinstalled vcpkg if installed or use our local +if "%VCPKG_INSTALLATION_ROOT%" neq "" ( + set "VCPKG_CMAKE=%VCPKG_INSTALLATION_ROOT%\scripts\buildsystems\vcpkg.cmake" +) else ( + set "VCPKG_CMAKE=%CD%\vcpkg\scripts\buildsystems\vcpkg.cmake" +) + +REM ******************************************************************** +REM Setup compiler environment +REM ******************************************************************** +call "%~dp0\vcvars.cmd" + + +REM ******************************************************************** +REM Use cmake +REM ******************************************************************** +set "PATH=%PATH%;C:\Program Files\CMake\bin\" + +REM ******************************************************************** +REM Build with nostd implementation +REM ******************************************************************** +set CONFIG=-DWITH_STL:BOOL=OFF %* +set "OUTDIR=%ROOT%\out\%VS_TOOLS_VERSION%\nostd" +call :build_config + +REM ******************************************************************** +REM Build with STL implementation - only for vs2017+ +REM ******************************************************************** +if "%VS_TOOLS_VERSION%" neq "vs2015" ( + set CONFIG=-DWITH_STL:BOOL=ON + set "OUTDIR=%ROOT%\out\%VS_TOOLS_VERSION%\stl" + call :build_config +) + +popd +REM ******************************************************************** + + +REM ******************************************************************** +REM Function that allows to build given build configuration +REM ******************************************************************** +:build_config +REM TODO: consider rmdir for clean builds +if not exist "%OUTDIR%" mkdir "%OUTDIR%" +cd "%OUTDIR%" +REM Optional platform specification parameter below: -Ax64 +cmake %ROOT% -G "%CMAKE_GEN%" -DCMAKE_TOOLCHAIN_FILE="%VCPKG_CMAKE%" %CONFIG% +set "SOLUTION=%OUTDIR%\opentelemetry-cpp.sln" +REM TODO: allow building [Release|Debug]x[Win32|x64|ARM|ARM64] +msbuild "%SOLUTION%" /p:Configuration=Release /p:Platform=x64 /p:VcpkgEnabled=true +exit /b 0 diff --git a/tools/build.sh b/tools/build.sh new file mode 100755 index 0000000000..fda44d9208 --- /dev/null +++ b/tools/build.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +export PATH=/usr/local/bin:$PATH + +DIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +WORKSPACE_ROOT=$DIR/.. +cd $WORKSPACE_ROOT +echo "Current directory is `pwd`" + +export NOROOT=$NOROOT + +if [ "$1" == "clean" ]; then + rm -f CMakeCache.txt *.cmake + rm -rf out + rm -rf .buildtools +# make clean +fi + +if [ "$1" == "noroot" ] || [ "$2" == "noroot" ]; then +export NOROOT=true +fi + +if [ "$1" == "release" ] || [ "$2" == "release" ]; then +BUILD_TYPE="release" +else +BUILD_TYPE="debug" +fi + +# Set target MacOS minver +export MACOSX_DEPLOYMENT_TARGET=10.10 + +# Install build tools +FILE=.buildtools +OS_NAME=`uname -a` +if [ ! -f $FILE ]; then +case "$OS_NAME" in + *Darwin*) tools/setup-buildtools-mac.sh ;; + *Linux*) [[ -z "$NOROOT" ]] && sudo tools/setup-buildtools.sh || echo "No root: skipping build tools installation." ;; + *) echo "WARNING: unsupported OS $OS_NAME , skipping build tools installation.." +esac +# Assume that the build tools have been successfully installed +echo > $FILE +fi + +# TODO: fix compiler-version reporting +if [ -f /usr/bin/gcc ]; then +echo "gcc version: `gcc --version`" +COMPILER_NAME=gcc-`gcc -dumpversion` +fi + +if [ -f /usr/bin/clang ]; then +echo "clang version: `clang --version`" +COMPILER_NAME=clang-`clang -dumpversion` +fi + +# TODO: do we need to build and install Google Test? +# tools/build-gtest.sh + +# +# Do the build for both configurations: WITH_STL=OFF and WITH_STL=ON +# +function build_configuration { + BUILD_CONFIG=$1 + BUILD_OPTIONS=$2 + + echo "Build configuration: $BUILD_CONFIG" + cd $WORKSPACE_ROOT + OUTDIR=out/$COMPILER_NAME/$BUILD_CONFIG + mkdir -p $OUTDIR + cmake -B $OUTDIR $BUILD_OPTIONS + cd $OUTDIR + make +} + +build_configuration nostd '-DWITH_STL:BOOL=OFF' +build_configuration stl '-DWITH_STL:BOOL=ON' diff --git a/tools/download.cmd b/tools/download.cmd new file mode 100644 index 0000000000..97f546863b --- /dev/null +++ b/tools/download.cmd @@ -0,0 +1,3 @@ +@REM This script allows to download a file to local machine. First argument is URL +set "PATH=C:\Windows;C:\Windows\System32;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\bin" +@powershell -File Download.ps1 %1 diff --git a/tools/download.ps1 b/tools/download.ps1 new file mode 100644 index 0000000000..c759601aea --- /dev/null +++ b/tools/download.ps1 @@ -0,0 +1,4 @@ +$url=$args[0] +$arr=$args[0].Split("/") +$fileName=$arr[$arr.Count-1] +Invoke-WebRequest -Uri $url -OutFile $fileName -UseBasicParsing diff --git a/tools/format.sh b/tools/format.sh index 7d5249d5e3..6fc4a4ae42 100755 --- a/tools/format.sh +++ b/tools/format.sh @@ -6,7 +6,7 @@ fi set -e -FIND="find . -name .git -prune -o -name _deps -prune -o -name .build -prune -o" +FIND="find . -name third_party -prune -o -name tools -prune -o -name .git -prune -o -name _deps -prune -o -name .build -prune -o" # GNU syntax. SED=(sed -i) diff --git a/tools/git-cl.sh b/tools/git-cl.sh old mode 100644 new mode 100755 diff --git a/tools/install-vs-addons.cmd b/tools/install-vs-addons.cmd new file mode 100644 index 0000000000..e4c866be33 --- /dev/null +++ b/tools/install-vs-addons.cmd @@ -0,0 +1,39 @@ +set "PATH=C:\Windows;C:\Windows\System32;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\bin" +cd %~dp0 +call powershell -File .\install_llvm-win64.ps1 + +REM Download Visual Studio LLVM extension required for clang build to succeed +call download.cmd https://llvmextensions.gallerycdn.vsassets.io/extensions/llvmextensions/llvm-toolchain/1.0.363769/1560930595399/llvm.vsix + +REM Install optional components required for ARM build - vs2017-BuildTools +IF EXIST "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\BuildTools" ( +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vs_installer.exe" ^ + -- modify --installPath "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\BuildTools" -q ^ + --add Microsoft.VisualStudio.Component.VC.ATL ^ + --add Microsoft.VisualStudio.Component.VC.ATL.ARM ^ + --add Microsoft.VisualStudio.Component.VC.ATL.ARM64 +"%ProgramFiles(x86)%\Microsoft Visual Studio\2017\BuildTools\Common7\IDE\VSIXInstaller.exe" /q /a llvm.vsix +) + +REM Install optional components required for ARM build - vs2017-Enterprise +IF EXIST "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise" ( +"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vs_installer.exe" ^ + -- modify --installPath "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise" -q ^ + --add Microsoft.VisualStudio.Component.VC.ATL ^ + --add Microsoft.VisualStudio.Component.VC.ATL.ARM ^ + --add Microsoft.VisualStudio.Component.VC.ATL.ARM64 +"%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\VSIXInstaller.exe" /q /a llvm.vsix +) + +REM Install optional components required for ARM build - vs2019-Enterprise +IF EXIST %ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise ( +"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" ^ + -- modify --installPath "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise" -q ^ + --add Microsoft.VisualStudio.Component.VC.ATL ^ + --add Microsoft.VisualStudio.Component.VC.ATL.ARM ^ + --add Microsoft.VisualStudio.Component.VC.ATL.ARM64 +"%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VSIXInstaller.exe" /q /a llvm.vsix +) + +REM Ignore failures if components have been already installed +EXIT /b 0 diff --git a/tools/install_llvm-win32.ps1 b/tools/install_llvm-win32.ps1 new file mode 100644 index 0000000000..89b697f400 --- /dev/null +++ b/tools/install_llvm-win32.ps1 @@ -0,0 +1,8 @@ +$llvmVersion = "10.0.0" +Write-Host "Installing LLVM $llvmVersion ..." -ForegroundColor Cyan +Write-Host "Downloading..." +$exePath = "$env:temp\LLVM-$llvmVersion-win32.exe" +(New-Object Net.WebClient).DownloadFile("https://github.com/llvm/llvm-project/releases/download/llvmorg-$llvmVersion/LLVM-$llvmVersion-win32.exe", $exePath) +Write-Host "Installing..." +cmd /c start /wait $exePath /S +Write-Host "Installed" -ForegroundColor Green diff --git a/tools/install_llvm-win64.ps1 b/tools/install_llvm-win64.ps1 new file mode 100644 index 0000000000..d4a2379fa2 --- /dev/null +++ b/tools/install_llvm-win64.ps1 @@ -0,0 +1,8 @@ +$llvmVersion = "10.0.0" +Write-Host "Installing LLVM $llvmVersion ..." -ForegroundColor Cyan +Write-Host "Downloading..." +$exePath = "$env:temp\LLVM-$llvmVersion-win64.exe" +(New-Object Net.WebClient).DownloadFile("https://github.com/llvm/llvm-project/releases/download/llvmorg-$llvmVersion/LLVM-$llvmVersion-win64.exe", $exePath) +Write-Host "Installing..." +cmd /c start /wait $exePath /S +Write-Host "Installed" -ForegroundColor Green diff --git a/tools/ports/benchmark/CONTROL b/tools/ports/benchmark/CONTROL new file mode 100644 index 0000000000..82e4b57e84 --- /dev/null +++ b/tools/ports/benchmark/CONTROL @@ -0,0 +1,5 @@ +Source: benchmark +Version: 1.5.1 +Homepage: https://github.com/google/benchmark +Description: A library to support the benchmarking of functions, similar to unit-tests. +Supports: !uwp \ No newline at end of file diff --git a/tools/ports/benchmark/portfile.cmake b/tools/ports/benchmark/portfile.cmake new file mode 100644 index 0000000000..06ff36d148 --- /dev/null +++ b/tools/ports/benchmark/portfile.cmake @@ -0,0 +1,49 @@ +if(VCPKG_CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + message(FATAL_ERROR "${PORT} does not currently support UWP") +endif() + +if (VCPKG_PLATFORM_TOOLSET STREQUAL "v140") + # set(CMAKE_C_COMPILER_WORKS 1) + # set(CMAKE_CXX_COMPILER_WORKS 1) + set(CMAKE_C_COMPILER cl.exe) + set(CMAKE_CXX_COMPILER cl.exe) + set(MSVC_TOOLSET_VERSION 140) + # set(VCPKG_VISUAL_STUDIO_PATH "C:\\Program Files (x86)\\Microsoft Visual Studio 14.0") + # set(VCPKG_PLATFORM_TOOLSET v140) +else() + # Make sure vs2019 compiled binaries are compat with vs2017 + set(VCPKG_CXX_FLAGS "/Zc:__cplusplus /d2FH4-") + set(VCPKG_C_FLAGS "/Zc:__cplusplus /d2FH4-") + set(PREFER PREFER_NINJA) +endif() + +include(vcpkg_common_functions) + +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO google/benchmark + HEAD_REF master +) + +vcpkg_configure_cmake( + SOURCE_PATH ${SOURCE_PATH} + ${PREFER} + OPTIONS + -DBENCHMARK_ENABLE_TESTING=OFF + -DCMAKE_DEBUG_POSTFIX=d +) + +vcpkg_install_cmake() + +vcpkg_copy_pdbs() + +vcpkg_fixup_cmake_targets(CONFIG_PATH lib/cmake/benchmark) + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) + +# Handle copyright +file(COPY ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/benchmark) +file(RENAME ${CURRENT_PACKAGES_DIR}/share/benchmark/LICENSE ${CURRENT_PACKAGES_DIR}/share/benchmark/copyright) diff --git a/tools/setup-buildtools-mac.sh b/tools/setup-buildtools-mac.sh old mode 100644 new mode 100755 index aee85a456f..ee14680409 --- a/tools/setup-buildtools-mac.sh +++ b/tools/setup-buildtools-mac.sh @@ -24,8 +24,10 @@ sudo chown -R $(whoami) /usr/local/var/homebrew sudo chown -R $(whoami) /usr/local/etc/bash_completion.d /usr/local/include /usr/local/lib/pkgconfig /usr/local/share/aclocal /usr/local/share/locale /usr/local/share/zsh /usr/local/share/zsh/site-functions /usr/local/var/homebrew/locks brew install cmake +brew install coreutils brew install wget brew install clang-format brew install google-benchmark brew tap nlohmann/json brew install nlohmann-json +./tools/build-gtest.sh diff --git a/tools/setup-buildtools.cmd b/tools/setup-buildtools.cmd new file mode 100644 index 0000000000..830c650c07 --- /dev/null +++ b/tools/setup-buildtools.cmd @@ -0,0 +1,50 @@ +@echo off +set "PATH=%PATH%;%~dp0;%~dp0\vcpkg" +pushd %~dp0 + +REM Fail if chocolatey is not installed +where /Q choco +if ERRORLEVEL 1 ( + echo This script requires chocolatey. Installation instructions: https://chocolatey.org/docs/installation + exit -1 +) + +REM Print current Visual Studio installations detected +where /Q vswhere +if ERRORLEVEL 0 ( + echo Visual Studio installations detected: + vswhere -property installationPath +) + +REM Install tools needed for building stuff +choco install -y cmake +choco install -y svn +choco install -y git +choco install -y llvm +choco install -y zip + +REM Try to autodetect Visual Studio +call "%~dp0\vcvars.cmd" +if "%TOOLS_VS_NOTFOUND%" == "1" ( + REM Cannot detect MSBuild path + REM TODO: no command line tools.. + REM TODO: use MSBuild from vswhere? +) + +where /Q vcpkg.exe +if ERRORLEVEL 1 ( + REM Build our own vcpkg from source + pushd .\vcpkg + call bootstrap-vcpkg.bat + popd +) + +REM Install it +vcpkg install gtest:x64-windows +vcpkg install --head --overlay-ports=%~dp0\ports benchmark:x64-windows +vcpkg install ms-gsl:x64-windows +vcpkg install nlohmann-json:x64-windows +vcpkg install abseil:x64-windows +vcpkg integrate install +popd +exit /b 0 diff --git a/tools/setup-buildtools.sh b/tools/setup-buildtools.sh old mode 100644 new mode 100755 index f118e16070..a5fe2c5f0c --- a/tools/setup-buildtools.sh +++ b/tools/setup-buildtools.sh @@ -22,13 +22,6 @@ yum -y install devtoolset-7-valgrind yum-config-manager --enable rhel-server-rhscl-7-rpms -if [ `gcc --version | grep 7` == "" ]; then -echo "*********************************************************" -echo "*** Please make sure you start the build with gcc-7 ***" -echo "*** > scl enable devtoolset-7 ./build.sh ***" -echo "*********************************************************" -exit 3 -fi if [ `cmake --version | grep 3` == "" ]; then yum -y remove cmake @@ -59,7 +52,13 @@ apt-get install -qq libsqlite3-dev #apt install libsqlite3-dev apt-get install -qq wget apt-get install -qq clang-format +apt-get install -qq gtest +apt-get install -qq libgtest-dev +apt-get install -qq libbenchmark-dev +apt-get install -qq nlohmann-json-dev fi ## Change owner from root to current dir owner chown -R `stat . -c %u:%g` * + +./tools/build-gtest.sh diff --git a/tools/setup-devenv.sh b/tools/setup-devenv.sh old mode 100644 new mode 100755 diff --git a/tools/vcpkg b/tools/vcpkg new file mode 160000 index 0000000000..ff1d20fd9a --- /dev/null +++ b/tools/vcpkg @@ -0,0 +1 @@ +Subproject commit ff1d20fd9a72d72d51a549c27b669cd6040fb7d9 diff --git a/tools/vcvars.cmd b/tools/vcvars.cmd new file mode 100644 index 0000000000..1b8215fcbb --- /dev/null +++ b/tools/vcvars.cmd @@ -0,0 +1,86 @@ +@echo off +REM +REM Make sure to enable the 'Visual C++ ATL' components for all platforms during the setup. +REM +REM This build script auto-detects and configures Visual Studio in the following order: +REM 1. Visual Studio 2017 Enterprise +REM 2. Visual Studio 2017 BuildTools +REM 3. Visual Studio 2019 Enterprise +REM 4. Visual Studio 2019 Community +REM 5. Visual Studio 2019 BuildTools +REM + +REM 1st parameter - Visual Studio version +if "%1" neq "" ( + goto %1 +) + +if "%VS_TOOLS_VERSION%" neq "" ( + goto %VS_TOOLS_VERSION% +) + +REM vs2017 Enterprise +:vs2017 +:vs2017_enterprise +set TOOLS_VS2017_ENTERPRISE="%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat" +if exist %TOOLS_VS2017_ENTERPRISE% ( + echo Building with vs2017 Enterprise... + call %TOOLS_VS2017_ENTERPRISE% + goto tools_configured +) + +REM vs2017 BuildTools +:vs2017_buildtools +set TOOLS_VS2017="%ProgramFiles(x86)%\Microsoft Visual Studio\2017\BuildTools\Common7\Tools\VsDevCmd.bat" +if exist %TOOLS_VS2017% ( + echo Building with vs2017 BuildTools... + call %TOOLS_VS2017% + goto tools_configured +) + +REM vs2019 Enterprise +:vs2019 +:vs2019_enterprise +set TOOLS_VS2019_ENTERPRISE="%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" +if exist %TOOLS_VS2019_ENTERPRISE% ( + echo Building with vs2019 Enterprise... + call %TOOLS_VS2019_ENTERPRISE% + goto tools_configured +) + +REM vs2019 Community +:vs2019_community +set TOOLS_VS2019_COMMUNITY="%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat" +if exist %TOOLS_VS2019_COMMUNITY% ( + echo Building with vs2019 Community... + call %TOOLS_VS2019_COMMUNITY% + goto tools_configured +) + +REM vs2019 BuildTools +:vs2019_buildtools +set TOOLS_VS2019="%ProgramFiles(x86)%\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" +if exist %TOOLS_VS2019% ( + echo Building with vs2019 BuildTools... + call %TOOLS_VS2019% + goto tools_configured +) + +REM vs2015 +:vs2015 +set TOOLS_VS2015="%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat" +if exist %TOOLS_VS2015% ( + echo Building with vs2015 BuildTools... + call %TOOLS_VS2015% + set "VCPKG_VISUAL_STUDIO_PATH=C:\Program Files (x86)\Microsoft Visual Studio 14.0" + set VCPKG_PLATFORM_TOOLSET=v140 + goto tools_configured +) + +echo WARNING:********************************************* +echo WARNING: cannot auto-detect Visual Studio version !!! +echo WARNING:********************************************* +set TOOLS_VS_NOTFOUND=1 +exit /b 0 + +:tools_configured