LCOV - code coverage report
Current view: top level - source/common/formatter - substitution_formatter.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 128 216 59.3 %
Date: 2024-01-05 06:35:25 Functions: 13 19 68.4 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <bitset>
       4             : #include <functional>
       5             : #include <list>
       6             : #include <string>
       7             : #include <vector>
       8             : 
       9             : #include "envoy/common/time.h"
      10             : #include "envoy/config/core/v3/base.pb.h"
      11             : #include "envoy/formatter/substitution_formatter.h"
      12             : #include "envoy/stream_info/stream_info.h"
      13             : 
      14             : #include "source/common/common/utility.h"
      15             : #include "source/common/formatter/http_specific_formatter.h"
      16             : #include "source/common/formatter/stream_info_formatter.h"
      17             : #include "source/common/json/json_loader.h"
      18             : 
      19             : #include "absl/container/flat_hash_map.h"
      20             : #include "absl/types/optional.h"
      21             : 
      22             : namespace Envoy {
      23             : namespace Formatter {
      24             : 
      25             : /**
      26             :  * Access log format parser.
      27             :  */
      28             : class SubstitutionFormatParser {
      29             : public:
      30             :   template <class FormatterContext = HttpFormatterContext>
      31             :   static std::vector<FormatterProviderBasePtr<FormatterContext>>
      32             :   parse(const std::string& format,
      33         983 :         const std::vector<CommandParserBasePtr<FormatterContext>>& command_parsers = {}) {
      34         983 :     std::string current_token;
      35         983 :     std::vector<FormatterProviderBasePtr<FormatterContext>> formatters;
      36             : 
      37       15059 :     for (size_t pos = 0; pos < format.size(); ++pos) {
      38       14095 :       if (format[pos] != '%') {
      39        9458 :         current_token += format[pos];
      40        9458 :         continue;
      41        9458 :       }
      42             : 
      43             :       // escape '%%'
      44        4637 :       if (format.size() > pos + 1) {
      45        4630 :         if (format[pos + 1] == '%') {
      46         620 :           current_token += '%';
      47         620 :           pos++;
      48         620 :           continue;
      49         620 :         }
      50        4630 :       }
      51             : 
      52        4017 :       if (!current_token.empty()) {
      53        2700 :         formatters.emplace_back(FormatterProviderBasePtr<FormatterContext>{
      54        2700 :             new PlainStringFormatterBase<FormatterContext>(current_token)});
      55        2700 :         current_token = "";
      56        2700 :       }
      57             : 
      58        4017 :       std::smatch m;
      59        4017 :       const std::string search_space = std::string(format.substr(pos));
      60        4017 :       if (!std::regex_search(search_space, m, commandWithArgsRegex())) {
      61          19 :         throwEnvoyExceptionOrPanic(
      62          19 :             fmt::format("Incorrect configuration: {}. Couldn't find valid command at position {}",
      63          19 :                         format, pos));
      64          19 :       }
      65             : 
      66        3998 :       const std::string match = m.str(0);
      67             :       // command is at at index 1.
      68        3998 :       const std::string command = m.str(1);
      69             :       // subcommand is at index 2.
      70        3998 :       const std::string subcommand = m.str(2);
      71             :       // optional length is at index 3. If present, validate that it is valid integer.
      72        3998 :       absl::optional<size_t> max_length;
      73        3998 :       if (m.str(3).length() != 0) {
      74           0 :         size_t length_value;
      75           0 :         if (!absl::SimpleAtoi(m.str(3), &length_value)) {
      76           0 :           throwEnvoyExceptionOrPanic(absl::StrCat("Length must be an integer, given: ", m.str(3)));
      77           0 :         }
      78           0 :         max_length = length_value;
      79           0 :       }
      80        3998 :       std::vector<std::string> path;
      81             : 
      82        3998 :       const size_t command_end_position = pos + m.str(0).length() - 1;
      83             : 
      84        3998 :       bool added = false;
      85             : 
      86             :       // First, try the built-in command parsers.
      87        3998 :       for (const auto& cmd : BuiltInCommandParsersBase<FormatterContext>::commandParsers()) {
      88        3998 :         auto formatter = cmd->parse(command, subcommand, max_length);
      89        3998 :         if (formatter) {
      90        1944 :           formatters.push_back(std::move(formatter));
      91        1944 :           added = true;
      92        1944 :           break;
      93        1944 :         }
      94        3998 :       }
      95             : 
      96             :       // Next, try the command parsers provided by the user.
      97        3998 :       if (!added) {
      98        2050 :         for (const auto& cmd : command_parsers) {
      99           0 :           auto formatter = cmd->parse(command, subcommand, max_length);
     100           0 :           if (formatter) {
     101           0 :             formatters.push_back(std::move(formatter));
     102           0 :             added = true;
     103           0 :             break;
     104           0 :           }
     105           0 :         }
     106        2050 :       }
     107             : 
     108        3998 :       if (!added) {
     109             :         // Finally, try the context independent formatters.
     110        2050 :         formatters.emplace_back(FormatterProviderBasePtr<FormatterContext>{
     111        2050 :             new StreamInfoFormatterBase<FormatterContext>(command, subcommand, max_length)});
     112        2050 :       }
     113             : 
     114        3998 :       pos = command_end_position;
     115        3998 :     }
     116             : 
     117         964 :     if (!current_token.empty() || format.empty()) {
     118             :       // Create a PlainStringFormatter with the final string literal. If the format string
     119             :       // was empty, this creates a PlainStringFormatter with an empty string.
     120         476 :       formatters.emplace_back(FormatterProviderBasePtr<FormatterContext>{
     121         476 :           new PlainStringFormatterBase<FormatterContext>(current_token)});
     122         476 :     }
     123             : 
     124         964 :     return formatters;
     125         983 :   }
     126             : 
     127             : private:
     128             :   static const std::regex& commandWithArgsRegex();
     129             : };
     130             : 
     131             : inline constexpr absl::string_view DefaultUnspecifiedValueStringView = "-";
     132             : 
     133             : /**
     134             :  * Composite formatter implementation.
     135             :  */
     136             : template <class FormatterContext>
     137             : class CommonFormatterBaseImpl : public FormatterBase<FormatterContext> {
     138             : public:
     139             :   using CommandParsers = std::vector<CommandParserBasePtr<FormatterContext>>;
     140             : 
     141             :   CommonFormatterBaseImpl(const std::string& format, bool omit_empty_values = false)
     142             :       : empty_value_string_(omit_empty_values ? absl::string_view{}
     143         982 :                                               : DefaultUnspecifiedValueStringView) {
     144         982 :     providers_ = SubstitutionFormatParser::parse<FormatterContext>(format);
     145         982 :   }
     146             :   CommonFormatterBaseImpl(const std::string& format, bool omit_empty_values,
     147             :                           const CommandParsers& command_parsers)
     148             :       : empty_value_string_(omit_empty_values ? absl::string_view{}
     149           0 :                                               : DefaultUnspecifiedValueStringView) {
     150           0 :     providers_ = SubstitutionFormatParser::parse<FormatterContext>(format, command_parsers);
     151           0 :   }
     152             : 
     153             :   // FormatterBase
     154             :   std::string formatWithContext(const FormatterContext& context,
     155        1345 :                                 const StreamInfo::StreamInfo& stream_info) const override {
     156        1345 :     std::string log_line;
     157        1345 :     log_line.reserve(256);
     158             : 
     159       20772 :     for (const auto& provider : providers_) {
     160       20772 :       const auto bit = provider->formatWithContext(context, stream_info);
     161       20772 :       log_line += bit.value_or(empty_value_string_);
     162       20772 :     }
     163             : 
     164        1345 :     return log_line;
     165        1345 :   }
     166             : 
     167             : private:
     168             :   const std::string empty_value_string_;
     169             :   std::vector<FormatterProviderBasePtr<FormatterContext>> providers_;
     170             : };
     171             : 
     172             : template <class FormatterContext>
     173             : class FormatterBaseImpl : public CommonFormatterBaseImpl<FormatterContext> {
     174             : public:
     175             :   using CommonFormatterBaseImpl<FormatterContext>::CommonFormatterBaseImpl;
     176             : };
     177             : 
     178             : // Helper classes for StructFormatter::StructFormatMapVisitor.
     179             : template <class... Ts> struct StructFormatMapVisitorHelper : Ts... { using Ts::operator()...; };
     180             : template <class... Ts> StructFormatMapVisitorHelper(Ts...) -> StructFormatMapVisitorHelper<Ts...>;
     181             : 
     182             : /**
     183             :  * An formatter for structured log formats, which returns a Struct proto that
     184             :  * can be converted easily into multiple formats.
     185             :  */
     186             : template <class FormatterContext> class StructFormatterBase {
     187             : public:
     188             :   using CommandParsers = std::vector<CommandParserBasePtr<FormatterContext>>;
     189             :   using PlainNumber = PlainNumberFormatterBase<FormatterContext>;
     190             :   using PlainString = PlainStringFormatterBase<FormatterContext>;
     191             : 
     192             :   StructFormatterBase(const ProtobufWkt::Struct& format_mapping, bool preserve_types,
     193             :                       bool omit_empty_values, const CommandParsers& commands = {})
     194             :       : omit_empty_values_(omit_empty_values), preserve_types_(preserve_types),
     195             :         empty_value_(omit_empty_values_ ? std::string()
     196             :                                         : std::string(DefaultUnspecifiedValueStringView)),
     197           1 :         struct_output_format_(FormatBuilder(commands).toFormatMapValue(format_mapping)) {}
     198             : 
     199             :   ProtobufWkt::Struct formatWithContext(const FormatterContext& context,
     200           1 :                                         const StreamInfo::StreamInfo& info) const {
     201           1 :     StructFormatMapVisitor visitor{
     202           1 :         [&](const std::vector<FormatterProviderBasePtr<FormatterContext>>& providers) {
     203           1 :           return providersCallback(providers, context, info);
     204           1 :         },
     205           1 :         [&, this](const StructFormatterBase::StructFormatMapWrapper& format_map) {
     206           0 :           return structFormatMapCallback(format_map, visitor);
     207           0 :         },
     208           1 :         [&, this](const StructFormatterBase::StructFormatListWrapper& format_list) {
     209           0 :           return structFormatListCallback(format_list, visitor);
     210           0 :         },
     211           1 :     };
     212           1 :     return structFormatMapCallback(struct_output_format_, visitor).struct_value();
     213           1 :   }
     214             : 
     215             : private:
     216             :   struct StructFormatMapWrapper;
     217             :   struct StructFormatListWrapper;
     218             :   using StructFormatValue =
     219             :       absl::variant<const std::vector<FormatterProviderBasePtr<FormatterContext>>,
     220             :                     const StructFormatMapWrapper, const StructFormatListWrapper>;
     221             :   // Although not required for Struct/JSON, it is nice to have the order of
     222             :   // properties preserved between the format and the log entry, thus std::map.
     223             :   using StructFormatMap = std::map<std::string, StructFormatValue>;
     224             :   using StructFormatMapPtr = std::unique_ptr<StructFormatMap>;
     225             :   struct StructFormatMapWrapper {
     226             :     StructFormatMapPtr value_;
     227             :   };
     228             : 
     229             :   using StructFormatList = std::list<StructFormatValue>;
     230             :   using StructFormatListPtr = std::unique_ptr<StructFormatList>;
     231             :   struct StructFormatListWrapper {
     232             :     StructFormatListPtr value_;
     233             :   };
     234             : 
     235             :   using StructFormatMapVisitor = StructFormatMapVisitorHelper<
     236             :       const std::function<ProtobufWkt::Value(
     237             :           const std::vector<FormatterProviderBasePtr<FormatterContext>>&)>,
     238             :       const std::function<ProtobufWkt::Value(const StructFormatterBase::StructFormatMapWrapper&)>,
     239             :       const std::function<ProtobufWkt::Value(const StructFormatterBase::StructFormatListWrapper&)>>;
     240             : 
     241             :   // Methods for building the format map.
     242             :   class FormatBuilder {
     243             :   public:
     244           1 :     explicit FormatBuilder(const CommandParsers& commands) : commands_(commands) {}
     245             :     std::vector<FormatterProviderBasePtr<FormatterContext>>
     246           1 :     toFormatStringValue(const std::string& string_format) const {
     247           1 :       return SubstitutionFormatParser::parse<FormatterContext>(string_format, commands_);
     248           1 :     }
     249             :     std::vector<FormatterProviderBasePtr<FormatterContext>>
     250           0 :     toFormatNumberValue(double value) const {
     251           0 :       std::vector<FormatterProviderBasePtr<FormatterContext>> formatters;
     252           0 :       formatters.emplace_back(FormatterProviderBasePtr<FormatterContext>{new PlainNumber(value)});
     253           0 :       return formatters;
     254           0 :     }
     255           1 :     StructFormatMapWrapper toFormatMapValue(const ProtobufWkt::Struct& struct_format) const {
     256           1 :       auto output = std::make_unique<StructFormatMap>();
     257           1 :       for (const auto& pair : struct_format.fields()) {
     258           1 :         switch (pair.second.kind_case()) {
     259           1 :         case ProtobufWkt::Value::kStringValue:
     260           1 :           output->emplace(pair.first, toFormatStringValue(pair.second.string_value()));
     261           1 :           break;
     262             : 
     263           0 :         case ProtobufWkt::Value::kStructValue:
     264           0 :           output->emplace(pair.first, toFormatMapValue(pair.second.struct_value()));
     265           0 :           break;
     266             : 
     267           0 :         case ProtobufWkt::Value::kListValue:
     268           0 :           output->emplace(pair.first, toFormatListValue(pair.second.list_value()));
     269           0 :           break;
     270             : 
     271           0 :         case ProtobufWkt::Value::kNumberValue:
     272           0 :           output->emplace(pair.first, toFormatNumberValue(pair.second.number_value()));
     273           0 :           break;
     274           0 :         default:
     275           0 :           throwEnvoyExceptionOrPanic(
     276           1 :               "Only string values, nested structs, list values and number values are "
     277           1 :               "supported in structured access log format.");
     278           1 :         }
     279           1 :       }
     280           1 :       return {std::move(output)};
     281           1 :     }
     282             :     StructFormatListWrapper
     283           0 :     toFormatListValue(const ProtobufWkt::ListValue& list_value_format) const {
     284           0 :       auto output = std::make_unique<StructFormatList>();
     285           0 :       for (const auto& value : list_value_format.values()) {
     286           0 :         switch (value.kind_case()) {
     287           0 :         case ProtobufWkt::Value::kStringValue:
     288           0 :           output->emplace_back(toFormatStringValue(value.string_value()));
     289           0 :           break;
     290             : 
     291           0 :         case ProtobufWkt::Value::kStructValue:
     292           0 :           output->emplace_back(toFormatMapValue(value.struct_value()));
     293           0 :           break;
     294             : 
     295           0 :         case ProtobufWkt::Value::kListValue:
     296           0 :           output->emplace_back(toFormatListValue(value.list_value()));
     297           0 :           break;
     298             : 
     299           0 :         case ProtobufWkt::Value::kNumberValue:
     300           0 :           output->emplace_back(toFormatNumberValue(value.number_value()));
     301           0 :           break;
     302             : 
     303           0 :         default:
     304           0 :           throwEnvoyExceptionOrPanic(
     305           0 :               "Only string values, nested structs, list values and number values are "
     306           0 :               "supported in structured access log format.");
     307           0 :         }
     308           0 :       }
     309           0 :       return {std::move(output)};
     310           0 :     }
     311             : 
     312             :   private:
     313             :     const CommandParsers& commands_;
     314             :   };
     315             : 
     316             :   // Methods for doing the actual formatting.
     317             :   ProtobufWkt::Value
     318             :   providersCallback(const std::vector<FormatterProviderBasePtr<FormatterContext>>& providers,
     319             :                     const FormatterContext& context,
     320           1 :                     const StreamInfo::StreamInfo& stream_info) const {
     321           1 :     ASSERT(!providers.empty());
     322           1 :     if (providers.size() == 1) {
     323           1 :       const auto& provider = providers.front();
     324           1 :       if (preserve_types_) {
     325           1 :         return provider->formatValueWithContext(context, stream_info);
     326           1 :       }
     327             : 
     328           0 :       if (omit_empty_values_) {
     329           0 :         return ValueUtil::optionalStringValue(provider->formatWithContext(context, stream_info));
     330           0 :       }
     331             : 
     332           0 :       const auto str = provider->formatWithContext(context, stream_info);
     333           0 :       return ValueUtil::stringValue(str.value_or(empty_value_));
     334           0 :     }
     335             :     // Multiple providers forces string output.
     336           0 :     std::string str;
     337           0 :     for (const auto& provider : providers) {
     338           0 :       const auto bit = provider->formatWithContext(context, stream_info);
     339           0 :       str += bit.value_or(empty_value_);
     340           0 :     }
     341           0 :     return ValueUtil::stringValue(str);
     342           1 :   }
     343             :   ProtobufWkt::Value
     344             :   structFormatMapCallback(const StructFormatterBase::StructFormatMapWrapper& format_map,
     345           1 :                           const StructFormatMapVisitor& visitor) const {
     346           1 :     ProtobufWkt::Struct output;
     347           1 :     auto* fields = output.mutable_fields();
     348           1 :     for (const auto& pair : *format_map.value_) {
     349           1 :       ProtobufWkt::Value value = absl::visit(visitor, pair.second);
     350           1 :       if (omit_empty_values_ && value.kind_case() == ProtobufWkt::Value::kNullValue) {
     351           0 :         continue;
     352           0 :       }
     353           1 :       (*fields)[pair.first] = value;
     354           1 :     }
     355           1 :     if (omit_empty_values_ && output.fields().empty()) {
     356           0 :       return ValueUtil::nullValue();
     357           0 :     }
     358           1 :     return ValueUtil::structValue(output);
     359           1 :   }
     360             :   ProtobufWkt::Value
     361             :   structFormatListCallback(const StructFormatterBase::StructFormatListWrapper& format_list,
     362           0 :                            const StructFormatMapVisitor& visitor) const {
     363           0 :     std::vector<ProtobufWkt::Value> output;
     364           0 :     for (const auto& val : *format_list.value_) {
     365           0 :       ProtobufWkt::Value value = absl::visit(visitor, val);
     366           0 :       if (omit_empty_values_ && value.kind_case() == ProtobufWkt::Value::kNullValue) {
     367           0 :         continue;
     368           0 :       }
     369           0 :       output.push_back(value);
     370           0 :     }
     371           0 :     return ValueUtil::listValue(output);
     372           0 :   }
     373             : 
     374             :   const bool omit_empty_values_;
     375             :   const bool preserve_types_;
     376             :   const std::string empty_value_;
     377             : 
     378             :   const StructFormatMapWrapper struct_output_format_;
     379             : };
     380             : 
     381             : template <class FormatterContext>
     382             : using StructFormatterBasePtr = std::unique_ptr<StructFormatterBase<FormatterContext>>;
     383             : 
     384             : template <class FormatterContext>
     385             : class CommonJsonFormatterBaseImpl : public FormatterBase<FormatterContext> {
     386             : public:
     387             :   using CommandParsers = std::vector<CommandParserBasePtr<FormatterContext>>;
     388             : 
     389             :   CommonJsonFormatterBaseImpl(const ProtobufWkt::Struct& format_mapping, bool preserve_types,
     390             :                               bool omit_empty_values, bool sort_properties,
     391             :                               const CommandParsers& commands = {})
     392             :       : struct_formatter_(format_mapping, preserve_types, omit_empty_values, commands),
     393           1 :         sort_properties_(sort_properties) {}
     394             : 
     395             :   // FormatterBase
     396             :   std::string formatWithContext(const FormatterContext& context,
     397           1 :                                 const StreamInfo::StreamInfo& info) const override {
     398           1 :     const ProtobufWkt::Struct output_struct = struct_formatter_.formatWithContext(context, info);
     399             : 
     400           1 :     std::string log_line = "";
     401           1 : #ifdef ENVOY_ENABLE_YAML
     402           1 :     if (sort_properties_) {
     403           0 :       log_line = Json::Factory::loadFromProtobufStruct(output_struct)->asJsonString();
     404           1 :     } else {
     405           1 :       log_line = MessageUtil::getJsonStringFromMessageOrError(output_struct, false, true);
     406           1 :     }
     407             : #else
     408             :     UNREFERENCED_PARAMETER(sort_properties_);
     409             :     IS_ENVOY_BUG("Json support compiled out");
     410             : #endif
     411           1 :     return absl::StrCat(log_line, "\n");
     412           1 :   }
     413             : 
     414             : private:
     415             :   const StructFormatterBase<FormatterContext> struct_formatter_;
     416             :   const bool sort_properties_;
     417             : };
     418             : 
     419             : template <class FormatterContext>
     420             : class JsonFormatterBaseImpl : public CommonJsonFormatterBaseImpl<FormatterContext> {
     421             : public:
     422             :   using CommonJsonFormatterBaseImpl<FormatterContext>::CommonJsonFormatterBaseImpl;
     423             : };
     424             : 
     425             : using StructFormatter = StructFormatterBase<HttpFormatterContext>;
     426             : using StructFormatterPtr = std::unique_ptr<StructFormatter>;
     427             : 
     428             : // Aliases for backwards compatibility.
     429             : using FormatterImpl = FormatterBaseImpl<HttpFormatterContext>;
     430             : using JsonFormatterImpl = JsonFormatterBaseImpl<HttpFormatterContext>;
     431             : 
     432             : } // namespace Formatter
     433             : } // namespace Envoy

Generated by: LCOV version 1.15