]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* |
2 | * This file is open source software, licensed to you under the terms | |
3 | * of the Apache License, Version 2.0 (the "License"). See the NOTICE file | |
4 | * distributed with this work for additional information regarding copyright | |
5 | * ownership. You may not use this file except in compliance with the License. | |
6 | * | |
7 | * You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, | |
12 | * software distributed under the License is distributed on an | |
13 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | * KIND, either express or implied. See the License for the | |
15 | * specific language governing permissions and limitations | |
16 | * under the License. | |
17 | */ | |
18 | /* | |
19 | * Copyright (C) 2014 Cloudius Systems, Ltd. | |
20 | */ | |
21 | ||
22 | #include <functional> | |
23 | #include <unordered_map> | |
24 | #include <forward_list> | |
25 | #include <utility> | |
26 | #include <string> | |
27 | #include <map> | |
28 | #include <iostream> | |
29 | #include <unordered_map> | |
30 | ||
f67539c2 | 31 | #include <seastar/core/seastar.hh> |
11fdf7f2 TL |
32 | #include <seastar/core/scollectd_api.hh> |
33 | #include <seastar/core/metrics_api.hh> | |
34 | #include <seastar/core/byteorder.hh> | |
9f95a23c | 35 | #include <seastar/core/print.hh> |
11fdf7f2 TL |
36 | |
37 | #include "core/scollectd-impl.hh" | |
38 | ||
39 | namespace seastar { | |
40 | ||
9f95a23c TL |
41 | void scollectd::type_instance_id::truncate(sstring& field, const char* field_desc) { |
42 | if (field.size() > max_collectd_field_text_len) { | |
43 | auto suffix_len = std::ceil(std::log10(++_next_truncated_idx)) + 1; | |
44 | sstring new_field(seastar::format( | |
45 | "{}~{:d}", sstring(field.data(), max_collectd_field_text_len - suffix_len), _next_truncated_idx)); | |
46 | ||
47 | logger.warn("Truncating \"{}\" to {} chars: \"{}\" -> \"{}\"", field_desc, max_collectd_field_text_len, field, | |
48 | new_field); | |
49 | field = std::move(new_field); | |
50 | } | |
51 | } | |
52 | ||
11fdf7f2 TL |
53 | bool scollectd::type_instance_id::operator<( |
54 | const scollectd::type_instance_id& id2) const { | |
55 | auto& id1 = *this; | |
56 | return std::tie(id1.plugin(), id1.plugin_instance(), id1.type(), | |
57 | id1.type_instance()) | |
58 | < std::tie(id2.plugin(), id2.plugin_instance(), id2.type(), | |
59 | id2.type_instance()); | |
60 | } | |
61 | bool scollectd::type_instance_id::operator==( | |
62 | const scollectd::type_instance_id & id2) const { | |
63 | auto& id1 = *this; | |
64 | return std::tie(id1.plugin(), id1.plugin_instance(), id1.type(), | |
65 | id1.type_instance()) | |
66 | == std::tie(id2.plugin(), id2.plugin_instance(), id2.type(), | |
67 | id2.type_instance()); | |
68 | } | |
69 | ||
70 | namespace scollectd { | |
71 | ||
72 | ::seastar::logger logger("scollectd"); | |
73 | thread_local unsigned type_instance_id::_next_truncated_idx = 0; | |
74 | ||
75 | registration::~registration() { | |
76 | unregister(); | |
77 | } | |
78 | ||
79 | registration::registration(const type_instance_id& id) | |
80 | : _id(id), _impl(seastar::metrics::impl::get_local_impl()) { | |
81 | } | |
82 | ||
83 | registration::registration(type_instance_id&& id) | |
84 | : _id(std::move(id)), _impl(seastar::metrics::impl::get_local_impl()) { | |
85 | } | |
86 | ||
87 | seastar::metrics::impl::metric_id to_metrics_id(const type_instance_id & id) { | |
88 | return seastar::metrics::impl::metric_id(id.plugin(), id.type_instance(), | |
f67539c2 | 89 | {{seastar::metrics::shard_label.name(), seastar::metrics::impl::shard()}}); |
11fdf7f2 TL |
90 | } |
91 | ||
92 | ||
93 | const plugin_instance_id per_cpu_plugin_instance("#cpu"); | |
94 | ||
95 | static const size_t payload_size = 1024; | |
96 | ||
97 | enum class part_type : uint16_t { | |
98 | Host = 0x0000, // The name of the host to associate with subsequent data values | |
99 | Time = 0x0001, // Time Numeric The timestamp to associate with subsequent data values, unix time format (seconds since epoch) | |
100 | TimeHr = 0x0008, // Time (high resolution) Numeric The timestamp to associate with subsequent data values. Time is defined in 2–30 seconds since epoch. New in Version 5.0. | |
101 | Plugin = 0x0002, // Plugin String The plugin name to associate with subsequent data values, e.g. "cpu" | |
102 | PluginInst = 0x0003, // Plugin instance String The plugin instance name to associate with subsequent data values, e.g. "1" | |
103 | Type = 0x0004, // Type String The type name to associate with subsequent data values, e.g. "cpu" | |
104 | TypeInst = 0x0005, // Type instance String The type instance name to associate with subsequent data values, e.g. "idle" | |
105 | Values = 0x0006, // Values other Data values, see above | |
106 | Interval = 0x0007, // Interval Numeric Interval used to set the "step" when creating new RRDs unless rrdtool plugin forces StepSize. Also used to detect values that have timed out. | |
107 | IntervalHr = 0x0009, // Interval (high resolution) Numeric The interval in which subsequent data values are collected. The interval is given in 2–30 seconds. New in Version 5.0. | |
108 | Message = 0x0100, // Message (notifications) String | |
109 | Severity = 0x0101, // Severity Numeric | |
110 | Signature = 0x0200, // Signature (HMAC-SHA-256) other (todo) | |
111 | Encryption = 0x0210, // Encryption (AES-256/OFB | |
112 | }; | |
113 | ||
114 | // "Time is defined in 2^–30 seconds since epoch. New in Version 5.0." | |
115 | typedef std::chrono::duration<uint64_t, std::ratio<1, 0x40000000>> collectd_hres_duration; | |
116 | ||
117 | // yet another writer type, this one to construct collectd network | |
118 | // protocol data. | |
119 | struct cpwriter { | |
120 | typedef std::array<char, payload_size> buffer_type; | |
121 | typedef buffer_type::iterator mark_type; | |
122 | typedef buffer_type::const_iterator const_mark_type; | |
123 | ||
124 | buffer_type _buf; | |
125 | mark_type _pos; | |
126 | bool _overflow = false; | |
127 | ||
128 | std::unordered_map<uint16_t, sstring> _cache; | |
129 | ||
130 | cpwriter() | |
131 | : _pos(_buf.begin()) { | |
132 | } | |
133 | mark_type mark() const { | |
134 | return _pos; | |
135 | } | |
136 | bool overflow() const { | |
137 | return _overflow; | |
138 | } | |
139 | void reset(mark_type m) { | |
140 | _pos = m; | |
141 | _overflow = false; | |
142 | } | |
143 | size_t size() const { | |
144 | return std::distance(_buf.begin(), const_mark_type(_pos)); | |
145 | } | |
146 | bool empty() const { | |
147 | return _pos == _buf.begin(); | |
148 | } | |
149 | void clear() { | |
150 | reset(_buf.begin()); | |
151 | _cache.clear(); | |
152 | _overflow = false; | |
153 | } | |
154 | const char * data() const { | |
155 | return &_buf.at(0); | |
156 | } | |
157 | char * data() { | |
158 | return &_buf.at(0); | |
159 | } | |
160 | cpwriter& check(size_t sz) { | |
161 | size_t av = std::distance(_pos, _buf.end()); | |
162 | _overflow |= av < sz; | |
163 | return *this; | |
164 | } | |
165 | ||
166 | sstring get_type_instance(const seastar::metrics::impl::metric_id & id) { | |
167 | if (id.labels().empty()) { | |
168 | return id.name(); | |
169 | } | |
170 | sstring res = id.name(); | |
171 | for (auto i : id.labels()) { | |
f67539c2 | 172 | if (i.first != seastar::metrics::shard_label.name()) { |
11fdf7f2 TL |
173 | res += "-" + i.second; |
174 | } | |
175 | } | |
176 | return res; | |
177 | } | |
178 | explicit operator bool() const { | |
179 | return !_overflow; | |
180 | } | |
181 | bool operator!() const { | |
182 | return !operator bool(); | |
183 | } | |
184 | template<typename _Iter> | |
185 | cpwriter & write(_Iter s, _Iter e) { | |
186 | if (check(std::distance(s, e))) { | |
187 | _pos = std::copy(s, e, _pos); | |
188 | } | |
189 | return *this; | |
190 | } | |
191 | template<typename T> | |
192 | typename std::enable_if<std::is_integral<T>::value, cpwriter &>::type write( | |
193 | const T & t) { | |
194 | T tmp = net::hton(t); | |
195 | auto * p = reinterpret_cast<const uint8_t *>(&tmp); | |
196 | auto * e = p + sizeof(T); | |
197 | write(p, e); | |
198 | return *this; | |
199 | } | |
200 | template<typename T> | |
201 | typename std::enable_if<std::is_integral<T>::value, cpwriter &>::type write_le(const T & t) { | |
202 | T tmp = cpu_to_le(t); | |
203 | auto * p = reinterpret_cast<const uint8_t *>(&tmp); | |
204 | auto * e = p + sizeof(T); | |
205 | write(p, e); | |
206 | return *this; | |
207 | } | |
208 | void write_value(const seastar::metrics::impl::metric_value& v) { | |
209 | switch (v.type()) { | |
210 | case data_type::GAUGE: { | |
211 | double tmpd = v.d(); | |
212 | uint64_t tmpi; | |
213 | std::copy_n(reinterpret_cast<const char*>(&tmpd), 8, reinterpret_cast<char*>(&tmpi)); | |
214 | write_le(tmpi); | |
215 | break; | |
216 | } | |
11fdf7f2 | 217 | case data_type::DERIVE: |
20effc67 TL |
218 | write(v.i()); // signed int 64, big endian |
219 | break; | |
220 | case data_type::COUNTER: | |
11fdf7f2 | 221 | case data_type::ABSOLUTE: |
20effc67 | 222 | write(v.ui()); // unsigned int 64, big endian |
11fdf7f2 TL |
223 | break; |
224 | default: | |
225 | assert(0); | |
226 | } | |
227 | } | |
228 | cpwriter & write(const sstring & s) { | |
229 | write(s.begin(), s.end() + 1); // include \0 | |
230 | return *this; | |
231 | } | |
232 | cpwriter & put(part_type type, const sstring & s) { | |
233 | write(uint16_t(type)); | |
234 | write(uint16_t(4 + s.size() + 1)); // include \0 | |
235 | write(s); // include \0 | |
236 | return *this; | |
237 | } | |
238 | cpwriter & put_cached(part_type type, const sstring & s) { | |
239 | auto & cached = _cache[uint16_t(type)]; | |
240 | if (cached != s) { | |
241 | put(type, s); | |
242 | cached = s; | |
243 | } | |
244 | return *this; | |
245 | } | |
246 | template<typename T> | |
247 | typename std::enable_if<std::is_integral<T>::value, cpwriter &>::type put( | |
248 | part_type type, T t) { | |
249 | write(uint16_t(type)); | |
250 | write(uint16_t(4 + sizeof(t))); | |
251 | write(t); | |
252 | return *this; | |
253 | } | |
254 | cpwriter & put(part_type type, const value_list & v) { | |
255 | auto s = v.size(); | |
256 | auto sz = 6 + s + s * sizeof(uint64_t); | |
257 | if (check(sz)) { | |
258 | write(uint16_t(type)); | |
259 | write(uint16_t(sz)); | |
260 | write(uint16_t(s)); | |
261 | v.types(reinterpret_cast<data_type *>(&(*_pos))); | |
262 | _pos += s; | |
263 | v.values(reinterpret_cast<net::packed<uint64_t> *>(&(*_pos))); | |
264 | _pos += s * sizeof(uint64_t); | |
265 | } | |
266 | return *this; | |
267 | } | |
268 | ||
269 | cpwriter & put(part_type type, const seastar::metrics::impl::metric_value & v) { | |
270 | auto sz = 7 + sizeof(uint64_t); | |
271 | if (check(sz)) { | |
272 | write(uint16_t(type)); | |
273 | write(uint16_t(sz)); | |
274 | write(uint16_t(1)); | |
275 | write(static_cast<uint8_t>(v.type())); | |
276 | write_value(v); | |
277 | } | |
278 | return *this; | |
279 | } | |
f67539c2 | 280 | cpwriter & put(const sstring & host, const seastar::metrics::impl::metric_id & id, const type_id& type) { |
11fdf7f2 TL |
281 | const auto ts = std::chrono::system_clock::now().time_since_epoch(); |
282 | const auto lrts = | |
283 | std::chrono::duration_cast<std::chrono::seconds>(ts).count(); | |
284 | ||
285 | put_cached(part_type::Host, host); | |
286 | put(part_type::Time, uint64_t(lrts)); | |
287 | // Seems hi-res timestamp does not work very well with | |
288 | // at the very least my default collectd in fedora (or I did it wrong?) | |
289 | // Use lo-res ts for now, it is probably quite sufficient. | |
290 | put_cached(part_type::Plugin, id.group_name()); | |
291 | // Optional | |
292 | put_cached(part_type::PluginInst, | |
293 | id.instance_id() == per_cpu_plugin_instance ? | |
f67539c2 TL |
294 | to_sstring(this_shard_id()) : id.instance_id()); |
295 | put_cached(part_type::Type, type); | |
11fdf7f2 TL |
296 | // Optional |
297 | put_cached(part_type::TypeInst, get_type_instance(id)); | |
298 | return *this; | |
299 | } | |
300 | cpwriter & put(const sstring & host, | |
301 | const duration & period, | |
302 | const type_instance_id & id, const value_list & v) { | |
303 | const auto ps = std::chrono::duration_cast<collectd_hres_duration>( | |
304 | period).count(); | |
f67539c2 | 305 | put(host, to_metrics_id(id), id.type()); |
11fdf7f2 TL |
306 | put(part_type::Values, v); |
307 | if (ps != 0) { | |
308 | put(part_type::IntervalHr, ps); | |
309 | } | |
310 | return *this; | |
311 | } | |
312 | ||
313 | cpwriter & put(const sstring & host, | |
314 | const duration & period, | |
f67539c2 | 315 | const type_id& type, |
11fdf7f2 TL |
316 | const seastar::metrics::impl::metric_id & id, const seastar::metrics::impl::metric_value & v) { |
317 | const auto ps = std::chrono::duration_cast<collectd_hres_duration>( | |
318 | period).count(); | |
f67539c2 | 319 | put(host, id, type); |
11fdf7f2 TL |
320 | put(part_type::Values, v); |
321 | if (ps != 0) { | |
322 | put(part_type::IntervalHr, ps); | |
323 | } | |
324 | return *this; | |
325 | } | |
326 | }; | |
327 | ||
328 | void impl::add_polled(const type_instance_id & id, | |
329 | const shared_ptr<value_list> & values, bool enable) { | |
330 | // do nothing | |
331 | // add_polled is now implemented on the metrics layer | |
332 | ||
333 | } | |
334 | ||
335 | void impl::remove_polled(const type_instance_id & id) { | |
336 | seastar::metrics::impl::unregister_metric(to_metrics_id(id)); | |
337 | } | |
338 | ||
339 | // explicitly send a type_instance value list (outside polling) | |
340 | future<> impl::send_metric(const type_instance_id & id, | |
341 | const value_list & values) { | |
342 | if (values.empty()) { | |
343 | return make_ready_future(); | |
344 | } | |
345 | cpwriter out; | |
346 | out.put(_host, duration(), id, values); | |
347 | return _chan.send(_addr, net::packet(out.data(), out.size())); | |
348 | } | |
349 | ||
350 | future<> impl::send_notification(const type_instance_id & id, | |
351 | const sstring & msg) { | |
352 | cpwriter out; | |
f67539c2 | 353 | out.put(_host, to_metrics_id(id), id.type()); |
11fdf7f2 TL |
354 | out.put(part_type::Message, msg); |
355 | return _chan.send(_addr, net::packet(out.data(), out.size())); | |
356 | } | |
357 | ||
358 | // initiates actual value polling -> send to target "loop" | |
359 | void impl::start(const sstring & host, const ipv4_addr & addr, const duration period) { | |
360 | _period = period; | |
361 | _addr = addr; | |
362 | _host = host; | |
f67539c2 | 363 | _chan = make_udp_channel(); |
11fdf7f2 TL |
364 | _timer.set_callback(std::bind(&impl::run, this)); |
365 | ||
366 | // dogfood ourselves | |
367 | namespace sm = seastar::metrics; | |
368 | ||
369 | _metrics.add_group("scollectd", { | |
370 | // total_bytes value:DERIVE:0:U | |
371 | sm::make_derive("total_bytes_sent", sm::description("total bytes sent"), _bytes), | |
372 | // total_requests value:DERIVE:0:U | |
373 | sm::make_derive("total_requests", sm::description("total requests"), _num_packets), | |
374 | // latency value:GAUGE:0:U | |
375 | sm::make_gauge("latency", sm::description("avrage latency"), _avg), | |
376 | // total_time_in_ms value:DERIVE:0:U | |
377 | sm::make_derive("total_time_in_ms", sm::description("total time in milliseconds"), _millis), | |
378 | // total_values value:DERIVE:0:U | |
379 | sm::make_gauge("total_values", sm::description("current number of values reported"), [this] {return values().size();}), | |
380 | // records value:GAUGE:0:U | |
381 | sm::make_gauge("records", sm::description("number of records reported"), [this] {return values().size();}), | |
382 | }); | |
383 | ||
9f95a23c TL |
384 | // FIXME: future is discarded |
385 | (void)send_notification( | |
11fdf7f2 TL |
386 | type_instance_id("scollectd", per_cpu_plugin_instance, |
387 | "network"), "daemon started"); | |
388 | arm(); | |
389 | } | |
390 | ||
391 | void impl::stop() { | |
392 | _timer.cancel(); | |
393 | _metrics.clear(); | |
394 | } | |
395 | ||
396 | ||
397 | void impl::arm() { | |
398 | if (_period != duration()) { | |
399 | _timer.arm(_period); | |
400 | } | |
401 | } | |
402 | ||
403 | void impl::run() { | |
404 | typedef size_t metric_family_id; | |
405 | typedef seastar::metrics::impl::value_vector::iterator value_iterator; | |
406 | typedef seastar::metrics::impl::metric_metadata_vector::iterator metadata_iterator; | |
f67539c2 | 407 | typedef std::tuple<metric_family_id, metadata_iterator, value_iterator, type_id, cpwriter> context; |
11fdf7f2 TL |
408 | |
409 | auto ctxt = make_lw_shared<context>(); | |
410 | foreign_ptr<shared_ptr<seastar::metrics::impl::values_copy>> vals = seastar::metrics::impl::get_values(); | |
411 | ||
412 | // note we're doing this unsynced since we assume | |
413 | // all registrations to this instance will be done on the | |
414 | // same cpu, and without interuptions (no wait-states) | |
415 | ||
416 | auto& values = vals->values; | |
417 | auto metadata = vals->metadata; | |
418 | std::get<metric_family_id>(*ctxt) = 0; | |
419 | if (values.size() > 0) { | |
420 | std::get<value_iterator>(*ctxt) = values[0].begin(); | |
421 | std::get<metadata_iterator>(*ctxt) = metadata->at(0).metrics.begin(); | |
f67539c2 | 422 | std::get<type_id>(*ctxt) = metadata->at(0).mf.inherit_type; |
11fdf7f2 TL |
423 | } |
424 | ||
425 | auto stop_when = [ctxt, metadata]() { | |
426 | auto done = std::get<metric_family_id>(*ctxt) == metadata->size(); | |
427 | return done; | |
428 | }; | |
429 | // append as many values as we can fit into a packet (1024 bytes) | |
430 | auto send_packet = [this, ctxt, &values, metadata]() mutable { | |
431 | auto start = steady_clock_type::now(); | |
432 | auto& mf = std::get<metric_family_id>(*ctxt); | |
433 | auto & md_iterator = std::get<metadata_iterator>(*ctxt); | |
434 | auto & i = std::get<value_iterator>(*ctxt); | |
435 | auto & out = std::get<cpwriter>(*ctxt); | |
436 | ||
437 | out.clear(); | |
438 | ||
439 | bool out_of_space = false; | |
440 | while (!out_of_space && mf < values.size()) { | |
441 | while (i != values[mf].end()) { | |
442 | if (i->type() == seastar::metrics::impl::data_type::HISTOGRAM) { | |
443 | ++i; | |
444 | ++md_iterator; | |
445 | continue; | |
446 | } | |
447 | auto m = out.mark(); | |
f67539c2 | 448 | out.put(_host, _period, std::get<type_id>(*ctxt), md_iterator->id, *i); |
11fdf7f2 TL |
449 | if (!out) { |
450 | out.reset(m); | |
451 | out_of_space = true; | |
452 | break; | |
453 | } | |
454 | ++i; | |
455 | ++md_iterator; | |
456 | } | |
457 | if (out_of_space) { | |
458 | break; | |
459 | } | |
460 | ++mf; | |
461 | if (mf < values.size()) { | |
462 | i = values[mf].begin(); | |
463 | md_iterator = metadata->at(mf).metrics.begin(); | |
f67539c2 | 464 | std::get<type_id>(*ctxt) = metadata->at(mf).mf.inherit_type; |
11fdf7f2 TL |
465 | } |
466 | } | |
467 | if (out.empty()) { | |
468 | return make_ready_future(); | |
469 | } | |
470 | return _chan.send(_addr, net::packet(out.data(), out.size())).then([start, ctxt, this]() { | |
471 | auto & out = std::get<cpwriter>(*ctxt); | |
472 | auto now = steady_clock_type::now(); | |
473 | // dogfood stats | |
474 | ++_num_packets; | |
475 | _millis += std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count(); | |
476 | _bytes += out.size(); | |
477 | _avg = double(_millis) / _num_packets; | |
478 | }).then_wrapped([] (auto&& f) { | |
479 | try { | |
480 | f.get(); | |
481 | } catch (std::exception & ex) { | |
482 | std::cout << "send failed: " << ex.what() << std::endl; | |
483 | } catch (...) { | |
484 | std::cout << "send failed: - unknown exception" << std::endl; | |
485 | } | |
486 | }); | |
487 | }; | |
9f95a23c TL |
488 | // No need to wait for future. |
489 | // The caller has to call impl::stop() to synchronize. | |
490 | (void)do_until(stop_when, send_packet).finally([this, vals = std::move(vals)]() mutable { | |
11fdf7f2 TL |
491 | arm(); |
492 | }); | |
493 | } | |
494 | ||
495 | std::vector<type_instance_id> impl::get_instance_ids() const { | |
496 | std::vector<type_instance_id> res; | |
497 | for (auto&& v: values()) { | |
498 | // Need to check for empty value_list, since unreg is two-stage. | |
499 | // Not an issue for most uses, but unit testing etc that would like | |
500 | // fully deterministic operation here would like us to only return | |
501 | // actually active ids | |
502 | for (auto i : v.second) { | |
503 | if (i.second) { | |
f67539c2 | 504 | res.emplace_back(i.second->get_id(), v.second.info().inherit_type); |
11fdf7f2 TL |
505 | } |
506 | } | |
507 | } | |
508 | return res; | |
509 | } | |
510 | ||
511 | void add_polled(const type_instance_id & id, | |
512 | const shared_ptr<value_list> & values, bool enabled) { | |
513 | get_impl().add_polled(id, values, enabled); | |
514 | } | |
515 | ||
516 | void remove_polled_metric(const type_instance_id & id) { | |
517 | get_impl().remove_polled(id); | |
518 | } | |
519 | ||
520 | future<> send_notification(const type_instance_id & id, | |
521 | const sstring & msg) { | |
522 | return get_impl().send_notification(id, msg); | |
523 | } | |
524 | ||
525 | future<> send_metric(const type_instance_id & id, | |
526 | const value_list & values) { | |
527 | return get_impl().send_metric(id, values); | |
528 | } | |
529 | ||
20effc67 TL |
530 | void configure(const options& opts) { |
531 | bool enable = opts.collectd.get_value(); | |
11fdf7f2 TL |
532 | if (!enable) { |
533 | return; | |
534 | } | |
20effc67 TL |
535 | auto addr = ipv4_addr(opts.collectd_address.get_value()); |
536 | auto period = std::chrono::milliseconds(opts.collectd_poll_period.get_value()); | |
11fdf7f2 | 537 | |
20effc67 | 538 | auto host = (opts.collectd_hostname.get_value() == "") |
11fdf7f2 | 539 | ? seastar::metrics::impl::get_local_impl()->get_config().hostname |
20effc67 | 540 | : sstring(opts.collectd_hostname.get_value()); |
11fdf7f2 TL |
541 | |
542 | // Now create send loops on each cpu | |
543 | for (unsigned c = 0; c < smp::count; c++) { | |
9f95a23c TL |
544 | // FIXME: future is discarded |
545 | (void)smp::submit_to(c, [=] () { | |
11fdf7f2 TL |
546 | get_impl().start(host, addr, period); |
547 | }); | |
548 | } | |
549 | } | |
550 | ||
20effc67 TL |
551 | options::options(program_options::option_group* parent_group) |
552 | : program_options::option_group(parent_group, "COLLECTD options") | |
553 | , collectd(*this, "collectd", false, | |
554 | "enable collectd daemon") | |
555 | , collectd_address(*this, "collectd-address", | |
556 | "239.192.74.66:25826", | |
557 | "address to send/broadcast metrics to") | |
558 | , collectd_poll_period(*this, "collectd-poll-period", | |
559 | 1000, | |
560 | "poll period - frequency of sending counter metrics (default: 1000ms, 0 disables)") | |
561 | , collectd_hostname(*this, "collectd-hostname", | |
562 | "", | |
563 | "Deprecated option, use metrics-hostname instead") | |
564 | { | |
11fdf7f2 TL |
565 | } |
566 | ||
567 | static seastar::metrics::impl::register_ref get_register(const scollectd::type_instance_id& i) { | |
568 | seastar::metrics::impl::metric_id id = to_metrics_id(i); | |
569 | return seastar::metrics::impl::get_value_map().at(id.full_name()).at(id.labels()); | |
570 | } | |
571 | ||
572 | std::vector<collectd_value> get_collectd_value( | |
573 | const scollectd::type_instance_id& id) { | |
574 | std::vector<collectd_value> vals; | |
575 | const seastar::metrics::impl::registered_metric& val = *get_register(id); | |
576 | vals.push_back(val()); | |
577 | return vals; | |
578 | } | |
579 | ||
580 | std::vector<scollectd::type_instance_id> get_collectd_ids() { | |
581 | return get_impl().get_instance_ids(); | |
582 | } | |
583 | ||
584 | bool is_enabled(const scollectd::type_instance_id& id) { | |
585 | return get_register(id)->is_enabled(); | |
586 | } | |
587 | ||
588 | void enable(const scollectd::type_instance_id& id, bool enable) { | |
589 | get_register(id)->set_enabled(enable); | |
590 | } | |
591 | ||
592 | type_instance_id plugin_instance_metrics::add_impl(const typed_value& v) { | |
593 | type_instance_id id(_plugin_id, _plugin_instance, v.type(), v.type_instance()); | |
594 | get_impl().add_polled(id, v.values()); | |
595 | return id; | |
596 | } | |
597 | ||
598 | void plugin_instance_metrics::add(const typed_value& v) { | |
599 | _registrations.emplace_back(add_impl(v)); | |
600 | } | |
601 | ||
602 | std::vector<type_instance_id> plugin_instance_metrics::bound_ids() const { | |
603 | std::vector<type_instance_id> res; | |
604 | res.reserve(_registrations.size()); | |
605 | std::transform(_registrations.begin(), _registrations.end(), std::back_inserter(res), [](const registration& r) { | |
606 | return r._id; | |
607 | }); | |
608 | return res; | |
609 | } | |
610 | ||
611 | type_id type_id_for(known_type t) { | |
612 | switch (t) { | |
613 | case known_type::absolute: | |
614 | return "absolute"; | |
615 | case known_type::backends: | |
616 | return "backends"; | |
617 | case known_type::bitrate: | |
618 | return "bitrate"; | |
619 | case known_type::blocked_clients: | |
620 | return "blocked_clients"; | |
621 | case known_type::bytes: | |
622 | return "bytes"; | |
623 | case known_type::cache_eviction: | |
624 | return "cache_eviction"; | |
625 | case known_type::cache_operation: | |
626 | return "cache_operation"; | |
627 | case known_type::cache_ratio: | |
628 | return "cache_ratio"; | |
629 | case known_type::cache_result: | |
630 | return "cache_result"; | |
631 | case known_type::cache_size: | |
632 | return "cache_size"; | |
633 | case known_type::capacity: | |
634 | return "capacity"; | |
635 | case known_type::changes_since_last_save: | |
636 | return "changes_since_last_save"; | |
637 | case known_type::charge: | |
638 | return "charge"; | |
639 | case known_type::clock_last_meas: | |
640 | return "clock_last_meas"; | |
641 | case known_type::clock_last_update: | |
642 | return "clock_last_update"; | |
643 | case known_type::clock_mode: | |
644 | return "clock_mode"; | |
645 | case known_type::clock_reachability: | |
646 | return "clock_reachability"; | |
647 | case known_type::clock_skew_ppm: | |
648 | return "clock_skew_ppm"; | |
649 | case known_type::clock_state: | |
650 | return "clock_state"; | |
651 | case known_type::clock_stratum: | |
652 | return "clock_stratum"; | |
653 | case known_type::compression: | |
654 | return "compression"; | |
655 | case known_type::compression_ratio: | |
656 | return "compression_ratio"; | |
657 | case known_type::connections: | |
658 | return "connections"; | |
659 | case known_type::conntrack: | |
660 | return "conntrack"; | |
661 | case known_type::contextswitch: | |
662 | return "contextswitch"; | |
663 | case known_type::count: | |
664 | return "count"; | |
665 | case known_type::counter: | |
666 | return "counter"; | |
667 | case known_type::cpu: | |
668 | return "cpu"; | |
669 | case known_type::cpufreq: | |
670 | return "cpufreq"; | |
671 | case known_type::current: | |
672 | return "current"; | |
673 | case known_type::current_connections: | |
674 | return "current_connections"; | |
675 | case known_type::current_sessions: | |
676 | return "current_sessions"; | |
677 | case known_type::delay: | |
678 | return "delay"; | |
679 | case known_type::derive: | |
680 | return "derive"; | |
681 | case known_type::df: | |
682 | return "df"; | |
683 | case known_type::df_complex: | |
684 | return "df_complex"; | |
685 | case known_type::df_inodes: | |
686 | return "df_inodes"; | |
687 | case known_type::disk_io_time: | |
688 | return "disk_io_time"; | |
689 | case known_type::disk_latency: | |
690 | return "disk_latency"; | |
691 | case known_type::disk_merged: | |
692 | return "disk_merged"; | |
693 | case known_type::disk_octets: | |
694 | return "disk_octets"; | |
695 | case known_type::disk_ops: | |
696 | return "disk_ops"; | |
697 | case known_type::disk_ops_complex: | |
698 | return "disk_ops_complex"; | |
699 | case known_type::disk_time: | |
700 | return "disk_time"; | |
701 | case known_type::dns_answer: | |
702 | return "dns_answer"; | |
703 | case known_type::dns_notify: | |
704 | return "dns_notify"; | |
705 | case known_type::dns_octets: | |
706 | return "dns_octets"; | |
707 | case known_type::dns_opcode: | |
708 | return "dns_opcode"; | |
709 | case known_type::dns_qtype: | |
710 | return "dns_qtype"; | |
711 | case known_type::dns_qtype_cached: | |
712 | return "dns_qtype_cached"; | |
713 | case known_type::dns_query: | |
714 | return "dns_query"; | |
715 | case known_type::dns_question: | |
716 | return "dns_question"; | |
717 | case known_type::dns_rcode: | |
718 | return "dns_rcode"; | |
719 | case known_type::dns_reject: | |
720 | return "dns_reject"; | |
721 | case known_type::dns_request: | |
722 | return "dns_request"; | |
723 | case known_type::dns_resolver: | |
724 | return "dns_resolver"; | |
725 | case known_type::dns_response: | |
726 | return "dns_response"; | |
727 | case known_type::dns_transfer: | |
728 | return "dns_transfer"; | |
729 | case known_type::dns_update: | |
730 | return "dns_update"; | |
731 | case known_type::dns_zops: | |
732 | return "dns_zops"; | |
733 | case known_type::drbd_resource: | |
734 | return "drbd_resource"; | |
735 | case known_type::duration: | |
736 | return "duration"; | |
737 | case known_type::email_check: | |
738 | return "email_check"; | |
739 | case known_type::email_count: | |
740 | return "email_count"; | |
741 | case known_type::email_size: | |
742 | return "email_size"; | |
743 | case known_type::entropy: | |
744 | return "entropy"; | |
745 | case known_type::evicted_keys: | |
746 | return "evicted_keys"; | |
747 | case known_type::expired_keys: | |
748 | return "expired_keys"; | |
749 | case known_type::fanspeed: | |
750 | return "fanspeed"; | |
751 | case known_type::file_handles: | |
752 | return "file_handles"; | |
753 | case known_type::file_size: | |
754 | return "file_size"; | |
755 | case known_type::files: | |
756 | return "files"; | |
757 | case known_type::flow: | |
758 | return "flow"; | |
759 | case known_type::fork_rate: | |
760 | return "fork_rate"; | |
761 | case known_type::frequency: | |
762 | return "frequency"; | |
763 | case known_type::frequency_error: | |
764 | return "frequency_error"; | |
765 | case known_type::frequency_offset: | |
766 | return "frequency_offset"; | |
767 | case known_type::fscache_stat: | |
768 | return "fscache_stat"; | |
769 | case known_type::gauge: | |
770 | return "gauge"; | |
771 | case known_type::hash_collisions: | |
772 | return "hash_collisions"; | |
773 | case known_type::http_request_methods: | |
774 | return "http_request_methods"; | |
775 | case known_type::http_requests: | |
776 | return "http_requests"; | |
777 | case known_type::http_response_codes: | |
778 | return "http_response_codes"; | |
779 | case known_type::humidity: | |
780 | return "humidity"; | |
781 | case known_type::if_collisions: | |
782 | return "if_collisions"; | |
783 | case known_type::if_dropped: | |
784 | return "if_dropped"; | |
785 | case known_type::if_errors: | |
786 | return "if_errors"; | |
787 | case known_type::if_multicast: | |
788 | return "if_multicast"; | |
789 | case known_type::if_octets: | |
790 | return "if_octets"; | |
791 | case known_type::if_packets: | |
792 | return "if_packets"; | |
793 | case known_type::if_rx_errors: | |
794 | return "if_rx_errors"; | |
795 | case known_type::if_rx_octets: | |
796 | return "if_rx_octets"; | |
797 | case known_type::if_tx_errors: | |
798 | return "if_tx_errors"; | |
799 | case known_type::if_tx_octets: | |
800 | return "if_tx_octets"; | |
801 | case known_type::invocations: | |
802 | return "invocations"; | |
803 | case known_type::io_octets: | |
804 | return "io_octets"; | |
805 | case known_type::io_packets: | |
806 | return "io_packets"; | |
807 | case known_type::ipt_bytes: | |
808 | return "ipt_bytes"; | |
809 | case known_type::ipt_packets: | |
810 | return "ipt_packets"; | |
811 | case known_type::irq: | |
812 | return "irq"; | |
813 | case known_type::latency: | |
814 | return "latency"; | |
815 | case known_type::links: | |
816 | return "links"; | |
817 | case known_type::load: | |
818 | return "load"; | |
819 | case known_type::md_disks: | |
820 | return "md_disks"; | |
821 | case known_type::memory: | |
822 | return "memory"; | |
823 | case known_type::memory_lua: | |
824 | return "memory_lua"; | |
825 | case known_type::memory_throttle_count: | |
826 | return "memory_throttle_count"; | |
827 | case known_type::multimeter: | |
828 | return "multimeter"; | |
829 | case known_type::mutex_operations: | |
830 | return "mutex_operations"; | |
831 | case known_type::objects: | |
832 | return "objects"; | |
833 | case known_type::operations: | |
834 | return "operations"; | |
835 | case known_type::packets: | |
836 | return "packets"; | |
837 | case known_type::pending_operations: | |
838 | return "pending_operations"; | |
839 | case known_type::percent: | |
840 | return "percent"; | |
841 | case known_type::percent_bytes: | |
842 | return "percent_bytes"; | |
843 | case known_type::percent_inodes: | |
844 | return "percent_inodes"; | |
845 | case known_type::ping: | |
846 | return "ping"; | |
847 | case known_type::ping_droprate: | |
848 | return "ping_droprate"; | |
849 | case known_type::ping_stddev: | |
850 | return "ping_stddev"; | |
851 | case known_type::players: | |
852 | return "players"; | |
853 | case known_type::power: | |
854 | return "power"; | |
855 | case known_type::pressure: | |
856 | return "pressure"; | |
857 | case known_type::protocol_counter: | |
858 | return "protocol_counter"; | |
859 | case known_type::pubsub: | |
860 | return "pubsub"; | |
861 | case known_type::queue_length: | |
862 | return "queue_length"; | |
863 | case known_type::records: | |
864 | return "records"; | |
865 | case known_type::requests: | |
866 | return "requests"; | |
867 | case known_type::response_code: | |
868 | return "response_code"; | |
869 | case known_type::response_time: | |
870 | return "response_time"; | |
871 | case known_type::root_delay: | |
872 | return "root_delay"; | |
873 | case known_type::root_dispersion: | |
874 | return "root_dispersion"; | |
875 | case known_type::route_etx: | |
876 | return "route_etx"; | |
877 | case known_type::route_metric: | |
878 | return "route_metric"; | |
879 | case known_type::routes: | |
880 | return "routes"; | |
881 | case known_type::segments: | |
882 | return "segments"; | |
883 | case known_type::serial_octets: | |
884 | return "serial_octets"; | |
885 | case known_type::signal_noise: | |
886 | return "signal_noise"; | |
887 | case known_type::signal_power: | |
888 | return "signal_power"; | |
889 | case known_type::signal_quality: | |
890 | return "signal_quality"; | |
891 | case known_type::snr: | |
892 | return "snr"; | |
893 | case known_type::spl: | |
894 | return "spl"; | |
895 | case known_type::swap: | |
896 | return "swap"; | |
897 | case known_type::swap_io: | |
898 | return "swap_io"; | |
899 | case known_type::tcp_connections: | |
900 | return "tcp_connections"; | |
901 | case known_type::temperature: | |
902 | return "temperature"; | |
903 | case known_type::threads: | |
904 | return "threads"; | |
905 | case known_type::time_dispersion: | |
906 | return "time_dispersion"; | |
907 | case known_type::time_offset: | |
908 | return "time_offset"; | |
909 | case known_type::time_offset_ntp: | |
910 | return "time_offset_ntp"; | |
911 | case known_type::time_offset_rms: | |
912 | return "time_offset_rms"; | |
913 | case known_type::time_ref: | |
914 | return "time_ref"; | |
915 | case known_type::timeleft: | |
916 | return "timeleft"; | |
917 | case known_type::total_bytes: | |
918 | return "total_bytes"; | |
919 | case known_type::total_connections: | |
920 | return "total_connections"; | |
921 | case known_type::total_objects: | |
922 | return "total_objects"; | |
923 | case known_type::total_operations: | |
924 | return "total_operations"; | |
925 | case known_type::total_requests: | |
926 | return "total_requests"; | |
927 | case known_type::total_sessions: | |
928 | return "total_sessions"; | |
929 | case known_type::total_threads: | |
930 | return "total_threads"; | |
931 | case known_type::total_time_in_ms: | |
932 | return "total_time_in_ms"; | |
933 | case known_type::total_values: | |
934 | return "total_values"; | |
935 | case known_type::uptime: | |
936 | return "uptime"; | |
937 | case known_type::users: | |
938 | return "users"; | |
939 | case known_type::vcl: | |
940 | return "vcl"; | |
941 | case known_type::vcpu: | |
942 | return "vcpu"; | |
943 | case known_type::virt_cpu_total: | |
944 | return "virt_cpu_total"; | |
945 | case known_type::virt_vcpu: | |
946 | return "virt_vcpu"; | |
947 | case known_type::vmpage_action: | |
948 | return "vmpage_action"; | |
949 | case known_type::vmpage_faults: | |
950 | return "vmpage_faults"; | |
951 | case known_type::vmpage_io: | |
952 | return "vmpage_io"; | |
953 | case known_type::vmpage_number: | |
954 | return "vmpage_number"; | |
955 | case known_type::volatile_changes: | |
956 | return "volatile_changes"; | |
957 | case known_type::voltage: | |
958 | return "voltage"; | |
959 | case known_type::voltage_threshold: | |
960 | return "voltage_threshold"; | |
961 | case known_type::vs_memory: | |
962 | return "vs_memory"; | |
963 | case known_type::vs_processes: | |
964 | return "vs_processes"; | |
965 | case known_type::vs_threads: | |
966 | return "vs_threads"; | |
967 | default: | |
968 | throw std::invalid_argument("Unknown type"); | |
969 | } | |
970 | } | |
971 | ||
972 | metrics::impl::value_map get_value_map() { | |
973 | return metrics::impl::get_value_map(); | |
974 | } | |
975 | ||
976 | } | |
977 | ||
978 | } |