Seastar
High performance C++ framework for concurrent servers
scollectd.hh
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 #pragma once
23 
24 #ifndef SEASTAR_MODULE
25 #include <type_traits>
26 #include <utility>
27 #include <functional>
28 #include <array>
29 #include <iterator>
30 #include <stdint.h>
31 #include <memory>
32 #include <string>
33 #include <tuple>
34 #include <chrono>
35 #endif
36 
37 #include <seastar/core/future.hh>
38 #include <seastar/net/byteorder.hh>
39 #include <seastar/core/shared_ptr.hh>
40 #include <seastar/core/sstring.hh>
41 #include <seastar/util/log.hh>
42 #include <seastar/util/program-options.hh>
43 #include <seastar/util/modules.hh>
45 
46 namespace seastar {
47 
86 namespace scollectd {
87 
88 extern seastar::logger logger;
89 
90 using data_type = seastar::metrics::impl::data_type;
91 
92 enum class known_type {
93  // from types.db. Defined collectd types (type_id) selection.
94  // This enum omits the very application specific types, such
95  // as mysql_* etc, since if you really are re-writing mysql
96  // in seastar, you probably know how to look the type up manually...
97 
98  absolute,
99  backends,
100  bitrate,
101  blocked_clients,
102  bytes,
103  cache_eviction,
104  cache_operation,
105  cache_ratio,
106  cache_result,
107  cache_size,
108  capacity,
109  changes_since_last_save,
110  charge,
111  clock_last_meas,
112  clock_last_update,
113  clock_mode,
114  clock_reachability,
115  clock_skew_ppm,
116  clock_state,
117  clock_stratum,
118  compression,
119  compression_ratio,
120  connections,
121  conntrack,
122  contextswitch,
123  count,
124  counter,
125  cpu,
126  cpufreq,
127  current,
128  current_connections,
129  current_sessions,
130  delay,
131  derive,
132  df,
133  df_complex,
134  df_inodes,
135  disk_io_time,
136  disk_latency,
137  disk_merged,
138  disk_octets,
139  disk_ops,
140  disk_ops_complex,
141  disk_time,
142  dns_answer,
143  dns_notify,
144  dns_octets,
145  dns_opcode,
146  dns_qtype,
147  dns_qtype_cached,
148  dns_query,
149  dns_question,
150  dns_rcode,
151  dns_reject,
152  dns_request,
153  dns_resolver,
154  dns_response,
155  dns_transfer,
156  dns_update,
157  dns_zops,
158  drbd_resource,
159  duration,
160  email_check,
161  email_count,
162  email_size,
163  entropy,
164  evicted_keys,
165  expired_keys,
166  fanspeed,
167  file_handles,
168  file_size,
169  files,
170  flow,
171  fork_rate,
172  frequency,
173  frequency_error,
174  frequency_offset,
175  fscache_stat,
176  gauge,
177  hash_collisions,
178  http_request_methods,
179  http_requests,
180  http_response_codes,
181  humidity,
182  if_collisions,
183  if_dropped,
184  if_errors,
185  if_multicast,
186  if_octets,
187  if_packets,
188  if_rx_errors,
189  if_rx_octets,
190  if_tx_errors,
191  if_tx_octets,
192  invocations,
193  io_octets,
194  io_packets,
195  ipt_bytes,
196  ipt_packets,
197  irq,
198  latency,
199  links,
200  load,
201  md_disks,
202  memory,
203  memory_lua,
204  memory_throttle_count,
205  multimeter,
206  mutex_operations,
207  objects,
208  operations,
209  packets,
210  pending_operations,
211  percent,
212  percent_bytes,
213  percent_inodes,
214  ping,
215  ping_droprate,
216  ping_stddev,
217  players,
218  power,
219  pressure,
220  protocol_counter,
221  pubsub,
222  queue_length,
223  records,
224  requests,
225  response_code,
226  response_time,
227  root_delay,
228  root_dispersion,
229  route_etx,
230  route_metric,
231  routes,
232  segments,
233  serial_octets,
234  signal_noise,
235  signal_power,
236  signal_quality,
237  snr,
238  spl,
239  swap,
240  swap_io,
241  tcp_connections,
242  temperature,
243  threads,
244  time_dispersion,
245  time_offset,
246  time_offset_ntp,
247  time_offset_rms,
248  time_ref,
249  timeleft,
250  total_bytes,
258  total_values,
259  uptime,
260  users,
261  vcl,
262  vcpu,
263  virt_cpu_total,
264  virt_vcpu,
265  vmpage_action,
266  vmpage_faults,
267  vmpage_io,
268  vmpage_number,
269  volatile_changes,
270  voltage,
271  voltage_threshold,
272  vs_memory,
273  vs_processes,
274  vs_threads,
275 };
276 
277 // don't use directly. use make_typed.
278 template<typename T>
279 struct typed {
280  typed(data_type t, T && v)
281  : type(t), value(std::forward<T>(v)) {
282  }
283  data_type type;
284  T value;
285 };
286 
287 template<typename T>
288 inline typed<T> make_typed(data_type type, T&& t) {
289  return typed<T>(type, std::forward<T>(t));
290 }
291 
292 using plugin_id = seastar::metrics::group_name_type;
293 using plugin_instance_id = seastar::metrics::instance_id_type;
294 using type_id = seastar::metrics::metric_type_def;
295 using type_instance = seastar::metrics::metric_name_type;
296 
297 type_id type_id_for(known_type);
298 
299 using description = seastar::metrics::description;
300 
301 static constexpr unsigned max_collectd_field_text_len = 63;
302 
304  static thread_local unsigned _next_truncated_idx;
305 
307  void truncate(sstring& field, const char* field_desc);
308 public:
309  type_instance_id() = default;
310  type_instance_id(plugin_id p, plugin_instance_id pi, type_id t,
311  scollectd::type_instance ti = std::string())
312  : _plugin(std::move(p)), _plugin_instance(std::move(pi)), _type(
313  std::move(t)), _type_instance(std::move(ti)) {
314  // truncate strings to the maximum allowed length
315  truncate(_plugin, "plugin");
316  truncate(_plugin_instance, "plugin_instance");
317  truncate(_type, "type");
318  truncate(_type_instance, "type_instance");
319  }
320  type_instance_id(const seastar::metrics::impl::metric_id &id, const type_id& inherit_type) : _plugin(id.group_name()),
321  _plugin_instance(id.instance_id()), _type(inherit_type),
322  _type_instance(id.name()) {
323  }
324  type_instance_id(type_instance_id &&) = default;
325  type_instance_id(const type_instance_id &) = default;
326 
327  type_instance_id & operator=(type_instance_id &&) = default;
328  type_instance_id & operator=(const type_instance_id &) = default;
329 
330  const plugin_id & plugin() const {
331  return _plugin;
332  }
333  const plugin_instance_id & plugin_instance() const {
334  return _plugin_instance;
335  }
336  const type_id & type() const {
337  return _type;
338  }
339  const scollectd::type_instance & type_instance() const {
340  return _type_instance;
341  }
342  bool operator<(const type_instance_id&) const;
343  bool operator==(const type_instance_id&) const;
344 private:
345  plugin_id _plugin;
346  plugin_instance_id _plugin_instance;
347  type_id _type;
348  scollectd::type_instance _type_instance;
349 };
350 
351 extern const plugin_instance_id per_cpu_plugin_instance;
352 
353 // Scollectd configuration options.
370 
372  options(program_options::option_group* parent_group);
374 };
375 
376 void configure(const options&);
377 void remove_polled_metric(const type_instance_id &);
378 
380 
393 struct registration {
394  registration() = default;
395  registration(const type_instance_id& id);
397  registration(const registration&) = delete;
398  registration(registration&&) = default;
399  ~registration();
400  registration & operator=(const registration&) = delete;
401  registration & operator=(registration&&) = default;
402 
403  void unregister() {
404  remove_polled_metric(_id);
405  _id = type_instance_id();
406  }
407 private:
408  friend class plugin_instance_metrics;
409  type_instance_id _id;
411 };
412 
420  : public std::vector<registration>
421 {
422 public:
423  typedef std::vector<registration> vector_type;
424 
425  registrations()
426  {}
427  registrations(vector_type&& v) : vector_type(std::move(v))
428  {}
429  registrations(const std::initializer_list<type_instance_id>& l)
430  : vector_type(l.begin(),l.end())
431  {}
432  registrations& operator=(vector_type&& v) {
433  vector_type::operator=(std::move(v));
434  return *this;
435  }
436  registrations& operator=(const std::initializer_list<type_instance_id>& l) {
437  return registrations::operator=(registrations(l));
438  }
439 };
440 
441 class value_list;
442 
443 struct typed_value {
448  template<typename... Args>
449  typed_value(const type_id& tid, const scollectd::type_instance& ti, description, Args&&... args);
450 
451  template<typename... Args>
452  typed_value(const type_id& tid, const scollectd::type_instance& ti, Args&&... args)
453  : typed_value(tid, ti, description(), std::forward<Args>(args)...)
454  {}
455 
456  const scollectd::type_instance& type_instance() const {
457  return _type_instance;
458  }
459  const shared_ptr<value_list>& values() const {
460  return _values;
461  }
462  const type_id & type() const {
463  return _type_id;
464  }
465 private:
466  type_id _type_id;
467  scollectd::type_instance _type_instance;
468  shared_ptr<value_list> _values;
469 };
470 
472 public:
473  template<typename... TypedValues>
474  plugin_instance_metrics(const plugin_id& p, const plugin_instance_id& pi, TypedValues&&... values)
475  : _plugin_id(p)
476  , _plugin_instance(pi)
477  , _registrations({ add_impl(values)... })
478  {}
479  std::vector<type_instance_id> bound_ids() const;
480  void add(const typed_value&);
481 private:
482  type_instance_id add_impl(const typed_value&);
483 
484  plugin_id _plugin_id;
485  plugin_instance_id _plugin_instance;
486  registrations _registrations;
487 };
488 
494 public:
495  template<typename... TypedValues>
496  percpu_plugin_instance_metrics(const plugin_id& p, TypedValues&&... values)
497  : plugin_instance_metrics(p, per_cpu_plugin_instance, std::forward<TypedValues>(values)...)
498  {}
499 };
500 
505 template<known_type Type>
507  template<typename ... Args>
508  typed_value_impl(const scollectd::type_instance& ti, Args&& ... args)
509  : typed_value(type_id_for(Type), ti, std::forward<Args>(args)...)
510  {}
511 
512  template<typename ... Args>
513  typed_value_impl(scollectd::type_instance ti, description d, Args&& ... args)
514  : typed_value(type_id_for(Type), std::move(ti), std::move(d), std::forward<Args>(args)...)
515  {}
516  template<typename ... Args>
517  typed_value_impl(description d, Args&& ... args)
518  : typed_value(type_id_for(Type), scollectd::type_instance(), std::move(d), std::forward<Args>(args)...)
519  {}
520 };
521 
540 
541 // lots of template junk to build typed value list tuples
542 // for registered values.
543 template<typename T, typename En = void>
545 
546 template<typename T, typename En = void>
547 struct is_callable;
548 
549 template<typename T>
550 struct is_callable<T,
551 std::enable_if_t<
552 !std::is_void_v<std::invoke_result_t<T>>,
553 void>> : public std::true_type {
554 };
555 
556 template<typename T>
557 struct is_callable<T,
558 std::enable_if_t<std::is_fundamental_v<T>, void>> : public std::false_type {
559 };
560 
561 template<typename T>
562 struct data_type_for<T,
563 std::enable_if_t<
564 std::is_integral_v<T> && std::is_unsigned_v<T>,
565 void>> : public std::integral_constant<data_type,
566 data_type::COUNTER> {
567 };
568 template<typename T>
569 struct data_type_for<T,
570 std::enable_if_t<std::is_floating_point_v<T>, void>> : public std::integral_constant<
571 data_type, data_type::GAUGE> {
572 };
573 template<typename T>
574 struct data_type_for<T,
575 std::enable_if_t<is_callable<T>::value, void>> : public data_type_for<
576 std::invoke_result_t<T>> {
577 };
578 template<typename T>
579 struct data_type_for<typed<T>> : public data_type_for<T> {
580 };
581 
582 template<typename T>
583 class value {
584 public:
585  template<typename W>
586  struct wrap {
587  wrap(const W & v)
588  : _v(v) {
589  }
590  const W & operator()() const {
591  return _v;
592  }
593  const W & _v;
594  };
595 
596  typedef std::remove_reference_t<T> value_type;
597  typedef std::conditional_t<
599  value_type, wrap<value_type> > stored_type;
600 
601  value(const value_type & t)
602  : value<T>(data_type_for<value_type>::value, t) {
603  }
604  value(data_type type, const value_type & t)
605  : _type(type), _t(t) {
606  }
607  uint64_t operator()() const {
608  auto v = _t();
609  if (_type == data_type::GAUGE) {
610  return convert(double(v));
611  } else {
612  uint64_t u = v;
613  return convert(u);
614  }
615  }
616  operator uint64_t() const {
617  return (*this)();
618  }
619  operator data_type() const {
620  return _type;
621  }
622  data_type type() const {
623  return _type;
624  }
625 private:
626  // not super quick value -> protocol endian 64-bit values.
627  template<typename _Iter>
628  void bpack(_Iter s, _Iter e, uint64_t v) const {
629  while (s != e) {
630  *s++ = (v & 0xff);
631  v >>= 8;
632  }
633  }
634  template<typename V>
635  std::enable_if_t<std::is_integral_v<V>, uint64_t> convert(
636  V v) const {
637  uint64_t i = v;
638  // network byte order
639  return ntohq(i);
640  }
641  template<typename V>
642  std::enable_if_t<std::is_floating_point_v<V>, uint64_t> convert(
643  V t) const {
644  union {
645  uint64_t i;
646  double v;
647  } v;
648  union {
649  uint64_t i;
650  uint8_t b[8];
651  } u;
652  v.v = t;
653  // intel byte order. could also obviously be faster.
654  // could be ignored if we just assume we're le (for now),
655  // but this is ok me thinks.
656  bpack(std::begin(u.b), std::end(u.b), v.i);
657  return u.i;
658  }
659  ;
660 
661  const data_type _type;
662  const stored_type _t;
663 };
664 
665 template<typename T>
666 class value<typed<T>> : public value<T> {
667 public:
668  value(const typed<T> & args)
669 : value<T>(args.type, args.value) {
670  }
671 };
672 
673 class value_list {
674  bool _enabled = true;
675 public:
676  value_list(description d) : _description(std::move(d))
677  {}
678  value_list(value_list&&) = default;
679  virtual ~value_list() {}
680 
681  virtual size_t size() const = 0;
682 
683  virtual void types(data_type *) const = 0;
684  virtual void values(net::packed<uint64_t> *) const = 0;
685 
686  const description& desc() const {
687  return _description;
688  }
689 
690  bool empty() const {
691  return size() == 0;
692  }
693 
694  bool is_enabled() const {
695  return _enabled;
696  }
697 
698  void set_enabled(bool b) {
699  _enabled = b;
700  }
701 private:
702  description _description;
703 };
704 
705 template<typename ... Args>
706 class values_impl: public value_list {
707 public:
708  static const size_t num_values = sizeof...(Args);
709 
710  values_impl(description d, Args&& ...args)
711  : value_list(std::move(d))
712  , _values(std::forward<Args>(args)...)
713  {}
714 
715  values_impl(values_impl<Args...>&& a) = default;
716  values_impl(const values_impl<Args...>& a) = default;
717 
718  size_t size() const override {
719  return num_values;
720  }
721  void types(data_type * p) const override {
722  unpack(_values, [p](Args... args) {
723  std::initializer_list<data_type> tmp = { args... };
724  std::copy(tmp.begin(), tmp.end(), p);
725  });
726  }
727  void values(net::packed<uint64_t> * p) const override {
728  unpack(_values, [p](Args... args) {
729  std::initializer_list<uint64_t> tmp = { args... };
730  std::copy(tmp.begin(), tmp.end(), p);
731  });
732  }
733 private:
734  template<typename _Op>
735  void unpack(const std::tuple<Args...>& t, _Op&& op) const {
736  do_unpack(t, std::index_sequence_for<Args...> {}, std::forward<_Op>(op));
737  }
738 
739  template<size_t ...S, typename _Op>
740  void do_unpack(const std::tuple<Args...>& t, const std::index_sequence<S...> &, _Op&& op) const {
741  op(std::get<S>(t)...);
742  }
743 
744  std::tuple < Args... > _values;
745 };
746 
747 void add_polled(const type_instance_id &, const shared_ptr<value_list> &, bool enabled = true);
748 
749 typedef std::function<void()> notify_function;
750 template<typename... _Args>
751 static auto make_type_instance(description d, _Args && ... args) -> values_impl < decltype(value<_Args>(std::forward<_Args>(args)))... >
752 {
753  return values_impl<decltype(value<_Args>(std::forward<_Args>(args)))...>(
754  std::move(d), value<_Args>(std::forward<_Args>(args))...);
755 }
760 template<typename ... _Args>
761 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const plugin_id & plugin,
762  const plugin_instance_id & plugin_instance, const type_id & type,
763  const scollectd::type_instance & type_instance, _Args&& ... args) {
764  return add_polled_metric(plugin, plugin_instance, type, type_instance, description(),
765  std::forward<_Args>(args)...);
766 }
771 template<typename ... _Args>
772 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const plugin_id & plugin,
773  const plugin_instance_id & plugin_instance, const type_id & type,
774  const scollectd::type_instance & type_instance, description d, _Args&& ... args) {
775  return add_polled_metric(
776  type_instance_id(plugin, plugin_instance, type, type_instance), std::move(d),
777  std::forward<_Args>(args)...);
778 }
779 template<typename ... _Args>
780 static future<> send_explicit_metric(const plugin_id & plugin,
781  const plugin_instance_id & plugin_instance, const type_id & type,
782  const scollectd::type_instance & type_instance, _Args&& ... args) {
783  return send_explicit_metric(
784  type_instance_id(plugin, plugin_instance, type, type_instance),
785  std::forward<_Args>(args)...);
786 }
787 template<typename ... _Args>
788 static notify_function create_explicit_metric(const plugin_id & plugin,
789  const plugin_instance_id & plugin_instance, const type_id & type,
790  const scollectd::type_instance & type_instance, _Args&& ... args) {
791  return create_explicit_metric(
792  type_instance_id(plugin, plugin_instance, type, type_instance),
793  std::forward<_Args>(args)...);
794 }
795 
796 seastar::metrics::impl::metric_id to_metrics_id(const type_instance_id & id);
801 template<typename Arg>
802 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const type_instance_id & id, description d,
803  Arg&& arg, bool enabled = true) {
804  seastar::metrics::impl::get_local_impl()->add_registration(to_metrics_id(id), arg.type, seastar::metrics::impl::make_function(arg.value, arg.type), d, enabled);
805  return id;
806 }
811 template<typename Arg>
812 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const type_instance_id & id,
813  Arg&& arg) {
814  return std::move(add_polled_metric(id, description(), std::forward<Arg>(arg)));
815 }
816 
821 template<typename Args>
822 [[deprecated("Use the metrics layer")]] static type_instance_id add_disabled_polled_metric(const type_instance_id & id, description d,
823  Args&& arg) {
824  return add_polled_metric(id, d, std::forward<Args>(arg), false);
825 }
826 
827 template<typename Args>
828 static type_instance_id add_disabled_polled_metric(const type_instance_id & id,
829  Args&& args) {
830  return add_disabled_polled_metric(id, description(), std::forward<Args>(args));
831 }
832 
833 template<typename ... Args>
834 static type_instance_id add_disabled_polled_metric(const type_instance_id & id,
835  Args&& ... args) {
836  return add_disabled_polled_metric(id, description(), std::forward<Args>(args)...);
837 }
838 
839 // "Explicit" metric sends. Sends a single value list as a message.
840 // Obviously not super efficient either. But maybe someone needs it sometime.
841 template<typename ... _Args>
842 static future<> send_explicit_metric(const type_instance_id & id,
843  _Args&& ... args) {
844  return send_metric(id, make_type_instance(std::forward<_Args>(args)...));
845 }
846 template<typename ... _Args>
847 static notify_function create_explicit_metric(const type_instance_id & id,
848  _Args&& ... args) {
849  auto list = make_type_instance(std::forward<_Args>(args)...);
850  return [id, list=std::move(list)]() {
851  send_metric(id, list);
852  };
853 }
854 
855 template<typename... Args>
856 typed_value::typed_value(const type_id& tid, const scollectd::type_instance& ti, description d, Args&&... args)
857  : _type_id(tid)
858  , _type_instance(ti)
859  , _values(::seastar::make_shared<decltype(make_type_instance(std::move(d), std::forward<Args>(args)...))>(make_type_instance(std::move(d), std::forward<Args>(args)...)))
860 {}
861 
862 // Send a message packet (string)
863 future<> send_notification(const type_instance_id & id, const sstring & msg);
864 }
865 
866 }
Logger class for ostream or syslog.
Definition: log.hh:90
Human-readable description of a metric/group.
Definition: metrics.hh:134
Definition: metrics_api.hh:104
Definition: program-options.hh:290
Definition: scollectd.hh:421
Definition: scollectd.hh:303
Definition: scollectd.hh:673
Definition: scollectd.hh:583
Definition: scollectd.hh:706
future< uint64_t > file_size(std::string_view name) noexcept
sstring metric_type_def
Definition: metrics.hh:114
sstring metric_name_type
Definition: metrics.hh:115
sstring instance_id_type
Definition: metrics.hh:116
header file for metric API layer (like prometheus or collectd)
sstring group_name_type
Definition: metrics_registration.hh:64
static type_instance_id add_polled_metric(const plugin_id &plugin, const plugin_instance_id &plugin_instance, const type_id &type, const scollectd::type_instance &type_instance, _Args &&... args)
Definition: scollectd.hh:761
typed_value_impl< known_type::total_bytes > total_bytes
Definition: scollectd.hh:527
static type_instance_id add_disabled_polled_metric(const type_instance_id &id, description d, Args &&arg)
Definition: scollectd.hh:822
Definition: scollectd.hh:547
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: scollectd.hh:544
Definition: scollectd.hh:354
program_options::value< std::string > collectd_hostname
Definition: scollectd.hh:369
program_options::value< unsigned > collectd_poll_period
Poll period (ms).
Definition: scollectd.hh:367
program_options::value< std::string > collectd_address
Address to send/broadcast metrics to.
Definition: scollectd.hh:362
program_options::value< bool > collectd
Enable collectd daemon.
Definition: scollectd.hh:358
Definition: scollectd.hh:393
Definition: scollectd.hh:506
Definition: scollectd.hh:443
typed_value(const type_id &tid, const scollectd::type_instance &ti, description, Args &&... args)
Definition: scollectd.hh:856
Definition: scollectd.hh:279
Definition: scollectd.hh:586
Definition: unaligned.hh:58