userver: userver/ugrpc/protobuf_logging.hpp Source File
Loading...
Searching...
No Matches
protobuf_logging.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @brief Public API for protobuf logging utilities.
5
6#include <limits>
7
8#include <fmt/format.h>
9#include <google/protobuf/message.h>
10#include <grpcpp/support/status.h>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace ugrpc {
15
16inline constexpr std::size_t kDefaultLoggingStringLimit = 1024;
17
18/// @brief Convert protobuf message to a limited JSON string for logging.
19///
20/// The message is serialized to [ProtoJSON](https://protobuf.dev/programming-guides/json/) with the following
21/// debugging tweaks:
22/// - Fields marked with the `[debug_redact]` option are hidden: their value is replaced with a `[REDACTED]` marker.
23/// - A `google.protobuf.Any` that cannot be expanded is written without its raw value, so those bytes cannot bypass
24/// `[debug_redact]`. An unknown payload type becomes `{"@type":"<type_url>","@error":"unresolved_any_type"}`.
25/// A payload that fails to parse becomes `{"@type":"<type_url>","@error":"invalid_payload"}`. The rest of the
26/// message is still serialized.
27/// - Serialization stops early once `limit` bytes have been produced instead of serializing the whole message and
28/// only then truncating, which saves CPU on large messages. The already-open JSON containers are closed, so the
29/// truncated part before the marker stays a well-formed JSON document.
30/// - When truncated, the string ends with a `...(truncated)` marker to indicate that the output was cut off.
31///
32/// @param message The protobuf message to convert.
33/// @param limit Maximum size of the resulting string (excluding the truncation marker).
34/// Avoid setting this to very large values as it may cause OOM (Out of Memory) issues.
35/// @returns JSON representation of the message, truncated if necessary.
36///
37/// @warning This is a debug representation of protobuf that is unstable and should only be used for diagnostics.
38/// The order of keys in maps is unstable; the format itself can change even within a single run.
39/// You CANNOT parse back from this (possibly truncated) representation.
40/// You CANNOT use it for equality match with reference values in gtest.
41///
42/// @note This function is `noexcept` and is safe to call from logging code and other no-throw contexts. If @a message
43/// cannot be serialized to ProtoJSON, the error is not propagated: the returned string instead has the form
44/// `serialization failed: <error description>`.
46 const google::protobuf::Message& message,
47 std::size_t limit = kDefaultLoggingStringLimit
48) noexcept;
49
50/// @brief Convert protobuf message to an unlimited JSON string for logging.
51///
52/// Convenience overload equivalent to calling @ref ToLimitedLoggingString with no effective size limit: the whole
53/// message is serialized and the result never carries a `...(truncated)` marker. See @ref ToLimitedLoggingString for
54/// the exact serialization behavior, the `noexcept` guarantee and the error-handling contract.
55///
56/// @param message The protobuf message to convert.
57/// @returns JSON representation of the whole message.
58///
59/// @warning Serializes the entire message, so avoid this overload on unbounded input; prefer @ref
60/// ToLimitedLoggingString for logging.
61inline std::string ToUnlimitedLoggingString(const google::protobuf::Message& message) noexcept {
62 return ToLimitedLoggingString(message, /*limit*/ std::numeric_limits<std::size_t>::max());
63}
64
65/// @brief Get error details from `grpc::Status` for logging with size limit.
66/// @param status The `grpc::Status` to extract details from.
67/// @param limit Maximum size of the `"details"` part: forwarded as-is to the nested @ref ToLimitedLoggingString
68/// call for the attached `google.rpc.Status`. `code`/`message` are not size-limited.
69/// Avoid setting this to very large values as it may cause OOM (Out of Memory) issues.
70/// @returns JSON object with a `"code"` key (always present, e.g. `{"code":"OK"}`), plus a `"message"` key when
71/// `status.error_message()` is non-empty, plus a `"details"` key when `status` carries a parseable
72/// `google.rpc.Status`, holding the JSON produced by
73/// @ref ToLimitedLoggingString(const google::protobuf::Message&, std::size_t) for that attached status. For an OK
74/// status both `message` and `details` are normally absent, so the result is just `{"code":"OK"}`.
75///
76/// @warning This is a debug representation of protobuf that is unstable and should only be used for diagnostics.
77/// The order of keys in maps is unstable; the format itself can change even within a single run.
78/// You CANNOT parse back from this representation.
79/// You CANNOT use it for equality match with reference values in gtest.
80/// @warning If `<details>` ends up truncated (or its own serialization fails), the `...(truncated)` marker (or a
81/// `serialization failed: ...` message) is embedded as raw, unquoted text in place of the `"details"` value, which
82/// makes the overall result invalid JSON in that case. `code` and `message` are always properly JSON-escaped and
83/// cannot break the surrounding JSON.
84///
85/// @note This function does not itself catch exceptions; its `noexcept` guarantee relies on
86/// `formats::json::StringBuilder` and the nested
87/// @ref ToLimitedLoggingString(const google::protobuf::Message&, std::size_t) call (used to produce `<details>`)
88/// never throwing.
89std::string ToLimitedLoggingString(const grpc::Status& status, std::size_t limit = kDefaultLoggingStringLimit) noexcept;
90
91/// @brief Get error details from `grpc::Status` for logging without size limit.
92///
93/// Convenience overload equivalent to calling @ref ToLimitedLoggingString with no effective size limit: the whole
94/// details part is serialized and never carries a `...(truncated)` marker. See @ref ToLimitedLoggingString for the
95/// output format, the `noexcept` guarantee and the error-handling contract.
96///
97/// @param status The `grpc::Status` to extract details from.
98/// @returns JSON representation of the status with its full details.
99///
100/// @warning Serializes the entire details part, so avoid this overload on unbounded input; prefer @ref
101/// ToLimitedLoggingString for logging.
102inline std::string ToUnlimitedLoggingString(const grpc::Status& status) noexcept {
103 return ToLimitedLoggingString(status, /*limit=*/std::numeric_limits<std::size_t>::max());
104}
105
106} // namespace ugrpc
107
108USERVER_NAMESPACE_END
109
110namespace fmt {
111
112/// @brief `fmt::format` support for protobuf messages
113template <typename T>
115struct formatter<T> {
116 constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
117
118 template <typename FormatContext>
119 auto format(const T& message, FormatContext& ctx) const {
120 return fmt::format_to(ctx.out(), "{}", USERVER_NAMESPACE::ugrpc::ToLimitedLoggingString(message));
121 }
122};
123
124} // namespace fmt