Line data Source code
1 : #include "tracer_telemetry.h"
2 :
3 : #include "logger.h"
4 : #include "platform_util.h"
5 : #include "span_defaults.h"
6 : #include "version.h"
7 :
8 : namespace datadog {
9 : namespace tracing {
10 :
11 607 : TracerTelemetry::TracerTelemetry(bool enabled, const Clock& clock,
12 : const std::shared_ptr<Logger>& logger,
13 607 : const TracerSignature& tracer_signature)
14 607 : : enabled_(enabled),
15 607 : clock_(clock),
16 607 : logger_(logger),
17 607 : tracer_signature_(tracer_signature),
18 1214 : hostname_(get_hostname().value_or("hostname-unavailable")) {
19 607 : if (enabled_) {
20 : // Register all the metrics that we're tracking by adding them to the
21 : // metrics_snapshots_ container. This allows for simpler iteration logic
22 : // when using the values in `generate-metrics` messages.
23 196 : metrics_snapshots_.emplace_back(metrics_.tracer.spans_created,
24 392 : MetricSnapshot{});
25 196 : metrics_snapshots_.emplace_back(metrics_.tracer.spans_finished,
26 392 : MetricSnapshot{});
27 196 : metrics_snapshots_.emplace_back(metrics_.tracer.trace_segments_created_new,
28 392 : MetricSnapshot{});
29 392 : metrics_snapshots_.emplace_back(
30 196 : metrics_.tracer.trace_segments_created_continued, MetricSnapshot{});
31 196 : metrics_snapshots_.emplace_back(metrics_.tracer.trace_segments_closed,
32 392 : MetricSnapshot{});
33 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.requests,
34 392 : MetricSnapshot{});
35 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.responses_1xx,
36 392 : MetricSnapshot{});
37 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.responses_2xx,
38 392 : MetricSnapshot{});
39 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.responses_3xx,
40 392 : MetricSnapshot{});
41 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.responses_4xx,
42 392 : MetricSnapshot{});
43 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.responses_5xx,
44 392 : MetricSnapshot{});
45 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.errors_timeout,
46 392 : MetricSnapshot{});
47 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.errors_network,
48 392 : MetricSnapshot{});
49 196 : metrics_snapshots_.emplace_back(metrics_.trace_api.errors_status_code,
50 392 : MetricSnapshot{});
51 : }
52 607 : }
53 :
54 40 : nlohmann::json TracerTelemetry::generate_telemetry_body(
55 : std::string request_type) {
56 40 : std::time_t tracer_time = std::chrono::duration_cast<std::chrono::seconds>(
57 40 : clock_().wall.time_since_epoch())
58 40 : .count();
59 40 : seq_id_++;
60 1000 : return nlohmann::json::object({
61 : {"api_version", "v2"},
62 40 : {"seq_id", seq_id_},
63 : {"request_type", request_type},
64 : {"tracer_time", tracer_time},
65 40 : {"runtime_id", tracer_signature_.runtime_id().string()},
66 40 : {"debug", debug_},
67 : {"application",
68 1040 : nlohmann::json::object({
69 80 : {"service_name", tracer_signature_.default_service()},
70 80 : {"env", tracer_signature_.default_environment()},
71 80 : {"tracer_version", tracer_signature_.library_version()},
72 80 : {"language_name", tracer_signature_.library_language()},
73 80 : {"language_version", tracer_signature_.library_language_version()},
74 : })},
75 : // TODO: host information (os, os_version, kernel, etc)
76 240 : {"host", nlohmann::json::object({
77 40 : {"hostname", hostname_},
78 : })},
79 1240 : });
80 : }
81 :
82 19 : std::string TracerTelemetry::app_started() {
83 38 : auto telemetry_body = generate_telemetry_body("app-started");
84 : // TODO: environment variables or finalized config details
85 114 : telemetry_body["payload"] = nlohmann::json::object({
86 38 : {"configuration", nlohmann::json::array({})},
87 :
88 19 : });
89 19 : auto app_started_payload = telemetry_body.dump();
90 38 : return app_started_payload;
91 19 : }
92 :
93 19 : void TracerTelemetry::capture_metrics() {
94 19 : std::time_t timepoint = std::chrono::duration_cast<std::chrono::seconds>(
95 19 : clock_().wall.time_since_epoch())
96 19 : .count();
97 285 : for (auto& m : metrics_snapshots_) {
98 266 : auto value = m.first.get().capture_and_reset_value();
99 266 : if (value == 0) {
100 230 : continue;
101 : }
102 36 : m.second.emplace_back(timepoint, value);
103 : }
104 19 : }
105 :
106 2 : std::string TracerTelemetry::heartbeat_and_telemetry() {
107 2 : auto batch_payloads = nlohmann::json::array();
108 :
109 8 : auto heartbeat = nlohmann::json::object({
110 : {"request_type", "app-heartbeat"},
111 4 : });
112 2 : batch_payloads.emplace_back(std::move(heartbeat));
113 :
114 2 : auto metrics = nlohmann::json::array();
115 30 : for (auto& m : metrics_snapshots_) {
116 28 : auto& metric = m.first.get();
117 28 : auto& points = m.second;
118 28 : if (!points.empty()) {
119 1 : auto type = metric.type();
120 1 : if (type == "count") {
121 23 : metrics.emplace_back(nlohmann::json::object({
122 2 : {"metric", metric.name()},
123 2 : {"tags", metric.tags()},
124 2 : {"type", metric.type()},
125 : {"points", points},
126 2 : {"common", metric.common()},
127 : }));
128 0 : } else if (type == "gauge") {
129 : // gauge metrics have a interval
130 0 : metrics.emplace_back(nlohmann::json::object({
131 0 : {"metric", metric.name()},
132 0 : {"tags", metric.tags()},
133 0 : {"type", metric.type()},
134 0 : {"interval", 10},
135 : {"points", points},
136 0 : {"common", metric.common()},
137 : }));
138 : }
139 1 : }
140 28 : points.clear();
141 : }
142 :
143 2 : if (!metrics.empty()) {
144 7 : auto generate_metrics = nlohmann::json::object({
145 : {"request_type", "generate-metrics"},
146 11 : {"payload", nlohmann::json::object({
147 : {"namespace", "tracers"},
148 : {"series", metrics},
149 : })},
150 4 : });
151 1 : batch_payloads.emplace_back(std::move(generate_metrics));
152 1 : }
153 :
154 4 : auto telemetry_body = generate_telemetry_body("message-batch");
155 2 : telemetry_body["payload"] = batch_payloads;
156 2 : auto message_batch_payload = telemetry_body.dump();
157 4 : return message_batch_payload;
158 2 : }
159 :
160 19 : std::string TracerTelemetry::app_closing() {
161 19 : auto batch_payloads = nlohmann::json::array();
162 :
163 76 : auto app_closing = nlohmann::json::object({
164 : {"request_type", "app-closing"},
165 38 : });
166 19 : batch_payloads.emplace_back(std::move(app_closing));
167 :
168 19 : auto metrics = nlohmann::json::array();
169 285 : for (auto& m : metrics_snapshots_) {
170 266 : auto& metric = m.first.get();
171 266 : auto& points = m.second;
172 266 : if (!points.empty()) {
173 35 : auto type = metric.type();
174 35 : if (type == "count") {
175 805 : metrics.emplace_back(nlohmann::json::object({
176 70 : {"metric", metric.name()},
177 70 : {"tags", metric.tags()},
178 70 : {"type", metric.type()},
179 : {"points", points},
180 70 : {"common", metric.common()},
181 : }));
182 0 : } else if (type == "gauge") {
183 : // gauge metrics have a interval
184 0 : metrics.emplace_back(nlohmann::json::object({
185 0 : {"metric", metric.name()},
186 0 : {"tags", metric.tags()},
187 0 : {"type", metric.type()},
188 0 : {"interval", 10},
189 : {"points", points},
190 0 : {"common", metric.common()},
191 : }));
192 : }
193 35 : }
194 266 : points.clear();
195 : }
196 :
197 19 : if (!metrics.empty()) {
198 49 : auto generate_metrics = nlohmann::json::object({
199 : {"request_type", "generate-metrics"},
200 77 : {"payload", nlohmann::json::object({
201 : {"namespace", "tracers"},
202 : {"series", metrics},
203 : })},
204 28 : });
205 7 : batch_payloads.emplace_back(std::move(generate_metrics));
206 7 : }
207 :
208 38 : auto telemetry_body = generate_telemetry_body("message-batch");
209 19 : telemetry_body["payload"] = batch_payloads;
210 19 : auto message_batch_payload = telemetry_body.dump();
211 38 : return message_batch_payload;
212 19 : }
213 :
214 : } // namespace tracing
215 : } // namespace datadog
|