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
|