diff --git a/BUILD.bazel b/BUILD.bazel
index 2ca95bd..7593cdb 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,7 +1,8 @@
cc_library(
name = "opentracing",
- srcs = glob(["src/*.cpp"]),
+ srcs = glob(["src/*.cpp"], exclude=["src/dynamic_load_unsupported.cpp"]),
hdrs = glob(["include/opentracing/*.h"]) + [
+ ":include/opentracing/config.h",
":include/opentracing/version.h",
],
strip_include_prefix = "include",
@@ -19,14 +20,19 @@ genrule(
"cmake/*",
"src/*",
]),
- outs = ["include/opentracing/version.h"],
+ outs = [
+ "include/opentracing/config.h",
+ "include/opentracing/version.h"
+ ],
cmd = """
TEMP_DIR=$$(mktemp -d)
- TARGET=$${PWD}/$@
+ CONFIG_H_OUT=$${PWD}/$(location :include/opentracing/config.h)
+ VERSION_H_OUT=$${PWD}/$(location :include/opentracing/version.h)
OPENTRACING_ROOT=$$(dirname $${PWD}/$(location :CMakeLists.txt))
cd $$TEMP_DIR
cmake -DBUILD_TESTING=OFF -DBUILD_MOCKTRACER=OFF -L $$OPENTRACING_ROOT
- mv include/opentracing/version.h $$TARGET
+ mv include/opentracing/config.h $$CONFIG_H_OUT
+ mv include/opentracing/version.h $$VERSION_H_OUT
rm -rf $$TEMP_DIR
""",
)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b223316..cb88913 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,14 +25,6 @@ SET(CPACK_PACKAGE_VERSION_MINOR ${OPENTRACING_VERSION_MINOR})
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENTRACING_VERSION_PATCH})
include(CPack)
-# ==============================================================================
-# Set up generated version.h
-
-configure_file(version.h.in include/opentracing/version.h)
-include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
-install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/opentracing
- DESTINATION include)
-
# ==============================================================================
# Configure compilers
@@ -40,6 +32,7 @@ set(CMAKE_CXX_STANDARD 11)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weverything \
-Wno-c++98-compat \
+ -Wno-c++98-compat-pedantic \
-Wno-c++98-compat-bind-to-temporary-copy \
-Wno-weak-vtables \
-Wno-exit-time-destructors \
@@ -65,23 +58,70 @@ if(ENABLE_LINTING)
endif()
# ==============================================================================
-# OpenTracing library targets
+# Check for weak symbol support
-include_directories(include)
-include_directories(SYSTEM 3rd_party/include)
+try_compile(
+ SUPPORTS_WEAK_SYMBOLS
+ "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp"
+ SOURCES ${CMAKE_SOURCE_DIR}/cmake/weak_symbol.cpp)
+
+# ==============================================================================
+# Set up options
option(BUILD_SHARED_LIBS "Build as a shared library" ON)
option(BUILD_STATIC_LIBS "Build as a static library" ON)
option(BUILD_MOCKTRACER "Build mocktracer library" ON)
+option(BUILD_DYNAMIC_LOADING "Build with dynamic loading support" ON)
+
+if (BUILD_DYNAMIC_LOADING)
+ if (NOT SUPPORTS_WEAK_SYMBOLS OR NOT UNIX)
+ message(WARNING "Building without dynamic loading support.")
+ set(BUILD_DYNAMIC_LOADING OFF)
+ endif()
+endif()
+
+set(OPENTRACING_BUILD_DYNAMIC_LOADING ${BUILD_DYNAMIC_LOADING})
if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS must be set to ON to build")
endif()
-set(SRCS src/propagation.cpp src/noop.cpp src/tracer.cpp)
+# ==============================================================================
+# Set up generated header files config.h and version.h
+
+configure_file(version.h.in include/opentracing/version.h)
+configure_file(config.h.in include/opentracing/config.h)
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
+install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/opentracing
+ DESTINATION include)
+
+# ==============================================================================
+# OpenTracing library targets
+
+include_directories(include)
+include_directories(SYSTEM 3rd_party/include)
+
+set(SRCS src/propagation.cpp
+ src/dynamic_load.cpp
+ src/noop.cpp
+ src/tracer.cpp
+ src/tracer_factory.cpp)
+
+if (BUILD_DYNAMIC_LOADING)
+ list(APPEND SRCS src/dynamic_load_unix.cpp)
+else()
+ list(APPEND SRCS src/dynamic_load_unsupported.cpp)
+endif()
+
+list(APPEND LIBRARIES "")
+if (BUILD_DYNAMIC_LOADING)
+ list(APPEND LIBRARIES ${CMAKE_DL_LIBS})
+endif()
+
if (BUILD_SHARED_LIBS)
add_library(opentracing SHARED ${SRCS})
+ target_link_libraries(opentracing ${LIBRARIES})
target_include_directories(opentracing INTERFACE "$")
set_target_properties(opentracing PROPERTIES VERSION ${OPENTRACING_VERSION_STRING}
SOVERSION ${OPENTRACING_VERSION_MAJOR})
@@ -96,6 +136,7 @@ endif()
if (BUILD_STATIC_LIBS)
add_library(opentracing-static STATIC ${SRCS})
+ target_link_libraries(opentracing-static ${LIBRARIES})
set_target_properties(opentracing-static PROPERTIES OUTPUT_NAME opentracing)
target_include_directories(opentracing-static INTERFACE "$")
install(TARGETS opentracing-static EXPORT OpenTracingTargets
@@ -147,3 +188,10 @@ include(CTest)
if(BUILD_TESTING)
add_subdirectory(test)
endif()
+
+# ==============================================================================
+# Examples
+
+if(BUILD_TESTING)
+ add_subdirectory(example)
+endif()
diff --git a/cmake/weak_symbol.cpp b/cmake/weak_symbol.cpp
new file mode 100644
index 0000000..902f683
--- /dev/null
+++ b/cmake/weak_symbol.cpp
@@ -0,0 +1,3 @@
+void __attribute((weak)) f();
+
+int main() { return 0; }
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..18d1172
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,3 @@
+#pragma once
+
+#cmakedefine OPENTRACING_BUILD_DYNAMIC_LOADING
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
new file mode 100644
index 0000000..39565cc
--- /dev/null
+++ b/example/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(tutorial)
+add_subdirectory(dynamic_load)
diff --git a/example/dynamic_load/CMakeLists.txt b/example/dynamic_load/CMakeLists.txt
new file mode 100644
index 0000000..1413a4b
--- /dev/null
+++ b/example/dynamic_load/CMakeLists.txt
@@ -0,0 +1,4 @@
+if (BUILD_DYNAMIC_LOADING AND BUILD_SHARED_LIBS)
+ add_executable(dynamic_load-example dynamic_load-example.cpp)
+ target_link_libraries(dynamic_load-example opentracing)
+endif()
diff --git a/example/dynamic_load/dynamic_load-example.cpp b/example/dynamic_load/dynamic_load-example.cpp
new file mode 100644
index 0000000..8ef1cb1
--- /dev/null
+++ b/example/dynamic_load/dynamic_load-example.cpp
@@ -0,0 +1,64 @@
+// Demonstrates how to load a tracer library in at runtime and how to use it
+// to construct spans. To run it using the mocktracer, invoke with
+//
+// TRACER_CONFIG=`mktemp`
+// echo '{ "output_file": "/dev/stdout" }' > $TRACER_CONFIG
+// dynamic_load-example /path/to/libopentracing_mocktracer.so $TRACER_CONFIG
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ std::cerr << "Usage: \n";
+ return -1;
+ }
+
+ // Load the tracer library.
+ std::string error_message;
+ auto handle_maybe =
+ opentracing::DynamicallyLoadTracingLibrary(argv[1], error_message);
+ if (!handle_maybe) {
+ std::cerr << "Failed to load tracer library " << error_message << "\n";
+ return -1;
+ }
+
+ // Read in the tracer's configuration.
+ std::ifstream istream{argv[2]};
+ if (!istream.good()) {
+ std::cerr << "Failed to open tracer config file " << argv[2] << ": "
+ << std::strerror(errno) << "\n";
+ return -1;
+ }
+ std::string tracer_config{std::istreambuf_iterator{istream},
+ std::istreambuf_iterator{}};
+
+ // Construct a tracer.
+ auto& tracer_factory = handle_maybe->tracer_factory();
+ auto tracer_maybe =
+ tracer_factory.MakeTracer(tracer_config.c_str(), error_message);
+ if (!tracer_maybe) {
+ std::cerr << "Failed to create tracer " << error_message << "\n";
+ return -1;
+ }
+ auto& tracer = *tracer_maybe;
+
+ // Use the tracer to create some spans.
+ {
+ auto span_a = tracer->StartSpan("A");
+ assert(span_a != nullptr);
+ span_a->SetTag("abc", 123);
+ auto span_b =
+ tracer->StartSpan("B", {opentracing::ChildOf(&span_a->context())});
+ assert(span_b != nullptr);
+ span_b->SetTag("xyz", 987);
+ }
+
+ tracer->Close();
+ return 0;
+}
diff --git a/example/tutorial/CMakeLists.txt b/example/tutorial/CMakeLists.txt
new file mode 100644
index 0000000..37ea6d3
--- /dev/null
+++ b/example/tutorial/CMakeLists.txt
@@ -0,0 +1,5 @@
+if (BUILD_MOCKTRACER AND BUILD_SHARED_LIBS)
+ include_directories(${CMAKE_SOURCE_DIR}/mocktracer/include)
+ add_executable(tutorial-example tutorial-example.cpp)
+ target_link_libraries(tutorial-example opentracing_mocktracer)
+endif()
diff --git a/example/tutorial/text_map_carrier.h b/example/tutorial/text_map_carrier.h
new file mode 100644
index 0000000..22829e8
--- /dev/null
+++ b/example/tutorial/text_map_carrier.h
@@ -0,0 +1,37 @@
+#ifndef LIGHTSTEP_TEXT_MAP_CARRIER
+#define LIGHTSTEP_TEXT_MAP_CARRIER
+
+#include
+#include
+#include
+
+using opentracing::TextMapReader;
+using opentracing::TextMapWriter;
+using opentracing::expected;
+using opentracing::string_view;
+
+class TextMapCarrier : public TextMapReader, public TextMapWriter {
+ public:
+ TextMapCarrier(std::unordered_map& text_map)
+ : text_map_(text_map) {}
+
+ expected Set(string_view key, string_view value) const override {
+ text_map_[key] = value;
+ return {};
+ }
+
+ expected ForeachKey(
+ std::function(string_view key, string_view value)> f)
+ const override {
+ for (const auto& key_value : text_map_) {
+ auto result = f(key_value.first, key_value.second);
+ if (!result) return result;
+ }
+ return {};
+ }
+
+ private:
+ std::unordered_map& text_map_;
+};
+
+#endif // LIGHTSTEP_TEXT_MAP_CARRIER
diff --git a/example/tutorial/tutorial-example.cpp b/example/tutorial/tutorial-example.cpp
new file mode 100644
index 0000000..e842765
--- /dev/null
+++ b/example/tutorial/tutorial-example.cpp
@@ -0,0 +1,99 @@
+// Demonstrates basic usage of the OpenTracing API. Uses OpenTracing's
+// mocktracer to capture all the recorded spans as JSON.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "text_map_carrier.h"
+using namespace opentracing;
+using namespace opentracing::mocktracer;
+
+int main() {
+ MockTracerOptions options;
+ std::unique_ptr output{new std::ostringstream{}};
+ std::ostringstream& oss = *output;
+ options.recorder = std::unique_ptr{
+ new JsonRecorder{std::move(output)}};
+
+ std::shared_ptr tracer{
+ new MockTracer{std::move(options)}};
+
+ auto parent_span = tracer->StartSpan("parent");
+ assert(parent_span);
+
+ // Create a child span.
+ {
+ auto child_span =
+ tracer->StartSpan("childA", {ChildOf(&parent_span->context())});
+ assert(child_span);
+
+ // Set a simple tag.
+ child_span->SetTag("simple tag", 123);
+
+ // Set a complex tag.
+ child_span->SetTag("complex tag",
+ Values{123, Dictionary{{"abc", 123}, {"xyz", 4.0}}});
+
+ // Log simple values.
+ child_span->Log({{"event", "simple log"}, {"abc", 123}});
+
+ // Log complex values.
+ child_span->Log({{"event", "complex log"},
+ {"data", Dictionary{{"a", 1}, {"b", Values{1, 2}}}}});
+
+ child_span->Finish();
+ }
+
+ // Create a follows from span.
+ {
+ auto child_span =
+ tracer->StartSpan("childB", {FollowsFrom(&parent_span->context())});
+
+ // child_span's destructor will finish the span if not done so explicitly.
+ }
+
+ // Use custom timestamps.
+ {
+ auto t1 = SystemClock::now();
+ auto t2 = SteadyClock::now();
+ auto span = tracer->StartSpan(
+ "useCustomTimestamps",
+ {ChildOf(&parent_span->context()), StartTimestamp(t1)});
+ assert(span);
+ span->Finish({FinishTimestamp(t2)});
+ }
+
+ // Extract and Inject a span context.
+ {
+ std::unordered_map text_map;
+ TextMapCarrier carrier(text_map);
+ auto err = tracer->Inject(parent_span->context(), carrier);
+ assert(err);
+ auto span_context_maybe = tracer->Extract(carrier);
+ assert(span_context_maybe);
+ auto span = tracer->StartSpan("propagationSpan",
+ {ChildOf(span_context_maybe->get())});
+ }
+
+ // You get an error when trying to extract a corrupt span.
+ {
+ std::unordered_map text_map = {
+ {"x-ot-span-context", "123"}};
+ TextMapCarrier carrier(text_map);
+ auto err = tracer->Extract(carrier);
+ assert(!err);
+ assert(err.error() == span_context_corrupted_error);
+ // How to get a readable message from the error.
+ std::cout << "Example error message: \"" << err.error().message() << "\"\n";
+ }
+
+ parent_span->Finish();
+ tracer->Close();
+
+ std::cout << "\nRecorded spans as JSON:\n\n";
+ std::cout << oss.str() << "\n";
+ return 0;
+}
diff --git a/include/opentracing/dynamic_load.h b/include/opentracing/dynamic_load.h
new file mode 100644
index 0000000..1d265ef
--- /dev/null
+++ b/include/opentracing/dynamic_load.h
@@ -0,0 +1,125 @@
+#ifndef OPENTRACING_DYNAMIC_LOAD_H
+#define OPENTRACING_DYNAMIC_LOAD_H
+
+#include
+#include
+#include
+#include
+#include
+
+// OpenTracingMakeTracerFactory provides a common hook that can be used to
+// create an TracerFactory from a dynamically loaded library. Users should
+// prefer to use the function DynamicallyLoadTracingLibrary over calling it
+// directly.
+//
+// It takes the parameter `opentracing_version` representing the version of
+// opentracing used by the caller. Upon success it returns the code `0` and
+// sets `tracer_factory` to point to an instance of TracerFactory.
+//
+// On failure, it returns a non-zero error code and sets `error_category` to
+// point to an std::error_category for the returned error code.
+//
+// Example usage,
+//
+// const std::error_category* error_category = nullptr;
+// opentracing::TracerFactory* tracer_factory = nullptr;
+// int rcode = opentracing_make_factory(
+// OPENTRACING_VERSION,
+// &static_cast(error_category),
+// &static_cast(tracer_factory));
+// if (rcode == 0) {
+// // success
+// assert(tracer_factory != nullptr);
+// } else {
+// // failure
+// assert(error_category != nullptr);
+// std::error_code error{rcode, *error_category};
+// }
+extern "C" {
+#ifdef OPENTRACING_BUILD_DYNAMIC_LOADING
+int __attribute((weak))
+OpenTracingMakeTracerFactory(const char* opentracing_version,
+ const void** error_category,
+ void** tracer_factory);
+#endif
+} // extern "C"
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+// Returns the std::error_category class used for opentracing dynamic loading
+// errors.
+//
+// See
+// http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html
+// https://ned14.github.io/boost.outcome/md_doc_md_03-tutorial_b.html
+const std::error_category& dynamic_load_error_category();
+
+// `dynamic_load_failure_error` occurs when dynamically loading a tracer library
+// fails. Possible reasons could be the library doesn't exist or it is missing
+// the required symbols.
+const std::error_code dynamic_load_failure_error(1,
+ dynamic_load_error_category());
+
+// `dynamic_load_not_supported_error` means dynamic loading of tracing libraries
+// is not supported for the platform used.
+const std::error_code dynamic_load_not_supported_error(
+ 2, dynamic_load_error_category());
+
+// `incompatible_library_versions_error` occurs if the tracing library
+// dynamically loaded uses an incompatible version of opentracing.
+const std::error_code incompatible_library_versions_error(
+ 3, dynamic_load_error_category());
+
+class DynamicLibraryHandle {
+ public:
+ virtual ~DynamicLibraryHandle() = default;
+};
+
+// Provides a handle to a dynamically loaded tracing library that can be used
+// to create tracers.
+//
+// Note: The handle must not be destructed while any associated tracers are
+// still in use.
+//
+// See TracerFactory
+class DynamicTracingLibraryHandle {
+ public:
+ DynamicTracingLibraryHandle() = default;
+
+ DynamicTracingLibraryHandle(
+ std::unique_ptr&& tracer_factory,
+ std::unique_ptr&& dynamic_library_handle) noexcept;
+
+ const TracerFactory& tracer_factory() const noexcept {
+ return *tracer_factory_;
+ }
+
+ private:
+ std::unique_ptr dynamic_library_handle_;
+ std::unique_ptr tracer_factory_;
+};
+
+// Dynamically loads a tracing library and returns a handle that can be used
+// to create tracers.
+//
+// Example:
+// std::string error_message;
+// auto handle_maybe = DynamicallyLoadTracingLibrary(
+// "libtracing_vendor.so",
+// error_message);
+// if (handle_maybe) {
+// // success
+// auto& tracer_factory = handle_maybe->tracer_factory();
+// } else {
+// // failure
+// std::error_code error = handle_maybe.error();
+// // `error_message` may also contain a more descriptive message
+// }
+//
+// See DynamicTracingLibraryHandle, TracerFactory
+expected DynamicallyLoadTracingLibrary(
+ const char* shared_library, std::string& error_message) noexcept;
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
+
+#endif // OPENTRACING_DYNAMIC_LOAD_H
diff --git a/include/opentracing/propagation.h b/include/opentracing/propagation.h
index d7392d3..62c9ce3 100644
--- a/include/opentracing/propagation.h
+++ b/include/opentracing/propagation.h
@@ -64,15 +64,14 @@ enum class SpanReferenceType {
// https://ned14.github.io/boost.outcome/md_doc_md_03-tutorial_b.html
const std::error_category& propagation_error_category();
-// `invalid_span_context_error` errors occur when Tracer::Inject() is asked to
-// operate on a SpanContext which it is not prepared to handle (for
-// example, since it was created by a different tracer implementation).
+// `invalid_span_context_error` occurs when Tracer::Inject() is asked to operate
+// on a SpanContext which it is not prepared to handle (for example, since it
+// was created by a different tracer implementation).
const std::error_code invalid_span_context_error(1,
propagation_error_category());
-// `invalid_carrier_error` errors occur when Tracer::Inject() or
-// Tracer::Extract() implementations expect a different type of `carrier` than
-// they are given.
+// `invalid_carrier_error` occurs when Tracer::Inject() or Tracer::Extract()
+// implementations expect a different type of `carrier` than they are given.
const std::error_code invalid_carrier_error(2, propagation_error_category());
// `span_context_corrupted_error` occurs when the `carrier` passed to
diff --git a/include/opentracing/string_view.h b/include/opentracing/string_view.h
index 83676dd..a30eb7f 100644
--- a/include/opentracing/string_view.h
+++ b/include/opentracing/string_view.h
@@ -85,6 +85,9 @@ class string_view {
// Returns a RandomAccessIterator for the last element.
const char* end() const noexcept { return data() + length(); }
+ // Returns the character in the i-th position.
+ const char& operator[](std::size_t i) { return *(data() + i); }
+
private:
const char* data_; // Pointer to external storage
size_t length_; // Length of data pointed to by 'data_'
diff --git a/include/opentracing/tracer_factory.h b/include/opentracing/tracer_factory.h
new file mode 100644
index 0000000..c2d0354
--- /dev/null
+++ b/include/opentracing/tracer_factory.h
@@ -0,0 +1,55 @@
+#ifndef OPENTRACING_TRACER_FACTORY_H
+#define OPENTRACING_TRACER_FACTORY_H
+
+#include
+#include
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+// Returns the std::error_category class used for tracer factory errors.
+//
+// See
+// http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-1.html
+// https://ned14.github.io/boost.outcome/md_doc_md_03-tutorial_b.html
+const std::error_category& tracer_factory_error_category();
+
+// `configuration_parse_error` occurs when the configuration string used to
+// construct a tracer does not adhere to the expected format.
+const std::error_code configuration_parse_error(
+ 1, tracer_factory_error_category());
+
+// `invalid_configuration_error` occurs if the requested configuration for a
+// tracer has invalid values.
+const std::error_code invalid_configuration_error(
+ 2, tracer_factory_error_category());
+
+// TracerFactory constructs tracers from configuration strings.
+class TracerFactory {
+ public:
+ virtual ~TracerFactory() = default;
+
+ // Creates a tracer with the requested `configuration`.
+ //
+ // Example,
+ // const char* configuration = R"(
+ // "collector": "localhost:123",
+ // "max_buffered_spans": 500
+ // )";
+ // std:string error_message;
+ // auto tracer_maybe = tracer_factory->MakeTracer(configuration,
+ // error_message);
+ // if (tracer_mabye) {
+ // // success
+ // std::shared_ptr tracer = *tracer_maybe;
+ // } else {
+ // // failure
+ // std::error_code error = tracer_maybe.error();
+ // // `error_message` may also contain a more descriptive message
+ // }
+ virtual expected> MakeTracer(
+ const char* configuration, std::string& error_message) const noexcept = 0;
+};
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
+
+#endif // OPENTRACING_TRACER_FACTORY_H
diff --git a/mocktracer/3rd_party/base64/BUILD b/mocktracer/3rd_party/base64/BUILD
deleted file mode 100644
index 5e4c781..0000000
--- a/mocktracer/3rd_party/base64/BUILD
+++ /dev/null
@@ -1,10 +0,0 @@
-cc_library(
- name = "base64",
- srcs = glob(["src/*.cpp"]),
- hdrs = glob(["include/opentracing/**/*.h"]),
- strip_include_prefix = "include",
- visibility = ["//visibility:public"],
- deps = [
- "//:opentracing",
- ]
-)
diff --git a/mocktracer/3rd_party/base64/README.txt b/mocktracer/3rd_party/base64/README.txt
deleted file mode 100644
index 9f893d6..0000000
--- a/mocktracer/3rd_party/base64/README.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Modiefied from Envoy (https://github.com/envoyproxy/envoy).
-
-commit d70404f7e5c89ab2c5fab72b516e2bf601969557
diff --git a/mocktracer/BUILD b/mocktracer/BUILD
index a639013..3b22bab 100644
--- a/mocktracer/BUILD
+++ b/mocktracer/BUILD
@@ -6,6 +6,14 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
"//:opentracing",
- "//mocktracer/3rd_party/base64:base64",
+ ],
+)
+
+cc_binary(
+ name = "libmocktracer_plugin.so",
+ linkshared = 1,
+ visibility = ["//visibility:public"],
+ deps = [
+ "//mocktracer:mocktracer"
],
)
diff --git a/mocktracer/CMakeLists.txt b/mocktracer/CMakeLists.txt
index 58a5c94..2f3d0a6 100644
--- a/mocktracer/CMakeLists.txt
+++ b/mocktracer/CMakeLists.txt
@@ -1,17 +1,17 @@
include_directories(include)
-include_directories(3rd_party/base64/include)
-set(SRCS 3rd_party/base64/src/base64.cpp
- src/mock_span_context.cpp
+set(SRCS src/mock_span_context.cpp
src/mock_span.cpp
src/in_memory_recorder.cpp
src/json_recorder.cpp
+ src/base64.cpp
src/propagation.cpp
src/json.cpp
- src/tracer.cpp)
+ src/tracer.cpp
+ src/tracer_factory.cpp)
if (BUILD_SHARED_LIBS)
- add_library(opentracing_mocktracer SHARED ${SRCS})
+ add_library(opentracing_mocktracer SHARED ${SRCS} src/dynamic_load.cpp)
target_include_directories(opentracing_mocktracer INTERFACE "$")
set_target_properties(opentracing_mocktracer PROPERTIES VERSION ${OPENTRACING_VERSION_STRING}
SOVERSION ${OPENTRACING_VERSION_MAJOR})
diff --git a/mocktracer/3rd_party/base64/LICENSE b/mocktracer/LICENSE.apache
similarity index 100%
rename from mocktracer/3rd_party/base64/LICENSE
rename to mocktracer/LICENSE.apache
diff --git a/mocktracer/include/opentracing/mocktracer/tracer_factory.h b/mocktracer/include/opentracing/mocktracer/tracer_factory.h
new file mode 100644
index 0000000..49227e0
--- /dev/null
+++ b/mocktracer/include/opentracing/mocktracer/tracer_factory.h
@@ -0,0 +1,21 @@
+#ifndef OPENTRACING_MOCKTRACER_TRACER_FACTORY_H
+#define OPENTRACING_MOCKTRACER_TRACER_FACTORY_H
+
+#include
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+namespace mocktracer {
+
+class MockTracerFactory : public TracerFactory {
+ public:
+ expected> MakeTracer(const char* configuration,
+ std::string& error_message) const
+ noexcept override;
+};
+
+} // namespace mocktracer
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
+
+#endif // OPENTRACING_MOCKTRACER_TRACER_FACTORY_H
diff --git a/mocktracer/3rd_party/base64/src/base64.cpp b/mocktracer/src/base64.cpp
similarity index 97%
rename from mocktracer/3rd_party/base64/src/base64.cpp
rename to mocktracer/src/base64.cpp
index 10fc4d4..d039dcf 100644
--- a/mocktracer/3rd_party/base64/src/base64.cpp
+++ b/mocktracer/src/base64.cpp
@@ -1,4 +1,11 @@
-#include
+/*
+ * Envoy
+ * Copyright 2016-2017 Lyft Inc.
+ *
+ * Licensed under Apache License 2.0. See LICENSE.apache for terms.
+ */
+
+#include "base64.h"
#include
#include
diff --git a/mocktracer/3rd_party/base64/include/opentracing/mocktracer/base64.h b/mocktracer/src/base64.h
similarity index 90%
rename from mocktracer/3rd_party/base64/include/opentracing/mocktracer/base64.h
rename to mocktracer/src/base64.h
index 67845c6..438bbda 100644
--- a/mocktracer/3rd_party/base64/include/opentracing/mocktracer/base64.h
+++ b/mocktracer/src/base64.h
@@ -1,5 +1,5 @@
-#ifndef OPENTRACING_MOCKTRACER_BASE64_h
-#define OPENTRACING_MOCKTRACER_BASE64_h
+#ifndef OPENTRACING_MOCKTRACER_BASE64_H
+#define OPENTRACING_MOCKTRACER_BASE64_H
#include
#include
@@ -45,4 +45,4 @@ class Base64 {
END_OPENTRACING_ABI_NAMESPACE
} // namespace opentracing
-#endif // OPENTRACING_MOCKTRACER_BASE64_h
+#endif // OPENTRACING_MOCKTRACER_BASE64_H
diff --git a/mocktracer/src/dynamic_load.cpp b/mocktracer/src/dynamic_load.cpp
new file mode 100644
index 0000000..3aad32a
--- /dev/null
+++ b/mocktracer/src/dynamic_load.cpp
@@ -0,0 +1,30 @@
+#include
+#include
+#include
+#include
+#include
+
+int OpenTracingMakeTracerFactory(const char* opentracing_version,
+ const void** error_category,
+ void** tracer_factory) {
+ if (error_category == nullptr || tracer_factory == nullptr) {
+ fprintf(stderr,
+ "`error_category` and `tracer_factory` must be non-null.\n");
+ std::terminate();
+ }
+
+ if (std::strcmp(opentracing_version, OPENTRACING_VERSION) != 0) {
+ *error_category =
+ static_cast(&opentracing::dynamic_load_error_category());
+ return opentracing::incompatible_library_versions_error.value();
+ }
+
+ *tracer_factory =
+ new (std::nothrow) opentracing::mocktracer::MockTracerFactory{};
+ if (*tracer_factory == nullptr) {
+ *error_category = static_cast(&std::generic_category());
+ return static_cast(std::errc::not_enough_memory);
+ }
+
+ return 0;
+}
diff --git a/mocktracer/src/propagation.cpp b/mocktracer/src/propagation.cpp
index 598a5fe..a037f90 100644
--- a/mocktracer/src/propagation.cpp
+++ b/mocktracer/src/propagation.cpp
@@ -1,10 +1,10 @@
#include "propagation.h"
-#include
#include
#include
#include
#include
#include
+#include "base64.h"
namespace opentracing {
BEGIN_OPENTRACING_ABI_NAMESPACE
diff --git a/mocktracer/src/tracer_factory.cpp b/mocktracer/src/tracer_factory.cpp
new file mode 100644
index 0000000..1c672ce
--- /dev/null
+++ b/mocktracer/src/tracer_factory.cpp
@@ -0,0 +1,130 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+namespace mocktracer {
+
+namespace {
+struct InvalidConfigurationError : public std::exception {
+ public:
+ InvalidConfigurationError(const char* position, std::string&& message)
+ : position_{position}, message_{std::move(message)} {}
+
+ const char* what() const noexcept override { return message_.c_str(); }
+
+ const char* position() const { return position_; }
+
+ private:
+ const char* position_;
+ std::string message_;
+};
+} // namespace
+
+static void Consume(const char*& i, const char* last, string_view s) {
+ if (static_cast(std::distance(i, last)) < s.size()) {
+ throw InvalidConfigurationError{i,
+ std::string{"expected "} + std::string{s}};
+ }
+
+ for (size_t index = 0; index < s.size(); ++index) {
+ if (*i++ != s[index]) {
+ throw InvalidConfigurationError{
+ i, std::string{"expected "} +
+ std::string{s.data() + index, s.data() + s.size()}};
+ }
+ }
+}
+
+static void ConsumeWhitespace(const char*& i, const char* last) {
+ for (; i != last; ++i) {
+ if (!std::isspace(*i)) {
+ return;
+ }
+ }
+}
+
+static void ConsumeToken(const char*& i, const char* last, string_view token) {
+ ConsumeWhitespace(i, last);
+ Consume(i, last, token);
+}
+
+static std::string ParseFilename(const char*& i, const char* last) {
+ ConsumeToken(i, last, "\"");
+ std::string result;
+ while (i != last) {
+ if (*i == '\"') {
+ ++i;
+ return result;
+ }
+ if (*i == '\\') {
+ throw InvalidConfigurationError{
+ i, "escaped characters are not supported in filename"};
+ }
+ if (std::isprint(*i)) {
+ result.push_back(*i);
+ } else {
+ throw InvalidConfigurationError{i, "invalid character"};
+ }
+ ++i;
+ }
+
+ throw InvalidConfigurationError{i, R"(no matching ")"};
+}
+
+static std::string ParseConfiguration(const char* i, const char* last) {
+ ConsumeToken(i, last, "{");
+ ConsumeToken(i, last, R"("output_file")");
+ ConsumeToken(i, last, ":");
+ auto filename = ParseFilename(i, last);
+ ConsumeToken(i, last, "}");
+ ConsumeWhitespace(i, last);
+ if (i != last) {
+ throw InvalidConfigurationError{i, "expected EOF"};
+ }
+
+ return filename;
+}
+
+struct MockTracerConfiguration {
+ std::string output_file;
+};
+
+expected> MockTracerFactory::MakeTracer(
+ const char* configuration, std::string& error_message) const noexcept try {
+ MockTracerConfiguration tracer_configuration;
+ try {
+ tracer_configuration.output_file = ParseConfiguration(
+ configuration, configuration + std::strlen(configuration));
+ } catch (const InvalidConfigurationError& e) {
+ error_message = std::string{"Error parsing configuration at position "} +
+ std::to_string(std::distance(configuration, e.position())) +
+ ": " + e.what();
+ return make_unexpected(invalid_configuration_error);
+ }
+
+ std::unique_ptr ostream{
+ new std::ofstream{tracer_configuration.output_file}};
+ if (!ostream->good()) {
+ error_message = "failed to open file `";
+ error_message += tracer_configuration.output_file + "`";
+ return make_unexpected(invalid_configuration_error);
+ }
+
+ MockTracerOptions tracer_options;
+ tracer_options.recorder =
+ std::unique_ptr{new JsonRecorder{std::move(ostream)}};
+
+ return std::shared_ptr{new MockTracer{std::move(tracer_options)}};
+} catch (const std::bad_alloc&) {
+ return make_unexpected(std::make_error_code(std::errc::not_enough_memory));
+}
+
+} // namespace mocktracer
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
diff --git a/src/dynamic_load.cpp b/src/dynamic_load.cpp
new file mode 100644
index 0000000..cee0dc3
--- /dev/null
+++ b/src/dynamic_load.cpp
@@ -0,0 +1,57 @@
+#include
+#include
+#include
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+namespace {
+class DynamicLoadErrorCategory : public std::error_category {
+ public:
+ DynamicLoadErrorCategory() {}
+
+ const char* name() const noexcept override {
+ return "OpenTracingDynamicLoadError";
+ }
+
+ std::error_condition default_error_condition(int code) const
+ noexcept override {
+ if (code == dynamic_load_failure_error.value()) {
+ return std::make_error_condition(std::errc::no_such_file_or_directory);
+ }
+ if (code == dynamic_load_not_supported_error.value()) {
+ return std::make_error_condition(std::errc::not_supported);
+ }
+ if (code == incompatible_library_versions_error.value()) {
+ return std::make_error_condition(std::errc::invalid_argument);
+ }
+ return std::error_condition(code, *this);
+ }
+
+ std::string message(int code) const override {
+ if (code == dynamic_load_failure_error.value()) {
+ return "opentracing: failed to load dynamic library";
+ }
+ if (code == dynamic_load_not_supported_error.value()) {
+ return "opentracing: dynamic library loading is not supported";
+ }
+ if (code == incompatible_library_versions_error.value()) {
+ return "opentracing: versions of opentracing libraries are incompatible";
+ }
+ return "opentracing: unknown dynamic load error";
+ }
+};
+} // anonymous namespace
+
+const std::error_category& dynamic_load_error_category() {
+ static const DynamicLoadErrorCategory error_category;
+ return error_category;
+}
+
+DynamicTracingLibraryHandle::DynamicTracingLibraryHandle(
+ std::unique_ptr&& tracer_factory,
+ std::unique_ptr&& dynamic_library_handle) noexcept
+ : dynamic_library_handle_{std::move(dynamic_library_handle)},
+ tracer_factory_{std::move(tracer_factory)} {}
+
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
diff --git a/src/dynamic_load_unix.cpp b/src/dynamic_load_unix.cpp
new file mode 100644
index 0000000..2bcfa07
--- /dev/null
+++ b/src/dynamic_load_unix.cpp
@@ -0,0 +1,85 @@
+#include
+#include
+#include
+
+// Copied from https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+#define GCC_VERSION \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+namespace {
+class DynamicLibraryHandleUnix : public DynamicLibraryHandle {
+ public:
+ explicit DynamicLibraryHandleUnix(void* handle) : handle_{handle} {}
+
+ ~DynamicLibraryHandleUnix() override { dlclose(handle_); }
+
+ private:
+ void* handle_;
+};
+} // namespace
+
+// Undefined behavior sanitizer has a bug where it will produce a false positive
+// when casting the result of dlsym to a function pointer.
+//
+// See https://github.com/envoyproxy/envoy/pull/2252#issuecomment-362668221
+// https://github.com/google/sanitizers/issues/911
+//
+// Note: undefined behavior sanitizer is supported in clang and gcc > 4.9
+#if defined(__clang__)
+__attribute__((no_sanitize("function")))
+#elif (GCC_VERSION >= 40900)
+__attribute__((no_sanitize_undefined))
+#endif
+expected
+DynamicallyLoadTracingLibrary(const char* shared_library,
+ std::string& error_message) noexcept try {
+ dlerror(); // Clear any existing error.
+
+ const auto handle = dlopen(shared_library, RTLD_NOW | RTLD_LOCAL);
+ if (handle == nullptr) {
+ error_message = dlerror();
+ return make_unexpected(dynamic_load_failure_error);
+ }
+
+ std::unique_ptr dynamic_library_handle{
+ new DynamicLibraryHandleUnix{handle}};
+
+ const auto make_tracer_factory =
+ reinterpret_cast(
+ dlsym(handle, "OpenTracingMakeTracerFactory"));
+ if (make_tracer_factory == nullptr) {
+ error_message = dlerror();
+ return make_unexpected(dynamic_load_failure_error);
+ }
+
+ const void* error_category = nullptr;
+ void* tracer_factory = nullptr;
+ const auto rcode = make_tracer_factory(OPENTRACING_VERSION, &error_category,
+ &tracer_factory);
+ if (rcode != 0) {
+ if (error_category != nullptr) {
+ return make_unexpected(std::error_code{
+ rcode, *static_cast(error_category)});
+ } else {
+ error_message = "failed to construct a TracerFactory: unknown error code";
+ return make_unexpected(dynamic_load_failure_error);
+ }
+ }
+
+ if (tracer_factory == nullptr) {
+ error_message =
+ "failed to construct a TracerFactory: `tracer_factory` is null";
+ return make_unexpected(dynamic_load_failure_error);
+ }
+
+ return DynamicTracingLibraryHandle{
+ std::unique_ptr{
+ static_cast(tracer_factory)},
+ std::move(dynamic_library_handle)};
+} catch (const std::bad_alloc&) {
+ return make_unexpected(std::make_error_code(std::errc::not_enough_memory));
+}
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
diff --git a/src/dynamic_load_unsupported.cpp b/src/dynamic_load_unsupported.cpp
new file mode 100644
index 0000000..fd0d767
--- /dev/null
+++ b/src/dynamic_load_unsupported.cpp
@@ -0,0 +1,10 @@
+#include
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+expected DynamicallyLoadTracingLibrary(
+ const char* /*shared_library*/, std::string& /*error_message*/) noexcept {
+ return make_unexpected(dynamic_load_not_supported_error);
+}
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
diff --git a/src/tracer_factory.cpp b/src/tracer_factory.cpp
new file mode 100644
index 0000000..8958310
--- /dev/null
+++ b/src/tracer_factory.cpp
@@ -0,0 +1,44 @@
+#include
+#include
+
+namespace opentracing {
+BEGIN_OPENTRACING_ABI_NAMESPACE
+namespace {
+class TracerFactoryErrorCategory : public std::error_category {
+ public:
+ TracerFactoryErrorCategory() {}
+
+ const char* name() const noexcept override {
+ return "OpenTracingTracerFactoryError";
+ }
+
+ std::error_condition default_error_condition(int code) const
+ noexcept override {
+ if (code == configuration_parse_error.value()) {
+ return std::make_error_condition(std::errc::invalid_argument);
+ }
+ if (code == invalid_configuration_error.value()) {
+ return std::make_error_condition(std::errc::invalid_argument);
+ }
+ return std::error_condition(code, *this);
+ }
+
+ std::string message(int code) const override {
+ if (code == configuration_parse_error.value()) {
+ return "opentracing: failed to parse configuration";
+ }
+ if (code == invalid_configuration_error.value()) {
+ return "opentracing: invalid configuration";
+ }
+ return "opentracing: unknown tracer factory error";
+ }
+};
+} // anonymous namespace
+
+const std::error_category& tracer_factory_error_category() {
+ static const TracerFactoryErrorCategory error_category;
+ return error_category;
+}
+
+END_OPENTRACING_ABI_NAMESPACE
+} // namespace opentracing
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2afbff6..ac39669 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -16,3 +16,12 @@ add_test(value_test value_test)
add_executable(util_test util_test.cpp)
add_test(util_test util_test)
+
+if (BUILD_SHARED_LIBS AND BUILD_MOCKTRACER AND BUILD_DYNAMIC_LOADING)
+ add_executable(dynamic_load_test dynamic_load_test.cpp)
+ target_link_libraries(dynamic_load_test ${OPENTRACING_LIBRARY})
+ add_dependencies(dynamic_load_test opentracing_mocktracer)
+ add_test(dynamic_load_test dynamic_load_test
+ --mocktracer_library
+ ${CMAKE_BINARY_DIR}/mocktracer/${CMAKE_SHARED_LIBRARY_PREFIX}opentracing_mocktracer${CMAKE_SHARED_LIBRARY_SUFFIX})
+endif()
diff --git a/test/dynamic_load_test.cpp b/test/dynamic_load_test.cpp
new file mode 100644
index 0000000..813f1e4
--- /dev/null
+++ b/test/dynamic_load_test.cpp
@@ -0,0 +1,97 @@
+#include
+#include
+#include
+#include
+#include
+using namespace opentracing;
+
+#define CATCH_CONFIG_RUNNER
+#include
+
+static std::string mocktracer_library;
+
+TEST_CASE("dynamic_load") {
+ std::string error_message;
+
+ SECTION(
+ "Dynamically loading a library that doesn't exists gives a proper error "
+ "code.") {
+ auto handle_maybe = DynamicallyLoadTracingLibrary("abc/123", error_message);
+ REQUIRE(!handle_maybe);
+ CHECK(handle_maybe.error() == dynamic_load_failure_error);
+ }
+
+ error_message.clear();
+ auto handle_maybe =
+ DynamicallyLoadTracingLibrary(mocktracer_library.c_str(), error_message);
+ REQUIRE(handle_maybe);
+ REQUIRE(error_message.empty());
+
+ SECTION("Creating a tracer from invalid json gives an error.") {
+ auto tracer_maybe =
+ handle_maybe->tracer_factory().MakeTracer("abc 123", error_message);
+ REQUIRE(!tracer_maybe);
+ }
+
+ SECTION("Creating a tracer from an invalid configuration gives an error.") {
+ auto tracer_maybe = handle_maybe->tracer_factory().MakeTracer(
+ R"({"abc": 123})", error_message);
+ REQUIRE(!tracer_maybe);
+ REQUIRE(tracer_maybe.error() == invalid_configuration_error);
+ }
+
+ SECTION("Creating a tracer with an invalid output_file gives an error.") {
+ auto tracer_maybe = handle_maybe->tracer_factory().MakeTracer(
+ R"({"output_file": ""})", error_message);
+ REQUIRE(!tracer_maybe);
+ REQUIRE(tracer_maybe.error() == invalid_configuration_error);
+ }
+
+ SECTION(
+ "We can create spans from an OpenTracing library dynamically loaded.") {
+ std::string span_filename{"spans."};
+ const auto random_id = std::random_device{}();
+ span_filename.append(std::to_string(random_id));
+ std::string configuration = R"({ "output_file": ")";
+ configuration.append(span_filename);
+ configuration.append(R"(" })");
+
+ {
+ auto tracer_maybe = handle_maybe->tracer_factory().MakeTracer(
+ configuration.c_str(), error_message);
+ REQUIRE(tracer_maybe);
+ auto tracer = *tracer_maybe;
+ tracer->StartSpan("abc");
+ tracer->Close();
+ }
+
+ std::ifstream istream{span_filename};
+ REQUIRE(istream.good());
+ std::string spans_json{std::istreambuf_iterator{istream},
+ std::istreambuf_iterator{}};
+ istream.close();
+ CHECK(std::remove(span_filename.c_str()) == 0);
+ CHECK(!spans_json.empty());
+ }
+}
+
+int main(int argc, char* argv[]) {
+ Catch::Session session;
+
+ using namespace Catch::clara;
+ auto cli = session.cli() | Opt(mocktracer_library,
+ "mocktracer_library")["--mocktracer_library"];
+
+ session.cli(cli);
+ int rcode = session.applyCommandLine(argc, argv);
+ if (rcode != 0) {
+ return rcode;
+ }
+
+ if (mocktracer_library.empty()) {
+ std::cerr << "Must provide mocktracer_library!\n";
+ return -1;
+ }
+
+ return session.run();
+}
diff --git a/test/string_view_test.cpp b/test/string_view_test.cpp
index f700ce6..33a8bc4 100644
--- a/test/string_view_test.cpp
+++ b/test/string_view_test.cpp
@@ -40,4 +40,10 @@ TEST_CASE("string_view") {
CHECK(val == cpy.data());
CHECK(val.length() == cpy.length());
}
+
+ SECTION("operator[] can be used to access characters in a string_view") {
+ string_view s = "abc123";
+ CHECK(&s[0] == s.data());
+ CHECK(&s[1] == s.data() + 1);
+ }
}