diff --git a/docs/configuration/http_conn_man/stats.rst b/docs/configuration/http_conn_man/stats.rst index c7be8974c1a6..49013f1019e9 100644 --- a/docs/configuration/http_conn_man/stats.rst +++ b/docs/configuration/http_conn_man/stats.rst @@ -68,3 +68,19 @@ the following statistics: downstream_cx_total, Counter, Total connections downstream_cx_destroy_remote_active_rq, Counter, Total connections destroyed remotely with 1+ active requests downstream_rq_total, Counter, Total requests + + +Per listener statistics +----------------------- + +Additional per listener statistics are rooted at *listener.
.http..* with the +following statistics: + + .. csv-table:: + :header: Name, Type, Description + :widths: 1, 1, 2 + + downstream_rq_2xx, Counter, Total 2xx responses + downstream_rq_3xx, Counter, Total 3xx responses + downstream_rq_4xx, Counter, Total 4xx responses + downstream_rq_5xx, Counter, Total 5xx responses diff --git a/include/envoy/server/filter_config.h b/include/envoy/server/filter_config.h index 0ee3ea5ac9cd..07de18b30856 100644 --- a/include/envoy/server/filter_config.h +++ b/include/envoy/server/filter_config.h @@ -116,6 +116,11 @@ class FactoryContext { * @return Server::Admin& the server's global admin HTTP endpoint. */ virtual Server::Admin& admin() PURE; + + /** + * @return Stats::Scope& the listener's stats scope. + */ + virtual Stats::Scope& listenerScope() PURE; }; /** diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 5953325b1c91..22cc43a975fc 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -48,6 +48,11 @@ ConnectionManagerTracingStats ConnectionManagerImpl::generateTracingStats(const return {CONN_MAN_TRACING_STATS(POOL_COUNTER_PREFIX(scope, prefix + "tracing."))}; } +ConnectionManagerListenerStats +ConnectionManagerImpl::generateListenerStats(const std::string& prefix, Stats::Scope& scope) { + return {CONN_MAN_LISTENER_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; +} + ConnectionManagerImpl::ConnectionManagerImpl(ConnectionManagerConfig& config, const Network::DrainDecision& drain_close, Runtime::RandomGenerator& random_generator, @@ -57,7 +62,8 @@ ConnectionManagerImpl::ConnectionManagerImpl(ConnectionManagerConfig& config, : config_(config), stats_(config_.stats()), conn_length_(stats_.named_.downstream_cx_length_ms_.allocateSpan()), drain_close_(drain_close), random_generator_(random_generator), tracer_(tracer), - runtime_(runtime), local_info_(local_info), cluster_manager_(cluster_manager) {} + runtime_(runtime), local_info_(local_info), cluster_manager_(cluster_manager), + listener_stats_(config_.listenerStats()) {} void ConnectionManagerImpl::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) { read_callbacks_ = &callbacks; @@ -401,12 +407,16 @@ void ConnectionManagerImpl::ActiveStream::chargeStats(HeaderMap& headers) { if (CodeUtility::is2xx(response_code)) { connection_manager_.stats_.named_.downstream_rq_2xx_.inc(); + connection_manager_.listener_stats_.downstream_rq_2xx_.inc(); } else if (CodeUtility::is3xx(response_code)) { connection_manager_.stats_.named_.downstream_rq_3xx_.inc(); + connection_manager_.listener_stats_.downstream_rq_3xx_.inc(); } else if (CodeUtility::is4xx(response_code)) { connection_manager_.stats_.named_.downstream_rq_4xx_.inc(); + connection_manager_.listener_stats_.downstream_rq_4xx_.inc(); } else if (CodeUtility::is5xx(response_code)) { connection_manager_.stats_.named_.downstream_rq_5xx_.inc(); + connection_manager_.listener_stats_.downstream_rq_5xx_.inc(); } } diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 294686155ab4..164cc075230c 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -75,7 +75,7 @@ namespace Http { COUNTER(downstream_rq_non_relative_path) \ COUNTER(downstream_rq_ws_on_non_ws_route) \ COUNTER(downstream_rq_non_ws_on_ws_route) \ - COUNTER(downstream_rq_too_large) \ + COUNTER(downstream_rq_too_large) \ COUNTER(downstream_rq_2xx) \ COUNTER(downstream_rq_3xx) \ COUNTER(downstream_rq_4xx) \ @@ -128,6 +128,24 @@ struct TracingConnectionManagerConfig { typedef std::unique_ptr TracingConnectionManagerConfigPtr; +/** + * Connection manager per listener stats. @see stats_macros.h + */ +// clang-format off +#define CONN_MAN_LISTENER_STATS(COUNTER) \ + COUNTER(downstream_rq_2xx) \ + COUNTER(downstream_rq_3xx) \ + COUNTER(downstream_rq_4xx) \ + COUNTER(downstream_rq_5xx) +// clang-format on + +/** + * Wrapper struct for connection manager listener stats. @see stats_macros.h + */ +struct ConnectionManagerListenerStats { + CONN_MAN_LISTENER_STATS(GENERATE_COUNTER_STRUCT) +}; + /** * Configuration for how to forward client certs. */ @@ -253,6 +271,11 @@ class ConnectionManagerConfig { * @return tracing config. */ virtual const TracingConnectionManagerConfig* tracingConfig() PURE; + + /** + * @return ConnectionManagerListenerStats& the stats to write to. + */ + virtual ConnectionManagerListenerStats& listenerStats() PURE; }; /** @@ -276,6 +299,8 @@ class ConnectionManagerImpl : Logger::Loggable, Stats::Scope& scope); static void chargeTracingStats(const Tracing::Reason& tracing_reason, ConnectionManagerTracingStats& tracing_stats); + static ConnectionManagerListenerStats generateListenerStats(const std::string& prefix, + Stats::Scope& scope); // Network::ReadFilter Network::FilterStatus onData(Buffer::Instance& data) override; @@ -603,6 +628,7 @@ class ConnectionManagerImpl : Logger::Loggable, Upstream::ClusterManager& cluster_manager_; WebSocket::WsHandlerImplPtr ws_connection_{}; Network::ReadFilterCallbacks* read_callbacks_{}; + ConnectionManagerListenerStats& listener_stats_; }; } // Http diff --git a/source/server/config/network/http_connection_manager.cc b/source/server/config/network/http_connection_manager.cc index ef6db21e7d49..ddb31d8ae932 100644 --- a/source/server/config/network/http_connection_manager.cc +++ b/source/server/config/network/http_connection_manager.cc @@ -124,8 +124,9 @@ HttpConnectionManagerConfig::HttpConnectionManagerConfig( http1_settings_(Http::Utility::parseHttp1Settings(config.http_protocol_options())), drain_timeout_(PROTOBUF_GET_MS_OR_DEFAULT(config, drain_timeout, 5000)), generate_request_id_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, generate_request_id, true)), - - date_provider_(date_provider) { + date_provider_(date_provider), + listener_stats_(Http::ConnectionManagerImpl::generateListenerStats( + stats_prefix_, context_.listenerScope())) { route_config_provider_ = Router::RouteConfigProviderUtil::create( config, context_.runtime(), context_.clusterManager(), context_.scope(), stats_prefix_, diff --git a/source/server/config/network/http_connection_manager.h b/source/server/config/network/http_connection_manager.h index f2ebf0c42551..699b9e80bde3 100644 --- a/source/server/config/network/http_connection_manager.h +++ b/source/server/config/network/http_connection_manager.h @@ -92,6 +92,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, } const Network::Address::Instance& localAddress() override; const Optional& userAgent() override { return user_agent_; } + Http::ConnectionManagerListenerStats& listenerStats() override { return listener_stats_; } static const std::string DEFAULT_SERVER_STRING; @@ -119,6 +120,7 @@ class HttpConnectionManagerConfig : Logger::Loggable, std::chrono::milliseconds drain_timeout_; bool generate_request_id_; Http::DateProvider& date_provider_; + Http::ConnectionManagerListenerStats listener_stats_; }; } // namespace Configuration diff --git a/source/server/http/admin.cc b/source/server/http/admin.cc index ec9e1e65f34a..5595c7ac2eb1 100644 --- a/source/server/http/admin.cc +++ b/source/server/http/admin.cc @@ -362,7 +362,9 @@ AdminImpl::AdminImpl(const std::string& access_log_path, const std::string& prof MAKE_ADMIN_HANDLER(handlerServerInfo), false}, {"/stats", "print server stats", MAKE_ADMIN_HANDLER(handlerStats), false}, {"/listeners", "print listener addresses", MAKE_ADMIN_HANDLER(handlerListenerInfo), - false}} { + false}}, + listener_stats_( + Http::ConnectionManagerImpl::generateListenerStats("http.admin.", server_.stats())) { if (!address_out_path.empty()) { std::ofstream address_out_file(address_out_path); diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 74389afe165d..a5d38a2997f4 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -78,6 +78,7 @@ class AdminImpl : public Admin, const Network::Address::Instance& localAddress() override; const Optional& userAgent() override { return user_agent_; } const Http::TracingConnectionManagerConfig* tracingConfig() override { return nullptr; } + Http::ConnectionManagerListenerStats& listenerStats() override { return listener_stats_; } private: /** @@ -143,6 +144,7 @@ class AdminImpl : public Admin, Optional user_agent_; Http::SlowDateProviderImpl date_provider_; std::vector set_current_client_cert_details_; + Http::ConnectionManagerListenerStats listener_stats_; }; /** diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index 8a3d1898a09b..a60f6956326b 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -74,7 +74,8 @@ class HttpConnectionManagerImplTest : public Test, public ConnectionManagerConfi POOL_TIMER(fake_stats_))}, "", fake_stats_}, - tracing_stats_{CONN_MAN_TRACING_STATS(POOL_COUNTER(fake_stats_))} { + tracing_stats_{CONN_MAN_TRACING_STATS(POOL_COUNTER(fake_stats_))}, + listener_stats_{CONN_MAN_LISTENER_STATS(POOL_COUNTER(fake_listener_stats_))} { tracing_config_.reset(new TracingConnectionManagerConfig( {Tracing::OperationName::Ingress, {LowerCaseString(":method")}})); @@ -234,6 +235,7 @@ class HttpConnectionManagerImplTest : public Test, public ConnectionManagerConfi const Network::Address::Instance& localAddress() override { return local_address_; } const Optional& userAgent() override { return user_agent_; } const TracingConnectionManagerConfig* tracingConfig() override { return tracing_config_.get(); } + ConnectionManagerListenerStats& listenerStats() override { return listener_stats_; } NiceMock tracer_; NiceMock runtime_; @@ -267,6 +269,8 @@ class HttpConnectionManagerImplTest : public Test, public ConnectionManagerConfi NiceMock cluster_manager_; uint32_t initial_buffer_limit_{}; bool streaming_filter_{false}; + Stats::IsolatedStoreImpl fake_listener_stats_; + ConnectionManagerListenerStats listener_stats_; // TODO(mattklein123): Not all tests have been converted over to better setup. Convert the rest. MockStreamEncoder response_encoder_; @@ -333,6 +337,7 @@ TEST_F(HttpConnectionManagerImplTest, HeaderOnlyRequestAndResponse) { conn_manager_->onData(fake_input); EXPECT_EQ(1U, stats_.named_.downstream_rq_2xx_.value()); + EXPECT_EQ(1U, listener_stats_.downstream_rq_2xx_.value()); } TEST_F(HttpConnectionManagerImplTest, InvalidPathWithDualFilter) { @@ -763,6 +768,7 @@ TEST_F(HttpConnectionManagerImplTest, DrainClose) { EXPECT_EQ(1U, stats_.named_.downstream_cx_drain_close_.value()); EXPECT_EQ(1U, stats_.named_.downstream_rq_3xx_.value()); + EXPECT_EQ(1U, listener_stats_.downstream_rq_3xx_.value()); } TEST_F(HttpConnectionManagerImplTest, ResponseBeforeRequestComplete) { diff --git a/test/mocks/http/mocks.h b/test/mocks/http/mocks.h index 0957d879e5df..9479e552f95b 100644 --- a/test/mocks/http/mocks.h +++ b/test/mocks/http/mocks.h @@ -103,6 +103,7 @@ class MockConnectionManagerConfig : public ConnectionManagerConfig { MOCK_METHOD0(localAddress, const Network::Address::Instance&()); MOCK_METHOD0(userAgent, const Optional&()); MOCK_METHOD0(tracingConfig, const Http::TracingConnectionManagerConfig*()); + MOCK_METHOD0(listenerStats, ConnectionManagerListenerStats&()); }; class MockConnectionCallbacks : public virtual ConnectionCallbacks { diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index 8147150b6aec..f7fed8ae73f0 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -131,6 +131,7 @@ MockFactoryContext::MockFactoryContext() : singleton_manager_(new Singleton::Man ON_CALL(*this, singletonManager()).WillByDefault(ReturnRef(*singleton_manager_)); ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, admin()).WillByDefault(ReturnRef(admin_)); + ON_CALL(*this, listenerScope()).WillByDefault(ReturnRef(listener_scope_)); } MockFactoryContext::~MockFactoryContext() {} diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index 00af46e34df0..137970aa87e5 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -338,6 +338,7 @@ class MockFactoryContext : public FactoryContext { MOCK_METHOD0(singletonManager, Singleton::Manager&()); MOCK_METHOD0(threadLocal, ThreadLocal::Instance&()); MOCK_METHOD0(admin, Server::Admin&()); + MOCK_METHOD0(listenerScope, Stats::Scope&()); testing::NiceMock access_log_manager_; testing::NiceMock cluster_manager_; @@ -352,6 +353,7 @@ class MockFactoryContext : public FactoryContext { testing::NiceMock thread_local_; Singleton::ManagerPtr singleton_manager_; testing::NiceMock admin_; + Stats::IsolatedStoreImpl listener_scope_; }; } // namespace Configuration