From b196c6498d22e47bb9d0da0153068ec54eac7956 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:44:33 +0200 Subject: Adding upstream version 1.6.0. Signed-off-by: Daniel Baumann --- include/opentracing/dynamic_load.h | 151 +++++++++++++++++++ include/opentracing/ext/tags.h | 122 +++++++++++++++ include/opentracing/noop.h | 28 ++++ include/opentracing/propagation.h | 205 ++++++++++++++++++++++++++ include/opentracing/span.h | 228 ++++++++++++++++++++++++++++ include/opentracing/string_view.h | 144 ++++++++++++++++++ include/opentracing/symbols.h | 34 +++++ include/opentracing/tracer.h | 277 +++++++++++++++++++++++++++++++++++ include/opentracing/tracer_factory.h | 56 +++++++ include/opentracing/util.h | 84 +++++++++++ include/opentracing/value.h | 68 +++++++++ 11 files changed, 1397 insertions(+) create mode 100644 include/opentracing/dynamic_load.h create mode 100644 include/opentracing/ext/tags.h create mode 100644 include/opentracing/noop.h create mode 100644 include/opentracing/propagation.h create mode 100644 include/opentracing/span.h create mode 100644 include/opentracing/string_view.h create mode 100644 include/opentracing/symbols.h create mode 100644 include/opentracing/tracer.h create mode 100644 include/opentracing/tracer_factory.h create mode 100644 include/opentracing/util.h create mode 100644 include/opentracing/value.h (limited to 'include') diff --git a/include/opentracing/dynamic_load.h b/include/opentracing/dynamic_load.h new file mode 100644 index 0000000..f8f9837 --- /dev/null +++ b/include/opentracing/dynamic_load.h @@ -0,0 +1,151 @@ +#ifndef OPENTRACING_DYNAMIC_LOAD_H +#define OPENTRACING_DYNAMIC_LOAD_H + +#include +#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` and `opentracing_abi_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; +// std::string error_message; +// opentracing::TracerFactory* tracer_factory = nullptr; +// int rcode = (*OpenTracingMakeTracerFactory)( +// OPENTRACING_VERSION, +// OPENTRACING_ABI_VERSION, +// &static_cast(error_category), +// static_cast(&error_message), +// &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}; +// } +using OpenTracingMakeTracerFactoryType = int( + const char* opentracing_version, const char* opentracing_abi_version, + const void** error_category, void* error_message, void** tracer_factory); + +#ifdef WIN32 + +#define OPENTRACING_DECLARE_IMPL_FACTORY(X) \ + extern "C" { \ + \ + extern __declspec(dllexport) \ + OpenTracingMakeTracerFactoryType* const OpenTracingMakeTracerFactory; \ + \ + __declspec(selectany) OpenTracingMakeTracerFactoryType* const \ + OpenTracingMakeTracerFactory = X; \ + } // extern "C" + +#else + +#define OPENTRACING_DECLARE_IMPL_FACTORY(X) \ + extern "C" { \ + \ + __attribute((weak)) extern OpenTracingMakeTracerFactoryType* const \ + OpenTracingMakeTracerFactory; \ + \ + OpenTracingMakeTracerFactoryType* const OpenTracingMakeTracerFactory = X; \ + } // extern "C" + +#endif + +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 +OPENTRACING_API 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 +OPENTRACING_API 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/ext/tags.h b/include/opentracing/ext/tags.h new file mode 100644 index 0000000..746cd37 --- /dev/null +++ b/include/opentracing/ext/tags.h @@ -0,0 +1,122 @@ +#ifndef OPENTRACING_EXT_TAGS_H +#define OPENTRACING_EXT_TAGS_H + +#include +#include +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +namespace ext { +// The following tags are described in greater detail at the following url: +// https://github.com/opentracing/specification/blob/master/semantic_conventions.md +// +// Here we define standard names for tags that can be added to spans by the +// instrumentation code. The actual tracing systems are not required to +// retain these as tags in the stored spans if they have other means of +// representing the same data. For example, the SPAN_KIND='server' can be +// inferred from a Zipkin span by the presence of ss/sr annotations. + +// --------------------------------------------------------------------------- +// span_kind hints at relationship between spans, e.g. client/server +// --------------------------------------------------------------------------- +OPENTRACING_API extern const opentracing::string_view span_kind; + +// Marks a span representing the client-side of an RPC or other remote call +OPENTRACING_API extern const opentracing::string_view span_kind_rpc_client; + +// Marks a span representing the server-side of an RPC or other remote call +OPENTRACING_API extern const opentracing::string_view span_kind_rpc_server; + +// --------------------------------------------------------------------------- +// error indicates whether a Span ended in an error state. +// --------------------------------------------------------------------------- +OPENTRACING_API extern const opentracing::string_view error; + +// --------------------------------------------------------------------------- +// component (string) ia s low-cardinality identifier of the module, library, +// or package that is generating a span. +// --------------------------------------------------------------------------- +OPENTRACING_API extern const opentracing::string_view component; + +// --------------------------------------------------------------------------- +// sampling_priority (uint16) determines the priority of sampling this Span. +// --------------------------------------------------------------------------- +OPENTRACING_API extern const opentracing::string_view sampling_priority; + +// --------------------------------------------------------------------------- +// peer_* tags can be emitted by either client-side of server-side to describe +// the other side/service in a peer-to-peer communications, like an RPC call. +// --------------------------------------------------------------------------- +// peer_service (string) records the service name of the peer +OPENTRACING_API extern const opentracing::string_view peer_service; + +// peer_hostname (string) records the host name of the peer +OPENTRACING_API extern const opentracing::string_view peer_hostname; + +// peer_address (string) suitable for use in a networking client library. +// This may be a "ip:port", a bare "hostname", a FQDN, or even a +// JDBC substring like "mysql://prod-db:3306" +OPENTRACING_API extern const opentracing::string_view peer_address; + +// peer_host_ipv4 (uint32) records IP v4 host address of the peer +OPENTRACING_API extern const opentracing::string_view peer_host_ipv4; + +// peer_host_ipv6 (string) records IP v6 host address of the peer +OPENTRACING_API extern const opentracing::string_view peer_host_ipv6; + +// peer_port (uint16) records port number of the peer +OPENTRACING_API extern const opentracing::string_view peer_port; + +// --------------------------------------------------------------------------- +// HTTP tags +// --------------------------------------------------------------------------- + +// http_url (string) should be the URL of the request being handled in this +// segment of the trace, in standard URI format. The protocol is optional. +OPENTRACING_API extern const opentracing::string_view http_url; + +// http_method (string) is the HTTP method of the request. +// Both upper/lower case values are allowed. +OPENTRACING_API extern const opentracing::string_view http_method; + +// http_status_code (int) is the numeric HTTP status code (200, 404, etc) +// of the HTTP response. +OPENTRACING_API extern const opentracing::string_view http_status_code; + +// --------------------------------------------------------------------------- +// DATABASE tags +// --------------------------------------------------------------------------- + +// database_instance (string) The database instance name. E.g., In java, if +// the jdbc.url="jdbc:mysql://127.0.0.1:3306/customers", the instance +// name is "customers" +OPENTRACING_API extern const opentracing::string_view database_instance; + +// database_statement (string) A database statement for the given database +// type. E.g., for db.type="SQL", "SELECT * FROM user_table"; +// for db.type="redis", "SET mykey 'WuValue'". +OPENTRACING_API extern const opentracing::string_view database_statement; + +// database_type (string) For any SQL database, "sql". For others, +// the lower-case database category, e.g. "cassandra", "hbase", or "redis". +OPENTRACING_API extern const opentracing::string_view database_type; + +// database_user (string) Username for accessing database. E.g., +// "readonly_user" or "reporting_user" +OPENTRACING_API extern const opentracing::string_view database_user; + +// --------------------------------------------------------------------------- +// message_bus tags +// --------------------------------------------------------------------------- + +// message_bus_destination (string) An address at which messages can be +// exchanged. E.g. A Kafka record has an associated "topic name" that can +// be extracted by the instrumented producer or consumer and stored +// using this tag. +OPENTRACING_API extern const opentracing::string_view message_bus_destination; +} // namespace ext +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_EXT_TAGS_H diff --git a/include/opentracing/noop.h b/include/opentracing/noop.h new file mode 100644 index 0000000..5313489 --- /dev/null +++ b/include/opentracing/noop.h @@ -0,0 +1,28 @@ +#ifndef OPENTRACING_NOOP_H +#define OPENTRACING_NOOP_H + +#include +#include +#include +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +// A NoopTracer is a trivial, minimum overhead implementation of Tracer +// for which all operations are no-ops. +// +// The primary use of this implementation is in libraries, such as RPC +// frameworks, that make tracing an optional feature controlled by the +// end user. A no-op implementation allows said libraries to use it +// as the default Tracer and to write instrumentation that does +// not need to keep checking if the tracer instance is nil. +// +// For the same reason, the NoopTracer is the default "global" tracer +// (see Tracer::Global and Tracer::InitGlobal functions). +// +// WARNING: NoopTracer does not support baggage propagation. +OPENTRACING_API std::shared_ptr MakeNoopTracer() noexcept; +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_NOOP_H diff --git a/include/opentracing/propagation.h b/include/opentracing/propagation.h new file mode 100644 index 0000000..c4edc6a --- /dev/null +++ b/include/opentracing/propagation.h @@ -0,0 +1,205 @@ +#ifndef OPENTRACING_PROPAGATION_H +#define OPENTRACING_PROPAGATION_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +class Tracer; +class SpanContext; + +enum class SpanReferenceType { + // ChildOfRef refers to a parent Span that caused *and* somehow depends + // upon the new child Span. Often (but not always), the parent Span cannot + // finish until the child Span does. + // + // An timing diagram for a ChildOfRef that's blocked on the new Span: + // + // [-Parent Span---------] + // [-Child Span----] + // + // See http://opentracing.io/spec/ + // + // See opentracing.ChildOf() + ChildOfRef = 1, + + // FollowsFromRef refers to a parent Span that does not depend in any way + // on the result of the new child Span. For instance, one might use + // FollowsFromRefs to describe pipeline stages separated by queues, + // or a fire-and-forget cache insert at the tail end of a web request. + // + // A FollowsFromRef Span is part of the same logical trace as the new Span: + // i.e., the new Span is somehow caused by the work of its FollowsFromRef. + // + // All of the following could be valid timing diagrams for children that + // "FollowFrom" a parent. + // + // [-Parent Span-] [-Child Span-] + // + // + // [-Parent Span--] + // [-Child Span-] + // + // + // [-Parent Span-] + // [-Child Span-] + // + // See http://opentracing.io/spec/ + // + // See opentracing.FollowsFrom() + FollowsFromRef = 2 +}; + +// Returns the std::error_category class used for opentracing propagation +// 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 +OPENTRACING_API const std::error_category& propagation_error_category(); + +// `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` 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 +// Tracer::Extract() is of the expected type but is corrupted. +const std::error_code span_context_corrupted_error( + 3, propagation_error_category()); + +// `key_not_found_error` occurs when TextMapReader::LookupKey fails to find +// an entry for the provided key. +const std::error_code key_not_found_error(4, propagation_error_category()); + +// `lookup_key_not_supported_error` occurs when TextMapReader::LookupKey is +// not supported for the provided key. +const std::error_code lookup_key_not_supported_error( + 5, propagation_error_category()); + +// TextMapReader is the Extract() carrier for the TextMap builtin format. With +// it, the caller can decode a SpanContext from entries in a propagated map of +// Unicode strings. +// +// See the HTTPHeaders examples. +class TextMapReader { + public: + virtual ~TextMapReader() = default; + + // LookupKey returns the value for the specified `key` if available. If no + // such key is present, it returns `key_not_found_error`. + // + // TextMapReaders are not required to implement this method. If not supported, + // the function returns `lookup_key_not_supported_error`. + // + // Tracers may use this as an alternative to `ForeachKey` as a faster way to + // extract span context. + virtual expected LookupKey(string_view /*key*/) const { + return make_unexpected(lookup_key_not_supported_error); + } + + // ForeachKey returns TextMap contents via repeated calls to the `f` + // function. If any call to `f` returns an error, ForeachKey terminates and + // returns that error. + // + // NOTE: The backing store for the TextMapReader may contain data unrelated + // to SpanContext. As such, Inject() and Extract() implementations that + // call the TextMapWriter and TextMapReader interfaces must agree on a + // prefix or other convention to distinguish their own key:value pairs. + // + // The "foreach" callback pattern reduces unnecessary copying in some cases + // and also allows implementations to hold locks while the map is read. + virtual expected ForeachKey( + std::function(string_view key, string_view value)> f) + const = 0; +}; + +// TextMapWriter is the Inject() carrier for the TextMap builtin format. With +// it, the caller can encode a SpanContext for propagation as entries in a map +// of unicode strings. +// +// See the HTTPHeaders examples. +class TextMapWriter { + public: + virtual ~TextMapWriter() = default; + + // Set a key:value pair to the carrier. Multiple calls to Set() for the + // same key leads to undefined behavior. + // + // NOTE: The backing store for the TextMapWriter may contain data unrelated + // to SpanContext. As such, Inject() and Extract() implementations that + // call the TextMapWriter and TextMapReader interfaces must agree on a + // prefix or other convention to distinguish their own key:value pairs. + virtual expected Set(string_view key, string_view value) const = 0; +}; + +// HTTPHeadersReader is the Extract() carrier for the HttpHeaders builtin +// format. With it, the caller can decode a SpanContext from entries in HTTP +// request headers. +// +// For example, Extract(): +// +// const Tracer& tracer = /* some tracer */ +// const HTTPHeadersReader& carrier_reader = /* some carrier */ +// auto span_context_maybe = tracer.Extract(carrier_reader); +// if (!span_context_maybe) { +// throw std::runtime_error(span_context_maybe.error().message()); +// } +// auto span = tracer.StartSpan("op", +// { ChildOf(span_context_maybe->get()) }); +class HTTPHeadersReader : public TextMapReader {}; + +// HTTPHeadersWriter is the Inject() carrier for the TextMap builtin format. +// With it, the caller can encode a SpanContext for propagation as entries in +// http request headers +// +// For example, Inject(): +// +// const HTTPHeadersWriter& carrier_writer = /* some carrier */ +// auto was_successful = span.tracer().Inject(span, +// carrier_writer); +// if (!was_successful) { +// throw std::runtime_error(was_successful.error().message()); +// } +class HTTPHeadersWriter : public TextMapWriter {}; + +// CustomCarrierReader is the Extract() carrier for a custom format. With it, +// the caller can decode a SpanContext from entries in a custom protocol. +class CustomCarrierReader { + public: + virtual ~CustomCarrierReader() = default; + + // Extract is expected to specialize on the tracer implementation so as to + // most efficiently decode its context. + virtual expected> Extract( + const Tracer& tracer) const = 0; +}; + +// CustomCarrierWriter is the Inject() carrier for a custom format. With it, +// the caller can encode a SpanContext for propagation as entries in a custom +// protocol. +class CustomCarrierWriter { + public: + virtual ~CustomCarrierWriter() = default; + + // Inject is expected to specialize on the tracer implementation so as to most + // efficiently encode its context. + virtual expected Inject(const Tracer& tracer, + const SpanContext& sc) const = 0; +}; +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_PROPAGATION_H diff --git a/include/opentracing/span.h b/include/opentracing/span.h new file mode 100644 index 0000000..a48b33c --- /dev/null +++ b/include/opentracing/span.h @@ -0,0 +1,228 @@ +#ifndef OPENTRACING_SPAN_H +#define OPENTRACING_SPAN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +class Tracer; + +// SpanContext represents Span state that must propagate to descendant Spans and +// across process boundaries (e.g., a tuple). +class SpanContext { + public: + virtual ~SpanContext() = default; + + // ForeachBaggageItem calls a function for each baggage item in the + // context. If the function returns false, it will not be called + // again and ForeachBaggageItem will return. + virtual void ForeachBaggageItem( + std::function f) + const = 0; + + // Clone creates a copy of SpanContext. + // + // Returns nullptr on failure. + virtual std::unique_ptr Clone() const noexcept = 0; + + // Return the ID of the trace. + // + // Should be globally unique. Every span in a trace shares this ID. + // + // An empty string will be returned if the tracer does not support this + // functionality or an error occurs (this is the case for no-op traces, for + // example). + virtual std::string ToTraceID() const noexcept { return {}; } + + // Return the ID of the associated Span. + // + // Should be unique within a trace. Each span within a trace contains a + // different ID. + // + // An empty string will be returned if the tracer does not support this + // functionality or an error occurs (this is the case for no-op traces, for + // example). + virtual std::string ToSpanID() const noexcept { return {}; } +}; + +struct LogRecord { + using Field = std::pair; + + SystemTime timestamp; + std::vector fields; +}; + +inline bool operator==(const LogRecord& lhs, const LogRecord& rhs) { + return lhs.timestamp == rhs.timestamp && lhs.fields == rhs.fields; +} + +inline bool operator!=(const LogRecord& lhs, const LogRecord& rhs) { + return !(lhs == rhs); +} + +// FinishOptions allows Span.Finish callers to override the finish +// timestamp. +struct FinishSpanOptions { + SteadyTime finish_steady_timestamp; + + // log_records allows the caller to specify the contents of many Log() calls + // with a single vector. May be empty. + // + // None of the LogRecord.timestamp values may be SystemTime() (i.e., they must + // be set explicitly). Also, they must be >= the Span's start system timestamp + // and <= the finish_steady_timestamp converted to system timestamp + // (or SystemTime::now() if finish_steady_timestamp is default-constructed). + // Otherwise the behavior of FinishWithOptions() is unspecified. + std::vector log_records; +}; + +// FinishSpanOption instances (zero or more) may be passed to Span.Finish. +class FinishSpanOption { + public: + FinishSpanOption(const FinishSpanOption&) = delete; + + virtual ~FinishSpanOption() = default; + + virtual void Apply(FinishSpanOptions& options) const noexcept = 0; + + protected: + FinishSpanOption() = default; +}; + +// Span represents an active, un-finished span in the OpenTracing system. +// +// Spans are created by the Tracer interface. +class Span { + public: + // If Finish has not already been called for the Span, it's destructor must + // do so. + virtual ~Span() = default; + + // Sets the end timestamp and finalizes Span state. + // + // If Finish is called a second time, it is guaranteed to do nothing. + void Finish(std::initializer_list> + option_list = {}) noexcept { + FinishSpanOptions options; + options.finish_steady_timestamp = SteadyClock::now(); + for (const auto& option : option_list) option.get().Apply(options); + FinishWithOptions(options); + } + + virtual void FinishWithOptions( + const FinishSpanOptions& finish_span_options) noexcept = 0; + + // Sets or changes the operation name. + // + // If SetOperationName is called after Finish it leaves the Span in a valid + // state, but its behavior is unspecified. + virtual void SetOperationName(string_view name) noexcept = 0; + + // Adds a tag to the span. + // + // If there is a pre-existing tag set for `key`, it is overwritten. + // + // Tag values can be numeric types, strings, or bools. The behavior of + // other tag value types is undefined at the OpenTracing level. If a + // tracing system does not know how to handle a particular value type, it + // may ignore the tag, but shall not panic. + // + // If SetTag is called after Finish it leaves the Span in a valid state, but + // its behavior is unspecified. + virtual void SetTag(string_view key, const Value& value) noexcept = 0; + + // SetBaggageItem sets a key:value pair on this Span and its SpanContext + // that also propagates to descendants of this Span. + // + // SetBaggageItem() enables powerful functionality given a full-stack + // opentracing integration (e.g., arbitrary application data from a mobile + // app can make it, transparently, all the way into the depths of a storage + // system), and with it some powerful costs: use this feature with care. + // + // IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to + // *future* causal descendants of the associated Span. + // + // IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and + // value is copied into every local *and remote* child of the associated + // Span, and that can add up to a lot of network and cpu overhead. + // + // If SetBaggageItem is called after Finish it leaves the Span in a valid + // state, but its behavior is unspecified. + virtual void SetBaggageItem(string_view restricted_key, + string_view value) noexcept = 0; + + // Gets the value for a baggage item given its key. Returns the empty string + // if the value isn't found in this Span. + virtual std::string BaggageItem(string_view restricted_key) const + noexcept = 0; + + // Log is an efficient and type-checked way to record key:value logging data + // about a Span. Here's an example: + // + // span.Log({ + // {"event", "soft error"}, + // {"type", "cache timeout"}, + // {"waited.millis", 1500}}); + virtual void Log( + std::initializer_list> fields) noexcept = 0; + + virtual void Log( + SystemTime timestamp, + std::initializer_list> fields) noexcept = 0; + + virtual void Log( + SystemTime timestamp, + const std::vector>& fields) noexcept = 0; + + // context() yields the SpanContext for this Span. Note that the return + // value of context() is still valid after a call to Span.Finish(), as is + // a call to Span.context() after a call to Span.Finish(). + virtual const SpanContext& context() const noexcept = 0; + + // Provides access to the Tracer that created this Span. + virtual const Tracer& tracer() const noexcept = 0; +}; + +// FinishTimestamp is a FinishSpanOption that sets an explicit finish timestamp +// for a Span. +class FinishTimestamp : public FinishSpanOption { + public: + explicit FinishTimestamp(SteadyTime steady_when) noexcept + : steady_when_(steady_when) {} + + // Construct a timestamp using a duration from the epoch of std::time_t. + // From the documentation on std::time_t's epoch: + // Although not defined, this is almost always an integral value holding + // the number of seconds (not counting leap seconds) since 00:00, Jan 1 + // 1970 UTC, corresponding to POSIX time + // See http://en.cppreference.com/w/cpp/chrono/c/time_t + template + explicit FinishTimestamp( + const std::chrono::duration& time_since_epoch) noexcept + : steady_when_(convert_time_point( + SystemClock::from_time_t(std::time_t(0)) + + std::chrono::duration_cast( + time_since_epoch))) {} + + FinishTimestamp(const FinishTimestamp& other) noexcept + : FinishSpanOption(), steady_when_(other.steady_when_) {} + + void Apply(FinishSpanOptions& options) const noexcept override { + options.finish_steady_timestamp = steady_when_; + } + + private: + SteadyTime steady_when_; +}; +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_SPAN_H diff --git a/include/opentracing/string_view.h b/include/opentracing/string_view.h new file mode 100644 index 0000000..a30eb7f --- /dev/null +++ b/include/opentracing/string_view.h @@ -0,0 +1,144 @@ +#ifndef OPENTRACING_STRING_VIEW_H +#define OPENTRACING_STRING_VIEW_H + +#include +#include +#include +#include +#include + +// =========== +// string_view.h +// =========== +// class string_view - Constant reference to an external string +// +// ----------------- +// String References +// ----------------- +// This string references is a simplified version of the boost::string_ref. +// Its purpose is to avoid a number of efficiency problems that appear +// commonly when interacting with 'std::string' and c-strings. +// +// See the boost documentation for more background: +// http://www.boost.org/doc/libs/master/libs/utility/doc/html/string_ref.html +// +// ----- +// Note: +// ----- +// Although we have the ability to use wide string refs, there are side +// effects in exposing an OpenTracing interface that works with narrow and wide +// strings at the same time. Storage on the implementation will have a 'native' +// format. +// +// Exposing references to that format avoid copies means clients would be +// dependent on that format. If they're dependent on that detail and then switch +// out the implementation to a different format, there would be lots of code +// that breaks if it was expecting wstring and starts receiving string all of a +// sudden. That design issue still needs to be addressed. + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +// =============== +// class string_view +// =============== +// Represent a constant reference to an external character array. The external +// array need not be null-terminated, if explicitly created with a known length. +// +// This class does not own the data. It is expected to be used in situations +// where the character data resides in some other buffer, whose lifetime extends +// past that of the string_view. For this reason, it is not in general safe to +// store a string_view. + +class string_view { + public: + // Construct an empty string_view + string_view() noexcept : data_(nullptr), length_(0) {} + + // create string reference from const character pointer + string_view(const char* str) noexcept + : data_(str), length_(std::strlen(str)) {} + + // Create constant string reference from pointer and length + string_view(const std::basic_string& str) noexcept + : data_(str.c_str()), length_(str.length()) {} + + // Create constant string reference from pointer and length + string_view(const char* str, size_t len) noexcept + : data_(str), length_(len) {} + + // Implicit conversion to std::string + operator std::string() const { return {data_, length_}; } + + // Return address of the referenced string + const char* data() const noexcept { return data_; } + + // Returns true if `length_` == 0 + bool empty() const noexcept { return length_ == 0; } + + // Return the length of the referenced string + size_t length() const noexcept { return length_; } + size_t size() const noexcept { return length_; } + + // Returns a RandomAccessIterator to the first element. + const char* begin() const noexcept { return data(); } + + // 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_' +}; + +inline bool operator==(string_view lhs, string_view rhs) noexcept { + return lhs.length() == rhs.length() && + std::equal(lhs.data(), lhs.data() + lhs.length(), rhs.data()); +} + +inline bool operator==(string_view lhs, const std::string& rhs) noexcept { + return lhs == string_view(rhs); +} + +inline bool operator==(const std::string& lhs, string_view rhs) noexcept { + return string_view(lhs) == rhs; +} + +inline bool operator==(string_view lhs, const char* rhs) noexcept { + return lhs == string_view(rhs); +} + +inline bool operator==(const char* lhs, string_view rhs) noexcept { + return string_view(lhs) == rhs; +} + +inline bool operator!=(string_view lhs, string_view rhs) noexcept { + return !(lhs == rhs); +} + +inline bool operator!=(string_view lhs, const std::string& rhs) noexcept { + return !(lhs == rhs); +} + +inline bool operator!=(const std::string& lhs, string_view rhs) noexcept { + return !(lhs == rhs); +} + +inline bool operator!=(string_view lhs, const char* rhs) noexcept { + return !(lhs == rhs); +} + +inline bool operator!=(const char* lhs, string_view rhs) noexcept { + return !(lhs == rhs); +} + +inline std::ostream& operator<<(std::ostream& os, + const opentracing::string_view& ref) { + return os.write(ref.data(), static_cast(ref.length())); +} +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_STRING_VIEW_H diff --git a/include/opentracing/symbols.h b/include/opentracing/symbols.h new file mode 100644 index 0000000..b359ced --- /dev/null +++ b/include/opentracing/symbols.h @@ -0,0 +1,34 @@ +#ifndef OPENTRACING_SYMBOLS_H +#define OPENTRACING_SYMBOLS_H + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4251) +#endif + +#ifdef _MSC_VER + +#define OPENTRACING_EXPORT __declspec(dllexport) + +// Export if this is our own source, otherwise import: +#ifndef OPENTRACING_STATIC +#ifdef OPENTRACING_EXPORTS +#define OPENTRACING_API __declspec(dllexport) +#else // OPENTRACING_STATIC +#define OPENTRACING_API __declspec(dllimport) +#endif // OPENTRACING_EXPORTS +#endif // OPENTRACING_STATIC + +#endif // _MSC_VER + +#ifndef OPENTRACING_EXPORT +#define OPENTRACING_EXPORT +#endif + +#ifndef OPENTRACING_API +#define OPENTRACING_API +#endif + +#endif // OPENTRACING_SYMBOLS_H diff --git a/include/opentracing/tracer.h b/include/opentracing/tracer.h new file mode 100644 index 0000000..a4a125a --- /dev/null +++ b/include/opentracing/tracer.h @@ -0,0 +1,277 @@ +#ifndef OPENTRACING_TRACER_H +#define OPENTRACING_TRACER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +// StartSpanOptions allows Tracer.StartSpan() callers a mechanism to override +// the start timestamp, specify Span References, and make a single Tag or +// multiple Tags available at Span start time. +// +// StartSpan() callers should look at the StartSpanOption interface and +// implementations available in this library. +struct StartSpanOptions { + // start_system_timestamp and start_steady_timestamp override the Span's start + // time, or implicitly become std::chrono::system_clock::now() and + // std::chrono::steady_clock::now() if both are equal to the epoch (default + // behavior). + // + // If one of the timestamps is set but not the other, the set timestamp is + // used to estimate the corresponding timestamp of the other. + SystemTime start_system_timestamp; + SteadyTime start_steady_timestamp; + + // Zero or more causal references to other Spans (via their SpanContext). + // If empty, start a "root" Span (i.e., start a new trace). + // + // Any nullptrs provided will be ignored. + std::vector> references; + + // Zero or more tags to apply to the newly created span. + std::vector> tags; +}; + +// StartSpanOption instances (zero or more) may be passed to Tracer.StartSpan. +class StartSpanOption { + public: + StartSpanOption(const StartSpanOption&) = delete; + + virtual ~StartSpanOption() = default; + + virtual void Apply(StartSpanOptions& options) const noexcept = 0; + + protected: + StartSpanOption() = default; +}; + +// Tracer is a simple, thin interface for Span creation and SpanContext +// propagation. +class OPENTRACING_API Tracer { + public: + virtual ~Tracer() = default; + + // Create, start, and return a new Span with the given `operationName` and + // incorporate the given StartSpanOption `option_list`. + // + // A Span with no SpanReference options (e.g., opentracing::ChildOf() or + // opentracing::FollowsFrom()) becomes the root of its own trace. + // + // Examples: + // + // opentracing::Tracer& tracer = ... + // + // // The root-span case: + // auto span = tracer.StartSpan("GetFeed") + // + // // The vanilla child span case: + // auto span = tracer.StartSpan( + // "GetFeed", + // {opentracing::ChildOf(&parentSpan.context())}) + // + // // All the bells and whistles: + // auto span = tracer.StartSpan( + // "GetFeed", + // {opentracing::ChildOf(&parentSpan.context()), + // opentracing::Tag{"user_agent", loggedReq.UserAgent}, + // opentracing::StartTimestamp(loggedReq.timestamp())}) + // + // If StartSpan is called after Close, it leaves the Tracer in a valid + // state, but its behavior is unspecified. + std::unique_ptr StartSpan( + string_view operation_name, + std::initializer_list> option_list = {}) + const noexcept { + StartSpanOptions options; + for (const auto& option : option_list) option.get().Apply(options); + return StartSpanWithOptions(operation_name, options); + } + + virtual std::unique_ptr StartSpanWithOptions( + string_view operation_name, const StartSpanOptions& options) const + noexcept = 0; + + // Inject() takes the `sc` SpanContext instance and injects it for propagation + // within `carrier`. + // + // OpenTracing defines a common set of `carrier` interfaces. + // + // Throws only if `writer` does. + // + // If `writer` is an `std::ostream`, then Inject() propagates `sc` as a blob + // of binary data. + virtual expected Inject(const SpanContext& sc, + std::ostream& writer) const = 0; + + virtual expected Inject(const SpanContext& sc, + const TextMapWriter& writer) const = 0; + + virtual expected Inject(const SpanContext& sc, + const HTTPHeadersWriter& writer) const = 0; + + virtual expected Inject(const SpanContext& sc, + const CustomCarrierWriter& writer) const { + return writer.Inject(*this, sc); + } + + // Extract() returns a SpanContext instance given `carrier`. + // + // OpenTracing defines a common set of `carrier` interfaces. + // + // Returns a `SpanContext` that is `non-null` on success or nullptr if no span + // is found; otherwise an std::error_code. + // + // Throws only if `reader` does. + virtual expected> Extract( + std::istream& reader) const = 0; + + virtual expected> Extract( + const TextMapReader& reader) const = 0; + + virtual expected> Extract( + const HTTPHeadersReader& reader) const = 0; + + virtual expected> Extract( + const CustomCarrierReader& reader) const { + return reader.Extract(*this); + } + + // Close is called when a tracer is finished processing spans. It is not + // required to be called and its effect is unspecified. For example, an + // implementation might use this function to flush buffered spans to its + // recording system and failing to call it could result in some spans being + // dropped. + virtual void Close() noexcept {} + + // GlobalTracer returns the global tracer. + static std::shared_ptr Global() noexcept; + + // InitGlobalTracer sets the global tracer pointer, returns the + // former global tracer value. + static std::shared_ptr InitGlobal( + std::shared_ptr tracer) noexcept; + + static bool IsGlobalTracerRegistered() noexcept; +}; + +// StartTimestamp is a StartSpanOption that sets an explicit start timestamp for +// the new Span. +class StartTimestamp : public StartSpanOption { + public: + StartTimestamp(SystemTime system_when, SteadyTime steady_when) noexcept + : system_when_(system_when), steady_when_(steady_when) {} + + StartTimestamp(SystemTime system_when) noexcept + : system_when_(system_when), + steady_when_(convert_time_point(system_when_)) {} + + // Construct a timestamp using a duration from the epoch of std::time_t. + // From the documentation on std::time_t's epoch: + // Although not defined, this is almost always an integral value holding + // the number of seconds (not counting leap seconds) since 00:00, Jan 1 + // 1970 UTC, corresponding to POSIX time + // See http://en.cppreference.com/w/cpp/chrono/c/time_t + template + explicit StartTimestamp( + const std::chrono::duration& time_since_epoch) noexcept + : StartTimestamp(SystemClock::from_time_t(std::time_t(0)) + + std::chrono::duration_cast( + time_since_epoch)) {} + + StartTimestamp(const StartTimestamp& other) noexcept + : StartSpanOption(), + system_when_(other.system_when_), + steady_when_(other.steady_when_) {} + + void Apply(StartSpanOptions& options) const noexcept override { + options.start_system_timestamp = system_when_; + options.start_steady_timestamp = steady_when_; + } + + private: + SystemTime system_when_; + SteadyTime steady_when_; +}; + +// SpanReference is a StartSpanOption that pairs a SpanReferenceType and a +// referenced SpanContext. See the SpanReferenceType documentation for +// supported relationships. +// +// If the referenced SpanContext is a nullptr, it is ignored. The passed +// SpanContext is copied during Span construction and the pointer is not +// retained. +class SpanReference : public StartSpanOption { + public: + SpanReference(SpanReferenceType type, const SpanContext* referenced) noexcept + : type_(type), referenced_(referenced) {} + + SpanReference(const SpanReference& other) noexcept + : StartSpanOption(), type_(other.type_), referenced_(other.referenced_) {} + + void Apply(StartSpanOptions& options) const noexcept override { + try { + if (referenced_) options.references.emplace_back(type_, referenced_); + } catch (const std::bad_alloc&) { + // Ignore reference if memory can't be allocated for it. + } + } + + private: + SpanReferenceType type_; + const SpanContext* referenced_; +}; + +// ChildOf returns a StartSpanOption pointing to a dependent parent span. +// +// See ChildOfRef, SpanReference +inline SpanReference ChildOf(const SpanContext* span_context) noexcept { + return {SpanReferenceType::ChildOfRef, span_context}; +} + +// FollowsFrom returns a StartSpanOption pointing to a parent Span that caused +// the child Span but does not directly depend on its result in any way. +// +// See FollowsFromRef, SpanReference +inline SpanReference FollowsFrom(const SpanContext* span_context) noexcept { + return {SpanReferenceType::FollowsFromRef, span_context}; +} + +// SetTag may be passed as a StartSpanOption to add a tag to new spans, +// for example: +// +// tracer.StartSpan("opName", SetTag{"Key", value}) +class SetTag : public StartSpanOption { + public: + SetTag(string_view key, const Value& value) noexcept + : key_(key), value_(value) {} + + SetTag(const SetTag& other) noexcept + : StartSpanOption(), key_(other.key_), value_(other.value_) {} + + void Apply(StartSpanOptions& options) const noexcept override { + try { + options.tags.emplace_back(key_, value_); + } catch (const std::bad_alloc&) { + // Ignore tag if memory can't be allocated for it. + } + } + + private: + const string_view key_; + const Value& value_; +}; +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_TRACER_H diff --git a/include/opentracing/tracer_factory.h b/include/opentracing/tracer_factory.h new file mode 100644 index 0000000..622fa5b --- /dev/null +++ b/include/opentracing/tracer_factory.h @@ -0,0 +1,56 @@ +#ifndef OPENTRACING_TRACER_FACTORY_H +#define OPENTRACING_TRACER_FACTORY_H + +#include +#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 +OPENTRACING_API 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 OPENTRACING_API 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/include/opentracing/util.h b/include/opentracing/util.h new file mode 100644 index 0000000..f3916cb --- /dev/null +++ b/include/opentracing/util.h @@ -0,0 +1,84 @@ +#ifndef OPENTRACING_UTIL_H +#define OPENTRACING_UTIL_H + +#include +#include +#include +#include + +// expected uses a C++11 implementation that follows the std::expected standard +// library proposal. +// +// See https://github.com/martinmoene/expected-lite +// https://github.com/viboes/std-make/blob/master/doc/proposal/expected/d0323r2.md +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +using SystemClock = std::chrono::system_clock; +using SteadyClock = std::chrono::steady_clock; +using SystemTime = SystemClock::time_point; +using SteadyTime = SteadyClock::time_point; + +// This is unsafe to do. +// +// This is like an unsafe std::reference_wrapper<> that allows taking +// references to temporaries. It must only be used for temporary +// SpanStartOption and SpanFinishOption objects. +template +class option_wrapper { + public: + option_wrapper(const T &opt) : ptr_(&opt) {} + + // This will dangle unless it is only used for short-lived initializer lists. + const T &get() const { return *ptr_; } + + private: + const T *ptr_; +}; + +// Support conversion between time_points from different clocks. There's no +// standard way to get the difference in epochs between clocks, so this uses +// an approximation suggested by Howard Hinnant. +// +// See https://stackoverflow.com/a/35282833/4447365 +template ::value>::type * = nullptr> +typename ToClock::time_point convert_time_point( + std::chrono::time_point from_time_point) { + auto from_now = FromClock::now(); + auto to_now = ToClock::now(); + return to_now + std::chrono::duration_cast( + from_time_point - from_now); +} + +template ::value>::type + * = nullptr> +typename ToClock::time_point convert_time_point( + std::chrono::time_point from_time_point) { + return std::chrono::time_point_cast( + from_time_point); +} + +// std::error_code's have default comparison operators; however, they make use +// of singleton addresses which can cause comparisons to fail when multiple +// versions of the opentracing library are linked in. Since this is a common +// deployment scenario when making OpenTracing plugins, we add this utility +// function to make comparing std::error_code across libraries easier. +// +// Note: There's a proposed change to the C++ standard that addresses this +// issue. See +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1196r0.html +inline bool are_errors_equal(std::error_code lhs, + std::error_code rhs) noexcept { + return opentracing::string_view{lhs.category().name()} == + opentracing::string_view{rhs.category().name()} && + lhs.value() == rhs.value(); +} + +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_UTIL_H diff --git a/include/opentracing/value.h b/include/opentracing/value.h new file mode 100644 index 0000000..1c91f8b --- /dev/null +++ b/include/opentracing/value.h @@ -0,0 +1,68 @@ +#ifndef OPENTRACING_VALUE_H +#define OPENTRACING_VALUE_H + +#include +#include +#include +#include +#include +#include +#include + +namespace opentracing { +BEGIN_OPENTRACING_ABI_NAMESPACE +// Variant value types for span tags and log payloads. +class Value; + +typedef std::unordered_map Dictionary; +typedef std::vector Values; +typedef util::variant, + util::recursive_wrapper> + variant_type; + +class Value : public variant_type { + public: + Value() noexcept : variant_type(nullptr) {} + Value(std::nullptr_t) noexcept : variant_type(nullptr) {} + + // variant_type's constructors will do some undesirable casting, for example + // variant_type(123) + // will construct a bool variant; hence, constructors are expanded + // out so as to provide more sensible behavior. + Value(bool x) noexcept : variant_type(x) {} + + template ::value && + std::is_signed::value>::type* = nullptr> + Value(T t) noexcept : variant_type(static_cast(t)) {} + + template ::value && + std::is_unsigned::value>::type* = nullptr> + Value(T t) noexcept : variant_type(static_cast(t)) {} + + template ::value>::type* = nullptr> + Value(T t) noexcept : variant_type(static_cast(t)) {} + + Value(const char* s) noexcept : variant_type(s) {} + + template + Value(const char (&cstr)[N]) : variant_type(std::string(cstr)) {} + + Value(const std::string& s) : variant_type(s) {} + Value(std::string&& s) : variant_type(std::move(s)) {} + Value(opentracing::string_view s) noexcept : variant_type(s) {} + + Value(const Values& values) : variant_type(values) {} + Value(Values&& values) : variant_type(std::move(values)) {} + + Value(const Dictionary& values) : variant_type(values) {} + Value(Dictionary&& values) : variant_type(std::move(values)) {} +}; +END_OPENTRACING_ABI_NAMESPACE +} // namespace opentracing + +#endif // OPENTRACING_VALUE_H -- cgit v1.2.3