.. _program_listing_file_include_opentelemetry_trace_trace_state.h: Program Listing for File trace_state.h ====================================== |exhale_lsh| :ref:`Return to documentation for file ` (``include/opentelemetry/trace/trace_state.h``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // 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 #include #if (__GNUC__ == 4 && (__GNUC_MINOR__ == 8)) # define HAVE_WORKING_REGEX 0 #else # define HAVE_WORKING_REGEX 1 #endif #include "opentelemetry/common/kv_properties.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace trace { class TraceState { public: static constexpr int kKeyMaxSize = 256; static constexpr int kValueMaxSize = 256; static constexpr int kMaxKeyValuePairs = 32; static constexpr auto kKeyValueSeparator = '='; static constexpr auto kMembersSeparator = ','; static nostd::shared_ptr GetDefault() { static nostd::shared_ptr ts{new TraceState()}; return ts; } static nostd::shared_ptr FromHeader(nostd::string_view header) { common::KeyValueStringTokenizer kv_str_tokenizer(header); size_t cnt = kv_str_tokenizer.NumTokens(); // upper bound on number of kv pairs if (cnt > kMaxKeyValuePairs) { cnt = kMaxKeyValuePairs; } nostd::shared_ptr ts(new TraceState(cnt)); bool kv_valid; nostd::string_view key, value; while (kv_str_tokenizer.next(kv_valid, key, value) && ts->kv_properties_->Size() < cnt) { if (kv_valid == false) { return GetDefault(); } if (!IsValidKey(key) || !IsValidValue(value)) { // invalid header. return empty TraceState ts->kv_properties_.reset(new opentelemetry::common::KeyValueProperties()); break; } ts->kv_properties_->AddEntry(key, value); } return ts; } std::string ToHeader() { std::string header_s; bool first = true; kv_properties_->GetAllEntries( [&header_s, &first](nostd::string_view key, nostd::string_view value) noexcept { if (!first) { header_s.append(","); } else { first = false; } header_s.append(std::string(key.data(), key.size())); header_s.append(1, kKeyValueSeparator); header_s.append(std::string(value.data(), value.size())); return true; }); return header_s; } bool Get(nostd::string_view key, std::string &value) const noexcept { if (!IsValidKey(key)) { return false; } return kv_properties_->GetValue(key, value); } nostd::shared_ptr Set(const nostd::string_view &key, const nostd::string_view &value) { auto curr_size = kv_properties_->Size(); if (!IsValidKey(key) || !IsValidValue(value)) { // max size reached or invalid key/value. Returning empty TraceState return TraceState::GetDefault(); } auto allocate_size = curr_size; if (curr_size < kMaxKeyValuePairs) { allocate_size += 1; } nostd::shared_ptr ts(new TraceState(allocate_size)); if (curr_size < kMaxKeyValuePairs) { // add new field first ts->kv_properties_->AddEntry(key, value); } // add rest of the fields. kv_properties_->GetAllEntries([&ts](nostd::string_view key, nostd::string_view value) { ts->kv_properties_->AddEntry(key, value); return true; }); return ts; } nostd::shared_ptr Delete(const nostd::string_view &key) { if (!IsValidKey(key)) { return TraceState::GetDefault(); } auto curr_size = kv_properties_->Size(); auto allocate_size = curr_size; std::string unused; if (kv_properties_->GetValue(key, unused)) { allocate_size -= 1; } nostd::shared_ptr ts(new TraceState(allocate_size)); kv_properties_->GetAllEntries( [&ts, &key](nostd::string_view e_key, nostd::string_view e_value) { if (key != e_key) ts->kv_properties_->AddEntry(e_key, e_value); return true; }); return ts; } // Returns true if there are no keys, false otherwise. bool Empty() const noexcept { return kv_properties_->Size() == 0; } // @return all key-values entris by repeatedly invoking the function reference passed as argument // for each entry bool GetAllEntries( nostd::function_ref callback) const noexcept { return kv_properties_->GetAllEntries(callback); } static bool IsValidKey(nostd::string_view key) { #if HAVE_WORKING_REGEX return IsValidKeyRegEx(key); #else return IsValidKeyNonRegEx(key); #endif } static bool IsValidValue(nostd::string_view value) { #if HAVE_WORKING_REGEX return IsValidValueRegEx(value); #else return IsValidValueNonRegEx(value); #endif } private: TraceState() : kv_properties_(new opentelemetry::common::KeyValueProperties()){}; TraceState(size_t size) : kv_properties_(new opentelemetry::common::KeyValueProperties(size)){}; static nostd::string_view TrimString(nostd::string_view str, size_t left, size_t right) { while (str[static_cast(right)] == ' ' && left < right) { right--; } while (str[static_cast(left)] == ' ' && left < right) { left++; } return str.substr(left, right - left + 1); } static bool IsValidKeyRegEx(nostd::string_view key) { static std::regex reg_key("^[a-z0-9][a-z0-9*_\\-/]{0,255}$"); static std::regex reg_key_multitenant( "^[a-z0-9][a-z0-9*_\\-/]{0,240}(@)[a-z0-9][a-z0-9*_\\-/]{0,13}$"); std::string key_s(key.data(), key.size()); if (std::regex_match(key_s, reg_key) || std::regex_match(key_s, reg_key_multitenant)) { return true; } return false; } static bool IsValidValueRegEx(nostd::string_view value) { // Hex 0x20 to 0x2B, 0x2D to 0x3C, 0x3E to 0x7E static std::regex reg_value( "^[\\x20-\\x2B\\x2D-\\x3C\\x3E-\\x7E]{0,255}[\\x21-\\x2B\\x2D-\\x3C\\x3E-\\x7E]$"); // Need to benchmark without regex, as a string object is created here. return std::regex_match(std::string(value.data(), value.size()), reg_value); } static bool IsValidKeyNonRegEx(nostd::string_view key) { if (key.empty() || key.size() > kKeyMaxSize || !IsLowerCaseAlphaOrDigit(key[0])) { return false; } int ats = 0; for (const char c : key) { if (!IsLowerCaseAlphaOrDigit(c) && c != '_' && c != '-' && c != '@' && c != '*' && c != '/') { return false; } if ((c == '@') && (++ats > 1)) { return false; } } return true; } static bool IsValidValueNonRegEx(nostd::string_view value) { if (value.empty() || value.size() > kValueMaxSize) { return false; } for (const char c : value) { if (c < ' ' || c > '~' || c == ',' || c == '=') { return false; } } return true; } static bool IsLowerCaseAlphaOrDigit(char c) { return isdigit(c) || islower(c); } private: // Store entries in a C-style array to avoid using std::array or std::vector. nostd::unique_ptr kv_properties_; }; } // namespace trace OPENTELEMETRY_END_NAMESPACE