LCOV - code coverage report
Current view: top level - source/common/access_log - access_log_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 22 212 10.4 %
Date: 2024-01-05 06:35:25 Functions: 3 26 11.5 %

          Line data    Source code
       1             : #include "source/common/access_log/access_log_impl.h"
       2             : 
       3             : #include <cstdint>
       4             : #include <string>
       5             : 
       6             : #include "envoy/common/time.h"
       7             : #include "envoy/config/accesslog/v3/accesslog.pb.h"
       8             : #include "envoy/config/accesslog/v3/accesslog.pb.validate.h"
       9             : #include "envoy/filesystem/filesystem.h"
      10             : #include "envoy/http/header_map.h"
      11             : #include "envoy/runtime/runtime.h"
      12             : #include "envoy/upstream/upstream.h"
      13             : 
      14             : #include "source/common/common/assert.h"
      15             : #include "source/common/common/utility.h"
      16             : #include "source/common/config/metadata.h"
      17             : #include "source/common/config/utility.h"
      18             : #include "source/common/http/header_map_impl.h"
      19             : #include "source/common/http/header_utility.h"
      20             : #include "source/common/http/headers.h"
      21             : #include "source/common/http/utility.h"
      22             : #include "source/common/protobuf/utility.h"
      23             : #include "source/common/stream_info/utility.h"
      24             : #include "source/common/tracing/http_tracer_impl.h"
      25             : 
      26             : #include "absl/types/optional.h"
      27             : 
      28             : namespace Envoy {
      29             : namespace AccessLog {
      30             : 
      31             : ComparisonFilter::ComparisonFilter(const envoy::config::accesslog::v3::ComparisonFilter& config,
      32             :                                    Runtime::Loader& runtime)
      33           0 :     : config_(config), runtime_(runtime) {}
      34             : 
      35           0 : bool ComparisonFilter::compareAgainstValue(uint64_t lhs) const {
      36           0 :   uint64_t value = config_.value().default_value();
      37             : 
      38           0 :   if (!config_.value().runtime_key().empty()) {
      39           0 :     value = runtime_.snapshot().getInteger(config_.value().runtime_key(), value);
      40           0 :   }
      41             : 
      42           0 :   switch (config_.op()) {
      43           0 :     PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
      44           0 :   case envoy::config::accesslog::v3::ComparisonFilter::GE:
      45           0 :     return lhs >= value;
      46           0 :   case envoy::config::accesslog::v3::ComparisonFilter::EQ:
      47           0 :     return lhs == value;
      48           0 :   case envoy::config::accesslog::v3::ComparisonFilter::LE:
      49           0 :     return lhs <= value;
      50           0 :   }
      51           0 :   IS_ENVOY_BUG("unexpected comparison op enum");
      52           0 :   return false;
      53           0 : }
      54             : 
      55             : FilterPtr FilterFactory::fromProto(const envoy::config::accesslog::v3::AccessLogFilter& config,
      56          70 :                                    Server::Configuration::FactoryContext& context) {
      57          70 :   Runtime::Loader& runtime = context.serverFactoryContext().runtime();
      58          70 :   Random::RandomGenerator& random = context.serverFactoryContext().api().randomGenerator();
      59          70 :   ProtobufMessage::ValidationVisitor& validation_visitor = context.messageValidationVisitor();
      60          70 :   switch (config.filter_specifier_case()) {
      61           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kStatusCodeFilter:
      62           0 :     return FilterPtr{new StatusCodeFilter(config.status_code_filter(), runtime)};
      63           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kDurationFilter:
      64           0 :     return FilterPtr{new DurationFilter(config.duration_filter(), runtime)};
      65          70 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kNotHealthCheckFilter:
      66          70 :     return FilterPtr{new NotHealthCheckFilter()};
      67           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kTraceableFilter:
      68           0 :     return FilterPtr{new TraceableRequestFilter()};
      69           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kRuntimeFilter:
      70           0 :     return FilterPtr{new RuntimeFilter(config.runtime_filter(), runtime, random)};
      71           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kAndFilter:
      72           0 :     return FilterPtr{new AndFilter(config.and_filter(), context)};
      73           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kOrFilter:
      74           0 :     return FilterPtr{new OrFilter(config.or_filter(), context)};
      75           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kHeaderFilter:
      76           0 :     return FilterPtr{new HeaderFilter(config.header_filter())};
      77           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kResponseFlagFilter:
      78           0 :     MessageUtil::validate(config, validation_visitor);
      79           0 :     return FilterPtr{new ResponseFlagFilter(config.response_flag_filter())};
      80           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kGrpcStatusFilter:
      81           0 :     MessageUtil::validate(config, validation_visitor);
      82           0 :     return FilterPtr{new GrpcStatusFilter(config.grpc_status_filter())};
      83           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kMetadataFilter:
      84           0 :     return FilterPtr{new MetadataFilter(config.metadata_filter())};
      85           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kLogTypeFilter:
      86           0 :     return FilterPtr{new LogTypeFilter(config.log_type_filter())};
      87           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kExtensionFilter:
      88           0 :     MessageUtil::validate(config, validation_visitor);
      89           0 :     {
      90           0 :       auto& factory =
      91           0 :           Config::Utility::getAndCheckFactory<ExtensionFilterFactory>(config.extension_filter());
      92           0 :       return factory.createFilter(config.extension_filter(), context);
      93           0 :     }
      94           0 :   case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::FILTER_SPECIFIER_NOT_SET:
      95           0 :     PANIC_DUE_TO_PROTO_UNSET;
      96          70 :   }
      97           0 :   IS_ENVOY_BUG("unexpected filter specifier value");
      98           0 :   return nullptr;
      99          70 : }
     100             : 
     101             : bool TraceableRequestFilter::evaluate(const Formatter::HttpFormatterContext&,
     102           0 :                                       const StreamInfo::StreamInfo& info) const {
     103           0 :   const Tracing::Decision decision = Tracing::TracerUtility::shouldTraceRequest(info);
     104           0 :   return decision.traced && decision.reason == Tracing::Reason::ServiceForced;
     105           0 : }
     106             : 
     107             : bool StatusCodeFilter::evaluate(const Formatter::HttpFormatterContext&,
     108           0 :                                 const StreamInfo::StreamInfo& info) const {
     109           0 :   if (!info.responseCode()) {
     110           0 :     return compareAgainstValue(0ULL);
     111           0 :   }
     112             : 
     113           0 :   return compareAgainstValue(info.responseCode().value());
     114           0 : }
     115             : 
     116             : bool DurationFilter::evaluate(const Formatter::HttpFormatterContext&,
     117           0 :                               const StreamInfo::StreamInfo& info) const {
     118           0 :   absl::optional<std::chrono::nanoseconds> duration = info.currentDuration();
     119           0 :   if (!duration.has_value()) {
     120           0 :     return false;
     121           0 :   }
     122             : 
     123           0 :   return compareAgainstValue(
     124           0 :       std::chrono::duration_cast<std::chrono::milliseconds>(duration.value()).count());
     125           0 : }
     126             : 
     127             : RuntimeFilter::RuntimeFilter(const envoy::config::accesslog::v3::RuntimeFilter& config,
     128             :                              Runtime::Loader& runtime, Random::RandomGenerator& random)
     129             :     : runtime_(runtime), random_(random), runtime_key_(config.runtime_key()),
     130             :       percent_(config.percent_sampled()),
     131           0 :       use_independent_randomness_(config.use_independent_randomness()) {}
     132             : 
     133             : bool RuntimeFilter::evaluate(const Formatter::HttpFormatterContext&,
     134           0 :                              const StreamInfo::StreamInfo& stream_info) const {
     135             :   // This code is verbose to avoid preallocating a random number that is not needed.
     136           0 :   uint64_t random_value;
     137           0 :   if (use_independent_randomness_) {
     138           0 :     random_value = random_.random();
     139           0 :   } else if (!stream_info.getStreamIdProvider().has_value()) {
     140           0 :     random_value = random_.random();
     141           0 :   } else {
     142           0 :     const auto rid_to_integer = stream_info.getStreamIdProvider()->toInteger();
     143           0 :     if (!rid_to_integer.has_value()) {
     144           0 :       random_value = random_.random();
     145           0 :     } else {
     146           0 :       random_value =
     147           0 :           rid_to_integer.value() %
     148           0 :           ProtobufPercentHelper::fractionalPercentDenominatorToInt(percent_.denominator());
     149           0 :     }
     150           0 :   }
     151             : 
     152           0 :   return runtime_.snapshot().featureEnabled(
     153           0 :       runtime_key_, percent_.numerator(), random_value,
     154           0 :       ProtobufPercentHelper::fractionalPercentDenominatorToInt(percent_.denominator()));
     155           0 : }
     156             : 
     157             : OperatorFilter::OperatorFilter(
     158             :     const Protobuf::RepeatedPtrField<envoy::config::accesslog::v3::AccessLogFilter>& configs,
     159           0 :     Server::Configuration::FactoryContext& context) {
     160           0 :   for (const auto& config : configs) {
     161           0 :     auto filter = FilterFactory::fromProto(config, context);
     162           0 :     if (filter != nullptr) {
     163           0 :       filters_.emplace_back(std::move(filter));
     164           0 :     }
     165           0 :   }
     166           0 : }
     167             : 
     168             : OrFilter::OrFilter(const envoy::config::accesslog::v3::OrFilter& config,
     169             :                    Server::Configuration::FactoryContext& context)
     170           0 :     : OperatorFilter(config.filters(), context) {}
     171             : 
     172             : AndFilter::AndFilter(const envoy::config::accesslog::v3::AndFilter& config,
     173             :                      Server::Configuration::FactoryContext& context)
     174           0 :     : OperatorFilter(config.filters(), context) {}
     175             : 
     176             : bool OrFilter::evaluate(const Formatter::HttpFormatterContext& context,
     177           0 :                         const StreamInfo::StreamInfo& info) const {
     178           0 :   bool result = false;
     179           0 :   for (auto& filter : filters_) {
     180           0 :     result |= filter->evaluate(context, info);
     181             : 
     182           0 :     if (result) {
     183           0 :       break;
     184           0 :     }
     185           0 :   }
     186             : 
     187           0 :   return result;
     188           0 : }
     189             : 
     190             : bool AndFilter::evaluate(const Formatter::HttpFormatterContext& context,
     191           0 :                          const StreamInfo::StreamInfo& info) const {
     192           0 :   bool result = true;
     193           0 :   for (auto& filter : filters_) {
     194           0 :     result &= filter->evaluate(context, info);
     195             : 
     196           0 :     if (!result) {
     197           0 :       break;
     198           0 :     }
     199           0 :   }
     200             : 
     201           0 :   return result;
     202           0 : }
     203             : 
     204             : bool NotHealthCheckFilter::evaluate(const Formatter::HttpFormatterContext&,
     205         514 :                                     const StreamInfo::StreamInfo& info) const {
     206         514 :   return !info.healthCheck();
     207         514 : }
     208             : 
     209             : HeaderFilter::HeaderFilter(const envoy::config::accesslog::v3::HeaderFilter& config)
     210           0 :     : header_data_(std::make_unique<Http::HeaderUtility::HeaderData>(config.header())) {}
     211             : 
     212             : bool HeaderFilter::evaluate(const Formatter::HttpFormatterContext& context,
     213           0 :                             const StreamInfo::StreamInfo&) const {
     214           0 :   return Http::HeaderUtility::matchHeaders(context.requestHeaders(), *header_data_);
     215           0 : }
     216             : 
     217             : ResponseFlagFilter::ResponseFlagFilter(
     218           0 :     const envoy::config::accesslog::v3::ResponseFlagFilter& config) {
     219           0 :   for (int i = 0; i < config.flags_size(); i++) {
     220           0 :     absl::optional<StreamInfo::ResponseFlag> response_flag =
     221           0 :         StreamInfo::ResponseFlagUtils::toResponseFlag(config.flags(i));
     222             :     // The config has been validated. Therefore, every flag in the config will have a mapping.
     223           0 :     ASSERT(response_flag.has_value());
     224           0 :     configured_flags_ |= response_flag.value();
     225           0 :   }
     226           0 : }
     227             : 
     228             : bool ResponseFlagFilter::evaluate(const Formatter::HttpFormatterContext&,
     229           0 :                                   const StreamInfo::StreamInfo& info) const {
     230           0 :   if (configured_flags_ != 0) {
     231           0 :     return info.intersectResponseFlags(configured_flags_);
     232           0 :   }
     233           0 :   return info.hasAnyResponseFlag();
     234           0 : }
     235             : 
     236           0 : GrpcStatusFilter::GrpcStatusFilter(const envoy::config::accesslog::v3::GrpcStatusFilter& config) {
     237           0 :   for (int i = 0; i < config.statuses_size(); i++) {
     238           0 :     statuses_.insert(protoToGrpcStatus(config.statuses(i)));
     239           0 :   }
     240             : 
     241           0 :   exclude_ = config.exclude();
     242           0 : }
     243             : 
     244             : bool GrpcStatusFilter::evaluate(const Formatter::HttpFormatterContext& context,
     245           0 :                                 const StreamInfo::StreamInfo& info) const {
     246             : 
     247           0 :   Grpc::Status::GrpcStatus status = Grpc::Status::WellKnownGrpcStatus::Unknown;
     248           0 :   const auto& optional_status =
     249           0 :       Grpc::Common::getGrpcStatus(context.responseTrailers(), context.responseHeaders(), info);
     250           0 :   if (optional_status.has_value()) {
     251           0 :     status = optional_status.value();
     252           0 :   }
     253             : 
     254           0 :   const bool found = statuses_.find(status) != statuses_.end();
     255           0 :   return exclude_ ? !found : found;
     256           0 : }
     257             : 
     258             : Grpc::Status::GrpcStatus GrpcStatusFilter::protoToGrpcStatus(
     259           0 :     envoy::config::accesslog::v3::GrpcStatusFilter::Status status) const {
     260           0 :   return static_cast<Grpc::Status::GrpcStatus>(status);
     261           0 : }
     262             : 
     263           0 : LogTypeFilter::LogTypeFilter(const envoy::config::accesslog::v3::LogTypeFilter& config) {
     264           0 :   for (auto type_as_int : config.types()) {
     265           0 :     types_.insert(static_cast<AccessLogType>(type_as_int));
     266           0 :   }
     267             : 
     268           0 :   exclude_ = config.exclude();
     269           0 : }
     270             : 
     271             : bool LogTypeFilter::evaluate(const Formatter::HttpFormatterContext& context,
     272           0 :                              const StreamInfo::StreamInfo&) const {
     273           0 :   const bool found = types_.contains(context.accessLogType());
     274           0 :   return exclude_ ? !found : found;
     275           0 : }
     276             : 
     277             : MetadataFilter::MetadataFilter(const envoy::config::accesslog::v3::MetadataFilter& filter_config)
     278             :     : default_match_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(filter_config, match_if_key_not_found, true)),
     279           0 :       filter_(filter_config.matcher().filter()) {
     280             : 
     281           0 :   if (filter_config.has_matcher()) {
     282           0 :     auto& matcher_config = filter_config.matcher();
     283             : 
     284           0 :     for (const auto& seg : matcher_config.path()) {
     285           0 :       path_.push_back(seg.key());
     286           0 :     }
     287             : 
     288             :     // Matches if the value equals the configured 'MetadataMatcher' value.
     289           0 :     const auto& val = matcher_config.value();
     290           0 :     value_matcher_ = Matchers::ValueMatcher::create(val);
     291           0 :   }
     292             : 
     293             :   // Matches if the value is present in dynamic metadata
     294           0 :   auto present_val = envoy::type::matcher::v3::ValueMatcher();
     295           0 :   present_val.set_present_match(true);
     296           0 :   present_matcher_ = Matchers::ValueMatcher::create(present_val);
     297           0 : }
     298             : 
     299             : bool MetadataFilter::evaluate(const Formatter::HttpFormatterContext&,
     300           0 :                               const StreamInfo::StreamInfo& info) const {
     301           0 :   const auto& value =
     302           0 :       Envoy::Config::Metadata::metadataValue(&info.dynamicMetadata(), filter_, path_);
     303             :   // If the key corresponds to a set value in dynamic metadata, return true if the value matches the
     304             :   // the configured 'MetadataMatcher' value and false otherwise
     305           0 :   if (present_matcher_->match(value)) {
     306           0 :     return value_matcher_ && value_matcher_->match(value);
     307           0 :   }
     308             : 
     309             :   // If the key does not correspond to a set value in dynamic metadata, return true if
     310             :   // 'match_if_key_not_found' is set to true and false otherwise
     311           0 :   return default_match_;
     312           0 : }
     313             : 
     314             : InstanceSharedPtr AccessLogFactory::fromProto(const envoy::config::accesslog::v3::AccessLog& config,
     315         168 :                                               Server::Configuration::FactoryContext& context) {
     316         168 :   FilterPtr filter;
     317         168 :   if (config.has_filter()) {
     318          70 :     filter = FilterFactory::fromProto(config.filter(), context);
     319          70 :   }
     320             : 
     321         168 :   auto& factory = Config::Utility::getAndCheckFactory<AccessLog::AccessLogInstanceFactory>(config);
     322         168 :   ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
     323         168 :       config, context.messageValidationVisitor(), factory);
     324             : 
     325         168 :   return factory.createAccessLogInstance(*message, std::move(filter), context);
     326         168 : }
     327             : 
     328             : } // namespace AccessLog
     329             : } // namespace Envoy

Generated by: LCOV version 1.15