Line data Source code
1 : #include "tag_propagation.h" 2 : 3 : #include <algorithm> 4 : #include <cstddef> 5 : #include <sstream> 6 : 7 : #include "error.h" 8 : #include "parse_util.h" 9 : 10 : namespace datadog { 11 : namespace tracing { 12 : 13 : // The following [eBNF][1] grammar describes the tag propagation encoding. 14 : // The grammar was copied from [an internal design document][2]. 15 : // 16 : // tagset = ( tag, { ",", tag } ) | ""; 17 : // tag = ( identifier - space or equal ), "=", identifier; 18 : // identifier = allowed characters, { allowed characters }; 19 : // allowed characters = ( ? ASCII characters 32-126 ? - "," ); 20 : // space or equal = " " | "="; 21 : // 22 : // That is, comma-separated "<key>=<value>" pairs. 23 : // 24 : // See `tag_propagation_test.cpp` for examples. 25 : // 26 : // [1]: https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form 27 : // [2]: 28 : // https://docs.google.com/document/d/1zeO6LGnvxk5XweObHAwJbK3SfK23z7jQzp7ozWJTa2A/edit#heading=h.yp07yuixga36 29 : 30 : namespace { 31 : 32 : // Insert into the specified `destination` a tag decoded from the specified 33 : // `entry`. Return an `Error` if an error occurs. 34 10042 : Expected<void> decode_tag( 35 : std::vector<std::pair<std::string, std::string>>& destination, 36 : StringView entry) { 37 10042 : const auto separator = std::find(entry.begin(), entry.end(), '='); 38 10042 : if (separator == entry.end()) { 39 2 : std::string message; 40 2 : message += "invalid key=value pair for encoded tag: missing \"=\" in: "; 41 2 : append(message, entry); 42 2 : return Error{Error::MALFORMED_TRACE_TAGS, std::move(message)}; 43 2 : } 44 : 45 10040 : const StringView key = range(entry.begin(), separator); 46 10040 : const StringView value = range(separator + 1, entry.end()); 47 10040 : destination.emplace_back(std::string(key), std::string(value)); 48 : 49 10040 : return nullopt; 50 : } 51 : 52 10015 : void append_tag(std::string& serialized_tags, StringView tag_key, 53 : StringView tag_value) { 54 10015 : serialized_tags.append(tag_key.begin(), tag_key.end()); 55 10015 : serialized_tags += '='; 56 10015 : serialized_tags.append(tag_value.begin(), tag_value.end()); 57 10015 : } 58 : 59 : } // namespace 60 : 61 24 : Expected<std::vector<std::pair<std::string, std::string>>> decode_tags( 62 : StringView header_value) { 63 24 : std::vector<std::pair<std::string, std::string>> tags; 64 : 65 24 : auto iter = header_value.begin(); 66 24 : const auto end = header_value.end(); 67 24 : if (iter == end) { 68 : // An empty string means no tags. 69 3 : return tags; 70 : } 71 : 72 : decltype(iter) next; 73 : do { 74 10042 : next = std::find(iter, end, ','); 75 10042 : auto result = decode_tag(tags, range(iter, next)); 76 10042 : if (auto* error = result.if_error()) { 77 2 : std::string prefix; 78 2 : prefix += "Error decoding trace tags \""; 79 2 : append(prefix, header_value); 80 2 : prefix += "\": "; 81 2 : return error->with_prefix(prefix); 82 2 : } 83 10040 : iter = next + 1; 84 20082 : } while (next != end); 85 : 86 19 : return tags; 87 24 : } 88 : 89 12 : std::string encode_tags( 90 : const std::vector<std::pair<std::string, std::string>>& trace_tags) { 91 12 : std::string result; 92 12 : auto iter = trace_tags.begin(); 93 12 : if (iter == trace_tags.end()) { 94 2 : return result; 95 : } 96 : 97 10 : append_tag(result, iter->first, iter->second); 98 10015 : for (++iter; iter != trace_tags.end(); ++iter) { 99 10005 : result += ','; 100 10005 : append_tag(result, iter->first, iter->second); 101 : } 102 : 103 10 : return result; 104 0 : } 105 : 106 : } // namespace tracing 107 : } // namespace datadog