diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000000..1712df25da --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,72 @@ +name: OpenTelemetry-cpp benchmarks +on: + push: + branches: + - main + +permissions: + contents: write + deployments: write + +jobs: + benchmark: + name: Run OpenTelemetry-cpp benchmarks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Mount Bazel Cache + uses: actions/cache@v2 + env: + cache-name: bazel_cache + with: + path: /home/runner/.cache/bazel + key: bazel_benchmark + - name: setup + run: | + sudo ./ci/setup_cmake.sh + sudo ./ci/setup_ci_environment.sh + - name: Run benchmark + id: run_benchmarks + run: | + ci/do_ci.sh bazel.benchmark + mkdir -p benchmarks + mv api-benchmark_result.json benchmarks + mv sdk-benchmark_result.json benchmarks + mv exporters-benchmark_result.json benchmarks + - uses: actions/upload-artifact@master + with: + name: benchmark_results + path: benchmarks + store_benchmark: + needs: benchmark + strategy: + matrix: + components: ["api", "sdk", "exporters"] + name: Store benchmark result + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/download-artifact@master + with: + name: benchmark_results + path: benchmarks + - name: Print json files + id: print_json + run: | + cat benchmarks/* + - name: Push benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + name: OpenTelemetry-cpp ${{ matrix.components }} Benchmark + tool: 'googlecpp' + output-file-path: benchmarks/${{ matrix.components }}-benchmark_result.json + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '200%' + comment-on-alert: true + fail-on-alert: true + gh-pages-branch: gh-pages + benchmark-data-dir-path: benchmarks diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6db5f15af..6ac51974e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,6 +150,28 @@ jobs: - name: run tests run: ./ci/do_ci.sh bazel.test + bazel_with_abseil: + name: Bazel with external abseil + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - name: Mount Bazel Cache + uses: actions/cache@v2 + env: + cache-name: bazel_cache + with: + path: /home/runner/.cache/bazel + key: bazel_test + - name: setup + run: | + sudo ./ci/setup_thrift.sh dependencies_only + sudo ./ci/setup_ci_environment.sh + sudo ./ci/install_bazelisk.sh + - name: run tests + run: ./ci/do_ci.sh bazel.with_abseil + bazel_valgrind: name: Bazel valgrind runs-on: ubuntu-latest @@ -386,7 +408,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: install docfx - run: choco install docfx -y + run: choco install docfx -y --version=2.58.5 - name: run ./ci/docfx.cmd shell: cmd run: ./ci/docfx.cmd diff --git a/.gitignore b/.gitignore index a307af966e..8a55a4ca4a 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ /bazel-* /plugin /build + +tags diff --git a/CHANGELOG.md b/CHANGELOG.md index fbae450f6f..aaef8a94f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,12 @@ Increment the: ## [Unreleased] +* [API/SDK] Logger: Support for Instrumentation library ([#1128](https://github.com/open-telemetry/opentelemetry-cpp/pull/1128)) * [SDK] Add LogLevel to internal_log ([#1147](https://github.com/open-telemetry/opentelemetry-cpp/pull/1147)) +* [API/SDK] Logger: Propagating resources through LoggerProvider ([#1154](https://github.com/open-telemetry/opentelemetry-cpp/pull/1154)) +* [API]: Allow to use external abseil for bazel targets ([#1172](https://github.com/open-telemetry/opentelemetry-cpp/pull/1172)) -## [1.1.1] 2021-12- +## [1.1.1] 2021-12-20 * [SDK] Rename OTEL_CPP_GET_ATTR macro, and define it using fully qualified attr function ([#1140](https://github.com/open-telemetry/opentelemetry-cpp/pull/1140)) * [SDK] Default resource attributes and attributes in OTEL_RESOURCE_ATTRIBUTES are missing when using Otlp*LogExporter ([#1082](https://github.com/open-telemetry/opentelemetry-cpp/pull/1082)) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6833a3b0c..90d3fb50ba 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,7 +102,9 @@ else() ) endif() -option(WITH_STL "Whether to use Standard Library for C++latest features" OFF) +option(WITH_STL "Whether to use Standard Library for C++ latest features" OFF) +option(WITH_GSL + "Whether to use Guidelines Support Library for C++ latest features" OFF) option(WITH_ABSEIL "Whether to use Abseil for C++latest features" OFF) @@ -137,18 +139,6 @@ if(WITH_STL) # (absl::variant or std::variant) in variant unit test code is consistent with # the global project build definitions. add_definitions(-DHAVE_CPP_STDLIB) - add_definitions(-DHAVE_GSL) - - # Guidelines Support Library path. Used if we are not on not get C++20. - # - # TODO: respect WITH_ABSEIL as alternate implementation of std::span - find_package(Microsoft.GSL QUIET) - if(TARGET Microsoft.GSL::GSL) - list(APPEND CORE_RUNTIME_LIBS Microsoft.GSL::GSL) - else() - set(GSL_DIR third_party/ms-gsl) - include_directories(${GSL_DIR}/include) - endif() # Optimize for speed to reduce the hops if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") @@ -165,6 +155,20 @@ if(WITH_STL) endif() endif() +if(WITH_GSL) + add_definitions(-DHAVE_GSL) + + # Guidelines Support Library path. Used if we are not on not get C++20. + # + find_package(Microsoft.GSL QUIET) + if(TARGET Microsoft.GSL::GSL) + list(APPEND CORE_RUNTIME_LIBS Microsoft.GSL::GSL) + else() + set(GSL_DIR third_party/ms-gsl) + include_directories(${GSL_DIR}/include) + endif() +endif() + option(WITH_OTLP "Whether to include the OpenTelemetry Protocol in the SDK" OFF) option(WITH_ZIPKIN "Whether to include the Zipkin exporter in the SDK" OFF) diff --git a/api/BUILD b/api/BUILD index 58610c654e..80ff5ad926 100644 --- a/api/BUILD +++ b/api/BUILD @@ -12,11 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") + package(default_visibility = ["//visibility:public"]) +bool_flag( + name = "with_abseil", + build_setting_default = False, +) + cc_library( name = "api", hdrs = glob(["include/**/*.h"]), + defines = select({ + ":with_external_abseil": ["HAVE_ABSEIL"], + "//conditions:default": [], + }), strip_include_prefix = "include", tags = ["api"], + deps = select({ + ":with_external_abseil": [ + "@com_google_absl//absl/base", + "@com_google_absl//absl/types:variant", + ], + "//conditions:default": [], + }), +) + +config_setting( + name = "with_external_abseil", + flag_values = {":with_abseil": "true"}, ) diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 83f89db094..adea77ca2d 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -47,8 +47,7 @@ endif() if(WITH_STL) message("Building with standard library types...") - target_compile_definitions(opentelemetry_api INTERFACE HAVE_CPP_STDLIB - HAVE_GSL) + target_compile_definitions(opentelemetry_api INTERFACE HAVE_CPP_STDLIB) else() message("Building with nostd types...") endif() diff --git a/api/include/opentelemetry/common/macros.h b/api/include/opentelemetry/common/macros.h new file mode 100644 index 0000000000..8c2b6bd175 --- /dev/null +++ b/api/include/opentelemetry/common/macros.h @@ -0,0 +1,26 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "opentelemetry/version.h" + +/// \brief Declare variable as maybe unused +/// usage: +/// OPENTELEMETRY_MAYBE_UNUSED int a; +/// class OPENTELEMETRY_MAYBE_UNUSED a; +/// OPENTELEMETRY_MAYBE_UNUSED int a(); +/// +#if defined(__cplusplus) && __cplusplus >= 201703L +# define OPENTELEMETRY_MAYBE_UNUSED [[maybe_unused]] +#elif defined(__clang__) +# define OPENTELEMETRY_MAYBE_UNUSED __attribute__((unused)) +#elif defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))) +# define OPENTELEMETRY_MAYBE_UNUSED __attribute__((unused)) +#elif (defined(_MSC_VER) && _MSC_VER >= 1910) && (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define OPENTELEMETRY_MAYBE_UNUSED [[maybe_unused]] +#else +# define OPENTELEMETRY_MAYBE_UNUSED +#endif diff --git a/api/include/opentelemetry/common/spin_lock_mutex.h b/api/include/opentelemetry/common/spin_lock_mutex.h index b30fd3f44b..b35f54081a 100644 --- a/api/include/opentelemetry/common/spin_lock_mutex.h +++ b/api/include/opentelemetry/common/spin_lock_mutex.h @@ -13,6 +13,7 @@ # ifndef NOMINMAX # define NOMINMAX # endif +# define _WINSOCKAPI_ // stops including winsock.h # include #elif defined(__i386__) || defined(__x86_64__) # if defined(__clang__) diff --git a/api/include/opentelemetry/logs/logger.h b/api/include/opentelemetry/logs/logger.h index 5ce878d760..91e882717f 100644 --- a/api/include/opentelemetry/logs/logger.h +++ b/api/include/opentelemetry/logs/logger.h @@ -43,8 +43,6 @@ class Logger * @param severity the severity level of the log event. * @param name the name of the log event. * @param message the string message of the log (perhaps support std::fmt or fmt-lib format). - * @param resource the resources, stored as a 2D list of key/value pairs, that are associated - * with the log event. * @param attributes the attributes, stored as a 2D list of key/value pairs, that are associated * with the log event. * @param trace_id the trace id associated with the log event. @@ -57,13 +55,10 @@ class Logger /** * The base Log(...) method that all other Log(...) overloaded methods will eventually call, * in order to create a log record. - * - * Note this takes in a KeyValueIterable for the resource and attributes fields. */ virtual void Log(Severity severity, nostd::string_view name, nostd::string_view body, - const common::KeyValueIterable &resource, const common::KeyValueIterable &attributes, trace::TraceId trace_id, trace::SpanId span_id, @@ -74,31 +69,25 @@ class Logger /** * The secondary base Log(...) method that all other Log(...) overloaded methods except the one * above will eventually call, in order to create a log record. - * - * Note this takes in template types for the resource and attributes fields. */ template ::value> * = nullptr, - nostd::enable_if_t::value> * = nullptr> + nostd::enable_if_t::value> * = nullptr> void Log(Severity severity, nostd::string_view name, nostd::string_view body, - const T &resource, - const U &attributes, + const T &attributes, trace::TraceId trace_id, trace::SpanId span_id, trace::TraceFlags trace_flags, common::SystemTimestamp timestamp) noexcept { - Log(severity, name, body, common::KeyValueIterableView(resource), - common::KeyValueIterableView(attributes), trace_id, span_id, trace_flags, timestamp); + Log(severity, name, body, common::KeyValueIterableView(attributes), trace_id, span_id, + trace_flags, timestamp); } void Log(Severity severity, nostd::string_view name, nostd::string_view body, - std::initializer_list> resource, std::initializer_list> attributes, trace::TraceId trace_id, trace::SpanId span_id, @@ -106,8 +95,6 @@ class Logger common::SystemTimestamp timestamp) noexcept { return this->Log(severity, name, body, - nostd::span>{ - resource.begin(), resource.end()}, nostd::span>{ attributes.begin(), attributes.end()}, trace_id, span_id, trace_flags, timestamp); @@ -122,7 +109,7 @@ class Logger */ void Log(Severity severity, nostd::string_view message) noexcept { - this->Log(severity, "", message, {}, {}, {}, {}, {}, std::chrono::system_clock::now()); + this->Log(severity, "", message, {}, {}, {}, {}, std::chrono::system_clock::now()); } /** @@ -133,7 +120,7 @@ class Logger */ void Log(Severity severity, nostd::string_view name, nostd::string_view message) noexcept { - this->Log(severity, name, message, {}, {}, {}, {}, {}, std::chrono::system_clock::now()); + this->Log(severity, name, message, {}, {}, {}, {}, std::chrono::system_clock::now()); } /** @@ -145,8 +132,7 @@ class Logger nostd::enable_if_t::value> * = nullptr> void Log(Severity severity, const T &attributes) noexcept { - this->Log(severity, "", "", std::map{}, attributes, {}, {}, {}, - std::chrono::system_clock::now()); + this->Log(severity, "", "", attributes, {}, {}, {}, std::chrono::system_clock::now()); } /** @@ -159,8 +145,7 @@ class Logger nostd::enable_if_t::value> * = nullptr> void Log(Severity severity, nostd::string_view name, const T &attributes) noexcept { - this->Log(severity, name, "", std::map{}, attributes, {}, {}, {}, - std::chrono::system_clock::now()); + this->Log(severity, name, "", attributes, {}, {}, {}, std::chrono::system_clock::now()); } /** @@ -172,7 +157,7 @@ class Logger std::initializer_list> attributes) noexcept { - this->Log(severity, "", "", {}, attributes, {}, {}, {}, std::chrono::system_clock::now()); + this->Log(severity, "", "", attributes, {}, {}, {}, std::chrono::system_clock::now()); } /** @@ -186,7 +171,7 @@ class Logger std::initializer_list> attributes) noexcept { - this->Log(severity, name, "", {}, attributes, {}, {}, {}, std::chrono::system_clock::now()); + this->Log(severity, name, "", attributes, {}, {}, {}, std::chrono::system_clock::now()); } /** @@ -200,10 +185,7 @@ class Logger nostd::string_view name, common::KeyValueIterable &attributes) noexcept { - this->Log(severity, name, {}, - common::KeyValueIterableView< - std::initializer_list>>({}), - attributes, {}, {}, {}, std::chrono::system_clock::now()); + this->Log(severity, name, {}, attributes, {}, {}, {}, std::chrono::system_clock::now()); } /** Trace severity overloads **/ diff --git a/api/include/opentelemetry/logs/logger_provider.h b/api/include/opentelemetry/logs/logger_provider.h index 93ba683f94..5f1795c8ea 100644 --- a/api/include/opentelemetry/logs/logger_provider.h +++ b/api/include/opentelemetry/logs/logger_provider.h @@ -31,10 +31,16 @@ class LoggerProvider */ virtual nostd::shared_ptr GetLogger(nostd::string_view logger_name, - nostd::string_view options = "") = 0; + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") = 0; virtual nostd::shared_ptr GetLogger(nostd::string_view logger_name, - nostd::span args) = 0; + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") = 0; }; } // namespace logs OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/logs/noop.h b/api/include/opentelemetry/logs/noop.h index a61507093c..8186fb8c76 100644 --- a/api/include/opentelemetry/logs/noop.h +++ b/api/include/opentelemetry/logs/noop.h @@ -44,7 +44,6 @@ class NoopLogger final : public Logger void Log(Severity severity, nostd::string_view name, nostd::string_view body, - const common::KeyValueIterable &resource, const common::KeyValueIterable &attributes, trace::TraceId trace_id, trace::SpanId span_id, @@ -65,13 +64,19 @@ class NoopLoggerProvider final : public opentelemetry::logs::LoggerProvider {} nostd::shared_ptr GetLogger(nostd::string_view logger_name, - nostd::string_view options) override + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") override { return logger_; } nostd::shared_ptr GetLogger(nostd::string_view logger_name, - nostd::span args) override + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") override { return logger_; } diff --git a/api/include/opentelemetry/nostd/absl/.clang-format b/api/include/opentelemetry/nostd/internal/absl/.clang-format similarity index 100% rename from api/include/opentelemetry/nostd/absl/.clang-format rename to api/include/opentelemetry/nostd/internal/absl/.clang-format diff --git a/api/include/opentelemetry/nostd/absl/README.md b/api/include/opentelemetry/nostd/internal/absl/README.md similarity index 100% rename from api/include/opentelemetry/nostd/absl/README.md rename to api/include/opentelemetry/nostd/internal/absl/README.md diff --git a/api/include/opentelemetry/nostd/absl/base/attributes.h b/api/include/opentelemetry/nostd/internal/absl/base/attributes.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/attributes.h rename to api/include/opentelemetry/nostd/internal/absl/base/attributes.h diff --git a/api/include/opentelemetry/nostd/absl/base/config.h b/api/include/opentelemetry/nostd/internal/absl/base/config.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/config.h rename to api/include/opentelemetry/nostd/internal/absl/base/config.h diff --git a/api/include/opentelemetry/nostd/absl/base/internal/identity.h b/api/include/opentelemetry/nostd/internal/absl/base/internal/identity.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/internal/identity.h rename to api/include/opentelemetry/nostd/internal/absl/base/internal/identity.h diff --git a/api/include/opentelemetry/nostd/absl/base/internal/inline_variable.h b/api/include/opentelemetry/nostd/internal/absl/base/internal/inline_variable.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/internal/inline_variable.h rename to api/include/opentelemetry/nostd/internal/absl/base/internal/inline_variable.h diff --git a/api/include/opentelemetry/nostd/absl/base/internal/invoke.h b/api/include/opentelemetry/nostd/internal/absl/base/internal/invoke.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/internal/invoke.h rename to api/include/opentelemetry/nostd/internal/absl/base/internal/invoke.h diff --git a/api/include/opentelemetry/nostd/absl/base/macros.h b/api/include/opentelemetry/nostd/internal/absl/base/macros.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/macros.h rename to api/include/opentelemetry/nostd/internal/absl/base/macros.h diff --git a/api/include/opentelemetry/nostd/absl/base/optimization.h b/api/include/opentelemetry/nostd/internal/absl/base/optimization.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/optimization.h rename to api/include/opentelemetry/nostd/internal/absl/base/optimization.h diff --git a/api/include/opentelemetry/nostd/absl/base/options.h b/api/include/opentelemetry/nostd/internal/absl/base/options.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/options.h rename to api/include/opentelemetry/nostd/internal/absl/base/options.h diff --git a/api/include/opentelemetry/nostd/absl/base/policy_checks.h b/api/include/opentelemetry/nostd/internal/absl/base/policy_checks.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/policy_checks.h rename to api/include/opentelemetry/nostd/internal/absl/base/policy_checks.h diff --git a/api/include/opentelemetry/nostd/absl/base/port.h b/api/include/opentelemetry/nostd/internal/absl/base/port.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/base/port.h rename to api/include/opentelemetry/nostd/internal/absl/base/port.h diff --git a/api/include/opentelemetry/nostd/absl/meta/type_traits.h b/api/include/opentelemetry/nostd/internal/absl/meta/type_traits.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/meta/type_traits.h rename to api/include/opentelemetry/nostd/internal/absl/meta/type_traits.h diff --git a/api/include/opentelemetry/nostd/absl/types/bad_variant_access.h b/api/include/opentelemetry/nostd/internal/absl/types/bad_variant_access.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/types/bad_variant_access.h rename to api/include/opentelemetry/nostd/internal/absl/types/bad_variant_access.h diff --git a/api/include/opentelemetry/nostd/absl/types/internal/variant.h b/api/include/opentelemetry/nostd/internal/absl/types/internal/variant.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/types/internal/variant.h rename to api/include/opentelemetry/nostd/internal/absl/types/internal/variant.h diff --git a/api/include/opentelemetry/nostd/absl/types/variant.h b/api/include/opentelemetry/nostd/internal/absl/types/variant.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/types/variant.h rename to api/include/opentelemetry/nostd/internal/absl/types/variant.h diff --git a/api/include/opentelemetry/nostd/absl/utility/utility.h b/api/include/opentelemetry/nostd/internal/absl/utility/utility.h similarity index 100% rename from api/include/opentelemetry/nostd/absl/utility/utility.h rename to api/include/opentelemetry/nostd/internal/absl/utility/utility.h diff --git a/api/include/opentelemetry/nostd/variant.h b/api/include/opentelemetry/nostd/variant.h index e9ef66630e..b6b2326998 100644 --- a/api/include/opentelemetry/nostd/variant.h +++ b/api/include/opentelemetry/nostd/variant.h @@ -47,9 +47,9 @@ OPENTELEMETRY_END_NAMESPACE # endif # ifdef HAVE_ABSEIL -# include +# include "absl/types/variant.h" # else -# include "./absl/types/variant.h" +# include "./internal/absl/types/variant.h" # endif # ifdef _MSC_VER diff --git a/api/include/opentelemetry/std/span.h b/api/include/opentelemetry/std/span.h index ae614a9425..5fdff57fd5 100644 --- a/api/include/opentelemetry/std/span.h +++ b/api/include/opentelemetry/std/span.h @@ -11,7 +11,7 @@ #if defined __has_include # if __has_include() // Check for __cpp_{feature} # include -# if defined(__cpp_lib_span) +# if defined(__cpp_lib_span) && __cplusplus > 201703L # define HAVE_SPAN # endif # endif @@ -21,7 +21,7 @@ # define HAVE_SPAN # endif # // Check for other compiler span implementation -# if !defined(_MSVC_LANG) && __has_include() +# if !defined(_MSVC_LANG) && __has_include() && __cplusplus > 201703L // This works as long as compiler standard is set to C++20 # define HAVE_SPAN # endif diff --git a/api/test/logs/logger_test.cc b/api/test/logs/logger_test.cc index 99c7474280..b2d2d1d60a 100644 --- a/api/test/logs/logger_test.cc +++ b/api/test/logs/logger_test.cc @@ -25,8 +25,9 @@ namespace trace = opentelemetry::trace; // Check that the default logger is a noop logger instance TEST(Logger, GetLoggerDefault) { - auto lp = Provider::GetLoggerProvider(); - auto logger = lp->GetLogger("TestLogger"); + auto lp = Provider::GetLoggerProvider(); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = lp->GetLogger("TestLogger", "", "opentelelemtry_library", "", schema_url); auto name = logger->GetName(); EXPECT_NE(nullptr, logger); EXPECT_EQ(name, "noop logger"); @@ -40,17 +41,19 @@ TEST(Logger, GetNoopLoggerNameWithArgs) // GetLogger(name, list(args)) std::array sv{"string"}; span args{sv}; - lp->GetLogger("NoopLoggerWithArgs", args); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + lp->GetLogger("NoopLoggerWithArgs", args, "opentelelemtry_library", "", schema_url); // GetLogger(name, string options) - lp->GetLogger("NoopLoggerWithOptions", "options"); + lp->GetLogger("NoopLoggerWithOptions", "options", "opentelelemtry_library", "", schema_url); } // Test the Log() overloads TEST(Logger, LogMethodOverloads) { - auto lp = Provider::GetLoggerProvider(); - auto logger = lp->GetLogger("TestLogger"); + auto lp = Provider::GetLoggerProvider(); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = lp->GetLogger("TestLogger", "", "opentelelemtry_library", "", schema_url); // Create a map to test the logs with std::map m = {{"key1", "value1"}}; @@ -80,7 +83,6 @@ class TestLogger : public Logger void Log(Severity severity, string_view name, string_view body, - const common::KeyValueIterable &resource, const common::KeyValueIterable &attributes, trace::TraceId trace_id, trace::SpanId span_id, @@ -92,12 +94,20 @@ class TestLogger : public Logger // Define a basic LoggerProvider class that returns an instance of the logger class defined above class TestProvider : public LoggerProvider { - shared_ptr GetLogger(string_view library_name, string_view options = "") override + nostd::shared_ptr GetLogger(nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") override { return shared_ptr(new TestLogger()); } - shared_ptr GetLogger(string_view library_name, span args) override + nostd::shared_ptr GetLogger(nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") override { return shared_ptr(new TestLogger()); } @@ -112,7 +122,8 @@ TEST(Logger, PushLoggerImplementation) auto lp = Provider::GetLoggerProvider(); // Check that the implementation was pushed by calling TestLogger's GetName() - auto logger = lp->GetLogger("TestLogger"); + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = lp->GetLogger("TestLogger", "", "opentelelemtry_library", "", schema_url); ASSERT_EQ("test logger", logger->GetName()); } #endif diff --git a/api/test/logs/provider_test.cc b/api/test/logs/provider_test.cc index c9eb7bddc7..64a6b0a86a 100644 --- a/api/test/logs/provider_test.cc +++ b/api/test/logs/provider_test.cc @@ -8,24 +8,29 @@ # include "opentelemetry/logs/provider.h" # include "opentelemetry/nostd/shared_ptr.h" -# include "opentelemetry/nostd/span.h" -# include "opentelemetry/nostd/string_view.h" using opentelemetry::logs::Logger; using opentelemetry::logs::LoggerProvider; using opentelemetry::logs::Provider; using opentelemetry::nostd::shared_ptr; -using opentelemetry::nostd::span; -using opentelemetry::nostd::string_view; +namespace nostd = opentelemetry::nostd; class TestProvider : public LoggerProvider { - shared_ptr GetLogger(string_view library_name, string_view options) override + nostd::shared_ptr GetLogger(nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") override { return shared_ptr(nullptr); } - shared_ptr GetLogger(string_view library_name, span args) override + nostd::shared_ptr GetLogger(nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") override { return shared_ptr(nullptr); } @@ -57,15 +62,16 @@ TEST(Provider, MultipleLoggerProviders) TEST(Provider, GetLogger) { auto tf = shared_ptr(new TestProvider()); - // tests GetLogger(name, options) - auto logger = tf->GetLogger("logger1"); + // tests GetLogger(name, version, schema) + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = tf->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); EXPECT_EQ(nullptr, logger); // tests GetLogger(name, arguments) - std::array sv{"string"}; - span args{sv}; - auto logger2 = tf->GetLogger("logger2", args); + std::array sv{"string"}; + nostd::span args{sv}; + auto logger2 = tf->GetLogger("logger2", args, "opentelelemtry_library", "", schema_url); EXPECT_EQ(nullptr, logger2); } #endif diff --git a/bazel/otel_cc_benchmark.bzl b/bazel/otel_cc_benchmark.bzl index 78da697f48..c40681917f 100644 --- a/bazel/otel_cc_benchmark.bzl +++ b/bazel/otel_cc_benchmark.bzl @@ -24,16 +24,17 @@ def otel_cc_benchmark(name, srcs, deps, tags = [""]): srcs = srcs, deps = deps + ["@com_github_google_benchmark//:benchmark"], tags = tags + ["manual"], + defines = ["BAZEL_BUILD"], ) # The result of running the benchmark, captured into a text file. native.genrule( name = name + "_result", - outs = [name + "_result.txt"], + outs = [name + "_result.json"], tools = [":" + name], tags = tags + ["benchmark_result", "manual"], testonly = True, - cmd = "$(location :" + name + (") --benchmark_color=false --benchmark_min_time=.1 &> $@"), + cmd = "$(location :" + name + (") --benchmark_format=json --benchmark_color=false --benchmark_min_time=.1 &> $@"), ) # This is run as part of "bazel test ..." to smoke-test benchmarks. It's @@ -44,4 +45,5 @@ def otel_cc_benchmark(name, srcs, deps, tags = [""]): deps = deps + ["@com_github_google_benchmark//:benchmark"], args = ["--benchmark_min_time=0"], tags = tags + ["benchmark"], + defines = ["BAZEL_BUILD"], ) diff --git a/bazel/repository.bzl b/bazel/repository.bzl index 6f9826e801..2b00ba18f6 100644 --- a/bazel/repository.bzl +++ b/bazel/repository.bzl @@ -9,10 +9,10 @@ def opentelemetry_cpp_deps(): maybe( http_archive, name = "com_github_google_benchmark", - sha256 = "dccbdab796baa1043f04982147e67bb6e118fe610da2c65f88912d73987e700c", - strip_prefix = "benchmark-1.5.2", + sha256 = "1f71c72ce08d2c1310011ea6436b31e39ccab8c2db94186d26657d41747c85d6", + strip_prefix = "benchmark-1.6.0", urls = [ - "https://github.com/google/benchmark/archive/v1.5.2.tar.gz", + "https://github.com/google/benchmark/archive/v1.6.0.tar.gz", ], ) @@ -28,6 +28,17 @@ def opentelemetry_cpp_deps(): ], ) + # Load abseil dependency(optional) + maybe( + http_archive, + name = "com_google_absl", + sha256 = "dd7db6815204c2a62a2160e32c55e97113b0a0178b2f090d6bab5ce36111db4b", + strip_prefix = "abseil-cpp-20210324.0", + urls = [ + "https://github.com/abseil/abseil-cpp/archive/20210324.0.tar.gz", + ], + ) + # Load gRPC dependency maybe( http_archive, diff --git a/ci/do_ci.sh b/ci/do_ci.sh index cb2547d9a1..368190b6ec 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -17,6 +17,42 @@ function install_prometheus_cpp_client popd } +function run_benchmarks +{ + docker run -d --rm -it -p 4317:4317 -p 4318:4318 -v \ + $(pwd)/examples/otlp:/cfg otel/opentelemetry-collector:0.38.0 \ + --config=/cfg/opentelemetry-collector-config/config.dev.yaml + + [ -z "${BENCHMARK_DIR}" ] && export BENCHMARK_DIR=$HOME/benchmark + mkdir -p $BENCHMARK_DIR + bazel $BAZEL_STARTUP_OPTIONS build $BAZEL_OPTIONS -c opt -- \ + $(bazel query 'attr("tags", "benchmark_result", ...)') + echo "" + echo "Benchmark results in $BENCHMARK_DIR:" + ( + cd bazel-bin + find . -name \*_result.json -exec bash -c \ + 'echo "$@" && mkdir -p "$BENCHMARK_DIR/$(dirname "$@")" && \ + cp "$@" "$BENCHMARK_DIR/$@" && chmod +w "$BENCHMARK_DIR/$@"' _ {} \; + ) + + # collect benchmark results into one array + pushd $BENCHMARK_DIR + components=(api sdk exporters) + for component in "${components[@]}" + do + out=$component-benchmark_result.json + find ./$component -type f -name "*_result.json" -exec cat {} \; > $component_tmp_bench.json + cat $component_tmp_bench.json | docker run -i --rm itchyny/gojq:0.12.6 -s \ + '.[0].benchmarks = ([.[].benchmarks] | add) | + if .[0].benchmarks == null then null else .[0] end' > $BENCHMARK_DIR/$out + done + + mv *benchmark_result.json ${SRC_DIR} + popd + docker kill $(docker ps -q) +} + [ -z "${SRC_DIR}" ] && export SRC_DIR="`pwd`" [ -z "${BUILD_DIR}" ] && export BUILD_DIR=$HOME/build mkdir -p "${BUILD_DIR}" @@ -124,6 +160,10 @@ elif [[ "$1" == "cmake.exporter.otprotocol.test" ]]; then make -j $(nproc) cd exporters/otlp && make test exit 0 +elif [[ "$1" == "bazel.with_abseil" ]]; then + bazel $BAZEL_STARTUP_OPTIONS build $BAZEL_OPTIONS --//api:with_abseil=true //... + bazel $BAZEL_STARTUP_OPTIONS test $BAZEL_TEST_OPTIONS --//api:with_abseil=true //... + exit 0 elif [[ "$1" == "cmake.test_example_plugin" ]]; then # Build the plugin cd "${BUILD_DIR}" @@ -162,6 +202,9 @@ elif [[ "$1" == "bazel.test" ]]; then bazel $BAZEL_STARTUP_OPTIONS build $BAZEL_OPTIONS //... bazel $BAZEL_STARTUP_OPTIONS test $BAZEL_TEST_OPTIONS //... exit 0 +elif [[ "$1" == "bazel.benchmark" ]]; then + run_benchmarks + exit 0 elif [[ "$1" == "bazel.macos.test" ]]; then bazel $BAZEL_STARTUP_OPTIONS build $BAZEL_MACOS_OPTIONS //... bazel $BAZEL_STARTUP_OPTIONS test $BAZEL_MACOS_TEST_OPTIONS //... @@ -196,7 +239,7 @@ elif [[ "$1" == "benchmark" ]]; then echo "Benchmark results in $BENCHMARK_DIR:" ( cd bazel-bin - find . -name \*_result.txt -exec bash -c \ + find . -name \*_result.json -exec bash -c \ 'echo "$@" && mkdir -p "$BENCHMARK_DIR/$(dirname "$@")" && \ cp "$@" "$BENCHMARK_DIR/$@" && chmod +w "$BENCHMARK_DIR/$@"' _ {} \; ) diff --git a/docs/dependencies.md b/docs/dependencies.md index 49f6320180..1028bd56d4 100644 --- a/docs/dependencies.md +++ b/docs/dependencies.md @@ -18,7 +18,7 @@ Both these dependencies are listed here: - [API](https://github.com/open-telemetry/opentelemetry-cpp/tree/v1.0.0-rc1/api) & [SDK](https://github.com/open-telemetry/opentelemetry-cpp/tree/v1.0.0-rc1/sdk): - Uses Standard C++ library for latest features (std::string_view, std::variant, std::span, std::shared_ptr, std::unique_ptr) with C++14/17/20 compiler if `WITH_STL` cmake option is enabled or `HAVE_CPP_STDLIB` macro is defined. License: `GNU General Public License` - For C++11/14/17 compilers, fallback to gsl::span if [GSL C++ library](https://github.com/microsoft/GSL) is installed. License: `MIT License` - - Uses Abseil C++ Library for `absl::variant` as default `nostd::variant` if `WITH_ABSEIL` cmake option is enabled. License: `Apache License 2.0` + - Uses Abseil C++ Library for `absl::variant` as default `nostd::variant` if `WITH_ABSEIL` cmake option or `--@io_opentelemetry_cpp/api:with_abseil=true` (aka `--//api:with_abseil=true`) bazel option is enabled. License: `Apache License 2.0` - [OTLP/HTTP+JSON](https://github.com/open-telemetry/opentelemetry-cpp/tree/v1.0.0-rc1/exporters/otlp) exporter: - [protobuf](https://github.com/protocolbuffers/protobuf): Library to serialize structured data. diff --git a/examples/common/logs_foo_library/foo_library.cc b/examples/common/logs_foo_library/foo_library.cc index a5b8d9ae98..2ae5ccfaf6 100644 --- a/examples/common/logs_foo_library/foo_library.cc +++ b/examples/common/logs_foo_library/foo_library.cc @@ -22,7 +22,7 @@ nostd::shared_ptr get_tracer() nostd::shared_ptr get_logger() { auto provider = logs::Provider::GetLoggerProvider(); - return provider->GetLogger("foo_library_logger"); + return provider->GetLogger("foo_library_logger", "", "foo_library"); } } // namespace @@ -32,7 +32,7 @@ void foo_library() auto scoped_span = trace::Scope(get_tracer()->StartSpan("foo_library")); auto ctx = span->GetContext(); auto logger = get_logger(); - logger->Log(opentelemetry::logs::Severity::kDebug, "name", "body", {}, {}, ctx.trace_id(), + logger->Log(opentelemetry::logs::Severity::kDebug, "name", "body", {}, ctx.trace_id(), ctx.span_id(), ctx.trace_flags(), opentelemetry::common::SystemTimestamp()); } #endif diff --git a/examples/otlp/README.md b/examples/otlp/README.md index 92f96e96bf..91f86f8f50 100644 --- a/examples/otlp/README.md +++ b/examples/otlp/README.md @@ -57,7 +57,9 @@ gRPC internally uses a different version of Abseil than OpenTelemetry C++ SDK. One option to optimize your code is to build the SDK with system-provided Abseil library. If you are using CMake, then `-DWITH_ABSEIL=ON` may be passed -during the build of SDK to reuse the same Abseil library as gRPC. +during the build of SDK to reuse the same Abseil library as gRPC. If you are +using Bazel, then `--@io_opentelemetry_cpp/api:with_abseil=true` may be passed +to reuse your Abseil library in your project. If you do not want to pursue the above option, and in case if you run into conflict between Abseil library and OpenTelemetry C++ `absl::variant` diff --git a/examples/otlp/grpc_log_main.cc b/examples/otlp/grpc_log_main.cc index d937e450ab..9a578c7fbd 100644 --- a/examples/otlp/grpc_log_main.cc +++ b/examples/otlp/grpc_log_main.cc @@ -44,11 +44,10 @@ void InitTracer() void InitLogger() { // Create OTLP exporter instance - auto exporter = std::unique_ptr(new otlp::OtlpGrpcLogExporter(opts)); - auto processor = std::shared_ptr( - new logs_sdk::SimpleLogProcessor(std::move(exporter))); - auto sdkProvider = std::shared_ptr(new logs_sdk::LoggerProvider()); - sdkProvider->SetProcessor(processor); + auto exporter = std::unique_ptr(new otlp::OtlpGrpcLogExporter(opts)); + auto sdkProvider = std::shared_ptr( + new logs_sdk::LoggerProvider(std::unique_ptr( + new logs_sdk::SimpleLogProcessor(std::move(exporter))))); auto apiProvider = nostd::shared_ptr(sdkProvider); auto provider = nostd::shared_ptr(apiProvider); opentelemetry::logs::Provider::SetLoggerProvider(provider); diff --git a/examples/otlp/http_log_main.cc b/examples/otlp/http_log_main.cc index 0f1989d853..b56dcf5aa5 100644 --- a/examples/otlp/http_log_main.cc +++ b/examples/otlp/http_log_main.cc @@ -49,10 +49,9 @@ void InitLogger() // Create OTLP exporter instance auto exporter = std::unique_ptr(new otlp::OtlpHttpLogExporter(logger_opts)); - auto processor = std::shared_ptr( - new logs_sdk::SimpleLogProcessor(std::move(exporter))); - auto sdkProvider = std::shared_ptr(new logs_sdk::LoggerProvider()); - sdkProvider->SetProcessor(processor); + auto sdkProvider = std::shared_ptr( + new logs_sdk::LoggerProvider(std::unique_ptr( + new logs_sdk::SimpleLogProcessor(std::move(exporter))))); auto apiProvider = nostd::shared_ptr(sdkProvider); auto provider = nostd::shared_ptr(apiProvider); opentelemetry::logs::Provider::SetLoggerProvider(provider); diff --git a/exporters/elasticsearch/CMakeLists.txt b/exporters/elasticsearch/CMakeLists.txt index 8f675e530a..1538a6f44a 100644 --- a/exporters/elasticsearch/CMakeLists.txt +++ b/exporters/elasticsearch/CMakeLists.txt @@ -8,8 +8,9 @@ target_include_directories( PUBLIC "$" "$") -target_link_libraries(opentelemetry_exporter_elasticsearch_logs - PUBLIC opentelemetry_trace http_client_curl) +target_link_libraries( + opentelemetry_exporter_elasticsearch_logs + PUBLIC opentelemetry_trace opentelemetry_logs http_client_curl) install( TARGETS opentelemetry_exporter_elasticsearch_logs diff --git a/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h b/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h index a14409af1d..ea58807e96 100644 --- a/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h +++ b/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h @@ -5,6 +5,7 @@ #ifdef ENABLE_LOGS_PREVIEW # include "nlohmann/json.hpp" +# include "opentelemetry/common/spin_lock_mutex.h" # include "opentelemetry/ext/http/client/curl/http_client_curl.h" # include "opentelemetry/nostd/type_traits.h" # include "opentelemetry/sdk/logs/exporter.h" @@ -104,6 +105,8 @@ class ElasticsearchLogExporter final : public opentelemetry::sdk::logs::LogExpor // Object that stores the HTTP sessions that have been created std::unique_ptr http_client_; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; }; } // namespace logs } // namespace exporter diff --git a/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h b/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h old mode 100644 new mode 100755 index 8a928d829e..7cc552d2d4 --- a/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h +++ b/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h @@ -5,7 +5,10 @@ #ifdef ENABLE_LOGS_PREVIEW # include +# include +# include # include + # include "nlohmann/json.hpp" # include "opentelemetry/sdk/common/attribute_utils.h" # include "opentelemetry/sdk/logs/recordable.h" @@ -67,6 +70,39 @@ class ElasticSearchRecordable final : public sdk::logs::Recordable } } + void WriteKeyValue(nostd::string_view key, + const opentelemetry::sdk::common::OwnedAttributeValue &value, + std::string name) + { + namespace common = opentelemetry::sdk::common; + switch (value.index()) + { + case common::kTypeBool: + json_[name][key.data()] = opentelemetry::nostd::get(value) ? true : false; + return; + case common::kTypeInt: + json_[name][key.data()] = opentelemetry::nostd::get(value); + return; + case common::kTypeInt64: + json_[name][key.data()] = opentelemetry::nostd::get(value); + return; + case common::kTypeUInt: + json_[name][key.data()] = opentelemetry::nostd::get(value); + return; + case common::kTypeUInt64: + json_[name][key.data()] = opentelemetry::nostd::get(value); + return; + case common::kTypeDouble: + json_[name][key.data()] = opentelemetry::nostd::get(value); + return; + case common::kTypeString: + json_[name][key.data()] = opentelemetry::nostd::get(value).data(); + return; + default: + return; + } + } + public: /** * Set the severity for this log. @@ -75,7 +111,18 @@ class ElasticSearchRecordable final : public sdk::logs::Recordable void SetSeverity(opentelemetry::logs::Severity severity) noexcept override { // Convert the severity enum to a string - json_["severity"] = opentelemetry::logs::SeverityNumToText[static_cast(severity)]; + int severity_index = static_cast(severity); + if (severity_index < 0 || + severity_index >= std::extent::value) + { + std::stringstream sout; + sout << "Invalid severity(" << severity_index << ")"; + json_["severity"] = sout.str(); + } + else + { + json_["severity"] = opentelemetry::logs::SeverityNumToText[severity_index]; + } } /** @@ -91,14 +138,15 @@ class ElasticSearchRecordable final : public sdk::logs::Recordable void SetBody(nostd::string_view message) noexcept override { json_["body"] = message.data(); } /** - * Set a resource for this log. - * @param name the name of the resource - * @param value the resource value + * Set Resource of this log + * @param Resource the resource to set */ - void SetResource(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept override + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override { - WriteKeyValue(key, value, "resource"); + for (auto &kv : resource.GetAttributes()) + { + WriteKeyValue(kv.first, kv.second, "resource"); + } } /** @@ -157,7 +205,29 @@ class ElasticSearchRecordable final : public sdk::logs::Recordable /** * Returns a JSON object contain the log information */ - nlohmann::json GetJSON() noexcept { return json_; }; + nlohmann::json GetJSON() noexcept { return json_; } + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept + { + instrumentation_library_ = &instrumentation_library; + } + + /** Returns the associated instruementation library */ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & + GetInstrumentationLibrary() const noexcept + { + return *instrumentation_library_; + } + +private: + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_ = nullptr; }; } // namespace logs } // namespace exporter diff --git a/exporters/elasticsearch/src/es_log_exporter.cc b/exporters/elasticsearch/src/es_log_exporter.cc index 1fbbcce807..a5a66ebe01 100644 --- a/exporters/elasticsearch/src/es_log_exporter.cc +++ b/exporters/elasticsearch/src/es_log_exporter.cc @@ -5,6 +5,7 @@ # include // std::stringstream +# include # include "opentelemetry/exporters/elasticsearch/es_log_exporter.h" # include "opentelemetry/exporters/elasticsearch/es_log_recordable.h" # include "opentelemetry/sdk_config.h" @@ -127,10 +128,10 @@ sdk::common::ExportResult ElasticsearchLogExporter::Export( const nostd::span> &records) noexcept { // Return failure if this exporter has been shutdown - if (is_shutdown_) + if (isShutdown()) { - - OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Export failed, exporter is shutdown"); + OTEL_INTERNAL_LOG_ERROR("[ES Log Exporter] Exporting " + << records.size() << " log(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } @@ -199,6 +200,7 @@ sdk::common::ExportResult ElasticsearchLogExporter::Export( bool ElasticsearchLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept { + const std::lock_guard locked(lock_); is_shutdown_ = true; // Shutdown the session manager @@ -207,6 +209,12 @@ bool ElasticsearchLogExporter::Shutdown(std::chrono::microseconds timeout) noexc return true; } + +bool ElasticsearchLogExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} } // namespace logs } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/elasticsearch/test/es_log_exporter_test.cc b/exporters/elasticsearch/test/es_log_exporter_test.cc index 8aa5cd654c..d32048e83d 100644 --- a/exporters/elasticsearch/test/es_log_exporter_test.cc +++ b/exporters/elasticsearch/test/es_log_exporter_test.cc @@ -77,8 +77,9 @@ TEST(ElasticsearchLogsExporterTests, RecordableCreation) // Attributes and resource support different types record->SetAttribute("key0", false); record->SetAttribute("key1", "1"); - record->SetResource("key2", 2); - record->SetResource("key3", 3.142); + + auto resource = opentelemetry::sdk::resource::Resource::Create({{"key2", 2}, {"key3", 3142}}); + record->SetResource(resource); exporter->Export(nostd::span>(&record, 1)); } diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h index 3420b3f5db..6f3a394b0d 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h @@ -9,6 +9,8 @@ # include # include # include +# include +# include # include @@ -101,7 +103,6 @@ class Logger : public opentelemetry::logs::Logger void Log(opentelemetry::logs::Severity severity, nostd::string_view name, nostd::string_view body, - const common::KeyValueIterable &resource, const common::KeyValueIterable &attributes, opentelemetry::trace::TraceId trace_id, opentelemetry::trace::SpanId span_id, @@ -111,8 +112,7 @@ class Logger : public opentelemetry::logs::Logger # ifdef RTTI_ENABLED common::KeyValueIterable &attribs = const_cast(attributes); - // common::KeyValueIterable &resr = const_cast(resource); - Properties *evt = dynamic_cast(&attribs); + Properties *evt = dynamic_cast(&attribs); // Properties *res = dynamic_cast(&resr); if (evt != nullptr) @@ -122,7 +122,6 @@ class Logger : public opentelemetry::logs::Logger } # endif Properties evtCopy = attributes; - // Properties resCopy = resource; return Log(severity, name, body, evtCopy, trace_id, span_id, trace_flags, timestamp); } @@ -169,8 +168,19 @@ class Logger : public opentelemetry::logs::Logger int64_t tsMs = std::chrono::duration_cast(ts.time_since_epoch()).count(); evt[ETW_FIELD_TIMESTAMP] = utils::formatUtcTimestampMsAsISO8601(tsMs); - evt[ETW_FIELD_LOG_SEVERITY_TEXT] = - opentelemetry::logs::SeverityNumToText[static_cast(severity)].data(); + int severity_index = static_cast(severity); + if (severity_index < 0 || + severity_index >= std::extent::value) + { + std::stringstream sout; + sout << "Invalid severity(" << severity_index << ")"; + evt[ETW_FIELD_LOG_SEVERITY_TEXT] = sout.str(); + } + else + { + evt[ETW_FIELD_LOG_SEVERITY_TEXT] = + opentelemetry::logs::SeverityNumToText[severity_index].data(); + } evt[ETW_FIELD_LOG_SEVERITY_NUM] = static_cast(severity); evt[ETW_FIELD_LOG_BODY] = std::string(body.data(), body.length()); etwProvider().write(provHandle, evt, nullptr, nullptr, 0, encoding); @@ -211,31 +221,49 @@ class LoggerProvider : public opentelemetry::logs::LoggerProvider config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST; } + nostd::shared_ptr GetLogger( + nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view version = "", + nostd::string_view schema_url = "") override + { + UNREFERENCED_PARAMETER(options); + UNREFERENCED_PARAMETER(library_name); + UNREFERENCED_PARAMETER(version); + UNREFERENCED_PARAMETER(schema_url); + ETWProvider::EventFormat evtFmt = config_.encoding; + return nostd::shared_ptr{ + new (std::nothrow) etw::Logger(*this, logger_name, evtFmt)}; + } + /** * @brief Obtain ETW Tracer. * @param name ProviderId (instrumentation name) - Name or GUID - * * @param args Additional arguments that controls `codec` of the provider. * Possible values are: * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events. * - "MSGPACK" - MessagePack-encoded binary payload ETW events. * - "XML" - XML events (reserved for future use) + * @param library_name Library name + * @param version Library version + * @param schema_url schema URL * @return */ - nostd::shared_ptr GetLogger(nostd::string_view name, - nostd::string_view args = "") override + nostd::shared_ptr GetLogger( + nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view version = "", + nostd::string_view schema_url = "") override { UNREFERENCED_PARAMETER(args); + UNREFERENCED_PARAMETER(library_name); + UNREFERENCED_PARAMETER(version); + UNREFERENCED_PARAMETER(schema_url); ETWProvider::EventFormat evtFmt = config_.encoding; - return nostd::shared_ptr{new (std::nothrow) - etw::Logger(*this, name, evtFmt)}; - } - - nostd::shared_ptr GetLogger( - nostd::string_view name, - nostd::span args) override - { - return GetLogger(name, args[0]); + return nostd::shared_ptr{ + new (std::nothrow) etw::Logger(*this, logger_name, evtFmt)}; } }; diff --git a/exporters/etw/test/etw_logger_test.cc b/exporters/etw/test/etw_logger_test.cc index 419e4ad0da..e4cf53ee34 100644 --- a/exporters/etw/test/etw_logger_test.cc +++ b/exporters/etw/test/etw_logger_test.cc @@ -48,7 +48,8 @@ TEST(ETWLogger, LoggerCheckWithBody) std::string providerName = kGlobalProviderName; // supply unique instrumentation name here exporter::etw::LoggerProvider lp; - auto logger = lp.GetLogger(providerName); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = lp.GetLogger(providerName, "", schema_url); Properties attribs = {{"attrib1", 1}, {"attrib2", 2}}; EXPECT_NO_THROW( logger->Log(opentelemetry::logs::Severity::kDebug, "My Log", "This is test log body")); @@ -90,7 +91,8 @@ TEST(ETWLogger, LoggerCheckWithAttributes) std::string providerName = kGlobalProviderName; // supply unique instrumentation name here exporter::etw::LoggerProvider lp; - auto logger = lp.GetLogger(providerName); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = lp.GetLogger(providerName, "", schema_url); // Log attributes Properties attribs = {{"attrib1", 1}, {"attrib2", 2}}; EXPECT_NO_THROW(logger->Log(opentelemetry::logs::Severity::kDebug, "My Log", attribs)); diff --git a/exporters/jaeger/CMakeLists.txt b/exporters/jaeger/CMakeLists.txt index b6108013d6..0659c399b0 100644 --- a/exporters/jaeger/CMakeLists.txt +++ b/exporters/jaeger/CMakeLists.txt @@ -58,6 +58,8 @@ install( PATTERN "recordable.h" EXCLUDE) if(BUILD_TESTING) + add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1) + add_executable(jaeger_recordable_test test/jaeger_recordable_test.cc) target_link_libraries( jaeger_recordable_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} @@ -69,7 +71,6 @@ if(BUILD_TESTING) TEST_LIST jaeger_recordable_test) add_executable(jaeger_exporter_test test/jaeger_exporter_test.cc) - add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1) if(MSVC) if(GMOCK_LIB) unset(GMOCK_LIB CACHE) diff --git a/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h b/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h index e58b2230ef..284bab2cab 100644 --- a/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h +++ b/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -75,6 +76,8 @@ class JaegerExporter final : public opentelemetry::sdk::trace::SpanExporter bool is_shutdown_ = false; JaegerExporterOptions options_; std::unique_ptr sender_; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; // For testing friend class JaegerExporterTestPeer; /** diff --git a/exporters/jaeger/src/jaeger_exporter.cc b/exporters/jaeger/src/jaeger_exporter.cc index e194def470..c07f2f0100 100644 --- a/exporters/jaeger/src/jaeger_exporter.cc +++ b/exporters/jaeger/src/jaeger_exporter.cc @@ -4,11 +4,13 @@ #include #include #include +#include "opentelemetry/sdk_config.h" #include "http_transport.h" #include "thrift_sender.h" #include "udp_transport.h" +#include #include namespace sdk_common = opentelemetry::sdk::common; @@ -39,8 +41,10 @@ std::unique_ptr JaegerExporter::MakeRecordable() noexcept sdk_common::ExportResult JaegerExporter::Export( const nostd::span> &spans) noexcept { - if (is_shutdown_) + if (isShutdown()) { + OTEL_INTERNAL_LOG_ERROR("[Jaeger Trace Exporter] Exporting " + << spans.size() << " span(s) failed, exporter is shutdown"); return sdk_common::ExportResult::kFailure; } @@ -91,10 +95,17 @@ void JaegerExporter::InitializeEndpoint() bool JaegerExporter::Shutdown(std::chrono::microseconds timeout) noexcept { + const std::lock_guard locked(lock_); is_shutdown_ = true; return true; } +bool JaegerExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} + } // namespace jaeger } // 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 index 0c2c537838..3e47ccb177 100644 --- a/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h +++ b/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h @@ -2,8 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/exporters/memory/in_memory_span_data.h" #include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk_config.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -42,8 +45,10 @@ class InMemorySpanExporter final : public opentelemetry::sdk::trace::SpanExporte sdk::common::ExportResult Export( const nostd::span> &recordables) noexcept override { - if (is_shutdown_) + if (isShutdown()) { + OTEL_INTERNAL_LOG_ERROR("[In Memory Span Exporter] Exporting " + << recordables.size() << " span(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } for (auto &recordable : recordables) @@ -67,6 +72,7 @@ class InMemorySpanExporter final : public opentelemetry::sdk::trace::SpanExporte bool Shutdown( std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override { + const std::lock_guard locked(lock_); is_shutdown_ = true; return true; }; @@ -82,6 +88,12 @@ class InMemorySpanExporter final : public opentelemetry::sdk::trace::SpanExporte private: std::shared_ptr data_; bool is_shutdown_ = false; + mutable opentelemetry::common::SpinLockMutex lock_; + const bool isShutdown() const noexcept + { + const std::lock_guard locked(lock_); + return is_shutdown_; + } }; } // namespace memory } // namespace exporter diff --git a/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h b/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h index f4ab0be58a..ad1d54a215 100644 --- a/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h +++ b/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h @@ -4,6 +4,7 @@ #pragma once #ifdef ENABLE_LOGS_PREVIEW +# include "opentelemetry/common/spin_lock_mutex.h" # include "opentelemetry/nostd/type_traits.h" # include "opentelemetry/sdk/logs/exporter.h" # include "opentelemetry/sdk/logs/log_record.h" @@ -49,6 +50,8 @@ class OStreamLogExporter final : public opentelemetry::sdk::logs::LogExporter std::ostream &sout_; // Whether this exporter has been shut down bool is_shutdown_ = false; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; }; } // namespace logs } // namespace exporter diff --git a/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h b/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h index 58c52989a2..8122b6777a 100644 --- a/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h +++ b/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h @@ -3,6 +3,7 @@ #pragma once +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/nostd/type_traits.h" #include "opentelemetry/sdk/trace/exporter.h" #include "opentelemetry/sdk/trace/span_data.h" @@ -42,7 +43,9 @@ class OStreamSpanExporter final : public opentelemetry::sdk::trace::SpanExporter private: std::ostream &sout_; - bool isShutdown_ = false; + bool is_shutdown_ = false; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; // Mapping status number to the string from api/include/opentelemetry/trace/canonical_code.h std::map statusMap{{0, "Unset"}, {1, "Ok"}, {2, "Error"}}; diff --git a/exporters/ostream/src/log_exporter.cc b/exporters/ostream/src/log_exporter.cc index 1def9ed1e6..c11ec13786 100644 --- a/exporters/ostream/src/log_exporter.cc +++ b/exporters/ostream/src/log_exporter.cc @@ -3,8 +3,11 @@ #ifdef ENABLE_LOGS_PREVIEW # include "opentelemetry/exporters/ostream/log_exporter.h" +# include +# include "opentelemetry/sdk_config.h" # include +# include namespace nostd = opentelemetry::nostd; namespace sdklogs = opentelemetry::sdk::logs; @@ -107,8 +110,10 @@ std::unique_ptr OStreamLogExporter::MakeRecordable() noexce sdk::common::ExportResult OStreamLogExporter::Export( const nostd::span> &records) noexcept { - if (is_shutdown_) + if (isShutdown()) { + OTEL_INTERNAL_LOG_ERROR("[Ostream Log Exporter] Exporting " + << records.size() << " log(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } @@ -142,14 +147,24 @@ sdk::common::ExportResult OStreamLogExporter::Export( sout_ << "{\n" << " timestamp : " << log_record->GetTimestamp().time_since_epoch().count() << "\n" << " severity_num : " << static_cast(log_record->GetSeverity()) << "\n" - << " severity_text : " - << opentelemetry::logs::SeverityNumToText[static_cast(log_record->GetSeverity())] - << "\n" - << " name : " << log_record->GetName() << "\n" + << " severity_text : "; + + int severity_index = static_cast(log_record->GetSeverity()); + if (severity_index < 0 || + severity_index >= std::extent::value) + { + sout_ << "Invalid severity(" << severity_index << ")\n"; + } + else + { + sout_ << opentelemetry::logs::SeverityNumToText[severity_index] << "\n"; + } + + sout_ << " name : " << log_record->GetName() << "\n" << " body : " << log_record->GetBody() << "\n" << " resource : "; - printMap(log_record->GetResource(), sout_); + printMap(log_record->GetResource().GetAttributes(), sout_); sout_ << "\n" << " attributes : "; @@ -168,10 +183,17 @@ sdk::common::ExportResult OStreamLogExporter::Export( bool OStreamLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept { + const std::lock_guard locked(lock_); is_shutdown_ = true; return true; } +bool OStreamLogExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} + } // namespace logs } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/ostream/src/span_exporter.cc b/exporters/ostream/src/span_exporter.cc index 48d81f0d69..dea72f57f8 100644 --- a/exporters/ostream/src/span_exporter.cc +++ b/exporters/ostream/src/span_exporter.cc @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #include "opentelemetry/exporters/ostream/span_exporter.h" - #include +#include +#include "opentelemetry/sdk_config.h" namespace nostd = opentelemetry::nostd; namespace trace_sdk = opentelemetry::sdk::trace; @@ -44,8 +45,10 @@ std::unique_ptr OStreamSpanExporter::MakeRecordable() noe sdk::common::ExportResult OStreamSpanExporter::Export( const nostd::span> &spans) noexcept { - if (isShutdown_) + if (isShutdown()) { + OTEL_INTERNAL_LOG_ERROR("[Ostream Trace Exporter] Exporting " + << spans.size() << " span(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } @@ -95,10 +98,16 @@ sdk::common::ExportResult OStreamSpanExporter::Export( bool OStreamSpanExporter::Shutdown(std::chrono::microseconds timeout) noexcept { - isShutdown_ = true; + const std::lock_guard locked(lock_); + is_shutdown_ = true; return true; } +bool OStreamSpanExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} void OStreamSpanExporter::printAttributes( const std::unordered_map &map, const std::string prefix) diff --git a/exporters/ostream/test/ostream_log_test.cc b/exporters/ostream/test/ostream_log_test.cc index a21be32f29..8878e9d037 100644 --- a/exporters/ostream/test/ostream_log_test.cc +++ b/exporters/ostream/test/ostream_log_test.cc @@ -46,8 +46,9 @@ TEST(OStreamLogExporter, Shutdown) // Restore original stringstream buffer std::cout.rdbuf(original); - - ASSERT_EQ(output.str(), ""); + std::string err_message = + "[Ostream Log Exporter] Exporting 1 log(s) failed, exporter is shutdown"; + EXPECT_TRUE(output.str().find(err_message) != std::string::npos); } // ---------------------------------- Print to cout ------------------------- @@ -80,7 +81,8 @@ TEST(OstreamLogExporter, DefaultLogRecordToCout) " severity_text : INVALID\n" " name : \n" " body : \n" - " resource : {}\n" + " resource : {{telemetry.sdk.version: " OPENTELEMETRY_VERSION + "}, {telemetry.sdk.name: opentelemetry}, {telemetry.sdk.language: cpp}}\n" " attributes : {}\n" " trace_id : 00000000000000000000000000000000\n" " span_id : 0000000000000000\n" @@ -128,7 +130,9 @@ TEST(OStreamLogExporter, SimpleLogToCout) " severity_text : TRACE\n" " name : Name\n" " body : Message\n" - " resource : {}\n" + " resource : {{telemetry.sdk.version: " OPENTELEMETRY_VERSION + "}, {telemetry.sdk.name: opentelemetry}, " + "{telemetry.sdk.language: cpp}}\n" " attributes : {}\n" " trace_id : 00000000000000000000000000000000\n" " span_id : 0000000000000000\n" @@ -156,7 +160,8 @@ TEST(OStreamLogExporter, LogWithStringAttributesToCerr) auto record = exporter->MakeRecordable(); // Set resources for this log record only of type - record->SetResource("key1", "val1"); + auto resource = opentelemetry::sdk::resource::Resource::Create({{"key1", "val1"}}); + record->SetResource(resource); // Set attributes to this log record of type record->SetAttribute("a", true); @@ -174,7 +179,9 @@ TEST(OStreamLogExporter, LogWithStringAttributesToCerr) " severity_text : INVALID\n" " name : \n" " body : \n" - " resource : {{key1: val1}}\n" + " resource : {{telemetry.sdk.version: " OPENTELEMETRY_VERSION + "}, {telemetry.sdk.name: opentelemetry}, {telemetry.sdk.language: cpp}, {service.name: " + "unknown_service}, {key1: val1}}\n" " attributes : {{a: 1}}\n" " trace_id : 00000000000000000000000000000000\n" " span_id : 0000000000000000\n" @@ -205,7 +212,9 @@ TEST(OStreamLogExporter, LogWithVariantTypesToClog) // Set resources for this log record of only integer types as the value std::array array1 = {1, 2, 3}; nostd::span data1{array1.data(), array1.size()}; - record->SetResource("res1", data1); + + auto resource = opentelemetry::sdk::resource::Resource::Create({{"res1", data1}}); + record->SetResource(resource); // Set resources for this log record of bool types as the value // e.g. key/value is a par of type @@ -225,7 +234,9 @@ TEST(OStreamLogExporter, LogWithVariantTypesToClog) " severity_text : INVALID\n" " name : \n" " body : \n" - " resource : {{res1: [1, 2, 3]}}\n" + " resource : {{service.name: unknown_service}, " + "{telemetry.sdk.version: " OPENTELEMETRY_VERSION + "}, {telemetry.sdk.name: opentelemetry}, {telemetry.sdk.language: cpp}, {res1: [1, 2, 3]}}\n" " attributes : {{attr1: [0, 1, 0]}}\n" " trace_id : 00000000000000000000000000000000\n" " span_id : 0000000000000000\n" @@ -241,15 +252,16 @@ TEST(OStreamLogExporter, LogWithVariantTypesToClog) TEST(OStreamLogExporter, IntegrationTest) { // Initialize a logger - auto exporter = std::unique_ptr(new exporterlogs::OStreamLogExporter); - auto processor = - std::shared_ptr(new sdklogs::SimpleLogProcessor(std::move(exporter))); + auto exporter = std::unique_ptr(new exporterlogs::OStreamLogExporter); auto sdkProvider = std::shared_ptr(new sdklogs::LoggerProvider()); - sdkProvider->SetProcessor(processor); + sdkProvider->AddProcessor( + std::unique_ptr(new sdklogs::SimpleLogProcessor(std::move(exporter)))); auto apiProvider = nostd::shared_ptr(sdkProvider); auto provider = nostd::shared_ptr(apiProvider); logs_api::Provider::SetLoggerProvider(provider); - auto logger = logs_api::Provider::GetLoggerProvider()->GetLogger("Logger"); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = logs_api::Provider::GetLoggerProvider()->GetLogger( + "Logger", "", "opentelelemtry_library", "", schema_url); // Back up cout's streambuf std::streambuf *original = std::cout.rdbuf(); @@ -260,7 +272,7 @@ TEST(OStreamLogExporter, IntegrationTest) // Write a log to ostream exporter common::SystemTimestamp now(std::chrono::system_clock::now()); - logger->Log(logs_api::Severity::kDebug, "", "Hello", {}, {}, {}, {}, {}, now); + logger->Log(logs_api::Severity::kDebug, "", "Hello", {}, {}, {}, {}, now); // Restore cout's original streambuf std::cout.rdbuf(original); @@ -275,7 +287,9 @@ TEST(OStreamLogExporter, IntegrationTest) " severity_text : DEBUG\n" " name : \n" " body : Hello\n" - " resource : {}\n" + " resource : {{service.name: unknown_service}, " + "{telemetry.sdk.version: " OPENTELEMETRY_VERSION + "}, {telemetry.sdk.name: opentelemetry}, {telemetry.sdk.language: cpp}}\n" " attributes : {}\n" " trace_id : 00000000000000000000000000000000\n" " span_id : 0000000000000000\n" diff --git a/exporters/ostream/test/ostream_span_test.cc b/exporters/ostream/test/ostream_span_test.cc index 4d0ca8728e..edfd66505e 100644 --- a/exporters/ostream/test/ostream_span_test.cc +++ b/exporters/ostream/test/ostream_span_test.cc @@ -51,8 +51,9 @@ TEST(OStreamSpanExporter, Shutdown) EXPECT_TRUE(processor->Shutdown()); processor->OnEnd(std::move(recordable)); }); - - EXPECT_EQ(captured, ""); + std::string err_message = + "[Ostream Trace Exporter] Exporting 1 span(s) failed, exporter is shutdown"; + EXPECT_TRUE(captured.find(err_message) != std::string::npos); } constexpr const char *kDefaultSpanPrinted = diff --git a/exporters/otlp/BUILD b/exporters/otlp/BUILD index 829d834ea6..1a39a82baf 100644 --- a/exporters/otlp/BUILD +++ b/exporters/otlp/BUILD @@ -279,5 +279,6 @@ otel_cc_benchmark( ], deps = [ ":otlp_grpc_exporter", + "//examples/common/foo_library:common_foo_library", ], ) diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h index 52b63b61f3..a28e6fca85 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h @@ -7,6 +7,7 @@ #include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/proto/collector/trace/v1/trace_service.grpc.pb.h" #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" @@ -77,6 +78,8 @@ class OtlpGrpcExporter final : public opentelemetry::sdk::trace::SpanExporter */ OtlpGrpcExporter(std::unique_ptr stub); bool is_shutdown_ = false; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; }; } // namespace otlp } // namespace exporter diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h index 929dd745ee..a8aeda85b8 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h @@ -8,6 +8,7 @@ # include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" # include "opentelemetry/proto/collector/logs/v1/logs_service.grpc.pb.h" +# include "opentelemetry/common/spin_lock_mutex.h" # include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" // clang-format on @@ -78,6 +79,8 @@ class OtlpGrpcLogExporter : public opentelemetry::sdk::logs::LogExporter */ OtlpGrpcLogExporter(std::unique_ptr stub); bool is_shutdown_ = false; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; }; } // namespace otlp diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h index ff2a943554..35939204b3 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h @@ -9,6 +9,7 @@ #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/ext/http/client/http_client.h" #include "opentelemetry/sdk/common/exporter_utils.h" @@ -124,6 +125,8 @@ class OtlpHttpClient std::shared_ptr http_client_; // Cached parsed URI std::string http_uri_; + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; }; } // namespace otlp } // namespace exporter diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h index 368505ca2a..b6d02bddcd 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h @@ -9,6 +9,7 @@ # include "opentelemetry/proto/logs/v1/logs.pb.h" # include "opentelemetry/proto/resource/v1/resource.pb.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" # include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" // clang-format on @@ -40,68 +41,77 @@ class OtlpLogRecordable final : public opentelemetry::sdk::logs::Recordable * Set the timestamp for this log. * @param timestamp the timestamp to set */ - virtual void SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept override; + void SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept override; /** * Set the severity for this log. * @param severity the severity of the event */ - virtual void SetSeverity(opentelemetry::logs::Severity severity) noexcept override; + void SetSeverity(opentelemetry::logs::Severity severity) noexcept override; /** * Set name for this log * @param name the name to set */ - virtual void SetName(nostd::string_view name) noexcept override; + void SetName(nostd::string_view name) noexcept override; /** * Set body field for this log. * @param message the body to set */ - virtual void SetBody(nostd::string_view message) noexcept override; + void SetBody(nostd::string_view message) noexcept override; /** - * Set a single resource of a log record. - * @param key the name of the resource to set - * @param value the resource value to set + * Set Resource of this log + * @param Resource the resource to set */ - virtual void SetResource(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept override; + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override; /** * Set an attribute of a log. * @param key the name of the attribute * @param value the attribute value */ - virtual void SetAttribute(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept override; + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override; /** * Set the trace id for this log. * @param trace_id the trace id to set */ - virtual void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override; + void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override; /** * Set the span id for this log. * @param span_id the span id to set */ - virtual void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override; + void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override; /** * Inject trace_flags for this log. * @param trace_flags the trace flags to set */ - virtual void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override; + void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override; + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept override; + + /** Returns the associated instruementation library */ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & + GetInstrumentationLibrary() const noexcept; private: proto::logs::v1::LogRecord log_record_; - opentelemetry::sdk::common::AttributeMap resource_attributes_; + const opentelemetry::sdk::resource::Resource *resource_ = nullptr; // TODO shared resource // const opentelemetry::sdk::resource::Resource *resource_ = nullptr; - // TODO InstrumentationLibrary - // const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary - // *instrumentation_library_ = nullptr; + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_ = nullptr; }; } // namespace otlp diff --git a/exporters/otlp/src/otlp_grpc_exporter.cc b/exporters/otlp/src/otlp_grpc_exporter.cc index d75174e4bb..0cd6bb8cda 100644 --- a/exporters/otlp/src/otlp_grpc_exporter.cc +++ b/exporters/otlp/src/otlp_grpc_exporter.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h" +#include #include "opentelemetry/exporters/otlp/otlp_recordable.h" #include "opentelemetry/exporters/otlp/otlp_recordable_utils.h" #include "opentelemetry/ext/http/common/url_parser.h" @@ -103,9 +104,10 @@ std::unique_ptr OtlpGrpcExporter::MakeRecordable() noexc sdk::common::ExportResult OtlpGrpcExporter::Export( const nostd::span> &spans) noexcept { - if (is_shutdown_) + if (isShutdown()) { - OTEL_INTERNAL_LOG_ERROR("[OTLP gRPC] Export failed, exporter is shutdown"); + OTEL_INTERNAL_LOG_ERROR("[OTLP gRPC] Exporting " << spans.size() + << " span(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } proto::collector::trace::v1::ExportTraceServiceRequest request; @@ -138,10 +140,17 @@ sdk::common::ExportResult OtlpGrpcExporter::Export( bool OtlpGrpcExporter::Shutdown(std::chrono::microseconds timeout) noexcept { + const std::lock_guard locked(lock_); is_shutdown_ = true; return true; } +bool OtlpGrpcExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/src/otlp_grpc_log_exporter.cc b/exporters/otlp/src/otlp_grpc_log_exporter.cc index e656e595db..4dd84cff11 100644 --- a/exporters/otlp/src/otlp_grpc_log_exporter.cc +++ b/exporters/otlp/src/otlp_grpc_log_exporter.cc @@ -125,9 +125,10 @@ std::unique_ptr OtlpGrpcLogExporter::MakeR opentelemetry::sdk::common::ExportResult OtlpGrpcLogExporter::Export( const nostd::span> &logs) noexcept { - if (is_shutdown_) + if (isShutdown()) { - OTEL_INTERNAL_LOG_ERROR("[OTLP gRPC log] Export failed, exporter is shutdown"); + OTEL_INTERNAL_LOG_ERROR("[OTLP gRPC log] Exporting " << logs.size() + << " log(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } proto::collector::logs::v1::ExportLogsServiceRequest request; @@ -157,10 +158,17 @@ opentelemetry::sdk::common::ExportResult OtlpGrpcLogExporter::Export( bool OtlpGrpcLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept { + const std::lock_guard locked(lock_); is_shutdown_ = true; return true; } +bool OtlpGrpcLogExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/src/otlp_http_client.cc b/exporters/otlp/src/otlp_http_client.cc index dbea61ec4a..fd86b1e136 100644 --- a/exporters/otlp/src/otlp_http_client.cc +++ b/exporters/otlp/src/otlp_http_client.cc @@ -16,6 +16,7 @@ #include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" +#include #include "google/protobuf/message.h" #include "google/protobuf/reflection.h" #include "google/protobuf/stubs/common.h" @@ -361,6 +362,18 @@ static void ConvertGenericMessageToJson(nlohmann::json &value, } } +static bool SerializeToHttpBody(http_client::Body &output, const google::protobuf::Message &message) +{ + auto body_size = message.ByteSizeLong(); + if (body_size > 0) + { + output.resize(body_size); + return message.SerializeWithCachedSizesToArray( + reinterpret_cast(&output[0])); + } + return true; +} + void ConvertGenericFieldToJson(nlohmann::json &value, const google::protobuf::Message &message, const google::protobuf::FieldDescriptor *field_descriptor, @@ -558,7 +571,7 @@ opentelemetry::sdk::common::ExportResult OtlpHttpClient::Export( const google::protobuf::Message &message) noexcept { // Return failure if this exporter has been shutdown - if (is_shutdown_) + if (isShutdown()) { const char *error_message = "[OTLP HTTP Client] Export failed, exporter is shutdown"; if (options_.console_debug) @@ -600,9 +613,7 @@ opentelemetry::sdk::common::ExportResult OtlpHttpClient::Export( std::string content_type; if (options_.content_type == HttpRequestContentType::kBinary) { - body_vec.resize(message.ByteSizeLong()); - if (message.SerializeWithCachedSizesToArray( - reinterpret_cast(&body_vec[0]))) + if (SerializeToHttpBody(body_vec, message)) { if (options_.console_debug) { @@ -682,7 +693,10 @@ opentelemetry::sdk::common::ExportResult OtlpHttpClient::Export( bool OtlpHttpClient::Shutdown(std::chrono::microseconds) noexcept { - is_shutdown_ = true; + { + const std::lock_guard locked(lock_); + is_shutdown_ = true; + } // Shutdown the session manager http_client_->CancelAllSessions(); @@ -691,6 +705,12 @@ bool OtlpHttpClient::Shutdown(std::chrono::microseconds) noexcept return true; } +bool OtlpHttpClient::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/src/otlp_log_recordable.cc b/exporters/otlp/src/otlp_log_recordable.cc index 5276b62690..e859981e9f 100644 --- a/exporters/otlp/src/otlp_log_recordable.cc +++ b/exporters/otlp/src/otlp_log_recordable.cc @@ -18,8 +18,15 @@ namespace otlp proto::resource::v1::Resource OtlpLogRecordable::ProtoResource() const noexcept { proto::resource::v1::Resource proto; - OtlpRecordableUtils::PopulateAttribute( - &proto, opentelemetry::sdk::resource::Resource::Create(resource_attributes_)); + if (nullptr == resource_) + { + OtlpRecordableUtils::PopulateAttribute(&proto, sdk::resource::Resource::GetDefault()); + } + else + { + OtlpRecordableUtils::PopulateAttribute(&proto, *resource_); + } + return proto; } @@ -170,10 +177,9 @@ void OtlpLogRecordable::SetBody(nostd::string_view message) noexcept log_record_.mutable_body()->set_string_value(message.data(), message.size()); } -void OtlpLogRecordable::SetResource(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept +void OtlpLogRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept { - resource_attributes_.SetAttribute(key, value); + resource_ = &resource; } void OtlpLogRecordable::SetAttribute(nostd::string_view key, @@ -199,6 +205,18 @@ void OtlpLogRecordable::SetTraceFlags(opentelemetry::trace::TraceFlags trace_fla log_record_.set_flags(trace_flags.flags()); } +void OtlpLogRecordable::SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept +{ + instrumentation_library_ = &instrumentation_library; +} + +const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & +OtlpLogRecordable::GetInstrumentationLibrary() const noexcept +{ + return *instrumentation_library_; +} } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc b/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc index ec90556e88..c86f5e53ba 100644 --- a/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc +++ b/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc @@ -4,6 +4,22 @@ #include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h" #include "opentelemetry/exporters/otlp/otlp_recordable.h" +#include +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/provider.h" + +#ifdef BAZEL_BUILD +# include "examples/common/foo_library/foo_library.h" +#else +# include "foo_library/foo_library.h" +#endif + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; +namespace trace_sdk = opentelemetry::sdk::trace; +namespace otlp = opentelemetry::exporter::otlp; + #include OPENTELEMETRY_BEGIN_NAMESPACE @@ -171,4 +187,30 @@ BENCHMARK(BM_OtlpExporterDenseSpans); } // namespace exporter OPENTELEMETRY_END_NAMESPACE +namespace +{ +opentelemetry::exporter::otlp::OtlpGrpcExporterOptions opts; +void InitTracer() +{ + // Create OTLP exporter instance + auto exporter = std::unique_ptr(new otlp::OtlpGrpcExporter(opts)); + auto processor = std::unique_ptr( + new trace_sdk::SimpleSpanProcessor(std::move(exporter))); + auto provider = + nostd::shared_ptr(new trace_sdk::TracerProvider(std::move(processor))); + // Set the global trace provider + trace::Provider::SetTracerProvider(provider); +} + +void BM_otlp_grpc_with_collector(benchmark::State &state) +{ + InitTracer(); + while (state.KeepRunning()) + { + foo_library(); + } +} +BENCHMARK(BM_otlp_grpc_with_collector); +} // namespace + BENCHMARK_MAIN(); diff --git a/exporters/otlp/test/otlp_grpc_log_exporter_test.cc b/exporters/otlp/test/otlp_grpc_log_exporter_test.cc index 0f28e46c24..f5f621bf9f 100644 --- a/exporters/otlp/test/otlp_grpc_log_exporter_test.cc +++ b/exporters/otlp/test/otlp_grpc_log_exporter_test.cc @@ -114,16 +114,16 @@ TEST_F(OtlpGrpcLogExporterTestPeer, ExportIntegrationTest) auto exporter = GetExporter(stub_interface); - bool resource_storage_bool_value[] = {true, false, true}; - int32_t resource_storage_int32_value[] = {1, 2}; - uint32_t resource_storage_uint32_value[] = {3, 4}; - int64_t resource_storage_int64_value[] = {5, 6}; - uint64_t resource_storage_uint64_value[] = {7, 8}; - double resource_storage_double_value[] = {3.2, 3.3}; - std::string resource_storage_string_value[] = {"vector", "string"}; + bool attribute_storage_bool_value[] = {true, false, true}; + int32_t attribute_storage_int32_value[] = {1, 2}; + uint32_t attribute_storage_uint32_value[] = {3, 4}; + int64_t attribute_storage_int64_value[] = {5, 6}; + uint64_t attribute_storage_uint64_value[] = {7, 8}; + double attribute_storage_double_value[] = {3.2, 3.3}; + std::string attribute_storage_string_value[] = {"vector", "string"}; auto provider = nostd::shared_ptr(new sdk::logs::LoggerProvider()); - provider->SetProcessor(std::unique_ptr( + provider->AddProcessor(std::unique_ptr( new sdk::logs::BatchLogProcessor(std::move(exporter), 5, std::chrono::milliseconds(256), 1))); EXPECT_CALL(*mock_stub, Export(_, _, _)) @@ -137,7 +137,8 @@ TEST_F(OtlpGrpcLogExporterTestPeer, ExportIntegrationTest) '3', '2', '1', '0'}; opentelemetry::trace::SpanId span_id{span_id_bin}; - auto logger = provider->GetLogger("test"); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = provider->GetLogger("test", "", "opentelelemtry_library", "", schema_url); logger->Log(opentelemetry::logs::Severity::kInfo, "Log name", "Log message", {{"service.name", "unit_test_service"}, {"tenant.id", "test_user"}, @@ -147,14 +148,14 @@ TEST_F(OtlpGrpcLogExporterTestPeer, ExportIntegrationTest) {"int64_value", static_cast(0x1100000000LL)}, {"uint64_value", static_cast(0x1200000000ULL)}, {"double_value", static_cast(3.1)}, - {"vec_bool_value", resource_storage_bool_value}, - {"vec_int32_value", resource_storage_int32_value}, - {"vec_uint32_value", resource_storage_uint32_value}, - {"vec_int64_value", resource_storage_int64_value}, - {"vec_uint64_value", resource_storage_uint64_value}, - {"vec_double_value", resource_storage_double_value}, - {"vec_string_value", resource_storage_string_value}}, - {{"log_attribute", "test_value"}}, trace_id, span_id, + {"vec_bool_value", attribute_storage_bool_value}, + {"vec_int32_value", attribute_storage_int32_value}, + {"vec_uint32_value", attribute_storage_uint32_value}, + {"vec_int64_value", attribute_storage_int64_value}, + {"vec_uint64_value", attribute_storage_uint64_value}, + {"vec_double_value", attribute_storage_double_value}, + {"vec_string_value", attribute_storage_string_value}}, + trace_id, span_id, opentelemetry::trace::TraceFlags{opentelemetry::trace::TraceFlags::kIsSampled}, std::chrono::system_clock::now()); } diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index 60c1766eea..446b9aec29 100755 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -240,7 +240,7 @@ TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest) report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex)); } - ASSERT_TRUE(waitForRequests(8, old_count + 1)); + ASSERT_TRUE(waitForRequests(30, old_count + 1)); auto check_json = received_requests_json_.back(); auto resource_span = *check_json["resource_spans"].begin(); auto instrumentation_library_span = *resource_span["instrumentation_library_spans"].begin(); @@ -310,7 +310,7 @@ TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest) report_trace_id.assign(reinterpret_cast(trace_id_binary), sizeof(trace_id_binary)); } - ASSERT_TRUE(waitForRequests(8, old_count + 1)); + ASSERT_TRUE(waitForRequests(30, old_count + 1)); auto received_trace_id = received_requests_binary_.back() .resource_spans(0) diff --git a/exporters/otlp/test/otlp_http_log_exporter_test.cc b/exporters/otlp/test/otlp_http_log_exporter_test.cc index 536636033e..c3c9095ec2 100755 --- a/exporters/otlp/test/otlp_http_log_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_log_exporter_test.cc @@ -199,16 +199,16 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportJsonIntegrationTest) size_t old_count = getCurrentRequestCount(); auto exporter = GetExporter(HttpRequestContentType::kJson); - bool resource_storage_bool_value[] = {true, false, true}; - int32_t resource_storage_int32_value[] = {1, 2}; - uint32_t resource_storage_uint32_value[] = {3, 4}; - int64_t resource_storage_int64_value[] = {5, 6}; - uint64_t resource_storage_uint64_value[] = {7, 8}; - double resource_storage_double_value[] = {3.2, 3.3}; - std::string resource_storage_string_value[] = {"vector", "string"}; + bool attribute_storage_bool_value[] = {true, false, true}; + int32_t attribute_storage_int32_value[] = {1, 2}; + uint32_t attribute_storage_uint32_value[] = {3, 4}; + int64_t attribute_storage_int64_value[] = {5, 6}; + uint64_t attribute_storage_uint64_value[] = {7, 8}; + double attribute_storage_double_value[] = {3.2, 3.3}; + std::string attribute_storage_string_value[] = {"vector", "string"}; auto provider = nostd::shared_ptr(new sdk::logs::LoggerProvider()); - provider->SetProcessor(std::unique_ptr( + provider->AddProcessor(std::unique_ptr( new sdk::logs::BatchLogProcessor(std::move(exporter), 5, std::chrono::milliseconds(256), 1))); std::string report_trace_id; @@ -223,7 +223,8 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportJsonIntegrationTest) char span_id_hex[2 * opentelemetry::trace::SpanId::kSize] = {0}; opentelemetry::trace::SpanId span_id{span_id_bin}; - auto logger = provider->GetLogger("test"); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = provider->GetLogger("test", "", "opentelelemtry_library", "", schema_url); logger->Log(opentelemetry::logs::Severity::kInfo, "Log name", "Log message", {{"service.name", "unit_test_service"}, {"tenant.id", "test_user"}, @@ -233,14 +234,14 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportJsonIntegrationTest) {"int64_value", static_cast(0x1100000000LL)}, {"uint64_value", static_cast(0x1200000000ULL)}, {"double_value", static_cast(3.1)}, - {"vec_bool_value", resource_storage_bool_value}, - {"vec_int32_value", resource_storage_int32_value}, - {"vec_uint32_value", resource_storage_uint32_value}, - {"vec_int64_value", resource_storage_int64_value}, - {"vec_uint64_value", resource_storage_uint64_value}, - {"vec_double_value", resource_storage_double_value}, - {"vec_string_value", resource_storage_string_value}}, - {{"log_attribute", "test_value"}}, trace_id, span_id, + {"vec_bool_value", attribute_storage_bool_value}, + {"vec_int32_value", attribute_storage_int32_value}, + {"vec_uint32_value", attribute_storage_uint32_value}, + {"vec_int64_value", attribute_storage_int64_value}, + {"vec_uint64_value", attribute_storage_uint64_value}, + {"vec_double_value", attribute_storage_double_value}, + {"vec_string_value", attribute_storage_string_value}}, + trace_id, span_id, opentelemetry::trace::TraceFlags{opentelemetry::trace::TraceFlags::kIsSampled}, std::chrono::system_clock::now()); @@ -251,7 +252,7 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportJsonIntegrationTest) report_span_id.assign(span_id_hex, sizeof(span_id_hex)); } - ASSERT_TRUE(waitForRequests(8, old_count + 1)); + ASSERT_TRUE(waitForRequests(30, old_count + 1)); auto check_json = received_requests_json_.back(); auto resource_logs = *check_json["resource_logs"].begin(); auto instrumentation_library_span = *resource_logs["instrumentation_library_logs"].begin(); @@ -262,10 +263,9 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportJsonIntegrationTest) EXPECT_EQ(received_span_id, report_span_id); EXPECT_EQ("Log name", log["name"].get()); EXPECT_EQ("Log message", log["body"]["string_value"].get()); - EXPECT_EQ("test_value", (*log["attributes"].begin())["value"]["string_value"].get()); - EXPECT_LE(15, resource_logs["resource"]["attributes"].size()); + EXPECT_LE(15, log["attributes"].size()); bool check_service_name = false; - for (auto attribute : resource_logs["resource"]["attributes"]) + for (auto attribute : log["attributes"]) { if ("service.name" == attribute["key"].get()) { @@ -292,16 +292,16 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportBinaryIntegrationTest) auto exporter = GetExporter(HttpRequestContentType::kBinary); - bool resource_storage_bool_value[] = {true, false, true}; - int32_t resource_storage_int32_value[] = {1, 2}; - uint32_t resource_storage_uint32_value[] = {3, 4}; - int64_t resource_storage_int64_value[] = {5, 6}; - uint64_t resource_storage_uint64_value[] = {7, 8}; - double resource_storage_double_value[] = {3.2, 3.3}; - std::string resource_storage_string_value[] = {"vector", "string"}; + bool attribute_storage_bool_value[] = {true, false, true}; + int32_t attribute_storage_int32_value[] = {1, 2}; + uint32_t attribute_storage_uint32_value[] = {3, 4}; + int64_t attribute_storage_int64_value[] = {5, 6}; + uint64_t attribute_storage_uint64_value[] = {7, 8}; + double attribute_storage_double_value[] = {3.2, 3.3}; + std::string attribute_storage_string_value[] = {"vector", "string"}; auto provider = nostd::shared_ptr(new sdk::logs::LoggerProvider()); - provider->SetProcessor(std::unique_ptr( + provider->AddProcessor(std::unique_ptr( new sdk::logs::BatchLogProcessor(std::move(exporter), 5, std::chrono::milliseconds(256), 1))); std::string report_trace_id; @@ -314,7 +314,8 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportBinaryIntegrationTest) '3', '2', '1', '0'}; opentelemetry::trace::SpanId span_id{span_id_bin}; - auto logger = provider->GetLogger("test"); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = provider->GetLogger("test", "", "opentelelemtry_library", "", schema_url); logger->Log(opentelemetry::logs::Severity::kInfo, "Log name", "Log message", {{"service.name", "unit_test_service"}, {"tenant.id", "test_user"}, @@ -324,14 +325,14 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportBinaryIntegrationTest) {"int64_value", static_cast(0x1100000000LL)}, {"uint64_value", static_cast(0x1200000000ULL)}, {"double_value", static_cast(3.1)}, - {"vec_bool_value", resource_storage_bool_value}, - {"vec_int32_value", resource_storage_int32_value}, - {"vec_uint32_value", resource_storage_uint32_value}, - {"vec_int64_value", resource_storage_int64_value}, - {"vec_uint64_value", resource_storage_uint64_value}, - {"vec_double_value", resource_storage_double_value}, - {"vec_string_value", resource_storage_string_value}}, - {{"log_attribute", "test_value"}}, trace_id, span_id, + {"vec_bool_value", attribute_storage_bool_value}, + {"vec_int32_value", attribute_storage_int32_value}, + {"vec_uint32_value", attribute_storage_uint32_value}, + {"vec_int64_value", attribute_storage_int64_value}, + {"vec_uint64_value", attribute_storage_uint64_value}, + {"vec_double_value", attribute_storage_double_value}, + {"vec_string_value", attribute_storage_string_value}}, + trace_id, span_id, opentelemetry::trace::TraceFlags{opentelemetry::trace::TraceFlags::kIsSampled}, std::chrono::system_clock::now()); @@ -339,17 +340,16 @@ TEST_F(OtlpHttpLogExporterTestPeer, ExportBinaryIntegrationTest) report_span_id.assign(reinterpret_cast(span_id_bin), sizeof(span_id_bin)); } - ASSERT_TRUE(waitForRequests(8, old_count + 1)); + ASSERT_TRUE(waitForRequests(30, old_count + 1)); auto received_log = received_requests_binary_.back().resource_logs(0).instrumentation_library_logs(0).logs(0); EXPECT_EQ(received_log.trace_id(), report_trace_id); EXPECT_EQ(received_log.span_id(), report_span_id); EXPECT_EQ("Log name", received_log.name()); EXPECT_EQ("Log message", received_log.body().string_value()); - EXPECT_EQ("test_value", received_log.attributes(0).value().string_value()); - EXPECT_LE(15, received_requests_binary_.back().resource_logs(0).resource().attributes_size()); + EXPECT_LE(15, received_log.attributes_size()); bool check_service_name = false; - for (auto &attribute : received_requests_binary_.back().resource_logs(0).resource().attributes()) + for (auto &attribute : received_log.attributes()) { if ("service.name" == attribute.key()) { diff --git a/exporters/otlp/test/otlp_log_recordable_test.cc b/exporters/otlp/test/otlp_log_recordable_test.cc index 808d04fafe..a4945d394b 100644 --- a/exporters/otlp/test/otlp_log_recordable_test.cc +++ b/exporters/otlp/test/otlp_log_recordable_test.cc @@ -86,7 +86,9 @@ TEST(OtlpLogRecordable, SetResource) OtlpLogRecordable rec; const std::string service_name_key = "service.name"; std::string service_name = "test-otlp"; - rec.SetResource(service_name_key, service_name); + auto resource = + opentelemetry::sdk::resource::Resource::Create({{service_name_key, service_name}}); + rec.SetResource(resource); auto proto_resource = rec.ProtoResource(); bool found_service_name = false; @@ -188,6 +190,15 @@ TEST(OtlpLogRecordable, SetArrayAttribute) } } +TEST(OtlpLogRecordable, SetInstrumentationLibrary) +{ + OtlpLogRecordable rec; + auto inst_lib = + opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create("test", "v1"); + rec.SetInstrumentationLibrary(*inst_lib); + EXPECT_EQ(rec.GetInstrumentationLibrary(), *inst_lib); +} + /** * AttributeValue can contain different int types, such as int, int64_t, * unsigned int, and uint64_t. To avoid writing test cases for each, we can diff --git a/exporters/zipkin/BUILD b/exporters/zipkin/BUILD index 2049829d73..6cd52b2d05 100644 --- a/exporters/zipkin/BUILD +++ b/exporters/zipkin/BUILD @@ -48,3 +48,17 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "zipkin_exporter_test", + srcs = ["test/zipkin_exporter_test.cc"], + tags = [ + "test", + "zipkin", + ], + deps = [ + ":zipkin_exporter", + ":zipkin_recordable", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/exporters/zipkin/CMakeLists.txt b/exporters/zipkin/CMakeLists.txt index 41efc826cd..b9591324fd 100644 --- a/exporters/zipkin/CMakeLists.txt +++ b/exporters/zipkin/CMakeLists.txt @@ -37,6 +37,8 @@ install( PATTERN "recordable.h" EXCLUDE) if(BUILD_TESTING) + add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1) + add_executable(zipkin_recordable_test test/zipkin_recordable_test.cc) target_link_libraries( @@ -47,4 +49,26 @@ if(BUILD_TESTING) TARGET zipkin_recordable_test TEST_PREFIX exporter. TEST_LIST zipkin_recordable_test) + + if(MSVC) + if(GMOCK_LIB) + unset(GMOCK_LIB CACHE) + endif() + endif() + if(MSVC AND CMAKE_BUILD_TYPE STREQUAL "Debug") + find_library(GMOCK_LIB gmockd PATH_SUFFIXES lib) + else() + find_library(GMOCK_LIB gmock PATH_SUFFIXES lib) + endif() + + add_executable(zipkin_exporter_test test/zipkin_exporter_test.cc) + + target_link_libraries( + zipkin_exporter_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + ${GMOCK_LIB} opentelemetry_exporter_zipkin_trace opentelemetry_resources) + + gtest_add_tests( + TARGET zipkin_exporter_test + TEST_PREFIX exporter. + TEST_LIST zipkin_exporter_test) endif() # BUILD_TESTING diff --git a/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h b/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h index aba926165f..ae0e8173f9 100644 --- a/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h +++ b/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h @@ -3,6 +3,7 @@ #pragma once +#include "opentelemetry/common/spin_lock_mutex.h" #include "opentelemetry/ext/http/client/http_client_factory.h" #include "opentelemetry/ext/http/common/url_parser.h" #include "opentelemetry/sdk/common/env_variables.h" @@ -89,11 +90,23 @@ class ZipkinExporter final : public opentelemetry::sdk::trace::SpanExporter private: // The configuration options associated with this exporter. - bool isShutdown_ = false; + bool is_shutdown_ = false; ZipkinExporterOptions options_; std::shared_ptr http_client_; opentelemetry::ext::http::common::UrlParser url_parser_; nlohmann::json local_end_point_; + + // For testing + friend class ZipkinExporterTestPeer; + /** + * Create an ZipkinExporter using the specified thrift sender. + * Only tests can call this constructor directly. + * @param http_client the http client to be used for exporting + */ + ZipkinExporter(std::shared_ptr http_client); + + mutable opentelemetry::common::SpinLockMutex lock_; + bool isShutdown() const noexcept; }; } // namespace zipkin } // namespace exporter diff --git a/exporters/zipkin/src/zipkin_exporter.cc b/exporters/zipkin/src/zipkin_exporter.cc index cbd49a344b..240144599f 100644 --- a/exporters/zipkin/src/zipkin_exporter.cc +++ b/exporters/zipkin/src/zipkin_exporter.cc @@ -1,7 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#define _WINSOCKAPI_ // stops including winsock.h #include "opentelemetry/exporters/zipkin/zipkin_exporter.h" +#include #include "opentelemetry/exporters/zipkin/recordable.h" #include "opentelemetry/ext/http/client/http_client_factory.h" #include "opentelemetry/ext/http/common/url_parser.h" @@ -30,6 +32,14 @@ ZipkinExporter::ZipkinExporter() : options_(ZipkinExporterOptions()), url_parser InitializeLocalEndpoint(); } +ZipkinExporter::ZipkinExporter( + std::shared_ptr http_client) + : options_(ZipkinExporterOptions()), url_parser_(options_.endpoint) +{ + http_client_ = http_client; + InitializeLocalEndpoint(); +} + // ----------------------------- Exporter methods ------------------------------ std::unique_ptr ZipkinExporter::MakeRecordable() noexcept @@ -40,8 +50,10 @@ std::unique_ptr ZipkinExporter::MakeRecordable() noexcep sdk::common::ExportResult ZipkinExporter::Export( const nostd::span> &spans) noexcept { - if (isShutdown_) + if (isShutdown()) { + OTEL_INTERNAL_LOG_ERROR("[Zipkin Trace Exporter] Exporting " + << spans.size() << " span(s) failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } exporter::zipkin::ZipkinSpan json_spans = {}; @@ -100,10 +112,17 @@ void ZipkinExporter::InitializeLocalEndpoint() bool ZipkinExporter::Shutdown(std::chrono::microseconds timeout) noexcept { - isShutdown_ = true; + const std::lock_guard locked(lock_); + is_shutdown_ = true; return true; } +bool ZipkinExporter::isShutdown() const noexcept +{ + const std::lock_guard locked(lock_); + return is_shutdown_; +} + } // namespace zipkin } // namespace exporter OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/zipkin/test/zipkin_exporter_test.cc b/exporters/zipkin/test/zipkin_exporter_test.cc new file mode 100644 index 0000000000..67d5c1c8ce --- /dev/null +++ b/exporters/zipkin/test/zipkin_exporter_test.cc @@ -0,0 +1,310 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifndef HAVE_CPP_STDLIB + +# include "opentelemetry/exporters/zipkin/zipkin_exporter.h" +# include +# include "opentelemetry/ext/http/client/curl/http_client_curl.h" +# include "opentelemetry/ext/http/server/http_server.h" +# include "opentelemetry/sdk/trace/batch_span_processor.h" +# include "opentelemetry/sdk/trace/tracer_provider.h" +# include "opentelemetry/trace/provider.h" + +# include +# include "gmock/gmock.h" + +# include "nlohmann/json.hpp" + +# if defined(_MSC_VER) +# include "opentelemetry/sdk/common/env_variables.h" +using opentelemetry::sdk::common::setenv; +using opentelemetry::sdk::common::unsetenv; +# endif +namespace sdk_common = opentelemetry::sdk::common; +using namespace testing; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace zipkin +{ + +namespace trace_api = opentelemetry::trace; +namespace resource = opentelemetry::sdk::resource; + +template +static nostd::span MakeSpan(T (&array)[N]) +{ + return nostd::span(array); +} + +class ZipkinExporterTestPeer : public ::testing::Test, HTTP_SERVER_NS::HttpRequestCallback +{ +protected: + HTTP_SERVER_NS::HttpServer server_; + std::string server_address_; + std::atomic is_setup_; + std::atomic is_running_; + std::mutex mtx_requests; + std::condition_variable cv_got_events; + std::vector received_requests_json_; + std::map received_requests_headers_; + +public: + ZipkinExporterTestPeer() : is_setup_(false), is_running_(false){}; + + virtual void SetUp() override + { + if (is_setup_.exchange(true)) + { + return; + } + int port = server_.addListeningPort(14371); + std::ostringstream os; + os << "localhost:" << port; + server_address_ = "http://" + os.str() + "/v1/traces"; + server_.setServerName(os.str()); + server_.setKeepalive(false); + server_.addHandler("/v1/traces", *this); + server_.start(); + is_running_ = true; + } + + virtual void TearDown() override + { + if (!is_setup_.exchange(false)) + return; + server_.stop(); + is_running_ = false; + } + + virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, + HTTP_SERVER_NS::HttpResponse &response) override + { + const std::string *request_content_type = nullptr; + { + auto it = request.headers.find("Content-Type"); + if (it != request.headers.end()) + { + request_content_type = &it->second; + } + } + received_requests_headers_ = request.headers; + + int response_status = 0; + std::string kHttpJsonContentType{"application/json"}; + if (request.uri == "/v1/traces") + { + response.headers["Content-Type"] = kHttpJsonContentType; + std::unique_lock lk(mtx_requests); + if (nullptr != request_content_type && *request_content_type == kHttpJsonContentType) + { + auto json = nlohmann::json::parse(request.content, nullptr, false); + response.headers["Content-Type"] = kHttpJsonContentType; + if (json.is_discarded()) + { + response.body = "{\"code\": 400, \"message\": \"Parse json failed\"}"; + response_status = 400; + } + else + { + received_requests_json_.push_back(json); + response.body = "{\"code\": 0, \"message\": \"success\"}"; + } + } + else + { + response.body = "{\"code\": 400, \"message\": \"Unsupported content type\"}"; + response_status = 400; + } + + response_status = 200; + } + else + { + std::unique_lock lk(mtx_requests); + response.headers["Content-Type"] = "text/plain"; + response.body = "404 Not Found"; + response_status = 200; + } + + cv_got_events.notify_one(); + + return response_status; + } + + bool waitForRequests(unsigned timeOutSec, size_t expected_count = 1) + { + std::unique_lock lk(mtx_requests); + if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), + [&] { return getCurrentRequestCount() >= expected_count; })) + { + return true; + } + return false; + } + + size_t getCurrentRequestCount() const { return received_requests_json_.size(); } + +public: + std::unique_ptr GetExporter() + { + ZipkinExporterOptions opts; + opts.endpoint = server_address_; + opts.headers.insert( + std::make_pair("Custom-Header-Key", "Custom-Header-Value")); + return std::unique_ptr(new ZipkinExporter(opts)); + } + + std::unique_ptr GetExporter( + std::shared_ptr http_client) + { + return std::unique_ptr(new ZipkinExporter(http_client)); + } + + // Get the options associated with the given exporter. + const ZipkinExporterOptions &GetOptions(std::unique_ptr &exporter) + { + return exporter->options_; + } +}; + +class MockHttpClient : public opentelemetry::ext::http::client::HttpClientSync +{ +public: + MOCK_METHOD(ext::http::client::Result, + Post, + (const nostd::string_view &, + const ext::http::client::Body &, + const ext::http::client::Headers &), + (noexcept, override)); + MOCK_METHOD(ext::http::client::Result, + Get, + (const nostd::string_view &, const ext::http::client::Headers &), + (noexcept, override)); +}; + +// Create spans, let processor call Export() +TEST_F(ZipkinExporterTestPeer, ExportJsonIntegrationTest) +{ + size_t old_count = getCurrentRequestCount(); + auto exporter = GetExporter(); + + resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"}, + {"tenant.id", "test_user"}}; + resource_attributes["bool_value"] = true; + resource_attributes["int32_value"] = static_cast(1); + resource_attributes["uint32_value"] = static_cast(2); + resource_attributes["int64_value"] = static_cast(0x1100000000LL); + resource_attributes["uint64_value"] = static_cast(0x1200000000ULL); + resource_attributes["double_value"] = static_cast(3.1); + resource_attributes["vec_bool_value"] = std::vector{true, false, true}; + resource_attributes["vec_int32_value"] = std::vector{1, 2}; + resource_attributes["vec_uint32_value"] = std::vector{3, 4}; + resource_attributes["vec_int64_value"] = std::vector{5, 6}; + resource_attributes["vec_uint64_value"] = std::vector{7, 8}; + resource_attributes["vec_double_value"] = std::vector{3.2, 3.3}; + resource_attributes["vec_string_value"] = std::vector{"vector", "string"}; + auto resource = resource::Resource::Create(resource_attributes); + + auto processor_opts = sdk::trace::BatchSpanProcessorOptions(); + processor_opts.max_export_batch_size = 5; + processor_opts.max_queue_size = 5; + processor_opts.schedule_delay_millis = std::chrono::milliseconds(256); + auto processor = std::unique_ptr( + new sdk::trace::BatchSpanProcessor(std::move(exporter), processor_opts)); + auto provider = nostd::shared_ptr( + new sdk::trace::TracerProvider(std::move(processor), resource)); + + std::string report_trace_id; + { + char trace_id_hex[2 * trace_api::TraceId::kSize] = {0}; + auto tracer = provider->GetTracer("test"); + auto parent_span = tracer->StartSpan("Test parent span"); + + trace_api::StartSpanOptions child_span_opts = {}; + child_span_opts.parent = parent_span->GetContext(); + + auto child_span = tracer->StartSpan("Test child span", child_span_opts); + child_span->End(); + parent_span->End(); + + nostd::get(child_span_opts.parent) + .trace_id() + .ToLowerBase16(MakeSpan(trace_id_hex)); + report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex)); + } + + ASSERT_TRUE(waitForRequests(30, old_count + 1)); + auto check_json = received_requests_json_.back(); + auto trace_id_kv = check_json.at(0).find("traceId"); + auto received_trace_id = trace_id_kv.value().get(); + EXPECT_EQ(received_trace_id, report_trace_id); + { + auto custom_header = received_requests_headers_.find("Custom-Header-Key"); + ASSERT_TRUE(custom_header != received_requests_headers_.end()); + if (custom_header != received_requests_headers_.end()) + { + EXPECT_EQ("Custom-Header-Value", custom_header->second); + } + } +} + +// Create spans, let processor call Export() +TEST_F(ZipkinExporterTestPeer, ShutdownTest) +{ + auto mock_http_client = new MockHttpClient; + // Leave a comment line here or different version of clang-format has a different result here + auto exporter = GetExporter( + std::shared_ptr{mock_http_client}); + auto recordable_1 = exporter->MakeRecordable(); + recordable_1->SetName("Test span 1"); + auto recordable_2 = exporter->MakeRecordable(); + recordable_2->SetName("Test span 2"); + + // exporter shuold not be shutdown by default + nostd::span> batch_1(&recordable_1, 1); + EXPECT_CALL(*mock_http_client, Post(_, _, _)) + .Times(Exactly(1)) + .WillOnce(Return(ByMove(std::move(ext::http::client::Result{ + std::unique_ptr{new ext::http::client::curl::Response()}, + ext::http::client::SessionState::Response})))); + auto result = exporter->Export(batch_1); + EXPECT_EQ(sdk_common::ExportResult::kSuccess, result); + + exporter->Shutdown(); + + nostd::span> batch_2(&recordable_2, 1); + result = exporter->Export(batch_2); + EXPECT_EQ(sdk_common::ExportResult::kFailure, result); +} + +// Test exporter configuration options +TEST_F(ZipkinExporterTestPeer, ConfigTest) +{ + ZipkinExporterOptions opts; + opts.endpoint = "http://localhost:45455/v1/traces"; + std::unique_ptr exporter(new ZipkinExporter(opts)); + EXPECT_EQ(GetOptions(exporter).endpoint, "http://localhost:45455/v1/traces"); +} + +# ifndef NO_GETENV +// Test exporter configuration options from env +TEST_F(ZipkinExporterTestPeer, ConfigFromEnv) +{ + const std::string endpoint = "http://localhost:9999/v1/traces"; + setenv("OTEL_EXPORTER_ZIPKIN_ENDPOINT", endpoint.c_str(), 1); + + std::unique_ptr exporter(new ZipkinExporter()); + EXPECT_EQ(GetOptions(exporter).endpoint, endpoint); + + unsetenv("OTEL_EXPORTER_ZIPKIN_ENDPOINT"); +} + +# endif // NO_GETENV + +} // namespace zipkin +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE +#endif // HAVE_CPP_STDLIB diff --git a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h index fa727899e4..5aaaa0a0e5 100644 --- a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h @@ -7,7 +7,6 @@ #include "opentelemetry/ext/http/client/http_client.h" #include "opentelemetry/version.h" -#include #include #include #include @@ -16,9 +15,11 @@ #include #ifdef _WIN32 # include +# include #else # include #endif +#include OPENTELEMETRY_BEGIN_NAMESPACE namespace ext diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 2101a8bdd5..bc89b3277e 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -198,7 +198,7 @@ TEST_F(BasicCurlHttpTests, SendGetRequest) request->SetUri("get/"); GetEventHandler *handler = new GetEventHandler(); session->SendRequest(*handler); - ASSERT_TRUE(waitForRequests(1, 1)); + ASSERT_TRUE(waitForRequests(30, 1)); session->FinishSession(); ASSERT_TRUE(handler->is_called_); delete handler; @@ -221,7 +221,7 @@ TEST_F(BasicCurlHttpTests, SendPostRequest) request->AddHeader("Content-Type", "text/plain"); PostEventHandler *handler = new PostEventHandler(); session->SendRequest(*handler); - ASSERT_TRUE(waitForRequests(1, 1)); + ASSERT_TRUE(waitForRequests(30, 1)); session->FinishSession(); ASSERT_TRUE(handler->is_called_); diff --git a/sdk/include/opentelemetry/sdk/common/empty_attributes.h b/sdk/include/opentelemetry/sdk/common/empty_attributes.h index 2b88751ffc..4f740bf10e 100644 --- a/sdk/include/opentelemetry/sdk/common/empty_attributes.h +++ b/sdk/include/opentelemetry/sdk/common/empty_attributes.h @@ -4,6 +4,7 @@ #pragma once #include "opentelemetry/common/key_value_iterable_view.h" +#include "opentelemetry/common/macros.h" #include #include @@ -18,7 +19,8 @@ namespace sdk * This helps to avoid constructing a new empty container every time a call is made * with default attributes. */ -static const opentelemetry::common::KeyValueIterableView, 0>> +OPENTELEMETRY_MAYBE_UNUSED static const opentelemetry::common::KeyValueIterableView< + std::array, 0>> &GetEmptyAttributes() noexcept { static const std::array, 0> array{}; diff --git a/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h b/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h index eeea6cd7aa..b288aa5b71 100644 --- a/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h +++ b/sdk/include/opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h @@ -3,6 +3,7 @@ #pragma once +#include #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/version.h" @@ -30,8 +31,8 @@ class InstrumentationLibrary nostd::string_view version = "", nostd::string_view schema_url = "") { - return nostd::unique_ptr(new InstrumentationLibrary{ - std::string{name}, std::string{version}, std::string{schema_url}}); + return nostd::unique_ptr( + new InstrumentationLibrary{name, version, schema_url}); } /** diff --git a/sdk/include/opentelemetry/sdk/logs/log_record.h b/sdk/include/opentelemetry/sdk/logs/log_record.h index 34ef5befb0..502a87a6f0 100644 --- a/sdk/include/opentelemetry/sdk/logs/log_record.h +++ b/sdk/include/opentelemetry/sdk/logs/log_record.h @@ -8,6 +8,7 @@ # include # include "opentelemetry/sdk/common/attribute_utils.h" # include "opentelemetry/sdk/logs/recordable.h" +# include "opentelemetry/sdk/resource/resource.h" # include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -27,8 +28,8 @@ class LogRecord final : public Recordable private: // Default values are set by the respective data structures' constructors for all fields, // except the severity field, which must be set manually (an enum with no default value). - opentelemetry::logs::Severity severity_ = opentelemetry::logs::Severity::kInvalid; - common::AttributeMap resource_map_; + opentelemetry::logs::Severity severity_ = opentelemetry::logs::Severity::kInvalid; + const opentelemetry::sdk::resource::Resource *resource_ = nullptr; common::AttributeMap attributes_map_; std::string name_; std::string body_; // Currently a simple string, but should be changed to "Any" type @@ -62,14 +63,12 @@ class LogRecord final : public Recordable void SetBody(nostd::string_view message) noexcept override { body_ = std::string(message); } /** - * Set a resource for this log. - * @param name the name of the resource - * @param value the resource value + * Set Resource of this log + * @param Resource the resource to set */ - void SetResource(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept override + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override { - resource_map_.SetAttribute(key, value); + resource_ = &resource; } /** @@ -141,12 +140,16 @@ class LogRecord final : public Recordable std::string GetBody() const noexcept { return body_; } /** - * Get the resource field for this log - * @return the resource field for this log + * Get the resource for this log + * @return the resource for this log */ - const std::unordered_map &GetResource() const noexcept + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept { - return resource_map_.GetAttributes(); + if (nullptr == resource_) + { + return sdk::resource::Resource::GetDefault(); + } + return *resource_; } /** @@ -181,6 +184,21 @@ class LogRecord final : public Recordable * @return the timestamp for this log */ opentelemetry::common::SystemTimestamp GetTimestamp() const noexcept { return timestamp_; } + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept + { + instrumentation_library_ = &instrumentation_library; + } + +private: + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_ = nullptr; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index 4fd1ed89ed..f6c9e0a226 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -5,8 +5,9 @@ #ifdef ENABLE_LOGS_PREVIEW # include "opentelemetry/logs/logger.h" +# include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h" +# include "opentelemetry/sdk/logs/logger_context.h" # include "opentelemetry/sdk/logs/logger_provider.h" -# include "opentelemetry/sdk/logs/processor.h" # include @@ -23,10 +24,13 @@ class Logger final : public opentelemetry::logs::Logger /** * Initialize a new logger. * @param name The name of this logger instance - * @param logger_provider The logger provider that owns this logger. + * @param context The logger provider that owns this logger. */ - explicit Logger(opentelemetry::nostd::string_view name, - std::shared_ptr logger_provider) noexcept; + explicit Logger( + opentelemetry::nostd::string_view name, + std::shared_ptr context, + std::unique_ptr instrumentation_library = + instrumentationlibrary::InstrumentationLibrary::Create("")) noexcept; /** * Returns the name of this logger. @@ -38,7 +42,6 @@ class Logger final : public opentelemetry::logs::Logger * @param severity the severity level of the log event. * @param name the name of the log event. * @param message the string message of the log (perhaps support std::fmt or fmt-lib format). - * @param resource the resources, stored as a 2D list of key/value pairs, that are associated * with the log event. * @param attributes the attributes, stored as a 2D list of key/value pairs, that are associated * with the log event. @@ -50,20 +53,23 @@ class Logger final : public opentelemetry::logs::Logger void Log(opentelemetry::logs::Severity severity, nostd::string_view name, nostd::string_view body, - const opentelemetry::common::KeyValueIterable &resource, const opentelemetry::common::KeyValueIterable &attributes, opentelemetry::trace::TraceId trace_id, opentelemetry::trace::SpanId span_id, opentelemetry::trace::TraceFlags trace_flags, opentelemetry::common::SystemTimestamp timestamp) noexcept override; + /** Returns the associated instruementation library */ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & + GetInstrumentationLibrary() const noexcept; + private: // The name of this logger std::string logger_name_; - // The logger provider of this Logger. Uses a weak_ptr to avoid cyclic dependency issues the with - // logger provider - std::weak_ptr logger_provider_; + // The logger context of this Logger. Uses a weak_ptr to avoid cyclic dependency issues the with + std::weak_ptr context_; + std::unique_ptr instrumentation_library_; }; } // namespace logs diff --git a/sdk/include/opentelemetry/sdk/logs/logger_context.h b/sdk/include/opentelemetry/sdk/logs/logger_context.h new file mode 100644 index 0000000000..01c2f7dd89 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/logger_context.h @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * A class which stores the LoggerContext context. + * + * This class meets the following design criteria: + * - A shared reference between LoggerProvider and Logger instantiated. + * - A thread-safe class that allows updating/altering processor/exporter pipelines + * and sampling config. + * - The owner/destroyer of Processors/Exporters. These will remain active until + * this class is destroyed. I.e. Sampling, Exporting, flushing, Custom Iterator etc. are all ok + * if this object is alive, and they will work together. If this object is destroyed, then no shared + * references to Processor, Exporter, Recordable, Custom Iterator etc. should exist, and all + * associated pipelines will have been flushed. + */ +class LoggerContext +{ +public: + explicit LoggerContext(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + + /** + * Attaches a log processor to list of configured processors to this tracer context. + * Processor once attached can't be removed. + * @param processor The new log processor for this tracer. This must not be + * a nullptr. Ownership is given to the `TracerContext`. + * + * Note: This method is not thread safe. + */ + void AddProcessor(std::unique_ptr processor) noexcept; + + /** + * Obtain the configured (composite) processor. + * + * Note: When more than one processor is active, this will + * return an "aggregate" processor + */ + LogProcessor &GetProcessor() const noexcept; + + /** + * Obtain the resource associated with this tracer context. + * @return The resource for this tracer context. + */ + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + + /** + * Force all active LogProcessors to flush any buffered logs + * within the given timeout. + */ + bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; + + /** + * Shutdown the log processor associated with this tracer provider. + */ + bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept; + +private: + // order of declaration is important here - resource object should be destroyed after processor. + opentelemetry::sdk::resource::Resource resource_; + std::unique_ptr processor_; +}; +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider.h b/sdk/include/opentelemetry/sdk/logs/logger_provider.h old mode 100644 new mode 100755 index db07ba5f66..fd0c163b8b --- a/sdk/include/opentelemetry/sdk/logs/logger_provider.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider.h @@ -7,7 +7,6 @@ # include # include # include -# include # include # include "opentelemetry/logs/logger_provider.h" @@ -15,6 +14,7 @@ # include "opentelemetry/nostd/shared_ptr.h" # include "opentelemetry/sdk/common/atomic_shared_ptr.h" # include "opentelemetry/sdk/logs/logger.h" +# include "opentelemetry/sdk/logs/logger_context.h" # include "opentelemetry/sdk/logs/processor.h" // Define the maximum number of loggers that are allowed to be registered to the loggerprovider. @@ -28,62 +28,94 @@ namespace logs { class Logger; -class LoggerProvider final : public opentelemetry::logs::LoggerProvider, - public std::enable_shared_from_this +class LoggerProvider final : public opentelemetry::logs::LoggerProvider { public: + /** + * Initialize a new logger provider + * @param processor The span processor for this logger provider. This must + * not be a nullptr. + * @param resource The resources for this logger provider. + * @param sampler The sampler for this logger provider. This must + * not be a nullptr. + * @param id_generator The custom id generator for this logger provider. This must + * not be a nullptr + */ + explicit LoggerProvider(std::unique_ptr &&processor, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + + explicit LoggerProvider(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource = + opentelemetry::sdk::resource::Resource::Create({})) noexcept; + /** * Initialize a new logger provider. A processor must later be assigned - * to this logger provider via the SetProcessor() method. + * to this logger provider via the AddProcessor() method. */ explicit LoggerProvider() noexcept; + /** + * Initialize a new logger provider with a specified context + * @param context The shared logger configuration/pipeline for this provider. + */ + explicit LoggerProvider(std::shared_ptr context) noexcept; + /** * Creates a logger with the given name, and returns a shared pointer to it. * If a logger with that name already exists, return a shared pointer to it - * @param name The name of the logger to be created. - * @param options (OPTIONAL) The options for the logger. TODO: Once the logging spec defines it, + * @param logger_name The name of the logger to be created. + * @param options The options for the logger. TODO: Once the logging spec defines it, * give a list of options that the logger supports. + * @param library_name The version of the library. + * @param library_version The version of the library. + * @param schema_url The schema URL. */ - opentelemetry::nostd::shared_ptr GetLogger( - opentelemetry::nostd::string_view name, - opentelemetry::nostd::string_view options = "") noexcept override; - + nostd::shared_ptr GetLogger( + nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") noexcept override; /** * Creates a logger with the given name, and returns a shared pointer to it. * If a logger with that name already exists, return a shared pointer to it * @param name The name of the logger to be created. - * @param args (OPTIONAL) The arguments for the logger. TODO: Once the logging spec defines it, + * @param args The arguments for the logger. TODO: Once the logging spec defines it, * give a list of arguments that the logger supports. + * @param library_name The version of the library. + * @param library_version The version of the library. + * @param schema_url The schema URL. */ - opentelemetry::nostd::shared_ptr GetLogger( - opentelemetry::nostd::string_view name, - nostd::span args) noexcept override; + nostd::shared_ptr GetLogger( + nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version = "", + nostd::string_view schema_url = "") noexcept override; /** - * Returns a shared pointer to the processor currently stored in the - * logger provider. If no processor exists, returns a nullptr + * Add the processor that is stored internally in the logger provider. + * @param processor The processor to be stored inside the logger provider. + * This must not be a nullptr. */ - std::shared_ptr GetProcessor() noexcept; + void AddProcessor(std::unique_ptr processor) noexcept; - // Sets the common processor that all the Logger instances will use /** - * Sets the processor that is stored internally in the logger provider. - * @param processor The processor to be stored inside the logger provider. - * This must not be a nullptr. + * Obtain the resource associated with this logger provider. + * @return The resource for this logger provider. */ - void SetProcessor(std::shared_ptr processor) noexcept; + const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; private: // A pointer to the processor stored by this logger provider - opentelemetry::sdk::common::AtomicSharedPtr processor_; + std::shared_ptr context_; // A vector of pointers to all the loggers that have been created - std::unordered_map> - loggers_; + std::vector> loggers_; // A mutex that ensures only one thread is using the map of loggers - std::mutex mu_; + std::mutex lock_; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h b/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h new file mode 100644 index 0000000000..c1bda24a0b --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/multi_log_processor.h @@ -0,0 +1,69 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include + +# include "opentelemetry/sdk/logs/multi_recordable.h" +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +/** + * Log processor allow hooks for receive method invocations. + * + * Built-in log processors are responsible for batching and conversion of + * logs to exportable representation and passing batches to exporters. + */ +class MultiLogProcessor : public LogProcessor +{ +public: + MultiLogProcessor(std::vector> &&processors); + ~MultiLogProcessor(); + + void AddProcessor(std::unique_ptr &&processor); + + std::unique_ptr MakeRecordable() noexcept override; + + /** + * OnReceive is called by the SDK once a log record has been successfully created. + * @param record the log record + */ + void OnReceive(std::unique_ptr &&record) noexcept override; + + /** + * Exports all log records that have not yet been exported to the configured Exporter. + * @param timeout that the forceflush is required to finish within. + * @return a result code indicating whether it succeeded, failed or timed out + */ + bool ForceFlush( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + + /** + * Shuts down the processor and does any cleanup required. + * ShutDown should only be called once for each processor. + * @param timeout minimum amount of microseconds to wait for + * shutdown before giving up and returning failure. + * @return true if the shutdown succeeded, false otherwise + */ + bool Shutdown( + std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override; + +private: + std::vector> processors_; +}; +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/include/opentelemetry/sdk/logs/multi_recordable.h b/sdk/include/opentelemetry/sdk/logs/multi_recordable.h new file mode 100644 index 0000000000..59c018d2d6 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/multi_recordable.h @@ -0,0 +1,110 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#ifdef ENABLE_LOGS_PREVIEW + +# include +# include +# include + +# include "opentelemetry/sdk/logs/processor.h" +# include "opentelemetry/sdk/logs/recordable.h" +# include "opentelemetry/sdk/resource/resource.h" +# include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +class MultiRecordable final : public Recordable +{ +public: + void AddRecordable(const LogProcessor &processor, + std::unique_ptr recordable) noexcept; + + const std::unique_ptr &GetRecordable(const LogProcessor &processor) const noexcept; + + std::unique_ptr ReleaseRecordable(const LogProcessor &processor) noexcept; + + /** + * Set the timestamp for this log. + * @param timestamp the timestamp to set + */ + void SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept override; + + /** + * Set the severity for this log. + * @param severity the severity of the event + */ + void SetSeverity(opentelemetry::logs::Severity severity) noexcept override; + + /** + * Set name for this log + * @param name the name to set + */ + void SetName(nostd::string_view name) noexcept override; + + /** + * Set body field for this log. + * @param message the body to set + */ + void SetBody(nostd::string_view message) noexcept override; + + /** + * Set Resource of this log + * @param Resource the resource to set + */ + void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override; + + /** + * Set an attribute of a log. + * @param key the name of the attribute + * @param value the attribute value + */ + void SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept override; + + /** + * Set the trace id for this log. + * @param trace_id the trace id to set + */ + void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override; + + /** + * Set the span id for this log. + * @param span_id the span id to set + */ + void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override; + + /** + * Inject trace_flags for this log. + * @param trace_flags the trace flags to set + */ + void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override; + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept override; + + /** Returns the associated instruementation library */ + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & + GetInstrumentationLibrary() const noexcept; + +private: + std::unordered_map> recordables_; + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + *instrumentation_library_ = nullptr; +}; +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE + +#endif diff --git a/sdk/include/opentelemetry/sdk/logs/recordable.h b/sdk/include/opentelemetry/sdk/logs/recordable.h index db730752d2..a858e67d62 100644 --- a/sdk/include/opentelemetry/sdk/logs/recordable.h +++ b/sdk/include/opentelemetry/sdk/logs/recordable.h @@ -18,8 +18,6 @@ # include "opentelemetry/trace/trace_id.h" # include "opentelemetry/version.h" -# include - OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -60,12 +58,10 @@ class Recordable virtual void SetBody(nostd::string_view message) noexcept = 0; /** - * Set a single resource of a log record. - * @param key the name of the resource to set - * @param value the resource value to set + * Set Resource of this log + * @param Resource the resource to set */ - virtual void SetResource(nostd::string_view key, - const opentelemetry::common::AttributeValue &value) noexcept = 0; + virtual void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept = 0; /** * Set an attribute of a log. @@ -92,6 +88,14 @@ class Recordable * @param trace_flags the trace flags to set */ virtual void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept = 0; + + /** + * Set instrumentation_library for this log. + * @param instrumentation_library the instrumentation library to set + */ + virtual void SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept = 0; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index 9ff35e0116..77a9b713ba 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -80,12 +80,6 @@ class TracerProvider final : public opentelemetry::trace::TracerProvider */ const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; - /** - * Obtain the span processor associated with this tracer provider. - * @return The span processor for this tracer provider. - */ - std::shared_ptr GetProcessor() const noexcept; - /** * Shutdown the span processor associated with this tracer provider. */ diff --git a/sdk/src/logs/BUILD b/sdk/src/logs/BUILD index c1a0542fa1..ac51799125 100644 --- a/sdk/src/logs/BUILD +++ b/sdk/src/logs/BUILD @@ -22,5 +22,6 @@ cc_library( deps = [ "//api", "//sdk:headers", + "//sdk/src/resource", ], ) diff --git a/sdk/src/logs/CMakeLists.txt b/sdk/src/logs/CMakeLists.txt index 70eccf2f15..20f13324e7 100644 --- a/sdk/src/logs/CMakeLists.txt +++ b/sdk/src/logs/CMakeLists.txt @@ -1,9 +1,17 @@ -add_library(opentelemetry_logs logger_provider.cc logger.cc - simple_log_processor.cc batch_log_processor.cc) +add_library( + opentelemetry_logs + logger_provider.cc + logger.cc + simple_log_processor.cc + batch_log_processor.cc + logger_context.cc + multi_log_processor.cc + multi_recordable.cc) set_target_properties(opentelemetry_logs PROPERTIES EXPORT_NAME logs) -target_link_libraries(opentelemetry_logs opentelemetry_common) +target_link_libraries(opentelemetry_logs PUBLIC opentelemetry_resources + opentelemetry_common) target_include_directories( opentelemetry_logs diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 735b527b80..3c1d38d03e 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -16,8 +16,13 @@ namespace trace_api = opentelemetry::trace; namespace nostd = opentelemetry::nostd; namespace common = opentelemetry::common; -Logger::Logger(nostd::string_view name, std::shared_ptr logger_provider) noexcept - : logger_name_(std::string(name)), logger_provider_(logger_provider) +Logger::Logger(nostd::string_view name, + std::shared_ptr context, + std::unique_ptr + instrumentation_library) noexcept + : logger_name_(std::string(name)), + context_(context), + instrumentation_library_{std::move(instrumentation_library)} {} const nostd::string_view Logger::GetName() noexcept @@ -33,7 +38,6 @@ const nostd::string_view Logger::GetName() noexcept void Logger::Log(opentelemetry::logs::Severity severity, nostd::string_view name, nostd::string_view body, - const common::KeyValueIterable &resource, const common::KeyValueIterable &attributes, trace_api::TraceId trace_id, trace_api::SpanId span_id, @@ -41,15 +45,16 @@ void Logger::Log(opentelemetry::logs::Severity severity, common::SystemTimestamp timestamp) noexcept { // If this logger does not have a processor, no need to create a log record - auto processor = logger_provider_.lock()->GetProcessor(); - if (processor == nullptr) + auto context = context_.lock(); + if (!context) { return; } + auto &processor = context->GetProcessor(); // TODO: Sampler (should include check for minSeverity) - auto recordable = processor->MakeRecordable(); + auto recordable = processor.MakeRecordable(); if (recordable == nullptr) { OTEL_INTERNAL_LOG_ERROR("[LOGGER] Recordable creation failed"); @@ -61,11 +66,9 @@ void Logger::Log(opentelemetry::logs::Severity severity, recordable->SetSeverity(severity); recordable->SetName(name); recordable->SetBody(body); + recordable->SetInstrumentationLibrary(GetInstrumentationLibrary()); - resource.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { - recordable->SetResource(key, value); - return true; - }); + recordable->SetResource(context->GetResource()); attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { recordable->SetAttribute(key, value); @@ -111,7 +114,13 @@ void Logger::Log(opentelemetry::logs::Severity severity, } // Send the log record to the processor - processor->OnReceive(std::move(recordable)); + processor.OnReceive(std::move(recordable)); +} + +const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & +Logger::GetInstrumentationLibrary() const noexcept +{ + return *instrumentation_library_; } } // namespace logs diff --git a/sdk/src/logs/logger_context.cc b/sdk/src/logs/logger_context.cc new file mode 100644 index 0000000000..b0025ff724 --- /dev/null +++ b/sdk/src/logs/logger_context.cc @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/logger_context.h" +# include "opentelemetry/sdk/logs/multi_log_processor.h" + +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +LoggerContext::LoggerContext(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource) noexcept + : resource_(resource), + processor_(std::unique_ptr(new MultiLogProcessor(std::move(processors)))) +{} + +void LoggerContext::AddProcessor(std::unique_ptr processor) noexcept +{ + auto multi_processor = static_cast(processor_.get()); + multi_processor->AddProcessor(std::move(processor)); +} + +LogProcessor &LoggerContext::GetProcessor() const noexcept +{ + return *processor_; +} + +const opentelemetry::sdk::resource::Resource &LoggerContext::GetResource() const noexcept +{ + return resource_; +} + +bool LoggerContext::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + return processor_->ForceFlush(timeout); +} + +bool LoggerContext::Shutdown(std::chrono::microseconds timeout) noexcept +{ + return processor_->ForceFlush(timeout); +} + +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/src/logs/logger_provider.cc b/sdk/src/logs/logger_provider.cc index 58e6a75ea4..08edce783d 100644 --- a/sdk/src/logs/logger_provider.cc +++ b/sdk/src/logs/logger_provider.cc @@ -5,6 +5,12 @@ # include "opentelemetry/sdk/logs/logger_provider.h" +# include +# include +# include +# include +# include + OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { @@ -14,19 +20,48 @@ namespace logs namespace nostd = opentelemetry::nostd; namespace logs_api = opentelemetry::logs; -LoggerProvider::LoggerProvider() noexcept : processor_{nullptr} {} +LoggerProvider::LoggerProvider(std::unique_ptr &&processor, + opentelemetry::sdk::resource::Resource resource) noexcept +{ + std::vector> processors; + processors.emplace_back(std::move(processor)); + context_ = std::make_shared(std::move(processors), std::move(resource)); +} + +LoggerProvider::LoggerProvider(std::vector> &&processors, + opentelemetry::sdk::resource::Resource resource) noexcept + : context_{ + std::make_shared(std::move(processors), std::move(resource))} +{} -nostd::shared_ptr LoggerProvider::GetLogger(nostd::string_view name, - nostd::string_view options) noexcept +LoggerProvider::LoggerProvider() noexcept + : context_{ + std::make_shared(std::vector>{})} +{} + +LoggerProvider::LoggerProvider(std::shared_ptr context) noexcept + : context_{context} +{} + +nostd::shared_ptr LoggerProvider::GetLogger( + nostd::string_view logger_name, + nostd::string_view options, + nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept { // Ensure only one thread can read/write from the map of loggers - std::lock_guard lock_guard{mu_}; + std::lock_guard lock_guard{lock_}; - // If a logger with a name "name" already exists, return it - auto loggerkv = loggers_.find(name.data()); - if (loggerkv != loggers_.end()) + // If a logger with a name "logger_name" already exists, return it + for (auto &logger : loggers_) { - return nostd::shared_ptr(loggerkv->second); + auto &logger_lib = logger->GetInstrumentationLibrary(); + if (logger->GetName() == logger_name && + logger_lib.equal(library_name, library_version, schema_url)) + { + return nostd::shared_ptr{logger}; + } } // Check if creating a new logger would exceed the max number of loggers @@ -43,29 +78,33 @@ nostd::shared_ptr LoggerProvider::GetLogger(nostd::string_view */ // If no logger with that name exists yet, create it and add it to the map of loggers - - nostd::shared_ptr logger(new Logger(name, this->shared_from_this())); - loggers_[name.data()] = logger; - return logger; + auto lib = instrumentationlibrary::InstrumentationLibrary::Create(library_name, library_version, + schema_url); + loggers_.push_back(std::shared_ptr( + new Logger(logger_name, context_, std::move(lib)))); + return nostd::shared_ptr{loggers_.back()}; } -nostd::shared_ptr LoggerProvider::GetLogger( - nostd::string_view name, - nostd::span args) noexcept +nostd::shared_ptr LoggerProvider::GetLogger( + nostd::string_view logger_name, + nostd::span args, + nostd::string_view library_name, + nostd::string_view library_version, + nostd::string_view schema_url) noexcept { - // Currently, no args support - return GetLogger(name); + return GetLogger(logger_name, "", library_name, library_version, schema_url); } -std::shared_ptr LoggerProvider::GetProcessor() noexcept +void LoggerProvider::AddProcessor(std::unique_ptr processor) noexcept { - return processor_.load(); + context_->AddProcessor(std::move(processor)); } -void LoggerProvider::SetProcessor(std::shared_ptr processor) noexcept +const opentelemetry::sdk::resource::Resource &LoggerProvider::GetResource() const noexcept { - processor_.store(processor); + return context_->GetResource(); } + } // namespace logs } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/logs/multi_log_processor.cc b/sdk/src/logs/multi_log_processor.cc new file mode 100644 index 0000000000..b73cc9e7a6 --- /dev/null +++ b/sdk/src/logs/multi_log_processor.cc @@ -0,0 +1,153 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/multi_log_processor.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +MultiLogProcessor::MultiLogProcessor(std::vector> &&processors) +{ + for (auto &processor : processors) + { + AddProcessor(std::move(processor)); + } +} +MultiLogProcessor::~MultiLogProcessor() +{ + ForceFlush(); + Shutdown(); +} + +void MultiLogProcessor::AddProcessor(std::unique_ptr &&processor) +{ + // Add preocessor to end of the list. + if (processor) + { + processors_.emplace_back(std::move(processor)); + } +} + +std::unique_ptr MultiLogProcessor::MakeRecordable() noexcept +{ + auto recordable = std::unique_ptr(new MultiRecordable); + auto multi_recordable = static_cast(recordable.get()); + for (auto &processor : processors_) + { + multi_recordable->AddRecordable(*processor, processor->MakeRecordable()); + } + return recordable; +} + +void MultiLogProcessor::OnReceive(std::unique_ptr &&record) noexcept +{ + if (!record) + { + return; + } + auto multi_recordable = static_cast(record.get()); + + for (auto &processor : processors_) + { + auto recordable = multi_recordable->ReleaseRecordable(*processor); + if (recordable) + { + processor->OnReceive(std::move(recordable)); + } + } +} + +bool MultiLogProcessor::ForceFlush(std::chrono::microseconds timeout) noexcept +{ + // Converto nanos to prevent overflow + std::chrono::nanoseconds timeout_ns = std::chrono::nanoseconds::max(); + if (std::chrono::duration_cast(timeout_ns) > timeout) + { + timeout_ns = std::chrono::duration_cast(timeout); + } + bool result = true; + auto start_time = std::chrono::system_clock::now(); + auto overflow_checker = std::chrono::system_clock::time_point::max(); + std::chrono::system_clock::time_point expire_time; + if (overflow_checker - start_time <= timeout_ns) + { + expire_time = overflow_checker; + } + else + { + expire_time = + start_time + std::chrono::duration_cast(timeout_ns); + } + for (auto &processor : processors_) + { + if (!processor->ForceFlush(std::chrono::duration_cast(timeout_ns))) + { + result = false; + } + start_time = std::chrono::system_clock::now(); + if (expire_time > start_time) + { + timeout_ns = std::chrono::duration_cast(expire_time - start_time); + } + else + { + timeout_ns = std::chrono::nanoseconds::zero(); + } + } + return result; +} + +bool MultiLogProcessor::Shutdown(std::chrono::microseconds timeout) noexcept +{ + // Converto nanos to prevent overflow + std::chrono::nanoseconds timeout_ns = std::chrono::nanoseconds::max(); + if (std::chrono::duration_cast(timeout_ns) > timeout) + { + timeout_ns = std::chrono::duration_cast(timeout); + } + bool result = true; + auto start_time = std::chrono::system_clock::now(); + auto overflow_checker = std::chrono::system_clock::time_point::max(); + std::chrono::system_clock::time_point expire_time; + if (overflow_checker - start_time <= timeout_ns) + { + expire_time = overflow_checker; + } + else + { + expire_time = + start_time + std::chrono::duration_cast(timeout_ns); + } + for (auto &processor : processors_) + { + if (!processor->Shutdown(std::chrono::duration_cast(timeout_ns))) + { + result = false; + } + start_time = std::chrono::system_clock::now(); + if (expire_time > start_time) + { + timeout_ns = std::chrono::duration_cast(expire_time - start_time); + } + else + { + timeout_ns = std::chrono::nanoseconds::zero(); + } + } + return result; +} + +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/src/logs/multi_recordable.cc b/sdk/src/logs/multi_recordable.cc new file mode 100644 index 0000000000..b7bb92c627 --- /dev/null +++ b/sdk/src/logs/multi_recordable.cc @@ -0,0 +1,148 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#ifdef ENABLE_LOGS_PREVIEW + +# include "opentelemetry/sdk/logs/multi_recordable.h" + +# include +# include +# include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +namespace +{ +std::size_t MakeKey(const opentelemetry::sdk::logs::LogProcessor &processor) +{ + return reinterpret_cast(&processor); +} + +} // namespace + +void MultiRecordable::AddRecordable(const LogProcessor &processor, + std::unique_ptr recordable) noexcept +{ + recordables_[MakeKey(processor)] = std::move(recordable); +} + +const std::unique_ptr &MultiRecordable::GetRecordable( + const LogProcessor &processor) const noexcept +{ + // TODO - return nullptr ref on failed lookup? + auto i = recordables_.find(MakeKey(processor)); + if (i != recordables_.end()) + { + return i->second; + } + static std::unique_ptr empty(nullptr); + return empty; +} + +std::unique_ptr MultiRecordable::ReleaseRecordable( + const LogProcessor &processor) noexcept +{ + auto i = recordables_.find(MakeKey(processor)); + if (i != recordables_.end()) + { + std::unique_ptr result(i->second.release()); + recordables_.erase(MakeKey(processor)); + return result; + } + return std::unique_ptr(nullptr); +} + +void MultiRecordable::SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetTimestamp(timestamp); + } +} + +void MultiRecordable::SetSeverity(opentelemetry::logs::Severity severity) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetSeverity(severity); + } +} + +void MultiRecordable::SetName(nostd::string_view name) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetName(name); + } +} + +void MultiRecordable::SetBody(nostd::string_view message) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetBody(message); + } +} + +void MultiRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetResource(resource); + } +} + +void MultiRecordable::SetAttribute(nostd::string_view key, + const opentelemetry::common::AttributeValue &value) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetAttribute(key, value); + } +} + +void MultiRecordable::SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetTraceId(trace_id); + } +} + +void MultiRecordable::SetSpanId(opentelemetry::trace::SpanId span_id) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetSpanId(span_id); + } +} + +void MultiRecordable::SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept +{ + for (auto &recordable : recordables_) + { + recordable.second->SetTraceFlags(trace_flags); + } +} + +void MultiRecordable::SetInstrumentationLibrary( + const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary + &instrumentation_library) noexcept +{ + instrumentation_library_ = &instrumentation_library; +} + +const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary & +MultiRecordable::GetInstrumentationLibrary() const noexcept +{ + return *instrumentation_library_; +} +} // namespace logs +} // namespace sdk + +OPENTELEMETRY_END_NAMESPACE +#endif diff --git a/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc b/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc index b6ac69df1f..a410ca99f2 100644 --- a/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc +++ b/sdk/test/instrumentationlibrary/instrumentationlibrary_test.cc @@ -14,10 +14,13 @@ using namespace opentelemetry::sdk::instrumentationlibrary; TEST(InstrumentationLibrary, CreateInstrumentationLibrary) { - std::string library_name = "opentelemetry-cpp"; - std::string library_version = "0.1.0"; - auto instrumentation_library = InstrumentationLibrary::Create(library_name, library_version); + std::string library_name = "opentelemetry-cpp"; + std::string library_version = "0.1.0"; + std::string schema_url = "https://opentelemetry.io/schemas/1.2.0"; + auto instrumentation_library = + InstrumentationLibrary::Create(library_name, library_version, schema_url); EXPECT_EQ(instrumentation_library->GetName(), library_name); EXPECT_EQ(instrumentation_library->GetVersion(), library_version); + EXPECT_EQ(instrumentation_library->GetSchemaURL(), schema_url); } diff --git a/sdk/test/logs/log_record_test.cc b/sdk/test/logs/log_record_test.cc index 9443091691..dda80cc1ee 100644 --- a/sdk/test/logs/log_record_test.cc +++ b/sdk/test/logs/log_record_test.cc @@ -26,7 +26,7 @@ TEST(LogRecord, GetDefaultValues) ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid); ASSERT_EQ(record.GetName(), ""); ASSERT_EQ(record.GetBody(), ""); - ASSERT_EQ(record.GetResource().size(), 0); + ASSERT_NE(record.GetResource().GetAttributes().size(), 0); ASSERT_EQ(record.GetAttributes().size(), 0); ASSERT_EQ(record.GetTraceId(), zero_trace_id); ASSERT_EQ(record.GetSpanId(), zero_span_id); @@ -44,10 +44,11 @@ TEST(LogRecord, SetAndGet) // Set all fields of the LogRecord LogRecord record; + auto resource = opentelemetry::sdk::resource::Resource::Create({{"res1", true}}); record.SetSeverity(logs_api::Severity::kInvalid); record.SetName("Log name"); record.SetBody("Message"); - record.SetResource("res1", (bool)true); + record.SetResource(resource); record.SetAttribute("attr1", (int64_t)314159); record.SetTraceId(trace_id); record.SetSpanId(span_id); @@ -58,7 +59,7 @@ TEST(LogRecord, SetAndGet) ASSERT_EQ(record.GetSeverity(), logs_api::Severity::kInvalid); ASSERT_EQ(record.GetName(), "Log name"); ASSERT_EQ(record.GetBody(), "Message"); - ASSERT_EQ(nostd::get(record.GetResource().at("res1")), 1); + ASSERT_TRUE(nostd::get(record.GetResource().GetAttributes().at("res1"))); ASSERT_EQ(nostd::get(record.GetAttributes().at("attr1")), 314159); ASSERT_EQ(record.GetTraceId(), trace_id); ASSERT_EQ(record.GetSpanId(), span_id); diff --git a/sdk/test/logs/logger_provider_sdk_test.cc b/sdk/test/logs/logger_provider_sdk_test.cc index 5c0be6fb94..e31c1b6825 100644 --- a/sdk/test/logs/logger_provider_sdk_test.cc +++ b/sdk/test/logs/logger_provider_sdk_test.cc @@ -31,19 +31,32 @@ TEST(LoggerProviderSDK, LoggerProviderGetLoggerSimple) { auto lp = std::shared_ptr(new LoggerProvider()); - auto logger1 = lp->GetLogger("logger1"); - auto logger2 = lp->GetLogger("logger2"); + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); + auto logger2 = lp->GetLogger("logger2", "", "opentelelemtry_library", "", schema_url); // Check that the logger is not nullptr ASSERT_NE(logger1, nullptr); ASSERT_NE(logger2, nullptr); + auto sdk_logger1 = static_cast(logger1.get()); + auto sdk_logger2 = static_cast(logger2.get()); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger1->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary().GetSchemaURL(), schema_url); + // Check that two loggers with different names aren't the same instance ASSERT_NE(logger1, logger2); // Check that two loggers with the same name are the same instance - auto logger3 = lp->GetLogger("logger1"); + auto logger3 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); ASSERT_EQ(logger1, logger3); + auto sdk_logger3 = static_cast(logger3.get()); + ASSERT_EQ(sdk_logger3->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary()); } TEST(LoggerProviderSDK, LoggerProviderLoggerArguments) @@ -53,12 +66,16 @@ TEST(LoggerProviderSDK, LoggerProviderLoggerArguments) // detail to this test auto lp = std::shared_ptr(new LoggerProvider()); - auto logger1 = lp->GetLogger("logger1", ""); + nostd::string_view schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger1 = lp->GetLogger("logger1", "", "opentelelemtry_library", "", schema_url); // Check GetLogger(logger_name, args) std::array sv{"string"}; nostd::span args{sv}; - auto logger2 = lp->GetLogger("logger2", args); + auto logger2 = lp->GetLogger("logger2", args, "opentelelemtry_library", "", schema_url); + auto sdk_logger1 = static_cast(logger1.get()); + auto sdk_logger2 = static_cast(logger2.get()); + ASSERT_EQ(sdk_logger2->GetInstrumentationLibrary(), sdk_logger1->GetInstrumentationLibrary()); } class DummyProcessor : public LogProcessor @@ -79,15 +96,11 @@ class DummyProcessor : public LogProcessor } }; -TEST(LoggerProviderSDK, GetAndSetProcessor) +TEST(LoggerProviderSDK, GetResource) { // Create a LoggerProvider without a processor - LoggerProvider lp; - ASSERT_EQ(lp.GetProcessor(), nullptr); - - // Create a new processor and check if it is pushed correctly - std::shared_ptr proc2 = std::shared_ptr(new DummyProcessor()); - lp.SetProcessor(proc2); - ASSERT_EQ(proc2, lp.GetProcessor()); + auto resource = opentelemetry::sdk::resource::Resource::Create({{"key", "value"}}); + LoggerProvider lp{nullptr, resource}; + ASSERT_EQ(nostd::get(lp.GetResource().GetAttributes().at("key")), "value"); } #endif diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index 26632f1f91..aad13f41e2 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -17,9 +17,14 @@ TEST(LoggerSDK, LogToNullProcessor) // even when there is no processor set // since it calls Processor::OnReceive() - auto lp = std::shared_ptr(new LoggerProvider()); - auto logger = lp->GetLogger("logger"); - + auto lp = std::shared_ptr(new LoggerProvider()); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); + + auto sdk_logger = static_cast(logger.get()); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url); // Log a sample log record to a nullptr processor logger->Debug("Test log"); } @@ -66,20 +71,23 @@ TEST(LoggerSDK, LogToAProcessor) { // Create an API LoggerProvider and logger auto api_lp = std::shared_ptr(new LoggerProvider()); - auto logger = api_lp->GetLogger("logger"); + const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"}; + auto logger = api_lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); // Cast the API LoggerProvider to an SDK Logger Provider and assert that it is still the same // LoggerProvider by checking that getting a logger with the same name as the previously defined // logger is the same instance auto lp = static_cast(api_lp.get()); - auto logger2 = lp->GetLogger("logger"); + auto logger2 = lp->GetLogger("logger", "", "opentelelemtry_library", "", schema_url); ASSERT_EQ(logger, logger2); + auto sdk_logger = static_cast(logger.get()); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetName(), "opentelelemtry_library"); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetVersion(), ""); + ASSERT_EQ(sdk_logger->GetInstrumentationLibrary().GetSchemaURL(), schema_url); // Set a processor for the LoggerProvider auto shared_recordable = std::shared_ptr(new LogRecord()); - auto processor = std::shared_ptr(new MockProcessor(shared_recordable)); - lp->SetProcessor(processor); - ASSERT_EQ(processor, lp->GetProcessor()); + lp->AddProcessor(std::unique_ptr(new MockProcessor(shared_recordable))); // Check that the recordable created by the Log() statement is set properly logger->Log(logs_api::Severity::kWarn, "Log Name", "Log Message");