diff --git a/api/include/opentelemetry/trace/span_context.h b/api/include/opentelemetry/trace/span_context.h new file mode 100644 index 0000000000..3469ce845b --- /dev/null +++ b/api/include/opentelemetry/trace/span_context.h @@ -0,0 +1,63 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +//#include +//#include + +#include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_flags.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/trace/trace_state.h" + +namespace opentelemetry +{ +namespace trace +{ + +// SpanContext contains the state that must propagate to child Spans and across +// process boundaries. It contains the identifiers TraceId and SpanId, +// TraceFlags, TraceState, and whether it has a remote parent. +class SpanContext final +{ +public: + // An invalid SpanContext. + SpanContext() noexcept : trace_state_(new TraceState) {} + + // TODO + // + // static SpanContext Create(TraceId traceId, SpanId spanId, TraceFlags traceFlags, TraceState + // traceState); static SpanContext CreateFromRemoteParent(...); + + const TraceId &trace_id() const noexcept { return trace_id_; } + const SpanId &span_id() const noexcept { return span_id_; } + const TraceFlags &trace_flags() const noexcept { return trace_flags_; } + const TraceState &trace_state() const noexcept { return *trace_state_; } + + bool IsValid() const noexcept { return trace_id_.IsValid() && span_id_.IsValid(); } + + bool HasRemoteParent() const noexcept { return remote_parent_; } + +private: + const TraceId trace_id_; + const SpanId span_id_; + const TraceFlags trace_flags_; + const nostd::unique_ptr trace_state_; // Never nullptr. + const bool remote_parent_ = false; +}; + +} // namespace trace +} // namespace opentelemetry diff --git a/api/include/opentelemetry/trace/trace_state.h b/api/include/opentelemetry/trace/trace_state.h new file mode 100644 index 0000000000..f039a3d143 --- /dev/null +++ b/api/include/opentelemetry/trace/trace_state.h @@ -0,0 +1,107 @@ +// Copyright 2020, OpenTelemetry Authors +// +// 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. + +#pragma once + +#include +#include + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" + +namespace opentelemetry +{ +namespace trace +{ + +// TraceState carries tracing-system specific context in a list of key-value pairs. TraceState +// allows different vendors to propagate additional information and inter-operate with their legacy +// Id formats. +// +// Implementation is optimized for a small list of key-value pairs. +// +// Key is opaque string up to 256 characters printable. It MUST begin with a lowercase letter, +// and can only contain lowercase letters a-z, digits 0-9, underscores _, dashes -, asterisks *, and +// forward slashes /. +// +// Value is opaque string up to 256 characters, of printable ASCII RFC0020 characters (i.e. the +// range 0x20 to 0x7E) except comma , and equals =. +class TraceState +{ +public: + static constexpr int kKeyMaxSize = 256; + static constexpr int kValueMaxSize = 256; + static constexpr int kMaxKeyValuePairs = 32; + + class Entry + { + public: + virtual ~Entry() {} + + virtual nostd::string_view key() = 0; + virtual nostd::string_view value() = 0; + }; + + // An empty TraceState. + TraceState() noexcept = default; + + virtual ~TraceState() = default; + + // Returns false if no such key, otherwise returns true and populates value. + virtual bool Get(nostd::string_view key, nostd::string_view *value) const noexcept + { + return false; + } + + // Returns true if there are no keys. + virtual bool empty() const noexcept { return true; } + + // Returns a span of all the entries. The TraceState object must outlive the span. + virtual nostd::span entries() const noexcept { return {}; } + + // Key is opaque string up to 256 characters printable. It MUST begin with a lowercase letter, and + // can only contain lowercase letters a-z, digits 0-9, underscores _, dashes -, asterisks *, and + // forward slashes /. For multi-tenant vendor scenarios, an at sign (@) can be used to prefix the + // vendor name. + static bool IsValidKey(nostd::string_view key) + { + if (key.empty() || key.length() > kKeyMaxSize || !IsNumberOrDigit(key[0])) + { + return false; + } + int ats = 0; + const int n = key.length(); + for (int i = 0; i < n; ++i) + { + char c = key[i]; + if (!IsNumberOrDigit(c) && c != '_' && c != '-' && c != '@' && c != '*' && c != '/') + { + return false; + } + if ((c == '@') && (++ats > 1)) + { + return false; + } + } + return true; + } + + // TODO: IsValidValue + +private: + static bool IsNumberOrDigit(char c) { return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); } +}; + +} // namespace trace +} // namespace opentelemetry diff --git a/api/test/trace/BUILD b/api/test/trace/BUILD index a8e8507e4e..14debe4e12 100644 --- a/api/test/trace/BUILD +++ b/api/test/trace/BUILD @@ -17,6 +17,17 @@ otel_cc_benchmark( deps = ["//api"], ) +cc_test( + name = "span_context_test", + srcs = [ + "span_context_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "span_id_test", srcs = [ @@ -49,3 +60,14 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "trace_state_test", + srcs = [ + "trace_state_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/api/test/trace/span_context_test.cc b/api/test/trace/span_context_test.cc new file mode 100644 index 0000000000..3326a0c318 --- /dev/null +++ b/api/test/trace/span_context_test.cc @@ -0,0 +1,15 @@ +#include "opentelemetry/trace/span_context.h" + +#include + +namespace +{ + +using opentelemetry::trace::SpanContext; + +TEST(SpanContextTest, DefaultConstruction) +{ + SpanContext ctx; +} + +} // namespace diff --git a/api/test/trace/trace_state_test.cc b/api/test/trace/trace_state_test.cc new file mode 100644 index 0000000000..2dd9263f80 --- /dev/null +++ b/api/test/trace/trace_state_test.cc @@ -0,0 +1,17 @@ +#include "opentelemetry/trace/trace_state.h" + +#include + +namespace +{ + +using opentelemetry::trace::TraceState; + +TEST(TraceStateTest, DefaultConstruction) +{ + TraceState s; + EXPECT_FALSE(s.Get("key", nullptr)); + EXPECT_TRUE(s.empty()); + EXPECT_EQ(0, s.entries().size()); +} +} // namespace