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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
19 * Copyright (C) 2014 Cloudius Systems, Ltd.
24 #include <type_traits>
34 #include <boost/program_options.hpp>
36 #include <seastar/core/future.hh>
37 #include <seastar/net/byteorder.hh>
38 #include <seastar/core/shared_ptr.hh>
39 #include <seastar/core/sstring.hh>
40 #include <seastar/util/log.hh>
42 #include <seastar/core/metrics_api.hh>
47 * Implementation of rudimentary collectd data gathering.
49 * Usage is hopefully straight forward. Though, feel free to read
50 * https://collectd.org/wiki/index.php/Naming_schema
51 * for an explanation on the naming model.
53 * Typically, you'll add values something like:
55 * scollectd::type_instance_id typ("<pluginname>", "<instance_name>", "<type_name>", "<instance_name>");
56 * scollectd::add_polled_metric(typ, [<metric var> | scollectd::make_typed(<data_type>, <metric_var>) [, ...]);
59 * <pluginname> would be the overall 'module', e.g. "cpu"
60 * <instance_name> -> optional distinguisher between plugin instances. For cpu, the built-in
61 * scollectd::per_cpu_plugin_instance constant is a good choice, i.e. 0->N cpu.
62 * If there are no instances (e.g. only one), empty constant is appropriate (none)
63 * <type_name> is the 'type' of metric collected, for ex. "usage" (cpu/0/usage)
64 * <type_instance> is a distinguisher for metric parts of the type, e.g. "idle", "user", "kernel"
65 * -> cpu/0/usage/idle | cpu/0/usage/user | cpu/0/usage/kernel
67 * Each type instance can bind an arbitrary number of values, ech representing some aspect in turn of the instance.
68 * The structure and interpretation is up to the producer/consumer
70 * There is a single "scollectd" instance per cpu, and values should be bound locally
71 * to this cpu. Polling is done at a frequency set in the seastar config (def once per s),
72 * and all registered values will be sent via UDP packages to the destination host(s)
74 * Note that the tuple { plugin, plugin_instance, type, type_instance } is considered a
75 * unique ID for a value registration, so using the same tuple twice will remove the previously
78 * Values can be unregistered at any time, though they must be so on the same thread/cpu
79 * as they we're registered. The "registration" achor type provides RAII style value unregistration
86 extern seastar::logger logger;
88 using data_type = seastar::metrics::impl::data_type;
90 enum class known_type {
91 // from types.db. Defined collectd types (type_id) selection.
92 // This enum omits the very application specific types, such
93 // as mysql_* etc, since if you really are re-writing mysql
94 // in seastar, you probably know how to look the type up manually...
107 changes_since_last_save,
176 http_request_methods,
202 memory_throttle_count,
275 // don't use directly. use make_typed.
278 typed(data_type t, T && v)
279 : type(t), value(std::forward<T>(v)) {
286 static inline typed<T> make_typed(data_type type, T&& t) {
287 return typed<T>(type, std::forward<T>(t));
290 using plugin_id = seastar::metrics::group_name_type;
291 using plugin_instance_id = seastar::metrics::instance_id_type;
292 using type_id = seastar::metrics::metric_type_def;
293 using type_instance = seastar::metrics::metric_name_type;
295 type_id type_id_for(known_type);
297 using description = seastar::metrics::description;
299 static constexpr unsigned max_collectd_field_text_len = 63;
301 class type_instance_id {
302 static thread_local unsigned _next_truncated_idx;
304 /// truncate a given field to the maximum allowed length
305 void truncate(sstring& field, const char* field_desc);
307 type_instance_id() = default;
308 type_instance_id(plugin_id p, plugin_instance_id pi, type_id t,
309 scollectd::type_instance ti = std::string())
310 : _plugin(std::move(p)), _plugin_instance(std::move(pi)), _type(
311 std::move(t)), _type_instance(std::move(ti)) {
312 // truncate strings to the maximum allowed length
313 truncate(_plugin, "plugin");
314 truncate(_plugin_instance, "plugin_instance");
315 truncate(_type, "type");
316 truncate(_type_instance, "type_instance");
318 type_instance_id(const seastar::metrics::impl::metric_id &id) : _plugin(id.group_name()),
319 _plugin_instance(id.instance_id()), _type(id.inherit_type()),
320 _type_instance(id.name()) {
322 type_instance_id(type_instance_id &&) = default;
323 type_instance_id(const type_instance_id &) = default;
325 type_instance_id & operator=(type_instance_id &&) = default;
326 type_instance_id & operator=(const type_instance_id &) = default;
328 const plugin_id & plugin() const {
331 const plugin_instance_id & plugin_instance() const {
332 return _plugin_instance;
334 const type_id & type() const {
337 const scollectd::type_instance & type_instance() const {
338 return _type_instance;
340 bool operator<(const type_instance_id&) const;
341 bool operator==(const type_instance_id&) const;
344 plugin_instance_id _plugin_instance;
346 scollectd::type_instance _type_instance;
349 extern const plugin_instance_id per_cpu_plugin_instance;
351 void configure(const boost::program_options::variables_map&);
352 boost::program_options::options_description get_options_description();
353 void remove_polled_metric(const type_instance_id &);
355 class plugin_instance_metrics;
358 * Anchor for polled registration.
359 * Iff the registered type is in some way none-persistent,
360 * use this as receiver of the reg and ensure it dies before the
365 * registration r = add_polled_metric(v);
367 * <scope end, above dies>
369 struct registration {
370 registration() = default;
371 registration(const type_instance_id& id);
372 registration(type_instance_id&& id);
373 registration(const registration&) = delete;
374 registration(registration&&) = default;
376 registration & operator=(const registration&) = delete;
377 registration & operator=(registration&&) = default;
380 remove_polled_metric(_id);
381 _id = type_instance_id();
384 friend class plugin_instance_metrics;
385 type_instance_id _id;
386 shared_ptr<seastar::metrics::impl::impl> _impl;
390 * Helper type to make generating vectors of registration objects
391 * easier, since it constructs from an initializer list of
392 * type_instance_id:s, avoiding early conversion to registration objs,
393 * which in case of init lists, are copy semantics, not move...
396 : public std::vector<registration>
399 typedef std::vector<registration> vector_type;
403 registrations(vector_type&& v) : vector_type(std::move(v))
405 registrations(const std::initializer_list<type_instance_id>& l)
406 : vector_type(l.begin(),l.end())
408 registrations& operator=(vector_type&& v) {
409 vector_type::operator=(std::move(v));
412 registrations& operator=(const std::initializer_list<type_instance_id>& l) {
413 return registrations::operator=(registrations(l));
421 * Wraps N values of a given type (type_id).
422 * Used to group types into a plugin_instance_metrics
424 template<typename... Args>
425 typed_value(const type_id& tid, const scollectd::type_instance& ti, description, Args&&... args);
427 template<typename... Args>
428 typed_value(const type_id& tid, const scollectd::type_instance& ti, Args&&... args)
429 : typed_value(tid, ti, description(), std::forward<Args>(args)...)
432 const scollectd::type_instance& type_instance() const {
433 return _type_instance;
435 const shared_ptr<value_list>& values() const {
438 const type_id & type() const {
443 scollectd::type_instance _type_instance;
444 shared_ptr<value_list> _values;
447 class plugin_instance_metrics {
449 template<typename... TypedValues>
450 plugin_instance_metrics(const plugin_id& p, const plugin_instance_id& pi, TypedValues&&... values)
452 , _plugin_instance(pi)
453 , _registrations({ add_impl(values)... })
455 std::vector<type_instance_id> bound_ids() const;
456 void add(const typed_value&);
458 type_instance_id add_impl(const typed_value&);
460 plugin_id _plugin_id;
461 plugin_instance_id _plugin_instance;
462 registrations _registrations;
466 * Simplified wrapper for the common case of per-cpu plugin instances
467 * (i.e. distributed objects)
469 class percpu_plugin_instance_metrics : public plugin_instance_metrics {
471 template<typename... TypedValues>
472 percpu_plugin_instance_metrics(const plugin_id& p, TypedValues&&... values)
473 : plugin_instance_metrics(p, per_cpu_plugin_instance, std::forward<TypedValues>(values)...)
478 * Template wrapper for type_id values, deriving type_id string
479 * from the known_types enum, for auto-completetion joy.
481 template<known_type Type>
482 struct typed_value_impl: public typed_value {
483 template<typename ... Args>
484 typed_value_impl(const scollectd::type_instance& ti, Args&& ... args)
485 : typed_value(type_id_for(Type), ti, std::forward<Args>(args)...)
488 template<typename ... Args>
489 typed_value_impl(scollectd::type_instance ti, description d, Args&& ... args)
490 : typed_value(type_id_for(Type), std::move(ti), std::move(d), std::forward<Args>(args)...)
492 template<typename ... Args>
493 typed_value_impl(description d, Args&& ... args)
494 : typed_value(type_id_for(Type), scollectd::type_instance(), std::move(d), std::forward<Args>(args)...)
499 * \deprecated metrics registration should be done using the metrics layer
501 * Some typedefs for common used types. Feel free to add.
503 typedef typed_value_impl<known_type::total_bytes> total_bytes;
504 typedef typed_value_impl<known_type::total_connections> total_connections;
505 typedef typed_value_impl<known_type::total_objects> total_objects;
506 typedef typed_value_impl<known_type::total_operations> total_operations;
507 typedef typed_value_impl<known_type::total_requests> total_requests;
508 typedef typed_value_impl<known_type::total_sessions> total_sessions;
509 typedef typed_value_impl<known_type::total_threads> total_threads;
510 typedef typed_value_impl<known_type::total_time_in_ms> total_time_in_ms;
511 typedef typed_value_impl<known_type::total_values> total_values;
512 typedef typed_value_impl<known_type::queue_length> queue_length;
513 typedef typed_value_impl<known_type::counter> counter;
514 typedef typed_value_impl<known_type::count> count;
515 typedef typed_value_impl<known_type::gauge> gauge;
517 // lots of template junk to build typed value list tuples
518 // for registered values.
519 template<typename T, typename En = void>
520 struct data_type_for;
522 template<typename T, typename En = void>
526 struct is_callable<T,
527 typename std::enable_if<
528 !std::is_void<typename std::result_of<T()>::type>::value,
529 void>::type> : public std::true_type {
533 struct is_callable<T,
534 typename std::enable_if<std::is_fundamental<T>::value, void>::type> : public std::false_type {
538 struct data_type_for<T,
539 typename std::enable_if<
540 std::is_integral<T>::value && std::is_unsigned<T>::value,
541 void>::type> : public std::integral_constant<data_type,
542 data_type::COUNTER> {
545 struct data_type_for<T,
546 typename std::enable_if<
547 std::is_integral<T>::value && std::is_signed<T>::value, void>::type> : public std::integral_constant<
548 data_type, data_type::DERIVE> {
551 struct data_type_for<T,
552 typename std::enable_if<std::is_floating_point<T>::value, void>::type> : public std::integral_constant<
553 data_type, data_type::GAUGE> {
556 struct data_type_for<T,
557 typename std::enable_if<is_callable<T>::value, void>::type> : public data_type_for<
558 typename std::result_of<T()>::type> {
561 struct data_type_for<typed<T>> : public data_type_for<T> {
572 const W & operator()() const {
578 typedef typename std::remove_reference<T>::type value_type;
579 typedef typename std::conditional<
580 is_callable<typename std::remove_reference<T>::type>::value,
581 value_type, wrap<value_type> >::type stored_type;
583 value(const value_type & t)
584 : value<T>(data_type_for<value_type>::value, t) {
586 value(data_type type, const value_type & t)
587 : _type(type), _t(t) {
589 uint64_t operator()() const {
591 if (_type == data_type::GAUGE) {
592 return convert(double(v));
598 operator uint64_t() const {
601 operator data_type() const {
604 data_type type() const {
608 // not super quick value -> protocol endian 64-bit values.
609 template<typename _Iter>
610 void bpack(_Iter s, _Iter e, uint64_t v) const {
617 typename std::enable_if<std::is_integral<V>::value, uint64_t>::type convert(
620 // network byte order
624 typename std::enable_if<std::is_floating_point<V>::value, uint64_t>::type convert(
635 // intel byte order. could also obviously be faster.
636 // could be ignored if we just assume we're le (for now),
637 // but this is ok me thinks.
638 bpack(std::begin(u.b), std::end(u.b), v.i);
643 const data_type _type;
644 const stored_type _t;
648 class value<typed<T>> : public value<T> {
650 value(const typed<T> & args)
651 : value<T>(args.type, args.value) {
656 bool _enabled = true;
658 value_list(description d) : _description(std::move(d))
660 value_list(value_list&&) = default;
661 virtual ~value_list() {}
663 virtual size_t size() const = 0;
665 virtual void types(data_type *) const = 0;
666 virtual void values(net::packed<uint64_t> *) const = 0;
668 const description& desc() const {
676 bool is_enabled() const {
680 void set_enabled(bool b) {
684 description _description;
687 template<typename ... Args>
688 class values_impl: public value_list {
690 static const size_t num_values = sizeof...(Args);
692 values_impl(description d, Args&& ...args)
693 : value_list(std::move(d))
694 , _values(std::forward<Args>(args)...)
697 values_impl(values_impl<Args...>&& a) = default;
698 values_impl(const values_impl<Args...>& a) = default;
700 size_t size() const override {
703 void types(data_type * p) const override {
704 unpack(_values, [p](Args... args) {
705 std::initializer_list<data_type> tmp = { args... };
706 std::copy(tmp.begin(), tmp.end(), p);
709 void values(net::packed<uint64_t> * p) const override {
710 unpack(_values, [p](Args... args) {
711 std::initializer_list<uint64_t> tmp = { args... };
712 std::copy(tmp.begin(), tmp.end(), p);
716 template<typename _Op>
717 void unpack(const std::tuple<Args...>& t, _Op&& op) const {
718 do_unpack(t, std::index_sequence_for<Args...> {}, std::forward<_Op>(op));
721 template<size_t ...S, typename _Op>
722 void do_unpack(const std::tuple<Args...>& t, const std::index_sequence<S...> &, _Op&& op) const {
723 op(std::get<S>(t)...);
726 std::tuple < Args... > _values;
729 void add_polled(const type_instance_id &, const shared_ptr<value_list> &, bool enabled = true);
731 typedef std::function<void()> notify_function;
732 template<typename... _Args>
733 static auto make_type_instance(description d, _Args && ... args) -> values_impl < decltype(value<_Args>(std::forward<_Args>(args)))... >
735 return values_impl<decltype(value<_Args>(std::forward<_Args>(args)))...>(
736 std::move(d), value<_Args>(std::forward<_Args>(args))...);
739 * \deprecated metrics registration should be done using the metrics layer
742 template<typename ... _Args>
743 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const plugin_id & plugin,
744 const plugin_instance_id & plugin_instance, const type_id & type,
745 const scollectd::type_instance & type_instance, _Args&& ... args) {
746 return add_polled_metric(plugin, plugin_instance, type, type_instance, description(),
747 std::forward<_Args>(args)...);
750 * \deprecated metrics registration should be done using the metrics layer
753 template<typename ... _Args>
754 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const plugin_id & plugin,
755 const plugin_instance_id & plugin_instance, const type_id & type,
756 const scollectd::type_instance & type_instance, description d, _Args&& ... args) {
757 return add_polled_metric(
758 type_instance_id(plugin, plugin_instance, type, type_instance), std::move(d),
759 std::forward<_Args>(args)...);
761 template<typename ... _Args>
762 static future<> send_explicit_metric(const plugin_id & plugin,
763 const plugin_instance_id & plugin_instance, const type_id & type,
764 const scollectd::type_instance & type_instance, _Args&& ... args) {
765 return send_explicit_metric(
766 type_instance_id(plugin, plugin_instance, type, type_instance),
767 std::forward<_Args>(args)...);
769 template<typename ... _Args>
770 static notify_function create_explicit_metric(const plugin_id & plugin,
771 const plugin_instance_id & plugin_instance, const type_id & type,
772 const scollectd::type_instance & type_instance, _Args&& ... args) {
773 return create_explicit_metric(
774 type_instance_id(plugin, plugin_instance, type, type_instance),
775 std::forward<_Args>(args)...);
778 seastar::metrics::impl::metric_id to_metrics_id(const type_instance_id & id);
780 * \deprecated metrics registration should be done using the metrics layer
783 template<typename Arg>
784 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const type_instance_id & id, description d,
785 Arg&& arg, bool enabled = true) {
786 namespace sm = seastar::metrics::impl;
788 seastar::metrics::impl::get_local_impl()->add_registration(to_metrics_id(id), arg.type, sm::make_function(arg.value, arg.type), d, enabled);
792 * \deprecated metrics registration should be done using the metrics layer
795 template<typename Arg>
796 [[deprecated("Use the metrics layer")]] static type_instance_id add_polled_metric(const type_instance_id & id,
798 return std::move(add_polled_metric(id, description(), std::forward<Arg>(arg)));
802 * \deprecated metrics registration should be done using the metrics layer
805 template<typename Args>
806 [[deprecated("Use the metrics layer")]] static type_instance_id add_disabled_polled_metric(const type_instance_id & id, description d,
808 return add_polled_metric(id, d, std::forward<Args>(arg), false);
811 template<typename Args>
812 static type_instance_id add_disabled_polled_metric(const type_instance_id & id,
814 return add_disabled_polled_metric(id, description(), std::forward<Args>(args));
817 template<typename ... Args>
818 static type_instance_id add_disabled_polled_metric(const type_instance_id & id,
820 return add_disabled_polled_metric(id, description(), std::forward<Args>(args)...);
823 // "Explicit" metric sends. Sends a single value list as a message.
824 // Obviously not super efficient either. But maybe someone needs it sometime.
825 template<typename ... _Args>
826 static future<> send_explicit_metric(const type_instance_id & id,
828 return send_metric(id, make_type_instance(std::forward<_Args>(args)...));
830 template<typename ... _Args>
831 static notify_function create_explicit_metric(const type_instance_id & id,
833 auto list = make_type_instance(std::forward<_Args>(args)...);
834 return [id, list=std::move(list)]() {
835 send_metric(id, list);
839 template<typename... Args>
840 typed_value::typed_value(const type_id& tid, const scollectd::type_instance& ti, description d, Args&&... args)
843 , _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)...)))
846 // Send a message packet (string)
847 future<> send_notification(const type_instance_id & id, const sstring & msg);