From c28f01c37a0ebd1cb48cab194ea75c1a5c63fbf0 Mon Sep 17 00:00:00 2001 From: zirain Date: Thu, 23 May 2024 08:30:12 +0800 Subject: [PATCH] accesslog: add %TRACE_ID% formatter (#34198) Risk Level: low Testing: unit test Docs Changes: formatter documented Release Notes: add Fixes #34102 Signed-off-by: zirain --- changelogs/current.yaml | 3 ++ .../observability/access_log/usage.rst | 6 ++++ .../formatter/http_specific_formatter.cc | 23 +++++++++++++ .../formatter/http_specific_formatter.h | 13 ++++++++ .../formatter/substitution_formatter_test.cc | 32 +++++++++++++++++++ 5 files changed, 77 insertions(+) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 44cf3692d08e..54f77ee15a56 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -182,6 +182,9 @@ new_features: change: | added support for :ref:`%UPSTREAM_HOST_NAME% ` for the upstream host identifier. +- area: access_loggers + change: | + Added ``TRACE_ID`` :ref:`access log formatter `. - area: healthcheck change: | Added support to healthcheck with ProxyProtocol in TCP Healthcheck by setting diff --git a/docs/root/configuration/observability/access_log/usage.rst b/docs/root/configuration/observability/access_log/usage.rst index 1b0440a1c607..cd2a68ad104f 100644 --- a/docs/root/configuration/observability/access_log/usage.rst +++ b/docs/root/configuration/observability/access_log/usage.rst @@ -1216,3 +1216,9 @@ UDP %ENVIRONMENT(X):Z% Environment value of environment variable X. If no valid environment variable X, '-' symbol will be used. Z is an optional parameter denoting string truncation up to Z characters long. + +%TRACE_ID% + HTTP + The trace ID of the request. If the request does not have a trace ID, this will be an empty string. + TCP/UDP + Not implemented ("-"). diff --git a/source/common/formatter/http_specific_formatter.cc b/source/common/formatter/http_specific_formatter.cc index 4ff9dd4fceae..7623c4b95ac9 100644 --- a/source/common/formatter/http_specific_formatter.cc +++ b/source/common/formatter/http_specific_formatter.cc @@ -197,6 +197,25 @@ HeadersByteSizeFormatter::formatValueWithContext(const HttpFormatterContext& con context.requestHeaders(), context.responseHeaders(), context.responseTrailers())); } +ProtobufWkt::Value TraceIDFormatter::formatValueWithContext(const HttpFormatterContext& context, + const StreamInfo::StreamInfo&) const { + auto trace_id = context.activeSpan().getTraceIdAsHex(); + if (trace_id.empty()) { + return SubstitutionFormatUtils::unspecifiedValue(); + } + return ValueUtil::stringValue(trace_id); +} + +absl::optional +TraceIDFormatter::formatWithContext(const HttpFormatterContext& context, + const StreamInfo::StreamInfo&) const { + auto trace_id = context.activeSpan().getTraceIdAsHex(); + if (trace_id.empty()) { + return absl::nullopt; + } + return trace_id; +} + GrpcStatusFormatter::Format GrpcStatusFormatter::parseFormat(absl::string_view format) { if (format.empty() || format == "CAMEL_STRING") { return GrpcStatusFormatter::CamelString; @@ -390,6 +409,10 @@ HttpBuiltInCommandParser::getKnownFormatters() { return std::make_unique(main_header, alternative_header, max_length); + }}}, + {"TRACE_ID", + {CommandSyntaxChecker::COMMAND_ONLY, [](const std::string&, absl::optional&) { + return std::make_unique(); }}}}); } diff --git a/source/common/formatter/http_specific_formatter.h b/source/common/formatter/http_specific_formatter.h index 106355aeb449..890322ff0c04 100644 --- a/source/common/formatter/http_specific_formatter.h +++ b/source/common/formatter/http_specific_formatter.h @@ -143,6 +143,19 @@ class ResponseTrailerFormatter : public FormatterProvider, HeaderFormatter { const StreamInfo::StreamInfo& stream_info) const override; }; +/** + * FormatterProvider for trace ID. + */ +class TraceIDFormatter : public FormatterProvider { +public: + absl::optional + formatWithContext(const HttpFormatterContext& context, + const StreamInfo::StreamInfo& stream_info) const override; + ProtobufWkt::Value + formatValueWithContext(const HttpFormatterContext& context, + const StreamInfo::StreamInfo& stream_info) const override; +}; + class GrpcStatusFormatter : public FormatterProvider, HeaderFormatter { public: enum Format { diff --git a/test/common/formatter/substitution_formatter_test.cc b/test/common/formatter/substitution_formatter_test.cc index b24cec5eae96..ef0b1704d9b4 100644 --- a/test/common/formatter/substitution_formatter_test.cc +++ b/test/common/formatter/substitution_formatter_test.cc @@ -25,6 +25,7 @@ #include "test/mocks/network/mocks.h" #include "test/mocks/ssl/mocks.h" #include "test/mocks/stream_info/mocks.h" +#include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/cluster_info.h" #include "test/test_common/environment.h" #include "test/test_common/printers.h" @@ -2144,6 +2145,37 @@ TEST(SubstitutionFormatterTest, responseTrailerFormatter) { } } +TEST(SubstitutionFormatterTest, TraceIDFormatter) { + StreamInfo::MockStreamInfo stream_info; + Http::TestRequestHeaderMapImpl request_header{}; + Http::TestResponseHeaderMapImpl response_header{}; + Http::TestResponseTrailerMapImpl response_trailer{}; + std::string body; + + Tracing::MockSpan active_span; + EXPECT_CALL(active_span, getTraceIdAsHex()) + .WillRepeatedly(Return("ae0046f9075194306d7de2931bd38ce3")); + + { + HttpFormatterContext formatter_context(&request_header, &response_header, &response_trailer, + body, AccessLogType::NotSet, &active_span); + TraceIDFormatter formatter{}; + EXPECT_EQ("ae0046f9075194306d7de2931bd38ce3", + formatter.formatWithContext(formatter_context, stream_info)); + EXPECT_THAT(formatter.formatValueWithContext(formatter_context, stream_info), + ProtoEq(ValueUtil::stringValue("ae0046f9075194306d7de2931bd38ce3"))); + } + + { + HttpFormatterContext formatter_context(&request_header, &response_header, &response_trailer, + body); + TraceIDFormatter formatter{}; + EXPECT_EQ(absl::nullopt, formatter.formatWithContext(formatter_context, stream_info)); + EXPECT_THAT(formatter.formatValueWithContext(formatter_context, stream_info), + ProtoEq(ValueUtil::nullValue())); + } +} + /** * Populate a metadata object with the following test data: * "com.test": {"test_key":"test_value","test_obj":{"inner_key":"inner_value"}}