Line data Source code
1 : #include "span_matcher.h"
2 :
3 : #include <algorithm>
4 :
5 : #include "error.h"
6 : #include "glob.h"
7 : #include "json.hpp"
8 : #include "optional.h"
9 : #include "span_data.h"
10 :
11 : namespace datadog {
12 : namespace tracing {
13 : namespace {
14 :
15 163051 : bool is_match(StringView pattern, StringView subject) {
16 : // Since "*" is the default pattern, optimize for that case.
17 163051 : return pattern == "*" || glob_match(pattern, subject);
18 : }
19 :
20 : } // namespace
21 :
22 34 : nlohmann::json SpanMatcher::to_json() const {
23 442 : return nlohmann::json::object({
24 34 : {"service", service},
25 34 : {"name", name},
26 34 : {"resource", resource},
27 34 : {"tags", tags},
28 476 : });
29 : }
30 :
31 54352 : bool SpanMatcher::match(const SpanData& span) const {
32 108702 : return is_match(service, span.service) && is_match(name, span.name) &&
33 163043 : is_match(resource, span.resource) &&
34 54341 : std::all_of(tags.begin(), tags.end(), [&](const auto& entry) {
35 5 : const auto& [name, pattern] = entry;
36 5 : auto found = span.tags.find(name);
37 5 : return found != span.tags.end() && is_match(pattern, found->second);
38 54352 : });
39 : }
40 :
41 26 : Expected<SpanMatcher> SpanMatcher::from_json(const nlohmann::json& json) {
42 26 : SpanMatcher result;
43 :
44 26 : std::string type = json.type_name();
45 26 : if (type != "object") {
46 2 : std::string message;
47 2 : message += "A rule must be a JSON object, but this is of type \"";
48 2 : message += type;
49 2 : message += "\": ";
50 2 : message += json.dump();
51 2 : return Error{Error::RULE_WRONG_TYPE, std::move(message)};
52 2 : }
53 :
54 : const auto check_property_type =
55 18 : [&](StringView property, const nlohmann::json& value,
56 : StringView expected_type) -> Optional<Error> {
57 18 : type = value.type_name();
58 18 : if (type == expected_type) {
59 10 : return nullopt;
60 : }
61 :
62 8 : std::string message;
63 8 : message += "Rule property \"";
64 8 : append(message, property);
65 8 : message += "\" should have type \"";
66 8 : append(message, expected_type);
67 8 : message += "\", but has type \"";
68 8 : message += type;
69 8 : message += "\": ";
70 8 : message += value.dump();
71 8 : message += " in rule ";
72 8 : message += json.dump();
73 8 : return Error{Error::RULE_PROPERTY_WRONG_TYPE, std::move(message)};
74 8 : };
75 :
76 46 : for (const auto& [key, value] : json.items()) {
77 32 : if (key == "service") {
78 3 : if (auto error = check_property_type(key, value, "string")) {
79 2 : return *error;
80 3 : }
81 1 : result.service = value;
82 29 : } else if (key == "name") {
83 6 : if (auto error = check_property_type(key, value, "string")) {
84 2 : return *error;
85 6 : }
86 4 : result.name = value;
87 23 : } else if (key == "resource") {
88 4 : if (auto error = check_property_type(key, value, "string")) {
89 2 : return *error;
90 4 : }
91 2 : result.resource = value;
92 19 : } else if (key == "tags") {
93 5 : if (auto error = check_property_type(key, value, "object")) {
94 2 : return *error;
95 5 : }
96 4 : for (const auto& [tag_name, tag_value] : value.items()) {
97 3 : type = tag_value.type_name();
98 3 : if (type != "string") {
99 2 : std::string message;
100 2 : message += "Rule tag pattern must be a string, but ";
101 2 : message += tag_value.dump();
102 2 : message += " has type \"";
103 2 : message += type;
104 2 : message += "\" for tag named \"";
105 2 : message += tag_name;
106 2 : message += "\" in rule: ";
107 2 : message += json.dump();
108 2 : return Error{Error::RULE_TAG_WRONG_TYPE, std::move(message)};
109 2 : }
110 1 : result.tags.emplace(std::string(tag_name), std::string(tag_value));
111 5 : }
112 : } else {
113 : // Unknown properties are OK. `SpanMatcher` is used as a base class for
114 : // trace sampling rules and span sampling rules. Those derived types
115 : // will have additional properties in their JSON representations.
116 : }
117 34 : }
118 :
119 14 : return result;
120 26 : }
121 :
122 : } // namespace tracing
123 : } // namespace datadog
|