Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,10 @@ new_features:
change: |
Added admin handler API to the bootstrap extension dynamic modules ABI, enabling modules to
register custom admin HTTP endpoints.
- area: formatter
change: |
Added ``SPAN_ID`` :ref:`access log formatter <config_access_log_format>` to log the span ID of
the active (downstream) span for a request, complementing the existing ``TRACE_ID`` formatter.
- area: formatter
change: |
Added ``QUERY_PARAMS`` support for substitution formatter to log all query params.
Expand Down
8 changes: 8 additions & 0 deletions docs/root/configuration/advanced/substitution_formatter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,14 @@ Current supported substitution commands include:
TCP/UDP
Not implemented. It will appear as ``"-"`` in the access logs.

``%SPAN_ID%``
HTTP
The span ID of the active (downstream) span for the request. If the request does not have a span ID,
this will be an empty string. Note that span ID availability depends on the tracing provider; not all
providers implement span ID retrieval.
TCP/UDP
Not implemented. It will appear as ``"-"`` in the access logs.

``%QUERY_PARAM(X):Z%``
HTTP
The value of the query parameter ``X``. If the query parameter ``X`` is not present, ``"-"`` symbol will be used.
Expand Down
32 changes: 32 additions & 0 deletions source/common/formatter/http_specific_formatter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,33 @@ absl::optional<std::string> TraceIDFormatter::format(const Context& context,
return trace_id;
}

Protobuf::Value SpanIDFormatter::formatValue(const Context& context,
const StreamInfo::StreamInfo&) const {
const auto active_span = context.activeSpan();
if (!active_span.has_value()) {
return SubstitutionFormatUtils::unspecifiedValue();
}
auto span_id = active_span->getSpanId();
if (span_id.empty()) {
return SubstitutionFormatUtils::unspecifiedValue();
}
return ValueUtil::stringValue(span_id);
}

absl::optional<std::string> SpanIDFormatter::format(const Context& context,
const StreamInfo::StreamInfo&) const {
const auto active_span = context.activeSpan();
if (!active_span.has_value()) {
return absl::nullopt;
}

auto span_id = active_span->getSpanId();
if (span_id.empty()) {
return absl::nullopt;
}
return span_id;
}

GrpcStatusFormatter::Format GrpcStatusFormatter::parseFormat(absl::string_view format) {
if (format.empty() || format == "CAMEL_STRING") {
return GrpcStatusFormatter::CamelString;
Expand Down Expand Up @@ -532,6 +559,11 @@ BuiltInHttpCommandParser::getKnownFormatters() {
[](absl::string_view, absl::optional<size_t>) {
return std::make_unique<TraceIDFormatter>();
}}},
{"SPAN_ID",
{CommandSyntaxChecker::COMMAND_ONLY,
[](absl::string_view, absl::optional<size_t>) {
return std::make_unique<SpanIDFormatter>();
}}},
{"QUERY_PARAM",
{CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
[](absl::string_view format, absl::optional<size_t> max_length) {
Expand Down
11 changes: 11 additions & 0 deletions source/common/formatter/http_specific_formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ class TraceIDFormatter : public FormatterProvider {
const StreamInfo::StreamInfo& stream_info) const override;
};

/**
* FormatterProvider for span ID.
*/
class SpanIDFormatter : public FormatterProvider {
public:
absl::optional<std::string> format(const Context& context,
const StreamInfo::StreamInfo& stream_info) const override;
Protobuf::Value formatValue(const Context& context,
const StreamInfo::StreamInfo& stream_info) const override;
};

class GrpcStatusFormatter : public FormatterProvider, HeaderFormatter {
public:
enum Format {
Expand Down
41 changes: 41 additions & 0 deletions test/common/formatter/substitution_formatter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3465,6 +3465,47 @@ TEST(SubstitutionFormatterTest, TraceIDFormatter) {
}
}

TEST(SubstitutionFormatterTest, SpanIDFormatter) {
StreamInfo::MockStreamInfo stream_info;

{
// Span present with valid span ID.
Tracing::MockSpan active_span;
EXPECT_CALL(active_span, getSpanId()).WillRepeatedly(Return("4041424344454647"));

Context formatter_context;
formatter_context.setActiveSpan(active_span);

SpanIDFormatter formatter{};
EXPECT_EQ("4041424344454647", formatter.format(formatter_context, stream_info));
EXPECT_THAT(formatter.formatValue(formatter_context, stream_info),
ProtoEq(ValueUtil::stringValue("4041424344454647")));
}

{
// No active span.
Context formatter_context;
SpanIDFormatter formatter{};
EXPECT_EQ(absl::nullopt, formatter.format(formatter_context, stream_info));
EXPECT_THAT(formatter.formatValue(formatter_context, stream_info),
ProtoEq(ValueUtil::nullValue()));
}

{
// Span present but getSpanId() returns empty (e.g., Zipkin, Datadog, SkyWalking).
Tracing::MockSpan active_span;
EXPECT_CALL(active_span, getSpanId()).WillRepeatedly(Return(""));

Context formatter_context;
formatter_context.setActiveSpan(active_span);

SpanIDFormatter formatter{};
EXPECT_EQ(absl::nullopt, formatter.format(formatter_context, stream_info));
EXPECT_THAT(formatter.formatValue(formatter_context, stream_info),
ProtoEq(ValueUtil::nullValue()));
}
}

/**
* Populate a metadata object with the following test data:
* "com.test":
Expand Down
Loading