diff --git a/CHANGELOG.md b/CHANGELOG.md index b18a951289..65c8b03289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [BUILD] Allow to use local GSL +* [EXPORTER] Jaeger Exporter - Add Thrift HTTP exporter ([#926](https://github.com/open-telemetry/opentelemetry-cpp/pull/926)) + ## [1.0.0-rc3] 2021-07-12 * [DOCS] Add doxygen reference docs for SDK ([#902](https://github.com/open-telemetry/opentelemetry-cpp/pull/902)) diff --git a/CMakeLists.txt b/CMakeLists.txt index c587a5c51d..822e78bf37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,8 +118,13 @@ if(WITH_STL) # Guidelines Support Library path. Used if we are not on not get C++20. # # TODO: respect WITH_ABSEIL as alternate implementation of std::span - set(GSL_DIR third_party/ms-gsl) - include_directories(${GSL_DIR}/include) + find_package(Microsoft.GSL QUIET) + if(TARGET Microsoft.GSL::GSL) + list(APPEND CORE_RUNTIME_LIBS Microsoft.GSL::GSL) + else() + set(GSL_DIR third_party/ms-gsl) + include_directories(${GSL_DIR}/include) + endif() # Optimize for speed to reduce the hops if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") diff --git a/docs/building-with-vcpkg.md b/docs/building-with-vcpkg.md index 8a51c988f1..7586486e88 100644 --- a/docs/building-with-vcpkg.md +++ b/docs/building-with-vcpkg.md @@ -102,6 +102,18 @@ dependencies: C++14 or C++17 compiler. - `nlohmann-json` - required when building with zPages module. - `prometheus-cpp` - required for Prometheus exporter. +- `gRPC` and `Protobuf` - required for OTLP exporter It is possible to adjust the build system to use either vcpkg-installed dependencies or OS-provided dependencies, e.g. `brew` or `deb` packages. +To install the dependencies through vcpkg, + +- Set the VCPKG_ROOT env variable to the vcpkg install directory, or +- Set the CMake variable CMAKE_TOOLCHAIN_FILE to vcpkg toolchain file `vcpkg.cmake`. + +With either of these settings, the appropriate vcpkg folders get added to the cmake +search path, and makes the required libraries to be found through `find_package()`. + +The opentelemetry-cpp repo also brings the vcpkg package under `tools` directory. +This would be used during Windows builds to install the missing dependencies ONLY +if the external vcpkg toolchain is not configured. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 782f20d18e..327f067121 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,6 +8,9 @@ endif() if(WITH_JAEGER) add_subdirectory(jaeger) endif() +if(WITH_ZIPKIN) + add_subdirectory(zipkin) +endif() add_subdirectory(plugin) add_subdirectory(simple) add_subdirectory(batch) diff --git a/examples/batch/main.cc b/examples/batch/main.cc index 5c007dfbc7..3c9a168071 100644 --- a/examples/batch/main.cc +++ b/examples/batch/main.cc @@ -31,11 +31,15 @@ void initTracer() // We export `kNumSpans` after every `schedule_delay_millis` milliseconds. options.max_export_batch_size = kNumSpans; + opentelemetry::sdk::resource::ResourceAttributes attributes = {{"service", "test_service"}, + {"version", (uint32_t)1}}; + auto resource = opentelemetry::sdk::resource::Resource::Create(attributes); + auto processor = std::unique_ptr( new sdktrace::BatchSpanProcessor(std::move(exporter), options)); auto provider = nostd::shared_ptr( - new sdktrace::TracerProvider(std::move(processor))); + new sdktrace::TracerProvider(std::move(processor), resource)); // Set the global trace provider. opentelemetry::trace::Provider::SetTracerProvider(provider); } diff --git a/examples/jaeger/CMakeLists.txt b/examples/jaeger/CMakeLists.txt index bccb03eeae..d705a8868c 100644 --- a/examples/jaeger/CMakeLists.txt +++ b/examples/jaeger/CMakeLists.txt @@ -7,4 +7,4 @@ target_link_libraries(jaeger_foo_library ${CMAKE_THREAD_LIBS_INIT} add_executable(example_jaeger main.cc) target_link_libraries( example_jaeger ${CMAKE_THREAD_LIBS_INIT} jaeger_foo_library - opentelemetry_trace jaeger_trace_exporter) + opentelemetry_trace opentelemetry_exporter_jaeger_trace) diff --git a/examples/jaeger/main.cc b/examples/jaeger/main.cc index f3f94884ca..63ed476aad 100644 --- a/examples/jaeger/main.cc +++ b/examples/jaeger/main.cc @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { if (argc == 2) { - opts.server_addr = argv[1]; + opts.endpoint = argv[1]; } // Removing this line will leave the default noop TracerProvider in place. InitTracer(); diff --git a/examples/zipkin/CMakeLists.txt b/examples/zipkin/CMakeLists.txt new file mode 100644 index 0000000000..40affab413 --- /dev/null +++ b/examples/zipkin/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories(${CMAKE_SOURCE_DIR}/exporters/zipkin/include) + +add_library(zipkin_foo_library foo_library/foo_library.cc) +target_link_libraries(zipkin_foo_library ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_api) + +add_executable(example_zipkin main.cc) +target_link_libraries( + example_zipkin ${CMAKE_THREAD_LIBS_INIT} zipkin_foo_library + opentelemetry_trace opentelemetry_exporter_zipkin_trace) diff --git a/examples/zipkin/README.md b/examples/zipkin/README.md new file mode 100644 index 0000000000..a07480213d --- /dev/null +++ b/examples/zipkin/README.md @@ -0,0 +1,31 @@ +# Zipkin Exporter Example + +This is an example of how to use the Zipkin exporter. + +The application in `main.cc` initializes an `ZipkinExporter` instance and uses it to +register a tracer provider from the [OpenTelemetry SDK](https://github.com/open-telemetry/opentelemetry-cpp). +The application then calls a `foo_library` which has been instrumented using the [OpenTelemetry +API](https://github.com/open-telemetry/opentelemetry-cpp/tree/main/api). + +Resulting spans are exported to the Zipkin server using the Zipkin exporter. + +Note that the Zipkin exporter connects to the server at `localhost:9411` by +default. + +## Running Zipkin server locally + +The quick way to run the Zipkin server is using Docker container : + +``console + +$ docker run -d -p 9411:9411 openzipkin/zipkin + +`` + +## Running Zipkin example + +Build this example using instructions in [INSTALL.md](../../INSTALL.md). + +## Viewing the traces + +Please visit the Zipkin UI endpoint `http://localhost:9411` diff --git a/examples/zipkin/foo_library/foo_library.cc b/examples/zipkin/foo_library/foo_library.cc new file mode 100644 index 0000000000..a0dec3c766 --- /dev/null +++ b/examples/zipkin/foo_library/foo_library.cc @@ -0,0 +1,37 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/version/version.h" +#include "opentelemetry/trace/provider.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +namespace +{ +nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("foo_library", OPENTELEMETRY_SDK_VERSION); +} + +void f1() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("f1")); +} + +void f2() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("f2")); + + f1(); + f1(); +} +} // namespace + +void foo_library() +{ + auto scoped_span = trace::Scope(get_tracer()->StartSpan("library")); + + f2(); +} diff --git a/examples/zipkin/foo_library/foo_library.h b/examples/zipkin/foo_library/foo_library.h new file mode 100644 index 0000000000..daebb2603f --- /dev/null +++ b/examples/zipkin/foo_library/foo_library.h @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +void foo_library(); diff --git a/examples/zipkin/main.cc b/examples/zipkin/main.cc new file mode 100644 index 0000000000..3ae7539cda --- /dev/null +++ b/examples/zipkin/main.cc @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/exporters/zipkin/zipkin_exporter.h" +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/provider.h" + +#include "foo_library/foo_library.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; +namespace sdktrace = opentelemetry::sdk::trace; +namespace zipkin = opentelemetry::exporter::zipkin; + +namespace +{ +opentelemetry::exporter::zipkin::ZipkinExporterOptions opts; +void InitTracer() +{ + // Create zipkin exporter instance + opentelemetry::sdk::resource::ResourceAttributes attributes = { + {"service.name", "zipkin_demo_service"}}; + auto resource = opentelemetry::sdk::resource::Resource::Create(attributes); + auto exporter = std::unique_ptr(new zipkin::ZipkinExporter(opts)); + auto processor = std::unique_ptr( + new sdktrace::SimpleSpanProcessor(std::move(exporter))); + auto provider = nostd::shared_ptr( + new sdktrace::TracerProvider(std::move(processor), resource)); + // Set the global trace provider + trace::Provider::SetTracerProvider(provider); +} +} // namespace + +int main(int argc, char *argv[]) +{ + if (argc == 2) + { + opts.endpoint = argv[1]; + } + // Removing this line will leave the default noop TracerProvider in place. + InitTracer(); + + foo_library(); +} diff --git a/exporters/elasticsearch/CMakeLists.txt b/exporters/elasticsearch/CMakeLists.txt index cad3b044ee..8f675e530a 100644 --- a/exporters/elasticsearch/CMakeLists.txt +++ b/exporters/elasticsearch/CMakeLists.txt @@ -22,7 +22,8 @@ install( DIRECTORY include/opentelemetry/exporters/elasticsearch DESTINATION include/opentelemetry/exporters/ FILES_MATCHING - PATTERN "*.h") + PATTERN "*.h" + PATTERN "es_log_recordable.h" EXCLUDE) if(BUILD_TESTING) add_executable(es_log_exporter_test test/es_log_exporter_test.cc) diff --git a/exporters/elasticsearch/src/es_log_exporter.cc b/exporters/elasticsearch/src/es_log_exporter.cc index c7201db907..777bfb9426 100644 --- a/exporters/elasticsearch/src/es_log_exporter.cc +++ b/exporters/elasticsearch/src/es_log_exporter.cc @@ -3,8 +3,11 @@ #ifdef ENABLE_LOGS_PREVIEW +# include // std::stringstream + # 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; @@ -74,23 +77,19 @@ class ResponseHandler : public http_client::EventHandler switch (state) { case http_client::SessionState::ConnectFailed: - if (console_debug_) - std::cout << "Connection to elasticsearch failed\n"; + OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Connection to elasticsearch failed"); cv_.notify_all(); break; case http_client::SessionState::SendFailed: - if (console_debug_) - std::cout << "Request failed to be sent to elasticsearch\n"; + OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Request failed to be sent to elasticsearch"); cv_.notify_all(); break; case http_client::SessionState::TimedOut: - if (console_debug_) - std::cout << "Request to elasticsearch timed out\n"; + OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Request to elasticsearch timed out"); cv_.notify_all(); break; case http_client::SessionState::NetworkError: - if (console_debug_) - std::cout << "Network error to elasticsearch\n"; + OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Network error to elasticsearch"); cv_.notify_all(); break; } @@ -131,11 +130,8 @@ sdk::common::ExportResult ElasticsearchLogExporter::Export( // Return failure if this exporter has been shutdown if (is_shutdown_) { - if (options_.console_debug_) - { - std::cout << "Export failed, exporter is shutdown" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[ES Trace Exporter] Export failed, exporter is shutdown"); return sdk::common::ExportResult::kFailure; } @@ -172,8 +168,9 @@ sdk::common::ExportResult ElasticsearchLogExporter::Export( // Wait for the response to be received if (options_.console_debug_) { - std::cout << "waiting for response from Elasticsearch (timeout = " << options_.response_timeout_ - << " seconds)" << std::endl; + OTEL_INTERNAL_LOG_DEBUG( + "[ES Trace Exporter] waiting for response from Elasticsearch (timeout = " + << options_.response_timeout_ << " seconds)"); } bool write_successful = handler->waitForResponse(); @@ -191,12 +188,9 @@ sdk::common::ExportResult ElasticsearchLogExporter::Export( std::string responseBody = handler->GetResponseBody(); if (responseBody.find("\"failed\" : 0") == std::string::npos) { - if (options_.console_debug_) - { - std::cout << "Logs were not written to Elasticsearch correctly, response body:" << std::endl; - std::cout << responseBody << std::endl; - } - + 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; } diff --git a/exporters/jaeger/CMakeLists.txt b/exporters/jaeger/CMakeLists.txt index 030c7e77bb..0ae9162c61 100644 --- a/exporters/jaeger/CMakeLists.txt +++ b/exporters/jaeger/CMakeLists.txt @@ -10,35 +10,41 @@ set(JAEGER_THRIFT_GENCPP_SOURCES 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/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(jaeger_trace_exporter ${JAEGER_EXPORTER_SOURCES} - ${JAEGER_THRIFT_GENCPP_SOURCES}) +add_library(opentelemetry_exporter_jaeger_trace ${JAEGER_EXPORTER_SOURCES} + ${JAEGER_THRIFT_GENCPP_SOURCES}) -set_target_properties(jaeger_trace_exporter PROPERTIES EXPORT_NAME - jaeger_trace_exporter) +set_target_properties(opentelemetry_exporter_jaeger_trace + PROPERTIES EXPORT_NAME jaeger_trace_exporter) target_include_directories( - jaeger_trace_exporter + opentelemetry_exporter_jaeger_trace PUBLIC "$" "$") target_link_libraries( - jaeger_trace_exporter - PUBLIC opentelemetry_resources + opentelemetry_exporter_jaeger_trace + PUBLIC opentelemetry_resources http_client_curl PRIVATE thrift::thrift) if(MSVC) - target_compile_definitions(jaeger_trace_exporter PUBLIC NOMINMAX) + target_compile_definitions(opentelemetry_exporter_jaeger_trace + PUBLIC NOMINMAX) if(NOT BUILD_SHARED_LIBS) - target_compile_definitions(jaeger_trace_exporter + target_compile_definitions(opentelemetry_exporter_jaeger_trace PUBLIC THRIFT_STATIC_DEFINE) endif() endif() install( - TARGETS jaeger_trace_exporter + TARGETS opentelemetry_exporter_jaeger_trace EXPORT "${PROJECT_NAME}-target" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -48,12 +54,14 @@ install( DIRECTORY include/opentelemetry/exporters/jaeger DESTINATION include/opentelemetry/exporters/ FILES_MATCHING - PATTERN "*.h") + PATTERN "*.h" + PATTERN "recordable.h" EXCLUDE) if(BUILD_TESTING) add_executable(jaeger_recordable_test test/jaeger_recordable_test.cc) - target_link_libraries(jaeger_recordable_test ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} jaeger_trace_exporter) + target_link_libraries( + jaeger_recordable_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + opentelemetry_exporter_jaeger_trace) gtest_add_tests( TARGET jaeger_recordable_test diff --git a/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h b/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h index 4e046ad7d1..31f6a8bf35 100644 --- a/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h +++ b/exporters/jaeger/include/opentelemetry/exporters/jaeger/jaeger_exporter.h @@ -3,6 +3,7 @@ #pragma once +#include #include OPENTELEMETRY_BEGIN_NAMESPACE @@ -25,10 +26,11 @@ class ThriftSender; */ struct JaegerExporterOptions { - // The endpoint to export to. - std::string server_addr = "localhost"; - uint16_t server_port = 6831; 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; }; namespace trace_sdk = opentelemetry::sdk::trace; diff --git a/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h b/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h index 7829eef011..d7551bfa07 100644 --- a/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h +++ b/exporters/jaeger/include/opentelemetry/exporters/jaeger/recordable.h @@ -49,10 +49,10 @@ inline uint64_t otel_bswap_64(uint64_t host_int) using namespace jaegertracing; -class Recordable final : public sdk::trace::Recordable +class JaegerRecordable final : public sdk::trace::Recordable { public: - Recordable(); + JaegerRecordable(); thrift::Span *Span() noexcept { return span_.release(); } std::vector Tags() noexcept { return std::move(tags_); } diff --git a/exporters/jaeger/src/THttpTransport.cc b/exporters/jaeger/src/THttpTransport.cc new file mode 100644 index 0000000000..cbb1b65cb2 --- /dev/null +++ b/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/exporters/jaeger/src/THttpTransport.h b/exporters/jaeger/src/THttpTransport.h new file mode 100644 index 0000000000..9c796e67a0 --- /dev/null +++ b/exporters/jaeger/src/THttpTransport.h @@ -0,0 +1,40 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace jaeger +{ + +class THttpTransport : public apache::thrift::transport::TVirtualTransport +{ +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 client; + std::vector request_buffer; +}; + +} // namespace jaeger +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/jaeger/src/TUDPTransport.cc b/exporters/jaeger/src/TUDPTransport.cc index 123ba66d96..c72881df91 100644 --- a/exporters/jaeger/src/TUDPTransport.cc +++ b/exporters/jaeger/src/TUDPTransport.cc @@ -1,7 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#include // std::stringstream + #include "TUDPTransport.h" +#include "opentelemetry/sdk_config.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -51,7 +54,7 @@ void TUDPTransport::open() if (error) { - // TODO: log error + OTEL_INTERNAL_LOG_ERROR("Jaeger Exporter: getaddrinfo failed with error: " << error); return; } diff --git a/exporters/jaeger/src/http_transport.cc b/exporters/jaeger/src/http_transport.cc new file mode 100644 index 0000000000..f804ccc843 --- /dev/null +++ b/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 + +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(std::move(endpoint), std::move(headers)); + protocol_ = std::shared_ptr(new TBinaryProtocol(endpoint_transport_)); +} + +int HttpTransport::EmitBatch(const thrift::Batch &batch) +{ + batch.write(protocol_.get()); + + if (!endpoint_transport_->sendSpans()) + { + return 0; + } + + return static_cast(batch.spans.size()); +} + +} // namespace jaeger +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/jaeger/src/http_transport.h b/exporters/jaeger/src/http_transport.h new file mode 100644 index 0000000000..8f5c9c0571 --- /dev/null +++ b/exporters/jaeger/src/http_transport.h @@ -0,0 +1,39 @@ +#pragma once + +#include "THttpTransport.h" +#include "transport.h" + +#include +#include +#include +#include + +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 endpoint_transport_; + std::shared_ptr protocol_; +}; + +} // namespace jaeger +} // namespace exporter +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/jaeger/src/jaeger_exporter.cc b/exporters/jaeger/src/jaeger_exporter.cc index 7770e5c299..2bf181c54a 100644 --- a/exporters/jaeger/src/jaeger_exporter.cc +++ b/exporters/jaeger/src/jaeger_exporter.cc @@ -5,6 +5,7 @@ #include #include +#include "http_transport.h" #include "thrift_sender.h" #include "udp_transport.h" @@ -25,7 +26,7 @@ JaegerExporter::JaegerExporter() : JaegerExporter(JaegerExporterOptions()) {} std::unique_ptr JaegerExporter::MakeRecordable() noexcept { - return std::unique_ptr(new Recordable); + return std::unique_ptr(new JaegerRecordable); } sdk_common::ExportResult JaegerExporter::Export( @@ -40,7 +41,8 @@ sdk_common::ExportResult JaegerExporter::Export( for (auto &recordable : spans) { - auto rec = std::unique_ptr(static_cast(recordable.release())); + auto rec = + std::unique_ptr(static_cast(recordable.release())); if (rec != nullptr) { exported_size += sender_->Append(std::move(rec)); @@ -63,14 +65,21 @@ void JaegerExporter::InitializeEndpoint() { // TODO: do we need support any authentication mechanism? auto transport = std::unique_ptr( - static_cast(new UDPTransport(options_.server_addr, options_.server_port))); + static_cast(new UDPTransport(options_.endpoint, options_.server_port))); sender_ = std::unique_ptr(new ThriftSender(std::move(transport))); + return; } - else + + if (options_.transport_format == TransportFormat::kThriftHttp) { - // The transport format is not implemented. - assert(false); + auto transport = + std::unique_ptr(new HttpTransport(options_.endpoint, options_.headers)); + sender_ = std::unique_ptr(new ThriftSender(std::move(transport))); + return; } + + // The transport format is not implemented. + assert(false); } } // namespace jaeger diff --git a/exporters/jaeger/src/recordable.cc b/exporters/jaeger/src/recordable.cc index b932140897..f0e455923d 100644 --- a/exporters/jaeger/src/recordable.cc +++ b/exporters/jaeger/src/recordable.cc @@ -12,11 +12,11 @@ namespace jaeger using namespace opentelemetry::sdk::resource; -Recordable::Recordable() : span_{new thrift::Span} {} +JaegerRecordable::JaegerRecordable() : span_{new thrift::Span} {} -void Recordable::PopulateAttribute(nostd::string_view key, - const common::AttributeValue &value, - std::vector &tags) +void JaegerRecordable::PopulateAttribute(nostd::string_view key, + const common::AttributeValue &value, + std::vector &tags) { if (nostd::holds_alternative(value)) { @@ -41,9 +41,9 @@ void Recordable::PopulateAttribute(nostd::string_view key, // TODO: extend other AttributeType to the types supported by Jaeger. } -void Recordable::PopulateAttribute(nostd::string_view key, - const sdk::common::OwnedAttributeValue &value, - std::vector &tags) +void JaegerRecordable::PopulateAttribute(nostd::string_view key, + const sdk::common::OwnedAttributeValue &value, + std::vector &tags) { if (nostd::holds_alternative(value)) { @@ -64,8 +64,8 @@ void Recordable::PopulateAttribute(nostd::string_view key, // TODO: extend other OwnedAttributeType to the types supported by Jaeger. } -void Recordable::SetIdentity(const trace::SpanContext &span_context, - trace::SpanId parent_span_id) noexcept +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 @@ -90,14 +90,15 @@ void Recordable::SetIdentity(const trace::SpanContext &span_context, // TODO: set trace_state. } -void Recordable::SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept +void JaegerRecordable::SetAttribute(nostd::string_view key, + const common::AttributeValue &value) noexcept { PopulateAttribute(key, value, tags_); } -void Recordable::AddEvent(nostd::string_view name, - common::SystemTimestamp timestamp, - const common::KeyValueIterable &attributes) noexcept +void JaegerRecordable::AddEvent(nostd::string_view name, + common::SystemTimestamp timestamp, + const common::KeyValueIterable &attributes) noexcept { std::vector tags; PopulateAttribute("event", static_cast(name.data()), tags); @@ -113,7 +114,7 @@ void Recordable::AddEvent(nostd::string_view name, logs_.push_back(log); } -void Recordable::SetInstrumentationLibrary( +void JaegerRecordable::SetInstrumentationLibrary( const opentelemetry::sdk::instrumentationlibrary::InstrumentationLibrary &instrumentation_library) noexcept { @@ -121,13 +122,13 @@ void Recordable::SetInstrumentationLibrary( AddTag("otel.library.version", instrumentation_library.GetVersion(), tags_); } -void Recordable::AddLink(const trace::SpanContext &span_context, - const common::KeyValueIterable &attributes) noexcept +void JaegerRecordable::AddLink(const trace::SpanContext &span_context, + const common::KeyValueIterable &attributes) noexcept { // TODO: convert link to SpanRefernece } -void Recordable::SetStatus(trace::StatusCode code, nostd::string_view description) noexcept +void JaegerRecordable::SetStatus(trace::StatusCode code, nostd::string_view description) noexcept { if (code == trace::StatusCode::kUnset) { @@ -147,12 +148,12 @@ void Recordable::SetStatus(trace::StatusCode code, nostd::string_view descriptio AddTag("otel.status_description", std::string{description}, tags_); } -void Recordable::SetName(nostd::string_view name) noexcept +void JaegerRecordable::SetName(nostd::string_view name) noexcept { span_->__set_operationName(static_cast(name)); } -void Recordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept +void JaegerRecordable::SetResource(const opentelemetry::sdk::resource::Resource &resource) noexcept { for (const auto &attribute_iter : resource.GetAttributes()) { @@ -168,18 +169,18 @@ void Recordable::SetResource(const opentelemetry::sdk::resource::Resource &resou } } -void Recordable::SetStartTime(common::SystemTimestamp start_time) noexcept +void JaegerRecordable::SetStartTime(common::SystemTimestamp start_time) noexcept { span_->__set_startTime( std::chrono::duration_cast(start_time.time_since_epoch()).count()); } -void Recordable::SetDuration(std::chrono::nanoseconds duration) noexcept +void JaegerRecordable::SetDuration(std::chrono::nanoseconds duration) noexcept { span_->__set_duration(std::chrono::duration_cast(duration).count()); } -void Recordable::SetSpanKind(trace::SpanKind span_kind) noexcept +void JaegerRecordable::SetSpanKind(trace::SpanKind span_kind) noexcept { const char *span_kind_str = nullptr; @@ -212,9 +213,9 @@ void Recordable::SetSpanKind(trace::SpanKind span_kind) noexcept } } -void Recordable::AddTag(const std::string &key, - const std::string &value, - std::vector &tags) +void JaegerRecordable::AddTag(const std::string &key, + const std::string &value, + std::vector &tags) { thrift::Tag tag; @@ -225,12 +226,14 @@ void Recordable::AddTag(const std::string &key, tags.push_back(tag); } -void Recordable::AddTag(const std::string &key, const char *value, std::vector &tags) +void JaegerRecordable::AddTag(const std::string &key, + const char *value, + std::vector &tags) { AddTag(key, std::string{value}, tags); } -void Recordable::AddTag(const std::string &key, bool value, std::vector &tags) +void JaegerRecordable::AddTag(const std::string &key, bool value, std::vector &tags) { thrift::Tag tag; @@ -241,7 +244,7 @@ void Recordable::AddTag(const std::string &key, bool value, std::vector &tags) +void JaegerRecordable::AddTag(const std::string &key, int64_t value, std::vector &tags) { thrift::Tag tag; @@ -252,7 +255,7 @@ void Recordable::AddTag(const std::string &key, int64_t value, std::vector &tags) +void JaegerRecordable::AddTag(const std::string &key, double value, std::vector &tags) { thrift::Tag tag; diff --git a/exporters/jaeger/src/sender.h b/exporters/jaeger/src/sender.h index 9a42e251c6..6162cf0d1b 100644 --- a/exporters/jaeger/src/sender.h +++ b/exporters/jaeger/src/sender.h @@ -19,7 +19,7 @@ class Sender Sender() = default; virtual ~Sender() = default; - virtual int Append(std::unique_ptr &&span) = 0; + virtual int Append(std::unique_ptr &&span) = 0; virtual int Flush() = 0; diff --git a/exporters/jaeger/src/thrift_sender.cc b/exporters/jaeger/src/thrift_sender.cc index 488f4fc8c1..770bb81991 100644 --- a/exporters/jaeger/src/thrift_sender.cc +++ b/exporters/jaeger/src/thrift_sender.cc @@ -18,7 +18,7 @@ ThriftSender::ThriftSender(std::unique_ptr &&transport) thrift_buffer_(new apache::thrift::transport::TMemoryBuffer()) {} -int ThriftSender::Append(std::unique_ptr &&span) noexcept +int ThriftSender::Append(std::unique_ptr &&span) noexcept { if (span == nullptr) { @@ -79,11 +79,11 @@ int ThriftSender::Flush() batch.__set_process(process_); batch.__set_spans(span_buffer_); - transport_->EmitBatch(batch); + int spans_flushed = transport_->EmitBatch(batch); ResetBuffers(); - return static_cast(batch.spans.size()); + return spans_flushed; } void ThriftSender::Close() diff --git a/exporters/jaeger/src/thrift_sender.h b/exporters/jaeger/src/thrift_sender.h index 23f0d3d5f3..15b37dc231 100644 --- a/exporters/jaeger/src/thrift_sender.h +++ b/exporters/jaeger/src/thrift_sender.h @@ -32,7 +32,7 @@ class ThriftSender : public Sender ThriftSender(std::unique_ptr &&transport); ~ThriftSender() override { Close(); } - int Append(std::unique_ptr &&span) noexcept override; + int Append(std::unique_ptr &&span) noexcept override; int Flush() override; void Close() override; @@ -57,7 +57,7 @@ class ThriftSender : public Sender } private: - std::vector> spans_; + std::vector> spans_; std::vector span_buffer_; std::unique_ptr transport_; std::unique_ptr protocol_factory_; diff --git a/exporters/jaeger/src/transport.h b/exporters/jaeger/src/transport.h index a507d5b4a3..8121e30076 100644 --- a/exporters/jaeger/src/transport.h +++ b/exporters/jaeger/src/transport.h @@ -21,8 +21,8 @@ class Transport Transport() = default; virtual ~Transport() = default; - virtual void EmitBatch(const thrift::Batch &batch) = 0; - virtual uint32_t MaxPacketSize() const = 0; + virtual int EmitBatch(const thrift::Batch &batch) = 0; + virtual uint32_t MaxPacketSize() const = 0; }; } // namespace jaeger diff --git a/exporters/jaeger/src/udp_transport.cc b/exporters/jaeger/src/udp_transport.cc index 82624ae313..6ae41e7b9a 100644 --- a/exporters/jaeger/src/udp_transport.cc +++ b/exporters/jaeger/src/udp_transport.cc @@ -1,6 +1,9 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#include // std::stringstream + +#include "opentelemetry/sdk_config.h" #include "udp_transport.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -36,7 +39,7 @@ void UDPTransport::InitSocket() int err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { - // TODO: handle error + OTEL_INTERNAL_LOG_ERROR("Jaeger Exporter: WSAStartup failed with error: " << error); return; } @@ -63,7 +66,7 @@ void UDPTransport::CleanSocket() #endif } -void UDPTransport::EmitBatch(const thrift::Batch &batch) +int UDPTransport::EmitBatch(const thrift::Batch &batch) { try { @@ -71,6 +74,8 @@ void UDPTransport::EmitBatch(const thrift::Batch &batch) } catch (...) {} + + return static_cast(batch.spans.size()); } } // namespace jaeger diff --git a/exporters/jaeger/src/udp_transport.h b/exporters/jaeger/src/udp_transport.h index b75987e4a5..0997a27c63 100644 --- a/exporters/jaeger/src/udp_transport.h +++ b/exporters/jaeger/src/udp_transport.h @@ -27,7 +27,6 @@ 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 TSocket = apache::thrift::transport::TSocket; using TTransport = apache::thrift::transport::TTransport; class UDPTransport : public Transport @@ -38,7 +37,7 @@ class UDPTransport : public Transport UDPTransport(const std::string &addr, uint16_t port); virtual ~UDPTransport(); - void EmitBatch(const thrift::Batch &batch) override; + int EmitBatch(const thrift::Batch &batch) override; uint32_t MaxPacketSize() const override { return max_packet_size_; } diff --git a/exporters/jaeger/test/jaeger_recordable_test.cc b/exporters/jaeger/test/jaeger_recordable_test.cc index 693e630fe3..04a114c3f7 100644 --- a/exporters/jaeger/test/jaeger_recordable_test.cc +++ b/exporters/jaeger/test/jaeger_recordable_test.cc @@ -20,7 +20,7 @@ using namespace opentelemetry::sdk::instrumentationlibrary; TEST(JaegerSpanRecordable, SetIdentity) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; int64_t trace_id_val[2] = {0x0000000000000000, 0x1000000000000000}; int64_t span_id_val = 0x2000000000000000; @@ -57,7 +57,7 @@ TEST(JaegerSpanRecordable, SetIdentity) TEST(JaegerSpanRecordable, SetName) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; nostd::string_view name = "Test Span"; rec.SetName(name); @@ -69,7 +69,7 @@ TEST(JaegerSpanRecordable, SetName) TEST(JaegerSpanRecordable, SetStartTime) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); opentelemetry::common::SystemTimestamp start_timestamp(start_time); @@ -84,7 +84,7 @@ TEST(JaegerSpanRecordable, SetStartTime) TEST(JaegerSpanRecordable, SetDuration) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; opentelemetry::common::SystemTimestamp start_timestamp; @@ -102,7 +102,7 @@ TEST(JaegerSpanRecordable, SetDuration) TEST(JaegerSpanRecordable, SetStatus) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; const char *error_description = "Error test"; rec.SetStatus(trace::StatusCode::kError, error_description); @@ -125,7 +125,7 @@ TEST(JaegerSpanRecordable, SetStatus) TEST(JaegerSpanRecordable, AddEvent) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; nostd::string_view name = "Test Event"; @@ -159,7 +159,7 @@ TEST(JaegerSpanRecordable, AddEvent) TEST(JaegerSpanRecordable, SetInstrumentationLibrary) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; std::string library_name = "opentelemetry-cpp"; std::string library_version = "0.1.0"; @@ -181,7 +181,7 @@ TEST(JaegerSpanRecordable, SetInstrumentationLibrary) TEST(JaegerSpanRecordable, SetResource) { - opentelemetry::exporter::jaeger::Recordable rec; + opentelemetry::exporter::jaeger::JaegerRecordable rec; const std::string service_name_key = "service.name"; std::string service_name_value = "test-jaeger-service-name"; diff --git a/exporters/otlp/CMakeLists.txt b/exporters/otlp/CMakeLists.txt index 1ca788af58..27c0171774 100644 --- a/exporters/otlp/CMakeLists.txt +++ b/exporters/otlp/CMakeLists.txt @@ -52,7 +52,8 @@ install( DIRECTORY include/opentelemetry/exporters/otlp DESTINATION include/opentelemetry/exporters/ FILES_MATCHING - PATTERN "*.h") + PATTERN "*.h" + PATTERN "otlp_recordable.h" EXCLUDE) if(BUILD_TESTING) add_executable(otlp_recordable_test test/otlp_recordable_test.cc) diff --git a/exporters/otlp/src/otlp_grpc_exporter.cc b/exporters/otlp/src/otlp_grpc_exporter.cc index 42c0b5d300..2d522ca3cd 100644 --- a/exporters/otlp/src/otlp_grpc_exporter.cc +++ b/exporters/otlp/src/otlp_grpc_exporter.cc @@ -3,10 +3,11 @@ #include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h" #include "opentelemetry/exporters/otlp/otlp_recordable.h" +#include "opentelemetry/sdk_config.h" #include #include -#include +#include // std::stringstream OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -112,7 +113,8 @@ sdk::common::ExportResult OtlpGrpcExporter::Export( if (!status.ok()) { - std::cerr << "[OTLP Exporter] Export() failed: " << status.error_message() << "\n"; + + OTEL_INTERNAL_LOG_ERROR("[OTLP Exporter] Export() failed: " << status.error_message()); return sdk::common::ExportResult::kFailure; } return sdk::common::ExportResult::kSuccess; diff --git a/exporters/otlp/src/otlp_http_exporter.cc b/exporters/otlp/src/otlp_http_exporter.cc index dec5268165..80cbfa40c7 100644 --- a/exporters/otlp/src/otlp_http_exporter.cc +++ b/exporters/otlp/src/otlp_http_exporter.cc @@ -16,11 +16,12 @@ #include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h" #include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" +#include "opentelemetry/sdk_config.h" #include #include -#include #include +#include #include #include @@ -66,14 +67,15 @@ class ResponseHandler : public http_client::EventHandler if (console_debug_) { - std::cout << "[OTLP HTTP Exporter] Status:" << response.GetStatusCode() << std::endl - << "Header:" << std::endl; - response.ForEachHeader([](opentelemetry::nostd::string_view header_name, - opentelemetry::nostd::string_view header_value) { - std::cout << "\t" << header_name.data() << " : " << header_value.data() << std::endl; + std::stringstream ss; + ss << "[OTLP HTTP Exporter] 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; }); - std::cout << "Body:" << std::endl << body_ << std::endl; + ss << "Body:" << body_; + OTEL_INTERNAL_LOG_DEBUG(ss.str()); } // Set the response_received_ flag to true and notify any threads waiting on this result @@ -82,7 +84,7 @@ class ResponseHandler : public http_client::EventHandler 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() */ @@ -111,114 +113,94 @@ class ResponseHandler : public http_client::EventHandler switch (state) { case http_client::SessionState::CreateFailed: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: session create failed" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: session create failed"); cv_.notify_all(); break; case http_client::SessionState::Created: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: session created" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: session created"); } break; case http_client::SessionState::Destroyed: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: session destroyed" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: session destroyed"); } break; case http_client::SessionState::Connecting: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: connecting to peer" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: connecting to peer"); } break; case http_client::SessionState::ConnectFailed: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: connection failed" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: connection failed"); cv_.notify_all(); break; case http_client::SessionState::Connected: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: connected" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: connected"); } break; case http_client::SessionState::Sending: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: sending request" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: sending request"); } break; case http_client::SessionState::SendFailed: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: request send failed" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: request send failed"); cv_.notify_all(); break; case http_client::SessionState::Response: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: response received" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: response received"); } break; case http_client::SessionState::SSLHandshakeFailed: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: SSL handshake failed" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: SSL handshake failed"); cv_.notify_all(); break; case http_client::SessionState::TimedOut: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: request time out" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: request time out"); cv_.notify_all(); break; case http_client::SessionState::NetworkError: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: network error" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: network error"); cv_.notify_all(); break; case http_client::SessionState::ReadError: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: error reading response" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Session state: error reading response"); } break; case http_client::SessionState::WriteError: if (console_debug_) { - std::cerr << "[OTLP HTTP Exporter] Session state: error writing request" << std::endl; + OTEL_INTERNAL_LOG_DEBUG( + "[OTLP HTTP Exporter] DEBUG:Session state: error writing request"); } break; case http_client::SessionState::Cancelled: - if (console_debug_) - { - std::cerr << "[OTLP HTTP Exporter] Session state: (manually) cancelled" << std::endl; - } + OTEL_INTERNAL_LOG_ERROR("[OTLP HTTP Exporter] Session state: (manually) cancelled\n"); cv_.notify_all(); break; @@ -612,16 +594,16 @@ sdk::common::ExportResult OtlpHttpExporter::Export( { if (options_.console_debug) { - std::cout << "[OTLP HTTP Exporter] Request body(Binary):\n" - << service_request.Utf8DebugString() << std::endl; + OTEL_INTERNAL_LOG_DEBUG( + "[OTLP HTTP Exporter] Request body(Binary): " << service_request.Utf8DebugString()); } } else { if (options_.console_debug) { - std::cout << "[OTLP HTTP Exporter] Serialize body failed(Binary):" - << service_request.InitializationErrorString() << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Serialize body failed(Binary):" + << service_request.InitializationErrorString()); } return sdk::common::ExportResult::kFailure; } @@ -638,7 +620,7 @@ sdk::common::ExportResult OtlpHttpExporter::Export( json_request.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); if (options_.console_debug) { - std::cout << "[OTLP HTTP Exporter] Request body(Json):\n" << post_body_json << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] Request body(Json)" << post_body_json); } body_vec.assign(post_body_json.begin(), post_body_json.end()); content_type = kHttpJsonContentType; @@ -660,8 +642,9 @@ sdk::common::ExportResult OtlpHttpExporter::Export( // Wait for the response to be received if (options_.console_debug) { - std::cout << "[OTLP HTTP Exporter] Waiting for response from " << options_.url - << " (timeout = " << options_.timeout.count() << " milliseconds)" << std::endl; + OTEL_INTERNAL_LOG_DEBUG("[OTLP HTTP Exporter] DEBUG: Waiting for response from " + << options_.url << " (timeout = " << options_.timeout.count() + << " milliseconds)"); } bool write_successful = handler->waitForResponse(); diff --git a/exporters/zipkin/BUILD b/exporters/zipkin/BUILD new file mode 100644 index 0000000000..4052640981 --- /dev/null +++ b/exporters/zipkin/BUILD @@ -0,0 +1,44 @@ +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", + 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", + deps = [ + ":zipkin_recordable", + "//ext/src/http/client/curl:http_client_curl", + ], +) + +cc_test( + name = "zipkin_recordable_test", + srcs = ["test/zipkin_recordable_test.cc"], + deps = [ + ":zipkin_recordable", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/exporters/zipkin/CMakeLists.txt b/exporters/zipkin/CMakeLists.txt index 5f6f9887a6..2316860960 100644 --- a/exporters/zipkin/CMakeLists.txt +++ b/exporters/zipkin/CMakeLists.txt @@ -34,7 +34,8 @@ install( DIRECTORY include/opentelemetry/exporters/zipkin DESTINATION include/opentelemetry/exporters/ FILES_MATCHING - PATTERN "*.h") + PATTERN "*.h" + PATTERN "recordable.h" EXCLUDE) if(BUILD_TESTING) add_executable(zipkin_recordable_test test/zipkin_recordable_test.cc) diff --git a/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h b/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h index 628d7330a8..51f83211f0 100644 --- a/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h +++ b/exporters/zipkin/include/opentelemetry/exporters/zipkin/recordable.h @@ -14,12 +14,6 @@ namespace zipkin { using ZipkinSpan = nlohmann::json; -enum class TransportFormat -{ - kJson, - kProtobuf -}; - class Recordable final : public sdk::trace::Recordable { public: diff --git a/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h b/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h index cb244867d7..7e9321435c 100644 --- a/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h +++ b/exporters/zipkin/include/opentelemetry/exporters/zipkin/zipkin_exporter.h @@ -3,7 +3,6 @@ #pragma once -#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/trace/exporter.h" @@ -41,6 +40,12 @@ inline const std::string GetDefaultZipkinEndpoint() return std::string{endpoint_from_env ? endpoint_from_env : kZipkinEndpointDefault}; } +enum class TransportFormat +{ + kJson, + kProtobuf +}; + /** * Struct to hold Zipkin exporter options. */ diff --git a/exporters/zipkin/src/zipkin_exporter.cc b/exporters/zipkin/src/zipkin_exporter.cc index 717f9e7c97..f09487becb 100644 --- a/exporters/zipkin/src/zipkin_exporter.cc +++ b/exporters/zipkin/src/zipkin_exporter.cc @@ -5,6 +5,7 @@ #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" OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -62,8 +63,8 @@ sdk::common::ExportResult ZipkinExporter::Export( 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); - if (result && result.GetResponse().GetStatusCode() == 200 || - result.GetResponse().GetStatusCode() == 202) + if (result && + (result.GetResponse().GetStatusCode() == 200 || result.GetResponse().GetStatusCode() == 202)) { return sdk::common::ExportResult::kSuccess; } @@ -71,7 +72,7 @@ sdk::common::ExportResult ZipkinExporter::Export( { if (result.GetSessionState() == http_client::SessionState::ConnectFailed) { - // TODO -> Handle error / retries + OTEL_INTERNAL_LOG_ERROR("ZIPKIN EXPORTER] Zipkin Exporter: Connection failed"); } return sdk::common::ExportResult::kFailure; } diff --git a/exporters/zipkin/test/zipkin_recordable_test.cc b/exporters/zipkin/test/zipkin_recordable_test.cc index 8a141462e8..f16ac2ab7e 100644 --- a/exporters/zipkin/test/zipkin_recordable_test.cc +++ b/exporters/zipkin/test/zipkin_recordable_test.cc @@ -144,7 +144,6 @@ TEST(ZipkinSpanRecordable, AddEventDefault) TEST(ZipkinSpanRecordable, AddEventWithAttributes) { opentelemetry::exporter::zipkin::Recordable rec; - nostd::string_view name = "Test Event"; std::chrono::system_clock::time_point event_time = std::chrono::system_clock::now(); opentelemetry::common::SystemTimestamp event_timestamp(event_time); diff --git a/ext/include/opentelemetry/ext/http/client/http_client.h b/ext/include/opentelemetry/ext/http/client/http_client.h index 4e3e9845e0..308335e492 100644 --- a/ext/include/opentelemetry/ext/http/client/http_client.h +++ b/ext/include/opentelemetry/ext/http/client/http_client.h @@ -183,8 +183,8 @@ class Result { if (response_ == nullptr) { - static NoopResponse res; - return res; + // let's not return nullptr + response_.reset(new NoopResponse()); } return *response_; } diff --git a/sdk/include/opentelemetry/sdk/common/global_log_handler.h b/sdk/include/opentelemetry/sdk/common/global_log_handler.h new file mode 100644 index 0000000000..f967111e3a --- /dev/null +++ b/sdk/include/opentelemetry/sdk/common/global_log_handler.h @@ -0,0 +1,209 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/common/attribute_utils.h" +#include "opentelemetry/version.h" + +#define OTEL_INTERNAL_LOG_LEVEL_ERROR 0 +#define OTEL_INTERNAL_LOG_LEVEL_WARN 1 +#define OTEL_INTERNAL_LOG_LEVEL_INFO 2 +#define OTEL_INTERNAL_LOG_LEVEL_DEBUG 3 // to be disabled in release + +#ifndef OTEL_INTERNAL_LOG_LEVEL +# define OTEL_INTERNAL_LOG_LEVEL OTEL_INTERNAL_LOG_LEVEL_WARN // ERROR and WARN +#endif + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace common +{ +namespace internal_log +{ + +enum class LogLevel +{ + Error = 0, + Warning, + Info, + Debug +}; + +inline std::string LevelToString(LogLevel level) +{ + switch (level) + { + case LogLevel::Error: + return "Error"; + case LogLevel::Warning: + return "Warning"; + case LogLevel::Info: + return "Info"; + case LogLevel::Debug: + return "Debug"; + } + return {}; +} + +class LogHandler +{ +public: + virtual void Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &attributes) noexcept = 0; +}; + +class DefaultLogHandler : public LogHandler +{ +public: + void Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &attributes) noexcept override + { + std::stringstream output_s; + output_s << "[" << LevelToString(level) << "] "; + if (file != nullptr) + { + output_s << "File: " << file << ":" << line; + } + if (msg != nullptr) + { + output_s << msg; + } + output_s << std::endl; + // TBD - print attributes + std::cout << output_s.str(); // thread safe. + } +}; + +class NoopLogHandler : public LogHandler +{ +public: + void Handle(LogLevel level, + const char *file, + int line, + const char *msg, + const sdk::common::AttributeMap &error_attributes) noexcept override + { + // ignore the log message + } +}; + +/** + * Stores the singleton global LogHandler. + */ +class GlobalLogHandler +{ +public: + /** + * Returns the singleton LogHandler. + * + * By default, a default LogHandler is returned. This will never return a + * nullptr LogHandler. + */ + static nostd::shared_ptr GetLogHandler() noexcept + { + return nostd::shared_ptr(GetHandler()); + } + + /** + * Changes the singleton LogHandler. + * This should be called once at the start of application before creating TracerProvider + * instance. + */ + static void SetLogHandler(nostd::shared_ptr eh) noexcept { GetHandler() = eh; } + +private: + static nostd::shared_ptr &GetHandler() noexcept + { + static nostd::shared_ptr handler(new DefaultLogHandler); + return handler; + } +}; + +} // namespace internal_log +} // namespace common +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE + +#define OTEL_INTERNAL_LOG_DISPATCH(level, message, attributes) \ + do \ + { \ + using namespace opentelemetry::sdk::common::internal_log; \ + std::stringstream tmp_stream; \ + tmp_stream << message; \ + GlobalLogHandler::GetLogHandler()->Handle(level, __FILE__, __LINE__, tmp_stream.str().c_str(), \ + attributes); \ + } while (0) + +#define OTEL_INTERNAL_LOG_GET_3RD_ARG(arg1, arg2, arg3, ...) arg3 + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_ERROR +# define OTEL_INTERNAL_LOG_ERROR_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Error, message, \ + {}) +# define OTEL_INTERNAL_LOG_ERROR_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Error, message, \ + attributes) +# define OTEL_INTERNAL_LOG_ERROR_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_ERROR_2_ARGS, \ + OTEL_INTERNAL_LOG_ERROR_1_ARGS) +# define OTEL_INTERNAL_LOG_ERROR(...) OTEL_INTERNAL_LOG_ERROR_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_ERROR(...) +#endif + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_WARN +# define OTEL_INTERNAL_LOG_WARN_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Warn, message, \ + {}) +# define OTEL_INTERNAL_LOG_WARN_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Warn, message, \ + attributes) +# define OTEL_INTERNAL_LOG_WARN_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_WARN_2_ARGS, \ + OTEL_INTERNAL_LOG_WARN_1_ARGS) +# define OTEL_INTERNAL_LOG_WARN(...) OTEL_INTERNAL_LOG_WARN_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_ERROR(...) +#endif + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_DEBUG +# define OTEL_INTERNAL_LOG_DEBUG_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_log::LogLevel::Debug, message, \ + {}) +# define OTEL_INTERNAL_LOG_DEBUG_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_logg::LogLevel::Debug, \ + message, attributes) +# define OTEL_INTERNAL_LOG_DEBUG_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_DEBUG_2_ARGS, \ + OTEL_INTERNAL_LOG_DEBUG_1_ARGS) +# define OTEL_INTERNAL_LOG_DEBUG(...) OTEL_INTERNAL_LOG_DEBUG_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_DEBUG(...) +#endif + +#if OTEL_INTERNAL_LOG_LEVEL >= OTEL_INTERNAL_LOG_LEVEL_INFO +# define OTEL_INTERNAL_LOG_INFO_1_ARGS(message) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_logger::LogLevel::Info, \ + message, {}) +# define OTEL_INTERNAL_LOG_INFO_2_ARGS(message, attributes) \ + OTEL_INTERNAL_LOG_DISPATCH(opentelemetry::sdk::common::internal_logger::LogLevel::Info, \ + message, attributes) +# define OTEL_INTERNAL_LOG_INFO_MACRO(...) \ + OTEL_INTERNAL_LOG_GET_3RD_ARG(__VA_ARGS__, OTEL_INTERNAL_LOG_ERROR_2_ARGS, \ + OTEL_INTERNAL_LOG_ERROR_1_ARGS) +# define OTEL_INTERNAL_LOG_INFO(...) OTEL_INTERNAL_LOG_INFO_MACRO(__VA_ARGS__)(__VA_ARGS__) +#else +# define OTEL_INTERNAL_LOG_INFO(...) +#endif diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index 6aeb19fa57..fd9f2c8e8d 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -60,8 +60,10 @@ class Tracer final : public trace_api::Tracer, public std::enable_shared_from_th Sampler &GetSampler() { return context_->GetSampler(); } private: - std::shared_ptr context_; + // order of declaration is important here - instrumentation library should destroy after + // tracer-context. std::shared_ptr instrumentation_library_; + std::shared_ptr context_; }; } // namespace trace } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_context.h b/sdk/include/opentelemetry/sdk/trace/tracer_context.h index e4d968d2b0..572f60cafe 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_context.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_context.h @@ -88,11 +88,11 @@ class TracerContext bool Shutdown() noexcept; private: - // This is an atomic pointer so we can adapt the processor pipeline dynamically. - std::unique_ptr processor_; + // order of declaration is important here - resource object should be destroyed after processor. opentelemetry::sdk::resource::Resource resource_; std::unique_ptr sampler_; std::unique_ptr id_generator_; + std::unique_ptr processor_; }; } // namespace trace diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index bd131704d5..ec825b6e3c 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -96,8 +96,9 @@ class TracerProvider final : public opentelemetry::trace::TracerProvider bool ForceFlush(std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept; private: - std::shared_ptr context_; + // order of declaration is important here - tracers should destroy only after context. std::vector> tracers_; + std::shared_ptr context_; std::mutex lock_; }; } // namespace trace diff --git a/sdk/include/opentelemetry/sdk_config.h b/sdk/include/opentelemetry/sdk_config.h new file mode 100644 index 0000000000..8ac72a10d5 --- /dev/null +++ b/sdk/include/opentelemetry/sdk_config.h @@ -0,0 +1,7 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/config.h" +#include "opentelemetry/sdk/common/global_log_handler.h" \ No newline at end of file diff --git a/sdk/src/common/global_log_handler.cc b/sdk/src/common/global_log_handler.cc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index 970f61b7e9..fe61dd4ce8 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -4,6 +4,7 @@ #ifdef ENABLE_LOGS_PREVIEW # include "opentelemetry/sdk/logs/logger.h" # include "opentelemetry/sdk/logs/log_record.h" +# include "opentelemetry/sdk_config.h" # include "opentelemetry/trace/provider.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -48,7 +49,7 @@ void Logger::Log(opentelemetry::logs::Severity severity, auto recordable = processor->MakeRecordable(); if (recordable == nullptr) { - // TODO: Error diagnostics should indicate "recordable creation failed" to user + OTEL_INTERNAL_LOG_ERROR("[LOGGER] Recordable creation failed"); return; } diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index c84d847bd9..977722d0ca 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -18,7 +18,7 @@ namespace trace Tracer::Tracer(std::shared_ptr context, std::unique_ptr instrumentation_library) noexcept - : context_{context}, instrumentation_library_{std::move(instrumentation_library)} + : instrumentation_library_{std::move(instrumentation_library)}, context_{context} {} nostd::shared_ptr Tracer::StartSpan( diff --git a/sdk/src/trace/tracer_context.cc b/sdk/src/trace/tracer_context.cc index 196cecd6a0..eaab96154c 100644 --- a/sdk/src/trace/tracer_context.cc +++ b/sdk/src/trace/tracer_context.cc @@ -14,10 +14,10 @@ TracerContext::TracerContext(std::vector> &&proce opentelemetry::sdk::resource::Resource resource, std::unique_ptr sampler, std::unique_ptr id_generator) noexcept - : processor_(std::unique_ptr(new MultiSpanProcessor(std::move(processors)))), - resource_(resource), + : resource_(resource), sampler_(std::move(sampler)), - id_generator_(std::move(id_generator)) + id_generator_(std::move(id_generator)), + processor_(std::unique_ptr(new MultiSpanProcessor(std::move(processors)))) {} Sampler &TracerContext::GetSampler() const noexcept diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index ed6853be67..911e66c2f0 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/sdk_config.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -40,9 +41,10 @@ nostd::shared_ptr TracerProvider::GetTracer( { library_name = ""; } - // if (library_name == "") { - // // TODO: log invalid library_name. - // } + if (library_name == "") + { + OTEL_INTERNAL_LOG_ERROR("[TracerProvider::GetTracer] Library name is empty."); + } const std::lock_guard guard(lock_);