summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/exporters/otlp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/jaegertracing/opentelemetry-cpp/exporters/otlp
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/exporters/otlp')
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/BUILD286
-rwxr-xr-xsrc/jaegertracing/opentelemetry-cpp/exporters/otlp/CMakeLists.txt195
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/README.md79
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_environment.h272
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h86
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h39
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h90
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h144
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h110
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_exporter.h110
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h118
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h72
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable_utils.h63
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_prefix.h37
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_suffix.h16
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_exporter.cc161
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_log_exporter.cc181
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_client.cc721
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_exporter.cc66
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_log_exporter.cc69
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_log_recordable.cc235
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable.cc187
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable_utils.cc362
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc216
-rwxr-xr-xsrc/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_test.cc217
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_log_exporter_test.cc163
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_exporter_test.cc357
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_log_exporter_test.cc381
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_log_recordable_test.cc240
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_recordable_test.cc321
30 files changed, 5594 insertions, 0 deletions
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/BUILD
new file mode 100644
index 000000000..496819138
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/BUILD
@@ -0,0 +1,286 @@
+# Copyright 2020, OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+package(default_visibility = ["//visibility:public"])
+
+load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")
+
+cc_library(
+ name = "otlp_recordable",
+ srcs = [
+ "src/otlp_log_recordable.cc",
+ "src/otlp_recordable.cc",
+ "src/otlp_recordable_utils.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/otlp/otlp_log_recordable.h",
+ "include/opentelemetry/exporters/otlp/otlp_recordable.h",
+ "include/opentelemetry/exporters/otlp/otlp_recordable_utils.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_prefix.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_suffix.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["otlp"],
+ deps = [
+ "//sdk/src/logs",
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ "@com_github_opentelemetry_proto//:logs_service_proto_cc",
+ "@com_github_opentelemetry_proto//:trace_service_proto_cc",
+ ],
+)
+
+cc_library(
+ name = "otlp_grpc_exporter",
+ srcs = [
+ "src/otlp_grpc_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/otlp/otlp_environment.h",
+ "include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h",
+ "include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_prefix.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_suffix.h",
+ ],
+ strip_include_prefix = "include",
+ tags = [
+ "otlp",
+ "otlp_grpc",
+ ],
+ deps = [
+ ":otlp_recordable",
+ "//ext:headers",
+ "//sdk/src/trace",
+
+ # For gRPC
+ "@com_github_opentelemetry_proto//:trace_service_grpc_cc",
+ "@com_github_grpc_grpc//:grpc++",
+ ],
+)
+
+cc_library(
+ name = "otlp_http_client",
+ srcs = [
+ "src/otlp_http_client.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/otlp/otlp_environment.h",
+ "include/opentelemetry/exporters/otlp/otlp_http_client.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_prefix.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_suffix.h",
+ ],
+ copts = [
+ "-DCURL_STATICLIB",
+ ],
+ linkopts = select({
+ "//bazel:windows": [
+ "-DEFAULTLIB:advapi32.lib",
+ "-DEFAULTLIB:crypt32.lib",
+ ],
+ "//conditions:default": [],
+ }),
+ strip_include_prefix = "include",
+ tags = [
+ "otlp",
+ "otlp_http",
+ "otlp_http_log",
+ ],
+ deps = [
+ "//api",
+ "//ext/src/http/client/curl:http_client_curl",
+ "//sdk:headers",
+ "@com_github_opentelemetry_proto//:common_proto_cc",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_library(
+ name = "otlp_http_exporter",
+ srcs = [
+ "src/otlp_http_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/otlp/otlp_environment.h",
+ "include/opentelemetry/exporters/otlp/otlp_http_exporter.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_prefix.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_suffix.h",
+ ],
+ strip_include_prefix = "include",
+ tags = [
+ "otlp",
+ "otlp_http",
+ ],
+ deps = [
+ ":otlp_http_client",
+ ":otlp_recordable",
+ "//sdk/src/trace",
+ "@com_github_opentelemetry_proto//:trace_service_proto_cc",
+ ],
+)
+
+cc_library(
+ name = "otlp_http_log_exporter",
+ srcs = [
+ "src/otlp_http_log_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/otlp/otlp_environment.h",
+ "include/opentelemetry/exporters/otlp/otlp_http_log_exporter.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_prefix.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_suffix.h",
+ ],
+ strip_include_prefix = "include",
+ tags = [
+ "otlp",
+ "otlp_http_log",
+ ],
+ deps = [
+ ":otlp_http_client",
+ ":otlp_recordable",
+ "//sdk/src/logs",
+ "@com_github_opentelemetry_proto//:logs_service_proto_cc",
+ ],
+)
+
+cc_library(
+ name = "otlp_grpc_log_exporter",
+ srcs = [
+ "src/otlp_grpc_log_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/otlp/otlp_environment.h",
+ "include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h",
+ "include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_prefix.h",
+ "include/opentelemetry/exporters/otlp/protobuf_include_suffix.h",
+ ],
+ strip_include_prefix = "include",
+ tags = [
+ "otlp",
+ "otlp_grpc_log",
+ ],
+ deps = [
+ ":otlp_recordable",
+ "//ext:headers",
+ "//sdk/src/logs",
+ "@com_github_opentelemetry_proto//:logs_service_proto_cc",
+ # For gRPC
+ "@com_github_opentelemetry_proto//:logs_service_grpc_cc",
+ "@com_github_grpc_grpc//:grpc++",
+ ],
+)
+
+cc_test(
+ name = "otlp_recordable_test",
+ srcs = ["test/otlp_recordable_test.cc"],
+ tags = [
+ "otlp",
+ "test",
+ ],
+ deps = [
+ ":otlp_recordable",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "otlp_log_recordable_test",
+ srcs = ["test/otlp_log_recordable_test.cc"],
+ tags = [
+ "otlp",
+ "test",
+ ],
+ deps = [
+ ":otlp_recordable",
+ "@com_github_opentelemetry_proto//:logs_service_proto_cc",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "otlp_grpc_exporter_test",
+ srcs = ["test/otlp_grpc_exporter_test.cc"],
+ tags = [
+ "otlp",
+ "otlp_grpc",
+ "test",
+ ],
+ deps = [
+ ":otlp_grpc_exporter",
+ "//api",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "otlp_http_exporter_test",
+ srcs = ["test/otlp_http_exporter_test.cc"],
+ tags = [
+ "otlp",
+ "otlp_http",
+ "test",
+ ],
+ deps = [
+ ":otlp_http_exporter",
+ "//api",
+ "//ext/src/http/client/nosend:http_client_nosend",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "otlp_http_log_exporter_test",
+ srcs = ["test/otlp_http_log_exporter_test.cc"],
+ tags = [
+ "otlp",
+ "otlp_http_log",
+ "test",
+ ],
+ deps = [
+ ":otlp_http_log_exporter",
+ "//api",
+ "//ext/src/http/client/nosend:http_client_nosend",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "otlp_grpc_log_exporter_test",
+ srcs = ["test/otlp_grpc_log_exporter_test.cc"],
+ tags = [
+ "otlp",
+ "otlp_grpc_log",
+ "test",
+ ],
+ deps = [
+ ":otlp_grpc_log_exporter",
+ "//api",
+ "//sdk/src/logs",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+otel_cc_benchmark(
+ name = "otlp_grpc_exporter_benchmark",
+ srcs = ["test/otlp_grpc_exporter_benchmark.cc"],
+ tags = [
+ "otlp",
+ "otlp_grpc",
+ "test",
+ ],
+ deps = [
+ ":otlp_grpc_exporter",
+ "//examples/common/foo_library:common_foo_library",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/CMakeLists.txt
new file mode 100755
index 000000000..b5dfa1e1a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/CMakeLists.txt
@@ -0,0 +1,195 @@
+add_library(
+ opentelemetry_otlp_recordable
+ src/otlp_log_recordable.cc src/otlp_recordable.cc
+ src/otlp_recordable_utils.cc)
+set_target_properties(opentelemetry_otlp_recordable PROPERTIES EXPORT_NAME
+ otlp_recordable)
+
+target_include_directories(
+ opentelemetry_otlp_recordable
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+set(OPENTELEMETRY_OTLP_TARGETS opentelemetry_otlp_recordable)
+target_link_libraries(
+ opentelemetry_otlp_recordable
+ PUBLIC opentelemetry_trace opentelemetry_resources opentelemetry_proto)
+
+if(WITH_OTLP_GRPC)
+ find_package(gRPC REQUIRED)
+ add_library(opentelemetry_exporter_otlp_grpc src/otlp_grpc_exporter.cc)
+
+ set_target_properties(opentelemetry_exporter_otlp_grpc
+ PROPERTIES EXPORT_NAME otlp_grpc_exporter)
+
+ target_link_libraries(opentelemetry_exporter_otlp_grpc
+ PUBLIC opentelemetry_otlp_recordable gRPC::grpc++)
+
+ list(APPEND OPENTELEMETRY_OTLP_TARGETS opentelemetry_exporter_otlp_grpc)
+
+ add_library(opentelemetry_exporter_otlp_grpc_log
+ src/otlp_grpc_log_exporter.cc)
+
+ set_target_properties(opentelemetry_exporter_otlp_grpc_log
+ PROPERTIES EXPORT_NAME otlp_grpc_log_exporter)
+
+ target_link_libraries(opentelemetry_exporter_otlp_grpc_log
+ PUBLIC opentelemetry_otlp_recordable gRPC::grpc++)
+
+ list(APPEND OPENTELEMETRY_OTLP_TARGETS opentelemetry_exporter_otlp_grpc_log)
+endif()
+
+if(WITH_OTLP_HTTP)
+ find_package(CURL REQUIRED)
+ add_library(opentelemetry_exporter_otlp_http_client src/otlp_http_client.cc)
+ set_target_properties(opentelemetry_exporter_otlp_http_client
+ PROPERTIES EXPORT_NAME otlp_http_client)
+ target_link_libraries(
+ opentelemetry_exporter_otlp_http_client
+ PUBLIC opentelemetry_sdk opentelemetry_proto opentelemetry_http_client_curl
+ nlohmann_json::nlohmann_json)
+ if(nlohmann_json_clone)
+ add_dependencies(opentelemetry_exporter_otlp_http_client
+ nlohmann_json::nlohmann_json)
+ endif()
+ target_include_directories(
+ opentelemetry_exporter_otlp_http_client
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+ list(APPEND OPENTELEMETRY_OTLP_TARGETS
+ opentelemetry_exporter_otlp_http_client)
+
+ add_library(opentelemetry_exporter_otlp_http src/otlp_http_exporter.cc)
+
+ set_target_properties(opentelemetry_exporter_otlp_http
+ PROPERTIES EXPORT_NAME otlp_http_exporter)
+
+ target_link_libraries(
+ opentelemetry_exporter_otlp_http
+ PUBLIC opentelemetry_otlp_recordable
+ opentelemetry_exporter_otlp_http_client)
+
+ list(APPEND OPENTELEMETRY_OTLP_TARGETS opentelemetry_exporter_otlp_http)
+
+ if(WITH_LOGS_PREVIEW)
+ add_library(opentelemetry_exporter_otlp_http_log
+ src/otlp_http_log_exporter.cc)
+
+ set_target_properties(opentelemetry_exporter_otlp_http_log
+ PROPERTIES EXPORT_NAME otlp_http_log_exporter)
+
+ target_link_libraries(
+ opentelemetry_exporter_otlp_http_log
+ PUBLIC opentelemetry_otlp_recordable
+ opentelemetry_exporter_otlp_http_client)
+
+ list(APPEND OPENTELEMETRY_OTLP_TARGETS opentelemetry_exporter_otlp_http_log)
+
+ endif()
+endif()
+
+install(
+ TARGETS ${OPENTELEMETRY_OTLP_TARGETS}
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/otlp
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h")
+
+if(BUILD_TESTING)
+ add_executable(otlp_recordable_test test/otlp_recordable_test.cc)
+ target_link_libraries(otlp_recordable_test ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_otlp_recordable)
+ gtest_add_tests(
+ TARGET otlp_recordable_test
+ TEST_PREFIX exporter.otlp.
+ TEST_LIST otlp_recordable_test)
+
+ if(WITH_LOGS_PREVIEW)
+ add_executable(otlp_log_recordable_test test/otlp_log_recordable_test.cc)
+ target_link_libraries(
+ otlp_log_recordable_test ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_otlp_recordable)
+ gtest_add_tests(
+ TARGET otlp_log_recordable_test
+ TEST_PREFIX exporter.otlp.
+ TEST_LIST otlp_log_recordable_test)
+ endif()
+
+ if(MSVC)
+ # Explicitly specify that we consume GTest from shared library. The rest of
+ # code logic below determines whether we link Release or Debug flavor of the
+ # library. These flavors have different prefix on Windows, gmock and gmockd
+ # respectively.
+ add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1)
+ if(GMOCK_LIB)
+ # unset GMOCK_LIB to force find_library to redo the lookup, as the cached
+ # entry could cause linking to incorrect flavor of gmock and leading to
+ # runtime error.
+ 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()
+
+ if(WITH_OTLP_GRPC)
+ add_executable(otlp_grpc_exporter_test test/otlp_grpc_exporter_test.cc)
+ target_link_libraries(
+ otlp_grpc_exporter_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ ${GMOCK_LIB} opentelemetry_exporter_otlp_grpc)
+ gtest_add_tests(
+ TARGET otlp_grpc_exporter_test
+ TEST_PREFIX exporter.otlp.
+ TEST_LIST otlp_grpc_exporter_test)
+
+ if(WITH_LOGS_PREVIEW)
+ add_executable(otlp_grpc_log_exporter_test
+ test/otlp_grpc_log_exporter_test.cc)
+ target_link_libraries(
+ otlp_grpc_log_exporter_test ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} ${GMOCK_LIB}
+ opentelemetry_exporter_otlp_grpc_log opentelemetry_logs)
+ gtest_add_tests(
+ TARGET otlp_grpc_log_exporter_test
+ TEST_PREFIX exporter.otlp.
+ TEST_LIST otlp_grpc_log_exporter_test)
+ endif()
+ endif()
+
+ if(WITH_OTLP_HTTP)
+ add_executable(otlp_http_exporter_test test/otlp_http_exporter_test.cc)
+ target_link_libraries(
+ otlp_http_exporter_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ ${GMOCK_LIB} opentelemetry_exporter_otlp_http http_client_nosend)
+ gtest_add_tests(
+ TARGET otlp_http_exporter_test
+ TEST_PREFIX exporter.otlp.
+ TEST_LIST otlp_http_exporter_test)
+
+ if(WITH_LOGS_PREVIEW)
+ add_executable(otlp_http_log_exporter_test
+ test/otlp_http_log_exporter_test.cc)
+ target_link_libraries(
+ otlp_http_log_exporter_test
+ ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${GMOCK_LIB}
+ opentelemetry_exporter_otlp_http_log
+ opentelemetry_logs
+ http_client_nosend)
+ gtest_add_tests(
+ TARGET otlp_http_log_exporter_test
+ TEST_PREFIX exporter.otlp.
+ TEST_LIST otlp_http_log_exporter_test)
+ endif()
+ endif()
+endif() # BUILD_TESTING
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/README.md b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/README.md
new file mode 100644
index 000000000..9ac6918ba
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/README.md
@@ -0,0 +1,79 @@
+# OTLP Exporter
+
+The [OpenTelemetry
+Protocol](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/README.md)
+(OTLP) is a vendor-agnostic protocol designed as part of the OpenTelemetry
+project. The OTLP exporter can be used to export to any backend that supports
+OTLP.
+
+The [OpenTelemetry
+Collector](https://github.com/open-telemetry/opentelemetry-collector) is a
+reference implementation of an OTLP backend. The Collector can be configured to
+export to many other backends, such as Zipkin and Jaegar.
+
+For a full list of backends supported by the Collector, see the [main Collector
+repo](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter)
+and [Collector
+contributions](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter).
+
+## Configuration
+
+The OTLP exporter offers some configuration options. To configure the exporter,
+create an `OtlpGrpcExporterOptions` struct (defined in
+[otlp_grpc_exporter.h](https://github.com/open-telemetry/opentelemetry-cpp/blob/main/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h)),
+set the options inside, and pass the struct to the `OtlpGrpcExporter` constructor,
+like so:
+
+```cpp
+OtlpGrpcExporterOptions options;
+options.endpoint = "localhost:12345";
+auto exporter = std::unique_ptr<sdktrace::SpanExporter>(new otlp::OtlpGrpcExporter(options));
+```
+
+The OTLP HTTP exporter offers some configuration options. To configure the exporter,
+create an `OtlpHttpExporterOptions` struct (defined in
+[otlp_http_exporter.h](https://github.com/open-telemetry/opentelemetry-cpp/blob/main/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h)),
+set the options inside, and pass the struct to the `OtlpHttpExporter` constructor,
+like so:
+
+```cpp
+OtlpHttpExporterOptions options;
+options.url = "localhost:12345";
+auto exporter = std::unique_ptr<sdktrace::SpanExporter>(new otlp::OtlpHttpExporter(options));
+```
+
+### Configuration options ( OTLP GRPC Exporter )
+
+| Option | Env Variable |Default | Description |
+| ------------ |---------------|------------ |----------------|
+| `endpoint` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4317`| The OTLP GRPC endpoint to connect to |
+| | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | | |
+| `use_ssl_credentials` | `OTEL_EXPORTER_OTLP_SSL_ENABLE`| `false` | Whether the endpoint is SSL enabled |
+| | `OTEL_EXPORTER_OTLP_TRACES_SSL_ENABLE` | | |
+| `ssl_credentials_cacert_path` | `OTEL_EXPORTER_OTLP_CERTIFICATE` | `""` | SSL Certificate file path |
+| | `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` | | |
+| `ssl_credentials_cacert_as_string` | `OTEL_EXPORTER_OTLP_CERTIFICATE_STRING` | `""` | SSL Certifcate as in-memory string |
+| | `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE_STRING` | | | |
+| `timeout` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10s` | GRPC deadline |
+| | `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | | |
+| `metadata` | `OTEL_EXPORTER_OTLP_HEADERS` | | Custom metadata for GRPC |
+| | `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | | |
+
+### Configuration options ( OTLP HTTP Exporter )
+
+| Option | Env Variable |Default | Description |
+| ------------ |-----|------------ |------|
+| `url` | `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318/v1/traces` | The OTLP HTTP endpoint to connect to |
+| | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | | |
+| `content_type` | n/a | `application/json` | Data format used - JSON or Binary |
+| `json_bytes_mapping` | n/a | `JsonBytesMappingKind::kHexId` | Encoding used for trace_id and span_id |
+| `use_json_name` | n/a | `false` | Whether to use json name of protobuf field to set the key of json |
+| `timeout` | `OTEL_EXPORTER_OTLP_TIMEOUT` | `10s` | http timeout |
+| | `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | |
+| `http_headers` | `OTEL_EXPORTER_OTLP_HEADERS` | | http headers |
+| | `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | | |
+
+## Example
+
+For a complete example demonstrating how to use the OTLP exporter, see
+[examples/otlp](https://github.com/open-telemetry/opentelemetry-cpp/blob/main/examples/otlp/).
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_environment.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_environment.h
new file mode 100644
index 000000000..bf7ea6a61
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_environment.h
@@ -0,0 +1,272 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/common/kv_properties.h"
+#include "opentelemetry/nostd/string_view.h"
+
+#include "opentelemetry/sdk/common/attribute_utils.h"
+#include "opentelemetry/sdk/common/env_variables.h"
+
+#include <algorithm>
+#include <chrono>
+#include <map>
+#include <string>
+#include <unordered_set>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+inline const std::string GetOtlpDefaultGrpcEndpoint()
+{
+ constexpr char kOtlpTracesEndpointEnv[] = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
+ constexpr char kOtlpEndpointEnv[] = "OTEL_EXPORTER_OTLP_ENDPOINT";
+ constexpr char kOtlpEndpointDefault[] = "http://localhost:4317";
+
+ auto endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTracesEndpointEnv);
+ if (endpoint.empty())
+ {
+ endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpEndpointEnv);
+ }
+ return endpoint.size() ? endpoint : kOtlpEndpointDefault;
+}
+
+inline const std::string GetOtlpDefaultHttpEndpoint()
+{
+ constexpr char kOtlpTracesEndpointEnv[] = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
+ constexpr char kOtlpEndpointEnv[] = "OTEL_EXPORTER_OTLP_ENDPOINT";
+ constexpr char kOtlpEndpointDefault[] = "http://localhost:4318/v1/traces";
+
+ auto endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTracesEndpointEnv);
+ if (endpoint.empty())
+ {
+ endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpEndpointEnv);
+ if (!endpoint.empty())
+ {
+ endpoint += "/v1/traces";
+ }
+ }
+ return endpoint.size() ? endpoint : kOtlpEndpointDefault;
+}
+
+inline bool GetOtlpDefaultIsSslEnable()
+{
+ constexpr char kOtlpTracesIsSslEnableEnv[] = "OTEL_EXPORTER_OTLP_TRACES_SSL_ENABLE";
+ constexpr char kOtlpIsSslEnableEnv[] = "OTEL_EXPORTER_OTLP_SSL_ENABLE";
+
+ auto ssl_enable = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTracesIsSslEnableEnv);
+ if (ssl_enable.empty())
+ {
+ ssl_enable = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpIsSslEnableEnv);
+ }
+ if (ssl_enable == "True" || ssl_enable == "TRUE" || ssl_enable == "true" || ssl_enable == "1")
+ {
+ return true;
+ }
+ return false;
+}
+
+inline const std::string GetOtlpDefaultSslCertificatePath()
+{
+ constexpr char kOtlpTracesSslCertificate[] = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE";
+ constexpr char kOtlpSslCertificate[] = "OTEL_EXPORTER_OTLP_CERTIFICATE ";
+ auto ssl_cert_path =
+ opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTracesSslCertificate);
+ if (ssl_cert_path.empty())
+ {
+ ssl_cert_path = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpSslCertificate);
+ }
+ return ssl_cert_path.size() ? ssl_cert_path : "";
+}
+
+inline const std::string GetOtlpDefaultSslCertificateString()
+{
+ constexpr char kOtlpTracesSslCertificateString[] = "OTEL_EXPORTER_OTLP_CERTIFICATE_STRING";
+ constexpr char kOtlpSslCertificateString[] = "OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE_STRING ";
+ auto ssl_cert =
+ opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTracesSslCertificateString);
+ if (ssl_cert.empty())
+ {
+ ssl_cert = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpSslCertificateString);
+ }
+ return ssl_cert.size() ? ssl_cert : "";
+}
+
+inline const std::chrono::system_clock::duration GetOtlpTimeoutFromString(const char *input)
+{
+ if (nullptr == input || 0 == *input)
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::seconds{10});
+ }
+
+ std::chrono::system_clock::duration::rep result = 0;
+ // Skip spaces
+ for (; *input && (' ' == *input || '\t' == *input || '\r' == *input || '\n' == *input); ++input)
+ ;
+
+ for (; *input && (*input >= '0' && *input <= '9'); ++input)
+ {
+ result = result * 10 + (*input - '0');
+ }
+
+ opentelemetry::nostd::string_view unit{input};
+ if ("ns" == unit)
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::nanoseconds{result});
+ }
+ else if ("us" == unit)
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::microseconds{result});
+ }
+ else if ("ms" == unit)
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::milliseconds{result});
+ }
+ else if ("m" == unit)
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::minutes{result});
+ }
+ else if ("h" == unit)
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::hours{result});
+ }
+ else
+ {
+ return std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::seconds{result});
+ }
+}
+
+inline const std::chrono::system_clock::duration GetOtlpDefaultTimeout()
+{
+ constexpr char kOtlpTracesTimeoutEnv[] = "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT";
+ constexpr char kOtlpTimeoutEnv[] = "OTEL_EXPORTER_OTLP_TIMEOUT";
+
+ auto timeout = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTracesTimeoutEnv);
+ if (timeout.empty())
+ {
+ timeout = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTimeoutEnv);
+ }
+ return GetOtlpTimeoutFromString(timeout.c_str());
+}
+
+struct cmp_ic
+{
+ bool operator()(const std::string &s1, const std::string &s2) const
+ {
+ return std::lexicographical_compare(
+ s1.begin(), s1.end(), s2.begin(), s2.end(),
+ [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); });
+ }
+};
+using OtlpHeaders = std::multimap<std::string, std::string, cmp_ic>;
+
+inline void DumpOtlpHeaders(OtlpHeaders &output,
+ const char *env_var_name,
+ std::unordered_set<std::string> &remove_cache)
+{
+ auto value = opentelemetry::sdk::common::GetEnvironmentVariable(env_var_name);
+ if (value.empty())
+ {
+ return;
+ }
+
+ opentelemetry::common::KeyValueStringTokenizer tokenizer{value};
+ opentelemetry::nostd::string_view header_key;
+ opentelemetry::nostd::string_view header_value;
+ bool header_valid = true;
+
+ while (tokenizer.next(header_valid, header_key, header_value))
+ {
+ if (header_valid)
+ {
+ std::string key = static_cast<std::string>(header_key);
+ if (remove_cache.end() == remove_cache.find(key))
+ {
+ remove_cache.insert(key);
+ auto range = output.equal_range(key);
+ if (range.first != range.second)
+ {
+ output.erase(range.first, range.second);
+ }
+ }
+
+ output.emplace(std::make_pair(std::move(key), static_cast<std::string>(header_value)));
+ }
+ }
+}
+
+inline OtlpHeaders GetOtlpDefaultHeaders()
+{
+ constexpr char kOtlpTracesHeadersEnv[] = "OTEL_EXPORTER_OTLP_TRACES_HEADERS";
+ constexpr char kOtlpHeadersEnv[] = "OTEL_EXPORTER_OTLP_HEADERS";
+
+ OtlpHeaders result;
+ std::unordered_set<std::string> trace_remove_cache;
+ DumpOtlpHeaders(result, kOtlpHeadersEnv, trace_remove_cache);
+
+ trace_remove_cache.clear();
+ DumpOtlpHeaders(result, kOtlpTracesHeadersEnv, trace_remove_cache);
+
+ return result;
+}
+
+inline const std::string GetOtlpDefaultHttpLogEndpoint()
+{
+ constexpr char kOtlpLogsEndpointEnv[] = "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT";
+ constexpr char kOtlpEndpointEnv[] = "OTEL_EXPORTER_OTLP_ENDPOINT";
+ constexpr char kOtlpEndpointDefault[] = "http://localhost:4318/v1/logs";
+
+ auto endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpLogsEndpointEnv);
+ if (endpoint.empty())
+ {
+ endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpEndpointEnv);
+ if (!endpoint.empty())
+ {
+ endpoint += "/v1/logs";
+ }
+ }
+ return endpoint.size() ? endpoint : kOtlpEndpointDefault;
+}
+
+inline const std::chrono::system_clock::duration GetOtlpDefaultLogTimeout()
+{
+ constexpr char kOtlpLogsTimeoutEnv[] = "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT";
+ constexpr char kOtlpTimeoutEnv[] = "OTEL_EXPORTER_OTLP_TIMEOUT";
+
+ auto timeout = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpLogsTimeoutEnv);
+ if (timeout.empty())
+ {
+ timeout = opentelemetry::sdk::common::GetEnvironmentVariable(kOtlpTimeoutEnv);
+ }
+ return GetOtlpTimeoutFromString(timeout.c_str());
+}
+
+inline OtlpHeaders GetOtlpDefaultLogHeaders()
+{
+ constexpr char kOtlpLogsHeadersEnv[] = "OTEL_EXPORTER_OTLP_LOGS_HEADERS";
+ constexpr char kOtlpHeadersEnv[] = "OTEL_EXPORTER_OTLP_HEADERS";
+
+ OtlpHeaders result;
+ std::unordered_set<std::string> log_remove_cache;
+ DumpOtlpHeaders(result, kOtlpHeadersEnv, log_remove_cache);
+
+ log_remove_cache.clear();
+ DumpOtlpHeaders(result, kOtlpLogsHeadersEnv, log_remove_cache);
+
+ return result;
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h
new file mode 100644
index 000000000..a28e6fca8
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter.h
@@ -0,0 +1,86 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <chrono>
+
+#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"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/otlp/otlp_environment.h"
+#include "opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+/**
+ * The OTLP exporter exports span data in OpenTelemetry Protocol (OTLP) format.
+ */
+class OtlpGrpcExporter final : public opentelemetry::sdk::trace::SpanExporter
+{
+public:
+ /**
+ * Create an OtlpGrpcExporter using all default options.
+ */
+ OtlpGrpcExporter();
+
+ /**
+ * Create an OtlpGrpcExporter using the given options.
+ */
+ explicit OtlpGrpcExporter(const OtlpGrpcExporterOptions &options);
+
+ /**
+ * Create a span recordable.
+ * @return a newly initialized Recordable object
+ */
+ std::unique_ptr<sdk::trace::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Export a batch of span recordables in OTLP format.
+ * @param spans a span of unique pointers to span recordables
+ */
+ sdk::common::ExportResult Export(
+ const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans) noexcept override;
+
+ /**
+ * Shut down the exporter.
+ * @param timeout an optional timeout, the default timeout of 0 means that no
+ * timeout is applied.
+ * @return return the status of this operation
+ */
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;
+
+private:
+ // The configuration options associated with this exporter.
+ const OtlpGrpcExporterOptions options_;
+
+ // For testing
+ friend class OtlpGrpcExporterTestPeer;
+
+ // Store service stub internally. Useful for testing.
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> trace_service_stub_;
+
+ /**
+ * Create an OtlpGrpcExporter using the specified service stub.
+ * Only tests can call this constructor directly.
+ * @param stub the service stub to be used for exporting
+ */
+ OtlpGrpcExporter(std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub);
+ bool is_shutdown_ = false;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+};
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h
new file mode 100644
index 000000000..c62a0f0af
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h
@@ -0,0 +1,39 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/exporters/otlp/otlp_environment.h"
+
+#include <memory>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+/**
+ * Struct to hold OTLP exporter options.
+ */
+struct OtlpGrpcExporterOptions
+{
+ // The endpoint to export to. By default the OpenTelemetry Collector's default endpoint.
+ std::string endpoint = GetOtlpDefaultGrpcEndpoint();
+ // By default when false, uses grpc::InsecureChannelCredentials(); If true,
+ // uses ssl_credentials_cacert_path if non-empty, else uses ssl_credentials_cacert_as_string
+ bool use_ssl_credentials = GetOtlpDefaultIsSslEnable();
+ // ssl_credentials_cacert_path specifies path to .pem file to be used for SSL encryption.
+ std::string ssl_credentials_cacert_path = GetOtlpDefaultSslCertificatePath();
+ // ssl_credentials_cacert_as_string in-memory string representation of .pem file to be used for
+ // SSL encryption.
+ std::string ssl_credentials_cacert_as_string = GetOtlpDefaultSslCertificateString();
+ // Timeout for grpc deadline
+ std::chrono::system_clock::duration timeout = GetOtlpDefaultTimeout();
+ // Additional HTTP headers
+ OtlpHeaders metadata = GetOtlpDefaultHeaders();
+};
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h
new file mode 100644
index 000000000..a8aeda85b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h
@@ -0,0 +1,90 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+// clang-format off
+
+# 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
+
+# include "opentelemetry/exporters/otlp/otlp_environment.h"
+# include "opentelemetry/exporters/otlp/otlp_grpc_exporter_options.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+/**
+ * The OTLP exporter exports log data in OpenTelemetry Protocol (OTLP) format in gRPC.
+ */
+class OtlpGrpcLogExporter : public opentelemetry::sdk::logs::LogExporter
+{
+public:
+ /**
+ * Create an OtlpGrpcLogExporter with default exporter options.
+ */
+ OtlpGrpcLogExporter();
+
+ /**
+ * Create an OtlpGrpcLogExporter with user specified options.
+ * @param options An object containing the user's configuration options.
+ */
+ OtlpGrpcLogExporter(const OtlpGrpcExporterOptions &options);
+
+ /**
+ * Creates a recordable that stores the data in protobuf.
+ * @return a newly initialized Recordable object.
+ */
+ std::unique_ptr<opentelemetry::sdk::logs::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Exports a vector of log records to the configured gRPC endpoint. Guaranteed to return after a
+ * timeout specified from the options passed to the constructor.
+ * @param records A list of log records.
+ */
+ opentelemetry::sdk::common::ExportResult Export(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::logs::Recordable>> &records) noexcept
+ override;
+
+ /**
+ * Shutdown this exporter.
+ * @param timeout The maximum time to wait for the shutdown method to return.
+ */
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;
+
+private:
+ // Configuration options for the exporter
+ const OtlpGrpcExporterOptions options_;
+
+ // For testing
+ friend class OtlpGrpcLogExporterTestPeer;
+
+ // Store service stub internally. Useful for testing.
+ std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> log_service_stub_;
+
+ /**
+ * Create an OtlpGrpcLogExporter using the specified service stub.
+ * Only tests can call this constructor directly.
+ * @param stub the service stub to be used for exporting
+ */
+ OtlpGrpcLogExporter(std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> stub);
+ bool is_shutdown_ = false;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+};
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h
new file mode 100644
index 000000000..1a199bed4
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_client.h
@@ -0,0 +1,144 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+#include "google/protobuf/message.h"
+
+#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"
+
+#include "opentelemetry/exporters/otlp/otlp_environment.h"
+
+#include <chrono>
+#include <memory>
+#include <mutex>
+#include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+// The default URL path to post metric data.
+constexpr char kDefaultMetricsPath[] = "/v1/metrics";
+// The HTTP header "Content-Type"
+constexpr char kHttpJsonContentType[] = "application/json";
+constexpr char kHttpBinaryContentType[] = "application/x-protobuf";
+
+enum class JsonBytesMappingKind
+{
+ kHexId,
+ kHex,
+ kBase64,
+};
+
+enum class HttpRequestContentType
+{
+ kJson,
+ kBinary,
+};
+
+/**
+ * Struct to hold OTLP HTTP client options.
+ */
+struct OtlpHttpClientOptions
+{
+ std::string url;
+
+ // By default, post json data
+ HttpRequestContentType content_type = HttpRequestContentType::kJson;
+
+ // If convert bytes into hex. By default, we will convert all bytes but id into base64
+ // This option is ignored if content_type is not kJson
+ JsonBytesMappingKind json_bytes_mapping = JsonBytesMappingKind::kHexId;
+
+ // If using the json name of protobuf field to set the key of json. By default, we will use the
+ // field name just like proto files.
+ bool use_json_name = false;
+
+ // Whether to print the status of the HTTP client in the console
+ bool console_debug = false;
+
+ // TODO: Enable/disable to verify SSL certificate
+ std::chrono::system_clock::duration timeout = GetOtlpDefaultTimeout();
+
+ // Additional HTTP headers
+ OtlpHeaders http_headers = GetOtlpDefaultHeaders();
+
+ inline OtlpHttpClientOptions(nostd::string_view input_url,
+ HttpRequestContentType input_content_type,
+ JsonBytesMappingKind input_json_bytes_mapping,
+ bool input_use_json_name,
+ bool input_console_debug,
+ std::chrono::system_clock::duration input_timeout,
+ const OtlpHeaders &input_http_headers)
+ : url(input_url),
+ content_type(input_content_type),
+ json_bytes_mapping(input_json_bytes_mapping),
+ use_json_name(input_use_json_name),
+ console_debug(input_console_debug),
+ timeout(input_timeout),
+ http_headers(input_http_headers)
+ {}
+};
+
+/**
+ * The OTLP HTTP client exports span data in OpenTelemetry Protocol (OTLP) format.
+ */
+class OtlpHttpClient
+{
+public:
+ /**
+ * Create an OtlpHttpClient using the given options.
+ */
+ explicit OtlpHttpClient(OtlpHttpClientOptions &&options);
+
+ /**
+ * Export
+ * @param message message to export, it should be ExportTraceServiceRequest,
+ * ExportMetricsServiceRequest or ExportLogsServiceRequest
+ */
+ sdk::common::ExportResult Export(const google::protobuf::Message &message) noexcept;
+
+ /**
+ * Shut down the HTTP client.
+ * @param timeout an optional timeout, the default timeout of 0 means that no
+ * timeout is applied.
+ * @return return the status of this operation
+ */
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept;
+
+private:
+ // Stores if this HTTP client had its Shutdown() method called
+ bool is_shutdown_ = false;
+
+ // The configuration options associated with this HTTP client.
+ const OtlpHttpClientOptions options_;
+
+ // Object that stores the HTTP sessions that have been created
+ std::shared_ptr<ext::http::client::HttpClient> http_client_;
+ // Cached parsed URI
+ std::string http_uri_;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+ // For testing
+ friend class OtlpHttpExporterTestPeer;
+ friend class OtlpHttpLogExporterTestPeer;
+ /**
+ * Create an OtlpHttpClient using the specified http client.
+ * Only tests can call this constructor directly.
+ * @param options the Otlp http client options to be used for exporting
+ * @param http_client the http client to be used for exporting
+ */
+ OtlpHttpClient(OtlpHttpClientOptions &&options,
+ std::shared_ptr<ext::http::client::HttpClient> http_client);
+};
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h
new file mode 100644
index 000000000..3e6a52119
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_exporter.h
@@ -0,0 +1,110 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+// We need include exporter.h first, which will include Windows.h with NOMINMAX on Windows
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/otlp/otlp_http_client.h"
+
+#include "opentelemetry/exporters/otlp/otlp_environment.h"
+
+#include <chrono>
+#include <memory>
+#include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+/**
+ * Struct to hold OTLP exporter options.
+ */
+struct OtlpHttpExporterOptions
+{
+ // The endpoint to export to. By default
+ // @see
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md
+ // @see https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver/otlpreceiver
+ std::string url = GetOtlpDefaultHttpEndpoint();
+
+ // By default, post json data
+ HttpRequestContentType content_type = HttpRequestContentType::kJson;
+
+ // If convert bytes into hex. By default, we will convert all bytes but id into base64
+ // This option is ignored if content_type is not kJson
+ JsonBytesMappingKind json_bytes_mapping = JsonBytesMappingKind::kHexId;
+
+ // If using the json name of protobuf field to set the key of json. By default, we will use the
+ // field name just like proto files.
+ bool use_json_name = false;
+
+ // Whether to print the status of the exporter in the console
+ bool console_debug = false;
+
+ // TODO: Enable/disable to verify SSL certificate
+ std::chrono::system_clock::duration timeout = GetOtlpDefaultTimeout();
+
+ // Additional HTTP headers
+ OtlpHeaders http_headers = GetOtlpDefaultHeaders();
+};
+
+/**
+ * The OTLP exporter exports span data in OpenTelemetry Protocol (OTLP) format.
+ */
+class OtlpHttpExporter final : public opentelemetry::sdk::trace::SpanExporter
+{
+public:
+ /**
+ * Create an OtlpHttpExporter using all default options.
+ */
+ OtlpHttpExporter();
+
+ /**
+ * Create an OtlpHttpExporter using the given options.
+ */
+ explicit OtlpHttpExporter(const OtlpHttpExporterOptions &options);
+
+ /**
+ * Create a span recordable.
+ * @return a newly initialized Recordable object
+ */
+ std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Export
+ * @param spans a span of unique pointers to span recordables
+ */
+ opentelemetry::sdk::common::ExportResult Export(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::trace::Recordable>> &spans) noexcept
+ override;
+
+ /**
+ * Shut down the exporter.
+ * @param timeout an optional timeout, the default timeout of 0 means that no
+ * timeout is applied.
+ * @return return the status of this operation
+ */
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override;
+
+private:
+ // The configuration options associated with this exporter.
+ const OtlpHttpExporterOptions options_;
+
+ // Object that stores the HTTP sessions that have been created
+ std::unique_ptr<OtlpHttpClient> http_client_;
+ // For testing
+ friend class OtlpHttpExporterTestPeer;
+ /**
+ * Create an OtlpHttpExporter using the specified http client.
+ * Only tests can call this constructor directly.
+ * @param http_client the http client to be used for exporting
+ */
+ OtlpHttpExporter(std::unique_ptr<OtlpHttpClient> http_client);
+};
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_exporter.h
new file mode 100644
index 000000000..d330e62be
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_http_log_exporter.h
@@ -0,0 +1,110 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/sdk/logs/exporter.h"
+
+# include "opentelemetry/exporters/otlp/otlp_http_client.h"
+
+# include "opentelemetry/exporters/otlp/otlp_environment.h"
+
+# include <chrono>
+# include <memory>
+# include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+/**
+ * Struct to hold OTLP exporter options.
+ */
+struct OtlpHttpLogExporterOptions
+{
+ // The endpoint to export to. By default
+ // @see
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md
+ // @see https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver/otlpreceiver
+ std::string url = GetOtlpDefaultHttpLogEndpoint();
+
+ // By default, post json data
+ HttpRequestContentType content_type = HttpRequestContentType::kJson;
+
+ // If convert bytes into hex. By default, we will convert all bytes but id into base64
+ // This option is ignored if content_type is not kJson
+ JsonBytesMappingKind json_bytes_mapping = JsonBytesMappingKind::kHexId;
+
+ // If using the json name of protobuf field to set the key of json. By default, we will use the
+ // field name just like proto files.
+ bool use_json_name = false;
+
+ // Whether to print the status of the exporter in the console
+ bool console_debug = false;
+
+ // TODO: Enable/disable to verify SSL certificate
+ std::chrono::system_clock::duration timeout = GetOtlpDefaultLogTimeout();
+
+ // Additional HTTP headers
+ OtlpHeaders http_headers = GetOtlpDefaultLogHeaders();
+};
+
+/**
+ * The OTLP exporter exports log data in OpenTelemetry Protocol (OTLP) format.
+ */
+class OtlpHttpLogExporter final : public opentelemetry::sdk::logs::LogExporter
+{
+public:
+ /**
+ * Create an OtlpHttpLogExporter with default exporter options.
+ */
+ OtlpHttpLogExporter();
+
+ /**
+ * Create an OtlpHttpLogExporter with user specified options.
+ * @param options An object containing the user's configuration options.
+ */
+ OtlpHttpLogExporter(const OtlpHttpLogExporterOptions &options);
+
+ /**
+ * Creates a recordable that stores the data in a JSON object
+ */
+ std::unique_ptr<opentelemetry::sdk::logs::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Exports a vector of log records to the Elasticsearch instance. Guaranteed to return after a
+ * timeout specified from the options passed from the constructor.
+ * @param records A list of log records to send to Elasticsearch.
+ */
+ opentelemetry::sdk::common::ExportResult Export(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::logs::Recordable>> &records) noexcept
+ override;
+
+ /**
+ * Shutdown this exporter.
+ * @param timeout The maximum time to wait for the shutdown method to return
+ */
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override;
+
+private:
+ // Configuration options for the exporter
+ const OtlpHttpLogExporterOptions options_;
+
+ // Object that stores the HTTP sessions that have been created
+ std::unique_ptr<OtlpHttpClient> http_client_;
+ // For testing
+ friend class OtlpHttpLogExporterTestPeer;
+ /**
+ * Create an OtlpHttpLogExporter using the specified http client.
+ * Only tests can call this constructor directly.
+ * @param http_client the http client to be used for exporting
+ */
+ OtlpHttpLogExporter(std::unique_ptr<OtlpHttpClient> http_client);
+};
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h
new file mode 100644
index 000000000..57ae94ea0
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_log_recordable.h
@@ -0,0 +1,118 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+// clang-format off
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+# 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
+
+# include "opentelemetry/sdk/common/attribute_utils.h"
+# include "opentelemetry/sdk/logs/recordable.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+/**
+ * An OTLP Recordable implemenation for Logs.
+ */
+class OtlpLogRecordable final : public opentelemetry::sdk::logs::Recordable
+{
+public:
+ virtual ~OtlpLogRecordable() = default;
+
+ proto::logs::v1::LogRecord &log_record() noexcept { return log_record_; }
+ const proto::logs::v1::LogRecord &log_record() const noexcept { return log_record_; }
+
+ /** Dynamically converts the resource of this log into a proto. */
+ proto::resource::v1::Resource ProtoResource() const 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 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;
+
+ /** Returns the associated resource */
+ const opentelemetry::sdk::resource::Resource &GetResource() const noexcept;
+
+ /**
+ * 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:
+ proto::logs::v1::LogRecord log_record_;
+ const opentelemetry::sdk::resource::Resource *resource_ = nullptr;
+ // TODO shared resource
+ // const opentelemetry::sdk::resource::Resource *resource_ = nullptr;
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ *instrumentation_library_ = nullptr;
+};
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h
new file mode 100644
index 000000000..a29d063a9
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable.h
@@ -0,0 +1,72 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+#include "opentelemetry/proto/resource/v1/resource.pb.h"
+#include "opentelemetry/proto/trace/v1/trace.pb.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+#include "opentelemetry/sdk/trace/recordable.h"
+#include "opentelemetry/version.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+class OtlpRecordable final : public opentelemetry::sdk::trace::Recordable
+{
+public:
+ proto::trace::v1::Span &span() noexcept { return span_; }
+ const proto::trace::v1::Span &span() const noexcept { return span_; }
+
+ /** Dynamically converts the resource of this span into a proto. */
+ proto::resource::v1::Resource ProtoResource() const noexcept;
+
+ const std::string GetResourceSchemaURL() const noexcept;
+ const std::string GetInstrumentationLibrarySchemaURL() const noexcept;
+
+ proto::common::v1::InstrumentationLibrary GetProtoInstrumentationLibrary() const noexcept;
+
+ void SetIdentity(const opentelemetry::trace::SpanContext &span_context,
+ opentelemetry::trace::SpanId parent_span_id) noexcept override;
+
+ void SetAttribute(nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value) noexcept override;
+
+ void AddEvent(nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept override;
+
+ void AddLink(const opentelemetry::trace::SpanContext &span_context,
+ const common::KeyValueIterable &attributes) noexcept override;
+
+ void SetStatus(trace::StatusCode code, nostd::string_view description) noexcept override;
+
+ void SetName(nostd::string_view name) noexcept override;
+
+ void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override;
+
+ void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override;
+
+ void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept override;
+
+ void SetDuration(std::chrono::nanoseconds duration) noexcept override;
+
+ void SetInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library) noexcept override;
+
+private:
+ proto::trace::v1::Span span_;
+ const opentelemetry::sdk::resource::Resource *resource_ = nullptr;
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ *instrumentation_library_ = nullptr;
+};
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable_utils.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable_utils.h
new file mode 100644
index 000000000..0c09bc871
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_recordable_utils.h
@@ -0,0 +1,63 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+#include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h"
+#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
+#include "opentelemetry/proto/resource/v1/resource.pb.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+#include "opentelemetry/common/attribute_value.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/version.h"
+
+#include "opentelemetry/sdk/common/attribute_utils.h"
+#include "opentelemetry/sdk/resource/resource.h"
+#include "opentelemetry/sdk/trace/recordable.h"
+
+#ifdef ENABLE_LOGS_PREVIEW
+# include "opentelemetry/sdk/logs/recordable.h"
+#endif
+
+#include <memory>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+/**
+ * The OtlpRecordableUtils contains utility functions for OTLP recordable
+ */
+class OtlpRecordableUtils
+{
+public:
+ static void PopulateAttribute(opentelemetry::proto::common::v1::KeyValue *attribute,
+ nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value) noexcept;
+
+ static void PopulateAttribute(
+ opentelemetry::proto::common::v1::KeyValue *attribute,
+ nostd::string_view key,
+ const opentelemetry::sdk::common::OwnedAttributeValue &value) noexcept;
+
+ static void PopulateAttribute(opentelemetry::proto::resource::v1::Resource *proto,
+ const opentelemetry::sdk::resource::Resource &resource) noexcept;
+
+ static void PopulateRequest(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::trace::Recordable>> &spans,
+ proto::collector::trace::v1::ExportTraceServiceRequest *request) noexcept;
+
+#ifdef ENABLE_LOGS_PREVIEW
+ static void PopulateRequest(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::logs::Recordable>> &logs,
+ proto::collector::logs::v1::ExportLogsServiceRequest *request) noexcept;
+#endif
+};
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_prefix.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_prefix.h
new file mode 100644
index 000000000..ae103b017
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_prefix.h
@@ -0,0 +1,37 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+// This file may be include multiple times, do not add #pragma once here
+
+#if defined(_MSC_VER)
+# pragma warning(push)
+# if ((defined(__cplusplus) && __cplusplus >= 201704L) || \
+ (defined(_MSVC_LANG) && _MSVC_LANG >= 201704L))
+# pragma warning(disable : 4996)
+# pragma warning(disable : 4309)
+# if _MSC_VER >= 1922
+# pragma warning(disable : 5054)
+# endif
+# endif
+
+# if _MSC_VER < 1910
+# pragma warning(disable : 4800)
+# endif
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4251)
+# pragma warning(disable : 4267)
+# pragma warning(disable : 4668)
+# pragma warning(disable : 4946)
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__apple_build_version__)
+# if (__GNUC__ * 100 + __GNUC_MINOR__ * 10) >= 460
+# pragma GCC diagnostic push
+# endif
+# pragma GCC diagnostic ignored "-Wunused-parameter"
+# pragma GCC diagnostic ignored "-Wtype-limits"
+#elif defined(__clang__) || defined(__apple_build_version__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wunused-parameter"
+# pragma clang diagnostic ignored "-Wtype-limits"
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_suffix.h b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_suffix.h
new file mode 100644
index 000000000..26e7a2913
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/include/opentelemetry/exporters/otlp/protobuf_include_suffix.h
@@ -0,0 +1,16 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+// This file may be include multiple times, do not add #pragma once here
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__apple_build_version__)
+# if (__GNUC__ * 100 + __GNUC_MINOR__ * 10) >= 460
+# pragma GCC diagnostic pop
+# endif
+#elif defined(__clang__) || defined(__apple_build_version__)
+# pragma clang diagnostic pop
+#endif
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_exporter.cc
new file mode 100644
index 000000000..32f4a60a5
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_exporter.cc
@@ -0,0 +1,161 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h"
+#include <mutex>
+#include "opentelemetry/exporters/otlp/otlp_recordable.h"
+#include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+#include "opentelemetry/ext/http/common/url_parser.h"
+#include "opentelemetry/sdk_config.h"
+
+#include <grpcpp/grpcpp.h>
+#include <fstream>
+#include <sstream> // std::stringstream
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+// ----------------------------- Helper functions ------------------------------
+static std::string get_file_contents(const char *fpath)
+{
+ std::ifstream finstream(fpath);
+ std::string contents;
+ contents.assign((std::istreambuf_iterator<char>(finstream)), std::istreambuf_iterator<char>());
+ finstream.close();
+ return contents;
+}
+
+/**
+ * Create gRPC channel from the exporter options.
+ */
+std::shared_ptr<grpc::Channel> MakeGrpcChannel(const OtlpGrpcExporterOptions &options)
+{
+ std::shared_ptr<grpc::Channel> channel;
+
+ //
+ // Scheme is allowed in OTLP endpoint definition, but is not allowed for creating gRPC channel.
+ // Passing URI with scheme to grpc::CreateChannel could resolve the endpoint to some unexpected
+ // address.
+ //
+
+ ext::http::common::UrlParser url(options.endpoint);
+ if (!url.success_)
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OTLP Exporter] invalid endpoint: " << options.endpoint);
+
+ return nullptr;
+ }
+
+ std::string grpc_target = url.host_ + ":" + std::to_string(static_cast<int>(url.port_));
+
+ if (options.use_ssl_credentials)
+ {
+ grpc::SslCredentialsOptions ssl_opts;
+ if (options.ssl_credentials_cacert_path.empty())
+ {
+ ssl_opts.pem_root_certs = options.ssl_credentials_cacert_as_string;
+ }
+ else
+ {
+ ssl_opts.pem_root_certs = get_file_contents((options.ssl_credentials_cacert_path).c_str());
+ }
+ channel = grpc::CreateChannel(grpc_target, grpc::SslCredentials(ssl_opts));
+ }
+ else
+ {
+ channel = grpc::CreateChannel(grpc_target, grpc::InsecureChannelCredentials());
+ }
+
+ return channel;
+}
+
+/**
+ * Create service stub to communicate with the OpenTelemetry Collector.
+ */
+std::unique_ptr<proto::collector::trace::v1::TraceService::Stub> MakeTraceServiceStub(
+ const OtlpGrpcExporterOptions &options)
+{
+ return proto::collector::trace::v1::TraceService::NewStub(MakeGrpcChannel(options));
+}
+
+// -------------------------------- Constructors --------------------------------
+
+OtlpGrpcExporter::OtlpGrpcExporter() : OtlpGrpcExporter(OtlpGrpcExporterOptions()) {}
+
+OtlpGrpcExporter::OtlpGrpcExporter(const OtlpGrpcExporterOptions &options)
+ : options_(options), trace_service_stub_(MakeTraceServiceStub(options))
+{}
+
+OtlpGrpcExporter::OtlpGrpcExporter(
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub)
+ : options_(OtlpGrpcExporterOptions()), trace_service_stub_(std::move(stub))
+{}
+
+// ----------------------------- Exporter methods ------------------------------
+
+std::unique_ptr<sdk::trace::Recordable> OtlpGrpcExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<sdk::trace::Recordable>(new OtlpRecordable);
+}
+
+sdk::common::ExportResult OtlpGrpcExporter::Export(
+ const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OTLP gRPC] Exporting " << spans.size()
+ << " span(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+ if (spans.empty())
+ {
+ return sdk::common::ExportResult::kSuccess;
+ }
+
+ proto::collector::trace::v1::ExportTraceServiceRequest request;
+ OtlpRecordableUtils::PopulateRequest(spans, &request);
+
+ grpc::ClientContext context;
+ proto::collector::trace::v1::ExportTraceServiceResponse response;
+
+ if (options_.timeout.count() > 0)
+ {
+ context.set_deadline(std::chrono::system_clock::now() + options_.timeout);
+ }
+
+ for (auto &header : options_.metadata)
+ {
+ context.AddMetadata(header.first, header.second);
+ }
+
+ grpc::Status status = trace_service_stub_->Export(&context, request, &response);
+
+ if (!status.ok())
+ {
+
+ OTEL_INTERNAL_LOG_ERROR(
+ "[OTLP TRACE GRPC Exporter] Export() failed: " << status.error_message());
+ return sdk::common::ExportResult::kFailure;
+ }
+ return sdk::common::ExportResult::kSuccess;
+}
+
+bool OtlpGrpcExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool OtlpGrpcExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_log_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_log_exporter.cc
new file mode 100644
index 000000000..38bfb0a5b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_grpc_log_exporter.cc
@@ -0,0 +1,181 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h"
+# include "opentelemetry/exporters/otlp/otlp_log_recordable.h"
+# include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+
+// clang-format off
+
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+# include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h"
+# include "opentelemetry/proto/collector/logs/v1/logs_service.grpc.pb.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+// clang-format on
+
+# include "opentelemetry/ext/http/common/url_parser.h"
+# include "opentelemetry/sdk/common/global_log_handler.h"
+
+# include <chrono>
+# include <fstream>
+# include <memory>
+
+# include <grpcpp/grpcpp.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+// ----------------------------- Helper functions ------------------------------
+// TODO: move exporters shared function to OTLP common library.
+static std::string get_file_contents(const char *fpath)
+{
+ std::ifstream finstream(fpath);
+ std::string contents;
+ contents.assign((std::istreambuf_iterator<char>(finstream)), std::istreambuf_iterator<char>());
+ finstream.close();
+ return contents;
+}
+
+struct OtlpGrpcExporterOptions;
+
+/**
+ * Create gRPC channel from the exporter options.
+ */
+static std::shared_ptr<grpc::Channel> MakeGrpcChannel(const OtlpGrpcExporterOptions &options)
+{
+ std::shared_ptr<grpc::Channel> channel;
+
+ //
+ // Scheme is allowed in OTLP endpoint definition, but is not allowed for creating gRPC channel.
+ // Passing URI with scheme to grpc::CreateChannel could resolve the endpoint to some unexpected
+ // address.
+ //
+
+ ext::http::common::UrlParser url(options.endpoint);
+ if (!url.success_)
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OTLP Exporter] invalid endpoint: " << options.endpoint);
+
+ return nullptr;
+ }
+
+ std::string grpc_target = url.host_ + ":" + std::to_string(static_cast<int>(url.port_));
+
+ if (options.use_ssl_credentials)
+ {
+ grpc::SslCredentialsOptions ssl_opts;
+ if (options.ssl_credentials_cacert_path.empty())
+ {
+ ssl_opts.pem_root_certs = options.ssl_credentials_cacert_as_string;
+ }
+ else
+ {
+ ssl_opts.pem_root_certs = get_file_contents((options.ssl_credentials_cacert_path).c_str());
+ }
+ channel = grpc::CreateChannel(grpc_target, grpc::SslCredentials(ssl_opts));
+ }
+ else
+ {
+ channel = grpc::CreateChannel(grpc_target, grpc::InsecureChannelCredentials());
+ }
+
+ return channel;
+}
+
+// ----------------------------- Helper functions ------------------------------
+
+/**
+ * Create log service stub to communicate with the OpenTelemetry Collector.
+ */
+std::unique_ptr<::opentelemetry::proto::collector::logs::v1::LogsService::Stub> MakeLogServiceStub(
+ const OtlpGrpcExporterOptions &options)
+{
+ return proto::collector::logs::v1::LogsService::NewStub(MakeGrpcChannel(options));
+}
+
+// -------------------------------- Constructors --------------------------------
+
+OtlpGrpcLogExporter::OtlpGrpcLogExporter() : OtlpGrpcLogExporter(OtlpGrpcExporterOptions()) {}
+
+OtlpGrpcLogExporter::OtlpGrpcLogExporter(const OtlpGrpcExporterOptions &options)
+ : options_(options), log_service_stub_(MakeLogServiceStub(options))
+{}
+
+OtlpGrpcLogExporter::OtlpGrpcLogExporter(
+ std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> stub)
+ : options_(OtlpGrpcExporterOptions()), log_service_stub_(std::move(stub))
+{}
+
+// ----------------------------- Exporter methods ------------------------------
+
+std::unique_ptr<opentelemetry::sdk::logs::Recordable> OtlpGrpcLogExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<opentelemetry::sdk::logs::Recordable>(
+ new exporter::otlp::OtlpLogRecordable());
+}
+
+opentelemetry::sdk::common::ExportResult OtlpGrpcLogExporter::Export(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::logs::Recordable>> &logs) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OTLP gRPC log] Exporting " << logs.size()
+ << " log(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+ if (logs.empty())
+ {
+ return sdk::common::ExportResult::kSuccess;
+ }
+
+ proto::collector::logs::v1::ExportLogsServiceRequest request;
+ OtlpRecordableUtils::PopulateRequest(logs, &request);
+
+ grpc::ClientContext context;
+ proto::collector::logs::v1::ExportLogsServiceResponse response;
+
+ if (options_.timeout.count() > 0)
+ {
+ context.set_deadline(std::chrono::system_clock::now() + options_.timeout);
+ }
+
+ for (auto &header : options_.metadata)
+ {
+ context.AddMetadata(header.first, header.second);
+ }
+ grpc::Status status = log_service_stub_->Export(&context, request, &response);
+
+ if (!status.ok())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OTLP LOG GRPC Exporter] Export() failed: " << status.error_message());
+ return sdk::common::ExportResult::kFailure;
+ }
+ return sdk::common::ExportResult::kSuccess;
+}
+
+bool OtlpGrpcLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool OtlpGrpcLogExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_client.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_client.cc
new file mode 100644
index 000000000..e3e1a8c46
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_client.cc
@@ -0,0 +1,721 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_http_client.h"
+
+#if defined(HAVE_GSL)
+# include <gsl/gsl>
+#else
+# include <assert.h>
+#endif
+
+#include "opentelemetry/ext/http/client/http_client_factory.h"
+#include "opentelemetry/ext/http/common/url_parser.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+#include <mutex>
+#include "google/protobuf/message.h"
+#include "google/protobuf/reflection.h"
+#include "google/protobuf/stubs/common.h"
+#include "google/protobuf/stubs/stringpiece.h"
+#include "nlohmann/json.hpp"
+
+#if defined(GOOGLE_PROTOBUF_VERSION) && GOOGLE_PROTOBUF_VERSION >= 3007000
+# include "google/protobuf/stubs/strutil.h"
+#else
+# include "google/protobuf/stubs/port.h"
+namespace google
+{
+namespace protobuf
+{
+LIBPROTOBUF_EXPORT void Base64Escape(StringPiece src, std::string *dest);
+} // namespace protobuf
+} // namespace google
+#endif
+
+#include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+#include "opentelemetry/sdk/common/global_log_handler.h"
+#include "opentelemetry/sdk_config.h"
+
+#include <condition_variable>
+#include <fstream>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifdef GetMessage
+# undef GetMessage
+#endif
+
+namespace nostd = opentelemetry::nostd;
+namespace http_client = opentelemetry::ext::http::client;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+namespace
+{
+
+/**
+ * This class handles the response message from the Elasticsearch request
+ */
+class ResponseHandler : public http_client::EventHandler
+{
+public:
+ /**
+ * Creates a response handler, that by default doesn't display to console
+ */
+ ResponseHandler(bool console_debug = false) : console_debug_{console_debug} {}
+
+ /**
+ * Automatically called when the response is received, store the body into a string and notify any
+ * threads blocked on this result
+ */
+ void OnResponse(http_client::Response &response) noexcept override
+ {
+ // Lock the private members so they can't be read while being modified
+ {
+ std::unique_lock<std::mutex> lk(mutex_);
+
+ // Store the body of the request
+ body_ = std::string(response.GetBody().begin(), response.GetBody().end());
+
+ if (console_debug_)
+ {
+ std::stringstream ss;
+ ss << "[OTLP HTTP Client] Status:" << response.GetStatusCode() << "Header:";
+ response.ForEachHeader([&ss](opentelemetry::nostd::string_view header_name,
+ opentelemetry::nostd::string_view header_value) {
+ ss << "\t" << header_name.data() << " : " << header_value.data() << ",";
+ return true;
+ });
+ ss << "Body:" << body_;
+ OTEL_INTERNAL_LOG_DEBUG(ss.str());
+ }
+
+ // Set the response_received_ flag to true and notify any threads waiting on this result
+ response_received_ = true;
+ stop_waiting_ = true;
+ }
+ cv_.notify_all();
+ }
+
+ /**resource
+ * A method the user calls to block their thread until the response is received. The longest
+ * duration is the timeout of the request, set by SetTimeoutMs()
+ */
+ bool waitForResponse()
+ {
+ std::unique_lock<std::mutex> lk(mutex_);
+ cv_.wait(lk, [this] { return stop_waiting_; });
+ return response_received_;
+ }
+
+ /**
+ * Returns the body of the response
+ */
+ std::string GetResponseBody()
+ {
+ // Lock so that body_ can't be written to while returning it
+ std::unique_lock<std::mutex> lk(mutex_);
+ return body_;
+ }
+
+ // Callback method when an http event occurs
+ void OnEvent(http_client::SessionState state,
+ opentelemetry::nostd::string_view reason) noexcept override
+ {
+ // need to modify stop_waiting_ under lock before calling notify_all
+ switch (state)
+ {
+ case http_client::SessionState::CreateFailed:
+ case http_client::SessionState::ConnectFailed:
+ case http_client::SessionState::SendFailed:
+ case http_client::SessionState::SSLHandshakeFailed:
+ case http_client::SessionState::TimedOut:
+ case http_client::SessionState::NetworkError:
+ case http_client::SessionState::Cancelled: {
+ std::unique_lock<std::mutex> lk(mutex_);
+ stop_waiting_ = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // If any failure event occurs, release the condition variable to unblock main thread
+ switch (state)
+ {
+ case http_client::SessionState::CreateFailed:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: session create failed");
+ cv_.notify_all();
+ break;
+
+ case http_client::SessionState::Created:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: session created");
+ }
+ break;
+
+ case http_client::SessionState::Destroyed:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: session destroyed");
+ }
+ break;
+
+ case http_client::SessionState::Connecting:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: connecting to peer");
+ }
+ break;
+
+ case http_client::SessionState::ConnectFailed:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: connection failed");
+ cv_.notify_all();
+ break;
+
+ case http_client::SessionState::Connected:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: connected");
+ }
+ break;
+
+ case http_client::SessionState::Sending:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: sending request");
+ }
+ break;
+
+ case http_client::SessionState::SendFailed:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: request send failed");
+ cv_.notify_all();
+ break;
+
+ case http_client::SessionState::Response:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: response received");
+ }
+ break;
+
+ case http_client::SessionState::SSLHandshakeFailed:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: SSL handshake failed");
+ cv_.notify_all();
+ break;
+
+ case http_client::SessionState::TimedOut:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: request time out");
+ cv_.notify_all();
+ break;
+
+ case http_client::SessionState::NetworkError:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: network error");
+ cv_.notify_all();
+ break;
+
+ case http_client::SessionState::ReadError:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Session state: error reading response");
+ }
+ break;
+
+ case http_client::SessionState::WriteError:
+ if (console_debug_)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] DEBUG:Session state: error writing request");
+ }
+ break;
+
+ case http_client::SessionState::Cancelled:
+ OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Client] Session state: (manually) cancelled\n");
+ cv_.notify_all();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+private:
+ // Define a condition variable and mutex
+ std::condition_variable cv_;
+ std::mutex mutex_;
+
+ // Whether notify has been called
+ bool stop_waiting_ = false;
+
+ // Whether the response has been received
+ bool response_received_ = false;
+
+ // A string to store the response body
+ std::string body_ = "";
+
+ // Whether to print the results from the callback
+ bool console_debug_ = false;
+};
+
+static inline char HexEncode(unsigned char byte)
+{
+#if defined(HAVE_GSL)
+ Expects(byte <= 16);
+#else
+ assert(byte <= 16);
+#endif
+ if (byte >= 10)
+ {
+ return byte - 10 + 'a';
+ }
+ else
+ {
+ return byte + '0';
+ }
+}
+
+static std::string HexEncode(const std::string &bytes)
+{
+ std::string ret;
+ ret.reserve(bytes.size() * 2);
+ for (std::string::size_type i = 0; i < bytes.size(); ++i)
+ {
+ unsigned char byte = static_cast<unsigned char>(bytes[i]);
+ ret.push_back(HexEncode(byte >> 4));
+ ret.push_back(HexEncode(byte & 0x0f));
+ }
+ return ret;
+}
+
+static std::string BytesMapping(const std::string &bytes,
+ const google::protobuf::FieldDescriptor *field_descriptor,
+ JsonBytesMappingKind kind)
+{
+ switch (kind)
+ {
+ case JsonBytesMappingKind::kHexId: {
+ if (field_descriptor->lowercase_name() == "trace_id" ||
+ field_descriptor->lowercase_name() == "span_id" ||
+ field_descriptor->lowercase_name() == "parent_span_id")
+ {
+ return HexEncode(bytes);
+ }
+ else
+ {
+ std::string base64_value;
+ google::protobuf::Base64Escape(bytes, &base64_value);
+ return base64_value;
+ }
+ }
+ case JsonBytesMappingKind::kBase64: {
+ // Base64 is the default bytes mapping of protobuf
+ std::string base64_value;
+ google::protobuf::Base64Escape(bytes, &base64_value);
+ return base64_value;
+ }
+ case JsonBytesMappingKind::kHex:
+ return HexEncode(bytes);
+ default:
+ return bytes;
+ }
+}
+
+static void ConvertGenericFieldToJson(nlohmann::json &value,
+ const google::protobuf::Message &message,
+ const google::protobuf::FieldDescriptor *field_descriptor,
+ const OtlpHttpClientOptions &options);
+
+static void ConvertListFieldToJson(nlohmann::json &value,
+ const google::protobuf::Message &message,
+ const google::protobuf::FieldDescriptor *field_descriptor,
+ const OtlpHttpClientOptions &options);
+
+static void ConvertGenericMessageToJson(nlohmann::json &value,
+ const google::protobuf::Message &message,
+ const OtlpHttpClientOptions &options)
+{
+ std::vector<const google::protobuf::FieldDescriptor *> fields_with_data;
+ message.GetReflection()->ListFields(message, &fields_with_data);
+ for (std::size_t i = 0; i < fields_with_data.size(); ++i)
+ {
+ const google::protobuf::FieldDescriptor *field_descriptor = fields_with_data[i];
+ nlohmann::json &child_value = options.use_json_name ? value[field_descriptor->json_name()]
+ : value[field_descriptor->name()];
+ if (field_descriptor->is_repeated())
+ {
+ ConvertListFieldToJson(child_value, message, field_descriptor, options);
+ }
+ else
+ {
+ ConvertGenericFieldToJson(child_value, message, field_descriptor, options);
+ }
+ }
+}
+
+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<google::protobuf::uint8 *>(&output[0]));
+ }
+ return true;
+}
+
+void ConvertGenericFieldToJson(nlohmann::json &value,
+ const google::protobuf::Message &message,
+ const google::protobuf::FieldDescriptor *field_descriptor,
+ const OtlpHttpClientOptions &options)
+{
+ switch (field_descriptor->cpp_type())
+ {
+ case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
+ value = message.GetReflection()->GetInt32(message, field_descriptor);
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_INT64: {
+ // According to Protobuf specs 64-bit integer numbers in JSON-encoded payloads are encoded as
+ // decimal strings, and either numbers or strings are accepted when decoding.
+ value = std::to_string(message.GetReflection()->GetInt64(message, field_descriptor));
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
+ value = message.GetReflection()->GetUInt32(message, field_descriptor);
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
+ // According to Protobuf specs 64-bit integer numbers in JSON-encoded payloads are encoded as
+ // decimal strings, and either numbers or strings are accepted when decoding.
+ value = std::to_string(message.GetReflection()->GetUInt64(message, field_descriptor));
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
+ std::string empty;
+ if (field_descriptor->type() == google::protobuf::FieldDescriptor::TYPE_BYTES)
+ {
+ value = BytesMapping(
+ message.GetReflection()->GetStringReference(message, field_descriptor, &empty),
+ field_descriptor, options.json_bytes_mapping);
+ }
+ else
+ {
+ value = message.GetReflection()->GetStringReference(message, field_descriptor, &empty);
+ }
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
+ ConvertGenericMessageToJson(
+ value, message.GetReflection()->GetMessage(message, field_descriptor, nullptr), options);
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: {
+ value = message.GetReflection()->GetDouble(message, field_descriptor);
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: {
+ value = message.GetReflection()->GetFloat(message, field_descriptor);
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: {
+ value = message.GetReflection()->GetBool(message, field_descriptor);
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
+ value = message.GetReflection()->GetEnumValue(message, field_descriptor);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+void ConvertListFieldToJson(nlohmann::json &value,
+ const google::protobuf::Message &message,
+ const google::protobuf::FieldDescriptor *field_descriptor,
+ const OtlpHttpClientOptions &options)
+{
+ auto field_size = message.GetReflection()->FieldSize(message, field_descriptor);
+
+ switch (field_descriptor->cpp_type())
+ {
+ case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(message.GetReflection()->GetRepeatedInt32(message, field_descriptor, i));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_INT64: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ // According to Protobuf specs 64-bit integer numbers in JSON-encoded payloads are encoded
+ // as decimal strings, and either numbers or strings are accepted when decoding.
+ value.push_back(std::to_string(
+ message.GetReflection()->GetRepeatedInt64(message, field_descriptor, i)));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(message.GetReflection()->GetRepeatedUInt32(message, field_descriptor, i));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ // According to Protobuf specs 64-bit integer numbers in JSON-encoded payloads are encoded
+ // as decimal strings, and either numbers or strings are accepted when decoding.
+ value.push_back(std::to_string(
+ message.GetReflection()->GetRepeatedUInt64(message, field_descriptor, i)));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
+ std::string empty;
+ if (field_descriptor->type() == google::protobuf::FieldDescriptor::TYPE_BYTES)
+ {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(BytesMapping(message.GetReflection()->GetRepeatedStringReference(
+ message, field_descriptor, i, &empty),
+ field_descriptor, options.json_bytes_mapping));
+ }
+ }
+ else
+ {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(message.GetReflection()->GetRepeatedStringReference(
+ message, field_descriptor, i, &empty));
+ }
+ }
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ nlohmann::json sub_value;
+ ConvertGenericMessageToJson(
+ sub_value, message.GetReflection()->GetRepeatedMessage(message, field_descriptor, i),
+ options);
+ value.push_back(std::move(sub_value));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(message.GetReflection()->GetRepeatedDouble(message, field_descriptor, i));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(message.GetReflection()->GetRepeatedFloat(message, field_descriptor, i));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(message.GetReflection()->GetRepeatedBool(message, field_descriptor, i));
+ }
+
+ break;
+ }
+ case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
+ for (int i = 0; i < field_size; ++i)
+ {
+ value.push_back(
+ message.GetReflection()->GetRepeatedEnumValue(message, field_descriptor, i));
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+} // namespace
+
+OtlpHttpClient::OtlpHttpClient(OtlpHttpClientOptions &&options)
+ : options_(options), http_client_(http_client::HttpClientFactory::Create())
+{}
+
+OtlpHttpClient::OtlpHttpClient(OtlpHttpClientOptions &&options,
+ std::shared_ptr<ext::http::client::HttpClient> http_client)
+ : options_(options), http_client_(http_client)
+{}
+
+// ----------------------------- HTTP Client methods ------------------------------
+opentelemetry::sdk::common::ExportResult OtlpHttpClient::Export(
+ const google::protobuf::Message &message) noexcept
+{
+ // Return failure if this exporter has been shutdown
+ if (isShutdown())
+ {
+ const char *error_message = "[OTLP HTTP Client] Export failed, exporter is shutdown";
+ if (options_.console_debug)
+ {
+ std::cerr << error_message << std::endl;
+ }
+ OTEL_INTERNAL_LOG_ERROR(error_message);
+
+ return opentelemetry::sdk::common::ExportResult::kFailure;
+ }
+
+ // Parse uri and store it to cache
+ if (http_uri_.empty())
+ {
+ auto parse_url = opentelemetry::ext::http::common::UrlParser(std::string(options_.url));
+ if (!parse_url.success_)
+ {
+ std::string error_message = "[OTLP HTTP Client] Export failed, invalid url: " + options_.url;
+ if (options_.console_debug)
+ {
+ std::cerr << error_message << std::endl;
+ }
+ OTEL_INTERNAL_LOG_ERROR(error_message.c_str());
+
+ return opentelemetry::sdk::common::ExportResult::kFailure;
+ }
+
+ if (!parse_url.path_.empty() && parse_url.path_[0] == '/')
+ {
+ http_uri_ = parse_url.path_.substr(1);
+ }
+ else
+ {
+ http_uri_ = parse_url.path_;
+ }
+ }
+
+ http_client::Body body_vec;
+ std::string content_type;
+ if (options_.content_type == HttpRequestContentType::kBinary)
+ {
+ if (SerializeToHttpBody(body_vec, message))
+ {
+ if (options_.console_debug)
+ {
+ OTEL_INTERNAL_LOG_DEBUG(
+ "[OTLP HTTP Client] Request body(Binary): " << message.Utf8DebugString());
+ }
+ }
+ else
+ {
+ if (options_.console_debug)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Serialize body failed(Binary):"
+ << message.InitializationErrorString());
+ }
+ return opentelemetry::sdk::common::ExportResult::kFailure;
+ }
+ content_type = kHttpBinaryContentType;
+ }
+ else
+ {
+ nlohmann::json json_request;
+
+ // Convert from proto into json object
+ ConvertGenericMessageToJson(json_request, message, options_);
+
+ std::string post_body_json =
+ json_request.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace);
+ if (options_.console_debug)
+ {
+ OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Client] Request body(Json)" << post_body_json);
+ }
+ body_vec.assign(post_body_json.begin(), post_body_json.end());
+ content_type = kHttpJsonContentType;
+ }
+
+ // Send the request
+ auto session = http_client_->CreateSession(options_.url);
+ auto request = session->CreateRequest();
+
+ for (auto &header : options_.http_headers)
+ {
+ request->AddHeader(header.first, header.second);
+ }
+ request->SetUri(http_uri_);
+ request->SetTimeoutMs(std::chrono::duration_cast<std::chrono::milliseconds>(options_.timeout));
+ request->SetMethod(http_client::Method::Post);
+ request->SetBody(body_vec);
+ request->ReplaceHeader("Content-Type", content_type);
+
+ // Send the request
+ std::unique_ptr<ResponseHandler> handler(new ResponseHandler(options_.console_debug));
+ session->SendRequest(*handler);
+
+ // Wait for the response to be received
+ if (options_.console_debug)
+ {
+ OTEL_INTERNAL_LOG_DEBUG(
+ "[OTLP HTTP Client] DEBUG: Waiting for response from "
+ << options_.url << " (timeout = "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(options_.timeout).count()
+ << " milliseconds)");
+ }
+ bool write_successful = handler->waitForResponse();
+
+ // End the session
+ session->FinishSession();
+
+ // If an error occurred with the HTTP request
+ if (!write_successful)
+ {
+ // TODO: retry logic
+ return opentelemetry::sdk::common::ExportResult::kFailure;
+ }
+
+ return opentelemetry::sdk::common::ExportResult::kSuccess;
+}
+
+bool OtlpHttpClient::Shutdown(std::chrono::microseconds) noexcept
+{
+ {
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ }
+
+ // Shutdown the session manager
+ http_client_->CancelAllSessions();
+ http_client_->FinishAllSessions();
+
+ return true;
+}
+
+bool OtlpHttpClient::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_exporter.cc
new file mode 100644
index 000000000..92155dd00
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_exporter.cc
@@ -0,0 +1,66 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_http_exporter.h"
+#include "opentelemetry/exporters/otlp/otlp_recordable.h"
+#include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+namespace nostd = opentelemetry::nostd;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+OtlpHttpExporter::OtlpHttpExporter() : OtlpHttpExporter(OtlpHttpExporterOptions()) {}
+
+OtlpHttpExporter::OtlpHttpExporter(const OtlpHttpExporterOptions &options)
+ : options_(options),
+ http_client_(new OtlpHttpClient(OtlpHttpClientOptions(options.url,
+ options.content_type,
+ options.json_bytes_mapping,
+ options.use_json_name,
+ options.console_debug,
+ options.timeout,
+ options.http_headers)))
+{}
+
+OtlpHttpExporter::OtlpHttpExporter(std::unique_ptr<OtlpHttpClient> http_client)
+ : options_(OtlpHttpExporterOptions()), http_client_(std::move(http_client))
+{}
+// ----------------------------- Exporter methods ------------------------------
+
+std::unique_ptr<opentelemetry::sdk::trace::Recordable> OtlpHttpExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<opentelemetry::sdk::trace::Recordable>(
+ new exporter::otlp::OtlpRecordable());
+}
+
+opentelemetry::sdk::common::ExportResult OtlpHttpExporter::Export(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::trace::Recordable>> &spans) noexcept
+{
+ if (spans.empty())
+ {
+ return opentelemetry::sdk::common::ExportResult::kSuccess;
+ }
+
+ proto::collector::trace::v1::ExportTraceServiceRequest service_request;
+ OtlpRecordableUtils::PopulateRequest(spans, &service_request);
+ return http_client_->Export(service_request);
+}
+
+bool OtlpHttpExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ return http_client_->Shutdown(timeout);
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_log_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_log_exporter.cc
new file mode 100644
index 000000000..436c77bea
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_http_log_exporter.cc
@@ -0,0 +1,69 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/exporters/otlp/otlp_http_log_exporter.h"
+# include "opentelemetry/exporters/otlp/otlp_log_recordable.h"
+# include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+# include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+namespace nostd = opentelemetry::nostd;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+OtlpHttpLogExporter::OtlpHttpLogExporter() : OtlpHttpLogExporter(OtlpHttpLogExporterOptions()) {}
+
+OtlpHttpLogExporter::OtlpHttpLogExporter(const OtlpHttpLogExporterOptions &options)
+ : options_(options),
+ http_client_(new OtlpHttpClient(OtlpHttpClientOptions(options.url,
+ options.content_type,
+ options.json_bytes_mapping,
+ options.use_json_name,
+ options.console_debug,
+ options.timeout,
+ options.http_headers)))
+{}
+
+OtlpHttpLogExporter::OtlpHttpLogExporter(std::unique_ptr<OtlpHttpClient> http_client)
+ : options_(OtlpHttpLogExporterOptions()), http_client_(std::move(http_client))
+{}
+// ----------------------------- Exporter methods ------------------------------
+
+std::unique_ptr<opentelemetry::sdk::logs::Recordable> OtlpHttpLogExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<opentelemetry::sdk::logs::Recordable>(
+ new exporter::otlp::OtlpLogRecordable());
+}
+
+opentelemetry::sdk::common::ExportResult OtlpHttpLogExporter::Export(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::logs::Recordable>> &logs) noexcept
+{
+ if (logs.empty())
+ {
+ return opentelemetry::sdk::common::ExportResult::kSuccess;
+ }
+ proto::collector::logs::v1::ExportLogsServiceRequest service_request;
+ OtlpRecordableUtils::PopulateRequest(logs, &service_request);
+ return http_client_->Export(service_request);
+}
+
+bool OtlpHttpLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ return http_client_->Shutdown(timeout);
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_log_recordable.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_log_recordable.cc
new file mode 100644
index 000000000..d38fe241f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_log_recordable.cc
@@ -0,0 +1,235 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/common/macros.h"
+
+# include "opentelemetry/exporters/otlp/otlp_log_recordable.h"
+
+# include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+
+namespace nostd = opentelemetry::nostd;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+proto::resource::v1::Resource OtlpLogRecordable::ProtoResource() const noexcept
+{
+ proto::resource::v1::Resource proto;
+ if (nullptr == resource_)
+ {
+ OtlpRecordableUtils::PopulateAttribute(&proto, sdk::resource::Resource::GetDefault());
+ }
+ else
+ {
+ OtlpRecordableUtils::PopulateAttribute(&proto, *resource_);
+ }
+
+ return proto;
+}
+
+void OtlpLogRecordable::SetTimestamp(opentelemetry::common::SystemTimestamp timestamp) noexcept
+{
+ log_record_.set_time_unix_nano(timestamp.time_since_epoch().count());
+}
+
+void OtlpLogRecordable::SetSeverity(opentelemetry::logs::Severity severity) noexcept
+{
+ switch (severity)
+ {
+ case opentelemetry::logs::Severity::kTrace: {
+ log_record_.set_severity_text("TRACE");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_TRACE);
+ break;
+ }
+ case opentelemetry::logs::Severity::kTrace2: {
+ log_record_.set_severity_text("TRACE2");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_TRACE2);
+ break;
+ }
+ case opentelemetry::logs::Severity::kTrace3: {
+ log_record_.set_severity_text("TRACE3");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_TRACE3);
+ break;
+ }
+ case opentelemetry::logs::Severity::kTrace4: {
+ log_record_.set_severity_text("TRACE4");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_TRACE4);
+ break;
+ }
+ case opentelemetry::logs::Severity::kDebug: {
+ log_record_.set_severity_text("DEBUG");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_DEBUG);
+ break;
+ }
+ case opentelemetry::logs::Severity::kDebug2: {
+ log_record_.set_severity_text("DEBUG2");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_DEBUG2);
+ break;
+ }
+ case opentelemetry::logs::Severity::kDebug3: {
+ log_record_.set_severity_text("DEBUG3");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_DEBUG3);
+ break;
+ }
+ case opentelemetry::logs::Severity::kDebug4: {
+ log_record_.set_severity_text("DEBUG4");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_DEBUG4);
+ break;
+ }
+ case opentelemetry::logs::Severity::kInfo: {
+ log_record_.set_severity_text("INFO");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_INFO);
+ break;
+ }
+ case opentelemetry::logs::Severity::kInfo2: {
+ log_record_.set_severity_text("INFO2");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_INFO2);
+ break;
+ }
+ case opentelemetry::logs::Severity::kInfo3: {
+ log_record_.set_severity_text("INFO3");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_INFO3);
+ break;
+ }
+ case opentelemetry::logs::Severity::kInfo4: {
+ log_record_.set_severity_text("INFO4");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_INFO4);
+ break;
+ }
+ case opentelemetry::logs::Severity::kWarn: {
+ log_record_.set_severity_text("WARN");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_WARN);
+ break;
+ }
+ case opentelemetry::logs::Severity::kWarn2: {
+ log_record_.set_severity_text("WARN2");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_WARN2);
+ break;
+ }
+ case opentelemetry::logs::Severity::kWarn3: {
+ log_record_.set_severity_text("WARN3");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_WARN3);
+ break;
+ }
+ case opentelemetry::logs::Severity::kWarn4: {
+ log_record_.set_severity_text("WARN4");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_WARN4);
+ break;
+ }
+ case opentelemetry::logs::Severity::kError: {
+ log_record_.set_severity_text("ERROR");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_ERROR);
+ break;
+ }
+ case opentelemetry::logs::Severity::kError2: {
+ log_record_.set_severity_text("ERROR2");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_ERROR2);
+ break;
+ }
+ case opentelemetry::logs::Severity::kError3: {
+ log_record_.set_severity_text("ERROR3");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_ERROR3);
+ break;
+ }
+ case opentelemetry::logs::Severity::kError4: {
+ log_record_.set_severity_text("ERROR4");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_ERROR4);
+ break;
+ }
+ case opentelemetry::logs::Severity::kFatal: {
+ log_record_.set_severity_text("FATAL");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_FATAL);
+ break;
+ }
+ case opentelemetry::logs::Severity::kFatal2: {
+ log_record_.set_severity_text("FATAL2");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_FATAL2);
+ break;
+ }
+ case opentelemetry::logs::Severity::kFatal3: {
+ log_record_.set_severity_text("FATAL3");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_FATAL3);
+ break;
+ }
+ case opentelemetry::logs::Severity::kFatal4: {
+ log_record_.set_severity_text("FATAL4");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_FATAL4);
+ break;
+ }
+ default: {
+ log_record_.set_severity_text("INVALID");
+ log_record_.set_severity_number(proto::logs::v1::SEVERITY_NUMBER_UNSPECIFIED);
+ break;
+ }
+ }
+}
+
+void OtlpLogRecordable::SetBody(nostd::string_view message) noexcept
+{
+ log_record_.mutable_body()->set_string_value(message.data(), message.size());
+}
+
+void OtlpLogRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept
+{
+ resource_ = &resource;
+}
+
+const opentelemetry::sdk::resource::Resource &OtlpLogRecordable::GetResource() const noexcept
+{
+ OPENTELEMETRY_LIKELY_IF(nullptr != resource_) { return *resource_; }
+
+ return opentelemetry::sdk::resource::Resource::GetDefault();
+}
+
+void OtlpLogRecordable::SetAttribute(nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value) noexcept
+{
+ OtlpRecordableUtils::PopulateAttribute(log_record_.add_attributes(), key, value);
+}
+
+void OtlpLogRecordable::SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept
+{
+ log_record_.set_trace_id(reinterpret_cast<const char *>(trace_id.Id().data()),
+ trace::TraceId::kSize);
+}
+
+void OtlpLogRecordable::SetSpanId(opentelemetry::trace::SpanId span_id) noexcept
+{
+ log_record_.set_span_id(reinterpret_cast<const char *>(span_id.Id().data()),
+ trace::SpanId::kSize);
+}
+
+void OtlpLogRecordable::SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept
+{
+ 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
+{
+ OPENTELEMETRY_LIKELY_IF(nullptr != instrumentation_library_) { return *instrumentation_library_; }
+
+ static opentelemetry::nostd::unique_ptr<
+ opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary>
+ default_instrumentation =
+ opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create(
+ "default", "1.0.0", "https://opentelemetry.io/schemas/1.11.0");
+ return *default_instrumentation;
+}
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable.cc
new file mode 100644
index 000000000..0e6aa8f8a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable.cc
@@ -0,0 +1,187 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_recordable.h"
+
+#include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+
+namespace nostd = opentelemetry::nostd;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+void OtlpRecordable::SetIdentity(const opentelemetry::trace::SpanContext &span_context,
+ opentelemetry::trace::SpanId parent_span_id) noexcept
+{
+ span_.set_trace_id(reinterpret_cast<const char *>(span_context.trace_id().Id().data()),
+ trace::TraceId::kSize);
+ span_.set_span_id(reinterpret_cast<const char *>(span_context.span_id().Id().data()),
+ trace::SpanId::kSize);
+ if (parent_span_id.IsValid())
+ {
+ span_.set_parent_span_id(reinterpret_cast<const char *>(parent_span_id.Id().data()),
+ trace::SpanId::kSize);
+ }
+ span_.set_trace_state(span_context.trace_state()->ToHeader());
+}
+
+proto::resource::v1::Resource OtlpRecordable::ProtoResource() const noexcept
+{
+ proto::resource::v1::Resource proto;
+ if (resource_)
+ {
+ OtlpRecordableUtils::PopulateAttribute(&proto, *resource_);
+ }
+
+ return proto;
+}
+
+const std::string OtlpRecordable::GetResourceSchemaURL() const noexcept
+{
+ std::string schema_url;
+ if (resource_)
+ {
+ schema_url = resource_->GetSchemaURL();
+ }
+
+ return schema_url;
+}
+
+const std::string OtlpRecordable::GetInstrumentationLibrarySchemaURL() const noexcept
+{
+ std::string schema_url;
+ if (instrumentation_library_)
+ {
+ schema_url = instrumentation_library_->GetSchemaURL();
+ }
+
+ return schema_url;
+}
+
+proto::common::v1::InstrumentationLibrary OtlpRecordable::GetProtoInstrumentationLibrary()
+ const noexcept
+{
+ proto::common::v1::InstrumentationLibrary instrumentation_library;
+ if (instrumentation_library_)
+ {
+ instrumentation_library.set_name(instrumentation_library_->GetName());
+ instrumentation_library.set_version(instrumentation_library_->GetVersion());
+ }
+ return instrumentation_library;
+}
+
+void OtlpRecordable::SetResource(const sdk::resource::Resource &resource) noexcept
+{
+ resource_ = &resource;
+};
+
+void OtlpRecordable::SetAttribute(nostd::string_view key,
+ const common::AttributeValue &value) noexcept
+{
+ auto *attribute = span_.add_attributes();
+ OtlpRecordableUtils::PopulateAttribute(attribute, key, value);
+}
+
+void OtlpRecordable::AddEvent(nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept
+{
+ auto *event = span_.add_events();
+ event->set_name(name.data(), name.size());
+ event->set_time_unix_nano(timestamp.time_since_epoch().count());
+
+ attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
+ OtlpRecordableUtils::PopulateAttribute(event->add_attributes(), key, value);
+ return true;
+ });
+}
+
+void OtlpRecordable::AddLink(const trace::SpanContext &span_context,
+ const common::KeyValueIterable &attributes) noexcept
+{
+ auto *link = span_.add_links();
+ link->set_trace_id(reinterpret_cast<const char *>(span_context.trace_id().Id().data()),
+ trace::TraceId::kSize);
+ link->set_span_id(reinterpret_cast<const char *>(span_context.span_id().Id().data()),
+ trace::SpanId::kSize);
+ link->set_trace_state(span_context.trace_state()->ToHeader());
+ attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
+ OtlpRecordableUtils::PopulateAttribute(link->add_attributes(), key, value);
+ return true;
+ });
+}
+
+void OtlpRecordable::SetStatus(trace::StatusCode code, nostd::string_view description) noexcept
+{
+ span_.mutable_status()->set_code(proto::trace::v1::Status_StatusCode(code));
+ if (code == trace::StatusCode::kError)
+ {
+ span_.mutable_status()->set_message(description.data(), description.size());
+ }
+}
+
+void OtlpRecordable::SetName(nostd::string_view name) noexcept
+{
+ span_.set_name(name.data(), name.size());
+}
+
+void OtlpRecordable::SetSpanKind(trace::SpanKind span_kind) noexcept
+{
+ proto::trace::v1::Span_SpanKind proto_span_kind =
+ proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_UNSPECIFIED;
+
+ switch (span_kind)
+ {
+
+ case trace::SpanKind::kInternal:
+ proto_span_kind = proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_INTERNAL;
+ break;
+
+ case trace::SpanKind::kServer:
+ proto_span_kind = proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_SERVER;
+ break;
+
+ case trace::SpanKind::kClient:
+ proto_span_kind = proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_CLIENT;
+ break;
+
+ case trace::SpanKind::kProducer:
+ proto_span_kind = proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_PRODUCER;
+ break;
+
+ case trace::SpanKind::kConsumer:
+ proto_span_kind = proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_CONSUMER;
+ break;
+
+ default:
+ // shouldn't reach here.
+ proto_span_kind = proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_UNSPECIFIED;
+ }
+
+ span_.set_kind(proto_span_kind);
+}
+
+void OtlpRecordable::SetStartTime(common::SystemTimestamp start_time) noexcept
+{
+ span_.set_start_time_unix_nano(start_time.time_since_epoch().count());
+}
+
+void OtlpRecordable::SetDuration(std::chrono::nanoseconds duration) noexcept
+{
+ const uint64_t unix_end_time = span_.start_time_unix_nano() + duration.count();
+ span_.set_end_time_unix_nano(unix_end_time);
+}
+
+void OtlpRecordable::SetInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library) noexcept
+{
+ instrumentation_library_ = &instrumentation_library;
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable_utils.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable_utils.cc
new file mode 100644
index 000000000..bb3c84604
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/src/otlp_recordable_utils.cc
@@ -0,0 +1,362 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_recordable_utils.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+#include "opentelemetry/proto/logs/v1/logs.pb.h"
+#include "opentelemetry/proto/trace/v1/trace.pb.h"
+
+#include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+#include "opentelemetry/exporters/otlp/otlp_log_recordable.h"
+#include "opentelemetry/exporters/otlp/otlp_recordable.h"
+
+#include <list>
+#include <unordered_map>
+
+namespace nostd = opentelemetry::nostd;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+namespace
+{
+struct InstrumentationLibraryPointerHasher
+{
+ std::size_t operator()(const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ *instrumentation) const noexcept
+ {
+ return instrumentation->HashCode();
+ }
+};
+
+struct InstrumentationLibraryPointerEqual
+{
+ std::size_t operator()(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary *left,
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary *right)
+ const noexcept
+ {
+ return *left == *right;
+ }
+};
+} // namespace
+
+//
+// See `attribute_value.h` for details.
+//
+const int kAttributeValueSize = 16;
+const int kOwnedAttributeValueSize = 15;
+
+void OtlpRecordableUtils::PopulateAttribute(
+ opentelemetry::proto::common::v1::KeyValue *attribute,
+ nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value) noexcept
+{
+ if (nullptr == attribute)
+ {
+ return;
+ }
+
+ // Assert size of variant to ensure that this method gets updated if the variant
+ // definition changes
+ static_assert(
+ nostd::variant_size<opentelemetry::common::AttributeValue>::value == kAttributeValueSize,
+ "AttributeValue contains unknown type");
+
+ attribute->set_key(key.data(), key.size());
+
+ if (nostd::holds_alternative<bool>(value))
+ {
+ attribute->mutable_value()->set_bool_value(nostd::get<bool>(value));
+ }
+ else if (nostd::holds_alternative<int>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<int>(value));
+ }
+ else if (nostd::holds_alternative<int64_t>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<int64_t>(value));
+ }
+ else if (nostd::holds_alternative<unsigned int>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<unsigned int>(value));
+ }
+ else if (nostd::holds_alternative<uint64_t>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<uint64_t>(value));
+ }
+ else if (nostd::holds_alternative<double>(value))
+ {
+ attribute->mutable_value()->set_double_value(nostd::get<double>(value));
+ }
+ else if (nostd::holds_alternative<const char *>(value))
+ {
+ attribute->mutable_value()->set_string_value(nostd::get<const char *>(value));
+ }
+ else if (nostd::holds_alternative<nostd::string_view>(value))
+ {
+ attribute->mutable_value()->set_string_value(nostd::get<nostd::string_view>(value).data(),
+ nostd::get<nostd::string_view>(value).size());
+ }
+ else if (nostd::holds_alternative<nostd::span<const uint8_t>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const uint8_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const bool>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const bool>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_bool_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const int>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const int>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const int64_t>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const int64_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const unsigned int>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const unsigned int>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const uint64_t>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const uint64_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const double>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const double>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_double_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const nostd::string_view>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const nostd::string_view>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_string_value(val.data(),
+ val.size());
+ }
+ }
+}
+
+/** Maps from C++ attribute into OTLP proto attribute. */
+void OtlpRecordableUtils::PopulateAttribute(
+ opentelemetry::proto::common::v1::KeyValue *attribute,
+ nostd::string_view key,
+ const opentelemetry::sdk::common::OwnedAttributeValue &value) noexcept
+{
+ if (nullptr == attribute)
+ {
+ return;
+ }
+
+ // Assert size of variant to ensure that this method gets updated if the variant
+ // definition changes
+ static_assert(nostd::variant_size<opentelemetry::sdk::common::OwnedAttributeValue>::value ==
+ kOwnedAttributeValueSize,
+ "OwnedAttributeValue contains unknown type");
+
+ attribute->set_key(key.data(), key.size());
+
+ if (nostd::holds_alternative<bool>(value))
+ {
+ attribute->mutable_value()->set_bool_value(nostd::get<bool>(value));
+ }
+ else if (nostd::holds_alternative<int32_t>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<int32_t>(value));
+ }
+ else if (nostd::holds_alternative<int64_t>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<int64_t>(value));
+ }
+ else if (nostd::holds_alternative<uint32_t>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<uint32_t>(value));
+ }
+ else if (nostd::holds_alternative<uint64_t>(value))
+ {
+ attribute->mutable_value()->set_int_value(nostd::get<uint64_t>(value));
+ }
+ else if (nostd::holds_alternative<double>(value))
+ {
+ attribute->mutable_value()->set_double_value(nostd::get<double>(value));
+ }
+ else if (nostd::holds_alternative<std::string>(value))
+ {
+ attribute->mutable_value()->set_string_value(nostd::get<std::string>(value));
+ }
+ else if (nostd::holds_alternative<std::vector<bool>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<bool>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_bool_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<std::vector<int32_t>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<int32_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<std::vector<uint32_t>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<uint32_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<std::vector<int64_t>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<int64_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<std::vector<uint64_t>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<uint64_t>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_int_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<std::vector<double>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<double>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_double_value(val);
+ }
+ }
+ else if (nostd::holds_alternative<std::vector<std::string>>(value))
+ {
+ for (const auto &val : nostd::get<std::vector<std::string>>(value))
+ {
+ attribute->mutable_value()->mutable_array_value()->add_values()->set_string_value(val);
+ }
+ }
+}
+
+void OtlpRecordableUtils::PopulateAttribute(
+ opentelemetry::proto::resource::v1::Resource *proto,
+ const opentelemetry::sdk::resource::Resource &resource) noexcept
+{
+ if (nullptr == proto)
+ {
+ return;
+ }
+
+ for (const auto &kv : resource.GetAttributes())
+ {
+ OtlpRecordableUtils::PopulateAttribute(proto->add_attributes(), kv.first, kv.second);
+ }
+}
+
+void OtlpRecordableUtils::PopulateRequest(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::trace::Recordable>> &spans,
+ proto::collector::trace::v1::ExportTraceServiceRequest *request) noexcept
+{
+ if (nullptr == request)
+ {
+ return;
+ }
+
+ for (auto &recordable : spans)
+ {
+ auto rec = std::unique_ptr<OtlpRecordable>(static_cast<OtlpRecordable *>(recordable.release()));
+ auto resource_span = request->add_resource_spans();
+ auto instrumentation_lib = resource_span->add_instrumentation_library_spans();
+
+ *instrumentation_lib->add_spans() = std::move(rec->span());
+ *instrumentation_lib->mutable_instrumentation_library() = rec->GetProtoInstrumentationLibrary();
+
+ instrumentation_lib->set_schema_url(rec->GetInstrumentationLibrarySchemaURL());
+
+ *resource_span->mutable_resource() = rec->ProtoResource();
+ resource_span->set_schema_url(rec->GetResourceSchemaURL());
+ }
+}
+
+#ifdef ENABLE_LOGS_PREVIEW
+void OtlpRecordableUtils::PopulateRequest(
+ const nostd::span<std::unique_ptr<opentelemetry::sdk::logs::Recordable>> &logs,
+ proto::collector::logs::v1::ExportLogsServiceRequest *request) noexcept
+{
+ if (nullptr == request)
+ {
+ return;
+ }
+
+ using logs_index_by_instrumentation_type =
+ std::unordered_map<const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary *,
+ std::list<std::unique_ptr<OtlpLogRecordable>>,
+ InstrumentationLibraryPointerHasher, InstrumentationLibraryPointerEqual>;
+ std::unordered_map<const opentelemetry::sdk::resource::Resource *,
+ logs_index_by_instrumentation_type>
+ logs_index_by_resource;
+
+ for (auto &recordable : logs)
+ {
+ auto rec =
+ std::unique_ptr<OtlpLogRecordable>(static_cast<OtlpLogRecordable *>(recordable.release()));
+ auto instrumentation = &rec->GetInstrumentationLibrary();
+ auto resource = &rec->GetResource();
+
+ logs_index_by_resource[resource][instrumentation].emplace_back(std::move(rec));
+ }
+
+ for (auto &input_resource_log : logs_index_by_resource)
+ {
+ auto output_resource_log = request->add_resource_logs();
+ for (auto &input_scope_log : input_resource_log.second)
+ {
+ auto output_scope_log = output_resource_log->add_scope_logs();
+ for (auto &input_log_record : input_scope_log.second)
+ {
+ if (!output_resource_log->has_resource())
+ {
+ *output_resource_log->mutable_resource() = input_log_record->ProtoResource();
+ output_resource_log->set_schema_url(input_resource_log.first->GetSchemaURL());
+ }
+
+ if (!output_scope_log->has_scope())
+ {
+ output_scope_log->mutable_scope()->set_name(input_scope_log.first->GetName());
+ output_scope_log->mutable_scope()->set_version(input_scope_log.first->GetVersion());
+ output_scope_log->set_schema_url(input_scope_log.first->GetSchemaURL());
+ }
+
+ *output_scope_log->add_log_records() = std::move(input_log_record->log_record());
+ }
+ }
+ }
+}
+#endif
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc
new file mode 100644
index 000000000..c86f5e53b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_benchmark.cc
@@ -0,0 +1,216 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h"
+#include "opentelemetry/exporters/otlp/otlp_recordable.h"
+
+#include <benchmark/benchmark.h>
+#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 <benchmark/benchmark.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+namespace trace_api = opentelemetry::trace;
+
+const int kBatchSize = 200;
+const int kNumAttributes = 5;
+const int kNumIterations = 1000;
+
+const trace::TraceId kTraceId(std::array<const uint8_t, trace::TraceId::kSize>(
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
+const trace::SpanId kSpanId(std::array<const uint8_t, trace::SpanId::kSize>({0, 0, 0, 0, 0, 0, 0,
+ 2}));
+const trace::SpanId kParentSpanId(std::array<const uint8_t, trace::SpanId::kSize>({0, 0, 0, 0, 0, 0,
+ 0, 3}));
+const auto kTraceState = trace_api::TraceState::GetDefault() -> Set("key1", "value");
+const trace_api::SpanContext kSpanContext{
+ kTraceId, kSpanId, trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}, true, kTraceState};
+
+// ----------------------- Helper classes and functions ------------------------
+
+// Create a fake service stub to avoid dependency on gmock
+class FakeServiceStub : public proto::collector::trace::v1::TraceService::StubInterface
+{
+ grpc::Status Export(grpc::ClientContext *,
+ const proto::collector::trace::v1::ExportTraceServiceRequest &,
+ proto::collector::trace::v1::ExportTraceServiceResponse *) override
+ {
+ return grpc::Status::OK;
+ }
+
+ grpc::ClientAsyncResponseReaderInterface<proto::collector::trace::v1::ExportTraceServiceResponse>
+ *AsyncExportRaw(grpc::ClientContext *,
+ const proto::collector::trace::v1::ExportTraceServiceRequest &,
+ grpc::CompletionQueue *) override
+ {
+ return nullptr;
+ }
+
+ grpc::ClientAsyncResponseReaderInterface<proto::collector::trace::v1::ExportTraceServiceResponse>
+ *PrepareAsyncExportRaw(grpc::ClientContext *,
+ const proto::collector::trace::v1::ExportTraceServiceRequest &,
+ grpc::CompletionQueue *) override
+ {
+ return nullptr;
+ }
+};
+
+// OtlpGrpcExporterTestPeer is a friend class of OtlpGrpcExporter
+class OtlpGrpcExporterTestPeer
+{
+public:
+ std::unique_ptr<sdk::trace::SpanExporter> GetExporter()
+ {
+ auto mock_stub = new FakeServiceStub();
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub_interface(
+ mock_stub);
+ return std::unique_ptr<sdk::trace::SpanExporter>(
+ new exporter::otlp::OtlpGrpcExporter(std::move(stub_interface)));
+ }
+};
+
+// Helper function to create empty spans
+void CreateEmptySpans(std::array<std::unique_ptr<sdk::trace::Recordable>, kBatchSize> &recordables)
+{
+ for (int i = 0; i < kBatchSize; i++)
+ {
+ auto recordable = std::unique_ptr<sdk::trace::Recordable>(new OtlpRecordable);
+ recordables[i] = std::move(recordable);
+ }
+}
+
+// Helper function to create sparse spans
+void CreateSparseSpans(std::array<std::unique_ptr<sdk::trace::Recordable>, kBatchSize> &recordables)
+{
+ for (int i = 0; i < kBatchSize; i++)
+ {
+ auto recordable = std::unique_ptr<sdk::trace::Recordable>(new OtlpRecordable);
+
+ recordable->SetIdentity(kSpanContext, kParentSpanId);
+ recordable->SetName("TestSpan");
+ recordable->SetStartTime(common::SystemTimestamp(std::chrono::system_clock::now()));
+ recordable->SetDuration(std::chrono::nanoseconds(10));
+
+ recordables[i] = std::move(recordable);
+ }
+}
+
+// Helper function to create dense spans
+void CreateDenseSpans(std::array<std::unique_ptr<sdk::trace::Recordable>, kBatchSize> &recordables)
+{
+ for (int i = 0; i < kBatchSize; i++)
+ {
+ auto recordable = std::unique_ptr<sdk::trace::Recordable>(new OtlpRecordable);
+
+ recordable->SetIdentity(kSpanContext, kParentSpanId);
+ recordable->SetName("TestSpan");
+ recordable->SetStartTime(common::SystemTimestamp(std::chrono::system_clock::now()));
+ recordable->SetDuration(std::chrono::nanoseconds(10));
+
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ recordable->SetAttribute("int_key_" + i, static_cast<int64_t>(i));
+ recordable->SetAttribute("str_key_" + i, "string_val_" + i);
+ recordable->SetAttribute("bool_key_" + i, true);
+ }
+
+ recordables[i] = std::move(recordable);
+ }
+}
+
+// ------------------------------ Benchmark tests ------------------------------
+
+// Benchmark Export() with empty spans
+void BM_OtlpExporterEmptySpans(benchmark::State &state)
+{
+ std::unique_ptr<OtlpGrpcExporterTestPeer> testpeer(new OtlpGrpcExporterTestPeer());
+ auto exporter = testpeer->GetExporter();
+
+ while (state.KeepRunningBatch(kNumIterations))
+ {
+ std::array<std::unique_ptr<sdk::trace::Recordable>, kBatchSize> recordables;
+ CreateEmptySpans(recordables);
+ exporter->Export(nostd::span<std::unique_ptr<sdk::trace::Recordable>>(recordables));
+ }
+}
+BENCHMARK(BM_OtlpExporterEmptySpans);
+
+// Benchmark Export() with sparse spans
+void BM_OtlpExporterSparseSpans(benchmark::State &state)
+{
+ std::unique_ptr<OtlpGrpcExporterTestPeer> testpeer(new OtlpGrpcExporterTestPeer());
+ auto exporter = testpeer->GetExporter();
+
+ while (state.KeepRunningBatch(kNumIterations))
+ {
+ std::array<std::unique_ptr<sdk::trace::Recordable>, kBatchSize> recordables;
+ CreateSparseSpans(recordables);
+ exporter->Export(nostd::span<std::unique_ptr<sdk::trace::Recordable>>(recordables));
+ }
+}
+BENCHMARK(BM_OtlpExporterSparseSpans);
+
+// Benchmark Export() with dense spans
+void BM_OtlpExporterDenseSpans(benchmark::State &state)
+{
+ std::unique_ptr<OtlpGrpcExporterTestPeer> testpeer(new OtlpGrpcExporterTestPeer());
+ auto exporter = testpeer->GetExporter();
+
+ while (state.KeepRunningBatch(kNumIterations))
+ {
+ std::array<std::unique_ptr<sdk::trace::Recordable>, kBatchSize> recordables;
+ CreateDenseSpans(recordables);
+ exporter->Export(nostd::span<std::unique_ptr<sdk::trace::Recordable>>(recordables));
+ }
+}
+BENCHMARK(BM_OtlpExporterDenseSpans);
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+namespace
+{
+opentelemetry::exporter::otlp::OtlpGrpcExporterOptions opts;
+void InitTracer()
+{
+ // Create OTLP exporter instance
+ auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new otlp::OtlpGrpcExporter(opts));
+ auto processor = std::unique_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+ auto provider =
+ nostd::shared_ptr<trace::TracerProvider>(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/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_test.cc
new file mode 100755
index 000000000..091cb0d0c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_exporter_test.cc
@@ -0,0 +1,217 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef HAVE_CPP_STDLIB
+// Unfortunately as of 04/27/2021 the fix is NOT in the vcpkg snapshot of Google Test.
+// Remove above `#ifdef` once the GMock fix for C++20 is in the mainline.
+//
+// Please refer to this GitHub issue for additional details:
+// https://github.com/google/googletest/issues/2914
+// https://github.com/google/googletest/commit/61f010d703b32de9bfb20ab90ece38ab2f25977f
+//
+// If we compile using Visual Studio 2019 with `c++latest` (C++20) without the GMock fix,
+// then the compilation here fails in `gmock-actions.h` from:
+// .\tools\vcpkg\installed\x64-windows\include\gmock\gmock-actions.h(819):
+// error C2653: 'result_of': is not a class or namespace name
+//
+// That is because `std::result_of` has been removed in C++20.
+
+# include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+// Problematic code that pulls in Gmock and breaks with vs2019/c++latest :
+# include "opentelemetry/proto/collector/trace/v1/trace_service_mock.grpc.pb.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+# include "opentelemetry/sdk/trace/simple_processor.h"
+# include "opentelemetry/sdk/trace/tracer_provider.h"
+# include "opentelemetry/trace/provider.h"
+
+# include <gtest/gtest.h>
+
+# if defined(_MSC_VER)
+# include "opentelemetry/sdk/common/env_variables.h"
+using opentelemetry::sdk::common::setenv;
+using opentelemetry::sdk::common::unsetenv;
+# endif
+
+using namespace testing;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+class OtlpGrpcExporterTestPeer : public ::testing::Test
+{
+public:
+ std::unique_ptr<sdk::trace::SpanExporter> GetExporter(
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> &stub_interface)
+ {
+ return std::unique_ptr<sdk::trace::SpanExporter>(
+ new OtlpGrpcExporter(std::move(stub_interface)));
+ }
+
+ // Get the options associated with the given exporter.
+ const OtlpGrpcExporterOptions &GetOptions(std::unique_ptr<OtlpGrpcExporter> &exporter)
+ {
+ return exporter->options_;
+ }
+};
+
+TEST_F(OtlpGrpcExporterTestPeer, ShutdownTest)
+{
+ auto mock_stub = new proto::collector::trace::v1::MockTraceServiceStub();
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub_interface(
+ mock_stub);
+ auto exporter = GetExporter(stub_interface);
+
+ 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<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
+ EXPECT_CALL(*mock_stub, Export(_, _, _)).Times(Exactly(1)).WillOnce(Return(grpc::Status::OK));
+ auto result = exporter->Export(batch_1);
+ EXPECT_EQ(sdk::common::ExportResult::kSuccess, result);
+
+ exporter->Shutdown();
+
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
+ result = exporter->Export(batch_2);
+ EXPECT_EQ(sdk::common::ExportResult::kFailure, result);
+}
+
+// Call Export() directly
+TEST_F(OtlpGrpcExporterTestPeer, ExportUnitTest)
+{
+ auto mock_stub = new proto::collector::trace::v1::MockTraceServiceStub();
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub_interface(
+ mock_stub);
+ auto exporter = GetExporter(stub_interface);
+
+ auto recordable_1 = exporter->MakeRecordable();
+ recordable_1->SetName("Test span 1");
+ auto recordable_2 = exporter->MakeRecordable();
+ recordable_2->SetName("Test span 2");
+
+ // Test successful RPC
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
+ EXPECT_CALL(*mock_stub, Export(_, _, _)).Times(Exactly(1)).WillOnce(Return(grpc::Status::OK));
+ auto result = exporter->Export(batch_1);
+ EXPECT_EQ(sdk::common::ExportResult::kSuccess, result);
+
+ // Test failed RPC
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
+ EXPECT_CALL(*mock_stub, Export(_, _, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(grpc::Status::CANCELLED));
+ result = exporter->Export(batch_2);
+ EXPECT_EQ(sdk::common::ExportResult::kFailure, result);
+}
+
+// Create spans, let processor call Export()
+TEST_F(OtlpGrpcExporterTestPeer, ExportIntegrationTest)
+{
+ auto mock_stub = new proto::collector::trace::v1::MockTraceServiceStub();
+ std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub_interface(
+ mock_stub);
+
+ auto exporter = GetExporter(stub_interface);
+
+ auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(
+ new sdk::trace::SimpleSpanProcessor(std::move(exporter)));
+ auto provider = nostd::shared_ptr<trace::TracerProvider>(
+ new sdk::trace::TracerProvider(std::move(processor)));
+ auto tracer = provider->GetTracer("test");
+
+ EXPECT_CALL(*mock_stub, Export(_, _, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(grpc::Status::OK));
+
+ auto parent_span = tracer->StartSpan("Test parent span");
+ auto child_span = tracer->StartSpan("Test child span");
+ child_span->End();
+ parent_span->End();
+}
+
+// Test exporter configuration options
+TEST_F(OtlpGrpcExporterTestPeer, ConfigTest)
+{
+ OtlpGrpcExporterOptions opts;
+ opts.endpoint = "localhost:45454";
+ std::unique_ptr<OtlpGrpcExporter> exporter(new OtlpGrpcExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).endpoint, "localhost:45454");
+}
+
+// Test exporter configuration options with use_ssl_credentials
+TEST_F(OtlpGrpcExporterTestPeer, ConfigSslCredentialsTest)
+{
+ std::string cacert_str = "--begin and end fake cert--";
+ OtlpGrpcExporterOptions opts;
+ opts.use_ssl_credentials = true;
+ opts.ssl_credentials_cacert_as_string = cacert_str;
+ std::unique_ptr<OtlpGrpcExporter> exporter(new OtlpGrpcExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).ssl_credentials_cacert_as_string, cacert_str);
+ EXPECT_EQ(GetOptions(exporter).use_ssl_credentials, true);
+}
+
+# ifndef NO_GETENV
+// Test exporter configuration options with use_ssl_credentials
+TEST_F(OtlpGrpcExporterTestPeer, ConfigFromEnv)
+{
+ const std::string cacert_str = "--begin and end fake cert--";
+ setenv("OTEL_EXPORTER_OTLP_CERTIFICATE_STRING", cacert_str.c_str(), 1);
+ setenv("OTEL_EXPORTER_OTLP_SSL_ENABLE", "True", 1);
+ const std::string endpoint = "http://localhost:9999";
+ setenv("OTEL_EXPORTER_OTLP_ENDPOINT", endpoint.c_str(), 1);
+ setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20050ms", 1);
+ setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
+ setenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "k1=v3,k1=v4", 1);
+
+ std::unique_ptr<OtlpGrpcExporter> exporter(new OtlpGrpcExporter());
+ EXPECT_EQ(GetOptions(exporter).ssl_credentials_cacert_as_string, cacert_str);
+ EXPECT_EQ(GetOptions(exporter).use_ssl_credentials, true);
+ EXPECT_EQ(GetOptions(exporter).endpoint, endpoint);
+ EXPECT_EQ(GetOptions(exporter).timeout.count(),
+ std::chrono::duration_cast<std::chrono::system_clock::duration>(
+ std::chrono::milliseconds{20050})
+ .count());
+ EXPECT_EQ(GetOptions(exporter).metadata.size(), 3);
+ {
+ // Test k2
+ auto range = GetOptions(exporter).metadata.equal_range("k2");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v2"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+ {
+ // Test k1
+ auto range = GetOptions(exporter).metadata.equal_range("k1");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v3"));
+ ++range.first;
+ EXPECT_EQ(range.first->second, std::string("v4"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+
+ unsetenv("OTEL_EXPORTER_OTLP_ENDPOINT");
+ unsetenv("OTEL_EXPORTER_OTLP_CERTIFICATE_STRING");
+ unsetenv("OTEL_EXPORTER_OTLP_SSL_ENABLE");
+ unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
+ unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
+ unsetenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS");
+}
+# endif
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_log_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_log_exporter_test.cc
new file mode 100644
index 000000000..b60aac61a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_grpc_log_exporter_test.cc
@@ -0,0 +1,163 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/exporters/otlp/otlp_grpc_log_exporter.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+# include "opentelemetry/proto/collector/logs/v1/logs_service_mock.grpc.pb.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+# include "opentelemetry/logs/provider.h"
+# include "opentelemetry/sdk/logs/batch_log_processor.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/sdk/logs/logger_provider.h"
+# include "opentelemetry/sdk/resource/resource.h"
+
+# include <gtest/gtest.h>
+
+# if defined(_MSC_VER)
+# include "opentelemetry/sdk/common/env_variables.h"
+using opentelemetry::sdk::common::setenv;
+using opentelemetry::sdk::common::unsetenv;
+# endif
+
+using namespace testing;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+class OtlpGrpcLogExporterTestPeer : public ::testing::Test
+{
+public:
+ std::unique_ptr<sdk::logs::LogExporter> GetExporter(
+ std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> &stub_interface)
+ {
+ return std::unique_ptr<sdk::logs::LogExporter>(
+ new OtlpGrpcLogExporter(std::move(stub_interface)));
+ }
+
+ // Get the options associated with the given exporter.
+ const OtlpGrpcExporterOptions &GetOptions(std::unique_ptr<OtlpGrpcLogExporter> &exporter)
+ {
+ return exporter->options_;
+ }
+};
+
+TEST_F(OtlpGrpcLogExporterTestPeer, ShutdownTest)
+{
+ auto mock_stub = new proto::collector::logs::v1::MockLogsServiceStub();
+ std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> stub_interface(mock_stub);
+ auto exporter = GetExporter(stub_interface);
+
+ auto recordable_1 = exporter->MakeRecordable();
+ auto recordable_2 = exporter->MakeRecordable();
+
+ // exporter shuold not be shutdown by default
+ nostd::span<std::unique_ptr<sdk::logs::Recordable>> batch_1(&recordable_1, 1);
+ EXPECT_CALL(*mock_stub, Export(_, _, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(grpc::Status::OK))
+ .WillOnce(Return(grpc::Status::CANCELLED));
+
+ auto result = exporter->Export(batch_1);
+ EXPECT_EQ(sdk::common::ExportResult::kSuccess, result);
+
+ exporter->Shutdown();
+
+ nostd::span<std::unique_ptr<sdk::logs::Recordable>> batch_2(&recordable_2, 1);
+ result = exporter->Export(batch_2);
+ EXPECT_EQ(sdk::common::ExportResult::kFailure, result);
+}
+
+// Call Export() directly
+TEST_F(OtlpGrpcLogExporterTestPeer, ExportUnitTest)
+{
+ auto mock_stub = new proto::collector::logs::v1::MockLogsServiceStub();
+ std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> stub_interface(mock_stub);
+ auto exporter = GetExporter(stub_interface);
+
+ auto recordable_1 = exporter->MakeRecordable();
+ auto recordable_2 = exporter->MakeRecordable();
+
+ // Test successful RPC
+ nostd::span<std::unique_ptr<sdk::logs::Recordable>> batch_1(&recordable_1, 1);
+ EXPECT_CALL(*mock_stub, Export(_, _, _)).Times(Exactly(1)).WillOnce(Return(grpc::Status::OK));
+ auto result = exporter->Export(batch_1);
+ EXPECT_EQ(sdk::common::ExportResult::kSuccess, result);
+
+ // Test failed RPC
+ nostd::span<std::unique_ptr<sdk::logs::Recordable>> batch_2(&recordable_2, 1);
+ EXPECT_CALL(*mock_stub, Export(_, _, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(grpc::Status::CANCELLED));
+ result = exporter->Export(batch_2);
+ EXPECT_EQ(sdk::common::ExportResult::kFailure, result);
+}
+
+// Create spans, let processor call Export()
+TEST_F(OtlpGrpcLogExporterTestPeer, ExportIntegrationTest)
+{
+ auto mock_stub = new proto::collector::logs::v1::MockLogsServiceStub();
+ std::unique_ptr<proto::collector::logs::v1::LogsService::StubInterface> stub_interface(mock_stub);
+
+ auto exporter = GetExporter(stub_interface);
+
+ 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<sdk::logs::LoggerProvider>(new sdk::logs::LoggerProvider());
+ provider->AddProcessor(std::unique_ptr<sdk::logs::LogProcessor>(
+ new sdk::logs::BatchLogProcessor(std::move(exporter), 5, std::chrono::milliseconds(256), 1)));
+
+ EXPECT_CALL(*mock_stub, Export(_, _, _))
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(grpc::Status::OK));
+
+ uint8_t trace_id_bin[opentelemetry::trace::TraceId::kSize] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ opentelemetry::trace::TraceId trace_id{trace_id_bin};
+ uint8_t span_id_bin[opentelemetry::trace::SpanId::kSize] = {'7', '6', '5', '4',
+ '3', '2', '1', '0'};
+ opentelemetry::trace::SpanId span_id{span_id_bin};
+
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto logger = provider->GetLogger("test", "", "opentelelemtry_library", "", schema_url);
+ logger->Log(opentelemetry::logs::Severity::kInfo, "Log message",
+ {{"service.name", "unit_test_service"},
+ {"tenant.id", "test_user"},
+ {"bool_value", true},
+ {"int32_value", static_cast<int32_t>(1)},
+ {"uint32_value", static_cast<uint32_t>(2)},
+ {"int64_value", static_cast<int64_t>(0x1100000000LL)},
+ {"uint64_value", static_cast<uint64_t>(0x1200000000ULL)},
+ {"double_value", static_cast<double>(3.1)},
+ {"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());
+}
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_exporter_test.cc
new file mode 100644
index 000000000..ef0b5a509
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_exporter_test.cc
@@ -0,0 +1,357 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef HAVE_CPP_STDLIB
+
+# include "opentelemetry/exporters/otlp/otlp_http_exporter.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+# include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+# include "opentelemetry/ext/http/client/http_client_factory.h"
+# include "opentelemetry/ext/http/client/nosend/http_client_nosend.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 <gtest/gtest.h>
+# 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
+using namespace testing;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+namespace trace_api = opentelemetry::trace;
+namespace resource = opentelemetry::sdk::resource;
+
+template <class T, size_t N>
+static nostd::span<T, N> MakeSpan(T (&array)[N])
+{
+ return nostd::span<T, N>(array);
+}
+
+OtlpHttpClientOptions MakeOtlpHttpClientOptions(HttpRequestContentType content_type)
+{
+ OtlpHttpExporterOptions options;
+ options.content_type = content_type;
+ options.console_debug = true;
+ options.timeout = std::chrono::system_clock::duration::zero();
+ options.http_headers.insert(
+ std::make_pair<const std::string, std::string>("Custom-Header-Key", "Custom-Header-Value"));
+ OtlpHttpClientOptions otlp_http_client_options(
+ options.url, options.content_type, options.json_bytes_mapping, options.use_json_name,
+ options.console_debug, options.timeout, options.http_headers);
+ return otlp_http_client_options;
+}
+
+namespace http_client = opentelemetry::ext::http::client;
+
+class OtlpHttpExporterTestPeer : public ::testing::Test
+{
+public:
+ std::unique_ptr<sdk::trace::SpanExporter> GetExporter(std::unique_ptr<OtlpHttpClient> http_client)
+ {
+ return std::unique_ptr<sdk::trace::SpanExporter>(new OtlpHttpExporter(std::move(http_client)));
+ }
+
+ // Get the options associated with the given exporter.
+ const OtlpHttpExporterOptions &GetOptions(std::unique_ptr<OtlpHttpExporter> &exporter)
+ {
+ return exporter->options_;
+ }
+
+ static std::pair<OtlpHttpClient *, std::shared_ptr<http_client::HttpClient>>
+ GetMockOtlpHttpClient(HttpRequestContentType content_type)
+ {
+ auto http_client = http_client::HttpClientFactory::CreateNoSend();
+ return {new OtlpHttpClient(MakeOtlpHttpClientOptions(content_type), http_client), http_client};
+ }
+};
+
+// Create spans, let processor call Export()
+TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest)
+{
+ auto mock_otlp_client =
+ OtlpHttpExporterTestPeer::GetMockOtlpHttpClient(HttpRequestContentType::kJson);
+ auto mock_otlp_http_client = mock_otlp_client.first;
+ auto client = mock_otlp_client.second;
+ auto exporter = GetExporter(std::unique_ptr<OtlpHttpClient>{mock_otlp_http_client});
+
+ resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"},
+ {"tenant.id", "test_user"}};
+ resource_attributes["bool_value"] = true;
+ resource_attributes["int32_value"] = static_cast<int32_t>(1);
+ resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
+ resource_attributes["int64_value"] = static_cast<int64_t>(0x1100000000LL);
+ resource_attributes["uint64_value"] = static_cast<uint64_t>(0x1200000000ULL);
+ resource_attributes["double_value"] = static_cast<double>(3.1);
+ resource_attributes["vec_bool_value"] = std::vector<bool>{true, false, true};
+ resource_attributes["vec_int32_value"] = std::vector<int32_t>{1, 2};
+ resource_attributes["vec_uint32_value"] = std::vector<uint32_t>{3, 4};
+ resource_attributes["vec_int64_value"] = std::vector<int64_t>{5, 6};
+ resource_attributes["vec_uint64_value"] = std::vector<uint64_t>{7, 8};
+ resource_attributes["vec_double_value"] = std::vector<double>{3.2, 3.3};
+ resource_attributes["vec_string_value"] = std::vector<std::string>{"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<sdk::trace::SpanProcessor>(
+ new sdk::trace::BatchSpanProcessor(std::move(exporter), processor_opts));
+ auto provider = nostd::shared_ptr<trace::TracerProvider>(
+ 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);
+
+ nostd::get<trace_api::SpanContext>(child_span_opts.parent)
+ .trace_id()
+ .ToLowerBase16(MakeSpan(trace_id_hex));
+ report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex));
+
+ auto no_send_client = std::static_pointer_cast<http_client::nosend::HttpClient>(client);
+ auto mock_session =
+ std::static_pointer_cast<http_client::nosend::Session>(no_send_client->session_);
+ EXPECT_CALL(*mock_session, SendRequest)
+ .WillOnce([&mock_session,
+ report_trace_id](opentelemetry::ext::http::client::EventHandler &callback) {
+ auto check_json = nlohmann::json::parse(mock_session->GetRequest()->body_, nullptr, false);
+ auto resource_span = *check_json["resource_spans"].begin();
+ auto instrumentation_library_span = *resource_span["instrumentation_library_spans"].begin();
+ auto span = *instrumentation_library_span["spans"].begin();
+ auto received_trace_id = span["trace_id"].get<std::string>();
+ EXPECT_EQ(received_trace_id, report_trace_id);
+
+ auto custom_header = mock_session->GetRequest()->headers_.find("Custom-Header-Key");
+ ASSERT_TRUE(custom_header != mock_session->GetRequest()->headers_.end());
+ if (custom_header != mock_session->GetRequest()->headers_.end())
+ {
+ EXPECT_EQ("Custom-Header-Value", custom_header->second);
+ }
+ // let the otlp_http_client to continue
+ http_client::nosend::Response response;
+ callback.OnResponse(response);
+ });
+
+ child_span->End();
+ parent_span->End();
+}
+
+// Create spans, let processor call Export()
+TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest)
+{
+ auto mock_otlp_client =
+ OtlpHttpExporterTestPeer::GetMockOtlpHttpClient(HttpRequestContentType::kBinary);
+ auto mock_otlp_http_client = mock_otlp_client.first;
+ auto client = mock_otlp_client.second;
+ auto exporter = GetExporter(std::unique_ptr<OtlpHttpClient>{mock_otlp_http_client});
+
+ resource::ResourceAttributes resource_attributes = {{"service.name", "unit_test_service"},
+ {"tenant.id", "test_user"}};
+ resource_attributes["bool_value"] = true;
+ resource_attributes["int32_value"] = static_cast<int32_t>(1);
+ resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
+ resource_attributes["int64_value"] = static_cast<int64_t>(0x1100000000LL);
+ resource_attributes["uint64_value"] = static_cast<uint64_t>(0x1200000000ULL);
+ resource_attributes["double_value"] = static_cast<double>(3.1);
+ resource_attributes["vec_bool_value"] = std::vector<bool>{true, false, true};
+ resource_attributes["vec_int32_value"] = std::vector<int32_t>{1, 2};
+ resource_attributes["vec_uint32_value"] = std::vector<uint32_t>{3, 4};
+ resource_attributes["vec_int64_value"] = std::vector<int64_t>{5, 6};
+ resource_attributes["vec_uint64_value"] = std::vector<uint64_t>{7, 8};
+ resource_attributes["vec_double_value"] = std::vector<double>{3.2, 3.3};
+ resource_attributes["vec_string_value"] = std::vector<std::string>{"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<sdk::trace::SpanProcessor>(
+ new sdk::trace::BatchSpanProcessor(std::move(exporter), processor_opts));
+ auto provider = nostd::shared_ptr<trace::TracerProvider>(
+ new sdk::trace::TracerProvider(std::move(processor), resource));
+
+ std::string report_trace_id;
+
+ uint8_t trace_id_binary[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);
+ nostd::get<trace_api::SpanContext>(child_span_opts.parent)
+ .trace_id()
+ .CopyBytesTo(MakeSpan(trace_id_binary));
+ report_trace_id.assign(reinterpret_cast<char *>(trace_id_binary), sizeof(trace_id_binary));
+
+ auto no_send_client = std::static_pointer_cast<http_client::nosend::HttpClient>(client);
+ auto mock_session =
+ std::static_pointer_cast<http_client::nosend::Session>(no_send_client->session_);
+ EXPECT_CALL(*mock_session, SendRequest)
+ .WillOnce([&mock_session,
+ report_trace_id](opentelemetry::ext::http::client::EventHandler &callback) {
+ opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request_body;
+ request_body.ParseFromArray(&mock_session->GetRequest()->body_[0],
+ static_cast<int>(mock_session->GetRequest()->body_.size()));
+ auto received_trace_id =
+ request_body.resource_spans(0).instrumentation_library_spans(0).spans(0).trace_id();
+ EXPECT_EQ(received_trace_id, report_trace_id);
+
+ auto custom_header = mock_session->GetRequest()->headers_.find("Custom-Header-Key");
+ ASSERT_TRUE(custom_header != mock_session->GetRequest()->headers_.end());
+ if (custom_header != mock_session->GetRequest()->headers_.end())
+ {
+ EXPECT_EQ("Custom-Header-Value", custom_header->second);
+ }
+ // let the otlp_http_client to continue
+ http_client::nosend::Response response;
+ callback.OnResponse(response);
+ });
+
+ child_span->End();
+ parent_span->End();
+}
+
+// Test exporter configuration options
+TEST_F(OtlpHttpExporterTestPeer, ConfigTest)
+{
+ OtlpHttpExporterOptions opts;
+ opts.url = "http://localhost:45455/v1/traces";
+ std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).url, "http://localhost:45455/v1/traces");
+}
+
+// Test exporter configuration options with use_json_name
+TEST_F(OtlpHttpExporterTestPeer, ConfigUseJsonNameTest)
+{
+ OtlpHttpExporterOptions opts;
+ opts.use_json_name = true;
+ std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).use_json_name, true);
+}
+
+// Test exporter configuration options with json_bytes_mapping=JsonBytesMappingKind::kHex
+TEST_F(OtlpHttpExporterTestPeer, ConfigJsonBytesMappingTest)
+{
+ OtlpHttpExporterOptions opts;
+ opts.json_bytes_mapping = JsonBytesMappingKind::kHex;
+ std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).json_bytes_mapping, JsonBytesMappingKind::kHex);
+}
+
+# ifndef NO_GETENV
+// Test exporter configuration options with use_ssl_credentials
+TEST_F(OtlpHttpExporterTestPeer, ConfigFromEnv)
+{
+ const std::string url = "http://localhost:9999/v1/traces";
+ setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:9999", 1);
+ setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20s", 1);
+ setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
+ setenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "k1=v3,k1=v4", 1);
+
+ std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter());
+ EXPECT_EQ(GetOptions(exporter).url, url);
+ EXPECT_EQ(
+ GetOptions(exporter).timeout.count(),
+ std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::seconds{20})
+ .count());
+ EXPECT_EQ(GetOptions(exporter).http_headers.size(), 3);
+ {
+ // Test k2
+ auto range = GetOptions(exporter).http_headers.equal_range("k2");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v2"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+ {
+ // k1
+ auto range = GetOptions(exporter).http_headers.equal_range("k1");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v3"));
+ ++range.first;
+ EXPECT_EQ(range.first->second, std::string("v4"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+
+ unsetenv("OTEL_EXPORTER_OTLP_ENDPOINT");
+ unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
+ unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
+ unsetenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS");
+}
+
+TEST_F(OtlpHttpExporterTestPeer, ConfigFromTracesEnv)
+{
+ const std::string url = "http://localhost:9999/v1/traces";
+ setenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", url.c_str(), 1);
+ setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20s", 1);
+ setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
+ setenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", "k1=v3,k1=v4", 1);
+
+ std::unique_ptr<OtlpHttpExporter> exporter(new OtlpHttpExporter());
+ EXPECT_EQ(GetOptions(exporter).url, url);
+ EXPECT_EQ(
+ GetOptions(exporter).timeout.count(),
+ std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::seconds{20})
+ .count());
+ EXPECT_EQ(GetOptions(exporter).http_headers.size(), 3);
+ {
+ // Test k2
+ auto range = GetOptions(exporter).http_headers.equal_range("k2");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v2"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+ {
+ // k1
+ auto range = GetOptions(exporter).http_headers.equal_range("k1");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v3"));
+ ++range.first;
+ EXPECT_EQ(range.first->second, std::string("v4"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+
+ unsetenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT");
+ unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
+ unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
+ unsetenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS");
+}
+# endif
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_log_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_log_exporter_test.cc
new file mode 100644
index 000000000..7e2c0808e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_http_log_exporter_test.cc
@@ -0,0 +1,381 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef HAVE_CPP_STDLIB
+# ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/exporters/otlp/otlp_http_log_exporter.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_prefix.h"
+
+# include "opentelemetry/proto/collector/logs/v1/logs_service.pb.h"
+
+# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"
+
+# include "opentelemetry/common/key_value_iterable_view.h"
+# include "opentelemetry/ext/http/client/http_client_factory.h"
+# include "opentelemetry/ext/http/client/nosend/http_client_nosend.h"
+# include "opentelemetry/ext/http/server/http_server.h"
+# include "opentelemetry/logs/provider.h"
+# include "opentelemetry/sdk/logs/batch_log_processor.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/sdk/logs/logger_provider.h"
+# include "opentelemetry/sdk/resource/resource.h"
+
+# include <gtest/gtest.h>
+# 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
+
+using namespace testing;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+
+template <class T, size_t N>
+static nostd::span<T, N> MakeSpan(T (&array)[N])
+{
+ return nostd::span<T, N>(array);
+}
+
+OtlpHttpClientOptions MakeOtlpHttpClientOptions(HttpRequestContentType content_type)
+{
+ OtlpHttpLogExporterOptions options;
+ options.content_type = content_type;
+ options.console_debug = true;
+ options.http_headers.insert(
+ std::make_pair<const std::string, std::string>("Custom-Header-Key", "Custom-Header-Value"));
+ OtlpHttpClientOptions otlp_http_client_options(
+ options.url, options.content_type, options.json_bytes_mapping, options.use_json_name,
+ options.console_debug, options.timeout, options.http_headers);
+ return otlp_http_client_options;
+}
+
+namespace http_client = opentelemetry::ext::http::client;
+
+class OtlpHttpLogExporterTestPeer : public ::testing::Test
+{
+public:
+ std::unique_ptr<sdk::logs::LogExporter> GetExporter(std::unique_ptr<OtlpHttpClient> http_client)
+ {
+ return std::unique_ptr<sdk::logs::LogExporter>(new OtlpHttpLogExporter(std::move(http_client)));
+ }
+
+ // Get the options associated with the given exporter.
+ const OtlpHttpLogExporterOptions &GetOptions(std::unique_ptr<OtlpHttpLogExporter> &exporter)
+ {
+ return exporter->options_;
+ }
+ static std::pair<OtlpHttpClient *, std::shared_ptr<http_client::HttpClient>>
+ GetMockOtlpHttpClient(HttpRequestContentType content_type)
+ {
+ auto http_client = http_client::HttpClientFactory::CreateNoSend();
+ return {new OtlpHttpClient(MakeOtlpHttpClientOptions(content_type), http_client), http_client};
+ }
+};
+
+// Create log records, let processor call Export()
+TEST_F(OtlpHttpLogExporterTestPeer, ExportJsonIntegrationTest)
+{
+ auto mock_otlp_client =
+ OtlpHttpLogExporterTestPeer::GetMockOtlpHttpClient(HttpRequestContentType::kJson);
+ auto mock_otlp_http_client = mock_otlp_client.first;
+ auto client = mock_otlp_client.second;
+ auto exporter = GetExporter(std::unique_ptr<OtlpHttpClient>{mock_otlp_http_client});
+
+ 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<sdk::logs::LoggerProvider>(new sdk::logs::LoggerProvider());
+ provider->AddProcessor(std::unique_ptr<sdk::logs::LogProcessor>(
+ new sdk::logs::BatchLogProcessor(std::move(exporter), 5, std::chrono::milliseconds(256), 5)));
+
+ std::string report_trace_id;
+ std::string report_span_id;
+ uint8_t trace_id_bin[opentelemetry::trace::TraceId::kSize] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ char trace_id_hex[2 * opentelemetry::trace::TraceId::kSize] = {0};
+ opentelemetry::trace::TraceId trace_id{trace_id_bin};
+ uint8_t span_id_bin[opentelemetry::trace::SpanId::kSize] = {'7', '6', '5', '4',
+ '3', '2', '1', '0'};
+ char span_id_hex[2 * opentelemetry::trace::SpanId::kSize] = {0};
+ opentelemetry::trace::SpanId span_id{span_id_bin};
+
+ 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 message",
+ {{"service.name", "unit_test_service"},
+ {"tenant.id", "test_user"},
+ {"bool_value", true},
+ {"int32_value", static_cast<int32_t>(1)},
+ {"uint32_value", static_cast<uint32_t>(2)},
+ {"int64_value", static_cast<int64_t>(0x1100000000LL)},
+ {"uint64_value", static_cast<uint64_t>(0x1200000000ULL)},
+ {"double_value", static_cast<double>(3.1)},
+ {"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());
+
+ trace_id.ToLowerBase16(MakeSpan(trace_id_hex));
+ report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex));
+
+ span_id.ToLowerBase16(MakeSpan(span_id_hex));
+ report_span_id.assign(span_id_hex, sizeof(span_id_hex));
+
+ auto no_send_client = std::static_pointer_cast<http_client::nosend::HttpClient>(client);
+ auto mock_session =
+ std::static_pointer_cast<http_client::nosend::Session>(no_send_client->session_);
+ EXPECT_CALL(*mock_session, SendRequest)
+ .WillOnce([&mock_session, report_trace_id,
+ report_span_id](opentelemetry::ext::http::client::EventHandler &callback) {
+ auto check_json = nlohmann::json::parse(mock_session->GetRequest()->body_, nullptr, false);
+ auto resource_logs = *check_json["resource_logs"].begin();
+ auto scope_logs = *resource_logs["scope_logs"].begin();
+ auto log = *scope_logs["log_records"].begin();
+ auto received_trace_id = log["trace_id"].get<std::string>();
+ auto received_span_id = log["span_id"].get<std::string>();
+ EXPECT_EQ(received_trace_id, report_trace_id);
+ EXPECT_EQ(received_span_id, report_span_id);
+ EXPECT_EQ("Log message", log["body"]["string_value"].get<std::string>());
+ EXPECT_LE(15, log["attributes"].size());
+ auto custom_header = mock_session->GetRequest()->headers_.find("Custom-Header-Key");
+ ASSERT_TRUE(custom_header != mock_session->GetRequest()->headers_.end());
+ if (custom_header != mock_session->GetRequest()->headers_.end())
+ {
+ EXPECT_EQ("Custom-Header-Value", custom_header->second);
+ }
+ // let the otlp_http_client to continue
+ http_client::nosend::Response response;
+ callback.OnResponse(response);
+ });
+}
+
+// Create log records, let processor call Export()
+TEST_F(OtlpHttpLogExporterTestPeer, ExportBinaryIntegrationTest)
+{
+ auto mock_otlp_client =
+ OtlpHttpLogExporterTestPeer::GetMockOtlpHttpClient(HttpRequestContentType::kBinary);
+ auto mock_otlp_http_client = mock_otlp_client.first;
+ auto client = mock_otlp_client.second;
+ auto exporter = GetExporter(std::unique_ptr<OtlpHttpClient>{mock_otlp_http_client});
+
+ 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<sdk::logs::LoggerProvider>(new sdk::logs::LoggerProvider());
+ provider->AddProcessor(std::unique_ptr<sdk::logs::LogProcessor>(
+ new sdk::logs::BatchLogProcessor(std::move(exporter), 5, std::chrono::milliseconds(256), 5)));
+
+ std::string report_trace_id;
+ std::string report_span_id;
+ uint8_t trace_id_bin[opentelemetry::trace::TraceId::kSize] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ opentelemetry::trace::TraceId trace_id{trace_id_bin};
+ uint8_t span_id_bin[opentelemetry::trace::SpanId::kSize] = {'7', '6', '5', '4',
+ '3', '2', '1', '0'};
+ opentelemetry::trace::SpanId span_id{span_id_bin};
+
+ 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 message",
+ {{"service.name", "unit_test_service"},
+ {"tenant.id", "test_user"},
+ {"bool_value", true},
+ {"int32_value", static_cast<int32_t>(1)},
+ {"uint32_value", static_cast<uint32_t>(2)},
+ {"int64_value", static_cast<int64_t>(0x1100000000LL)},
+ {"uint64_value", static_cast<uint64_t>(0x1200000000ULL)},
+ {"double_value", static_cast<double>(3.1)},
+ {"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());
+
+ report_trace_id.assign(reinterpret_cast<const char *>(trace_id_bin), sizeof(trace_id_bin));
+ report_span_id.assign(reinterpret_cast<const char *>(span_id_bin), sizeof(span_id_bin));
+
+ auto no_send_client = std::static_pointer_cast<http_client::nosend::HttpClient>(client);
+ auto mock_session =
+ std::static_pointer_cast<http_client::nosend::Session>(no_send_client->session_);
+ EXPECT_CALL(*mock_session, SendRequest)
+ .WillOnce([&mock_session, report_trace_id,
+ report_span_id](opentelemetry::ext::http::client::EventHandler &callback) {
+ opentelemetry::proto::collector::logs::v1::ExportLogsServiceRequest request_body;
+ request_body.ParseFromArray(&mock_session->GetRequest()->body_[0],
+ static_cast<int>(mock_session->GetRequest()->body_.size()));
+ auto &received_log = request_body.resource_logs(0).scope_logs(0).log_records(0);
+ EXPECT_EQ(received_log.trace_id(), report_trace_id);
+ EXPECT_EQ(received_log.span_id(), report_span_id);
+ EXPECT_EQ("Log message", received_log.body().string_value());
+ EXPECT_LE(15, received_log.attributes_size());
+ bool check_service_name = false;
+ for (auto &attribute : received_log.attributes())
+ {
+ if ("service.name" == attribute.key())
+ {
+ check_service_name = true;
+ EXPECT_EQ("unit_test_service", attribute.value().string_value());
+ }
+ }
+ ASSERT_TRUE(check_service_name);
+ http_client::nosend::Response response;
+ callback.OnResponse(response);
+ });
+}
+
+// Test exporter configuration options
+TEST_F(OtlpHttpLogExporterTestPeer, ConfigTest)
+{
+ OtlpHttpLogExporterOptions opts;
+ opts.url = "http://localhost:45456/v1/logs";
+ std::unique_ptr<OtlpHttpLogExporter> exporter(new OtlpHttpLogExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).url, "http://localhost:45456/v1/logs");
+}
+
+// Test exporter configuration options with use_json_name
+TEST_F(OtlpHttpLogExporterTestPeer, ConfigUseJsonNameTest)
+{
+ OtlpHttpLogExporterOptions opts;
+ opts.use_json_name = true;
+ std::unique_ptr<OtlpHttpLogExporter> exporter(new OtlpHttpLogExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).use_json_name, true);
+}
+
+// Test exporter configuration options with json_bytes_mapping=JsonBytesMappingKind::kHex
+TEST_F(OtlpHttpLogExporterTestPeer, ConfigJsonBytesMappingTest)
+{
+ OtlpHttpLogExporterOptions opts;
+ opts.json_bytes_mapping = JsonBytesMappingKind::kHex;
+ std::unique_ptr<OtlpHttpLogExporter> exporter(new OtlpHttpLogExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).json_bytes_mapping, JsonBytesMappingKind::kHex);
+}
+
+# ifndef NO_GETENV
+// Test exporter configuration options with use_ssl_credentials
+TEST_F(OtlpHttpLogExporterTestPeer, ConfigFromEnv)
+{
+ const std::string url = "http://localhost:9999/v1/logs";
+ setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:9999", 1);
+ setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20s", 1);
+ setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
+ setenv("OTEL_EXPORTER_OTLP_LOGS_HEADERS", "k1=v3,k1=v4", 1);
+
+ std::unique_ptr<OtlpHttpLogExporter> exporter(new OtlpHttpLogExporter());
+ EXPECT_EQ(GetOptions(exporter).url, url);
+ EXPECT_EQ(
+ GetOptions(exporter).timeout.count(),
+ std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::seconds{20})
+ .count());
+ EXPECT_EQ(GetOptions(exporter).http_headers.size(), 3);
+ {
+ // Test k2
+ auto range = GetOptions(exporter).http_headers.equal_range("k2");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v2"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+ {
+ // k1
+ auto range = GetOptions(exporter).http_headers.equal_range("k1");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v3"));
+ ++range.first;
+ EXPECT_EQ(range.first->second, std::string("v4"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+
+ unsetenv("OTEL_EXPORTER_OTLP_ENDPOINT");
+ unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
+ unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
+ unsetenv("OTEL_EXPORTER_OTLP_LOGS_HEADERS");
+}
+
+TEST_F(OtlpHttpLogExporterTestPeer, ConfigFromLogsEnv)
+{
+ const std::string url = "http://localhost:9999/v1/logs";
+ setenv("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", url.c_str(), 1);
+ setenv("OTEL_EXPORTER_OTLP_TIMEOUT", "20s", 1);
+ setenv("OTEL_EXPORTER_OTLP_HEADERS", "k1=v1,k2=v2", 1);
+ setenv("OTEL_EXPORTER_OTLP_LOGS_HEADERS", "k1=v3,k1=v4", 1);
+
+ std::unique_ptr<OtlpHttpLogExporter> exporter(new OtlpHttpLogExporter());
+ EXPECT_EQ(GetOptions(exporter).url, url);
+ EXPECT_EQ(
+ GetOptions(exporter).timeout.count(),
+ std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::seconds{20})
+ .count());
+ EXPECT_EQ(GetOptions(exporter).http_headers.size(), 3);
+ {
+ // Test k2
+ auto range = GetOptions(exporter).http_headers.equal_range("k2");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v2"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+ {
+ // k1
+ auto range = GetOptions(exporter).http_headers.equal_range("k1");
+ EXPECT_TRUE(range.first != range.second);
+ EXPECT_EQ(range.first->second, std::string("v3"));
+ ++range.first;
+ EXPECT_EQ(range.first->second, std::string("v4"));
+ ++range.first;
+ EXPECT_TRUE(range.first == range.second);
+ }
+
+ unsetenv("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT");
+ unsetenv("OTEL_EXPORTER_OTLP_TIMEOUT");
+ unsetenv("OTEL_EXPORTER_OTLP_HEADERS");
+ unsetenv("OTEL_EXPORTER_OTLP_LOGS_HEADERS");
+}
+
+TEST_F(OtlpHttpLogExporterTestPeer, DefaultEndpoint)
+{
+ EXPECT_EQ("http://localhost:4318/v1/logs", GetOtlpDefaultHttpLogEndpoint());
+ EXPECT_EQ("http://localhost:4318/v1/traces", GetOtlpDefaultHttpEndpoint());
+ EXPECT_EQ("http://localhost:4317", GetOtlpDefaultGrpcEndpoint());
+}
+
+# endif
+
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+# endif
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_log_recordable_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_log_recordable_test.cc
new file mode 100644
index 000000000..bb48fd54e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_log_recordable_test.cc
@@ -0,0 +1,240 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <gtest/gtest.h>
+
+# include "opentelemetry/exporters/otlp/otlp_log_recordable.h"
+# include "opentelemetry/sdk/resource/experimental_semantic_conventions.h"
+# include "opentelemetry/sdk/resource/resource.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+namespace resource = opentelemetry::sdk::resource;
+namespace proto = opentelemetry::proto;
+
+TEST(OtlpLogRecordable, SetTimestamp)
+{
+ OtlpLogRecordable rec;
+ std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
+ common::SystemTimestamp start_timestamp(now);
+
+ uint64_t unix_start =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
+
+ rec.SetTimestamp(start_timestamp);
+ EXPECT_EQ(rec.log_record().time_unix_nano(), unix_start);
+}
+
+TEST(OtlpLogRecordable, SetSeverity)
+{
+ OtlpLogRecordable rec;
+ rec.SetSeverity(opentelemetry::logs::Severity::kError);
+
+ EXPECT_EQ(rec.log_record().severity_number(), proto::logs::v1::SEVERITY_NUMBER_ERROR);
+ EXPECT_EQ(rec.log_record().severity_text(), "ERROR");
+}
+
+TEST(OtlpLogRecordable, SetBody)
+{
+ OtlpLogRecordable rec;
+ nostd::string_view name = "Test Log Message";
+ rec.SetBody(name);
+ EXPECT_EQ(rec.log_record().body().string_value(), name);
+}
+
+TEST(OtlpLogRecordable, SetTraceId)
+{
+ OtlpLogRecordable rec;
+ uint8_t trace_id_bin[opentelemetry::trace::TraceId::kSize] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ std::string expected_bytes;
+ expected_bytes.assign(
+ reinterpret_cast<char *>(trace_id_bin),
+ reinterpret_cast<char *>(trace_id_bin) + opentelemetry::trace::TraceId::kSize);
+ rec.SetTraceId(opentelemetry::trace::TraceId{trace_id_bin});
+ EXPECT_EQ(rec.log_record().trace_id(), expected_bytes);
+}
+
+TEST(OtlpLogRecordable, SetSpanId)
+{
+ OtlpLogRecordable rec;
+ uint8_t span_id_bin[opentelemetry::trace::SpanId::kSize] = {'7', '6', '5', '4',
+ '3', '2', '1', '0'};
+ std::string expected_bytes;
+ expected_bytes.assign(
+ reinterpret_cast<char *>(span_id_bin),
+ reinterpret_cast<char *>(span_id_bin) + opentelemetry::trace::SpanId::kSize);
+ rec.SetSpanId(opentelemetry::trace::SpanId{span_id_bin});
+ EXPECT_EQ(rec.log_record().span_id(), expected_bytes);
+}
+
+TEST(OtlpLogRecordable, SetResource)
+{
+ OtlpLogRecordable rec;
+ const std::string service_name_key = "service.name";
+ std::string service_name = "test-otlp";
+ 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;
+ for (int i = 0; i < proto_resource.attributes_size(); i++)
+ {
+ auto attr = proto_resource.attributes(static_cast<int>(i));
+ if (attr.key() == service_name_key && attr.value().string_value() == service_name)
+ {
+ found_service_name = true;
+ }
+ }
+ EXPECT_TRUE(found_service_name);
+}
+
+TEST(OtlpLogRecordable, DefaultResource)
+{
+ OtlpLogRecordable rec;
+
+ auto proto_resource = rec.ProtoResource();
+ int found_resource_count = 0;
+ for (int i = 0; i < proto_resource.attributes_size(); i++)
+ {
+ auto attr = proto_resource.attributes(static_cast<int>(i));
+ if (attr.key() ==
+ opentelemetry::sdk::resource::attr(OTEL_CPP_CONST_HASHCODE(AttrTelemetrySdkLanguage)))
+ {
+ EXPECT_EQ(attr.value().string_value(), "cpp");
+ ++found_resource_count;
+ }
+ else if (attr.key() ==
+ opentelemetry::sdk::resource::attr(OTEL_CPP_CONST_HASHCODE(AttrTelemetrySdkName)))
+ {
+ EXPECT_EQ(attr.value().string_value(), "opentelemetry");
+ ++found_resource_count;
+ }
+ else if (attr.key() ==
+ opentelemetry::sdk::resource::attr(OTEL_CPP_CONST_HASHCODE(AttrTelemetrySdkVersion)))
+ {
+ EXPECT_EQ(attr.value().string_value(), OPENTELEMETRY_SDK_VERSION);
+ ++found_resource_count;
+ }
+ }
+ EXPECT_EQ(3, found_resource_count);
+}
+
+// Test non-int single types. Int single types are tested using templates (see IntAttributeTest)
+TEST(OtlpLogRecordable, SetSingleAttribute)
+{
+ OtlpLogRecordable rec;
+ nostd::string_view bool_key = "bool_attr";
+ common::AttributeValue bool_val(true);
+ rec.SetAttribute(bool_key, bool_val);
+
+ nostd::string_view double_key = "double_attr";
+ common::AttributeValue double_val(3.3);
+ rec.SetAttribute(double_key, double_val);
+
+ nostd::string_view str_key = "str_attr";
+ common::AttributeValue str_val(nostd::string_view("Test"));
+ rec.SetAttribute(str_key, str_val);
+
+ EXPECT_EQ(rec.log_record().attributes(0).key(), bool_key);
+ EXPECT_EQ(rec.log_record().attributes(0).value().bool_value(), nostd::get<bool>(bool_val));
+
+ EXPECT_EQ(rec.log_record().attributes(1).key(), double_key);
+ EXPECT_EQ(rec.log_record().attributes(1).value().double_value(), nostd::get<double>(double_val));
+
+ EXPECT_EQ(rec.log_record().attributes(2).key(), str_key);
+ EXPECT_EQ(rec.log_record().attributes(2).value().string_value(),
+ nostd::get<nostd::string_view>(str_val).data());
+}
+
+// Test non-int array types. Int array types are tested using templates (see IntAttributeTest)
+TEST(OtlpLogRecordable, SetArrayAttribute)
+{
+ OtlpLogRecordable rec;
+ const int kArraySize = 3;
+
+ bool bool_arr[kArraySize] = {true, false, true};
+ nostd::span<const bool> bool_span(bool_arr);
+ rec.SetAttribute("bool_arr_attr", bool_span);
+
+ double double_arr[kArraySize] = {22.3, 33.4, 44.5};
+ nostd::span<const double> double_span(double_arr);
+ rec.SetAttribute("double_arr_attr", double_span);
+
+ nostd::string_view str_arr[kArraySize] = {"Hello", "World", "Test"};
+ nostd::span<const nostd::string_view> str_span(str_arr);
+ rec.SetAttribute("str_arr_attr", str_span);
+
+ for (int i = 0; i < kArraySize; i++)
+ {
+ EXPECT_EQ(rec.log_record().attributes(0).value().array_value().values(i).bool_value(),
+ bool_span[i]);
+ EXPECT_EQ(rec.log_record().attributes(1).value().array_value().values(i).double_value(),
+ double_span[i]);
+ EXPECT_EQ(rec.log_record().attributes(2).value().array_value().values(i).string_value(),
+ str_span[i]);
+ }
+}
+
+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
+ * use a template approach to test all int types.
+ */
+template <typename T>
+struct OtlpLogRecordableIntAttributeTest : public testing::Test
+{
+ using IntParamType = T;
+};
+
+using IntTypes = testing::Types<int, int64_t, unsigned int, uint64_t>;
+TYPED_TEST_SUITE(OtlpLogRecordableIntAttributeTest, IntTypes);
+
+TYPED_TEST(OtlpLogRecordableIntAttributeTest, SetIntSingleAttribute)
+{
+ using IntType = typename TestFixture::IntParamType;
+ IntType i = 2;
+ common::AttributeValue int_val(i);
+
+ OtlpLogRecordable rec;
+ rec.SetAttribute("int_attr", int_val);
+ EXPECT_EQ(rec.log_record().attributes(0).value().int_value(), nostd::get<IntType>(int_val));
+}
+
+TYPED_TEST(OtlpLogRecordableIntAttributeTest, SetIntArrayAttribute)
+{
+ using IntType = typename TestFixture::IntParamType;
+
+ const int kArraySize = 3;
+ IntType int_arr[kArraySize] = {4, 5, 6};
+ nostd::span<const IntType> int_span(int_arr);
+
+ OtlpLogRecordable rec;
+ rec.SetAttribute("int_arr_attr", int_span);
+
+ for (int i = 0; i < kArraySize; i++)
+ {
+ EXPECT_EQ(rec.log_record().attributes(0).value().array_value().values(i).int_value(),
+ int_span[i]);
+ }
+}
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_recordable_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_recordable_test.cc
new file mode 100644
index 000000000..b46802d87
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/otlp/test/otlp_recordable_test.cc
@@ -0,0 +1,321 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/otlp/otlp_recordable.h"
+#include <gtest/gtest.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace otlp
+{
+namespace trace_api = opentelemetry::trace;
+namespace trace_sdk = opentelemetry::sdk::trace;
+namespace resource = opentelemetry::sdk::resource;
+namespace proto = opentelemetry::proto;
+
+TEST(OtlpRecordable, SetIdentity)
+{
+ constexpr uint8_t trace_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
+ constexpr uint8_t span_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8};
+ constexpr uint8_t parent_span_id_buf[] = {8, 7, 6, 5, 4, 3, 2, 1};
+ trace_api::TraceId trace_id{trace_id_buf};
+ trace_api::SpanId span_id{span_id_buf};
+ trace_api::SpanId parent_span_id{parent_span_id_buf};
+ const auto trace_state = trace_api::TraceState::GetDefault()->Set("key1", "value");
+ const trace_api::SpanContext span_context{
+ trace_id, span_id, trace_api::TraceFlags{trace_api::TraceFlags::kIsSampled}, true,
+ trace_state};
+
+ OtlpRecordable rec;
+
+ rec.SetIdentity(span_context, parent_span_id);
+
+ EXPECT_EQ(rec.span().trace_id(), std::string(reinterpret_cast<const char *>(trace_id.Id().data()),
+ trace::TraceId::kSize));
+ EXPECT_EQ(rec.span().span_id(),
+ std::string(reinterpret_cast<const char *>(span_id.Id().data()), trace::SpanId::kSize));
+ EXPECT_EQ(rec.span().trace_state(), "key1=value");
+ EXPECT_EQ(rec.span().parent_span_id(),
+ std::string(reinterpret_cast<const char *>(parent_span_id.Id().data()),
+ trace::SpanId::kSize));
+
+ OtlpRecordable rec_invalid_parent;
+
+ constexpr uint8_t invalid_parent_span_id_buf[] = {0, 0, 0, 0, 0, 0, 0, 0};
+ trace_api::SpanId invalid_parent_span_id{invalid_parent_span_id_buf};
+ rec_invalid_parent.SetIdentity(span_context, invalid_parent_span_id);
+
+ EXPECT_EQ(rec_invalid_parent.span().parent_span_id(), std::string{});
+}
+
+TEST(OtlpRecordable, SetSpanKind)
+{
+ OtlpRecordable rec;
+ trace_api::SpanKind span_kind = trace_api::SpanKind::kServer;
+ rec.SetSpanKind(span_kind);
+ EXPECT_EQ(rec.span().kind(), proto::trace::v1::Span_SpanKind::Span_SpanKind_SPAN_KIND_SERVER);
+}
+
+TEST(OtlpRecordable, SetInstrumentationLibrary)
+{
+ OtlpRecordable rec;
+ auto inst_lib = trace_sdk::InstrumentationLibrary::Create("test", "v1");
+ rec.SetInstrumentationLibrary(*inst_lib);
+ auto proto_instr_libr = rec.GetProtoInstrumentationLibrary();
+ EXPECT_EQ(proto_instr_libr.name(), inst_lib->GetName());
+ EXPECT_EQ(proto_instr_libr.version(), inst_lib->GetVersion());
+}
+
+TEST(OtlpRecordable, SetInstrumentationLibraryWithSchemaURL)
+{
+ OtlpRecordable rec;
+ const std::string expected_schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto inst_lib = trace_sdk::InstrumentationLibrary::Create("test", "v1", expected_schema_url);
+ rec.SetInstrumentationLibrary(*inst_lib);
+ EXPECT_EQ(expected_schema_url, rec.GetInstrumentationLibrarySchemaURL());
+}
+
+TEST(OtlpRecordable, SetStartTime)
+{
+ OtlpRecordable rec;
+ std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
+ common::SystemTimestamp start_timestamp(start_time);
+
+ uint64_t unix_start =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(start_time.time_since_epoch()).count();
+
+ rec.SetStartTime(start_timestamp);
+ EXPECT_EQ(rec.span().start_time_unix_nano(), unix_start);
+}
+
+TEST(OtlpRecordable, SetDuration)
+{
+ OtlpRecordable rec;
+ // Start time is 0
+ common::SystemTimestamp start_timestamp;
+
+ std::chrono::nanoseconds duration(10);
+ uint64_t unix_end = duration.count();
+
+ rec.SetStartTime(start_timestamp);
+ rec.SetDuration(duration);
+
+ EXPECT_EQ(rec.span().end_time_unix_nano(), unix_end);
+}
+
+TEST(OtlpRecordable, SetStatus)
+{
+ OtlpRecordable rec1;
+ trace::StatusCode code_error(trace::StatusCode::kError);
+ nostd::string_view description = "For test";
+ rec1.SetStatus(code_error, description);
+
+ EXPECT_EQ(rec1.span().status().code(), proto::trace::v1::Status_StatusCode(code_error));
+ EXPECT_EQ(rec1.span().status().message(), description);
+
+ OtlpRecordable rec2;
+ trace::StatusCode code_ok(trace::StatusCode::kOk);
+ rec2.SetStatus(code_ok, description);
+ EXPECT_EQ(rec2.span().status().code(), proto::trace::v1::Status_StatusCode(code_ok));
+ EXPECT_EQ(rec2.span().status().message(), "");
+}
+
+TEST(OtlpRecordable, AddEventDefault)
+{
+ OtlpRecordable rec;
+ nostd::string_view name = "Test Event";
+
+ std::chrono::system_clock::time_point event_time = std::chrono::system_clock::now();
+ common::SystemTimestamp event_timestamp(event_time);
+
+ rec.sdk::trace::Recordable::AddEvent(name, event_timestamp);
+
+ uint64_t unix_event_time =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(event_time.time_since_epoch()).count();
+
+ EXPECT_EQ(rec.span().events(0).name(), name);
+ EXPECT_EQ(rec.span().events(0).time_unix_nano(), unix_event_time);
+ EXPECT_EQ(rec.span().events(0).attributes().size(), 0);
+}
+
+TEST(OtlpRecordable, AddEventWithAttributes)
+{
+ OtlpRecordable rec;
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int values[kNumAttributes] = {4, 7, 23};
+ std::map<std::string, int> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ rec.AddEvent("Test Event", std::chrono::system_clock::now(),
+ common::KeyValueIterableView<std::map<std::string, int>>(attributes));
+
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ EXPECT_EQ(rec.span().events(0).attributes(i).key(), keys[i]);
+ EXPECT_EQ(rec.span().events(0).attributes(i).value().int_value(), values[i]);
+ }
+}
+
+TEST(OtlpRecordable, AddLink)
+{
+ OtlpRecordable rec;
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int values[kNumAttributes] = {5, 12, 40};
+ std::map<std::string, int> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ auto trace_id = rec.span().trace_id();
+ auto span_id = rec.span().span_id();
+
+ trace::TraceFlags flags;
+ std::string trace_state_header = "k1=v1,k2=v2";
+ auto ts = trace::TraceState::FromHeader(trace_state_header);
+
+ rec.AddLink(trace::SpanContext(trace::TraceId(), trace::SpanId(), flags, false, ts),
+ common::KeyValueIterableView<std::map<std::string, int>>(attributes));
+
+ EXPECT_EQ(rec.span().trace_id(), trace_id);
+ EXPECT_EQ(rec.span().span_id(), span_id);
+ EXPECT_EQ(rec.span().links(0).trace_state(), trace_state_header);
+ for (int i = 0; i < kNumAttributes; i++)
+ {
+ EXPECT_EQ(rec.span().links(0).attributes(i).key(), keys[i]);
+ EXPECT_EQ(rec.span().links(0).attributes(i).value().int_value(), values[i]);
+ }
+}
+
+TEST(OtlpRecordable, SetResource)
+{
+ OtlpRecordable rec;
+ const std::string service_name_key = "service.name";
+ std::string service_name = "test-otlp";
+ auto resource = resource::Resource::Create({{service_name_key, service_name}});
+ rec.SetResource(resource);
+
+ auto proto_resource = rec.ProtoResource();
+ bool found_service_name = false;
+ for (int i = 0; i < proto_resource.attributes_size(); i++)
+ {
+ auto attr = proto_resource.attributes(static_cast<int>(i));
+ if (attr.key() == service_name_key && attr.value().string_value() == service_name)
+ {
+ found_service_name = true;
+ }
+ }
+ EXPECT_TRUE(found_service_name);
+}
+
+TEST(OtlpRecordable, SetResourceWithSchemaURL)
+{
+ OtlpRecordable rec;
+ const std::string service_name_key = "service.name";
+ const std::string service_name = "test-otlp";
+ const std::string expected_schema_url = "https://opentelemetry.io/schemas/1.11.0";
+ auto resource =
+ resource::Resource::Create({{service_name_key, service_name}}, expected_schema_url);
+ rec.SetResource(resource);
+
+ EXPECT_EQ(expected_schema_url, rec.GetResourceSchemaURL());
+}
+
+// Test non-int single types. Int single types are tested using templates (see IntAttributeTest)
+TEST(OtlpRecordable, SetSingleAttribute)
+{
+ OtlpRecordable rec;
+ nostd::string_view bool_key = "bool_attr";
+ common::AttributeValue bool_val(true);
+ rec.SetAttribute(bool_key, bool_val);
+
+ nostd::string_view double_key = "double_attr";
+ common::AttributeValue double_val(3.3);
+ rec.SetAttribute(double_key, double_val);
+
+ nostd::string_view str_key = "str_attr";
+ common::AttributeValue str_val(nostd::string_view("Test"));
+ rec.SetAttribute(str_key, str_val);
+
+ EXPECT_EQ(rec.span().attributes(0).key(), bool_key);
+ EXPECT_EQ(rec.span().attributes(0).value().bool_value(), nostd::get<bool>(bool_val));
+
+ EXPECT_EQ(rec.span().attributes(1).key(), double_key);
+ EXPECT_EQ(rec.span().attributes(1).value().double_value(), nostd::get<double>(double_val));
+
+ EXPECT_EQ(rec.span().attributes(2).key(), str_key);
+ EXPECT_EQ(rec.span().attributes(2).value().string_value(),
+ nostd::get<nostd::string_view>(str_val).data());
+}
+
+// Test non-int array types. Int array types are tested using templates (see IntAttributeTest)
+TEST(OtlpRecordable, SetArrayAttribute)
+{
+ OtlpRecordable rec;
+ const int kArraySize = 3;
+
+ bool bool_arr[kArraySize] = {true, false, true};
+ nostd::span<const bool> bool_span(bool_arr);
+ rec.SetAttribute("bool_arr_attr", bool_span);
+
+ double double_arr[kArraySize] = {22.3, 33.4, 44.5};
+ nostd::span<const double> double_span(double_arr);
+ rec.SetAttribute("double_arr_attr", double_span);
+
+ nostd::string_view str_arr[kArraySize] = {"Hello", "World", "Test"};
+ nostd::span<const nostd::string_view> str_span(str_arr);
+ rec.SetAttribute("str_arr_attr", str_span);
+
+ for (int i = 0; i < kArraySize; i++)
+ {
+ EXPECT_EQ(rec.span().attributes(0).value().array_value().values(i).bool_value(), bool_span[i]);
+ EXPECT_EQ(rec.span().attributes(1).value().array_value().values(i).double_value(),
+ double_span[i]);
+ EXPECT_EQ(rec.span().attributes(2).value().array_value().values(i).string_value(), str_span[i]);
+ }
+}
+
+/**
+ * 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
+ * use a template approach to test all int types.
+ */
+template <typename T>
+struct IntAttributeTest : public testing::Test
+{
+ using IntParamType = T;
+};
+
+using IntTypes = testing::Types<int, int64_t, unsigned int, uint64_t>;
+TYPED_TEST_SUITE(IntAttributeTest, IntTypes);
+
+TYPED_TEST(IntAttributeTest, SetIntSingleAttribute)
+{
+ using IntType = typename TestFixture::IntParamType;
+ IntType i = 2;
+ common::AttributeValue int_val(i);
+
+ OtlpRecordable rec;
+ rec.SetAttribute("int_attr", int_val);
+ EXPECT_EQ(rec.span().attributes(0).value().int_value(), nostd::get<IntType>(int_val));
+}
+
+TYPED_TEST(IntAttributeTest, SetIntArrayAttribute)
+{
+ using IntType = typename TestFixture::IntParamType;
+
+ const int kArraySize = 3;
+ IntType int_arr[kArraySize] = {4, 5, 6};
+ nostd::span<const IntType> int_span(int_arr);
+
+ OtlpRecordable rec;
+ rec.SetAttribute("int_arr_attr", int_span);
+
+ for (int i = 0; i < kArraySize; i++)
+ {
+ EXPECT_EQ(rec.span().attributes(0).value().array_value().values(i).int_value(), int_span[i]);
+ }
+}
+} // namespace otlp
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE