#include "counters.h"
#include <library/cpp/monlib/service/pages/templates.h>
#include <util/generic/cast.h>
using namespace NMonitoring;
namespace {
TDynamicCounters* AsDynamicCounters(const TIntrusivePtr<TCountableBase>& ptr) {
return dynamic_cast<TDynamicCounters*>(ptr.Get());
}
TCounterForPtr* AsCounter(const TIntrusivePtr<TCountableBase>& ptr) {
return dynamic_cast<TCounterForPtr*>(ptr.Get());
}
TExpiringCounter* AsExpiringCounter(const TIntrusivePtr<TCountableBase>& ptr) {
return dynamic_cast<TExpiringCounter*>(ptr.Get());
}
TExpiringHistogramCounter* AsExpiringHistogramCounter(const TIntrusivePtr<TCountableBase>& ptr) {
return dynamic_cast<TExpiringHistogramCounter*>(ptr.Get());
}
THistogramCounter* AsHistogram(const TIntrusivePtr<TCountableBase>& ptr) {
return dynamic_cast<THistogramCounter*>(ptr.Get());
}
TIntrusivePtr<TCounterForPtr> AsCounterRef(const TIntrusivePtr<TCountableBase>& ptr) {
return VerifyDynamicCast<TCounterForPtr*>(ptr.Get());
}
TIntrusivePtr<TDynamicCounters> AsGroupRef(const TIntrusivePtr<TCountableBase>& ptr) {
return VerifyDynamicCast<TDynamicCounters*>(ptr.Get());
}
THistogramPtr AsHistogramRef(const TIntrusivePtr<TCountableBase>& ptr) {
return VerifyDynamicCast<THistogramCounter*>(ptr.Get());
}
bool IsExpiringCounter(const TIntrusivePtr<TCountableBase>& ptr) {
return AsExpiringCounter(ptr) != nullptr || AsExpiringHistogramCounter(ptr) != nullptr;
}
}
static constexpr TStringBuf INDENT = " ";
TDynamicCounters::TDynamicCounters(EVisibility vis)
{
Visibility_ = vis;
}
TDynamicCounters::~TDynamicCounters() {
}
TDynamicCounters::TCounterPtr TDynamicCounters::GetExpiringCounter(const TString& value, bool derivative, EVisibility vis) {
return GetExpiringNamedCounter("sensor", value, derivative, vis);
}
TDynamicCounters::TCounterPtr TDynamicCounters::GetExpiringNamedCounter(const TString& name, const TString& value, bool derivative, EVisibility vis) {
return AsCounterRef(GetNamedCounterImpl<true, TExpiringCounter>(name, value, derivative, vis));
}
TDynamicCounters::TCounterPtr TDynamicCounters::GetCounter(const TString& value, bool derivative, EVisibility vis) {
return GetNamedCounter("sensor", value, derivative, vis);
}
TDynamicCounters::TCounterPtr TDynamicCounters::GetNamedCounter(const TString& name, const TString& value, bool derivative, EVisibility vis) {
return AsCounterRef(GetNamedCounterImpl<false, TCounterForPtr>(name, value, derivative, vis));
}
THistogramPtr TDynamicCounters::GetHistogram(const TString& value, IHistogramCollectorPtr collector, bool derivative, EVisibility vis) {
return GetNamedHistogram("sensor", value, std::move(collector), derivative, vis);
}
THistogramPtr TDynamicCounters::GetNamedHistogram(const TString& name, const TString& value, IHistogramCollectorPtr collector, bool derivative, EVisibility vis) {
return AsHistogramRef(GetNamedCounterImpl<false, THistogramCounter>(name, value, std::move(collector), derivative, vis));
}
THistogramPtr TDynamicCounters::GetExpiringHistogram(const TString& value, IHistogramCollectorPtr collector, bool derivative, EVisibility vis) {
return GetExpiringNamedHistogram("sensor", value, std::move(collector), derivative, vis);
}
THistogramPtr TDynamicCounters::GetExpiringNamedHistogram(const TString& name, const TString& value, IHistogramCollectorPtr collector, bool derivative, EVisibility vis) {
return AsHistogramRef(GetNamedCounterImpl<true, TExpiringHistogramCounter>(name, value, std::move(collector), derivative, vis));
}
TDynamicCounters::TCounterPtr TDynamicCounters::FindCounter(const TString& value) const {
return FindNamedCounter("sensor", value);
}
TDynamicCounters::TCounterPtr TDynamicCounters::FindNamedCounter(const TString& name, const TString& value) const {
return AsCounterRef(FindNamedCounterImpl<TCounterForPtr>(name, value));
}
THistogramPtr TDynamicCounters::FindHistogram(const TString& value) const {
return FindNamedHistogram("sensor", value);
}
THistogramPtr TDynamicCounters::FindNamedHistogram(const TString& name,const TString& value) const {
return AsHistogramRef(FindNamedCounterImpl<THistogramCounter>(name, value));
}
void TDynamicCounters::RemoveCounter(const TString &value) {
RemoveNamedCounter("sensor", value);
}
bool TDynamicCounters::RemoveNamedCounter(const TString& name, const TString &value) {
auto g = LockForUpdate("RemoveNamedCounter", name, value);
if (const auto it = Counters.find({name, value}); it != Counters.end() && AsCounter(it->second)) {
Counters.erase(it);
}
return Counters.empty();
}
void TDynamicCounters::RemoveSubgroupChain(const std::vector<std::pair<TString, TString>>& chain) {
std::vector<TIntrusivePtr<TDynamicCounters>> basePointers;
basePointers.push_back(this);
for (size_t i = 0; i < chain.size() - 1; ++i) {
const auto& [name, value] = chain[i];
auto& base = basePointers.back();
basePointers.push_back(base->GetSubgroup(name, value));
Y_VERIFY(basePointers.back());
}
for (size_t i = chain.size(); i-- && basePointers[i]->RemoveSubgroup(chain[i].first, chain[i].second); ) {}
}
TIntrusivePtr<TDynamicCounters> TDynamicCounters::GetSubgroup(const TString& name, const TString& value) {
auto res = FindSubgroup(name, value);
if (!res) {
auto g = LockForUpdate("GetSubgroup", name, value);
const TChildId key(name, value);
if (const auto it = Counters.lower_bound(key); it != Counters.end() && it->first == key) {
res = AsGroupRef(it->second);
} else {
res = MakeIntrusive<TDynamicCounters>(this);
Counters.emplace_hint(it, key, res);
}
}
return res;
}
TIntrusivePtr<TDynamicCounters> TDynamicCounters::FindSubgroup(const TString& name, const TString& value) const {
TReadGuard g(Lock);
const auto it = Counters.find({name, value});
return it != Counters.end() ? AsDynamicCounters(it->second) : nullptr;
}
bool TDynamicCounters::RemoveSubgroup(const TString& name, const TString& value) {
auto g = LockForUpdate("RemoveSubgroup", name, value);
if (const auto it = Counters.find({name, value}); it != Counters.end() && AsDynamicCounters(it->second)) {
Counters.erase(it);
}
return Counters.empty();
}
void TDynamicCounters::ReplaceSubgroup(const TString& name, const TString& value, TIntrusivePtr<TDynamicCounters> subgroup) {
auto g = LockForUpdate("ReplaceSubgroup", name, value);
const auto it = Counters.find({name, value});
Y_VERIFY(it != Counters.end() && AsDynamicCounters(it->second));
it->second = std::move(subgroup);
}
void TDynamicCounters::MergeWithSubgroup(const TString& name, const TString& value) {
auto g = LockForUpdate("MergeWithSubgroup", name, value);
auto it = Counters.find({name, value});
Y_VERIFY(it != Counters.end());
TIntrusivePtr<TDynamicCounters> subgroup = AsDynamicCounters(it->second);
Y_VERIFY(subgroup);
Counters.erase(it);
Counters.merge(subgroup->Resign());
AtomicAdd(ExpiringCount, AtomicSwap(&subgroup->ExpiringCount, 0));
}
void TDynamicCounters::ResetCounters(bool derivOnly) {
TReadGuard g(Lock);
for (auto& [key, value] : Counters) {
if (auto counter = AsCounter(value)) {
if (!derivOnly || counter->ForDerivative()) {
*counter = 0;
}
} else if (auto subgroup = AsDynamicCounters(value)) {
subgroup->ResetCounters(derivOnly);
}
}
}
void TDynamicCounters::RegisterCountable(const TString& name, const TString& value, TCountablePtr countable) {
Y_VERIFY(countable);
auto g = LockForUpdate("RegisterCountable", name, value);
const bool inserted = Counters.emplace(TChildId(name, value), std::move(countable)).second;
Y_VERIFY(inserted);
}
void TDynamicCounters::RegisterSubgroup(const TString& name, const TString& value, TIntrusivePtr<TDynamicCounters> subgroup) {
RegisterCountable(name, value, subgroup);
}
void TDynamicCounters::OutputHtml(IOutputStream& os) const {
HTML(os) {
PRE() {
OutputPlainText(os);
}
}
}
void TDynamicCounters::EnumerateSubgroups(const std::function<void(const TString& name, const TString& value)>& output) const {
TReadGuard g(Lock);
for (const auto& [key, value] : Counters) {
if (AsDynamicCounters(value)) {
output(key.LabelName, key.LabelValue);
}
}
}
void TDynamicCounters::OutputPlainText(IOutputStream& os, const TString& indent) const {
auto snap = ReadSnapshot();
// mark private records in plain text output
auto outputVisibilityMarker = [] (EVisibility vis) {
return vis == EVisibility::Private ? "\t[PRIVATE]" : "";
};
for (const auto& [key, value] : snap) {
if (const auto counter = AsCounter(value)) {
os << indent
<< key.LabelName << '=' << key.LabelValue
<< ": " << counter->Val()
<< outputVisibilityMarker(counter->Visibility())
<< '\n';
} else if (const auto histogram = AsHistogram(value)) {
os << indent
<< key.LabelName << '=' << key.LabelValue
<< ":"
<< outputVisibilityMarker(histogram->Visibility())
<< "\n";
auto snapshot = histogram->Snapshot();
for (ui32 i = 0, count = snapshot->Count(); i < count; i++) {
os << indent << INDENT << TStringBuf("bin=");
TBucketBound bound = snapshot->UpperBound(i);
if (bound == Max<TBucketBound>()) {
os << TStringBuf("inf");
} else {
os << bound;
}
os << ": " << snapshot->Value(i) << '\n';
}
}
}
for (const auto& [key, value] : snap) {
if (const auto subgroup = AsDynamicCounters(value)) {
os << "\n";
os << indent << key.LabelName << "=" << key.LabelValue << ":\n";
subgroup->OutputPlainText(os, indent + INDENT);
}
}
}
void TDynamicCounters::Accept(const TString& labelName, const TString& labelValue, ICountableConsumer& consumer) const {
if (!IsVisible(Visibility(), consumer.Visibility())) {
return;
}
consumer.OnGroupBegin(labelName, labelValue, this);
for (auto& [key, value] : ReadSnapshot()) {
value->Accept(key.LabelName, key.LabelValue, consumer);
}
consumer.OnGroupEnd(labelName, labelValue, this);
}
void TDynamicCounters::RemoveExpired() const {
if (AtomicGet(ExpiringCount) == 0) {
return;
}
TWriteGuard g(Lock);
TAtomicBase count = 0;
for (auto it = Counters.begin(); it != Counters.end();) {
if (IsExpiringCounter(it->second) && it->second->RefCount() == 1) {
it = Counters.erase(it);
++count;
} else {
++it;
}
}
AtomicSub(ExpiringCount, count);
}
template <bool expiring, class TCounterType, class... TArgs>
TDynamicCounters::TCountablePtr TDynamicCounters::GetNamedCounterImpl(const TString& name, const TString& value, TArgs&&... args) {
{
TReadGuard g(Lock);
auto it = Counters.find({name, value});
if (it != Counters.end()) {
return it->second;
}
}
auto g = LockForUpdate("GetNamedCounterImpl", name, value);
const TChildId key(name, value);
auto it = Counters.lower_bound(key);
if (it == Counters.end() || it->first != key) {
auto value = MakeIntrusive<TCounterType>(std::forward<TArgs>(args)...);
it = Counters.emplace_hint(it, key, value);
if constexpr (expiring) {
AtomicIncrement(ExpiringCount);
}
}
return it->second;
}
template <class TCounterType>
TDynamicCounters::TCountablePtr TDynamicCounters::FindNamedCounterImpl(const TString& name, const TString& value) const {
TReadGuard g(Lock);
auto it = Counters.find({name, value});
return it != Counters.end() ? it->second : nullptr;
}