summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/exporters
diff options
context:
space:
mode:
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/exporters')
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/CMakeLists.txt40
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/BUILD43
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/CMakeLists.txt41
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h114
-rwxr-xr-xsrc/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h229
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/src/es_log_exporter.cc221
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/test/es_log_exporter_test.cc86
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD69
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt63
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md226
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE23
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h3458
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h186
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h142
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h297
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h30
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h456
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h629
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h15
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h995
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h28
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h274
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h412
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc101
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc190
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc64
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc388
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/BUILD217
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/CMakeLists.txt95
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/README.md61
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h93
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h119
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.cc61
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.h40
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.cc113
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.h53
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.cc37
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.h39
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/jaeger_exporter.cc111
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/recordable.cc365
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/sender.h32
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.cc99
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.h78
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/transport.h30
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.cc86
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.h57
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_exporter_test.cc175
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_recordable_test.cc334
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.cpp380
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.h309
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent_server.skeleton.cpp47
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.cpp481
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.h299
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector_server.skeleton.cpp42
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.cpp481
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.h299
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector_server.skeleton.cpp42
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/agent_types.h28
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.cpp1354
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.h481
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.cpp49
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.h40
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.cpp913
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.h493
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/memory/BUILD57
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/memory/CMakeLists.txt46
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h74
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h100
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_data_test.cc28
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_exporter_test.cc29
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/BUILD131
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/CMakeLists.txt125
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/common_utils.h80
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h62
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h67
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h166
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h70
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/log_exporter.cc132
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metric_exporter.cc214
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metrics_exporter.cc59
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/span_exporter.cc178
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_capture.h60
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_log_test.cc330
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metric_test.cc269
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metrics_test.cc295
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_span_test.cc398
-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
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/BUILD144
-rwxr-xr-xsrc/jaegertracing/opentelemetry-cpp/exporters/prometheus/CMakeLists.txt90
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h87
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h126
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h115
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h88
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter.h98
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h171
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/collector.cc89
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter.cc100
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter_utils.cc314
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_collector.cc166
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter.cc110
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter_utils.cc446
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/CMakeLists.txt13
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_collector_test.cc756
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_test.cc218
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_utils_test.cc460
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/BUILD64
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/CMakeLists.txt75
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/README.md56
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h60
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h113
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/recordable.cc254
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/zipkin_exporter.cc128
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_exporter_test.cc215
-rw-r--r--src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_recordable_test.cc285
143 files changed, 30258 insertions, 0 deletions
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/CMakeLists.txt
new file mode 100644
index 000000000..862d2c779
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Copyright 2021, 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.
+
+if(WITH_OTLP)
+ add_subdirectory(otlp)
+endif()
+
+add_subdirectory(ostream)
+add_subdirectory(memory)
+
+if(WITH_PROMETHEUS)
+ add_subdirectory(prometheus)
+endif()
+
+if(WITH_ZIPKIN)
+ add_subdirectory(zipkin)
+endif()
+
+if(WITH_ELASTICSEARCH AND WITH_LOGS_PREVIEW)
+ add_subdirectory(elasticsearch)
+endif()
+
+if(WITH_ETW)
+ add_subdirectory(etw)
+endif()
+
+if(WITH_JAEGER)
+ add_subdirectory(jaeger)
+endif()
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/BUILD
new file mode 100644
index 000000000..78ff94d46
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/BUILD
@@ -0,0 +1,43 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "es_log_exporter",
+ srcs = [
+ "src/es_log_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/elasticsearch/es_log_exporter.h",
+ "include/opentelemetry/exporters/elasticsearch/es_log_recordable.h",
+ ],
+ linkopts = select({
+ "//bazel:windows": [
+ "-DEFAULTLIB:advapi32.lib",
+ "-DEFAULTLIB:crypt32.lib",
+ "-DEFAULTLIB:Normaliz.lib",
+ ],
+ "//conditions:default": [],
+ }),
+ strip_include_prefix = "include",
+ tags = ["es"],
+ deps = [
+ "//ext:headers",
+ "//ext/src/http/client/curl:http_client_curl",
+ "//sdk/src/logs",
+ "@curl",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "es_log_exporter_test",
+ srcs = ["test/es_log_exporter_test.cc"],
+ tags = [
+ "es",
+ "test",
+ ],
+ deps = [
+ ":es_log_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@curl",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/CMakeLists.txt
new file mode 100644
index 000000000..ed6488316
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/CMakeLists.txt
@@ -0,0 +1,41 @@
+add_library(opentelemetry_exporter_elasticsearch_logs src/es_log_exporter.cc)
+
+set_target_properties(opentelemetry_exporter_elasticsearch_logs
+ PROPERTIES EXPORT_NAME elasticsearch_log_exporter)
+
+target_include_directories(
+ opentelemetry_exporter_elasticsearch_logs
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+target_link_libraries(
+ opentelemetry_exporter_elasticsearch_logs
+ PUBLIC opentelemetry_trace opentelemetry_logs opentelemetry_http_client_curl
+ nlohmann_json::nlohmann_json)
+
+install(
+ TARGETS opentelemetry_exporter_elasticsearch_logs
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/elasticsearch
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h"
+ PATTERN "es_log_recordable.h" EXCLUDE)
+
+if(BUILD_TESTING)
+ add_executable(es_log_exporter_test test/es_log_exporter_test.cc)
+
+ target_link_libraries(
+ es_log_exporter_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_exporter_elasticsearch_logs)
+
+ gtest_add_tests(
+ TARGET es_log_exporter_test
+ TEST_PREFIX exporter.
+ TEST_LIST es_log_exporter_test)
+endif() # BUILD_TESTING
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h
new file mode 100644
index 000000000..ea58807e9
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_exporter.h
@@ -0,0 +1,114 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "nlohmann/json.hpp"
+# include "opentelemetry/common/spin_lock_mutex.h"
+# include "opentelemetry/ext/http/client/curl/http_client_curl.h"
+# include "opentelemetry/nostd/type_traits.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+
+# include <time.h>
+# include <iostream>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace logs
+{
+/**
+ * Struct to hold Elasticsearch exporter configuration options.
+ */
+struct ElasticsearchExporterOptions
+{
+ // Configuration options to establish Elasticsearch connection
+ std::string host_;
+ int port_;
+ std::string index_;
+
+ // Maximum time to wait for response after sending request to Elasticsearch
+ int response_timeout_;
+
+ // Whether to print the status of the exporter in the console
+ bool console_debug_;
+
+ /**
+ * Constructor for the ElasticsearchExporterOptions. By default, the endpoint is
+ * localhost:9200/logs with a timeout of 30 seconds and disabled console debugging
+ * @param host The host of the Elasticsearch instance
+ * @param port The port of the Elasticsearch instance
+ * @param index The index/shard that the logs will be written to
+ * @param response_timeout The maximum time in seconds the exporter should wait for a response
+ * from elasticsearch
+ * @param console_debug If true, print the status of the exporter methods in the console
+ */
+ ElasticsearchExporterOptions(std::string host = "localhost",
+ int port = 9200,
+ std::string index = "logs",
+ int response_timeout = 30,
+ bool console_debug = false)
+ : host_{host},
+ port_{port},
+ index_{index},
+ response_timeout_{response_timeout},
+ console_debug_{console_debug}
+ {}
+};
+
+/**
+ * The ElasticsearchLogExporter exports logs to Elasticsearch in JSON format
+ */
+class ElasticsearchLogExporter final : public opentelemetry::sdk::logs::LogExporter
+{
+public:
+ /**
+ * Create an ElasticsearchLogExporter with default exporter options.
+ */
+ ElasticsearchLogExporter();
+
+ /**
+ * Create an ElasticsearchLogExporter with user specified options.
+ * @param options An object containing the user's configuration options.
+ */
+ ElasticsearchLogExporter(const ElasticsearchExporterOptions &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.
+ */
+ sdk::common::ExportResult Export(
+ const opentelemetry::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:
+ // Stores if this exporter had its Shutdown() method called
+ bool is_shutdown_ = false;
+
+ // Configuration options for the exporter
+ ElasticsearchExporterOptions options_;
+
+ // Object that stores the HTTP sessions that have been created
+ std::unique_ptr<ext::http::client::HttpClient> http_client_;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+};
+} // namespace logs
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h
new file mode 100755
index 000000000..c38bf8769
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/include/opentelemetry/exporters/elasticsearch/es_log_recordable.h
@@ -0,0 +1,229 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <map>
+# include <sstream>
+# include <type_traits>
+# include <unordered_map>
+
+# include "nlohmann/json.hpp"
+# include "opentelemetry/sdk/common/attribute_utils.h"
+# include "opentelemetry/sdk/logs/recordable.h"
+# include "opentelemetry/version.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace logs
+{
+
+/**
+ * An Elasticsearch Recordable implemenation that stores the 10 fields of the Log Data Model inside
+ * a JSON object,
+ */
+class ElasticSearchRecordable final : public sdk::logs::Recordable
+{
+private:
+ // Define a JSON object that will be populated with the log data
+ nlohmann::json json_;
+
+ /**
+ * A helper method that writes a key/value pair under a specified name, the two names used here
+ * being "attributes" and "resources"
+ */
+ void WriteKeyValue(nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value,
+ std::string name)
+ {
+ switch (value.index())
+ {
+ case common::AttributeType::kTypeBool:
+ json_[name][key.data()] = opentelemetry::nostd::get<bool>(value) ? true : false;
+ return;
+ case common::AttributeType::kTypeInt:
+ json_[name][key.data()] = opentelemetry::nostd::get<int>(value);
+ return;
+ case common::AttributeType::kTypeInt64:
+ json_[name][key.data()] = opentelemetry::nostd::get<int64_t>(value);
+ return;
+ case common::AttributeType::kTypeUInt:
+ json_[name][key.data()] = opentelemetry::nostd::get<unsigned int>(value);
+ return;
+ case common::AttributeType::kTypeUInt64:
+ json_[name][key.data()] = opentelemetry::nostd::get<uint64_t>(value);
+ return;
+ case common::AttributeType::kTypeDouble:
+ json_[name][key.data()] = opentelemetry::nostd::get<double>(value);
+ return;
+ case common::AttributeType::kTypeCString:
+ json_[name][key.data()] = opentelemetry::nostd::get<const char *>(value);
+ return;
+ case common::AttributeType::kTypeString:
+ json_[name][key.data()] =
+ opentelemetry::nostd::get<opentelemetry::nostd::string_view>(value).data();
+ return;
+ default:
+ return;
+ }
+ }
+
+ void WriteKeyValue(nostd::string_view key,
+ const opentelemetry::sdk::common::OwnedAttributeValue &value,
+ std::string name)
+ {
+ namespace common = opentelemetry::sdk::common;
+ switch (value.index())
+ {
+ case common::kTypeBool:
+ json_[name][key.data()] = opentelemetry::nostd::get<bool>(value) ? true : false;
+ return;
+ case common::kTypeInt:
+ json_[name][key.data()] = opentelemetry::nostd::get<int>(value);
+ return;
+ case common::kTypeInt64:
+ json_[name][key.data()] = opentelemetry::nostd::get<int64_t>(value);
+ return;
+ case common::kTypeUInt:
+ json_[name][key.data()] = opentelemetry::nostd::get<unsigned int>(value);
+ return;
+ case common::kTypeUInt64:
+ json_[name][key.data()] = opentelemetry::nostd::get<uint64_t>(value);
+ return;
+ case common::kTypeDouble:
+ json_[name][key.data()] = opentelemetry::nostd::get<double>(value);
+ return;
+ case common::kTypeString:
+ json_[name][key.data()] = opentelemetry::nostd::get<std::string>(value).data();
+ return;
+ default:
+ return;
+ }
+ }
+
+public:
+ /**
+ * Set the severity for this log.
+ * @param severity the severity of the event
+ */
+ void SetSeverity(opentelemetry::logs::Severity severity) noexcept override
+ {
+ // Convert the severity enum to a string
+ std::uint32_t severity_index = static_cast<std::uint32_t>(severity);
+ if (severity_index >= std::extent<decltype(opentelemetry::logs::SeverityNumToText)>::value)
+ {
+ std::stringstream sout;
+ sout << "Invalid severity(" << severity_index << ")";
+ json_["severity"] = sout.str();
+ }
+ else
+ {
+ json_["severity"] = opentelemetry::logs::SeverityNumToText[severity_index];
+ }
+ }
+
+ /**
+ * Set body field for this log.
+ * @param message the body to set
+ */
+ void SetBody(nostd::string_view message) noexcept override { json_["body"] = message.data(); }
+
+ /**
+ * Set Resource of this log
+ * @param Resource the resource to set
+ */
+ void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override
+ {
+ for (auto &kv : resource.GetAttributes())
+ {
+ WriteKeyValue(kv.first, kv.second, "resource");
+ }
+ }
+
+ /**
+ * Set an attribute of a log.
+ * @param key the key of the attribute
+ * @param value the attribute value
+ */
+ void SetAttribute(nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value) noexcept override
+ {
+ WriteKeyValue(key, value, "attributes");
+ }
+
+ /**
+ * Set trace id for this log.
+ * @param trace_id the trace id to set
+ */
+ void SetTraceId(opentelemetry::trace::TraceId trace_id) noexcept override
+ {
+ char trace_buf[32];
+ trace_id.ToLowerBase16(trace_buf);
+ json_["traceid"] = std::string(trace_buf, sizeof(trace_buf));
+ }
+
+ /**
+ * Set span id for this log.
+ * @param span_id the span id to set
+ */
+ virtual void SetSpanId(opentelemetry::trace::SpanId span_id) noexcept override
+ {
+ char span_buf[16];
+ span_id.ToLowerBase16(span_buf);
+ json_["spanid"] = std::string(span_buf, sizeof(span_buf));
+ }
+
+ /**
+ * Inject a trace_flags for this log.
+ * @param trace_flags the span id to set
+ */
+ void SetTraceFlags(opentelemetry::trace::TraceFlags trace_flags) noexcept override
+ {
+ char flag_buf[2];
+ trace_flags.ToLowerBase16(flag_buf);
+ json_["traceflags"] = std::string(flag_buf, sizeof(flag_buf));
+ }
+
+ /**
+ * Set the timestamp for this log.
+ * @param timestamp the timestamp of the event
+ */
+ void SetTimestamp(common::SystemTimestamp timestamp) noexcept override
+ {
+ json_["timestamp"] = timestamp.time_since_epoch().count();
+ }
+
+ /**
+ * Returns a JSON object contain the log information
+ */
+ nlohmann::json GetJSON() noexcept { return json_; }
+
+ /**
+ * Set instrumentation_library for this log.
+ * @param instrumentation_library the instrumentation library to set
+ */
+ void SetInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library) noexcept
+ {
+ json_["name"] = instrumentation_library.GetName();
+ instrumentation_library_ = &instrumentation_library;
+ }
+
+ /** Returns the associated instruementation library */
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary &
+ GetInstrumentationLibrary() const noexcept
+ {
+ return *instrumentation_library_;
+ }
+
+private:
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ *instrumentation_library_ = nullptr;
+};
+} // namespace logs
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/src/es_log_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/src/es_log_exporter.cc
new file mode 100644
index 000000000..a5a66ebe0
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/src/es_log_exporter.cc
@@ -0,0 +1,221 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <sstream> // std::stringstream
+
+# include <mutex>
+# include "opentelemetry/exporters/elasticsearch/es_log_exporter.h"
+# include "opentelemetry/exporters/elasticsearch/es_log_recordable.h"
+# include "opentelemetry/sdk_config.h"
+
+namespace nostd = opentelemetry::nostd;
+namespace sdklogs = opentelemetry::sdk::logs;
+namespace http_client = opentelemetry::ext::http::client;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace logs
+{
+/**
+ * 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());
+
+ // Set the response_received_ flag to true and notify any threads waiting on this result
+ response_received_ = true;
+ }
+ cv_.notify_all();
+ }
+
+ /**
+ * 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);
+ 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, nostd::string_view reason) noexcept override
+ {
+ // If any failure event occurs, release the condition variable to unblock main thread
+ switch (state)
+ {
+ case http_client::SessionState::ConnectFailed:
+ OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Connection to elasticsearch failed");
+ cv_.notify_all();
+ break;
+ case http_client::SessionState::SendFailed:
+ OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Request failed to be sent to elasticsearch");
+ cv_.notify_all();
+ break;
+ case http_client::SessionState::TimedOut:
+ OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Request to elasticsearch timed out");
+ cv_.notify_all();
+ break;
+ case http_client::SessionState::NetworkError:
+ OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Network error to elasticsearch");
+ cv_.notify_all();
+ break;
+ }
+ }
+
+private:
+ // Define a condition variable and mutex
+ std::condition_variable cv_;
+ std::mutex mutex_;
+
+ // Whether the response from Elasticsearch 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;
+};
+
+ElasticsearchLogExporter::ElasticsearchLogExporter()
+ : options_{ElasticsearchExporterOptions()},
+ http_client_{new ext::http::client::curl::HttpClient()}
+{}
+
+ElasticsearchLogExporter::ElasticsearchLogExporter(const ElasticsearchExporterOptions &options)
+ : options_{options}, http_client_{new ext::http::client::curl::HttpClient()}
+{}
+
+std::unique_ptr<sdklogs::Recordable> ElasticsearchLogExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<sdklogs::Recordable>(new ElasticSearchRecordable);
+}
+
+sdk::common::ExportResult ElasticsearchLogExporter::Export(
+ const nostd::span<std::unique_ptr<sdklogs::Recordable>> &records) noexcept
+{
+ // Return failure if this exporter has been shutdown
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[ES Log Exporter] Exporting "
+ << records.size() << " log(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+
+ // Create a connection to the ElasticSearch instance
+ auto session = http_client_->CreateSession(options_.host_ + std::to_string(options_.port_));
+ auto request = session->CreateRequest();
+
+ // Populate the request with headers and methods
+ request->SetUri(options_.index_ + "/_bulk?pretty");
+ request->SetMethod(http_client::Method::Post);
+ request->AddHeader("Content-Type", "application/json");
+ request->SetTimeoutMs(std::chrono::milliseconds(1000 * options_.response_timeout_));
+
+ // Create the request body
+ std::string body = "";
+ for (auto &record : records)
+ {
+ // Append {"index":{}} before JSON body, which tells Elasticsearch to write to index specified
+ // in URI
+ body += "{\"index\" : {}}\n";
+
+ // Add the context of the Recordable
+ auto json_record = std::unique_ptr<ElasticSearchRecordable>(
+ static_cast<ElasticSearchRecordable *>(record.release()));
+ body += json_record->GetJSON().dump() + "\n";
+ }
+ std::vector<uint8_t> body_vec(body.begin(), body.end());
+ request->SetBody(body_vec);
+
+ // 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(
+ "[ES Trace Exporter] waiting for response from Elasticsearch (timeout = "
+ << options_.response_timeout_ << " seconds)");
+ }
+ 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 sdk::common::ExportResult::kFailure;
+ }
+
+ // Parse the response output to determine if Elasticsearch consumed it correctly
+ std::string responseBody = handler->GetResponseBody();
+ if (responseBody.find("\"failed\" : 0") == std::string::npos)
+ {
+ OTEL_INTERNAL_LOG_ERROR(
+ "[ES Trace Exporter] Logs were not written to Elasticsearch correctly, response body: "
+ << responseBody);
+ // TODO: Retry logic
+ return sdk::common::ExportResult::kFailure;
+ }
+
+ return sdk::common::ExportResult::kSuccess;
+}
+
+bool ElasticsearchLogExporter::Shutdown(std::chrono::microseconds timeout) 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 ElasticsearchLogExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+} // namespace logs
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/test/es_log_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/test/es_log_exporter_test.cc
new file mode 100644
index 000000000..943f9fa7b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/elasticsearch/test/es_log_exporter_test.cc
@@ -0,0 +1,86 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/exporters/elasticsearch/es_log_exporter.h"
+# include "opentelemetry/ext/http/server/http_server.h"
+# include "opentelemetry/logs/provider.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/sdk/logs/logger_provider.h"
+# include "opentelemetry/sdk/logs/simple_log_processor.h"
+
+# include <gtest/gtest.h>
+# include <iostream>
+
+namespace sdklogs = opentelemetry::sdk::logs;
+namespace logs_api = opentelemetry::logs;
+namespace nostd = opentelemetry::nostd;
+namespace logs_exporter = opentelemetry::exporter::logs;
+
+TEST(ElasticsearchLogsExporterTests, Dummy)
+{
+ // to enable linking
+}
+
+# if 0
+// Attempt to write a log to an invalid host/port, test that the Export() returns failure
+TEST(ElasticsearchLogsExporterTests, InvalidEndpoint)
+{
+ // Create invalid connection options for the elasticsearch exporter
+ logs_exporter::ElasticsearchExporterOptions options("localhost", -1);
+
+ // Create an elasticsearch exporter
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new logs_exporter::ElasticsearchLogExporter(options));
+
+ // Create a log record and send to the exporter
+ auto record = exporter->MakeRecordable();
+ auto result = exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+
+ // Ensure the return value is failure
+ ASSERT_EQ(result, sdk::common::ExportResult::kFailure);
+}
+
+// Test that when the exporter is shutdown, any call to Export should return failure
+TEST(ElasticsearchLogsExporterTests, Shutdown)
+{
+ // Create an elasticsearch exporter and immediately shut it down
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new logs_exporter::ElasticsearchLogExporter);
+ bool shutdownResult = exporter->Shutdown();
+ ASSERT_TRUE(shutdownResult);
+
+ // Write a log to the shutdown exporter
+ auto record = exporter->MakeRecordable();
+ auto result = exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+
+ // Ensure the return value is failure
+ ASSERT_EQ(result, sdk::common::ExportResult::kFailure);
+}
+
+// Test the elasticsearch recordable object
+TEST(ElasticsearchLogsExporterTests, RecordableCreation)
+{
+ // Create an elasticsearch exporter
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new logs_exporter::ElasticsearchLogExporter);
+
+ // Create a recordable
+ auto record = exporter->MakeRecordable();
+ record->SetSeverity(logs_api::Severity::kFatal);
+ record->SetTimestamp(std::chrono::system_clock::now());
+ record->SetBody("Body of the log message");
+
+ // Attributes and resource support different types
+ record->SetAttribute("key0", false);
+ record->SetAttribute("key1", "1");
+
+ auto resource = opentelemetry::sdk::resource::Resource::Create({{"key2", 2}, {"key3", 3142}});
+ record->SetResource(resource);
+
+ exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+}
+# endif
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD
new file mode 100644
index 000000000..c2328ed4e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/BUILD
@@ -0,0 +1,69 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "etw_exporter",
+ hdrs = glob([
+ "include/opentelemetry/exporters/etw/*.h",
+ ]),
+ includes = ["include"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = ["etw"],
+ deps = [
+ "//api",
+ "//sdk/src/trace",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "etw_provider_test",
+ srcs = ["test/etw_provider_test.cc"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = [
+ "etw",
+ "test",
+ ],
+ deps = [
+ ":etw_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "etw_tracer_test",
+ srcs = ["test/etw_tracer_test.cc"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = [
+ "etw",
+ "test",
+ ],
+ deps = [
+ ":etw_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_test(
+ name = "etw_logger_test",
+ srcs = ["test/etw_logger_test.cc"],
+ local_defines = [
+ "HAVE_MSGPACK",
+ ],
+ tags = [
+ "etw",
+ "test",
+ ],
+ deps = [
+ ":etw_exporter",
+ "@com_google_googletest//:gtest_main",
+ "@github_nlohmann_json//:json",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt
new file mode 100644
index 000000000..47791b4ab
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/CMakeLists.txt
@@ -0,0 +1,63 @@
+add_library(opentelemetry_exporter_etw INTERFACE)
+
+target_include_directories(
+ opentelemetry_exporter_etw
+ INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+set_target_properties(opentelemetry_exporter_etw PROPERTIES EXPORT_NAME
+ etw_exporter)
+
+target_link_libraries(opentelemetry_exporter_etw
+ INTERFACE nlohmann_json::nlohmann_json)
+if(nlohmann_json_clone)
+ add_dependencies(opentelemetry_exporter_etw nlohmann_json::nlohmann_json)
+endif()
+
+install(
+ TARGETS opentelemetry_exporter_etw
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/etw
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h")
+
+if(BUILD_TESTING)
+ add_executable(etw_provider_test test/etw_provider_test.cc)
+ add_executable(etw_tracer_test test/etw_tracer_test.cc)
+ add_executable(etw_logger_test test/etw_logger_test.cc)
+
+ add_executable(etw_perf_test test/etw_perf_test.cc)
+
+ target_link_libraries(etw_provider_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ target_link_libraries(etw_tracer_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ target_link_libraries(etw_logger_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ target_link_libraries(
+ etw_perf_test benchmark::benchmark ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_etw ${CMAKE_THREAD_LIBS_INIT})
+
+ gtest_add_tests(
+ TARGET etw_provider_test
+ TEST_PREFIX exporter.
+ TEST_LIST etw_provider_test)
+ gtest_add_tests(
+ TARGET etw_tracer_test
+ TEST_PREFIX exporter.
+ TEST_LIST etw_tracer_test)
+ gtest_add_tests(
+ TARGET etw_logger_test
+ TEST_PREFIX exporter.
+ TEST_LIST etw_logger_test)
+
+endif() # BUILD_TESTING
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md b/src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md
new file mode 100644
index 000000000..29dc3de43
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/README.md
@@ -0,0 +1,226 @@
+# Getting Started with OpenTelemetry C++ SDK and ETW exporter on Windows
+
+Event Tracing for Windows (ETW) is an efficient kernel-level tracing facility
+that lets you log kernel or application-defined events to a log file. You can
+consume the events in real time or from a log file and use them to debug an
+application or to determine where performance issues are occurring in the
+application.
+
+OpenTelemetry C++ SDK ETW exporter allows the code instrumented using
+OpenTelemetry API to forward events to out-of-process ETW listener, for
+subsequent data recording or forwarding to alternate pipelines and flows.
+Windows Event Tracing infrastructure is available to any vendor or application
+being deployed on Windows.
+
+## API support
+
+These are the features planned to be supported by ETW exporter:
+
+- [x] OpenTelemetry Tracing API and SDK headers are **stable** and moving
+ towards GA.
+- [ ] OpenTelemetry Logging API is work-in-progress, pending implementation of
+ [Latest Logging API spec
+ here](https://github.com/open-telemetry/oteps/pull/150)
+- [ ] OpenTelemetry Metrics API is not implemented yet.
+
+Implementation of OpenTelemetry C++ SDK ETW exporter on Windows OS is `header
+only` :
+
+- full definitions of all macros, functions and classes comprising the library
+are visible to the compiler in a header file form.
+- implementation does not need to be separately compiled, packaged and installed
+ in order to be used.
+
+All that is required is to point the compiler at the location of the headers,
+and then `#include` the header files into the application source. Compiler's
+optimizer can do a much better job when all the library's source code is
+available. Several options below may be turned on to optimize the code with the
+usage of standard C++ library, Microsoft Guidelines Support library, Google
+Abseil Variant library. Or enabling support for non-standard features, such as
+8-bit byte arrays support that enables performance-efficient representation of
+binary blobs on ETW wire.
+
+## Example project
+
+The following include directories are required, relative to the top-level source
+tree of OpenTelemetry C++ repo:
+
+- api/include/
+- exporters/etw/include/
+- sdk/include/
+
+Code that instantiates ETW TracerProvider, subsequently obtaining a Tracer bound
+to `OpenTelemetry-ETW-Provider`, and emitting a span named `MySpan` with
+attributes on it, as well as `MyEvent` within that span.
+
+```cpp
+
+#include <map>
+#include <string>
+
+#include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+using namespace opentelemetry::exporter::etw;
+
+// Supply unique instrumentation name (ETW Provider Name) here:
+std::string providerName = "OpenTelemetry-ETW-Provider";
+
+exporter::etw::TracerProvider tp;
+
+int main(int argc, const char* argv[])
+{
+ // Obtain a Tracer object for instrumentation name.
+ // Each Tracer is associated with unique TraceId.
+ auto tracer = tp.GetTracer(providerName, "TLD");
+
+ // Properties is a helper class in ETW namespace that is otherwise compatible
+ // with Key-Value Iterable accepted by OpenTelemetry API. Using Properties
+ // should enable more efficient data transfer without unnecessary memcpy.
+
+ // Span attributes
+ Properties attribs =
+ {
+ {"attrib1", 1},
+ {"attrib2", 2}
+ };
+
+ // Start Span with attributes
+ auto span = tracer->StartSpan("MySpan", attribs);
+
+ // Emit an event on Span
+ std::string eventName = "MyEvent1";
+ Properties event =
+ {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}
+ };
+ span->AddEvent(eventName, event);
+
+ // End Span.
+ span->End();
+
+ // Close the Tracer on application stop.
+ tracer->CloseWithMicroseconds(0);
+
+ return 0;
+}
+```
+
+Note that different `Tracer` objects may be bound to different ETW destinations.
+
+## Build options and Compiler Defines
+
+While including OpenTelemetry C++ SDK with ETW exporter, the customers are in
+complete control of what options they would like to enable for their project
+using `Preprocessor Definitions`.
+
+These options affect how "embedded in application" OpenTelemetry C++ SDK code is
+compiled:
+
+| Name | Description |
+|---------------------|------------------------------------------------------------------------------------------------------------------------|
+| HAVE_CPP_STDLIB | Use STL classes for API surface. This option requires at least C++17. C++20 is recommended. Some customers may benefit from STL library provided with the compiler instead of using custom OpenTelemetry `nostd::` implementation due to security and performance considerations. |
+| HAVE_GSL | Use [Microsoft GSL](https://github.com/microsoft/GSL) for `gsl::span` implementation. Library must be in include path. Microsoft GSL claims to be the most feature-complete implementation of `std::span`. It may be used instead of `nostd::span` implementation in projects that statically link OpenTelemetry SDK. |
+| HAVE_TLD | Use ETW/TraceLogging Dynamic protocol. This is the default implementation compatible with existing C# "listeners" / "decoders" of ETW events. This option requires an additional optional Microsoft MIT-licensed `TraceLoggingDynamic.h` header. |
+
+## Debugging
+
+### ETW TraceLogging Dynamic Events
+
+ETW supports a mode that is called "Dynamic Manifest", where event may contain
+strongly-typed key-value pairs, with primitive types such as `integer`,
+`double`, `string`, etc. This mode requires `TraceLoggingDynamic.h` header.
+Please refer to upstream repository containining [Microsoft TraceLogging Dynamic
+framework](https://github.com/microsoft/tracelogging-dynamic-windows) licensed
+under [MIT
+License](https://github.com/microsoft/tracelogging-dynamic-windows/blob/main/LICENSE).
+
+Complete [list of ETW
+types](https://docs.microsoft.com/en-us/windows/win32/wes/eventmanifestschema-outputtype-complextype#remarks).
+
+OpenTelemetry C++ ETW exporter implements the following type mapping:
+
+| OpenTelemetry C++ API type | ETW type |
+|----------------------------|-----------------|
+| bool | xs:byte |
+| int (32-bit) | xs:int |
+| int (64-bit) | xs:long |
+| uint (32-bit) | xs:unsignedInt |
+| uint (64-bit) | xs:unsignedLong |
+| double | xs:double |
+| string | win:Utf8 |
+
+Support for arrays of primitive types is not implemented yet.
+
+Visual Studio 2019 allows to use `View -> Other Windows -> Diagnostic Events` to
+capture events that are emitted by instrumented application and sent to ETW
+provider in a live view. Instrumentation name passed to `GetTracer` API above
+corresponds to `ETW Provider Name`. If Instrumentation name contains a GUID -
+starts with a curly brace, e.g. `{deadbeef-fade-dead-c0de-cafebabefeed}` then
+the parameter is assumed to be `ETW Provider GUID`.
+
+Click on `Settings` and add the provider to monitor either by its Name or by
+GUID. In above example, the provider name is `OpenTelemetry-ETW-Provider`.
+Please refer to Diagnostic Events usage instructions
+[here](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-diagnostics-how-to-monitor-and-diagnose-services-locally#view-service-fabric-system-events-in-visual-studio)
+to learn more. Note that running ETW Listener in Visual Studio requires
+Elevation, i.e. Visual Studio would prompt you to confirm that you accept to run
+the ETW Listener process as Administrator. This is a limitation of ETW
+Listeners, they must be run as privileged process.
+
+### ETW events encoded in MessagePack
+
+OpenTelemetry ETW exporter optionally allows to encode the incoming event
+payload using [MessagePack](https://msgpack.org/index.html) compact binary
+protocol. ETW/MsgPack encoding requires
+[nlohmann/json](https://github.com/nlohmann/json) library to be included in the
+build of OpenTelemetry ETW exporter. Any recent version of `nlohmann/json` is
+compatible with ETW exporter. For example, the version included in
+`third_party/nlohmann-json` directory may be used.
+
+There is currently **no built-in decoder available** for this format. However,
+there is ongoing effort to include the ETW/MsgPack decoder in
+[Azure/diagnostics-eventflow](https://github.com/Azure/diagnostics-eventflow)
+project, which may be used as a side-car listener to forward incoming
+ETW/MsgPack events to many other destinations, such as:
+
+- StdOutput (console output)
+- HTTP (json via http)
+- Application Insights
+- Azure EventHub
+- Elasticsearch
+- Azure Monitor Logs
+
+And community-contributed exporters:
+
+- Google Big Query output
+- SQL Server output
+- ReflectInsight output
+- Splunk output
+
+[This PR](https://github.com/Azure/diagnostics-eventflow/pull/382) implements
+the `Input adapter` for OpenTelemetry ETW/MsgPack protocol encoded events for
+Azure EventFlow.
+
+Other standard tools for processing ETW events on Windows OS, such as:
+
+- [PerfView](https://github.com/microsoft/perfview)
+- [PerfViewJS](https://github.com/microsoft/perfview/tree/main/src/PerfViewJS)
+
+will be augmented in future with support for ETW/MsgPack encoding.
+
+## Addendum
+
+This document needs to be supplemented with additional information:
+
+- [ ] mapping between OpenTelemetry fields and concepts and their corresponding
+ ETW counterparts
+- [ ] links to E2E instrumentation example and ETW listener
+- [ ] Logging API example
+- [ ] Metrics API example (once Metrics spec is finalized)
+- [ ] example how ETW Listener may employ OpenTelemetry .NET SDK to 1-1
+ transform from ETW events back to OpenTelemetry flow
+- [ ] links to NuGet package that contains the source code of SDK that includes
+ OpenTelemetry SDK and ETW exporter
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE
new file mode 100644
index 000000000..cfc21fd2c
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/LICENSE
@@ -0,0 +1,23 @@
+TraceLogging Dynamic for Windows
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h
new file mode 100644
index 000000000..17ee108c6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/TraceLoggingDynamic.h
@@ -0,0 +1,3458 @@
+/* ++
+
+Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+SPDX-License-Identifier: MIT
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ TraceLoggingDynamic.h
+
+Abstract:
+
+ Dynamic TraceLogging Provider API for C++.
+
+Environment:
+
+ User mode.
+
+--*/
+
+#pragma once
+#include <windows.h>
+#include <evntprov.h>
+#include <stdlib.h> // byteswap
+
+namespace tld
+{
+#pragma region Public interface
+
+ /*
+ GENERAL:
+
+ - This implementation of manifest-free ETW supports more functionality
+ than the implementation in TraceLoggingProvider.h, but it also has
+ higher runtime costs. This implementation is intended for use only when
+ the set of events is not known at compile-time. For example,
+ TraceLoggingDynamic.h might be used to implement a library providing
+ manifest-free ETW to a scripting language like JavaScript or Perl.
+ - This header is not optimized for direct use by developers adding events
+ to their code. There is no way to make an optimal solution that
+ works for all of the intended target users. Instead, this header
+ provides various pieces that you can build upon to create a user-friendly
+ implementation of manifest-free ETW tailored for a specific scenario.
+
+ HIGH-LEVEL API:
+
+ The high-level API provides an easy way to get up and running with
+ TraceLogging ETW events.
+
+ Contents:
+ - Class: tld::Provider
+ - Class: tld::Event
+ - Class: tld::EventBuilder
+ - Enum: tld::Type
+
+ Basic usage:
+ - Create a Provider object.
+ - Check provider.IsEnabled(...) so that you don't do the remaining steps
+ if nobody is listening for your event.
+ - Create an Event<std::vector<BYTE>> object.
+ - Add fields definitions (metadata) and values (data) using methods on
+ Event. (You are responsible for making sure that the metadata you add
+ matches the data you add -- the Event object does not validate this.)
+ - Some methods on the Event object return EventBuilder objects, which are
+ used to build the fields of nested structures. Note that Event inherits
+ from EventBuilder, so if you write a function that accepts an
+ EventBuilder&, it will also accept an Event&.
+ - Once you've added all data and metadata, call event.Write() to send the
+ event to ETW.
+
+ LOW-LEVEL API:
+
+ The low-level API provides components that you can mix and match to build
+ your own solution when the high-level components don't precisely meet your
+ needs. For example, you might use the high-level Provider class to manage
+ your REGHANDLE and the ETW callbacks, but you might use
+ EventMetadataBuilder and EventDataBuilder directly instead of using Event
+ so that you can cache event definitions.
+
+ Contents:
+ - Function: tld::RegisterProvider
+ - Function: tld::UnregisterProvider
+ - Function: tld::GetGuidForName
+ - Function: tld::SetInformation
+ - Class: tld::ProviderMetadataBuilder
+ - Class: tld::EventMetadataBuilder
+ - Class: tld::EventDataBuilder
+ - Struct: tld::EventDescriptor
+ - Class: tld::ByteArrayWrapper
+ - Enum: tld::InType
+ - Enum: tld::OutType
+ - Enum: tld::ProviderTraitType
+ - Function: tld::WriteEvent
+ - Function: tld::WriteEventEx
+ - Function: tld::PushBackAsUtf8
+ - Function: tld::MakeType
+ - Macro: TLD_HAVE_EVENT_SET_INFORMATION
+ - Macro: TLD_HAVE_EVENT_WRITE_EX
+
+ Notes:
+ - EventDataDescCreate is a native ETW API from <evntprov.h>. It is not
+ part of this API, but you may want to use it to initialize
+ EVENT_DATA_DESCRIPTOR structures instead of doing it directly.
+ - If you directly initialize your EVENT_DATA_DESCRIPTOR structures instead
+ of using EventDataDescCreate, be sure to properly initialize the
+ EVENT_DATA_DESCRIPTOR.Reserved field (e.g. by setting it to 0).
+ Initializing the Reserved field is NOT optional. (EventDataDescCreate
+ does correctly initialize the Reserved field.)
+ - When the API asks for a byte-buffer, you can use std::vector<BYTE> or
+ another similar type. If you don't want to use a vector, the provided
+ ByteArrayWrapper type allows you to use your own allocation strategy for
+ the buffer.
+ - By default, TraceLogging events have Id=0 and Version=0, indicating
+ that the event does not have an assigned Id. However, events can have
+ Id and Version assigned (typically assigned manually). If you don't want
+ to manage event IDs, set both Id and Version to 0. If you do assign
+ IDs to your events, the Id must be non-zero and there should be a
+ one-to-one mapping between {provider.Id + event.Id + event.Version} and
+ {event metadata}. In other words, any event with a given non-zero
+ Id+Version combination must always have exactly the same event metadata.
+ (Note to decoders: this can be used as an optimization, but do not rely
+ on providers to follow this rule.)
+ - TraceLogging events default to channel 11 (WINEVENT_CHANNEL_TRACELOGGING).
+ This channel has no effect other than to mark the event as TraceLogging-
+ compatible. Other channels can be used, but channels other than 11 will
+ only work if the provider is running on a version of Windows that
+ understands TraceLogging (Windows 7sp1 with latest updates, Windows 8.1
+ with latest updates, Windows 10, or later). If your provider is running
+ on a version of Windows that does not understand TraceLogging and you use
+ a channel other than 11, the resulting events will not decode correctly.
+
+ Low-level provider management:
+ - Use tld::ProviderMetadataBuilder to build a provider metadata blob.
+ - If you don't have a specific provider GUID already selected, use
+ tld::GetGuidForName to compute your provider GUID.
+ - Use tld::RegisterProvider to open the REGHANDLE.
+ - Use the REGHANDLE in calls to tld::WriteEvent.
+ - Use tld::UnregisterProvider to close the REGHANDLE.
+
+ Low-level event management:
+ - Use tld::EventMetadataBuilder to build an event metadata blob.
+ - Use tld::Type values to define field types, or create non-standard
+ types using tld::MakeType, tld::InType, and tld::OutType.
+ - Use tld::EventDataBuilder to build an event data blob.
+ - Create an EVENT_DESCRIPTOR for your event.
+ - Optionally use the tld::EventDescriptor wrapper class to simplify
+ initialization of EVENT_DESCRIPTOR structures.
+ - Allocate an array of EVENT_DATA_DESCRIPTORs. Size should be two more
+ than you need for your event payload (the first two are reserved for the
+ provider and event metadata).
+ - Use EventDataDescCreate to fill in the data descriptor array (skipping
+ the first two) to reference your event payload data.
+ - Use tld::WriteEvent to write your event.
+ */
+
+ /*
+ class Provider (high-level API):
+
+ Manages an ETW REGHANDLE and the provider metadata blob.
+ - Constructor creates the provider metadata blob and registers the provider.
+ Note that this includes configuring ETW callbacks.
+ - Destructor unregisters the provider and frees the metadata blob.
+ - IsEnabled APIs provide very efficient checks for whether an event needs
+ to be written.
+ - Write APIs call WriteEvent, automatically providing the correct REGHANDLE
+ and provider metadata blobs.
+
+ Typical usage:
+
+ tld::Provider provider("ProviderName", ...);
+ if (provider.IsEnabled(eventLevel, eventKeywords))
+ {
+ (prepare event);
+ provider.Write(...);
+ }
+
+ It is not a problem to call provider.Write without checking
+ provider.IsEnabled(...), but it is more efficient to check
+ provider.IsEnabled(...) so that you can skip the process of preparing the
+ event if the event is not enabled.
+ */
+ class Provider;
+
+ /*
+ class Event (high-level API):
+
+ Manages the data and metadata for an event. You create an Event object,
+ set properties, add field definitions, add field values, and then Write
+ the event.
+
+ ByteVectorTy will usually be std::vector<BYTE>, though other similar
+ types could be used instead.
+
+ Typical usage:
+
+ if (provider.IsEnabled(eventLevel, eventKeywords))
+ {
+ Event event(provider, szEventName, eventLevel, eventKeywords);
+ (add fields definitions and values);
+ event.Write();
+ }
+
+ You can reuse an Event object by calling event.Reset(...).
+ */
+ template<class ByteVectorTy>
+ class Event;
+
+ /*
+ class EventBuilder (high-level API):
+
+ This class exposes an interface for adding field metadata/data to an event.
+ The Event class inherits from EventBuilder so that it can expose this
+ interface. In addition, each call to eventBuilder.AddStruct(...) returns a
+ new EventBuilder that can be used to add fields to a nested structure.
+ You won't directly create instances of this type -- you'll use instances
+ returned by Event.AddStruct or by EventBuilder.AddStruct.
+
+ You can use EventBuilder& as the type of a parameter to a function, and
+ then pass either an Event or an EventBuilder to that function (allowing
+ for recursive processing of event fields).
+
+ Note that order is important but not checked when using multiple Event or
+ EventBuilder objects. Fields are always added to the end of the overall
+ event, regardless of which Event or EventBuilder you use to add the field.
+ The separate Event and EventBuilder objects are used for the sole purpose
+ of tracking the number of fields in each struct.
+
+ Assume that you have obtained EventBuilder object b from object a, i.e.
+ EventBuilder b = a.AddStruct(...). You must complete all operations using
+ b before resuming any operations using a. If this rule is not followed,
+ fields may end up nesting in unexpected ways.
+ */
+ template<class ByteVectorTy>
+ class EventBuilder;
+
+ /*
+ enum Type (high-level API and low-level API):
+
+ Types for event fields, corresponding to a combination of TDH_INTYPE and
+ TDH_OUTTYPE values. The Type enumeration contains predefined combinations
+ of InType and OutType that are known to work well and are recognized by
+ decoders such as TDH and xperf.
+
+ Advanced use: It is possible to create other Type values by combining
+ values from the InType and OutType enumerations using the tld::MakeType
+ function. (Valid Type values consist of one InType combined with one
+ OutType.) However, custom combinations not already present in the Type
+ enumeration are unlikely to be recognized by xperf or other decoders,
+ and will usually end up being decoded as if they had used OutTypeDefault.
+
+ When providing payload for a field, the payload data must match the InType.
+ Payload is always packed tightly with no alignment or padding.
+
+ ****
+
+ How to pack single values:
+
+ Primitive values (int, GUID) are packed directly, with no padding or
+ alignment. Just reserve sizeof(value) bytes in the buffer, then memcpy the
+ value into the buffer. Note that there should be no alignment or padding
+ between items -- the payload is always tightly-packed.
+
+ NUL-terminated strings are also simply memcpy'ed into the buffer. Be sure
+ to include the NUL termination when reserving space and copying the value.
+ There is no special encoding reserved for a NULL string. The encoding
+ helpers in this header simply treat a NULL string the same as an empty
+ string, i.e. AddString((char*)NULL) is the same as AppendString("").
+
+ Binary, CountedString, and CountedAnsiString scalars are all encoded as a
+ UINT16 byte-count followed by the string data. In this case, no NUL
+ termination should be included. Remember that the size is a byte count,
+ not a character count.
+
+ ****
+
+ How to pack arrays:
+
+ Assume you have a function Pack(payloadVec, item) that correctly appends an
+ item to the end of payloadVec.
+
+ Packing a variable-length array of N items is done by appending N (encoded
+ as UINT16) to payloadVec followed by calling Pack(...) N times.
+
+ Packing a constant-length array of N items is done by calling Pack(...) N
+ times. The value of N is encoded in the metadata (it was provided in the
+ field's declaration) so it does not need to be provided in the payload.
+
+ Note: while this header allows you to create arrays of anything, you
+ should avoid creating arrays of the following, as they might not decode
+ correctly:
+ - TypeNone (or anything based on InTypeNull).
+ - TypeBinary (or anything based on InTypeBinary).
+
+ It is ok to create an array of nested structures where fields in the
+ structure have TypeNone or TypeBinary.
+ */
+ enum Type : UINT16;
+
+ /*
+ Used for composing Type values.
+ Normally you'll use a precomposed Type... value, but you can compose a
+ custom Type... value by combining a value from InType with a value from
+ OutType using MakeType.
+
+ The InType tells the pipeline how to encode the field's payload (primarily
+ how to determine payload size). In addition, each InType has an implicit
+ default formatting behavior that will be used if not overridden by an
+ OutType.
+
+ The comments for each InType value indicate the payload encoding rules and
+ the OutTypes that are most likely to be usable with this InType.
+ */
+ enum InType : UINT8;
+
+ /*
+ Used for composing Type values.
+ Normally you'll use a precomposed Type... value, but you can compose a
+ custom Type... value by combining a value from InType with a value from
+ OutType using MakeType.
+
+ The OutType gives the pipeline a formatting hint that the trace consumer
+ may use to override the InType's default formatting behavior. If no
+ OutType is specified (i.e. if OutTypeDefault is used) or if the trace
+ consumer does not recognize the specified InType+OutType combination, the
+ trace consumer will perform default formatting based solely on InType.
+
+ The comments for each OutType indicate the InTypes for which the OutType
+ might have valid semantics. However, most trace consumer only recognize a
+ small subset of the "valid" InType+OutType combinations, so even if the
+ semantics are valid, the OutType might still be ignored by the trace
+ consumer. The most commonly-supported combinations are the combinations
+ with corresponding precomposed Type... values.
+ */
+ enum OutType : UINT8;
+
+ /*
+ enum ProviderTraitType (low-level API):
+
+ The type of a provider trait. Used when building up provider metadata.
+ */
+ enum ProviderTraitType : UINT8;
+
+ /*
+ macro TLD_HAVE_EVENT_SET_INFORMATION (low-level API):
+
+ Configuration macro for controlling the behavior of SetInformation.
+
+ Not all versions of Windows support the EventSetInformation API. The
+ tld::SetInformation wrapper provides default behavior that works well in
+ most cases, but may not meet the needs of all users. The configuration
+ macros can be used to control how the tld::SetInformation function invokes
+ the EventSetInformation API. (Note that tld::SetInformation is called
+ automatically by the Provider class's constructor to register the
+ provider's traits with ETW.)
+
+ When TLD_HAVE_EVENT_SET_INFORMATION is not defined and WINVER < 0x0602:
+ SetInformation uses GetModuleHandleExW+GetProcAddress to find the
+ EventSetInformation function. If found, SetInformation calls it and
+ returns whatever EventSetInformation returns. Otherwise, SetInformation
+ returns an error.
+
+ When TLD_HAVE_EVENT_SET_INFORMATION is not defined and WINVER >= 0x0602:
+ SetInformation directly calls the EventSetInformation(...) function and
+ returns whatever EventSetInformation returns.
+
+ If you set TLD_HAVE_EVENT_SET_INFORMATION:
+ - If TLD_HAVE_EVENT_SET_INFORMATION == 0, SetInformation always returns an
+ error.
+ - If TLD_HAVE_EVENT_SET_INFORMATION == 1, SetInformation calls
+ EventSetInformation.
+ - If TLD_HAVE_EVENT_SET_INFORMATION == 2, SetInformation locates
+ EventSetInformation via GetProcAddress.
+ */
+#ifndef TLD_HAVE_EVENT_SET_INFORMATION
+ #if WINVER < 0x0602 // If targeting Windows before Windows 8
+ #define TLD_HAVE_EVENT_SET_INFORMATION 2 // Find "EventSetInformation" via GetModuleHandleExW/GetProcAddress
+ #else
+ #define TLD_HAVE_EVENT_SET_INFORMATION 1 // Directly invoke EventSetInformation(...)
+ #endif
+#endif // TLD_HAVE_EVENT_SET_INFORMATION
+
+ /*
+ macro TLD_HAVE_EVENT_WRITE_EX (low-level API):
+
+ Configuration macro for enabling/disabling WriteEventEx, WriteEx.
+
+ If TLD_HAVE_EVENT_WRITE_EX is not defined and WINVER < 0x0601, or
+ if TLD_HAVE_EVENT_WRITE_EX is defined as 0,
+ then the WriteEventEx, WriteEx, and related APIs will not be available.
+
+ If TLD_HAVE_EVENT_WRITE_EX is not defined and WINVER >= 0x0601, or
+ if TLD_HAVE_EVENT_WRITE_EX is defined as 1,
+ then the WriteEventEx, WriteEx, and related APIs will be available.
+ */
+#ifndef TLD_HAVE_EVENT_WRITE_EX
+ #if WINVER < 0x0601 // If targeting Windows before Windows 7
+ #define TLD_HAVE_EVENT_WRITE_EX 0
+ #else
+ #define TLD_HAVE_EVENT_WRITE_EX 1
+ #endif
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+
+ /*
+ class ProviderMetadataBuilder (low-level API):
+
+ Helper for building the provider metadata blob.
+ The type provided for ByteBufferTy must behave like a std::vector<BYTE>.
+
+ Example usage:
+
+ std::vector<BYTE> byteVector;
+ tld::ProviderMetadataBuilder<std::vector<BYTE>> builder(byteVector);
+ builder.Begin(szProviderName); // Note: calls byteVector.clear().
+ if (builder.End()) // Returns false if the metadata is too large.
+ {
+ // byteVector now contains a valid provider metadata blob.
+ }
+ */
+ template<class ByteBufferTy>
+ class ProviderMetadataBuilder;
+
+ /*
+ class EventMetadataBuilder (low-level API):
+
+ Helper for building the event metadata blob, including field definitions.
+ The type provided for ByteBufferTy must behave like a std::vector<BYTE>.
+
+ Example usage:
+
+ std::vector<BYTE> byteVector;
+ tld::EventMetadataBuilder<std::vector<BYTE>> builder(byteVector);
+ builder.Begin(eventName); // Note: calls byteVector.clear().
+ builder.AddField("Field1", TypeBOOL32); // top-level field
+ auto struct1 = builder.AddStruct("Struct1");
+ struct1.AddField("Nested1", TypeINT32) // nested field
+ struct1.AddField("Nested2", TypeANSISTRING); // nested field
+ if (builder.End()) // Returns false if the metadata is too large.
+ {
+ // byteVector now contains a valid event metadata blob.
+ }
+ */
+ template<class ByteBufferTy>
+ class EventMetadataBuilder;
+
+ /*
+ class EventDataBuilder (low-level API):
+
+ Helpers for adding event data to an event data blob.
+ The type provided for ByteBufferTy must behave like a std::vector<BYTE>.
+ */
+ template<class ByteBufferTy>
+ class EventDataBuilder;
+
+ /*
+ class EventDescriptor (low-level API):
+
+ Derives from EVENT_DESCRIPTOR. Adds convenient constructor and Reset
+ members. Contains an event's Id/Version/Level/Opcode/Task/Keyword
+ settings. Use of this class is optional - you can use this class if you
+ want to use the constructor or Reset methods, or you can use
+ EVENT_DESCRIPTOR directly if you don't need the convenience methods.
+ */
+ struct EventDescriptor;
+
+ /*
+ class ByteArrayWrapper (low-level API):
+
+ Adapter that allows a regular BYTE array (stack-allocated or
+ manually-allocated) to be used as the buffer in MetadataBuilder
+ operations. If the underlying BYTE array is too small, or if no array is
+ provided at all, this adapter will stop writing data beyond the end of the
+ array, but will continue to "accept" data (and will increment it size
+ member accordingly). This allows you to determine the size of the required
+ array so that the operation can be retried with a correctly-sized array.
+
+ Example usage:
+
+ // Create a wrapper with a default (0-length) buffer.
+ tld::ByteArrayWrapper wrapper;
+
+ // Create a metadata builder attached to the wrapper.
+ tld::ProviderMetadataBuilder<tld::ByteArrayWrapper> builder(wrapper);
+
+ // Determine the size needed for provider metadata.
+ builder.Begin(providerName);
+ builder.End();
+
+ // Allocate a real buffer and reset the wrapper to use it.
+ BYTE* pBuffer = new BYTE[wrapper.size()];
+ wrapper.reset(pBuffer, wrapper.size());
+
+ // Redo the operation with the real buffer.
+ builder.Begin(providerName);
+ if (builder.End())
+ {
+ // pBuffer now contains a valid provider metadata blob.
+ }
+ */
+ class ByteArrayWrapper;
+
+ /*
+ function RegisterProvider (low-level API):
+
+ Calls EventRegister. If EventRegister succeeds, calls EventSetInformation
+ to register your provider traits with ETW. Use this instead of calling
+ EventRegister directly to ensure that your provider traits are properly
+ registered.
+ */
+ inline HRESULT RegisterProvider(
+ _Out_ REGHANDLE* phProvider,
+ _In_ GUID const* pProviderId,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_opt_ PENABLECALLBACK pEnableCallback = 0,
+ _In_opt_ void* pEnableCallbackContext = 0);
+
+ /*
+ function UnregisterProvider (low-level API):
+
+ Calls EventUnregister.
+ */
+ inline HRESULT UnregisterProvider(
+ REGHANDLE hProvider);
+
+ /*
+ function WriteEvent (low-level API):
+
+ Calls EventWriteTransfer. You must provide 2 more data descriptors than
+ are required for your data. The first two elements in pDataDescriptors
+ will be used for provider and event metadata. The actual payload (if any)
+ is expected to be in the remaining elements of pDataDescriptors.
+
+ Example usage:
+
+ tld::EventDescriptor eventDescriptor(
+ level,
+ opcode,
+ task,
+ keywords);
+ EVENT_DATA_DESCRIPTOR pDataDescriptors[cFields + 2];
+ for (int i = 0; i < cFields; i++)
+ {
+ EventDataDescCreate(&pDataDescriptors[i + 2], ...);
+ }
+ tld::WriteEvent(
+ hProvider,
+ eventDescriptor,
+ pProviderMetadata,
+ pEventMetadata,
+ cFields + 2,
+ pDataDescriptors);
+ */
+ inline HRESULT WriteEvent(
+ _In_ REGHANDLE hProvider,
+ _In_ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ _In_opt_ GUID const* pActivityId = 0,
+ _In_opt_ GUID const* pRelatedActivityId = 0);
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ /*
+ function WriteEventEx (low-level API):
+ Calls EventWriteEx. Otherwise works like the WriteEvent function.
+ */
+ inline HRESULT WriteEventEx(
+ _In_ REGHANDLE hProvider,
+ _In_ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ ULONG64 filter = 0,
+ ULONG flags = 0,
+ _In_opt_ GUID const* pActivityId = 0,
+ _In_opt_ GUID const* pRelatedActivityId = 0);
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+ /*
+ function SetInformation (low-level API):
+
+ Wrapper for ETW API EventSetInformation.
+ If TraceLoggingDynamic.cpp was compiled to require Win8 or later (as
+ determined by WINVER), this directly calls EventSetInformation. Otherwise,
+ this attempts to dynamically load the EventSetInformation API via
+ GetModuleHandleExW.
+
+ The behavior of this function (e.g. to override the WINVER check) can be
+ adjusted by setting the TLD_HAVE_EVENT_SET_INFORMATION macro as described
+ below.
+ */
+ inline HRESULT SetInformation(
+ _In_ REGHANDLE hProvider,
+ EVENT_INFO_CLASS informationClass,
+ _In_reads_bytes_opt_(cbInformation) void* pbInformation,
+ ULONG cbInformation);
+
+ /*
+ function GetGuidForName (low-level API):
+
+ Hashes a provider name to generate a GUID. Uses the same GUID generation
+ algorithm as System.Diagnostics.Tracing.EventSource (from .NET) and
+ Windows.Foundation.Diagnostics.LoggingChannel (from Windows Runtime).
+ */
+ inline GUID GetGuidForName(
+ _In_z_ char const* szUtf8Name);
+
+ /*
+ function GetGuidForName (low-level API):
+
+ Hashes a provider name to generate a GUID. Uses the same GUID generation
+ algorithm as .NET System.Diagnostics.Tracing.EventSource and Windows
+ Runtime Windows.Foundation.Diagnostics.LoggingChannel.
+ */
+ inline GUID GetGuidForName(
+ _In_z_ wchar_t const* szUtf16Name);
+
+ /*
+ function PushBackAsUtf8 (low-level API):
+
+ Transcodes a NUL-terminated UTF-16LE string to a NUL-terminated UTF-8
+ string. Intended for use when appending provider/event/field names to
+ metadata buffers.
+ */
+ template<class ByteBufferTy>
+ void PushBackAsUtf8(
+ ByteBufferTy& buffer,
+ _In_z_ wchar_t const* szUtf16);
+
+#pragma endregion
+
+ namespace detail
+ {
+#pragma region Internal macros
+
+#define _tld_MAKE_TYPE(inType, outType) \
+ static_cast<::tld::Type>((inType) | (static_cast<int>(outType) << 8))
+
+#ifndef TLD_DEBUG
+ #if (DBG || defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG)
+ #define TLD_DEBUG 1
+ #else // DBG
+ #define TLD_DEBUG 0
+ #endif // DBG
+#endif // TLD_DEBUG
+
+#ifndef _tld_ASSERT
+ #if TLD_DEBUG
+ #define _tld_ASSERT(exp, str) ((void)(!(exp) ? (__annotation(L"Debug", L"AssertFail", L"TraceLogging: " L#exp L" : " L##str), DbgRaiseAssertionFailure(), 0) : 0))
+ #else // TLD_DEBUG
+ #define _tld_ASSERT(exp, str) ((void)0)
+ #endif // TLD_DEBUG
+#endif // _tld_ASSERT
+
+#pragma endregion
+
+#pragma region MetadataBuilderBase
+
+ template<class ByteBufferTy>
+ class MetadataBuilderBase
+ {
+ ByteBufferTy& m_buffer;
+
+ MetadataBuilderBase(MetadataBuilderBase const&); // = delete
+ void operator=(MetadataBuilderBase const&); // = delete
+
+ protected:
+
+ explicit MetadataBuilderBase(ByteBufferTy& buffer)
+ : m_buffer(buffer)
+ {
+ return;
+ }
+
+ void BaseBegin()
+ {
+ m_buffer.clear();
+ AddU16(0); // Metadata size filled in by BaseEnd
+ }
+
+ bool BaseEnd()
+ {
+ auto size = m_buffer.size();
+ _tld_ASSERT(2 <= size, "Begin was not called");
+ SetU16(0, static_cast<UINT16>(size));
+ return size < 32768;
+ }
+
+ void AddBytes(
+ _In_reads_bytes_(cb) void const* p,
+ unsigned cb)
+ {
+ auto pb = static_cast<UINT8 const*>(p);
+ for (unsigned i = 0; i != cb; i++)
+ {
+ m_buffer.push_back(pb[i]);
+ }
+ }
+
+ void AddString(
+ _In_z_ char const* szUtf8)
+ {
+ for (unsigned i = 0;; i++)
+ {
+ m_buffer.push_back(szUtf8[i]);
+ if (szUtf8[i] == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ void AddString(
+ _In_z_ wchar_t const* szUtf16)
+ {
+ PushBackAsUtf8(m_buffer, szUtf16);
+ }
+
+ void AddU8(
+ UINT8 val)
+ {
+ m_buffer.push_back(val);;
+ }
+
+ void AddU16(
+ UINT16 val)
+ {
+ m_buffer.push_back(static_cast<UINT8>(val));
+ m_buffer.push_back(static_cast<UINT8>(val >> 8));
+ }
+
+ UINT32 GetBookmark()
+ {
+ return static_cast<UINT32>(m_buffer.size());
+ }
+
+ void SetU8(
+ UINT32 bookmark,
+ UINT8 val)
+ {
+ _tld_ASSERT(bookmark < m_buffer.size(), "bookmark out of range");
+ m_buffer[bookmark] = val;
+ }
+
+ void SetU16(
+ UINT32 bookmark,
+ UINT16 val)
+ {
+ _tld_ASSERT(1 <= bookmark + 1, "bookmark out of range");
+ SetU8(bookmark + 0, static_cast<UINT8>(val));
+ SetU8(bookmark + 1, static_cast<UINT8>(val >> 8));
+ }
+
+ void IncrementU7(
+ UINT32 bookmark)
+ {
+ _tld_ASSERT(bookmark < m_buffer.size(), "bookmark out of range");
+ UINT8 result;
+ result = ++m_buffer[bookmark];
+ _tld_ASSERT((result & 0x7f) != 0, "too many fields in struct");
+ }
+
+ public:
+
+ ByteBufferTy& GetBuffer()
+ {
+ return m_buffer;
+ }
+
+ ByteBufferTy const& GetBuffer() const
+ {
+ return m_buffer;
+ }
+ };
+
+#pragma endregion
+
+#pragma region Sha1ForNonSecretPurposes
+
+ /*
+ Implements the SHA1 hashing algorithm. Note that this implementation is
+ for hashing public information. Do not use this code to hash private data,
+ as this implementation does not take any steps to avoid information
+ disclosure (i.e. does not scrub its buffers).
+ */
+ class Sha1ForNonSecretPurposes
+ {
+ Sha1ForNonSecretPurposes(Sha1ForNonSecretPurposes const&); // = delete
+ void operator=(Sha1ForNonSecretPurposes const&); // = delete
+
+ UINT64 m_length; // Total message length in bits
+ unsigned m_pos; // Length of current chunk in bytes
+ UINT32 m_results[5];
+ UINT32 m_w[80]; // Workspace
+
+ public:
+
+ Sha1ForNonSecretPurposes()
+ : m_length()
+ , m_pos()
+ {
+ m_results[0] = 0x67452301;
+ m_results[1] = 0xEFCDAB89;
+ m_results[2] = 0x98BADCFE;
+ m_results[3] = 0x10325476;
+ m_results[4] = 0xC3D2E1F0;
+ }
+
+ void Append(
+ _In_reads_bytes_(cbInput) void const* pInput,
+ unsigned cbInput)
+ {
+ for (unsigned i = 0; i != cbInput; i++)
+ {
+ Append(static_cast<UINT8 const*>(pInput)[i]);
+ }
+ }
+
+ void Append(UINT8 input)
+ {
+ reinterpret_cast<UINT8*>(m_w)[m_pos++] = input;
+ if (64 == m_pos)
+ {
+ Drain();
+ }
+ }
+
+ _Ret_bytecount_(20) UINT8* Finish()
+ {
+ UINT64 totalBitCount = m_length + 8 * m_pos;
+ Append(0x80);
+ while (m_pos != 56)
+ {
+ Append(0x00);
+ }
+
+ *reinterpret_cast<UINT64*>(m_w + 14) = _byteswap_uint64(totalBitCount);
+ Drain();
+
+ for (unsigned i = 0; i != 5; i++)
+ {
+ m_results[i] = _byteswap_ulong(m_results[i]);
+ }
+
+ return reinterpret_cast<UINT8*>(m_results);
+ }
+
+ private:
+
+ void Drain()
+ {
+ for (unsigned i = 0; i != 16; i++)
+ {
+ m_w[i] = _byteswap_ulong(m_w[i]);
+ }
+
+ for (unsigned i = 16; i != 80; i++)
+ {
+ m_w[i] = _rotl((m_w[i - 3] ^ m_w[i - 8] ^ m_w[i - 14] ^ m_w[i - 16]), 1);
+ }
+
+ UINT32 a = m_results[0];
+ UINT32 b = m_results[1];
+ UINT32 c = m_results[2];
+ UINT32 d = m_results[3];
+ UINT32 e = m_results[4];
+
+ for (unsigned i = 0; i != 20; i++)
+ {
+ UINT32 const k = 0x5A827999;
+ UINT32 f = (b & c) | ((~b) & d);
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ for (unsigned i = 20; i != 40; i++)
+ {
+ const UINT32 k = 0x6ED9EBA1;
+ UINT32 f = b ^ c ^ d;
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ for (unsigned i = 40; i != 60; i++)
+ {
+ const UINT32 k = 0x8F1BBCDC;
+ UINT32 f = (b & c) | (b & d) | (c & d);
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ for (unsigned i = 60; i != 80; i++)
+ {
+ const UINT32 k = 0xCA62C1D6;
+ UINT32 f = b ^ c ^ d;
+ UINT32 temp = _rotl(a, 5) + f + e + k + m_w[i]; e = d; d = c; c = _rotl(b, 30); b = a; a = temp;
+ }
+
+ m_results[0] += a;
+ m_results[1] += b;
+ m_results[2] += c;
+ m_results[3] += d;
+ m_results[4] += e;
+
+ m_length += 512; // 64 bytes == 512 bits
+ m_pos = 0;
+ }
+ };
+
+#pragma endregion
+
+#pragma region CopyUtfCodePoint
+
+ /*
+ Transcodes one code point from UTF-8 to UTF-16LE.
+
+ Note that this function requires the input buffer to be nul-terminated.
+ This function may try to read several bytes from the input buffer, and the
+ nul-termination is required to ensure that it doesn't read off the end of
+ the valid buffer when decoding what appears to be a multi-byte code point.
+
+ The UTF-8 validation is very permissive -- anything that is not valid
+ UTF-8 is treated as if it were ISO-8859-1 (i.e. the BYTE value is cast
+ to wchar_t and assumed to be a valid Unicode code point). This usually
+ works well - it has a reasonable probability of doing what the developer
+ expects even when the input is CP1252/Latin1 instead of UTF-8, and doesn't
+ fail too badly when it doesn't do what the developer expects.
+
+ pchUtf16Output:
+ Buffer that receives a single code point, encoded in UTF-16-LE as one or
+ two 16-bit wchar_t values.
+
+ pszUtf8Input:
+ Pointer into a nul-terminated UTF-8 byte sequence. On entry, this points
+ to the next char to be consumed. This function will advance the pointer
+ past the consumed data.
+
+ returns:
+ The number of 16-bit wchar_t values added to pchUtf16Output (one or two).
+ */
+ static unsigned CopyUtfCodePoint(
+ _Out_writes_to_(2, return) wchar_t* pchUtf16Output,
+ char const*& pszUtf8Input)
+ {
+ unsigned cchOutput = 1;
+ unsigned ch = static_cast<unsigned char>(*pszUtf8Input);
+ pszUtf8Input += 1;
+ if (ch <= 0xbf)
+ {
+ // 0x01..0x7f: ASCII - pass through.
+ // 0x80..0xbf: Invalid - pass through.
+ }
+ else if ((pszUtf8Input[0] & 0xc0) != 0x80)
+ {
+ // Invalid trail byte - pass through.
+ }
+ else if (ch <= 0xdf)
+ {
+ // 0xc0..0xdf: Two-byte encoding for 0x0080..0x07ff.
+ unsigned ch2 = ((ch & 0x1f) << 6)
+ | (pszUtf8Input[0] & 0x3f);
+ if (0x0080 <= ch2)
+ {
+ ch = ch2;
+ pszUtf8Input += 1;
+ }
+ }
+ else if ((pszUtf8Input[1] & 0xc0) != 0x80)
+ {
+ // Invalid trail byte - pass through.
+ }
+ else if (ch <= 0xef)
+ {
+ // 0xe0..0xef: Three-byte encoding for 0x0800..0xffff.
+ unsigned ch2 = ((ch & 0x0f) << 12)
+ | ((pszUtf8Input[0] & 0x3f) << 6)
+ | (pszUtf8Input[1] & 0x3f);
+ if (0x0800 <= ch2)
+ {
+ ch = ch2;
+ pszUtf8Input += 2;
+ }
+ }
+ else if ((pszUtf8Input[2] & 0xc0) != 0x80)
+ {
+ // Invalid trail byte - pass through.
+ }
+ else if (ch <= 0xf4)
+ {
+ // 0xf0..0xf4: Four-byte encoding for 0x010000..0x10ffff.
+ unsigned ch2 = ((ch & 0x07) << 18)
+ | ((pszUtf8Input[0] & 0x3f) << 12)
+ | ((pszUtf8Input[1] & 0x3f) << 6)
+ | (pszUtf8Input[2] & 0x3f);
+ ch2 -= 0x10000;
+ if (ch2 <= 0xfffff)
+ {
+ // Decode into surrogate pair (2 wchar_t units)
+ pchUtf16Output[1] = static_cast<wchar_t>(0xdc00 | (ch2 & 0x3ff));
+ ch = 0xd800 | (ch2 >> 10);
+ pszUtf8Input += 3;
+ cchOutput = 2;
+ }
+ }
+
+ pchUtf16Output[0] = static_cast<wchar_t>(ch);
+ return cchOutput;
+ }
+
+ /*
+ Copies one code point of UTF-16LE data.
+
+ Note that this function requires the input buffer to be nul-terminated.
+ This function may try to read several words from the input buffer, and the
+ nul-termination is required to ensure that it doesn't read off the end of
+ the valid buffer when copying what appears to be a surrogate pair.
+
+ pchUtf16Output:
+ Buffer that receives a single code point, encoded in UTF-16-LE as one or
+ two 16-bit wchar_t values.
+
+ pszUtf16Input:
+ Pointer into a nul-terminated UTF-16-LE wchar_t sequence. On entry, this
+ points to the next wchar_t to be consumed. This function will advance the
+ pointer past the consumed data.
+
+ returns:
+ The number of 16-bit wchar_t values added to pchUtf16Output (one or two).
+ */
+ static unsigned CopyUtfCodePoint(
+ _Out_writes_to_(2, return) wchar_t* pchUtf16Output,
+ wchar_t const*& pszUtf16Input)
+ {
+ unsigned cchOutput = 1;
+ wchar_t ch = *pszUtf16Input;
+ pszUtf16Input += 1;
+ if (0xd800 <= ch && ch < 0xdc00 &&
+ 0xdc00 <= *pszUtf16Input && *pszUtf16Input < 0xe000)
+ {
+ pchUtf16Output[1] = *pszUtf16Input;
+ pszUtf16Input += 1;
+ cchOutput = 2;
+ }
+ pchUtf16Output[0] = ch;
+ return cchOutput;
+ }
+
+#pragma endregion
+
+#pragma region GetGuidForNameImpl
+
+ __declspec(selectany) extern UINT32 const NamespaceValue[] = {
+ 0xB22D2C48, 0xC84790C3, 0x151AF887, 0xFB30C1Bf
+ };
+
+ template<class CharTy>
+ GUID GetGuidForNameImpl(
+ _In_z_ CharTy const* szUtfName)
+ {
+ /*
+ Algorithm:
+ szNameUpperBE = ByteSwap(ToUpper(szName));
+ hash = SHA1(namespaceBytes + szNameUpperBE); // Does not include zero-termination in hash.
+ hash[7] = (hash[7] & 0x0F) | 0x50;
+ guid = First16Bytes(hash);
+ */
+
+ WCHAR buffer[16];
+ unsigned const cchBuffer = ARRAYSIZE(buffer);
+ Sha1ForNonSecretPurposes sha1;
+
+ sha1.Append(NamespaceValue, sizeof(NamespaceValue));
+ if (szUtfName[0] != 0)
+ {
+ CharTy const* pszName = szUtfName;
+ unsigned iBuffer = 0;
+ for (;;)
+ {
+ // CopyUtfCodePoint may add up to 2 wchar_t units to buffer.
+ iBuffer += CopyUtfCodePoint(buffer + iBuffer, pszName);
+ _tld_ASSERT(iBuffer <= cchBuffer, "buffer overflow");
+
+ bool const bDone = *pszName == 0;
+ if (bDone || cchBuffer - 1 <= iBuffer)
+ {
+ #pragma warning(suppress: 26035) // string is counted, not nul-terminated.
+ LCMapStringEx(
+ LOCALE_NAME_INVARIANT,
+ LCMAP_UPPERCASE | LCMAP_BYTEREV,
+ buffer,
+ iBuffer,
+ buffer,
+ iBuffer,
+ NULL,
+ NULL,
+ 0);
+ sha1.Append(buffer, iBuffer * 2);
+
+ if (bDone)
+ {
+ break;
+ }
+
+ iBuffer = 0;
+ }
+ }
+ }
+
+ UINT8* pSha1 = sha1.Finish();
+
+ // Set high 4 bits of octet 7 to 5, as per RFC 4122
+ pSha1[7] = (pSha1[7] & 0x0f) | 0x50;
+
+ return *reinterpret_cast<GUID*>(pSha1);
+ }
+
+#pragma endregion
+ }
+ // namespace detail
+
+#pragma region Enumerations
+
+ enum InType : UINT8
+ {
+ /*
+ A field with no value (0-length payload).
+ Arrays of InTypeNull are illegal.
+ */
+ InTypeNull,
+
+ /*
+ A nul-terminated CHAR16 string.
+ Useful OutTypes: Xml, Json.
+ */
+ InTypeUnicodeString,
+
+ /*
+ A nul-terminated CHAR8 string.
+ Useful OutTypes: Xml, Json, Utf8.
+ */
+ InTypeAnsiString,
+
+ /*
+ INT8.
+ */
+ InTypeInt8,
+
+ /*
+ UINT8.
+ Useful OutTypes: String, Boolean, Hex.
+ */
+ InTypeUInt8,
+
+ /*
+ INT16.
+ */
+ InTypeInt16,
+
+ /*
+ UINT16.
+ Useful OutTypes: String, Hex, Port.
+ */
+ InTypeUInt16,
+
+ /*
+ INT32.
+ Useful OutTypes: HResult.
+ */
+ InTypeInt32,
+
+ /*
+ UINT32.
+ Useful OutTypes: Pid, Tid, IPv4, Win32Error, NTStatus.
+ */
+ InTypeUInt32,
+
+ /*
+ INT64.
+ */
+ InTypeInt64,
+
+ /*
+ UINT64.
+ */
+ InTypeUInt64,
+
+ /*
+ FLOAT.
+ */
+ InTypeFloat,
+
+ /*
+ DOUBLE.
+ */
+ InTypeDouble,
+
+ /*
+ BOOL.
+ */
+ InTypeBool32,
+
+ /*
+ UINT16 byte-count followed by binary data.
+ Useful OutTypes: IPv6, SocketAddress.
+ Arrays of InTypeBinary are not supported. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ InTypeBinary,
+
+ /*
+ GUID.
+ */
+ InTypeGuid,
+
+ /*
+ Not supported. Use InTypePointer. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ InTypePointer_PlatformSpecific,
+
+ /*
+ FILETIME.
+ */
+ InTypeFileTime,
+
+ /*
+ SYSTEMTIME.
+ */
+ InTypeSystemTime,
+
+ /*
+ SID. Size is determined from the number of subauthorities.
+ */
+ InTypeSid,
+
+ /*
+ INT32.
+ */
+ InTypeHexInt32,
+
+ /*
+ INT64.
+ */
+ InTypeHexInt64,
+
+ /*
+ UINT16 byte-count followed by UTF-16 data.
+ */
+ InTypeCountedString,
+
+ /*
+ UINT16 byte-count followed by MBCS data.
+ */
+ InTypeCountedAnsiString,
+
+ /*
+ This field has no value by itself, but it causes the decoder to treat
+ the next N fields as part of this field. The value of N is taken from
+ the field's OutType.
+
+ Note that it is valid to have an array of structs. The array length
+ will apply to the entire group of fields contained in the array.
+
+ The Struct type is special. Do not use it directly. Use helper APIs to
+ create nested structures.
+ */
+ InTypeStruct,
+
+ /*
+ INT_PTR.
+ InTypeIntPtr is an alias for either InTypeInt32 or InTypeInt64.
+ */
+ InTypeIntPtr = sizeof(void*) == 8 ? InTypeInt64 : InTypeInt32,
+
+ /*
+ UINT_PTR.
+ InTypeUIntPtr is an alias for either InTypeUInt32 or InTypeUInt64.
+ */
+ InTypeUIntPtr = sizeof(void*) == 8 ? InTypeUInt64 : InTypeUInt32,
+
+ /*
+ LPVOID.
+ InTypePointer is an alias for either InTypeHexInt32 or InTypeHexInt64.
+ */
+ InTypePointer = sizeof(void*) == 8 ? InTypeHexInt64 : InTypeHexInt32,
+
+ /*
+ InType must fit in 5 bits.
+ */
+ InTypeMask = 31
+ };
+
+ enum OutType : UINT8
+ {
+ OutTypeDefault = 0x00,
+ OutTypeNoPrint = 0x01,
+ OutTypeString = 0x02, // affects 8-bit and 16-bit integral types.
+ OutTypeBoolean = 0x03, // affects 8-bit and 32-bit integral types.
+ OutTypeHex = 0x04, // affects integral types.
+ OutTypePid = 0x05, // affects 32-bit integral types.
+ OutTypeTid = 0x06, // affects 32-bit integral types.
+ OutTypePort = 0x07, // affects 16-bit integral types.
+ OutTypeIPv4 = 0x08, // affects 32-bit integral types.
+ OutTypeIPv6 = 0x09, // affects arrays of 8-bit integral types, e.g. UInt8[].
+ OutTypeSocketAddress = 0x0a, // affects arrays of 8-bit integral types, e.g. UInt8[].
+ OutTypeXml = 0x0b, // affects strings; affects arrays of 8-bit and 16-bit integral types.
+ OutTypeJson = 0x0c, // affects strings; affects arrays of 8-bit and 16-bit integral types.
+ OutTypeWin32Error = 0x0d,// affects 32-bit integral types.
+ OutTypeNTStatus = 0x0e, // affects 32-bit integral types.
+ OutTypeHResult = 0x0f, // affects 32-bit integral types.
+ OutTypeFileTime = 0x10, // affects 64-bit integral types.
+ OutTypeSigned = 0x11, // affects integral types.
+ OutTypeUnsigned = 0x12, // affects integral types.
+ OutTypeDateTimeCultureInsensitive = 0x21, // affects FileTime, SystemTime.
+ OutTypeUtf8 = 0x23, // affects AnsiString types.
+ OutTypePkcs7WithTypeInfo = 0x24, // affects binary types.
+ OutTypeCodePointer = 0x25, // affects UInt32, UInt64, HexInt32, HexInt64.
+ OutTypeDateTimeUtc = 0x26, // affects FileTime, SystemTime.
+ OutTypeMask = 0x7f // OutType must fit into 7 bits.
+ };
+
+ enum Type : UINT16
+ {
+ /*
+ Field with no data (empty). 0-length payload.
+ Can only be used on scalar fields. Illegal for arrays.
+ NOTE: Not well-supported by decoders.
+ */
+ TypeNone = InTypeNull,
+
+ /*
+ Encoding assumes 0-terminated CHAR16 string.
+ Formatting treats as UTF-16LE string.
+ */
+ TypeUtf16String = InTypeUnicodeString,
+
+ /*
+ Encoding assumes 0-terminated CHAR8 string.
+ Formatting treats as MBCS string.
+ */
+ TypeMbcsString = InTypeAnsiString,
+
+ /*
+ Encoding assumes nul-terminated CHAR8 string.
+ Formatting treats as UTF-8 string.
+ */
+ TypeUtf8String = _tld_MAKE_TYPE(InTypeAnsiString, OutTypeUtf8),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt8 = InTypeInt8,
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt8 = InTypeUInt8,
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt16 = InTypeInt16,
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt16 = InTypeUInt16,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt32 = InTypeInt32,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt32 = InTypeUInt32,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as signed integer.
+ */
+ TypeInt64 = InTypeInt64,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as unsigned integer.
+ */
+ TypeUInt64 = InTypeUInt64,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as Float32.
+ */
+ TypeFloat = InTypeFloat,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as Float64.
+ */
+ TypeDouble = InTypeDouble,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as Boolean.
+ */
+ TypeBool32 = InTypeBool32,
+
+ /*
+ Encoding assumes 2-byte length followed by binary data.
+ Formatting treats as binary blob.
+ Arrays of TypeBinary are not supported. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ TypeBinary = InTypeBinary,
+
+ /*
+ Encoding assumes 16-byte value.
+ Formatting treats as GUID.
+ */
+ TypeGuid = InTypeGuid,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as FILETIME.
+ */
+ TypeFileTime = InTypeFileTime,
+
+ /*
+ Encoding assumes 16-byte value.
+ Formatting treats as SYSTEMTIME.
+ */
+ TypeSystemTime = InTypeSystemTime,
+
+ /*
+ Encoding assumes 16-byte value.
+ Formatting treats as UTC SYSTEMTIME.
+ */
+ TypeSystemTimeUtc = _tld_MAKE_TYPE(InTypeSystemTime, OutTypeDateTimeUtc),
+
+ /*
+ Encoding assumes SID. Length is determined from number of subauthorities.
+ Formatting treats as a SID.
+ */
+ TypeSid = InTypeSid,
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt32 = InTypeHexInt32,
+
+ /*
+ Encoding assumes 8-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt64 = InTypeHexInt64,
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR16 data.
+ Formatting treats as UTF-16LE string.
+ */
+ TypeCountedUtf16String = InTypeCountedString,
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as MBCS string.
+ */
+ TypeCountedMbcsString = InTypeCountedAnsiString,
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as UTF-8 string.
+ */
+ TypeCountedUtf8String = _tld_MAKE_TYPE(InTypeCountedAnsiString, OutTypeUtf8),
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as signed integer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is an alias for TypeInt32 when compiled for a 32-bit target.
+ This is an alias for TypeInt64 when compiled for a 64-bit target.
+ */
+ TypeIntPtr = InTypeIntPtr,
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as unsigned integer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is an alias for TypeUInt32 when compiled for a 32-bit target.
+ This is an alias for TypeUInt64 when compiled for a 64-bit target.
+ */
+ TypeUIntPtr = InTypeUIntPtr,
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as hexadecimal unsigned integer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is an alias for TypeHexInt32 when compiled for a 32-bit target.
+ This is an alias for TypeHexInt64 when compiled for a 64-bit target.
+ */
+ TypePointer = InTypePointer,
+
+ /*
+ Encoding assumes pointer-sized value.
+ Formatting treats as code pointer.
+ Note: the value of this enum is different on 32-bit and 64-bit systems.
+ This is a subtype of TypeHexInt32 when compiled for a 32-bit target.
+ This is a subtype of TypeHexInt64 when compiled for a 64-bit target.
+ */
+ TypeCodePointer = _tld_MAKE_TYPE(InTypePointer, OutTypeCodePointer),
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as UTF-16LE character.
+ */
+ TypeChar16 = _tld_MAKE_TYPE(InTypeUInt16, OutTypeString),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as ANSI character.
+ */
+ TypeChar8 = _tld_MAKE_TYPE(InTypeUInt8, OutTypeString),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as Boolean.
+ */
+ TypeBool8 = _tld_MAKE_TYPE(InTypeUInt8, OutTypeBoolean),
+
+ /*
+ Encoding assumes 1-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt8 = _tld_MAKE_TYPE(InTypeUInt8, OutTypeHex),
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as hexadecimal unsigned integer.
+ */
+ TypeHexInt16 = _tld_MAKE_TYPE(InTypeUInt16, OutTypeHex),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as process identifier.
+ */
+ TypePid = _tld_MAKE_TYPE(InTypeUInt32, OutTypePid),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as thread identifier.
+ */
+ TypeTid = _tld_MAKE_TYPE(InTypeUInt32, OutTypeTid),
+
+ /*
+ Encoding assumes 2-byte value.
+ Formatting treats as big-endian IP port(s).
+ */
+ TypePort = _tld_MAKE_TYPE(InTypeUInt16, OutTypePort),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as IPv4 address.
+ */
+ TypeIPv4 = _tld_MAKE_TYPE(InTypeUInt32, OutTypeIPv4),
+
+ /*
+ Encoding assumes 2-byte length followed by binary data.
+ Formatting treats as IPv6 address.
+ Arrays of TypeIPv6 are not supported. (No error will be reported,
+ but decoding tools will probably not be able to decode them).
+ */
+ TypeIPv6 = _tld_MAKE_TYPE(InTypeBinary, OutTypeIPv6),
+
+ /*
+ Encoding assumes 2-byte length followed by binary data.
+ Formatting treats as SOCKADDR.
+ Arrays of TypeSocketAddress are not supported. (No error will be
+ reported, but decoding tools will probably not be able to decode them).
+ */
+ TypeSocketAddress = _tld_MAKE_TYPE(InTypeBinary, OutTypeSocketAddress),
+
+ /*
+ Encoding assumes nul-terminated CHAR16 string.
+ Formatting treats as UTF-16LE XML.
+ */
+ TypeUtf16Xml = _tld_MAKE_TYPE(InTypeUnicodeString, OutTypeXml),
+
+ /*
+ Encoding assumes nul-terminated CHAR8 string.
+ Formatting treats as UTF-8 XML.
+ */
+ TypeMbcsXml = _tld_MAKE_TYPE(InTypeAnsiString, OutTypeXml),
+
+ /*
+ Encoding assumes nul-terminated CHAR16 string.
+ Formatting treats as UTF-16LE JSON.
+ */
+ TypeUtf16Json = _tld_MAKE_TYPE(InTypeUnicodeString, OutTypeJson),
+
+ /*
+ Encoding assumes nul-terminated CHAR8 string.
+ Formatting treats as UTF-8 JSON.
+ */
+ TypeMbcsJson = _tld_MAKE_TYPE(InTypeAnsiString, OutTypeJson),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR16 data.
+ Formatting treats as UTF-16LE XML.
+ */
+ TypeCountedUtf16Xml = _tld_MAKE_TYPE(InTypeCountedString, OutTypeXml),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as UTF-8 XML.
+ */
+ TypeCountedMbcsXml = _tld_MAKE_TYPE(InTypeCountedAnsiString, OutTypeXml),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR16 data.
+ Formatting treats as UTF-16LE JSON.
+ */
+ TypeCountedUtf16Json = _tld_MAKE_TYPE(InTypeCountedString, OutTypeJson),
+
+ /*
+ Encoding assumes 2-byte bytecount followed by CHAR8 data.
+ Formatting treats as UTF-8 JSON.
+ */
+ TypeCountedMbcsJson = _tld_MAKE_TYPE(InTypeCountedAnsiString, OutTypeJson),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as Win32 error(s).
+ */
+ TypeWin32Error = _tld_MAKE_TYPE(InTypeUInt32, OutTypeWin32Error),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as NTSTATUS(s).
+ */
+ TypeNTStatus = _tld_MAKE_TYPE(InTypeUInt32, OutTypeNTStatus),
+
+ /*
+ Encoding assumes 4-byte value.
+ Formatting treats as HRESULT(s).
+ */
+ TypeHResult = _tld_MAKE_TYPE(InTypeInt32, OutTypeHResult)
+ };
+
+ enum ProviderTraitType : UINT8
+ {
+ ProviderTraitNone = 0, // Invalid value
+ ProviderTraitGroupGuid = 1 // Data is the group GUID.
+ };
+
+#pragma endregion
+
+#pragma region EventDescriptor
+
+ /*
+ Simple wrapper around the ETW EVENT_DESCRIPTOR structure.
+ Provides a constructor to make initialization easier.
+ Can be used anywhere an EVENT_DESCRIPTOR is required.
+
+ Notes:
+ - Channel defaults to 11 (WINEVENT_CHANNEL_TRACELOGGING). Other channels
+ will only work if the provider is running on a TraceLogging-enabled OS
+ (Windows 10 or later, or Windows 7 sp1 with updates, or Windows 8.1 with
+ updates). If the provider is running on an earlier version of Windows and
+ you use a channel other than 11, the events will not decode correctly.
+ - If you are not assigning unique Ids for your events, set both Id and
+ Version to 0.
+ */
+ struct EventDescriptor
+ : EVENT_DESCRIPTOR
+ {
+ explicit EventDescriptor(
+ UCHAR level = 5, // 5 = WINEVENT_LEVEL_VERBOSE
+ ULONGLONG keyword = 0, // 0 = no keywords
+ UCHAR opcode = 0, // 0 = WINEVENT_OPCODE_INFO
+ USHORT task = 0) // 0 = WINEVENT_TASK_NONE
+ {
+ Reset(level, keyword, opcode, task);
+ }
+
+ void Reset(
+ UCHAR level = 5, // 5 = WINEVENT_LEVEL_VERBOSE
+ ULONGLONG keyword = 0, // 0 = no keywords
+ UCHAR opcode = 0, // 0 = WINEVENT_OPCODE_INFO
+ USHORT task = 0) // 0 = WINEVENT_TASK_NONE
+ {
+ Id = 0;
+ Version = 0;
+ Channel = 11; // WINEVENT_CHANNEL_TRACELOGGING
+ Level = level;
+ Opcode = opcode;
+ Task = task;
+ Keyword = keyword;
+ }
+ };
+
+#pragma endregion
+
+#pragma region Support functions
+
+#pragma warning(push)
+#pragma warning(disable: 26018) // PREfast: Potential read overflow. (Analysis is incorrect.)
+
+ template<class ByteBufferTy>
+ void PushBackAsUtf8(
+ ByteBufferTy& buffer,
+ _In_z_ wchar_t const* szUtf16)
+ {
+ for (unsigned i = 0;; i++)
+ {
+ unsigned ch = szUtf16[i];
+ if (ch < 0x80)
+ {
+ buffer.push_back(static_cast<UINT8>(ch));
+ if (ch == 0)
+ {
+ break;
+ }
+ }
+ else if (ch < 0x800)
+ {
+ buffer.push_back(static_cast<UINT8>(((ch >> 6)) | 0xc0));
+ buffer.push_back(static_cast<UINT8>(((ch)& 0x3f) | 0x80));
+ }
+ else if (
+ 0xd800 <= ch && ch < 0xdc00 &&
+ 0xdc00 <= szUtf16[i + 1] && szUtf16[i + 1] < 0xe000)
+ {
+ // Decode surrogate pair.
+ ++i;
+ ch = 0x010000 + (((ch - 0xd800) << 10) | (szUtf16[i] - 0xdc00));
+ buffer.push_back(static_cast<UINT8>(((ch >> 18)) | 0xf0));
+ buffer.push_back(static_cast<UINT8>(((ch >> 12) & 0x3f) | 0x80));
+ buffer.push_back(static_cast<UINT8>(((ch >> 6) & 0x3f) | 0x80));
+ buffer.push_back(static_cast<UINT8>(((ch)& 0x3f) | 0x80));
+ }
+ else
+ {
+ buffer.push_back(static_cast<UINT8>(((ch >> 12)) | 0xe0));
+ buffer.push_back(static_cast<UINT8>(((ch >> 6) & 0x3f) | 0x80));
+ buffer.push_back(static_cast<UINT8>(((ch)& 0x3f) | 0x80));
+ }
+ }
+ }
+
+#pragma warning(pop)
+
+ inline ::tld::Type MakeType(
+ ::tld::InType inType,
+ ::tld::OutType outType)
+ {
+ return _tld_MAKE_TYPE(inType & InTypeMask, outType & OutTypeMask);
+ }
+
+ _Use_decl_annotations_
+ inline GUID GetGuidForName(
+ wchar_t const* szUtf16Name)
+ {
+ return ::tld::detail::GetGuidForNameImpl(szUtf16Name);
+ }
+
+ _Use_decl_annotations_
+ inline GUID GetGuidForName(
+ char const* szUtf8Name)
+ {
+ return ::tld::detail::GetGuidForNameImpl(szUtf8Name);
+ }
+
+ inline HRESULT RegisterProvider(
+ _Out_ REGHANDLE* phProvider,
+ _In_ GUID const* pProviderId,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata,
+ _In_opt_ PENABLECALLBACK pEnableCallback,
+ _In_opt_ void* pEnableCallbackContext)
+ {
+ HRESULT hr;
+ REGHANDLE hProvider = 0;
+ ULONG status = ::EventRegister(
+ pProviderId,
+ pEnableCallback,
+ pEnableCallbackContext,
+ &hProvider);
+ if (status == 0)
+ {
+ auto cbMetadata = *reinterpret_cast<UINT16 const*>(pProviderMetadata);
+ status = tld::SetInformation(
+ hProvider,
+ static_cast<EVENT_INFO_CLASS>(2), // EventProviderSetTraits
+ const_cast<UINT8*>(pProviderMetadata),
+ cbMetadata);
+ hr = S_OK; // Ignore any failures from SetInformation.
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(status);
+ }
+
+ *phProvider = hProvider;
+ return hr;
+ }
+
+ inline HRESULT UnregisterProvider(
+ REGHANDLE hProvider)
+ {
+ ULONG status = ::EventUnregister(hProvider);
+ return HRESULT_FROM_WIN32(status);
+ }
+
+ _Use_decl_annotations_
+ inline HRESULT SetInformation(
+ REGHANDLE hProvider,
+ EVENT_INFO_CLASS informationClass,
+ void* pvInformation,
+ ULONG cbInformation)
+ {
+ ULONG status;
+
+#if TLD_HAVE_EVENT_SET_INFORMATION == 1
+
+#pragma warning(suppress: 6387) // It's ok for pvInformation to be null if cbInformation is 0.
+ status = ::EventSetInformation(
+ hProvider,
+ informationClass,
+ pvInformation,
+ cbInformation);
+
+#elif TLD_HAVE_EVENT_SET_INFORMATION == 2
+
+ HMODULE hEventing;
+ status = ERROR_NOT_SUPPORTED;
+ if (GetModuleHandleExW(0, L"api-ms-win-eventing-provider-l1-1-0", &hEventing) ||
+ GetModuleHandleExW(0, L"advapi32.dll", &hEventing))
+ {
+ typedef ULONG(WINAPI* PFEventSetInformation)(
+ _In_ REGHANDLE RegHandle,
+ _In_ EVENT_INFO_CLASS InformationClass,
+ _In_reads_bytes_opt_(InformationLength) PVOID EventInformation,
+ _In_ ULONG InformationLength);
+ PFEventSetInformation pfEventSetInformation =
+ (PFEventSetInformation)GetProcAddress(hEventing, "EventSetInformation");
+ if (pfEventSetInformation)
+ {
+ status = pfEventSetInformation(
+ hProvider,
+ informationClass,
+ pvInformation,
+ cbInformation);
+ }
+
+ FreeLibrary(hEventing);
+ }
+
+#else // TLD_HAVE_EVENT_SET_INFORMATION == 0
+
+ (void)hProvider;
+ (void)informationClass;
+ (void)pvInformation;
+ (void)cbInformation;
+
+ status = ERROR_NOT_SUPPORTED;
+
+#endif // TLD_HAVE_EVENT_SET_INFORMATION
+
+ return HRESULT_FROM_WIN32(status);
+ }
+
+ _Use_decl_annotations_
+ inline HRESULT WriteEvent(
+ REGHANDLE hProvider,
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ UINT8 const* pProviderMetadata,
+ UINT8 const* pEventMetadata,
+ ULONG cDataDescriptors,
+ EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId)
+ {
+ _tld_ASSERT(3 <= *(UINT16*)pProviderMetadata, "Invalid provider metadata.");
+ _tld_ASSERT(4 <= *(UINT16*)pEventMetadata, "Invalid event metadata.");
+ pDataDescriptors[0].Ptr = (ULONG_PTR)pProviderMetadata;
+ pDataDescriptors[0].Size = *(UINT16*)pProviderMetadata;
+ pDataDescriptors[0].Reserved = 2; // Descriptor contains provider metadata.
+ pDataDescriptors[1].Ptr = (ULONG_PTR)pEventMetadata;
+ pDataDescriptors[1].Size = *(UINT16*)pEventMetadata;
+ pDataDescriptors[1].Reserved = 1; // Descriptor contains event metadata.
+ ULONG status = ::EventWriteTransfer(
+ hProvider,
+ &eventDescriptor,
+ pActivityId,
+ pRelatedActivityId,
+ cDataDescriptors,
+ pDataDescriptors);
+ return HRESULT_FROM_WIN32(status);
+ }
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ _Use_decl_annotations_
+ inline HRESULT WriteEventEx(
+ REGHANDLE hProvider,
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ UINT8 const* pProviderMetadata,
+ UINT8 const* pEventMetadata,
+ ULONG cDataDescriptors,
+ EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ ULONG64 filter,
+ ULONG flags,
+ GUID const* pActivityId,
+ GUID const* pRelatedActivityId)
+ {
+ _tld_ASSERT(3 <= *(UINT16*)pProviderMetadata, "Invalid provider metadata.");
+ _tld_ASSERT(4 <= *(UINT16*)pEventMetadata, "Invalid event metadata.");
+ pDataDescriptors[0].Ptr = (ULONG_PTR)pProviderMetadata;
+ pDataDescriptors[0].Size = *(UINT16*)pProviderMetadata;
+ pDataDescriptors[0].Reserved = 2; // Descriptor contains provider metadata.
+ pDataDescriptors[1].Ptr = (ULONG_PTR)pEventMetadata;
+ pDataDescriptors[1].Size = *(UINT16*)pEventMetadata;
+ pDataDescriptors[1].Reserved = 1; // Descriptor contains event metadata.
+ ULONG status = ::EventWriteEx(
+ hProvider,
+ &eventDescriptor,
+ filter,
+ flags,
+ pActivityId,
+ pRelatedActivityId,
+ cDataDescriptors,
+ pDataDescriptors);
+ return HRESULT_FROM_WIN32(status);
+ }
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+#pragma endregion
+
+#pragma region ByteArrayWrapper
+
+ class ByteArrayWrapper
+ {
+ UINT8* m_pb;
+ UINT32 m_cbMax;
+ UINT32 m_cbCur;
+
+ public:
+
+ ByteArrayWrapper()
+ {
+ reset();
+ }
+
+ ByteArrayWrapper(
+ _Out_writes_(cbMax) UINT8* pb,
+ UINT32 cbMax)
+ {
+ reset(pb, cbMax);
+ }
+
+ void reset()
+ {
+ m_pb = 0;
+ m_cbMax = 0;
+ m_cbCur = 0;
+ }
+
+ void reset(
+ _Inout_updates_bytes_(cbMax) UINT8* pb,
+ UINT32 cbMax)
+ {
+ m_pb = pb;
+ m_cbMax = cbMax;
+ m_cbCur = 0;
+ }
+
+ void clear()
+ {
+ m_cbCur = 0;
+ }
+
+ UINT32 size() const
+ {
+ return m_cbCur;
+ }
+
+ BYTE const& front() const
+ {
+ return *m_pb;
+ }
+
+ BYTE const* data() const
+ {
+ return m_pb;
+ }
+
+ UINT32 capacity() const
+ {
+ return m_cbMax;
+ }
+
+ void push_back(UINT8 val)
+ {
+ if (m_cbCur < m_cbMax)
+ {
+ m_pb[m_cbCur] = val;
+ }
+
+ m_cbCur += 1;
+ }
+
+ UINT8& operator[](UINT32 index)
+ {
+ static UINT8 dummy;
+ return index < m_cbMax
+ ? m_pb[index]
+ : dummy;
+ }
+ };
+
+#pragma endregion
+
+#pragma region ProviderMetadataBuilder
+
+ template<class ByteBufferTy>
+ class ProviderMetadataBuilder
+ : public detail::MetadataBuilderBase<ByteBufferTy>
+ {
+ /*
+ ProviderMetadata =
+ Size : UINT16; // Size counts the whole data structure, including the Size field.
+ Name : Nul-terminated UTF-8.
+ Traits[]: ProviderTrait (parse until you have consumed Size bytes)
+
+ ProviderTrait =
+ TraitSize: UINT16;
+ TraitType: UINT8;
+ TraitData: UINT8[TraitSize - 3];
+ */
+
+ public:
+
+ explicit ProviderMetadataBuilder(ByteBufferTy& buffer)
+ : detail::MetadataBuilderBase<ByteBufferTy>(buffer)
+ {
+ return;
+ }
+
+ template<class CharTy>
+ void Begin(
+ _In_z_ CharTy const* szUtfProviderName)
+ {
+ this->BaseBegin();
+ this->AddString(szUtfProviderName);
+ }
+
+ void AddTrait(
+ ProviderTraitType type,
+ _In_reads_bytes_(cbData) void const* pData,
+ unsigned cbData)
+ {
+ this->AddU16(static_cast<UINT16>(cbData + 3));
+ this->AddU8(static_cast<UINT8>(type));
+ this->AddBytes(pData, cbData);
+ }
+
+ bool End()
+ {
+ return this->BaseEnd();
+ }
+ };
+
+#pragma endregion
+
+#pragma region EventMetadataBuilder
+
+ template<class ByteBufferTy>
+ class EventMetadataBuilder
+ : public detail::MetadataBuilderBase<ByteBufferTy>
+ {
+ /*
+ EventMetadata =
+ Size : UINT16; // Size counts the whole data structure, including the Size field.
+ Tags[] : UINT8 (read bytes until you encounter a byte with high bit unset);
+ Name : Nul-terminated UTF-8;
+ Fields[]: FieldMetadata (parse until you get to end of EventMetadata);
+
+ FieldMetadata =
+ Name : Nul-terminated UTF-8.
+ InMeta : UINT8.
+ OutMeta : UINT8 (only present if InMeta & InMetaChain);
+ Tags[] : UINT8 (only present if OutMeta & OutMetaChain, read bytes until you encounter a byte with high bit unset);
+ Ccount : UINT16 (only present if InMeta & InMetaCountMask == InMetaCcount);
+ CbSchema: UINT16 (only present if InMeta & InMetaCountMask == InMetaCustom);
+ Schema : UINT8[CbSchema]
+ */
+
+ static const UINT8 InMetaScalar = 0;
+ static const UINT8 InMetaCcount = 32;
+ static const UINT8 InMetaVcount = 64;
+ static const UINT8 InMetaCustom = 96;
+ static const UINT8 InMetaCountMask = 96;
+ static const UINT8 InMetaChain = 128;
+ static const UINT8 OutMetaChain = 128;
+
+ UINT32 const m_bookmark;
+
+ EventMetadataBuilder(
+ ByteBufferTy& buffer,
+ UINT32 bookmark)
+ : detail::MetadataBuilderBase<ByteBufferTy>(buffer)
+ , m_bookmark(bookmark)
+ {
+ return;
+ }
+
+ void AddTags(UINT32 tags)
+ {
+ _tld_ASSERT(0 == (tags & 0xf0000000), "Tags only supports 28-bit values.");
+ for (;;)
+ {
+ auto val = static_cast<UINT8>(tags >> 21);
+ if ((tags & 0x1fffff) == 0)
+ {
+ this->AddU8(val & 0x7f);
+ break;
+ }
+
+ this->AddU8(val | 0x80);
+ tags = tags << 7;
+ }
+ }
+
+ void AddFieldInfo(UINT8 arity, Type type, UINT32 tags)
+ {
+ _tld_ASSERT((type & InTypeMask) == (type & 0xff), "InType out of range");
+ _tld_ASSERT((type & _tld_MAKE_TYPE(0, OutTypeMask)) == (Type)(type & 0xffffff00), "OutType out of range");
+
+ UINT8 inMeta = arity | static_cast<UINT8>(type);
+ UINT8 outMeta = static_cast<UINT8>(type >> 8);
+ if (tags != 0)
+ {
+ this->AddU8(static_cast<UINT8>(inMeta | InMetaChain));
+ this->AddU8(static_cast<UINT8>(outMeta | OutMetaChain));
+ AddTags(tags);
+ }
+ else if (outMeta != 0)
+ {
+ this->AddU8(static_cast<UINT8>(inMeta | InMetaChain));
+ this->AddU8(static_cast<UINT8>(outMeta));
+ }
+ else
+ {
+ this->AddU8(inMeta);
+ }
+
+ if (m_bookmark != 0)
+ {
+ this->IncrementU7(m_bookmark);
+ }
+ }
+
+ UINT32 AddStructInfo(UINT8 arity, UINT32 tags)
+ {
+ UINT8 inMeta = arity | InTypeStruct;
+ this->AddU8(static_cast<UINT8>(inMeta | InMetaChain));
+ UINT32 bookmark = this->GetBookmark();
+ if (tags == 0)
+ {
+ this->AddU8(0);
+ }
+ else
+ {
+ this->AddU8(OutMetaChain);
+ AddTags(tags);
+ }
+
+ if (m_bookmark != 0)
+ {
+ this->IncrementU7(m_bookmark);
+ }
+
+ return bookmark;
+ }
+
+ public:
+
+ explicit EventMetadataBuilder(
+ ByteBufferTy& buffer)
+ : detail::MetadataBuilderBase<ByteBufferTy>(buffer)
+ , m_bookmark(0)
+ {
+ return;
+ }
+
+ EventMetadataBuilder(EventMetadataBuilder& other)
+ : detail::MetadataBuilderBase<ByteBufferTy>(other.GetBuffer())
+ , m_bookmark(other.m_bookmark)
+ {
+ return;
+ }
+
+ EventMetadataBuilder(EventMetadataBuilder&& other)
+ : detail::MetadataBuilderBase<ByteBufferTy>(other.GetBuffer())
+ , m_bookmark(other.m_bookmark)
+ {
+ return;
+ }
+
+ template<class CharTy>
+ void Begin(
+ _In_z_ CharTy const* szUtfEventName,
+ UINT32 eventTags = 0)
+ {
+ _tld_ASSERT(m_bookmark == 0, "Must not call Begin on a struct builder");
+ this->BaseBegin();
+ AddTags(eventTags);
+ this->AddString(szUtfEventName);
+ }
+
+ bool End()
+ {
+ _tld_ASSERT(m_bookmark == 0, "Must not call End on a struct builder");
+ return this->BaseEnd();
+ }
+
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ EventMetadataBuilder AddStruct(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaScalar, fieldTags);
+ return EventMetadataBuilder(this->GetBuffer(), bookmark);
+ }
+
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ UINT32 AddStructRaw(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaScalar, fieldTags);
+ return bookmark;
+ }
+
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ EventMetadataBuilder AddStructArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaVcount, fieldTags);
+ return EventMetadataBuilder(this->GetBuffer(), bookmark);
+ }
+
+ // Note: Do not use 0 for itemCount.
+ // Note: Do not create structs with 0 fields.
+ template<class CharTy>
+ EventMetadataBuilder AddStructFixedArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT16 itemCount,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfStructName);
+ auto bookmark = AddStructInfo(InMetaCcount, fieldTags);
+ this->AddU16(itemCount);
+ return EventMetadataBuilder(this->GetBuffer(), bookmark);
+ }
+
+ template<class CharTy>
+ void AddField(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaScalar, type, fieldTags);
+ }
+
+ template<class CharTy>
+ void AddFieldArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaVcount, type, fieldTags);
+ }
+
+ // Note: Do not use 0 for itemCount.
+ template<class CharTy>
+ void AddFieldFixedArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ UINT16 itemCount,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaCcount, type, fieldTags);
+ this->AddU16(itemCount);
+ }
+
+ template<class CharTy>
+ void AddFieldCustom(
+ _In_z_ CharTy const* szUtfFieldName,
+ _In_reads_bytes_(cbSchema) void const* pSchema,
+ UINT16 cbSchema,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ this->AddString(szUtfFieldName);
+ AddFieldInfo(InMetaCustom, type, fieldTags);
+ this->AddU16(cbSchema);
+ auto pbSchema = static_cast<UINT8 const*>(pSchema);
+ for (UINT32 i = 0; i != cbSchema; i++)
+ {
+ this->AddU8(pbSchema[i]);
+ }
+ }
+ };
+
+#pragma endregion
+
+#pragma region EventDataBuilder
+
+ template<class ByteBufferTy>
+ class EventDataBuilder
+ {
+ void operator=(EventDataBuilder const&); // = delete
+ ByteBufferTy& m_buffer;
+
+ public:
+
+ explicit EventDataBuilder(ByteBufferTy& buffer)
+ : m_buffer(buffer)
+ {
+ return;
+ }
+
+ EventDataBuilder(EventDataBuilder& other)
+ : m_buffer(other.m_buffer)
+ {
+ return;
+ }
+
+ EventDataBuilder(EventDataBuilder&& other)
+ : m_buffer(other.m_buffer)
+ {
+ return;
+ }
+
+ ByteBufferTy& Buffer()
+ {
+ return m_buffer;
+ }
+
+ ByteBufferTy const& Buffer() const
+ {
+ return m_buffer;
+ }
+
+ void Clear()
+ {
+ m_buffer.clear();
+ }
+
+ UINT32 Size() const
+ {
+ return static_cast<UINT32>(m_buffer.size());
+ }
+
+ UINT8& operator[](UINT32 index)
+ {
+ return m_buffer[index];
+ }
+
+ void AddArrayCount(UINT16 cItems)
+ {
+ AddBytes(&cItems, sizeof(cItems));
+ }
+
+ /*
+ Adds a value to the payload.
+ Note: should only be used for blittable POD types with no padding.
+ */
+ template<class T>
+ void AddValue(T const& value)
+ {
+ AddBytes(&reinterpret_cast<const char&>(value), sizeof(T));
+ }
+
+ /*
+ Adds an array of values to the payload.
+ Note: should only be used for blittable POD types with no padding.
+ */
+ template<class T>
+ void AddValues(_In_reads_(cValues) T const* pValues, unsigned cValues)
+ {
+ AddBytes(pValues, sizeof(T) * cValues);
+ }
+
+ /*
+ Appends an InTypeBinary field to the payload.
+ Compatible with: TypeBinary, TypeIPv6, TypeSocketAddress.
+ */
+ void AddBinary(_In_reads_bytes_(cbData) void const* pbData, UINT16 cbData)
+ {
+ AddBytes(&cbData, sizeof(cbData));
+ AddBytes(pbData, cbData);
+ }
+
+ /*
+ Appends an InTypeCountedAnsiString field to the payload.
+ Compatible with: TypeCountedMbcsString, TypeCountedMbcsXml, TypeCountedMbcsJson.
+ */
+ void AddCountedString(_In_reads_(cch) char const* pch, UINT16 cch)
+ {
+ AddBinary(pch, cch * sizeof(*pch));
+ }
+
+ /*
+ Appends an InTypeCountedString field to the payload.
+ Compatible with: TypeCountedUtf16String, TypeCountedUtf16Xml, TypeCountedUtf16Json.
+ */
+ void AddCountedString(_In_reads_(cch) wchar_t const* pch, UINT16 cch)
+ {
+ AddBinary(pch, cch * sizeof(*pch));
+ }
+
+ /*
+ Appends an InTypeAnsiString field to the payload.
+ Compatible with: TypeMbcsString, TypeMbcsXml, TypeMbcsJson.
+ */
+ void AddString(_In_z_ char const* psz)
+ {
+ AddBytes(psz, static_cast<unsigned>(strlen(psz) + 1));
+ }
+
+ /*
+ Appends a InTypeUnicodeString field to the payload.
+ Compatible with: TypeUtf16String, TypeUtf16Xml, TypeUtf16Json
+ */
+ void AddString(_In_z_ wchar_t const* psz)
+ {
+ AddBytes(psz, static_cast<unsigned>(2 * (wcslen(psz) + 1)));
+ }
+
+ /*
+ Appends a raw byte to the payload. (Calls buffer.push_back)
+ */
+ void AddByte(UINT8 val)
+ {
+ m_buffer.push_back(val);
+ }
+
+ /*
+ Appends raw bytes to the payload. (Calls buffer.insert)
+ */
+ void AddBytes(_In_reads_bytes_(cbData) void const* pbData, unsigned cbData)
+ {
+ auto pb = static_cast<UINT8 const*>(pbData);
+ m_buffer.insert(m_buffer.end(), pb + 0, pb + cbData);
+ }
+ };
+
+#pragma endregion
+
+#pragma region Provider
+
+ class Provider
+ {
+ Provider(Provider const&); // = delete
+ void operator=(Provider const&); // = delete
+
+ UINT32 m_levelPlus1;
+ HRESULT m_hrInitialization;
+ ULONGLONG m_keywordsAny;
+ ULONGLONG m_keywordsAll;
+ REGHANDLE m_hProvider;
+ UINT8 const* m_pbMetadata;
+ PENABLECALLBACK m_pfCallback;
+ LPVOID m_pCallbackContext;
+ GUID m_id;
+
+ /*
+ Shortest possible valid provider metadata blob:
+ Size = 0x0003, Name = "", no options.
+ */
+ static _Ret_bytecount_(3) UINT8 const* NullMetadata()
+ {
+ return reinterpret_cast<UINT8 const*>("\03\0");
+ }
+
+ static VOID NTAPI EnableCallback(
+ _In_ LPCGUID pSourceId,
+ _In_ ULONG callbackType,
+ _In_ UCHAR level,
+ _In_ ULONGLONG keywordsAny,
+ _In_ ULONGLONG keywordsAll,
+ _In_opt_ PEVENT_FILTER_DESCRIPTOR pFilterData,
+ _Inout_opt_ PVOID pCallbackContext)
+ {
+ if (pCallbackContext)
+ {
+ Provider* pProvider = static_cast<Provider*>(pCallbackContext);
+ switch (callbackType)
+ {
+ case 0: // EVENT_CONTROL_CODE_DISABLE_PROVIDER
+ pProvider->m_levelPlus1 = 0;
+ break;
+ case 1: // EVENT_CONTROL_CODE_ENABLE_PROVIDER
+ pProvider->m_levelPlus1 = level != 0 ? (UINT32)level + 1u : 256u;
+ pProvider->m_keywordsAny = keywordsAny;
+ pProvider->m_keywordsAll = keywordsAll;
+ break;
+ }
+
+ if (pProvider->m_pfCallback)
+ {
+ pProvider->m_pfCallback(
+ pSourceId,
+ callbackType,
+ level,
+ keywordsAny,
+ keywordsAll,
+ pFilterData,
+ pProvider->m_pCallbackContext);
+ }
+ }
+ }
+
+ _Ret_opt_bytecount_(cbMetadata)
+ static UINT8* MetadataAlloc(
+ UINT32 cbMetadata)
+ {
+ return static_cast<UINT8*>(HeapAlloc(GetProcessHeap(), 0, cbMetadata));
+ }
+
+ static void MetadataFree(
+ _Post_invalid_ UINT8* pbMetadata)
+ {
+ if (pbMetadata)
+ {
+ HeapFree(GetProcessHeap(), 0, pbMetadata);
+ }
+ }
+
+ void InitFail(
+ HRESULT hr)
+ {
+ _tld_ASSERT(m_pbMetadata != NullMetadata(), "InitFail called with m_pbMetadata == NullMetadata()");
+ MetadataFree(const_cast<UINT8*>(m_pbMetadata));
+ m_hrInitialization = hr;
+ m_pbMetadata = NullMetadata();
+ }
+
+ template<class CharTy>
+ void Init(
+ _In_z_ CharTy const* szName,
+ _In_opt_ GUID const* pGroupId)
+ {
+ // Already initialized: m_pfCallback, m_pCallbackContext, m_id.
+ m_levelPlus1 = 0;
+ m_hrInitialization = 0;
+ m_keywordsAny = 0;
+ m_keywordsAll = 0;
+ m_hProvider = 0;
+ m_pbMetadata = 0;
+
+ ByteArrayWrapper wrapper(0, 0);
+ ProviderMetadataBuilder<ByteArrayWrapper> builder(wrapper);
+ builder.Begin(szName);
+ if (pGroupId)
+ {
+ builder.AddTrait(ProviderTraitGroupGuid, pGroupId, sizeof(GUID));
+ }
+ if (!builder.End())
+ {
+ InitFail(E_INVALIDARG); // Metadata > 64KB.
+ goto Done;
+ }
+
+ {
+ UINT32 const cbMetadata = wrapper.size();
+ UINT8* pbMetadata = MetadataAlloc(cbMetadata);
+ if (!pbMetadata)
+ {
+ InitFail(E_OUTOFMEMORY);
+ goto Done;
+ }
+
+ m_pbMetadata = pbMetadata;
+ wrapper.reset(pbMetadata, cbMetadata);
+ builder.Begin(szName);
+ if (pGroupId)
+ {
+ builder.AddTrait(ProviderTraitGroupGuid, pGroupId, sizeof(GUID));
+ }
+ if (!builder.End() || cbMetadata < wrapper.size())
+ {
+ InitFail(0x8000000CL); // E_CHANGED_STATE
+ goto Done;
+ }
+
+ {
+ HRESULT hr = ::tld::RegisterProvider(&m_hProvider, &m_id, m_pbMetadata, &EnableCallback, this);
+ if (FAILED(hr))
+ {
+ InitFail(hr);
+ }
+ }
+ }
+
+ Done:
+
+ return;
+ }
+
+ bool IsEnabledForKeywords(ULONGLONG keywords) const
+ {
+ return keywords == 0 || (
+ (keywords & m_keywordsAny) != 0 &&
+ (keywords & m_keywordsAll) == m_keywordsAll);
+ }
+
+ public:
+
+ ~Provider()
+ {
+ EventUnregister(m_hProvider);
+ if (m_pbMetadata != NullMetadata())
+ {
+ MetadataFree(const_cast<UINT8*>(m_pbMetadata));
+ }
+ }
+
+ Provider(
+ _In_z_ wchar_t const* szUtf16Name,
+ GUID const& providerId,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(providerId)
+ {
+ Init(szUtf16Name, pGroupId);
+ }
+
+ Provider(
+ _In_z_ char const* szUtf8Name,
+ GUID const& providerId,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(providerId)
+ {
+ Init(szUtf8Name, pGroupId);
+ }
+
+ explicit Provider(
+ _In_z_ wchar_t const* szUtf16Name,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(GetGuidForName(szUtf16Name))
+ {
+ Init(szUtf16Name, pGroupId);
+ }
+
+ explicit Provider(
+ _In_z_ char const* szUtf8Name,
+ _In_opt_ GUID const* pGroupId = 0,
+ PENABLECALLBACK pfCallback = 0,
+ LPVOID pCallbackContext = 0)
+ : m_pfCallback(pfCallback)
+ , m_pCallbackContext(pCallbackContext)
+ , m_id(GetGuidForName(szUtf8Name))
+ {
+ Init(szUtf8Name, pGroupId);
+ }
+
+ HRESULT InitializationResult() const
+ {
+ return m_hrInitialization;
+ }
+
+ _Ret_z_ char const* Name() const
+ {
+ return reinterpret_cast<char const*>(m_pbMetadata + 2);
+ }
+
+ GUID const& Id() const
+ {
+ return m_id;
+ }
+
+ _Ret_notnull_ UINT8 const* GetMetadata() const
+ {
+ return m_pbMetadata;
+ }
+
+ UINT16 GetMetadataSize() const
+ {
+ return *reinterpret_cast<UINT16 const*>(m_pbMetadata);
+ }
+
+ bool IsEnabled() const
+ {
+ return m_levelPlus1 != 0;
+ }
+
+ bool IsEnabled(UCHAR level) const
+ {
+ return level < m_levelPlus1;
+ }
+
+ bool IsEnabled(UCHAR level, ULONGLONG keywords) const
+ {
+ return level < m_levelPlus1 && IsEnabledForKeywords(keywords);
+ }
+
+ HRESULT SetInformation(
+ EVENT_INFO_CLASS informationClass,
+ _In_reads_bytes_opt_(cbInformation) void* pbInformation,
+ ULONG cbInformation
+ ) const
+ {
+ return ::tld::SetInformation(m_hProvider, informationClass, pbInformation, cbInformation);
+ }
+
+ HRESULT WriteGather(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ return ::tld::WriteEvent(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cDataDescriptors,
+ pDataDescriptors,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+ HRESULT Write(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_reads_bytes_(cbData) LPCVOID pbData,
+ UINT32 cbData,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ EventDataDescCreate(dataDesc + 2, pbData, cbData);
+ return ::tld::WriteEvent(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cbData ? 3 : 2,
+ dataDesc,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ HRESULT WriteGatherEx(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_range_(2, 128) ULONG cDataDescriptors,
+ _Inout_count_(cDataDescriptors) EVENT_DATA_DESCRIPTOR* pDataDescriptors,
+ ULONG64 filter,
+ ULONG flags,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ return ::tld::WriteEventEx(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cDataDescriptors,
+ pDataDescriptors,
+ filter,
+ flags,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+ HRESULT WriteEx(
+ EVENT_DESCRIPTOR const& eventDescriptor,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pEventMetadata,
+ _In_reads_bytes_(cbData) LPCVOID pbData,
+ UINT32 cbData,
+ ULONG64 filter,
+ ULONG flags,
+ _In_opt_ LPCGUID pActivityId = 0,
+ _In_opt_ LPCGUID pRelatedActivityId = 0
+ ) const
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ EventDataDescCreate(dataDesc + 2, pbData, cbData);
+ return ::tld::WriteEventEx(
+ m_hProvider,
+ eventDescriptor,
+ m_pbMetadata,
+ pEventMetadata,
+ cbData ? 3 : 2,
+ dataDesc,
+ filter,
+ flags,
+ pActivityId,
+ pRelatedActivityId);
+ }
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+ };
+
+#pragma endregion
+
+#pragma region EventBuilder
+
+ enum EventState
+ {
+ EventStateOpen,
+ EventStateClosed,
+ EventStateError
+ };
+
+ template<class ByteBufferTy>
+ class EventBuilder
+ {
+ void operator=(EventBuilder const&); // = delete
+
+ EventMetadataBuilder<ByteBufferTy> m_metaBuilder;
+ EventDataBuilder<ByteBufferTy> m_dataBuilder;
+ EventState& m_eventState;
+
+ EventBuilder(EventBuilder& other, EventMetadataBuilder<ByteBufferTy>&& metaBuilder)
+ : m_metaBuilder(metaBuilder)
+ , m_dataBuilder(other.m_dataBuilder)
+ , m_eventState(other.m_eventState)
+ {
+ return;
+ }
+
+ protected:
+
+ EventBuilder(
+ ByteBufferTy& metaBuffer,
+ ByteBufferTy& dataBuffer,
+ EventState& eventState)
+ : m_metaBuilder(metaBuffer)
+ , m_dataBuilder(dataBuffer)
+ , m_eventState(eventState)
+ {
+ return;
+ }
+
+ public:
+
+ EventBuilder(EventBuilder& other)
+ : m_metaBuilder(other.m_metaBuilder)
+ , m_dataBuilder(other.m_dataBuilder)
+ , m_eventState(other.m_eventState)
+ {
+ return;
+ }
+
+ EventBuilder(EventBuilder&& other)
+ : m_metaBuilder(other.m_metaBuilder)
+ , m_dataBuilder(other.m_dataBuilder)
+ , m_eventState(other.m_eventState)
+ {
+ return;
+ }
+
+ /*
+ Returns a const reference to the event metadata's underlying buffer.
+ */
+ ByteBufferTy const& MetaBuffer() const
+ {
+ return m_metaBuilder.GetBuffer();
+ }
+
+ /*
+ Returns a const reference to the event payload data's underlying buffer.
+ */
+ ByteBufferTy const& DataBuffer() const
+ {
+ return m_dataBuilder.Buffer();
+ }
+
+ /*
+ Adds a nested struct to the event's metadata.
+ Use the returned EventBuilder to add data and metadata for the fields
+ of the nested struct.
+ Note: do not call any Add methods on this builder object until you are
+ done calling Add methods on the nested builder object.
+ Note: Do not create structs with 0 fields.
+ */
+ template<class CharTy>
+ EventBuilder AddStruct(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ return EventBuilder(*this, m_metaBuilder.AddStruct(szUtfStructName, fieldTags));
+ }
+
+ /*
+ Adds a nested array of struct to the event's metadata.
+ Use the returned EventBuilder to add data and metadata for the fields
+ of the nested struct.
+ Note: do not call any Add methods on this builder object until you are
+ done calling Add methods on the nested builder object.
+ Note: Do not create structs with 0 fields.
+ */
+ template<class CharTy>
+ EventBuilder AddStructArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ return EventBuilder(*this, m_metaBuilder.AddStructArray(szUtfStructName, fieldTags));
+ }
+
+ /*
+ Adds a nested array of struct to the event's metadata.
+ Use the returned EventBuilder to add data and metadata for the fields
+ of the nested struct.
+ Note: do not call any Add methods on this builder object until you are
+ done calling Add methods on the nested builder object.
+ Note: Do not use 0 for itemCount.
+ Note: Do not create structs with 0 fields.
+ */
+ template<class CharTy>
+ EventBuilder AddStructFixedArray(
+ _In_z_ CharTy const* szUtfStructName,
+ UINT16 itemCount,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ return EventBuilder(*this, m_metaBuilder.AddStructFixedArray(szUtfStructName, itemCount, fieldTags));
+ }
+
+ /*
+ Adds a scalar field to the event's metadata.
+ */
+ template<class CharTy>
+ void AddField(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddField(szUtfFieldName, type, fieldTags);
+ }
+
+ /*
+ Adds a variable-length array field to the event's metadata.
+ The length (item count) must be added to the event's payload
+ immediately before the array item values.
+ */
+ template<class CharTy>
+ void AddFieldArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddFieldArray(szUtfFieldName, type, fieldTags);
+ }
+
+ /*
+ Adds a fixed-length array field to the event's metadata.
+ The length (item count) is encoded in the metadata, so it does not
+ need to be included in the event's payload.
+ Note: Do not use 0 for itemCount.
+ */
+ template<class CharTy>
+ void AddFieldFixedArray(
+ _In_z_ CharTy const* szUtfFieldName,
+ UINT16 itemCount,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddFieldFixedArray(szUtfFieldName, itemCount, type, fieldTags);
+ }
+
+ /*
+ Adds a custom-encoded field to the event's metadata.
+ Custom-encoded means that you are using some other serialization
+ system (e.g. Bond, Protocol Buffers) to produce the schema and data.
+ The payload is encoded as a UINT16 byte count followed by the data.
+ */
+ template<class CharTy>
+ void AddFieldCustom(
+ _In_z_ CharTy const* szUtfFieldName,
+ _In_reads_bytes_(cbSchema) void const* pSchema,
+ UINT16 cbSchema,
+ Type type,
+ UINT32 fieldTags = 0)
+ {
+ _tld_ASSERT(m_eventState == EventStateOpen, "Metadata already prepared.");
+ m_metaBuilder.AddFieldCustom(szUtfFieldName, pSchema, cbSchema, type, fieldTags);
+ }
+
+ /*
+ Advanced use: adds raw metadata bytes.
+ */
+ void AddRawMetadata(
+ _In_reads_bytes_(cbMetadata) void const* pMetadata,
+ UINT32 cbMetadata)
+ {
+ auto& buf = m_metaBuilder.GetBuffer();
+ auto pb = static_cast<BYTE const*>(pMetadata);
+ buf.insert(buf.end(), pb + 0u, pb + cbMetadata);
+ }
+
+ /*
+ Adds an array length (item count) to the payload. This is for
+ variable-length arrays only (not fixed-length), and must occur
+ immediately before the array item values are written to the payload.
+ */
+ void AddArrayCount(UINT16 cItems)
+ {
+ m_dataBuilder.AddArrayCount(cItems);
+ }
+
+ /*
+ Adds a value to the payload.
+ Note: should only be used for blittable POD types with no padding,
+ e.g. INT32, FILETIME, GUID, not for strings or structs.
+ */
+ template<class T>
+ void AddValue(T const& value)
+ {
+ m_dataBuilder.AddValue(value);
+ }
+
+ /*
+ Adds an array of values to the payload.
+ Note: should only be used for blittable POD types with no padding,
+ e.g. INT32, FILETIME, GUID, not for strings or structs.
+ */
+ template<class T>
+ void AddValues(_In_reads_(cValues) T const* pValues, unsigned cValues)
+ {
+ m_dataBuilder.AddValues(pValues, cValues);
+ }
+
+ /*
+ Appends an InTypeBinary field to the payload.
+ Compatible with: TypeBinary, TypeIPv6, TypeSocketAddress.
+ */
+ void AddBinary(_In_reads_bytes_(cbData) void const* pbData, UINT16 cbData)
+ {
+ m_dataBuilder.AddBinary(pbData, cbData);
+ }
+
+ /*
+ Appends an InTypeCountedAnsiString field to the payload.
+ Compatible with: TypeCountedMbcsString, TypeCountedMbcsXml, TypeCountedMbcsJson.
+ */
+ void AddCountedString(_In_reads_(cch) char const* pch, UINT16 cch)
+ {
+ m_dataBuilder.AddCountedString(pch, cch);
+ }
+
+ /*
+ Appends an InTypeCountedString field to the payload.
+ Compatible with: TypeCountedUtf16String, TypeCountedUtf16Xml, TypeCountedUtf16Json.
+ */
+ void AddCountedString(_In_reads_(cch) wchar_t const* pch, UINT16 cch)
+ {
+ m_dataBuilder.AddCountedString(pch, cch);
+ }
+
+ /*
+ Appends an InTypeAnsiString field to the payload.
+ Compatible with: TypeMbcsString, TypeMbcsXml, TypeMbcsJson.
+ */
+ void AddString(_In_z_ char const* psz)
+ {
+ m_dataBuilder.AddString(psz);
+ }
+
+ /*
+ Appends a InTypeUnicodeString field to the payload.
+ Compatible with: TypeUtf16String, TypeUtf16Xml, TypeUtf16Json
+ */
+ void AddString(_In_z_ wchar_t const* psz)
+ {
+ m_dataBuilder.AddString(psz);
+ }
+
+ /*
+ Appends a raw byte to the payload.
+ */
+ void AddByte(UINT8 val)
+ {
+ m_dataBuilder.AddByte(val);
+ }
+
+ /*
+ Appends raw bytes to the payload.
+ */
+ void AddBytes(_In_reads_bytes_(cbData) void const* pbData, unsigned cbData)
+ {
+ m_dataBuilder.AddBytes(pbData, cbData);
+ }
+ };
+
+#pragma endregion
+
+#pragma region Event
+
+ template<class ByteBufferTy>
+ class Event
+ : public EventBuilder<ByteBufferTy>
+ {
+ Event(Event const&); // = delete
+ void operator=(Event const&); // = delete
+
+ ByteBufferTy m_metaBuffer;
+ ByteBufferTy m_dataBuffer;
+ EventDescriptor m_descriptor;
+ GUID const* m_pActivityId;
+ GUID const* m_pRelatedActivityId;
+ EventState m_state;
+
+ public:
+
+ Event(
+ _In_z_ char const* szUtf8Name,
+ UCHAR level = 5,
+ ULONGLONG keywords = 0,
+ UINT32 tags = 0,
+ UCHAR opcode = 0,
+ USHORT task = 0,
+ GUID const* pActivityId = 0,
+ GUID const* pRelatedActivityId = 0)
+ : EventBuilder<ByteBufferTy>(m_metaBuffer, m_dataBuffer, m_state)
+ , m_descriptor(level, keywords, opcode, task)
+ , m_pActivityId(pActivityId)
+ , m_pRelatedActivityId(pRelatedActivityId)
+ , m_state(EventStateOpen)
+ {
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).Begin(szUtf8Name, tags);
+ }
+
+ Event(
+ _In_z_ wchar_t const* szUtf16Name,
+ UCHAR level = 5,
+ ULONGLONG keywords = 0,
+ UINT32 tags = 0,
+ UCHAR opcode = 0,
+ USHORT task = 0,
+ GUID const* pActivityId = 0,
+ GUID const* pRelatedActivityId = 0)
+ : EventBuilder<ByteBufferTy>(m_metaBuffer, m_dataBuffer, m_state)
+ , m_descriptor(level, keywords, opcode, task)
+ , m_pActivityId(pActivityId)
+ , m_pRelatedActivityId(pRelatedActivityId)
+ , m_state(EventStateOpen)
+ {
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).Begin(szUtf16Name, tags);
+ }
+
+ /*
+ Clears the metadata and the payload.
+ */
+ template<class CharTy>
+ void Reset(
+ _In_z_ CharTy const* szUtfName,
+ UCHAR level = 5,
+ ULONGLONG keywords = 0,
+ UINT32 tags = 0,
+ UCHAR opcode = 0,
+ USHORT task = 0,
+ GUID* pActivityId = 0,
+ GUID* pRelatedActivityId = 0)
+ {
+ m_metaBuffer.clear();
+ m_dataBuffer.clear();
+ m_descriptor.Reset(level, keywords, opcode, task);
+ m_pActivityId = pActivityId;
+ m_pRelatedActivityId = pRelatedActivityId;
+ m_state = EventStateOpen;
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).Begin(szUtfName, tags);
+ }
+
+ /*
+ Clears the payload without clearing the metadata.
+ */
+ void ResetData()
+ {
+ m_dataBuffer.clear();
+ }
+
+ /*
+ Sends the event to ETW using EventWriteTransfer.
+ */
+ HRESULT Write(Provider const& provider)
+ {
+ HRESULT hr = E_INVALIDARG;
+ if (PrepareEvent())
+ {
+ hr = provider.Write(
+ m_descriptor,
+ &m_metaBuffer.front(),
+ m_dataBuffer.empty() ? NULL : &m_dataBuffer.front(),
+ static_cast<UINT32>(m_dataBuffer.size()),
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+ return hr;
+ }
+
+ /*
+ Sends the event to ETW using EventWriteTransfer.
+ */
+ HRESULT Write(
+ REGHANDLE hProvider,
+ _In_count_x_((UINT16*)pProviderMetadata) UINT8 const* pProviderMetadata)
+ {
+ HRESULT hr = E_INVALIDARG;
+
+ if (PrepareEvent())
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ ULONG cDataDesc = 2;
+ if (!m_dataBuffer.empty())
+ {
+ EventDataDescCreate(
+ &dataDesc[cDataDesc++],
+ &m_dataBuffer.front(),
+ static_cast<ULONG>(m_dataBuffer.size()));
+ }
+
+ hr = ::tld::WriteEvent(
+ hProvider,
+ m_descriptor,
+ pProviderMetadata,
+ &m_metaBuffer.front(),
+ cDataDesc,
+ dataDesc,
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+
+ return hr;
+ }
+
+#if TLD_HAVE_EVENT_WRITE_EX == 1
+
+ /*
+ Sends the event to ETW using EventWriteEx.
+ */
+ HRESULT WriteEx(
+ Provider const& provider,
+ ULONG64 filter,
+ ULONG flags)
+ {
+ HRESULT hr = E_INVALIDARG;
+ if (PrepareEvent())
+ {
+ hr = provider.WriteEx(
+ m_descriptor,
+ &m_metaBuffer.front(),
+ m_dataBuffer.empty() ? NULL : &m_dataBuffer.front(),
+ static_cast<UINT32>(m_dataBuffer.size()),
+ filter,
+ flags,
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+ return hr;
+ }
+
+ /*
+ Sends the event to ETW using EventWriteEx.
+ */
+ HRESULT WriteEx(
+ REGHANDLE hProvider,
+ _In_count_x_((UINT16*)pEventMetadata) UINT8 const* pProviderMetadata,
+ ULONG64 filter,
+ ULONG flags)
+ {
+ HRESULT hr = E_INVALIDARG;
+
+ if (PrepareEvent())
+ {
+ EVENT_DATA_DESCRIPTOR dataDesc[3];
+ ULONG cDataDesc = 2;
+ if (!m_dataBuffer.empty())
+ {
+ EventDataDescCreate(
+ &dataDesc[cDataDesc++],
+ &m_dataBuffer.front(),
+ static_cast<ULONG>(m_dataBuffer.size()));
+ }
+
+ hr = ::tld::WriteEventEx(
+ hProvider,
+ m_descriptor,
+ pProviderMetadata,
+ &m_metaBuffer.front(),
+ cDataDesc,
+ dataDesc,
+ filter,
+ flags,
+ m_pActivityId,
+ m_pRelatedActivityId);
+ }
+
+ return hr;
+ }
+
+#endif // TLD_HAVE_EVENT_WRITE_EX
+
+#pragma region Basic properties
+
+ UCHAR Channel() const
+ {
+ return m_descriptor.Channel;
+ }
+
+ /*
+ Note: the default channel is 11 (WINEVENT_CHANNEL_TRACELOGGING).
+ Other channels are only supported if the provider is running on
+ Windows 10 or later. If a provider is running on an earlier version
+ of Windows and it uses a channel other than 11, TDH will not be able
+ to decode the events.
+ */
+ void Channel(UCHAR channel)
+ {
+ m_descriptor.Channel = channel;
+ }
+
+ UCHAR Level() const
+ {
+ return m_descriptor.Level;
+ }
+
+ void Level(UCHAR value)
+ {
+ m_descriptor.Level = value;
+ }
+
+ ULONGLONG Keywords() const
+ {
+ return m_descriptor.Keyword;
+ }
+
+ void Keywords(ULONGLONG value)
+ {
+ m_descriptor.Keyword = value;
+ }
+
+ UCHAR Opcode() const
+ {
+ return m_descriptor.Opcode;
+ }
+
+ void Opcode(UCHAR value)
+ {
+ m_descriptor.Opcode = value;
+ }
+
+ USHORT Task() const
+ {
+ return m_descriptor.Task;
+ }
+
+ void Task(USHORT value)
+ {
+ m_descriptor.Task = value;
+ }
+
+ const GUID* ActivityId() const
+ {
+ return m_pActivityId;
+ }
+
+ void ActivityId(GUID* pValue)
+ {
+ m_pActivityId = pValue;
+ }
+
+ const GUID* RelatedActivityId() const
+ {
+ return m_pRelatedActivityId;
+ }
+
+ void RelatedActivityId(GUID* pValue)
+ {
+ m_pRelatedActivityId = pValue;
+ }
+
+ bool IsEnabledFor(Provider const& provider) const
+ {
+ return provider.IsEnabled(m_descriptor.Level, m_descriptor.Keyword);
+ }
+
+#pragma endregion
+
+ private:
+
+ bool PrepareEvent()
+ {
+ if (m_state == EventStateOpen)
+ {
+ PrepareEventImpl();
+ }
+ return m_state == EventStateClosed;
+ }
+
+ void PrepareEventImpl()
+ {
+ m_state =
+ EventMetadataBuilder<ByteBufferTy>(m_metaBuffer).End()
+ ? EventStateClosed
+ : EventStateError;
+ }
+ };
+
+#pragma endregion
+}
+// namespace tld
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h
new file mode 100644
index 000000000..60cac48a5
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h
@@ -0,0 +1,186 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#include <map>
+
+#include "opentelemetry/nostd/shared_ptr.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/nostd/unique_ptr.h"
+#include "opentelemetry/nostd/variant.h"
+#include "opentelemetry/trace/span_id.h"
+
+#include "opentelemetry/exporters/etw/etw_provider.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+/**
+ * @brief TelemetryProvider Options passed via SDK API.
+ */
+using TelemetryProviderOptions =
+ std::map<std::string, nostd::variant<std::string, uint64_t, float, bool>>;
+
+/**
+ * @brief TelemetryProvider runtime configuration class. Internal representation
+ * of TelemetryProviderOptions used by various components of SDK.
+ */
+typedef struct
+{
+ bool enableTraceId; // Set `TraceId` on ETW events
+ bool enableSpanId; // Set `SpanId` on ETW events
+ bool enableActivityId; // Assign `SpanId` to `ActivityId`
+ bool enableActivityTracking; // Emit TraceLogging events for Span/Start and Span/Stop Not used
+ // for Logs
+ bool enableRelatedActivityId; // Assign parent `SpanId` to `RelatedActivityId`
+ bool enableAutoParent; // Start new spans as children of current active span, Not used for Logs
+ ETWProvider::EventFormat
+ encoding; // Event encoding to use for this provider (TLD, MsgPack, XML, etc.).
+} TelemetryProviderConfiguration;
+
+/**
+ * @brief Helper template to convert a variant value from TelemetryProviderOptions to
+ * LoggerProviderConfiguration
+ *
+ * @param options TelemetryProviderOptions passed on API surface
+ * @param key Option name
+ * @param value Reference to destination value
+ * @param defaultValue Default value if option is not supplied
+ */
+template <typename T>
+static inline void GetOption(const TelemetryProviderOptions &options,
+ const char *key,
+ T &value,
+ T defaultValue)
+{
+ auto it = options.find(key);
+ if (it != options.end())
+ {
+ auto val = it->second;
+ value = nostd::get<T>(val);
+ }
+ else
+ {
+ value = defaultValue;
+ }
+}
+
+/**
+ * @brief Helper template to convert encoding config option to EventFormat.
+ * Configuration option passed as `options["encoding"] = "MsgPack"`.
+ * Default encoding is TraceLogging Dynamic Manifest (TLD).
+ *
+ * Valid encoding names listed below.
+ *
+ * For MessagePack encoding:
+ * - "MSGPACK"
+ * - "MsgPack"
+ * - "MessagePack"
+ *
+ * For XML encoding:
+ * - "XML"
+ * - "xml"
+ *
+ * For TraceLogging Dynamic encoding:
+ * - "TLD"
+ * - "tld"
+ *
+ */
+static inline ETWProvider::EventFormat GetEncoding(const TelemetryProviderOptions &options)
+{
+ ETWProvider::EventFormat evtFmt = ETWProvider::EventFormat::ETW_MANIFEST;
+
+ auto it = options.find("encoding");
+ if (it != options.end())
+ {
+ auto varValue = it->second;
+ std::string val = nostd::get<std::string>(varValue);
+
+#pragma warning(push)
+#pragma warning(disable : 4307) /* Integral constant overflow - OK while computing hash */
+ auto h = utils::hashCode(val.c_str());
+ switch (h)
+ {
+ case CONST_HASHCODE(MSGPACK):
+ // nobrk
+ case CONST_HASHCODE(MsgPack):
+ // nobrk
+ case CONST_HASHCODE(MessagePack):
+ evtFmt = ETWProvider::EventFormat::ETW_MSGPACK;
+ break;
+
+ case CONST_HASHCODE(XML):
+ // nobrk
+ case CONST_HASHCODE(xml):
+ evtFmt = ETWProvider::EventFormat::ETW_XML;
+ break;
+
+ case CONST_HASHCODE(TLD):
+ // nobrk
+ case CONST_HASHCODE(tld):
+ // nobrk
+ evtFmt = ETWProvider::EventFormat::ETW_MANIFEST;
+ break;
+
+ default:
+ break;
+ }
+#pragma warning(pop)
+ }
+
+ return evtFmt;
+}
+
+/**
+ * @brief Utility template to obtain etw::TracerProvider._config or etw::LoggerProvider._config
+ *
+ * @tparam T etw::TracerProvider
+ * @param t etw::TracerProvider ref
+ * @return TelemetryProviderConfiguration ref
+ */
+template <class T>
+TelemetryProviderConfiguration &GetConfiguration(T &t)
+{
+ return t.config_;
+}
+
+/**
+ * @brief Utility template to convert SpanId or TraceId to hex.
+ * @param id - value of SpanId or TraceId
+ * @return Hexadecimal representation of Id as string.
+ */
+template <class T>
+static inline std::string ToLowerBase16(const T &id)
+{
+ char buf[2 * T::kSize] = {0};
+ id.ToLowerBase16(buf);
+ return std::string(buf, sizeof(buf));
+}
+
+/**
+ * @brief Utility method to convert span_id (8 byte) to ActivityId GUID (16 bytes)
+ * @param span OpenTelemetry Span Id object
+ * @return GUID struct containing 8-bytes of SpanId + 8 NUL bytes.
+ */
+static inline bool CopySpanIdToActivityId(const opentelemetry::trace::SpanId &span_id,
+ GUID &outGuid)
+{
+ memset(&outGuid, 0, sizeof(outGuid));
+ if (!span_id.IsValid())
+ {
+ return false;
+ }
+ auto spanId = span_id.Id().data();
+ uint8_t *guidPtr = reinterpret_cast<uint8_t *>(&outGuid);
+ for (size_t i = 0; i < 8; i++)
+ {
+ guidPtr[i] = spanId[i];
+ }
+ return true;
+}
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h
new file mode 100644
index 000000000..a2fd6c727
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_fields.h
@@ -0,0 +1,142 @@
+/* // Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <unordered_map>
+
+#include "opentelemetry/exporters/etw/utils.h"
+
+/* clang-format off */
+#ifdef CUSTOM_ETW_FIELDS_H
+/* Customers may redefine the default field names by including CUSTOM_ETW_FIELDS_H header */
+# include CUSTOM_ETW_FIELDS_H
+#else
+
+/**
+
+ List of configurable Field Name constants:
+
+ Version - Schema version (optional for ETW exporter).
+ _name - Built-in ETW name at envelope level (dedicated ETW field).
+ _time - Built-in ETW time at envelope level (dedicated ETW field).
+ SpanId - OT SpanId
+ TraceId - OT TraceId
+ StartTime - OT Span start time
+ Kind - OT Span kind
+ Name - OT Span name in ETW 'Payload["Name"]'
+ ParentId - OT Span parentId
+ Links - OT Span links array
+
+ Other standard fields (reserved names) that may be appended by ETW channel:
+
+ Level - a 1-byte integer that enables filtering based on the severity or verbosity of events
+ ProviderGuid - ETW Provider Guid
+ ProviderName - ETW Provider Name
+ OpcodeName - Name of Opcode (e.g. Start, Stop)
+ KeywordName - Name of Keyword
+ TaskName - TaskName, could be handled as an alias to Payload['name']
+ ChannelName - ETW Channel Name
+ EventMessage - ETW Event Message string for unstructured events
+ ActivityId - ActivityId for EventSource parenting (current event)
+ RelatedActivityId - RelatedActivityId for EventSource parenting (parent event)
+ Pid - Process Id
+ Tid - Thread Id
+
+ Example "Span" as shown in Visual Studio "Diagnostic Events" view. EventName="Span":
+
+ {
+ "Timestamp": "2021-04-01T00:33:25.5876605-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 20,
+ "Message": null,
+ "ProcessId": 10424,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "56f2366b-5475-496f-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 0,
+ "Name": "B.max",
+ "ParentId": "8ad900d0587fad4a",
+ "SpanId": "6b36f25675546f49",
+ "StartTime": "2021-04-01T07:33:25.587Z",
+ "TraceId": "8f8ac710c37c5a419f0fe574f335e986"
+ }
+ }
+
+ Example named Event on Span. Note that EventName="MyEvent2" in this case:
+
+ {
+ "Timestamp": "2021-04-01T00:33:22.5848789-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 15,
+ "Message": null,
+ "ProcessId": 10424,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "MyEvent2",
+ "ActivityID": null,
+ "RelatedActivityID": null,
+ "Payload": {
+ "SpanId": "0da9f6bf7524a449",
+ "TraceId": "7715c9d490f54f44a5d0c6b62570f1b2",
+ "strKey": "anotherValue",
+ "uint32Key": 9876,
+ "uint64Key": 987654321
+ }
+ }
+
+ */
+# define ETW_FIELD_VERSION "Version" /* Event version */
+# define ETW_FIELD_TYPE "Type" /* Event type */
+# define ETW_FIELD_NAME "_name" /* Event name */
+# define ETW_FIELD_TIME "_time" /* Event time */
+# define ETW_FIELD_OPCODE "OpCode" /* OpCode for TraceLogging */
+
+# define ETW_FIELD_TRACE_ID "TraceId" /* Trace Id */
+# define ETW_FIELD_SPAN_ID "SpanId" /* Span Id */
+# define ETW_FIELD_SPAN_PARENTID "ParentId" /* Span ParentId */
+# define ETW_FIELD_SPAN_KIND "Kind" /* Span Kind */
+# define ETW_FIELD_SPAN_LINKS "Links" /* Span Links array */
+
+# define ETW_FIELD_PAYLOAD_NAME "Name" /* ETW Payload["Name"] */
+
+/* Span option constants */
+# define ETW_FIELD_STARTTIME "StartTime" /* Operation start time */
+# define ETW_FIELD_DURATION "Duration" /* Operation duration */
+# define ETW_FIELD_STATUSCODE "StatusCode" /* Span status code */
+# define ETW_FIELD_STATUSMESSAGE "StatusMessage" /* Span status message */
+# define ETW_FIELD_SUCCESS "Success" /* Span success */
+# define ETW_FIELD_TIMESTAMP "Timestamp" /* Log timestamp */
+
+/* Value constants */
+# define ETW_VALUE_SPAN "Span" /* ETW event name for Span */
+# define ETW_VALUE_LOG "Log" /* ETW event name for Log */
+
+# define ETW_VALUE_SPAN_START "SpanStart" /* ETW for Span Start */
+# define ETW_VALUE_SPAN_END "SpanEnd" /* ETW for Span Start */
+
+
+/* Log specific */
+# define ETW_FIELD_LOG_BODY "body" /* Log body */
+# define ETW_FIELD_LOG_SEVERITY_TEXT "severityText" /* Sev text */
+# define ETW_FIELD_LOG_SEVERITY_NUM "severityNumber" /* Sev num */
+
+
+#endif
+
+/* clang-format on */
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h
new file mode 100644
index 000000000..bd2478224
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger.h
@@ -0,0 +1,297 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <algorithm>
+
+# include <cstdint>
+# include <cstdio>
+# include <cstdlib>
+# include <sstream>
+# include <type_traits>
+
+# include <fstream>
+
+# include <map>
+
+# include "opentelemetry/nostd/shared_ptr.h"
+# include "opentelemetry/nostd/string_view.h"
+# include "opentelemetry/nostd/unique_ptr.h"
+# include "opentelemetry/nostd/variant.h"
+
+# include "opentelemetry/common/key_value_iterable_view.h"
+
+# include "opentelemetry/logs/logger_provider.h"
+# include "opentelemetry/trace/span_id.h"
+# include "opentelemetry/trace/trace_id.h"
+
+# include "opentelemetry/exporters/etw/etw_config.h"
+# include "opentelemetry/exporters/etw/etw_fields.h"
+# include "opentelemetry/exporters/etw/etw_properties.h"
+# include "opentelemetry/exporters/etw/etw_provider.h"
+# include "opentelemetry/exporters/etw/utils.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+
+class LoggerProvider;
+
+/**
+ * @brief Logger class that allows to send logs to ETW Provider.
+ */
+class Logger : public opentelemetry::logs::Logger
+{
+
+ /**
+ * @brief Parent provider of this Tracer
+ */
+ etw::LoggerProvider &loggerProvider_;
+
+ /**
+ * @brief ProviderId (Name or GUID)
+ */
+ std::string provId;
+
+ /**
+ * @brief Encoding (Manifest, MessagePack or XML)
+ */
+ ETWProvider::EventFormat encoding;
+
+ /**
+ * @brief Provider Handle
+ */
+ ETWProvider::Handle &provHandle;
+
+ /**
+ * @brief ETWProvider is a singleton that aggregates all ETW writes.
+ * @return
+ */
+ static ETWProvider &etwProvider()
+ {
+ static ETWProvider instance; // C++11 magic static
+ return instance;
+ }
+
+ /**
+ * @brief Init a reference to etw::ProviderHandle
+ * @return Provider Handle
+ */
+ ETWProvider::Handle &initProvHandle() { return etwProvider().open(provId, encoding); }
+
+public:
+ /**
+ * @brief Tracer constructor
+ * @param parent Parent LoggerProvider
+ * @param providerId ProviderId - Name or GUID
+ * @param encoding ETW encoding format to use.
+ */
+ Logger(etw::LoggerProvider &parent,
+ nostd::string_view providerId = "",
+ ETWProvider::EventFormat encoding = ETWProvider::EventFormat::ETW_MANIFEST)
+ : opentelemetry::logs::Logger(),
+ loggerProvider_(parent),
+ provId(providerId.data(), providerId.size()),
+ encoding(encoding),
+ provHandle(initProvHandle())
+ {}
+
+ void Log(opentelemetry::logs::Severity severity,
+ nostd::string_view body,
+ const common::KeyValueIterable &attributes,
+ opentelemetry::trace::TraceId trace_id,
+ opentelemetry::trace::SpanId span_id,
+ opentelemetry::trace::TraceFlags trace_flags,
+ common::SystemTimestamp timestamp) noexcept override
+ {
+
+# ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ // Properties *res = dynamic_cast<Properties *>(&resr);
+
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return Log(severity, provId, body, *evt, trace_id, span_id, trace_flags, timestamp);
+ }
+# endif
+ Properties evtCopy = attributes;
+ return Log(severity, provId, body, evtCopy, trace_id, span_id, trace_flags, timestamp);
+ }
+
+ void Log(opentelemetry::logs::Severity severity,
+ nostd::string_view name,
+ nostd::string_view body,
+ const common::KeyValueIterable &attributes,
+ opentelemetry::trace::TraceId trace_id,
+ opentelemetry::trace::SpanId span_id,
+ opentelemetry::trace::TraceFlags trace_flags,
+ common::SystemTimestamp timestamp) noexcept override
+ {
+
+# ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ // Properties *res = dynamic_cast<Properties *>(&resr);
+
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return Log(severity, name, body, *evt, trace_id, span_id, trace_flags, timestamp);
+ }
+# endif
+ Properties evtCopy = attributes;
+ return Log(severity, name, body, evtCopy, trace_id, span_id, trace_flags, timestamp);
+ }
+
+ virtual void Log(opentelemetry::logs::Severity severity,
+ nostd::string_view name,
+ nostd::string_view body,
+ Properties &evt,
+ opentelemetry::trace::TraceId trace_id,
+ opentelemetry::trace::SpanId span_id,
+ opentelemetry::trace::TraceFlags trace_flags,
+ common::SystemTimestamp timestamp) noexcept
+ {
+ // Populate Etw.EventName attribute at envelope level
+ evt[ETW_FIELD_NAME] = ETW_VALUE_LOG;
+
+# ifdef HAVE_FIELD_TIME
+ {
+ auto timeNow = std::chrono::system_clock::now().time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow).count();
+ evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis);
+ }
+# endif
+ const auto &cfg = GetConfiguration(loggerProvider_);
+ if (cfg.enableSpanId)
+ {
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(span_id);
+ }
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(trace_id);
+ }
+ // Populate ActivityId if enabled
+ GUID ActivityId;
+ LPGUID ActivityIdPtr = nullptr;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(span_id, ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+ evt[ETW_FIELD_PAYLOAD_NAME] = std::string(name.data(), name.size());
+ std::chrono::system_clock::time_point ts = timestamp;
+ int64_t tsMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(ts.time_since_epoch()).count();
+ evt[ETW_FIELD_TIMESTAMP] = utils::formatUtcTimestampMsAsISO8601(tsMs);
+ int severity_index = static_cast<int>(severity);
+ if (severity_index < 0 ||
+ severity_index >= std::extent<decltype(opentelemetry::logs::SeverityNumToText)>::value)
+ {
+ std::stringstream sout;
+ sout << "Invalid severity(" << severity_index << ")";
+ evt[ETW_FIELD_LOG_SEVERITY_TEXT] = sout.str();
+ }
+ else
+ {
+ evt[ETW_FIELD_LOG_SEVERITY_TEXT] =
+ opentelemetry::logs::SeverityNumToText[severity_index].data();
+ }
+ evt[ETW_FIELD_LOG_SEVERITY_NUM] = static_cast<uint32_t>(severity);
+ evt[ETW_FIELD_LOG_BODY] = std::string(body.data(), body.length());
+ etwProvider().write(provHandle, evt, nullptr, nullptr, 0, encoding);
+ }
+
+ const nostd::string_view GetName() noexcept override { return std::string(); }
+ // TODO : Flush and Shutdown method in main Logger API
+ ~Logger() { etwProvider().close(provHandle); }
+};
+
+/**
+ * @brief ETW LoggerProvider
+ */
+class LoggerProvider : public opentelemetry::logs::LoggerProvider
+{
+public:
+ /**
+ * @brief LoggerProvider options supplied during initialization.
+ */
+ TelemetryProviderConfiguration config_;
+
+ /**
+ * @brief Construct instance of LoggerProvider with given options
+ * @param options Configuration options
+ */
+ LoggerProvider(TelemetryProviderOptions options) : opentelemetry::logs::LoggerProvider()
+ {
+ GetOption(options, "enableTraceId", config_.enableTraceId, true);
+ GetOption(options, "enableSpanId", config_.enableSpanId, true);
+ GetOption(options, "enableActivityId", config_.enableActivityId, false);
+
+ // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc.
+ config_.encoding = GetEncoding(options);
+ }
+
+ LoggerProvider() : opentelemetry::logs::LoggerProvider()
+ {
+ config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST;
+ }
+
+ nostd::shared_ptr<opentelemetry::logs::Logger> GetLogger(
+ nostd::string_view logger_name,
+ nostd::string_view options,
+ nostd::string_view library_name,
+ nostd::string_view version = "",
+ nostd::string_view schema_url = "") override
+ {
+ UNREFERENCED_PARAMETER(options);
+ UNREFERENCED_PARAMETER(library_name);
+ UNREFERENCED_PARAMETER(version);
+ UNREFERENCED_PARAMETER(schema_url);
+ ETWProvider::EventFormat evtFmt = config_.encoding;
+ return nostd::shared_ptr<opentelemetry::logs::Logger>{
+ new (std::nothrow) etw::Logger(*this, logger_name, evtFmt)};
+ }
+
+ /**
+ * @brief Obtain ETW Tracer.
+ * @param name ProviderId (instrumentation name) - Name or GUID
+ * @param args Additional arguments that controls `codec` of the provider.
+ * Possible values are:
+ * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events.
+ * - "MSGPACK" - MessagePack-encoded binary payload ETW events.
+ * - "XML" - XML events (reserved for future use)
+ * @param library_name Library name
+ * @param version Library version
+ * @param schema_url schema URL
+ * @return
+ */
+ nostd::shared_ptr<opentelemetry::logs::Logger> GetLogger(
+ nostd::string_view logger_name,
+ nostd::span<nostd::string_view> args,
+ nostd::string_view library_name,
+ nostd::string_view version = "",
+ nostd::string_view schema_url = "") override
+ {
+ UNREFERENCED_PARAMETER(args);
+ UNREFERENCED_PARAMETER(library_name);
+ UNREFERENCED_PARAMETER(version);
+ UNREFERENCED_PARAMETER(schema_url);
+ ETWProvider::EventFormat evtFmt = config_.encoding;
+ return nostd::shared_ptr<opentelemetry::logs::Logger>{
+ new (std::nothrow) etw::Logger(*this, logger_name, evtFmt)};
+ }
+};
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h
new file mode 100644
index 000000000..9b96cb27a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_logger_exporter.h
@@ -0,0 +1,30 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#ifdef ENABLE_LOGS_PREVIEW
+# include <cstdint>
+# include <cstdio>
+# include <cstdlib>
+
+# include <mutex>
+
+# include "opentelemetry/nostd/shared_ptr.h"
+# include "opentelemetry/nostd/string_view.h"
+# include "opentelemetry/nostd/unique_ptr.h"
+
+# include "opentelemetry/common/key_value_iterable_view.h"
+
+# include "opentelemetry/logs/logger_provider.h"
+# include "opentelemetry/trace/span_id.h"
+# include "opentelemetry/trace/trace_id.h"
+
+# include "opentelemetry/sdk/logs/exporter.h"
+
+# include "opentelemetry/exporters/etw/etw_config.h"
+# include "opentelemetry/exporters/etw/etw_logger.h"
+# include "opentelemetry/exporters/etw/etw_provider.h"
+
+# include "opentelemetry/exporters/etw/utils.h"
+
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h
new file mode 100644
index 000000000..3cf365c8a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_properties.h
@@ -0,0 +1,456 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include "opentelemetry/version.h"
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+
+#include <opentelemetry/nostd/span.h>
+#include <map>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+# include <Windows.h>
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+
+/**
+ * @brief PropertyVariant provides:
+ * - a constructor to initialize from initializer lists
+ * - an owning wrapper around `common::AttributeValue`
+ */
+using PropertyVariant =
+ nostd::variant<bool,
+ int32_t,
+ int64_t,
+ uint32_t,
+ uint64_t,
+ double,
+ std::string,
+ const char *,
+ // 8-bit byte arrays / binary blobs are not part of OT spec yet!
+ // Ref: https://github.com/open-telemetry/opentelemetry-specification/issues/780
+ std::vector<uint8_t>,
+ std::vector<bool>,
+ std::vector<int32_t>,
+ std::vector<int64_t>,
+ std::vector<uint32_t>,
+ std::vector<uint64_t>,
+ std::vector<double>,
+ std::vector<std::string>>;
+
+enum PropertyType
+{
+ kTypeBool,
+ kTypeInt,
+ kTypeInt64,
+ kTypeUInt,
+ kTypeUInt64,
+ kTypeDouble,
+ kTypeString,
+ kTypeCString,
+ kTypeSpanByte,
+ kTypeSpanBool,
+ kTypeSpanInt,
+ kTypeSpanInt64,
+ kTypeSpanUInt,
+ kTypeSpanUInt64,
+ kTypeSpanDouble,
+ kTypeSpanString
+};
+
+/**
+ * @brief PropertyValue class that holds PropertyVariant and
+ * provides converter for non-owning common::AttributeValue
+ */
+class PropertyValue : public PropertyVariant
+{
+
+ /**
+ * @brief Convert span<T> to vector<T>
+ * @tparam T
+ * @param source
+ * @return
+ */
+ template <typename T>
+ static std::vector<T> to_vector(const nostd::span<const T, nostd::dynamic_extent> &source)
+ {
+ return std::vector<T>(source.begin(), source.end());
+ }
+
+ /**
+ * @brief Convert span<string_view> to vector<string>
+ * @param source Span of non-owning string views.
+ * @return Vector of owned strings.
+ */
+ std::vector<std::string> static to_vector(const nostd::span<const nostd::string_view> &source)
+ {
+ std::vector<std::string> result(source.size());
+ for (const auto &item : source)
+ {
+ result.push_back(std::string(item.data()));
+ }
+ return result;
+ }
+
+ /**
+ * @brief Convert vector<INTEGRAL> to span<INTEGRAL>.
+ * @tparam T Integral type
+ * @param vec Vector of integral type primitives to convert to span.
+ * @return Span of integral type primitives.
+ */
+ template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
+ static nostd::span<const T> to_span(const std::vector<T> &vec)
+ {
+ nostd::span<const T> result(vec.data(), vec.size());
+ return result;
+ }
+
+ /**
+ * @brief Convert vector<FLOAT> to span<const FLOAT>.
+ * @tparam T Float type
+ * @param vec Vector of float type primitives to convert to span.
+ * @return Span of float type primitives.
+ */
+ template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
+ static nostd::span<const T> to_span(const std::vector<T> &vec)
+ {
+ nostd::span<const T> result(vec.data(), vec.size());
+ return result;
+ }
+
+public:
+ /**
+ * @brief PropertyValue from bool
+ * @param v
+ * @return
+ */
+ PropertyValue(bool value) : PropertyVariant(value) {}
+
+ /**
+ * @brief PropertyValue from integral.
+ * @param v
+ * @return
+ */
+ template <typename TInteger, std::enable_if_t<std::is_integral<TInteger>::value, bool> = true>
+ PropertyValue(TInteger number) : PropertyVariant(number)
+ {}
+
+ /**
+ * @brief PropertyValue from floating point.
+ * @param v
+ * @return
+ */
+ template <typename TFloat, std::enable_if_t<std::is_floating_point<TFloat>::value, bool> = true>
+ PropertyValue(TFloat number) : PropertyVariant(double(number))
+ {}
+
+ /**
+ * @brief Default PropertyValue (int32_t=0)
+ * @param v
+ * @return
+ */
+ PropertyValue() : PropertyVariant(int32_t(0)) {}
+
+ /**
+ * @brief PropertyValue from array of characters as string.
+ *
+ * @param v
+ * @return
+ */
+ PropertyValue(char value[]) : PropertyVariant(std::string(value)) {}
+
+ /**
+ * @brief PropertyValue from array of characters as string.
+ *
+ * @param v
+ * @return
+ */
+ PropertyValue(const char *value) : PropertyVariant(std::string(value)) {}
+
+ /**
+ * @brief PropertyValue from string.
+ *
+ * @param v
+ * @return
+ */
+ PropertyValue(const std::string &value) : PropertyVariant(value) {}
+
+ /**
+ * @brief PropertyValue from vector as array.
+ * @return
+ */
+ template <typename T>
+ PropertyValue(std::vector<T> value) : PropertyVariant(value)
+ {}
+
+ /**
+ * @brief Convert non-owning common::AttributeValue to owning PropertyValue.
+ * @return
+ */
+ PropertyValue &FromAttributeValue(const common::AttributeValue &v)
+ {
+ switch (v.index())
+ {
+ case common::AttributeType::kTypeBool:
+ PropertyVariant::operator=(nostd::get<bool>(v));
+ break;
+ case common::AttributeType::kTypeInt:
+ PropertyVariant::operator=(nostd::get<int32_t>(v));
+ break;
+ case common::AttributeType::kTypeInt64:
+ PropertyVariant::operator=(nostd::get<int64_t>(v));
+ break;
+ case common::AttributeType::kTypeUInt:
+ PropertyVariant::operator=(nostd::get<uint32_t>(v));
+ break;
+ case common::AttributeType::kTypeUInt64:
+ PropertyVariant::operator=(nostd::get<uint64_t>(v));
+ break;
+ case common::AttributeType::kTypeDouble:
+ PropertyVariant::operator=(nostd::get<double>(v));
+ break;
+ case common::AttributeType::kTypeCString: {
+ PropertyVariant::operator=(nostd::get<const char *>(v));
+ break;
+ }
+ case common::AttributeType::kTypeString: {
+ PropertyVariant::operator=
+ (std::string{nostd::string_view(nostd::get<nostd::string_view>(v)).data()});
+ break;
+ }
+
+ case common::AttributeType::kTypeSpanByte:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const uint8_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanBool:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const bool>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanInt:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const int32_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanInt64:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const int64_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanUInt:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const uint32_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanUInt64:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const uint64_t>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanDouble:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const double>>(v)));
+ break;
+
+ case common::AttributeType::kTypeSpanString:
+ PropertyVariant::operator=(to_vector(nostd::get<nostd::span<const nostd::string_view>>(v)));
+ break;
+
+ default:
+ break;
+ }
+ return (*this);
+ }
+
+ /**
+ * @brief Convert owning PropertyValue to non-owning common::AttributeValue
+ * @param other
+ */
+ common::AttributeValue ToAttributeValue() const
+ {
+ common::AttributeValue value;
+
+ switch (this->index())
+ {
+ case PropertyType::kTypeBool:
+ value = nostd::get<bool>(*this);
+ break;
+ case PropertyType::kTypeInt:
+ value = nostd::get<int32_t>(*this);
+ break;
+ case PropertyType::kTypeInt64:
+ value = nostd::get<int64_t>(*this);
+ break;
+ case PropertyType::kTypeUInt:
+ value = nostd::get<uint32_t>(*this);
+ break;
+ case PropertyType::kTypeUInt64:
+ value = nostd::get<uint64_t>(*this);
+ break;
+ case PropertyType::kTypeDouble:
+ value = nostd::get<double>(*this);
+ break;
+ case PropertyType::kTypeString: {
+ const std::string &str = nostd::get<std::string>(*this);
+ return nostd::string_view(str.data(), str.size());
+ break;
+ }
+ case PropertyType::kTypeCString: {
+ const char *data = nostd::get<const char *>(*this);
+ return nostd::string_view(data, (data) ? strlen(data) : 0);
+ break;
+ }
+ case PropertyType::kTypeSpanByte: {
+ value = to_span(nostd::get<std::vector<uint8_t>>(*this));
+ break;
+ }
+ case PropertyType::kTypeSpanBool: {
+ const auto &vec = nostd::get<std::vector<bool>>(*this);
+ // FIXME: sort out how to remap from vector<bool> to span<bool>
+ UNREFERENCED_PARAMETER(vec);
+ break;
+ }
+ case PropertyType::kTypeSpanInt:
+ value = to_span(nostd::get<std::vector<int32_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanInt64:
+ value = to_span(nostd::get<std::vector<int64_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanUInt:
+ value = to_span(nostd::get<std::vector<uint32_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanUInt64:
+ value = to_span(nostd::get<std::vector<uint64_t>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanDouble:
+ value = to_span(nostd::get<std::vector<double>>(*this));
+ break;
+
+ case PropertyType::kTypeSpanString:
+ // FIXME: sort out how to remap from vector<string> to span<string_view>
+ // value = to_span(nostd::get<std::vector<std::string>>(self));
+ break;
+
+ default:
+ break;
+ }
+ return value;
+ }
+};
+
+/**
+ * @brief Map of PropertyValue
+ */
+using PropertyValueMap = std::map<std::string, PropertyValue>;
+
+/**
+ * @brief Map of PropertyValue with common::KeyValueIterable interface.
+ */
+class Properties : public common::KeyValueIterable, public PropertyValueMap
+{
+
+ /**
+ * @brief Helper tyoe for map constructor.
+ */
+ using PropertyValueType = std::pair<const std::string, PropertyValue>;
+
+public:
+ /**
+ * @brief PropertyValueMap constructor.
+ */
+ Properties() : PropertyValueMap() {}
+
+ /**
+ * @brief PropertyValueMap constructor from initializer list.
+ */
+ Properties(const std::initializer_list<PropertyValueType> properties) : PropertyValueMap()
+ {
+ (*this) = (properties);
+ }
+
+ /**
+ * @brief PropertyValueMap assignment operator from initializer list.
+ */
+ Properties &operator=(std::initializer_list<PropertyValueType> properties)
+ {
+ PropertyValueMap::operator=(properties);
+ return (*this);
+ }
+
+ /**
+ * @brief PropertyValueMap constructor from map.
+ */
+ Properties(const PropertyValueMap &properties) : PropertyValueMap() { (*this) = properties; }
+
+ /**
+ * @brief PropertyValueMap assignment operator from map.
+ */
+ Properties &operator=(const PropertyValueMap &properties)
+ {
+ PropertyValueMap::operator=(properties);
+ return (*this);
+ }
+
+ /**
+ * @brief PropertyValueMap constructor from KeyValueIterable
+ * allows to convert non-Owning KeyValueIterable to owning
+ * container.
+ *
+ */
+ Properties(const common::KeyValueIterable &other) { (*this) = other; }
+
+ /**
+ * @brief PropertyValueMap assignment operator.
+ */
+ Properties &operator=(const common::KeyValueIterable &other)
+ {
+ clear();
+ other.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
+ std::string k(key.data(), key.length());
+ (*this)[k].FromAttributeValue(value);
+ return true;
+ });
+ return (*this);
+ }
+
+ /**
+ * @brief PropertyValueMap property accessor.
+ */
+ PropertyValue &operator[](const std::string &k) { return PropertyValueMap::operator[](k); }
+
+ /**
+ * Iterate over key-value pairs
+ * @param callback a callback to invoke for each key-value. If the callback returns false,
+ * the iteration is aborted.
+ * @return true if every key-value pair was iterated over
+ */
+ bool ForEachKeyValue(nostd::function_ref<bool(nostd::string_view, common::AttributeValue)>
+ callback) const noexcept override
+ {
+ for (const auto &kv : (*this))
+ {
+ const common::AttributeValue &value = kv.second.ToAttributeValue();
+ if (!callback(nostd::string_view{kv.first}, value))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return the number of key-value pairs
+ */
+ size_t size() const noexcept override { return PropertyValueMap::size(); }
+};
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h
new file mode 100644
index 000000000..51cca03a6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_provider.h
@@ -0,0 +1,629 @@
+// // Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4459)
+# pragma warning(disable : 4018)
+# pragma warning(disable : 5054)
+#endif
+
+#include "opentelemetry/exporters/etw/etw_properties.h"
+#include "opentelemetry/exporters/etw/uuid.h"
+#include "opentelemetry/version.h"
+
+#include "opentelemetry/exporters/etw/etw_fields.h"
+#include "opentelemetry/exporters/etw/utils.h"
+
+#ifdef HAVE_MSGPACK
+# include "nlohmann/json.hpp"
+#endif
+
+#include "opentelemetry/exporters/etw/etw_traceloggingdynamic.h"
+
+#include <map>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#ifdef HAVE_KRABS_TESTS
+// krabs.hpp requires this definition of min macro from Windows.h
+# ifndef min
+# define min(a, b) (((a) < (b)) ? (a) : (b))
+# endif
+#endif
+
+#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000
+
+using namespace OPENTELEMETRY_NAMESPACE::exporter::etw;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+class ETWProvider
+{
+
+public:
+ const unsigned long STATUS_OK = 0;
+ const unsigned long STATUS_ERROR = ULONG_MAX;
+ const unsigned long STATUS_EFBIG = ULONG_MAX - 1;
+
+ enum EventFormat
+ {
+ ETW_MANIFEST = 0,
+ ETW_MSGPACK = 1,
+ ETW_XML = 2
+ };
+
+ /// <summary>
+ /// Entry that contains Provider Handle, Provider MetaData and Provider GUID
+ /// </summary>
+ struct Handle
+ {
+ uint64_t refCount;
+ REGHANDLE providerHandle;
+ std::vector<BYTE> providerMetaVector;
+ GUID providerGuid;
+ };
+
+ /// <summary>
+ /// Check if given provider is registered.
+ /// </summary>
+ /// <param name="providerId"></param>
+ /// <returns></returns>
+ bool is_registered(const std::string &providerId)
+ {
+ std::lock_guard<std::mutex> lock(m_providerMapLock);
+ auto it = providers().find(providerId);
+ if (it != providers().end())
+ {
+ if (it->second.providerHandle != INVALID_HANDLE)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Get Provider by Name or string representation of GUID
+ /// </summary>
+ /// <param name="providerId"></param>
+ /// <returns></returns>
+ Handle &open(const std::string &providerId, EventFormat format = EventFormat::ETW_MSGPACK)
+ {
+ std::lock_guard<std::mutex> lock(m_providerMapLock);
+
+ // Check and return if provider is already registered
+ auto it = providers().find(providerId);
+ if (it != providers().end())
+ {
+ if (it->second.providerHandle != INVALID_HANDLE)
+ {
+ it->second.refCount++;
+ return it->second;
+ }
+ }
+
+ // Register provider if necessary
+ auto &data = providers()[providerId];
+ data.providerMetaVector.clear();
+
+ utils::UUID guid = (providerId.rfind("{", 0) == 0) ? utils::UUID(providerId.c_str())
+ : // It's a ProviderGUID
+ utils::GetProviderGuid(providerId.c_str()); // It's a ProviderName
+
+ data.providerGuid = guid.to_GUID();
+
+ // TODO: currently we do not allow to specify a custom group GUID
+ GUID providerGroupGuid = NULL_GUID;
+
+ switch (format)
+ {
+ // Register with TraceLoggingDynamic facility - dynamic manifest ETW events.
+ case EventFormat::ETW_MANIFEST: {
+ tld::ProviderMetadataBuilder<std::vector<BYTE>> providerMetaBuilder(
+ data.providerMetaVector);
+
+ // Use Tenant ID as provider Name
+ providerMetaBuilder.Begin(providerId.c_str());
+ providerMetaBuilder.AddTrait(tld::ProviderTraitType::ProviderTraitGroupGuid,
+ (void *)&providerGroupGuid, sizeof(GUID));
+ providerMetaBuilder.End();
+
+ REGHANDLE hProvider = 0;
+ if (0 !=
+ tld::RegisterProvider(&hProvider, &data.providerGuid, data.providerMetaVector.data()))
+ {
+ // There was an error registering the ETW provider
+ data.refCount = 0;
+ data.providerHandle = INVALID_HANDLE;
+ }
+ else
+ {
+ data.refCount = 1;
+ data.providerHandle = hProvider;
+ }
+ }
+ break;
+
+#ifdef HAVE_MSGPACK
+ // Register for MsgPack payload ETW events.
+ case EventFormat::ETW_MSGPACK: {
+ REGHANDLE hProvider = 0;
+ if (EventRegister(&data.providerGuid, NULL, NULL, &hProvider) != ERROR_SUCCESS)
+ {
+ // There was an error registering the ETW provider
+ data.refCount = 0;
+ data.providerHandle = INVALID_HANDLE;
+ }
+ else
+ {
+ data.refCount = 1;
+ data.providerHandle = hProvider;
+ }
+ }
+ break;
+#endif
+
+ default:
+ // TODO: other protocols, e.g. XML events - not supported yet
+ break;
+ }
+
+ // We always return an entry even if we failed to register.
+ // Caller should check whether the hProvider handle is valid.
+ return data;
+ }
+
+ /**
+ * @brief Unregister provider
+ * @param data Provider Handle
+ * @return status code
+ */
+ unsigned long close(Handle handle)
+ {
+ std::lock_guard<std::mutex> lock(m_providerMapLock);
+
+ // use reference to provider list, NOT it' copy.
+ auto &m = providers();
+ auto it = m.begin();
+ while (it != m.end())
+ {
+ if (it->second.providerHandle == handle.providerHandle)
+ {
+ // reference to item in the map of open provider handles
+ auto &data = it->second;
+ unsigned long result = STATUS_OK;
+
+ data.refCount--;
+ if (data.refCount == 0)
+ {
+ if (data.providerMetaVector.size())
+ {
+ // ETW/TraceLoggingDynamic provider
+ result = tld::UnregisterProvider(data.providerHandle);
+ }
+ else
+ {
+ // Other provider types, e.g. ETW/MsgPack
+ result = EventUnregister(data.providerHandle);
+ }
+
+ it->second.providerHandle = INVALID_HANDLE;
+ if (result == STATUS_OK)
+ {
+ m.erase(it);
+ }
+ }
+ return result;
+ }
+ }
+ return STATUS_ERROR;
+ }
+
+ unsigned long writeMsgPack(Handle &providerData,
+ exporter::etw::Properties &eventData,
+ LPCGUID ActivityId = nullptr,
+ LPCGUID RelatedActivityId = nullptr,
+ uint8_t Opcode = 0)
+ {
+#ifdef HAVE_MSGPACK
+ // Events can only be sent if provider is registered
+ if (providerData.providerHandle == INVALID_HANDLE)
+ {
+ // Provider not registered!
+ return STATUS_ERROR;
+ }
+
+ std::string eventName = "NoName";
+ auto nameField = eventData[ETW_FIELD_NAME];
+
+# ifdef HAVE_FIELD_TIME
+ // Event time is appended by ETW layer itself by default. This code allows
+ // to override the timestamp by millisecond timestamp, in case if it has
+ // not been already provided by the upper layer.
+ if (!eventData.count(ETW_FIELD_TIME))
+ {
+ // TODO: if nanoseconds resolution is required, then we can populate it in 96-bit MsgPack
+ // spec. auto nanos =
+ // std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
+ eventData[ETW_FIELD_TIME] = opentelemetry::utils::getUtcSystemTimeMs();
+ }
+# endif
+
+ switch (nameField.index())
+ {
+ case PropertyType::kTypeString:
+ eventName = (char *)(nostd::get<std::string>(nameField).data()); // must be 0-terminated!
+ break;
+ case PropertyType::kTypeCString:
+ eventName = (char *)(nostd::get<const char *>(nameField));
+ break;
+ default:
+ // If invalid event name is supplied, then we replace it with 'NoName'
+ break;
+ }
+
+ /* clang-format off */
+ nlohmann::json jObj =
+ {
+ { ETW_FIELD_NAME, eventName },
+ { ETW_FIELD_OPCODE, Opcode }
+ };
+ /* clang-format on */
+
+ std::string eventFieldName(ETW_FIELD_NAME);
+ for (auto &kv : eventData)
+ {
+ const char *name = kv.first.data();
+
+ // Don't include event name field in the Payload section
+ if (eventFieldName == name)
+ continue;
+
+ auto &value = kv.second;
+ switch (value.index())
+ {
+ case PropertyType::kTypeBool: {
+ UINT8 temp = static_cast<UINT8>(nostd::get<bool>(value));
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeInt: {
+ auto temp = nostd::get<int32_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeInt64: {
+ auto temp = nostd::get<int64_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeUInt: {
+ auto temp = nostd::get<uint32_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeUInt64: {
+ auto temp = nostd::get<uint64_t>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeDouble: {
+ auto temp = nostd::get<double>(value);
+ jObj[name] = temp;
+ break;
+ }
+ case PropertyType::kTypeString: {
+ jObj[name] = nostd::get<std::string>(value);
+ break;
+ }
+ case PropertyType::kTypeCString: {
+ auto temp = nostd::get<const char *>(value);
+ jObj[name] = temp;
+ break;
+ }
+# if HAVE_TYPE_GUID
+ // TODO: consider adding UUID/GUID to spec
+ case common::AttributeType::TYPE_GUID: {
+ auto temp = nostd::get<GUID>(value);
+ // TODO: add transform from GUID type to string?
+ jObj[name] = temp;
+ break;
+ }
+# endif
+
+ // TODO: arrays are not supported yet
+ case PropertyType::kTypeSpanByte:
+ case PropertyType::kTypeSpanBool:
+ case PropertyType::kTypeSpanInt:
+ case PropertyType::kTypeSpanInt64:
+ case PropertyType::kTypeSpanUInt:
+ case PropertyType::kTypeSpanUInt64:
+ case PropertyType::kTypeSpanDouble:
+ case PropertyType::kTypeSpanString:
+ default:
+ // TODO: unsupported type
+ break;
+ }
+ }
+
+ std::vector<uint8_t> v = nlohmann::json::to_msgpack(jObj);
+
+ EVENT_DESCRIPTOR evtDescriptor;
+ // TODO: event descriptor may be populated with additional values as follows:
+ // Id - if 0, auto-incremented sequence number that uniquely identifies event in a trace
+ // Version - event version
+ // Channel - 11 for TraceLogging
+ // Level - verbosity level
+ // Task - TaskId
+ // Opcode - described in evntprov.h:259 : 0 - info, 1 - activity start, 2 - activity stop.
+ EventDescCreate(&evtDescriptor, 0, 0x1, 0, 0, 0, Opcode, 0);
+ EVENT_DATA_DESCRIPTOR evtData[1];
+ EventDataDescCreate(&evtData[0], v.data(), static_cast<ULONG>(v.size()));
+ ULONG writeResponse = 0;
+ if ((ActivityId != nullptr) || (RelatedActivityId != nullptr))
+ {
+ writeResponse = EventWriteTransfer(providerData.providerHandle, &evtDescriptor, ActivityId,
+ RelatedActivityId, 1, evtData);
+ }
+ else
+ {
+ writeResponse = EventWrite(providerData.providerHandle, &evtDescriptor, 1, evtData);
+ }
+
+ switch (writeResponse)
+ {
+ case ERROR_INVALID_PARAMETER:
+ break;
+ case ERROR_INVALID_HANDLE:
+ break;
+ case ERROR_ARITHMETIC_OVERFLOW:
+ break;
+ case ERROR_MORE_DATA:
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ break;
+ default:
+ break;
+ }
+
+ if (writeResponse == ERROR_ARITHMETIC_OVERFLOW)
+ {
+ return STATUS_EFBIG;
+ }
+ return (unsigned long)(writeResponse);
+#else
+ UNREFERENCED_PARAMETER(providerData);
+ UNREFERENCED_PARAMETER(eventData);
+ UNREFERENCED_PARAMETER(ActivityId);
+ UNREFERENCED_PARAMETER(RelatedActivityId);
+ UNREFERENCED_PARAMETER(Opcode);
+ return STATUS_ERROR;
+#endif
+ }
+
+ /// <summary>
+ /// Send event to Provider Id
+ /// </summary>
+ /// <param name="providerId"></param>
+ /// <param name="eventData"></param>
+ /// <returns></returns>
+ unsigned long writeTld(Handle &providerData,
+ Properties &eventData,
+ LPCGUID ActivityId = nullptr,
+ LPCGUID RelatedActivityId = nullptr,
+ uint8_t Opcode = 0 /* Information */)
+ {
+ // Make sure you stop sending event before register unregistering providerData
+ if (providerData.providerHandle == INVALID_HANDLE)
+ {
+ // Provider not registered!
+ return STATUS_ERROR;
+ }
+
+ UINT32 eventTags = MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE;
+
+ std::vector<BYTE> byteVector;
+ std::vector<BYTE> byteDataVector;
+ tld::EventMetadataBuilder<std::vector<BYTE>> builder(byteVector);
+ tld::EventDataBuilder<std::vector<BYTE>> dbuilder(byteDataVector);
+
+ const std::string EVENT_NAME = ETW_FIELD_NAME;
+ std::string eventName = "NoName";
+ auto nameField = eventData[EVENT_NAME];
+ switch (nameField.index())
+ {
+ case PropertyType::kTypeString:
+ eventName = (char *)(nostd::get<std::string>(nameField).data());
+ break;
+ case PropertyType::kTypeCString:
+ eventName = (char *)(nostd::get<const char *>(nameField));
+ break;
+ default:
+ // This is user error. Invalid event name!
+ // We supply default 'NoName' event name in this case.
+ break;
+ }
+
+ builder.Begin(eventName.c_str(), eventTags);
+
+ for (auto &kv : eventData)
+ {
+ const char *name = kv.first.data();
+ auto &value = kv.second;
+ switch (value.index())
+ {
+ case PropertyType::kTypeBool: {
+ builder.AddField(name, tld::TypeBool8);
+ UINT8 temp = static_cast<UINT8>(nostd::get<bool>(value));
+ dbuilder.AddByte(temp);
+ break;
+ }
+ case PropertyType::kTypeInt: {
+ builder.AddField(name, tld::TypeInt32);
+ auto temp = nostd::get<int32_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeInt64: {
+ builder.AddField(name, tld::TypeInt64);
+ auto temp = nostd::get<int64_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeUInt: {
+ builder.AddField(name, tld::TypeUInt32);
+ auto temp = nostd::get<uint32_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeUInt64: {
+ builder.AddField(name, tld::TypeUInt64);
+ auto temp = nostd::get<uint64_t>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeDouble: {
+ builder.AddField(name, tld::TypeDouble);
+ auto temp = nostd::get<double>(value);
+ dbuilder.AddValue(temp);
+ break;
+ }
+ case PropertyType::kTypeString: {
+ builder.AddField(name, tld::TypeUtf8String);
+ dbuilder.AddString(nostd::get<std::string>(value).data());
+ break;
+ }
+ case PropertyType::kTypeCString: {
+ builder.AddField(name, tld::TypeUtf8String);
+ auto temp = nostd::get<const char *>(value);
+ dbuilder.AddString(temp);
+ break;
+ }
+#if HAVE_TYPE_GUID
+ // TODO: consider adding UUID/GUID to spec
+ case PropertyType::kGUID: {
+ builder.AddField(name.c_str(), TypeGuid);
+ auto temp = nostd::get<GUID>(value);
+ dbuilder.AddBytes(&temp, sizeof(GUID));
+ break;
+ }
+#endif
+
+ // TODO: arrays are not supported
+ case PropertyType::kTypeSpanByte:
+ case PropertyType::kTypeSpanBool:
+ case PropertyType::kTypeSpanInt:
+ case PropertyType::kTypeSpanInt64:
+ case PropertyType::kTypeSpanUInt:
+ case PropertyType::kTypeSpanUInt64:
+ case PropertyType::kTypeSpanDouble:
+ case PropertyType::kTypeSpanString:
+ default:
+ // TODO: unsupported type
+ break;
+ }
+ }
+
+ if (!builder.End()) // Returns false if the metadata is too large.
+ {
+ return STATUS_EFBIG; // if event is too big for UTC to handle
+ }
+
+ tld::EventDescriptor eventDescriptor;
+ eventDescriptor.Opcode = Opcode;
+ eventDescriptor.Level = 0; /* FIXME: Always on for now */
+
+ EVENT_DATA_DESCRIPTOR pDataDescriptors[3];
+ EventDataDescCreate(&pDataDescriptors[2], byteDataVector.data(),
+ static_cast<ULONG>(byteDataVector.size()));
+
+ HRESULT writeResponse = 0;
+ if ((ActivityId != nullptr) || (RelatedActivityId != nullptr))
+ {
+ writeResponse = tld::WriteEvent(providerData.providerHandle, eventDescriptor,
+ providerData.providerMetaVector.data(), byteVector.data(), 3,
+ pDataDescriptors, ActivityId, RelatedActivityId);
+ }
+ else
+ {
+ writeResponse = tld::WriteEvent(providerData.providerHandle, eventDescriptor,
+ providerData.providerMetaVector.data(), byteVector.data(), 3,
+ pDataDescriptors);
+ }
+
+ // Event is larger than ETW max sized of 64KB
+ if (writeResponse == HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW))
+ {
+ return STATUS_EFBIG;
+ }
+
+ return (unsigned long)(writeResponse);
+ }
+
+ unsigned long write(Handle &providerData,
+ Properties &eventData,
+ LPCGUID ActivityId,
+ LPCGUID RelatedActivityId,
+ uint8_t Opcode,
+ ETWProvider::EventFormat format)
+ {
+ if (format == ETWProvider::EventFormat::ETW_MANIFEST)
+ {
+ return writeTld(providerData, eventData, ActivityId, RelatedActivityId, Opcode);
+ }
+ if (format == ETWProvider::EventFormat::ETW_MSGPACK)
+ {
+ return writeMsgPack(providerData, eventData, ActivityId, RelatedActivityId, Opcode);
+ }
+ if (format == ETWProvider::EventFormat::ETW_XML)
+ {
+ // TODO: not implemented
+ return STATUS_ERROR;
+ }
+ return STATUS_ERROR;
+ }
+
+ static const REGHANDLE INVALID_HANDLE = _UI64_MAX;
+
+protected:
+ const unsigned int LargeEventSizeKB = 62;
+
+ const GUID NULL_GUID = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
+
+ mutable std::mutex m_providerMapLock;
+
+ using ProviderMap = std::map<std::string, Handle>;
+
+ ProviderMap &providers()
+ {
+ static std::map<std::string, Handle> providers;
+ return providers;
+ }
+};
+
+OPENTELEMETRY_END_NAMESPACE
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h
new file mode 100644
index 000000000..a6243c46d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_traceloggingdynamic.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#ifdef __has_include
+# if __has_include("TraceLoggingDynamic.h")
+# include "TraceLoggingDynamic.h"
+# ifndef HAVE_TLD
+# define HAVE_TLD
+# endif
+# endif
+#else
+# ifndef HAVE_TLD
+# define HAVE_TLD
+# endif
+# include "TraceLoggingDynamic.h"
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
new file mode 100644
index 000000000..5db31a7a7
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
@@ -0,0 +1,995 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <algorithm>
+#include <atomic>
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <vector>
+
+#include "opentelemetry/nostd/shared_ptr.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/nostd/unique_ptr.h"
+#include "opentelemetry/nostd/variant.h"
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+
+#include "opentelemetry/trace/span.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+#include "opentelemetry/trace/span_id.h"
+#include "opentelemetry/trace/trace_id.h"
+#include "opentelemetry/trace/tracer_provider.h"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/etw/etw_config.h"
+#include "opentelemetry/exporters/etw/etw_fields.h"
+#include "opentelemetry/exporters/etw/etw_properties.h"
+#include "opentelemetry/exporters/etw/etw_provider.h"
+#include "opentelemetry/exporters/etw/utils.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace etw
+{
+
+class Span;
+
+/**
+ * @brief Template that allows to instantiate new Span object for header-only forward-declared
+ * etw::Span type
+ *
+ * @tparam SpanType Expected to be etw::Span
+ * @tparam TracerType expected to be etw::Tracer
+ * @param objPtr Pointer to parent
+ * @param name Span Name
+ * @param options Span Options
+ * @return Span instance
+ */
+template <class SpanType, class TracerType>
+SpanType *new_span(TracerType *objPtr,
+ nostd::string_view name,
+ const opentelemetry::trace::StartSpanOptions &options)
+{
+ return new (std::nothrow) SpanType{*objPtr, name, options};
+}
+
+/**
+ * @brief Template that allows to convert etw::Span pointer to smart shared pointer to
+ * `opentelemetry::trace::Span`
+ * @tparam SpanType Expected to be etw::Span
+ * @param ptr Pointer to etw::Span
+ * @return Smart shared pointer to `opentelemetry::trace::Span`
+ */
+template <class SpanType>
+nostd::shared_ptr<opentelemetry::trace::Span> to_span_ptr(SpanType *ptr)
+{
+ return nostd::shared_ptr<opentelemetry::trace::Span>{ptr};
+}
+
+class TracerProvider;
+
+/**
+ * @brief Utility template for obtaining Span Name
+ * @tparam T etw::Span
+ * @param t instance of etw::Span
+ * @return Span Name
+ */
+template <class T>
+std::string GetName(T &t)
+{
+ auto sV = t.GetName();
+ return std::string(sV.data(), sV.length());
+}
+
+/**
+ * @brief Utility template to obtain Span start time
+ * @tparam T etw::Span
+ * @param t instance of etw::Span
+ * @return Span Start timestamp
+ */
+template <class T>
+common::SystemTimestamp GetStartTime(T &t)
+{
+ return t.GetStartTime();
+}
+
+/**
+ * @brief Utility template to obtain Span end time
+ * @tparam T etw::Span
+ * @param t instance of etw::Span
+ * @return Span Stop timestamp
+ */
+template <class T>
+common::SystemTimestamp GetEndTime(T &t)
+{
+ return t.GetEndTime();
+}
+
+class Properties;
+
+/**
+ * @brief Utility template to store Attributes on Span
+ * @tparam T etw::Span
+ * @param instance instance of etw::Span
+ * @param t Properties to store as Attributes
+ */
+template <class T>
+void SetSpanAttributes(T &instance, Properties &t)
+{
+ instance.SetAttributes(t);
+}
+
+/**
+ * @brief Utility template to obtain Span Attributes
+ * @tparam T etw::Span
+ * @param instance instance of etw::Span
+ * @return ref to Span Attributes
+ */
+template <class T>
+Properties &GetSpanAttributes(T &instance)
+{
+ return instance.GetAttributes();
+}
+
+template <class T>
+void UpdateStatus(T &t, Properties &props)
+{
+ t.UpdateStatus(props);
+}
+
+/**
+ * @brief Tracer class that allows to send spans to ETW Provider.
+ */
+class Tracer : public opentelemetry::trace::Tracer
+{
+
+ /**
+ * @brief Parent provider of this Tracer
+ */
+ etw::TracerProvider &tracerProvider_;
+
+ /**
+ * @brief ProviderId (Name or GUID)
+ */
+ std::string provId;
+
+ /**
+ * @brief Encoding (Manifest, MessagePack or XML)
+ */
+ ETWProvider::EventFormat encoding;
+
+ /**
+ * @brief Provider Handle
+ */
+ ETWProvider::Handle &provHandle;
+
+ opentelemetry::trace::TraceId traceId_;
+
+ std::atomic<bool> isClosed_{true};
+
+ /**
+ * @brief ETWProvider is a singleton that aggregates all ETW writes.
+ * @return
+ */
+ static ETWProvider &etwProvider()
+ {
+ static ETWProvider instance; // C++11 magic static
+ return instance;
+ }
+
+ /**
+ * @brief Internal method that allows to populate Links to other Spans.
+ * Span links are in hexadecimal representation, comma-separated in their
+ * order of appearance.
+ *
+ * @param attributes
+ * @param links
+ */
+ virtual void DecorateLinks(Properties &attributes,
+ const opentelemetry::trace::SpanContextKeyValueIterable &links) const
+ {
+ // Add `SpanLinks` attribute if the list is not empty
+ if (links.size())
+ {
+ size_t idx = 0;
+ std::string linksValue;
+ links.ForEachKeyValue(
+ [&](opentelemetry::trace::SpanContext ctx, const common::KeyValueIterable &) {
+ if (!linksValue.empty())
+ {
+ linksValue += ',';
+ linksValue += ToLowerBase16(ctx.span_id());
+ }
+ idx++;
+ return true;
+ });
+ attributes[ETW_FIELD_SPAN_LINKS] = linksValue;
+ }
+ }
+
+ /**
+ * @brief Allow our friendly etw::Span to end itself on Tracer.
+ * @param span
+ * @param
+ */
+ virtual void EndSpan(const Span &span,
+ const opentelemetry::trace::Span *parentSpan = nullptr,
+ const opentelemetry::trace::EndSpanOptions & = {})
+ {
+ const auto &cfg = GetConfiguration(tracerProvider_);
+ const opentelemetry::trace::Span &spanBase =
+ reinterpret_cast<const opentelemetry::trace::Span &>(span);
+ auto spanContext = spanBase.GetContext();
+
+ // Populate Span with presaved attributes
+ Span &currentSpan = const_cast<Span &>(span);
+ Properties evt = GetSpanAttributes(currentSpan);
+ evt[ETW_FIELD_NAME] = GetName(span);
+
+ if (cfg.enableSpanId)
+ {
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
+ }
+
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
+ }
+
+ // Populate ActivityId if enabled
+ GUID ActivityId;
+ LPGUID ActivityIdPtr = nullptr;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(spanBase.GetContext().span_id(), ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+
+ // Populate RelatedActivityId if enabled
+ GUID RelatedActivityId;
+ LPGUID RelatedActivityIdPtr = nullptr;
+ if (cfg.enableRelatedActivityId)
+ {
+ if (parentSpan != nullptr)
+ {
+ if (CopySpanIdToActivityId(parentSpan->GetContext().span_id(), RelatedActivityId))
+ {
+ RelatedActivityIdPtr = &RelatedActivityId;
+ }
+ }
+ }
+
+ if (cfg.enableActivityTracking)
+ {
+ // TODO: check what EndSpanOptions should be supported for this exporter.
+ // The only option available currently (end_steady_time) does not apply.
+ //
+ // This event on Span Stop enables generation of "non-transactional"
+ // OpCode=Stop in alignment with TraceLogging Activity "EventSource"
+ // spec.
+ etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 2, encoding);
+ }
+
+ {
+ // Now since the span has ended, we need to emit the "Span" event that
+ // contains the entire span information, attributes, time, etc. on it.
+ evt[ETW_FIELD_NAME] = ETW_VALUE_SPAN;
+ evt[ETW_FIELD_PAYLOAD_NAME] = GetName(span);
+
+ // Add timing details in ISO8601 format, which adequately represents
+ // the actual time, taking Timezone into consideration. This is NOT
+ // local time, but rather UTC time (Z=0).
+ std::chrono::system_clock::time_point startTime = GetStartTime(currentSpan);
+ std::chrono::system_clock::time_point endTime = GetEndTime(currentSpan);
+ int64_t startTimeMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(startTime.time_since_epoch())
+ .count();
+ int64_t endTimeMs =
+ std::chrono::duration_cast<std::chrono::milliseconds>(endTime.time_since_epoch()).count();
+
+ // It may be more optimal to enable passing timestamps as UTC milliseconds
+ // since Unix epoch instead of string, but that implies additional tooling
+ // is needed to convert it, rendering it NOT human-readable.
+ evt[ETW_FIELD_STARTTIME] = utils::formatUtcTimestampMsAsISO8601(startTimeMs);
+#ifdef ETW_FIELD_ENDTTIME
+ // ETW has its own precise timestamp at envelope layer for every event.
+ // However, in some scenarios it is easier to deal with ISO8601 strings.
+ // In that case we convert the app-created timestamp and place it into
+ // Payload[$ETW_FIELD_TIME] field. The option configurable at compile-time.
+ evt[ETW_FIELD_ENDTTIME] = utils::formatUtcTimestampMsAsISO8601(endTimeMs);
+#endif
+ // Duration of Span in milliseconds
+ evt[ETW_FIELD_DURATION] = endTimeMs - startTimeMs;
+ // Presently we assume that all spans are server spans
+ evt[ETW_FIELD_SPAN_KIND] = uint32_t(opentelemetry::trace::SpanKind::kServer);
+ UpdateStatus(currentSpan, evt);
+ etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 0, encoding);
+ }
+ }
+
+ const opentelemetry::trace::TraceId &trace_id() { return traceId_; }
+
+ friend class Span;
+
+ /**
+ * @brief Init a reference to etw::ProviderHandle
+ * @return Provider Handle
+ */
+ ETWProvider::Handle &initProvHandle()
+ {
+ isClosed_ = false;
+ return etwProvider().open(provId, encoding);
+ }
+
+public:
+ /**
+ * @brief Tracer constructor
+ * @param parent Parent TraceProvider
+ * @param providerId ProviderId - Name or GUID
+ * @param encoding ETW encoding format to use.
+ */
+ Tracer(etw::TracerProvider &parent,
+ nostd::string_view providerId = "",
+ ETWProvider::EventFormat encoding = ETWProvider::EventFormat::ETW_MANIFEST)
+ : opentelemetry::trace::Tracer(),
+ tracerProvider_(parent),
+ provId(providerId.data(), providerId.size()),
+ encoding(encoding),
+ provHandle(initProvHandle())
+ {
+ // Generate random GUID
+ GUID trace_id;
+ CoCreateGuid(&trace_id);
+ // Populate TraceId of the Tracer with the above GUID
+ const auto *traceIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(trace_id));
+ nostd::span<const uint8_t, opentelemetry::trace::TraceId::kSize> traceIdBytes(
+ traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize);
+ traceId_ = opentelemetry::trace::TraceId(traceIdBytes);
+ }
+
+ /**
+ * @brief Start Span
+ * @param name Span name
+ * @param attributes Span attributes
+ * @param links Span links
+ * @param options Span options
+ * @return
+ */
+ nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
+ nostd::string_view name,
+ const common::KeyValueIterable &attributes,
+ const opentelemetry::trace::SpanContextKeyValueIterable &links,
+ const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
+ {
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return StartSpan(name, *evt, links, options);
+ }
+#endif
+ Properties evtCopy = attributes;
+ return StartSpan(name, evtCopy, links, options);
+ }
+
+ /**
+ * @brief Start Span
+ * @param name Span name
+ * @param attributes Span attributes
+ * @param links Span links
+ * @param options Span options
+ * @return
+ */
+ virtual nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
+ nostd::string_view name,
+ Properties &evt,
+ const opentelemetry::trace::SpanContextKeyValueIterable &links,
+ const opentelemetry::trace::StartSpanOptions &options = {}) noexcept
+ {
+ const auto &cfg = GetConfiguration(tracerProvider_);
+
+ // Parent Context:
+ // - either use current span
+ // - or attach to parent SpanContext specified in options
+ opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
+ if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
+ {
+ auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
+ if (span_context.IsValid())
+ {
+ parentContext = span_context;
+ }
+ }
+
+ // Populate Etw.RelatedActivityId at envelope level if enabled
+ GUID RelatedActivityId;
+ LPCGUID RelatedActivityIdPtr = nullptr;
+ if (cfg.enableAutoParent)
+ {
+ if (cfg.enableRelatedActivityId)
+ {
+ if (CopySpanIdToActivityId(parentContext.span_id(), RelatedActivityId))
+ {
+ RelatedActivityIdPtr = &RelatedActivityId;
+ }
+ }
+ }
+
+ // This template pattern allows us to forward-declare the etw::Span,
+ // create an instance of it, then assign it to tracer::Span result.
+ auto currentSpan = new_span<Span, Tracer>(this, name, options);
+ nostd::shared_ptr<opentelemetry::trace::Span> result = to_span_ptr<Span>(currentSpan);
+
+ auto spanContext = result->GetContext();
+
+ // Decorate with additional standard fields
+ std::string eventName = name.data();
+
+ // Populate Etw.EventName attribute at envelope level
+ evt[ETW_FIELD_NAME] = eventName;
+
+ // Populate Payload["SpanId"] attribute
+ // Populate Payload["ParentSpanId"] attribute if parent Span is valid
+ if (cfg.enableSpanId)
+ {
+ if (parentContext.IsValid())
+ {
+ evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id());
+ }
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
+ }
+
+ // Populate Etw.Payload["TraceId"] attribute
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
+ }
+
+ // Populate Etw.ActivityId at envelope level if enabled
+ GUID ActivityId;
+ LPCGUID ActivityIdPtr = nullptr;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(result.get()->GetContext().span_id(), ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+
+ // Links
+ DecorateLinks(evt, links);
+
+ // Remember Span attributes to be passed down to ETW on Span end
+ SetSpanAttributes(*currentSpan, evt);
+
+ if (cfg.enableActivityTracking)
+ {
+ // TODO: add support for options that are presently ignored :
+ // - options.kind
+ // - options.start_steady_time
+ // - options.start_system_time
+ etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding);
+ }
+
+ return result;
+ }
+
+ /**
+ * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush.
+ * NOTE: this method has no effect for the realtime streaming Tracer.
+ *
+ * @param timeout Allow Tracer to drop data if timeout is reached
+ * @return
+ */
+ void ForceFlushWithMicroseconds(uint64_t) noexcept override {}
+
+ /**
+ * @brief Close tracer, spending up to given amount of microseconds to flush and close.
+ * NOTE: This method decrements the reference count on current ETW Provider Handle and
+ * closes it if reference count on that provider handle is zero.
+ *
+ * @param timeout Allow Tracer to drop data if timeout is reached.
+ * @return
+ */
+ void CloseWithMicroseconds(uint64_t) noexcept override
+ {
+ // Close once only
+ if (!isClosed_.exchange(true))
+ {
+ etwProvider().close(provHandle);
+ }
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Parent span.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @param attributes Event attributes.
+ * @return
+ */
+ void AddEvent(opentelemetry::trace::Span &span,
+ nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept
+ {
+#ifdef OPENTELEMETRY_RTTI_ENABLED
+ common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
+ Properties *evt = dynamic_cast<Properties *>(&attribs);
+ if (evt != nullptr)
+ {
+ // Pass as a reference to original modifyable collection without creating a copy
+ return AddEvent(span, name, timestamp, *evt);
+ }
+#endif
+ // Pass a copy converted to Properties object on stack
+ Properties evtCopy = attributes;
+ return AddEvent(span, name, timestamp, evtCopy);
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Parent span.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @param attributes Event attributes.
+ * @return
+ */
+ void AddEvent(opentelemetry::trace::Span &span,
+ nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ Properties &evt) noexcept
+ {
+ // TODO: respect originating timestamp. Do we need to reserve
+ // a special 'Timestamp' field or is it an overkill? The delta
+ // between when `AddEvent` API is called and when ETW layer
+ // timestamp is appended is nanos- to micros-, thus handling
+ // the explicitly provided timestamp is only necessary in case
+ // if a process wants to submit back-dated or future-dated
+ // timestamp. Unless there is a strong ask from any ETW customer
+ // to have it, this feature (custom timestamp) remains unimplemented.
+ (void)timestamp;
+
+ const auto &cfg = GetConfiguration(tracerProvider_);
+
+ evt[ETW_FIELD_NAME] = name.data();
+
+ const auto &spanContext = span.GetContext();
+ if (cfg.enableSpanId)
+ {
+ evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
+ }
+
+ if (cfg.enableTraceId)
+ {
+ evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
+ }
+
+ LPGUID ActivityIdPtr = nullptr;
+ GUID ActivityId;
+ if (cfg.enableActivityId)
+ {
+ if (CopySpanIdToActivityId(spanContext.span_id(), ActivityId))
+ {
+ ActivityIdPtr = &ActivityId;
+ }
+ }
+
+#ifdef HAVE_FIELD_TIME
+ {
+ auto timeNow = std::chrono::system_clock::now().time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(timeNow).count();
+ evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis);
+ }
+#endif
+
+ etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding);
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Span.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @return
+ */
+ void AddEvent(opentelemetry::trace::Span &span,
+ nostd::string_view name,
+ common::SystemTimestamp timestamp) noexcept
+ {
+ AddEvent(span, name, timestamp, sdk::GetEmptyAttributes());
+ }
+
+ /**
+ * @brief Add event data to span associated with tracer.
+ * @param span Spab.
+ * @param name Event name.
+ */
+ void AddEvent(opentelemetry::trace::Span &span, nostd::string_view name)
+ {
+ AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes());
+ }
+
+ /**
+ * @brief Tracer destructor.
+ */
+ virtual ~Tracer() { CloseWithMicroseconds(0); }
+};
+
+/**
+ * @brief etw::Span allows to send event data to ETW listener.
+ */
+class Span : public opentelemetry::trace::Span
+{
+protected:
+ friend class Tracer;
+
+ /**
+ * @brief Span properties are attached on "Span" event on end of Span.
+ */
+ Properties attributes_;
+
+ common::SystemTimestamp start_time_;
+ common::SystemTimestamp end_time_;
+
+ opentelemetry::trace::StatusCode status_code_{opentelemetry::trace::StatusCode::kUnset};
+ std::string status_description_;
+
+ /**
+ * @brief Owner Tracer of this Span
+ */
+ Tracer &owner_;
+
+ /**
+ * @brief Span name.
+ */
+ nostd::string_view name_;
+
+ /**
+ * @brief Attribute indicating that the span has ended.
+ */
+ std::atomic<bool> has_ended_{false};
+
+ /**
+ * @brief Attribute indicating that the span has started.
+ */
+ std::atomic<bool> has_started_{false};
+
+ /**
+ * @brief Parent Span of this nested Span (optional)
+ */
+ Span *parent_{nullptr};
+
+ /**
+ * @brief Get Parent Span of this nested Span.
+ * @return Pointer to Parent or nullptr if no Parent.
+ */
+ Span *GetParent() const { return parent_; }
+
+ opentelemetry::trace::SpanContext context_;
+
+ const opentelemetry::trace::SpanContext CreateContext()
+ {
+ GUID activity_id;
+ // Generate random GUID
+ CoCreateGuid(&activity_id);
+ const auto *activityIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(activity_id));
+
+ // Populate SpanId with that GUID
+ nostd::span<const uint8_t, opentelemetry::trace::SpanId::kSize> spanIdBytes(
+ activityIdPtr, activityIdPtr + opentelemetry::trace::SpanId::kSize);
+ const opentelemetry::trace::SpanId spanId(spanIdBytes);
+
+ // Inherit trace_id from Tracer
+ const opentelemetry::trace::TraceId traceId{owner_.trace_id()};
+ // TODO: TraceFlags are not supported by ETW exporter.
+ const opentelemetry::trace::TraceFlags flags{0};
+ // TODO: Remote parent is not supported by ETW exporter.
+ const bool hasRemoteParent = false;
+ return opentelemetry::trace::SpanContext{traceId, spanId, flags, hasRemoteParent};
+ }
+
+public:
+ /**
+ * @brief Update Properties object with current Span status
+ * @param evt
+ */
+ void UpdateStatus(Properties &evt)
+ {
+ /* Should we avoid populating this extra field if status is unset? */
+ if ((status_code_ == opentelemetry::trace::StatusCode::kUnset) ||
+ (status_code_ == opentelemetry::trace::StatusCode::kOk))
+ {
+ evt[ETW_FIELD_SUCCESS] = "True";
+ evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_);
+ evt[ETW_FIELD_STATUSMESSAGE] = status_description_;
+ }
+ else
+ {
+ evt[ETW_FIELD_SUCCESS] = "False";
+ evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_);
+ evt[ETW_FIELD_STATUSMESSAGE] = status_description_;
+ }
+ }
+
+ /**
+ * @brief Get start time of this Span.
+ * @return
+ */
+ common::SystemTimestamp GetStartTime() { return start_time_; }
+
+ /**
+ * @brief Get end time of this Span.
+ * @return
+ */
+ common::SystemTimestamp GetEndTime() { return end_time_; }
+
+ /**
+ * @brief Get Span Name.
+ * @return Span Name.
+ */
+ nostd::string_view GetName() const { return name_; }
+
+ /**
+ * @brief Span constructor
+ * @param owner Owner Tracer
+ * @param name Span name
+ * @param options Span options
+ * @param parent Parent Span (optional)
+ * @return
+ */
+ Span(Tracer &owner,
+ nostd::string_view name,
+ const opentelemetry::trace::StartSpanOptions &options,
+ Span *parent = nullptr) noexcept
+ : opentelemetry::trace::Span(),
+ start_time_(std::chrono::system_clock::now()),
+ owner_(owner),
+ parent_(parent),
+ context_(CreateContext())
+ {
+ name_ = name;
+ UNREFERENCED_PARAMETER(options);
+ }
+
+ /**
+ * @brief Span Destructor
+ */
+ ~Span() { End(); }
+
+ /**
+ * @brief Add named event with no attributes.
+ * @param name Event name.
+ * @return
+ */
+ void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); }
+
+ /**
+ * @brief Add named event with custom timestamp.
+ * @param name
+ * @param timestamp
+ * @return
+ */
+ void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override
+ {
+ owner_.AddEvent(*this, name, timestamp);
+ }
+
+ /**
+ * @brief Add named event with custom timestamp and attributes.
+ * @param name Event name.
+ * @param timestamp Event timestamp.
+ * @param attributes Event attributes.
+ * @return
+ */
+ void AddEvent(nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept override
+ {
+ owner_.AddEvent(*this, name, timestamp, attributes);
+ }
+
+ /**
+ * @brief Set Span status
+ * @param code Span status code.
+ * @param description Span description.
+ * @return
+ */
+ void SetStatus(opentelemetry::trace::StatusCode code,
+ nostd::string_view description) noexcept override
+ {
+ status_code_ = code;
+ status_description_ = description.data();
+ }
+
+ void SetAttributes(Properties attributes) { attributes_ = attributes; }
+
+ /**
+ * @brief Obtain span attributes specified at Span start.
+ * NOTE: please consider that this method is NOT thread-safe.
+ *
+ * @return ref to Properties collection
+ */
+ Properties &GetAttributes() { return attributes_; }
+
+ /**
+ * @brief Sets an attribute on the Span. If the Span previously contained a mapping
+ * for the key, the old value is replaced.
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override
+ {
+ // don't override fields propagated from span data.
+ if (key == ETW_FIELD_NAME || key == ETW_FIELD_SPAN_ID || key == ETW_FIELD_TRACE_ID)
+ {
+ return;
+ }
+ attributes_[std::string{key}].FromAttributeValue(value);
+ }
+
+ /**
+ * @brief Update Span name.
+ *
+ * NOTE: this method is a no-op for streaming implementation.
+ * We cannot change the Span name after it started streaming.
+ *
+ * @param name
+ * @return
+ */
+ void UpdateName(nostd::string_view) noexcept override
+ {
+ // We can't do that!
+ // name_ = name;
+ }
+
+ /**
+ * @brief End Span.
+ * @param EndSpanOptions
+ * @return
+ */
+ void End(const opentelemetry::trace::EndSpanOptions &options = {}) noexcept override
+ {
+ end_time_ = std::chrono::system_clock::now();
+
+ if (!has_ended_.exchange(true))
+ {
+ owner_.EndSpan(*this, parent_, options);
+ }
+ }
+
+ /**
+ * @brief Obtain SpanContext
+ * @return
+ */
+ opentelemetry::trace::SpanContext GetContext() const noexcept override { return context_; }
+
+ /**
+ * @brief Check if Span is recording data.
+ * @return
+ */
+ bool IsRecording() const noexcept override
+ {
+ // For streaming implementation this should return the state of ETW Listener.
+ // In certain unprivileged environments, ex. containers, it is impossible
+ // to determine if a listener is registered. Thus, we always return true.
+ return true;
+ }
+
+ virtual void SetToken(nostd::unique_ptr<context::Token> &&token) noexcept
+ {
+ // TODO: not implemented
+ UNREFERENCED_PARAMETER(token);
+ }
+
+ /// <summary>
+ /// Get Owner tracer of this Span
+ /// </summary>
+ /// <returns></returns>
+ opentelemetry::trace::Tracer &tracer() const noexcept { return this->owner_; }
+};
+
+/**
+ * @brief ETW TracerProvider
+ */
+class TracerProvider : public opentelemetry::trace::TracerProvider
+{
+public:
+ /**
+ * @brief TracerProvider options supplied during initialization.
+ */
+ TelemetryProviderConfiguration config_;
+
+ /**
+ * @brief Construct instance of TracerProvider with given options
+ * @param options Configuration options
+ */
+ TracerProvider(TelemetryProviderOptions options) : opentelemetry::trace::TracerProvider()
+ {
+ // By default we ensure that all events carry their with TraceId and SpanId
+ GetOption(options, "enableTraceId", config_.enableTraceId, true);
+ GetOption(options, "enableSpanId", config_.enableSpanId, true);
+
+ // Backwards-compatibility option that allows to reuse ETW-specific parenting described here:
+ // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid
+ // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer
+
+ // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop
+ // format for every Span emitted.
+ GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false);
+
+ // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL,
+ // ETW gets the identifier from the thread local storage. For details on getting this
+ // identifier, see EventActivityIdControl.
+ GetOption(options, "enableActivityId", config_.enableActivityId, false);
+
+ // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous
+ // component. Use this parameter to link your component's events to the previous component's
+ // events.
+ GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false);
+
+ // When a new Span is started, the current span automatically becomes its parent.
+ GetOption(options, "enableAutoParent", config_.enableAutoParent, false);
+
+ // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc.
+ config_.encoding = GetEncoding(options);
+ }
+
+ TracerProvider() : opentelemetry::trace::TracerProvider()
+ {
+ config_.enableTraceId = true;
+ config_.enableSpanId = true;
+ config_.enableActivityId = false;
+ config_.enableActivityTracking = false;
+ config_.enableRelatedActivityId = false;
+ config_.enableAutoParent = false;
+ config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST;
+ }
+
+ /**
+ * @brief Obtain ETW Tracer.
+ * @param name ProviderId (instrumentation name) - Name or GUID
+ *
+ * @param args Additional arguments that controls `codec` of the provider.
+ * Possible values are:
+ * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events.
+ * - "MSGPACK" - MessagePack-encoded binary payload ETW events.
+ * - "XML" - XML events (reserved for future use)
+ * @return
+ */
+ nostd::shared_ptr<opentelemetry::trace::Tracer> GetTracer(
+ nostd::string_view name,
+ nostd::string_view args = "",
+ nostd::string_view schema_url = "") noexcept override
+ {
+ UNREFERENCED_PARAMETER(args);
+ UNREFERENCED_PARAMETER(schema_url);
+ ETWProvider::EventFormat evtFmt = config_.encoding;
+ return nostd::shared_ptr<opentelemetry::trace::Tracer>{new (std::nothrow)
+ Tracer(*this, name, evtFmt)};
+ }
+};
+
+} // namespace etw
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h
new file mode 100644
index 000000000..b28f1a021
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer_exporter.h
@@ -0,0 +1,28 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#pragma once
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include <mutex>
+
+#include "opentelemetry/nostd/shared_ptr.h"
+#include "opentelemetry/nostd/string_view.h"
+#include "opentelemetry/nostd/unique_ptr.h"
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+#include "opentelemetry/trace/span.h"
+#include "opentelemetry/trace/span_context_kv_iterable_view.h"
+#include "opentelemetry/trace/span_id.h"
+#include "opentelemetry/trace/trace_id.h"
+#include "opentelemetry/trace/tracer_provider.h"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/etw/etw_config.h"
+#include "opentelemetry/exporters/etw/etw_provider.h"
+#include "opentelemetry/exporters/etw/etw_tracer.h"
+
+#include "opentelemetry/exporters/etw/utils.h"
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h
new file mode 100644
index 000000000..4d38c1fb6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/utils.h
@@ -0,0 +1,274 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <algorithm>
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <string>
+
+#include "opentelemetry/common/macros.h"
+#include "opentelemetry/exporters/etw/uuid.h"
+#include "opentelemetry/version.h"
+
+#ifdef _WIN32
+# include <Windows.h>
+# include <evntprov.h>
+# include <wincrypt.h>
+# pragma comment(lib, "Advapi32.lib")
+# pragma comment(lib, "Rpcrt4.lib")
+# include <Objbase.h>
+# pragma comment(lib, "Ole32.Lib")
+#else
+# include <codecvt>
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace utils
+{
+
+/// <summary>
+/// Compile-time constexpr djb2 hash function for strings
+/// </summary>
+static constexpr uint32_t hashCode(const char *str, uint32_t h = 0)
+{
+ return (uint32_t)(!str[h] ? 5381 : ((uint32_t)hashCode(str, h + 1) * (uint32_t)33) ^ str[h]);
+}
+
+#define CONST_UINT32_T(x) std::integral_constant<uint32_t, (uint32_t)x>::value
+
+#define CONST_HASHCODE(name) CONST_UINT32_T(OPENTELEMETRY_NAMESPACE::utils::hashCode(#name))
+
+#ifdef _WIN32
+
+/// <summary>
+/// Compute SHA-1 hash of input buffer and save to output
+/// </summary>
+/// <param name="pData">Input buffer</param>
+/// <param name="nData">Input buffer size</param>
+/// <param name="pHashedData">Output buffer</param>
+/// <param name="nHashedData">Output buffer size</param>
+/// <returns></returns>
+static inline bool sha1(const BYTE *pData, DWORD nData, BYTE *pHashedData, DWORD &nHashedData)
+{
+ bool bRet = false;
+ HCRYPTPROV hProv = NULL;
+ HCRYPTHASH hHash = NULL;
+
+ if (!CryptAcquireContext(&hProv, // handle of the CSP
+ NULL, // key container name
+ NULL, // CSP name
+ PROV_RSA_FULL, // provider type
+ CRYPT_VERIFYCONTEXT)) // no key access is requested
+ {
+ bRet = false;
+ goto CleanUp;
+ }
+
+ if (!CryptCreateHash(hProv, // handle of the CSP
+ CALG_SHA1, // hash algorithm to use
+ 0, // hash key
+ 0, // reserved
+ &hHash)) //
+ {
+ bRet = false;
+ goto CleanUp;
+ }
+
+ if (!CryptHashData(hHash, // handle of the HMAC hash object
+ pData, // message to hash
+ nData, // number of bytes of data to add
+ 0)) // flags
+ {
+ bRet = false;
+ goto CleanUp;
+ }
+
+ if (!CryptGetHashParam(hHash, // handle of the HMAC hash object
+ HP_HASHVAL, // query on the hash value
+ pHashedData, // filled on second call
+ &nHashedData, // length, in bytes,of the hash
+ 0))
+ {
+ bRet = false;
+ goto CleanUp;
+ }
+
+ bRet = true;
+
+CleanUp:
+
+ if (hHash)
+ {
+ CryptDestroyHash(hHash);
+ }
+
+ if (hProv)
+ {
+ CryptReleaseContext(hProv, 0);
+ }
+ return bRet;
+}
+
+/// <summary>
+/// Convert UTF-8 string to UTF-16 wide string.
+///
+/// </summary>
+/// <param name="in"></param>
+/// <returns></returns>
+static inline std::wstring to_utf16_string(const std::string &in)
+{
+# ifdef _WIN32
+ int in_length = static_cast<int>(in.size());
+ int out_length = MultiByteToWideChar(CP_UTF8, 0, &in[0], in_length, NULL, 0);
+ std::wstring result(out_length, '\0');
+ MultiByteToWideChar(CP_UTF8, 0, &in[0], in_length, &result[0], out_length);
+ return result;
+# else
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
+ return converter.from_bytes(in);
+# endif
+}
+
+/// <summary>
+/// Transform ETW provider name to provider GUID as described here:
+/// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
+/// </summary>
+/// <param name="providerName"></param>
+/// <returns></returns>
+static inline GUID GetProviderGuid(const char *providerName)
+{
+ std::string name(providerName);
+ std::transform(name.begin(), name.end(), name.begin(),
+ [](unsigned char c) { return (char)::toupper(c); });
+
+ size_t len = name.length() * 2 + 0x10;
+ uint8_t *buffer = new uint8_t[len];
+ uint32_t num = 0x482c2db2;
+ uint32_t num2 = 0xc39047c8;
+ uint32_t num3 = 0x87f81a15;
+ uint32_t num4 = 0xbfc130fb;
+
+ for (int i = 3; i >= 0; i--)
+ {
+ buffer[i] = (uint8_t)num;
+ num = num >> 8;
+ buffer[i + 4] = (uint8_t)num2;
+ num2 = num2 >> 8;
+ buffer[i + 8] = (uint8_t)num3;
+ num3 = num3 >> 8;
+ buffer[i + 12] = (uint8_t)num4;
+ num4 = num4 >> 8;
+ }
+
+ for (size_t j = 0; j < name.length(); j++)
+ {
+ buffer[((2 * j) + 0x10) + 1] = (uint8_t)name[j];
+ buffer[(2 * j) + 0x10] = (uint8_t)(name[j] >> 8);
+ }
+
+ const size_t sha1_hash_size = 21;
+ uint8_t *buffer2 = new uint8_t[sha1_hash_size];
+ DWORD len2 = sha1_hash_size;
+ sha1((const BYTE *)buffer, (DWORD)len, (BYTE *)buffer2, len2);
+
+ unsigned long a = (((((buffer2[3] << 8) + buffer2[2]) << 8) + buffer2[1]) << 8) + buffer2[0];
+ unsigned short b = (unsigned short)((buffer2[5] << 8) + buffer2[4]);
+ unsigned short num9 = (unsigned short)((buffer2[7] << 8) + buffer2[6]);
+
+ GUID guid;
+ guid.Data1 = a;
+ guid.Data2 = b;
+ guid.Data3 = (unsigned short)((num9 & 0xfff) | 0x5000);
+ guid.Data4[0] = buffer2[8];
+ guid.Data4[1] = buffer2[9];
+ guid.Data4[2] = buffer2[10];
+ guid.Data4[3] = buffer2[11];
+ guid.Data4[4] = buffer2[12];
+ guid.Data4[5] = buffer2[13];
+ guid.Data4[6] = buffer2[14];
+ guid.Data4[7] = buffer2[15];
+
+ delete[] buffer;
+ delete[] buffer2;
+
+ return guid;
+}
+#endif
+
+static inline int64_t getUtcSystemTimeMs()
+{
+#ifdef _WIN32
+ ULARGE_INTEGER now;
+ ::GetSystemTimeAsFileTime(reinterpret_cast<FILETIME *>(&now));
+ return (now.QuadPart - 116444736000000000ull) / 10000;
+#else
+ return std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1);
+#endif
+}
+
+static inline int64_t getUtcSystemTimeinTicks()
+{
+#ifdef _WIN32
+ FILETIME tocks;
+ ::GetSystemTimeAsFileTime(&tocks);
+ ULONGLONG ticks = (ULONGLONG(tocks.dwHighDateTime) << 32) | tocks.dwLowDateTime;
+ // number of days from beginning to 1601 multiplied by ticks per day
+ return ticks + 0x701ce1722770000ULL;
+#else
+ // On Un*x systems system_clock de-facto contains UTC time. Ref:
+ // https://en.cppreference.com/w/cpp/chrono/system_clock
+ // This UTC epoch contract has been signed in blood since C++20
+ std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
+ auto duration = now.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
+ uint64_t ticks = millis;
+ ticks *= 10000; // convert millis to ticks (1 tick = 100ns)
+ ticks += 0x89F7FF5F7B58000ULL; // UTC time 0 in .NET ticks
+ return ticks;
+#endif
+}
+
+/**
+ * @brief Convert local system milliseconds time to ISO8601 string UTC time
+ *
+ * @param timestampMs Milliseconds since epoch in system time
+ *
+ * @return ISO8601 UTC string with microseconds part set to 000
+ */
+static inline std::string formatUtcTimestampMsAsISO8601(int64_t timestampMs)
+{
+ char buf[sizeof("YYYY-MM-DDTHH:MM:SS.ssssssZ") + 1] = {0};
+#ifdef _WIN32
+ __time64_t seconds = static_cast<__time64_t>(timestampMs / 1000);
+ int milliseconds = static_cast<int>(timestampMs % 1000);
+ tm tm;
+ if (::_gmtime64_s(&tm, &seconds) != 0)
+ {
+ memset(&tm, 0, sizeof(tm));
+ }
+ ::_snprintf_s(buf, _TRUNCATE, "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm.tm_year,
+ 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 1000 * milliseconds);
+#else
+ time_t seconds = static_cast<time_t>(timestampMs / 1000);
+ int milliseconds = static_cast<int>(timestampMs % 1000);
+ tm tm;
+ bool valid = (gmtime_r(&seconds, &tm) != NULL);
+ if (!valid)
+ {
+ memset(&tm, 0, sizeof(tm));
+ }
+ (void)snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm.tm_year,
+ 1 + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 1000 * milliseconds);
+#endif
+ return buf;
+}
+
+}; // namespace utils
+
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h
new file mode 100644
index 000000000..a004aec9e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/include/opentelemetry/exporters/etw/uuid.h
@@ -0,0 +1,412 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/version.h"
+
+#include <cstddef>
+#include <cstring>
+#include <functional>
+#include <initializer_list>
+#include <string>
+#include <vector>
+
+#ifdef _WIN32
+# include "Windows.h"
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace utils
+{
+
+/// <summary>
+/// The UUID structure represents the portable cross-platform implementation of a GUID (Globally
+/// Unique ID).
+/// </summary>
+/// <remarks>
+/// UUIDs identify objects such as interfaces, manager entry-point vectors (EPVs), and class
+/// objects. A UUID is a 128-bit value consisting of one group of eight hexadecimal digits, followed
+/// by three groups of four hexadecimal digits, each followed by one group of 12 hexadecimal digits.
+/// </remarks>
+#pragma pack(push) /* push current alignment to stack */
+#pragma pack(1) /* set alignment to 1 byte boundary */
+struct UUID
+{
+ /// <summary>
+ /// Specifies the first eight hexadecimal digits of the GUID.
+ /// </summary>
+ uint32_t Data1;
+
+ /// <summary>
+ /// Specifies the first group of four hexadecimal digits.
+ ///</summary>
+ uint16_t Data2;
+
+ /// <summary>
+ /// Specifies the second group of four hexadecimal digits.
+ /// </summary>
+ uint16_t Data3;
+
+ /// <summary>
+ /// An array of eight bytes.
+ /// The first two bytes contain the third group of four hexadecimal digits.
+ /// The remaining six bytes contain the final 12 hexadecimal digits.
+ /// </summary>
+ uint8_t Data4[8];
+
+ /// <summary>
+ /// The default UUID constructor.
+ /// Creates a null instance of the UUID object (initialized to all zeros).
+ /// {00000000-0000-0000-0000-000000000000}.
+ /// </summary>
+ UUID() : Data1(0), Data2(0), Data3(0)
+ {
+ for (size_t i = 0; i < 8; i++)
+ {
+ Data4[i] = 0;
+ }
+ }
+
+ /// <summary>
+ /// A constructor that creates a UUID object from a hyphenated string as defined by
+ /// https://tools.ietf.org/html/rfc4122#page-4
+ /// </summary>
+ /// <param name="uuid_string">A hyphenated string that contains the UUID (curly braces
+ /// optional).</param>
+ UUID(const char *uuid_string)
+ {
+ const char *str = uuid_string;
+ // Skip curly brace
+ if (str[0] == '{')
+ {
+ str++;
+ }
+ // Convert to set of integer values
+ unsigned long p0;
+ unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
+ if (
+ // Parse input with dashes
+ (11 == sscanf_s(str, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2,
+ &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10)) ||
+ // Parse input without dashes
+ (11 == sscanf_s(str, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", &p0, &p1, &p2, &p3,
+ &p4, &p5, &p6, &p7, &p8, &p9, &p10)))
+ {
+ Data1 = static_cast<uint32_t>(p0);
+ Data2 = static_cast<uint16_t>(p1);
+ Data3 = static_cast<uint16_t>(p2);
+ Data4[0] = static_cast<uint8_t>(p3);
+ Data4[1] = static_cast<uint8_t>(p4);
+ Data4[2] = static_cast<uint8_t>(p5);
+ Data4[3] = static_cast<uint8_t>(p6);
+ Data4[4] = static_cast<uint8_t>(p7);
+ Data4[5] = static_cast<uint8_t>(p8);
+ Data4[6] = static_cast<uint8_t>(p9);
+ Data4[7] = static_cast<uint8_t>(p10);
+ }
+ else // Invalid input--use a safe default value
+ {
+ Data1 = 0;
+ Data2 = 0;
+ Data3 = 0;
+ Data4[0] = 0;
+ Data4[1] = 0;
+ Data4[2] = 0;
+ Data4[3] = 0;
+ Data4[4] = 0;
+ Data4[5] = 0;
+ Data4[6] = 0;
+ Data4[7] = 0;
+ }
+ }
+
+ /// <summary>
+ /// A constructor that creates a UUID object from a byte array.
+ /// </summary>
+ /// <param name="guid_bytes">A byte array.</param>
+ /// <param name="bigEndian">
+ /// A boolean value that specifies the byte order.<br>
+ /// A value of <i>true</i> specifies the more natural human-readable order.<br>
+ /// A value of <i>false</i> (the default) specifies the same order as the .NET GUID constructor.
+ /// </param>
+ UUID(const uint8_t guid_bytes[16], bool bigEndian = false)
+ {
+ if (bigEndian)
+ {
+ /* Use big endian - human-readable */
+ // Part 1
+ Data1 = guid_bytes[3];
+ Data1 |= ((uint32_t)(guid_bytes[2])) << 8;
+ Data1 |= ((uint32_t)(guid_bytes[1])) << 16;
+ Data1 |= ((uint32_t)(guid_bytes[0])) << 24;
+ // Part 2
+ Data2 = guid_bytes[5];
+ Data2 |= ((uint16_t)(guid_bytes[4])) << 8;
+ // Part 3
+ Data3 = guid_bytes[7];
+ Data3 |= ((uint16_t)(guid_bytes[6])) << 8;
+ }
+ else
+ {
+ /* Use little endian - the same order as .NET C# Guid() class uses */
+ // Part 1
+ Data1 = guid_bytes[0];
+ Data1 |= ((uint32_t)(guid_bytes[1])) << 8;
+ Data1 |= ((uint32_t)(guid_bytes[2])) << 16;
+ Data1 |= ((uint32_t)(guid_bytes[3])) << 24;
+ // Part 2
+ Data2 = guid_bytes[4];
+ Data2 |= ((uint16_t)(guid_bytes[5])) << 8;
+ // Part 3
+ Data3 = guid_bytes[6];
+ Data3 |= ((uint16_t)(guid_bytes[7])) << 8;
+ }
+ // Part 4
+ for (size_t i = 0; i < 8; i++)
+ {
+ Data4[i] = guid_bytes[8 + i];
+ }
+ }
+
+ /// <summary>
+ /// A constructor that creates a UUID object from three integers and a byte array.
+ /// </summary>
+ /// <param name="d1">An integer that specifies the first eight hexadecimal digits of the
+ /// UUID.</param> <param name="d2">An integer that specifies the first group of four hexadecimal
+ /// digits.</param> <param name="d3">An integer that specifies the second group of four
+ /// hexadecimal digits.</param> <param name="v">A reference to an array of eight bytes. The first
+ /// two bytes contain the third group of four hexadecimal digits. The remaining six bytes contain
+ /// the final 12 hexadecimal digits.</param>
+ UUID(int d1, int d2, int d3, const std::initializer_list<uint8_t> &v)
+ : Data1((uint32_t)d1), Data2((uint16_t)d2), Data3((uint16_t)d3)
+ {
+ size_t i = 0;
+ for (auto val : v)
+ {
+ Data4[i] = val;
+ i++;
+ }
+ }
+
+ /// <summary>
+ /// The UUID copy constructor.
+ /// </summary>
+ /// <param name="uuid">A UUID object.</param>
+ UUID(const UUID &uuid)
+ {
+ this->Data1 = uuid.Data1;
+ this->Data2 = uuid.Data2;
+ this->Data3 = uuid.Data3;
+ memcpy(&(this->Data4[0]), &(uuid.Data4[0]), sizeof(uuid.Data4));
+ }
+
+#ifdef _WIN32
+
+ /// <summary>
+ /// A constructor that creates a UUID object from a Windows GUID object.
+ /// </summary>
+ /// <param name="guid">A Windows GUID object.</param>
+ UUID(GUID guid)
+ {
+ this->Data1 = guid.Data1;
+ this->Data2 = guid.Data2;
+ this->Data3 = guid.Data3;
+ std::memcpy(&(this->Data4[0]), &(guid.Data4[0]), sizeof(guid.Data4));
+ }
+
+ /// <summary>
+ /// Converts a standard vector of bytes into a Windows GUID object.
+ /// </summary>
+ /// <param name="bytes">A standard vector of bytes.</param>
+ /// <returns>A GUID.</returns>
+ static GUID to_GUID(std::vector<uint8_t> const &bytes)
+ {
+ UUID temp_t = UUID(bytes.data());
+ GUID temp;
+ temp.Data1 = temp_t.Data1;
+ temp.Data2 = temp_t.Data2;
+ temp.Data3 = temp_t.Data3;
+ for (size_t i = 0; i < 8; i++)
+ {
+ temp.Data4[i] = temp_t.Data4[i];
+ }
+ return temp;
+ }
+
+ GUID to_GUID()
+ {
+ GUID temp;
+ temp.Data1 = Data1;
+ temp.Data2 = Data2;
+ temp.Data3 = Data3;
+ for (size_t i = 0; i < 8; i++)
+ {
+ temp.Data4[i] = Data4[i];
+ }
+ return temp;
+ }
+
+#endif
+
+ /// <summary>
+ /// Converts this UUID to an array of bytes.
+ /// </summary>
+ /// <param name="guid_bytes">A uint8_t array of 16 bytes.</param>
+ void to_bytes(uint8_t (&guid_bytes)[16]) const
+ {
+ // Part 1
+ guid_bytes[0] = (uint8_t)((Data1)&0xFF);
+ guid_bytes[1] = (uint8_t)((Data1 >> 8) & 0xFF);
+ guid_bytes[2] = (uint8_t)((Data1 >> 16) & 0xFF);
+ guid_bytes[3] = (uint8_t)((Data1 >> 24) & 0xFF);
+ // Part 2
+ guid_bytes[4] = (uint8_t)((Data2)&0xFF);
+ guid_bytes[5] = (uint8_t)((Data2 >> 8) & 0xFF);
+ // Part 3
+ guid_bytes[6] = (uint8_t)((Data3)&0xFF);
+ guid_bytes[7] = (uint8_t)((Data3 >> 8) & 0xFF);
+ // Part 4
+ for (size_t i = 0; i < 8; i++)
+ {
+ guid_bytes[8 + i] = Data4[i];
+ }
+ }
+
+ /// <summary>
+ /// Convert this UUID object to a string.
+ /// </summary>
+ /// <returns>This UUID object in a string.</returns>
+ std::string to_string() const
+ {
+ static char inttoHex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ const unsigned buffSize = 36 + 1; // 36 + null-terminator
+ char buf[buffSize] = {0};
+
+ int test = (Data1 >> 28 & 0x0000000F);
+ buf[0] = inttoHex[test];
+ test = (int)(Data1 >> 24 & 0x0000000F);
+ buf[1] = inttoHex[test];
+ test = (int)(Data1 >> 20 & 0x0000000F);
+ buf[2] = inttoHex[test];
+ test = (int)(Data1 >> 16 & 0x0000000F);
+ buf[3] = inttoHex[test];
+ test = (int)(Data1 >> 12 & 0x0000000F);
+ buf[4] = inttoHex[test];
+ test = (int)(Data1 >> 8 & 0x0000000F);
+ buf[5] = inttoHex[test];
+ test = (int)(Data1 >> 4 & 0x0000000F);
+ buf[6] = inttoHex[test];
+ test = (int)(Data1 & 0x0000000F);
+ buf[7] = inttoHex[test];
+ buf[8] = '-';
+ test = (int)(Data2 >> 12 & 0x000F);
+ buf[9] = inttoHex[test];
+ test = (int)(Data2 >> 8 & 0x000F);
+ buf[10] = inttoHex[test];
+ test = (int)(Data2 >> 4 & 0x000F);
+ buf[11] = inttoHex[test];
+ test = (int)(Data2 & 0x000F);
+ buf[12] = inttoHex[test];
+ buf[13] = '-';
+ test = (int)(Data3 >> 12 & 0x000F);
+ buf[14] = inttoHex[test];
+ test = (int)(Data3 >> 8 & 0x000F);
+ buf[15] = inttoHex[test];
+ test = (int)(Data3 >> 4 & 0x000F);
+ buf[16] = inttoHex[test];
+ test = (int)(Data3 & 0x000F);
+ buf[17] = inttoHex[test];
+ buf[18] = '-';
+ test = (int)(Data4[0] >> 4 & 0x0F);
+ buf[19] = inttoHex[test];
+ test = (int)(Data4[0] & 0x0F);
+ buf[20] = inttoHex[test];
+ test = (int)(Data4[1] >> 4 & 0x0F);
+ buf[21] = inttoHex[test];
+ test = (int)(Data4[1] & 0x0F);
+ buf[22] = inttoHex[test];
+ buf[23] = '-';
+ test = (int)(Data4[2] >> 4 & 0x0F);
+ buf[24] = inttoHex[test];
+ test = (int)(Data4[2] & 0x0F);
+ buf[25] = inttoHex[test];
+ test = (int)(Data4[3] >> 4 & 0x0F);
+ buf[26] = inttoHex[test];
+ test = (int)(Data4[3] & 0x0F);
+ buf[27] = inttoHex[test];
+ test = (int)(Data4[4] >> 4 & 0x0F);
+ buf[28] = inttoHex[test];
+ test = (int)(Data4[4] & 0x0F);
+ buf[29] = inttoHex[test];
+ test = (int)(Data4[5] >> 4 & 0x0F);
+ buf[30] = inttoHex[test];
+ test = (int)(Data4[5] & 0x0F);
+ buf[31] = inttoHex[test];
+ test = (int)(Data4[6] >> 4 & 0x0F);
+ buf[32] = inttoHex[test];
+ test = (int)(Data4[6] & 0x0F);
+ buf[33] = inttoHex[test];
+ test = (int)(Data4[7] >> 4 & 0x0F);
+ buf[34] = inttoHex[test];
+ test = (int)(Data4[7] & 0x0F);
+ buf[35] = inttoHex[test];
+ buf[36] = 0;
+
+ return std::string(buf);
+ }
+
+ /// <summary>
+ /// Calculates the size of this UUID object.
+ /// The output from this method is compatible with std::unordered_map.
+ /// </summary>
+ /// <returns>The size of the UUID object in bytes.</returns>
+ size_t Hash() const
+ {
+ // Compute individual hash values for Data1, Data2, Data3, and parts of Data4
+ size_t res = 17;
+ res = res * 31 + Data1;
+ res = res * 31 + Data2;
+ res = res * 31 + Data3;
+ res = res * 31 + (Data4[0] << 24 | Data4[1] << 16 | Data4[6] << 8 | Data4[7]);
+ return res;
+ }
+
+ /// <summary>
+ /// Tests to determine whether two UUID objects are equivalent (needed for maps).
+ /// </summary>
+ /// <returns>A boolean value that indicates success or failure.</returns>
+ bool operator==(UUID const &other) const
+ {
+ return Data1 == other.Data1 && Data2 == other.Data2 && Data3 == other.Data3 &&
+ (0 == memcmp(Data4, other.Data4, sizeof(Data4)));
+ }
+
+ /// <summary>
+ /// Tests to determine how to sort 2 UUID objects
+ /// </summary>
+ /// <returns>A boolean value that indicates success or failure.</returns>
+ bool operator<(UUID const &other) const
+ {
+ return Data1 < other.Data1 || Data2 < other.Data2 || Data3 == other.Data3 ||
+ (memcmp(Data4, other.Data4, sizeof(Data4)) < 0);
+ }
+};
+#pragma pack(pop) /* restore original alignment from stack */
+
+/// <summary>
+/// Declare UUIDComparer as the Comparer when using UUID as a key in a map or set
+/// </summary>
+struct UUIDComparer : std::less<UUID>
+{
+ inline size_t operator()(UUID const &key) const { return key.Hash(); }
+
+ inline bool operator()(UUID const &lhs, UUID const &rhs) const { return lhs.Hash() < rhs.Hash(); }
+};
+
+} // namespace utils
+
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc
new file mode 100644
index 000000000..4e2210118
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_logger_test.cc
@@ -0,0 +1,101 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+# ifdef _WIN32
+
+# include <gtest/gtest.h>
+# include <map>
+# include <string>
+
+# include "opentelemetry/exporters/etw/etw_logger_exporter.h"
+# include "opentelemetry/sdk/trace/simple_processor.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+using namespace opentelemetry::exporter::etw;
+
+const char *kGlobalProviderName = "OpenTelemetry-ETW-TLD";
+
+/**
+ * @brief Logger test with name and unstructured text
+ * {
+ * "Timestamp": "2021-09-30T16:40:40.0820563-07:00",
+ * "ProviderName": "OpenTelemetry-ETW-TLD",
+ * "Id": 2,
+ * "Message": null,
+ * "ProcessId": 23180,
+ * "Level": "Always",
+ * "Keywords": "0x0000000000000000",
+ * "EventName": "Log",
+ * "ActivityID": null,
+ * "RelatedActivityID": null,
+ * "Payload": {
+ * "Name": "test",
+ * "SpanId": "0000000000000000",
+ * "Timestamp": "2021-09-30T23:40:40.081000Z",
+ * "TraceId": "00000000000000000000000000000000",
+ * "_name": "Log",
+ * "body": "This is test message",
+ * "severityNumber": 5,
+ * "severityText": "DEBUG"
+ * }
+ * }
+ */
+
+TEST(ETWLogger, LoggerCheckWithBody)
+{
+ std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
+ exporter::etw::LoggerProvider lp;
+
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"};
+ auto logger = lp.GetLogger(providerName, "", schema_url);
+ Properties attribs = {{"attrib1", 1}, {"attrib2", 2}};
+ EXPECT_NO_THROW(logger->Log(opentelemetry::logs::Severity::kDebug, "This is test log body"));
+}
+
+/**
+ * @brief Logger Test with structured attributes
+ *
+ * Example Event for below test:
+ * {
+ * "Timestamp": "2021-09-30T15:04:15.4227815-07:00",
+ * "ProviderName": "OpenTelemetry-ETW-TLD",
+ * "Id": 1,
+ * "Message": null,
+ * "ProcessId": 33544,
+ * "Level": "Always",
+ * "Keywords": "0x0000000000000000",
+ * "EventName": "Log",
+ * "ActivityID": null,
+ * "RelatedActivityID": null,
+ * "Payload": {
+ * "Name": "test",
+ * "SpanId": "0000000000000000",
+ * "Timestamp": "2021-09-30T22:04:15.422000Z",
+ * "TraceId": "00000000000000000000000000000000",
+ * "_name": "Log",
+ * "attrib1": 1,
+ * "attrib2": 2,
+ * "body": "",
+ * "severityNumber": 5,
+ * "severityText": "DEBUG"
+ * }
+ * }
+ *
+ */
+
+TEST(ETWLogger, LoggerCheckWithAttributes)
+{
+ std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
+ exporter::etw::LoggerProvider lp;
+
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.2.0"};
+ auto logger = lp.GetLogger(providerName, "", schema_url);
+ // Log attributes
+ Properties attribs = {{"attrib1", 1}, {"attrib2", 2}};
+ EXPECT_NO_THROW(logger->Log(opentelemetry::logs::Severity::kDebug, attribs));
+}
+
+# endif // _WIN32
+#endif // ENABLE_LOGS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc
new file mode 100644
index 000000000..79052464e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_perf_test.cc
@@ -0,0 +1,190 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _WIN32
+
+# include <benchmark/benchmark.h>
+# include <gtest/gtest.h>
+# include <map>
+# include <string>
+# include <unordered_map>
+
+# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
+# include "opentelemetry/sdk/trace/simple_processor.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+using namespace opentelemetry::exporter::etw;
+
+namespace
+{
+static constexpr const char *providerName = "OpenTelemetry-ETW-StressTest";
+
+static exporter::etw::TelemetryProviderOptions providerOptions = {
+ {"enableTraceId", false},
+ {"enableSpanId", false},
+ {"enableActivityId", false},
+ {"enableRelatedActivityId", false},
+ {"enableAutoParent", false}};
+
+class ETWProviderStressTest
+{
+ exporter::etw::TracerProvider provider_;
+ std::string mode_;
+ nostd::shared_ptr<trace::Tracer> tracer_;
+ nostd::shared_ptr<trace::Span> span_;
+
+public:
+ /**
+ * @brief Construct ETW Provider stress test object
+ * @param mode Operational mode: "TLD" or "MsgPack"
+ */
+ ETWProviderStressTest(std::string mode = "TLD") : mode_(mode), provider_(providerOptions) {}
+
+ /**
+ * @brief Initializer tracer and start a Span
+ */
+ void Initialize()
+ {
+ tracer_ = provider_.GetTracer(providerName, mode_);
+ span_ = tracer_->StartSpan("Span");
+ }
+
+ /**
+ * @brief Obtain the tracer
+ */
+ nostd::shared_ptr<trace::Tracer> GetTracer() { return tracer_; }
+
+ /**
+ * @brief Add event using Properties container
+ */
+ bool AddProperties()
+ {
+ std::string eventName = "MyEvent";
+ Properties event = {{"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}};
+ span_->AddEvent(eventName, event);
+ return true;
+ }
+
+ /**
+ * @brief Add event using static preallocated "reusable" Properties container.
+ * This approach works well for single-threaded flows, but may not be safe in
+ * some multithreaded scenarios in case if reusable `Properties` get concurrently
+ * modified by different threads (writes to Properties are not thread-safe).
+ */
+ bool AddPropertiesStatic()
+ {
+ std::string eventName = "MyEvent";
+ static Properties event = {{"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}};
+ span_->AddEvent(eventName, event);
+ return true;
+ }
+
+ /**
+ * @brief Add event using initializer list
+ */
+ bool AddInitList()
+ {
+ std::string eventName = "MyEvent";
+ span_->AddEvent(eventName, {{"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}});
+ return true;
+ }
+
+ /**
+ * @brief Add event using unordered_map
+ */
+ bool AddMap()
+ {
+ std::string eventName = "MyEvent";
+ std::unordered_map<const char *, common::AttributeValue> m = {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}};
+ span_->AddEvent(eventName, m);
+ return true;
+ }
+
+ /**
+ * @brief End Span and close tracer.
+ */
+ void Teardown()
+ {
+ span_->End();
+ tracer_->CloseWithMicroseconds(0);
+ }
+};
+
+ETWProviderStressTest provider;
+
+/**
+ * @brief Create Properties and AddEvent(Properties) to Tracer
+ * @param state Benchmark state.
+ */
+void BM_AddPropertiesToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddProperties());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddPropertiesToTracer);
+
+/**
+ * @brief Create static Properties and AddEvent(Properties) to Tracer
+ * @param state Benchmark state.
+ */
+void BM_AddPropertiesStaticToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddPropertiesStatic());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddPropertiesStaticToTracer);
+
+/**
+ * @brief Create event via initializer list and AddEvent({...}) to Tracer
+ * @param state Benchmark state.
+ */
+void BM_AddInitListToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddInitList());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddInitListToTracer);
+
+/**
+ * @brief Create event as `std::map<std::string, common::AttributeValue>`
+ * and AddEvent(event) to Tracer.
+ * @param state Benchmark state.
+ */
+void BM_AddMapToTracer(benchmark::State &state)
+{
+ provider.Initialize();
+ while (state.KeepRunning())
+ {
+ benchmark::DoNotOptimize(provider.AddMap());
+ }
+ provider.Teardown();
+}
+BENCHMARK(BM_AddMapToTracer);
+
+} // namespace
+
+BENCHMARK_MAIN();
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc
new file mode 100644
index 000000000..d5ebbcad4
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_provider_test.cc
@@ -0,0 +1,64 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _WIN32
+
+# include <gtest/gtest.h>
+# include <string>
+
+# include "opentelemetry/exporters/etw/etw_provider.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+TEST(ETWProvider, ProviderIsRegisteredSuccessfully)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider";
+ static ETWProvider etw;
+ auto handle = etw.open(providerName.c_str());
+
+ bool registered = etw.is_registered(providerName);
+ ASSERT_TRUE(registered);
+ etw.close(handle);
+}
+
+TEST(ETWProvider, ProviderIsNotRegisteredSuccessfully)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider-NULL";
+ static ETWProvider etw;
+
+ bool registered = etw.is_registered(providerName);
+ ASSERT_FALSE(registered);
+}
+
+TEST(ETWProvider, CheckOpenGUIDDataSuccessfully)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider";
+
+ // get GUID from the handle returned
+ static ETWProvider etw;
+ auto handle = etw.open(providerName.c_str());
+
+ utils::UUID uuid_handle(handle.providerGuid);
+ auto guidStrHandle = uuid_handle.to_string();
+
+ // get GUID from the providerName
+ auto guid = utils::GetProviderGuid(providerName.c_str());
+ utils::UUID uuid_name(guid);
+ auto guidStrName = uuid_name.to_string();
+
+ ASSERT_STREQ(guidStrHandle.c_str(), guidStrName.c_str());
+ etw.close(handle);
+}
+
+TEST(ETWProvider, CheckCloseSuccess)
+{
+ std::string providerName = "OpenTelemetry-ETW-Provider";
+
+ static ETWProvider etw;
+ auto handle = etw.open(providerName.c_str(), ETWProvider::EventFormat::ETW_MANIFEST);
+ auto result = etw.close(handle);
+ ASSERT_NE(result, etw.STATUS_ERROR);
+ ASSERT_FALSE(etw.is_registered(providerName));
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc
new file mode 100644
index 000000000..1c9c1f09b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/etw/test/etw_tracer_test.cc
@@ -0,0 +1,388 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef _WIN32
+
+# include <gtest/gtest.h>
+# include <map>
+# include <string>
+
+# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
+# include "opentelemetry/sdk/trace/simple_processor.h"
+
+using namespace OPENTELEMETRY_NAMESPACE;
+
+using namespace opentelemetry::exporter::etw;
+
+const char *kGlobalProviderName = "OpenTelemetry-ETW-TLD";
+
+std::string getTemporaryValue()
+{
+ return std::string("Value from Temporary std::string");
+}
+
+/* clang-format off */
+TEST(ETWTracer, TracerCheck)
+{
+ // SDK customer specifies their unique ETW ProviderName. Every component or library
+ // is assumed to have its own instrumentation name. Traces are routed to dedicated
+ // provider. Standard hash function maps from ProviderName to ProviderGUID.
+ //
+ // Prominent naming examples from `logman query providers` :
+ //
+ // [Docker] {a3693192-9ed6-46d2-a981-f8226c8363bd}
+ // ...
+ // Intel-Autologger-iclsClient {B8D7E9A0-65D5-40BE-AFEA-83593FC0164E}
+ // Intel-Autologger-iclsProxy {301B773F-50F3-4C8E-83F0-53BA9590A13E}
+ // Intel-Autologger-PTTEKRecertification {F33E9E07-8792-47E8-B3FA-2C92AB32C5B3}
+ // ...
+ // NodeJS-ETW-provider {77754E9B-264B-4D8D-B981-E4135C1ECB0C}
+ // ...
+ // OpenSSH {C4B57D35-0636-4BC3-A262-370F249F9802}
+ // ...
+ // Windows Connect Now {C100BECE-D33A-4A4B-BF23-BBEF4663D017}
+ // Windows Defender Firewall API {28C9F48F-D244-45A8-842F-DC9FBC9B6E92}
+ // Windows Defender Firewall API - GP {0EFF663F-8B6E-4E6D-8182-087A8EAA29CB}
+ // Windows Defender Firewall Driver {D5E09122-D0B2-4235-ADC1-C89FAAAF1069}
+
+ std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
+ exporter::etw::TracerProvider tp;
+
+ auto tracer = tp.GetTracer(providerName);
+
+ // Span attributes
+ Properties attribs =
+ {
+ {"attrib1", 1},
+ {"attrib2", 2}
+ };
+ {
+ auto topSpan = tracer->StartSpan("MySpanTop");
+ auto topScope = tracer->WithActiveSpan(topSpan);
+ {
+ auto outerSpan = tracer->StartSpan("MySpanL2", attribs);
+ auto outerScope = tracer->WithActiveSpan(outerSpan);
+
+ // Create nested span. Note how we share the attributes here.
+ // It is Okay to either reuse/share or have your own attributes.
+ {
+ auto innerSpan = tracer->StartSpan("MySpanL3", attribs);
+ auto innerScope = tracer->WithActiveSpan(innerSpan);
+
+ // Add span attribute
+ EXPECT_NO_THROW(outerSpan->SetAttribute("AttrName1", "AttrValue1"));
+
+ // Add first event
+ std::string eventName1 = "MyEvent1";
+ Properties event1 =
+ {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}
+ };
+ EXPECT_NO_THROW(outerSpan->AddEvent(eventName1, event1));
+
+ // Add second event
+ std::string eventName2 = "MyEvent2";
+ Properties event2 =
+ {
+ {"uint32Key", (uint32_t)9876},
+ {"uint64Key", (uint64_t)987654321},
+ {"strKey", "anotherValue"}
+ };
+ EXPECT_NO_THROW(outerSpan->AddEvent(eventName2, event2));
+
+ std::string eventName3= "MyEvent3";
+ Properties event3 =
+ {
+ /* Extra metadata that allows event to flow to A.I. pipeline */
+ {"metadata", "ai_event"},
+ {"uint32Key", (uint32_t)9876},
+ {"uint64Key", (uint64_t)987654321},
+ // {"int32array", {{-1,0,1,2,3}} },
+ {"tempString", getTemporaryValue() }
+ };
+ EXPECT_NO_THROW(innerSpan->AddEvent(eventName3, event3));
+ EXPECT_NO_THROW(innerSpan->End());
+
+ }
+ EXPECT_NO_THROW(outerSpan->End());
+
+ }
+ EXPECT_NO_THROW(topSpan->End());
+ }
+
+ EXPECT_NO_THROW(tracer->CloseWithMicroseconds(0));
+}
+
+// Lowest decoration level -> smaller ETW event size.
+// Expected output in C# listener on the other side:
+// no ActivityID GUID, no SpanId, no TraceId.
+/*
+{
+ "Timestamp": "2021-03-19T21:04:38.411193-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 13,
+ "Message": null,
+ "ProcessId": 15120,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "C.min/Stop",
+ "ActivityID": null,
+ "RelatedActivityID": null,
+ "Payload": {}
+}
+*/
+TEST(ETWTracer, TracerCheckMinDecoration)
+{
+ std::string providerName = kGlobalProviderName;
+ exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", false},
+ {"enableSpanId", false},
+ {"enableActivityId", false},
+ {"enableActivityTracking", true},
+ {"enableRelatedActivityId", false},
+ {"enableAutoParent", false}
+ });
+ auto tracer = tp.GetTracer(providerName);
+ {
+ auto aSpan = tracer->StartSpan("A.min");
+ auto aScope = tracer->WithActiveSpan(aSpan);
+ {
+ auto bSpan = tracer->StartSpan("B.min");
+ auto bScope = tracer->WithActiveSpan(bSpan);
+ {
+ auto cSpan = tracer->StartSpan("C.min");
+ auto cScope = tracer->WithActiveSpan(cSpan);
+ EXPECT_NO_THROW(cSpan->End());
+ }
+ EXPECT_NO_THROW(bSpan->End());
+ }
+ EXPECT_NO_THROW(aSpan->End());
+}
+ tracer->CloseWithMicroseconds(0);
+}
+
+// Highest decoration level -> larger ETW event size
+// Expected output in C# listener on the other side:
+// ActivityID GUID (==SpanId), SpanId, TraceId.
+/*
+{
+ "Timestamp": "2021-03-19T21:04:38.4120274-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 21,
+ "Message": null,
+ "ProcessId": 15120,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "C.max/Stop",
+ "ActivityID": "d55a2c25-8033-40ab-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "SpanId": "252c5ad53380ab40",
+ "TraceId": "4dea2a63c188894ea5ab979e5cd7ec36"
+ }
+}
+*/
+TEST(ETWTracer, TracerCheckMaxDecoration)
+{
+ std::string providerName = kGlobalProviderName;
+ exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", true},
+ {"enableSpanId", true},
+ {"enableActivityId", true},
+ {"enableRelatedActivityId", true},
+ {"enableAutoParent", true}
+ });
+ auto tracer = tp.GetTracer(providerName);
+ {
+ auto aSpan = tracer->StartSpan("A.max");
+ auto aScope = tracer->WithActiveSpan(aSpan);
+ {
+ auto bSpan = tracer->StartSpan("B.max");
+ auto bScope = tracer->WithActiveSpan(bSpan);
+ {
+ auto cSpan = tracer->StartSpan("C.max");
+ auto cScope = tracer->WithActiveSpan(cSpan);
+ EXPECT_NO_THROW(cSpan->End());
+ }
+ EXPECT_NO_THROW(bSpan->End());
+ }
+ EXPECT_NO_THROW(aSpan->End());
+ }
+ tracer->CloseWithMicroseconds(0);
+}
+
+TEST(ETWTracer, TracerCheckMsgPack)
+{
+ std::string providerName = "OpenTelemetry-ETW-MsgPack";
+ exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", true},
+ {"enableSpanId", true},
+ {"enableActivityId", true},
+ {"enableRelatedActivityId", true},
+ {"enableAutoParent", true}
+ });
+ auto tracer = tp.GetTracer(providerName);
+ {
+ auto aSpan = tracer->StartSpan("A.max");
+ auto aScope = tracer->WithActiveSpan(aSpan);
+ {
+ auto bSpan = tracer->StartSpan("B.max");
+ auto bScope = tracer->WithActiveSpan(bSpan);
+ {
+ auto cSpan = tracer->StartSpan("C.max");
+ auto cScope = tracer->WithActiveSpan(cSpan);
+ std::string eventName = "MyMsgPackEvent";
+ Properties event =
+ {
+ {"uint32Key", (uint32_t)1234},
+ {"uint64Key", (uint64_t)1234567890},
+ {"strKey", "someValue"}
+ };
+ cSpan->AddEvent(eventName, event);
+ EXPECT_NO_THROW(cSpan->End());
+ }
+ EXPECT_NO_THROW(bSpan->End());
+ }
+ EXPECT_NO_THROW(aSpan->End());
+ }
+ tracer->CloseWithMicroseconds(0);
+}
+
+/**
+ * @brief Global Tracer singleton may be placed in .h header and
+ * shared across different compilation units. All would get the
+ * same object.
+ *
+ * @return Single global tracer instance.
+*/
+static OPENTELEMETRY_NAMESPACE::trace::TracerProvider& GetGlobalTracerProvider()
+{
+ static exporter::etw::TracerProvider tp
+ ({
+ {"enableTraceId", true},
+ {"enableSpanId", true},
+ {"enableActivityId", true},
+ {"enableRelatedActivityId", true},
+ {"enableAutoParent", true}
+ });
+ return tp;
+}
+
+static OPENTELEMETRY_NAMESPACE::trace::Tracer& GetGlobalTracer()
+{
+ static auto tracer = GetGlobalTracerProvider().GetTracer(kGlobalProviderName);
+ return (*tracer.get());
+}
+
+TEST(ETWTracer, GlobalSingletonTracer)
+{
+ // Obtain a global tracer using C++11 magic static.
+ auto& globalTracer = GetGlobalTracer();
+ auto s1 = globalTracer.StartSpan("Span1");
+ auto traceId1 = s1->GetContext().trace_id();
+ s1->End();
+/* === Span 1 - "TraceId": "182a64258fb1864ca4e1a542eecbd9bf"
+{
+ "Timestamp": "2021-05-10T11:45:27.028827-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 5,
+ "Message": null,
+ "ProcessId": 23712,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "6ed94703-6b0a-4e76-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 0,
+ "Kind": 1,
+ "Name": "Span1",
+ "SpanId": "0347d96e0a6b764e",
+ "StartTime": "2021-05-10T18:45:27.028000Z",
+ "StatusCode": 0,
+ "StatusMessage": "",
+ "Success": "True",
+ "TraceId": "182a64258fb1864ca4e1a542eecbd9bf",
+ "_name": "Span"
+ }
+}
+*/
+
+ // Obtain a different tracer withs its own trace-id.
+ auto localTracer = GetGlobalTracerProvider().GetTracer(kGlobalProviderName);
+ auto s2 = localTracer->StartSpan("Span2");
+ auto traceId2 = s2->GetContext().trace_id();
+ s2->End();
+/* === Span 2 - "TraceId": "334bf9a1eed98d40a873a606295a9368"
+{
+ "Timestamp": "2021-05-10T11:45:27.0289654-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 5,
+ "Message": null,
+ "ProcessId": 23712,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "3b7b2ecb-2e84-4903-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 0,
+ "Kind": 1,
+ "Name": "Span2",
+ "SpanId": "cb2e7b3b842e0349",
+ "StartTime": "2021-05-10T18:45:27.028000Z",
+ "StatusCode": 0,
+ "StatusMessage": "",
+ "Success": "True",
+ "TraceId": "334bf9a1eed98d40a873a606295a9368",
+ "_name": "Span"
+ }
+}
+*/
+
+ // Obtain the same global tracer with the same trace-id as before.
+ auto& globalTracer2 = GetGlobalTracer();
+ auto s3 = globalTracer2.StartSpan("Span3");
+ auto traceId3 = s3->GetContext().trace_id();
+ s3->End();
+/* === Span 3 - "TraceId": "182a64258fb1864ca4e1a542eecbd9bf"
+{
+ "Timestamp": "2021-05-10T11:45:27.0290936-07:00",
+ "ProviderName": "OpenTelemetry-ETW-TLD",
+ "Id": 5,
+ "Message": null,
+ "ProcessId": 23712,
+ "Level": "Always",
+ "Keywords": "0x0000000000000000",
+ "EventName": "Span",
+ "ActivityID": "0a970247-ba0e-4d4b-0000-000000000000",
+ "RelatedActivityID": null,
+ "Payload": {
+ "Duration": 1,
+ "Kind": 1,
+ "Name": "Span3",
+ "SpanId": "4702970a0eba4b4d",
+ "StartTime": "2021-05-10T18:45:27.028000Z",
+ "StatusCode": 0,
+ "StatusMessage": "",
+ "Success": "True",
+ "TraceId": "182a64258fb1864ca4e1a542eecbd9bf",
+ "_name": "Span"
+ }
+}
+*/
+ EXPECT_NE(traceId1, traceId2);
+ EXPECT_EQ(traceId1, traceId3);
+
+ localTracer->CloseWithMicroseconds(0);
+ globalTracer.CloseWithMicroseconds(0);
+}
+
+/* clang-format on */
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/BUILD
new file mode 100644
index 000000000..cc1babae0
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/BUILD
@@ -0,0 +1,217 @@
+package(default_visibility = ["//visibility:public"])
+
+load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake", "configure_make", "configure_make_variant")
+
+constraint_setting(
+ name = "incompatible_setting",
+)
+
+constraint_value(
+ name = "incompatible",
+ constraint_setting = ":incompatible_setting",
+)
+
+config_setting(
+ name = "windows",
+ constraint_values = [
+ "@bazel_tools//platforms:windows",
+ ],
+ tags = ["jaeger"],
+ visibility = ["//visibility:private"],
+)
+
+THRIFT_CACHE_ENTRIES = {
+ "CMAKE_BUILD_TYPE": "Release",
+ "BUILD_COMPILER": "OFF",
+ "BUILD_CPP": "ON",
+ "BUILD_LIBRARIES": "ON",
+ "BUILD_NODEJS": "OFF",
+ "BUILD_PYTHON": "OFF",
+ "BUILD_JAVASCRIPT": "OFF",
+ "BUILD_C_GLIB": "OFF",
+ "BUILD_JAVA": "OFF",
+ "BUILD_TESTING": "OFF",
+ "BUILD_TUTORIALS": "OFF",
+ "WITH_HASKELL": "OFF",
+}
+
+THRIFT_CACHE_ENTRIES_WIN = {
+ "CMAKE_BUILD_TYPE": "Release",
+ "BUILD_COMPILER": "OFF",
+ "BUILD_CPP": "ON",
+ "BUILD_LIBRARIES": "ON",
+ "BUILD_NODEJS": "OFF",
+ "BUILD_PYTHON": "OFF",
+ "BUILD_JAVASCRIPT": "OFF",
+ "BUILD_C_GLIB": "OFF",
+ "BUILD_JAVA": "OFF",
+ "BUILD_TESTING": "OFF",
+ "BUILD_TUTORIALS": "OFF",
+ "WITH_HASKELL": "OFF",
+ "WITH_STDTHREADS": "ON",
+ "WITH_BOOSTTHREADS": "OFF",
+ "WITH_BOOST_FUNCTIONAL": "OFF",
+ "WITH_BOOST_SMART_PTR": "OFF",
+ "BUILD_SHARED_LIBS": "OFF",
+ "CMAKE_TOOLCHAIN_FILE": "$VCPKG_DIR/scripts/buildsystems/vcpkg.cmake",
+}
+
+cmake(
+ name = "thrift",
+ cache_entries = select({
+ "@bazel_tools//platforms:osx": THRIFT_CACHE_ENTRIES,
+ "@bazel_tools//platforms:linux": THRIFT_CACHE_ENTRIES,
+ "@bazel_tools//platforms:windows": THRIFT_CACHE_ENTRIES_WIN,
+ }),
+ copts = [
+ "-Ilibs/exporters/jaeger/openssl/include",
+ "-fexceptions",
+ ],
+ generate_args = select({
+ "@bazel_tools//platforms:osx": [],
+ "@bazel_tools//platforms:linux": [],
+ "@bazel_tools//platforms:windows": [
+ "-G \"NMake Makefiles\"",
+ ],
+ }),
+ install = True,
+ lib_source = "@com_github_thrift//:all_srcs",
+ out_lib_dir = select({
+ "@bazel_tools//platforms:osx": "lib",
+ "@bazel_tools//platforms:linux": "lib",
+ "@bazel_tools//platforms:windows": "bin",
+ }),
+ out_static_libs = select({
+ "@bazel_tools//platforms:osx": [
+ "libthrift.a",
+ "libthriftz.a",
+ ],
+ "@bazel_tools//platforms:linux": [
+ "libthrift.a",
+ "libthriftz.a",
+ ],
+ "@bazel_tools//platforms:windows": [
+ "thriftmd.lib",
+ ],
+ }),
+ tags = ["jaeger"],
+ visibility = ["//visibility:private"],
+ deps = [],
+)
+
+THRIFT_GEN_DEPS = [
+ ":thrift",
+ "//ext/src/http/client/curl:http_client_curl",
+]
+
+THRIFT_GEN_DEPS_WIN = THRIFT_GEN_DEPS + [
+ "@boost_all_hdrs//:boost_all_hdrs",
+]
+
+cc_library(
+ name = "jaeger_thrift_gencpp",
+ srcs = [
+ "thrift-gen/Agent.cpp",
+ "thrift-gen/Collector.cpp",
+ "thrift-gen/ZipkinCollector.cpp",
+ "thrift-gen/jaeger_types.cpp",
+ "thrift-gen/zipkincore_constants.cpp",
+ "thrift-gen/zipkincore_types.cpp",
+ ],
+ hdrs = [
+ "thrift-gen/Agent.h",
+ "thrift-gen/Collector.h",
+ "thrift-gen/ZipkinCollector.h",
+ "thrift-gen/agent_types.h",
+ "thrift-gen/jaeger_types.h",
+ "thrift-gen/zipkincore_constants.h",
+ "thrift-gen/zipkincore_types.h",
+ ],
+ copts = [
+ "-fexceptions",
+ ],
+ strip_include_prefix = "thrift-gen",
+ tags = ["jaeger"],
+ deps = select({
+ "@bazel_tools//platforms:osx": THRIFT_GEN_DEPS,
+ "@bazel_tools//platforms:linux": THRIFT_GEN_DEPS,
+ "@bazel_tools//platforms:windows": THRIFT_GEN_DEPS_WIN,
+ }),
+)
+
+cc_library(
+ name = "jaeger_exporter",
+ srcs = [
+ ],
+ hdrs = [
+ "src/THttpTransport.h",
+ "src/TUDPTransport.h",
+ "src/http_transport.h",
+ "src/sender.h",
+ "src/thrift_sender.h",
+ "src/transport.h",
+ "src/udp_transport.h",
+ ],
+ copts = ["-fexceptions"],
+ strip_include_prefix = "src",
+ tags = ["jaeger"],
+ deps = [
+ ":jaeger_thrift_gencpp",
+ ],
+)
+
+cc_library(
+ name = "opentelemetry_exporter_jaeger_trace",
+ srcs = [
+ "src/THttpTransport.cc",
+ "src/TUDPTransport.cc",
+ "src/http_transport.cc",
+ "src/jaeger_exporter.cc",
+ "src/recordable.cc",
+ "src/thrift_sender.cc",
+ "src/udp_transport.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/jaeger/jaeger_exporter.h",
+ "include/opentelemetry/exporters/jaeger/recordable.h",
+ ],
+ copts = ["-fexceptions"],
+ strip_include_prefix = "include",
+ tags = ["jaeger"],
+ deps = [
+ ":jaeger_exporter",
+ "//sdk/src/common:global_log_handler",
+ ],
+)
+
+cc_test(
+ name = "jaeger_recordable_test",
+ srcs = ["test/jaeger_recordable_test.cc"],
+ copts = ["-fexceptions"],
+ tags = [
+ "jaeger",
+ "test",
+ ],
+ deps = [
+ ":opentelemetry_exporter_jaeger_trace",
+ "//sdk/src/resource",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "jaeger_exporter_test",
+ srcs = ["test/jaeger_exporter_test.cc"],
+ copts = ["-fexceptions"],
+ defines = ["BAZEL_BUILD"],
+ tags = [
+ "jaeger",
+ "test",
+ ],
+ deps = [
+ ":opentelemetry_exporter_jaeger_trace",
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/CMakeLists.txt
new file mode 100644
index 000000000..a95c0cc94
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/CMakeLists.txt
@@ -0,0 +1,95 @@
+include_directories(thrift-gen)
+
+find_package(Thrift REQUIRED)
+# vcpkg config recipe points to THRIFT_INCLUDE_DIR=...\thrift . Ensure that the
+# include dir for thrift-gen code is 1 level-up from that:
+include_directories(SYSTEM ${THRIFT_INCLUDE_DIR}/..)
+
+set(JAEGER_THRIFT_GENCPP_SOURCES
+ thrift-gen/Agent.cpp thrift-gen/jaeger_types.cpp thrift-gen/Collector.cpp
+ thrift-gen/zipkincore_types.cpp)
+
+set(JAEGER_EXPORTER_SOURCES
+ src/jaeger_exporter.cc
+ src/thrift_sender.cc
+ src/udp_transport.cc
+ src/recordable.cc
+ src/TUDPTransport.cc
+ src/http_transport.cc
+ src/THttpTransport.cc)
+
+add_library(opentelemetry_exporter_jaeger_trace ${JAEGER_EXPORTER_SOURCES}
+ ${JAEGER_THRIFT_GENCPP_SOURCES})
+
+set_target_properties(opentelemetry_exporter_jaeger_trace
+ PROPERTIES EXPORT_NAME jaeger_trace_exporter)
+
+target_include_directories(
+ opentelemetry_exporter_jaeger_trace
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+target_link_libraries(
+ opentelemetry_exporter_jaeger_trace
+ PUBLIC opentelemetry_resources opentelemetry_http_client_curl
+ PRIVATE thrift::thrift)
+
+if(MSVC)
+ target_compile_definitions(opentelemetry_exporter_jaeger_trace
+ PUBLIC NOMINMAX)
+ if(NOT BUILD_SHARED_LIBS)
+ target_compile_definitions(opentelemetry_exporter_jaeger_trace
+ PUBLIC THRIFT_STATIC_DEFINE)
+ endif()
+endif()
+
+install(
+ TARGETS opentelemetry_exporter_jaeger_trace
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/jaeger
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h"
+ PATTERN "recordable.h" EXCLUDE)
+
+if(BUILD_TESTING)
+ add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1)
+
+ add_executable(jaeger_recordable_test test/jaeger_recordable_test.cc)
+ target_link_libraries(
+ jaeger_recordable_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_exporter_jaeger_trace)
+
+ gtest_add_tests(
+ TARGET jaeger_recordable_test
+ TEST_PREFIX exporter.
+ TEST_LIST jaeger_recordable_test)
+
+ add_executable(jaeger_exporter_test test/jaeger_exporter_test.cc)
+ if(MSVC)
+ if(GMOCK_LIB)
+ unset(GMOCK_LIB CACHE)
+ endif()
+ endif()
+ if(MSVC AND CMAKE_BUILD_TYPE STREQUAL "Debug")
+ find_library(GMOCK_LIB gmockd PATH_SUFFIXES lib)
+ else()
+ find_library(GMOCK_LIB gmock PATH_SUFFIXES lib)
+ endif()
+ target_link_libraries(
+ jaeger_exporter_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ ${GMOCK_LIB} opentelemetry_trace opentelemetry_exporter_jaeger_trace)
+
+ target_include_directories(jaeger_exporter_test
+ PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src)
+
+ gtest_add_tests(
+ TARGET jaeger_exporter_test
+ TEST_PREFIX exporter.
+ TEST_LIST jaeger_exporter_test)
+endif() # BUILD_TESTING
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/README.md b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/README.md
new file mode 100644
index 000000000..d4fa3b45b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/README.md
@@ -0,0 +1,61 @@
+# Jaeger Exporter for OpenTelemetry C++
+
+## Prerequisite
+
+* [Get Jaeger](https://www.jaegertracing.io/docs/getting-started/) and run
+ Jaeger agent.
+
+## Installation
+
+### CMake Installation Instructions
+
+Refer to install instructions
+[INSTALL.md](../../INSTALL.md#building-as-standalone-cmake-project). Modify step
+2 to create `cmake` build configuration for compiling with Jaeger exporter as
+below:
+
+```console
+ $ cmake -DWITH_JAEGER=ON ..
+ -- The C compiler identification is GNU 9.3.0
+ -- The CXX compiler identification is GNU 9.3.0
+ ...
+ -- Configuring done
+ -- Generating done
+ -- Build files have been written to: /home/<user>/source/opentelemetry-cpp/build
+ $
+```
+
+### Bazel install Instructions
+
+Refer to install instructions
+[INSTALL.md](../../INSTALL.md#building-as-standalone-bazel-project).
+
+```console
+bazel build //exporters/jaeger:opentelemetry_exporter_jaeger_trace
+```
+
+## Usage
+
+Install the exporter in your application, initialize and pass the `options` to it.
+
+```cpp
+opentelemetry::exporter::jaeger::JaegerExporterOptions options;
+options.server_addr = "localhost";
+options.server_port = 6831;
+options.transport_format = opentelemetry::exporter::jaeger::TransportFormat::kThriftUdpCompact;
+
+auto exporter = std::unique_ptr<opentelemetry::sdk::trace::SpanExporter>(
+ new opentelemetry::exporter::jaeger::JaegerExporter(options));
+auto processor = std::shared_ptr<sdktrace::SpanProcessor>(
+ new sdktrace::SimpleSpanProcessor(std::move(exporter)));
+auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
+ new sdktrace::TracerProvider(processor));
+
+// Set the global trace provider
+opentelemetry::trace::Provider::SetTracerProvider(provider);
+
+```
+
+## Viewing your traces
+
+Please visit the Jaeger UI endpoint <http://localhost:16686>.
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h
new file mode 100644
index 000000000..284bab2ca
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h
@@ -0,0 +1,93 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <opentelemetry/common/spin_lock_mutex.h>
+#include <opentelemetry/ext/http/client/http_client.h>
+#include <opentelemetry/sdk/trace/exporter.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+enum class TransportFormat
+{
+ kThriftUdp,
+ kThriftUdpCompact,
+ kThriftHttp,
+ kProtobufGrpc,
+};
+
+class ThriftSender;
+
+/**
+ * Struct to hold Jaeger exporter options.
+ */
+struct JaegerExporterOptions
+{
+ TransportFormat transport_format = TransportFormat::kThriftUdpCompact;
+ std::string endpoint = "localhost";
+ uint16_t server_port = 6831;
+ // Only applicable when using kThriftHttp transport.
+ ext::http::client::Headers headers;
+};
+
+class JaegerExporter final : public opentelemetry::sdk::trace::SpanExporter
+{
+public:
+ /**
+ * Create a JaegerExporter using all default options.
+ */
+ JaegerExporter();
+
+ /**
+ * Create a JaegerExporter using the given options.
+ */
+ explicit JaegerExporter(const JaegerExporterOptions &options);
+
+ /**
+ * Create a span recordable.
+ * @return a new initialized Recordable object.
+ */
+ std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Export a batch of spans.
+ * @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;
+
+ /**
+ * Shutdown the exporter.
+ * @param timeout an option timeout, default to max.
+ */
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;
+
+private:
+ void InitializeEndpoint();
+
+private:
+ // The configuration options associated with this exporter.
+ bool is_shutdown_ = false;
+ JaegerExporterOptions options_;
+ std::unique_ptr<ThriftSender> sender_;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+ // For testing
+ friend class JaegerExporterTestPeer;
+ /**
+ * Create an JaegerExporter using the specified thrift sender.
+ * Only tests can call this constructor directly.
+ * @param sender the thrift sender to be used for exporting
+ */
+ JaegerExporter(std::unique_ptr<ThriftSender> sender);
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h
new file mode 100644
index 000000000..98e1e6131
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h
@@ -0,0 +1,119 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <jaeger_types.h>
+#include <opentelemetry/sdk/trace/recordable.h>
+#include <opentelemetry/version.h>
+
+#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+# define JAEGER_IS_LITTLE_ENDIAN 1
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define JAEGER_IS_LITTLE_ENDIAN 0
+#elif defined(_WIN32)
+# define JAEGER_IS_LITTLE_ENDIAN 1
+#else
+# error "Endian detection needs to be set up for your compiler"
+#endif
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+#if JAEGER_IS_LITTLE_ENDIAN == 1
+
+# if defined(__clang__) || \
+ (defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
+inline uint64_t otel_bswap_64(uint64_t host_int)
+{
+ return __builtin_bswap64(host_int);
+}
+
+# elif defined(_MSC_VER)
+inline uint64_t otel_bswap_64(uint64_t host_int)
+{
+ return _byteswap_uint64(host_int);
+}
+
+# else
+# error "Port need to support endianess conversion"
+
+# endif
+
+#endif
+
+using namespace jaegertracing;
+
+class JaegerRecordable final : public sdk::trace::Recordable
+{
+public:
+ JaegerRecordable();
+
+ thrift::Span *Span() noexcept { return span_.release(); }
+ std::vector<thrift::Tag> Tags() noexcept { return std::move(tags_); }
+ std::vector<thrift::Tag> ResourceTags() noexcept { return std::move(resource_tags_); }
+ std::vector<thrift::Log> Logs() noexcept { return std::move(logs_); }
+ std::vector<thrift::SpanRef> References() noexcept { return std::move(references_); }
+ const std::string &ServiceName() const noexcept { return service_name_; }
+
+ 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 key,
+ 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 SetStartTime(common::SystemTimestamp start_time) noexcept override;
+
+ void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override;
+
+ void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override;
+
+ void SetDuration(std::chrono::nanoseconds duration) noexcept override;
+
+ void SetInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library) noexcept override;
+
+private:
+ void AddTag(const std::string &key, const std::string &value, std::vector<thrift::Tag> &tags);
+ void AddTag(const std::string &key, const char *value, std::vector<thrift::Tag> &tags);
+ void AddTag(const std::string &key, bool value, std::vector<thrift::Tag> &tags);
+ void AddTag(const std::string &key, int64_t value, std::vector<thrift::Tag> &tags);
+ void AddTag(const std::string &key, double value, std::vector<thrift::Tag> &tags);
+
+ void PopulateAttribute(nostd::string_view key,
+ const opentelemetry::common::AttributeValue &value,
+ std::vector<thrift::Tag> &tags);
+
+ void PopulateAttribute(nostd::string_view key,
+ const sdk::common::OwnedAttributeValue &value,
+ std::vector<thrift::Tag> &tags);
+
+private:
+ std::unique_ptr<thrift::Span> span_;
+ std::vector<thrift::Tag> tags_;
+ std::vector<thrift::Tag> resource_tags_;
+ std::vector<thrift::Log> logs_;
+ std::vector<thrift::SpanRef> references_;
+ std::string service_name_;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.cc
new file mode 100644
index 000000000..cbb1b65cb
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.cc
@@ -0,0 +1,61 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "THttpTransport.h"
+#include "opentelemetry/ext/http/client/http_client_factory.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+THttpTransport::THttpTransport(std::string endpoint, ext::http::client::Headers extra_headers)
+ : endpoint(std::move(endpoint)),
+ headers(std::move(extra_headers)),
+ client(ext::http::client::HttpClientFactory::CreateSync())
+{
+ headers.insert({{"Content-Type", "application/vnd.apache.thrift.binary"}});
+}
+
+THttpTransport::~THttpTransport() {}
+
+bool THttpTransport::isOpen() const
+{
+ return true;
+}
+
+uint32_t THttpTransport::read(uint8_t *buf, uint32_t len)
+{
+ (void)buf;
+ (void)len;
+ return 0;
+}
+
+void THttpTransport::write(const uint8_t *buf, uint32_t len)
+{
+ request_buffer.insert(request_buffer.end(), buf, buf + len);
+}
+
+bool THttpTransport::sendSpans()
+{
+ auto result = client->Post(endpoint, request_buffer, headers);
+ request_buffer.clear();
+
+ // TODO: Add logging once global log handling is available.
+ if (!result)
+ {
+ return false;
+ }
+
+ if (result.GetResponse().GetStatusCode() >= 400)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.h
new file mode 100644
index 000000000..9c796e67a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/THttpTransport.h
@@ -0,0 +1,40 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <opentelemetry/ext/http/client/http_client.h>
+#include <opentelemetry/version.h>
+
+#include <thrift/transport/TVirtualTransport.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+class THttpTransport : public apache::thrift::transport::TVirtualTransport<THttpTransport>
+{
+public:
+ THttpTransport(std::string endpoint, ext::http::client::Headers extra_headers);
+ ~THttpTransport() override;
+
+ bool isOpen() const override;
+
+ uint32_t read(uint8_t *buf, uint32_t len);
+
+ void write(const uint8_t *buf, uint32_t len);
+
+ bool sendSpans();
+
+private:
+ std::string endpoint;
+ ext::http::client::Headers headers;
+ std::shared_ptr<ext::http::client::HttpClientSync> client;
+ std::vector<uint8_t> request_buffer;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.cc
new file mode 100644
index 000000000..e41112739
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.cc
@@ -0,0 +1,113 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <sstream> // std::stringstream
+
+#include "TUDPTransport.h"
+#include "opentelemetry/sdk_config.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+TUDPTransport::TUDPTransport(const std::string &host, int port)
+ : host_(host), port_(port), socket_(THRIFT_INVALID_SOCKET)
+{}
+
+TUDPTransport::~TUDPTransport()
+{
+ if (server_addr_info_)
+ {
+ freeaddrinfo(server_addr_info_);
+ server_addr_info_ = nullptr;
+ sockaddr_len = 0;
+ }
+ close();
+}
+
+bool TUDPTransport::isOpen() const
+{
+ return (socket_ != THRIFT_INVALID_SOCKET);
+}
+
+void TUDPTransport::open()
+{
+ if (isOpen())
+ {
+ return;
+ }
+
+ struct addrinfo hints;
+ int error;
+ char port[sizeof("65535") + 1];
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+
+ sprintf(port, "%d", port_);
+
+ error = getaddrinfo(host_.c_str(), port, &hints, &server_addr_info_);
+
+ if (error)
+ {
+ OTEL_INTERNAL_LOG_ERROR("Jaeger Exporter: getaddrinfo failed with error: " << error);
+ return;
+ }
+
+ socket_ = socket(server_addr_info_->ai_family, server_addr_info_->ai_socktype,
+ server_addr_info_->ai_protocol);
+ sockaddr_len = server_addr_info_->ai_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in)
+ : sizeof(struct sockaddr_in6);
+}
+
+void TUDPTransport::close()
+{
+ if (socket_ != THRIFT_INVALID_SOCKET)
+ {
+ ::THRIFT_CLOSESOCKET(socket_);
+ }
+ socket_ = THRIFT_INVALID_SOCKET;
+}
+
+uint32_t TUDPTransport::read(uint8_t *buf, uint32_t len)
+{
+ if (!server_addr_info_)
+ {
+ return 0;
+ }
+ uint32_t num_read = recvfrom(socket_,
+#if defined(_WIN32)
+ reinterpret_cast<char *>(buf), len, 0, server_addr_info_->ai_addr,
+ reinterpret_cast<int *>(&sockaddr_len)
+#else
+ buf, len, 0, server_addr_info_->ai_addr, &sockaddr_len
+#endif
+ );
+
+ return num_read;
+}
+
+void TUDPTransport::write(const uint8_t *buf, uint32_t len)
+{
+ if (!server_addr_info_)
+ {
+ return;
+ }
+ sendto(socket_,
+#if defined(_WIN32)
+ reinterpret_cast<const char *>(buf),
+#else
+ buf,
+#endif
+ len, 0, server_addr_info_->ai_addr, sockaddr_len);
+}
+
+void TUDPTransport::flush() {}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.h
new file mode 100644
index 000000000..df3151d11
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.h
@@ -0,0 +1,53 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#ifdef _WIN32
+# include <winsock2.h>
+#else
+# include <netdb.h>
+# include <string.h>
+# include <sys/socket.h>
+# include <sys/types.h>
+#endif
+
+#include <opentelemetry/version.h>
+#include <thrift/transport/PlatformSocket.h>
+#include <thrift/transport/TVirtualTransport.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+class TUDPTransport : public apache::thrift::transport::TVirtualTransport<TUDPTransport>
+{
+public:
+ TUDPTransport(const std::string &host, int port);
+ ~TUDPTransport() override;
+
+ bool isOpen() const override;
+
+ void open() override;
+
+ void close() override;
+
+ uint32_t read(uint8_t *buf, uint32_t len);
+
+ void write(const uint8_t *buf, uint32_t len);
+
+ void flush() override;
+
+private:
+ std::string host_;
+ int port_;
+ THRIFT_SOCKET socket_;
+ struct addrinfo *server_addr_info_ = nullptr;
+ uint32_t sockaddr_len = 0;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.cc
new file mode 100644
index 000000000..f804ccc84
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.cc
@@ -0,0 +1,37 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "http_transport.h"
+
+#include <thrift/protocol/TBinaryProtocol.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using TBinaryProtocol = apache::thrift::protocol::TBinaryProtocol;
+using TTransport = apache::thrift::transport::TTransport;
+
+HttpTransport::HttpTransport(std::string endpoint, ext::http::client::Headers headers)
+{
+ endpoint_transport_ = std::make_shared<THttpTransport>(std::move(endpoint), std::move(headers));
+ protocol_ = std::shared_ptr<TProtocol>(new TBinaryProtocol(endpoint_transport_));
+}
+
+int HttpTransport::EmitBatch(const thrift::Batch &batch)
+{
+ batch.write(protocol_.get());
+
+ if (!endpoint_transport_->sendSpans())
+ {
+ return 0;
+ }
+
+ return static_cast<int>(batch.spans.size());
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.h
new file mode 100644
index 000000000..8f5c9c057
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/http_transport.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "THttpTransport.h"
+#include "transport.h"
+
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/transport/TTransport.h>
+#include <memory>
+#include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using TProtocol = apache::thrift::protocol::TProtocol;
+
+class HttpTransport : public Transport
+{
+public:
+ HttpTransport(std::string endpoint, ext::http::client::Headers headers);
+
+ int EmitBatch(const thrift::Batch &batch) override;
+
+ uint32_t MaxPacketSize() const override
+ {
+ // Default to 4 MiB POST body size.
+ return 1 << 22;
+ }
+
+private:
+ std::shared_ptr<THttpTransport> endpoint_transport_;
+ std::shared_ptr<TProtocol> protocol_;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/jaeger_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/jaeger_exporter.cc
new file mode 100644
index 000000000..c07f2f010
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/jaeger_exporter.cc
@@ -0,0 +1,111 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <agent_types.h>
+#include <opentelemetry/exporters/jaeger/jaeger_exporter.h>
+#include <opentelemetry/exporters/jaeger/recordable.h>
+#include "opentelemetry/sdk_config.h"
+
+#include "http_transport.h"
+#include "thrift_sender.h"
+#include "udp_transport.h"
+
+#include <mutex>
+#include <vector>
+
+namespace sdk_common = opentelemetry::sdk::common;
+namespace trace_sdk = opentelemetry::sdk::trace;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+JaegerExporter::JaegerExporter(const JaegerExporterOptions &options) : options_(options)
+{
+ InitializeEndpoint();
+}
+
+JaegerExporter::JaegerExporter() : JaegerExporter(JaegerExporterOptions()) {}
+
+JaegerExporter::JaegerExporter(std::unique_ptr<ThriftSender> sender)
+ : options_(JaegerExporterOptions()), sender_(std::move(sender))
+{}
+
+std::unique_ptr<trace_sdk::Recordable> JaegerExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<sdk::trace::Recordable>(new JaegerRecordable);
+}
+
+sdk_common::ExportResult JaegerExporter::Export(
+ const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[Jaeger Trace Exporter] Exporting "
+ << spans.size() << " span(s) failed, exporter is shutdown");
+ return sdk_common::ExportResult::kFailure;
+ }
+
+ std::size_t exported_size = 0;
+
+ for (auto &recordable : spans)
+ {
+ auto rec =
+ std::unique_ptr<JaegerRecordable>(static_cast<JaegerRecordable *>(recordable.release()));
+ if (rec != nullptr)
+ {
+ exported_size += sender_->Append(std::move(rec));
+ }
+ }
+
+ exported_size += sender_->Flush();
+
+ if (exported_size == 0)
+ {
+ return sdk_common::ExportResult::kFailure;
+ }
+
+ return sdk_common::ExportResult::kSuccess;
+}
+
+void JaegerExporter::InitializeEndpoint()
+{
+ if (options_.transport_format == TransportFormat::kThriftUdpCompact)
+ {
+ // TODO: do we need support any authentication mechanism?
+ auto transport = std::unique_ptr<Transport>(
+ static_cast<Transport *>(new UDPTransport(options_.endpoint, options_.server_port)));
+ sender_ = std::unique_ptr<ThriftSender>(new ThriftSender(std::move(transport)));
+ return;
+ }
+
+ if (options_.transport_format == TransportFormat::kThriftHttp)
+ {
+ auto transport =
+ std::unique_ptr<HttpTransport>(new HttpTransport(options_.endpoint, options_.headers));
+ sender_ = std::unique_ptr<ThriftSender>(new ThriftSender(std::move(transport)));
+ return;
+ }
+
+ // The transport format is not implemented.
+ assert(false);
+}
+
+bool JaegerExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool JaegerExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/recordable.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/recordable.cc
new file mode 100644
index 000000000..c4a61a5d2
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/recordable.cc
@@ -0,0 +1,365 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/jaeger/recordable.h"
+#include "opentelemetry/sdk/common/global_log_handler.h"
+#include "opentelemetry/sdk/resource/experimental_semantic_conventions.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using namespace opentelemetry::sdk::resource;
+namespace trace_api = opentelemetry::trace;
+
+JaegerRecordable::JaegerRecordable() : span_{new thrift::Span} {}
+
+void JaegerRecordable::PopulateAttribute(nostd::string_view key,
+ const common::AttributeValue &value,
+ std::vector<thrift::Tag> &tags)
+{
+ if (nostd::holds_alternative<int32_t>(value))
+ {
+ AddTag(std::string{key}, int64_t{nostd::get<int32_t>(value)}, tags);
+ }
+ else if (nostd::holds_alternative<uint32_t>(value))
+ {
+ AddTag(std::string{key}, int64_t{nostd::get<uint32_t>(value)}, tags);
+ }
+ else if (nostd::holds_alternative<int64_t>(value))
+ {
+ AddTag(std::string{key}, nostd::get<int64_t>(value), tags);
+ }
+ else if (nostd::holds_alternative<bool>(value))
+ {
+ AddTag(std::string{key}, nostd::get<bool>(value), tags);
+ }
+ else if (nostd::holds_alternative<double>(value))
+ {
+ AddTag(std::string{key}, nostd::get<double>(value), tags);
+ }
+ else if (nostd::holds_alternative<const char *>(value))
+ {
+ AddTag(std::string{key}, std::string{nostd::get<const char *>(value)}, tags);
+ }
+ else if (nostd::holds_alternative<nostd::string_view>(value))
+ {
+ AddTag(std::string{key}, std::string{nostd::get<nostd::string_view>(value)}, tags);
+ }
+ else if (nostd::holds_alternative<nostd::span<const bool>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const bool>>(value))
+ {
+ AddTag(std::string{key}, val, tags);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const int32_t>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const int32_t>>(value))
+ {
+ AddTag(std::string{key}, int64_t{val}, tags);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const int64_t>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const int64_t>>(value))
+ {
+ AddTag(std::string{key}, val, tags);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const uint32_t>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const uint32_t>>(value))
+ {
+ AddTag(std::string{key}, int64_t{val}, tags);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const double>>(value))
+ {
+ for (const auto &val : nostd::get<nostd::span<const double>>(value))
+ {
+ AddTag(std::string{key}, val, tags);
+ }
+ }
+ 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))
+ {
+ AddTag(std::string{key}, std::string{val}, tags);
+ }
+ }
+ else
+ {
+ OTEL_INTERNAL_LOG_ERROR(
+ "[TRACE JAEGER Exporter] SetAttribute() failed, attribute type not supported ");
+ }
+}
+
+void JaegerRecordable::PopulateAttribute(nostd::string_view key,
+ const sdk::common::OwnedAttributeValue &value,
+ std::vector<thrift::Tag> &tags)
+{
+ if (nostd::holds_alternative<int32_t>(value))
+ {
+ AddTag(std::string{key}, int64_t{nostd::get<int32_t>(value)}, tags);
+ }
+ else if (nostd::holds_alternative<uint32_t>(value))
+ {
+ AddTag(std::string{key}, int64_t{nostd::get<uint32_t>(value)}, tags);
+ }
+ else if (nostd::holds_alternative<int64_t>(value))
+ {
+ AddTag(std::string{key}, nostd::get<int64_t>(value), tags);
+ }
+ else if (nostd::holds_alternative<bool>(value))
+ {
+ AddTag(std::string{key}, nostd::get<bool>(value), tags);
+ }
+ else if (nostd::holds_alternative<double>(value))
+ {
+ AddTag(std::string{key}, nostd::get<double>(value), tags);
+ }
+ else if (nostd::holds_alternative<std::string>(value))
+ {
+ AddTag(std::string{key}, std::string{nostd::get<std::string>(value)}, tags);
+ }
+ else
+ {
+ OTEL_INTERNAL_LOG_ERROR(
+ "[TRACE JAEGER Exporter] SetAttribute() failed, attribute type not supported ");
+ }
+}
+
+void JaegerRecordable::SetIdentity(const trace::SpanContext &span_context,
+ trace::SpanId parent_span_id) noexcept
+{
+ // IDs should be converted to big endian before transmission.
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md#ids
+#if JAEGER_IS_LITTLE_ENDIAN == 1
+ span_->__set_traceIdHigh(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data()))));
+ span_->__set_traceIdLow(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data()) + 1)));
+ span_->__set_spanId(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(span_context.span_id().Id().data()))));
+ span_->__set_parentSpanId(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(parent_span_id.Id().data()))));
+#else
+ span_->__set_traceIdLow(
+ *(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data())));
+ span_->__set_traceIdHigh(
+ *(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data()) + 1));
+ span_->__set_spanId(*(reinterpret_cast<const int64_t *>(span_context.span_id().Id().data())));
+ span_->__set_parentSpanId(*(reinterpret_cast<const int64_t *>(parent_span_id.Id().data())));
+#endif
+
+ // TODO: set trace_state.
+}
+
+void JaegerRecordable::SetAttribute(nostd::string_view key,
+ const common::AttributeValue &value) noexcept
+{
+ PopulateAttribute(key, value, tags_);
+}
+
+void JaegerRecordable::AddEvent(nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept
+{
+ std::vector<thrift::Tag> tags;
+ PopulateAttribute("event", static_cast<common::AttributeValue>(name.data()), tags);
+
+ attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
+ PopulateAttribute(key, value, tags);
+ return true;
+ });
+ thrift::Log log;
+ log.__set_fields(tags);
+ log.__set_timestamp(
+ std::chrono::duration_cast<std::chrono::microseconds>(timestamp.time_since_epoch()).count());
+ logs_.push_back(log);
+}
+
+void JaegerRecordable::SetInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library) noexcept
+{
+ AddTag("otel.library.name", instrumentation_library.GetName(), tags_);
+ AddTag("otel.library.version", instrumentation_library.GetVersion(), tags_);
+}
+
+void JaegerRecordable::AddLink(const trace::SpanContext &span_context,
+ const common::KeyValueIterable &attributes) noexcept
+{
+ // Note: "The Link’s attributes cannot be represented in Jaeger explicitly."
+ // -- https://opentelemetry.io/docs/reference/specification/trace/sdk_exporters/jaeger/#links
+ //
+ // This implementation does not (currently) implement the optional conversion to span logs.
+
+ thrift::SpanRef reference;
+
+ reference.__set_refType(thrift::SpanRefType::FOLLOWS_FROM);
+
+ // IDs should be converted to big endian before transmission.
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md#ids
+#if JAEGER_IS_LITTLE_ENDIAN == 1
+ reference.__set_traceIdHigh(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data()))));
+ reference.__set_traceIdLow(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data()) + 1)));
+ reference.__set_spanId(
+ otel_bswap_64(*(reinterpret_cast<const int64_t *>(span_context.span_id().Id().data()))));
+#else
+ reference.__set_traceIdLow(
+ *(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data())));
+ reference.__set_traceIdHigh(
+ *(reinterpret_cast<const int64_t *>(span_context.trace_id().Id().data()) + 1));
+ reference.__set_spanId(*(reinterpret_cast<const int64_t *>(span_context.span_id().Id().data())));
+#endif
+
+ references_.push_back(reference);
+}
+
+void JaegerRecordable::SetStatus(trace::StatusCode code, nostd::string_view description) noexcept
+{
+ if (code == trace::StatusCode::kUnset)
+ {
+ return;
+ }
+
+ if (code == trace::StatusCode::kOk)
+ {
+ AddTag("otel.status_code", "OK", tags_);
+ }
+ else if (code == trace::StatusCode::kError)
+ {
+ AddTag("otel.status_code", "ERROR", tags_);
+ AddTag("error", true, tags_);
+ }
+
+ AddTag("otel.status_description", std::string{description}, tags_);
+}
+
+void JaegerRecordable::SetName(nostd::string_view name) noexcept
+{
+ span_->__set_operationName(static_cast<std::string>(name));
+}
+
+void JaegerRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept
+{
+ for (const auto &attribute_iter : resource.GetAttributes())
+ {
+ if (attribute_iter.first != "service.name")
+ {
+ PopulateAttribute(nostd::string_view{attribute_iter.first}, attribute_iter.second,
+ resource_tags_);
+ }
+ else
+ {
+ service_name_ = nostd::get<std::string>(attribute_iter.second);
+ }
+ }
+}
+
+void JaegerRecordable::SetStartTime(common::SystemTimestamp start_time) noexcept
+{
+ span_->__set_startTime(
+ std::chrono::duration_cast<std::chrono::microseconds>(start_time.time_since_epoch()).count());
+}
+
+void JaegerRecordable::SetDuration(std::chrono::nanoseconds duration) noexcept
+{
+ span_->__set_duration(std::chrono::duration_cast<std::chrono::microseconds>(duration).count());
+}
+
+void JaegerRecordable::SetSpanKind(trace::SpanKind span_kind) noexcept
+{
+ const char *span_kind_str = nullptr;
+
+ // map SpanKind to Jaeger tag span.kind.
+ switch (span_kind)
+ {
+ case trace_api::SpanKind::kClient: {
+ span_kind_str = "client";
+ break;
+ }
+ case trace_api::SpanKind::kServer: {
+ span_kind_str = "server";
+ break;
+ }
+ case trace_api::SpanKind::kConsumer: {
+ span_kind_str = "consumer";
+ break;
+ }
+ case trace_api::SpanKind::kProducer: {
+ span_kind_str = "producer";
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (span_kind_str != nullptr)
+ {
+ AddTag("span.kind", span_kind_str, tags_);
+ }
+}
+
+void JaegerRecordable::AddTag(const std::string &key,
+ const std::string &value,
+ std::vector<thrift::Tag> &tags)
+{
+ thrift::Tag tag;
+
+ tag.__set_key(key);
+ tag.__set_vType(thrift::TagType::STRING);
+ tag.__set_vStr(value);
+
+ tags.push_back(tag);
+}
+
+void JaegerRecordable::AddTag(const std::string &key,
+ const char *value,
+ std::vector<thrift::Tag> &tags)
+{
+ AddTag(key, std::string{value}, tags);
+}
+
+void JaegerRecordable::AddTag(const std::string &key, bool value, std::vector<thrift::Tag> &tags)
+{
+ thrift::Tag tag;
+
+ tag.__set_key(key);
+ tag.__set_vType(thrift::TagType::BOOL);
+ tag.__set_vBool(value);
+
+ tags.push_back(tag);
+}
+
+void JaegerRecordable::AddTag(const std::string &key, int64_t value, std::vector<thrift::Tag> &tags)
+{
+ thrift::Tag tag;
+
+ tag.__set_key(key);
+ tag.__set_vType(thrift::TagType::LONG);
+ tag.__set_vLong(value);
+
+ tags.push_back(tag);
+}
+
+void JaegerRecordable::AddTag(const std::string &key, double value, std::vector<thrift::Tag> &tags)
+{
+ thrift::Tag tag;
+
+ tag.__set_key(key);
+ tag.__set_vType(thrift::TagType::DOUBLE);
+ tag.__set_vDouble(value);
+
+ tags.push_back(tag);
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/sender.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/sender.h
new file mode 100644
index 000000000..f4827b550
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/sender.h
@@ -0,0 +1,32 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <opentelemetry/exporters/jaeger/recordable.h>
+#include <opentelemetry/version.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using namespace jaegertracing;
+
+class Sender
+{
+public:
+ Sender() = default;
+ virtual ~Sender() = default;
+
+ virtual int Append(std::unique_ptr<JaegerRecordable> &&span) = 0;
+
+ virtual int Flush() = 0;
+
+ virtual void Close() = 0;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.cc
new file mode 100644
index 000000000..46f0dcd5a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.cc
@@ -0,0 +1,99 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "thrift_sender.h"
+#include <opentelemetry/exporters/jaeger/recordable.h>
+#include "opentelemetry/sdk/common/global_log_handler.h"
+#include "udp_transport.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using namespace jaegertracing;
+
+ThriftSender::ThriftSender(std::unique_ptr<Transport> &&transport)
+ : transport_(std::move(transport)),
+ protocol_factory_(new apache::thrift::protocol::TCompactProtocolFactory()),
+ thrift_buffer_(new apache::thrift::transport::TMemoryBuffer(transport_->MaxPacketSize()))
+{}
+
+int ThriftSender::Append(std::unique_ptr<JaegerRecordable> &&span) noexcept
+{
+ if (span == nullptr)
+ {
+ return 0;
+ }
+
+ uint32_t max_span_bytes = transport_->MaxPacketSize() - kEmitBatchOverhead;
+ if (process_.serviceName.empty())
+ {
+ process_.serviceName = span->ServiceName();
+ process_.__set_tags(span->ResourceTags());
+
+ process_bytes_size_ = CalcSizeOfSerializedThrift(process_);
+ max_span_bytes -= process_bytes_size_;
+ }
+
+ auto jaeger_span = std::unique_ptr<thrift::Span>(span->Span());
+ jaeger_span->__set_tags(span->Tags());
+ jaeger_span->__set_logs(span->Logs());
+ jaeger_span->__set_references(span->References());
+
+ const uint32_t span_size = CalcSizeOfSerializedThrift(*jaeger_span);
+ if (span_size > max_span_bytes)
+ {
+ OTEL_INTERNAL_LOG_ERROR("[JAEGER TRACE Exporter] Append() failed: too large span");
+ return 0;
+ }
+
+ byte_buffer_size_ += span_size;
+ if (byte_buffer_size_ <= max_span_bytes)
+ {
+ span_buffer_.push_back(*jaeger_span);
+ if (byte_buffer_size_ < max_span_bytes)
+ {
+ return 0;
+ }
+ else
+ {
+ // byte buffer is full so flush it before appending new span.
+ return Flush();
+ }
+ }
+
+ const auto flushed = Flush();
+ span_buffer_.push_back(*jaeger_span);
+ byte_buffer_size_ = span_size + process_bytes_size_;
+
+ return flushed;
+}
+
+int ThriftSender::Flush()
+{
+ if (span_buffer_.empty())
+ {
+ return 0;
+ }
+
+ thrift::Batch batch;
+ batch.__set_process(process_);
+ batch.__set_spans(span_buffer_);
+
+ int spans_flushed = transport_->EmitBatch(batch);
+
+ ResetBuffers();
+
+ return spans_flushed;
+}
+
+void ThriftSender::Close()
+{
+ Flush();
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.h
new file mode 100644
index 000000000..0ec8d47f1
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/thrift_sender.h
@@ -0,0 +1,78 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <Agent.h>
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <thrift/protocol/TCompactProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+
+#include "sender.h"
+#include "transport.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using namespace jaegertracing;
+
+class ThriftSender : public Sender
+{
+public:
+ static constexpr uint32_t kEmitBatchOverhead = 30;
+
+ ThriftSender(std::unique_ptr<Transport> &&transport);
+ ~ThriftSender() override { Close(); }
+
+ int Append(std::unique_ptr<JaegerRecordable> &&span) noexcept override;
+ int Flush() override;
+ void Close() override;
+
+private:
+ void ResetBuffers()
+ {
+ span_buffer_.clear();
+ byte_buffer_size_ = process_bytes_size_;
+ }
+
+ template <typename ThriftType>
+ uint32_t CalcSizeOfSerializedThrift(const ThriftType &base)
+ {
+ uint8_t *data = nullptr;
+ uint32_t size = 0;
+
+ thrift_buffer_->resetBuffer();
+ auto protocol = protocol_factory_->getProtocol(thrift_buffer_);
+ base.write(protocol.get());
+ thrift_buffer_->getBuffer(&data, &size);
+ return size;
+ }
+
+private:
+ std::vector<std::unique_ptr<JaegerRecordable>> spans_;
+ std::vector<thrift::Span> span_buffer_;
+ std::unique_ptr<Transport> transport_;
+ std::unique_ptr<apache::thrift::protocol::TProtocolFactory> protocol_factory_;
+ std::shared_ptr<apache::thrift::transport::TMemoryBuffer> thrift_buffer_;
+ thrift::Process process_;
+
+ // Size in bytes of the serialization buffer.
+ uint32_t byte_buffer_size_ = 0;
+ uint32_t process_bytes_size_ = 0;
+ uint32_t max_span_bytes_ = 0;
+ friend class MockThriftSender;
+
+protected:
+ ThriftSender() = default;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/transport.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/transport.h
new file mode 100644
index 000000000..8121e3007
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/transport.h
@@ -0,0 +1,30 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <opentelemetry/version.h>
+
+#include <jaeger_types.h>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using namespace jaegertracing;
+
+class Transport
+{
+public:
+ Transport() = default;
+ virtual ~Transport() = default;
+
+ virtual int EmitBatch(const thrift::Batch &batch) = 0;
+ virtual uint32_t MaxPacketSize() const = 0;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.cc
new file mode 100644
index 000000000..9b1fe0caf
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.cc
@@ -0,0 +1,86 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <sstream> // std::stringstream
+
+#include "opentelemetry/sdk_config.h"
+#include "udp_transport.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+UDPTransport::UDPTransport(const std::string &addr, uint16_t port)
+ : max_packet_size_(kUDPPacketMaxLength)
+{
+ InitSocket();
+
+ endpoint_transport_ = std::shared_ptr<TTransport>(new TUDPTransport(addr, port));
+ endpoint_transport_->open();
+ transport_ =
+ std::shared_ptr<TTransport>(new TBufferedTransport(endpoint_transport_, max_packet_size_));
+ protocol_ = std::shared_ptr<TProtocol>(new TCompactProtocol(transport_));
+ agent_ = std::unique_ptr<AgentClient>(new AgentClient(protocol_));
+}
+
+UDPTransport::~UDPTransport()
+{
+ CleanSocket();
+}
+
+void UDPTransport::InitSocket()
+{
+#if defined(_WIN32)
+ /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
+ WORD wVersionRequested = MAKEWORD(2, 2);
+
+ WSADATA wsaData;
+ int err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0)
+ {
+ OTEL_INTERNAL_LOG_ERROR("Jaeger Exporter: WSAStartup failed with error: " << err);
+ return;
+ }
+
+ /* Confirm that the WinSock DLL supports 2.2. */
+ /* Note that if the DLL supports versions greater */
+ /* than 2.2 in addition to 2.2, it will still return */
+ /* 2.2 in wVersion since that is the version we */
+ /* requested. */
+
+ if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
+ {
+ OTEL_INTERNAL_LOG_ERROR("Jaeger Exporter: winsock " << LOBYTE(wsaData.wVersion) << "."
+ << HIBYTE(wsaData.wVersion)
+ << " is not supported.");
+ WSACleanup();
+
+ return;
+ }
+#endif
+}
+
+void UDPTransport::CleanSocket()
+{
+#if defined(_WIN32)
+ WSACleanup();
+#endif
+}
+
+int UDPTransport::EmitBatch(const thrift::Batch &batch)
+{
+ try
+ {
+ agent_->emitBatch(batch);
+ }
+ catch (...)
+ {}
+
+ return static_cast<int>(batch.spans.size());
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.h
new file mode 100644
index 000000000..0997a27c6
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/udp_transport.h
@@ -0,0 +1,57 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "TUDPTransport.h"
+#include "transport.h"
+
+#include <Agent.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TCompactProtocol.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TTransport.h>
+#include <memory>
+#include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+using AgentClient = jaegertracing::agent::thrift::AgentClient;
+using TBinaryProtocol = apache::thrift::protocol::TBinaryProtocol;
+using TCompactProtocol = apache::thrift::protocol::TCompactProtocol;
+using TBufferedTransport = apache::thrift::transport::TBufferedTransport;
+using TProtocol = apache::thrift::protocol::TProtocol;
+using TTransport = apache::thrift::transport::TTransport;
+
+class UDPTransport : public Transport
+{
+public:
+ static constexpr auto kUDPPacketMaxLength = 65000;
+
+ UDPTransport(const std::string &addr, uint16_t port);
+ virtual ~UDPTransport();
+
+ int EmitBatch(const thrift::Batch &batch) override;
+
+ uint32_t MaxPacketSize() const override { return max_packet_size_; }
+
+ void InitSocket();
+ void CleanSocket();
+
+private:
+ std::unique_ptr<AgentClient> agent_;
+ std::shared_ptr<TTransport> endpoint_transport_;
+ std::shared_ptr<TTransport> transport_;
+ std::shared_ptr<TProtocol> protocol_;
+ uint32_t max_packet_size_;
+};
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_exporter_test.cc
new file mode 100644
index 000000000..7f86f877b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_exporter_test.cc
@@ -0,0 +1,175 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <opentelemetry/exporters/jaeger/jaeger_exporter.h>
+#include <memory>
+#include <vector>
+#include "opentelemetry/sdk/trace/batch_span_processor.h"
+#include "opentelemetry/sdk/trace/tracer_provider.h"
+
+#ifdef BAZEL_BUILD
+# include "exporters/jaeger/src/thrift_sender.h"
+#else
+# include "thrift_sender.h"
+#endif
+
+#include <gtest/gtest.h>
+#include "gmock/gmock.h"
+
+namespace trace = opentelemetry::trace;
+namespace nostd = opentelemetry::nostd;
+namespace sdktrace = opentelemetry::sdk::trace;
+namespace common = opentelemetry::common;
+namespace sdk_common = opentelemetry::sdk::common;
+
+using namespace testing;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace jaeger
+{
+
+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);
+}
+
+class JaegerExporterTestPeer : public ::testing::Test
+{
+public:
+ std::unique_ptr<sdk::trace::SpanExporter> GetExporter(std::unique_ptr<ThriftSender> sender)
+ {
+ return std::unique_ptr<sdk::trace::SpanExporter>(new JaegerExporter(std::move(sender)));
+ }
+
+ // Get the options associated with the given exporter.
+ const JaegerExporterOptions &GetOptions(std::unique_ptr<JaegerExporter> &exporter)
+ {
+ return exporter->options_;
+ }
+};
+
+class MockThriftSender : public ThriftSender
+{
+public:
+ MOCK_METHOD(int, Append, (std::unique_ptr<JaegerRecordable> &&), (noexcept, override));
+};
+
+class MockTransport : public Transport
+{
+public:
+ MOCK_METHOD(int, EmitBatch, (const thrift::Batch &), (override));
+ MOCK_METHOD(uint32_t, MaxPacketSize, (), (const, override));
+};
+
+// Create spans, let processor call Export()
+TEST_F(JaegerExporterTestPeer, ExportIntegrationTest)
+{
+ auto mock_transport = new MockTransport;
+ auto mock_thrift_sender = new ThriftSender(std::unique_ptr<MockTransport>{mock_transport});
+ auto exporter = GetExporter(std::unique_ptr<ThriftSender>{mock_thrift_sender});
+
+ 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["double_value"] = static_cast<double>(3.1);
+ 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));
+
+ EXPECT_CALL(*mock_transport, EmitBatch(_)).Times(Exactly(1)).WillOnce(Return(1));
+
+ auto tracer = provider->GetTracer("test");
+ auto parent_span = tracer->StartSpan("Test parent span");
+
+ trace_api::StartSpanOptions child_span_opts = {};
+ child_span_opts.parent = parent_span->GetContext();
+ auto child_span = tracer->StartSpan("Test child span", child_span_opts);
+
+ child_span->End();
+ parent_span->End();
+
+ auto parent_ctx = parent_span->GetContext();
+ auto child_ctx = child_span->GetContext();
+ EXPECT_EQ(parent_ctx.trace_id(), child_ctx.trace_id());
+ EXPECT_EQ(parent_ctx.trace_state(), child_ctx.trace_state());
+ ASSERT_TRUE(parent_ctx.IsValid());
+ ASSERT_TRUE(child_ctx.IsValid());
+}
+
+TEST_F(JaegerExporterTestPeer, ShutdownTest)
+{
+ auto mock_thrift_sender = new MockThriftSender;
+ auto exporter = GetExporter(std::unique_ptr<ThriftSender>{mock_thrift_sender});
+
+ 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_thrift_sender, Append(_)).Times(Exactly(1)).WillOnce(Return(1));
+ 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(JaegerExporterTestPeer, ExportTest)
+{
+ auto mock_thrift_sender = new MockThriftSender;
+ auto exporter = GetExporter(std::unique_ptr<ThriftSender>{mock_thrift_sender});
+
+ auto recordable_1 = exporter->MakeRecordable();
+ recordable_1->SetName("Test span 1");
+ auto recordable_2 = exporter->MakeRecordable();
+ recordable_2->SetName("Test span 2");
+
+ // Test successful send
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
+ EXPECT_CALL(*mock_thrift_sender, Append(_)).Times(Exactly(1)).WillOnce(Return(1));
+ auto result = exporter->Export(batch_1);
+ EXPECT_EQ(sdk_common::ExportResult::kSuccess, result);
+
+ // Test failed send
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
+ EXPECT_CALL(*mock_thrift_sender, Append(_)).Times(Exactly(1)).WillOnce(Return(0));
+ result = exporter->Export(batch_2);
+ EXPECT_EQ(sdk::common::ExportResult::kFailure, result);
+}
+
+// Test exporter configuration options
+TEST_F(JaegerExporterTestPeer, ConfigTest)
+{
+ JaegerExporterOptions opts;
+ opts.endpoint = "localhost";
+ opts.server_port = 6851;
+ std::unique_ptr<JaegerExporter> exporter(new JaegerExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).endpoint, "localhost");
+ EXPECT_EQ(GetOptions(exporter).server_port, 6851);
+}
+
+} // namespace jaeger
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_recordable_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_recordable_test.cc
new file mode 100644
index 000000000..4a0a1f74b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/test/jaeger_recordable_test.cc
@@ -0,0 +1,334 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <vector>
+#include "opentelemetry/exporters/jaeger/recordable.h"
+#include "opentelemetry/sdk/instrumentationlibrary/instrumentation_library.h"
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/sdk/trace/tracer_provider.h"
+#include "opentelemetry/trace/provider.h"
+
+#include <gtest/gtest.h>
+
+namespace trace = opentelemetry::trace;
+namespace nostd = opentelemetry::nostd;
+namespace sdktrace = opentelemetry::sdk::trace;
+namespace common = opentelemetry::common;
+
+using namespace jaegertracing;
+using namespace opentelemetry::exporter::jaeger;
+using namespace opentelemetry::sdk::instrumentationlibrary;
+using std::vector;
+
+using Attributes = std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>;
+
+TEST(JaegerSpanRecordable, SetIdentity)
+{
+ JaegerRecordable rec;
+
+ int64_t trace_id_val[2] = {0x0000000000000000, 0x1000000000000000};
+ int64_t span_id_val = 0x2000000000000000;
+ int64_t parent_span_id_val = 0x3000000000000000;
+
+ const trace::TraceId trace_id{
+ nostd::span<uint8_t, 16>(reinterpret_cast<uint8_t *>(trace_id_val), 16)};
+
+ const trace::SpanId span_id(
+ nostd::span<uint8_t, 8>(reinterpret_cast<uint8_t *>(&span_id_val), 8));
+
+ const trace::SpanId parent_span_id(
+ nostd::span<uint8_t, 8>(reinterpret_cast<uint8_t *>(&parent_span_id_val), 8));
+
+ const trace::SpanContext span_context{trace_id, span_id,
+ trace::TraceFlags{trace::TraceFlags::kIsSampled}, true};
+ rec.SetIdentity(span_context, parent_span_id);
+
+ std::unique_ptr<thrift::Span> span{rec.Span()};
+
+#if JAEGER_IS_LITTLE_ENDIAN == 1
+ EXPECT_EQ(span->traceIdLow, otel_bswap_64(trace_id_val[1]));
+ EXPECT_EQ(span->traceIdHigh, otel_bswap_64(trace_id_val[0]));
+ EXPECT_EQ(span->spanId, otel_bswap_64(span_id_val));
+ EXPECT_EQ(span->parentSpanId, otel_bswap_64(parent_span_id_val));
+#else
+ EXPECT_EQ(span->traceIdLow, trace_id_val[0]);
+ EXPECT_EQ(span->traceIdHigh, trace_id_val[1]);
+ EXPECT_EQ(span->spanId, span_id_val);
+ EXPECT_EQ(span->parentSpanId, parent_span_id_val);
+#endif
+}
+
+TEST(JaegerSpanRecordable, SetName)
+{
+ JaegerRecordable rec;
+
+ nostd::string_view name = "Test Span";
+ rec.SetName(name);
+
+ std::unique_ptr<thrift::Span> span{rec.Span()};
+
+ EXPECT_EQ(span->operationName, name);
+}
+
+TEST(JaegerSpanRecordable, SetStartTime)
+{
+ JaegerRecordable 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::microseconds>(start_time.time_since_epoch()).count();
+ rec.SetStartTime(start_timestamp);
+
+ std::unique_ptr<thrift::Span> span{rec.Span()};
+
+ EXPECT_EQ(span->startTime, unix_start);
+}
+
+TEST(JaegerSpanRecordable, SetDuration)
+{
+ JaegerRecordable rec;
+
+ common::SystemTimestamp start_timestamp;
+
+ std::chrono::microseconds duration(10);
+ uint64_t unix_end = duration.count();
+
+ rec.SetStartTime(start_timestamp);
+ rec.SetDuration(duration);
+
+ std::unique_ptr<thrift::Span> span{rec.Span()};
+
+ EXPECT_EQ(span->startTime, 0);
+ EXPECT_EQ(span->duration, unix_end);
+}
+
+TEST(JaegerSpanRecordable, SetStatus)
+{
+ JaegerRecordable rec;
+
+ const char *error_description = "Error test";
+ rec.SetStatus(trace::StatusCode::kError, error_description);
+
+ auto tags = rec.Tags();
+ EXPECT_EQ(tags.size(), 3);
+
+ EXPECT_EQ(tags[0].key, "otel.status_code");
+ EXPECT_EQ(tags[0].vType, thrift::TagType::STRING);
+ EXPECT_EQ(tags[0].vStr, "ERROR");
+
+ EXPECT_EQ(tags[1].key, "error");
+ EXPECT_EQ(tags[1].vType, thrift::TagType::BOOL);
+ EXPECT_EQ(tags[1].vBool, true);
+
+ EXPECT_EQ(tags[2].key, "otel.status_description");
+ EXPECT_EQ(tags[2].vType, thrift::TagType::STRING);
+ EXPECT_EQ(tags[2].vStr, error_description);
+}
+
+TEST(JaegerSpanRecordable, AddEvent)
+{
+ JaegerRecordable rec;
+
+ std::chrono::system_clock::time_point event_time = std::chrono::system_clock::now();
+ common::SystemTimestamp event_timestamp(event_time);
+ uint64_t epoch_us =
+ std::chrono::duration_cast<std::chrono::microseconds>(event_time.time_since_epoch()).count();
+
+ const int kNumAttributes = 3;
+ std::string keys[kNumAttributes] = {"attr1", "attr2", "attr3"};
+ int64_t values[kNumAttributes] = {4, 7, 23};
+ std::map<std::string, int64_t> attributes = {
+ {keys[0], values[0]}, {keys[1], values[1]}, {keys[2], values[2]}};
+
+ rec.AddEvent("Test Event", event_timestamp,
+ common::KeyValueIterableView<std::map<std::string, int64_t>>(attributes));
+ thrift::Log log = rec.Logs().at(0);
+ EXPECT_EQ(log.timestamp, epoch_us);
+ auto tags = log.fields;
+ size_t index = 0;
+ EXPECT_EQ(tags[index].key, "event");
+ EXPECT_EQ(tags[index++].vStr, "Test Event");
+ while (index <= kNumAttributes)
+ {
+ EXPECT_EQ(tags[index].key, keys[index - 1]);
+ EXPECT_EQ(tags[index].vLong, values[index - 1]);
+ index++;
+ }
+}
+
+template <typename value_type>
+void addTag(thrift::TagType::type tag_type,
+ const std::string &key,
+ value_type value,
+ vector<thrift::Tag> &tags)
+{
+ thrift::Tag tag;
+
+ tag.__set_key(key);
+ tag.__set_vType(tag_type);
+ if (tag_type == thrift::TagType::LONG)
+ {
+ tag.__set_vLong(static_cast<int64_t>(value));
+ }
+ else if (tag_type == thrift::TagType::DOUBLE)
+ {
+ tag.__set_vDouble(static_cast<double>(value));
+ }
+ else if (tag_type == thrift::TagType::BOOL)
+ {
+ tag.__set_vBool(static_cast<bool>(value));
+ }
+
+ tags.push_back(tag);
+}
+
+void addTag(const std::string &key, std::string value, vector<thrift::Tag> &tags)
+{
+ thrift::Tag tag;
+
+ tag.__set_key(key);
+ tag.__set_vType(thrift::TagType::STRING);
+ tag.__set_vStr(value);
+
+ tags.push_back(tag);
+}
+
+TEST(JaegerSpanRecordable, SetAttributes)
+{
+ JaegerRecordable rec;
+ std::string string_val{"string_val"};
+ vector<common::AttributeValue> values{
+ bool{false},
+ int32_t{-32},
+ int64_t{-64},
+ uint32_t{32},
+ double{3.14},
+ string_val.c_str(),
+ nostd::string_view{"string_view"},
+ };
+ for (const auto &val : values)
+ {
+ rec.SetAttribute("key1", val);
+ }
+ rec.SetAttribute("key2", nostd::span<const bool>{{false, true}});
+ rec.SetAttribute("key3", nostd::span<const int32_t>{{-320, 320}});
+ rec.SetAttribute("key4", nostd::span<const int64_t>{{-640, 640}});
+ rec.SetAttribute("key5", nostd::span<const uint32_t>{{320, 322}});
+ rec.SetAttribute("key6", nostd::span<const double>{{4.15, 5.15}});
+ rec.SetAttribute("key7", nostd::span<const nostd::string_view>{{"string_v1", "string_v2"}});
+
+ auto tags = rec.Tags();
+ EXPECT_EQ(tags.size(), values.size() + 12);
+
+ vector<thrift::Tag> expected_tags;
+ addTag(thrift::TagType::BOOL, "key1", bool{false}, expected_tags);
+ addTag(thrift::TagType::LONG, "key1", int32_t{-32}, expected_tags);
+ addTag(thrift::TagType::LONG, "key1", int64_t{-64}, expected_tags);
+ addTag(thrift::TagType::LONG, "key1", int32_t{32}, expected_tags);
+ addTag(thrift::TagType::DOUBLE, "key1", double{3.14}, expected_tags);
+ addTag("key1", string_val, expected_tags);
+ addTag("key1", std::string{"string_view"}, expected_tags);
+
+ addTag(thrift::TagType::BOOL, "key2", bool{false}, expected_tags);
+ addTag(thrift::TagType::BOOL, "key2", bool{true}, expected_tags);
+ addTag(thrift::TagType::LONG, "key3", int32_t{-320}, expected_tags);
+ addTag(thrift::TagType::LONG, "key3", int32_t{320}, expected_tags);
+ addTag(thrift::TagType::LONG, "key4", int64_t{-640}, expected_tags);
+ addTag(thrift::TagType::LONG, "key4", int64_t{640}, expected_tags);
+ addTag(thrift::TagType::LONG, "key5", uint32_t{320}, expected_tags);
+ addTag(thrift::TagType::LONG, "key5", uint32_t{322}, expected_tags);
+ addTag(thrift::TagType::DOUBLE, "key6", double{4.15}, expected_tags);
+ addTag(thrift::TagType::DOUBLE, "key6", double{5.15}, expected_tags);
+ addTag("key7", std::string{"string_v1"}, expected_tags);
+ addTag("key7", std::string{"string_v2"}, expected_tags);
+
+ EXPECT_EQ(tags, expected_tags);
+}
+
+TEST(JaegerSpanRecordable, SetInstrumentationLibrary)
+{
+ JaegerRecordable rec;
+
+ std::string library_name = "opentelemetry-cpp";
+ std::string library_version = "0.1.0";
+ auto instrumentation_library = InstrumentationLibrary::Create(library_name, library_version);
+
+ rec.SetInstrumentationLibrary(*instrumentation_library);
+
+ auto tags = rec.Tags();
+ EXPECT_EQ(tags.size(), 2);
+
+ EXPECT_EQ(tags[0].key, "otel.library.name");
+ EXPECT_EQ(tags[0].vType, thrift::TagType::STRING);
+ EXPECT_EQ(tags[0].vStr, library_name);
+
+ EXPECT_EQ(tags[1].key, "otel.library.version");
+ EXPECT_EQ(tags[1].vType, thrift::TagType::STRING);
+ EXPECT_EQ(tags[1].vStr, library_version);
+}
+
+TEST(JaegerSpanRecordable, SetResource)
+{
+ JaegerRecordable rec;
+
+ const std::string service_name_key = "service.name";
+ std::string service_name_value = "test-jaeger-service-name";
+ auto resource = opentelemetry::sdk::resource::Resource::Create(
+ {{service_name_key, service_name_value}, {"key1", "value1"}, {"key2", "value2"}});
+ rec.SetResource(resource);
+
+ auto service_name = rec.ServiceName();
+ auto resource_tags = rec.ResourceTags();
+
+ EXPECT_GE(resource_tags.size(), 2);
+ EXPECT_EQ(service_name, service_name_value);
+
+ for (const auto &tag : resource_tags)
+ {
+ if (tag.key == "key1")
+ {
+ EXPECT_EQ(tag.vType, thrift::TagType::STRING);
+ EXPECT_EQ(tag.vStr, "value1");
+ }
+ else if (tag.key == "key2")
+ {
+ EXPECT_EQ(tag.vType, thrift::TagType::STRING);
+ EXPECT_EQ(tag.vStr, "value2");
+ }
+ }
+}
+
+TEST(JaegerSpanRecordable, AddLink)
+{
+ JaegerRecordable rec;
+
+ int64_t trace_id_val[2] = {0x0000000000000000, 0x1000000000000000};
+ int64_t span_id_val = 0x2000000000000000;
+
+ const trace::TraceId trace_id{
+ nostd::span<uint8_t, 16>(reinterpret_cast<uint8_t *>(trace_id_val), 16)};
+
+ const trace::SpanId span_id(
+ nostd::span<uint8_t, 8>(reinterpret_cast<uint8_t *>(&span_id_val), 8));
+
+ const trace::SpanContext span_context{trace_id, span_id,
+ trace::TraceFlags{trace::TraceFlags::kIsSampled}, true};
+ rec.AddLink(span_context, common::KeyValueIterableView<Attributes>({{"attr1", "string"}}));
+
+ auto references = rec.References();
+ EXPECT_EQ(references.size(), 1);
+
+ auto reference = references.front();
+
+#if JAEGER_IS_LITTLE_ENDIAN == 1
+ EXPECT_EQ(reference.traceIdLow, otel_bswap_64(trace_id_val[1]));
+ EXPECT_EQ(reference.traceIdHigh, otel_bswap_64(trace_id_val[0]));
+ EXPECT_EQ(reference.spanId, otel_bswap_64(span_id_val));
+#else
+ EXPECT_EQ(reference.traceIdLow, trace_id_val[0]);
+ EXPECT_EQ(reference.traceIdHigh, trace_id_val[1]);
+ EXPECT_EQ(reference.spanId, span_id_val);
+#endif
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.cpp
new file mode 100644
index 000000000..4ff023650
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.cpp
@@ -0,0 +1,380 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#include "Agent.h"
+
+namespace jaegertracing { namespace agent { namespace thrift {
+
+
+Agent_emitZipkinBatch_args::~Agent_emitZipkinBatch_args() noexcept {
+}
+
+
+uint32_t Agent_emitZipkinBatch_args::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->spans.clear();
+ uint32_t _size0;
+ ::apache::thrift::protocol::TType _etype3;
+ xfer += iprot->readListBegin(_etype3, _size0);
+ this->spans.resize(_size0);
+ uint32_t _i4;
+ for (_i4 = 0; _i4 < _size0; ++_i4)
+ {
+ xfer += this->spans[_i4].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.spans = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Agent_emitZipkinBatch_args::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Agent_emitZipkinBatch_args");
+
+ xfer += oprot->writeFieldBegin("spans", ::apache::thrift::protocol::T_LIST, 1);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->spans.size()));
+ std::vector< ::twitter::zipkin::thrift::Span> ::const_iterator _iter5;
+ for (_iter5 = this->spans.begin(); _iter5 != this->spans.end(); ++_iter5)
+ {
+ xfer += (*_iter5).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+Agent_emitZipkinBatch_pargs::~Agent_emitZipkinBatch_pargs() noexcept {
+}
+
+
+uint32_t Agent_emitZipkinBatch_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Agent_emitZipkinBatch_pargs");
+
+ xfer += oprot->writeFieldBegin("spans", ::apache::thrift::protocol::T_LIST, 1);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>((*(this->spans)).size()));
+ std::vector< ::twitter::zipkin::thrift::Span> ::const_iterator _iter6;
+ for (_iter6 = (*(this->spans)).begin(); _iter6 != (*(this->spans)).end(); ++_iter6)
+ {
+ xfer += (*_iter6).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+Agent_emitBatch_args::~Agent_emitBatch_args() noexcept {
+}
+
+
+uint32_t Agent_emitBatch_args::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_STRUCT) {
+ xfer += this->batch.read(iprot);
+ this->__isset.batch = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Agent_emitBatch_args::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Agent_emitBatch_args");
+
+ xfer += oprot->writeFieldBegin("batch", ::apache::thrift::protocol::T_STRUCT, 1);
+ xfer += this->batch.write(oprot);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+Agent_emitBatch_pargs::~Agent_emitBatch_pargs() noexcept {
+}
+
+
+uint32_t Agent_emitBatch_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Agent_emitBatch_pargs");
+
+ xfer += oprot->writeFieldBegin("batch", ::apache::thrift::protocol::T_STRUCT, 1);
+ xfer += (*(this->batch)).write(oprot);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void AgentClient::emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans)
+{
+ send_emitZipkinBatch(spans);
+}
+
+void AgentClient::send_emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans)
+{
+ int32_t cseqid = 0;
+ oprot_->writeMessageBegin("emitZipkinBatch", ::apache::thrift::protocol::T_ONEWAY, cseqid);
+
+ Agent_emitZipkinBatch_pargs args;
+ args.spans = &spans;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+}
+
+void AgentClient::emitBatch(const ::jaegertracing::thrift::Batch& batch)
+{
+ send_emitBatch(batch);
+}
+
+void AgentClient::send_emitBatch(const ::jaegertracing::thrift::Batch& batch)
+{
+ int32_t cseqid = 0;
+ oprot_->writeMessageBegin("emitBatch", ::apache::thrift::protocol::T_ONEWAY, cseqid);
+
+ Agent_emitBatch_pargs args;
+ args.batch = &batch;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+}
+
+bool AgentProcessor::dispatchCall(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, const std::string& fname, int32_t seqid, void* callContext) {
+ ProcessMap::iterator pfn;
+ pfn = processMap_.find(fname);
+ if (pfn == processMap_.end()) {
+ iprot->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot->readMessageEnd();
+ iprot->getTransport()->readEnd();
+ ::apache::thrift::TApplicationException x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, "Invalid method name: '"+fname+"'");
+ oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid);
+ x.write(oprot);
+ oprot->writeMessageEnd();
+ oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+ return true;
+ }
+ (this->*(pfn->second))(seqid, iprot, oprot, callContext);
+ return true;
+}
+
+void AgentProcessor::process_emitZipkinBatch(int32_t, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol*, void* callContext)
+{
+ void* ctx = nullptr;
+ if (this->eventHandler_.get() != nullptr) {
+ ctx = this->eventHandler_->getContext("Agent.emitZipkinBatch", callContext);
+ }
+ ::apache::thrift::TProcessorContextFreer freer(this->eventHandler_.get(), ctx, "Agent.emitZipkinBatch");
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->preRead(ctx, "Agent.emitZipkinBatch");
+ }
+
+ Agent_emitZipkinBatch_args args;
+ args.read(iprot);
+ iprot->readMessageEnd();
+ uint32_t bytes = iprot->getTransport()->readEnd();
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->postRead(ctx, "Agent.emitZipkinBatch", bytes);
+ }
+
+ try {
+ iface_->emitZipkinBatch(args.spans);
+ } catch (const std::exception&) {
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->handlerError(ctx, "Agent.emitZipkinBatch");
+ }
+ return;
+ }
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->asyncComplete(ctx, "Agent.emitZipkinBatch");
+ }
+
+ return;
+}
+
+void AgentProcessor::process_emitBatch(int32_t, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol*, void* callContext)
+{
+ void* ctx = nullptr;
+ if (this->eventHandler_.get() != nullptr) {
+ ctx = this->eventHandler_->getContext("Agent.emitBatch", callContext);
+ }
+ ::apache::thrift::TProcessorContextFreer freer(this->eventHandler_.get(), ctx, "Agent.emitBatch");
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->preRead(ctx, "Agent.emitBatch");
+ }
+
+ Agent_emitBatch_args args;
+ args.read(iprot);
+ iprot->readMessageEnd();
+ uint32_t bytes = iprot->getTransport()->readEnd();
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->postRead(ctx, "Agent.emitBatch", bytes);
+ }
+
+ try {
+ iface_->emitBatch(args.batch);
+ } catch (const std::exception&) {
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->handlerError(ctx, "Agent.emitBatch");
+ }
+ return;
+ }
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->asyncComplete(ctx, "Agent.emitBatch");
+ }
+
+ return;
+}
+
+::std::shared_ptr< ::apache::thrift::TProcessor > AgentProcessorFactory::getProcessor(const ::apache::thrift::TConnectionInfo& connInfo) {
+ ::apache::thrift::ReleaseHandler< AgentIfFactory > cleanup(handlerFactory_);
+ ::std::shared_ptr< AgentIf > handler(handlerFactory_->getHandler(connInfo), cleanup);
+ ::std::shared_ptr< ::apache::thrift::TProcessor > processor(new AgentProcessor(handler));
+ return processor;
+}
+
+void AgentConcurrentClient::emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans)
+{
+ send_emitZipkinBatch(spans);
+}
+
+void AgentConcurrentClient::send_emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans)
+{
+ int32_t cseqid = 0;
+ ::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());
+ oprot_->writeMessageBegin("emitZipkinBatch", ::apache::thrift::protocol::T_ONEWAY, cseqid);
+
+ Agent_emitZipkinBatch_pargs args;
+ args.spans = &spans;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+
+ sentry.commit();
+}
+
+void AgentConcurrentClient::emitBatch(const ::jaegertracing::thrift::Batch& batch)
+{
+ send_emitBatch(batch);
+}
+
+void AgentConcurrentClient::send_emitBatch(const ::jaegertracing::thrift::Batch& batch)
+{
+ int32_t cseqid = 0;
+ ::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());
+ oprot_->writeMessageBegin("emitBatch", ::apache::thrift::protocol::T_ONEWAY, cseqid);
+
+ Agent_emitBatch_pargs args;
+ args.batch = &batch;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+
+ sentry.commit();
+}
+
+}}} // namespace
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.h
new file mode 100644
index 000000000..49abaf54a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent.h
@@ -0,0 +1,309 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef Agent_H
+#define Agent_H
+
+#include <thrift/TDispatchProcessor.h>
+#include <thrift/async/TConcurrentClientSyncInfo.h>
+#include <memory>
+#include "agent_types.h"
+
+namespace jaegertracing { namespace agent { namespace thrift {
+
+#ifdef _MSC_VER
+ #pragma warning( push )
+ #pragma warning (disable : 4250 ) //inheriting methods via dominance
+#endif
+
+class AgentIf {
+ public:
+ virtual ~AgentIf() {}
+ virtual void emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans) = 0;
+ virtual void emitBatch(const ::jaegertracing::thrift::Batch& batch) = 0;
+};
+
+class AgentIfFactory {
+ public:
+ typedef AgentIf Handler;
+
+ virtual ~AgentIfFactory() {}
+
+ virtual AgentIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) = 0;
+ virtual void releaseHandler(AgentIf* /* handler */) = 0;
+};
+
+class AgentIfSingletonFactory : virtual public AgentIfFactory {
+ public:
+ AgentIfSingletonFactory(const ::std::shared_ptr<AgentIf>& iface) : iface_(iface) {}
+ virtual ~AgentIfSingletonFactory() {}
+
+ virtual AgentIf* getHandler(const ::apache::thrift::TConnectionInfo&) {
+ return iface_.get();
+ }
+ virtual void releaseHandler(AgentIf* /* handler */) {}
+
+ protected:
+ ::std::shared_ptr<AgentIf> iface_;
+};
+
+class AgentNull : virtual public AgentIf {
+ public:
+ virtual ~AgentNull() {}
+ void emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & /* spans */) {
+ return;
+ }
+ void emitBatch(const ::jaegertracing::thrift::Batch& /* batch */) {
+ return;
+ }
+};
+
+typedef struct _Agent_emitZipkinBatch_args__isset {
+ _Agent_emitZipkinBatch_args__isset() : spans(false) {}
+ bool spans :1;
+} _Agent_emitZipkinBatch_args__isset;
+
+class Agent_emitZipkinBatch_args {
+ public:
+
+ Agent_emitZipkinBatch_args(const Agent_emitZipkinBatch_args&);
+ Agent_emitZipkinBatch_args& operator=(const Agent_emitZipkinBatch_args&);
+ Agent_emitZipkinBatch_args() {
+ }
+
+ virtual ~Agent_emitZipkinBatch_args() noexcept;
+ std::vector< ::twitter::zipkin::thrift::Span> spans;
+
+ _Agent_emitZipkinBatch_args__isset __isset;
+
+ void __set_spans(const std::vector< ::twitter::zipkin::thrift::Span> & val);
+
+ bool operator == (const Agent_emitZipkinBatch_args & rhs) const
+ {
+ if (!(spans == rhs.spans))
+ return false;
+ return true;
+ }
+ bool operator != (const Agent_emitZipkinBatch_args &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Agent_emitZipkinBatch_args & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+
+class Agent_emitZipkinBatch_pargs {
+ public:
+
+
+ virtual ~Agent_emitZipkinBatch_pargs() noexcept;
+ const std::vector< ::twitter::zipkin::thrift::Span> * spans;
+
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+typedef struct _Agent_emitBatch_args__isset {
+ _Agent_emitBatch_args__isset() : batch(false) {}
+ bool batch :1;
+} _Agent_emitBatch_args__isset;
+
+class Agent_emitBatch_args {
+ public:
+
+ Agent_emitBatch_args(const Agent_emitBatch_args&);
+ Agent_emitBatch_args& operator=(const Agent_emitBatch_args&);
+ Agent_emitBatch_args() {
+ }
+
+ virtual ~Agent_emitBatch_args() noexcept;
+ ::jaegertracing::thrift::Batch batch;
+
+ _Agent_emitBatch_args__isset __isset;
+
+ void __set_batch(const ::jaegertracing::thrift::Batch& val);
+
+ bool operator == (const Agent_emitBatch_args & rhs) const
+ {
+ if (!(batch == rhs.batch))
+ return false;
+ return true;
+ }
+ bool operator != (const Agent_emitBatch_args &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Agent_emitBatch_args & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+
+class Agent_emitBatch_pargs {
+ public:
+
+
+ virtual ~Agent_emitBatch_pargs() noexcept;
+ const ::jaegertracing::thrift::Batch* batch;
+
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+class AgentClient : virtual public AgentIf {
+ public:
+ AgentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot);
+ }
+ AgentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ setProtocol(iprot,oprot);
+ }
+ private:
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot,prot);
+ }
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ piprot_=iprot;
+ poprot_=oprot;
+ iprot_ = iprot.get();
+ oprot_ = oprot.get();
+ }
+ public:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {
+ return piprot_;
+ }
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {
+ return poprot_;
+ }
+ void emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans);
+ void send_emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans);
+ void emitBatch(const ::jaegertracing::thrift::Batch& batch);
+ void send_emitBatch(const ::jaegertracing::thrift::Batch& batch);
+ protected:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;
+ ::apache::thrift::protocol::TProtocol* iprot_;
+ ::apache::thrift::protocol::TProtocol* oprot_;
+};
+
+class AgentProcessor : public ::apache::thrift::TDispatchProcessor {
+ protected:
+ ::std::shared_ptr<AgentIf> iface_;
+ virtual bool dispatchCall(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, const std::string& fname, int32_t seqid, void* callContext);
+ private:
+ typedef void (AgentProcessor::*ProcessFunction)(int32_t, ::apache::thrift::protocol::TProtocol*, ::apache::thrift::protocol::TProtocol*, void*);
+ typedef std::map<std::string, ProcessFunction> ProcessMap;
+ ProcessMap processMap_;
+ void process_emitZipkinBatch(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext);
+ void process_emitBatch(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext);
+ public:
+ AgentProcessor(::std::shared_ptr<AgentIf> iface) :
+ iface_(iface) {
+ processMap_["emitZipkinBatch"] = &AgentProcessor::process_emitZipkinBatch;
+ processMap_["emitBatch"] = &AgentProcessor::process_emitBatch;
+ }
+
+ virtual ~AgentProcessor() {}
+};
+
+class AgentProcessorFactory : public ::apache::thrift::TProcessorFactory {
+ public:
+ AgentProcessorFactory(const ::std::shared_ptr< AgentIfFactory >& handlerFactory) :
+ handlerFactory_(handlerFactory) {}
+
+ ::std::shared_ptr< ::apache::thrift::TProcessor > getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);
+
+ protected:
+ ::std::shared_ptr< AgentIfFactory > handlerFactory_;
+};
+
+class AgentMultiface : virtual public AgentIf {
+ public:
+ AgentMultiface(std::vector<std::shared_ptr<AgentIf> >& ifaces) : ifaces_(ifaces) {
+ }
+ virtual ~AgentMultiface() {}
+ protected:
+ std::vector<std::shared_ptr<AgentIf> > ifaces_;
+ AgentMultiface() {}
+ void add(::std::shared_ptr<AgentIf> iface) {
+ ifaces_.push_back(iface);
+ }
+ public:
+ void emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans) {
+ size_t sz = ifaces_.size();
+ size_t i = 0;
+ for (; i < (sz - 1); ++i) {
+ ifaces_[i]->emitZipkinBatch(spans);
+ }
+ ifaces_[i]->emitZipkinBatch(spans);
+ }
+
+ void emitBatch(const ::jaegertracing::thrift::Batch& batch) {
+ size_t sz = ifaces_.size();
+ size_t i = 0;
+ for (; i < (sz - 1); ++i) {
+ ifaces_[i]->emitBatch(batch);
+ }
+ ifaces_[i]->emitBatch(batch);
+ }
+
+};
+
+// The 'concurrent' client is a thread safe client that correctly handles
+// out of order responses. It is slower than the regular client, so should
+// only be used when you need to share a connection among multiple threads
+class AgentConcurrentClient : virtual public AgentIf {
+ public:
+ AgentConcurrentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot, std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync) : sync_(sync)
+{
+ setProtocol(prot);
+ }
+ AgentConcurrentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot, std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync) : sync_(sync)
+{
+ setProtocol(iprot,oprot);
+ }
+ private:
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot,prot);
+ }
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ piprot_=iprot;
+ poprot_=oprot;
+ iprot_ = iprot.get();
+ oprot_ = oprot.get();
+ }
+ public:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {
+ return piprot_;
+ }
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {
+ return poprot_;
+ }
+ void emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans);
+ void send_emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans);
+ void emitBatch(const ::jaegertracing::thrift::Batch& batch);
+ void send_emitBatch(const ::jaegertracing::thrift::Batch& batch);
+ protected:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;
+ ::apache::thrift::protocol::TProtocol* iprot_;
+ ::apache::thrift::protocol::TProtocol* oprot_;
+ std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync_;
+};
+
+#ifdef _MSC_VER
+ #pragma warning( pop )
+#endif
+
+}}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent_server.skeleton.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent_server.skeleton.cpp
new file mode 100644
index 000000000..60fdd9436
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Agent_server.skeleton.cpp
@@ -0,0 +1,47 @@
+// This autogenerated skeleton file illustrates how to build a server.
+// You should copy it to another filename to avoid overwriting it.
+
+#include "Agent.h"
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+
+using namespace ::apache::thrift;
+using namespace ::apache::thrift::protocol;
+using namespace ::apache::thrift::transport;
+using namespace ::apache::thrift::server;
+
+using namespace ::jaegertracing::agent::thrift;
+
+class AgentHandler : virtual public AgentIf {
+ public:
+ AgentHandler() {
+ // Your initialization goes here
+ }
+
+ void emitZipkinBatch(const std::vector< ::twitter::zipkin::thrift::Span> & spans) {
+ // Your implementation goes here
+ printf("emitZipkinBatch\n");
+ }
+
+ void emitBatch(const ::jaegertracing::thrift::Batch& batch) {
+ // Your implementation goes here
+ printf("emitBatch\n");
+ }
+
+};
+
+int main(int argc, char **argv) {
+ int port = 9090;
+ ::std::shared_ptr<AgentHandler> handler(new AgentHandler());
+ ::std::shared_ptr<TProcessor> processor(new AgentProcessor(handler));
+ ::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
+ ::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
+ ::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
+
+ TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
+ server.serve();
+ return 0;
+}
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.cpp
new file mode 100644
index 000000000..f96b40f20
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.cpp
@@ -0,0 +1,481 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#include "Collector.h"
+
+namespace jaegertracing { namespace thrift {
+
+
+Collector_submitBatches_args::~Collector_submitBatches_args() noexcept {
+}
+
+
+uint32_t Collector_submitBatches_args::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->batches.clear();
+ uint32_t _size52;
+ ::apache::thrift::protocol::TType _etype55;
+ xfer += iprot->readListBegin(_etype55, _size52);
+ this->batches.resize(_size52);
+ uint32_t _i56;
+ for (_i56 = 0; _i56 < _size52; ++_i56)
+ {
+ xfer += this->batches[_i56].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.batches = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Collector_submitBatches_args::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Collector_submitBatches_args");
+
+ xfer += oprot->writeFieldBegin("batches", ::apache::thrift::protocol::T_LIST, 1);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->batches.size()));
+ std::vector<Batch> ::const_iterator _iter57;
+ for (_iter57 = this->batches.begin(); _iter57 != this->batches.end(); ++_iter57)
+ {
+ xfer += (*_iter57).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+Collector_submitBatches_pargs::~Collector_submitBatches_pargs() noexcept {
+}
+
+
+uint32_t Collector_submitBatches_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Collector_submitBatches_pargs");
+
+ xfer += oprot->writeFieldBegin("batches", ::apache::thrift::protocol::T_LIST, 1);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>((*(this->batches)).size()));
+ std::vector<Batch> ::const_iterator _iter58;
+ for (_iter58 = (*(this->batches)).begin(); _iter58 != (*(this->batches)).end(); ++_iter58)
+ {
+ xfer += (*_iter58).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+Collector_submitBatches_result::~Collector_submitBatches_result() noexcept {
+}
+
+
+uint32_t Collector_submitBatches_result::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 0:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->success.clear();
+ uint32_t _size59;
+ ::apache::thrift::protocol::TType _etype62;
+ xfer += iprot->readListBegin(_etype62, _size59);
+ this->success.resize(_size59);
+ uint32_t _i63;
+ for (_i63 = 0; _i63 < _size59; ++_i63)
+ {
+ xfer += this->success[_i63].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.success = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Collector_submitBatches_result::write(::apache::thrift::protocol::TProtocol* oprot) const {
+
+ uint32_t xfer = 0;
+
+ xfer += oprot->writeStructBegin("Collector_submitBatches_result");
+
+ if (this->__isset.success) {
+ xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->success.size()));
+ std::vector<BatchSubmitResponse> ::const_iterator _iter64;
+ for (_iter64 = this->success.begin(); _iter64 != this->success.end(); ++_iter64)
+ {
+ xfer += (*_iter64).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+Collector_submitBatches_presult::~Collector_submitBatches_presult() noexcept {
+}
+
+
+uint32_t Collector_submitBatches_presult::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 0:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ (*(this->success)).clear();
+ uint32_t _size65;
+ ::apache::thrift::protocol::TType _etype68;
+ xfer += iprot->readListBegin(_etype68, _size65);
+ (*(this->success)).resize(_size65);
+ uint32_t _i69;
+ for (_i69 = 0; _i69 < _size65; ++_i69)
+ {
+ xfer += (*(this->success))[_i69].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.success = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+void CollectorClient::submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches)
+{
+ send_submitBatches(batches);
+ recv_submitBatches(_return);
+}
+
+void CollectorClient::send_submitBatches(const std::vector<Batch> & batches)
+{
+ int32_t cseqid = 0;
+ oprot_->writeMessageBegin("submitBatches", ::apache::thrift::protocol::T_CALL, cseqid);
+
+ Collector_submitBatches_pargs args;
+ args.batches = &batches;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+}
+
+void CollectorClient::recv_submitBatches(std::vector<BatchSubmitResponse> & _return)
+{
+
+ int32_t rseqid = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TMessageType mtype;
+
+ iprot_->readMessageBegin(fname, mtype, rseqid);
+ if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {
+ ::apache::thrift::TApplicationException x;
+ x.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ throw x;
+ }
+ if (mtype != ::apache::thrift::protocol::T_REPLY) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ }
+ if (fname.compare("submitBatches") != 0) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ }
+ Collector_submitBatches_presult result;
+ result.success = &_return;
+ result.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+
+ if (result.__isset.success) {
+ // _return pointer has now been filled
+ return;
+ }
+ throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "submitBatches failed: unknown result");
+}
+
+bool CollectorProcessor::dispatchCall(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, const std::string& fname, int32_t seqid, void* callContext) {
+ ProcessMap::iterator pfn;
+ pfn = processMap_.find(fname);
+ if (pfn == processMap_.end()) {
+ iprot->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot->readMessageEnd();
+ iprot->getTransport()->readEnd();
+ ::apache::thrift::TApplicationException x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, "Invalid method name: '"+fname+"'");
+ oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid);
+ x.write(oprot);
+ oprot->writeMessageEnd();
+ oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+ return true;
+ }
+ (this->*(pfn->second))(seqid, iprot, oprot, callContext);
+ return true;
+}
+
+void CollectorProcessor::process_submitBatches(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext)
+{
+ void* ctx = nullptr;
+ if (this->eventHandler_.get() != nullptr) {
+ ctx = this->eventHandler_->getContext("Collector.submitBatches", callContext);
+ }
+ ::apache::thrift::TProcessorContextFreer freer(this->eventHandler_.get(), ctx, "Collector.submitBatches");
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->preRead(ctx, "Collector.submitBatches");
+ }
+
+ Collector_submitBatches_args args;
+ args.read(iprot);
+ iprot->readMessageEnd();
+ uint32_t bytes = iprot->getTransport()->readEnd();
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->postRead(ctx, "Collector.submitBatches", bytes);
+ }
+
+ Collector_submitBatches_result result;
+ try {
+ iface_->submitBatches(result.success, args.batches);
+ result.__isset.success = true;
+ } catch (const std::exception& e) {
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->handlerError(ctx, "Collector.submitBatches");
+ }
+
+ ::apache::thrift::TApplicationException x(e.what());
+ oprot->writeMessageBegin("submitBatches", ::apache::thrift::protocol::T_EXCEPTION, seqid);
+ x.write(oprot);
+ oprot->writeMessageEnd();
+ oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+ return;
+ }
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->preWrite(ctx, "Collector.submitBatches");
+ }
+
+ oprot->writeMessageBegin("submitBatches", ::apache::thrift::protocol::T_REPLY, seqid);
+ result.write(oprot);
+ oprot->writeMessageEnd();
+ bytes = oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->postWrite(ctx, "Collector.submitBatches", bytes);
+ }
+}
+
+::std::shared_ptr< ::apache::thrift::TProcessor > CollectorProcessorFactory::getProcessor(const ::apache::thrift::TConnectionInfo& connInfo) {
+ ::apache::thrift::ReleaseHandler< CollectorIfFactory > cleanup(handlerFactory_);
+ ::std::shared_ptr< CollectorIf > handler(handlerFactory_->getHandler(connInfo), cleanup);
+ ::std::shared_ptr< ::apache::thrift::TProcessor > processor(new CollectorProcessor(handler));
+ return processor;
+}
+
+void CollectorConcurrentClient::submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches)
+{
+ int32_t seqid = send_submitBatches(batches);
+ recv_submitBatches(_return, seqid);
+}
+
+int32_t CollectorConcurrentClient::send_submitBatches(const std::vector<Batch> & batches)
+{
+ int32_t cseqid = this->sync_->generateSeqId();
+ ::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());
+ oprot_->writeMessageBegin("submitBatches", ::apache::thrift::protocol::T_CALL, cseqid);
+
+ Collector_submitBatches_pargs args;
+ args.batches = &batches;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+
+ sentry.commit();
+ return cseqid;
+}
+
+void CollectorConcurrentClient::recv_submitBatches(std::vector<BatchSubmitResponse> & _return, const int32_t seqid)
+{
+
+ int32_t rseqid = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TMessageType mtype;
+
+ // the read mutex gets dropped and reacquired as part of waitForWork()
+ // The destructor of this sentry wakes up other clients
+ ::apache::thrift::async::TConcurrentRecvSentry sentry(this->sync_.get(), seqid);
+
+ while(true) {
+ if(!this->sync_->getPending(fname, mtype, rseqid)) {
+ iprot_->readMessageBegin(fname, mtype, rseqid);
+ }
+ if(seqid == rseqid) {
+ if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {
+ ::apache::thrift::TApplicationException x;
+ x.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ sentry.commit();
+ throw x;
+ }
+ if (mtype != ::apache::thrift::protocol::T_REPLY) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ }
+ if (fname.compare("submitBatches") != 0) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+
+ // in a bad state, don't commit
+ using ::apache::thrift::protocol::TProtocolException;
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ }
+ Collector_submitBatches_presult result;
+ result.success = &_return;
+ result.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+
+ if (result.__isset.success) {
+ // _return pointer has now been filled
+ sentry.commit();
+ return;
+ }
+ // in a bad state, don't commit
+ throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "submitBatches failed: unknown result");
+ }
+ // seqid != rseqid
+ this->sync_->updatePending(fname, mtype, rseqid);
+
+ // this will temporarily unlock the readMutex, and let other clients get work done
+ this->sync_->waitForWork(seqid);
+ } // end while(true)
+}
+
+}} // namespace
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.h
new file mode 100644
index 000000000..75daa69a7
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector.h
@@ -0,0 +1,299 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef Collector_H
+#define Collector_H
+
+#include <thrift/TDispatchProcessor.h>
+#include <thrift/async/TConcurrentClientSyncInfo.h>
+#include <memory>
+#include "jaeger_types.h"
+
+namespace jaegertracing { namespace thrift {
+
+#ifdef _MSC_VER
+ #pragma warning( push )
+ #pragma warning (disable : 4250 ) //inheriting methods via dominance
+#endif
+
+class CollectorIf {
+ public:
+ virtual ~CollectorIf() {}
+ virtual void submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches) = 0;
+};
+
+class CollectorIfFactory {
+ public:
+ typedef CollectorIf Handler;
+
+ virtual ~CollectorIfFactory() {}
+
+ virtual CollectorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) = 0;
+ virtual void releaseHandler(CollectorIf* /* handler */) = 0;
+};
+
+class CollectorIfSingletonFactory : virtual public CollectorIfFactory {
+ public:
+ CollectorIfSingletonFactory(const ::std::shared_ptr<CollectorIf>& iface) : iface_(iface) {}
+ virtual ~CollectorIfSingletonFactory() {}
+
+ virtual CollectorIf* getHandler(const ::apache::thrift::TConnectionInfo&) {
+ return iface_.get();
+ }
+ virtual void releaseHandler(CollectorIf* /* handler */) {}
+
+ protected:
+ ::std::shared_ptr<CollectorIf> iface_;
+};
+
+class CollectorNull : virtual public CollectorIf {
+ public:
+ virtual ~CollectorNull() {}
+ void submitBatches(std::vector<BatchSubmitResponse> & /* _return */, const std::vector<Batch> & /* batches */) {
+ return;
+ }
+};
+
+typedef struct _Collector_submitBatches_args__isset {
+ _Collector_submitBatches_args__isset() : batches(false) {}
+ bool batches :1;
+} _Collector_submitBatches_args__isset;
+
+class Collector_submitBatches_args {
+ public:
+
+ Collector_submitBatches_args(const Collector_submitBatches_args&);
+ Collector_submitBatches_args& operator=(const Collector_submitBatches_args&);
+ Collector_submitBatches_args() {
+ }
+
+ virtual ~Collector_submitBatches_args() noexcept;
+ std::vector<Batch> batches;
+
+ _Collector_submitBatches_args__isset __isset;
+
+ void __set_batches(const std::vector<Batch> & val);
+
+ bool operator == (const Collector_submitBatches_args & rhs) const
+ {
+ if (!(batches == rhs.batches))
+ return false;
+ return true;
+ }
+ bool operator != (const Collector_submitBatches_args &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Collector_submitBatches_args & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+
+class Collector_submitBatches_pargs {
+ public:
+
+
+ virtual ~Collector_submitBatches_pargs() noexcept;
+ const std::vector<Batch> * batches;
+
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+typedef struct _Collector_submitBatches_result__isset {
+ _Collector_submitBatches_result__isset() : success(false) {}
+ bool success :1;
+} _Collector_submitBatches_result__isset;
+
+class Collector_submitBatches_result {
+ public:
+
+ Collector_submitBatches_result(const Collector_submitBatches_result&);
+ Collector_submitBatches_result& operator=(const Collector_submitBatches_result&);
+ Collector_submitBatches_result() {
+ }
+
+ virtual ~Collector_submitBatches_result() noexcept;
+ std::vector<BatchSubmitResponse> success;
+
+ _Collector_submitBatches_result__isset __isset;
+
+ void __set_success(const std::vector<BatchSubmitResponse> & val);
+
+ bool operator == (const Collector_submitBatches_result & rhs) const
+ {
+ if (!(success == rhs.success))
+ return false;
+ return true;
+ }
+ bool operator != (const Collector_submitBatches_result &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Collector_submitBatches_result & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+typedef struct _Collector_submitBatches_presult__isset {
+ _Collector_submitBatches_presult__isset() : success(false) {}
+ bool success :1;
+} _Collector_submitBatches_presult__isset;
+
+class Collector_submitBatches_presult {
+ public:
+
+
+ virtual ~Collector_submitBatches_presult() noexcept;
+ std::vector<BatchSubmitResponse> * success;
+
+ _Collector_submitBatches_presult__isset __isset;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+
+};
+
+class CollectorClient : virtual public CollectorIf {
+ public:
+ CollectorClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot);
+ }
+ CollectorClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ setProtocol(iprot,oprot);
+ }
+ private:
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot,prot);
+ }
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ piprot_=iprot;
+ poprot_=oprot;
+ iprot_ = iprot.get();
+ oprot_ = oprot.get();
+ }
+ public:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {
+ return piprot_;
+ }
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {
+ return poprot_;
+ }
+ void submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches);
+ void send_submitBatches(const std::vector<Batch> & batches);
+ void recv_submitBatches(std::vector<BatchSubmitResponse> & _return);
+ protected:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;
+ ::apache::thrift::protocol::TProtocol* iprot_;
+ ::apache::thrift::protocol::TProtocol* oprot_;
+};
+
+class CollectorProcessor : public ::apache::thrift::TDispatchProcessor {
+ protected:
+ ::std::shared_ptr<CollectorIf> iface_;
+ virtual bool dispatchCall(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, const std::string& fname, int32_t seqid, void* callContext);
+ private:
+ typedef void (CollectorProcessor::*ProcessFunction)(int32_t, ::apache::thrift::protocol::TProtocol*, ::apache::thrift::protocol::TProtocol*, void*);
+ typedef std::map<std::string, ProcessFunction> ProcessMap;
+ ProcessMap processMap_;
+ void process_submitBatches(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext);
+ public:
+ CollectorProcessor(::std::shared_ptr<CollectorIf> iface) :
+ iface_(iface) {
+ processMap_["submitBatches"] = &CollectorProcessor::process_submitBatches;
+ }
+
+ virtual ~CollectorProcessor() {}
+};
+
+class CollectorProcessorFactory : public ::apache::thrift::TProcessorFactory {
+ public:
+ CollectorProcessorFactory(const ::std::shared_ptr< CollectorIfFactory >& handlerFactory) :
+ handlerFactory_(handlerFactory) {}
+
+ ::std::shared_ptr< ::apache::thrift::TProcessor > getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);
+
+ protected:
+ ::std::shared_ptr< CollectorIfFactory > handlerFactory_;
+};
+
+class CollectorMultiface : virtual public CollectorIf {
+ public:
+ CollectorMultiface(std::vector<std::shared_ptr<CollectorIf> >& ifaces) : ifaces_(ifaces) {
+ }
+ virtual ~CollectorMultiface() {}
+ protected:
+ std::vector<std::shared_ptr<CollectorIf> > ifaces_;
+ CollectorMultiface() {}
+ void add(::std::shared_ptr<CollectorIf> iface) {
+ ifaces_.push_back(iface);
+ }
+ public:
+ void submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches) {
+ size_t sz = ifaces_.size();
+ size_t i = 0;
+ for (; i < (sz - 1); ++i) {
+ ifaces_[i]->submitBatches(_return, batches);
+ }
+ ifaces_[i]->submitBatches(_return, batches);
+ return;
+ }
+
+};
+
+// The 'concurrent' client is a thread safe client that correctly handles
+// out of order responses. It is slower than the regular client, so should
+// only be used when you need to share a connection among multiple threads
+class CollectorConcurrentClient : virtual public CollectorIf {
+ public:
+ CollectorConcurrentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot, std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync) : sync_(sync)
+{
+ setProtocol(prot);
+ }
+ CollectorConcurrentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot, std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync) : sync_(sync)
+{
+ setProtocol(iprot,oprot);
+ }
+ private:
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot,prot);
+ }
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ piprot_=iprot;
+ poprot_=oprot;
+ iprot_ = iprot.get();
+ oprot_ = oprot.get();
+ }
+ public:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {
+ return piprot_;
+ }
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {
+ return poprot_;
+ }
+ void submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches);
+ int32_t send_submitBatches(const std::vector<Batch> & batches);
+ void recv_submitBatches(std::vector<BatchSubmitResponse> & _return, const int32_t seqid);
+ protected:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;
+ ::apache::thrift::protocol::TProtocol* iprot_;
+ ::apache::thrift::protocol::TProtocol* oprot_;
+ std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync_;
+};
+
+#ifdef _MSC_VER
+ #pragma warning( pop )
+#endif
+
+}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector_server.skeleton.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector_server.skeleton.cpp
new file mode 100644
index 000000000..c59c8aa49
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/Collector_server.skeleton.cpp
@@ -0,0 +1,42 @@
+// This autogenerated skeleton file illustrates how to build a server.
+// You should copy it to another filename to avoid overwriting it.
+
+#include "Collector.h"
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+
+using namespace ::apache::thrift;
+using namespace ::apache::thrift::protocol;
+using namespace ::apache::thrift::transport;
+using namespace ::apache::thrift::server;
+
+using namespace ::jaegertracing::thrift;
+
+class CollectorHandler : virtual public CollectorIf {
+ public:
+ CollectorHandler() {
+ // Your initialization goes here
+ }
+
+ void submitBatches(std::vector<BatchSubmitResponse> & _return, const std::vector<Batch> & batches) {
+ // Your implementation goes here
+ printf("submitBatches\n");
+ }
+
+};
+
+int main(int argc, char **argv) {
+ int port = 9090;
+ ::std::shared_ptr<CollectorHandler> handler(new CollectorHandler());
+ ::std::shared_ptr<TProcessor> processor(new CollectorProcessor(handler));
+ ::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
+ ::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
+ ::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
+
+ TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
+ server.serve();
+ return 0;
+}
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.cpp
new file mode 100644
index 000000000..42d813dfd
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.cpp
@@ -0,0 +1,481 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#include "ZipkinCollector.h"
+
+namespace twitter { namespace zipkin { namespace thrift {
+
+
+ZipkinCollector_submitZipkinBatch_args::~ZipkinCollector_submitZipkinBatch_args() noexcept {
+}
+
+
+uint32_t ZipkinCollector_submitZipkinBatch_args::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->spans.clear();
+ uint32_t _size23;
+ ::apache::thrift::protocol::TType _etype26;
+ xfer += iprot->readListBegin(_etype26, _size23);
+ this->spans.resize(_size23);
+ uint32_t _i27;
+ for (_i27 = 0; _i27 < _size23; ++_i27)
+ {
+ xfer += this->spans[_i27].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.spans = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t ZipkinCollector_submitZipkinBatch_args::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("ZipkinCollector_submitZipkinBatch_args");
+
+ xfer += oprot->writeFieldBegin("spans", ::apache::thrift::protocol::T_LIST, 1);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->spans.size()));
+ std::vector<Span> ::const_iterator _iter28;
+ for (_iter28 = this->spans.begin(); _iter28 != this->spans.end(); ++_iter28)
+ {
+ xfer += (*_iter28).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+ZipkinCollector_submitZipkinBatch_pargs::~ZipkinCollector_submitZipkinBatch_pargs() noexcept {
+}
+
+
+uint32_t ZipkinCollector_submitZipkinBatch_pargs::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("ZipkinCollector_submitZipkinBatch_pargs");
+
+ xfer += oprot->writeFieldBegin("spans", ::apache::thrift::protocol::T_LIST, 1);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>((*(this->spans)).size()));
+ std::vector<Span> ::const_iterator _iter29;
+ for (_iter29 = (*(this->spans)).begin(); _iter29 != (*(this->spans)).end(); ++_iter29)
+ {
+ xfer += (*_iter29).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+ZipkinCollector_submitZipkinBatch_result::~ZipkinCollector_submitZipkinBatch_result() noexcept {
+}
+
+
+uint32_t ZipkinCollector_submitZipkinBatch_result::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 0:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->success.clear();
+ uint32_t _size30;
+ ::apache::thrift::protocol::TType _etype33;
+ xfer += iprot->readListBegin(_etype33, _size30);
+ this->success.resize(_size30);
+ uint32_t _i34;
+ for (_i34 = 0; _i34 < _size30; ++_i34)
+ {
+ xfer += this->success[_i34].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.success = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t ZipkinCollector_submitZipkinBatch_result::write(::apache::thrift::protocol::TProtocol* oprot) const {
+
+ uint32_t xfer = 0;
+
+ xfer += oprot->writeStructBegin("ZipkinCollector_submitZipkinBatch_result");
+
+ if (this->__isset.success) {
+ xfer += oprot->writeFieldBegin("success", ::apache::thrift::protocol::T_LIST, 0);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->success.size()));
+ std::vector<Response> ::const_iterator _iter35;
+ for (_iter35 = this->success.begin(); _iter35 != this->success.end(); ++_iter35)
+ {
+ xfer += (*_iter35).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+
+ZipkinCollector_submitZipkinBatch_presult::~ZipkinCollector_submitZipkinBatch_presult() noexcept {
+}
+
+
+uint32_t ZipkinCollector_submitZipkinBatch_presult::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 0:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ (*(this->success)).clear();
+ uint32_t _size36;
+ ::apache::thrift::protocol::TType _etype39;
+ xfer += iprot->readListBegin(_etype39, _size36);
+ (*(this->success)).resize(_size36);
+ uint32_t _i40;
+ for (_i40 = 0; _i40 < _size36; ++_i40)
+ {
+ xfer += (*(this->success))[_i40].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.success = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+void ZipkinCollectorClient::submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans)
+{
+ send_submitZipkinBatch(spans);
+ recv_submitZipkinBatch(_return);
+}
+
+void ZipkinCollectorClient::send_submitZipkinBatch(const std::vector<Span> & spans)
+{
+ int32_t cseqid = 0;
+ oprot_->writeMessageBegin("submitZipkinBatch", ::apache::thrift::protocol::T_CALL, cseqid);
+
+ ZipkinCollector_submitZipkinBatch_pargs args;
+ args.spans = &spans;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+}
+
+void ZipkinCollectorClient::recv_submitZipkinBatch(std::vector<Response> & _return)
+{
+
+ int32_t rseqid = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TMessageType mtype;
+
+ iprot_->readMessageBegin(fname, mtype, rseqid);
+ if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {
+ ::apache::thrift::TApplicationException x;
+ x.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ throw x;
+ }
+ if (mtype != ::apache::thrift::protocol::T_REPLY) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ }
+ if (fname.compare("submitZipkinBatch") != 0) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ }
+ ZipkinCollector_submitZipkinBatch_presult result;
+ result.success = &_return;
+ result.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+
+ if (result.__isset.success) {
+ // _return pointer has now been filled
+ return;
+ }
+ throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "submitZipkinBatch failed: unknown result");
+}
+
+bool ZipkinCollectorProcessor::dispatchCall(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, const std::string& fname, int32_t seqid, void* callContext) {
+ ProcessMap::iterator pfn;
+ pfn = processMap_.find(fname);
+ if (pfn == processMap_.end()) {
+ iprot->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot->readMessageEnd();
+ iprot->getTransport()->readEnd();
+ ::apache::thrift::TApplicationException x(::apache::thrift::TApplicationException::UNKNOWN_METHOD, "Invalid method name: '"+fname+"'");
+ oprot->writeMessageBegin(fname, ::apache::thrift::protocol::T_EXCEPTION, seqid);
+ x.write(oprot);
+ oprot->writeMessageEnd();
+ oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+ return true;
+ }
+ (this->*(pfn->second))(seqid, iprot, oprot, callContext);
+ return true;
+}
+
+void ZipkinCollectorProcessor::process_submitZipkinBatch(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext)
+{
+ void* ctx = nullptr;
+ if (this->eventHandler_.get() != nullptr) {
+ ctx = this->eventHandler_->getContext("ZipkinCollector.submitZipkinBatch", callContext);
+ }
+ ::apache::thrift::TProcessorContextFreer freer(this->eventHandler_.get(), ctx, "ZipkinCollector.submitZipkinBatch");
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->preRead(ctx, "ZipkinCollector.submitZipkinBatch");
+ }
+
+ ZipkinCollector_submitZipkinBatch_args args;
+ args.read(iprot);
+ iprot->readMessageEnd();
+ uint32_t bytes = iprot->getTransport()->readEnd();
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->postRead(ctx, "ZipkinCollector.submitZipkinBatch", bytes);
+ }
+
+ ZipkinCollector_submitZipkinBatch_result result;
+ try {
+ iface_->submitZipkinBatch(result.success, args.spans);
+ result.__isset.success = true;
+ } catch (const std::exception& e) {
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->handlerError(ctx, "ZipkinCollector.submitZipkinBatch");
+ }
+
+ ::apache::thrift::TApplicationException x(e.what());
+ oprot->writeMessageBegin("submitZipkinBatch", ::apache::thrift::protocol::T_EXCEPTION, seqid);
+ x.write(oprot);
+ oprot->writeMessageEnd();
+ oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+ return;
+ }
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->preWrite(ctx, "ZipkinCollector.submitZipkinBatch");
+ }
+
+ oprot->writeMessageBegin("submitZipkinBatch", ::apache::thrift::protocol::T_REPLY, seqid);
+ result.write(oprot);
+ oprot->writeMessageEnd();
+ bytes = oprot->getTransport()->writeEnd();
+ oprot->getTransport()->flush();
+
+ if (this->eventHandler_.get() != nullptr) {
+ this->eventHandler_->postWrite(ctx, "ZipkinCollector.submitZipkinBatch", bytes);
+ }
+}
+
+::std::shared_ptr< ::apache::thrift::TProcessor > ZipkinCollectorProcessorFactory::getProcessor(const ::apache::thrift::TConnectionInfo& connInfo) {
+ ::apache::thrift::ReleaseHandler< ZipkinCollectorIfFactory > cleanup(handlerFactory_);
+ ::std::shared_ptr< ZipkinCollectorIf > handler(handlerFactory_->getHandler(connInfo), cleanup);
+ ::std::shared_ptr< ::apache::thrift::TProcessor > processor(new ZipkinCollectorProcessor(handler));
+ return processor;
+}
+
+void ZipkinCollectorConcurrentClient::submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans)
+{
+ int32_t seqid = send_submitZipkinBatch(spans);
+ recv_submitZipkinBatch(_return, seqid);
+}
+
+int32_t ZipkinCollectorConcurrentClient::send_submitZipkinBatch(const std::vector<Span> & spans)
+{
+ int32_t cseqid = this->sync_->generateSeqId();
+ ::apache::thrift::async::TConcurrentSendSentry sentry(this->sync_.get());
+ oprot_->writeMessageBegin("submitZipkinBatch", ::apache::thrift::protocol::T_CALL, cseqid);
+
+ ZipkinCollector_submitZipkinBatch_pargs args;
+ args.spans = &spans;
+ args.write(oprot_);
+
+ oprot_->writeMessageEnd();
+ oprot_->getTransport()->writeEnd();
+ oprot_->getTransport()->flush();
+
+ sentry.commit();
+ return cseqid;
+}
+
+void ZipkinCollectorConcurrentClient::recv_submitZipkinBatch(std::vector<Response> & _return, const int32_t seqid)
+{
+
+ int32_t rseqid = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TMessageType mtype;
+
+ // the read mutex gets dropped and reacquired as part of waitForWork()
+ // The destructor of this sentry wakes up other clients
+ ::apache::thrift::async::TConcurrentRecvSentry sentry(this->sync_.get(), seqid);
+
+ while(true) {
+ if(!this->sync_->getPending(fname, mtype, rseqid)) {
+ iprot_->readMessageBegin(fname, mtype, rseqid);
+ }
+ if(seqid == rseqid) {
+ if (mtype == ::apache::thrift::protocol::T_EXCEPTION) {
+ ::apache::thrift::TApplicationException x;
+ x.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ sentry.commit();
+ throw x;
+ }
+ if (mtype != ::apache::thrift::protocol::T_REPLY) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+ }
+ if (fname.compare("submitZipkinBatch") != 0) {
+ iprot_->skip(::apache::thrift::protocol::T_STRUCT);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+
+ // in a bad state, don't commit
+ using ::apache::thrift::protocol::TProtocolException;
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ }
+ ZipkinCollector_submitZipkinBatch_presult result;
+ result.success = &_return;
+ result.read(iprot_);
+ iprot_->readMessageEnd();
+ iprot_->getTransport()->readEnd();
+
+ if (result.__isset.success) {
+ // _return pointer has now been filled
+ sentry.commit();
+ return;
+ }
+ // in a bad state, don't commit
+ throw ::apache::thrift::TApplicationException(::apache::thrift::TApplicationException::MISSING_RESULT, "submitZipkinBatch failed: unknown result");
+ }
+ // seqid != rseqid
+ this->sync_->updatePending(fname, mtype, rseqid);
+
+ // this will temporarily unlock the readMutex, and let other clients get work done
+ this->sync_->waitForWork(seqid);
+ } // end while(true)
+}
+
+}}} // namespace
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.h
new file mode 100644
index 000000000..97e111d24
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector.h
@@ -0,0 +1,299 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef ZipkinCollector_H
+#define ZipkinCollector_H
+
+#include <thrift/TDispatchProcessor.h>
+#include <thrift/async/TConcurrentClientSyncInfo.h>
+#include <memory>
+#include "zipkincore_types.h"
+
+namespace twitter { namespace zipkin { namespace thrift {
+
+#ifdef _MSC_VER
+ #pragma warning( push )
+ #pragma warning (disable : 4250 ) //inheriting methods via dominance
+#endif
+
+class ZipkinCollectorIf {
+ public:
+ virtual ~ZipkinCollectorIf() {}
+ virtual void submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans) = 0;
+};
+
+class ZipkinCollectorIfFactory {
+ public:
+ typedef ZipkinCollectorIf Handler;
+
+ virtual ~ZipkinCollectorIfFactory() {}
+
+ virtual ZipkinCollectorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) = 0;
+ virtual void releaseHandler(ZipkinCollectorIf* /* handler */) = 0;
+};
+
+class ZipkinCollectorIfSingletonFactory : virtual public ZipkinCollectorIfFactory {
+ public:
+ ZipkinCollectorIfSingletonFactory(const ::std::shared_ptr<ZipkinCollectorIf>& iface) : iface_(iface) {}
+ virtual ~ZipkinCollectorIfSingletonFactory() {}
+
+ virtual ZipkinCollectorIf* getHandler(const ::apache::thrift::TConnectionInfo&) {
+ return iface_.get();
+ }
+ virtual void releaseHandler(ZipkinCollectorIf* /* handler */) {}
+
+ protected:
+ ::std::shared_ptr<ZipkinCollectorIf> iface_;
+};
+
+class ZipkinCollectorNull : virtual public ZipkinCollectorIf {
+ public:
+ virtual ~ZipkinCollectorNull() {}
+ void submitZipkinBatch(std::vector<Response> & /* _return */, const std::vector<Span> & /* spans */) {
+ return;
+ }
+};
+
+typedef struct _ZipkinCollector_submitZipkinBatch_args__isset {
+ _ZipkinCollector_submitZipkinBatch_args__isset() : spans(false) {}
+ bool spans :1;
+} _ZipkinCollector_submitZipkinBatch_args__isset;
+
+class ZipkinCollector_submitZipkinBatch_args {
+ public:
+
+ ZipkinCollector_submitZipkinBatch_args(const ZipkinCollector_submitZipkinBatch_args&);
+ ZipkinCollector_submitZipkinBatch_args& operator=(const ZipkinCollector_submitZipkinBatch_args&);
+ ZipkinCollector_submitZipkinBatch_args() {
+ }
+
+ virtual ~ZipkinCollector_submitZipkinBatch_args() noexcept;
+ std::vector<Span> spans;
+
+ _ZipkinCollector_submitZipkinBatch_args__isset __isset;
+
+ void __set_spans(const std::vector<Span> & val);
+
+ bool operator == (const ZipkinCollector_submitZipkinBatch_args & rhs) const
+ {
+ if (!(spans == rhs.spans))
+ return false;
+ return true;
+ }
+ bool operator != (const ZipkinCollector_submitZipkinBatch_args &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const ZipkinCollector_submitZipkinBatch_args & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+
+class ZipkinCollector_submitZipkinBatch_pargs {
+ public:
+
+
+ virtual ~ZipkinCollector_submitZipkinBatch_pargs() noexcept;
+ const std::vector<Span> * spans;
+
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+typedef struct _ZipkinCollector_submitZipkinBatch_result__isset {
+ _ZipkinCollector_submitZipkinBatch_result__isset() : success(false) {}
+ bool success :1;
+} _ZipkinCollector_submitZipkinBatch_result__isset;
+
+class ZipkinCollector_submitZipkinBatch_result {
+ public:
+
+ ZipkinCollector_submitZipkinBatch_result(const ZipkinCollector_submitZipkinBatch_result&);
+ ZipkinCollector_submitZipkinBatch_result& operator=(const ZipkinCollector_submitZipkinBatch_result&);
+ ZipkinCollector_submitZipkinBatch_result() {
+ }
+
+ virtual ~ZipkinCollector_submitZipkinBatch_result() noexcept;
+ std::vector<Response> success;
+
+ _ZipkinCollector_submitZipkinBatch_result__isset __isset;
+
+ void __set_success(const std::vector<Response> & val);
+
+ bool operator == (const ZipkinCollector_submitZipkinBatch_result & rhs) const
+ {
+ if (!(success == rhs.success))
+ return false;
+ return true;
+ }
+ bool operator != (const ZipkinCollector_submitZipkinBatch_result &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const ZipkinCollector_submitZipkinBatch_result & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+};
+
+typedef struct _ZipkinCollector_submitZipkinBatch_presult__isset {
+ _ZipkinCollector_submitZipkinBatch_presult__isset() : success(false) {}
+ bool success :1;
+} _ZipkinCollector_submitZipkinBatch_presult__isset;
+
+class ZipkinCollector_submitZipkinBatch_presult {
+ public:
+
+
+ virtual ~ZipkinCollector_submitZipkinBatch_presult() noexcept;
+ std::vector<Response> * success;
+
+ _ZipkinCollector_submitZipkinBatch_presult__isset __isset;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+
+};
+
+class ZipkinCollectorClient : virtual public ZipkinCollectorIf {
+ public:
+ ZipkinCollectorClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot);
+ }
+ ZipkinCollectorClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ setProtocol(iprot,oprot);
+ }
+ private:
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot,prot);
+ }
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ piprot_=iprot;
+ poprot_=oprot;
+ iprot_ = iprot.get();
+ oprot_ = oprot.get();
+ }
+ public:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {
+ return piprot_;
+ }
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {
+ return poprot_;
+ }
+ void submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans);
+ void send_submitZipkinBatch(const std::vector<Span> & spans);
+ void recv_submitZipkinBatch(std::vector<Response> & _return);
+ protected:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;
+ ::apache::thrift::protocol::TProtocol* iprot_;
+ ::apache::thrift::protocol::TProtocol* oprot_;
+};
+
+class ZipkinCollectorProcessor : public ::apache::thrift::TDispatchProcessor {
+ protected:
+ ::std::shared_ptr<ZipkinCollectorIf> iface_;
+ virtual bool dispatchCall(::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, const std::string& fname, int32_t seqid, void* callContext);
+ private:
+ typedef void (ZipkinCollectorProcessor::*ProcessFunction)(int32_t, ::apache::thrift::protocol::TProtocol*, ::apache::thrift::protocol::TProtocol*, void*);
+ typedef std::map<std::string, ProcessFunction> ProcessMap;
+ ProcessMap processMap_;
+ void process_submitZipkinBatch(int32_t seqid, ::apache::thrift::protocol::TProtocol* iprot, ::apache::thrift::protocol::TProtocol* oprot, void* callContext);
+ public:
+ ZipkinCollectorProcessor(::std::shared_ptr<ZipkinCollectorIf> iface) :
+ iface_(iface) {
+ processMap_["submitZipkinBatch"] = &ZipkinCollectorProcessor::process_submitZipkinBatch;
+ }
+
+ virtual ~ZipkinCollectorProcessor() {}
+};
+
+class ZipkinCollectorProcessorFactory : public ::apache::thrift::TProcessorFactory {
+ public:
+ ZipkinCollectorProcessorFactory(const ::std::shared_ptr< ZipkinCollectorIfFactory >& handlerFactory) :
+ handlerFactory_(handlerFactory) {}
+
+ ::std::shared_ptr< ::apache::thrift::TProcessor > getProcessor(const ::apache::thrift::TConnectionInfo& connInfo);
+
+ protected:
+ ::std::shared_ptr< ZipkinCollectorIfFactory > handlerFactory_;
+};
+
+class ZipkinCollectorMultiface : virtual public ZipkinCollectorIf {
+ public:
+ ZipkinCollectorMultiface(std::vector<std::shared_ptr<ZipkinCollectorIf> >& ifaces) : ifaces_(ifaces) {
+ }
+ virtual ~ZipkinCollectorMultiface() {}
+ protected:
+ std::vector<std::shared_ptr<ZipkinCollectorIf> > ifaces_;
+ ZipkinCollectorMultiface() {}
+ void add(::std::shared_ptr<ZipkinCollectorIf> iface) {
+ ifaces_.push_back(iface);
+ }
+ public:
+ void submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans) {
+ size_t sz = ifaces_.size();
+ size_t i = 0;
+ for (; i < (sz - 1); ++i) {
+ ifaces_[i]->submitZipkinBatch(_return, spans);
+ }
+ ifaces_[i]->submitZipkinBatch(_return, spans);
+ return;
+ }
+
+};
+
+// The 'concurrent' client is a thread safe client that correctly handles
+// out of order responses. It is slower than the regular client, so should
+// only be used when you need to share a connection among multiple threads
+class ZipkinCollectorConcurrentClient : virtual public ZipkinCollectorIf {
+ public:
+ ZipkinCollectorConcurrentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot, std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync) : sync_(sync)
+{
+ setProtocol(prot);
+ }
+ ZipkinCollectorConcurrentClient(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot, std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync) : sync_(sync)
+{
+ setProtocol(iprot,oprot);
+ }
+ private:
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> prot) {
+ setProtocol(prot,prot);
+ }
+ void setProtocol(std::shared_ptr< ::apache::thrift::protocol::TProtocol> iprot, std::shared_ptr< ::apache::thrift::protocol::TProtocol> oprot) {
+ piprot_=iprot;
+ poprot_=oprot;
+ iprot_ = iprot.get();
+ oprot_ = oprot.get();
+ }
+ public:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getInputProtocol() {
+ return piprot_;
+ }
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> getOutputProtocol() {
+ return poprot_;
+ }
+ void submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans);
+ int32_t send_submitZipkinBatch(const std::vector<Span> & spans);
+ void recv_submitZipkinBatch(std::vector<Response> & _return, const int32_t seqid);
+ protected:
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> piprot_;
+ std::shared_ptr< ::apache::thrift::protocol::TProtocol> poprot_;
+ ::apache::thrift::protocol::TProtocol* iprot_;
+ ::apache::thrift::protocol::TProtocol* oprot_;
+ std::shared_ptr<::apache::thrift::async::TConcurrentClientSyncInfo> sync_;
+};
+
+#ifdef _MSC_VER
+ #pragma warning( pop )
+#endif
+
+}}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector_server.skeleton.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector_server.skeleton.cpp
new file mode 100644
index 000000000..cca6ef752
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/ZipkinCollector_server.skeleton.cpp
@@ -0,0 +1,42 @@
+// This autogenerated skeleton file illustrates how to build a server.
+// You should copy it to another filename to avoid overwriting it.
+
+#include "ZipkinCollector.h"
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TBufferTransports.h>
+
+using namespace ::apache::thrift;
+using namespace ::apache::thrift::protocol;
+using namespace ::apache::thrift::transport;
+using namespace ::apache::thrift::server;
+
+using namespace ::twitter::zipkin::thrift;
+
+class ZipkinCollectorHandler : virtual public ZipkinCollectorIf {
+ public:
+ ZipkinCollectorHandler() {
+ // Your initialization goes here
+ }
+
+ void submitZipkinBatch(std::vector<Response> & _return, const std::vector<Span> & spans) {
+ // Your implementation goes here
+ printf("submitZipkinBatch\n");
+ }
+
+};
+
+int main(int argc, char **argv) {
+ int port = 9090;
+ ::std::shared_ptr<ZipkinCollectorHandler> handler(new ZipkinCollectorHandler());
+ ::std::shared_ptr<TProcessor> processor(new ZipkinCollectorProcessor(handler));
+ ::std::shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
+ ::std::shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
+ ::std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
+
+ TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
+ server.serve();
+ return 0;
+}
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/agent_types.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/agent_types.h
new file mode 100644
index 000000000..0b576e34f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/agent_types.h
@@ -0,0 +1,28 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef agent_TYPES_H
+#define agent_TYPES_H
+
+#include <iosfwd>
+
+#include <thrift/Thrift.h>
+#include <thrift/TApplicationException.h>
+#include <thrift/TBase.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/transport/TTransport.h>
+
+#include <functional>
+#include <memory>
+#include "jaeger_types.h"
+#include "zipkincore_types.h"
+
+
+namespace jaegertracing { namespace agent { namespace thrift {
+
+}}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.cpp
new file mode 100644
index 000000000..5e4140cc9
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.cpp
@@ -0,0 +1,1354 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#include "jaeger_types.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include <thrift/TToString.h>
+
+namespace jaegertracing { namespace thrift {
+
+int _kTagTypeValues[] = {
+ TagType::STRING,
+ TagType::DOUBLE,
+ TagType::BOOL,
+ TagType::LONG,
+ TagType::BINARY
+};
+const char* _kTagTypeNames[] = {
+ "STRING",
+ "DOUBLE",
+ "BOOL",
+ "LONG",
+ "BINARY"
+};
+const std::map<int, const char*> _TagType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(5, _kTagTypeValues, _kTagTypeNames), ::apache::thrift::TEnumIterator(-1, nullptr, nullptr));
+
+std::ostream& operator<<(std::ostream& out, const TagType::type& val) {
+ std::map<int, const char*>::const_iterator it = _TagType_VALUES_TO_NAMES.find(val);
+ if (it != _TagType_VALUES_TO_NAMES.end()) {
+ out << it->second;
+ } else {
+ out << static_cast<int>(val);
+ }
+ return out;
+}
+
+std::string to_string(const TagType::type& val) {
+ std::map<int, const char*>::const_iterator it = _TagType_VALUES_TO_NAMES.find(val);
+ if (it != _TagType_VALUES_TO_NAMES.end()) {
+ return std::string(it->second);
+ } else {
+ return std::to_string(static_cast<int>(val));
+ }
+}
+
+int _kSpanRefTypeValues[] = {
+ SpanRefType::CHILD_OF,
+ SpanRefType::FOLLOWS_FROM
+};
+const char* _kSpanRefTypeNames[] = {
+ "CHILD_OF",
+ "FOLLOWS_FROM"
+};
+const std::map<int, const char*> _SpanRefType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(2, _kSpanRefTypeValues, _kSpanRefTypeNames), ::apache::thrift::TEnumIterator(-1, nullptr, nullptr));
+
+std::ostream& operator<<(std::ostream& out, const SpanRefType::type& val) {
+ std::map<int, const char*>::const_iterator it = _SpanRefType_VALUES_TO_NAMES.find(val);
+ if (it != _SpanRefType_VALUES_TO_NAMES.end()) {
+ out << it->second;
+ } else {
+ out << static_cast<int>(val);
+ }
+ return out;
+}
+
+std::string to_string(const SpanRefType::type& val) {
+ std::map<int, const char*>::const_iterator it = _SpanRefType_VALUES_TO_NAMES.find(val);
+ if (it != _SpanRefType_VALUES_TO_NAMES.end()) {
+ return std::string(it->second);
+ } else {
+ return std::to_string(static_cast<int>(val));
+ }
+}
+
+
+Tag::~Tag() noexcept {
+}
+
+
+void Tag::__set_key(const std::string& val) {
+ this->key = val;
+}
+
+void Tag::__set_vType(const TagType::type val) {
+ this->vType = val;
+}
+
+void Tag::__set_vStr(const std::string& val) {
+ this->vStr = val;
+__isset.vStr = true;
+}
+
+void Tag::__set_vDouble(const double val) {
+ this->vDouble = val;
+__isset.vDouble = true;
+}
+
+void Tag::__set_vBool(const bool val) {
+ this->vBool = val;
+__isset.vBool = true;
+}
+
+void Tag::__set_vLong(const int64_t val) {
+ this->vLong = val;
+__isset.vLong = true;
+}
+
+void Tag::__set_vBinary(const std::string& val) {
+ this->vBinary = val;
+__isset.vBinary = true;
+}
+std::ostream& operator<<(std::ostream& out, const Tag& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Tag::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_key = false;
+ bool isset_vType = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->key);
+ isset_key = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_I32) {
+ int32_t ecast0;
+ xfer += iprot->readI32(ecast0);
+ this->vType = (TagType::type)ecast0;
+ isset_vType = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->vStr);
+ this->__isset.vStr = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 4:
+ if (ftype == ::apache::thrift::protocol::T_DOUBLE) {
+ xfer += iprot->readDouble(this->vDouble);
+ this->__isset.vDouble = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 5:
+ if (ftype == ::apache::thrift::protocol::T_BOOL) {
+ xfer += iprot->readBool(this->vBool);
+ this->__isset.vBool = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 6:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->vLong);
+ this->__isset.vLong = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 7:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readBinary(this->vBinary);
+ this->__isset.vBinary = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_key)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_vType)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t Tag::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Tag");
+
+ xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1);
+ xfer += oprot->writeString(this->key);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("vType", ::apache::thrift::protocol::T_I32, 2);
+ xfer += oprot->writeI32((int32_t)this->vType);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.vStr) {
+ xfer += oprot->writeFieldBegin("vStr", ::apache::thrift::protocol::T_STRING, 3);
+ xfer += oprot->writeString(this->vStr);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.vDouble) {
+ xfer += oprot->writeFieldBegin("vDouble", ::apache::thrift::protocol::T_DOUBLE, 4);
+ xfer += oprot->writeDouble(this->vDouble);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.vBool) {
+ xfer += oprot->writeFieldBegin("vBool", ::apache::thrift::protocol::T_BOOL, 5);
+ xfer += oprot->writeBool(this->vBool);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.vLong) {
+ xfer += oprot->writeFieldBegin("vLong", ::apache::thrift::protocol::T_I64, 6);
+ xfer += oprot->writeI64(this->vLong);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.vBinary) {
+ xfer += oprot->writeFieldBegin("vBinary", ::apache::thrift::protocol::T_STRING, 7);
+ xfer += oprot->writeBinary(this->vBinary);
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Tag &a, Tag &b) {
+ using ::std::swap;
+ swap(a.key, b.key);
+ swap(a.vType, b.vType);
+ swap(a.vStr, b.vStr);
+ swap(a.vDouble, b.vDouble);
+ swap(a.vBool, b.vBool);
+ swap(a.vLong, b.vLong);
+ swap(a.vBinary, b.vBinary);
+ swap(a.__isset, b.__isset);
+}
+
+Tag::Tag(const Tag& other1) {
+ key = other1.key;
+ vType = other1.vType;
+ vStr = other1.vStr;
+ vDouble = other1.vDouble;
+ vBool = other1.vBool;
+ vLong = other1.vLong;
+ vBinary = other1.vBinary;
+ __isset = other1.__isset;
+}
+Tag& Tag::operator=(const Tag& other2) {
+ key = other2.key;
+ vType = other2.vType;
+ vStr = other2.vStr;
+ vDouble = other2.vDouble;
+ vBool = other2.vBool;
+ vLong = other2.vLong;
+ vBinary = other2.vBinary;
+ __isset = other2.__isset;
+ return *this;
+}
+void Tag::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Tag(";
+ out << "key=" << to_string(key);
+ out << ", " << "vType=" << to_string(vType);
+ out << ", " << "vStr="; (__isset.vStr ? (out << to_string(vStr)) : (out << "<null>"));
+ out << ", " << "vDouble="; (__isset.vDouble ? (out << to_string(vDouble)) : (out << "<null>"));
+ out << ", " << "vBool="; (__isset.vBool ? (out << to_string(vBool)) : (out << "<null>"));
+ out << ", " << "vLong="; (__isset.vLong ? (out << to_string(vLong)) : (out << "<null>"));
+ out << ", " << "vBinary="; (__isset.vBinary ? (out << to_string(vBinary)) : (out << "<null>"));
+ out << ")";
+}
+
+
+Log::~Log() noexcept {
+}
+
+
+void Log::__set_timestamp(const int64_t val) {
+ this->timestamp = val;
+}
+
+void Log::__set_fields(const std::vector<Tag> & val) {
+ this->fields = val;
+}
+std::ostream& operator<<(std::ostream& out, const Log& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Log::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_timestamp = false;
+ bool isset_fields = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->timestamp);
+ isset_timestamp = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->fields.clear();
+ uint32_t _size3;
+ ::apache::thrift::protocol::TType _etype6;
+ xfer += iprot->readListBegin(_etype6, _size3);
+ this->fields.resize(_size3);
+ uint32_t _i7;
+ for (_i7 = 0; _i7 < _size3; ++_i7)
+ {
+ xfer += this->fields[_i7].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ isset_fields = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_timestamp)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_fields)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t Log::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Log");
+
+ xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 1);
+ xfer += oprot->writeI64(this->timestamp);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("fields", ::apache::thrift::protocol::T_LIST, 2);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->fields.size()));
+ std::vector<Tag> ::const_iterator _iter8;
+ for (_iter8 = this->fields.begin(); _iter8 != this->fields.end(); ++_iter8)
+ {
+ xfer += (*_iter8).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Log &a, Log &b) {
+ using ::std::swap;
+ swap(a.timestamp, b.timestamp);
+ swap(a.fields, b.fields);
+}
+
+Log::Log(const Log& other9) {
+ timestamp = other9.timestamp;
+ fields = other9.fields;
+}
+Log& Log::operator=(const Log& other10) {
+ timestamp = other10.timestamp;
+ fields = other10.fields;
+ return *this;
+}
+void Log::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Log(";
+ out << "timestamp=" << to_string(timestamp);
+ out << ", " << "fields=" << to_string(fields);
+ out << ")";
+}
+
+
+SpanRef::~SpanRef() noexcept {
+}
+
+
+void SpanRef::__set_refType(const SpanRefType::type val) {
+ this->refType = val;
+}
+
+void SpanRef::__set_traceIdLow(const int64_t val) {
+ this->traceIdLow = val;
+}
+
+void SpanRef::__set_traceIdHigh(const int64_t val) {
+ this->traceIdHigh = val;
+}
+
+void SpanRef::__set_spanId(const int64_t val) {
+ this->spanId = val;
+}
+std::ostream& operator<<(std::ostream& out, const SpanRef& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t SpanRef::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_refType = false;
+ bool isset_traceIdLow = false;
+ bool isset_traceIdHigh = false;
+ bool isset_spanId = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_I32) {
+ int32_t ecast11;
+ xfer += iprot->readI32(ecast11);
+ this->refType = (SpanRefType::type)ecast11;
+ isset_refType = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->traceIdLow);
+ isset_traceIdLow = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->traceIdHigh);
+ isset_traceIdHigh = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 4:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->spanId);
+ isset_spanId = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_refType)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_traceIdLow)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_traceIdHigh)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_spanId)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t SpanRef::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("SpanRef");
+
+ xfer += oprot->writeFieldBegin("refType", ::apache::thrift::protocol::T_I32, 1);
+ xfer += oprot->writeI32((int32_t)this->refType);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("traceIdLow", ::apache::thrift::protocol::T_I64, 2);
+ xfer += oprot->writeI64(this->traceIdLow);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("traceIdHigh", ::apache::thrift::protocol::T_I64, 3);
+ xfer += oprot->writeI64(this->traceIdHigh);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("spanId", ::apache::thrift::protocol::T_I64, 4);
+ xfer += oprot->writeI64(this->spanId);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(SpanRef &a, SpanRef &b) {
+ using ::std::swap;
+ swap(a.refType, b.refType);
+ swap(a.traceIdLow, b.traceIdLow);
+ swap(a.traceIdHigh, b.traceIdHigh);
+ swap(a.spanId, b.spanId);
+}
+
+SpanRef::SpanRef(const SpanRef& other12) {
+ refType = other12.refType;
+ traceIdLow = other12.traceIdLow;
+ traceIdHigh = other12.traceIdHigh;
+ spanId = other12.spanId;
+}
+SpanRef& SpanRef::operator=(const SpanRef& other13) {
+ refType = other13.refType;
+ traceIdLow = other13.traceIdLow;
+ traceIdHigh = other13.traceIdHigh;
+ spanId = other13.spanId;
+ return *this;
+}
+void SpanRef::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "SpanRef(";
+ out << "refType=" << to_string(refType);
+ out << ", " << "traceIdLow=" << to_string(traceIdLow);
+ out << ", " << "traceIdHigh=" << to_string(traceIdHigh);
+ out << ", " << "spanId=" << to_string(spanId);
+ out << ")";
+}
+
+
+Span::~Span() noexcept {
+}
+
+
+void Span::__set_traceIdLow(const int64_t val) {
+ this->traceIdLow = val;
+}
+
+void Span::__set_traceIdHigh(const int64_t val) {
+ this->traceIdHigh = val;
+}
+
+void Span::__set_spanId(const int64_t val) {
+ this->spanId = val;
+}
+
+void Span::__set_parentSpanId(const int64_t val) {
+ this->parentSpanId = val;
+}
+
+void Span::__set_operationName(const std::string& val) {
+ this->operationName = val;
+}
+
+void Span::__set_references(const std::vector<SpanRef> & val) {
+ this->references = val;
+__isset.references = true;
+}
+
+void Span::__set_flags(const int32_t val) {
+ this->flags = val;
+}
+
+void Span::__set_startTime(const int64_t val) {
+ this->startTime = val;
+}
+
+void Span::__set_duration(const int64_t val) {
+ this->duration = val;
+}
+
+void Span::__set_tags(const std::vector<Tag> & val) {
+ this->tags = val;
+__isset.tags = true;
+}
+
+void Span::__set_logs(const std::vector<Log> & val) {
+ this->logs = val;
+__isset.logs = true;
+}
+std::ostream& operator<<(std::ostream& out, const Span& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Span::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_traceIdLow = false;
+ bool isset_traceIdHigh = false;
+ bool isset_spanId = false;
+ bool isset_parentSpanId = false;
+ bool isset_operationName = false;
+ bool isset_flags = false;
+ bool isset_startTime = false;
+ bool isset_duration = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->traceIdLow);
+ isset_traceIdLow = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->traceIdHigh);
+ isset_traceIdHigh = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->spanId);
+ isset_spanId = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 4:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->parentSpanId);
+ isset_parentSpanId = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 5:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->operationName);
+ isset_operationName = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 6:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->references.clear();
+ uint32_t _size14;
+ ::apache::thrift::protocol::TType _etype17;
+ xfer += iprot->readListBegin(_etype17, _size14);
+ this->references.resize(_size14);
+ uint32_t _i18;
+ for (_i18 = 0; _i18 < _size14; ++_i18)
+ {
+ xfer += this->references[_i18].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.references = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 7:
+ if (ftype == ::apache::thrift::protocol::T_I32) {
+ xfer += iprot->readI32(this->flags);
+ isset_flags = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 8:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->startTime);
+ isset_startTime = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 9:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->duration);
+ isset_duration = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 10:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->tags.clear();
+ uint32_t _size19;
+ ::apache::thrift::protocol::TType _etype22;
+ xfer += iprot->readListBegin(_etype22, _size19);
+ this->tags.resize(_size19);
+ uint32_t _i23;
+ for (_i23 = 0; _i23 < _size19; ++_i23)
+ {
+ xfer += this->tags[_i23].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.tags = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 11:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->logs.clear();
+ uint32_t _size24;
+ ::apache::thrift::protocol::TType _etype27;
+ xfer += iprot->readListBegin(_etype27, _size24);
+ this->logs.resize(_size24);
+ uint32_t _i28;
+ for (_i28 = 0; _i28 < _size24; ++_i28)
+ {
+ xfer += this->logs[_i28].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.logs = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_traceIdLow)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_traceIdHigh)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_spanId)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_parentSpanId)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_operationName)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_flags)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_startTime)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_duration)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t Span::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Span");
+
+ xfer += oprot->writeFieldBegin("traceIdLow", ::apache::thrift::protocol::T_I64, 1);
+ xfer += oprot->writeI64(this->traceIdLow);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("traceIdHigh", ::apache::thrift::protocol::T_I64, 2);
+ xfer += oprot->writeI64(this->traceIdHigh);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("spanId", ::apache::thrift::protocol::T_I64, 3);
+ xfer += oprot->writeI64(this->spanId);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("parentSpanId", ::apache::thrift::protocol::T_I64, 4);
+ xfer += oprot->writeI64(this->parentSpanId);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("operationName", ::apache::thrift::protocol::T_STRING, 5);
+ xfer += oprot->writeString(this->operationName);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.references) {
+ xfer += oprot->writeFieldBegin("references", ::apache::thrift::protocol::T_LIST, 6);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->references.size()));
+ std::vector<SpanRef> ::const_iterator _iter29;
+ for (_iter29 = this->references.begin(); _iter29 != this->references.end(); ++_iter29)
+ {
+ xfer += (*_iter29).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldBegin("flags", ::apache::thrift::protocol::T_I32, 7);
+ xfer += oprot->writeI32(this->flags);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("startTime", ::apache::thrift::protocol::T_I64, 8);
+ xfer += oprot->writeI64(this->startTime);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("duration", ::apache::thrift::protocol::T_I64, 9);
+ xfer += oprot->writeI64(this->duration);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.tags) {
+ xfer += oprot->writeFieldBegin("tags", ::apache::thrift::protocol::T_LIST, 10);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->tags.size()));
+ std::vector<Tag> ::const_iterator _iter30;
+ for (_iter30 = this->tags.begin(); _iter30 != this->tags.end(); ++_iter30)
+ {
+ xfer += (*_iter30).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.logs) {
+ xfer += oprot->writeFieldBegin("logs", ::apache::thrift::protocol::T_LIST, 11);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->logs.size()));
+ std::vector<Log> ::const_iterator _iter31;
+ for (_iter31 = this->logs.begin(); _iter31 != this->logs.end(); ++_iter31)
+ {
+ xfer += (*_iter31).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Span &a, Span &b) {
+ using ::std::swap;
+ swap(a.traceIdLow, b.traceIdLow);
+ swap(a.traceIdHigh, b.traceIdHigh);
+ swap(a.spanId, b.spanId);
+ swap(a.parentSpanId, b.parentSpanId);
+ swap(a.operationName, b.operationName);
+ swap(a.references, b.references);
+ swap(a.flags, b.flags);
+ swap(a.startTime, b.startTime);
+ swap(a.duration, b.duration);
+ swap(a.tags, b.tags);
+ swap(a.logs, b.logs);
+ swap(a.__isset, b.__isset);
+}
+
+Span::Span(const Span& other32) {
+ traceIdLow = other32.traceIdLow;
+ traceIdHigh = other32.traceIdHigh;
+ spanId = other32.spanId;
+ parentSpanId = other32.parentSpanId;
+ operationName = other32.operationName;
+ references = other32.references;
+ flags = other32.flags;
+ startTime = other32.startTime;
+ duration = other32.duration;
+ tags = other32.tags;
+ logs = other32.logs;
+ __isset = other32.__isset;
+}
+Span& Span::operator=(const Span& other33) {
+ traceIdLow = other33.traceIdLow;
+ traceIdHigh = other33.traceIdHigh;
+ spanId = other33.spanId;
+ parentSpanId = other33.parentSpanId;
+ operationName = other33.operationName;
+ references = other33.references;
+ flags = other33.flags;
+ startTime = other33.startTime;
+ duration = other33.duration;
+ tags = other33.tags;
+ logs = other33.logs;
+ __isset = other33.__isset;
+ return *this;
+}
+void Span::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Span(";
+ out << "traceIdLow=" << to_string(traceIdLow);
+ out << ", " << "traceIdHigh=" << to_string(traceIdHigh);
+ out << ", " << "spanId=" << to_string(spanId);
+ out << ", " << "parentSpanId=" << to_string(parentSpanId);
+ out << ", " << "operationName=" << to_string(operationName);
+ out << ", " << "references="; (__isset.references ? (out << to_string(references)) : (out << "<null>"));
+ out << ", " << "flags=" << to_string(flags);
+ out << ", " << "startTime=" << to_string(startTime);
+ out << ", " << "duration=" << to_string(duration);
+ out << ", " << "tags="; (__isset.tags ? (out << to_string(tags)) : (out << "<null>"));
+ out << ", " << "logs="; (__isset.logs ? (out << to_string(logs)) : (out << "<null>"));
+ out << ")";
+}
+
+
+Process::~Process() noexcept {
+}
+
+
+void Process::__set_serviceName(const std::string& val) {
+ this->serviceName = val;
+}
+
+void Process::__set_tags(const std::vector<Tag> & val) {
+ this->tags = val;
+__isset.tags = true;
+}
+std::ostream& operator<<(std::ostream& out, const Process& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Process::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_serviceName = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->serviceName);
+ isset_serviceName = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->tags.clear();
+ uint32_t _size34;
+ ::apache::thrift::protocol::TType _etype37;
+ xfer += iprot->readListBegin(_etype37, _size34);
+ this->tags.resize(_size34);
+ uint32_t _i38;
+ for (_i38 = 0; _i38 < _size34; ++_i38)
+ {
+ xfer += this->tags[_i38].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.tags = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_serviceName)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t Process::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Process");
+
+ xfer += oprot->writeFieldBegin("serviceName", ::apache::thrift::protocol::T_STRING, 1);
+ xfer += oprot->writeString(this->serviceName);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.tags) {
+ xfer += oprot->writeFieldBegin("tags", ::apache::thrift::protocol::T_LIST, 2);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->tags.size()));
+ std::vector<Tag> ::const_iterator _iter39;
+ for (_iter39 = this->tags.begin(); _iter39 != this->tags.end(); ++_iter39)
+ {
+ xfer += (*_iter39).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Process &a, Process &b) {
+ using ::std::swap;
+ swap(a.serviceName, b.serviceName);
+ swap(a.tags, b.tags);
+ swap(a.__isset, b.__isset);
+}
+
+Process::Process(const Process& other40) {
+ serviceName = other40.serviceName;
+ tags = other40.tags;
+ __isset = other40.__isset;
+}
+Process& Process::operator=(const Process& other41) {
+ serviceName = other41.serviceName;
+ tags = other41.tags;
+ __isset = other41.__isset;
+ return *this;
+}
+void Process::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Process(";
+ out << "serviceName=" << to_string(serviceName);
+ out << ", " << "tags="; (__isset.tags ? (out << to_string(tags)) : (out << "<null>"));
+ out << ")";
+}
+
+
+Batch::~Batch() noexcept {
+}
+
+
+void Batch::__set_process(const Process& val) {
+ this->process = val;
+}
+
+void Batch::__set_spans(const std::vector<Span> & val) {
+ this->spans = val;
+}
+std::ostream& operator<<(std::ostream& out, const Batch& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Batch::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_process = false;
+ bool isset_spans = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_STRUCT) {
+ xfer += this->process.read(iprot);
+ isset_process = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->spans.clear();
+ uint32_t _size42;
+ ::apache::thrift::protocol::TType _etype45;
+ xfer += iprot->readListBegin(_etype45, _size42);
+ this->spans.resize(_size42);
+ uint32_t _i46;
+ for (_i46 = 0; _i46 < _size42; ++_i46)
+ {
+ xfer += this->spans[_i46].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ isset_spans = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_process)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ if (!isset_spans)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t Batch::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Batch");
+
+ xfer += oprot->writeFieldBegin("process", ::apache::thrift::protocol::T_STRUCT, 1);
+ xfer += this->process.write(oprot);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("spans", ::apache::thrift::protocol::T_LIST, 2);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->spans.size()));
+ std::vector<Span> ::const_iterator _iter47;
+ for (_iter47 = this->spans.begin(); _iter47 != this->spans.end(); ++_iter47)
+ {
+ xfer += (*_iter47).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Batch &a, Batch &b) {
+ using ::std::swap;
+ swap(a.process, b.process);
+ swap(a.spans, b.spans);
+}
+
+Batch::Batch(const Batch& other48) {
+ process = other48.process;
+ spans = other48.spans;
+}
+Batch& Batch::operator=(const Batch& other49) {
+ process = other49.process;
+ spans = other49.spans;
+ return *this;
+}
+void Batch::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Batch(";
+ out << "process=" << to_string(process);
+ out << ", " << "spans=" << to_string(spans);
+ out << ")";
+}
+
+
+BatchSubmitResponse::~BatchSubmitResponse() noexcept {
+}
+
+
+void BatchSubmitResponse::__set_ok(const bool val) {
+ this->ok = val;
+}
+std::ostream& operator<<(std::ostream& out, const BatchSubmitResponse& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t BatchSubmitResponse::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_ok = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_BOOL) {
+ xfer += iprot->readBool(this->ok);
+ isset_ok = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_ok)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t BatchSubmitResponse::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("BatchSubmitResponse");
+
+ xfer += oprot->writeFieldBegin("ok", ::apache::thrift::protocol::T_BOOL, 1);
+ xfer += oprot->writeBool(this->ok);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(BatchSubmitResponse &a, BatchSubmitResponse &b) {
+ using ::std::swap;
+ swap(a.ok, b.ok);
+}
+
+BatchSubmitResponse::BatchSubmitResponse(const BatchSubmitResponse& other50) {
+ ok = other50.ok;
+}
+BatchSubmitResponse& BatchSubmitResponse::operator=(const BatchSubmitResponse& other51) {
+ ok = other51.ok;
+ return *this;
+}
+void BatchSubmitResponse::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "BatchSubmitResponse(";
+ out << "ok=" << to_string(ok);
+ out << ")";
+}
+
+}} // namespace
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.h
new file mode 100644
index 000000000..b30a3740b
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/jaeger_types.h
@@ -0,0 +1,481 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef jaeger_TYPES_H
+#define jaeger_TYPES_H
+
+#include <iosfwd>
+
+#include <thrift/Thrift.h>
+#include <thrift/TApplicationException.h>
+#include <thrift/TBase.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/transport/TTransport.h>
+
+#include <functional>
+#include <memory>
+
+
+namespace jaegertracing { namespace thrift {
+
+struct TagType {
+ enum type {
+ STRING = 0,
+ DOUBLE = 1,
+ BOOL = 2,
+ LONG = 3,
+ BINARY = 4
+ };
+};
+
+extern const std::map<int, const char*> _TagType_VALUES_TO_NAMES;
+
+std::ostream& operator<<(std::ostream& out, const TagType::type& val);
+
+std::string to_string(const TagType::type& val);
+
+struct SpanRefType {
+ enum type {
+ CHILD_OF = 0,
+ FOLLOWS_FROM = 1
+ };
+};
+
+extern const std::map<int, const char*> _SpanRefType_VALUES_TO_NAMES;
+
+std::ostream& operator<<(std::ostream& out, const SpanRefType::type& val);
+
+std::string to_string(const SpanRefType::type& val);
+
+class Tag;
+
+class Log;
+
+class SpanRef;
+
+class Span;
+
+class Process;
+
+class Batch;
+
+class BatchSubmitResponse;
+
+typedef struct _Tag__isset {
+ _Tag__isset() : vStr(false), vDouble(false), vBool(false), vLong(false), vBinary(false) {}
+ bool vStr :1;
+ bool vDouble :1;
+ bool vBool :1;
+ bool vLong :1;
+ bool vBinary :1;
+} _Tag__isset;
+
+class Tag : public virtual ::apache::thrift::TBase {
+ public:
+
+ Tag(const Tag&);
+ Tag& operator=(const Tag&);
+ Tag() : key(), vType((TagType::type)0), vStr(), vDouble(0), vBool(0), vLong(0), vBinary() {
+ }
+
+ virtual ~Tag() noexcept;
+ std::string key;
+ /**
+ *
+ * @see TagType
+ */
+ TagType::type vType;
+ std::string vStr;
+ double vDouble;
+ bool vBool;
+ int64_t vLong;
+ std::string vBinary;
+
+ _Tag__isset __isset;
+
+ void __set_key(const std::string& val);
+
+ void __set_vType(const TagType::type val);
+
+ void __set_vStr(const std::string& val);
+
+ void __set_vDouble(const double val);
+
+ void __set_vBool(const bool val);
+
+ void __set_vLong(const int64_t val);
+
+ void __set_vBinary(const std::string& val);
+
+ bool operator == (const Tag & rhs) const
+ {
+ if (!(key == rhs.key))
+ return false;
+ if (!(vType == rhs.vType))
+ return false;
+ if (__isset.vStr != rhs.__isset.vStr)
+ return false;
+ else if (__isset.vStr && !(vStr == rhs.vStr))
+ return false;
+ if (__isset.vDouble != rhs.__isset.vDouble)
+ return false;
+ else if (__isset.vDouble && !(vDouble == rhs.vDouble))
+ return false;
+ if (__isset.vBool != rhs.__isset.vBool)
+ return false;
+ else if (__isset.vBool && !(vBool == rhs.vBool))
+ return false;
+ if (__isset.vLong != rhs.__isset.vLong)
+ return false;
+ else if (__isset.vLong && !(vLong == rhs.vLong))
+ return false;
+ if (__isset.vBinary != rhs.__isset.vBinary)
+ return false;
+ else if (__isset.vBinary && !(vBinary == rhs.vBinary))
+ return false;
+ return true;
+ }
+ bool operator != (const Tag &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Tag & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Tag &a, Tag &b);
+
+std::ostream& operator<<(std::ostream& out, const Tag& obj);
+
+
+class Log : public virtual ::apache::thrift::TBase {
+ public:
+
+ Log(const Log&);
+ Log& operator=(const Log&);
+ Log() : timestamp(0) {
+ }
+
+ virtual ~Log() noexcept;
+ int64_t timestamp;
+ std::vector<Tag> fields;
+
+ void __set_timestamp(const int64_t val);
+
+ void __set_fields(const std::vector<Tag> & val);
+
+ bool operator == (const Log & rhs) const
+ {
+ if (!(timestamp == rhs.timestamp))
+ return false;
+ if (!(fields == rhs.fields))
+ return false;
+ return true;
+ }
+ bool operator != (const Log &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Log & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Log &a, Log &b);
+
+std::ostream& operator<<(std::ostream& out, const Log& obj);
+
+
+class SpanRef : public virtual ::apache::thrift::TBase {
+ public:
+
+ SpanRef(const SpanRef&);
+ SpanRef& operator=(const SpanRef&);
+ SpanRef() : refType((SpanRefType::type)0), traceIdLow(0), traceIdHigh(0), spanId(0) {
+ }
+
+ virtual ~SpanRef() noexcept;
+ /**
+ *
+ * @see SpanRefType
+ */
+ SpanRefType::type refType;
+ int64_t traceIdLow;
+ int64_t traceIdHigh;
+ int64_t spanId;
+
+ void __set_refType(const SpanRefType::type val);
+
+ void __set_traceIdLow(const int64_t val);
+
+ void __set_traceIdHigh(const int64_t val);
+
+ void __set_spanId(const int64_t val);
+
+ bool operator == (const SpanRef & rhs) const
+ {
+ if (!(refType == rhs.refType))
+ return false;
+ if (!(traceIdLow == rhs.traceIdLow))
+ return false;
+ if (!(traceIdHigh == rhs.traceIdHigh))
+ return false;
+ if (!(spanId == rhs.spanId))
+ return false;
+ return true;
+ }
+ bool operator != (const SpanRef &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const SpanRef & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(SpanRef &a, SpanRef &b);
+
+std::ostream& operator<<(std::ostream& out, const SpanRef& obj);
+
+typedef struct _Span__isset {
+ _Span__isset() : references(false), tags(false), logs(false) {}
+ bool references :1;
+ bool tags :1;
+ bool logs :1;
+} _Span__isset;
+
+class Span : public virtual ::apache::thrift::TBase {
+ public:
+
+ Span(const Span&);
+ Span& operator=(const Span&);
+ Span() : traceIdLow(0), traceIdHigh(0), spanId(0), parentSpanId(0), operationName(), flags(0), startTime(0), duration(0) {
+ }
+
+ virtual ~Span() noexcept;
+ int64_t traceIdLow;
+ int64_t traceIdHigh;
+ int64_t spanId;
+ int64_t parentSpanId;
+ std::string operationName;
+ std::vector<SpanRef> references;
+ int32_t flags;
+ int64_t startTime;
+ int64_t duration;
+ std::vector<Tag> tags;
+ std::vector<Log> logs;
+
+ _Span__isset __isset;
+
+ void __set_traceIdLow(const int64_t val);
+
+ void __set_traceIdHigh(const int64_t val);
+
+ void __set_spanId(const int64_t val);
+
+ void __set_parentSpanId(const int64_t val);
+
+ void __set_operationName(const std::string& val);
+
+ void __set_references(const std::vector<SpanRef> & val);
+
+ void __set_flags(const int32_t val);
+
+ void __set_startTime(const int64_t val);
+
+ void __set_duration(const int64_t val);
+
+ void __set_tags(const std::vector<Tag> & val);
+
+ void __set_logs(const std::vector<Log> & val);
+
+ bool operator == (const Span & rhs) const
+ {
+ if (!(traceIdLow == rhs.traceIdLow))
+ return false;
+ if (!(traceIdHigh == rhs.traceIdHigh))
+ return false;
+ if (!(spanId == rhs.spanId))
+ return false;
+ if (!(parentSpanId == rhs.parentSpanId))
+ return false;
+ if (!(operationName == rhs.operationName))
+ return false;
+ if (__isset.references != rhs.__isset.references)
+ return false;
+ else if (__isset.references && !(references == rhs.references))
+ return false;
+ if (!(flags == rhs.flags))
+ return false;
+ if (!(startTime == rhs.startTime))
+ return false;
+ if (!(duration == rhs.duration))
+ return false;
+ if (__isset.tags != rhs.__isset.tags)
+ return false;
+ else if (__isset.tags && !(tags == rhs.tags))
+ return false;
+ if (__isset.logs != rhs.__isset.logs)
+ return false;
+ else if (__isset.logs && !(logs == rhs.logs))
+ return false;
+ return true;
+ }
+ bool operator != (const Span &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Span & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Span &a, Span &b);
+
+std::ostream& operator<<(std::ostream& out, const Span& obj);
+
+typedef struct _Process__isset {
+ _Process__isset() : tags(false) {}
+ bool tags :1;
+} _Process__isset;
+
+class Process : public virtual ::apache::thrift::TBase {
+ public:
+
+ Process(const Process&);
+ Process& operator=(const Process&);
+ Process() : serviceName() {
+ }
+
+ virtual ~Process() noexcept;
+ std::string serviceName;
+ std::vector<Tag> tags;
+
+ _Process__isset __isset;
+
+ void __set_serviceName(const std::string& val);
+
+ void __set_tags(const std::vector<Tag> & val);
+
+ bool operator == (const Process & rhs) const
+ {
+ if (!(serviceName == rhs.serviceName))
+ return false;
+ if (__isset.tags != rhs.__isset.tags)
+ return false;
+ else if (__isset.tags && !(tags == rhs.tags))
+ return false;
+ return true;
+ }
+ bool operator != (const Process &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Process & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Process &a, Process &b);
+
+std::ostream& operator<<(std::ostream& out, const Process& obj);
+
+
+class Batch : public virtual ::apache::thrift::TBase {
+ public:
+
+ Batch(const Batch&);
+ Batch& operator=(const Batch&);
+ Batch() {
+ }
+
+ virtual ~Batch() noexcept;
+ Process process;
+ std::vector<Span> spans;
+
+ void __set_process(const Process& val);
+
+ void __set_spans(const std::vector<Span> & val);
+
+ bool operator == (const Batch & rhs) const
+ {
+ if (!(process == rhs.process))
+ return false;
+ if (!(spans == rhs.spans))
+ return false;
+ return true;
+ }
+ bool operator != (const Batch &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Batch & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Batch &a, Batch &b);
+
+std::ostream& operator<<(std::ostream& out, const Batch& obj);
+
+
+class BatchSubmitResponse : public virtual ::apache::thrift::TBase {
+ public:
+
+ BatchSubmitResponse(const BatchSubmitResponse&);
+ BatchSubmitResponse& operator=(const BatchSubmitResponse&);
+ BatchSubmitResponse() : ok(0) {
+ }
+
+ virtual ~BatchSubmitResponse() noexcept;
+ bool ok;
+
+ void __set_ok(const bool val);
+
+ bool operator == (const BatchSubmitResponse & rhs) const
+ {
+ if (!(ok == rhs.ok))
+ return false;
+ return true;
+ }
+ bool operator != (const BatchSubmitResponse &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const BatchSubmitResponse & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(BatchSubmitResponse &a, BatchSubmitResponse &b);
+
+std::ostream& operator<<(std::ostream& out, const BatchSubmitResponse& obj);
+
+}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.cpp
new file mode 100644
index 000000000..8dd451e48
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.cpp
@@ -0,0 +1,49 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#include "zipkincore_constants.h"
+
+namespace twitter { namespace zipkin { namespace thrift {
+
+const zipkincoreConstants g_zipkincore_constants;
+
+zipkincoreConstants::zipkincoreConstants() {
+ CLIENT_SEND = "cs";
+
+ CLIENT_RECV = "cr";
+
+ SERVER_SEND = "ss";
+
+ SERVER_RECV = "sr";
+
+ MESSAGE_SEND = "ms";
+
+ MESSAGE_RECV = "mr";
+
+ WIRE_SEND = "ws";
+
+ WIRE_RECV = "wr";
+
+ CLIENT_SEND_FRAGMENT = "csf";
+
+ CLIENT_RECV_FRAGMENT = "crf";
+
+ SERVER_SEND_FRAGMENT = "ssf";
+
+ SERVER_RECV_FRAGMENT = "srf";
+
+ LOCAL_COMPONENT = "lc";
+
+ CLIENT_ADDR = "ca";
+
+ SERVER_ADDR = "sa";
+
+ MESSAGE_ADDR = "ma";
+
+}
+
+}}} // namespace
+
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.h
new file mode 100644
index 000000000..22b4b3b67
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_constants.h
@@ -0,0 +1,40 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef zipkincore_CONSTANTS_H
+#define zipkincore_CONSTANTS_H
+
+#include "zipkincore_types.h"
+
+namespace twitter { namespace zipkin { namespace thrift {
+
+class zipkincoreConstants {
+ public:
+ zipkincoreConstants();
+
+ std::string CLIENT_SEND;
+ std::string CLIENT_RECV;
+ std::string SERVER_SEND;
+ std::string SERVER_RECV;
+ std::string MESSAGE_SEND;
+ std::string MESSAGE_RECV;
+ std::string WIRE_SEND;
+ std::string WIRE_RECV;
+ std::string CLIENT_SEND_FRAGMENT;
+ std::string CLIENT_RECV_FRAGMENT;
+ std::string SERVER_SEND_FRAGMENT;
+ std::string SERVER_RECV_FRAGMENT;
+ std::string LOCAL_COMPONENT;
+ std::string CLIENT_ADDR;
+ std::string SERVER_ADDR;
+ std::string MESSAGE_ADDR;
+};
+
+extern const zipkincoreConstants g_zipkincore_constants;
+
+}}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.cpp b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.cpp
new file mode 100644
index 000000000..a8b52e902
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.cpp
@@ -0,0 +1,913 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#include "zipkincore_types.h"
+
+#include <algorithm>
+#include <ostream>
+
+#include <thrift/TToString.h>
+
+namespace twitter { namespace zipkin { namespace thrift {
+
+int _kAnnotationTypeValues[] = {
+ AnnotationType::BOOL,
+ AnnotationType::BYTES,
+ AnnotationType::I16,
+ AnnotationType::I32,
+ AnnotationType::I64,
+ AnnotationType::DOUBLE,
+ AnnotationType::STRING
+};
+const char* _kAnnotationTypeNames[] = {
+ "BOOL",
+ "BYTES",
+ "I16",
+ "I32",
+ "I64",
+ "DOUBLE",
+ "STRING"
+};
+const std::map<int, const char*> _AnnotationType_VALUES_TO_NAMES(::apache::thrift::TEnumIterator(7, _kAnnotationTypeValues, _kAnnotationTypeNames), ::apache::thrift::TEnumIterator(-1, nullptr, nullptr));
+
+std::ostream& operator<<(std::ostream& out, const AnnotationType::type& val) {
+ std::map<int, const char*>::const_iterator it = _AnnotationType_VALUES_TO_NAMES.find(val);
+ if (it != _AnnotationType_VALUES_TO_NAMES.end()) {
+ out << it->second;
+ } else {
+ out << static_cast<int>(val);
+ }
+ return out;
+}
+
+std::string to_string(const AnnotationType::type& val) {
+ std::map<int, const char*>::const_iterator it = _AnnotationType_VALUES_TO_NAMES.find(val);
+ if (it != _AnnotationType_VALUES_TO_NAMES.end()) {
+ return std::string(it->second);
+ } else {
+ return std::to_string(static_cast<int>(val));
+ }
+}
+
+
+Endpoint::~Endpoint() noexcept {
+}
+
+
+void Endpoint::__set_ipv4(const int32_t val) {
+ this->ipv4 = val;
+}
+
+void Endpoint::__set_port(const int16_t val) {
+ this->port = val;
+}
+
+void Endpoint::__set_service_name(const std::string& val) {
+ this->service_name = val;
+}
+
+void Endpoint::__set_ipv6(const std::string& val) {
+ this->ipv6 = val;
+__isset.ipv6 = true;
+}
+std::ostream& operator<<(std::ostream& out, const Endpoint& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Endpoint::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_I32) {
+ xfer += iprot->readI32(this->ipv4);
+ this->__isset.ipv4 = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_I16) {
+ xfer += iprot->readI16(this->port);
+ this->__isset.port = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->service_name);
+ this->__isset.service_name = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 4:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readBinary(this->ipv6);
+ this->__isset.ipv6 = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Endpoint::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Endpoint");
+
+ xfer += oprot->writeFieldBegin("ipv4", ::apache::thrift::protocol::T_I32, 1);
+ xfer += oprot->writeI32(this->ipv4);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("port", ::apache::thrift::protocol::T_I16, 2);
+ xfer += oprot->writeI16(this->port);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("service_name", ::apache::thrift::protocol::T_STRING, 3);
+ xfer += oprot->writeString(this->service_name);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.ipv6) {
+ xfer += oprot->writeFieldBegin("ipv6", ::apache::thrift::protocol::T_STRING, 4);
+ xfer += oprot->writeBinary(this->ipv6);
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Endpoint &a, Endpoint &b) {
+ using ::std::swap;
+ swap(a.ipv4, b.ipv4);
+ swap(a.port, b.port);
+ swap(a.service_name, b.service_name);
+ swap(a.ipv6, b.ipv6);
+ swap(a.__isset, b.__isset);
+}
+
+Endpoint::Endpoint(const Endpoint& other0) {
+ ipv4 = other0.ipv4;
+ port = other0.port;
+ service_name = other0.service_name;
+ ipv6 = other0.ipv6;
+ __isset = other0.__isset;
+}
+Endpoint& Endpoint::operator=(const Endpoint& other1) {
+ ipv4 = other1.ipv4;
+ port = other1.port;
+ service_name = other1.service_name;
+ ipv6 = other1.ipv6;
+ __isset = other1.__isset;
+ return *this;
+}
+void Endpoint::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Endpoint(";
+ out << "ipv4=" << to_string(ipv4);
+ out << ", " << "port=" << to_string(port);
+ out << ", " << "service_name=" << to_string(service_name);
+ out << ", " << "ipv6="; (__isset.ipv6 ? (out << to_string(ipv6)) : (out << "<null>"));
+ out << ")";
+}
+
+
+Annotation::~Annotation() noexcept {
+}
+
+
+void Annotation::__set_timestamp(const int64_t val) {
+ this->timestamp = val;
+}
+
+void Annotation::__set_value(const std::string& val) {
+ this->value = val;
+}
+
+void Annotation::__set_host(const Endpoint& val) {
+ this->host = val;
+__isset.host = true;
+}
+std::ostream& operator<<(std::ostream& out, const Annotation& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Annotation::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->timestamp);
+ this->__isset.timestamp = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->value);
+ this->__isset.value = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_STRUCT) {
+ xfer += this->host.read(iprot);
+ this->__isset.host = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Annotation::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Annotation");
+
+ xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 1);
+ xfer += oprot->writeI64(this->timestamp);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRING, 2);
+ xfer += oprot->writeString(this->value);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.host) {
+ xfer += oprot->writeFieldBegin("host", ::apache::thrift::protocol::T_STRUCT, 3);
+ xfer += this->host.write(oprot);
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Annotation &a, Annotation &b) {
+ using ::std::swap;
+ swap(a.timestamp, b.timestamp);
+ swap(a.value, b.value);
+ swap(a.host, b.host);
+ swap(a.__isset, b.__isset);
+}
+
+Annotation::Annotation(const Annotation& other2) {
+ timestamp = other2.timestamp;
+ value = other2.value;
+ host = other2.host;
+ __isset = other2.__isset;
+}
+Annotation& Annotation::operator=(const Annotation& other3) {
+ timestamp = other3.timestamp;
+ value = other3.value;
+ host = other3.host;
+ __isset = other3.__isset;
+ return *this;
+}
+void Annotation::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Annotation(";
+ out << "timestamp=" << to_string(timestamp);
+ out << ", " << "value=" << to_string(value);
+ out << ", " << "host="; (__isset.host ? (out << to_string(host)) : (out << "<null>"));
+ out << ")";
+}
+
+
+BinaryAnnotation::~BinaryAnnotation() noexcept {
+}
+
+
+void BinaryAnnotation::__set_key(const std::string& val) {
+ this->key = val;
+}
+
+void BinaryAnnotation::__set_value(const std::string& val) {
+ this->value = val;
+}
+
+void BinaryAnnotation::__set_annotation_type(const AnnotationType::type val) {
+ this->annotation_type = val;
+}
+
+void BinaryAnnotation::__set_host(const Endpoint& val) {
+ this->host = val;
+__isset.host = true;
+}
+std::ostream& operator<<(std::ostream& out, const BinaryAnnotation& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t BinaryAnnotation::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->key);
+ this->__isset.key = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 2:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readBinary(this->value);
+ this->__isset.value = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_I32) {
+ int32_t ecast4;
+ xfer += iprot->readI32(ecast4);
+ this->annotation_type = (AnnotationType::type)ecast4;
+ this->__isset.annotation_type = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 4:
+ if (ftype == ::apache::thrift::protocol::T_STRUCT) {
+ xfer += this->host.read(iprot);
+ this->__isset.host = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t BinaryAnnotation::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("BinaryAnnotation");
+
+ xfer += oprot->writeFieldBegin("key", ::apache::thrift::protocol::T_STRING, 1);
+ xfer += oprot->writeString(this->key);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("value", ::apache::thrift::protocol::T_STRING, 2);
+ xfer += oprot->writeBinary(this->value);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("annotation_type", ::apache::thrift::protocol::T_I32, 3);
+ xfer += oprot->writeI32((int32_t)this->annotation_type);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.host) {
+ xfer += oprot->writeFieldBegin("host", ::apache::thrift::protocol::T_STRUCT, 4);
+ xfer += this->host.write(oprot);
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(BinaryAnnotation &a, BinaryAnnotation &b) {
+ using ::std::swap;
+ swap(a.key, b.key);
+ swap(a.value, b.value);
+ swap(a.annotation_type, b.annotation_type);
+ swap(a.host, b.host);
+ swap(a.__isset, b.__isset);
+}
+
+BinaryAnnotation::BinaryAnnotation(const BinaryAnnotation& other5) {
+ key = other5.key;
+ value = other5.value;
+ annotation_type = other5.annotation_type;
+ host = other5.host;
+ __isset = other5.__isset;
+}
+BinaryAnnotation& BinaryAnnotation::operator=(const BinaryAnnotation& other6) {
+ key = other6.key;
+ value = other6.value;
+ annotation_type = other6.annotation_type;
+ host = other6.host;
+ __isset = other6.__isset;
+ return *this;
+}
+void BinaryAnnotation::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "BinaryAnnotation(";
+ out << "key=" << to_string(key);
+ out << ", " << "value=" << to_string(value);
+ out << ", " << "annotation_type=" << to_string(annotation_type);
+ out << ", " << "host="; (__isset.host ? (out << to_string(host)) : (out << "<null>"));
+ out << ")";
+}
+
+
+Span::~Span() noexcept {
+}
+
+
+void Span::__set_trace_id(const int64_t val) {
+ this->trace_id = val;
+}
+
+void Span::__set_name(const std::string& val) {
+ this->name = val;
+}
+
+void Span::__set_id(const int64_t val) {
+ this->id = val;
+}
+
+void Span::__set_parent_id(const int64_t val) {
+ this->parent_id = val;
+__isset.parent_id = true;
+}
+
+void Span::__set_annotations(const std::vector<Annotation> & val) {
+ this->annotations = val;
+}
+
+void Span::__set_binary_annotations(const std::vector<BinaryAnnotation> & val) {
+ this->binary_annotations = val;
+}
+
+void Span::__set_debug(const bool val) {
+ this->debug = val;
+__isset.debug = true;
+}
+
+void Span::__set_timestamp(const int64_t val) {
+ this->timestamp = val;
+__isset.timestamp = true;
+}
+
+void Span::__set_duration(const int64_t val) {
+ this->duration = val;
+__isset.duration = true;
+}
+
+void Span::__set_trace_id_high(const int64_t val) {
+ this->trace_id_high = val;
+__isset.trace_id_high = true;
+}
+std::ostream& operator<<(std::ostream& out, const Span& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Span::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->trace_id);
+ this->__isset.trace_id = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 3:
+ if (ftype == ::apache::thrift::protocol::T_STRING) {
+ xfer += iprot->readString(this->name);
+ this->__isset.name = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 4:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->id);
+ this->__isset.id = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 5:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->parent_id);
+ this->__isset.parent_id = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 6:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->annotations.clear();
+ uint32_t _size7;
+ ::apache::thrift::protocol::TType _etype10;
+ xfer += iprot->readListBegin(_etype10, _size7);
+ this->annotations.resize(_size7);
+ uint32_t _i11;
+ for (_i11 = 0; _i11 < _size7; ++_i11)
+ {
+ xfer += this->annotations[_i11].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.annotations = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 8:
+ if (ftype == ::apache::thrift::protocol::T_LIST) {
+ {
+ this->binary_annotations.clear();
+ uint32_t _size12;
+ ::apache::thrift::protocol::TType _etype15;
+ xfer += iprot->readListBegin(_etype15, _size12);
+ this->binary_annotations.resize(_size12);
+ uint32_t _i16;
+ for (_i16 = 0; _i16 < _size12; ++_i16)
+ {
+ xfer += this->binary_annotations[_i16].read(iprot);
+ }
+ xfer += iprot->readListEnd();
+ }
+ this->__isset.binary_annotations = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 9:
+ if (ftype == ::apache::thrift::protocol::T_BOOL) {
+ xfer += iprot->readBool(this->debug);
+ this->__isset.debug = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 10:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->timestamp);
+ this->__isset.timestamp = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 11:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->duration);
+ this->__isset.duration = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ case 12:
+ if (ftype == ::apache::thrift::protocol::T_I64) {
+ xfer += iprot->readI64(this->trace_id_high);
+ this->__isset.trace_id_high = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ return xfer;
+}
+
+uint32_t Span::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Span");
+
+ xfer += oprot->writeFieldBegin("trace_id", ::apache::thrift::protocol::T_I64, 1);
+ xfer += oprot->writeI64(this->trace_id);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("name", ::apache::thrift::protocol::T_STRING, 3);
+ xfer += oprot->writeString(this->name);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("id", ::apache::thrift::protocol::T_I64, 4);
+ xfer += oprot->writeI64(this->id);
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.parent_id) {
+ xfer += oprot->writeFieldBegin("parent_id", ::apache::thrift::protocol::T_I64, 5);
+ xfer += oprot->writeI64(this->parent_id);
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldBegin("annotations", ::apache::thrift::protocol::T_LIST, 6);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->annotations.size()));
+ std::vector<Annotation> ::const_iterator _iter17;
+ for (_iter17 = this->annotations.begin(); _iter17 != this->annotations.end(); ++_iter17)
+ {
+ xfer += (*_iter17).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldBegin("binary_annotations", ::apache::thrift::protocol::T_LIST, 8);
+ {
+ xfer += oprot->writeListBegin(::apache::thrift::protocol::T_STRUCT, static_cast<uint32_t>(this->binary_annotations.size()));
+ std::vector<BinaryAnnotation> ::const_iterator _iter18;
+ for (_iter18 = this->binary_annotations.begin(); _iter18 != this->binary_annotations.end(); ++_iter18)
+ {
+ xfer += (*_iter18).write(oprot);
+ }
+ xfer += oprot->writeListEnd();
+ }
+ xfer += oprot->writeFieldEnd();
+
+ if (this->__isset.debug) {
+ xfer += oprot->writeFieldBegin("debug", ::apache::thrift::protocol::T_BOOL, 9);
+ xfer += oprot->writeBool(this->debug);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.timestamp) {
+ xfer += oprot->writeFieldBegin("timestamp", ::apache::thrift::protocol::T_I64, 10);
+ xfer += oprot->writeI64(this->timestamp);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.duration) {
+ xfer += oprot->writeFieldBegin("duration", ::apache::thrift::protocol::T_I64, 11);
+ xfer += oprot->writeI64(this->duration);
+ xfer += oprot->writeFieldEnd();
+ }
+ if (this->__isset.trace_id_high) {
+ xfer += oprot->writeFieldBegin("trace_id_high", ::apache::thrift::protocol::T_I64, 12);
+ xfer += oprot->writeI64(this->trace_id_high);
+ xfer += oprot->writeFieldEnd();
+ }
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Span &a, Span &b) {
+ using ::std::swap;
+ swap(a.trace_id, b.trace_id);
+ swap(a.name, b.name);
+ swap(a.id, b.id);
+ swap(a.parent_id, b.parent_id);
+ swap(a.annotations, b.annotations);
+ swap(a.binary_annotations, b.binary_annotations);
+ swap(a.debug, b.debug);
+ swap(a.timestamp, b.timestamp);
+ swap(a.duration, b.duration);
+ swap(a.trace_id_high, b.trace_id_high);
+ swap(a.__isset, b.__isset);
+}
+
+Span::Span(const Span& other19) {
+ trace_id = other19.trace_id;
+ name = other19.name;
+ id = other19.id;
+ parent_id = other19.parent_id;
+ annotations = other19.annotations;
+ binary_annotations = other19.binary_annotations;
+ debug = other19.debug;
+ timestamp = other19.timestamp;
+ duration = other19.duration;
+ trace_id_high = other19.trace_id_high;
+ __isset = other19.__isset;
+}
+Span& Span::operator=(const Span& other20) {
+ trace_id = other20.trace_id;
+ name = other20.name;
+ id = other20.id;
+ parent_id = other20.parent_id;
+ annotations = other20.annotations;
+ binary_annotations = other20.binary_annotations;
+ debug = other20.debug;
+ timestamp = other20.timestamp;
+ duration = other20.duration;
+ trace_id_high = other20.trace_id_high;
+ __isset = other20.__isset;
+ return *this;
+}
+void Span::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Span(";
+ out << "trace_id=" << to_string(trace_id);
+ out << ", " << "name=" << to_string(name);
+ out << ", " << "id=" << to_string(id);
+ out << ", " << "parent_id="; (__isset.parent_id ? (out << to_string(parent_id)) : (out << "<null>"));
+ out << ", " << "annotations=" << to_string(annotations);
+ out << ", " << "binary_annotations=" << to_string(binary_annotations);
+ out << ", " << "debug="; (__isset.debug ? (out << to_string(debug)) : (out << "<null>"));
+ out << ", " << "timestamp="; (__isset.timestamp ? (out << to_string(timestamp)) : (out << "<null>"));
+ out << ", " << "duration="; (__isset.duration ? (out << to_string(duration)) : (out << "<null>"));
+ out << ", " << "trace_id_high="; (__isset.trace_id_high ? (out << to_string(trace_id_high)) : (out << "<null>"));
+ out << ")";
+}
+
+
+Response::~Response() noexcept {
+}
+
+
+void Response::__set_ok(const bool val) {
+ this->ok = val;
+}
+std::ostream& operator<<(std::ostream& out, const Response& obj)
+{
+ obj.printTo(out);
+ return out;
+}
+
+
+uint32_t Response::read(::apache::thrift::protocol::TProtocol* iprot) {
+
+ ::apache::thrift::protocol::TInputRecursionTracker tracker(*iprot);
+ uint32_t xfer = 0;
+ std::string fname;
+ ::apache::thrift::protocol::TType ftype;
+ int16_t fid;
+
+ xfer += iprot->readStructBegin(fname);
+
+ using ::apache::thrift::protocol::TProtocolException;
+
+ bool isset_ok = false;
+
+ while (true)
+ {
+ xfer += iprot->readFieldBegin(fname, ftype, fid);
+ if (ftype == ::apache::thrift::protocol::T_STOP) {
+ break;
+ }
+ switch (fid)
+ {
+ case 1:
+ if (ftype == ::apache::thrift::protocol::T_BOOL) {
+ xfer += iprot->readBool(this->ok);
+ isset_ok = true;
+ } else {
+ xfer += iprot->skip(ftype);
+ }
+ break;
+ default:
+ xfer += iprot->skip(ftype);
+ break;
+ }
+ xfer += iprot->readFieldEnd();
+ }
+
+ xfer += iprot->readStructEnd();
+
+ if (!isset_ok)
+ throw TProtocolException(TProtocolException::INVALID_DATA);
+ return xfer;
+}
+
+uint32_t Response::write(::apache::thrift::protocol::TProtocol* oprot) const {
+ uint32_t xfer = 0;
+ ::apache::thrift::protocol::TOutputRecursionTracker tracker(*oprot);
+ xfer += oprot->writeStructBegin("Response");
+
+ xfer += oprot->writeFieldBegin("ok", ::apache::thrift::protocol::T_BOOL, 1);
+ xfer += oprot->writeBool(this->ok);
+ xfer += oprot->writeFieldEnd();
+
+ xfer += oprot->writeFieldStop();
+ xfer += oprot->writeStructEnd();
+ return xfer;
+}
+
+void swap(Response &a, Response &b) {
+ using ::std::swap;
+ swap(a.ok, b.ok);
+}
+
+Response::Response(const Response& other21) {
+ ok = other21.ok;
+}
+Response& Response::operator=(const Response& other22) {
+ ok = other22.ok;
+ return *this;
+}
+void Response::printTo(std::ostream& out) const {
+ using ::apache::thrift::to_string;
+ out << "Response(";
+ out << "ok=" << to_string(ok);
+ out << ")";
+}
+
+}}} // namespace
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.h b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.h
new file mode 100644
index 000000000..c03421f43
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/thrift-gen/zipkincore_types.h
@@ -0,0 +1,493 @@
+/**
+ * Autogenerated by Thrift Compiler (0.14.0)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ * @generated
+ */
+#ifndef zipkincore_TYPES_H
+#define zipkincore_TYPES_H
+
+#include <iosfwd>
+
+#include <thrift/Thrift.h>
+#include <thrift/TApplicationException.h>
+#include <thrift/TBase.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/transport/TTransport.h>
+
+#include <functional>
+#include <memory>
+
+
+namespace twitter { namespace zipkin { namespace thrift {
+
+struct AnnotationType {
+ enum type {
+ BOOL = 0,
+ BYTES = 1,
+ I16 = 2,
+ I32 = 3,
+ I64 = 4,
+ DOUBLE = 5,
+ STRING = 6
+ };
+};
+
+extern const std::map<int, const char*> _AnnotationType_VALUES_TO_NAMES;
+
+std::ostream& operator<<(std::ostream& out, const AnnotationType::type& val);
+
+std::string to_string(const AnnotationType::type& val);
+
+class Endpoint;
+
+class Annotation;
+
+class BinaryAnnotation;
+
+class Span;
+
+class Response;
+
+typedef struct _Endpoint__isset {
+ _Endpoint__isset() : ipv4(false), port(false), service_name(false), ipv6(false) {}
+ bool ipv4 :1;
+ bool port :1;
+ bool service_name :1;
+ bool ipv6 :1;
+} _Endpoint__isset;
+
+/**
+ * Indicates the network context of a service recording an annotation with two
+ * exceptions.
+ *
+ * When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR,
+ * the endpoint indicates the source or destination of an RPC. This exception
+ * allows zipkin to display network context of uninstrumented services, or
+ * clients such as web browsers.
+ */
+class Endpoint : public virtual ::apache::thrift::TBase {
+ public:
+
+ Endpoint(const Endpoint&);
+ Endpoint& operator=(const Endpoint&);
+ Endpoint() : ipv4(0), port(0), service_name(), ipv6() {
+ }
+
+ virtual ~Endpoint() noexcept;
+ /**
+ * IPv4 host address packed into 4 bytes.
+ *
+ * Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4
+ */
+ int32_t ipv4;
+ /**
+ * IPv4 port
+ *
+ * Note: this is to be treated as an unsigned integer, so watch for negatives.
+ *
+ * Conventionally, when the port isn't known, port = 0.
+ */
+ int16_t port;
+ /**
+ * Service name in lowercase, such as "memcache" or "zipkin-web"
+ *
+ * Conventionally, when the service name isn't known, service_name = "unknown".
+ */
+ std::string service_name;
+ /**
+ * IPv6 host address packed into 16 bytes. Ex Inet6Address.getBytes()
+ */
+ std::string ipv6;
+
+ _Endpoint__isset __isset;
+
+ void __set_ipv4(const int32_t val);
+
+ void __set_port(const int16_t val);
+
+ void __set_service_name(const std::string& val);
+
+ void __set_ipv6(const std::string& val);
+
+ bool operator == (const Endpoint & rhs) const
+ {
+ if (!(ipv4 == rhs.ipv4))
+ return false;
+ if (!(port == rhs.port))
+ return false;
+ if (!(service_name == rhs.service_name))
+ return false;
+ if (__isset.ipv6 != rhs.__isset.ipv6)
+ return false;
+ else if (__isset.ipv6 && !(ipv6 == rhs.ipv6))
+ return false;
+ return true;
+ }
+ bool operator != (const Endpoint &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Endpoint & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Endpoint &a, Endpoint &b);
+
+std::ostream& operator<<(std::ostream& out, const Endpoint& obj);
+
+typedef struct _Annotation__isset {
+ _Annotation__isset() : timestamp(false), value(false), host(false) {}
+ bool timestamp :1;
+ bool value :1;
+ bool host :1;
+} _Annotation__isset;
+
+/**
+ * An annotation is similar to a log statement. It includes a host field which
+ * allows these events to be attributed properly, and also aggregatable.
+ */
+class Annotation : public virtual ::apache::thrift::TBase {
+ public:
+
+ Annotation(const Annotation&);
+ Annotation& operator=(const Annotation&);
+ Annotation() : timestamp(0), value() {
+ }
+
+ virtual ~Annotation() noexcept;
+ /**
+ * Microseconds from epoch.
+ *
+ * This value should use the most precise value possible. For example,
+ * gettimeofday or syncing nanoTime against a tick of currentTimeMillis.
+ */
+ int64_t timestamp;
+ std::string value;
+ /**
+ * Always the host that recorded the event. By specifying the host you allow
+ * rollup of all events (such as client requests to a service) by IP address.
+ */
+ Endpoint host;
+
+ _Annotation__isset __isset;
+
+ void __set_timestamp(const int64_t val);
+
+ void __set_value(const std::string& val);
+
+ void __set_host(const Endpoint& val);
+
+ bool operator == (const Annotation & rhs) const
+ {
+ if (!(timestamp == rhs.timestamp))
+ return false;
+ if (!(value == rhs.value))
+ return false;
+ if (__isset.host != rhs.__isset.host)
+ return false;
+ else if (__isset.host && !(host == rhs.host))
+ return false;
+ return true;
+ }
+ bool operator != (const Annotation &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Annotation & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Annotation &a, Annotation &b);
+
+std::ostream& operator<<(std::ostream& out, const Annotation& obj);
+
+typedef struct _BinaryAnnotation__isset {
+ _BinaryAnnotation__isset() : key(false), value(false), annotation_type(false), host(false) {}
+ bool key :1;
+ bool value :1;
+ bool annotation_type :1;
+ bool host :1;
+} _BinaryAnnotation__isset;
+
+/**
+ * Binary annotations are tags applied to a Span to give it context. For
+ * example, a binary annotation of "http.uri" could the path to a resource in a
+ * RPC call.
+ *
+ * Binary annotations of type STRING are always queryable, though more a
+ * historical implementation detail than a structural concern.
+ *
+ * Binary annotations can repeat, and vary on the host. Similar to Annotation,
+ * the host indicates who logged the event. This allows you to tell the
+ * difference between the client and server side of the same key. For example,
+ * the key "http.uri" might be different on the client and server side due to
+ * rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field,
+ * you can see the different points of view, which often help in debugging.
+ */
+class BinaryAnnotation : public virtual ::apache::thrift::TBase {
+ public:
+
+ BinaryAnnotation(const BinaryAnnotation&);
+ BinaryAnnotation& operator=(const BinaryAnnotation&);
+ BinaryAnnotation() : key(), value(), annotation_type((AnnotationType::type)0) {
+ }
+
+ virtual ~BinaryAnnotation() noexcept;
+ std::string key;
+ std::string value;
+ /**
+ *
+ * @see AnnotationType
+ */
+ AnnotationType::type annotation_type;
+ /**
+ * The host that recorded tag, which allows you to differentiate between
+ * multiple tags with the same key. There are two exceptions to this.
+ *
+ * When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or
+ * destination of an RPC. This exception allows zipkin to display network
+ * context of uninstrumented services, or clients such as web browsers.
+ */
+ Endpoint host;
+
+ _BinaryAnnotation__isset __isset;
+
+ void __set_key(const std::string& val);
+
+ void __set_value(const std::string& val);
+
+ void __set_annotation_type(const AnnotationType::type val);
+
+ void __set_host(const Endpoint& val);
+
+ bool operator == (const BinaryAnnotation & rhs) const
+ {
+ if (!(key == rhs.key))
+ return false;
+ if (!(value == rhs.value))
+ return false;
+ if (!(annotation_type == rhs.annotation_type))
+ return false;
+ if (__isset.host != rhs.__isset.host)
+ return false;
+ else if (__isset.host && !(host == rhs.host))
+ return false;
+ return true;
+ }
+ bool operator != (const BinaryAnnotation &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const BinaryAnnotation & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(BinaryAnnotation &a, BinaryAnnotation &b);
+
+std::ostream& operator<<(std::ostream& out, const BinaryAnnotation& obj);
+
+typedef struct _Span__isset {
+ _Span__isset() : trace_id(false), name(false), id(false), parent_id(false), annotations(false), binary_annotations(false), debug(true), timestamp(false), duration(false), trace_id_high(false) {}
+ bool trace_id :1;
+ bool name :1;
+ bool id :1;
+ bool parent_id :1;
+ bool annotations :1;
+ bool binary_annotations :1;
+ bool debug :1;
+ bool timestamp :1;
+ bool duration :1;
+ bool trace_id_high :1;
+} _Span__isset;
+
+/**
+ * A trace is a series of spans (often RPC calls) which form a latency tree.
+ *
+ * The root span is where trace_id = id and parent_id = Nil. The root span is
+ * usually the longest interval in the trace, starting with a SERVER_RECV
+ * annotation and ending with a SERVER_SEND.
+ */
+class Span : public virtual ::apache::thrift::TBase {
+ public:
+
+ Span(const Span&);
+ Span& operator=(const Span&);
+ Span() : trace_id(0), name(), id(0), parent_id(0), debug(false), timestamp(0), duration(0), trace_id_high(0) {
+ }
+
+ virtual ~Span() noexcept;
+ int64_t trace_id;
+ /**
+ * Span name in lowercase, rpc method for example
+ *
+ * Conventionally, when the span name isn't known, name = "unknown".
+ */
+ std::string name;
+ int64_t id;
+ int64_t parent_id;
+ std::vector<Annotation> annotations;
+ std::vector<BinaryAnnotation> binary_annotations;
+ bool debug;
+ /**
+ * Microseconds from epoch of the creation of this span.
+ *
+ * This value should be set directly by instrumentation, using the most
+ * precise value possible. For example, gettimeofday or syncing nanoTime
+ * against a tick of currentTimeMillis.
+ *
+ * For compatibility with instrumentation that precede this field, collectors
+ * or span stores can derive this via Annotation.timestamp.
+ * For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp.
+ *
+ * This field is optional for compatibility with old data: first-party span
+ * stores are expected to support this at time of introduction.
+ */
+ int64_t timestamp;
+ /**
+ * Measurement of duration in microseconds, used to support queries.
+ *
+ * This value should be set directly, where possible. Doing so encourages
+ * precise measurement decoupled from problems of clocks, such as skew or NTP
+ * updates causing time to move backwards.
+ *
+ * For compatibility with instrumentation that precede this field, collectors
+ * or span stores can derive this by subtracting Annotation.timestamp.
+ * For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp.
+ *
+ * If this field is persisted as unset, zipkin will continue to work, except
+ * duration query support will be implementation-specific. Similarly, setting
+ * this field non-atomically is implementation-specific.
+ *
+ * This field is i64 vs i32 to support spans longer than 35 minutes.
+ */
+ int64_t duration;
+ /**
+ * Optional unique 8-byte additional identifier for a trace. If non zero, this
+ * means the trace uses 128 bit traceIds instead of 64 bit.
+ */
+ int64_t trace_id_high;
+
+ _Span__isset __isset;
+
+ void __set_trace_id(const int64_t val);
+
+ void __set_name(const std::string& val);
+
+ void __set_id(const int64_t val);
+
+ void __set_parent_id(const int64_t val);
+
+ void __set_annotations(const std::vector<Annotation> & val);
+
+ void __set_binary_annotations(const std::vector<BinaryAnnotation> & val);
+
+ void __set_debug(const bool val);
+
+ void __set_timestamp(const int64_t val);
+
+ void __set_duration(const int64_t val);
+
+ void __set_trace_id_high(const int64_t val);
+
+ bool operator == (const Span & rhs) const
+ {
+ if (!(trace_id == rhs.trace_id))
+ return false;
+ if (!(name == rhs.name))
+ return false;
+ if (!(id == rhs.id))
+ return false;
+ if (__isset.parent_id != rhs.__isset.parent_id)
+ return false;
+ else if (__isset.parent_id && !(parent_id == rhs.parent_id))
+ return false;
+ if (!(annotations == rhs.annotations))
+ return false;
+ if (!(binary_annotations == rhs.binary_annotations))
+ return false;
+ if (__isset.debug != rhs.__isset.debug)
+ return false;
+ else if (__isset.debug && !(debug == rhs.debug))
+ return false;
+ if (__isset.timestamp != rhs.__isset.timestamp)
+ return false;
+ else if (__isset.timestamp && !(timestamp == rhs.timestamp))
+ return false;
+ if (__isset.duration != rhs.__isset.duration)
+ return false;
+ else if (__isset.duration && !(duration == rhs.duration))
+ return false;
+ if (__isset.trace_id_high != rhs.__isset.trace_id_high)
+ return false;
+ else if (__isset.trace_id_high && !(trace_id_high == rhs.trace_id_high))
+ return false;
+ return true;
+ }
+ bool operator != (const Span &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Span & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Span &a, Span &b);
+
+std::ostream& operator<<(std::ostream& out, const Span& obj);
+
+
+class Response : public virtual ::apache::thrift::TBase {
+ public:
+
+ Response(const Response&);
+ Response& operator=(const Response&);
+ Response() : ok(0) {
+ }
+
+ virtual ~Response() noexcept;
+ bool ok;
+
+ void __set_ok(const bool val);
+
+ bool operator == (const Response & rhs) const
+ {
+ if (!(ok == rhs.ok))
+ return false;
+ return true;
+ }
+ bool operator != (const Response &rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator < (const Response & ) const;
+
+ uint32_t read(::apache::thrift::protocol::TProtocol* iprot);
+ uint32_t write(::apache::thrift::protocol::TProtocol* oprot) const;
+
+ virtual void printTo(std::ostream& out) const;
+};
+
+void swap(Response &a, Response &b);
+
+std::ostream& operator<<(std::ostream& out, const Response& obj);
+
+}}} // namespace
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/memory/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/memory/BUILD
new file mode 100644
index 000000000..b2dd48347
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/memory/BUILD
@@ -0,0 +1,57 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "in_memory_span_data",
+ hdrs = [
+ "include/opentelemetry/exporters/memory/in_memory_span_data.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["memory"],
+ deps = [
+ "//api",
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ ],
+)
+
+cc_test(
+ name = "in_memory_span_data_test",
+ srcs = ["test/in_memory_span_data_test.cc"],
+ tags = [
+ "memory",
+ "test",
+ ],
+ deps = [
+ ":in_memory_span_data",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "in_memory_span_exporter",
+ hdrs = [
+ "include/opentelemetry/exporters/memory/in_memory_span_exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = [
+ "memory",
+ "test",
+ ],
+ deps = [
+ ":in_memory_span_data",
+ "//sdk/src/trace",
+ ],
+)
+
+cc_test(
+ name = "in_memory_span_exporter_test",
+ srcs = ["test/in_memory_span_exporter_test.cc"],
+ tags = [
+ "memory",
+ "test",
+ ],
+ deps = [
+ ":in_memory_span_exporter",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/memory/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/memory/CMakeLists.txt
new file mode 100644
index 000000000..d489ec160
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/memory/CMakeLists.txt
@@ -0,0 +1,46 @@
+add_library(opentelemetry_exporter_in_memory INTERFACE)
+
+target_include_directories(
+ opentelemetry_exporter_in_memory
+ INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+set_target_properties(opentelemetry_exporter_in_memory
+ PROPERTIES EXPORT_NAME in_memory_span_exporter)
+
+install(
+ TARGETS opentelemetry_exporter_in_memory
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/memory
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h")
+
+if(BUILD_TESTING)
+ add_executable(in_memory_span_data_test test/in_memory_span_data_test.cc)
+ add_executable(in_memory_span_exporter_test
+ test/in_memory_span_exporter_test.cc)
+
+ target_link_libraries(
+ in_memory_span_data_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_exporter_in_memory opentelemetry_resources)
+
+ target_link_libraries(
+ in_memory_span_exporter_test ${GTEST_BOTH_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT} opentelemetry_exporter_in_memory
+ opentelemetry_resources)
+
+ gtest_add_tests(
+ TARGET in_memory_span_data_test
+ TEST_PREFIX exporter.
+ TEST_LIST in_memory_span_data_test)
+ gtest_add_tests(
+ TARGET in_memory_span_exporter_test
+ TEST_PREFIX exporter.
+ TEST_LIST in_memory_span_exporter_test)
+endif()
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h b/src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h
new file mode 100644
index 000000000..df6d377b9
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_data.h
@@ -0,0 +1,74 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/sdk/common/circular_buffer.h"
+#include "opentelemetry/sdk/trace/recordable.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+
+#include <vector>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace memory
+{
+/**
+ * A wrapper class holding in memory exporter data
+ */
+class InMemorySpanData final
+{
+public:
+ /**
+ * @param buffer_size a required value that sets the size of the CircularBuffer
+ */
+ InMemorySpanData(size_t buffer_size) : spans_received_(buffer_size) {}
+
+ /**
+ * @param data a required unique pointer to the data to add to the CircularBuffer
+ */
+ void Add(std::unique_ptr<opentelemetry::sdk::trace::SpanData> data) noexcept
+ {
+ std::unique_ptr<opentelemetry::sdk::trace::SpanData> span_data(
+ static_cast<opentelemetry::sdk::trace::SpanData *>(data.release()));
+ spans_received_.Add(span_data);
+ }
+
+ /**
+ * @return Returns a vector of unique pointers containing all the span data in the
+ * CircularBuffer. This operation will empty the Buffer, which is why the data
+ * is returned as unique pointers
+ */
+ std::vector<std::unique_ptr<opentelemetry::sdk::trace::SpanData>> GetSpans() noexcept
+ {
+ std::vector<std::unique_ptr<opentelemetry::sdk::trace::SpanData>> res;
+
+ // Pointer swap is required because the Consume function requires that the
+ // AtomicUniquePointer be set to null
+ spans_received_.Consume(
+ spans_received_.size(),
+ [&](opentelemetry::sdk::common::CircularBufferRange<
+ opentelemetry::sdk::common::AtomicUniquePtr<opentelemetry::sdk::trace::SpanData>>
+ range) noexcept {
+ range.ForEach(
+ [&](opentelemetry::sdk::common::AtomicUniquePtr<opentelemetry::sdk::trace::SpanData>
+ &ptr) noexcept {
+ std::unique_ptr<opentelemetry::sdk::trace::SpanData> swap_ptr =
+ std::unique_ptr<opentelemetry::sdk::trace::SpanData>(nullptr);
+ ptr.Swap(swap_ptr);
+ res.push_back(
+ std::unique_ptr<opentelemetry::sdk::trace::SpanData>(swap_ptr.release()));
+ return true;
+ });
+ });
+
+ return res;
+ }
+
+private:
+ opentelemetry::sdk::common::CircularBuffer<opentelemetry::sdk::trace::SpanData> spans_received_;
+};
+} // namespace memory
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h
new file mode 100644
index 000000000..28b7bc34e
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/memory/include/opentelemetry/exporters/memory/in_memory_span_exporter.h
@@ -0,0 +1,100 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#include <mutex>
+#include "opentelemetry/common/spin_lock_mutex.h"
+#include "opentelemetry/exporters/memory/in_memory_span_data.h"
+#include "opentelemetry/sdk/trace/exporter.h"
+#include "opentelemetry/sdk_config.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace memory
+{
+const size_t MAX_BUFFER_SIZE = 100;
+
+/**
+ * A in memory exporter that switches a flag once a valid recordable was received
+ * and keeps track of all received spans in memory.
+ */
+class InMemorySpanExporter final : public opentelemetry::sdk::trace::SpanExporter
+{
+public:
+ /**
+ * @param buffer_size an optional value that sets the size of the InMemorySpanData
+ */
+ InMemorySpanExporter(size_t buffer_size = MAX_BUFFER_SIZE)
+ : data_(new opentelemetry::exporter::memory::InMemorySpanData(buffer_size))
+ {}
+
+ /**
+ * @return Returns a unique pointer to an empty recordable object
+ */
+ std::unique_ptr<sdk::trace::Recordable> MakeRecordable() noexcept override
+ {
+ return std::unique_ptr<sdk::trace::Recordable>(new sdk::trace::SpanData());
+ }
+
+ /**
+ * @param recordables a required span containing unique pointers to the data
+ * to add to the InMemorySpanData
+ * @return Returns the result of the operation
+ */
+ sdk::common::ExportResult Export(
+ const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &recordables) noexcept override
+ {
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[In Memory Span Exporter] Exporting "
+ << recordables.size() << " span(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+ for (auto &recordable : recordables)
+ {
+ auto span = std::unique_ptr<sdk::trace::SpanData>(
+ static_cast<sdk::trace::SpanData *>(recordable.release()));
+ if (span != nullptr)
+ {
+ data_->Add(std::move(span));
+ }
+ }
+
+ return sdk::common::ExportResult::kSuccess;
+ }
+
+ /**
+ * @param timeout an optional value containing the timeout of the exporter
+ * note: passing custom timeout values is not currently supported for this exporter
+ * @return Returns the status of the operation
+ */
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override
+ {
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+ };
+
+ /**
+ * @return Returns a shared pointer to this exporters InMemorySpanData
+ */
+ std::shared_ptr<opentelemetry::exporter::memory::InMemorySpanData> GetData() noexcept
+ {
+ return data_;
+ }
+
+private:
+ std::shared_ptr<opentelemetry::exporter::memory::InMemorySpanData> data_;
+ bool is_shutdown_ = false;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ const bool isShutdown() const noexcept
+ {
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+ }
+};
+} // namespace memory
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_data_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_data_test.cc
new file mode 100644
index 000000000..bc1955026
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_data_test.cc
@@ -0,0 +1,28 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/memory/in_memory_span_data.h"
+#include "opentelemetry/nostd/span.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+
+#include <gtest/gtest.h>
+
+using opentelemetry::exporter::memory::InMemorySpanData;
+using opentelemetry::sdk::trace::Recordable;
+using opentelemetry::sdk::trace::SpanData;
+
+TEST(InMemorySpanData, AddRecordable)
+{
+ InMemorySpanData data(100);
+
+ ASSERT_EQ(0, data.GetSpans().size());
+
+ std::unique_ptr<SpanData> spandata(new SpanData());
+
+ data.Add(std::move(spandata));
+
+ // Consumes all spans in exporter
+ ASSERT_EQ(1, data.GetSpans().size());
+
+ ASSERT_EQ(0, data.GetSpans().size());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_exporter_test.cc
new file mode 100644
index 000000000..56a0ef779
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/memory/test/in_memory_span_exporter_test.cc
@@ -0,0 +1,29 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/memory/in_memory_span_exporter.h"
+#include "opentelemetry/nostd/span.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+
+#include <gtest/gtest.h>
+
+using opentelemetry::exporter::memory::InMemorySpanExporter;
+using opentelemetry::sdk::trace::Recordable;
+using opentelemetry::sdk::trace::SpanData;
+
+TEST(InMemorySpanExporter, ExportBatch)
+{
+ InMemorySpanExporter exporter;
+
+ ASSERT_EQ(0, exporter.GetData().get()->GetSpans().size());
+
+ std::unique_ptr<Recordable> spandata(new SpanData());
+ opentelemetry::nostd::span<std::unique_ptr<Recordable>> batch(&spandata, 1);
+
+ exporter.Export(batch);
+
+ ASSERT_EQ(1, exporter.GetData().get()->GetSpans().size());
+
+ // Consumes all spans in exporter
+ ASSERT_EQ(0, exporter.GetData().get()->GetSpans().size());
+}
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/BUILD
new file mode 100644
index 000000000..917b70450
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/BUILD
@@ -0,0 +1,131 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "ostream_log_exporter",
+ srcs = [
+ "src/log_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/ostream/common_utils.h",
+ "include/opentelemetry/exporters/ostream/log_exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["ostream"],
+ deps = [
+ "//sdk/src/logs",
+ ],
+)
+
+cc_test(
+ name = "ostream_log_test",
+ srcs = ["test/ostream_log_test.cc"],
+ tags = [
+ "ostream",
+ "test",
+ ],
+ deps = [
+ ":ostream_log_exporter",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "ostream_metrics_exporter_deprecated",
+ srcs = [
+ "src/metrics_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/ostream/metrics_exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["ostream"],
+ deps = [
+ "//sdk/src/_metrics:metrics_deprecated",
+ ],
+)
+
+cc_library(
+ name = "ostream_metric_exporter",
+ srcs = [
+ "src/metric_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/ostream/common_utils.h",
+ "include/opentelemetry/exporters/ostream/metric_exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = [
+ "metrics",
+ "ostream",
+ ],
+ deps = [
+ "//sdk/src/metrics",
+ ],
+)
+
+cc_test(
+ name = "ostream_metric_test",
+ srcs = ["test/ostream_metric_test.cc"],
+ tags = [
+ "ostream",
+ "test",
+ ],
+ deps = [
+ ":ostream_metric_exporter",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "ostream_metrics_test_deprecated",
+ srcs = ["test/ostream_metrics_test.cc"],
+ tags = [
+ "ostream",
+ "test",
+ ],
+ deps = [
+ ":ostream_metrics_exporter_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "ostream_span_exporter",
+ srcs = [
+ "src/span_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/ostream/common_utils.h",
+ "include/opentelemetry/exporters/ostream/span_exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["ostream"],
+ deps = [
+ "//sdk/src/trace",
+ ],
+)
+
+cc_library(
+ name = "ostream_capture",
+ hdrs = [
+ "test/ostream_capture.h",
+ ],
+ tags = ["ostream"],
+ deps = [
+ "//api",
+ ],
+)
+
+cc_test(
+ name = "ostream_span_test",
+ srcs = ["test/ostream_span_test.cc"],
+ tags = [
+ "ostream",
+ "test",
+ ],
+ deps = [
+ ":ostream_capture",
+ ":ostream_span_exporter",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/CMakeLists.txt
new file mode 100644
index 000000000..db2562ddd
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/CMakeLists.txt
@@ -0,0 +1,125 @@
+add_library(opentelemetry_exporter_ostream_span src/span_exporter.cc)
+
+set_target_properties(opentelemetry_exporter_ostream_span
+ PROPERTIES EXPORT_NAME ostream_span_exporter)
+
+target_include_directories(
+ opentelemetry_exporter_ostream_span
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>")
+
+target_link_libraries(opentelemetry_exporter_ostream_span
+ PUBLIC opentelemetry_trace)
+
+install(
+ TARGETS opentelemetry_exporter_ostream_span
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/ostream
+ DESTINATION include/opentelemetry/exporters
+ PATTERN "*.h"
+ PATTERN "metrics_exporter.h" EXCLUDE
+ PATTERN "log_Exporter.h" EXCLUDE)
+
+if(BUILD_TESTING)
+ add_executable(ostream_span_test test/ostream_span_test.cc)
+ target_link_libraries(ostream_span_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_ostream_span)
+ gtest_add_tests(
+ TARGET ostream_span_test
+ TEST_PREFIX exporter.
+ TEST_LIST ostream_span_test)
+endif() # BUILD_TESTING
+
+if(WITH_METRICS_PREVIEW)
+ add_library(opentelemetry_exporter_ostream_metrics_deprecated
+ src/metrics_exporter.cc)
+ set_target_properties(opentelemetry_exporter_ostream_metrics_deprecated
+ PROPERTIES EXPORT_NAME ostream_metrics_exporter)
+ target_include_directories(
+ opentelemetry_exporter_ostream_metrics_deprecated
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>")
+ target_link_libraries(opentelemetry_exporter_ostream_metrics_deprecated
+ PUBLIC opentelemetry_metrics_deprecated)
+ install(
+ TARGETS opentelemetry_exporter_ostream_metrics_deprecated
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ install(
+ DIRECTORY include/opentelemetry/exporters/ostream
+ DESTINATION include/opentelemetry/exporters
+ PATTERN "metrics_exporter.h")
+ if(BUILD_TESTING)
+ add_executable(ostream_metrics_test test/ostream_metrics_test.cc)
+ target_link_libraries(ostream_metrics_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_ostream_metrics_deprecated)
+ gtest_add_tests(
+ TARGET ostream_metrics_test
+ TEST_PREFIX exporter.
+ TEST_LIST ostream_metrics_test)
+ endif()
+else()
+ add_library(opentelemetry_exporter_ostream_metrics src/metric_exporter.cc)
+ set_target_properties(opentelemetry_exporter_ostream_metrics
+ PROPERTIES EXPORT_NAME ostream_metrics_exporter)
+ target_include_directories(
+ opentelemetry_exporter_ostream_metrics
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>")
+ target_link_libraries(opentelemetry_exporter_ostream_metrics
+ PUBLIC opentelemetry_metrics)
+ install(
+ TARGETS opentelemetry_exporter_ostream_metrics
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ install(
+ DIRECTORY include/opentelemetry/exporters/ostream
+ DESTINATION include/opentelemetry/exporters
+ PATTERN "metric_exporter.h")
+ if(BUILD_TESTING)
+ add_executable(ostream_metric_test test/ostream_metric_test.cc)
+ target_link_libraries(
+ ostream_metric_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_ostream_metrics opentelemetry_resources)
+ gtest_add_tests(
+ TARGET ostream_metric_test
+ TEST_PREFIX exporter.
+ TEST_LIST ostream_metric_test)
+ endif()
+endif()
+
+if(WITH_LOGS_PREVIEW)
+ add_library(opentelemetry_exporter_ostream_logs src/log_exporter.cc)
+ set_target_properties(opentelemetry_exporter_ostream_logs
+ PROPERTIES EXPORT_NAME ostream_log_exporter)
+ target_include_directories(
+ opentelemetry_exporter_ostream_logs
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>")
+ target_link_libraries(opentelemetry_exporter_ostream_logs
+ PUBLIC opentelemetry_logs)
+ install(
+ TARGETS opentelemetry_exporter_ostream_logs
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ install(
+ DIRECTORY include/opentelemetry/exporters/ostream
+ DESTINATION include/opentelemetry/exporters
+ PATTERN "log_exporter.h")
+ if(BUILD_TESTING)
+ add_executable(ostream_log_test test/ostream_log_test.cc)
+ target_link_libraries(ostream_log_test ${GTEST_BOTH_LIBRARIES}
+ opentelemetry_exporter_ostream_logs)
+ gtest_add_tests(
+ TARGET ostream_log_test
+ TEST_PREFIX exporter.
+ TEST_LIST ostream_log_test)
+ endif()
+endif()
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/common_utils.h b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/common_utils.h
new file mode 100644
index 000000000..cfebfe8fc
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/common_utils.h
@@ -0,0 +1,80 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include "opentelemetry/nostd/variant.h"
+#include "opentelemetry/sdk/common/attribute_utils.h"
+
+#pragma once
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace ostream_common
+{
+/*
+ print_value is used to print out the value of an attribute within a vector.
+ These values are held in a variant which makes the process of printing them much more
+ complicated.
+*/
+
+template <typename T>
+void print_value(const T &item, std::ostream &sout)
+{
+ sout << item;
+}
+
+template <typename T>
+void print_value(const std::vector<T> &vec, std::ostream &sout)
+{
+ sout << '[';
+ size_t i = 1;
+ size_t sz = vec.size();
+ for (auto v : vec)
+ {
+ sout << v;
+ if (i != sz)
+ sout << ',';
+ i++;
+ };
+ sout << ']';
+}
+
+// Prior to C++14, generic lambda is not available so fallback to functor.
+#if __cplusplus < 201402L
+
+class OwnedAttributeValueVisitor
+{
+public:
+ OwnedAttributeValueVisitor(std::ostream &sout) : sout_(sout) {}
+
+ template <typename T>
+ void operator()(T &&arg)
+ {
+ print_value(arg, sout_);
+ }
+
+private:
+ std::ostream &sout_;
+};
+
+#endif
+
+void print_value(const opentelemetry::sdk::common::OwnedAttributeValue &value, std::ostream &sout)
+{
+#if __cplusplus < 201402L
+ opentelemetry::nostd::visit(OwnedAttributeValueVisitor(sout), value);
+#else
+ opentelemetry::nostd::visit(
+ [&sout](auto &&arg) {
+ /* explicit this is needed by some gcc versions (observed with v5.4.0)*/
+ print_value(arg, sout);
+ },
+ value);
+#endif
+}
+
+} // namespace ostream_common
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE \ No newline at end of file
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h
new file mode 100644
index 000000000..2f6acbb48
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/log_exporter.h
@@ -0,0 +1,62 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include "opentelemetry/common/spin_lock_mutex.h"
+# include "opentelemetry/nostd/type_traits.h"
+# include "opentelemetry/sdk/logs/exporter.h"
+# include "opentelemetry/sdk/logs/log_record.h"
+# include "opentelemetry/version.h"
+
+# include <iostream>
+# include <sstream>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace logs
+{
+/**
+ * The OStreamLogExporter exports logs through an ostream (default set to std::cout)
+ */
+class OStreamLogExporter final : public opentelemetry::sdk::logs::LogExporter
+{
+public:
+ /**
+ * Create an OStreamLogExporter. This constructor takes in a reference to an ostream that the
+ * Export() method will send log data into. The default ostream is set to stdout.
+ */
+ explicit OStreamLogExporter(std::ostream &sout = std::cout) noexcept;
+
+ std::unique_ptr<sdk::logs::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Exports a span of logs sent from the processor.
+ */
+ opentelemetry::sdk::common::ExportResult Export(
+ const opentelemetry::nostd::span<std::unique_ptr<sdk::logs::Recordable>> &records) noexcept
+ override;
+
+ /**
+ * Marks the OStream Log Exporter as shut down.
+ */
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;
+
+private:
+ // The OStream to send the logs to
+ std::ostream &sout_;
+ // Whether this exporter has been shut down
+ bool is_shutdown_ = false;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+ void printAttributes(
+ const std::unordered_map<std::string, opentelemetry::sdk::common::OwnedAttributeValue> &map,
+ const std::string prefix = "\n\t");
+};
+} // namespace logs
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h
new file mode 100644
index 000000000..e34332d77
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metric_exporter.h
@@ -0,0 +1,67 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifndef ENABLE_METRICS_PREVIEW
+
+# include <iostream>
+# include <string>
+# include "opentelemetry/common/spin_lock_mutex.h"
+# include "opentelemetry/sdk/metrics/data/metric_data.h"
+# include "opentelemetry/sdk/metrics/instruments.h"
+# include "opentelemetry/sdk/metrics/metric_exporter.h"
+# include "opentelemetry/version.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+
+/**
+ * The OStreamMetricExporter exports record data through an ostream
+ */
+class OStreamMetricExporter final : public opentelemetry::sdk::metrics::MetricExporter
+{
+public:
+ /**
+ * Create an OStreamMetricExporter. This constructor takes in a reference to an ostream that the
+ * export() function will send metrics data into.
+ * The default ostream is set to stdout
+ */
+ explicit OStreamMetricExporter(std::ostream &sout = std::cout) noexcept;
+
+ /**
+ * Export
+ * @param data metrics data
+ */
+ sdk::common::ExportResult Export(const sdk::metrics::ResourceMetrics &data) noexcept override;
+
+ /**
+ * Force flush the exporter.
+ */
+ bool ForceFlush(
+ std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) 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:
+ std::ostream &sout_;
+ bool is_shutdown_ = false;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+ void printInstrumentationInfoMetricData(
+ const sdk::metrics::InstrumentationInfoMetrics &info_metrics);
+ void printPointData(const opentelemetry::sdk::metrics::PointType &point_data);
+ void printPointAttributes(const opentelemetry::sdk::metrics::PointAttributes &point_attributes);
+};
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h
new file mode 100644
index 000000000..5ae168aa0
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/metrics_exporter.h
@@ -0,0 +1,166 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_METRICS_PREVIEW
+
+# include <iostream>
+# include <string>
+# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h"
+# include "opentelemetry/sdk/_metrics/exporter.h"
+# include "opentelemetry/sdk/_metrics/record.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+
+/**
+ * The OStreamMetricsExporter exports record data through an ostream
+ */
+class OStreamMetricsExporter final : public opentelemetry::sdk::metrics::MetricsExporter
+{
+public:
+ /**
+ * Create an OStreamMetricsExporter. This constructor takes in a reference to an ostream that the
+ * export() function will send span data into.
+ * The default ostream is set to stdout
+ */
+ explicit OStreamMetricsExporter(std::ostream &sout = std::cout) noexcept;
+
+ sdk::common::ExportResult Export(
+ const std::vector<opentelemetry::sdk::metrics::Record> &records) noexcept override;
+
+private:
+ std::ostream &sout_;
+
+ /**
+ * Send specific data from the given AggregatorVariant based on what AggregatorKind
+ * it is holding. Each Aggregator holds data differently, so each have their own
+ * custom printing.
+ */
+ template <typename T>
+ void PrintAggregatorVariant(opentelemetry::sdk::metrics::AggregatorVariant value)
+ {
+ auto agg = nostd::get<std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<T>>>(value);
+ auto aggKind = agg->get_aggregator_kind();
+
+ if (!agg)
+ return;
+ switch (aggKind)
+ {
+ case opentelemetry::sdk::metrics::AggregatorKind::Counter: {
+ sout_ << "\n sum : " << agg->get_checkpoint()[0];
+ }
+ break;
+ case opentelemetry::sdk::metrics::AggregatorKind::MinMaxSumCount: {
+ auto mmsc = agg->get_checkpoint();
+ sout_ << "\n min : " << mmsc[0] << "\n max : " << mmsc[1]
+ << "\n sum : " << mmsc[2] << "\n count : " << mmsc[3];
+ }
+ break;
+ case opentelemetry::sdk::metrics::AggregatorKind::Gauge: {
+ auto timestamp = agg->get_checkpoint_timestamp();
+
+ sout_ << "\n last value : " << agg->get_checkpoint()[0]
+ << "\n timestamp : " << std::to_string(timestamp.time_since_epoch().count());
+ }
+ break;
+ case opentelemetry::sdk::metrics::AggregatorKind::Exact: {
+ // TODO: Find better way to print quantiles
+ if (agg->get_quant_estimation())
+ {
+ sout_ << "\n quantiles : "
+ << "[0: " << agg->get_quantiles(0) << ", "
+ << ".25: " << agg->get_quantiles(.25) << ", "
+ << ".50: " << agg->get_quantiles(.50) << ", "
+ << ".75: " << agg->get_quantiles(.75) << ", "
+ << "1: " << agg->get_quantiles(1) << ']';
+ }
+ else
+ {
+ auto vec = agg->get_checkpoint();
+ size_t size = vec.size();
+ size_t i = 1;
+
+ sout_ << "\n values : " << '[';
+
+ for (auto val : vec)
+ {
+ sout_ << val;
+ if (i != size)
+ sout_ << ", ";
+ i++;
+ }
+ sout_ << ']';
+ }
+ }
+ break;
+ case opentelemetry::sdk::metrics::AggregatorKind::Histogram: {
+ auto boundaries = agg->get_boundaries();
+ auto counts = agg->get_counts();
+
+ size_t boundaries_size = boundaries.size();
+ size_t counts_size = counts.size();
+
+ sout_ << "\n buckets : " << '[';
+
+ for (size_t i = 0; i < boundaries_size; i++)
+ {
+ sout_ << boundaries[i];
+
+ if (i != boundaries_size - 1)
+ sout_ << ", ";
+ }
+ sout_ << ']';
+
+ sout_ << "\n counts : " << '[';
+ for (size_t i = 0; i < counts_size; i++)
+ {
+ sout_ << counts[i];
+
+ if (i != counts_size - 1)
+ sout_ << ", ";
+ }
+ sout_ << ']';
+ }
+ break;
+ case opentelemetry::sdk::metrics::AggregatorKind::Sketch: {
+ auto boundaries = agg->get_boundaries();
+ auto counts = agg->get_counts();
+
+ size_t boundaries_size = boundaries.size();
+ size_t counts_size = counts.size();
+
+ sout_ << "\n buckets : " << '[';
+
+ for (size_t i = 0; i < boundaries_size; i++)
+ {
+ sout_ << boundaries[i];
+
+ if (i != boundaries_size - 1)
+ sout_ << ", ";
+ }
+ sout_ << ']';
+
+ sout_ << "\n counts : " << '[';
+ for (size_t i = 0; i < counts_size; i++)
+ {
+ sout_ << counts[i];
+
+ if (i != counts_size - 1)
+ sout_ << ", ";
+ }
+ sout_ << ']';
+ }
+ break;
+ }
+ }
+};
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h
new file mode 100644
index 000000000..c8603db02
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/include/opentelemetry/exporters/ostream/span_exporter.h
@@ -0,0 +1,70 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/common/spin_lock_mutex.h"
+#include "opentelemetry/nostd/type_traits.h"
+#include "opentelemetry/sdk/trace/exporter.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/version.h"
+
+#include <iostream>
+#include <map>
+#include <sstream>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace trace
+{
+
+/**
+ * The OStreamSpanExporter exports span data through an ostream
+ */
+class OStreamSpanExporter final : public opentelemetry::sdk::trace::SpanExporter
+{
+public:
+ /**
+ * Create an OStreamSpanExporter. This constructor takes in a reference to an ostream that the
+ * export() function will send span data into.
+ * The default ostream is set to stdout
+ */
+ explicit OStreamSpanExporter(std::ostream &sout = std::cout) noexcept;
+
+ std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override;
+
+ sdk::common::ExportResult Export(
+ const opentelemetry::nostd::span<std::unique_ptr<opentelemetry::sdk::trace::Recordable>>
+ &spans) noexcept override;
+
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;
+
+private:
+ std::ostream &sout_;
+ bool is_shutdown_ = false;
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+
+ // Mapping status number to the string from api/include/opentelemetry/trace/canonical_code.h
+ std::map<int, std::string> statusMap{{0, "Unset"}, {1, "Ok"}, {2, "Error"}};
+
+ // various print helpers
+ void printAttributes(
+ const std::unordered_map<std::string, opentelemetry::sdk::common::OwnedAttributeValue> &map,
+ const std::string prefix = "\n\t");
+
+ void printEvents(const std::vector<opentelemetry::sdk::trace::SpanDataEvent> &events);
+
+ void printLinks(const std::vector<opentelemetry::sdk::trace::SpanDataLink> &links);
+
+ void printResources(const opentelemetry::sdk::resource::Resource &resources);
+
+ void printInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library);
+};
+} // namespace trace
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/log_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/log_exporter.cc
new file mode 100644
index 000000000..e6ddd4c9f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/log_exporter.cc
@@ -0,0 +1,132 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+# include "opentelemetry/exporters/ostream/log_exporter.h"
+# include <mutex>
+# include "opentelemetry/exporters/ostream/common_utils.h"
+# include "opentelemetry/sdk_config.h"
+
+# include <iostream>
+# include <type_traits>
+
+namespace nostd = opentelemetry::nostd;
+namespace sdklogs = opentelemetry::sdk::logs;
+namespace sdkcommon = opentelemetry::sdk::common;
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace logs
+{
+
+/*********************** Constructor ***********************/
+
+OStreamLogExporter::OStreamLogExporter(std::ostream &sout) noexcept : sout_(sout) {}
+
+/*********************** Exporter methods ***********************/
+
+std::unique_ptr<sdklogs::Recordable> OStreamLogExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<sdklogs::Recordable>(new sdklogs::LogRecord());
+}
+
+sdk::common::ExportResult OStreamLogExporter::Export(
+ const nostd::span<std::unique_ptr<sdklogs::Recordable>> &records) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[Ostream Log Exporter] Exporting "
+ << records.size() << " log(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+
+ for (auto &record : records)
+ {
+ // Convert recordable to a LogRecord so that the getters of the LogRecord can be used
+ auto log_record =
+ std::unique_ptr<sdklogs::LogRecord>(static_cast<sdklogs::LogRecord *>(record.release()));
+
+ if (log_record == nullptr)
+ {
+ // TODO: Log Internal SDK error "recordable data was lost"
+ continue;
+ }
+
+ // Convert trace, spanid, traceflags into exportable representation
+ constexpr int trace_id_len = 32;
+ constexpr int span_id__len = 16;
+ constexpr int trace_flags_len = 2;
+
+ char trace_id[trace_id_len] = {0};
+ char span_id[span_id__len] = {0};
+ char trace_flags[trace_flags_len] = {0};
+
+ log_record->GetTraceId().ToLowerBase16(trace_id);
+ log_record->GetSpanId().ToLowerBase16(span_id);
+ log_record->GetTraceFlags().ToLowerBase16(trace_flags);
+
+ // Print out each field of the log record, noting that severity is separated
+ // into severity_num and severity_text
+ sout_ << "{\n"
+ << " timestamp : " << log_record->GetTimestamp().time_since_epoch().count() << "\n"
+ << " severity_num : " << static_cast<std::uint32_t>(log_record->GetSeverity()) << "\n"
+ << " severity_text : ";
+
+ std::uint32_t severity_index = static_cast<std::uint32_t>(log_record->GetSeverity());
+ if (severity_index >= std::extent<decltype(opentelemetry::logs::SeverityNumToText)>::value)
+ {
+ sout_ << "Invalid severity(" << severity_index << ")\n";
+ }
+ else
+ {
+ sout_ << opentelemetry::logs::SeverityNumToText[severity_index] << "\n";
+ }
+
+ sout_ << " body : " << log_record->GetBody() << "\n"
+ << " resource : ";
+
+ printAttributes(log_record->GetResource().GetAttributes());
+
+ sout_ << "\n"
+ << " attributes : ";
+
+ printAttributes(log_record->GetAttributes());
+
+ sout_ << "\n"
+ << " trace_id : " << std::string(trace_id, trace_id_len) << "\n"
+ << " span_id : " << std::string(span_id, span_id__len) << "\n"
+ << " trace_flags : " << std::string(trace_flags, trace_flags_len) << "\n"
+ << "}\n";
+ }
+
+ return sdk::common::ExportResult::kSuccess;
+}
+
+bool OStreamLogExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool OStreamLogExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+void OStreamLogExporter::printAttributes(
+ const std::unordered_map<std::string, sdkcommon::OwnedAttributeValue> &map,
+ const std::string prefix)
+{
+ for (const auto &kv : map)
+ {
+ sout_ << prefix << kv.first << ": ";
+ opentelemetry::exporter::ostream_common::print_value(kv.second, sout_);
+ }
+}
+
+} // namespace logs
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metric_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metric_exporter.cc
new file mode 100644
index 000000000..2e90e0845
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metric_exporter.cc
@@ -0,0 +1,214 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <chrono>
+#ifndef ENABLE_METRICS_PREVIEW
+# include <algorithm>
+# include "opentelemetry/exporters/ostream/common_utils.h"
+# include "opentelemetry/exporters/ostream/metric_exporter.h"
+# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h"
+# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h"
+# include "opentelemetry/sdk_config.h"
+
+namespace
+{
+std::string timeToString(opentelemetry::common::SystemTimestamp time_stamp)
+{
+ std::time_t epoch_time = std::chrono::system_clock::to_time_t(time_stamp);
+
+ struct tm *tm_ptr = nullptr;
+# if defined(_MSC_VER)
+ struct tm buf_tm;
+ if (!gmtime_s(&buf_tm, &epoch_time))
+ {
+ tm_ptr = &buf_tm;
+ }
+# else
+ tm_ptr = std::gmtime(&epoch_time);
+# endif
+
+ char buf[100];
+ char *date_str = nullptr;
+ if (tm_ptr == nullptr)
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OStream Metric] gmtime failed for " << epoch_time);
+ }
+ else if (std::strftime(buf, sizeof(buf), "%c", tm_ptr) > 0)
+ {
+ date_str = buf;
+ }
+ else
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OStream Metric] strftime failed for " << epoch_time);
+ }
+
+ return std::string{date_str};
+}
+} // namespace
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+
+template <typename Container>
+inline void printVec(std::ostream &os, Container &vec)
+{
+ using T = typename std::decay<decltype(*vec.begin())>::type;
+ os << '[';
+ if (vec.size() > 1)
+ {
+ std::copy(vec.begin(), vec.end(), std::ostream_iterator<T>(os, ", "));
+ }
+ os << ']';
+}
+
+OStreamMetricExporter::OStreamMetricExporter(std::ostream &sout) noexcept : sout_(sout) {}
+
+sdk::common::ExportResult OStreamMetricExporter::Export(
+ const sdk::metrics::ResourceMetrics &data) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[OStream Metric] Exporting "
+ << data.instrumentation_info_metric_data_.size()
+ << " records(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+
+ for (auto &record : data.instrumentation_info_metric_data_)
+ {
+ printInstrumentationInfoMetricData(record);
+ }
+ return sdk::common::ExportResult::kSuccess;
+}
+
+void OStreamMetricExporter::printInstrumentationInfoMetricData(
+ const sdk::metrics::InstrumentationInfoMetrics &info_metric)
+{
+ // sout_ is shared
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ sout_ << "{";
+ sout_ << "\n name\t\t: " << info_metric.instrumentation_library_->GetName()
+ << "\n schema url\t: " << info_metric.instrumentation_library_->GetSchemaURL()
+ << "\n version\t: " << info_metric.instrumentation_library_->GetVersion();
+ for (const auto &record : info_metric.metric_data_)
+ {
+ sout_ << "\n start time\t: " << timeToString(record.start_ts)
+ << "\n end time\t: " << timeToString(record.end_ts)
+ << "\n name\t\t: " << record.instrument_descriptor.name_
+ << "\n description\t: " << record.instrument_descriptor.description_
+ << "\n unit\t\t: " << record.instrument_descriptor.unit_;
+
+ for (const auto &pd : record.point_data_attr_)
+ {
+ if (!nostd::holds_alternative<sdk::metrics::DropPointData>(pd.point_data))
+ {
+ printPointData(pd.point_data);
+ printPointAttributes(pd.attributes);
+ }
+ }
+ }
+ sout_ << "\n}\n";
+}
+
+void OStreamMetricExporter::printPointData(const opentelemetry::sdk::metrics::PointType &point_data)
+{
+ if (nostd::holds_alternative<sdk::metrics::SumPointData>(point_data))
+ {
+ auto sum_point_data = nostd::get<sdk::metrics::SumPointData>(point_data);
+ sout_ << "\n type\t\t: SumPointData";
+ sout_ << "\n value\t\t: ";
+ if (nostd::holds_alternative<double>(sum_point_data.value_))
+ {
+ sout_ << nostd::get<double>(sum_point_data.value_);
+ }
+ else if (nostd::holds_alternative<long>(sum_point_data.value_))
+ {
+ sout_ << nostd::get<long>(sum_point_data.value_);
+ }
+ }
+ else if (nostd::holds_alternative<sdk::metrics::HistogramPointData>(point_data))
+ {
+ auto histogram_point_data = nostd::get<sdk::metrics::HistogramPointData>(point_data);
+ sout_ << "\n type : HistogramPointData";
+ sout_ << "\n count : " << histogram_point_data.count_;
+ sout_ << "\n sum : ";
+ if (nostd::holds_alternative<double>(histogram_point_data.sum_))
+ {
+ sout_ << nostd::get<double>(histogram_point_data.sum_);
+ }
+ else if (nostd::holds_alternative<long>(histogram_point_data.sum_))
+ {
+ sout_ << nostd::get<long>(histogram_point_data.sum_);
+ }
+
+ sout_ << "\n buckets : ";
+ if (nostd::holds_alternative<std::list<double>>(histogram_point_data.boundaries_))
+ {
+ auto &double_boundaries = nostd::get<std::list<double>>(histogram_point_data.boundaries_);
+ printVec(sout_, double_boundaries);
+ }
+ else if (nostd::holds_alternative<std::list<long>>(histogram_point_data.boundaries_))
+ {
+ auto &long_boundaries = nostd::get<std::list<long>>(histogram_point_data.boundaries_);
+ printVec(sout_, long_boundaries);
+ }
+
+ sout_ << "\n counts : ";
+ printVec(sout_, histogram_point_data.counts_);
+ }
+ else if (nostd::holds_alternative<sdk::metrics::LastValuePointData>(point_data))
+ {
+ auto last_point_data = nostd::get<sdk::metrics::LastValuePointData>(point_data);
+ sout_ << "\n type : LastValuePointData";
+ sout_ << "\n timestamp : "
+ << std::to_string(last_point_data.sample_ts_.time_since_epoch().count()) << std::boolalpha
+ << "\n valid : " << last_point_data.is_lastvalue_valid_;
+ sout_ << "\n value : ";
+ if (nostd::holds_alternative<double>(last_point_data.value_))
+ {
+ sout_ << nostd::get<double>(last_point_data.value_);
+ }
+ else if (nostd::holds_alternative<long>(last_point_data.value_))
+ {
+ sout_ << nostd::get<long>(last_point_data.value_);
+ }
+ }
+}
+
+void OStreamMetricExporter::printPointAttributes(
+ const opentelemetry::sdk::metrics::PointAttributes &point_attributes)
+{
+ sout_ << "\n attributes\t\t: ";
+ for (const auto &kv : point_attributes)
+ {
+ sout_ << "\n\t" << kv.first << ": ";
+ opentelemetry::exporter::ostream_common::print_value(kv.second, sout_);
+ }
+}
+
+bool OStreamMetricExporter::ForceFlush(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return true;
+}
+
+bool OStreamMetricExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool OStreamMetricExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metrics_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metrics_exporter.cc
new file mode 100644
index 000000000..4f4bbfd06
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/metrics_exporter.cc
@@ -0,0 +1,59 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/exporters/ostream/metrics_exporter.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+
+OStreamMetricsExporter::OStreamMetricsExporter(std::ostream &sout) noexcept : sout_(sout) {}
+
+sdk::common::ExportResult OStreamMetricsExporter::Export(
+ const std::vector<sdk::metrics::Record> &records) noexcept
+{
+ for (auto record : records)
+ {
+ sout_ << "{"
+ << "\n name : " << record.GetName()
+ << "\n description : " << record.GetDescription()
+ << "\n labels : " << record.GetLabels();
+
+ auto aggregator = record.GetAggregator();
+
+ /**
+ * Unpack the AggregatorVariant from the record so we can pass the data type to
+ * PrintAggregatorVariant to unpack the Aggregator from the variant.
+ */
+ if (nostd::holds_alternative<std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<int>>>(
+ aggregator))
+ {
+ PrintAggregatorVariant<int>(aggregator);
+ }
+ else if (nostd::holds_alternative<
+ std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<short>>>(aggregator))
+ {
+ PrintAggregatorVariant<short>(aggregator);
+ }
+ else if (nostd::holds_alternative<
+ std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<double>>>(aggregator))
+ {
+ PrintAggregatorVariant<double>(aggregator);
+ }
+ else if (nostd::holds_alternative<
+ std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<float>>>(aggregator))
+ {
+ PrintAggregatorVariant<float>(aggregator);
+ }
+ sout_ << "\n}\n";
+ }
+ return sdk::common::ExportResult::kSuccess;
+}
+
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/span_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/span_exporter.cc
new file mode 100644
index 000000000..226f98737
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/src/span_exporter.cc
@@ -0,0 +1,178 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/ostream/span_exporter.h"
+#include "opentelemetry/exporters/ostream/common_utils.h"
+
+#include <iostream>
+#include <mutex>
+#include "opentelemetry/sdk_config.h"
+
+namespace nostd = opentelemetry::nostd;
+namespace trace_sdk = opentelemetry::sdk::trace;
+namespace trace_api = opentelemetry::trace;
+namespace sdkcommon = opentelemetry::sdk::common;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace trace
+{
+
+std::ostream &operator<<(std::ostream &os, trace_api::SpanKind span_kind)
+{
+ switch (span_kind)
+ {
+ case trace_api::SpanKind::kClient:
+ return os << "Client";
+ case trace_api::SpanKind::kInternal:
+ return os << "Internal";
+ case trace_api::SpanKind::kServer:
+ return os << "Server";
+ case trace_api::SpanKind::kProducer:
+ return os << "Producer";
+ case trace_api::SpanKind::kConsumer:
+ return os << "Consumer";
+ };
+ return os << "";
+}
+
+OStreamSpanExporter::OStreamSpanExporter(std::ostream &sout) noexcept : sout_(sout) {}
+
+std::unique_ptr<trace_sdk::Recordable> OStreamSpanExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<trace_sdk::Recordable>(new trace_sdk::SpanData);
+}
+
+sdk::common::ExportResult OStreamSpanExporter::Export(
+ const nostd::span<std::unique_ptr<trace_sdk::Recordable>> &spans) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[Ostream Trace Exporter] Exporting "
+ << spans.size() << " span(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+
+ for (auto &recordable : spans)
+ {
+ auto span = std::unique_ptr<trace_sdk::SpanData>(
+ static_cast<trace_sdk::SpanData *>(recordable.release()));
+
+ if (span != nullptr)
+ {
+
+ char trace_id[32] = {0};
+ char span_id[16] = {0};
+ char parent_span_id[16] = {0};
+
+ span->GetTraceId().ToLowerBase16(trace_id);
+ span->GetSpanId().ToLowerBase16(span_id);
+ span->GetParentSpanId().ToLowerBase16(parent_span_id);
+
+ sout_ << "{"
+ << "\n name : " << span->GetName()
+ << "\n trace_id : " << std::string(trace_id, 32)
+ << "\n span_id : " << std::string(span_id, 16)
+ << "\n tracestate : " << span->GetSpanContext().trace_state()->ToHeader()
+ << "\n parent_span_id: " << std::string(parent_span_id, 16)
+ << "\n start : " << span->GetStartTime().time_since_epoch().count()
+ << "\n duration : " << span->GetDuration().count()
+ << "\n description : " << span->GetDescription()
+ << "\n span kind : " << span->GetSpanKind()
+ << "\n status : " << statusMap[int(span->GetStatus())]
+ << "\n attributes : ";
+ printAttributes(span->GetAttributes());
+ sout_ << "\n events : ";
+ printEvents(span->GetEvents());
+ sout_ << "\n links : ";
+ printLinks(span->GetLinks());
+ sout_ << "\n resources : ";
+ printResources(span->GetResource());
+ sout_ << "\n instr-lib : ";
+ printInstrumentationLibrary(span->GetInstrumentationLibrary());
+ sout_ << "\n}\n";
+ }
+ }
+
+ return sdk::common::ExportResult::kSuccess;
+}
+
+bool OStreamSpanExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool OStreamSpanExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+void OStreamSpanExporter::printAttributes(
+ const std::unordered_map<std::string, sdkcommon::OwnedAttributeValue> &map,
+ const std::string prefix)
+{
+ for (const auto &kv : map)
+ {
+ sout_ << prefix << kv.first << ": ";
+ opentelemetry::exporter::ostream_common::print_value(kv.second, sout_);
+ }
+}
+
+void OStreamSpanExporter::printEvents(const std::vector<trace_sdk::SpanDataEvent> &events)
+{
+ for (const auto &event : events)
+ {
+ sout_ << "\n\t{"
+ << "\n\t name : " << event.GetName()
+ << "\n\t timestamp : " << event.GetTimestamp().time_since_epoch().count()
+ << "\n\t attributes : ";
+ printAttributes(event.GetAttributes(), "\n\t\t");
+ sout_ << "\n\t}";
+ }
+}
+
+void OStreamSpanExporter::printLinks(const std::vector<trace_sdk::SpanDataLink> &links)
+{
+ for (const auto &link : links)
+ {
+ char trace_id[32] = {0};
+ char span_id[16] = {0};
+ link.GetSpanContext().trace_id().ToLowerBase16(trace_id);
+ link.GetSpanContext().span_id().ToLowerBase16(span_id);
+ sout_ << "\n\t{"
+ << "\n\t trace_id : " << std::string(trace_id, 32)
+ << "\n\t span_id : " << std::string(span_id, 16)
+ << "\n\t tracestate : " << link.GetSpanContext().trace_state()->ToHeader()
+ << "\n\t attributes : ";
+ printAttributes(link.GetAttributes(), "\n\t\t");
+ sout_ << "\n\t}";
+ }
+}
+
+void OStreamSpanExporter::printResources(const opentelemetry::sdk::resource::Resource &resources)
+{
+ auto attributes = resources.GetAttributes();
+ if (attributes.size())
+ {
+ printAttributes(attributes, "\n\t");
+ }
+}
+
+void OStreamSpanExporter::printInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library)
+{
+ sout_ << instrumentation_library.GetName();
+ auto version = instrumentation_library.GetVersion();
+ if (version.size())
+ {
+ sout_ << "-" << version;
+ }
+}
+
+} // namespace trace
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_capture.h b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_capture.h
new file mode 100644
index 000000000..6c61c7152
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_capture.h
@@ -0,0 +1,60 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace ostream
+{
+namespace test
+{
+/**
+ * The OStreamCapture captures from the specified stream for its lifetime
+ */
+class OStreamCapture
+{
+public:
+ /**
+ * Create a OStreamCapture which will capture the output of the ostream that it was constructed
+ * with for the lifetime of the instance.
+ */
+ OStreamCapture(std::ostream &ostream) : stream_(ostream), buf_(ostream.rdbuf())
+ {
+ stream_.rdbuf(captured_.rdbuf());
+ }
+
+ ~OStreamCapture() { stream_.rdbuf(buf_); }
+
+ /**
+ * Returns the captured data from the stream.
+ */
+ std::string GetCaptured() const { return captured_.str(); }
+
+private:
+ std::ostream &stream_;
+ std::streambuf *buf_;
+ std::stringstream captured_;
+};
+
+/**
+ * Helper method to invoke the passed func while recording the output of the specified stream and
+ * return the output afterwards.
+ */
+template <typename Func>
+std::string WithOStreamCapture(std::ostream &stream, Func func)
+{
+ OStreamCapture capture(stream);
+ func();
+ return capture.GetCaptured();
+}
+
+} // namespace test
+} // namespace ostream
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_log_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_log_test.cc
new file mode 100644
index 000000000..91d8fbf24
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_log_test.cc
@@ -0,0 +1,330 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_LOGS_PREVIEW
+
+# include <array>
+# include "opentelemetry/exporters/ostream/log_exporter.h"
+# include "opentelemetry/logs/provider.h"
+# include "opentelemetry/nostd/span.h"
+# include "opentelemetry/sdk/logs/logger_provider.h"
+# include "opentelemetry/sdk/logs/simple_log_processor.h"
+
+# include <gtest/gtest.h>
+# include <iostream>
+
+namespace sdklogs = opentelemetry::sdk::logs;
+namespace logs_api = opentelemetry::logs;
+namespace nostd = opentelemetry::nostd;
+namespace exporterlogs = opentelemetry::exporter::logs;
+namespace common = opentelemetry::common;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace logs
+{
+
+// Test that when OStream Log exporter is shutdown, no logs should be sent to stream
+TEST(OStreamLogExporter, Shutdown)
+{
+ auto exporter = std::unique_ptr<sdklogs::LogExporter>(new exporterlogs::OStreamLogExporter);
+
+ // Save cout's original buffer here
+ std::streambuf *original = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::stringstream output;
+ std::cout.rdbuf(output.rdbuf());
+
+ EXPECT_TRUE(exporter->Shutdown());
+
+ // After processor/exporter is shutdown, no logs should be sent to stream
+ auto record = exporter->MakeRecordable();
+ record->SetBody("Log record not empty");
+ exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+
+ // Restore original stringstream buffer
+ std::cout.rdbuf(original);
+ std::string err_message =
+ "[Ostream Log Exporter] Exporting 1 log(s) failed, exporter is shutdown";
+ EXPECT_TRUE(output.str().find(err_message) != std::string::npos);
+}
+
+// ---------------------------------- Print to cout -------------------------
+
+// Testing what a default log record that has no values changed will print out
+// This function tests MakeRecordable() as well as Export().
+TEST(OstreamLogExporter, DefaultLogRecordToCout)
+{
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new exporterlogs::OStreamLogExporter(std::cout));
+
+ // Save cout's original buffer here
+ std::streambuf *original = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::stringstream output;
+ std::cout.rdbuf(output.rdbuf());
+
+ // Pass a default recordable created by the exporter to be exported
+ auto log_record = exporter->MakeRecordable();
+ exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&log_record, 1));
+
+ // Restore cout's original stringstream
+ std::cout.rdbuf(original);
+
+ std::vector<std::string> expected_output{
+ "{\n"
+ " timestamp : 0\n"
+ " severity_num : 0\n"
+ " severity_text : INVALID\n"
+ " body : \n",
+ " resource : \n",
+ "telemetry.sdk.version: " OPENTELEMETRY_VERSION "\n",
+ "telemetry.sdk.name: opentelemetry\n",
+ "telemetry.sdk.language: cpp\n",
+ " attributes : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " trace_flags : 00\n"
+ "}\n"};
+
+ for (auto &expected : expected_output)
+ {
+ ASSERT_NE(output.str().find(expected), std::string::npos);
+ }
+}
+
+// Testing what a log record with only the "timestamp", "severity", "name" and "message" fields set,
+// will print out
+TEST(OStreamLogExporter, SimpleLogToCout)
+{
+ // Initialize an Ostream exporter to std::cout
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new exporterlogs::OStreamLogExporter(std::cout));
+
+ // Save original stream buffer, then redirect cout to our new stream buffer
+ std::streambuf *original = std::cout.rdbuf();
+ std::stringstream output;
+ std::cout.rdbuf(output.rdbuf());
+
+ // Pass a default recordable created by the exporter to be exported
+ // Create a log record and manually timestamp, severity, name, message
+ common::SystemTimestamp now(std::chrono::system_clock::now());
+
+ auto record = std::unique_ptr<sdklogs::Recordable>(new sdklogs::LogRecord());
+ record->SetTimestamp(now);
+ record->SetSeverity(logs_api::Severity::kTrace); // kTrace has enum value of 1
+ record->SetBody("Message");
+
+ // Log a record to cout
+ exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+
+ // Reset cout's original stringstream buffer
+ std::cout.rdbuf(original);
+
+ std::vector<std::string> expected_output{
+ "{\n"
+ " timestamp : " +
+ std::to_string(now.time_since_epoch().count()) +
+ "\n"
+ " severity_num : 1\n"
+ " severity_text : TRACE\n"
+ " body : Message\n",
+ " resource : \n",
+ "telemetry.sdk.version: " OPENTELEMETRY_VERSION "\n",
+ "telemetry.sdk.name: opentelemetry\n",
+ "telemetry.sdk.language: cpp\n",
+ " attributes : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " trace_flags : 00\n"
+ "}\n"};
+
+ for (auto &expected : expected_output)
+ {
+ ASSERT_NE(output.str().find(expected), std::string::npos);
+ }
+}
+
+// ---------------------------------- Print to cerr --------------------------
+
+// Testing what a log record with only the "resource" and "attributes" fields
+// (i.e. KeyValueIterable types) set with primitive types, will print out
+TEST(OStreamLogExporter, LogWithStringAttributesToCerr)
+{
+ // Initialize an Ostream exporter to cerr
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new exporterlogs::OStreamLogExporter(std::cerr));
+
+ // Save original stream buffer, then redirect cout to our new stream buffer
+ std::streambuf *original = std::cerr.rdbuf();
+ std::stringstream stdcerrOutput;
+ std::cerr.rdbuf(stdcerrOutput.rdbuf());
+
+ // Pass a recordable created by the exporter to be exported
+ auto record = exporter->MakeRecordable();
+
+ // Set resources for this log record only of type <string, string>
+ auto resource = opentelemetry::sdk::resource::Resource::Create({{"key1", "val1"}});
+ record->SetResource(resource);
+
+ // Set attributes to this log record of type <string, AttributeValue>
+ record->SetAttribute("a", true);
+
+ // Log record to cerr
+ exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+
+ // Reset cerr's original stringstream buffer
+ std::cerr.rdbuf(original);
+
+ std::vector<std::string> expected_output{
+ "{\n"
+ " timestamp : 0\n"
+ " severity_num : 0\n"
+ " severity_text : INVALID\n"
+ " body : \n",
+ " resource : \n",
+ "telemetry.sdk.version: " OPENTELEMETRY_VERSION "\n",
+ "telemetry.sdk.name: opentelemetry\n",
+ "telemetry.sdk.language: cpp\n",
+ "service.name: unknown_service\n",
+ "key1: val1\n",
+ " attributes : \n",
+ "\ta: 1\n",
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " trace_flags : 00\n"
+ "}\n"};
+
+ for (auto &expected : expected_output)
+ {
+ ASSERT_NE(stdcerrOutput.str().find(expected), std::string::npos);
+ }
+}
+
+// ---------------------------------- Print to clog -------------------------
+
+// Testing what a log record with only the "resource", and "attributes" fields
+// (i.e. KeyValueIterable types), set with 2D arrays as values, will print out
+TEST(OStreamLogExporter, LogWithVariantTypesToClog)
+{
+
+ // Initialize an Ostream exporter to cerr
+ auto exporter =
+ std::unique_ptr<sdklogs::LogExporter>(new exporterlogs::OStreamLogExporter(std::clog));
+
+ // Save original stream buffer, then redirect cout to our new stream buffer
+ std::streambuf *original = std::clog.rdbuf();
+ std::stringstream stdclogOutput;
+ std::clog.rdbuf(stdclogOutput.rdbuf());
+
+ // Pass a recordable created by the exporter to be exported
+ auto record = exporter->MakeRecordable();
+
+ // Set resources for this log record of only integer types as the value
+ std::array<int, 3> array1 = {1, 2, 3};
+ nostd::span<int> data1{array1.data(), array1.size()};
+
+ auto resource = opentelemetry::sdk::resource::Resource::Create({{"res1", data1}});
+ record->SetResource(resource);
+
+ // Set resources for this log record of bool types as the value
+ // e.g. key/value is a par of type <string, array of bools>
+ std::array<bool, 3> array = {false, true, false};
+ record->SetAttribute("attr1", nostd::span<bool>{array.data(), array.size()});
+
+ // Log a record to clog
+ exporter->Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
+
+ // Reset clog's original stringstream buffer
+ std::clog.rdbuf(original);
+
+ std::vector<std::string> expected_output{
+ "{\n"
+ " timestamp : 0\n"
+ " severity_num : 0\n"
+ " severity_text : INVALID\n"
+ " body : \n",
+ " resource : \n",
+ "service.name: unknown_service\n",
+ "telemetry.sdk.version: " OPENTELEMETRY_VERSION "\n",
+ "telemetry.sdk.name: opentelemetry\n",
+ "telemetry.sdk.language: cpp\n",
+ "res1: [1,2,3]\n",
+ "attributes : \n",
+ "\tattr1: [0,1,0]\n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " trace_flags : 00\n"
+ "}\n"};
+
+ for (auto &expected : expected_output)
+ {
+ ASSERT_NE(stdclogOutput.str().find(expected), std::string::npos);
+ }
+}
+
+// // ---------------------------------- Integration Tests -------------------------
+
+// Test using the simple log processor and ostream exporter to cout
+// and use the rest of the logging pipeline (Logger, LoggerProvider, Provider) as well
+TEST(OStreamLogExporter, IntegrationTest)
+{
+ // Initialize a logger
+ auto exporter = std::unique_ptr<sdklogs::LogExporter>(new exporterlogs::OStreamLogExporter);
+ auto sdkProvider = std::shared_ptr<sdklogs::LoggerProvider>(new sdklogs::LoggerProvider());
+ sdkProvider->AddProcessor(
+ std::unique_ptr<sdklogs::LogProcessor>(new sdklogs::SimpleLogProcessor(std::move(exporter))));
+ auto apiProvider = nostd::shared_ptr<logs_api::LoggerProvider>(sdkProvider);
+ auto provider = nostd::shared_ptr<logs_api::LoggerProvider>(apiProvider);
+ logs_api::Provider::SetLoggerProvider(provider);
+ const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"};
+ auto logger = logs_api::Provider::GetLoggerProvider()->GetLogger(
+ "Logger", "", "opentelelemtry_library", "", schema_url);
+
+ // Back up cout's streambuf
+ std::streambuf *original = std::cout.rdbuf();
+
+ // Redirect cout to our string stream
+ std::stringstream stdcoutOutput;
+ std::cout.rdbuf(stdcoutOutput.rdbuf());
+
+ // Write a log to ostream exporter
+ common::SystemTimestamp now(std::chrono::system_clock::now());
+ logger->Log(logs_api::Severity::kDebug, "Hello", {}, {}, {}, {}, now);
+
+ // Restore cout's original streambuf
+ std::cout.rdbuf(original);
+
+ // Compare actual vs expected outputs
+ std::vector<std::string> expected_output{
+ "{\n"
+ " timestamp : " +
+ std::to_string(now.time_since_epoch().count()) +
+ "\n"
+ " severity_num : 5\n"
+ " severity_text : DEBUG\n"
+ " body : Hello\n",
+ " resource : \n",
+ "telemetry.sdk.version: " OPENTELEMETRY_VERSION "\n",
+ "service.name: unknown_service\n",
+ "telemetry.sdk.name: opentelemetry\n",
+ "telemetry.sdk.language: cpp\n",
+ " attributes : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " trace_flags : 00\n"
+ "}\n"};
+
+ for (auto &expected : expected_output)
+ {
+ ASSERT_NE(stdcoutOutput.str().find(expected), std::string::npos);
+ }
+}
+
+} // namespace logs
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metric_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metric_test.cc
new file mode 100644
index 000000000..45d6d8f88
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metric_test.cc
@@ -0,0 +1,269 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+#ifndef ENABLE_METRICS_PREVIEW
+
+# include <gtest/gtest.h>
+# include <memory>
+# include <vector>
+# include "opentelemetry/sdk/metrics/instruments.h"
+# include "opentelemetry/sdk/resource/resource_detector.h"
+
+# include <iostream>
+# include "opentelemetry/exporters/ostream/metric_exporter.h"
+# include "opentelemetry/sdk/metrics/aggregation/default_aggregation.h"
+# include "opentelemetry/sdk/metrics/aggregation/histogram_aggregation.h"
+# include "opentelemetry/sdk/metrics/data/metric_data.h"
+# include "opentelemetry/sdk/resource/resource.h"
+
+namespace metric_sdk = opentelemetry::sdk::metrics;
+namespace nostd = opentelemetry::nostd;
+namespace exportermetrics = opentelemetry::exporter::metrics;
+
+TEST(OStreamMetricsExporter, Shutdown)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricExporter>(new exportermetrics::OStreamMetricExporter);
+ ASSERT_TRUE(exporter->Shutdown());
+ auto result = exporter->Export(metric_sdk::ResourceMetrics{});
+ EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kFailure);
+}
+
+TEST(OStreamMetricsExporter, ExportSumPointData)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricExporter>(new exportermetrics::OStreamMetricExporter);
+
+ metric_sdk::SumPointData sum_point_data{};
+ sum_point_data.value_ = 10.0;
+ metric_sdk::SumPointData sum_point_data2{};
+ sum_point_data2.value_ = 20.0;
+ metric_sdk::ResourceMetrics data;
+ auto resource = opentelemetry::sdk::resource::Resource::Create(
+ opentelemetry::sdk::resource::ResourceAttributes{});
+ data.resource_ = &resource;
+ auto instrumentation_library =
+ opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create("library_name",
+ "1.2.0");
+ metric_sdk::MetricData metric_data{
+ metric_sdk::InstrumentDescriptor{"library_name", "description", "unit",
+ metric_sdk::InstrumentType::kCounter,
+ metric_sdk::InstrumentValueType::kDouble},
+ opentelemetry::common::SystemTimestamp{}, opentelemetry::common::SystemTimestamp{},
+ std::vector<metric_sdk::PointDataAttributes>{
+ {metric_sdk::PointAttributes{{"a1", "b1"}}, sum_point_data},
+ {metric_sdk::PointAttributes{{"a1", "b1"}}, sum_point_data2}}};
+ data.instrumentation_info_metric_data_ = std::vector<metric_sdk::InstrumentationInfoMetrics>{
+ {instrumentation_library.get(), std::vector<metric_sdk::MetricData>{metric_data}}};
+
+ std::stringstream stdoutOutput;
+ std::streambuf *sbuf = std::cout.rdbuf();
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ auto result = exporter->Export(data);
+ EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
+ std::cout.rdbuf(sbuf);
+
+ std::string expected_output =
+ "{"
+ "\n name\t\t: library_name"
+ "\n schema url\t: "
+ "\n version\t: 1.2.0"
+ "\n start time\t: Thu Jan 1 00:00:00 1970"
+ "\n end time\t: Thu Jan 1 00:00:00 1970"
+ "\n name\t\t: library_name"
+ "\n description\t: description"
+ "\n unit\t\t: unit"
+ "\n type\t\t: SumPointData"
+ "\n value\t\t: 10"
+ "\n attributes\t\t: "
+ "\n\ta1: b1"
+ "\n type\t\t: SumPointData"
+ "\n value\t\t: 20"
+ "\n attributes\t\t: "
+ "\n\ta1: b1"
+ "\n}\n";
+ ASSERT_EQ(stdoutOutput.str(), expected_output);
+}
+
+TEST(OStreamMetricsExporter, ExportHistogramPointData)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricExporter>(new exportermetrics::OStreamMetricExporter);
+
+ metric_sdk::HistogramPointData histogram_point_data{};
+ histogram_point_data.boundaries_ = std::list<double>{10.1, 20.2, 30.2};
+ histogram_point_data.count_ = 3;
+ histogram_point_data.counts_ = {200, 300, 400, 500};
+ histogram_point_data.sum_ = 900.5;
+ metric_sdk::HistogramPointData histogram_point_data2{};
+ histogram_point_data2.boundaries_ = std::list<long>{10, 20, 30};
+ histogram_point_data2.count_ = 3;
+ histogram_point_data2.counts_ = {200, 300, 400, 500};
+ histogram_point_data2.sum_ = 900l;
+ metric_sdk::ResourceMetrics data;
+ auto resource = opentelemetry::sdk::resource::Resource::Create(
+ opentelemetry::sdk::resource::ResourceAttributes{});
+ data.resource_ = &resource;
+ auto instrumentation_library =
+ opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create("library_name",
+ "1.2.0");
+ metric_sdk::MetricData metric_data{
+ metric_sdk::InstrumentDescriptor{"library_name", "description", "unit",
+ metric_sdk::InstrumentType::kCounter,
+ metric_sdk::InstrumentValueType::kDouble},
+ opentelemetry::common::SystemTimestamp{}, opentelemetry::common::SystemTimestamp{},
+ std::vector<metric_sdk::PointDataAttributes>{
+ {metric_sdk::PointAttributes{{"a1", "b1"}, {"a2", "b2"}}, histogram_point_data},
+ {metric_sdk::PointAttributes{{"a1", "b1"}}, histogram_point_data2}}};
+ data.instrumentation_info_metric_data_ = std::vector<metric_sdk::InstrumentationInfoMetrics>{
+ {instrumentation_library.get(), std::vector<metric_sdk::MetricData>{metric_data}}};
+
+ std::stringstream stdoutOutput;
+ std::streambuf *sbuf = std::cout.rdbuf();
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ auto result = exporter->Export(data);
+ EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
+ std::cout.rdbuf(sbuf);
+
+ std::string expected_output =
+ "{"
+ "\n name\t\t: library_name"
+ "\n schema url\t: "
+ "\n version\t: 1.2.0"
+ "\n start time\t: Thu Jan 1 00:00:00 1970"
+ "\n end time\t: Thu Jan 1 00:00:00 1970"
+ "\n name\t\t: library_name"
+ "\n description\t: description"
+ "\n unit\t\t: unit"
+ "\n type : HistogramPointData"
+ "\n count : 3"
+ "\n sum : 900.5"
+ "\n buckets : [10.1, 20.2, 30.2, ]"
+ "\n counts : [200, 300, 400, 500, ]"
+ "\n attributes\t\t: "
+ "\n\ta1: b1"
+ "\n\ta2: b2"
+ "\n type : HistogramPointData"
+ "\n count : 3"
+ "\n sum : 900"
+ "\n buckets : [10, 20, 30, ]"
+ "\n counts : [200, 300, 400, 500, ]"
+ "\n attributes\t\t: "
+ "\n\ta1: b1"
+ "\n}\n";
+ ASSERT_EQ(stdoutOutput.str(), expected_output);
+}
+
+TEST(OStreamMetricsExporter, ExportLastValuePointData)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricExporter>(new exportermetrics::OStreamMetricExporter);
+
+ metric_sdk::ResourceMetrics data;
+ auto resource = opentelemetry::sdk::resource::Resource::Create(
+ opentelemetry::sdk::resource::ResourceAttributes{});
+ data.resource_ = &resource;
+ auto instrumentation_library =
+ opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create("library_name",
+ "1.2.0");
+ metric_sdk::LastValuePointData last_value_point_data{};
+ last_value_point_data.value_ = 10.0;
+ last_value_point_data.is_lastvalue_valid_ = true;
+ last_value_point_data.sample_ts_ = opentelemetry::common::SystemTimestamp{};
+ metric_sdk::LastValuePointData last_value_point_data2{};
+ last_value_point_data2.value_ = 20l;
+ last_value_point_data2.is_lastvalue_valid_ = true;
+ last_value_point_data2.sample_ts_ = opentelemetry::common::SystemTimestamp{};
+ metric_sdk::MetricData metric_data{
+ metric_sdk::InstrumentDescriptor{"library_name", "description", "unit",
+ metric_sdk::InstrumentType::kCounter,
+ metric_sdk::InstrumentValueType::kDouble},
+ opentelemetry::common::SystemTimestamp{}, opentelemetry::common::SystemTimestamp{},
+ std::vector<metric_sdk::PointDataAttributes>{
+ {metric_sdk::PointAttributes{}, last_value_point_data},
+ {metric_sdk::PointAttributes{}, last_value_point_data2}}};
+ data.instrumentation_info_metric_data_ = std::vector<metric_sdk::InstrumentationInfoMetrics>{
+ {instrumentation_library.get(), std::vector<metric_sdk::MetricData>{metric_data}}};
+
+ std::stringstream stdoutOutput;
+ std::streambuf *sbuf = std::cout.rdbuf();
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ auto result = exporter->Export(data);
+ EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
+ std::cout.rdbuf(sbuf);
+
+ std::string expected_output =
+ "{"
+ "\n name\t\t: library_name"
+ "\n schema url\t: "
+ "\n version\t: 1.2.0"
+ "\n start time\t: Thu Jan 1 00:00:00 1970"
+ "\n end time\t: Thu Jan 1 00:00:00 1970"
+ "\n name\t\t: library_name"
+ "\n description\t: description"
+ "\n unit\t\t: unit"
+ "\n type : LastValuePointData"
+ "\n timestamp : 0"
+ "\n valid : true"
+ "\n value : 10"
+ "\n attributes\t\t: "
+ "\n type : LastValuePointData"
+ "\n timestamp : 0"
+ "\n valid : true"
+ "\n value : 20"
+ "\n attributes\t\t: "
+ "\n}\n";
+ ASSERT_EQ(stdoutOutput.str(), expected_output);
+}
+
+TEST(OStreamMetricsExporter, ExportDropPointData)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricExporter>(new exportermetrics::OStreamMetricExporter);
+
+ metric_sdk::ResourceMetrics data;
+ auto resource = opentelemetry::sdk::resource::Resource::Create(
+ opentelemetry::sdk::resource::ResourceAttributes{});
+ data.resource_ = &resource;
+ auto instrumentation_library =
+ opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary::Create("library_name",
+ "1.2.0");
+ metric_sdk::DropPointData drop_point_data{};
+ metric_sdk::DropPointData drop_point_data2{};
+ metric_sdk::MetricData metric_data{
+ metric_sdk::InstrumentDescriptor{"library_name", "description", "unit",
+ metric_sdk::InstrumentType::kCounter,
+ metric_sdk::InstrumentValueType::kDouble},
+ opentelemetry::common::SystemTimestamp{}, opentelemetry::common::SystemTimestamp{},
+ std::vector<metric_sdk::PointDataAttributes>{
+ {metric_sdk::PointAttributes{}, drop_point_data},
+ {metric_sdk::PointAttributes{}, drop_point_data2}}};
+ data.instrumentation_info_metric_data_ = std::vector<metric_sdk::InstrumentationInfoMetrics>{
+ {instrumentation_library.get(), std::vector<metric_sdk::MetricData>{metric_data}}};
+
+ std::stringstream stdoutOutput;
+ std::streambuf *sbuf = std::cout.rdbuf();
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ auto result = exporter->Export(data);
+ EXPECT_EQ(result, opentelemetry::sdk::common::ExportResult::kSuccess);
+ std::cout.rdbuf(sbuf);
+
+ std::string expected_output =
+ "{"
+ "\n name\t\t: library_name"
+ "\n schema url\t: "
+ "\n version\t: 1.2.0"
+ "\n start time\t: Thu Jan 1 00:00:00 1970"
+ "\n end time\t: Thu Jan 1 00:00:00 1970"
+ "\n name\t\t: library_name"
+ "\n description\t: description"
+ "\n unit\t\t: unit"
+ "\n}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expected_output);
+}
+
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metrics_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metrics_test.cc
new file mode 100644
index 000000000..f5539c3d4
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_metrics_test.cc
@@ -0,0 +1,295 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include <gtest/gtest.h>
+#ifdef ENABLE_METRICS_PREVIEW
+
+# include "opentelemetry/exporters/ostream/metrics_exporter.h"
+# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h"
+# include "opentelemetry/sdk/_metrics/exporter.h"
+# include "opentelemetry/sdk/_metrics/record.h"
+
+# include <iostream>
+
+namespace metric_sdk = opentelemetry::sdk::metrics;
+namespace metrics_api = opentelemetry::metrics;
+namespace nostd = opentelemetry::nostd;
+namespace exportermetrics = opentelemetry::exporter::metrics;
+
+TEST(OStreamMetricsExporter, PrintCounter)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricsExporter>(new exportermetrics::OStreamMetricsExporter);
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(5.5);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ std::vector<metric_sdk::Record> records;
+ records.push_back(r);
+
+ // Create stringstream to redirect to
+ std::stringstream stdoutOutput;
+
+ // Save cout's buffer here
+ std::streambuf *sbuf = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ exporter->Export(records);
+
+ std::cout.rdbuf(sbuf);
+
+ std::string expectedOutput =
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " sum : 5.5\n"
+ "}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expectedOutput);
+}
+
+TEST(OStreamMetricsExporter, PrintMinMaxSumCount)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricsExporter>(new exportermetrics::OStreamMetricsExporter);
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::MinMaxSumCountAggregator<int>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(1);
+ aggregator->update(2);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ std::vector<metric_sdk::Record> records;
+ records.push_back(r);
+
+ // Create stringstream to redirect to
+ std::stringstream stdoutOutput;
+
+ // Save cout's buffer here
+ std::streambuf *sbuf = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ exporter->Export(records);
+
+ std::cout.rdbuf(sbuf);
+
+ std::string expectedOutput =
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " min : 1\n"
+ " max : 2\n"
+ " sum : 3\n"
+ " count : 2\n"
+ "}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expectedOutput);
+}
+
+TEST(OStreamMetricsExporter, PrintGauge)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricsExporter>(new exportermetrics::OStreamMetricsExporter);
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::GaugeAggregator<short>(metrics_api::InstrumentKind::Counter));
+
+ aggregator->update(1);
+ aggregator->update(9);
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ std::vector<metric_sdk::Record> records;
+ records.push_back(r);
+
+ // Create stringstream to redirect to
+ std::stringstream stdoutOutput;
+
+ // Save cout's buffer here
+ std::streambuf *sbuf = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ exporter->Export(records);
+
+ std::cout.rdbuf(sbuf);
+
+ std::string expectedOutput =
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " last value : 9\n"
+ " timestamp : " +
+ std::to_string(aggregator->get_checkpoint_timestamp().time_since_epoch().count()) +
+ "\n"
+ "}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expectedOutput);
+}
+
+TEST(OStreamMetricsExporter, PrintExact)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricsExporter>(new exportermetrics::OStreamMetricsExporter);
+
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::ExactAggregator<short>(metrics_api::InstrumentKind::Counter, true));
+
+ auto aggregator2 = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::ExactAggregator<short>(metrics_api::InstrumentKind::Counter, false));
+
+ for (int i = 0; i < 10; i++)
+ {
+ aggregator->update(i);
+ aggregator2->update(i);
+ }
+ aggregator->checkpoint();
+ aggregator2->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ metric_sdk::Record r2("name", "description", "labels", aggregator2);
+ std::vector<metric_sdk::Record> records;
+ records.push_back(r);
+ records.push_back(r2);
+
+ // Create stringstream to redirect to
+ std::stringstream stdoutOutput;
+
+ // Save cout's buffer here
+ std::streambuf *sbuf = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ exporter->Export(records);
+
+ std::cout.rdbuf(sbuf);
+
+ std::string expectedOutput =
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " quantiles : [0: 0, .25: 3, .50: 5, .75: 7, 1: 9]\n"
+ "}\n"
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " values : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n"
+ "}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expectedOutput);
+}
+
+TEST(OStreamMetricsExporter, PrintHistogram)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricsExporter>(new exportermetrics::OStreamMetricsExporter);
+
+ std::vector<double> boundaries{10, 20, 30, 40, 50};
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<float>>(
+ new metric_sdk::HistogramAggregator<float>(metrics_api::InstrumentKind::Counter, boundaries));
+
+ for (float i = 0; i < 60; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ std::vector<metric_sdk::Record> records;
+ records.push_back(r);
+
+ // Create stringstream to redirect to
+ std::stringstream stdoutOutput;
+
+ // Save cout's buffer here
+ std::streambuf *sbuf = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ exporter->Export(records);
+
+ std::cout.rdbuf(sbuf);
+
+ std::string expectedOutput =
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " buckets : [10, 20, 30, 40, 50]\n"
+ " counts : [10, 10, 10, 10, 10, 10]\n"
+ "}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expectedOutput);
+}
+
+TEST(OStreamMetricsExporter, PrintSketch)
+{
+ auto exporter =
+ std::unique_ptr<metric_sdk::MetricsExporter>(new exportermetrics::OStreamMetricsExporter);
+
+ std::vector<double> boundaries{1, 3, 5, 7, 9};
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::SketchAggregator<int>(metrics_api::InstrumentKind::Counter, .000005));
+
+ for (int i = 0; i < 10; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+
+ metric_sdk::Record r("name", "description", "labels", aggregator);
+ std::vector<metric_sdk::Record> records;
+ records.push_back(r);
+
+ // Create stringstream to redirect to
+ std::stringstream stdoutOutput;
+
+ // Save cout's buffer here
+ std::streambuf *sbuf = std::cout.rdbuf();
+
+ // Redirect cout to our stringstream buffer
+ std::cout.rdbuf(stdoutOutput.rdbuf());
+
+ exporter->Export(records);
+
+ std::cout.rdbuf(sbuf);
+
+ std::string expectedOutput =
+ "{\n"
+ " name : name\n"
+ " description : description\n"
+ " labels : labels\n"
+ " buckets : [0, 0.999995, 2, 3.00001, 4, 4.99999, 5.99997, 7.00003, 8.00003, 9]\n"
+ " counts : [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n"
+ "}\n";
+
+ ASSERT_EQ(stdoutOutput.str(), expectedOutput);
+}
+#else
+TEST(OStreamMetricsExporter, DummyTest)
+{
+ // empty
+}
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_span_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_span_test.cc
new file mode 100644
index 000000000..edfd66505
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/ostream/test/ostream_span_test.cc
@@ -0,0 +1,398 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/common/key_value_iterable_view.h"
+#include "opentelemetry/sdk/trace/recordable.h"
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/sdk/trace/tracer_provider.h"
+#include "opentelemetry/trace/provider.h"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/exporters/ostream/span_exporter.h"
+
+#include "ostream_capture.h"
+
+#include <gtest/gtest.h>
+#include <iostream>
+
+using namespace opentelemetry::exporter::ostream::test;
+
+namespace trace = opentelemetry::trace;
+namespace common = opentelemetry::common;
+namespace nostd = opentelemetry::nostd;
+namespace trace_sdk = opentelemetry::sdk::trace;
+namespace resource = opentelemetry::sdk::resource;
+namespace exportertrace = opentelemetry::exporter::trace;
+
+using Attributes = std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>;
+
+class TestResource : public resource::Resource
+{
+public:
+ TestResource(resource::ResourceAttributes attributes = resource::ResourceAttributes())
+ : resource::Resource(attributes)
+ {}
+};
+
+// Testing Shutdown functionality of OStreamSpanExporter, should expect no data to be sent to Stream
+TEST(OStreamSpanExporter, Shutdown)
+{
+ auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter);
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+ recordable->SetName("Test Span");
+
+ // Capture the output of cout
+ const auto captured = WithOStreamCapture(std::cout, [&]() {
+ EXPECT_TRUE(processor->Shutdown());
+ processor->OnEnd(std::move(recordable));
+ });
+ std::string err_message =
+ "[Ostream Trace Exporter] Exporting 1 span(s) failed, exporter is shutdown";
+ EXPECT_TRUE(captured.find(err_message) != std::string::npos);
+}
+
+constexpr const char *kDefaultSpanPrinted =
+ "{\n"
+ " name : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " tracestate : \n"
+ " parent_span_id: 0000000000000000\n"
+ " start : 0\n"
+ " duration : 0\n"
+ " description : \n"
+ " span kind : Internal\n"
+ " status : Unset\n"
+ " attributes : \n"
+ " events : \n"
+ " links : \n"
+ " resources : \n"
+ " instr-lib : unknown_service\n"
+ "}\n";
+
+// Testing what a default span that is not changed will print out, either all 0's or empty values
+TEST(OStreamSpanExporter, PrintDefaultSpan)
+{
+ std::stringstream output;
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(output));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ processor->OnEnd(std::move(recordable));
+
+ EXPECT_EQ(output.str(), kDefaultSpanPrinted);
+}
+
+TEST(OStreamSpanExporter, PrintSpanWithBasicFields)
+{
+ std::stringstream output;
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(output));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ 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::TraceId trace_id{trace_id_buf};
+ trace::SpanId span_id{span_id_buf};
+ trace::SpanId parent_span_id{parent_span_id_buf};
+ const auto trace_state = trace::TraceState::GetDefault()->Set("state1", "value");
+ const trace::SpanContext span_context{
+ trace_id, span_id, trace::TraceFlags{trace::TraceFlags::kIsSampled}, true, trace_state};
+
+ recordable->SetIdentity(span_context, parent_span_id);
+ recordable->SetName("Test Span");
+ common::SystemTimestamp now(std::chrono::system_clock::now());
+ recordable->SetStartTime(now);
+ recordable->SetDuration(std::chrono::nanoseconds(100));
+ recordable->SetStatus(trace::StatusCode::kOk, "Test Description");
+ recordable->SetSpanKind(trace::SpanKind::kClient);
+
+ TestResource resource1(resource::ResourceAttributes({{"key1", "val1"}}));
+ recordable->SetResource(resource1);
+
+ processor->OnEnd(std::move(recordable));
+
+ std::string start = std::to_string(now.time_since_epoch().count());
+
+ std::string expectedOutput =
+ "{\n"
+ " name : Test Span\n"
+ " trace_id : 01020304050607080102030405060708\n"
+ " span_id : 0102030405060708\n"
+ " tracestate : state1=value\n"
+ " parent_span_id: 0807060504030201\n"
+ " start : " +
+ start +
+ "\n"
+ " duration : 100\n"
+ " description : Test Description\n"
+ " span kind : Client\n"
+ " status : Ok\n"
+ " attributes : \n"
+ " events : \n"
+ " links : \n"
+ " resources : \n"
+ "\tkey1: val1\n"
+ " instr-lib : unknown_service\n"
+ "}\n";
+ EXPECT_EQ(output.str(), expectedOutput);
+}
+
+TEST(OStreamSpanExporter, PrintSpanWithAttribute)
+{
+ std::stringstream output;
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(output));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ recordable->SetAttribute("attr1", "string");
+
+ processor->OnEnd(std::move(recordable));
+
+ std::string expectedOutput =
+ "{\n"
+ " name : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " tracestate : \n"
+ " parent_span_id: 0000000000000000\n"
+ " start : 0\n"
+ " duration : 0\n"
+ " description : \n"
+ " span kind : Internal\n"
+ " status : Unset\n"
+ " attributes : \n"
+ "\tattr1: string\n"
+ " events : \n"
+ " links : \n"
+ " resources : \n"
+ " instr-lib : unknown_service\n"
+ "}\n";
+ EXPECT_EQ(output.str(), expectedOutput);
+}
+
+TEST(OStreamSpanExporter, PrintSpanWithArrayAttribute)
+{
+ std::stringstream output;
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(output));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ std::array<int, 3> array1 = {1, 2, 3};
+ nostd::span<int> span1{array1.data(), array1.size()};
+ recordable->SetAttribute("array1", span1);
+
+ processor->OnEnd(std::move(recordable));
+
+ std::string expectedOutput =
+ "{\n"
+ " name : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " tracestate : \n"
+ " parent_span_id: 0000000000000000\n"
+ " start : 0\n"
+ " duration : 0\n"
+ " description : \n"
+ " span kind : Internal\n"
+ " status : Unset\n"
+ " attributes : \n"
+ "\tarray1: [1,2,3]\n"
+ " events : \n"
+ " links : \n"
+ " resources : \n"
+ " instr-lib : unknown_service\n"
+ "}\n";
+ EXPECT_EQ(output.str(), expectedOutput);
+}
+
+TEST(OStreamSpanExporter, PrintSpanWithEvents)
+{
+ std::stringstream output;
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(output));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+ common::SystemTimestamp now(std::chrono::system_clock::now());
+ common::SystemTimestamp next(std::chrono::system_clock::now() + std::chrono::seconds(1));
+
+ std::string now_str = std::to_string(now.time_since_epoch().count());
+ std::string next_str = std::to_string(next.time_since_epoch().count());
+
+ recordable->AddEvent("hello", now);
+ recordable->AddEvent("world", next,
+ common::KeyValueIterableView<Attributes>({{"attr1", "string"}}));
+
+ processor->OnEnd(std::move(recordable));
+
+ std::string expectedOutput =
+ "{\n"
+ " name : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " tracestate : \n"
+ " parent_span_id: 0000000000000000\n"
+ " start : 0\n"
+ " duration : 0\n"
+ " description : \n"
+ " span kind : Internal\n"
+ " status : Unset\n"
+ " attributes : \n"
+ " events : \n"
+ "\t{\n"
+ "\t name : hello\n"
+ "\t timestamp : " +
+ now_str +
+ "\n"
+ "\t attributes : \n"
+ "\t}\n"
+ "\t{\n"
+ "\t name : world\n"
+ "\t timestamp : " +
+ next_str +
+ "\n"
+ "\t attributes : \n"
+ "\t\tattr1: string\n"
+ "\t}\n"
+ " links : \n"
+ " resources : \n"
+ " instr-lib : unknown_service\n"
+ "}\n";
+ EXPECT_EQ(output.str(), expectedOutput);
+}
+
+TEST(OStreamSpanExporter, PrintSpanWithLinks)
+{
+ std::stringstream output;
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(output));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ // produce valid SpanContext with pseudo span and trace Id.
+ uint8_t span_id_buf[trace::SpanId::kSize] = {
+ 1,
+ };
+ trace::SpanId span_id{span_id_buf};
+ uint8_t trace_id_buf[trace::TraceId::kSize] = {
+ 2,
+ };
+ trace::TraceId trace_id{trace_id_buf};
+ const auto span_context =
+ trace::SpanContext(trace_id, span_id, trace::TraceFlags{trace::TraceFlags::kIsSampled}, true);
+
+ // and another to check preserving order.
+ uint8_t span_id_buf2[trace::SpanId::kSize] = {
+ 3,
+ };
+ trace::SpanId span_id2{span_id_buf2};
+ const auto span_context2 =
+ trace::SpanContext(trace_id, span_id2, trace::TraceFlags{trace::TraceFlags::kIsSampled}, true,
+ trace::TraceState::FromHeader("state1=value"));
+
+ recordable->AddLink(span_context);
+ recordable->AddLink(span_context2,
+ common::KeyValueIterableView<Attributes>({{"attr1", "string"}}));
+
+ processor->OnEnd(std::move(recordable));
+
+ std::string expectedOutput =
+ "{\n"
+ " name : \n"
+ " trace_id : 00000000000000000000000000000000\n"
+ " span_id : 0000000000000000\n"
+ " tracestate : \n"
+ " parent_span_id: 0000000000000000\n"
+ " start : 0\n"
+ " duration : 0\n"
+ " description : \n"
+ " span kind : Internal\n"
+ " status : Unset\n"
+ " attributes : \n"
+ " events : \n"
+ " links : \n"
+ "\t{\n"
+ "\t trace_id : 02000000000000000000000000000000\n"
+ "\t span_id : 0100000000000000\n"
+ "\t tracestate : \n"
+ "\t attributes : \n"
+ "\t}\n"
+ "\t{\n"
+ "\t trace_id : 02000000000000000000000000000000\n"
+ "\t span_id : 0300000000000000\n"
+ "\t tracestate : state1=value\n"
+ "\t attributes : \n"
+ "\t\tattr1: string\n"
+ "\t}\n"
+ " resources : \n"
+ " instr-lib : unknown_service\n"
+ "}\n";
+ EXPECT_EQ(output.str(), expectedOutput);
+}
+
+// Test with the three common ostreams, tests are more of a sanity check and usage examples.
+TEST(OStreamSpanExporter, PrintSpanToCout)
+{
+ auto exporter = std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter);
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ const auto captured =
+ WithOStreamCapture(std::cout, [&]() { processor->OnEnd(std::move(recordable)); });
+
+ EXPECT_EQ(captured, kDefaultSpanPrinted);
+}
+
+TEST(OStreamSpanExporter, PrintSpanToCerr)
+{
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(std::cerr));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ const auto captured =
+ WithOStreamCapture(std::cerr, [&]() { processor->OnEnd(std::move(recordable)); });
+
+ EXPECT_EQ(captured, kDefaultSpanPrinted);
+}
+
+TEST(OStreamSpanExporter, PrintSpanToClog)
+{
+ auto exporter =
+ std::unique_ptr<trace_sdk::SpanExporter>(new exportertrace::OStreamSpanExporter(std::clog));
+ auto processor = std::shared_ptr<trace_sdk::SpanProcessor>(
+ new trace_sdk::SimpleSpanProcessor(std::move(exporter)));
+
+ auto recordable = processor->MakeRecordable();
+
+ const auto captured =
+ WithOStreamCapture(std::clog, [&]() { processor->OnEnd(std::move(recordable)); });
+
+ EXPECT_EQ(captured, kDefaultSpanPrinted);
+}
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
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/BUILD
new file mode 100644
index 000000000..47c9bbab1
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/BUILD
@@ -0,0 +1,144 @@
+# 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"])
+
+cc_library(
+ name = "prometheus_exporter_deprecated",
+ srcs = [
+ "src/prometheus_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/prometheus/prometheus_exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["prometheus"],
+ deps = [
+ ":prometheus_collector_deprecated",
+ ":prometheus_exporter_utils_deprecated",
+ "//api",
+ "//sdk:headers",
+ "@com_github_jupp0r_prometheus_cpp//core",
+ "@com_github_jupp0r_prometheus_cpp//pull",
+ ],
+)
+
+cc_library(
+ name = "prometheus_exporter_utils_deprecated",
+ srcs = [
+ "src/prometheus_exporter_utils.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["prometheus"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_github_jupp0r_prometheus_cpp//core",
+ "@com_github_jupp0r_prometheus_cpp//pull",
+ ],
+)
+
+cc_library(
+ name = "prometheus_collector_deprecated",
+ srcs = [
+ "src/prometheus_collector.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/prometheus/prometheus_collector.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["prometheus"],
+ deps = [
+ ":prometheus_exporter_utils_deprecated",
+ "//api",
+ "//sdk:headers",
+ "@com_github_jupp0r_prometheus_cpp//core",
+ "@com_github_jupp0r_prometheus_cpp//pull",
+ ],
+)
+
+cc_test(
+ name = "prometheus_exporter_test_deprecated",
+ srcs = [
+ "test/prometheus_exporter_test.cc",
+ ],
+ tags = [
+ "prometheus",
+ "test",
+ ],
+ deps = [
+ ":prometheus_exporter_deprecated",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_library(
+ name = "prometheus_exporter",
+ srcs = [
+ "src/exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/prometheus/exporter.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["prometheus"],
+ deps = [
+ ":prometheus_collector",
+ ":prometheus_exporter_utils",
+ "//api",
+ "//sdk:headers",
+ "@com_github_jupp0r_prometheus_cpp//core",
+ "@com_github_jupp0r_prometheus_cpp//pull",
+ ],
+)
+
+cc_library(
+ name = "prometheus_exporter_utils",
+ srcs = [
+ "src/exporter_utils.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/prometheus/exporter_utils.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["prometheus"],
+ deps = [
+ "//api",
+ "//sdk:headers",
+ "@com_github_jupp0r_prometheus_cpp//core",
+ "@com_github_jupp0r_prometheus_cpp//pull",
+ ],
+)
+
+cc_library(
+ name = "prometheus_collector",
+ srcs = [
+ "src/collector.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/prometheus/collector.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["prometheus"],
+ deps = [
+ ":prometheus_exporter_utils",
+ "//api",
+ "//sdk:headers",
+ "@com_github_jupp0r_prometheus_cpp//core",
+ "@com_github_jupp0r_prometheus_cpp//pull",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/CMakeLists.txt
new file mode 100755
index 000000000..56523ec84
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/CMakeLists.txt
@@ -0,0 +1,90 @@
+# Copyright 2020, OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include_directories(include)
+if(NOT TARGET prometheus-cpp::core)
+ find_package(prometheus-cpp CONFIG REQUIRED)
+endif()
+if(WITH_METRICS_PREVIEW)
+ add_library(
+ prometheus_exporter_deprecated
+ src/prometheus_exporter.cc src/prometheus_collector.cc
+ src/prometheus_exporter_utils.cc)
+ target_include_directories(
+ prometheus_exporter_deprecated
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+ set(PROMETHEUS_EXPORTER_TARGETS_DEPRECATED prometheus_exporter_deprecated)
+ if(TARGET pull)
+ list(APPEND PROMETHEUS_EXPORTER_TARGETS_DEPRECATED pull)
+ endif()
+ if(TARGET core)
+ list(APPEND PROMETHEUS_EXPORTER_TARGETS_DEPRECATED core)
+ endif()
+ target_link_libraries(
+ prometheus_exporter_deprecated
+ PUBLIC opentelemetry_metrics_deprecated prometheus-cpp::pull
+ prometheus-cpp::core)
+ install(
+ TARGETS ${PROMETHEUS_EXPORTER_TARGETS_DEPRECATED}
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+ install(
+ DIRECTORY include/opentelemetry/exporters/prometheus
+ DESTINATION include/opentelemetry/exporters/
+ FILES_MATCHING
+ PATTERN "*.h")
+ if(BUILD_TESTING)
+ add_subdirectory(test)
+ endif()
+else()
+
+ add_library(prometheus_exporter src/exporter.cc src/collector.cc
+ src/exporter_utils.cc)
+ target_include_directories(
+ prometheus_exporter
+ PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>")
+
+ set(PROMETHEUS_EXPORTER_TARGETS prometheus_exporter)
+ if(TARGET pull)
+ list(APPEND PROMETHEUS_EXPORTER_TARGETS pull)
+ endif()
+ if(TARGET core)
+ list(APPEND PROMETHEUS_EXPORTER_TARGETS core)
+ endif()
+ target_link_libraries(
+ prometheus_exporter PUBLIC opentelemetry_metrics prometheus-cpp::pull
+ prometheus-cpp::core)
+ install(
+ TARGETS ${PROMETHEUS_EXPORTER_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/prometheus
+ DESTINATION include/opentelemetry/exporters/
+ FILES_MATCHING
+ PATTERN "*.h")
+
+ if(BUILD_TESTING)
+ add_subdirectory(test)
+ endif()
+endif()
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h
new file mode 100644
index 000000000..68f50d29f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/collector.h
@@ -0,0 +1,87 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifndef ENABLE_METRICS_PREVIEW
+
+# include <memory>
+# include <mutex>
+# include <vector>
+
+# include <prometheus/collectable.h>
+# include <prometheus/metric_family.h>
+# include "opentelemetry/exporters/prometheus/exporter_utils.h"
+
+namespace prometheus_client = ::prometheus;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+/**
+ * The Prometheus Collector maintains the intermediate collection in Prometheus Exporter
+ */
+class PrometheusCollector : public prometheus_client::Collectable
+{
+public:
+ /**
+ * Default Constructor.
+ *
+ * This constructor initializes the collection for metrics to export
+ * in this class with default capacity
+ */
+ explicit PrometheusCollector(size_t max_collection_size = 2048);
+
+ /**
+ * Collects all metrics data from metricsToCollect collection.
+ *
+ * @return all metrics in the metricsToCollect snapshot
+ */
+ std::vector<prometheus_client::MetricFamily> Collect() const override;
+
+ /**
+ * This function is called by export() function and add the collection of
+ * records to the metricsToCollect collection
+ *
+ * @param records a collection of records to add to the metricsToCollect collection
+ */
+ void AddMetricData(const sdk::metrics::ResourceMetrics &data);
+
+ /**
+ * Get the current collection in the collector.
+ *
+ * @return the current metricsToCollect collection
+ */
+ std::vector<std::unique_ptr<sdk::metrics::ResourceMetrics>> &GetCollection();
+
+ /**
+ * Gets the maximum size of the collection.
+ *
+ * @return max collection size
+ */
+ int GetMaxCollectionSize() const;
+
+private:
+ /**
+ * Collection of metrics data from the export() function, and to be export
+ * to user when they send a pull request. This collection is a pointer
+ * to a collection so Collect() is able to clear the collection, even
+ * though it is a const function.
+ */
+ mutable std::vector<std::unique_ptr<sdk::metrics::ResourceMetrics>> metrics_to_collect_;
+
+ /**
+ * Maximum size of the metricsToCollect collection.
+ */
+ size_t max_collection_size_;
+
+ /*
+ * Lock when operating the metricsToCollect collection
+ */
+ mutable std::mutex collection_lock_;
+};
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h
new file mode 100644
index 000000000..59ef1a11a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter.h
@@ -0,0 +1,126 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifndef ENABLE_METRICS_PREVIEW
+# include <memory>
+# include <string>
+# include <vector>
+
+# include <prometheus/exposer.h>
+# include "opentelemetry/common/spin_lock_mutex.h"
+# include "opentelemetry/exporters/prometheus/collector.h"
+# include "opentelemetry/nostd/span.h"
+# include "opentelemetry/sdk/common/env_variables.h"
+# include "opentelemetry/sdk/metrics/metric_exporter.h"
+# include "opentelemetry/version.h"
+
+/**
+ * This class is an implementation of the MetricsExporter interface and
+ * exports Prometheus metrics data. Functions in this class should be
+ * called by the Controller in our data pipeline.
+ */
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace exporter
+{
+namespace metrics
+{
+
+inline const std::string GetPrometheusDefaultHttpEndpoint()
+{
+ constexpr char kPrometheusEndpointEnv[] = "PROMETHEUS_EXPORTER_ENDPOINT";
+ constexpr char kPrometheusEndpointDefault[] = "localhost:9464";
+
+ auto endpoint = opentelemetry::sdk::common::GetEnvironmentVariable(kPrometheusEndpointEnv);
+ return endpoint.size() ? endpoint : kPrometheusEndpointDefault;
+}
+
+/**
+ * Struct to hold Prometheus exporter options.
+ */
+struct PrometheusExporterOptions
+{
+ // The endpoint the Prometheus backend can collect metrics from
+ std::string url = GetPrometheusDefaultHttpEndpoint();
+};
+
+class PrometheusExporter : public sdk::metrics::MetricExporter
+{
+public:
+ /**
+ * Constructor - binds an exposer and collector to the exporter
+ * @param options: options for an exposer that exposes
+ * an HTTP endpoint for the exporter to connect to
+ */
+ PrometheusExporter(const PrometheusExporterOptions &options);
+
+ /**
+ * Exports a batch of Metric Records.
+ * @param records: a collection of records to export
+ * @return: returns a ReturnCode detailing a success, or type of failure
+ */
+ sdk::common::ExportResult Export(const sdk::metrics::ResourceMetrics &data) noexcept override;
+
+ /**
+ * Force flush the exporter.
+ */
+ bool ForceFlush(
+ std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override;
+
+ /**
+ * Shuts down the exporter and does cleanup.
+ * Since Prometheus is a pull based interface,
+ * we cannot serve data remaining in the intermediate
+ * collection to to client an HTTP request being sent,
+ * so we flush the data.
+ */
+ bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override;
+
+ /**
+ * @return: returns a shared_ptr to
+ * the PrometheusCollector instance
+ */
+ std::shared_ptr<PrometheusCollector> &GetCollector();
+
+ /**
+ * @return: Gets the shutdown status of the exporter
+ */
+ bool IsShutdown() const;
+
+private:
+ // The configuration options associated with this exporter.
+ const PrometheusExporterOptions options_;
+ /**
+ * exporter shutdown status
+ */
+ bool is_shutdown_;
+
+ /**
+ * Pointer to a
+ * PrometheusCollector instance
+ */
+ std::shared_ptr<PrometheusCollector> collector_;
+
+ /**
+ * Pointer to an
+ * Exposer instance
+ */
+ std::unique_ptr<::prometheus::Exposer> exposer_;
+
+ /**
+ * friend class for testing
+ */
+ friend class PrometheusExporterTest;
+
+ /**
+ * PrometheusExporter constructor with no parameters
+ * Used for testing only
+ */
+ PrometheusExporter();
+};
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // ENABLE_METRICS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h
new file mode 100644
index 000000000..c8df4f6cf
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h
@@ -0,0 +1,115 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#ifndef ENABLE_METRICS_PREVIEW
+
+# include <prometheus/metric_family.h>
+# include <string>
+# include <vector>
+# include "opentelemetry/metrics/provider.h"
+# include "opentelemetry/sdk/metrics/meter.h"
+# include "opentelemetry/version.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+/**
+ * The Prometheus Utils contains utility functions for Prometheus Exporter
+ */
+class PrometheusExporterUtils
+{
+public:
+ /**
+ * Helper function to convert OpenTelemetry metrics data collection
+ * to Prometheus metrics data collection
+ *
+ * @param records a collection of metrics in OpenTelemetry
+ * @return a collection of translated metrics that is acceptable by Prometheus
+ */
+ static std::vector<::prometheus::MetricFamily> TranslateToPrometheus(
+ const std::vector<std::unique_ptr<sdk::metrics::ResourceMetrics>> &data);
+
+private:
+ /**
+ * Sanitize the given metric name or label according to Prometheus rule.
+ *
+ * This function is needed because names in OpenTelemetry can contain
+ * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the
+ * name should only contain alphanumeric characters and '_'.
+ */
+ static std::string SanitizeNames(std::string name);
+
+ static opentelemetry::sdk::metrics::AggregationType getAggregationType(
+ const opentelemetry::sdk::metrics::PointType &point_type);
+
+ /**
+ * Translate the OTel metric type to Prometheus metric type
+ */
+ static ::prometheus::MetricType TranslateType(opentelemetry::sdk::metrics::AggregationType kind);
+
+ /**
+ * Set metric data for:
+ * Counter => Prometheus Counter
+ */
+ template <typename T>
+ static void SetData(std::vector<T> values,
+ const opentelemetry::sdk::metrics::PointAttributes &labels,
+ ::prometheus::MetricType type,
+ std::chrono::nanoseconds time,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Set metric data for:
+ * Histogram => Prometheus Histogram
+ */
+ template <typename T>
+ static void SetData(std::vector<T> values,
+ const opentelemetry::sdk::metrics::ListType &boundaries,
+ const std::vector<uint64_t> &counts,
+ const opentelemetry::sdk::metrics::PointAttributes &labels,
+ std::chrono::nanoseconds time,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Set time and labels to metric data
+ */
+ static void SetMetricBasic(::prometheus::ClientMetric &metric,
+ std::chrono::nanoseconds time,
+ const opentelemetry::sdk::metrics::PointAttributes &labels);
+
+ /**
+ * Convert attribute value to string
+ */
+ static std::string AttributeValueToString(
+ const opentelemetry::sdk::common::OwnedAttributeValue &value);
+
+ /**
+ * Handle Counter and Gauge.
+ */
+ template <typename T>
+ static void SetValue(std::vector<T> values,
+ ::prometheus::MetricType type,
+ ::prometheus::ClientMetric *metric);
+
+ /**
+ * Handle Gauge from MinMaxSumCount
+ */
+ static void SetValue(double value, ::prometheus::ClientMetric *metric);
+
+ /**
+ * Handle Histogram
+ */
+ template <typename T, typename U>
+ static void SetValue(std::vector<T> values,
+ const std::list<U> &boundaries,
+ const std::vector<uint64_t> &counts,
+ ::prometheus::ClientMetric *metric);
+};
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h
new file mode 100644
index 000000000..4be748385
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_collector.h
@@ -0,0 +1,88 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_METRICS_PREVIEW
+
+# include <memory>
+# include <mutex>
+# include <vector>
+
+# include "opentelemetry/exporters/prometheus/prometheus_exporter_utils.h"
+# include "opentelemetry/sdk/_metrics/record.h"
+# include "prometheus/collectable.h"
+# include "prometheus/metric_family.h"
+
+namespace prometheus_client = ::prometheus;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace prometheus
+{
+/**
+ * The Prometheus Collector maintains the intermediate collection in Prometheus Exporter
+ */
+class PrometheusCollector : public prometheus_client::Collectable
+{
+public:
+ /**
+ * Default Constructor.
+ *
+ * This constructor initializes the collection for metrics to export
+ * in this class with default capacity
+ */
+ explicit PrometheusCollector(size_t max_collection_size = 2048);
+
+ /**
+ * Collects all metrics data from metricsToCollect collection.
+ *
+ * @return all metrics in the metricsToCollect snapshot
+ */
+ std::vector<prometheus_client::MetricFamily> Collect() const override;
+
+ /**
+ * This function is called by export() function and add the collection of
+ * records to the metricsToCollect collection
+ *
+ * @param records a collection of records to add to the metricsToCollect collection
+ */
+ void AddMetricData(const std::vector<opentelemetry::sdk::metrics::Record> &records);
+
+ /**
+ * Get the current collection in the collector.
+ *
+ * @return the current metricsToCollect collection
+ */
+ std::vector<opentelemetry::sdk::metrics::Record> GetCollection();
+
+ /**
+ * Gets the maximum size of the collection.
+ *
+ * @return max collection size
+ */
+ int GetMaxCollectionSize() const;
+
+private:
+ /**
+ * Collection of metrics data from the export() function, and to be export
+ * to user when they send a pull request. This collection is a pointer
+ * to a collection so Collect() is able to clear the collection, even
+ * though it is a const function.
+ */
+ std::unique_ptr<std::vector<opentelemetry::sdk::metrics::Record>> metrics_to_collect_;
+
+ /**
+ * Maximum size of the metricsToCollect collection.
+ */
+ size_t max_collection_size_;
+
+ /*
+ * Lock when operating the metricsToCollect collection
+ */
+ mutable std::mutex collection_lock_;
+};
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter.h
new file mode 100644
index 000000000..7c1f99a75
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter.h
@@ -0,0 +1,98 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_METRICS_PREVIEW
+# include <memory>
+# include <string>
+# include <vector>
+
+# include "opentelemetry/exporters/prometheus/prometheus_collector.h"
+# include "opentelemetry/sdk/_metrics/exporter.h"
+# include "opentelemetry/sdk/_metrics/record.h"
+# include "opentelemetry/version.h"
+# include "prometheus/exposer.h"
+
+/**
+ * This class is an implementation of the MetricsExporter interface and
+ * exports Prometheus metrics data. Functions in this class should be
+ * called by the Controller in our data pipeline.
+ */
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace exporter
+{
+namespace prometheus
+{
+class PrometheusExporter : public sdk::metrics::MetricsExporter
+{
+public:
+ /**
+ * Constructor - binds an exposer and collector to the exporter
+ * @param address: an address for an exposer that exposes
+ * an HTTP endpoint for the exporter to connect to
+ */
+ PrometheusExporter(std::string &address);
+
+ /**
+ * Exports a batch of Metric Records.
+ * @param records: a collection of records to export
+ * @return: returns a ReturnCode detailing a success, or type of failure
+ */
+ sdk::common::ExportResult Export(
+ const std::vector<sdk::metrics::Record> &records) noexcept override;
+
+ /**
+ * Shuts down the exporter and does cleanup.
+ * Since Prometheus is a pull based interface,
+ * we cannot serve data remaining in the intermediate
+ * collection to to client an HTTP request being sent,
+ * so we flush the data.
+ */
+ void Shutdown() noexcept;
+
+ /**
+ * @return: returns a shared_ptr to
+ * the PrometheusCollector instance
+ */
+ std::shared_ptr<PrometheusCollector> &GetCollector();
+
+ /**
+ * @return: Gets the shutdown status of the exporter
+ */
+ bool IsShutdown() const;
+
+private:
+ /**
+ * exporter shutdown status
+ */
+ bool is_shutdown_;
+
+ /**
+ * Pointer to a
+ * PrometheusCollector instance
+ */
+ std::shared_ptr<PrometheusCollector> collector_;
+
+ /**
+ * Pointer to an
+ * Exposer instance
+ */
+ std::unique_ptr<::prometheus::Exposer> exposer_;
+
+ /**
+ * friend class for testing
+ */
+ friend class PrometheusExporterTest;
+
+ /**
+ * PrometheusExporter constructor with no parameters
+ * Used for testing only
+ */
+ PrometheusExporter();
+};
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // ENABLE_METRICS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h
new file mode 100644
index 000000000..7efea9d39
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/include/opentelemetry/exporters/prometheus/prometheus_exporter_utils.h
@@ -0,0 +1,171 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+#ifdef ENABLE_METRICS_PREVIEW
+
+# include <string>
+# include <vector>
+
+# include "opentelemetry/sdk/_metrics/record.h"
+# include "prometheus/metric_family.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace prometheus
+{
+/**
+ * The Prometheus Utils contains utility functions for Prometheus Exporter
+ */
+class PrometheusExporterUtils
+{
+public:
+ /**
+ * Helper function to convert OpenTelemetry metrics data collection
+ * to Prometheus metrics data collection
+ *
+ * @param records a collection of metrics in OpenTelemetry
+ * @return a collection of translated metrics that is acceptable by Prometheus
+ */
+ static std::vector<::prometheus::MetricFamily> TranslateToPrometheus(
+ const std::vector<opentelemetry::sdk::metrics::Record> &records);
+
+private:
+ /**
+ * Set value to metric family according to record
+ */
+ static void SetMetricFamily(opentelemetry::sdk::metrics::Record &record,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Sanitize the given metric name or label according to Prometheus rule.
+ *
+ * This function is needed because names in OpenTelemetry can contain
+ * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the
+ * name should only contain alphanumeric characters and '_'.
+ */
+ static std::string SanitizeNames(std::string name);
+
+ /**
+ * Set value to metric family for different aggregator
+ */
+ template <typename T>
+ static void SetMetricFamilyByAggregator(
+ std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<T>> aggregator,
+ std::string labels_str,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Translate the OTel metric type to Prometheus metric type
+ */
+ static ::prometheus::MetricType TranslateType(opentelemetry::sdk::metrics::AggregatorKind kind);
+
+ /**
+ * Set metric data for:
+ * Counter => Prometheus Counter
+ * Gauge => Prometheus Gauge
+ */
+ template <typename T>
+ static void SetData(std::vector<T> values,
+ const std::string &labels,
+ ::prometheus::MetricType type,
+ std::chrono::nanoseconds time,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Set metric data for:
+ * Histogram => Prometheus Histogram
+ */
+ template <typename T>
+ static void SetData(std::vector<T> values,
+ const std::vector<double> &boundaries,
+ const std::vector<int> &counts,
+ const std::string &labels,
+ std::chrono::nanoseconds time,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Set metric data for:
+ * MinMaxSumCount => Prometheus Gauge
+ * Use Average (sum / count) as the gauge metric
+ */
+ static void SetData(double value,
+ const std::string &labels,
+ std::chrono::nanoseconds time,
+ ::prometheus::MetricFamily *metric_family);
+
+ /**
+ * Set metric data for:
+ * Exact => Prometheus Summary
+ * Sketch => Prometheus Summary
+ */
+ template <typename T>
+ static void SetData(std::vector<T> values,
+ opentelemetry::sdk::metrics::AggregatorKind kind,
+ const std::vector<T> &quantiles,
+ const std::string &labels,
+ std::chrono::nanoseconds time,
+ ::prometheus::MetricFamily *metric_family,
+ bool do_quantile,
+ std::vector<double> quantile_points);
+
+ /**
+ * Set time and labels to metric data
+ */
+ static void SetMetricBasic(::prometheus::ClientMetric &metric,
+ std::chrono::nanoseconds time,
+ const std::string &labels);
+
+ /**
+ * Parse a string of labels (key:value) into a vector of pairs
+ * {,}
+ * {l1:v1,l2:v2,...,}
+ */
+ static std::vector<std::pair<std::string, std::string>> ParseLabel(std::string labels);
+
+ /**
+ * Build a quantiles vector from aggregator
+ */
+ template <typename T>
+ static std::vector<T> GetQuantilesVector(
+ std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<T>> aggregator,
+ const std::vector<double> &quantile_points);
+
+ /**
+ * Handle Counter and Gauge.
+ */
+ template <typename T>
+ static void SetValue(std::vector<T> values,
+ ::prometheus::MetricType type,
+ ::prometheus::ClientMetric *metric);
+
+ /**
+ * Handle Gauge from MinMaxSumCount
+ */
+ static void SetValue(double value, ::prometheus::ClientMetric *metric);
+
+ /**
+ * Handle Histogram
+ */
+ template <typename T>
+ static void SetValue(std::vector<T> values,
+ std::vector<double> boundaries,
+ std::vector<int> counts,
+ ::prometheus::ClientMetric *metric);
+
+ /**
+ * Handle Exact and Sketch
+ */
+ template <typename T>
+ static void SetValue(std::vector<T> values,
+ opentelemetry::sdk::metrics::AggregatorKind kind,
+ std::vector<T> quantiles,
+ ::prometheus::ClientMetric *metric,
+ bool do_quantile,
+ const std::vector<double> &quantile_points);
+};
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/collector.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/collector.cc
new file mode 100644
index 000000000..03793a8ee
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/collector.cc
@@ -0,0 +1,89 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+
+# include "opentelemetry/exporters/prometheus/collector.h"
+
+namespace metric_sdk = opentelemetry::sdk::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+/**
+ * Default Constructor.
+ *
+ * This constructor initializes the collection for metrics to export
+ * in this class with default capacity
+ */
+PrometheusCollector::PrometheusCollector(size_t max_collection_size)
+ : max_collection_size_(max_collection_size)
+{}
+
+/**
+ * Collects all metrics data from metricsToCollect collection.
+ *
+ * @return all metrics in the metricsToCollect snapshot
+ */
+std::vector<prometheus_client::MetricFamily> PrometheusCollector::Collect() const
+{
+ this->collection_lock_.lock();
+ if (metrics_to_collect_.empty())
+ {
+ this->collection_lock_.unlock();
+ return {};
+ }
+
+ std::vector<prometheus_client::MetricFamily> result;
+
+ // copy the intermediate collection, and then clear it
+ std::vector<std::unique_ptr<sdk::metrics::ResourceMetrics>> copied_data;
+ copied_data.swap(metrics_to_collect_);
+ this->collection_lock_.unlock();
+
+ result = PrometheusExporterUtils::TranslateToPrometheus(copied_data);
+ return result;
+}
+
+/**
+ * This function is called by export() function and add the collection of
+ * records to the metricsToCollect collection
+ *
+ * @param records a collection of records to add to the metricsToCollect collection
+ */
+void PrometheusCollector::AddMetricData(const sdk::metrics::ResourceMetrics &data)
+{
+ collection_lock_.lock();
+ if (metrics_to_collect_.size() + 1 <= max_collection_size_)
+ {
+ metrics_to_collect_.emplace_back(new sdk::metrics::ResourceMetrics{data});
+ }
+ collection_lock_.unlock();
+}
+
+/**
+ * Get the current collection in the collector.
+ *
+ * @return the current metrics_to_collect collection
+ */
+std::vector<std::unique_ptr<sdk::metrics::ResourceMetrics>> &PrometheusCollector::GetCollection()
+{
+ return metrics_to_collect_;
+}
+
+/**
+ * Gets the maximum size of the collection.
+ *
+ * @return max collection size
+ */
+int PrometheusCollector::GetMaxCollectionSize() const
+{
+ return max_collection_size_;
+}
+
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter.cc
new file mode 100644
index 000000000..a0bd9e27a
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter.cc
@@ -0,0 +1,100 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/exporters/prometheus/exporter.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace exporter
+{
+namespace metrics
+{
+/**
+ * Constructor - binds an exposer and collector to the exporter
+ * @param address: an address for an exposer that exposes
+ * an HTTP endpoint for the exporter to connect to
+ */
+PrometheusExporter::PrometheusExporter(const PrometheusExporterOptions &options)
+ : options_(options), is_shutdown_(false)
+{
+ exposer_ = std::unique_ptr<::prometheus::Exposer>(new ::prometheus::Exposer{options_.url});
+ collector_ = std::shared_ptr<PrometheusCollector>(new PrometheusCollector);
+
+ exposer_->RegisterCollectable(collector_);
+}
+
+/**
+ * PrometheusExporter constructor with no parameters
+ * Used for testing only
+ */
+PrometheusExporter::PrometheusExporter() : is_shutdown_(false)
+{
+ collector_ = std::unique_ptr<PrometheusCollector>(new PrometheusCollector);
+}
+
+/**
+ * Exports a batch of Metric Records.
+ * @param records: a collection of records to export
+ * @return: returns a ReturnCode detailing a success, or type of failure
+ */
+sdk::common::ExportResult PrometheusExporter::Export(
+ const sdk::metrics::ResourceMetrics &data) noexcept
+{
+ if (is_shutdown_)
+ {
+ return sdk::common::ExportResult::kFailure;
+ }
+ else if (collector_->GetCollection().size() + 1 > (size_t)collector_->GetMaxCollectionSize())
+ {
+ return sdk::common::ExportResult::kFailureFull;
+ }
+ else
+ {
+ collector_->AddMetricData(data);
+ return sdk::common::ExportResult::kSuccess;
+ }
+ return sdk::common::ExportResult::kSuccess;
+}
+
+bool PrometheusExporter::ForceFlush(std::chrono::microseconds timeout) noexcept
+{
+ return true;
+}
+
+/**
+ * Shuts down the exporter and does cleanup.
+ * Since Prometheus is a pull based interface,
+ * we cannot serve data remaining in the intermediate
+ * collection to to client an HTTP request being sent,
+ * so we flush the data.
+ */
+bool PrometheusExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ is_shutdown_ = true;
+ return true;
+
+ collector_->GetCollection().clear();
+}
+
+/**
+ * @return: returns a shared_ptr to
+ * the PrometheusCollector instance
+ */
+std::shared_ptr<PrometheusCollector> &PrometheusExporter::GetCollector()
+{
+ return collector_;
+}
+
+/**
+ * @return: Gets the shutdown status of the exporter
+ */
+bool PrometheusExporter::IsShutdown() const
+{
+ return is_shutdown_;
+}
+
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // ENABLE_METRICS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter_utils.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter_utils.cc
new file mode 100644
index 000000000..383925f98
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/exporter_utils.cc
@@ -0,0 +1,314 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef ENABLE_METRICS_PREVIEW
+# include <sstream>
+# include <utility>
+# include <vector>
+
+# include <prometheus/metric_type.h>
+# include "opentelemetry/exporters/prometheus/exporter_utils.h"
+# include "opentelemetry/sdk/metrics/export/metric_producer.h"
+
+# include "opentelemetry/sdk/common/global_log_handler.h"
+
+namespace prometheus_client = ::prometheus;
+namespace metric_sdk = opentelemetry::sdk::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace metrics
+{
+/**
+ * Helper function to convert OpenTelemetry metrics data collection
+ * to Prometheus metrics data collection
+ *
+ * @param records a collection of metrics in OpenTelemetry
+ * @return a collection of translated metrics that is acceptable by Prometheus
+ */
+std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateToPrometheus(
+ const std::vector<std::unique_ptr<sdk::metrics::ResourceMetrics>> &data)
+{
+ if (data.empty())
+ {
+ return {};
+ }
+
+ // initialize output vector
+ std::vector<prometheus_client::MetricFamily> output;
+
+ // iterate through the vector and set result data into it
+ for (const auto &r : data)
+ {
+ for (const auto &instrumentation_info : r->instrumentation_info_metric_data_)
+ {
+ for (const auto &metric_data : instrumentation_info.metric_data_)
+ {
+ auto origin_name = metric_data.instrument_descriptor.name_;
+ auto sanitized = SanitizeNames(origin_name);
+ prometheus_client::MetricFamily metric_family;
+ metric_family.name = sanitized;
+ metric_family.help = metric_data.instrument_descriptor.description_;
+ auto time = metric_data.start_ts.time_since_epoch();
+ for (const auto &point_data_attr : metric_data.point_data_attr_)
+ {
+ auto kind = getAggregationType(point_data_attr.point_data);
+ const prometheus_client::MetricType type = TranslateType(kind);
+ metric_family.type = type;
+ if (type == prometheus_client::MetricType::Histogram) // Histogram
+ {
+ auto histogram_point_data =
+ nostd::get<sdk::metrics::HistogramPointData>(point_data_attr.point_data);
+ auto boundaries = histogram_point_data.boundaries_;
+ auto counts = histogram_point_data.counts_;
+ SetData(std::vector<double>{nostd::get<double>(histogram_point_data.sum_),
+ (double)histogram_point_data.count_},
+ boundaries, counts, point_data_attr.attributes, time, &metric_family);
+ }
+ else // Counter, Untyped
+ {
+ auto sum_point_data =
+ nostd::get<sdk::metrics::SumPointData>(point_data_attr.point_data);
+ std::vector<metric_sdk::ValueType> values{sum_point_data.value_};
+ SetData(values, point_data_attr.attributes, type, time, &metric_family);
+ }
+ }
+ output.emplace_back(metric_family);
+ }
+ }
+ }
+ return output;
+}
+
+/**
+ * Sanitize the given metric name or label according to Prometheus rule.
+ *
+ * This function is needed because names in OpenTelemetry can contain
+ * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the
+ * name should only contain alphanumeric characters and '_'.
+ */
+std::string PrometheusExporterUtils::SanitizeNames(std::string name)
+{
+ // replace all '.' and '-' with '_'
+ std::replace(name.begin(), name.end(), '.', '_');
+ std::replace(name.begin(), name.end(), '-', '_');
+
+ return name;
+}
+
+metric_sdk::AggregationType PrometheusExporterUtils::getAggregationType(
+ const metric_sdk::PointType &point_type)
+{
+
+ if (nostd::holds_alternative<sdk::metrics::SumPointData>(point_type))
+ {
+ return metric_sdk::AggregationType::kSum;
+ }
+ else if (nostd::holds_alternative<sdk::metrics::DropPointData>(point_type))
+ {
+ return metric_sdk::AggregationType::kDrop;
+ }
+ else if (nostd::holds_alternative<sdk::metrics::HistogramPointData>(point_type))
+ {
+ return metric_sdk::AggregationType::kHistogram;
+ }
+ else if (nostd::holds_alternative<sdk::metrics::LastValuePointData>(point_type))
+ {
+ return metric_sdk::AggregationType::kLastValue;
+ }
+ return metric_sdk::AggregationType::kDefault;
+}
+
+/**
+ * Translate the OTel metric type to Prometheus metric type
+ */
+prometheus_client::MetricType PrometheusExporterUtils::TranslateType(
+ metric_sdk::AggregationType kind)
+{
+ switch (kind)
+ {
+ case metric_sdk::AggregationType::kSum:
+ return prometheus_client::MetricType::Counter;
+ case metric_sdk::AggregationType::kHistogram:
+ return prometheus_client::MetricType::Histogram;
+ default:
+ return prometheus_client::MetricType::Untyped;
+ }
+}
+
+/**
+ * Set metric data for:
+ * sum => Prometheus Counter
+ */
+template <typename T>
+void PrometheusExporterUtils::SetData(std::vector<T> values,
+ const metric_sdk::PointAttributes &labels,
+ prometheus_client::MetricType type,
+ std::chrono::nanoseconds time,
+ prometheus_client::MetricFamily *metric_family)
+{
+ metric_family->metric.emplace_back();
+ prometheus_client::ClientMetric &metric = metric_family->metric.back();
+ SetMetricBasic(metric, time, labels);
+ SetValue(values, type, &metric);
+}
+
+/**
+ * Set metric data for:
+ * Histogram => Prometheus Histogram
+ */
+template <typename T>
+void PrometheusExporterUtils::SetData(std::vector<T> values,
+ const opentelemetry::sdk::metrics::ListType &boundaries,
+ const std::vector<uint64_t> &counts,
+ const metric_sdk::PointAttributes &labels,
+ std::chrono::nanoseconds time,
+ prometheus_client::MetricFamily *metric_family)
+{
+ metric_family->metric.emplace_back();
+ prometheus_client::ClientMetric &metric = metric_family->metric.back();
+ SetMetricBasic(metric, time, labels);
+ if (nostd::holds_alternative<std::list<long>>(boundaries))
+ {
+ SetValue(values, nostd::get<std::list<long>>(boundaries), counts, &metric);
+ }
+ else
+ {
+ SetValue(values, nostd::get<std::list<double>>(boundaries), counts, &metric);
+ }
+}
+
+/**
+ * Set time and labels to metric data
+ */
+void PrometheusExporterUtils::SetMetricBasic(prometheus_client::ClientMetric &metric,
+ std::chrono::nanoseconds time,
+ const metric_sdk::PointAttributes &labels)
+{
+ metric.timestamp_ms = time.count() / 1000000;
+
+ // auto label_pairs = ParseLabel(labels);
+ if (!labels.empty())
+ {
+ metric.label.resize(labels.size());
+ size_t i = 0;
+ for (auto const &label : labels)
+ {
+ auto sanitized = SanitizeNames(label.first);
+ metric.label[i].name = sanitized;
+ metric.label[i++].value = AttributeValueToString(label.second);
+ }
+ }
+};
+
+std::string PrometheusExporterUtils::AttributeValueToString(
+ const opentelemetry::sdk::common::OwnedAttributeValue &value)
+{
+ std::string result;
+ if (nostd::holds_alternative<bool>(value))
+ {
+ result = nostd::get<bool>(value) ? "true" : "false";
+ }
+ else if (nostd::holds_alternative<int>(value))
+ {
+ result = std::to_string(nostd::get<int>(value));
+ }
+ else if (nostd::holds_alternative<int64_t>(value))
+ {
+ result = std::to_string(nostd::get<int64_t>(value));
+ }
+ else if (nostd::holds_alternative<unsigned int>(value))
+ {
+ result = std::to_string(nostd::get<unsigned int>(value));
+ }
+ else if (nostd::holds_alternative<uint64_t>(value))
+ {
+ result = std::to_string(nostd::get<uint64_t>(value));
+ }
+ else if (nostd::holds_alternative<double>(value))
+ {
+ result = std::to_string(nostd::get<double>(value));
+ }
+ else if (nostd::holds_alternative<std::string>(value))
+ {
+ result = nostd::get<std::string>(value);
+ }
+ else
+ {
+ OTEL_INTERNAL_LOG_WARN(
+ "[Prometheus Exporter] AttributeValueToString - "
+ " Nested attributes not supported - ignored");
+ }
+ return result;
+}
+
+/**
+ * Handle Counter.
+ */
+template <typename T>
+void PrometheusExporterUtils::SetValue(std::vector<T> values,
+ prometheus_client::MetricType type,
+ prometheus_client::ClientMetric *metric)
+{
+ double value = 0.0;
+ const auto &value_var = values[0];
+ if (nostd::holds_alternative<long>(value_var))
+ {
+ value = nostd::get<long>(value_var);
+ }
+ else
+ {
+ value = nostd::get<double>(value_var);
+ }
+
+ switch (type)
+ {
+ case prometheus_client::MetricType::Counter: {
+ metric->counter.value = value;
+ break;
+ }
+ case prometheus_client::MetricType::Untyped: {
+ metric->untyped.value = value;
+ break;
+ }
+ default:
+ return;
+ }
+}
+
+/**
+ * Handle Histogram
+ */
+template <typename T, typename U>
+void PrometheusExporterUtils::SetValue(std::vector<T> values,
+ const std::list<U> &boundaries,
+ const std::vector<uint64_t> &counts,
+ prometheus_client::ClientMetric *metric)
+{
+ metric->histogram.sample_sum = values[0];
+ metric->histogram.sample_count = values[1];
+ int cumulative = 0;
+ std::vector<prometheus_client::ClientMetric::Bucket> buckets;
+ uint32_t idx = 0;
+ for (const auto &boundary : boundaries)
+ {
+ prometheus_client::ClientMetric::Bucket bucket;
+ cumulative += counts[idx];
+ bucket.cumulative_count = cumulative;
+ bucket.upper_bound = boundary;
+ buckets.emplace_back(bucket);
+ ++idx;
+ }
+ prometheus_client::ClientMetric::Bucket bucket;
+ cumulative += counts[idx];
+ bucket.cumulative_count = cumulative;
+ bucket.upper_bound = std::numeric_limits<double>::infinity();
+ buckets.emplace_back(bucket);
+ metric->histogram.bucket = buckets;
+}
+
+} // namespace metrics
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_collector.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_collector.cc
new file mode 100644
index 000000000..53b7913de
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_collector.cc
@@ -0,0 +1,166 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <iostream>
+
+# include "opentelemetry/exporters/prometheus/prometheus_collector.h"
+
+namespace metric_sdk = opentelemetry::sdk::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace prometheus
+{
+/**
+ * Default Constructor.
+ *
+ * This constructor initializes the collection for metrics to export
+ * in this class with default capacity
+ */
+PrometheusCollector::PrometheusCollector(size_t max_collection_size)
+ : max_collection_size_(max_collection_size)
+{
+ metrics_to_collect_ =
+ std::unique_ptr<std::vector<metric_sdk::Record>>(new std::vector<metric_sdk::Record>);
+}
+
+/**
+ * Collects all metrics data from metricsToCollect collection.
+ *
+ * @return all metrics in the metricsToCollect snapshot
+ */
+std::vector<prometheus_client::MetricFamily> PrometheusCollector::Collect() const
+{
+ this->collection_lock_.lock();
+ if (metrics_to_collect_->empty())
+ {
+ this->collection_lock_.unlock();
+ return {};
+ }
+ this->collection_lock_.unlock();
+
+ std::vector<prometheus_client::MetricFamily> result;
+
+ // copy the intermediate collection, and then clear it
+ std::vector<metric_sdk::Record> copied_data;
+
+ this->collection_lock_.lock();
+ copied_data = std::vector<metric_sdk::Record>(*metrics_to_collect_);
+ metrics_to_collect_->clear();
+ this->collection_lock_.unlock();
+
+ result = PrometheusExporterUtils::TranslateToPrometheus(copied_data);
+ return result;
+}
+
+/**
+ * This function is called by export() function and add the collection of
+ * records to the metricsToCollect collection
+ *
+ * @param records a collection of records to add to the metricsToCollect collection
+ */
+void PrometheusCollector::AddMetricData(const std::vector<sdk::metrics::Record> &records)
+{
+ if (records.empty())
+ {
+ return;
+ }
+
+ collection_lock_.lock();
+ if (metrics_to_collect_->size() + records.size() <= max_collection_size_)
+ {
+ /**
+ * ValidAggregator is a lambda that checks a Record to see if its
+ * Aggregator is a valid nostd::shared_ptr and not a nullptr.
+ */
+ auto ValidAggregator = [](sdk::metrics::Record record) {
+ auto aggregator_variant = record.GetAggregator();
+ if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<int>>>(
+ aggregator_variant))
+ {
+ auto aggregator =
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(aggregator_variant);
+ if (!aggregator)
+ {
+ return false;
+ }
+ }
+ else if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<short>>>(
+ aggregator_variant))
+ {
+ auto aggregator =
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(aggregator_variant);
+ if (!aggregator)
+ {
+ return false;
+ }
+ }
+ else if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<float>>>(
+ aggregator_variant))
+ {
+ auto aggregator =
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(aggregator_variant);
+ if (!aggregator)
+ {
+ return false;
+ }
+ }
+ else if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<double>>>(
+ aggregator_variant))
+ {
+ auto aggregator =
+ nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(aggregator_variant);
+ if (!aggregator)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ for (auto &r : records)
+ {
+ if (ValidAggregator(r))
+ {
+ metrics_to_collect_->emplace_back(r);
+ }
+ // Drop the record and write to std::cout
+ else
+ {
+ // Cannot call non const functions on const Record r
+ sdk::metrics::Record c = r;
+ std::cout << "Dropped Record containing invalid aggregator with name: " + c.GetName()
+ << std::endl;
+ }
+ }
+ }
+ collection_lock_.unlock();
+}
+
+/**
+ * Get the current collection in the collector.
+ *
+ * @return the current metrics_to_collect collection
+ */
+std::vector<sdk::metrics::Record> PrometheusCollector::GetCollection()
+{
+ return *metrics_to_collect_;
+}
+
+/**
+ * Gets the maximum size of the collection.
+ *
+ * @return max collection size
+ */
+int PrometheusCollector::GetMaxCollectionSize() const
+{
+ return max_collection_size_;
+}
+
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter.cc
new file mode 100644
index 000000000..b64af1e90
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright The 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.
+ */
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include "opentelemetry/exporters/prometheus/prometheus_exporter.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+namespace exporter
+{
+namespace prometheus
+{
+/**
+ * Constructor - binds an exposer and collector to the exporter
+ * @param address: an address for an exposer that exposes
+ * an HTTP endpoint for the exporter to connect to
+ */
+PrometheusExporter::PrometheusExporter(std::string &address) : is_shutdown_(false)
+{
+ exposer_ = std::unique_ptr<::prometheus::Exposer>(new ::prometheus::Exposer{address});
+ collector_ = std::shared_ptr<PrometheusCollector>(new PrometheusCollector);
+
+ exposer_->RegisterCollectable(collector_);
+}
+
+/**
+ * PrometheusExporter constructor with no parameters
+ * Used for testing only
+ */
+PrometheusExporter::PrometheusExporter() : is_shutdown_(false)
+{
+ collector_ = std::unique_ptr<PrometheusCollector>(new PrometheusCollector);
+}
+
+/**
+ * Exports a batch of Metric Records.
+ * @param records: a collection of records to export
+ * @return: returns a ReturnCode detailing a success, or type of failure
+ */
+sdk::common::ExportResult PrometheusExporter::Export(
+ const std::vector<sdk::metrics::Record> &records) noexcept
+{
+ if (is_shutdown_)
+ {
+ return sdk::common::ExportResult::kFailure;
+ }
+ else if (records.empty())
+ {
+ return sdk::common::ExportResult::kFailureInvalidArgument;
+ }
+ else if (collector_->GetCollection().size() + records.size() >
+ (size_t)collector_->GetMaxCollectionSize())
+ {
+ return sdk::common::ExportResult::kFailureFull;
+ }
+ else
+ {
+ collector_->AddMetricData(records);
+ return sdk::common::ExportResult::kSuccess;
+ }
+}
+
+/**
+ * Shuts down the exporter and does cleanup.
+ * Since Prometheus is a pull based interface,
+ * we cannot serve data remaining in the intermediate
+ * collection to to client an HTTP request being sent,
+ * so we flush the data.
+ */
+void PrometheusExporter::Shutdown() noexcept
+{
+ is_shutdown_ = true;
+
+ collector_->GetCollection().clear();
+}
+
+/**
+ * @return: returns a shared_ptr to
+ * the PrometheusCollector instance
+ */
+std::shared_ptr<PrometheusCollector> &PrometheusExporter::GetCollector()
+{
+ return collector_;
+}
+
+/**
+ * @return: Gets the shutdown status of the exporter
+ */
+bool PrometheusExporter::IsShutdown() const
+{
+ return is_shutdown_;
+}
+
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // ENABLE_METRICS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter_utils.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter_utils.cc
new file mode 100644
index 000000000..5eec9e3e8
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/src/prometheus_exporter_utils.cc
@@ -0,0 +1,446 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <iostream>
+# include <sstream>
+# include <utility>
+# include <vector>
+
+# include "opentelemetry/exporters/prometheus/prometheus_exporter_utils.h"
+# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h"
+# include "prometheus/metric_type.h"
+
+namespace prometheus_client = ::prometheus;
+namespace metric_sdk = opentelemetry::sdk::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace prometheus
+{
+/**
+ * Helper function to convert OpenTelemetry metrics data collection
+ * to Prometheus metrics data collection
+ *
+ * @param records a collection of metrics in OpenTelemetry
+ * @return a collection of translated metrics that is acceptable by Prometheus
+ */
+std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateToPrometheus(
+ const std::vector<metric_sdk::Record> &records)
+{
+ if (records.empty())
+ {
+ return {};
+ }
+
+ // initialize output vector
+ std::vector<prometheus_client::MetricFamily> output(records.size());
+
+ // iterate through the vector and set result data into it
+ int i = 0;
+ for (auto r : records)
+ {
+ SetMetricFamily(r, &output[i]);
+ i++;
+ }
+
+ return output;
+}
+
+// ======================= private helper functions =========================
+/**
+ * Set value to metric family according to record
+ */
+void PrometheusExporterUtils::SetMetricFamily(metric_sdk::Record &record,
+ prometheus_client::MetricFamily *metric_family)
+{
+
+ auto origin_name = record.GetName();
+ auto sanitized = SanitizeNames(origin_name);
+ metric_family->name = sanitized;
+ metric_family->help = record.GetDescription();
+
+ // unpack the variant and set the metric data to metric family struct
+ auto agg_var = record.GetAggregator();
+ auto labels_str = record.GetLabels();
+ if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<int>>>(agg_var))
+ {
+ auto aggregator = nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(agg_var);
+ SetMetricFamilyByAggregator(aggregator, labels_str, metric_family);
+ }
+ else if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<short>>>(agg_var))
+ {
+ auto aggregator = nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(agg_var);
+ SetMetricFamilyByAggregator(aggregator, labels_str, metric_family);
+ }
+ else if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<float>>>(
+ record.GetAggregator()))
+ {
+ auto aggregator = nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(agg_var);
+ SetMetricFamilyByAggregator(aggregator, labels_str, metric_family);
+ }
+ else if (nostd::holds_alternative<std::shared_ptr<metric_sdk::Aggregator<double>>>(
+ record.GetAggregator()))
+ {
+ auto aggregator = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(agg_var);
+ SetMetricFamilyByAggregator(aggregator, labels_str, metric_family);
+ }
+}
+
+/**
+ * Sanitize the given metric name or label according to Prometheus rule.
+ *
+ * This function is needed because names in OpenTelemetry can contain
+ * alphanumeric characters, '_', '.', and '-', whereas in Prometheus the
+ * name should only contain alphanumeric characters and '_'.
+ */
+std::string PrometheusExporterUtils::SanitizeNames(std::string name)
+{
+ // replace all '.' and '-' with '_'
+ std::replace(name.begin(), name.end(), '.', '_');
+ std::replace(name.begin(), name.end(), '-', '_');
+
+ return name;
+}
+
+/**
+ * Set value to metric family for different aggregator
+ */
+template <typename T>
+void PrometheusExporterUtils::SetMetricFamilyByAggregator(
+ std::shared_ptr<metric_sdk::Aggregator<T>> aggregator,
+ std::string labels_str,
+ prometheus_client::MetricFamily *metric_family)
+{
+ // get aggregator kind and translate to Prometheus metric type
+ auto kind = aggregator->get_aggregator_kind();
+ const prometheus_client::MetricType type = TranslateType(kind);
+ metric_family->type = type;
+ // get check-pointed values, label string and check-pointed time
+ auto checkpointed_values = aggregator->get_checkpoint();
+ auto time = aggregator->get_checkpoint_timestamp().time_since_epoch();
+
+ if (type == prometheus_client::MetricType::Histogram) // Histogram
+ {
+ auto boundaries = aggregator->get_boundaries();
+ auto counts = aggregator->get_counts();
+ SetData(checkpointed_values, boundaries, counts, labels_str, time, metric_family);
+ }
+ else if (type == prometheus_client::MetricType::Summary) // Sketch, Exact
+ {
+ std::vector<double> quantile_points = {0, 0.5, 0.9, 0.95, 0.99, 1};
+ if (kind == metric_sdk::AggregatorKind::Exact)
+ {
+ std::vector<T> quantiles;
+ bool do_quantile = aggregator->get_quant_estimation();
+ if (do_quantile)
+ {
+ quantiles = GetQuantilesVector(aggregator, quantile_points);
+ }
+ SetData(checkpointed_values, kind, quantiles, labels_str, time, metric_family, do_quantile,
+ quantile_points);
+ }
+ else if (kind == metric_sdk::AggregatorKind::Sketch)
+ {
+ auto quantiles = GetQuantilesVector(aggregator, quantile_points);
+ SetData(checkpointed_values, kind, quantiles, labels_str, time, metric_family, true,
+ quantile_points);
+ }
+ }
+ else // Counter, Gauge, MinMaxSumCount, Untyped
+ {
+ // Handle MinMaxSumCount: https://github.com/open-telemetry/opentelemetry-cpp/issues/228
+ // Use sum/count is ok.
+ if (kind == metric_sdk::AggregatorKind::MinMaxSumCount)
+ {
+ double avg = (double)checkpointed_values[2] / checkpointed_values[3];
+ SetData(avg, labels_str, time, metric_family);
+ }
+ else
+ {
+ SetData(checkpointed_values, labels_str, type, time, metric_family);
+ }
+ }
+}
+
+/**
+ * Translate the OTel metric type to Prometheus metric type
+ */
+prometheus_client::MetricType PrometheusExporterUtils::TranslateType(
+ metric_sdk::AggregatorKind kind)
+{
+ switch (kind)
+ {
+ case metric_sdk::AggregatorKind::Counter:
+ return prometheus_client::MetricType::Counter;
+ case metric_sdk::AggregatorKind::Gauge:
+ case metric_sdk::AggregatorKind::MinMaxSumCount:
+ return prometheus_client::MetricType::Gauge;
+ case metric_sdk::AggregatorKind::Histogram:
+ return prometheus_client::MetricType::Histogram;
+ case metric_sdk::AggregatorKind::Sketch:
+ case metric_sdk::AggregatorKind::Exact:
+ return prometheus_client::MetricType::Summary;
+ default:
+ return prometheus_client::MetricType::Untyped;
+ }
+}
+
+/**
+ * Set metric data for:
+ * Counter => Prometheus Counter
+ * Gauge => Prometheus Gauge
+ */
+template <typename T>
+void PrometheusExporterUtils::SetData(std::vector<T> values,
+ const std::string &labels,
+ prometheus_client::MetricType type,
+ std::chrono::nanoseconds time,
+ prometheus_client::MetricFamily *metric_family)
+{
+ metric_family->metric.emplace_back();
+ prometheus_client::ClientMetric &metric = metric_family->metric.back();
+ SetMetricBasic(metric, time, labels);
+ SetValue(values, type, &metric);
+}
+
+/**
+ * Set metric data for:
+ * Histogram => Prometheus Histogram
+ */
+template <typename T>
+void PrometheusExporterUtils::SetData(std::vector<T> values,
+ const std::vector<double> &boundaries,
+ const std::vector<int> &counts,
+ const std::string &labels,
+ std::chrono::nanoseconds time,
+ prometheus_client::MetricFamily *metric_family)
+{
+ metric_family->metric.emplace_back();
+ prometheus_client::ClientMetric &metric = metric_family->metric.back();
+ SetMetricBasic(metric, time, labels);
+ SetValue(values, boundaries, counts, &metric);
+}
+
+/**
+ * Set metric data for:
+ * MinMaxSumCount => Prometheus Gauge
+ * Use Average (sum / count) as the gauge metric
+ */
+void PrometheusExporterUtils::SetData(double value,
+ const std::string &labels,
+ std::chrono::nanoseconds time,
+ prometheus_client::MetricFamily *metric_family)
+{
+ metric_family->metric.emplace_back();
+ prometheus_client::ClientMetric &metric = metric_family->metric.back();
+ SetMetricBasic(metric, time, labels);
+ SetValue(value, &metric);
+}
+
+/**
+ * Set metric data for:
+ * Exact => Prometheus Summary
+ * Sketch => Prometheus Summary
+ */
+template <typename T>
+void PrometheusExporterUtils::SetData(std::vector<T> values,
+ metric_sdk::AggregatorKind kind,
+ const std::vector<T> &quantiles,
+ const std::string &labels,
+ std::chrono::nanoseconds time,
+ prometheus_client::MetricFamily *metric_family,
+ bool do_quantile,
+ std::vector<double> quantile_points)
+{
+ metric_family->metric.emplace_back();
+ prometheus_client::ClientMetric &metric = metric_family->metric.back();
+ SetMetricBasic(metric, time, labels);
+ SetValue(values, kind, quantiles, &metric, do_quantile, quantile_points);
+}
+
+/**
+ * Set time and labels to metric data
+ */
+void PrometheusExporterUtils::SetMetricBasic(prometheus_client::ClientMetric &metric,
+ std::chrono::nanoseconds time,
+ const std::string &labels)
+{
+ metric.timestamp_ms = time.count() / 1000000;
+
+ auto label_pairs = ParseLabel(labels);
+ if (!label_pairs.empty())
+ {
+ metric.label.resize(label_pairs.size());
+ for (size_t i = 0; i < label_pairs.size(); ++i)
+ {
+ auto origin_name = label_pairs[i].first;
+ auto sanitized = SanitizeNames(origin_name);
+ metric.label[i].name = sanitized;
+ metric.label[i].value = label_pairs[i].second;
+ }
+ }
+};
+
+/**
+ * Parse a string of labels (key:value) into a vector of pairs
+ * {,}
+ * {l1:v1,l2:v2,...,}
+ */
+std::vector<std::pair<std::string, std::string>> PrometheusExporterUtils::ParseLabel(
+ std::string labels)
+{
+ labels = labels.substr(1, labels.size() - 2);
+
+ std::vector<std::string> paired_labels;
+ std::stringstream s_stream(labels);
+ while (s_stream.good())
+ {
+ std::string substr;
+ getline(s_stream, substr, ','); // get first string delimited by comma
+ if (!substr.empty())
+ {
+ paired_labels.push_back(substr);
+ }
+ }
+
+ std::vector<std::pair<std::string, std::string>> result;
+ for (auto &paired : paired_labels)
+ {
+ std::size_t split_index = paired.find(':');
+ std::string label = paired.substr(0, split_index);
+ std::string value = paired.substr(split_index + 1);
+ result.emplace_back(std::pair<std::string, std::string>(label, value));
+ }
+
+ return result;
+}
+
+/**
+ * Build a quantiles vector from aggregator
+ */
+template <typename T>
+std::vector<T> PrometheusExporterUtils::GetQuantilesVector(
+ std::shared_ptr<metric_sdk::Aggregator<T>> aggregator,
+ const std::vector<double> &quantile_points)
+{
+ std::vector<T> quantiles;
+ for (double q : quantile_points)
+ {
+ T quantile = aggregator->get_quantiles(q);
+ quantiles.emplace_back(quantile);
+ }
+ return quantiles;
+}
+
+/**
+ * Handle Counter and Gauge.
+ */
+template <typename T>
+void PrometheusExporterUtils::SetValue(std::vector<T> values,
+ prometheus_client::MetricType type,
+ prometheus_client::ClientMetric *metric)
+{
+ switch (type)
+ {
+ case prometheus_client::MetricType::Counter: {
+ metric->counter.value = values[0];
+ break;
+ }
+ case prometheus_client::MetricType::Gauge: {
+ metric->gauge.value = values[0];
+ break;
+ }
+ case prometheus_client::MetricType::Untyped: {
+ metric->untyped.value = values[0];
+ break;
+ }
+ default:
+ return;
+ }
+}
+
+/**
+ * Handle Gauge from MinMaxSumCount
+ */
+void PrometheusExporterUtils::SetValue(double value, prometheus_client::ClientMetric *metric)
+{
+ metric->gauge.value = value;
+}
+
+/**
+ * Handle Histogram
+ */
+template <typename T>
+void PrometheusExporterUtils::SetValue(std::vector<T> values,
+ std::vector<double> boundaries,
+ std::vector<int> counts,
+ prometheus_client::ClientMetric *metric)
+{
+ metric->histogram.sample_sum = values[0];
+ metric->histogram.sample_count = values[1];
+ int cumulative = 0;
+ std::vector<prometheus_client::ClientMetric::Bucket> buckets;
+ for (size_t i = 0; i < boundaries.size() + 1; i++)
+ {
+ prometheus_client::ClientMetric::Bucket bucket;
+ cumulative += counts[i];
+ bucket.cumulative_count = cumulative;
+ if (i != boundaries.size())
+ {
+ bucket.upper_bound = boundaries[i];
+ }
+ else
+ {
+ bucket.upper_bound = std::numeric_limits<double>::infinity();
+ }
+ buckets.emplace_back(bucket);
+ }
+ metric->histogram.bucket = buckets;
+}
+
+/**
+ * Handle Exact and Sketch
+ */
+template <typename T>
+void PrometheusExporterUtils::SetValue(std::vector<T> values,
+ metric_sdk::AggregatorKind kind,
+ std::vector<T> quantiles,
+ prometheus_client::ClientMetric *metric,
+ bool do_quantile,
+ const std::vector<double> &quantile_points)
+{
+ if (kind == metric_sdk::AggregatorKind::Exact)
+ {
+ metric->summary.sample_count = values.size();
+ auto sum = 0;
+ for (auto val : values)
+ {
+ sum += val;
+ }
+ metric->summary.sample_sum = sum;
+ }
+ else if (kind == metric_sdk::AggregatorKind::Sketch)
+ {
+ metric->summary.sample_sum = values[0];
+ metric->summary.sample_count = values[1];
+ }
+
+ if (do_quantile)
+ {
+ std::vector<prometheus_client::ClientMetric::Quantile> prometheus_quantiles;
+ for (size_t i = 0; i < quantiles.size(); i++)
+ {
+ prometheus_client::ClientMetric::Quantile quantile;
+ quantile.quantile = quantile_points[i];
+ quantile.value = quantiles[i];
+ prometheus_quantiles.emplace_back(quantile);
+ }
+ metric->summary.quantile = prometheus_quantiles;
+ }
+}
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/CMakeLists.txt
new file mode 100644
index 000000000..1a2246979
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/CMakeLists.txt
@@ -0,0 +1,13 @@
+if(WITH_METRICS_PREVIEW)
+ foreach(testname prometheus_exporter_test prometheus_collector_test
+ prometheus_exporter_utils_test)
+ add_executable(${testname} "${testname}.cc")
+ target_link_libraries(
+ ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ prometheus_exporter_deprecated prometheus-cpp::pull)
+ gtest_add_tests(
+ TARGET ${testname}
+ TEST_PREFIX exporter.
+ TEST_LIST ${testname})
+ endforeach()
+endif()
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_collector_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_collector_test.cc
new file mode 100644
index 000000000..d1aff1b0f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_collector_test.cc
@@ -0,0 +1,756 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include <future>
+# include <map>
+# include <thread>
+
+# include "opentelemetry/_metrics/instrument.h"
+# include "opentelemetry/exporters/prometheus/prometheus_collector.h"
+# include "opentelemetry/sdk/_metrics/aggregator/aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h"
+# include "opentelemetry/sdk/_metrics/record.h"
+# include "opentelemetry/version.h"
+
+using opentelemetry::exporter::prometheus::PrometheusCollector;
+namespace metric_api = opentelemetry::metrics;
+namespace metric_sdk = opentelemetry::sdk::metrics;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+
+/**
+ * CreateAgg() is a helper function that returns a
+ * nostd::shared_ptr given an AggregatorKind
+ */
+template <typename T>
+std::shared_ptr<metric_sdk::Aggregator<T>> CreateAgg(metric_sdk::AggregatorKind kind,
+ bool exactMode = true)
+{
+ std::shared_ptr<metric_sdk::Aggregator<T>> aggregator;
+ switch (kind)
+ {
+ case metric_sdk::AggregatorKind::Counter: {
+ aggregator = std::shared_ptr<metric_sdk::Aggregator<T>>(
+ new metric_sdk::CounterAggregator<T>(metric_api::InstrumentKind::Counter));
+ break;
+ }
+ case metric_sdk::AggregatorKind::MinMaxSumCount: {
+ aggregator = std::shared_ptr<metric_sdk::Aggregator<T>>(
+ new metric_sdk::MinMaxSumCountAggregator<T>(metric_api::InstrumentKind::Counter));
+ break;
+ }
+ case metric_sdk::AggregatorKind::Gauge: {
+ aggregator = std::shared_ptr<metric_sdk::Aggregator<T>>(
+ new metric_sdk::GaugeAggregator<T>(metric_api::InstrumentKind::Counter));
+ break;
+ }
+ case metric_sdk::AggregatorKind::Sketch: {
+ aggregator = std::shared_ptr<metric_sdk::Aggregator<T>>(
+ new metric_sdk::SketchAggregator<T>(metric_api::InstrumentKind::Counter, 0.000005));
+ break;
+ }
+ case metric_sdk::AggregatorKind::Histogram: {
+ std::vector<double> boundaries{10, 20};
+ aggregator = std::shared_ptr<metric_sdk::Aggregator<T>>(
+ new metric_sdk::HistogramAggregator<T>(metric_api::InstrumentKind::Counter, boundaries));
+ break;
+ }
+ case metric_sdk::AggregatorKind::Exact: {
+ aggregator = std::shared_ptr<metric_sdk::Aggregator<T>>(
+ new metric_sdk::ExactAggregator<T>(metric_api::InstrumentKind::Counter, exactMode));
+ break;
+ }
+ default:
+ aggregator = nullptr;
+ }
+ return aggregator;
+}
+
+/**
+ * Populate() updates the aggregator with values and checkpoints it based
+ * on what its AggregatorKind is
+ */
+template <typename T>
+void Populate(std::shared_ptr<metric_sdk::Aggregator<T>> &aggregator)
+{
+ if (aggregator->get_aggregator_kind() == metric_sdk::AggregatorKind::Counter)
+ {
+ aggregator->update(10.0);
+ aggregator->update(5.0);
+ aggregator->checkpoint();
+ }
+ else if (aggregator->get_aggregator_kind() == metric_sdk::AggregatorKind::MinMaxSumCount)
+ {
+ aggregator->update(10);
+ aggregator->update(2);
+ aggregator->update(5);
+ aggregator->checkpoint();
+ }
+ else if (aggregator->get_aggregator_kind() == metric_sdk::AggregatorKind::Gauge)
+ {
+ aggregator->update(10);
+ aggregator->update(5);
+ aggregator->checkpoint();
+ }
+ else if (aggregator->get_aggregator_kind() == metric_sdk::AggregatorKind::Sketch)
+ {
+ for (double i = 0; i < 10.0; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ }
+ else if (aggregator->get_aggregator_kind() == metric_sdk::AggregatorKind::Histogram)
+ {
+ for (float i = 0; i < 30.0; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ }
+ else if (aggregator->get_aggregator_kind() == metric_sdk::AggregatorKind::Exact)
+ {
+ for (double i = 0; i < 10.0; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ }
+}
+
+/**
+ * Helper function to create a collection of records taken from
+ * a aggregator of specified AggregatorKind
+ */
+template <typename T>
+std::vector<metric_sdk::Record> CreateRecords(int num,
+ metric_sdk::AggregatorKind kind,
+ bool exactMode = true)
+{
+ std::vector<metric_sdk::Record> records;
+
+ for (int i = 0; i < num; i++)
+ {
+ std::string name = "record-" + std::to_string(i);
+ std::string description = "record " + std::to_string(i) + " for test purpose";
+ std::string labels = "{label1:v1,label2:v2,}";
+ std::shared_ptr<metric_sdk::Aggregator<T>> aggregator = CreateAgg<T>(kind, exactMode);
+ Populate(aggregator);
+
+ metric_sdk::Record r{name, description, labels, aggregator};
+ records.push_back(r);
+ }
+ return records;
+}
+
+// ==================== Test for addMetricsData() function ======================
+
+/**
+ * AddMetricData() should be able to successfully add a collection
+ * of Records with Counter Aggregators. It checks that the cumulative
+ * sum of updates to the aggregator of a record before and after AddMetricData()
+ * is called are equal.
+ */
+TEST(PrometheusCollector, AddMetricDataWithCounterRecordsSuccessfully)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 2;
+
+ // construct a collection of records with CounterAggregators and double
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<double>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Collection size should be the same as the size
+ // of the records collection passed to addMetricData()
+ ASSERT_EQ(collector.GetCollection().size(), records.size());
+
+ // check values of records created vs records from metricsToCollect,
+ // accessed by getCollection()
+
+ for (int i = 0; i < num_records; i++)
+ {
+ metric_sdk::Record before = records[i];
+ metric_sdk::Record after = collector.GetCollection()[i];
+
+ ASSERT_EQ(before.GetName(), after.GetName());
+
+ ASSERT_EQ(before.GetDescription(), after.GetDescription());
+
+ ASSERT_EQ(before.GetLabels(), after.GetLabels());
+
+ auto before_agg_var = before.GetAggregator();
+ auto before_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(before_agg_var);
+
+ auto after_agg_var = after.GetAggregator();
+ auto after_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(after_agg_var);
+
+ ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size());
+ for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]);
+ }
+ }
+}
+
+/**
+ * AddMetricData() should be able to successfully add a collection
+ * of Records with MinMaxSumCount Aggregators. It checks that the min, max,
+ * sum, and count of updates to the aggregator of a record before and after AddMetricData()
+ * is called are equal.
+ */
+TEST(PrometheusCollector, AddMetricDataWithMinMaxSumCountRecordsSuccessfully)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 2;
+
+ // construct a collection of records with MinMaxSumCountAggregators and short
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<short>(num_records, metric_sdk::AggregatorKind::MinMaxSumCount);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Collection size should be the same as the size
+ // of the records collection passed to addMetricData()
+ ASSERT_EQ(collector.GetCollection().size(), records.size());
+
+ // check values of records created vs records from metricsToCollect,
+ // accessed by getCollection()
+
+ for (int i = 0; i < num_records; i++)
+ {
+ metric_sdk::Record before = records[i];
+ metric_sdk::Record after = collector.GetCollection()[i];
+
+ ASSERT_EQ(before.GetName(), after.GetName());
+
+ ASSERT_EQ(before.GetDescription(), after.GetDescription());
+
+ ASSERT_EQ(before.GetLabels(), after.GetLabels());
+
+ auto before_agg_var = before.GetAggregator();
+ auto before_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(before_agg_var);
+
+ auto after_agg_var = after.GetAggregator();
+ auto after_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<short>>>(after_agg_var);
+
+ ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size());
+ for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]);
+ }
+ }
+}
+
+/**
+ * AddMetricData() should be able to successfully add a collection
+ * of Records with Gauge Aggregators. It checks that the last update
+ * to the aggregator of a record before and after AddMetricData()
+ * is called are equal.
+ */
+TEST(PrometheusCollector, AddMetricDataWithGaugeRecordsSuccessfully)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 2;
+
+ // construct a collection of records with GaugeAggregators and int
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Gauge);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Collection size should be the same as the size
+ // of the records collection passed to addMetricData()
+ ASSERT_EQ(collector.GetCollection().size(), records.size());
+
+ // check values of records created vs records from metricsToCollect,
+ // accessed by getCollection()
+
+ for (int i = 0; i < num_records; i++)
+ {
+ metric_sdk::Record before = records[i];
+ metric_sdk::Record after = collector.GetCollection()[i];
+
+ ASSERT_EQ(before.GetName(), after.GetName());
+
+ ASSERT_EQ(before.GetDescription(), after.GetDescription());
+
+ ASSERT_EQ(before.GetLabels(), after.GetLabels());
+
+ auto before_agg_var = before.GetAggregator();
+ auto before_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(before_agg_var);
+
+ auto after_agg_var = after.GetAggregator();
+ auto after_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<int>>>(after_agg_var);
+
+ ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size());
+ for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]);
+ }
+ }
+}
+
+/**
+ * AddMetricData() should be able to successfully add a collection
+ * of Records with Sketch Aggregators. It checks that the sum of updates
+ * and count of values added for a record before and after being added are
+ * equal using get_checkpoint(). It also checks the same for buckets, in
+ * get_boundaries(), and counts for buckets, in get_counts().
+ */
+TEST(PrometheusCollector, AddMetricDataWithSketchRecordsSuccessfully)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 2;
+
+ // construct a collection of records with SketchAggregators and double
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<double>(num_records, metric_sdk::AggregatorKind::Sketch);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Collection size should be the same as the size
+ // of the records collection passed to addMetricData()
+ ASSERT_EQ(collector.GetCollection().size(), records.size());
+
+ // check values of records created vs records from metricsToCollect,
+ // accessed by getCollection()
+
+ for (int i = 0; i < num_records; i++)
+ {
+ metric_sdk::Record before = records[i];
+ metric_sdk::Record after = collector.GetCollection()[i];
+
+ ASSERT_EQ(before.GetName(), after.GetName());
+
+ ASSERT_EQ(before.GetDescription(), after.GetDescription());
+
+ ASSERT_EQ(before.GetLabels(), after.GetLabels());
+
+ auto before_agg_var = before.GetAggregator();
+ auto before_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(before_agg_var);
+
+ auto after_agg_var = after.GetAggregator();
+ auto after_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(after_agg_var);
+
+ ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size());
+ for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]);
+ }
+ for (size_t i = 0; i < before_agg->get_boundaries().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_boundaries()[i], after_agg->get_boundaries()[i]);
+ }
+ for (size_t i = 0; i < before_agg->get_counts().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_counts()[i], after_agg->get_counts()[i]);
+ }
+ }
+}
+
+/**
+ * AddMetricData() should be able to successfully add a collection
+ * of Records with Histogram Aggregators. It checks that the sum of
+ * updates, number of updates, boundaries, and counts for each bucket
+ * for the aggregator of a record before and after AddMetricData()
+ * is called are equal.
+ */
+TEST(PrometheusCollector, AddMetricDataWithHistogramRecordsSuccessfully)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 2;
+
+ // construct a collection of records with HistogramAggregators and float
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<float>(num_records, metric_sdk::AggregatorKind::Histogram);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Collection size should be the same as the size
+ // of the records collection passed to addMetricData()
+ ASSERT_EQ(collector.GetCollection().size(), records.size());
+
+ // check values of records created vs records from metricsToCollect,
+ // accessed by getCollection()
+
+ for (int i = 0; i < num_records; i++)
+ {
+ metric_sdk::Record before = records[i];
+ metric_sdk::Record after = collector.GetCollection()[i];
+
+ ASSERT_EQ(before.GetName(), after.GetName());
+
+ ASSERT_EQ(before.GetDescription(), after.GetDescription());
+
+ ASSERT_EQ(before.GetLabels(), after.GetLabels());
+
+ auto before_agg_var = before.GetAggregator();
+ auto before_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(before_agg_var);
+
+ auto after_agg_var = after.GetAggregator();
+ auto after_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<float>>>(after_agg_var);
+
+ ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size());
+ for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]);
+ }
+ for (size_t i = 0; i < before_agg->get_boundaries().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_boundaries()[i], after_agg->get_boundaries()[i]);
+ }
+ for (size_t i = 0; i < before_agg->get_counts().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_counts()[i], after_agg->get_counts()[i]);
+ }
+ }
+}
+
+/**
+ * AddMetricData() should be able to successfully add a collection
+ * of Records with Exact Aggregators. If the Exact Aggregator is in
+ * quantile mode, it will check quantiles at selected values of 0, 0.25,
+ * 0.5, 0.75, and 1. If not, it will check the vector of checkpointed
+ * values in get_checkpoint().
+ */
+TEST(PrometheusCollector, AddMetricDataWithExactRecordsSuccessfully)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 1;
+
+ // construct a collection of a single record with a quantile
+ // estimation ExactAggregator and double
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<double>(num_records, metric_sdk::AggregatorKind::Exact, true);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // construct a collection of a single record with an in-order
+ // ExactAggregator and double
+ records = CreateRecords<double>(num_records, metric_sdk::AggregatorKind::Exact, false);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Collection size should be the same as the size
+ // of the records collection passed to addMetricData()
+ ASSERT_EQ(collector.GetCollection().size(), records.size() * 2);
+
+ // check values of records created vs records from metricsToCollect,
+ // accessed by getCollection()
+
+ for (int i = 0; i < num_records; i++)
+ {
+ metric_sdk::Record before = records[i];
+ metric_sdk::Record after = collector.GetCollection()[i];
+
+ ASSERT_EQ(before.GetName(), after.GetName());
+
+ ASSERT_EQ(before.GetDescription(), after.GetDescription());
+
+ ASSERT_EQ(before.GetLabels(), after.GetLabels());
+
+ auto before_agg_var = before.GetAggregator();
+ auto before_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(before_agg_var);
+
+ auto after_agg_var = after.GetAggregator();
+ auto after_agg = nostd::get<std::shared_ptr<metric_sdk::Aggregator<double>>>(after_agg_var);
+
+ if (before_agg->get_quant_estimation() && after_agg->get_quant_estimation())
+ {
+ for (double i = 0; i <= 1;)
+ {
+ ASSERT_EQ(before_agg->get_quantiles(i), after_agg->get_quantiles(i));
+ i += 0.25;
+ }
+ }
+ else
+ {
+ ASSERT_EQ(before_agg->get_checkpoint().size(), after_agg->get_checkpoint().size());
+ for (size_t i = 0; i < before_agg->get_checkpoint().size(); i++)
+ {
+ ASSERT_EQ(before_agg->get_checkpoint()[i], after_agg->get_checkpoint()[i]);
+ }
+ }
+ }
+}
+
+TEST(PrometheusCollector, AddMetricDataDoesNotAddWithInsufficentSpace)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = collector.GetMaxCollectionSize() - 5;
+
+ // construct a collection close to max capacity
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<double>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ collector.AddMetricData(records);
+
+ // Check if all the records have been added
+ ASSERT_EQ(collector.GetCollection().size(), num_records);
+
+ // Try adding the same collection of records again to
+ // metricsToCollect.
+ collector.AddMetricData(records);
+
+ // Check that the number of records in metricsToCollect
+ // has not changed.
+ ASSERT_EQ(collector.GetCollection().size(), num_records);
+}
+
+TEST(PrometheusCollector, AddMetricDataDoesNotAddBadIndividualRecords)
+{
+ PrometheusCollector collector;
+
+ // number of records to create
+ int num_records = 5;
+
+ // construct a collection with the specified number of records
+ std::vector<metric_sdk::Record> records =
+ CreateRecords<double>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Check if all the records have been added
+ ASSERT_EQ(collector.GetCollection().size(), num_records);
+
+ // Creates a bad record, with a nullptr aggregator and adds
+ // it to the colelction of records
+ std::string name = "bad_record";
+ std::string description = "nullptr_agg";
+ std::string labels = "{label1:v1}";
+ std::shared_ptr<metric_sdk::Aggregator<int>> aggregator;
+ metric_sdk::Record bad_record{name, description, labels, aggregator};
+
+ records.push_back(bad_record);
+
+ // add records to collection
+ collector.AddMetricData(records);
+
+ // Check if all the records except the bad
+ // record have been added; the number of records added
+ // should be twice the original number of records
+ // epecified to be created
+ ASSERT_EQ(collector.GetCollection().size(), num_records * 2);
+}
+
+// ==================== Test for Constructor ======================
+TEST(PrometheusCollector, ConstructorInitializesCollector)
+{
+ PrometheusCollector collector;
+
+ // current size should be 0, capacity should be set to default
+ ASSERT_EQ(collector.GetCollection().size(), 0);
+}
+
+// ==================== Tests for collect() function ======================
+
+/**
+ * When collector is initialized, the collection inside is should also be initialized
+ */
+TEST(PrometheusCollector, CollectInitializesMetricFamilyCollection)
+{
+ PrometheusCollector collector;
+ auto c1 = collector.Collect();
+ ASSERT_EQ(c1.size(), 0);
+}
+
+/**
+ * Collect function should collect all data and clear the intermediate collection
+ */
+TEST(PrometheusCollector, CollectClearsTheCollection)
+{
+ PrometheusCollector collector;
+
+ // construct a collection to add metric records
+ int num_records = 2;
+ auto records = CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Counter);
+ collector.AddMetricData(records);
+
+ // the collection should not be empty now
+ ASSERT_EQ(collector.GetCollection().size(), num_records);
+
+ // don't care the collected result in this test
+ collector.Collect();
+
+ // after the collect() call, the collection should be empty
+ ASSERT_EQ(collector.GetCollection().size(), 0);
+}
+
+/**
+ * Collected data should be already be parsed to Prometheus Metric format
+ */
+TEST(PrometheusCollector, CollectParsesDataToMetricFamily)
+{
+ PrometheusCollector collector;
+
+ // construct a collection to add metric records
+ int num_records = 1;
+ auto records = CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Counter);
+ collector.AddMetricData(records);
+
+ // the collection should not be empty now
+ ASSERT_EQ(collector.GetCollection().size(), num_records);
+ auto collected = collector.Collect();
+
+ ASSERT_EQ(collected.size(), num_records);
+
+ auto metric_family = collected[0];
+
+ // Collect function really collects a vector of MetricFamily
+ ASSERT_EQ(metric_family.name, "record_0");
+ ASSERT_EQ(metric_family.help, "record 0 for test purpose");
+ ASSERT_EQ(metric_family.type, prometheus_client::MetricType::Counter);
+ ASSERT_EQ(metric_family.metric.size(), 1);
+ ASSERT_DOUBLE_EQ(metric_family.metric[0].counter.value, 15);
+}
+
+/**
+ * Concurrency Test 1: After adding data concurrently, the intermediate collection should
+ * contain all data from all threads.
+ */
+TEST(PrometheusCollector, ConcurrencyAddingRecords)
+{
+ PrometheusCollector collector;
+
+ // construct a collection to add metric records
+ int num_records = 2;
+ std::vector<metric_sdk::Record> records1 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ std::vector<metric_sdk::Record> records2 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Gauge);
+
+ std::thread first(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records1));
+ std::thread second(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records2));
+
+ first.join();
+ second.join();
+
+ ASSERT_EQ(collector.GetCollection().size(), 4);
+}
+
+/**
+ * Concurrency Test 2: After adding data concurrently and collecting, the intermediate collection
+ * should be empty, and all data are collected in the result vector.
+ */
+TEST(PrometheusCollector, ConcurrentlyAddingAndThenCollecting)
+{
+ PrometheusCollector collector;
+
+ // construct a collection to add metric records
+ int num_records = 2;
+ std::vector<metric_sdk::Record> records1 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ std::vector<metric_sdk::Record> records2 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Gauge);
+
+ std::thread first(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records1));
+ std::thread second(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records2));
+ first.join();
+ second.join();
+
+ auto collect_future = std::async(&PrometheusCollector::Collect, std::ref(collector));
+ auto res = collect_future.get();
+
+ ASSERT_EQ(collector.GetCollection().size(), 0);
+ ASSERT_EQ(res.size(), 4);
+}
+
+/**
+ * Concurrency Test 3: Concurrently adding and collecting. We don't know when the collect function
+ * is called, but all data entries are either collected or left in the collection.
+ */
+TEST(PrometheusCollector, ConcurrentlyAddingAndCollecting)
+{
+ PrometheusCollector collector;
+
+ // construct a collection to add metric records
+ int num_records = 2;
+ std::vector<metric_sdk::Record> records1 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ std::vector<metric_sdk::Record> records2 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Gauge);
+
+ std::thread first(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records1));
+ std::thread second(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records2));
+ auto collect_future = std::async(&PrometheusCollector::Collect, std::ref(collector));
+
+ first.join();
+ second.join();
+
+ auto res = collect_future.get();
+
+ // the size of collection can be 0, 2, 4, because we don't know when the collect()
+ // is really called. However, we claim that if the data in the collection is collected,
+ // they must be in the res. So res.size() + collection.size() must be the total number
+ // of data records we generated.
+ ASSERT_EQ(res.size() + collector.GetCollection().size(), 4);
+}
+
+/**
+ * Concurrency Test 4: Concurrently adding then concurrently collecting. We don't know which
+ * collecting thread fetches all data, but either one should succeed.
+ */
+TEST(PrometheusCollector, ConcurrentlyAddingAndConcurrentlyCollecting)
+{
+ PrometheusCollector collector;
+
+ // construct a collection to add metric records
+ int num_records = 2;
+ std::vector<metric_sdk::Record> records1 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Counter);
+
+ std::vector<metric_sdk::Record> records2 =
+ CreateRecords<int>(num_records, metric_sdk::AggregatorKind::Gauge);
+
+ // concurrently adding
+ std::thread first(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records1));
+ std::thread second(&PrometheusCollector::AddMetricData, std::ref(collector), std::ref(records2));
+ first.join();
+ second.join();
+
+ // after adding, then concurrently consuming
+ auto collect_future1 = std::async(&PrometheusCollector::Collect, std::ref(collector));
+ auto collect_future2 = std::async(&PrometheusCollector::Collect, std::ref(collector));
+ auto res1 = collect_future1.get();
+ auto res2 = collect_future2.get();
+
+ // all added data must be collected in either res1 or res2
+ ASSERT_EQ(res1.size() + res2.size(), 4);
+}
+
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_test.cc
new file mode 100644
index 000000000..564880c26
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_test.cc
@@ -0,0 +1,218 @@
+/*
+ * Copyright The 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.
+ */
+
+#ifdef ENABLE_METRICS_PREVIEW
+
+# include <gtest/gtest.h>
+# include <typeinfo>
+
+# include "opentelemetry/exporters/prometheus/prometheus_collector.h"
+# include "opentelemetry/exporters/prometheus/prometheus_exporter.h"
+# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h"
+# include "opentelemetry/version.h"
+
+/**
+ * PrometheusExporterTest is a friend class of PrometheusExporter.
+ * It has access to a private constructor that does not take in
+ * an exposer as an argument, and instead takes no arguments; this
+ * private constructor is only to be used here for testing
+ */
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace prometheus
+{
+class PrometheusExporterTest // : public ::testing::Test
+{
+public:
+ PrometheusExporter GetExporter() { return PrometheusExporter(); }
+};
+} // namespace prometheus
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+
+using opentelemetry::exporter::prometheus::PrometheusCollector;
+using opentelemetry::exporter::prometheus::PrometheusExporter;
+using opentelemetry::exporter::prometheus::PrometheusExporterTest;
+using opentelemetry::sdk::common::ExportResult;
+using opentelemetry::sdk::metrics::CounterAggregator;
+using opentelemetry::sdk::metrics::Record;
+
+/**
+ * Helper function to create a collection of records taken from
+ * a counter aggregator
+ */
+std::vector<Record> CreateRecords(int num)
+{
+
+ std::vector<Record> records;
+
+ for (int i = 0; i < num; i++)
+ {
+ std::string name = "record-" + std::to_string(i);
+ std::string description = "record-" + std::to_string(i);
+ std::string labels = "record-" + std::to_string(i) + "-label-1.0";
+ auto aggregator = std::shared_ptr<opentelemetry::sdk::metrics::Aggregator<int>>(
+ new opentelemetry::sdk::metrics::CounterAggregator<int>(
+ opentelemetry::metrics::InstrumentKind::Counter));
+ aggregator->update(10);
+ aggregator->checkpoint();
+
+ Record r{name, description, labels, aggregator};
+ records.push_back(r);
+ }
+ return records;
+}
+
+/**
+ * When a PrometheusExporter is initialized,
+ * isShutdown should be false.
+ */
+TEST(PrometheusExporter, InitializeConstructorIsNotShutdown)
+{
+ PrometheusExporterTest p;
+ PrometheusExporter exporter = p.GetExporter();
+
+ // // Asserts that the exporter is not shutdown.
+ ASSERT_TRUE(!exporter.IsShutdown());
+}
+
+/**
+ * The shutdown() function should set the isShutdown field to true.
+ */
+TEST(PrometheusExporter, ShutdownSetsIsShutdownToTrue)
+{
+ PrometheusExporterTest p;
+ PrometheusExporter exporter = p.GetExporter();
+
+ // exporter shuold not be shutdown by default
+ ASSERT_TRUE(!exporter.IsShutdown());
+
+ exporter.Shutdown();
+
+ // the exporter shuold be shutdown
+ ASSERT_TRUE(exporter.IsShutdown());
+
+ // shutdown function should be idempotent
+ exporter.Shutdown();
+ ASSERT_TRUE(exporter.IsShutdown());
+}
+
+/**
+ * The Export() function should return kSuccess = 0
+ * when data is exported successfully.
+ */
+TEST(PrometheusExporter, ExportSuccessfully)
+{
+ PrometheusExporterTest p;
+ PrometheusExporter exporter = p.GetExporter();
+
+ int num_records = 2;
+
+ std::vector<Record> records = CreateRecords(num_records);
+
+ auto res = exporter.Export(records);
+
+ // result should be kSuccess = 0
+ ExportResult code = ExportResult::kSuccess;
+ ASSERT_EQ(res, code);
+}
+
+/**
+ * If the exporter is shutdown, it cannot process
+ * any more export requests and returns kFailure = 1.
+ */
+TEST(PrometheusExporter, ExporterIsShutdown)
+{
+ PrometheusExporterTest p;
+ PrometheusExporter exporter = p.GetExporter();
+
+ int num_records = 1;
+
+ std::vector<Record> records = CreateRecords(num_records);
+
+ exporter.Shutdown();
+
+ // send export request after shutdown
+ auto res = exporter.Export(records);
+
+ // result code should be kFailure = 1
+ ExportResult code = ExportResult::kFailure;
+ ASSERT_EQ(res, code);
+}
+
+/**
+ * The Export() function should return
+ * kFailureFull = 2 when the collection is full,
+ * or when the collection is not full but does not have enough
+ * space to hold the batch data.
+ */
+TEST(PrometheusExporter, CollectionNotEnoughSpace)
+{
+ PrometheusExporterTest p;
+ PrometheusExporter exporter = p.GetExporter();
+
+ int num_records = 2;
+
+ // prepare two collections of records to export,
+ // one close to max size and another one that, when added
+ // to the first, will exceed the size of the collection
+
+ int max_collection_size = exporter.GetCollector()->GetMaxCollectionSize();
+
+ std::vector<Record> full_records = CreateRecords(max_collection_size - 1);
+ std::vector<Record> records = CreateRecords(num_records);
+
+ // send export request to fill the
+ // collection in the collector
+ auto res = exporter.Export(full_records);
+
+ // the result code should be kSuccess = 0
+ ExportResult code = ExportResult::kSuccess;
+ ASSERT_EQ(res, code);
+
+ // send export request that does not complete
+ // due to not enough space in the collection
+ res = exporter.Export(records);
+
+ // the result code should be kFailureFull = 2
+ code = ExportResult::kFailureFull;
+ ASSERT_EQ(res, code);
+}
+
+/**
+ * The Export() function should return
+ * kFailureInvalidArgument = 3 when an empty collection
+ * of records is passed to the Export() function.
+ */
+TEST(PrometheusExporter, InvalidArgumentWhenPassedEmptyRecordCollection)
+{
+ PrometheusExporterTest p;
+ PrometheusExporter exporter = p.GetExporter();
+
+ // Initializes an empty colelction of records
+ std::vector<Record> records;
+
+ // send export request to fill the
+ // collection in the collector
+ auto res = exporter.Export(records);
+
+ // the result code should be kFailureInvalidArgument = 3
+ ExportResult code = ExportResult::kFailureInvalidArgument;
+ ASSERT_EQ(res, code);
+}
+
+#endif // ENABLE_METRICS_PREVIEW
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_utils_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_utils_test.cc
new file mode 100644
index 000000000..22ce3f5fd
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/prometheus/test/prometheus_exporter_utils_test.cc
@@ -0,0 +1,460 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ENABLE_METRICS_PREVIEW
+# include <gtest/gtest.h>
+# include <map>
+# include <numeric>
+# include <string>
+# include <typeinfo>
+
+# include <opentelemetry/version.h>
+# include "opentelemetry/exporters/prometheus/prometheus_exporter_utils.h"
+# include "opentelemetry/sdk/_metrics/aggregator/counter_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/exact_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/gauge_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/histogram_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/min_max_sum_count_aggregator.h"
+# include "opentelemetry/sdk/_metrics/aggregator/sketch_aggregator.h"
+
+using opentelemetry::exporter::prometheus::PrometheusExporterUtils;
+namespace metric_sdk = opentelemetry::sdk::metrics;
+namespace metric_api = opentelemetry::metrics;
+namespace prometheus_client = ::prometheus;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+template <typename T>
+void assert_basic(prometheus_client::MetricFamily &metric,
+ const std::string &sanitized_name,
+ const std::string &description,
+ prometheus_client::MetricType type,
+ int label_num,
+ std::vector<T> vals)
+{
+ ASSERT_EQ(metric.name, sanitized_name); // name sanitized
+ ASSERT_EQ(metric.help, description); // description not changed
+ ASSERT_EQ(metric.type, type); // type translated
+
+ auto metric_data = metric.metric[0];
+ ASSERT_EQ(metric_data.label.size(), label_num);
+
+ switch (type)
+ {
+ case prometheus_client::MetricType::Counter: {
+ ASSERT_DOUBLE_EQ(metric_data.counter.value, vals[0]);
+ break;
+ }
+ case prometheus_client::MetricType::Gauge: {
+ ASSERT_EQ(metric_data.gauge.value, vals[0]);
+ break;
+ }
+ case prometheus_client::MetricType::Histogram: {
+ ASSERT_DOUBLE_EQ(metric_data.histogram.sample_count, vals[0]);
+ ASSERT_DOUBLE_EQ(metric_data.histogram.sample_sum, vals[1]);
+ auto buckets = metric_data.histogram.bucket;
+ ASSERT_EQ(buckets.size(), vals[2]);
+ break;
+ }
+ case prometheus_client::MetricType::Summary: {
+ ASSERT_DOUBLE_EQ(metric_data.summary.sample_count, vals[0]);
+ ASSERT_DOUBLE_EQ(metric_data.summary.sample_sum, vals[1]);
+ break;
+ }
+ case prometheus::MetricType::Untyped:
+ break;
+ }
+}
+
+void assert_histogram(prometheus_client::MetricFamily &metric,
+ std::vector<double> boundaries,
+ std::vector<int> correct)
+{
+ int cumulative_count = 0;
+ auto buckets = metric.metric[0].histogram.bucket;
+ for (size_t i = 0; i < buckets.size(); i++)
+ {
+ auto bucket = buckets[i];
+ if (i != buckets.size() - 1)
+ {
+ ASSERT_DOUBLE_EQ(boundaries[i], bucket.upper_bound);
+ }
+ else
+ {
+ ASSERT_DOUBLE_EQ(std::numeric_limits<double>::infinity(), bucket.upper_bound);
+ }
+ cumulative_count += correct[i];
+ ASSERT_EQ(cumulative_count, bucket.cumulative_count);
+ }
+}
+
+template <typename T>
+metric_sdk::Record get_record(const std::string &type,
+ int version,
+ const std::string &label,
+ std::shared_ptr<metric_sdk::Aggregator<T>> aggregator)
+{
+ std::string name = "test-" + type + "-metric-record-v_" + std::to_string(version) + ".0";
+ std::string desc = "this is a test " + type + " metric record";
+ metric_sdk::Record record(name, desc, label, aggregator);
+ return record;
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusEmptyInputReturnsEmptyCollection)
+{
+ std::vector<metric_sdk::Record> collection;
+ auto translated2 = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated2.size(), 0);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusIntegerCounter)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::CounterAggregator<int>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+
+ auto record1 = get_record("int-counter", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ aggregator->update(10);
+ aggregator->checkpoint();
+ collection.emplace_back(record1);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric1 = translated[0];
+ std::vector<int> vals = {10};
+ assert_basic(metric1, "test_int_counter_metric_record_v_1_0", record1.GetDescription(),
+ prometheus_client::MetricType::Counter, 3, vals);
+
+ auto record2 = get_record("int-counter", 2, "{,}", aggregator);
+ aggregator->update(20);
+ aggregator->update(30);
+ aggregator->checkpoint();
+ collection.emplace_back(record2);
+
+ translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric2 = translated[1];
+ vals = {50};
+ assert_basic(metric2, "test_int_counter_metric_record_v_2_0", record2.GetDescription(),
+ prometheus_client::MetricType::Counter, 0, vals);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusDoubleCounter)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+ aggregator->update(10.5);
+ aggregator->checkpoint();
+ auto record1 = get_record("double-counter", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ aggregator->update(22.4);
+ aggregator->update(31.2);
+ aggregator->checkpoint();
+ auto record2 = get_record("double-counter", 2, "{,}", aggregator);
+ collection.emplace_back(record1);
+ collection.emplace_back(record2);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric1 = translated[0];
+ std::vector<double> vals = {53.6};
+ assert_basic(metric1, "test_double_counter_metric_record_v_1_0", record1.GetDescription(),
+ prometheus_client::MetricType::Counter, 3, vals);
+ auto metric2 = translated[1];
+ assert_basic(metric2, "test_double_counter_metric_record_v_2_0", record2.GetDescription(),
+ prometheus_client::MetricType::Counter, 0, vals);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusShortCounter)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<short>>(
+ new metric_sdk::CounterAggregator<short>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+ aggregator->update(10);
+ aggregator->checkpoint();
+ auto record1 = get_record("short-counter", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ aggregator->update(20);
+ aggregator->update(30);
+ aggregator->checkpoint();
+ auto record2 = get_record("short-counter", 2, "{,}", aggregator);
+ collection.emplace_back(record1);
+ collection.emplace_back(record2);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric1 = translated[0];
+ std::vector<short> vals = {50};
+ assert_basic(metric1, "test_short_counter_metric_record_v_1_0", record1.GetDescription(),
+ prometheus_client::MetricType::Counter, 3, vals);
+ auto metric2 = translated[1];
+ assert_basic(metric2, "test_short_counter_metric_record_v_2_0", record2.GetDescription(),
+ prometheus_client::MetricType::Counter, 0, vals);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusFloatCounter)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<float>>(
+ new metric_sdk::CounterAggregator<float>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+ aggregator->update(10.5f);
+ aggregator->checkpoint();
+ auto record1 = get_record("float-counter", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ aggregator->update(22.4f);
+ aggregator->update(31.2f);
+ aggregator->checkpoint();
+ auto record2 = get_record("float-counter", 2, "{,}", aggregator);
+ collection.emplace_back(record1);
+ collection.emplace_back(record2);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric1 = translated[0];
+ std::vector<float> vals = {53.6f};
+ assert_basic(metric1, "test_float_counter_metric_record_v_1_0", record1.GetDescription(),
+ prometheus_client::MetricType::Counter, 3, vals);
+ auto metric2 = translated[1];
+ assert_basic(metric2, "test_float_counter_metric_record_v_2_0", record2.GetDescription(),
+ prometheus_client::MetricType::Counter, 0, vals);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusGauge)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::GaugeAggregator<int>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+ aggregator->update(10);
+ aggregator->checkpoint();
+ auto record1 = get_record("gauge", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ aggregator->update(20);
+ aggregator->update(30);
+ aggregator->checkpoint();
+ auto record2 = get_record("gauge", 2, "{,}", aggregator);
+ collection.emplace_back(record1);
+ collection.emplace_back(record2);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric1 = translated[0];
+ std::vector<int> vals = {30};
+ assert_basic(metric1, "test_gauge_metric_record_v_1_0", record1.GetDescription(),
+ prometheus_client::MetricType::Gauge, 3, vals);
+ auto metric2 = translated[1];
+ assert_basic(metric2, "test_gauge_metric_record_v_2_0", record2.GetDescription(),
+ prometheus_client::MetricType::Gauge, 0, vals);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramUniform)
+{
+ std::vector<double> boundaries{10, 20, 30, 40, 50};
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::HistogramAggregator<int>(metric_api::InstrumentKind::Counter, boundaries));
+
+ std::vector<metric_sdk::Record> collection;
+ auto record = get_record("histogram-uniform", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ int count_num = 60;
+ for (int i = 0; i < count_num; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ collection.emplace_back(record);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric = translated[0];
+ std::vector<int> vals = {aggregator->get_checkpoint()[1], aggregator->get_checkpoint()[0],
+ (int)boundaries.size() + 1};
+ assert_basic(metric, "test_histogram_uniform_metric_record_v_1_0", record.GetDescription(),
+ prometheus_client::MetricType::Histogram, 3, vals);
+ std::vector<int> correct = aggregator->get_counts();
+ assert_histogram(metric, boundaries, correct);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramNormal)
+{
+ std::vector<double> boundaries{2, 4, 6, 8, 10, 12};
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::HistogramAggregator<int>(metric_api::InstrumentKind::Counter, boundaries));
+
+ std::vector<metric_sdk::Record> collection;
+ auto record = get_record("histogram-normal", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ std::vector<int> values{1, 3, 3, 5, 5, 5, 7, 7, 7, 7, 9, 9, 9, 11, 11, 13};
+ for (int i : values)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ collection.emplace_back(record);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric = translated[0];
+ std::vector<int> vals = {aggregator->get_checkpoint()[1], aggregator->get_checkpoint()[0],
+ (int)boundaries.size() + 1};
+ assert_basic(metric, "test_histogram_normal_metric_record_v_1_0", record.GetDescription(),
+ prometheus_client::MetricType::Histogram, 3, vals);
+ std::vector<int> correct = aggregator->get_counts();
+ assert_histogram(metric, boundaries, correct);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusExact)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::ExactAggregator<int>(metric_api::InstrumentKind::Counter, true));
+
+ std::vector<metric_sdk::Record> collection;
+ int count_num = 100;
+ for (int i = 0; i <= count_num; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ auto record = get_record("exact", 1, "{label-1:v1,label_2:v2,label3:v3,}", aggregator);
+ collection.emplace_back(record);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric = translated[0];
+ std::vector<int> vals = {101, 5050};
+ assert_basic(metric, "test_exact_metric_record_v_1_0", record.GetDescription(),
+ prometheus_client::MetricType::Summary, 3, vals);
+ auto quantile = metric.metric[0].summary.quantile;
+ ASSERT_EQ(quantile.size(), 6);
+ ASSERT_DOUBLE_EQ(quantile[0].value, 0);
+ ASSERT_DOUBLE_EQ(quantile[1].value, 50);
+ ASSERT_DOUBLE_EQ(quantile[2].value, 90);
+ ASSERT_DOUBLE_EQ(quantile[3].value, 95);
+ ASSERT_DOUBLE_EQ(quantile[4].value, 99);
+ ASSERT_DOUBLE_EQ(quantile[5].value, 100);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusExactNoQuantile)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::ExactAggregator<int>(metric_api::InstrumentKind::Counter, false));
+
+ std::vector<metric_sdk::Record> collection;
+ int count_num = 10;
+ for (int i = 0; i < count_num; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ auto record = get_record("exact-no-quantile", 1, "{label1:v1,label2:v2,}", aggregator);
+ collection.emplace_back(record);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric = translated[0];
+ std::vector<int> vals = {count_num, 45};
+ assert_basic(metric, "test_exact_no_quantile_metric_record_v_1_0", record.GetDescription(),
+ prometheus_client::MetricType::Summary, 2, vals);
+ auto quantile = metric.metric[0].summary.quantile;
+ ASSERT_EQ(quantile.size(), 0);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusMinMaxSumCount)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::MinMaxSumCountAggregator<int>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+ // min: 1, max: 10, sum: 55, count: 10
+ for (int i = 1; i <= 10; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ auto record = get_record("mmsc", 1, "{label1:v1,label2:v2,label3:v3,}", aggregator);
+ collection.emplace_back(record);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric = translated[0];
+ // in this version of implementation, we use the sum/count as a gauge
+ std::vector<double> vals = {5.5};
+ assert_basic(metric, "test_mmsc_metric_record_v_1_0", record.GetDescription(),
+ prometheus_client::MetricType::Gauge, 3, vals);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusSketch)
+{
+ auto aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::SketchAggregator<int>(metric_api::InstrumentKind::Counter, 0.0005));
+
+ std::vector<metric_sdk::Record> collection;
+ for (int i = 0; i <= 100; i++)
+ {
+ aggregator->update(i);
+ }
+ aggregator->checkpoint();
+ auto record = get_record("sketch", 1, "{label1:v1,label2:v2,}", aggregator);
+ collection.emplace_back(record);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric = translated[0];
+ std::vector<int> vals = {aggregator->get_checkpoint()[1], aggregator->get_checkpoint()[0]};
+ assert_basic(metric, "test_sketch_metric_record_v_1_0", record.GetDescription(),
+ prometheus_client::MetricType::Summary, 2, vals);
+
+ auto quantile = metric.metric[0].summary.quantile;
+ ASSERT_EQ(quantile.size(), 6);
+ ASSERT_DOUBLE_EQ(quantile[0].value, 0);
+ ASSERT_DOUBLE_EQ(quantile[1].value, 49);
+ ASSERT_DOUBLE_EQ(quantile[2].value, 89);
+ ASSERT_DOUBLE_EQ(quantile[3].value, 94);
+ ASSERT_DOUBLE_EQ(quantile[4].value, 98);
+ ASSERT_DOUBLE_EQ(quantile[5].value, 99);
+}
+
+TEST(PrometheusExporterUtils, TranslateToPrometheusMultipleAggregators)
+{
+ auto counter_aggregator = std::shared_ptr<metric_sdk::Aggregator<double>>(
+ new metric_sdk::CounterAggregator<double>(metric_api::InstrumentKind::Counter));
+ auto gauge_aggregator = std::shared_ptr<metric_sdk::Aggregator<int>>(
+ new metric_sdk::GaugeAggregator<int>(metric_api::InstrumentKind::Counter));
+
+ std::vector<metric_sdk::Record> collection;
+ counter_aggregator->update(10);
+ counter_aggregator->update(20);
+ counter_aggregator->checkpoint();
+ auto record1 = get_record("counter", 1, "{label1:v1,label2:v2,label3:v3,}", counter_aggregator);
+ gauge_aggregator->update(10);
+ gauge_aggregator->update(30);
+ gauge_aggregator->update(20);
+ gauge_aggregator->checkpoint();
+ auto record2 = get_record("gauge", 1, "{label1:v1,}", gauge_aggregator);
+ collection.emplace_back(record1);
+ collection.emplace_back(record2);
+
+ auto translated = PrometheusExporterUtils::TranslateToPrometheus(collection);
+ ASSERT_EQ(translated.size(), collection.size());
+
+ auto metric1 = translated[0];
+ std::vector<int> vals = {30};
+ assert_basic(metric1, "test_counter_metric_record_v_1_0", record1.GetDescription(),
+ prometheus_client::MetricType::Counter, 3, vals);
+ auto metric2 = translated[1];
+ vals = {20};
+ assert_basic(metric2, "test_gauge_metric_record_v_1_0", record2.GetDescription(),
+ prometheus_client::MetricType::Gauge, 1, vals);
+}
+OPENTELEMETRY_END_NAMESPACE
+#endif
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/BUILD b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/BUILD
new file mode 100644
index 000000000..6cd52b2d0
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/BUILD
@@ -0,0 +1,64 @@
+package(default_visibility = ["//visibility:public"])
+
+cc_library(
+ name = "zipkin_recordable",
+ srcs = [
+ "src/recordable.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/zipkin/recordable.h",
+ ],
+ strip_include_prefix = "include",
+ tags = ["zipkin"],
+ deps = [
+ "//sdk/src/resource",
+ "//sdk/src/trace",
+ "@github_nlohmann_json//:json",
+ ],
+)
+
+cc_library(
+ name = "zipkin_exporter",
+ srcs = [
+ "src/zipkin_exporter.cc",
+ ],
+ hdrs = [
+ "include/opentelemetry/exporters/zipkin/zipkin_exporter.h",
+ ],
+ copts = [
+ "-DCURL_STATICLIB",
+ ],
+ strip_include_prefix = "include",
+ tags = ["zipkin"],
+ deps = [
+ ":zipkin_recordable",
+ "//ext/src/http/client/curl:http_client_curl",
+ ],
+)
+
+cc_test(
+ name = "zipkin_recordable_test",
+ srcs = ["test/zipkin_recordable_test.cc"],
+ tags = [
+ "test",
+ "zipkin",
+ ],
+ deps = [
+ ":zipkin_recordable",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "zipkin_exporter_test",
+ srcs = ["test/zipkin_exporter_test.cc"],
+ tags = [
+ "test",
+ "zipkin",
+ ],
+ deps = [
+ ":zipkin_exporter",
+ ":zipkin_recordable",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/CMakeLists.txt b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/CMakeLists.txt
new file mode 100644
index 000000000..559e8d500
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/CMakeLists.txt
@@ -0,0 +1,75 @@
+# Copyright 2021, OpenTelemetry Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include_directories(include)
+find_package(CURL REQUIRED)
+add_definitions(-DWITH_CURL)
+add_library(opentelemetry_exporter_zipkin_trace src/zipkin_exporter.cc
+ src/recordable.cc)
+
+target_link_libraries(
+ opentelemetry_exporter_zipkin_trace
+ PUBLIC opentelemetry_trace opentelemetry_http_client_curl
+ nlohmann_json::nlohmann_json)
+
+install(
+ TARGETS opentelemetry_exporter_zipkin_trace
+ EXPORT "${PROJECT_NAME}-target"
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+
+install(
+ DIRECTORY include/opentelemetry/exporters/zipkin
+ DESTINATION include/opentelemetry/exporters
+ FILES_MATCHING
+ PATTERN "*.h"
+ PATTERN "recordable.h" EXCLUDE)
+
+if(BUILD_TESTING)
+ add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1)
+
+ add_executable(zipkin_recordable_test test/zipkin_recordable_test.cc)
+
+ target_link_libraries(
+ zipkin_recordable_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ opentelemetry_exporter_zipkin_trace opentelemetry_resources)
+
+ gtest_add_tests(
+ TARGET zipkin_recordable_test
+ TEST_PREFIX exporter.
+ TEST_LIST zipkin_recordable_test)
+
+ if(MSVC)
+ if(GMOCK_LIB)
+ unset(GMOCK_LIB CACHE)
+ endif()
+ endif()
+ if(MSVC AND CMAKE_BUILD_TYPE STREQUAL "Debug")
+ find_library(GMOCK_LIB gmockd PATH_SUFFIXES lib)
+ else()
+ find_library(GMOCK_LIB gmock PATH_SUFFIXES lib)
+ endif()
+
+ add_executable(zipkin_exporter_test test/zipkin_exporter_test.cc)
+
+ target_link_libraries(
+ zipkin_exporter_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}
+ ${GMOCK_LIB} opentelemetry_exporter_zipkin_trace opentelemetry_resources)
+
+ gtest_add_tests(
+ TARGET zipkin_exporter_test
+ TEST_PREFIX exporter.
+ TEST_LIST zipkin_exporter_test)
+endif() # BUILD_TESTING
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/README.md b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/README.md
new file mode 100644
index 000000000..40bb1f061
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/README.md
@@ -0,0 +1,56 @@
+# Zipkin Exporter for OpenTelemetry C++
+
+## Prerequisite
+
+* [Get Zipkin](https://zipkin.io/pages/quickstart.html)
+
+## Installation
+
+### CMake Install Instructions
+
+Refer to install instructions [INSTALL.md](../../INSTALL.md#building-as-standalone-cmake-project).
+Modify step 2 to create `cmake` build configuration for compiling Zipkin as below:
+
+```console
+ $ cmake -DWITH_ZIPKIN=ON ..
+ -- The C compiler identification is GNU 9.3.0
+ -- The CXX compiler identification is GNU 9.3.0
+ ...
+ -- Configuring done
+ -- Generating done
+ -- Build files have been written to: /home/<user>/source/opentelemetry-cpp/build
+ $
+```
+
+### Bazel Install Instructions
+
+TODO
+
+## Usage
+
+Install the exporter on your application and pass the options. `service_name`
+is an optional string. If omitted, the exporter will first try to get the
+service name from the Resource. If no service name can be detected on the
+Resource, a fallback name of "unknown_service" will be used.
+
+```cpp
+
+opentelemetry::exporter::zipkin::ZipkinExporterOptions options;
+options.endpoint = "http://localhost:9411/api/v2/spans";
+options.service_name = "my_service";
+
+auto exporter = std::unique_ptr<opentelemetry::sdk::trace::SpanExporter>(
+ new opentelemetry::exporter::zipkin::ZipkinExporter(options));
+auto processor = std::shared_ptr<sdktrace::SpanProcessor>(
+ new sdktrace::SimpleSpanProcessor(std::move(exporter)));
+auto provider = nostd::shared_ptr<opentelemetry::trace::TracerProvider>(
+ new sdktrace::TracerProvider(processor, opentelemetry::sdk::resource::Resource::Create({}),
+ std::make_shared<opentelemetry::sdk::trace::AlwaysOnSampler>()));
+
+// Set the global trace provider
+opentelemetry::trace::Provider::SetTracerProvider(provider);
+```
+
+## Viewing your traces
+
+Please visit the Zipkin UI endpoint <http://localhost:9411>
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h
new file mode 100644
index 000000000..51f83211f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h
@@ -0,0 +1,60 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "nlohmann/json.hpp"
+#include "opentelemetry/sdk/trace/recordable.h"
+#include "opentelemetry/version.h"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace zipkin
+{
+using ZipkinSpan = nlohmann::json;
+
+class Recordable final : public sdk::trace::Recordable
+{
+public:
+ const ZipkinSpan &span() const noexcept { return span_; }
+
+ const std::string &GetServiceName() const noexcept { return service_name_; }
+
+ 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(opentelemetry::trace::StatusCode code,
+ nostd::string_view description) noexcept override;
+
+ void SetName(nostd::string_view name) noexcept override;
+
+ void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept override;
+
+ void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override;
+
+ void SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept override;
+
+ void SetDuration(std::chrono::nanoseconds duration) noexcept override;
+
+ void SetInstrumentationLibrary(
+ const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary
+ &instrumentation_library) noexcept override;
+
+private:
+ ZipkinSpan span_;
+ std::string service_name_;
+};
+} // namespace zipkin
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h
new file mode 100644
index 000000000..ae0e8173f
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h
@@ -0,0 +1,113 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#include "opentelemetry/common/spin_lock_mutex.h"
+#include "opentelemetry/ext/http/client/http_client_factory.h"
+#include "opentelemetry/ext/http/common/url_parser.h"
+#include "opentelemetry/sdk/common/env_variables.h"
+#include "opentelemetry/sdk/trace/exporter.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+
+#include "nlohmann/json.hpp"
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace zipkin
+{
+
+inline const std::string GetDefaultZipkinEndpoint()
+{
+ const char *otel_exporter_zipkin_endpoint_env = "OTEL_EXPORTER_ZIPKIN_ENDPOINT";
+ const char *kZipkinEndpointDefault = "http://localhost:9411/api/v2/spans";
+
+ auto endpoint =
+ opentelemetry::sdk::common::GetEnvironmentVariable(otel_exporter_zipkin_endpoint_env);
+ return endpoint.size() ? endpoint : kZipkinEndpointDefault;
+}
+
+enum class TransportFormat
+{
+ kJson,
+ kProtobuf
+};
+
+/**
+ * Struct to hold Zipkin exporter options.
+ */
+struct ZipkinExporterOptions
+{
+ // The endpoint to export to. By default the OpenTelemetry Collector's default endpoint.
+ std::string endpoint = GetDefaultZipkinEndpoint();
+ TransportFormat format = TransportFormat::kJson;
+ std::string service_name = "default-service";
+ std::string ipv4;
+ std::string ipv6;
+ ext::http::client::Headers headers = {{"content-type", "application/json"}};
+};
+
+/**
+ * The Zipkin exporter exports span data in JSON format as expected by Zipkin
+ */
+class ZipkinExporter final : public opentelemetry::sdk::trace::SpanExporter
+{
+public:
+ /**
+ * Create a ZipkinExporter using all default options.
+ */
+ ZipkinExporter();
+
+ /**
+ * Create a ZipkinExporter using the given options.
+ */
+ explicit ZipkinExporter(const ZipkinExporterOptions &options);
+
+ /**
+ * Create a span recordable.
+ * @return a newly initialized Recordable object
+ */
+ std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override;
+
+ /**
+ * Export a batch of span recordables in JSON format.
+ * @param spans a span of unique pointers to span recordables
+ */
+ 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, default to max.
+ */
+ bool Shutdown(
+ std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override;
+
+private:
+ void InitializeLocalEndpoint();
+
+private:
+ // The configuration options associated with this exporter.
+ bool is_shutdown_ = false;
+ ZipkinExporterOptions options_;
+ std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync> http_client_;
+ opentelemetry::ext::http::common::UrlParser url_parser_;
+ nlohmann::json local_end_point_;
+
+ // For testing
+ friend class ZipkinExporterTestPeer;
+ /**
+ * Create an ZipkinExporter using the specified thrift sender.
+ * Only tests can call this constructor directly.
+ * @param http_client the http client to be used for exporting
+ */
+ ZipkinExporter(std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync> http_client);
+
+ mutable opentelemetry::common::SpinLockMutex lock_;
+ bool isShutdown() const noexcept;
+};
+} // namespace zipkin
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/recordable.cc b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/recordable.cc
new file mode 100644
index 000000000..700d6a964
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/recordable.cc
@@ -0,0 +1,254 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/exporters/zipkin/recordable.h"
+#include "opentelemetry/sdk/resource/experimental_semantic_conventions.h"
+
+#include <map>
+#include <string>
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace zipkin
+{
+
+using namespace opentelemetry::sdk::resource;
+namespace trace_api = opentelemetry::trace;
+namespace common = opentelemetry::common;
+namespace sdk = opentelemetry::sdk;
+
+// constexpr needs keys to be constexpr, const is next best to use.
+static const std::map<trace_api::SpanKind, std::string> kSpanKindMap = {
+ {trace_api::SpanKind::kClient, "CLIENT"},
+ {trace_api::SpanKind::kServer, "SERVER"},
+ {trace_api::SpanKind::kConsumer, "CONSUMER"},
+ {trace_api::SpanKind::kProducer, "PRODUCER"},
+};
+
+//
+// See `attribute_value.h` for details.
+//
+const int kAttributeValueSize = 16;
+
+void Recordable::SetIdentity(const trace_api::SpanContext &span_context,
+ trace_api::SpanId parent_span_id) noexcept
+{
+ char trace_id_lower_base16[trace::TraceId::kSize * 2] = {0};
+ span_context.trace_id().ToLowerBase16(trace_id_lower_base16);
+ char span_id_lower_base16[trace::SpanId::kSize * 2] = {0};
+ span_context.span_id().ToLowerBase16(span_id_lower_base16);
+ if (parent_span_id.IsValid())
+ {
+ char parent_span_id_lower_base16[trace::SpanId::kSize * 2] = {0};
+ parent_span_id.ToLowerBase16(parent_span_id_lower_base16);
+ span_["parentId"] = std::string(parent_span_id_lower_base16, 16);
+ }
+
+ span_["id"] = std::string(span_id_lower_base16, 16);
+ span_["traceId"] = std::string(trace_id_lower_base16, 32);
+}
+
+void PopulateAttribute(nlohmann::json &attribute,
+ nostd::string_view key,
+ const common::AttributeValue &value)
+{
+ // Assert size of variant to ensure that this method gets updated if the variant
+ // definition changes
+ static_assert(nostd::variant_size<common::AttributeValue>::value == kAttributeValueSize,
+ "AttributeValue contains unknown type");
+
+ if (nostd::holds_alternative<bool>(value))
+ {
+ attribute[key.data()] = nostd::get<bool>(value);
+ }
+ else if (nostd::holds_alternative<int>(value))
+ {
+ attribute[key.data()] = nostd::get<int>(value);
+ }
+ else if (nostd::holds_alternative<int64_t>(value))
+ {
+ attribute[key.data()] = nostd::get<int64_t>(value);
+ }
+ else if (nostd::holds_alternative<unsigned int>(value))
+ {
+ attribute[key.data()] = nostd::get<unsigned int>(value);
+ }
+ else if (nostd::holds_alternative<uint64_t>(value))
+ {
+ attribute[key.data()] = nostd::get<uint64_t>(value);
+ }
+ else if (nostd::holds_alternative<double>(value))
+ {
+ attribute[key.data()] = nostd::get<double>(value);
+ }
+ else if (nostd::holds_alternative<const char *>(value))
+ {
+ attribute[key.data()] = nostd::get<const char *>(value);
+ }
+ else if (nostd::holds_alternative<nostd::string_view>(value))
+ {
+ attribute[key.data()] = nostd::string_view(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))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const uint8_t>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const bool>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const bool>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const int>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const int>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const int64_t>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const int64_t>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const unsigned int>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const unsigned int>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const uint64_t>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const uint64_t>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const double>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const double>>(value))
+ {
+ attribute[key.data()].push_back(val);
+ }
+ }
+ else if (nostd::holds_alternative<nostd::span<const nostd::string_view>>(value))
+ {
+ attribute[key.data()] = {};
+ for (const auto &val : nostd::get<nostd::span<const nostd::string_view>>(value))
+ {
+ attribute[key.data()].push_back(std::string(val.data(), val.size()));
+ }
+ }
+}
+
+void Recordable::SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept
+{
+ if (!span_.contains("tags"))
+ {
+ span_["tags"] = nlohmann::json::object();
+ }
+ PopulateAttribute(span_["tags"], key, value);
+}
+
+void Recordable::AddEvent(nostd::string_view name,
+ common::SystemTimestamp timestamp,
+ const common::KeyValueIterable &attributes) noexcept
+{
+ nlohmann::json attrs = nlohmann::json::object(); // empty object
+ attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept {
+ PopulateAttribute(attrs, key, value);
+ return true;
+ });
+
+ nlohmann::json annotation = {{"value", nlohmann::json::object({{name.data(), attrs}}).dump()},
+ {"timestamp", std::chrono::duration_cast<std::chrono::microseconds>(
+ timestamp.time_since_epoch())
+ .count()}};
+
+ if (!span_.contains("annotations"))
+ {
+ span_["annotations"] = nlohmann::json::array();
+ }
+ span_["annotations"].push_back(annotation);
+}
+
+void Recordable::AddLink(const trace_api::SpanContext &span_context,
+ const common::KeyValueIterable &attributes) noexcept
+{
+ // TODO: Currently not supported by specs:
+ // https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk_exporters/zipkin.md
+}
+
+void Recordable::SetStatus(trace::StatusCode code, nostd::string_view description) noexcept
+{
+ if (code != trace::StatusCode::kUnset)
+ {
+ span_["tags"]["otel.status_code"] = code;
+ if (code == trace::StatusCode::kError)
+ {
+ span_["tags"]["error"] = description;
+ }
+ }
+}
+
+void Recordable::SetName(nostd::string_view name) noexcept
+{
+ span_["name"] = name.data();
+}
+
+void Recordable::SetResource(const sdk::resource::Resource &resource) noexcept
+{
+ // only service.name attribute is supported by specs as of now.
+ auto attributes = resource.GetAttributes();
+ if (attributes.find(OTEL_GET_RESOURCE_ATTR(AttrServiceName)) != attributes.end())
+ {
+ service_name_ = nostd::get<std::string>(attributes[OTEL_GET_RESOURCE_ATTR(AttrServiceName)]);
+ }
+}
+
+void Recordable::SetStartTime(common::SystemTimestamp start_time) noexcept
+{
+ span_["timestamp"] =
+ std::chrono::duration_cast<std::chrono::microseconds>(start_time.time_since_epoch()).count();
+}
+
+void Recordable::SetDuration(std::chrono::nanoseconds duration) noexcept
+{
+ span_["duration"] = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
+}
+
+void Recordable::SetSpanKind(trace_api::SpanKind span_kind) noexcept
+{
+ auto span_iter = kSpanKindMap.find(span_kind);
+ if (span_iter != kSpanKindMap.end())
+ {
+ span_["kind"] = span_iter->second;
+ }
+}
+
+void Recordable::SetInstrumentationLibrary(
+ const sdk::instrumentationlibrary::InstrumentationLibrary &instrumentation_library) noexcept
+{
+ span_["tags"]["otel.library.name"] = instrumentation_library.GetName();
+ span_["tags"]["otel.library.version"] = instrumentation_library.GetVersion();
+}
+
+} // namespace zipkin
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/zipkin_exporter.cc b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/zipkin_exporter.cc
new file mode 100644
index 000000000..240144599
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/src/zipkin_exporter.cc
@@ -0,0 +1,128 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#define _WINSOCKAPI_ // stops including winsock.h
+#include "opentelemetry/exporters/zipkin/zipkin_exporter.h"
+#include <mutex>
+#include "opentelemetry/exporters/zipkin/recordable.h"
+#include "opentelemetry/ext/http/client/http_client_factory.h"
+#include "opentelemetry/ext/http/common/url_parser.h"
+#include "opentelemetry/sdk_config.h"
+
+namespace http_client = opentelemetry::ext::http::client;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace zipkin
+{
+
+// -------------------------------- Constructors --------------------------------
+
+ZipkinExporter::ZipkinExporter(const ZipkinExporterOptions &options)
+ : options_(options), url_parser_(options_.endpoint)
+{
+ http_client_ = ext::http::client::HttpClientFactory::CreateSync();
+ InitializeLocalEndpoint();
+}
+
+ZipkinExporter::ZipkinExporter() : options_(ZipkinExporterOptions()), url_parser_(options_.endpoint)
+{
+ http_client_ = ext::http::client::HttpClientFactory::CreateSync();
+ InitializeLocalEndpoint();
+}
+
+ZipkinExporter::ZipkinExporter(
+ std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync> http_client)
+ : options_(ZipkinExporterOptions()), url_parser_(options_.endpoint)
+{
+ http_client_ = http_client;
+ InitializeLocalEndpoint();
+}
+
+// ----------------------------- Exporter methods ------------------------------
+
+std::unique_ptr<sdk::trace::Recordable> ZipkinExporter::MakeRecordable() noexcept
+{
+ return std::unique_ptr<sdk::trace::Recordable>(new Recordable);
+}
+
+sdk::common::ExportResult ZipkinExporter::Export(
+ const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans) noexcept
+{
+ if (isShutdown())
+ {
+ OTEL_INTERNAL_LOG_ERROR("[Zipkin Trace Exporter] Exporting "
+ << spans.size() << " span(s) failed, exporter is shutdown");
+ return sdk::common::ExportResult::kFailure;
+ }
+ exporter::zipkin::ZipkinSpan json_spans = {};
+ for (auto &recordable : spans)
+ {
+ auto rec = std::unique_ptr<Recordable>(static_cast<Recordable *>(recordable.release()));
+ if (rec != nullptr)
+ {
+ auto json_span = rec->span();
+ // add localEndPoint
+ json_span["localEndpoint"] = local_end_point_;
+ // check service.name
+ auto service_name = rec->GetServiceName();
+ if (service_name.size())
+ {
+ json_span["localEndpoint"]["serviceName"] = service_name;
+ }
+ json_spans.push_back(json_span);
+ }
+ }
+ auto body_s = json_spans.dump();
+ http_client::Body body_v(body_s.begin(), body_s.end());
+ auto result = http_client_->Post(url_parser_.url_, body_v, options_.headers);
+ if (result &&
+ (result.GetResponse().GetStatusCode() == 200 || result.GetResponse().GetStatusCode() == 202))
+ {
+ return sdk::common::ExportResult::kSuccess;
+ }
+ else
+ {
+ if (result.GetSessionState() == http_client::SessionState::ConnectFailed)
+ {
+ OTEL_INTERNAL_LOG_ERROR("ZIPKIN EXPORTER] Zipkin Exporter: Connection failed");
+ }
+ return sdk::common::ExportResult::kFailure;
+ }
+ return sdk::common::ExportResult::kSuccess;
+}
+
+void ZipkinExporter::InitializeLocalEndpoint()
+{
+ if (options_.service_name.length())
+ {
+ local_end_point_["serviceName"] = options_.service_name;
+ }
+ if (options_.ipv4.length())
+ {
+ local_end_point_["ipv4"] = options_.ipv4;
+ }
+ if (options_.ipv6.length())
+ {
+ local_end_point_["ipv6"] = options_.ipv6;
+ }
+ local_end_point_["port"] = url_parser_.port_;
+}
+
+bool ZipkinExporter::Shutdown(std::chrono::microseconds timeout) noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ is_shutdown_ = true;
+ return true;
+}
+
+bool ZipkinExporter::isShutdown() const noexcept
+{
+ const std::lock_guard<opentelemetry::common::SpinLockMutex> locked(lock_);
+ return is_shutdown_;
+}
+
+} // namespace zipkin
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_exporter_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_exporter_test.cc
new file mode 100644
index 000000000..eec71f43d
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_exporter_test.cc
@@ -0,0 +1,215 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef HAVE_CPP_STDLIB
+
+# include "opentelemetry/exporters/zipkin/zipkin_exporter.h"
+# include <string>
+# include "opentelemetry/ext/http/client/curl/http_client_curl.h"
+# include "opentelemetry/ext/http/server/http_server.h"
+# include "opentelemetry/sdk/trace/batch_span_processor.h"
+# include "opentelemetry/sdk/trace/tracer_provider.h"
+# include "opentelemetry/trace/provider.h"
+
+# include <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
+namespace sdk_common = opentelemetry::sdk::common;
+using namespace testing;
+
+OPENTELEMETRY_BEGIN_NAMESPACE
+namespace exporter
+{
+namespace zipkin
+{
+
+namespace trace_api = opentelemetry::trace;
+namespace resource = opentelemetry::sdk::resource;
+
+template <class T, size_t N>
+static nostd::span<T, N> MakeSpan(T (&array)[N])
+{
+ return nostd::span<T, N>(array);
+}
+
+class ZipkinExporterTestPeer : public ::testing::Test
+{
+public:
+ std::unique_ptr<sdk::trace::SpanExporter> GetExporter(
+ std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync> http_client)
+ {
+ return std::unique_ptr<sdk::trace::SpanExporter>(new ZipkinExporter(http_client));
+ }
+
+ // Get the options associated with the given exporter.
+ const ZipkinExporterOptions &GetOptions(std::unique_ptr<ZipkinExporter> &exporter)
+ {
+ return exporter->options_;
+ }
+};
+
+class MockHttpClient : public opentelemetry::ext::http::client::HttpClientSync
+{
+public:
+ MOCK_METHOD(ext::http::client::Result,
+ Post,
+ (const nostd::string_view &,
+ const ext::http::client::Body &,
+ const ext::http::client::Headers &),
+ (noexcept, override));
+ MOCK_METHOD(ext::http::client::Result,
+ Get,
+ (const nostd::string_view &, const ext::http::client::Headers &),
+ (noexcept, override));
+};
+
+class IsValidMessageMatcher
+{
+public:
+ IsValidMessageMatcher(const std::string &trace_id) : trace_id_(trace_id) {}
+ template <typename T>
+ bool MatchAndExplain(const T &p, MatchResultListener * /* listener */) const
+ {
+ auto body = std::string(p.begin(), p.end());
+ nlohmann::json check_json = nlohmann::json::parse(body);
+ auto trace_id_kv = check_json.at(0).find("traceId");
+ auto received_trace_id = trace_id_kv.value().get<std::string>();
+ return trace_id_ == received_trace_id;
+ }
+
+ void DescribeTo(std::ostream *os) const { *os << "received trace_id matches"; }
+
+ void DescribeNegationTo(std::ostream *os) const { *os << "received trace_id does not matche"; }
+
+private:
+ std::string trace_id_;
+};
+
+PolymorphicMatcher<IsValidMessageMatcher> IsValidMessage(const std::string &trace_id)
+{
+ return MakePolymorphicMatcher(IsValidMessageMatcher(trace_id));
+}
+
+// Create spans, let processor call Export()
+TEST_F(ZipkinExporterTestPeer, ExportJsonIntegrationTest)
+{
+ auto mock_http_client = new MockHttpClient;
+ // Leave a comment line here or different version of clang-format has a different result here
+ auto exporter = GetExporter(
+ std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync>{mock_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 expected_url = nostd::string_view{"http://localhost:9411/api/v2/spans"};
+ EXPECT_CALL(*mock_http_client, Post(expected_url, IsValidMessage(report_trace_id), _))
+ .Times(Exactly(1))
+ .WillOnce(Return(ByMove(std::move(ext::http::client::Result{
+ std::unique_ptr<ext::http::client::Response>{new ext::http::client::curl::Response()},
+ ext::http::client::SessionState::Response}))));
+
+ child_span->End();
+ parent_span->End();
+}
+
+// Create spans, let processor call Export()
+TEST_F(ZipkinExporterTestPeer, ShutdownTest)
+{
+ auto mock_http_client = new MockHttpClient;
+ // Leave a comment line here or different version of clang-format has a different result here
+ auto exporter = GetExporter(
+ std::shared_ptr<opentelemetry::ext::http::client::HttpClientSync>{mock_http_client});
+ auto recordable_1 = exporter->MakeRecordable();
+ recordable_1->SetName("Test span 1");
+ auto recordable_2 = exporter->MakeRecordable();
+ recordable_2->SetName("Test span 2");
+
+ // exporter shuold not be shutdown by default
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
+ EXPECT_CALL(*mock_http_client, Post(_, _, _))
+ .Times(Exactly(1))
+ .WillOnce(Return(ByMove(std::move(ext::http::client::Result{
+ std::unique_ptr<ext::http::client::Response>{new ext::http::client::curl::Response()},
+ ext::http::client::SessionState::Response}))));
+ auto result = exporter->Export(batch_1);
+ EXPECT_EQ(sdk_common::ExportResult::kSuccess, result);
+
+ exporter->Shutdown();
+
+ nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
+ result = exporter->Export(batch_2);
+ EXPECT_EQ(sdk_common::ExportResult::kFailure, result);
+}
+
+// Test exporter configuration options
+TEST_F(ZipkinExporterTestPeer, ConfigTest)
+{
+ ZipkinExporterOptions opts;
+ opts.endpoint = "http://localhost:45455/v1/traces";
+ std::unique_ptr<ZipkinExporter> exporter(new ZipkinExporter(opts));
+ EXPECT_EQ(GetOptions(exporter).endpoint, "http://localhost:45455/v1/traces");
+}
+
+# ifndef NO_GETENV
+// Test exporter configuration options from env
+TEST_F(ZipkinExporterTestPeer, ConfigFromEnv)
+{
+ const std::string endpoint = "http://localhost:9999/v1/traces";
+ setenv("OTEL_EXPORTER_ZIPKIN_ENDPOINT", endpoint.c_str(), 1);
+
+ std::unique_ptr<ZipkinExporter> exporter(new ZipkinExporter());
+ EXPECT_EQ(GetOptions(exporter).endpoint, endpoint);
+
+ unsetenv("OTEL_EXPORTER_ZIPKIN_ENDPOINT");
+}
+
+# endif // NO_GETENV
+
+} // namespace zipkin
+} // namespace exporter
+OPENTELEMETRY_END_NAMESPACE
+#endif // HAVE_CPP_STDLIB
diff --git a/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_recordable_test.cc b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_recordable_test.cc
new file mode 100644
index 000000000..967aebaab
--- /dev/null
+++ b/src/jaegertracing/opentelemetry-cpp/exporters/zipkin/test/zipkin_recordable_test.cc
@@ -0,0 +1,285 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include "opentelemetry/sdk/trace/recordable.h"
+#include "opentelemetry/sdk/trace/simple_processor.h"
+#include "opentelemetry/sdk/trace/span_data.h"
+#include "opentelemetry/sdk/trace/tracer_provider.h"
+#include "opentelemetry/trace/provider.h"
+
+#include "opentelemetry/sdk/trace/exporter.h"
+
+#include "opentelemetry/common/timestamp.h"
+#include "opentelemetry/exporters/zipkin/recordable.h"
+
+#include <gtest/gtest.h>
+
+namespace trace = opentelemetry::trace;
+namespace nostd = opentelemetry::nostd;
+namespace sdktrace = opentelemetry::sdk::trace;
+namespace common = opentelemetry::common;
+namespace zipkin = opentelemetry::exporter::zipkin;
+using json = nlohmann::json;
+
+// Testing Shutdown functionality of OStreamSpanExporter, should expect no data to be sent to Stream
+TEST(ZipkinSpanRecordable, SetIdentity)
+{
+ json j_span = {{"id", "0000000000000002"},
+ {"parentId", "0000000000000003"},
+ {"traceId", "00000000000000000000000000000001"}};
+ zipkin::Recordable rec;
+ const trace::TraceId trace_id(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 span_id(
+ std::array<const uint8_t, trace::SpanId::kSize>({0, 0, 0, 0, 0, 0, 0, 2}));
+
+ const trace::SpanId parent_span_id(
+ std::array<const uint8_t, trace::SpanId::kSize>({0, 0, 0, 0, 0, 0, 0, 3}));
+
+ const trace::SpanContext span_context{trace_id, span_id,
+ trace::TraceFlags{trace::TraceFlags::kIsSampled}, true};
+
+ rec.SetIdentity(span_context, parent_span_id);
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+// according to https://zipkin.io/zipkin-api/#/ in case root span is created
+// the parentId filed should be absent.
+TEST(ZipkinSpanRecordable, SetIdentityEmptyParent)
+{
+ json j_span = {{"id", "0000000000000002"}, {"traceId", "00000000000000000000000000000001"}};
+ zipkin::Recordable rec;
+ const trace::TraceId trace_id(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 span_id(
+ std::array<const uint8_t, trace::SpanId::kSize>({0, 0, 0, 0, 0, 0, 0, 2}));
+
+ const trace::SpanId parent_span_id(
+ std::array<const uint8_t, trace::SpanId::kSize>({0, 0, 0, 0, 0, 0, 0, 0}));
+
+ const trace::SpanContext span_context{trace_id, span_id,
+ trace::TraceFlags{trace::TraceFlags::kIsSampled}, true};
+
+ rec.SetIdentity(span_context, parent_span_id);
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, SetName)
+{
+ nostd::string_view name = "Test Span";
+ json j_span = {{"name", name}};
+ zipkin::Recordable rec;
+ rec.SetName(name);
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, SetStartTime)
+{
+ zipkin::Recordable 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::microseconds>(start_time.time_since_epoch()).count();
+ json j_span = {{"timestamp", unix_start}};
+ rec.SetStartTime(start_timestamp);
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, SetDuration)
+{
+ std::chrono::nanoseconds durationNS(1000000000); // in ns
+ std::chrono::microseconds durationMS =
+ std::chrono::duration_cast<std::chrono::microseconds>(durationNS); // in ms
+ json j_span = {{"duration", durationMS.count()}, {"timestamp", 0}};
+ zipkin::Recordable rec;
+ // Start time is 0
+ common::SystemTimestamp start_timestamp;
+
+ rec.SetStartTime(start_timestamp);
+ rec.SetDuration(durationNS);
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, SetInstrumentationLibrary)
+{
+ using InstrumentationLibrary = opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary;
+
+ const char *library_name = "otel-cpp";
+ const char *library_version = "0.5.0";
+ json j_span = {
+ {"tags", {{"otel.library.name", library_name}, {"otel.library.version", library_version}}}};
+ zipkin::Recordable rec;
+
+ rec.SetInstrumentationLibrary(*InstrumentationLibrary::Create(library_name, library_version));
+
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, SetStatus)
+{
+ std::string description = "Error description";
+ std::vector<trace::StatusCode> status_codes = {trace::StatusCode::kError, trace::StatusCode::kOk};
+ for (auto &status_code : status_codes)
+ {
+ zipkin::Recordable rec;
+ trace::StatusCode code(status_code);
+ json j_span;
+ if (status_code == trace::StatusCode::kError)
+ j_span = {{"tags", {{"otel.status_code", status_code}, {"error", description}}}};
+ else
+ j_span = {{"tags", {{"otel.status_code", status_code}}}};
+
+ rec.SetStatus(code, description);
+ EXPECT_EQ(rec.span(), j_span);
+ }
+}
+
+TEST(ZipkinSpanRecordable, SetSpanKind)
+{
+ json j_json_client = {{"kind", "CLIENT"}};
+ zipkin::Recordable rec;
+ rec.SetSpanKind(trace::SpanKind::kClient);
+ EXPECT_EQ(rec.span(), j_json_client);
+}
+
+TEST(ZipkinSpanRecordable, AddEventDefault)
+{
+ zipkin::Recordable 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.sdktrace::Recordable::AddEvent(name, event_timestamp);
+
+ uint64_t unix_event_time =
+ std::chrono::duration_cast<std::chrono::microseconds>(event_time.time_since_epoch()).count();
+
+ json j_span = {
+ {"annotations",
+ {{{"value", json({{name, json::object()}}).dump()}, {"timestamp", unix_event_time}}}}};
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, AddEventWithAttributes)
+{
+ zipkin::Recordable rec;
+
+ std::chrono::system_clock::time_point event_time = std::chrono::system_clock::now();
+ common::SystemTimestamp event_timestamp(event_time);
+ uint64_t unix_event_time =
+ std::chrono::duration_cast<std::chrono::microseconds>(event_time.time_since_epoch()).count();
+
+ 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", event_timestamp,
+ common::KeyValueIterableView<std::map<std::string, int>>(attributes));
+
+ nlohmann::json j_span = {
+ {"annotations",
+ {{{"value", json({{"Test Event", {{"attr1", 4}, {"attr2", 7}, {"attr3", 23}}}}).dump()},
+ {"timestamp", unix_event_time}}}}};
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+// Test non-int single types. Int single types are tested using templates (see IntAttributeTest)
+TEST(ZipkinSpanRecordable, SetSingleAtrribute)
+{
+ zipkin::Recordable 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);
+ nlohmann::json j_span = {
+ {"tags", {{"bool_attr", true}, {"double_attr", 3.3}, {"str_attr", "Test"}}}};
+
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+// Test non-int array types. Int array types are tested using templates (see IntAttributeTest)
+TEST(ZipkinSpanRecordable, SetArrayAtrribute)
+{
+ zipkin::Recordable rec;
+ nlohmann::json j_span = {{"tags",
+ {{"bool_arr_attr", {true, false, true}},
+ {"double_arr_attr", {22.3, 33.4, 44.5}},
+ {"str_arr_attr", {"Hello", "World", "Test"}}}}};
+ 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);
+
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TEST(ZipkinSpanRecordable, SetResource)
+{
+ zipkin::Recordable rec;
+ std::string service_name = "test";
+ auto resource = opentelemetry::sdk::resource::Resource::Create({{"service.name", service_name}});
+ rec.SetResource(resource);
+ EXPECT_EQ(rec.GetServiceName(), service_name);
+}
+
+/**
+ * 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 ZipkinIntAttributeTest : public testing::Test
+{
+ using IntParamType = T;
+};
+
+using IntTypes = testing::Types<int, int64_t, unsigned int, uint64_t>;
+TYPED_TEST_SUITE(ZipkinIntAttributeTest, IntTypes);
+
+TYPED_TEST(ZipkinIntAttributeTest, SetIntSingleAttribute)
+{
+ using IntType = typename TestFixture::IntParamType;
+ IntType i = 2;
+ common::AttributeValue int_val(i);
+
+ zipkin::Recordable rec;
+ rec.SetAttribute("int_attr", int_val);
+ nlohmann::json j_span = {{"tags", {{"int_attr", 2}}}};
+ EXPECT_EQ(rec.span(), j_span);
+}
+
+TYPED_TEST(ZipkinIntAttributeTest, 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);
+
+ zipkin::Recordable rec;
+ rec.SetAttribute("int_arr_attr", int_span);
+ nlohmann::json j_span = {{"tags", {{"int_arr_attr", {4, 5, 6}}}}};
+ EXPECT_EQ(rec.span(), j_span);
+}