diff --git a/include/envoy/http/filter.h b/include/envoy/http/filter.h index 1959d6d047f44..d8d47e225bc3c 100644 --- a/include/envoy/http/filter.h +++ b/include/envoy/http/filter.h @@ -292,6 +292,12 @@ class FilterChainFactoryCallbacks { * @param filter supplies the filter to add. */ virtual void addStreamFilter(Http::StreamFilterPtr filter) PURE; + + /** + * Add an access log handler that is called when the stream is destroyed. + * @param handler supplies the handler to add. + */ + virtual void addAccessLogHandler(Http::AccessLog::InstancePtr handler) PURE; }; /** diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index cd0ca2e3782af..085dd1f426391 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -284,6 +284,9 @@ ConnectionManagerImpl::ActiveStream::~ActiveStream() { for (AccessLog::InstancePtr access_log : connection_manager_.config_.accessLogs()) { access_log->log(request_headers_.get(), response_headers_.get(), request_info_); } + for (const auto& log_handler : access_log_handlers_) { + log_handler->log(request_headers_.get(), response_headers_.get(), request_info_); + } if (active_span_ && !request_info_.healthCheck()) { Tracing::HttpTracerUtility::finalizeSpan(*active_span_, request_info_); @@ -307,6 +310,11 @@ void ConnectionManagerImpl::ActiveStream::addStreamFilter(StreamFilterPtr filter addStreamEncoderFilter(filter); } +void ConnectionManagerImpl::ActiveStream::addAccessLogHandler( + Http::AccessLog::InstancePtr handler) { + access_log_handlers_.push_back(handler); +} + void ConnectionManagerImpl::ActiveStream::chargeStats(HeaderMap& headers) { uint64_t response_code = Utility::getResponseStatus(headers); request_info_.response_code_.value(response_code); diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h index 2cc8ff4a932a3..dbed9dc089b3d 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h @@ -380,6 +380,7 @@ class ConnectionManagerImpl : Logger::Loggable, void addStreamDecoderFilter(StreamDecoderFilterPtr filter) override; void addStreamEncoderFilter(StreamEncoderFilterPtr filter) override; void addStreamFilter(StreamFilterPtr filter) override; + void addAccessLogHandler(Http::AccessLog::InstancePtr handler) override; // Tracing::TracingConfig virtual const std::string& operationName() const override; @@ -406,6 +407,7 @@ class ConnectionManagerImpl : Logger::Loggable, HeaderMapPtr request_trailers_; std::list decoder_filters_; std::list encoder_filters_; + std::list access_log_handlers_; Stats::TimespanPtr request_timer_; std::list> reset_callbacks_; State state_; diff --git a/test/common/http/conn_manager_impl_test.cc b/test/common/http/conn_manager_impl_test.cc index bea59e344426a..606150dfe9593 100644 --- a/test/common/http/conn_manager_impl_test.cc +++ b/test/common/http/conn_manager_impl_test.cc @@ -3,13 +3,13 @@ #include "envoy/http/access_log.h" #include "common/buffer/buffer_impl.h" -#include "common/http/access_log/access_log_impl.h" #include "common/http/access_log/access_log_formatter.h" +#include "common/http/access_log/access_log_impl.h" #include "common/http/conn_manager_impl.h" #include "common/http/date_provider_impl.h" #include "common/http/exception.h" -#include "common/http/headers.h" #include "common/http/header_map_impl.h" +#include "common/http/headers.h" #include "common/stats/stats_impl.h" #include "test/mocks/access_log/mocks.h" @@ -239,6 +239,49 @@ TEST_F(HttpConnectionManagerImplTest, StartAndFinishSpanNormalFlow) { conn_manager_->onData(fake_input); } +TEST_F(HttpConnectionManagerImplTest, TestAccessLog) { + setup(false, ""); + + std::shared_ptr filter( + new NiceMock()); + std::shared_ptr handler( + new NiceMock()); + + EXPECT_CALL(filter_factory_, createFilterChain(_)) + .WillOnce(Invoke([&](Http::FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamDecoderFilter(filter); + callbacks.addAccessLogHandler(handler); + })); + + EXPECT_CALL(*handler, log(_, _, _)) + .WillOnce(Invoke([](const Http::HeaderMap*, const Http::HeaderMap*, + const Http::AccessLog::RequestInfo& request_info) { + EXPECT_TRUE(request_info.responseCode().valid()); + EXPECT_EQ(request_info.responseCode().value(), uint32_t(200)); + })); + + Http::StreamDecoder* decoder = nullptr; + NiceMock encoder; + EXPECT_CALL(*codec_, dispatch(_)) + .WillRepeatedly(Invoke([&](Buffer::Instance& data) -> void { + decoder = &conn_manager_->newStream(encoder); + + Http::HeaderMapPtr headers{ + new TestHeaderMapImpl{{":authority", "host"}, + {":path", "/"}, + {"x-request-id", "125a4afb-6f55-a4ba-ad80-413f09f48a28"}}}; + decoder->decodeHeaders(std::move(headers), true); + + Http::HeaderMapPtr response_headers{new TestHeaderMapImpl{{":status", "200"}}}; + filter->callbacks_->encodeHeaders(std::move(response_headers), true); + + data.drain(4); + })); + + Buffer::OwnedImpl fake_input("1234"); + conn_manager_->onData(fake_input); +} + TEST_F(HttpConnectionManagerImplTest, DoNotStartSpanIfTracingIsNotEnabled) { setup(false, "");