New internal-use-only classes `AsStructuredLiteralImpl` and `AsStructuredValueImpl` PiperOrigin-RevId: 714130164 Change-Id: Ib76de9efeecf5344ed67ca27680937971d9a84e3
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt index 085a228..73deb45 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt
@@ -206,6 +206,7 @@ absl::log_internal_proto absl::log_internal_log_sink_set absl::log_internal_nullguard + absl::log_internal_structured_proto absl::log_globals absl::log_entry absl::log_severity @@ -666,9 +667,11 @@ LINKOPTS ${ABSL_DEFAULT_LINKOPTS} DEPS + absl::any_invocable absl::config absl::core_headers absl::log_internal_message + absl::log_internal_structured_proto absl::strings )
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel index 4fcbb83..2d74ae9 100644 --- a/absl/log/internal/BUILD.bazel +++ b/absl/log/internal/BUILD.bazel
@@ -23,7 +23,7 @@ package( default_visibility = [ - "//absl/log:__pkg__", + ":internal_users", ], features = [ "header_modules", @@ -35,6 +35,13 @@ licenses(["notice"]) package_group( + name = "internal_users", + packages = [ + "//absl/log", + ], +) + +package_group( name = "structured_proto_users", packages = [ "//absl/log/...", @@ -183,6 +190,7 @@ ":log_sink_set", ":nullguard", ":proto", + ":structured_proto", "//absl/base", "//absl/base:config", "//absl/base:core_headers", @@ -287,10 +295,16 @@ hdrs = ["structured.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + ":internal_users", + ":structured_proto_users", + ], deps = [ ":log_message", + ":structured_proto", "//absl/base:config", "//absl/base:core_headers", + "//absl/functional:any_invocable", "//absl/strings", ], ) @@ -355,6 +369,10 @@ hdrs = ["test_helpers.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + ":internal_users", + ":structured_proto_users", + ], deps = [ ":globals", "//absl/base:config", @@ -372,6 +390,10 @@ hdrs = ["test_matchers.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + ":internal_users", + ":structured_proto_users", + ], deps = [ ":test_helpers", "//absl/base:config", @@ -402,6 +424,10 @@ hdrs = ["proto.h"], copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = [ + ":internal_users", + ":structured_proto_users", + ], deps = [ "//absl/base", "//absl/base:config",
diff --git a/absl/log/internal/log_message.cc b/absl/log/internal/log_message.cc index 51961fd..9e7722d 100644 --- a/absl/log/internal/log_message.cc +++ b/absl/log/internal/log_message.cc
@@ -48,6 +48,7 @@ #include "absl/log/internal/log_format.h" #include "absl/log/internal/log_sink_set.h" #include "absl/log/internal/proto.h" +#include "absl/log/internal/structured_proto.h" #include "absl/log/log_entry.h" #include "absl/log/log_sink.h" #include "absl/log/log_sink_registry.h" @@ -632,6 +633,47 @@ template void LogMessage::CopyToEncodedBuffer< LogMessage::StringType::kNotLiteral>(char ch, size_t num); +template void LogMessage::CopyToEncodedBufferWithStructuredProtoField< + LogMessage::StringType::kLiteral>(StructuredProtoField field, + absl::string_view str); +template void LogMessage::CopyToEncodedBufferWithStructuredProtoField< + LogMessage::StringType::kNotLiteral>(StructuredProtoField field, + absl::string_view str); + +template <LogMessage::StringType str_type> +void LogMessage::CopyToEncodedBufferWithStructuredProtoField( + StructuredProtoField field, absl::string_view str) { + auto encoded_remaining_copy = data_->encoded_remaining(); + size_t encoded_field_size = BufferSizeForStructuredProtoField(field); + constexpr uint8_t tag_value = str_type == StringType::kLiteral + ? ValueTag::kStringLiteral + : ValueTag::kString; + auto start = EncodeMessageStart( + EventTag::kValue, + encoded_field_size + + BufferSizeFor(tag_value, WireType::kLengthDelimited) + str.size(), + &encoded_remaining_copy); + + // Write the encoded proto field. + if (!EncodeStructuredProtoField(field, encoded_remaining_copy)) { + // The header / field will not fit; zero `encoded_remaining()` so we + // don't write anything else later. + data_->encoded_remaining().remove_suffix(data_->encoded_remaining().size()); + return; + } + + // Write the string, truncating if necessary. + if (!EncodeStringTruncate(ValueTag::kString, str, &encoded_remaining_copy)) { + // The length of the string itself did not fit; zero `encoded_remaining()` + // so the value is not encoded at all. + data_->encoded_remaining().remove_suffix(data_->encoded_remaining().size()); + return; + } + + EncodeMessageLength(start, &encoded_remaining_copy); + data_->encoded_remaining() = encoded_remaining_copy; +} + // We intentionally don't return from these destructors. Disable MSVC's warning // about the destructor never returning as we do so intentionally here. #if defined(_MSC_VER) && !defined(__clang__)
diff --git a/absl/log/internal/log_message.h b/absl/log/internal/log_message.h index 474d1da..7d0e403 100644 --- a/absl/log/internal/log_message.h +++ b/absl/log/internal/log_message.h
@@ -41,6 +41,7 @@ #include "absl/base/log_severity.h" #include "absl/base/nullability.h" #include "absl/log/internal/nullguard.h" +#include "absl/log/internal/structured_proto.h" #include "absl/log/log_entry.h" #include "absl/log/log_sink.h" #include "absl/strings/has_absl_stringify.h" @@ -52,6 +53,8 @@ namespace log_internal { constexpr int kLogMessageBufferSize = 15000; +enum class StructuredStringType; + class LogMessage { public: struct InfoTag {}; @@ -217,6 +220,10 @@ struct LogMessageData; // Opaque type containing message state friend class AsLiteralImpl; friend class StringifySink; + template <StructuredStringType str_type> + friend class AsStructuredStringTypeImpl; + template <typename T> + friend class AsStructuredValueImpl; // This streambuf writes directly into the structured logging buffer so that // arbitrary types can be encoded as string data (using @@ -247,6 +254,13 @@ template <StringType str_type> void CopyToEncodedBuffer(char ch, size_t num) ABSL_ATTRIBUTE_NOINLINE; + // Copies `field` to the encoded buffer, then appends `str` after it + // (truncating `str` if necessary to fit). + template <StringType str_type> + void CopyToEncodedBufferWithStructuredProtoField(StructuredProtoField field, + absl::string_view str) + ABSL_ATTRIBUTE_NOINLINE; + // Returns `true` if the message is fatal or enabled debug-fatal. bool IsFatal() const;
diff --git a/absl/log/internal/structured.h b/absl/log/internal/structured.h index abc0a5a..50783df 100644 --- a/absl/log/internal/structured.h +++ b/absl/log/internal/structured.h
@@ -20,10 +20,14 @@ #define ABSL_LOG_INTERNAL_STRUCTURED_H_ #include <ostream> +#include <string> -#include "absl/base/config.h" #include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/functional/any_invocable.h" #include "absl/log/internal/log_message.h" +#include "absl/log/internal/structured_proto.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" namespace absl { @@ -54,6 +58,105 @@ } }; +enum class StructuredStringType { + kLiteral, + kNotLiteral, +}; + +// Structured log data for a string and associated structured proto field, +// both of which must outlive this object. +template <StructuredStringType str_type> +class ABSL_MUST_USE_RESULT AsStructuredStringTypeImpl final { + public: + constexpr AsStructuredStringTypeImpl( + absl::string_view str ABSL_ATTRIBUTE_LIFETIME_BOUND, + StructuredProtoField field ABSL_ATTRIBUTE_LIFETIME_BOUND) + : str_(str), field_(field) {} + + private: + absl::string_view str_; + StructuredProtoField field_; + + friend std::ostream& operator<<(std::ostream& os, + const AsStructuredStringTypeImpl& impl) { + return os << impl.str_; + } + void AddToMessage(LogMessage& m) const { + if (str_type == StructuredStringType::kLiteral) { + return m.CopyToEncodedBufferWithStructuredProtoField< + log_internal::LogMessage::StringType::kLiteral>(field_, str_); + } else { + return m.CopyToEncodedBufferWithStructuredProtoField< + log_internal::LogMessage::StringType::kNotLiteral>(field_, str_); + } + } + friend LogMessage& operator<<(LogMessage& m, + const AsStructuredStringTypeImpl& impl) { + impl.AddToMessage(m); + return m; + } +}; + +using AsStructuredLiteralImpl = + AsStructuredStringTypeImpl<StructuredStringType::kLiteral>; +using AsStructuredNotLiteralImpl = + AsStructuredStringTypeImpl<StructuredStringType::kNotLiteral>; + +// Structured log data for a stringifyable type T and associated structured +// proto field, both of which must outlive this object. +template <typename T> +class ABSL_MUST_USE_RESULT AsStructuredValueImpl final { + public: + using ValueFormatter = absl::AnyInvocable<std::string(T) const>; + + constexpr AsStructuredValueImpl( + T value ABSL_ATTRIBUTE_LIFETIME_BOUND, + StructuredProtoField field ABSL_ATTRIBUTE_LIFETIME_BOUND, + ValueFormatter value_formatter = + [](T value) { return absl::StrCat(value); }) + : value_(value), + field_(field), + value_formatter_(std::move(value_formatter)) {} + + private: + T value_; + StructuredProtoField field_; + ValueFormatter value_formatter_; + + friend std::ostream& operator<<(std::ostream& os, + const AsStructuredValueImpl& impl) { + return os << impl.value_formatter_(impl.value_); + } + void AddToMessage(LogMessage& m) const { + m.CopyToEncodedBufferWithStructuredProtoField< + log_internal::LogMessage::StringType::kNotLiteral>( + field_, value_formatter_(value_)); + } + friend LogMessage& operator<<(LogMessage& m, + const AsStructuredValueImpl& impl) { + impl.AddToMessage(m); + return m; + } +}; + +#ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION + +// Template deduction guide so `AsStructuredValueImpl(42, data)` works +// without specifying the template type. +template <typename T> +AsStructuredValueImpl(T value, StructuredProtoField field) + -> AsStructuredValueImpl<T>; + +// Template deduction guide so `AsStructuredValueImpl(42, data, formatter)` +// works without specifying the template type. +template <typename T> +AsStructuredValueImpl( + T value, StructuredProtoField field, + typename AsStructuredValueImpl<T>::ValueFormatter value_formatter) + -> AsStructuredValueImpl<T>; + +#endif // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION + } // namespace log_internal ABSL_NAMESPACE_END } // namespace absl