blob: cb49884fefb60b59b0ad80315ec0e68aceac7bac [file] [log] [blame]
// 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.
#pragma once
#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>
#include <atomic>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "util/histogram.h"
namespace doris {
namespace rj = RAPIDJSON_NAMESPACE;
enum class MetricType { COUNTER, GAUGE, HISTOGRAM, SUMMARY, UNTYPED };
enum class MetricUnit {
NANOSECONDS,
MICROSECONDS,
MILLISECONDS,
SECONDS,
BYTES,
ROWS,
PERCENT,
REQUESTS,
OPERATIONS,
BLOCKS,
ROWSETS,
CONNECTIONS,
PACKETS,
NOUNIT,
FILESYSTEM
};
std::ostream& operator<<(std::ostream& os, MetricType type);
const char* unit_name(MetricUnit unit);
using Labels = std::unordered_map<std::string, std::string>;
class Metric {
public:
Metric() = default;
virtual ~Metric() = default;
virtual std::string to_string() const = 0;
virtual std::string to_prometheus(const std::string& display_name, const Labels& entity_labels,
const Labels& metric_labels) const;
virtual rj::Value to_json_value(rj::Document::AllocatorType& allocator) const = 0;
private:
friend class MetricRegistry;
};
// Metric that only can increment
template <typename T>
class AtomicMetric : public Metric {
public:
AtomicMetric() : _value(T()) {}
virtual ~AtomicMetric() = default;
std::string to_string() const override { return std::to_string(value()); }
T value() const { return _value.load(); }
void increment(const T& delta) { _value.fetch_add(delta); }
void set_value(const T& value) { _value.store(value); }
rj::Value to_json_value(rj::Document::AllocatorType& allocator) const override {
return rj::Value(value());
}
protected:
std::atomic<T> _value;
};
class HistogramMetric : public Metric {
public:
HistogramMetric() = default;
virtual ~HistogramMetric() = default;
HistogramMetric(const HistogramMetric&) = delete;
HistogramMetric& operator=(const HistogramMetric&) = delete;
void clear();
bool is_empty() const;
void add(const uint64_t& value);
void merge(const HistogramMetric& other);
void set_histogram(const HistogramStat& stats);
uint64_t min() const { return _stats.min(); }
uint64_t max() const { return _stats.max(); }
uint64_t num() const { return _stats.num(); }
uint64_t sum() const { return _stats.sum(); }
double median() const;
double percentile(double p) const;
double average() const;
double standard_deviation() const;
std::string to_string() const override;
std::string to_prometheus(const std::string& display_name, const Labels& entity_labels,
const Labels& metric_labels) const override;
rj::Value to_json_value(rj::Document::AllocatorType& allocator) const override;
protected:
static std::map<std::string, double> _s_output_percentiles;
mutable std::mutex _lock;
HistogramStat _stats;
};
template <typename T>
class AtomicCounter : public AtomicMetric<T> {
public:
AtomicCounter() = default;
virtual ~AtomicCounter() = default;
};
template <typename T>
class AtomicGauge : public AtomicMetric<T> {
public:
AtomicGauge() : AtomicMetric<T>() {}
virtual ~AtomicGauge() = default;
};
using IntCounter = AtomicCounter<int64_t>;
using UIntCounter = AtomicCounter<uint64_t>;
using DoubleCounter = AtomicCounter<double>;
using IntGauge = AtomicGauge<int64_t>;
using UIntGauge = AtomicGauge<uint64_t>;
using DoubleGauge = AtomicGauge<double>;
using Labels = std::unordered_map<std::string, std::string>;
struct MetricPrototype {
public:
MetricPrototype(MetricType type_, MetricUnit unit_, std::string name_,
std::string description_ = "", std::string group_name_ = "",
Labels labels_ = Labels(), bool is_core_metric_ = false)
: is_core_metric(is_core_metric_),
type(type_),
unit(unit_),
name(std::move(name_)),
description(std::move(description_)),
group_name(std::move(group_name_)),
labels(std::move(labels_)) {}
std::string simple_name() const;
std::string combine_name(const std::string& registry_name) const;
std::string to_prometheus(const std::string& registry_name) const;
bool is_core_metric;
MetricType type;
MetricUnit unit;
std::string name;
std::string description;
std::string group_name;
Labels labels;
};
#define DEFINE_METRIC_PROTOTYPE(name, type, unit, desc, group, labels, core) \
::doris::MetricPrototype METRIC_##name(type, unit, #name, desc, group, labels, core)
#define DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(name, unit) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::COUNTER, unit, "", "", Labels(), false)
#define DEFINE_COUNTER_METRIC_PROTOTYPE_3ARG(name, unit, desc) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::COUNTER, unit, desc, "", Labels(), false)
#define DEFINE_COUNTER_METRIC_PROTOTYPE_5ARG(name, unit, desc, group, labels) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::COUNTER, unit, desc, #group, labels, false)
#define DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(name, unit) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::GAUGE, unit, "", "", Labels(), false)
#define DEFINE_GAUGE_CORE_METRIC_PROTOTYPE_2ARG(name, unit) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::GAUGE, unit, "", "", Labels(), true)
#define DEFINE_GAUGE_METRIC_PROTOTYPE_3ARG(name, unit, desc) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::GAUGE, unit, desc, "", Labels(), false)
#define DEFINE_GAUGE_METRIC_PROTOTYPE_5ARG(name, unit, desc, group, labels) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::GAUGE, unit, desc, #group, labels, false)
#define DEFINE_HISTOGRAM_METRIC_PROTOTYPE_2ARG(name, unit) \
DEFINE_METRIC_PROTOTYPE(name, MetricType::HISTOGRAM, unit, "", "", Labels(), false)
#define INT_COUNTER_METRIC_REGISTER(entity, metric) \
metric = (IntCounter*)(entity->register_metric<IntCounter>(&METRIC_##metric))
#define INT_GAUGE_METRIC_REGISTER(entity, metric) \
metric = (IntGauge*)(entity->register_metric<IntGauge>(&METRIC_##metric))
#define DOUBLE_GAUGE_METRIC_REGISTER(entity, metric) \
metric = (DoubleGauge*)(entity->register_metric<DoubleGauge>(&METRIC_##metric))
#define INT_UGAUGE_METRIC_REGISTER(entity, metric) \
metric = (UIntGauge*)(entity->register_metric<UIntGauge>(&METRIC_##metric))
#define HISTOGRAM_METRIC_REGISTER(entity, metric) \
metric = (HistogramMetric*)(entity->register_metric<HistogramMetric>(&METRIC_##metric))
#define METRIC_DEREGISTER(entity, metric) entity->deregister_metric(&METRIC_##metric)
// For 'metrics' in MetricEntity.
struct MetricPrototypeHash {
size_t operator()(const MetricPrototype* metric_prototype) const {
return std::hash<std::string>()(metric_prototype->group_name.empty()
? metric_prototype->name
: metric_prototype->group_name);
}
};
struct MetricPrototypeEqualTo {
bool operator()(const MetricPrototype* first, const MetricPrototype* second) const {
return first->group_name == second->group_name && first->name == second->name;
}
};
using MetricMap = std::unordered_map<const MetricPrototype*, Metric*, MetricPrototypeHash,
MetricPrototypeEqualTo>;
enum class MetricEntityType { kServer, kTablet };
class MetricEntity {
public:
MetricEntity(MetricEntityType type, std::string name, Labels labels)
: _type(type), _name(std::move(name)), _labels(std::move(labels)) {}
~MetricEntity() {
for (auto& metric : _metrics) {
delete metric.second;
}
}
const std::string& name() const { return _name; }
template <typename T>
Metric* register_metric(const MetricPrototype* metric_type) {
std::lock_guard<std::mutex> l(_lock);
auto inserted_metric = _metrics.insert(std::make_pair(metric_type, nullptr));
if (inserted_metric.second) {
// If not exist, make a new metric pointer
inserted_metric.first->second = new T();
}
return inserted_metric.first->second;
}
void deregister_metric(const MetricPrototype* metric_type);
Metric* get_metric(const std::string& name, const std::string& group_name = "") const;
// Register a hook, this hook will called before get_metric is called
void register_hook(const std::string& name, const std::function<void()>& hook);
void deregister_hook(const std::string& name);
void trigger_hook_unlocked(bool force) const;
private:
friend class MetricRegistry;
friend struct MetricEntityHash;
friend struct MetricEntityEqualTo;
MetricEntityType _type;
std::string _name;
Labels _labels;
mutable std::mutex _lock;
MetricMap _metrics;
std::map<std::string, std::function<void()>> _hooks;
};
struct MetricEntityHash {
size_t operator()(const std::shared_ptr<MetricEntity> metric_entity) const {
return std::hash<std::string>()(metric_entity->name());
}
};
struct MetricEntityEqualTo {
bool operator()(const std::shared_ptr<MetricEntity> first,
const std::shared_ptr<MetricEntity> second) const {
return first->_type == second->_type && first->_name == second->_name &&
first->_labels == second->_labels;
}
};
using EntityMetricsByType =
std::unordered_map<const MetricPrototype*, std::vector<std::pair<MetricEntity*, Metric*>>,
MetricPrototypeHash, MetricPrototypeEqualTo>;
class MetricRegistry {
public:
MetricRegistry(std::string name) : _name(std::move(name)) {}
~MetricRegistry();
std::shared_ptr<MetricEntity> register_entity(
const std::string& name, const Labels& labels = {},
MetricEntityType type = MetricEntityType::kServer);
void deregister_entity(const std::shared_ptr<MetricEntity>& entity);
std::shared_ptr<MetricEntity> get_entity(const std::string& name, const Labels& labels = {},
MetricEntityType type = MetricEntityType::kServer);
void trigger_all_hooks(bool force) const;
std::string to_prometheus(bool with_tablet_metrics = false) const;
std::string to_json(bool with_tablet_metrics = false) const;
std::string to_core_string() const;
private:
const std::string _name;
mutable std::mutex _lock;
// MetricEntity -> register count
std::unordered_map<std::shared_ptr<MetricEntity>, int32_t, MetricEntityHash,
MetricEntityEqualTo>
_entities;
};
} // namespace doris