// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "telegram-bot-api/Stats.h" #include "td/utils/common.h" #include "td/utils/logging.h" #include "td/utils/port/thread.h" #include "td/utils/SliceBuilder.h" #include "td/utils/StringBuilder.h" namespace telegram_bot_api { ServerCpuStat::ServerCpuStat() { for (std::size_t i = 1; i < SIZE; i++) { stat_[i] = td::TimedStat(DURATIONS[i], td::Time::now()); } } void ServerCpuStat::update(double now) { auto r_cpu_stat = td::cpu_stat(); if (r_cpu_stat.is_error()) { return; } auto &cpu_stat = instance(); std::lock_guard guard(cpu_stat.mutex_); for (auto &stat : cpu_stat.stat_) { stat.add_event(r_cpu_stat.ok(), now); } LOG(WARNING) << "CPU usage: " << cpu_stat.stat_[1].get_stat(now).as_vector()[0].value_; } td::string ServerCpuStat::get_description() { td::string res = "DURATION"; for (auto &descr : DESCR) { res += '\t'; res += descr; } return res; } static td::string to_percentage(td::uint64 ticks, td::uint64 total_ticks) { static double multiplier = 100.0 * (td::thread::hardware_concurrency() ? td::thread::hardware_concurrency() : 1); return PSTRING() << (static_cast(ticks) / static_cast(total_ticks) * multiplier) << '%'; } td::vector CpuStat::as_vector() const { td::vector res; if (cnt_ < 2 || first_.total_ticks_ >= last_.total_ticks_) { res.push_back({"total_cpu", "UNKNOWN"}); res.push_back({"user_cpu", "UNKNOWN"}); res.push_back({"system_cpu", "UNKNOWN"}); } else { auto total_ticks = last_.total_ticks_ - first_.total_ticks_; auto user_ticks = last_.process_user_ticks_ - first_.process_user_ticks_; auto system_ticks = last_.process_system_ticks_ - first_.process_system_ticks_; res.push_back({"total_cpu", to_percentage(user_ticks + system_ticks, total_ticks)}); res.push_back({"user_cpu", to_percentage(user_ticks, total_ticks)}); res.push_back({"system_cpu", to_percentage(system_ticks, total_ticks)}); } return res; } td::vector ServerCpuStat::as_vector(double now) { std::lock_guard guard(mutex_); td::vector res = stat_[0].get_stat(now).as_vector(); for (std::size_t i = 1; i < SIZE; i++) { auto other = stat_[i].get_stat(now).as_vector(); CHECK(other.size() == res.size()); for (size_t j = 0; j < res.size(); j++) { res[j].value_ += "\t"; res[j].value_ += other[j].value_; } } return res; } td::vector> ServerCpuStat::as_json_ready_vector(double now) { std::lock_guard guard(mutex_); td::vector> res; auto first = stat_[0].get_stat(now).as_vector(); auto first_size = first.size(); res.push_back(first); for (std::size_t i = 1; i < SIZE; i++) { auto other = stat_[i].get_stat(now).as_vector(); CHECK(other.size() == first_size); res.push_back(other); } return res; } constexpr int ServerCpuStat::DURATIONS[SIZE]; constexpr const char *ServerCpuStat::DESCR[SIZE]; void ServerBotStat::normalize(double duration) { if (duration == 0) { return; } request_count_ /= duration; request_bytes_ /= duration; request_file_count_ /= duration; request_files_bytes_ /= duration; response_count_ /= duration; response_count_ok_ /= duration; response_count_error_ /= duration; response_bytes_ /= duration; update_count_ /= duration; } void ServerBotStat::add(const ServerBotStat &stat) { request_count_ += stat.request_count_; request_bytes_ += stat.request_bytes_; request_file_count_ += stat.request_file_count_; request_files_bytes_ += stat.request_files_bytes_; request_files_max_bytes_ = td::max(request_files_max_bytes_, stat.request_files_max_bytes_); response_count_ += stat.response_count_; response_count_ok_ += stat.response_count_ok_; response_count_error_ += stat.response_count_error_; response_bytes_ += stat.response_bytes_; update_count_ += stat.update_count_; } td::vector ServerBotStat::as_vector() const { td::vector res; auto add_item = [&res](td::string name, auto value) { res.push_back({std::move(name), td::to_string(value)}); }; add_item("request_count", request_count_); add_item("request_bytes", request_bytes_); add_item("request_file_count", request_file_count_); add_item("request_files_bytes", request_files_bytes_); add_item("request_max_bytes", request_files_max_bytes_); add_item("response_count", response_count_); add_item("response_count_ok", response_count_ok_); add_item("response_count_error", response_count_error_); add_item("response_bytes", response_bytes_); add_item("update_count", update_count_); return res; } td::vector BotStatActor::as_vector(double now) { auto first_sd = stat_[0].stat_duration(now); first_sd.first.normalize(first_sd.second); td::vector res = first_sd.first.as_vector(); for (std::size_t i = 1; i < SIZE; i++) { auto next_sd = stat_[i].stat_duration(now); next_sd.first.normalize(next_sd.second); auto other = next_sd.first.as_vector(); CHECK(other.size() == res.size()); for (size_t j = 0; j < res.size(); j++) { res[j].value_ += "\t"; res[j].value_ += other[j].value_; } } return res; } td::vector BotStatActor::as_json_ready_vector(double now) { std::pair first_sd = stat_[0].stat_duration(now); first_sd.first.normalize(first_sd.second); td::vector res; for (auto & single_stat : stat_) { auto next_sd = single_stat.stat_duration(now); next_sd.first.normalize(next_sd.second); res.push_back(next_sd.first); } return res; } td::string BotStatActor::get_description() { td::string res = "DURATION"; for (auto &descr : DESCR) { res += "\t"; res += descr; } return res; } td::vector BotStatActor::get_jsonable_description() const { td::vector strings; strings.push_back("duration"); for (auto &descr : DESCR) { strings.push_back(descr); } return strings; } double BotStatActor::get_score(double now) { auto minute_stat = stat_[2].stat_duration(now); double minute_score = minute_stat.first.request_count_ + minute_stat.first.update_count_; if (minute_stat.second != 0) { minute_score /= minute_stat.second; } auto all_time_stat = stat_[0].stat_duration(now); double all_time_score = 0.01 * (all_time_stat.first.request_count_ + all_time_stat.first.update_count_); if (all_time_stat.second != 0) { all_time_score /= all_time_stat.second; } auto active_request_score = static_cast(td::max(get_active_request_count() - 10, static_cast(0))); auto active_file_upload_score = static_cast(get_active_file_upload_bytes()) * 1e-8; return minute_score + all_time_score + active_request_score + active_file_upload_score; } double BotStatActor::get_minute_update_count(double now) { auto minute_stat = stat_[2].stat_duration(now); double result = minute_stat.first.update_count_; if (minute_stat.second != 0) { result /= minute_stat.second; } return result; } td::int64 BotStatActor::get_active_request_count() const { return active_request_count_; } td::int64 BotStatActor::get_active_file_upload_bytes() const { return active_file_upload_bytes_; } td::int64 BotStatActor::get_active_file_upload_count() const { return active_file_upload_count_; } bool BotStatActor::is_active(double now) const { return last_activity_timestamp_ > now - 86400; } constexpr int BotStatActor::DURATIONS[SIZE]; constexpr const char *BotStatActor::DESCR[SIZE]; } // namespace telegram_bot_api