summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/opentelemetry-cpp/exporters/ostream
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/jaegertracing/opentelemetry-cpp/exporters/ostream
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/jaegertracing/opentelemetry-cpp/exporters/ostream')
-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
16 files changed, 2636 insertions, 0 deletions
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);
+}