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 36cacc29086..aede8301c68 100644 --- a/src/envoy/mixer/http_filter.cc +++ b/src/envoy/mixer/http_filter.cc @@ -29,6 +29,7 @@ #include "src/envoy/mixer/config.h" #include "src/envoy/mixer/grpc_transport.h" #include "src/envoy/mixer/mixer_control.h" +#include "src/envoy/mixer/stats.h" #include "src/envoy/mixer/utils.h" #include @@ -36,6 +37,7 @@ #include using ::google::protobuf::util::Status; +using ::istio::mixer_client::Statistics; using HttpCheckData = ::istio::mixer_control::http::CheckData; using HttpHeaderUpdate = ::istio::mixer_control::http::HeaderUpdate; using HttpReportData = ::istio::mixer_control::http::ReportData; @@ -60,6 +62,9 @@ const LowerCaseString kIstioAttributeHeader("x-istio-attributes"); // Referer header const LowerCaseString kRefererHeaderKey("referer"); +// Envoy stats perfix for HTTP filter stats. +const std::string kHttpStatsPrefix("http_mixer_filter."); + // Set of headers excluded from request.headers attribute. const std::set RequestHeaderExclusives = { kIstioAttributeHeader.get(), @@ -86,20 +91,23 @@ class Config : public Logger::Loggable { Upstream::ClusterManager& cm_; HttpMixerConfig mixer_config_; ThreadLocal::SlotPtr tls_; + MixerFilterStats stats_; public: Config(const Json::Object& config, Server::Configuration::FactoryContext& context) : cm_(context.clusterManager()), - tls_(context.threadLocal().allocateSlot()) { + tls_(context.threadLocal().allocateSlot()), + stats_{ALL_MIXER_FILTER_STATS( + POOL_COUNTER_PREFIX(context.scope(), kHttpStatsPrefix))} { 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)); - }); + tls_->set([this, &random](Event::Dispatcher& dispatcher) + -> ThreadLocal::ThreadLocalObjectSharedPtr { + return ThreadLocal::ThreadLocalObjectSharedPtr( + new HttpMixerControl(mixer_config_, cm_, dispatcher, + random, stats_)); + }); } HttpMixerControl& mixer_control() { diff --git a/src/envoy/mixer/mixer_control.cc b/src/envoy/mixer/mixer_control.cc index ce62f509b86..9f207de7206 100644 --- a/src/envoy/mixer/mixer_control.cc +++ b/src/envoy/mixer/mixer_control.cc @@ -15,6 +15,8 @@ #include "src/envoy/mixer/mixer_control.h" +using ::istio::mixer_client::Statistics; + namespace Envoy { namespace Http { namespace Mixer { @@ -66,8 +68,19 @@ void CreateEnvironment(Upstream::ClusterManager& cm, HttpMixerControl::HttpMixerControl(const HttpMixerConfig& mixer_config, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, - Runtime::RandomGenerator& random) - : config_(mixer_config), cm_(cm) { + Runtime::RandomGenerator& random, + MixerFilterStats& stats) + : config_(mixer_config), + cm_(cm), + stats_obj_(dispatcher, stats, + mixer_config.http_config.transport().stats_update_interval(), + [this](Statistics* stat) -> bool { + if (!controller_) { + return false; + } + controller_->GetStatistics(stat); + return true; + }) { ::istio::mixer_control::http::Controller::Options options( mixer_config.http_config); @@ -80,8 +93,19 @@ HttpMixerControl::HttpMixerControl(const HttpMixerConfig& mixer_config, TcpMixerControl::TcpMixerControl(const TcpMixerConfig& mixer_config, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, - Runtime::RandomGenerator& random) - : config_(mixer_config), dispatcher_(dispatcher) { + Runtime::RandomGenerator& random, + MixerFilterStats& stats) + : config_(mixer_config), + dispatcher_(dispatcher), + stats_obj_(dispatcher, stats, + mixer_config.tcp_config.transport().stats_update_interval(), + [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 27dbd8fe411..619636450c1 100644 --- a/src/envoy/mixer/mixer_control.h +++ b/src/envoy/mixer/mixer_control.h @@ -23,6 +23,7 @@ #include "envoy/upstream/cluster_manager.h" #include "src/envoy/mixer/config.h" #include "src/envoy/mixer/grpc_transport.h" +#include "src/envoy/mixer/stats.h" namespace Envoy { namespace Http { @@ -33,7 +34,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, MixerFilterStats& stats); ::istio::mixer_control::http::Controller* controller() { return controller_.get(); @@ -50,6 +51,8 @@ class HttpMixerControl final : public ThreadLocal::ThreadLocalObject { Upstream::ClusterManager& cm_; // The mixer control std::unique_ptr<::istio::mixer_control::http::Controller> controller_; + + MixerStatsObject stats_obj_; }; class TcpMixerControl final : public ThreadLocal::ThreadLocalObject { @@ -57,7 +60,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, MixerFilterStats& stats); ::istio::mixer_control::tcp::Controller* controller() { return controller_.get(); @@ -79,6 +82,8 @@ class TcpMixerControl final : public ThreadLocal::ThreadLocalObject { std::chrono::milliseconds report_interval_ms_; Event::Dispatcher& dispatcher_; + + 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..2500aa053fd --- /dev/null +++ b/src/envoy/mixer/stats.cc @@ -0,0 +1,105 @@ +/* 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, + MixerFilterStats& stats, + ::google::protobuf::Duration update_interval, + GetStatsFunc func) + : stats_(stats), get_stats_func_(func) { + stats_update_interval_ = + update_interval.seconds() * 1000 + update_interval.nanos() / 1000000; + if (stats_update_interval_ <= 0) { + stats_update_interval_ = kStatsUpdateIntervalInMs; + } + memset(&old_stats_, 0, sizeof(old_stats_)); + + if (get_stats_func_) { + timer_ = dispatcher.createTimer([this]() { OnTimer(); }); + timer_->enableTimer(std::chrono::milliseconds(stats_update_interval_)); + } +} + +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(stats_update_interval_)); +} + +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..3e1f403235c --- /dev/null +++ b/src/envoy/mixer/stats.h @@ -0,0 +1,87 @@ +/* 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, MixerFilterStats& stats, + ::google::protobuf::Duration update_interval, + 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_; + + // Time interval at which Envoy stats get updated. If stats update interval + // from config is larger than 0, then store configured interval here. + // Otherwise, set interval to 10 seconds. + int stats_update_interval_; +}; + +} // namespace Mixer +} // namespace Http +} // namespace Envoy diff --git a/src/envoy/mixer/tcp_filter.cc b/src/envoy/mixer/tcp_filter.cc index 837426c5746..7b8410aa8f4 100644 --- a/src/envoy/mixer/tcp_filter.cc +++ b/src/envoy/mixer/tcp_filter.cc @@ -22,34 +22,45 @@ #include "server/config/network/http_connection_manager.h" #include "src/envoy/mixer/config.h" #include "src/envoy/mixer/mixer_control.h" +#include "src/envoy/mixer/stats.h" #include "src/envoy/mixer/utils.h" using ::google::protobuf::util::Status; using StatusCode = ::google::protobuf::util::error::Code; +using ::istio::mixer_client::Statistics; namespace Envoy { namespace Http { namespace Mixer { +namespace { + +// Envoy stats perfix for TCP filter stats. +const std::string kTcpStatsPrefix("tcp_mixer_filter."); + +} // namespace class TcpConfig : public Logger::Loggable { private: Upstream::ClusterManager& cm_; TcpMixerConfig mixer_config_; ThreadLocal::SlotPtr tls_; + MixerFilterStats stats_; public: TcpConfig(const Json::Object& config, Server::Configuration::FactoryContext& context) : cm_(context.clusterManager()), - tls_(context.threadLocal().allocateSlot()) { + tls_(context.threadLocal().allocateSlot()), + stats_{ALL_MIXER_FILTER_STATS( + POOL_COUNTER_PREFIX(context.scope(), kTcpStatsPrefix))} { 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)); - }); + tls_->set([this, &random](Event::Dispatcher& dispatcher) + -> ThreadLocal::ThreadLocalObjectSharedPtr { + return ThreadLocal::ThreadLocalObjectSharedPtr( + new TcpMixerControl(mixer_config_, cm_, dispatcher, + random, stats_)); + }); } TcpMixerControl& mixer_control() { return tls_->getTyped(); }