From a92f637df38a8980996271fb56b3ebfec6d7cefa Mon Sep 17 00:00:00 2001 From: isaachier Date: Mon, 20 Nov 2017 11:52:47 -0500 Subject: [PATCH] Add crossdock tests (#12) --- .gitignore | 7 +- .travis.yml | 4 +- CMakeLists.txt | 64 +- cmake/CodeCoverage.cmake | 65 +- cmake/Hunter/config.cmake | 2 +- codecov.yml | 1 + crossdock/Dockerfile | 19 + crossdock/Server.cpp | 781 ++++++++++++++++++ crossdock/Server.h | 86 ++ crossdock/docker-compose.yml | 54 ++ examples/hotrod/CustomerService.cpp | 179 ---- examples/hotrod/CustomerService.h | 68 -- examples/hotrod/Delay.cpp | 48 -- examples/hotrod/HTTPServer.cpp | 127 --- examples/hotrod/HTTPServer.h | 64 -- idl | 2 +- scripts/clang-format.sh | 4 +- scripts/clang-tidy.sh | 2 +- scripts/update-licenses.sh | 5 +- src/jaegertracing/Span.cpp | 63 ++ src/jaegertracing/Span.h | 6 + src/jaegertracing/SpanContext.h | 2 + src/jaegertracing/SpanContextTest.cpp | 13 +- src/jaegertracing/Tracer.cpp | 8 +- src/jaegertracing/Tracer.h | 38 +- src/jaegertracing/TracerTest.cpp | 8 +- src/jaegertracing/baggage/BaggageTest.cpp | 2 +- .../baggage/RemoteRestrictionJSON.cpp | 24 +- .../baggage/RemoteRestrictionJSON.h | 58 ++ .../baggage/RemoteRestrictionManager.cpp | 27 +- src/jaegertracing/net/IPAddress.cpp | 60 ++ src/jaegertracing/net/IPAddress.h | 46 +- src/jaegertracing/net/IPAddressTest.cpp | 9 +- src/jaegertracing/net/Socket.h | 6 +- src/jaegertracing/net/URI.cpp | 29 - src/jaegertracing/net/URI.h | 15 - src/jaegertracing/net/URITest.cpp | 9 +- src/jaegertracing/net/http/Header.h | 6 +- src/jaegertracing/net/http/Request.cpp | 4 + src/jaegertracing/net/http/Request.h | 3 + src/jaegertracing/net/http/Response.cpp | 5 +- src/jaegertracing/propagation/Propagator.h | 4 +- .../reporters/RemoteReporter.cpp | 12 +- .../samplers/RemoteSamplingJSON.cpp | 17 + .../samplers/RemoteSamplingJSON.h | 168 ++++ .../samplers/RemotelyControlledSampler.cpp | 28 +- src/jaegertracing/testutils/MockAgent.cpp | 7 +- src/jaegertracing/testutils/MockAgentTest.cpp | 29 +- 48 files changed, 1512 insertions(+), 776 deletions(-) create mode 100644 crossdock/Dockerfile create mode 100644 crossdock/Server.cpp create mode 100644 crossdock/Server.h create mode 100644 crossdock/docker-compose.yml delete mode 100644 examples/hotrod/CustomerService.cpp delete mode 100644 examples/hotrod/CustomerService.h delete mode 100644 examples/hotrod/Delay.cpp delete mode 100644 examples/hotrod/HTTPServer.cpp delete mode 100644 examples/hotrod/HTTPServer.h rename examples/hotrod/Delay.h => src/jaegertracing/baggage/RemoteRestrictionJSON.cpp (53%) create mode 100644 src/jaegertracing/baggage/RemoteRestrictionJSON.h create mode 100644 src/jaegertracing/samplers/RemoteSamplingJSON.cpp create mode 100644 src/jaegertracing/samplers/RemoteSamplingJSON.h diff --git a/.gitignore b/.gitignore index 52f02ec8..a63030df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ -# Build directory +# Build files /build +/crossdock/crossdock +/crossdock/jaeger-docker-compose.yml # vim swap files *.swo @@ -37,3 +39,6 @@ *.exe *.out *.app + +# Log files +*.log diff --git a/.travis.yml b/.travis.yml index e930bbcf..494ab729 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,9 +19,9 @@ matrix: sources: - ubuntu-toolchain-r-test packages: - - g++-4.8 + - g++-4.9 env: - - MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8" + - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" branches: only: - master diff --git a/CMakeLists.txt b/CMakeLists.txt index 81cf855f..e50c1884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,10 +6,11 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/cmake/toolchain.cmake") set(HUNTER_CACHE_SERVERS "https://github.com/isaachier/cpp-client-hunter-cache") +include(CMakeDependentOption) include(HunterGate) HunterGate( - URL "https://github.com/ruslo/hunter/archive/v0.19.141.tar.gz" - SHA1 "8d7162c0665aba01aaf6bf62f1ed015dc66c5e7c" + URL "https://github.com/ruslo/hunter/archive/v0.19.158.tar.gz" + SHA1 "7bd94f374bca4eff51292ca6ba029b5d688744db" LOCAL ) @@ -88,9 +89,16 @@ find_package(OpenTracing CONFIG REQUIRED) list(APPEND LIBS OpenTracing::opentracing-static) list(APPEND package_deps opentracing-cpp) -option(JAEGERTRACING_WITH_YAML_CPP "Use yaml-cpp to parse config files" ON) +hunter_add_package(nlohmann_json) +find_package(nlohmann_json CONFIG REQUIRED) +list(APPEND LIBS nlohmann_json) +list(APPEND package_deps nlohmann_json) + option(JAEGERTRACING_COVERAGE "Build with coverage" $ENV{COVERAGE}) -option(JAEGERTRACING_BUILD_EXAMPLES "Build examples" ON) +option(JAEGERTRACING_BUILD_CROSSDOCK "Build crossdock" $ENV{CROSSDOCK}) +cmake_dependent_option( + JAEGERTRACING_WITH_YAML_CPP "Use yaml-cpp to parse config files" ON + "NOT JAEGERTRACING_BUILD_CROSSDOCK" ON) if(JAEGERTRACING_WITH_YAML_CPP) hunter_add_package(yaml-cpp) @@ -118,20 +126,36 @@ if(BUILD_TESTING) endif() endif() -if(JAEGERTRACING_BUILD_EXAMPLES) - set(EXAMPLES_SRC - examples/hotrod/Delay.cpp - examples/hotrod/HTTPServer.cpp) - add_executable(customer examples/hotrod/CustomerService.cpp ${EXAMPLES_SRC}) - # TODO: add_executable(driver examples/hotrod/DriverService.cpp) - # TODO: add_executable(frontend examples/hotrod/FrontendService.cpp) - # TODO: add_executable(route examples/hotrod/RouteService.cpp) - set(services customer) # TODO: driver frontend route) - foreach(service IN LISTS services) - target_include_directories(${service} PUBLIC - $) - target_link_libraries(${service} PUBLIC jaegertracing-static) - endforeach() +if(JAEGERTRACING_BUILD_CROSSDOCK) + set(CROSSDOCK_SRC crossdock/Server.cpp) + add_executable(crossdock ${CROSSDOCK_SRC}) + target_include_directories(crossdock PUBLIC + $) + target_link_libraries(crossdock PUBLIC jaegertracing-static) + + set(JAEGER_CROSSDOCK_URL +"https://raw.githubusercontent.com/jaegertracing/jaeger/master/docker-compose/\ +jaeger-docker-compose.yml") + file(DOWNLOAD ${JAEGER_CROSSDOCK_URL} + "${PROJECT_SOURCE_DIR}/crossdock/jaeger-docker-compose.yml") + find_program(DOCKER_COMPOSE_EXE docker-compose REQUIRED) + set(DOCKER_COMPOSE_CMD ${DOCKER_COMPOSE_EXE} + -f ${PROJECT_SOURCE_DIR}/crossdock/docker-compose.yml + -f ${PROJECT_SOURCE_DIR}/crossdock/jaeger-docker-compose.yml) + add_custom_target(crossdock-kill + COMMAND ${DOCKER_COMPOSE_CMD} kill + COMMAND ${DOCKER_COMPOSE_CMD} rm --force) + add_custom_target(crossdock-run + COMMAND ${DOCKER_COMPOSE_CMD} build + COMMAND ${DOCKER_COMPOSE_CMD} run crossdock + DEPENDS crossdock-kill) + add_custom_target(crossdock-fresh + COMMAND ${DOCKER_COMPOSE_CMD} pull + COMMAND ${DOCKER_COMPOSE_CMD} build + COMMAND ${DOCKER_COMPOSE_CMD} run crossdock + DEPENDS crossdock-kill) + add_custom_target(crossdock-logs + COMMAND ${DOCKER_COMPOSE_CMD} logs) endif() set(SRC @@ -147,10 +171,11 @@ set(SRC src/jaegertracing/Transport.cpp src/jaegertracing/UDPTransport.cpp src/jaegertracing/baggage/BaggageSetter.cpp + src/jaegertracing/baggage/RemoteRestrictionJSON.cpp + src/jaegertracing/baggage/RemoteRestrictionManager.cpp src/jaegertracing/baggage/Restriction.cpp src/jaegertracing/baggage/RestrictionManager.cpp src/jaegertracing/baggage/RestrictionsConfig.cpp - src/jaegertracing/baggage/RemoteRestrictionManager.cpp src/jaegertracing/metrics/Counter.cpp src/jaegertracing/metrics/Gauge.cpp src/jaegertracing/metrics/InMemoryStatsReporter.cpp @@ -192,6 +217,7 @@ set(SRC src/jaegertracing/samplers/GuaranteedThroughputProbabilisticSampler.cpp src/jaegertracing/samplers/ProbabilisticSampler.cpp src/jaegertracing/samplers/RateLimitingSampler.cpp + src/jaegertracing/samplers/RemoteSamplingJSON.cpp src/jaegertracing/samplers/RemotelyControlledSampler.cpp src/jaegertracing/samplers/Sampler.cpp src/jaegertracing/samplers/SamplingStatus.cpp diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index 5c39d565..f63ca532 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -72,7 +72,6 @@ include(CMakeParseArguments) find_program( GCOV_PATH gcov ) find_program( LCOV_PATH lcov ) find_program( GENHTML_PATH genhtml ) -find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) find_program( SIMPLE_PYTHON_EXECUTABLE python ) if(NOT GCOV_PATH) @@ -128,13 +127,12 @@ endif() # NOTE! The executable should always have a ZERO as exit code otherwise # the coverage generation will not complete. # -# SETUP_TARGET_FOR_COVERAGE( +# setup_target_for_coverage( # NAME testrunner_coverage # New target name # EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR # DEPENDENCIES testrunner # Dependencies to build first # ) -function(SETUP_TARGET_FOR_COVERAGE) - +function(setup_target_for_coverage) set(options NONE) set(oneValueArgs NAME) set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) @@ -174,63 +172,10 @@ function(SETUP_TARGET_FOR_COVERAGE) COMMAND ; COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." ) +endfunction() # setup_target_for_coverage -endfunction() # SETUP_TARGET_FOR_COVERAGE - -# Defines a target for running and collection code coverage information -# Builds dependencies, runs the given executable and outputs reports. -# NOTE! The executable should always have a ZERO as exit code otherwise -# the coverage generation will not complete. -# -# SETUP_TARGET_FOR_COVERAGE_COBERTURA( -# NAME ctest_coverage # New target name -# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR -# DEPENDENCIES executable_target # Dependencies to build first -# ) -function(SETUP_TARGET_FOR_COVERAGE_COBERTURA) - - set(options NONE) - set(oneValueArgs NAME) - set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) - cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - if(NOT SIMPLE_PYTHON_EXECUTABLE) - message(FATAL_ERROR "python not found! Aborting...") - endif() # NOT SIMPLE_PYTHON_EXECUTABLE - - if(NOT GCOVR_PATH) - message(FATAL_ERROR "gcovr not found! Aborting...") - endif() # NOT GCOVR_PATH - - # Combine excludes to several -e arguments - set(COBERTURA_EXCLUDES "") - foreach(EXCLUDE ${COVERAGE_EXCLUDES}) - set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}") - endforeach() - - add_custom_target(${Coverage_NAME} - - # Run tests - ${Coverage_EXECUTABLE} - - # Running gcovr - COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES} - -o ${Coverage_NAME}.xml - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - DEPENDS ${Coverage_DEPENDENCIES} - COMMENT "Running gcovr to produce Cobertura code coverage report." - ) - - # Show info where to find the report - add_custom_command(TARGET ${Coverage_NAME} POST_BUILD - COMMAND ; - COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." - ) - -endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA - -function(APPEND_COVERAGE_COMPILER_FLAGS) +function(append_coverage_compiler_flags) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") -endfunction() # APPEND_COVERAGE_COMPILER_FLAGS +endfunction() # append_coverage_compiler_flags diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index 653180a5..8bc95a80 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -1 +1 @@ -hunter_config(thrift VERSION 0.9.2-p1) +hunter_config(thrift VERSION 0.9.2-p2) diff --git a/codecov.yml b/codecov.yml index 167301b1..f47db21f 100644 --- a/codecov.yml +++ b/codecov.yml @@ -28,3 +28,4 @@ comment: ignore: - "src/jaegertracing/thrift-gen" - "**/*Test.cpp" + - "crossdock" diff --git a/crossdock/Dockerfile b/crossdock/Dockerfile new file mode 100644 index 00000000..284109ca --- /dev/null +++ b/crossdock/Dockerfile @@ -0,0 +1,19 @@ +FROM gcc:7.2 + +WORKDIR / +RUN curl -O https://cmake.org/files/v3.10/cmake-3.10.0-rc5-Linux-x86_64.sh && \ + bash cmake-3.10.0-rc5-Linux-x86_64.sh --skip-license + +COPY . /app/jaegertracing +RUN rm -rf /app/jaegertracing/build && \ + mkdir /app/jaegertracing/build && \ + cd /app/jaegertracing/build && \ + cmake -DCMAKE_BUILD_TYPE=Debug -DJAEGERTRACING_BUILD_CROSSDOCK=ON .. && \ + make crossdock -j3 + +ENV AGENT_HOST_PORT=jaeger-agent:5775 +ENV SAMPLING_SERVER_URL=http://test_driver:5778/sampling + +EXPOSE 8080-8082 + +CMD ["/app/jaegertracing/build/crossdock"] diff --git a/crossdock/Server.cpp b/crossdock/Server.cpp new file mode 100644 index 00000000..e5be6001 --- /dev/null +++ b/crossdock/Server.cpp @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Server.h" + +#include +#include +#include +#include +#include + +#include + +#include "jaegertracing/Tracer.h" +#include "jaegertracing/net/IPAddress.h" +#include "jaegertracing/net/Socket.h" +#include "jaegertracing/net/http/Request.h" +#include "jaegertracing/net/http/Response.h" + +namespace jaegertracing { +namespace crossdock { +namespace thrift { + +#define JSON_FROM_FIELD(var, field) \ + { \ + json[#field] = var.field; \ + } + +#define FIELD_FROM_JSON(var, field) \ + { \ + var.__set_##field(json.at(#field)); \ + } + +void to_json(nlohmann::json& json, const Transport::type& transport) +{ + json = _Transport_VALUES_TO_NAMES.at(static_cast(transport)); +} + +void from_json(const nlohmann::json& json, Transport::type& transport) +{ + const auto str = json.get(); + if (str == "HTTP") { + transport = Transport::HTTP; + return; + } + if (str == "TCHANNEL") { + transport = Transport::TCHANNEL; + return; + } + if (str == "DUMMY") { + transport = Transport::DUMMY; + return; + } + std::ostringstream oss; + oss << "Invalid transport value " << str; + throw std::invalid_argument(oss.str()); +} + +void to_json(nlohmann::json& json, const Downstream& downstream) +{ + JSON_FROM_FIELD(downstream, serviceName); + JSON_FROM_FIELD(downstream, serverRole); + JSON_FROM_FIELD(downstream, host); + JSON_FROM_FIELD(downstream, port); + JSON_FROM_FIELD(downstream, transport); + if (downstream.downstream) { + json["downstream"] = *downstream.downstream; + } +} + +void from_json(const nlohmann::json& json, Downstream& downstream) +{ + FIELD_FROM_JSON(downstream, serviceName); + FIELD_FROM_JSON(downstream, serverRole); + FIELD_FROM_JSON(downstream, host); + FIELD_FROM_JSON(downstream, port); + downstream.__set_transport(json.at("transport").get()); + auto itr = json.find("downstream"); + if (itr != std::end(json) && !itr->is_null()) { + downstream.__set_downstream(itr->get()); + } +} + +void to_json(nlohmann::json& json, const StartTraceRequest& request) +{ + JSON_FROM_FIELD(request, serverRole); + JSON_FROM_FIELD(request, sampled); + JSON_FROM_FIELD(request, baggage); + JSON_FROM_FIELD(request, downstream); +} + +void from_json(const nlohmann::json& json, StartTraceRequest& request) +{ + FIELD_FROM_JSON(request, serverRole); + FIELD_FROM_JSON(request, sampled); + FIELD_FROM_JSON(request, baggage); + FIELD_FROM_JSON(request, downstream); +} + +void to_json(nlohmann::json& json, const JoinTraceRequest& request) +{ + JSON_FROM_FIELD(request, serverRole); + if (request.__isset.downstream) { + json["downstream"] = request.downstream; + } +} + +void from_json(const nlohmann::json& json, JoinTraceRequest& request) +{ + FIELD_FROM_JSON(request, serverRole); + auto itr = json.find("downstream"); + if (itr != std::end(json) && !itr->is_null()) { + request.__set_downstream(itr->get()); + } +} + +void to_json(nlohmann::json& json, const ObservedSpan& observedSpan) +{ + JSON_FROM_FIELD(observedSpan, traceId); + JSON_FROM_FIELD(observedSpan, sampled); + JSON_FROM_FIELD(observedSpan, baggage); +} + +void from_json(const nlohmann::json& json, ObservedSpan& observedSpan) +{ + FIELD_FROM_JSON(observedSpan, traceId); + FIELD_FROM_JSON(observedSpan, sampled); + FIELD_FROM_JSON(observedSpan, baggage); +} + +void to_json(nlohmann::json& json, const TraceResponse& response) +{ + if (response.__isset.span) { + JSON_FROM_FIELD(response, span); + } + if (response.downstream) { + json["downstream"] = *response.downstream; + } + JSON_FROM_FIELD(response, notImplementedError); +} + +void from_json(const nlohmann::json& json, TraceResponse& response) +{ + auto itr = json.find("span"); + if (itr != std::end(json) && !itr->is_null()) { + response.__set_span(itr->get()); + } + itr = json.find("downstream"); + if (itr != std::end(json) && !itr->is_null()) { + response.__set_downstream(itr->get()); + } + FIELD_FROM_JSON(response, notImplementedError); +} + +#undef FIELD_FROM_JSON +#undef JSON_FROM_FIELD + +} // namespace thrift + +namespace { + +constexpr auto kBaggageKey = "crossdock-baggage-key"; +constexpr auto kDefaultTracerServiceName = "crossdock-cpp"; + +std::string escape(const std::string& str) +{ + std::string result; + result.reserve(str.size()); + for (auto&& ch : str) { + switch (ch) { + case '\n': { + result += "\\n"; + } break; + case '\r': { + result += "\\r"; + } break; + default: { + result += ch; + } break; + } + } + return result; +} + +std::string bufferedRead(net::Socket& socket) +{ + constexpr auto kBufferSize = 256; + std::array buffer; + std::string data; + auto numRead = ::read(socket.handle(), &buffer[0], buffer.size()); + data.append(&buffer[0], numRead); + while (numRead == kBufferSize) { + numRead = ::read(socket.handle(), &buffer[0], buffer.size()); + data.append(&buffer[0], numRead); + } + return data; +} + +class RequestReader : public opentracing::HTTPHeadersReader { + public: + explicit RequestReader(const net::http::Request& request) + : _request(request) + { + } + + opentracing::expected ForeachKey( + std::function(opentracing::string_view, + opentracing::string_view)> f) + const override + { + for (auto&& header : _request.headers()) { + const auto result = f(header.key(), header.value()); + if (!result) { + return result; + } + } + return opentracing::make_expected(); + } + + private: + const net::http::Request& _request; +}; + +class RequestWriter : public opentracing::HTTPHeadersWriter { + public: + explicit RequestWriter(std::ostream& requestStream) + : _requestStream(requestStream) + { + } + + opentracing::expected Set( + opentracing::string_view key, opentracing::string_view value) + const override + { + _requestStream << key << ": " << value << "\r\n"; + return opentracing::make_expected(); + } + + private: + std::ostream& _requestStream; +}; + +thrift::ObservedSpan observeSpan(const opentracing::SpanContext& ctx) +{ + const auto& sc = static_cast(ctx); + thrift::ObservedSpan observedSpan; + std::ostringstream oss; + oss << sc.traceID(); + observedSpan.__set_traceId(oss.str()); + observedSpan.__set_sampled(sc.isSampled()); + auto itr = sc.baggage().find(kBaggageKey); + if (itr != std::end(sc.baggage())) { + observedSpan.__set_baggage(itr->second); + } + return observedSpan; +} + +thrift::TraceResponse callDownstreamHTTP(const opentracing::SpanContext& ctx, + const thrift::Downstream& target, + opentracing::Tracer& tracer, + logging::Logger& logger) +{ + thrift::JoinTraceRequest request; + request.__set_serverRole(target.serverRole); + if (target.downstream) { + request.__set_downstream(*target.downstream); + } + + const auto requestJSON = nlohmann::json(request).dump(); + net::Socket socket; + socket.open(AF_INET, SOCK_STREAM); + const auto authority = target.host + ':' + target.port; + socket.connect("http://" + authority); + std::ostringstream oss; + oss << "POST /join_trace HTTP/1.1\r\n" + "Host: " + << authority << "\r\n"; + RequestWriter writer(oss); + tracer.Inject(ctx, writer); + oss << "Connection: close\r\n" + "Content-Type: application/json\r\n" + "Content-Length: " << requestJSON.size() << "\r\n\r\n" + << requestJSON; + const auto message = oss.str(); + logger.info("Sending request downstream: " + escape(message)); + const auto numWritten = + ::write(socket.handle(), &message[0], message.size()); + (void)numWritten; + + const auto responseStr = bufferedRead(socket); + logger.info("Received downstream response: " + escape(responseStr)); + std::istringstream iss(responseStr); + auto response = net::http::Response::parse(iss); + return nlohmann::json::parse(response.body()); +} + +thrift::TraceResponse callDownstream(const opentracing::SpanContext& ctx, + const std::string& /* role */, + const thrift::Downstream& downstream, + opentracing::Tracer& tracer, + logging::Logger& logger) +{ + thrift::TraceResponse response; + + switch (downstream.transport) { + case thrift::Transport::HTTP: { + response = callDownstreamHTTP(ctx, downstream, tracer, logger); + } break; + case thrift::Transport::TCHANNEL: { + response.__set_notImplementedError( + "TCHANNEL transport not implemented"); + } break; + case thrift::Transport::DUMMY: { + response.__set_notImplementedError("DUMMY transport not implemented"); + } break; + default: { + throw std::invalid_argument("Unrecognized protocol " + + std::to_string(downstream.transport)); + } break; + } + + return response; +} + +thrift::TraceResponse prepareResponse( + const opentracing::SpanContext& ctx, + const std::string& role, + const thrift::Downstream* downstream, + opentracing::Tracer& tracer, + logging::Logger& logger) +{ + const auto observedSpan = observeSpan(ctx); + thrift::TraceResponse response; + response.__set_span(observedSpan); + if (downstream) { + response.__set_downstream( + callDownstream(ctx, role, *downstream, tracer, logger)); + } + return response; +} + +struct GenerateTracesRequest { + using StrMap = std::unordered_map; + + std::string _type; + std::string _operation; + StrMap _tags; + int _count; +}; + +void from_json(const nlohmann::json& json, GenerateTracesRequest& request) +{ + request._type = json.at("type"); + request._operation = json.at("operation"); + request._tags = json.at("tags").get(); + request._count = json.at("count"); +} + +} // anonymous namespace + +using Handler = std::function; + +class Server::SocketListener { + public: + SocketListener(const net::IPAddress& ip, + const std::shared_ptr& logger, + Handler handler) + : _ip(ip) + , _logger(logger) + , _handler(handler) + , _running(false) + { + assert(_logger); + } + + ~SocketListener() { stop(); } + + void start() + { + std::promise started; + _thread = std::thread([this, &started]() { start(_ip, started); }); + started.get_future().get(); + } + + void stop() noexcept + { + if (_running) { + _running = false; + _thread.join(); + _socket.close(); + } + } + + private: + void start(const net::IPAddress& ip, std::promise& started) + { + _socket.open(AF_INET, SOCK_STREAM); + const auto enable = 1; + ::setsockopt(_socket.handle(), + SOL_SOCKET, + SO_REUSEADDR, + &enable, + sizeof(enable)); + _socket.bind(ip); + _socket.listen(); + _running = true; + started.set_value(); + + using TaskList = std::deque>; + TaskList tasks; + + while (_running) { + auto client = _socket.accept(); + auto future = std::async( + std::launch::async, + [this](net::Socket&& socket) { + net::Socket client(std::move(socket)); + auto requestStr = bufferedRead(client); + _logger->info("Received request: " + escape(requestStr)); + + try { + std::istringstream iss(requestStr); + const auto request = net::http::Request::parse(iss); + const auto responseStr = _handler(request); + const auto numWritten = ::write( + client.handle(), + &responseStr[0], + responseStr.size()); + if (numWritten != + static_cast(responseStr.size())) { + std::ostringstream oss; + oss << "Unable to write entire response" + ", numWritten=" << numWritten + << ", responseSize=" << responseStr.size(); + _logger->error(oss.str()); + } + } catch (...) { + utils::ErrorUtil::logError(*_logger, "Server error"); + constexpr auto message = + "HTTP/1.1 500 Internal Server Error\r\n\r\n"; + constexpr auto messageSize = sizeof(message) - 1; + const auto numWritten = + ::write(client.handle(), + message, + messageSize); + (void)numWritten; + } + + client.close(); + }, std::move(client)); + tasks.emplace_back(std::move(future)); + } + + std::for_each(std::begin(tasks), + std::end(tasks), + [](TaskList::value_type& future) { future.get(); }); + } + + net::IPAddress _ip; + net::Socket _socket; + std::shared_ptr _logger; + Handler _handler; + std::atomic _running; + std::thread _thread; +}; + +class Server::EndToEndHandler { + public: + using TracerPtr = std::shared_ptr; + + EndToEndHandler(const std::string& agentHostPort, + const std::string& samplingServerURL) + : _agentHostPort(agentHostPort) + , _samplingServerURL(samplingServerURL) + { + } + + TracerPtr findOrMakeTracer(std::string samplerType) + { + if (samplerType.empty()) { + samplerType = kSamplerTypeRemote; + } + + std::lock_guard lock(_mutex); + auto itr = _tracers.find(samplerType); + if (itr != std::end(_tracers)) { + return itr->second; + } + return init(samplerType); + } + + private: + Config makeEndToEndConfig(const std::string& samplerType) const + { + return Config( + false, + samplers::Config(samplerType, + 1.0, + _samplingServerURL, + samplers::Config::kDefaultMaxOperations, + std::chrono::seconds(5)), + reporters::Config(reporters::Config::kDefaultQueueSize, + std::chrono::seconds(1), + false, + _agentHostPort)); + } + + TracerPtr init(const std::string& samplerType) + { + const auto config = makeEndToEndConfig(samplerType); + auto tracer = Tracer::make(kDefaultTracerServiceName, config); + _tracers[config.sampler().type()] = tracer; + return tracer; + } + + std::string _agentHostPort; + std::string _samplingServerURL; + std::unordered_map _tracers; + std::mutex _mutex; +}; + +Server::Server(const net::IPAddress& clientIP, + const net::IPAddress& serverIP, + const std::string& agentHostPort, + const std::string& samplingServerURL) + : _logger(logging::consoleLogger()) + , _tracer(Tracer::make(kDefaultTracerServiceName, Config(), _logger)) + , _clientListener( + new SocketListener(clientIP, _logger, + [this](const net::http::Request& request) { + return handleRequest(request); + })) + , _serverListener( + new SocketListener(serverIP, _logger, + [this](const net::http::Request& request) { + return handleRequest(request); + })) + , _handler(new EndToEndHandler(agentHostPort, samplingServerURL)) +{ +} + +Server::~Server() = default; + +void Server::serve() +{ + _clientListener->start(); + _serverListener->start(); +} + +template +std::string Server::handleJSON( + const net::http::Request& request, + std::function handler) +{ + RequestReader reader(request); + auto result = _tracer->Extract(reader); + if (!result) { + std::ostringstream oss; + oss << "Cannot read request body: opentracing error code " + << result.error().value(); + const auto message = oss.str(); + oss.str(""); + oss.clear(); + oss << "HTTP/1.1 400 Bad Request\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + } + + std::unique_ptr ctx(result->release()); + opentracing::StartSpanOptions options; + options.start_system_timestamp = std::chrono::system_clock::now(); + options.start_steady_timestamp = std::chrono::steady_clock::now(); + if (ctx) { + options.references.emplace_back(std::make_pair( + opentracing::SpanReferenceType::ChildOfRef, ctx.get())); + } + auto span = _tracer->StartSpanWithOptions("post", options); + + RequestType thriftRequest; + try { + thriftRequest = nlohmann::json::parse(request.body()); + } catch (const std::exception& ex) { + std::ostringstream oss; + oss << "Cannot parse request JSON: " << ex.what() + << ", json: " << request.body(); + const auto message = oss.str(); + oss.str(""); + oss.clear(); + oss << "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } catch (...) { + std::ostringstream oss; + oss << "Cannot parse request JSON, json: " << request.body(); + const auto message = oss.str(); + oss.str(""); + oss.clear(); + oss << "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } + + const auto thriftResponse = handler(thriftRequest, span->context()); + try { + const auto message = nlohmann::json(thriftResponse).dump(); + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } catch (const std::exception& ex) { + std::ostringstream oss; + oss << "Cannot marshal response to JSON: " << ex.what(); + const auto message = oss.str(); + oss.str(""); + oss.clear(); + oss << "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } catch (...) { + std::ostringstream oss; + oss << "Cannot marshal response to JSON"; + const auto message = oss.str(); + oss.str(""); + oss.clear(); + oss << "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } +} + +std::string Server::handleRequest(const net::http::Request& request) +{ + if (request.target() == "/") { + return "HTTP/1.1 200 OK\r\n\r\n"; + } + if (request.target() == "/start_trace") { + return handleJSON( + request, + [this](const thrift::StartTraceRequest& request, + const opentracing::SpanContext& /* ctx */) { + return startTrace(request); + }); + } + if (request.target() == "/join_trace") { + return handleJSON( + request, + [this](const thrift::JoinTraceRequest& request, + const opentracing::SpanContext& ctx) { + return joinTrace(request, ctx); + }); + } + if (request.target() == "/create_traces") { + return generateTraces(request); + } + return "HTTP/1.1 404 Not Found\r\n\r\n"; +} + +thrift::TraceResponse +Server::startTrace(const crossdock::thrift::StartTraceRequest& request) +{ + auto span = _tracer->StartSpan(request.serverRole); + if (request.sampled) { + span->SetTag("sampling.priority", 1); + } + span->SetBaggageItem(kBaggageKey, request.baggage); + + return prepareResponse( + span->context(), + request.serverRole, + &request.downstream, + *_tracer, + *_logger); +} + +thrift::TraceResponse +Server::joinTrace(const crossdock::thrift::JoinTraceRequest& request, + const opentracing::SpanContext& ctx) +{ + return prepareResponse( + ctx, + request.serverRole, + request.__isset.downstream ? &request.downstream : nullptr, + *_tracer, + *_logger); +} + +std::string Server::generateTraces(const net::http::Request& requestHTTP) +{ + GenerateTracesRequest request; + try { + request = nlohmann::json::parse(requestHTTP.body()); + } catch (const std::exception& ex) { + std::ostringstream oss; + oss << "JSON payload is invalid: " << ex.what(); + const auto message = oss.str(); + oss.str(""); + oss.clear(); + oss << "HTTP/1.1 400 Bad Request\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } catch (...) { + const std::string message("JSON payload is invalid"); + std::ostringstream oss; + oss << "HTTP/1.1 400 Bad Request\r\n" + "Content-Length: " << message.size() << "\r\n\r\n" + << message; + return oss.str(); + } + + auto tracer = _handler->findOrMakeTracer(request._type); + if (!tracer) { + const std::string message("Tracer is not initialized"); + std::ostringstream oss; + oss << "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Length: " << message.size() << "\r\n" + << message; + return oss.str(); + } + + for (auto i = 0; i < request._count; ++i) { + auto span = tracer->StartSpan(request._operation); + for (auto&& pair : request._tags) { + span->SetTag(pair.first, pair.second); + } + span->Finish(); + } + + return "HTTP/1.1 200 OK\r\n\r\n"; +} + +} // namespace crossdock +} // namespace jaegertracing + +int main() +{ + const auto rawAgentHostPort = std::getenv("AGENT_HOST_PORT"); + const std::string agentHostPort(rawAgentHostPort ? rawAgentHostPort : ""); + if (agentHostPort.empty()) { + std::cerr << "env AGENT_HOST_PORT is not specified!\n"; + return 1; + } + + const auto rawSamplingServerURL = std::getenv("SAMPLING_SERVER_URL"); + const std::string samplingServerURL( + rawSamplingServerURL ? rawSamplingServerURL : ""); + if (samplingServerURL.empty()) { + std::cerr << "env SAMPLING_SERVER_URL is not specified!\n"; + return 1; + } + + jaegertracing::crossdock::Server server( + jaegertracing::net::IPAddress::v4("0.0.0.0:8080"), + jaegertracing::net::IPAddress::v4("0.0.0.0:8081"), + agentHostPort, + samplingServerURL); + server.serve(); + + std::this_thread::sleep_for(std::chrono::hours(1)); + return 0; +} diff --git a/crossdock/Server.h b/crossdock/Server.h new file mode 100644 index 00000000..f88b8ae6 --- /dev/null +++ b/crossdock/Server.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JAEGERTRACING_CROSSDOCK_SERVER_H +#define JAEGERTRACING_CROSSDOCK_SERVER_H + +#include + +#include + +#include "jaegertracing/thrift-gen/tracetest_types.h" + +namespace jaegertracing { +namespace logging { + +class Logger; + +} // namespace logging + +namespace net { + +class IPAddress; + +namespace http { + +class Request; + +} // namespace http +} // namespace net + +namespace crossdock { + +class Server { + public: + Server(const net::IPAddress& clientIP, + const net::IPAddress& serverIP, + const std::string& agentHostPort, + const std::string& samplingServerURL); + + ~Server(); + + void serve(); + + private: + template + std::string handleJSON( + const net::http::Request& request, + std::function handler); + + std::string handleRequest(const net::http::Request& request); + + thrift::TraceResponse startTrace(const thrift::StartTraceRequest& request); + + thrift::TraceResponse joinTrace(const thrift::JoinTraceRequest& request, + const opentracing::SpanContext& ctx); + + std::string generateTraces(const net::http::Request& request); + + class SocketListener; + class EndToEndHandler; + + std::shared_ptr _logger; + std::shared_ptr _tracer; + std::unique_ptr _clientListener; + std::unique_ptr _serverListener; + std::unique_ptr _handler; +}; + +} // namespace crossdock +} // namespace jaegertracing + +#endif // JAEGERTRACING_CROSSDOCK_SERVER_H diff --git a/crossdock/docker-compose.yml b/crossdock/docker-compose.yml new file mode 100644 index 00000000..1ab6faf2 --- /dev/null +++ b/crossdock/docker-compose.yml @@ -0,0 +1,54 @@ +version: '2' + +services: + crossdock: + image: crossdock/crossdock + links: + - test_driver + - go + - cpp + environment: + - WAIT_FOR=test_driver,go,cpp + - WAIT_FOR_TIMEOUT=60s + + - CALL_TIMEOUT=60s + + - AXIS_CLIENT=go + - AXIS_S1NAME=go,cpp + - AXIS_SAMPLED=true,false + - AXIS_S2NAME=go,cpp + - AXIS_S2TRANSPORT=http + - AXIS_S3NAME=go,cpp + - AXIS_S3TRANSPORT=http + + - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2transport,s3name,s3transport + + - AXIS_TESTDRIVER=test_driver + - AXIS_SERVICES=cpp + + - BEHAVIOR_ENDTOEND=testdriver,services + + - REPORT=compact + + go: + image: jaegertracing/xdock-go + ports: + - "8080-8082" + + cpp: + depends_on: + - test_driver + build: + context: .. + dockerfile: crossdock/Dockerfile + ports: + - "8080-8082" + + test_driver: + image: jaegertracing/test-driver + depends_on: + - jaeger-query + - jaeger-collector + - jaeger-agent + ports: + - "8080" diff --git a/examples/hotrod/CustomerService.cpp b/examples/hotrod/CustomerService.cpp deleted file mode 100644 index 7c98e8eb..00000000 --- a/examples/hotrod/CustomerService.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CustomerService.h" - -#include -#include -#include -#include - -#include - -#include "jaegertracing/Logging.h" -#include "jaegertracing/Tracer.h" - -#include "Delay.h" -#include "HTTPServer.h" - -namespace jaegertracing { -namespace examples { -namespace hotrod { -namespace { - -class Database { - public: - using Table = std::unordered_map; - - Database(opentracing::Tracer& tracer, logging::Logger& logger) - : _tracer(tracer) - , _logger(logger) - , _customers({ - {"123", Customer("123", "Rachel's Floral Designs", "115,227")}, - {"567", Customer("567", "Amazing Coffee Roasters", "211,653")}, - {"392", Customer("392", "Trom Chocolatier", "577,322")}, - {"731", Customer("731", "Japanese Deserts", "728,326")} - }) - { - } - - Customer get(const std::string& customerID, - const opentracing::Span& parentSpan) const - { - _logger.info("Loading customer, customer_id=" + customerID); - opentracing::StartSpanOptions options; - options.start_system_timestamp = std::chrono::system_clock::now(); - options.start_steady_timestamp = std::chrono::steady_clock::now(); - options.references.emplace_back( - opentracing::SpanReferenceType::ChildOfRef, - &parentSpan.context()); - - auto span = _tracer.StartSpanWithOptions("SQL SELECT", options); - const auto query = - "SELECT * FROM customer WHERE customer_id=" + customerID; - span->SetTag("sql.query", query); - // Span destructor calls `Finish`, so not invoking explicitly. - - std::lock_guard lock(_mutex); - delay::sleep(std::chrono::seconds(1), std::chrono::milliseconds(100)); - auto itr = _customers.find(customerID); - if (itr == std::end(_customers)) { - return Customer(); - } - return itr->second; - } - - private: - opentracing::Tracer& _tracer; - logging::Logger& _logger; - Table _customers; - mutable std::mutex _mutex; -}; - -class CustomerServiceImpl : public CustomerService { - public: - CustomerServiceImpl() - : _logger(logging::consoleLogger()) - , _tracer(Tracer::make( - "customer", - Config(false, - samplers::Config("const", 1), - reporters::Config( - reporters::Config::kDefaultQueueSize, - reporters::Config::defaultBufferFlushInterval(), - true)), - _logger)) - , _database(*_tracer, *_logger) - { - } - - Customer get(const std::string& customerID) override - { - // TODO: Consider client sending span to server. - auto span = _tracer->StartSpan("GET /customer"); - return _database.get(customerID, *span); - } - - private: - std::shared_ptr _logger; - std::shared_ptr _tracer; - Database _database; -}; - -} // anonymous namespace - -} // namespace hotrod -} // namespace examples -} // namespace jaegertracing - -int main(int argc, const char* argv[]) -{ - namespace hotrod = jaegertracing::examples::hotrod; - - hotrod::CustomerServiceImpl impl; - hotrod::HTTPServer server( - jaegertracing::net::IPAddress::v4("127.0.0.1:8080")); - server.registerHandler( - std::regex("/customer"), - [&impl](jaegertracing::net::Socket&& socket, - const jaegertracing::net::http::Request& request) { - const auto uri = jaegertracing::net::URI::parse( - "http://127.0.0.1:8080" + request.target()); - const auto queryValues = uri.parseQueryValues(); - auto itr = queryValues.find("customer"); - if (itr == std::end(queryValues)) { - const std::string message( - "HTTP/1.1 400 Bad Request\r\n\r\n" - "Missing required 'customer' parameter"); - const auto numWritten = - ::write(socket.handle(), &message[0], message.size()); - (void)numWritten; - return; - } - - const auto customerID = itr->second; - const auto customer = impl.get(customerID); - - if (customer.id().empty()) { - const std::string message( - "HTTP/1.1 500 Internal Server Error\r\n\r\n" - "Request failed, cannot find customer"); - const auto numWritten = - ::write(socket.handle(), &message[0], message.size()); - (void)numWritten; - return; - } - - std::ostringstream oss; - oss << "{ \"ID\": \"" << customer.id() << '"' - << ", \"name\": \"" << customer.name() << '"' - << ", \"location\": \"" << customer.location() << '"' - << " }"; - const auto jsonData = oss.str(); - oss.clear(); - oss.str(""); - oss << "HTTP/1.1 200 OK\r\n" - << "Content-Length: " << jsonData.size() << "\r\n" - << "Content-Type: application/json\r\n\r\n" - << jsonData; - const auto message = oss.str(); - const auto numWritten = - ::write(socket.handle(), &message[0], message.size()); - (void)numWritten; - }); - server.start(); - return 0; -} diff --git a/examples/hotrod/CustomerService.h b/examples/hotrod/CustomerService.h deleted file mode 100644 index eb80adfa..00000000 --- a/examples/hotrod/CustomerService.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef JAEGERTRACING_EXAMPLES_HOTROD_CUSTOMERSERVICE_H -#define JAEGERTRACING_EXAMPLES_HOTROD_CUSTOMERSERVICE_H - -#include - -namespace jaegertracing { -namespace examples { -namespace hotrod { - -class Customer { - public: - Customer() = default; - - Customer(const std::string& id, - const std::string& name, - const std::string& location) - : _id(id) - , _name(name) - , _location(location) - { - } - - const std::string& id() const { return _id; } - - void setID(const std::string& id) { _id = id; } - - const std::string& name() const { return _name; } - - void setName(const std::string& name) { _name = name; } - - const std::string& location() const { return _location; } - - void setLocation(const std::string& location) { _location = location; } - - private: - std::string _id; - std::string _name; - std::string _location; -}; - -class CustomerService { - public: - virtual ~CustomerService() = default; - - virtual Customer get(const std::string& customerID) = 0; -}; - -} // namespace hotrod -} // namespace examples -} // namespace jaegertracing - -#endif // JAEGERTRACING_EXAMPLES_HOTROD_CUSTOMERSERVICE_H diff --git a/examples/hotrod/Delay.cpp b/examples/hotrod/Delay.cpp deleted file mode 100644 index 1764cfa2..00000000 --- a/examples/hotrod/Delay.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Delay.h" - -#include - -namespace jaegertracing { -namespace examples { -namespace hotrod { -namespace delay { -void sleep(const Clock::duration& average, - const Clock::duration& standardDeviation) -{ - static std::random_device device; - static std::mt19937 gen(device()); - static std::mutex mutex; - - using FractionalSecond = std::chrono::duration; - std::normal_distribution<> distribution( - std::chrono::duration_cast(average).count(), - std::chrono::duration_cast( - standardDeviation).count()); - FractionalSecond secondsToSleep; - { - std::lock_guard lock(mutex); - secondsToSleep = FractionalSecond(distribution(gen)); - } - std::this_thread::sleep_for(secondsToSleep); -} - -} // namespace delay -} // namespace hotrod -} // namespace examples -} // namespace jaegertracing diff --git a/examples/hotrod/HTTPServer.cpp b/examples/hotrod/HTTPServer.cpp deleted file mode 100644 index 44c59f75..00000000 --- a/examples/hotrod/HTTPServer.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "HTTPServer.h" - -#include -#include - -#include "jaegertracing/utils/ErrorUtil.h" - -namespace jaegertracing { -namespace examples { -namespace hotrod { -namespace { - -auto defaultHandler = - [](net::Socket&& socket, const net::http::Request& /* request */) { - const std::string message = - "HTTP/1.1 404 Not found\r\n"; - const auto numWritten = - ::write(socket.handle(), &message[0], message.size()); - if (numWritten < static_cast(message.size())) { - std::cerr - << "Failed to write entire response to client" - ", message=" << message << '\n'; - } - }; - -} // anonymous namespace - -HTTPServer::HTTPServer(const net::IPAddress& address) - : _address(address) -{ - _socket.open(AF_INET, SOCK_STREAM); -} - -void HTTPServer::registerHandler(const std::regex& pattern, Handler handler) -{ - _handlers.emplace_back(std::make_pair(pattern, handler)); -} - -void HTTPServer::start() -{ - const auto enable = 1; - ::setsockopt( - _socket.handle(), SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); - _socket.bind(_address); - _socket.listen(); - while (true) { - auto clientSocket = _socket.accept(); - - constexpr auto kBufferSize = 256; - std::string buffer(kBufferSize, '\0'); - std::string requestStr; - auto numRead = - ::read(clientSocket.handle(), &buffer[0], buffer.size()); - while (numRead == static_cast(buffer.size())) { - requestStr.append(&buffer[0], numRead); - numRead = ::read(clientSocket.handle(), &buffer[0], buffer.size()); - } - - std::istringstream iss(requestStr); - try { - const auto request = net::http::Request::parse(iss); - auto itr = - std::find_if(std::begin(_handlers), - std::end(_handlers), - [&request](const HandlerVec::value_type& pair) { - return std::regex_search(request.target(), - pair.first); - }); - - Handler handler = ((itr == std::end(_handlers)) ? defaultHandler - : itr->second); - - std::packaged_task task( - handler); - _tasks.push_back(task.get_future()); - task(std::move(clientSocket), request); - } catch (...) { - auto logger = logging::consoleLogger(); - utils::ErrorUtil::logError(*logger, "Error parsing request"); - } - - _tasks.erase( - std::remove_if(std::begin(_tasks), - std::end(_tasks), - [](const TaskList::value_type& task) { - return task.valid(); - }), - std::end(_tasks)); - } -} - -void HTTPServer::close() noexcept -{ - std::for_each(std::begin(_tasks), - std::end(_tasks), - [](TaskList::value_type& task) { - try { - task.get(); - } catch (...) { - auto logger = logging::consoleLogger(); - utils::ErrorUtil::logError( - *logger, "Error finishing task"); - } - }); - _socket.close(); -} - -} // namespace hotrod -} // namespace examples -} // namespace jaegertracing diff --git a/examples/hotrod/HTTPServer.h b/examples/hotrod/HTTPServer.h deleted file mode 100644 index 0fd00b0e..00000000 --- a/examples/hotrod/HTTPServer.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef JAEGERTRACING_EXAMPLES_HOTROD_HTTPSERVER_H -#define JAEGERTRACING_EXAMPLES_HOTROD_HTTPSERVER_H - -#include -#include -#include -#include -#include -#include -#include - -#include "jaegertracing/net/IPAddress.h" -#include "jaegertracing/net/Socket.h" -#include "jaegertracing/net/http/Request.h" - -namespace jaegertracing { -namespace examples { -namespace hotrod { - -class HTTPServer { - public: - using Handler = - std::function; - using HandlerVec = std::vector>; - using TaskList = std::deque>; - - explicit HTTPServer(const net::IPAddress& address); - - ~HTTPServer() { close(); } - - void registerHandler(const std::regex& pattern, Handler handler); - - void start(); - - void close() noexcept; - - private: - net::IPAddress _address; - net::Socket _socket; - HandlerVec _handlers; - TaskList _tasks; -}; - -} // namespace hotrod -} // namespace examples -} // namespace jaegertracing - -#endif // JAEGERTRACING_EXAMPLES_HOTROD_HTTPSERVER_H diff --git a/idl b/idl index 3e77ca5e..6710b74a 160000 --- a/idl +++ b/idl @@ -1 +1 @@ -Subproject commit 3e77ca5eeeffb608ed010f5d9190e071e6e2ea01 +Subproject commit 6710b74a8559dfba9773f972619d2a8cd80178f2 diff --git a/scripts/clang-format.sh b/scripts/clang-format.sh index a0651680..3f1a4383 100755 --- a/scripts/clang-format.sh +++ b/scripts/clang-format.sh @@ -6,7 +6,9 @@ function main() { cd "$project_dir" || exit 1 local srcs - srcs=$(git ls-files src | grep -E -v 'thrift-gen' | grep -E '\.(cpp|h)$') + srcs=$(git ls-files src crossdock | + grep -E -v 'thrift-gen' | + grep -E '\.(cpp|h)$') local cmd for src in $srcs; do diff --git a/scripts/clang-tidy.sh b/scripts/clang-tidy.sh index c799d77a..2946db95 100755 --- a/scripts/clang-tidy.sh +++ b/scripts/clang-tidy.sh @@ -6,7 +6,7 @@ function main() { cd "$project_dir" || exit 1 local srcs - srcs=$(git ls-files src | + srcs=$(git ls-files src crossdock | grep -E -v 'thrift-gen|Test\.cpp' | grep -E '\.cpp$') diff --git a/scripts/update-licenses.sh b/scripts/update-licenses.sh index 622ccc80..e912743c 100755 --- a/scripts/update-licenses.sh +++ b/scripts/update-licenses.sh @@ -2,4 +2,7 @@ set -e -python scripts/update-license.py $(git ls-files "*\.cpp" "*\.h" | grep -v thrift-gen | grep -v tracetest) src/jaegertracing/Constants.h.in +python scripts/update-license.py $(git ls-files "*\.cpp" "*\.h" | + grep -v thrift-gen | + grep -v tracetest) \ + src/jaegertracing/Constants.h.in diff --git a/src/jaegertracing/Span.cpp b/src/jaegertracing/Span.cpp index 8a72e83f..02f767ef 100644 --- a/src/jaegertracing/Span.cpp +++ b/src/jaegertracing/Span.cpp @@ -16,9 +16,49 @@ #include "jaegertracing/Span.h" +#include + #include "jaegertracing/Tracer.h" namespace jaegertracing { +namespace { + +struct SamplingPriorityVisitor { + using result_type = bool; + + bool operator()(bool boolValue) const { return boolValue; } + + bool operator()(double doubleValue) const { return doubleValue > 0; } + + bool operator()(int64_t intValue) const { return intValue > 0; } + + bool operator()(uint64_t uintValue) const { return uintValue > 0; } + + bool operator()(const std::string& str) const + { + std::istringstream iss(str); + auto intValue = 0; + if (!(iss >> intValue)) { + return false; + } + return intValue > 0; + } + + bool operator()(std::nullptr_t) const { return false; } + + bool operator()(const char* str) const + { + return operator()(std::string(str)); + } + + template + bool operator()(OtherType) const + { + return false; + } +}; + +} // anonymous namespace void Span::SetBaggageItem(opentracing::string_view restrictedKey, opentracing::string_view value) noexcept @@ -83,4 +123,27 @@ std::string Span::serviceNameNoLock() const noexcept return _tracer->serviceName(); } +void Span::setSamplingPriority(const opentracing::Value& value) +{ + SamplingPriorityVisitor visitor; + const auto priority = opentracing::Value::visit(value, visitor); + + std::lock_guard lock(_mutex); + auto newFlags = _context.flags(); + if (priority) { + newFlags |= static_cast(SpanContext::Flag::kSampled) | + static_cast(SpanContext::Flag::kDebug); + } + else { + newFlags &= ~static_cast(SpanContext::Flag::kSampled); + } + + _context = SpanContext(_context.traceID(), + _context.spanID(), + _context.parentID(), + newFlags, + _context.baggage(), + _context.debugID()); +} + } // namespace jaegertracing diff --git a/src/jaegertracing/Span.h b/src/jaegertracing/Span.h index 92a31fc5..7d8d411a 100644 --- a/src/jaegertracing/Span.h +++ b/src/jaegertracing/Span.h @@ -208,6 +208,10 @@ class Span : public opentracing::Span { void SetTag(opentracing::string_view key, const opentracing::Value& value) noexcept override { + if (key == "sampling.priority") { + setSamplingPriority(value); + return; + } std::lock_guard lock(_mutex); if (isFinished() || !_context.isSampled()) { return; @@ -271,6 +275,8 @@ class Span : public opentracing::Span { _logs.push_back(log); } + void setSamplingPriority(const opentracing::Value& value); + std::shared_ptr _tracer; SpanContext _context; std::string _operationName; diff --git a/src/jaegertracing/SpanContext.h b/src/jaegertracing/SpanContext.h index a48372e2..38e1ba84 100644 --- a/src/jaegertracing/SpanContext.h +++ b/src/jaegertracing/SpanContext.h @@ -124,6 +124,8 @@ class SpanContext : public opentracing::SpanContext { unsigned char flags() const { return _flags; } + const std::string& debugID() const { return _debugID; } + bool isSampled() const { return _flags & static_cast(Flag::kSampled); diff --git a/src/jaegertracing/SpanContextTest.cpp b/src/jaegertracing/SpanContextTest.cpp index 42bd0631..6e8b7d71 100644 --- a/src/jaegertracing/SpanContextTest.cpp +++ b/src/jaegertracing/SpanContextTest.cpp @@ -90,13 +90,12 @@ TEST(SpanContext, testBaggage) SpanContext::StrMap({ { "key1", "value1" }, { "key2", "value2" } })); std::string keyCopy; std::string valueCopy; - spanContext.ForeachBaggageItem( - [&keyCopy, &valueCopy](const std::string& key, - const std::string& value) { - keyCopy = key; - valueCopy = value; - return false; - }); + spanContext.ForeachBaggageItem([&keyCopy, &valueCopy]( + const std::string& key, const std::string& value) { + keyCopy = key; + valueCopy = value; + return false; + }); ASSERT_TRUE(keyCopy == "key1" || keyCopy == "key2"); if (keyCopy == "key1") { ASSERT_EQ("value1", valueCopy); diff --git a/src/jaegertracing/Tracer.cpp b/src/jaegertracing/Tracer.cpp index e2299061..c1bde46a 100644 --- a/src/jaegertracing/Tracer.cpp +++ b/src/jaegertracing/Tracer.cpp @@ -22,6 +22,8 @@ namespace jaegertracing { using StrMap = SpanContext::StrMap; +constexpr int Tracer::kGen128BitOption; + std::unique_ptr Tracer::StartSpanWithOptions(string_view operationName, const opentracing::StartSpanOptions& options) const @@ -37,7 +39,11 @@ Tracer::StartSpanWithOptions(string_view operationName, SpanContext ctx; if (!parent || !parent->isValid()) { newTrace = true; - const TraceID traceID(randomID(), randomID()); + auto highID = static_cast(0); + if (_options & kGen128BitOption) { + highID = randomID(); + } + const TraceID traceID(highID, randomID()); const auto spanID = traceID.low(); const auto parentID = 0; auto flags = static_cast(0); diff --git a/src/jaegertracing/Tracer.h b/src/jaegertracing/Tracer.h index 9827d097..f2649990 100644 --- a/src/jaegertracing/Tracer.h +++ b/src/jaegertracing/Tracer.h @@ -50,6 +50,8 @@ class Tracer : public opentracing::Tracer, using SystemClock = Span::SystemClock; using string_view = opentracing::string_view; + static constexpr auto kGen128BitOption = 1; + static std::shared_ptr make(const std::string& serviceName, const Config& config) { @@ -72,6 +74,16 @@ class Tracer : public opentracing::Tracer, const Config& config, const std::shared_ptr& logger, metrics::StatsFactory& statsFactory) + { + return make(serviceName, config, logger, statsFactory, 0); + } + + static std::shared_ptr + make(const std::string& serviceName, + const Config& config, + const std::shared_ptr& logger, + metrics::StatsFactory& statsFactory, + int options) { if (serviceName.empty()) { throw std::invalid_argument("no service name provided"); @@ -87,7 +99,8 @@ class Tracer : public opentracing::Tracer, std::shared_ptr reporter( config.reporter().makeReporter(serviceName, *logger, *metrics)); return std::shared_ptr( - new Tracer(serviceName, sampler, reporter, logger, metrics)); + new Tracer( + serviceName, sampler, reporter, logger, metrics, options)); } ~Tracer() { Close(); } @@ -138,22 +151,34 @@ class Tracer : public opentracing::Tracer, opentracing::expected> Extract(std::istream& reader) const override { + const auto spanContext = _binaryPropagator.extract(reader); + if (spanContext == SpanContext()) { + return std::unique_ptr(); + } return std::unique_ptr( - new SpanContext(_binaryPropagator.extract(reader))); + new SpanContext(spanContext)); } opentracing::expected> Extract(const opentracing::TextMapReader& reader) const override { + const auto spanContext = _textPropagator.extract(reader); + if (spanContext == SpanContext()) { + return std::unique_ptr(); + } return std::unique_ptr( - new SpanContext(_textPropagator.extract(reader))); + new SpanContext(spanContext)); } opentracing::expected> Extract(const opentracing::HTTPHeadersReader& reader) const override { + const auto spanContext = _httpHeaderPropagator.extract(reader); + if (spanContext == SpanContext()) { + return std::unique_ptr(); + } return std::unique_ptr( - new SpanContext(_httpHeaderPropagator.extract(reader))); + new SpanContext(spanContext)); } void Close() noexcept override @@ -192,7 +217,8 @@ class Tracer : public opentracing::Tracer, const std::shared_ptr& sampler, const std::shared_ptr& reporter, const std::shared_ptr& logger, - const std::shared_ptr& metrics) + const std::shared_ptr& metrics, + int options) : _serviceName(serviceName) , _hostIPv4(net::IPAddress::localIP(AF_INET)) , _sampler(sampler) @@ -206,6 +232,7 @@ class Tracer : public opentracing::Tracer, , _tags() , _restrictionManager(new baggage::DefaultRestrictionManager(0)) , _baggageSetter(*_restrictionManager, *_metrics) + , _options(options) { _tags.push_back(Tag(kJaegerClientVersionTagKey, kJaegerClientVersion)); @@ -279,6 +306,7 @@ class Tracer : public opentracing::Tracer, std::vector _tags; std::unique_ptr _restrictionManager; baggage::BaggageSetter _baggageSetter; + int _options; }; } // namespace jaegertracing diff --git a/src/jaegertracing/TracerTest.cpp b/src/jaegertracing/TracerTest.cpp index 070e5035..43f31b3f 100644 --- a/src/jaegertracing/TracerTest.cpp +++ b/src/jaegertracing/TracerTest.cpp @@ -116,10 +116,10 @@ TEST(Tracer, testTracer) &debugCtx); const auto& tags = tracer->tags(); - auto itr = std::find_if( - std::begin(tags), - std::end(tags), - [](const Tag& tag) { return tag.key() == kTracerIPTagKey; }); + auto itr = + std::find_if(std::begin(tags), std::end(tags), [](const Tag& tag) { + return tag.key() == kTracerIPTagKey; + }); ASSERT_NE(std::end(tags), itr); ASSERT_TRUE(itr->value().is()); ASSERT_EQ(net::IPAddress::v4(itr->value().get()).host(), diff --git a/src/jaegertracing/baggage/BaggageTest.cpp b/src/jaegertracing/baggage/BaggageTest.cpp index a50e7df5..06a613d5 100644 --- a/src/jaegertracing/baggage/BaggageTest.cpp +++ b/src/jaegertracing/baggage/BaggageTest.cpp @@ -91,7 +91,7 @@ TEST(Baggage, testRemoteRestrictionManagerDefaults) TEST(Baggage, testRemoteRestrictionManagerFunctionality) { - auto logger = logging::nullLogger(); + auto logger = logging::consoleLogger(); auto metrics = metrics::Metrics::makeNullMetrics(); auto mockAgent = testutils::MockAgent::make(); mockAgent->start(); diff --git a/examples/hotrod/Delay.h b/src/jaegertracing/baggage/RemoteRestrictionJSON.cpp similarity index 53% rename from examples/hotrod/Delay.h rename to src/jaegertracing/baggage/RemoteRestrictionJSON.cpp index dd517808..b4e387ef 100644 --- a/examples/hotrod/Delay.h +++ b/src/jaegertracing/baggage/RemoteRestrictionJSON.cpp @@ -14,26 +14,4 @@ * limitations under the License. */ -#ifndef JAEGERTRACING_EXAMPLES_HOTROD_DELAY_H -#define JAEGERTRACING_EXAMPLES_HOTROD_DELAY_H - -#include -#include -#include - -namespace jaegertracing { -namespace examples { -namespace hotrod { -namespace delay { - -using Clock = std::chrono::steady_clock; - -void sleep(const Clock::duration& average, - const Clock::duration& standardDeviation); - -} // namespace delay -} // namespace hotrod -} // namespace examples -} // namespace jaegertracing - -#endif // JAEGERTRACING_EXAMPLES_HOTROD_DELAY_H +#include "jaegertracing/baggage/RemoteRestrictionJSON.h" diff --git a/src/jaegertracing/baggage/RemoteRestrictionJSON.h b/src/jaegertracing/baggage/RemoteRestrictionJSON.h new file mode 100644 index 00000000..c7b46ee5 --- /dev/null +++ b/src/jaegertracing/baggage/RemoteRestrictionJSON.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JAEGERTRACING_BAGGAGE_REMOTERESTRICTIONJSON_H +#define JAEGERTRACING_BAGGAGE_REMOTERESTRICTIONJSON_H + +#include + +#include "jaegertracing/thrift-gen/BaggageRestrictionManager.h" +#include "jaegertracing/thrift-gen/baggage_types.h" + +namespace jaegertracing { +namespace thrift { + +inline void to_json(nlohmann::json& json, const BaggageRestriction& restriction) +{ + json["baggageKey"] = restriction.baggageKey; + json["maxValueLength"] = restriction.maxValueLength; +} + +inline void from_json( + const nlohmann::json& json, BaggageRestriction& restriction) +{ + restriction.__set_baggageKey(json.at("baggageKey")); + restriction.__set_maxValueLength(json.at("maxValueLength")); +} + +using BaggageRestrictionList = + BaggageRestrictionManager_getBaggageRestrictions_result; + +inline void to_json(nlohmann::json& json, const BaggageRestrictionList& list) +{ + json = list.success; +} + +inline void from_json(const nlohmann::json& json, BaggageRestrictionList& list) +{ + list.success = json.get>(); + list.__isset.success = true; +} + +} // namespace thrift +} // namespace jaegertracing + +#endif // JAEGERTRACING_BAGGAGE_REMOTERESTRICTIONJSON_H diff --git a/src/jaegertracing/baggage/RemoteRestrictionManager.cpp b/src/jaegertracing/baggage/RemoteRestrictionManager.cpp index 916c99a2..39024adc 100644 --- a/src/jaegertracing/baggage/RemoteRestrictionManager.cpp +++ b/src/jaegertracing/baggage/RemoteRestrictionManager.cpp @@ -18,8 +18,7 @@ #include -#include - +#include "jaegertracing/baggage/RemoteRestrictionJSON.h" #include "jaegertracing/net/http/Response.h" #include "jaegertracing/utils/ErrorUtil.h" @@ -97,8 +96,8 @@ void RemoteRestrictionManager::poll() noexcept updateRestrictions(remoteURI); } catch (...) { auto logger = logging::consoleLogger(); - utils::ErrorUtil::logError( - *logger, "Failed in RemoteRestrictionManager::poll"); + utils::ErrorUtil::logError(*logger, + "Failed in RemoteRestrictionManager::poll"); return; } @@ -106,11 +105,9 @@ void RemoteRestrictionManager::poll() noexcept while (true) { { std::unique_lock lock(_mutex); - _cv.wait_until(lock, - lastUpdateTime + _refreshInterval, - [this]() { - return !_running; - }); + _cv.wait_until(lock, lastUpdateTime + _refreshInterval, [this]() { + return !_running; + }); if (!_running) { return; } @@ -138,18 +135,8 @@ void RemoteRestrictionManager::updateRestrictions( return; } - boost::shared_ptr transport( - new apache::thrift::transport::TMemoryBuffer()); - apache::thrift::protocol::TJSONProtocol protocol(transport); - std::vector buffer; - buffer.reserve(responseHTTP.body().size()); - buffer.insert(std::end(buffer), - std::begin(responseHTTP.body()), - std::end(responseHTTP.body())); - transport->write(&buffer[0], buffer.size()); thrift::BaggageRestrictionManager_getBaggageRestrictions_result - response; - response.read(&protocol); + response = nlohmann::json::parse(responseHTTP.body()); if (response.__isset.success) { KeyRestrictionMap restrictions; restrictions.reserve(response.success.size()); diff --git a/src/jaegertracing/net/IPAddress.cpp b/src/jaegertracing/net/IPAddress.cpp index 8316eee7..bc53439b 100644 --- a/src/jaegertracing/net/IPAddress.cpp +++ b/src/jaegertracing/net/IPAddress.cpp @@ -57,5 +57,65 @@ IPAddress IPAddress::localIP(std::function filter) return IPAddress(); } +IPAddress IPAddress::versionFromString( + const std::string& ip, int port, int family) +{ + ::sockaddr_storage addrStorage; + std::memset(&addrStorage, 0, sizeof(addrStorage)); + + auto* addrBuffer = static_cast(nullptr); + if (family == AF_INET) { + ::sockaddr_in& addr = + *reinterpret_cast<::sockaddr_in*>(&addrStorage); + addr.sin_family = family; + addr.sin_port = htons(port); + addrBuffer = &addr.sin_addr; + } + else { + assert(family == AF_INET6); + ::sockaddr_in6& addr = + *reinterpret_cast<::sockaddr_in6*>(&addrStorage); + addr.sin6_family = family; + addr.sin6_port = htons(port); + addrBuffer = &addr.sin6_addr; + } + + const auto returnCode = inet_pton(family, ip.c_str(), addrBuffer); + if (returnCode == 0) { + auto result = resolveAddress(ip, port, family); + assert(result); + std::memcpy(&addrStorage, result->ai_addr, result->ai_addrlen); + } + return IPAddress(addrStorage, + family == AF_INET ? sizeof(::sockaddr_in) + : sizeof(::sockaddr_in6)); +} + +std::unique_ptr<::addrinfo, AddrInfoDeleter> +resolveAddress(const std::string& host, int port, int family, int type) +{ + ::addrinfo hints; + std::memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = type; + + std::string service; + if (port != 0) { + service = std::to_string(port); + } + + auto* servInfoPtr = static_cast<::addrinfo*>(nullptr); + const auto returnCode = + getaddrinfo(host.c_str(), service.c_str(), &hints, &servInfoPtr); + std::unique_ptr<::addrinfo, AddrInfoDeleter> servInfo(servInfoPtr); + if (returnCode != 0) { + std::ostringstream oss; + oss << "Error resolving address: " << gai_strerror(returnCode); + throw std::runtime_error(oss.str()); + } + + return servInfo; +} + } // namespace net } // namespace jaegertracing diff --git a/src/jaegertracing/net/IPAddress.h b/src/jaegertracing/net/IPAddress.h index 710568b2..868c6676 100644 --- a/src/jaegertracing/net/IPAddress.h +++ b/src/jaegertracing/net/IPAddress.h @@ -18,6 +18,7 @@ #define JAEGERTRACING_NET_IPADDRESS_H #include +#include #include #include #include @@ -165,45 +166,22 @@ class IPAddress { private: static IPAddress - versionFromString(const std::string& ip, int port, int family) - { - ::sockaddr_storage addrStorage; - std::memset(&addrStorage, 0, sizeof(addrStorage)); - - auto* addrBuffer = static_cast(nullptr); - if (family == AF_INET) { - ::sockaddr_in& addr = - *reinterpret_cast<::sockaddr_in*>(&addrStorage); - addr.sin_family = family; - addr.sin_port = htons(port); - addrBuffer = &addr.sin_addr; - } - else { - assert(family == AF_INET6); - ::sockaddr_in6& addr = - *reinterpret_cast<::sockaddr_in6*>(&addrStorage); - addr.sin6_family = family; - addr.sin6_port = htons(port); - addrBuffer = &addr.sin6_addr; - } - - const auto returnCode = inet_pton(family, ip.c_str(), addrBuffer); - if (returnCode == 0) { - std::ostringstream oss; - oss << "Invalid IP address" - ", ip=" - << ip << ", port=" << port; - throw std::invalid_argument(oss.str()); - } - return IPAddress(addrStorage, - family == AF_INET ? sizeof(::sockaddr_in) - : sizeof(::sockaddr_in6)); - } + versionFromString(const std::string& ip, int port, int family); ::sockaddr_storage _addr; ::socklen_t _addrLen; }; +struct AddrInfoDeleter : public std::function { + void operator()(::addrinfo* addrInfo) const { ::freeaddrinfo(addrInfo); } +}; + +std::unique_ptr<::addrinfo, AddrInfoDeleter> +resolveAddress(const std::string& host, + int port, + int family, + int type = SOCK_STREAM); + } // namespace net } // namespace jaegertracing diff --git a/src/jaegertracing/net/IPAddressTest.cpp b/src/jaegertracing/net/IPAddressTest.cpp index 719498f9..4ee6d656 100644 --- a/src/jaegertracing/net/IPAddressTest.cpp +++ b/src/jaegertracing/net/IPAddressTest.cpp @@ -23,7 +23,7 @@ namespace net { TEST(IPAddress, testParseFail) { - ASSERT_THROW(IPAddress::v4("256.256.256.256", 0), std::invalid_argument); + ASSERT_THROW(IPAddress::v4("", 0), std::runtime_error); } TEST(IPAddress, testAuthority) @@ -38,5 +38,12 @@ TEST(IPAddress, testIPv6) IPAddress::v6("2001:db8:ac10:fe01::", 0).authority()); } +TEST(IPAddress, testResolveAddress) +{ + ASSERT_NO_THROW(resolveAddress("localhost", 80, AF_INET, SOCK_STREAM)); + ASSERT_NO_THROW(resolveAddress("123456", 80, AF_INET, SOCK_STREAM)); + ASSERT_THROW(resolveAddress("localhost", 80, -1), std::runtime_error); +} + } // namespace net } // namespace jaegertracing diff --git a/src/jaegertracing/net/Socket.h b/src/jaegertracing/net/Socket.h index 57785225..6afc61b4 100644 --- a/src/jaegertracing/net/Socket.h +++ b/src/jaegertracing/net/Socket.h @@ -41,6 +41,7 @@ class Socket { , _family(socket._family) , _type(socket._type) { + socket._handle = -1; } Socket& operator=(Socket&& rhs) @@ -110,7 +111,8 @@ class Socket { IPAddress connect(const URI& serverURI) { - auto result = resolveAddress(serverURI, _type); + auto result = + resolveAddress(serverURI._host, serverURI._port, AF_INET, _type); for (const auto* itr = result.get(); itr; itr = itr->ai_next) { const auto returnCode = ::connect(_handle, itr->ai_addr, itr->ai_addrlen); @@ -154,7 +156,7 @@ class Socket { return clientSocket; } - void close() + void close() noexcept { if (_handle >= 0) { ::close(_handle); diff --git a/src/jaegertracing/net/URI.cpp b/src/jaegertracing/net/URI.cpp index 6901fa1f..d6a0a203 100644 --- a/src/jaegertracing/net/URI.cpp +++ b/src/jaegertracing/net/URI.cpp @@ -220,34 +220,5 @@ URI::QueryValueMap URI::parseQueryValues() const return values; } -std::unique_ptr<::addrinfo, AddrInfoDeleter> resolveAddress(const URI& uri, - int socketType) -{ - ::addrinfo hints; - std::memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = socketType; - - std::string service; - if (uri._port != 0) { - service = std::to_string(uri._port); - } - else { - service = uri._scheme; - } - - auto* servInfoPtr = static_cast<::addrinfo*>(nullptr); - const auto returnCode = - getaddrinfo(uri._host.c_str(), service.c_str(), &hints, &servInfoPtr); - std::unique_ptr<::addrinfo, AddrInfoDeleter> servInfo(servInfoPtr); - if (returnCode != 0) { - std::ostringstream oss; - oss << "Error resolving address: " << gai_strerror(returnCode); - throw std::runtime_error(oss.str()); - } - - return servInfo; -} - } // namespace net } // namespace jaegertracing diff --git a/src/jaegertracing/net/URI.h b/src/jaegertracing/net/URI.h index e25aea9c..51ba3399 100644 --- a/src/jaegertracing/net/URI.h +++ b/src/jaegertracing/net/URI.h @@ -17,8 +17,6 @@ #ifndef JAEGERTRACING_NET_URI_H #define JAEGERTRACING_NET_URI_H -#include - #include #include #include @@ -83,19 +81,6 @@ struct URI { std::string _query; }; -struct AddrInfoDeleter : public std::function { - void operator()(::addrinfo* addrInfo) const { ::freeaddrinfo(addrInfo); } -}; - -std::unique_ptr<::addrinfo, AddrInfoDeleter> resolveAddress(const URI& uri, - int socketType); - -inline std::unique_ptr<::addrinfo, AddrInfoDeleter> -resolveAddress(const std::string& uriStr, int socketType) -{ - return resolveAddress(URI::parse(uriStr), socketType); -} - } // namespace net } // namespace jaegertracing diff --git a/src/jaegertracing/net/URITest.cpp b/src/jaegertracing/net/URITest.cpp index 4bc5e5b3..c39bec45 100644 --- a/src/jaegertracing/net/URITest.cpp +++ b/src/jaegertracing/net/URITest.cpp @@ -53,6 +53,7 @@ TEST(URI, queryEscape) ASSERT_EQ("hello.world", URI::queryEscape("hello.world")); ASSERT_EQ("hello_world", URI::queryEscape("hello_world")); ASSERT_EQ("hello~world", URI::queryEscape("hello~world")); + ASSERT_EQ("hello%3Aworld", URI::queryEscape("hello:world")); } TEST(URI, queryUnescape) @@ -109,13 +110,5 @@ TEST(URI, testParseQueryValues) } } -TEST(URI, testResolveAddress) -{ - ASSERT_NO_THROW(resolveAddress("http://localhost", SOCK_STREAM)); - ASSERT_NO_THROW(resolveAddress("http://localhost:80", SOCK_STREAM)); - ASSERT_NO_THROW(resolveAddress("http://123456", SOCK_STREAM)); - ASSERT_THROW(resolveAddress("http://localhost", -1), std::runtime_error); -} - } // namespace net } // namespace jaegertracing diff --git a/src/jaegertracing/net/http/Header.h b/src/jaegertracing/net/http/Header.h index 87b18c60..7787743d 100644 --- a/src/jaegertracing/net/http/Header.h +++ b/src/jaegertracing/net/http/Header.h @@ -38,6 +38,10 @@ class Header { { } + const std::string& key() const { return _key; } + + const std::string& value() const { return _value; } + private: std::string _key; std::string _value; @@ -74,7 +78,7 @@ inline std::istream& readLineCRLF(std::istream& in, std::string& line) inline void readHeaders(std::istream& in, std::vector
& headers) { - const regex_namespace::regex headerPattern("([^:]+):(.+)$"); + const regex_namespace::regex headerPattern("([^:]+): (.+)$"); std::string line; regex_namespace::smatch match; while (readLineCRLF(in, line)) { diff --git a/src/jaegertracing/net/http/Request.cpp b/src/jaegertracing/net/http/Request.cpp index 1341f0ee..19d97b17 100644 --- a/src/jaegertracing/net/http/Request.cpp +++ b/src/jaegertracing/net/http/Request.cpp @@ -39,6 +39,10 @@ Request Request::parse(std::istream& in) request._target = match[2]; request._version = match[3]; + readHeaders(in, request._headers); + request._body = std::string(std::istreambuf_iterator(in), + std::istreambuf_iterator{}); + return request; } diff --git a/src/jaegertracing/net/http/Request.h b/src/jaegertracing/net/http/Request.h index a7fea1cb..c12a891e 100644 --- a/src/jaegertracing/net/http/Request.h +++ b/src/jaegertracing/net/http/Request.h @@ -46,11 +46,14 @@ class Request { const std::vector
& headers() const { return _headers; } + const std::string& body() const { return _body; } + private: Method _method; std::string _target; std::string _version; std::vector
_headers; + std::string _body; }; } // namespace http diff --git a/src/jaegertracing/net/http/Response.cpp b/src/jaegertracing/net/http/Response.cpp index 94648729..2b48a948 100644 --- a/src/jaegertracing/net/http/Response.cpp +++ b/src/jaegertracing/net/http/Response.cpp @@ -63,9 +63,8 @@ Response get(const URI& uri) socket.connect(uri); std::ostringstream requestStream; requestStream << "GET " << uri.target() << " HTTP/1.1\r\n" - << "Host: " << uri.authority() - << "\r\n" - "User-Agent: jaegertracing/" + << "Host: " << uri.authority() << "\r\n" + "User-Agent: jaegertracing/" << kJaegerClientVersion << "\r\n\r\n"; const auto request = requestStream.str(); const auto numWritten = diff --git a/src/jaegertracing/propagation/Propagator.h b/src/jaegertracing/propagation/Propagator.h index 14f22d55..4fe8b19d 100644 --- a/src/jaegertracing/propagation/Propagator.h +++ b/src/jaegertracing/propagation/Propagator.h @@ -64,8 +64,8 @@ class Propagator : public Extractor, public Injector { SpanContext ctx; StrMap baggage; std::string debugID; - const auto result = reader.ForeachKey( - [this, &reader, &ctx, &debugID, &baggage]( + const auto result = + reader.ForeachKey([this, &reader, &ctx, &debugID, &baggage]( const std::string& rawKey, const std::string& value) { const auto key = normalizeKey(rawKey); if (key == _headerKeys.traceContextHeaderName()) { diff --git a/src/jaegertracing/reporters/RemoteReporter.cpp b/src/jaegertracing/reporters/RemoteReporter.cpp index 5de93feb..5b115070 100644 --- a/src/jaegertracing/reporters/RemoteReporter.cpp +++ b/src/jaegertracing/reporters/RemoteReporter.cpp @@ -79,11 +79,9 @@ void RemoteReporter::sweepQueue() while (true) { try { std::unique_lock lock(_mutex); - _cv.wait_until(lock, - _lastFlush + _bufferFlushInterval, - [this]() { - return !_running || !_queue.empty(); - }); + _cv.wait_until(lock, _lastFlush + _bufferFlushInterval, [this]() { + return !_running || !_queue.empty(); + }); if (!_running && _queue.empty()) { return; @@ -101,8 +99,8 @@ void RemoteReporter::sweepQueue() } catch (...) { auto logger = logging::consoleLogger(); assert(logger); - utils::ErrorUtil::logError( - *logger, "Failed in Reporter::sweepQueue"); + utils::ErrorUtil::logError(*logger, + "Failed in Reporter::sweepQueue"); } } } diff --git a/src/jaegertracing/samplers/RemoteSamplingJSON.cpp b/src/jaegertracing/samplers/RemoteSamplingJSON.cpp new file mode 100644 index 00000000..e1fc45f7 --- /dev/null +++ b/src/jaegertracing/samplers/RemoteSamplingJSON.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jaegertracing/samplers/RemoteSamplingJSON.h" diff --git a/src/jaegertracing/samplers/RemoteSamplingJSON.h b/src/jaegertracing/samplers/RemoteSamplingJSON.h new file mode 100644 index 00000000..a6ed836e --- /dev/null +++ b/src/jaegertracing/samplers/RemoteSamplingJSON.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JAEGERTRACING_SAMPLERS_REMOTESAMPLINGJSON_H +#define JAEGERTRACING_SAMPLERS_REMOTESAMPLINGJSON_H + +#include +#include + +#include + +#include "jaegertracing/thrift-gen/sampling_types.h" + +namespace jaegertracing { +namespace sampling_manager { +namespace thrift { + +#define JSON_FROM_FIELD(var, field) \ + { \ + json[#field] = var.field; \ + } + +#define FIELD_FROM_JSON(var, field) \ + { \ + var.__set_##field(json.at(#field)); \ + } + +inline void to_json( + nlohmann::json& json, const SamplingStrategyType::type& type) +{ + json = _SamplingStrategyType_VALUES_TO_NAMES.at(static_cast(type)); +} + +inline void from_json( + const nlohmann::json& json, SamplingStrategyType::type& type) +{ + const auto str = json.get(); + if (str == "PROBABILISTIC") { + type = SamplingStrategyType::PROBABILISTIC; + return; + } + if (str == "RATE_LIMITING") { + type = SamplingStrategyType::RATE_LIMITING; + return; + } + std::ostringstream oss; + oss << "Invalid sampling strategy type " << str; + throw std::invalid_argument(oss.str()); +} + +inline void to_json( + nlohmann::json& json, const ProbabilisticSamplingStrategy& strategy) +{ + JSON_FROM_FIELD(strategy, samplingRate); +} + +inline void from_json( + const nlohmann::json& json, ProbabilisticSamplingStrategy& strategy) +{ + FIELD_FROM_JSON(strategy, samplingRate); +} + +inline void to_json( + nlohmann::json& json, const RateLimitingSamplingStrategy& strategy) +{ + JSON_FROM_FIELD(strategy, maxTracesPerSecond); +} + +inline void from_json( + const nlohmann::json& json, RateLimitingSamplingStrategy& strategy) +{ + FIELD_FROM_JSON(strategy, maxTracesPerSecond); +} + +inline void to_json( + nlohmann::json& json, const OperationSamplingStrategy& strategy) +{ + JSON_FROM_FIELD(strategy, operation); + JSON_FROM_FIELD(strategy, probabilisticSampling); +} + +inline void from_json( + const nlohmann::json& json, OperationSamplingStrategy& strategy) +{ + FIELD_FROM_JSON(strategy, operation); + FIELD_FROM_JSON(strategy, probabilisticSampling); +} + +inline void to_json( + nlohmann::json& json, const PerOperationSamplingStrategies& strategies) +{ + JSON_FROM_FIELD(strategies, defaultSamplingProbability); + JSON_FROM_FIELD(strategies, defaultLowerBoundTracesPerSecond); + JSON_FROM_FIELD(strategies, perOperationStrategies); + if (strategies.__isset.defaultUpperBoundTracesPerSecond) { + JSON_FROM_FIELD(strategies, defaultUpperBoundTracesPerSecond); + } +} + +inline void from_json( + const nlohmann::json& json, PerOperationSamplingStrategies& strategies) +{ + FIELD_FROM_JSON(strategies, defaultSamplingProbability); + FIELD_FROM_JSON(strategies, defaultLowerBoundTracesPerSecond); + FIELD_FROM_JSON(strategies, perOperationStrategies); + auto itr = json.find("defaultUpperBoundTracesPerSecond"); + if (itr != std::end(json)) { + strategies.__set_defaultUpperBoundTracesPerSecond(itr->get()); + } +} + +inline void to_json( + nlohmann::json& json, const SamplingStrategyResponse& response) +{ + JSON_FROM_FIELD(response, strategyType); + if (response.__isset.probabilisticSampling) { + JSON_FROM_FIELD(response, probabilisticSampling); + } + if (response.__isset.rateLimitingSampling) { + JSON_FROM_FIELD(response, rateLimitingSampling); + } + if (response.__isset.operationSampling) { + JSON_FROM_FIELD(response, operationSampling); + } +} + +inline void from_json( + const nlohmann::json& json, SamplingStrategyResponse& response) +{ + FIELD_FROM_JSON(response, strategyType); + auto itr = json.find("probabilisticSampling"); + if (itr != std::end(json)) { + response.__set_probabilisticSampling( + itr->get()); + } + itr = json.find("rateLimitingSampling"); + if (itr != std::end(json)) { + response.__set_rateLimitingSampling( + itr->get()); + } + itr = json.find("operationSampling"); + if (itr != std::end(json)) { + response.__set_operationSampling( + itr->get()); + } +} + +#undef FIELD_FROM_JSON +#undef JSON_FROM_FIELD + +} // namespace thrift +} // namespace sampling_manager +} // namespace jaegertracing + +#endif // JAEGERTRACING_SAMPLERS_REMOTESAMPLINGJSON_H diff --git a/src/jaegertracing/samplers/RemotelyControlledSampler.cpp b/src/jaegertracing/samplers/RemotelyControlledSampler.cpp index 7463018e..4fff47ef 100644 --- a/src/jaegertracing/samplers/RemotelyControlledSampler.cpp +++ b/src/jaegertracing/samplers/RemotelyControlledSampler.cpp @@ -19,8 +19,6 @@ #include #include -#include - #include "jaegertracing/metrics/Counter.h" #include "jaegertracing/metrics/Gauge.h" #include "jaegertracing/net/IPAddress.h" @@ -28,6 +26,8 @@ #include "jaegertracing/net/URI.h" #include "jaegertracing/net/http/Response.h" #include "jaegertracing/samplers/AdaptiveSampler.h" +#include "jaegertracing/samplers/RemoteSamplingJSON.h" +#include "jaegertracing/utils/ErrorUtil.h" namespace jaegertracing { namespace samplers { @@ -42,14 +42,21 @@ class HTTPSamplingManager : public sampling_manager::thrift::SamplingManagerIf { : _serverURI(net::URI::parse(serverURL)) , _logger(logger) { - net::Socket socket; - socket.open(AF_INET, SOCK_STREAM); - _serverAddr = socket.connect(serverURL); + try { + net::Socket socket; + socket.open(AF_INET, SOCK_STREAM); + _serverAddr = socket.connect(serverURL); + } catch (...) { + utils::ErrorUtil::logError(_logger, "cannot connect to socket"); + } } void getSamplingStrategy(SamplingStrategyResponse& result, const std::string& serviceName) override { + if (_serverAddr == net::IPAddress()) { + return; + } auto uri = _serverURI; uri._query = "service=" + net::URI::queryEscape(serviceName); const auto responseHTTP = net::http::get(uri); @@ -63,16 +70,7 @@ class HTTPSamplingManager : public sampling_manager::thrift::SamplingManagerIf { return; } - boost::shared_ptr transport( - new apache::thrift::transport::TMemoryBuffer()); - apache::thrift::protocol::TJSONProtocol protocol(transport); - std::vector buffer; - buffer.reserve(responseHTTP.body().size()); - buffer.insert(std::end(buffer), - std::begin(responseHTTP.body()), - std::end(responseHTTP.body())); - transport->write(&buffer[0], buffer.size()); - result.read(&protocol); + result = nlohmann::json::parse(responseHTTP.body()); } private: diff --git a/src/jaegertracing/testutils/MockAgent.cpp b/src/jaegertracing/testutils/MockAgent.cpp index 089b79b4..57091c15 100644 --- a/src/jaegertracing/testutils/MockAgent.cpp +++ b/src/jaegertracing/testutils/MockAgent.cpp @@ -19,13 +19,14 @@ #include #include -#include #include #include "jaegertracing/Logging.h" +#include "jaegertracing/baggage/RemoteRestrictionJSON.h" #include "jaegertracing/net/http/Error.h" #include "jaegertracing/net/http/Request.h" #include "jaegertracing/net/http/Response.h" +#include "jaegertracing/samplers/RemoteSamplingJSON.h" #include "jaegertracing/utils/ErrorUtil.h" #include "jaegertracing/utils/Regex.h" #include "jaegertracing/utils/UDPClient.h" @@ -183,7 +184,7 @@ void MockAgent::serveHTTP(std::promise& started) case Resource::kSampler: { sampling_manager::thrift::SamplingStrategyResponse response; _samplingMgr.getSamplingStrategy(response, serviceName); - responseJSON = apache::thrift::ThriftJSONString(response); + responseJSON = nlohmann::json(response).dump(); } break; default: { assert(resource == Resource::kBaggage); @@ -203,7 +204,7 @@ void MockAgent::serveHTTP(std::promise& started) }); response.success = std::move(restrictions); response.__isset.success = true; - responseJSON = apache::thrift::ThriftJSONString(response); + responseJSON = nlohmann::json(response).dump(); } break; } std::ostringstream oss; diff --git a/src/jaegertracing/testutils/MockAgentTest.cpp b/src/jaegertracing/testutils/MockAgentTest.cpp index c6b8ce7c..a9b3d298 100644 --- a/src/jaegertracing/testutils/MockAgentTest.cpp +++ b/src/jaegertracing/testutils/MockAgentTest.cpp @@ -14,11 +14,14 @@ * limitations under the License. */ -#include +#include -#include +#include +#include +#include "jaegertracing/baggage/RemoteRestrictionJSON.h" #include "jaegertracing/net/http/Response.h" +#include "jaegertracing/samplers/RemoteSamplingJSON.h" #include "jaegertracing/testutils/MockAgent.h" namespace jaegertracing { @@ -96,16 +99,7 @@ TEST(MockAgent, testSamplingManager) const auto uri = net::URI::parse(uriStr); const auto responseHTTP = net::http::get(uri); sampling_manager::thrift::SamplingStrategyResponse response; - boost::shared_ptr transport( - new apache::thrift::transport::TMemoryBuffer()); - apache::thrift::protocol::TJSONProtocol protocol(transport); - std::vector buffer; - buffer.reserve(responseHTTP.body().size()); - buffer.insert(std::end(buffer), - std::begin(responseHTTP.body()), - std::end(responseHTTP.body())); - transport->write(&buffer[0], buffer.size()); - response.read(&protocol); + response = nlohmann::json::parse(responseHTTP.body()); ASSERT_EQ(sampling_manager::thrift::SamplingStrategyType::PROBABILISTIC, response.strategyType); } @@ -125,16 +119,7 @@ TEST(MockAgent, testSamplingManager) const auto uri = net::URI::parse(uriStr); const auto responseHTTP = net::http::get(uri); sampling_manager::thrift::SamplingStrategyResponse response; - boost::shared_ptr transport( - new apache::thrift::transport::TMemoryBuffer()); - apache::thrift::protocol::TJSONProtocol protocol(transport); - std::vector buffer; - buffer.reserve(responseHTTP.body().size()); - buffer.insert(std::end(buffer), - std::begin(responseHTTP.body()), - std::end(responseHTTP.body())); - transport->write(&buffer[0], buffer.size()); - response.read(&protocol); + response = nlohmann::json::parse(responseHTTP.body()); ASSERT_EQ(config, response); } }