diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 4631f141d5d59..2e93e4253a5ea 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -49,5 +49,9 @@ new_features: Added config field :ref:`filter_metadata ` for injecting arbitrary data to the filter state for logging. +- area: access_log + change: | + added %UPSTREAM_CLUSTER_RAW% access log formatter to log the original upstream cluster name, regadless of whether + ``alt_stat_name`` is set. deprecated: diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 0d49af10f5600..6b9d0fadfcabf 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -576,6 +576,10 @@ UDP Upstream cluster to which the upstream host belongs to. :ref:`alt_stat_name ` will be used if provided. +%UPSTREAM_CLUSTER_RAW% + Upstream cluster to which the upstream host belongs to. :ref:`alt_stat_name + ` does NOT modify this value. + %UPSTREAM_LOCAL_ADDRESS% Local address of the upstream connection. If the address is an IP address it includes both address and port. diff --git a/source/common/formatter/stream_info_formatter.cc b/source/common/formatter/stream_info_formatter.cc index d7c3dde4d707a..41512f458d6a4 100644 --- a/source/common/formatter/stream_info_formatter.cc +++ b/source/common/formatter/stream_info_formatter.cc @@ -1123,6 +1123,22 @@ const StreamInfoFormatterProviderLookupTable& getKnownStreamInfoFormatterProvide stream_info.upstreamClusterInfo().value()->observabilityName(); } + return upstream_cluster_name.empty() + ? absl::nullopt + : absl::make_optional(upstream_cluster_name); + }); + }}}, + {"UPSTREAM_CLUSTER_RAW", + {CommandSyntaxChecker::COMMAND_ONLY, + [](const std::string&, absl::optional) { + return std::make_unique( + [](const StreamInfo::StreamInfo& stream_info) { + std::string upstream_cluster_name; + if (stream_info.upstreamClusterInfo().has_value() && + stream_info.upstreamClusterInfo().value() != nullptr) { + upstream_cluster_name = stream_info.upstreamClusterInfo().value()->name(); + } + return upstream_cluster_name.empty() ? absl::nullopt : absl::make_optional(upstream_cluster_name); diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index cf299188ee91c..c92b61245cb30 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -787,6 +787,27 @@ TEST(SubstitutionFormatterTest, streamInfoFormatter) { ProtoEq(ValueUtil::nullValue())); } + { + StreamInfoFormatter upstream_format("UPSTREAM_CLUSTER_RAW"); + const std::string raw_cluster_name = "raw_name"; + auto cluster_info_mock = std::make_shared(); + absl::optional cluster_info = cluster_info_mock; + EXPECT_CALL(stream_info, upstreamClusterInfo()).WillRepeatedly(Return(cluster_info)); + EXPECT_CALL(*cluster_info_mock, name()).WillRepeatedly(ReturnRef(raw_cluster_name)); + EXPECT_EQ("raw_name", upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::stringValue("raw_name"))); + } + + { + StreamInfoFormatter upstream_format("UPSTREAM_CLUSTER_RAW"); + absl::optional cluster_info = nullptr; + EXPECT_CALL(stream_info, upstreamClusterInfo()).WillRepeatedly(Return(cluster_info)); + EXPECT_EQ(absl::nullopt, upstream_format.formatWithContext({}, stream_info)); + EXPECT_THAT(upstream_format.formatValueWithContext({}, stream_info), + ProtoEq(ValueUtil::nullValue())); + } + { NiceMock os_sys_calls; TestThreadsafeSingletonInjector os_calls(&os_sys_calls); diff --git a/test/common/router/router_upstream_log_test.cc b/test/common/router/router_upstream_log_test.cc index 66b951a5f0faa..f9889c9e39bff 100644 --- a/test/common/router/router_upstream_log_test.cc +++ b/test/common/router/router_upstream_log_test.cc @@ -88,10 +88,11 @@ class RouterUpstreamLogTest : public testing::Test { bool enable_periodic_upstream_log = false) { envoy::extensions::filters::http::router::v3::Router router_proto; static const std::string cluster_name = "cluster_0"; + static const std::string observability_name = "cluster-0"; cluster_info_ = std::make_shared>(); ON_CALL(*cluster_info_, name()).WillByDefault(ReturnRef(cluster_name)); - ON_CALL(*cluster_info_, observabilityName()).WillByDefault(ReturnRef(cluster_name)); + ON_CALL(*cluster_info_, observabilityName()).WillByDefault(ReturnRef(observability_name)); ON_CALL(callbacks_.stream_info_, upstreamClusterInfo()).WillByDefault(Return(cluster_info_)); callbacks_.stream_info_.downstream_bytes_meter_ = std::make_shared(); EXPECT_CALL(callbacks_.dispatcher_, deferredDelete_).Times(testing::AnyNumber()); @@ -435,6 +436,27 @@ name: accesslog init(absl::optional(upstream_log)); run(); + EXPECT_EQ(output_.size(), 1U); + EXPECT_EQ(output_.front(), "cluster-0"); +} + +// Test UPSTREAM_CLUSTER_RAW log formatter. +TEST_F(RouterUpstreamLogTest, RawUpstreamCluster) { + const std::string yaml = R"EOF( +name: accesslog +typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + log_format: + text_format_source: + inline_string: "%UPSTREAM_CLUSTER_RAW%" + path: "/dev/null" + )EOF"; + + envoy::config::accesslog::v3::AccessLog upstream_log; + TestUtility::loadFromYaml(yaml, upstream_log); + init(absl::optional(upstream_log)); + run(); + EXPECT_EQ(output_.size(), 1U); EXPECT_EQ(output_.front(), "cluster_0"); } @@ -461,9 +483,9 @@ name: accesslog EXPECT_EQ(output_.size(), 2U); EXPECT_EQ( output_.front(), - absl::StrCat("cluster_0 ", AccessLogType_Name(AccessLog::AccessLogType::UpstreamPoolReady))); + absl::StrCat("cluster-0 ", AccessLogType_Name(AccessLog::AccessLogType::UpstreamPoolReady))); EXPECT_EQ(output_.back(), - absl::StrCat("cluster_0 ", AccessLogType_Name(AccessLog::AccessLogType::UpstreamEnd))); + absl::StrCat("cluster-0 ", AccessLogType_Name(AccessLog::AccessLogType::UpstreamEnd))); } TEST_F(RouterUpstreamLogTest, PeriodicLog) {