blob: ad352a02c9504aa532d5eaeda777aea27a16e5af [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// Date: 2021/11/17 10:57:43
#ifndef BVAR_MULTI_DIMENSION_H
#define BVAR_MULTI_DIMENSION_H
#include <memory>
#include <type_traits>
#include "butil/logging.h" // LOG
#include "butil/macros.h" // BAIDU_CASSERT
#include "butil/scoped_lock.h" // BAIDU_SCOPE_LOCK
#include "butil/containers/doubly_buffered_data.h" // DBD
#include "butil/containers/flat_map.h" // butil::FlatMap
#include "butil/strings/string_piece.h"
#include "bvar/mvariable.h"
namespace bvar {
// KeyType requirements:
// 1. KeyType must be a container type with iterator, e.g. std::vector, std::list, std::set.
// 2. KeyType::value_type must be std::string.
// 3. KeyType::size() returns the number of labels.
// 4. KeyType::push_back() adds a label to the end of the container.
//
// If `Shared' is false, `get_stats' returns a raw pointer,
// `delete_stats' and `clear_stats' are not thread safe.
// If `Shared' is true, `get_stats` returns a shared_ptr,
// `delete_stats' and `clear_stats' are thread safe.
// Note: The shared mode may be less performant than the non-shared mode.
template <typename T, typename KeyType = std::list<std::string>, bool Shared = false>
class MultiDimension : public MVariable<KeyType> {
typedef std::shared_ptr<T> shared_value_type;
public:
enum STATS_OP {
READ_ONLY,
READ_OR_INSERT,
};
typedef KeyType key_type;
typedef T value_type;
typedef typename std::conditional<Shared, shared_value_type, T*>::type value_ptr_type;
typedef MVariable<key_type> Base;
struct KeyHash {
template <typename K>
size_t operator() (const K& key) const {
size_t hash_value = 0;
for (auto& k : key) {
hash_value += BUTIL_HASH_NAMESPACE::hash<butil::StringPiece>()(
butil::StringPiece(k));
}
return hash_value;
}
};
struct KeyEqualTo {
template <typename K>
bool operator()(const key_type& k1, const K& k2) const {
return k1.size() == k2.size() &&
std::equal(k1.cbegin(), k1.cend(), k2.cbegin());
}
};
typedef value_ptr_type op_value_type;
typedef butil::FlatMap<key_type, op_value_type, KeyHash, KeyEqualTo> MetricMap;
typedef typename MetricMap::const_iterator MetricMapConstIterator;
typedef butil::DoublyBufferedData<MetricMap> MetricMapDBD;
typedef typename MetricMapDBD::ScopedPtr MetricMapScopedPtr;
explicit MultiDimension(const key_type& labels);
MultiDimension(const butil::StringPiece& name,
const key_type& labels);
MultiDimension(const butil::StringPiece& prefix,
const butil::StringPiece& name,
const key_type& labels);
~MultiDimension() override;
// Implement this method to print the variable into ostream.
void describe(std::ostream& os) override;
// Dump real bvar pointer
size_t dump(Dumper* dumper, const DumpOptions* options) override {
return dump_impl(dumper, options);
}
// Get real bvar pointer object
// Return real bvar pointer on success, NULL otherwise.
// K requirements:
// 1. K must be a container type with iterator,
// e.g. std::vector, std::list, std::set, std::array.
// 2. K::value_type must be able to convert to std::string and butil::StringPiece
// through operator std::string() function and operator butil::StringPiece() function.
// 3. K::value_type must be able to compare with std::string.
//
// Returns a shared_ptr if `Shared' is true, otherwise returns a raw pointer.
template <typename K = key_type>
value_ptr_type get_stats(const K& labels_value) {
return get_stats_impl(labels_value, READ_OR_INSERT);
}
// `delete_stats' and `clear_stats' are thread safe
// if `Shared' is true, otherwise not.
// Remove stat so those not count and dump
template <typename K = key_type>
void delete_stats(const K& labels_value);
// Remove all stat
void clear_stats();
// True if bvar pointer exists
template <typename K = key_type>
bool has_stats(const K& labels_value);
// Get number of stats
size_t count_stats();
// Put name of all stats label into `names'
void list_stats(std::vector<key_type>* names);
void set_max_stats_count(size_t max_stats_count) {
_max_stats_count = std::max(max_stats_count, max_stats_count);
}
#ifdef UNIT_TEST
// Get real bvar pointer object
// Return real bvar pointer if labels_name exist, NULL otherwise.
// CAUTION!!! Just For Debug!!!
template <typename K = key_type>
value_ptr_type get_stats_read_only(const K& labels_value) {
return get_stats_impl(labels_value);
}
// Get real bvar pointer object
// Return real bvar pointer if labels_name exist, otherwise(not exist) create bvar pointer.
// CAUTION!!! Just For Debug!!!
template <typename K = key_type>
value_ptr_type get_stats_read_or_insert(const K& labels_value, bool* do_write = NULL) {
return get_stats_impl(labels_value, READ_OR_INSERT, do_write);
}
#endif
private:
template <typename K>
value_ptr_type get_stats_impl(const K& labels_value);
template <typename K>
value_ptr_type get_stats_impl(
const K& labels_value, STATS_OP stats_op, bool* do_write = NULL);
template <typename K>
static typename std::enable_if<butil::is_same<K, key_type>::value>::type
insert_metrics_map(MetricMap& bg, const K& labels_value, op_value_type metric) {
bg.insert(labels_value, metric);
}
template <typename K>
static typename std::enable_if<!butil::is_same<K, key_type>::value>::type
insert_metrics_map(MetricMap& bg, const K& labels_value, op_value_type metric) {
// key_type::value_type must be able to convert to std::string.
key_type labels_value_str(labels_value.cbegin(), labels_value.cend());
bg.insert(labels_value_str, metric);
}
template <typename U = T>
typename std::enable_if<!butil::is_same<LatencyRecorder, U>::value, size_t>::type
dump_impl(Dumper* dumper, const DumpOptions* options);
template <typename U = T>
typename std::enable_if<butil::is_same<LatencyRecorder, U>::value, size_t>::type
dump_impl(Dumper* dumper, const DumpOptions* options);
void make_dump_key(std::ostream& os, const key_type& labels_value,
const std::string& suffix = "", int quantile = 0);
void make_labels_kvpair_string(
std::ostream& os, const key_type& labels_value, int quantile);
template <typename K>
bool is_valid_lables_value(const K& labels_value) const;
// Remove all stats so those not count and dump
void delete_stats();
static size_t init_flatmap(MetricMap& bg);
// If Shared is true, return std::shared_ptr, otherwise return raw pointer.
template <bool S = Shared>
typename std::enable_if<S, value_ptr_type>::type new_value() {
return std::make_shared<value_type>();
}
template <bool S = Shared>
typename std::enable_if<!S, value_ptr_type>::type new_value() {
return new value_type();
}
// If Shared is true, reset std::shared_ptr, otherwise delete raw pointer.
template <bool S = Shared>
typename std::enable_if<S>::type delete_value(value_ptr_type& v) {
v.reset();
}
template <bool S = Shared>
typename std::enable_if<!S>::type delete_value(value_ptr_type& v) {
delete v;
}
size_t _max_stats_count;
MetricMapDBD _metric_map;
};
} // namespace bvar
#include "bvar/multi_dimension_inl.h"
#endif // BVAR_MULTI_DIMENSION_H