diff --git a/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto b/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto index e5bbe7f1e0c6..c9eb7316b60e 100644 --- a/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto +++ b/api/envoy/extensions/filters/udp/udp_proxy/v3/udp_proxy.proto @@ -26,7 +26,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#extension: envoy.filters.udp_listener.udp_proxy] // Configuration for the UDP proxy filter. -// [#next-free-field: 10] +// [#next-free-field: 11] message UdpProxyConfig { option (udpa.annotations.versioning).previous_message_type = "envoy.config.filter.udp.udp_proxy.v2alpha.UdpProxyConfig"; @@ -105,6 +105,9 @@ message UdpProxyConfig { // to upstream host selected on first chunk receival for that "session" (identified by source IP/port and local IP/port). bool use_per_packet_load_balancing = 7; - // Configuration for access logs emitted by the UDP proxy. Note that certain UDP specific data is emitted as :ref:`Dynamic Metadata `. + // Configuration for session access logs emitted by the UDP proxy. Note that certain UDP specific data is emitted as :ref:`Dynamic Metadata `. repeated config.accesslog.v3.AccessLog access_log = 8; + + // Configuration for proxy access logs emitted by the UDP proxy. Note that certain UDP specific data is emitted as :ref:`Dynamic Metadata `. + repeated config.accesslog.v3.AccessLog proxy_access_log = 10; } diff --git a/changelogs/current.yaml b/changelogs/current.yaml index fec25a1a3dfd..55e829e05079 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -71,5 +71,8 @@ new_features: - area: http change: | allowing the dynamic forward proxy cluster to :ref:`allow_coalesced_connections ` for HTTP/2 and HTTP/3 connections. +- area: udp_proxy + change: | + added support for :ref:`proxy_access_log `. deprecated: diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 5d9907cfd748..38211dbdee20 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -650,28 +650,50 @@ The following command operators are supported: UDP For :ref:`UDP Proxy `, - NAMESPACE should be always set to "udp.proxy", optional KEYs are as follows: + when NAMESPACE is set to "udp.proxy.session", optional KEYs are as follows: * ``cluster_name``: Name of the cluster. * ``bytes_sent``: Total number of downstream bytes sent to the upstream in the session. * ``bytes_received``: Total number of downstream bytes received from the upstream in the session. * ``errors_sent``: Number of errors that have occurred when sending datagrams to the upstream in the session. - * ``errors_received``: Number of errors that have occurred when receiving datagrams from the upstream in UDP proxy. - Since the receiving errors are counted in at the listener level (vs. the session), this counter is global to all sessions and may not be directly attributable to the session being logged. * ``datagrams_sent``: Number of datagrams sent to the upstream successfully in the session. * ``datagrams_received``: Number of datagrams received from the upstream successfully in the session. - Recommended access log format for UDP proxy: + Recommended session access log format for UDP proxy: + + .. code-block:: none + + [%START_TIME%] %DYNAMIC_METADATA(udp.proxy.session:cluster_name)% + %DYNAMIC_METADATA(udp.proxy.session:bytes_sent)% + %DYNAMIC_METADATA(udp.proxy.session:bytes_received)% + %DYNAMIC_METADATA(udp.proxy.session:errors_sent)% + %DYNAMIC_METADATA(udp.proxy.session:datagrams_sent)% + %DYNAMIC_METADATA(udp.proxy.session:datagrams_received)%\n + + when NAMESPACE is set to "udp.proxy.proxy", optional KEYs are as follows: + + * ``bytes_sent``: Total number of downstream bytes sent to the upstream in UDP proxy. + * ``bytes_received``: Total number of downstream bytes received from the upstream in UDP proxy. + * ``errors_sent``: Number of errors that have occurred when sending datagrams to the upstream in UDP proxy. + * ``errors_received``: Number of errors that have occurred when receiving datagrams from the upstream in UDP proxy. + * ``datagrams_sent``: Number of datagrams sent to the upstream successfully in UDP proxy. + * ``datagrams_received``: Number of datagrams received from the upstream successfully in UDP proxy. + * ``no_route``: Number of times that no upstream cluster found in UDP proxy. + * ``session_total``: Total number of sessions in UDP proxy. + * ``idle_timeout``: Number of times that sessions idle timeout occurred in UDP proxy. + + Recommended proxy access log format for UDP proxy: .. code-block:: none - [%START_TIME%] %DYNAMIC_METADATA(udp.proxy:cluster_name)% - %DYNAMIC_METADATA(udp.proxy:bytes_sent)% - %DYNAMIC_METADATA(udp.proxy:bytes_received)% - %DYNAMIC_METADATA(udp.proxy:errors_sent)% - %DYNAMIC_METADATA(udp.proxy:errors_received)% - %DYNAMIC_METADATA(udp.proxy:datagrams_sent)% - %DYNAMIC_METADATA(udp.proxy:datagrams_received)%\n + [%START_TIME%] + %DYNAMIC_METADATA(udp.proxy.proxy:bytes_sent)% + %DYNAMIC_METADATA(udp.proxy.proxy:bytes_received)% + %DYNAMIC_METADATA(udp.proxy.proxy:errors_sent)% + %DYNAMIC_METADATA(udp.proxy.proxy:errors_received)% + %DYNAMIC_METADATA(udp.proxy.proxy:datagrams_sent)% + %DYNAMIC_METADATA(udp.proxy.proxy:datagrams_received)% + %DYNAMIC_METADATA(udp.proxy.proxy:session_total)%\n THRIFT For :ref:`Thrift Proxy `, diff --git a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc index db9db3a84141..118d206bc786 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.cc @@ -20,6 +20,19 @@ UdpProxyFilter::UdpProxyFilter(Network::UdpReadFilterCallbacks& callbacks, onClusterAddOrUpdate(*cluster); } } + + if (!config_->proxyAccessLogs().empty()) { + udp_proxy_stats_.emplace(StreamInfo::StreamInfoImpl(config_->timeSource(), nullptr)); + } +} + +UdpProxyFilter::~UdpProxyFilter() { + if (!config_->proxyAccessLogs().empty()) { + fillProxyStreamInfo(); + for (const auto& access_log : config_->proxyAccessLogs()) { + access_log->log(nullptr, nullptr, nullptr, udp_proxy_stats_.value()); + } + } } void UdpProxyFilter::onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) { @@ -234,8 +247,8 @@ UdpProxyFilter::ActiveSession::ActiveSession(ClusterInfo& cluster, // NOTE: The socket call can only fail due to memory/fd exhaustion. No local ephemeral port // is bound until the first packet is sent to the upstream host. socket_(cluster.filter_.createSocket(host)) { - if (!cluster_.filter_.config_->accessLogs().empty()) { - udp_sess_stats_.emplace( + if (!cluster_.filter_.config_->sessionAccessLogs().empty()) { + udp_session_stats_.emplace( StreamInfo::StreamInfoImpl(cluster_.filter_.config_->timeSource(), nullptr)); } @@ -281,29 +294,51 @@ UdpProxyFilter::ActiveSession::~ActiveSession() { .connections() .dec(); - if (!cluster_.filter_.config_->accessLogs().empty()) { - fillStreamInfo(); - for (const auto& access_log : cluster_.filter_.config_->accessLogs()) { - access_log->log(nullptr, nullptr, nullptr, udp_sess_stats_.value()); + if (!cluster_.filter_.config_->sessionAccessLogs().empty()) { + fillSessionStreamInfo(); + for (const auto& access_log : cluster_.filter_.config_->sessionAccessLogs()) { + access_log->log(nullptr, nullptr, nullptr, udp_session_stats_.value()); } } } -void UdpProxyFilter::ActiveSession::fillStreamInfo() { +void UdpProxyFilter::ActiveSession::fillSessionStreamInfo() { ProtobufWkt::Struct stats_obj; auto& fields_map = *stats_obj.mutable_fields(); fields_map["cluster_name"] = ValueUtil::stringValue(cluster_.cluster_.info()->name()); fields_map["bytes_sent"] = ValueUtil::numberValue(session_stats_.downstream_sess_tx_bytes_); fields_map["bytes_received"] = ValueUtil::numberValue(session_stats_.downstream_sess_rx_bytes_); fields_map["errors_sent"] = ValueUtil::numberValue(session_stats_.downstream_sess_tx_errors_); - fields_map["errors_received"] = - ValueUtil::numberValue(cluster_.filter_.config_->stats().downstream_sess_rx_errors_.value()); fields_map["datagrams_sent"] = ValueUtil::numberValue(session_stats_.downstream_sess_tx_datagrams_); fields_map["datagrams_received"] = ValueUtil::numberValue(session_stats_.downstream_sess_rx_datagrams_); - udp_sess_stats_.value().setDynamicMetadata("udp.proxy", stats_obj); + udp_session_stats_.value().setDynamicMetadata("udp.proxy.session", stats_obj); +} + +void UdpProxyFilter::fillProxyStreamInfo() { + ProtobufWkt::Struct stats_obj; + auto& fields_map = *stats_obj.mutable_fields(); + fields_map["bytes_sent"] = + ValueUtil::numberValue(config_->stats().downstream_sess_tx_bytes_.value()); + fields_map["bytes_received"] = + ValueUtil::numberValue(config_->stats().downstream_sess_rx_bytes_.value()); + fields_map["errors_sent"] = + ValueUtil::numberValue(config_->stats().downstream_sess_tx_errors_.value()); + fields_map["errors_received"] = + ValueUtil::numberValue(config_->stats().downstream_sess_rx_errors_.value()); + fields_map["datagrams_sent"] = + ValueUtil::numberValue(config_->stats().downstream_sess_tx_datagrams_.value()); + fields_map["datagrams_received"] = + ValueUtil::numberValue(config_->stats().downstream_sess_rx_datagrams_.value()); + fields_map["no_route"] = + ValueUtil::numberValue(config_->stats().downstream_sess_no_route_.value()); + fields_map["session_total"] = + ValueUtil::numberValue(config_->stats().downstream_sess_total_.value()); + fields_map["idle_timeout"] = ValueUtil::numberValue(config_->stats().idle_timeout_.value()); + + udp_proxy_stats_.value().setDynamicMetadata("udp.proxy.proxy", stats_obj); } void UdpProxyFilter::ActiveSession::onIdleTimer() { diff --git a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h index 77049babe030..e44edf9c5492 100644 --- a/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h +++ b/source/extensions/filters/udp/udp_proxy/udp_proxy_filter.h @@ -25,8 +25,6 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" -// TODO(mattklein123): UDP session access logging. - namespace Envoy { namespace Extensions { namespace UdpFilters { @@ -90,9 +88,15 @@ class UdpProxyFilterConfig { "is not running with the CAP_NET_ADMIN capability."); } - access_logs_.reserve(config.access_log_size()); + session_access_logs_.reserve(config.access_log_size()); for (const envoy::config::accesslog::v3::AccessLog& log_config : config.access_log()) { - access_logs_.emplace_back(AccessLog::AccessLogFactory::fromProto(log_config, context)); + session_access_logs_.emplace_back( + AccessLog::AccessLogFactory::fromProto(log_config, context)); + } + + proxy_access_logs_.reserve(config.proxy_access_log_size()); + for (const envoy::config::accesslog::v3::AccessLog& log_config : config.proxy_access_log()) { + proxy_access_logs_.emplace_back(AccessLog::AccessLogFactory::fromProto(log_config, context)); } if (!config.hash_policies().empty()) { @@ -116,7 +120,12 @@ class UdpProxyFilterConfig { const Network::ResolvedUdpSocketConfig& upstreamSocketConfig() const { return upstream_socket_config_; } - const std::vector& accessLogs() const { return access_logs_; } + const std::vector& sessionAccessLogs() const { + return session_access_logs_; + } + const std::vector& proxyAccessLogs() const { + return proxy_access_logs_; + } private: static UdpProxyDownstreamStats generateStats(const std::string& stat_prefix, @@ -135,7 +144,8 @@ class UdpProxyFilterConfig { std::unique_ptr hash_policy_; mutable UdpProxyDownstreamStats stats_; const Network::ResolvedUdpSocketConfig upstream_socket_config_; - std::vector access_logs_; + std::vector session_access_logs_; + std::vector proxy_access_logs_; Random::RandomGenerator& random_; }; @@ -165,6 +175,7 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, public: UdpProxyFilter(Network::UdpReadFilterCallbacks& callbacks, const UdpProxyFilterConfigSharedPtr& config); + ~UdpProxyFilter() override; // Network::UdpListenerReadFilter Network::FilterStatus onData(Network::UdpRecvData& data) override; @@ -193,7 +204,7 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, private: void onIdleTimer(); void onReadReady(); - void fillStreamInfo(); + void fillSessionStreamInfo(); // Network::UdpPacketProcessor void processPacket(Network::Address::InstanceConstSharedPtr local_address, @@ -242,7 +253,7 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, bool skip_connect_{}; UdpProxySessionStats session_stats_{}; - absl::optional udp_sess_stats_; + absl::optional udp_session_stats_; }; using ActiveSessionPtr = std::unique_ptr; @@ -380,6 +391,8 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, nullptr, Network::SocketCreationOptions{}); } + void fillProxyStreamInfo(); + // Upstream::ClusterUpdateCallbacks void onClusterAddOrUpdate(Upstream::ThreadLocalCluster& cluster) final; void onClusterRemoval(const std::string& cluster_name) override; @@ -388,6 +401,8 @@ class UdpProxyFilter : public Network::UdpListenerReadFilter, const Upstream::ClusterUpdateCallbacksHandlePtr cluster_update_callbacks_; // Map for looking up cluster info with its name. absl::flat_hash_map cluster_infos_; + + absl::optional udp_proxy_stats_; }; } // namespace UdpProxy diff --git a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc index ab418e711b65..6fae57176a9b 100644 --- a/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc +++ b/test/extensions/filters/udp/udp_proxy/udp_proxy_filter_test.cc @@ -182,7 +182,8 @@ class UdpProxyFilterTest : public testing::Test { peer_address_(std::move(peer_address)) { // Disable strict mock warnings. ON_CALL(*factory_context_.access_log_manager_.file_, write(_)) - .WillByDefault(SaveArg<0>(&access_log_data_)); + .WillByDefault( + Invoke([&](absl::string_view data) { output_.push_back(std::string(data)); })); ON_CALL(os_sys_calls_, supportsIpTransparent()).WillByDefault(Return(true)); EXPECT_CALL(os_sys_calls_, supportsUdpGro()).Times(AtLeast(0)).WillRepeatedly(Return(true)); EXPECT_CALL(callbacks_, udpListener()).Times(AtLeast(0)); @@ -290,16 +291,31 @@ use_original_src_ip: true // Return the config from yaml, plus one file access log with the specified format envoy::extensions::filters::udp::udp_proxy::v3::UdpProxyConfig - accessLogConfig(const std::string& yaml, const std::string& access_log_format) { + accessLogConfig(const std::string& yaml, const std::string& session_access_log_format, + const std::string& proxy_access_log_format) { auto config = readConfig(yaml); - envoy::config::accesslog::v3::AccessLog* access_log = config.mutable_access_log()->Add(); - access_log->set_name("envoy.access_loggers.file"); - envoy::extensions::access_loggers::file::v3::FileAccessLog file_access_log; - file_access_log.set_path("unused"); - file_access_log.mutable_log_format()->mutable_text_format_source()->set_inline_string( - access_log_format); - access_log->mutable_typed_config()->PackFrom(file_access_log); + if (!session_access_log_format.empty()) { + envoy::config::accesslog::v3::AccessLog* session_access_log = + config.mutable_access_log()->Add(); + session_access_log->set_name("envoy.access_loggers.file"); + envoy::extensions::access_loggers::file::v3::FileAccessLog session_file_access_log; + session_file_access_log.set_path("unused"); + session_file_access_log.mutable_log_format()->mutable_text_format_source()->set_inline_string( + session_access_log_format); + session_access_log->mutable_typed_config()->PackFrom(session_file_access_log); + } + + if (!proxy_access_log_format.empty()) { + envoy::config::accesslog::v3::AccessLog* proxy_access_log = + config.mutable_proxy_access_log()->Add(); + proxy_access_log->set_name("envoy.access_loggers.file"); + envoy::extensions::access_loggers::file::v3::FileAccessLog proxy_file_access_log; + proxy_file_access_log.set_path("unused"); + proxy_file_access_log.mutable_log_format()->mutable_text_format_source()->set_inline_string( + proxy_access_log_format); + proxy_access_log->mutable_typed_config()->PackFrom(proxy_file_access_log); + } return config; } @@ -329,6 +345,7 @@ use_original_src_ip: true std::unique_ptr filter_; std::vector test_sessions_; StringViewSaver access_log_data_; + std::vector output_; bool expect_gro_{}; const Network::Address::InstanceConstSharedPtr upstream_address_; const Network::Address::InstanceConstSharedPtr peer_address_; @@ -383,10 +400,20 @@ class UdpProxyFilterIpv4Ipv6Test : public UdpProxyFilterIpv6Test { TEST_F(UdpProxyFilterTest, BasicFlow) { InSequence s; - const std::string access_log_format = "%DYNAMIC_METADATA(udp.proxy:bytes_received)% " - "%DYNAMIC_METADATA(udp.proxy:datagrams_received)% " - "%DYNAMIC_METADATA(udp.proxy:bytes_sent)% " - "%DYNAMIC_METADATA(udp.proxy:datagrams_sent)%"; + const std::string session_access_log_format = + "%DYNAMIC_METADATA(udp.proxy.session:bytes_received)% " + "%DYNAMIC_METADATA(udp.proxy.session:datagrams_received)% " + "%DYNAMIC_METADATA(udp.proxy.session:bytes_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:datagrams_sent)%"; + + const std::string proxy_access_log_format = + "%DYNAMIC_METADATA(udp.proxy.proxy:bytes_received)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:datagrams_received)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:bytes_sent)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:datagrams_sent)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:no_route)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:session_total)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:idle_timeout)%"; setup(accessLogConfig(R"EOF( stat_prefix: foo @@ -400,7 +427,7 @@ stat_prefix: foo upstream_socket_config: prefer_gro: false )EOF", - access_log_format), + session_access_log_format, proxy_access_log_format), true, false); expectSessionCreate(upstream_address_); @@ -425,7 +452,9 @@ stat_prefix: foo checkTransferStats(17 /*rx_bytes*/, 3 /*rx_datagrams*/, 17 /*tx_bytes*/, 3 /*tx_datagrams*/); filter_.reset(); - EXPECT_EQ(access_log_data_.value(), "17 3 17 3"); + EXPECT_EQ(output_.size(), 2); + EXPECT_EQ(output_.front(), "17 3 17 3 0 1 0"); + EXPECT_EQ(output_.back(), "17 3 17 3"); } // Route with source IP. @@ -475,7 +504,12 @@ stat_prefix: foo TEST_F(UdpProxyFilterTest, IdleTimeout) { InSequence s; - setup(readConfig(R"EOF( + const std::string session_access_log_format = ""; + + const std::string proxy_access_log_format = "%DYNAMIC_METADATA(udp.proxy.proxy:session_total)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:idle_timeout)%"; + + setup(accessLogConfig(R"EOF( stat_prefix: foo matcher: on_no_match: @@ -484,7 +518,8 @@ stat_prefix: foo typed_config: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route cluster: fake_cluster - )EOF")); + )EOF", + session_access_log_format, proxy_access_log_format)); expectSessionCreate(upstream_address_); test_sessions_[0].expectWriteToUpstream("hello", 0, nullptr, true); @@ -501,19 +536,28 @@ stat_prefix: foo recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); EXPECT_EQ(2, config_->stats().downstream_sess_total_.value()); EXPECT_EQ(1, config_->stats().downstream_sess_active_.value()); + + filter_.reset(); + EXPECT_EQ(output_.size(), 1); + EXPECT_EQ(output_.front(), "2 1"); } // Verify downstream send and receive error handling. TEST_F(UdpProxyFilterTest, SendReceiveErrorHandling) { InSequence s; - const std::string access_log_format = "%DYNAMIC_METADATA(udp.proxy:cluster_name)% " - "%DYNAMIC_METADATA(udp.proxy:bytes_sent)% " - "%DYNAMIC_METADATA(udp.proxy:bytes_received)% " - "%DYNAMIC_METADATA(udp.proxy:errors_sent)% " - "%DYNAMIC_METADATA(udp.proxy:errors_received)% " - "%DYNAMIC_METADATA(udp.proxy:datagrams_sent)% " - "%DYNAMIC_METADATA(udp.proxy:datagrams_received)%"; + const std::string session_access_log_format = + "%DYNAMIC_METADATA(udp.proxy.session:cluster_name)% " + "%DYNAMIC_METADATA(udp.proxy.session:bytes_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:bytes_received)% " + "%DYNAMIC_METADATA(udp.proxy.session:errors_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:datagrams_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:datagrams_received)%"; + + const std::string proxy_access_log_format = "%DYNAMIC_METADATA(udp.proxy.proxy:errors_received)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:no_route)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:session_total)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:idle_timeout)%"; setup(accessLogConfig(R"EOF( stat_prefix: foo @@ -525,7 +569,7 @@ stat_prefix: foo '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route cluster: fake_cluster )EOF", - access_log_format)); + session_access_log_format, proxy_access_log_format)); filter_->onReceiveError(Api::IoError::IoErrorCode::UnknownError); EXPECT_EQ(1, config_->stats().downstream_sess_rx_errors_.value()); @@ -565,20 +609,25 @@ stat_prefix: foo ->value()); filter_.reset(); - EXPECT_EQ(access_log_data_.value(), "fake_cluster 0 10 1 1 0 2"); + EXPECT_EQ(output_.size(), 2); + EXPECT_EQ(output_.front(), "1 0 1 0"); + EXPECT_EQ(output_.back(), "fake_cluster 0 10 1 0 2"); } // Verify upstream connect error handling. TEST_F(UdpProxyFilterTest, ConnectErrorHandling) { InSequence s; - const std::string access_log_format = "%DYNAMIC_METADATA(udp.proxy:cluster_name)% " - "%DYNAMIC_METADATA(udp.proxy:bytes_sent)% " - "%DYNAMIC_METADATA(udp.proxy:bytes_received)% " - "%DYNAMIC_METADATA(udp.proxy:errors_sent)% " - "%DYNAMIC_METADATA(udp.proxy:errors_received)% " - "%DYNAMIC_METADATA(udp.proxy:datagrams_sent)% " - "%DYNAMIC_METADATA(udp.proxy:datagrams_received)%"; + const std::string session_access_log_format = + "%DYNAMIC_METADATA(udp.proxy.session:cluster_name)% " + "%DYNAMIC_METADATA(udp.proxy.session:bytes_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:bytes_received)% " + "%DYNAMIC_METADATA(udp.proxy.session:errors_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:datagrams_sent)% " + "%DYNAMIC_METADATA(udp.proxy.session:datagrams_received)%"; + + const std::string proxy_access_log_format = "%DYNAMIC_METADATA(udp.proxy.proxy:errors_received)% " + "%DYNAMIC_METADATA(udp.proxy.proxy:session_total)%"; setup(accessLogConfig(R"EOF( stat_prefix: foo @@ -590,7 +639,7 @@ stat_prefix: foo '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route cluster: fake_cluster )EOF", - access_log_format)); + session_access_log_format, proxy_access_log_format)); expectSessionCreate(upstream_address_); test_sessions_[0].expectWriteToUpstream("hello", 0, nullptr, true, 1); @@ -604,7 +653,9 @@ stat_prefix: foo ->value()); filter_.reset(); - EXPECT_EQ(access_log_data_.value(), "fake_cluster 0 5 0 0 0 1"); + EXPECT_EQ(output_.size(), 2); + EXPECT_EQ(output_.front(), "0 1"); + EXPECT_EQ(output_.back(), "fake_cluster 0 5 0 0 1"); } // No upstream host handling. @@ -633,7 +684,11 @@ stat_prefix: foo TEST_F(UdpProxyFilterTest, NoUpstreamClusterAtCreation) { InSequence s; - setup(readConfig(R"EOF( + const std::string session_access_log_format = ""; + + const std::string proxy_access_log_format = "%DYNAMIC_METADATA(udp.proxy.proxy:no_route)%"; + + setup(accessLogConfig(R"EOF( stat_prefix: foo matcher: on_no_match: @@ -642,11 +697,16 @@ stat_prefix: foo typed_config: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route cluster: fake_cluster - )EOF"), + )EOF", + session_access_log_format, proxy_access_log_format), false); recvDataFromDownstream("10.0.0.1:1000", "10.0.0.2:80", "hello"); EXPECT_EQ(1, config_->stats().downstream_sess_no_route_.value()); + + filter_.reset(); + EXPECT_EQ(output_.size(), 1); + EXPECT_EQ(output_.front(), "1"); } // Dynamic cluster addition and removal handling.