diff --git a/src/envoy/mixer/BUILD b/src/envoy/mixer/BUILD index 055fd301661..8b3e0a086d0 100644 --- a/src/envoy/mixer/BUILD +++ b/src/envoy/mixer/BUILD @@ -30,6 +30,8 @@ envoy_cc_library( "http_filter.cc", "mixer_control.cc", "mixer_control.h", + "stats.cc", + "stats.h", "tcp_filter.cc", "utils.cc", "utils.h", diff --git a/src/envoy/mixer/http_filter.cc b/src/envoy/mixer/http_filter.cc index 87b0fd2a828..2e781b602ff 100644 --- a/src/envoy/mixer/http_filter.cc +++ b/src/envoy/mixer/http_filter.cc @@ -115,12 +115,13 @@ class Config { tls_(context.threadLocal().allocateSlot()) { mixer_config_.Load(config); Runtime::RandomGenerator& random = context.random(); - tls_->set( - [this, &random](Event::Dispatcher& dispatcher) - -> ThreadLocal::ThreadLocalObjectSharedPtr { - return ThreadLocal::ThreadLocalObjectSharedPtr( - new HttpMixerControl(mixer_config_, cm_, dispatcher, random)); - }); + Stats::Scope& scope = context.scope(); + tls_->set([this, &random, &scope](Event::Dispatcher& dispatcher) + -> ThreadLocal::ThreadLocalObjectSharedPtr { + return ThreadLocal::ThreadLocalObjectSharedPtr( + new HttpMixerControl(mixer_config_, cm_, dispatcher, + random, scope)); + }); std::vector> issuers; CreateAuthIssuers(mixer_config_, &issuers); diff --git a/src/envoy/mixer/mixer_control.cc b/src/envoy/mixer/mixer_control.cc index 0bfca19e403..5b057310301 100644 --- a/src/envoy/mixer/mixer_control.cc +++ b/src/envoy/mixer/mixer_control.cc @@ -16,11 +16,16 @@ #include "src/envoy/mixer/mixer_control.h" #include "src/envoy/mixer/grpc_transport.h" +using ::istio::mixer_client::Statistics; + namespace Envoy { namespace Http { namespace Mixer { namespace { +const std::string kHttpStatsPrefix("http_mixer_filter."); +const std::string kTcpStatsPrefix("tcp_mixer_filter."); + // A class to wrap envoy timer for mixer client timer. class EnvoyTimer : public ::istio::mixer_client::Timer { public: @@ -59,8 +64,17 @@ void CreateEnvironment(Upstream::ClusterManager& cm, HttpMixerControl::HttpMixerControl(const HttpMixerConfig& mixer_config, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, - Runtime::RandomGenerator& random) - : cm_(cm) { + Runtime::RandomGenerator& random, + Stats::Scope& scope) + : cm_(cm), + stats_obj_(dispatcher, kHttpStatsPrefix, scope, + [this](Statistics* stat) -> bool { + if (!controller_) { + return false; + } + controller_->GetStatistics(stat); + return true; + }) { ::istio::mixer_control::http::Controller::Options options( mixer_config.http_config, mixer_config.legacy_quotas); @@ -74,7 +88,16 @@ HttpMixerControl::HttpMixerControl(const HttpMixerConfig& mixer_config, TcpMixerControl::TcpMixerControl(const TcpMixerConfig& mixer_config, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, - Runtime::RandomGenerator& random) { + Runtime::RandomGenerator& random, + Stats::Scope& scope) + : stats_obj_(dispatcher, kTcpStatsPrefix, scope, + [this](Statistics* stat) -> bool { + if (!controller_) { + return false; + } + controller_->GetStatistics(stat); + return true; + }) { ::istio::mixer_control::tcp::Controller::Options options( mixer_config.tcp_config); diff --git a/src/envoy/mixer/mixer_control.h b/src/envoy/mixer/mixer_control.h index 8fdd63a7e01..7f072e46217 100644 --- a/src/envoy/mixer/mixer_control.h +++ b/src/envoy/mixer/mixer_control.h @@ -22,6 +22,7 @@ #include "envoy/thread_local/thread_local.h" #include "envoy/upstream/cluster_manager.h" #include "src/envoy/mixer/config.h" +#include "src/envoy/mixer/stats.h" namespace Envoy { namespace Http { @@ -32,7 +33,7 @@ class HttpMixerControl final : public ThreadLocal::ThreadLocalObject { // The constructor. HttpMixerControl(const HttpMixerConfig& mixer_config, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, - Runtime::RandomGenerator& random); + Runtime::RandomGenerator& random, Stats::Scope& scope); Upstream::ClusterManager& cm() { return cm_; } @@ -49,6 +50,8 @@ class HttpMixerControl final : public ThreadLocal::ThreadLocalObject { std::unique_ptr<::istio::mixer_control::http::Controller> controller_; // has v2 config; bool has_v2_config_; + + MixerStatsObject stats_obj_; }; class TcpMixerControl final : public ThreadLocal::ThreadLocalObject { @@ -56,7 +59,7 @@ class TcpMixerControl final : public ThreadLocal::ThreadLocalObject { // The constructor. TcpMixerControl(const TcpMixerConfig& mixer_config, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, - Runtime::RandomGenerator& random); + Runtime::RandomGenerator& random, Stats::Scope& scope); ::istio::mixer_control::tcp::Controller* controller() { return controller_.get(); @@ -65,6 +68,8 @@ class TcpMixerControl final : public ThreadLocal::ThreadLocalObject { private: // The mixer control std::unique_ptr<::istio::mixer_control::tcp::Controller> controller_; + + MixerStatsObject stats_obj_; }; } // namespace Mixer diff --git a/src/envoy/mixer/stats.cc b/src/envoy/mixer/stats.cc new file mode 100644 index 00000000000..6796aa851a6 --- /dev/null +++ b/src/envoy/mixer/stats.cc @@ -0,0 +1,100 @@ +/* Copyright 2017 Istio Authors. All Rights Reserved. + * + * Licensed 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. + */ + +#include + +#include "src/envoy/mixer/stats.h" + +namespace Envoy { +namespace Http { +namespace Mixer { +namespace { + +// The time interval for envoy stats update. +const int kStatsUpdateIntervalInMs = 10000; + +} // namespace + +MixerStatsObject::MixerStatsObject(Event::Dispatcher& dispatcher, + const std::string& name, Stats::Scope& scope, + GetStatsFunc func) + : stats_{ALL_MIXER_FILTER_STATS(POOL_COUNTER_PREFIX(scope, name))}, + get_stats_func_(func) { + memset(&old_stats_, 0, sizeof(old_stats_)); + + if (get_stats_func_) { + timer_ = dispatcher.createTimer([this]() { OnTimer(); }); + timer_->enableTimer(std::chrono::milliseconds(kStatsUpdateIntervalInMs)); + } +} + +void MixerStatsObject::OnTimer() { + ::istio::mixer_client::Statistics new_stats; + bool get_stats = get_stats_func_(&new_stats); + if (get_stats) { + CheckAndUpdateStats(new_stats); + } + timer_->enableTimer(std::chrono::milliseconds(kStatsUpdateIntervalInMs)); +} + +void MixerStatsObject::CheckAndUpdateStats( + const ::istio::mixer_client::Statistics& new_stats) { + if (new_stats.total_check_calls > old_stats_.total_check_calls) { + stats_.total_check_calls_.add(new_stats.total_check_calls - + old_stats_.total_check_calls); + } + if (new_stats.total_remote_check_calls > + old_stats_.total_remote_check_calls) { + stats_.total_remote_check_calls_.add(new_stats.total_remote_check_calls - + old_stats_.total_remote_check_calls); + } + if (new_stats.total_blocking_remote_check_calls > + old_stats_.total_blocking_remote_check_calls) { + stats_.total_blocking_remote_check_calls_.add( + new_stats.total_blocking_remote_check_calls - + old_stats_.total_blocking_remote_check_calls); + } + if (new_stats.total_quota_calls > old_stats_.total_quota_calls) { + stats_.total_quota_calls_.add(new_stats.total_quota_calls - + old_stats_.total_quota_calls); + } + if (new_stats.total_remote_quota_calls > + old_stats_.total_remote_quota_calls) { + stats_.total_remote_quota_calls_.add(new_stats.total_remote_quota_calls - + old_stats_.total_remote_quota_calls); + } + if (new_stats.total_blocking_remote_quota_calls > + old_stats_.total_blocking_remote_quota_calls) { + stats_.total_blocking_remote_quota_calls_.add( + new_stats.total_blocking_remote_quota_calls - + old_stats_.total_blocking_remote_quota_calls); + } + if (new_stats.total_report_calls > old_stats_.total_report_calls) { + stats_.total_report_calls_.add(new_stats.total_report_calls - + old_stats_.total_report_calls); + } + if (new_stats.total_remote_report_calls > + old_stats_.total_remote_report_calls) { + stats_.total_remote_report_calls_.add(new_stats.total_remote_report_calls - + old_stats_.total_remote_report_calls); + } + + // Copy new_stats to old_stats_ for next stats update. + old_stats_ = new_stats; +} + +} // namespace Mixer +} // namespace Http +} // namespace Envoy diff --git a/src/envoy/mixer/stats.h b/src/envoy/mixer/stats.h new file mode 100644 index 00000000000..5c2a05e8770 --- /dev/null +++ b/src/envoy/mixer/stats.h @@ -0,0 +1,81 @@ +/* Copyright 2017 Istio Authors. All Rights Reserved. + * + * Licensed 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 "envoy/event/dispatcher.h" +#include "envoy/event/timer.h" +#include "envoy/stats/stats_macros.h" +#include "include/client.h" + +namespace Envoy { +namespace Http { +namespace Mixer { + +/** + * All mixer filter stats. @see stats_macros.h + */ +// clang-format off +#define ALL_MIXER_FILTER_STATS(COUNTER) \ + COUNTER(total_check_calls) \ + COUNTER(total_remote_check_calls) \ + COUNTER(total_blocking_remote_check_calls) \ + COUNTER(total_quota_calls) \ + COUNTER(total_remote_quota_calls) \ + COUNTER(total_blocking_remote_quota_calls) \ + COUNTER(total_report_calls) \ + COUNTER(total_remote_report_calls) +// clang-format on + +/** + * Struct definition for all mixer filter stats. @see stats_macros.h + */ +struct MixerFilterStats { + ALL_MIXER_FILTER_STATS(GENERATE_COUNTER_STRUCT) +}; + +typedef std::function GetStatsFunc; + +// MixerStatsObject maintains statistics for number of check, quota and report +// calls issued by a mixer filter. +class MixerStatsObject { + public: + MixerStatsObject(Event::Dispatcher& dispatcher, const std::string& name, + Stats::Scope& scope, GetStatsFunc func); + + private: + // This function is invoked when timer event fires. + void OnTimer(); + + // Compares old stats with new stats and updates envoy stats. + void CheckAndUpdateStats(const ::istio::mixer_client::Statistics& new_stats); + + // A set of Envoy stats for the number of check, quota and report calls. + MixerFilterStats stats_; + // Stores a function which gets statistics from mixer controller. + GetStatsFunc get_stats_func_; + + // stats from last call to get_stats_func_. This is needed to calculate the + // variances of stats and update envoy stats. + ::istio::mixer_client::Statistics old_stats_; + + // These members are used for creating a timer which update Envoy stats + // periodically. + ::Envoy::Event::TimerPtr timer_; +}; + +} // namespace Mixer +} // namespace Http +} // namespace Envoy diff --git a/src/envoy/mixer/tcp_filter.cc b/src/envoy/mixer/tcp_filter.cc index e9b54bf5037..ff7c0d24168 100644 --- a/src/envoy/mixer/tcp_filter.cc +++ b/src/envoy/mixer/tcp_filter.cc @@ -44,12 +44,13 @@ class TcpConfig : public Logger::Loggable { tls_(context.threadLocal().allocateSlot()) { mixer_config_.Load(config); Runtime::RandomGenerator& random = context.random(); - tls_->set( - [this, &random](Event::Dispatcher& dispatcher) - -> ThreadLocal::ThreadLocalObjectSharedPtr { - return ThreadLocal::ThreadLocalObjectSharedPtr( - new TcpMixerControl(mixer_config_, cm_, dispatcher, random)); - }); + Stats::Scope& scope = context.scope(); + tls_->set([this, &random, &scope](Event::Dispatcher& dispatcher) + -> ThreadLocal::ThreadLocalObjectSharedPtr { + return ThreadLocal::ThreadLocalObjectSharedPtr( + new TcpMixerControl(mixer_config_, cm_, dispatcher, + random, scope)); + }); } TcpMixerControl& mixer_control() { return tls_->getTyped(); }