diff --git a/MODULE.bazel b/MODULE.bazel index 0b9e8326929bf..7b9106086414d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -21,7 +21,7 @@ bazel_dep(name = "gazelle", version = "0.45.0") bazel_dep(name = "abseil-cpp", version = "20250814.0") bazel_dep(name = "bazel_skylib", version = "1.8.1") bazel_dep(name = "crc32c", version = "1.1.0") -bazel_dep(name = "fmt", version = "9.1.0") +bazel_dep(name = "fmt", version = "12.1.0") bazel_dep(name = "googletest", version = "1.17.0") bazel_dep(name = "lexy", version = "2025.05.0") bazel_dep(name = "liburing", version = "2.14") diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index f2d10125453c3..06132df3333f6 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -68,8 +68,8 @@ "https://bcr.bazel.build/modules/curl/8.4.0/MODULE.bazel": "0bc250aa1cb69590049383df7a9537c809591fcf876c620f5f097c58fdc9bc10", "https://bcr.bazel.build/modules/doctest/2.4.12/MODULE.bazel": "0d9d294db1af4579e9ec46aae15d6cdf8cba885de668457739b89efd679ce05b", "https://bcr.bazel.build/modules/doctest/2.4.12/source.json": "b83b2edbf46d98dd3aae9ea5321e0e8b589112330bec73d280a173e40aab7da8", - "https://bcr.bazel.build/modules/fmt/9.1.0/MODULE.bazel": "e0db57b98ac793a576184554cd55b8782f5fbd043c43cda3088e2a5420462e8d", - "https://bcr.bazel.build/modules/fmt/9.1.0/source.json": "2e2bf876e44d0d43aa5d964b5f7f6e5db2d93118e1f653de575bca693d702641", + "https://bcr.bazel.build/modules/fmt/12.1.0/MODULE.bazel": "c6b460c936f408b371bf12ccee5ecbd5aed8f6abfb6e8b63fad0c622715fd458", + "https://bcr.bazel.build/modules/fmt/12.1.0/source.json": "d7b35221043d8d7c69e2a64d6a0783c97aa49c5ce198ee0a5ccd18c99f070b1a", "https://bcr.bazel.build/modules/gazelle/0.27.0/MODULE.bazel": "3446abd608295de6d90b4a8a118ed64a9ce11dcb3dda2dc3290a22056bd20996", "https://bcr.bazel.build/modules/gazelle/0.30.0/MODULE.bazel": "f888a1effe338491f35f0e0e85003b47bb9d8295ccba73c37e07702d8d31c65b", "https://bcr.bazel.build/modules/gazelle/0.32.0/MODULE.bazel": "b499f58a5d0d3537f3cf5b76d8ada18242f64ec474d8391247438bf04f58c7b8", @@ -179,6 +179,7 @@ "https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0", "https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8", "https://bcr.bazel.build/modules/rules_cc/0.2.0/MODULE.bazel": "b5c17f90458caae90d2ccd114c81970062946f49f355610ed89bebf954f5783c", + "https://bcr.bazel.build/modules/rules_cc/0.2.13/MODULE.bazel": "eecdd666eda6be16a8d9dc15e44b5c75133405e820f620a234acc4b1fdc5aa37", "https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84", "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07", "https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/MODULE.bazel": "b9527010e5fef060af92b6724edb3691970a5b1f76f74b21d39f7d433641be60", @@ -303,7 +304,7 @@ "moduleExtensions": { "//bazel:extensions.bzl%non_module_dependencies": { "general": { - "bzlTransitiveDigest": "G21akrZz7lt26dMKLVJ0IvM1AUCU8dzDuF6Uksw4ifQ=", + "bzlTransitiveDigest": "wLKvpZbW0I9xXgkrj1e5pRqR6N6bLiXc2VZIyW0KEr8=", "usagesDigest": "FEiDyZe9eAU6yEqnarZf0XMEUk+prUyYClvq1RU1J98=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -325,7 +326,8 @@ "strip_prefix": "avro-46fe1e36f680d75219cba46368de38321f1810ed", "url": "https://github.com/redpanda-data/avro/archive/46fe1e36f680d75219cba46368de38321f1810ed.tar.gz", "patches": [ - "@@//bazel/thirdparty:avro-snappy-includes.patch" + "@@//bazel/thirdparty:avro-snappy-includes.patch", + "@@//bazel/thirdparty:avro-fmt-const.patch" ], "patch_args": [ "-p1" @@ -1017,7 +1019,7 @@ }, "@@rules_apple+//apple:apple.bzl%provisioning_profile_repository_extension": { "general": { - "bzlTransitiveDigest": "l8RKpoHR7zGm/jAf9MjCgnfCV0G4HzOsM8aQwFTjH/w=", + "bzlTransitiveDigest": "hDzbh+WIiAvCEUYZMndgeIO3vBcMhyqJKruJJ9nVQkc=", "usagesDigest": "vsJl8Rw5NL+5Ag2wdUDoTeRF/5klkXO8545Iy7U1Q08=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1066,6 +1068,16 @@ ], [ "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", "rules_cc", "rules_cc+" ], @@ -4167,7 +4179,7 @@ }, "@@rules_rust+//crate_universe/private:internal_extensions.bzl%cu_nr": { "general": { - "bzlTransitiveDigest": "EEGkTefVGEP/2phgEvJ89Zb0Y5ni/qLP4QxeqnLq0Ao=", + "bzlTransitiveDigest": "kpf68imtNI7puIfWsTZkjSjvjnKLn7yaT8EPPoIBQeQ=", "usagesDigest": "WcrwUq7tMYKrrQoXBAJOrk0OAZY0JyWHpnidg71TX10=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -4267,6 +4279,16 @@ ], [ "rules_cc+", + "cc_compatibility_proxy", + "rules_cc++compatibility_proxy+cc_compatibility_proxy" + ], + [ + "rules_cc+", + "rules_cc", + "rules_cc+" + ], + [ + "rules_cc++compatibility_proxy+cc_compatibility_proxy", "rules_cc", "rules_cc+" ], diff --git a/bazel/internal.bzl b/bazel/internal.bzl index 6a7cfc51355f3..3211d683a33fb 100644 --- a/bazel/internal.bzl +++ b/bazel/internal.bzl @@ -18,12 +18,6 @@ def redpanda_copts(): copts.append("-Wno-missing-field-initializers") copts.append("-Wimplicit-fallthrough") - # for fmt v9 so that we do not need to write fmt::formatter wrappers for - # every output stream operator. note that we can't move to fmt >=v10 because - # this workaround macro was removed. so we'll need to rewrite the format - # handling for >1000 types. - copts.append("-DFMT_DEPRECATED_OSTREAM") - return copts def antithesis_deps(): diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 4b18a75f79d42..b593cec86ce52 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -25,7 +25,10 @@ def data_dependency(): sha256 = "791d9f163f458d0ba4c94251f58ef5af9157952a9569ce0968d89aeb585af34f", strip_prefix = "avro-46fe1e36f680d75219cba46368de38321f1810ed", url = "https://github.com/redpanda-data/avro/archive/46fe1e36f680d75219cba46368de38321f1810ed.tar.gz", - patches = ["//bazel/thirdparty:avro-snappy-includes.patch"], + patches = [ + "//bazel/thirdparty:avro-snappy-includes.patch", + "//bazel/thirdparty:avro-fmt-const.patch", + ], patch_args = ["-p1"], ) diff --git a/bazel/thirdparty/avro-fmt-const.patch b/bazel/thirdparty/avro-fmt-const.patch new file mode 100644 index 0000000000000..decda350f1860 --- /dev/null +++ b/bazel/thirdparty/avro-fmt-const.patch @@ -0,0 +1,26 @@ +diff --git a/lang/c++/include/avro/Types.hh b/lang/c++/include/avro/Types.hh +index 0000000..0000001 100644 +--- a/lang/c++/include/avro/Types.hh ++++ b/lang/c++/include/avro/Types.hh +@@ -113,7 +113,7 @@ + template<> + struct fmt::formatter : fmt::formatter { + template +- auto format(avro::Type t, FormatContext &ctx) { ++ auto format(avro::Type t, FormatContext &ctx) const { + return fmt::formatter::format(avro::toString(t), ctx); + } + }; +diff --git a/lang/c++/include/avro/Node.hh b/lang/c++/include/avro/Node.hh +index 0000000..0000001 100644 +--- a/lang/c++/include/avro/Node.hh ++++ b/lang/c++/include/avro/Node.hh +@@ -232,7 +232,7 @@ + template<> + struct fmt::formatter : fmt::formatter { + template +- auto format(const avro::Name &n, FormatContext &ctx) { ++ auto format(const avro::Name &n, FormatContext &ctx) const { + return fmt::formatter::format(n.fullname(), ctx); + } + }; diff --git a/src/v/base/BUILD b/src/v/base/BUILD index d638eb18386e9..bff2c57ef7917 100644 --- a/src/v/base/BUILD +++ b/src/v/base/BUILD @@ -7,6 +7,7 @@ redpanda_cc_library( ], hdrs = [ "compiler_utils.h", + "external_fmt.h", "format_to.h", "likely.h", "oncore.h", @@ -23,7 +24,10 @@ redpanda_cc_library( ], visibility = ["//visibility:public"], deps = [ + "@boost//:beast", + "@boost//:filesystem", "@boost//:outcome", + "@boost//:system", "@fmt", "@seastar", ], diff --git a/src/v/base/external_fmt.h b/src/v/base/external_fmt.h new file mode 100644 index 0000000000000..c07a762298668 --- /dev/null +++ b/src/v/base/external_fmt.h @@ -0,0 +1,34 @@ +/* + * Copyright 2026 Redpanda Data, Inc. + * + * Use of this software is governed by the Business Source License + * included in the file licenses/BSL.md + * + * As of the Change Date specified in that file, in accordance with + * the Business Source License, use of this software will be governed + * by the Apache License, Version 2.0 + */ + +#pragma once + +/// Formatters for commonly-used external / third-party types that have +/// operator<< but no fmt::formatter specialization. Include this header +/// whenever you need to format one of these types with {fmt}. + +#include +#include +#include +#include +#include + +template<> +struct fmt::formatter : fmt::ostream_formatter {}; + +template<> +struct fmt::formatter : fmt::ostream_formatter {}; + +template<> +struct fmt::formatter : fmt::ostream_formatter {}; + +template<> +struct fmt::formatter : fmt::ostream_formatter {}; diff --git a/src/v/base/format_to.h b/src/v/base/format_to.h index 8f7511bc6a546..a3aa6057945c5 100644 --- a/src/v/base/format_to.h +++ b/src/v/base/format_to.h @@ -13,9 +13,26 @@ #include #include +#include namespace fmt { +/// Formatter for std::unique_ptr — formats the pointee or "null". +template +requires is_formattable::value +struct formatter> { + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } + template + auto format(const std::unique_ptr& p, Ctx& ctx) const { + if (p) { + return fmt::format_to(ctx.out(), "{}", *p); + } + return fmt::format_to(ctx.out(), "null"); + } +}; + using iterator = format_context::iterator; template @@ -23,6 +40,17 @@ concept HasFormatToMethod = requires(const T& obj, iterator out) { { obj.format_to(out) } -> std::same_as; }; +/// Concept for types with a free function `format_to(const T&, iterator)`. +/// Used for enums and other types that cannot have member functions. +/// The free function must be ADL-findable (i.e. in the same namespace as T). +template +concept HasFormatToFreeFunction = !HasFormatToMethod + && requires(const T& obj, iterator out) { + { + format_to(obj, out) + } -> std::same_as; + }; + /** * A formatter that supports any type that implements a `format_to` method. * @@ -66,16 +94,67 @@ struct formatter { } }; +/** + * A formatter for types that provide a free function + * `format_to(const T&, fmt::iterator) -> fmt::iterator`, found via ADL. + * + * This is the counterpart of HasFormatToMethod for types that cannot have + * member functions (e.g. enums). + */ +template +struct formatter { + constexpr fmt::format_parse_context::iterator + parse(fmt::format_parse_context& ctx) const { + auto it = ctx.begin(); + if (it != ctx.end() && *it != '}') { + throw fmt::format_error("invalid format specifier for this type"); + } + return it; + } + + iterator format(const T& obj, format_context& ctx) const { + return format_to(obj, ctx.out()); + } +}; + } // namespace fmt +/// Checked wrapper around fmt::streamed(). +/// +/// fmt::streamed(x) formats x via operator<<. But types that satisfy +/// HasFormatToMethod / HasFormatToFreeFunction have a blanket operator<< +/// (below) that delegates back to fmt, creating an infinite recursion: +/// +/// format_to() -> streamed(x) -> operator<< -> fmt::print -> format_to() +/// +/// If a type is formattable through format_to, just use fmt::format("{}", x) +/// directly — there is no need for streamed(). This wrapper enforces that +/// invariant at compile time. +template +auto fmt_streamed(const T& val) { + using raw = std::remove_cvref_t; + static_assert( + !fmt::HasFormatToMethod && !fmt::HasFormatToFreeFunction, + "Do not use fmt::streamed() / fmt_streamed() on types with format_to — " + "use fmt::format(\"{}\", val) instead. streamed() triggers operator<< " + "which calls back into fmt, causing infinite recursion."); + return fmt::streamed(val); +} + namespace std { // For both googletest and for other external libraries that may use // `operator<<` to print stuff, give a blanket implementation that delegates to -// the `format_to` method. +// the `format_to` method or free function. // // We have to put this in the std namespace for overload resolution rules to be // able to find it for arbitrary T. -template +// +// WARNING: this creates a potential infinite-recursion footgun with +// fmt::streamed(). Never pass a type that has format_to through +// fmt::streamed() — use fmt_streamed() (above) which static_asserts against +// it, or just use fmt::format("{}", val) directly. +template +requires fmt::HasFormatToMethod || fmt::HasFormatToFreeFunction // NOLINTNEXTLINE(*-dcl58-*) ostream& operator<<(ostream& os, const T& obj) { fmt::print(os, "{}", obj); diff --git a/src/v/base/outcome.h b/src/v/base/outcome.h index 56dde5446302a..e37ba0463b30d 100644 --- a/src/v/base/outcome.h +++ b/src/v/base/outcome.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace outcome = boost::outcome_v2; @@ -43,3 +44,18 @@ using checked template constexpr bool is_result_v = outcome::is_basic_result_v; + +template +struct fmt::formatter> { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + template + auto + format(const boost::outcome_v2::basic_result& r, Ctx& ctx) const { + if (r.has_value()) { + return fmt::format_to(ctx.out(), "{}", r.value()); + } + return fmt::format_to(ctx.out(), "{}", r.error()); + } +}; diff --git a/src/v/base/source_location.h b/src/v/base/source_location.h index c07bdd8772e6d..fe14bf8950163 100644 --- a/src/v/base/source_location.h +++ b/src/v/base/source_location.h @@ -9,8 +9,9 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" + #include -#include #include namespace vlog { @@ -32,11 +33,6 @@ consteval const char* file_basename( } } // namespace detail -// file_line represents a source file (without the full path) and the line -// number in a file. -// -// Usage is similar to std::source_location, but the full path is dropped so -// that we don't include local paths from CI machines, etc. struct file_line { const char* filename; unsigned line; @@ -48,8 +44,8 @@ struct file_line { .line = src.line()}; } - friend std::ostream& operator<<(std::ostream& o, const file_line& fl) { - return o << fl.filename << ":" << fl.line; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}:{}", filename, line); } }; diff --git a/src/v/base/tests/format_to_test.cc b/src/v/base/tests/format_to_test.cc index fb10e9a801fa9..a32cf68c635ed 100644 --- a/src/v/base/tests/format_to_test.cc +++ b/src/v/base/tests/format_to_test.cc @@ -52,7 +52,7 @@ TEST(Formatter, InsideNamespacedFormatTo) { EXPECT_EQ("hello: {a: bar, b: 3}", fmt::format("hello: {}", f)) << f; ns::bar b{.f = f}; EXPECT_EQ( - "world: {f: {a: bar, b: 3}}", fmt::format("world: {}", fmt::streamed(b))) + "world: {f: {a: bar, b: 3}}", fmt::format("world: {}", fmt_streamed(b))) << b; } diff --git a/src/v/bytes/bytes.cc b/src/v/bytes/bytes.cc index d6d0bd6ab7281..5bca57930b8e1 100644 --- a/src/v/bytes/bytes.cc +++ b/src/v/bytes/bytes.cc @@ -28,11 +28,6 @@ size_t to_hex(ss::sstring& out, size_t pos, bytes_view data) { return data.size() * 2; } -std::ostream& operator<<(std::ostream& os, const bytes& b) { - return os << bytes_view(b); -} - -std::ostream& operator<<(std::ostream& os, const bytes_view& b) { - fmt::print(os, "{{bytes:{}}}", b.size()); - return os; +fmt::iterator bytes::format_to(fmt::iterator it) const { + return bytes_view(*this).format_to(it); } diff --git a/src/v/bytes/bytes.h b/src/v/bytes/bytes.h index f2ddfab362df3..e23b943feac4b 100644 --- a/src/v/bytes/bytes.h +++ b/src/v/bytes/bytes.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/inlined_vector.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" @@ -19,7 +20,6 @@ #include #include -#include #include class bytes_view; @@ -99,7 +99,7 @@ class bytes { return a.data_ < b.data_; } - friend std::ostream& operator<<(std::ostream& os, const bytes& b); + fmt::iterator format_to(fmt::iterator it) const; private: container_type data_; @@ -159,7 +159,9 @@ class bytes_view { a.begin(), a.end(), b.begin(), b.end()); } - friend std::ostream& operator<<(std::ostream& os, const bytes_view& b); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{bytes:{}}}", size()); + } private: explicit bytes_view(container_type data) @@ -304,3 +306,11 @@ operator^(const std::array& a, const std::array& b) { a.begin(), a.end(), b.begin(), out.begin(), std::bit_xor<>()); return out; } + +template<> +struct fmt::range_format_kind<::bytes, char> + : std::integral_constant {}; + +template<> +struct fmt::range_format_kind + : std::integral_constant {}; diff --git a/src/v/bytes/iobuf.cc b/src/v/bytes/iobuf.cc index 0ec17080be18a..c3000f87fc612 100644 --- a/src/v/bytes/iobuf.cc +++ b/src/v/bytes/iobuf.cc @@ -25,11 +25,6 @@ #include #include -std::ostream& operator<<(std::ostream& o, const iobuf& io) { - return o << "{bytes=" << io.size_bytes() - << ", fragments=" << std::distance(io.cbegin(), io.cend()) << "}"; -} - iobuf iobuf::copy() const { auto in = iobuf::iterator_consumer(cbegin(), cend()); return iobuf_copy(in, _size); diff --git a/src/v/bytes/iobuf.h b/src/v/bytes/iobuf.h index c29ef8575f309..42fa3541c0787 100644 --- a/src/v/bytes/iobuf.h +++ b/src/v/bytes/iobuf.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/likely.h" #include "base/seastarx.h" #include "bytes/details/io_allocation_size.h" @@ -276,6 +277,14 @@ class iobuf { // this method will throw as to not cause an oversized allocation. ss::sstring linearize_to_string() const; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{bytes={}, fragments={}}}", + size_bytes(), + std::distance(cbegin(), cend())); + } + private: void prepend(std::unique_ptr); @@ -292,9 +301,12 @@ class iobuf { container _frags; size_t _size{0}; - friend std::ostream& operator<<(std::ostream&, const iobuf&); }; +template<> +struct fmt::range_format_kind + : std::integral_constant {}; + inline void iobuf::clear() { _frags.clear_and_dispose(&details::dispose_io_fragment); _size = 0; diff --git a/src/v/bytes/tests/iobuf_fuzz.cc b/src/v/bytes/tests/iobuf_fuzz.cc index 94ba7a7e77f0e..1a81a5f50b299 100644 --- a/src/v/bytes/tests/iobuf_fuzz.cc +++ b/src/v/bytes/tests/iobuf_fuzz.cc @@ -16,6 +16,7 @@ * llvm-cov show src/v/bytes/tests/fuzz_iobuf -instr-profile=default.profdata * -format=html ../src/v/bytes/iobuf.h ../src/v/bytes/iobuf.cc > cov.html */ +#include "base/format_to.h" #include "base/units.h" #include "base/vassert.h" #include "bytes/iobuf.h" @@ -699,14 +700,13 @@ class driver { explicit op_spec(op_type op) : op(op) {} - friend std::ostream& operator<<(std::ostream& os, const op_spec& op) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{}(size={}, data.size={})", - op.op, - (op.size.has_value() ? fmt::format("{}", *op.size) : "null"), - op.data.size()); - return os; + op, + (size.has_value() ? fmt::format("{}", *size) : "null"), + data.size()); } }; diff --git a/src/v/cloud_io/BUILD b/src/v/cloud_io/BUILD index 7996d79d7de61..f87833c7ac386 100644 --- a/src/v/cloud_io/BUILD +++ b/src/v/cloud_io/BUILD @@ -34,6 +34,9 @@ redpanda_cc_library( "io_result.h", ], visibility = ["//visibility:public"], + deps = [ + "//src/v/base", + ], ) redpanda_cc_library( diff --git a/src/v/cloud_io/basic_cache_service_api.cc b/src/v/cloud_io/basic_cache_service_api.cc index b08e4d2ae4b69..07e0e6f2b7cc5 100644 --- a/src/v/cloud_io/basic_cache_service_api.cc +++ b/src/v/cloud_io/basic_cache_service_api.cc @@ -15,21 +15,6 @@ namespace cloud_io { -std::ostream& operator<<(std::ostream& o, cache_element_status s) { - switch (s) { - case cache_element_status::available: - o << "cache_element_available"; - break; - case cache_element_status::not_available: - o << "cache_element_not_available"; - break; - case cache_element_status::in_progress: - o << "cache_element_in_progress"; - break; - } - return o; -} - template void basic_space_reservation_guard::wrote_data( uint64_t written_bytes, size_t written_objects) { diff --git a/src/v/cloud_io/basic_cache_service_api.h b/src/v/cloud_io/basic_cache_service_api.h index 2acbc7659ea7b..930952d28460f 100644 --- a/src/v/cloud_io/basic_cache_service_api.h +++ b/src/v/cloud_io/basic_cache_service_api.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "base/units.h" @@ -44,7 +45,16 @@ enum class [[nodiscard]] cache_element_status { in_progress, }; -std::ostream& operator<<(std::ostream& o, cache_element_status); +inline fmt::iterator format_to(cache_element_status s, fmt::iterator out) { + switch (s) { + case cache_element_status::available: + return fmt::format_to(out, "cache_element_available"); + case cache_element_status::not_available: + return fmt::format_to(out, "cache_element_not_available"); + case cache_element_status::in_progress: + return fmt::format_to(out, "cache_element_in_progress"); + } +} template class basic_cache_service_api; diff --git a/src/v/cloud_io/io_result.cc b/src/v/cloud_io/io_result.cc index 48e1868ebaad6..a94a623871729 100644 --- a/src/v/cloud_io/io_result.cc +++ b/src/v/cloud_io/io_result.cc @@ -8,43 +8,3 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #include "cloud_io/io_result.h" - -namespace cloud_io { - -std::ostream& operator<<(std::ostream& o, const download_result& r) { - switch (r) { - case download_result::success: - o << "{success}"; - break; - case download_result::notfound: - o << "{key_not_found}"; - break; - case download_result::timedout: - o << "{timed_out}"; - break; - case download_result::failed: - o << "{failed}"; - break; - }; - return o; -} - -std::ostream& operator<<(std::ostream& o, const upload_result& r) { - switch (r) { - case upload_result::success: - o << "{success}"; - break; - case upload_result::timedout: - o << "{timed_out}"; - break; - case upload_result::failed: - o << "{failed}"; - break; - case upload_result::cancelled: - o << "{cancelled}"; - break; - }; - return o; -} - -} // namespace cloud_io diff --git a/src/v/cloud_io/io_result.h b/src/v/cloud_io/io_result.h index a9f8e7c296b2e..36c5f37a14cfe 100644 --- a/src/v/cloud_io/io_result.h +++ b/src/v/cloud_io/io_result.h @@ -9,8 +9,9 @@ */ #pragma once +#include "base/format_to.h" + #include -#include namespace cloud_io { @@ -21,13 +22,37 @@ enum class [[nodiscard]] download_result : int32_t { failed, }; +inline fmt::iterator format_to(download_result r, fmt::iterator out) { + switch (r) { + case download_result::success: + return fmt::format_to(out, "{{success}}"); + case download_result::notfound: + return fmt::format_to(out, "{{key_not_found}}"); + case download_result::timedout: + return fmt::format_to(out, "{{timed_out}}"); + case download_result::failed: + return fmt::format_to(out, "{{failed}}"); + } +} + enum class [[nodiscard]] upload_result : int32_t { success, timedout, failed, cancelled, }; -std::ostream& operator<<(std::ostream& o, const download_result& r); -std::ostream& operator<<(std::ostream& o, const upload_result& r); + +inline fmt::iterator format_to(upload_result r, fmt::iterator out) { + switch (r) { + case upload_result::success: + return fmt::format_to(out, "{{success}}"); + case upload_result::timedout: + return fmt::format_to(out, "{{timed_out}}"); + case upload_result::failed: + return fmt::format_to(out, "{{failed}}"); + case upload_result::cancelled: + return fmt::format_to(out, "{{cancelled}}"); + } +} } // namespace cloud_io diff --git a/src/v/cloud_roles/apply_abs_credentials.cc b/src/v/cloud_roles/apply_abs_credentials.cc index b99e34aa28f00..08d64ba4a74cd 100644 --- a/src/v/cloud_roles/apply_abs_credentials.cc +++ b/src/v/cloud_roles/apply_abs_credentials.cc @@ -33,9 +33,8 @@ void apply_abs_credentials::reset_creds(credentials creds) { _signature = signature_abs{abs_creds.storage_account, abs_creds.shared_key}; }; -std::ostream& apply_abs_credentials::print(std::ostream& os) const { - fmt::print(os, "apply_abs_credentials"); - return os; +fmt::iterator apply_abs_credentials::format_to(fmt::iterator it) const { + return fmt::format_to(it, "apply_abs_credentials"); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/apply_abs_credentials.h b/src/v/cloud_roles/apply_abs_credentials.h index 4a819a797e68e..dea04223da309 100644 --- a/src/v/cloud_roles/apply_abs_credentials.h +++ b/src/v/cloud_roles/apply_abs_credentials.h @@ -23,7 +23,7 @@ class apply_abs_credentials final : public apply_credentials::impl { add_auth(http::client::request_header& header) const override; void reset_creds(credentials creds) override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; bool is_oauth() const override { return false; } private: diff --git a/src/v/cloud_roles/apply_abs_oauth_credentials.cc b/src/v/cloud_roles/apply_abs_oauth_credentials.cc index 20f246faab21a..380ca4342042f 100644 --- a/src/v/cloud_roles/apply_abs_oauth_credentials.cc +++ b/src/v/cloud_roles/apply_abs_oauth_credentials.cc @@ -40,9 +40,8 @@ void apply_abs_oauth_credentials::reset_creds(credentials creds) { *this = apply_abs_oauth_credentials{std::get(creds)}; } -std::ostream& apply_abs_oauth_credentials::print(std::ostream& os) const { - fmt::print(os, "apply_abs_oauth_credentials"); - return os; +fmt::iterator apply_abs_oauth_credentials::format_to(fmt::iterator it) const { + return fmt::format_to(it, "apply_abs_oauth_credentials"); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/apply_abs_oauth_credentials.h b/src/v/cloud_roles/apply_abs_oauth_credentials.h index bd81b9474d39d..dfe6502742cea 100644 --- a/src/v/cloud_roles/apply_abs_oauth_credentials.h +++ b/src/v/cloud_roles/apply_abs_oauth_credentials.h @@ -25,7 +25,7 @@ class apply_abs_oauth_credentials final : public apply_credentials::impl { add_auth(http::client::request_header& header) const override; void reset_creds(credentials creds) override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; bool is_oauth() const override { return true; } private: diff --git a/src/v/cloud_roles/apply_aws_credentials.cc b/src/v/cloud_roles/apply_aws_credentials.cc index c246b5e83b526..a205d48db58ea 100644 --- a/src/v/cloud_roles/apply_aws_credentials.cc +++ b/src/v/cloud_roles/apply_aws_credentials.cc @@ -107,9 +107,8 @@ void apply_aws_credentials::reset_creds(credentials creds) { _session_token = aws_creds.session_token; } -std::ostream& apply_aws_credentials::print(std::ostream& os) const { - fmt::print(os, "apply_aws_credentials"); - return os; +fmt::iterator apply_aws_credentials::format_to(fmt::iterator it) const { + return fmt::format_to(it, "apply_aws_credentials"); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/apply_aws_credentials.h b/src/v/cloud_roles/apply_aws_credentials.h index 879c975cca36c..78ce912270075 100644 --- a/src/v/cloud_roles/apply_aws_credentials.h +++ b/src/v/cloud_roles/apply_aws_credentials.h @@ -23,7 +23,7 @@ class apply_aws_credentials final : public apply_credentials::impl { add_auth(http::client::request_header& header) const override; void reset_creds(credentials creds) override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; bool is_oauth() const override { return false; } private: diff --git a/src/v/cloud_roles/apply_credentials.cc b/src/v/cloud_roles/apply_credentials.cc index 7ee3ed985c0be..6c9619346819d 100644 --- a/src/v/cloud_roles/apply_credentials.cc +++ b/src/v/cloud_roles/apply_credentials.cc @@ -34,7 +34,4 @@ apply_credentials make_credentials_applier(credentials creds) { })}; } -std::ostream& operator<<(std::ostream& os, const apply_credentials& ac) { - return ac.print(os); -} } // namespace cloud_roles diff --git a/src/v/cloud_roles/apply_credentials.h b/src/v/cloud_roles/apply_credentials.h index 92d692964ad1c..76d3f9ddd99fa 100644 --- a/src/v/cloud_roles/apply_credentials.h +++ b/src/v/cloud_roles/apply_credentials.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_roles/types.h" #include "http/client.h" @@ -35,7 +36,7 @@ class apply_credentials { /// headers. virtual void reset_creds(credentials creds) = 0; - virtual std::ostream& print(std::ostream& os) const = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; /// Returns true if this implementation is based on oauth tokens. /// meant to deal with API that do not support OAuth @@ -55,7 +56,9 @@ class apply_credentials { _impl->reset_creds(std::move(creds)); } - std::ostream& print(std::ostream& os) const { return _impl->print(os); } + fmt::iterator format_to(fmt::iterator it) const { + return _impl->format_to(it); + } bool is_oauth() const { return _impl->is_oauth(); } @@ -63,8 +66,6 @@ class apply_credentials { std::unique_ptr _impl; }; -std::ostream& operator<<(std::ostream& os, const apply_credentials& ac); - /// Creates a credential applier based on the kind of credentials passed in. The /// input credentials object is a sum type, and it can contain any one of the /// variants for all supported credentials sources. This function uses the diff --git a/src/v/cloud_roles/apply_gcp_credentials.cc b/src/v/cloud_roles/apply_gcp_credentials.cc index 4a124ff8885de..cd4b1d36a7e38 100644 --- a/src/v/cloud_roles/apply_gcp_credentials.cc +++ b/src/v/cloud_roles/apply_gcp_credentials.cc @@ -35,9 +35,8 @@ void apply_gcp_credentials::reset_creds(credentials creds) { fmt::format("Bearer {}", std::get(creds).oauth_token())}; } -std::ostream& apply_gcp_credentials::print(std::ostream& os) const { - fmt::print(os, "apply_gcp_credentials"); - return os; +fmt::iterator apply_gcp_credentials::format_to(fmt::iterator it) const { + return fmt::format_to(it, "apply_gcp_credentials"); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/apply_gcp_credentials.h b/src/v/cloud_roles/apply_gcp_credentials.h index 5c382d53e0d51..9494bb0bf36bf 100644 --- a/src/v/cloud_roles/apply_gcp_credentials.h +++ b/src/v/cloud_roles/apply_gcp_credentials.h @@ -22,7 +22,7 @@ class apply_gcp_credentials final : public apply_credentials::impl { add_auth(http::client::request_header& header) const override; void reset_creds(credentials creds) override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; bool is_oauth() const override { return true; } private: diff --git a/src/v/cloud_roles/aws_refresh_impl.cc b/src/v/cloud_roles/aws_refresh_impl.cc index d5a80adcb3570..4a53fd4be6116 100644 --- a/src/v/cloud_roles/aws_refresh_impl.cc +++ b/src/v/cloud_roles/aws_refresh_impl.cc @@ -219,9 +219,8 @@ ss::future aws_refresh_impl::make_request_with_token( co_await make_api_client("aws"), std::move(req)); } -std::ostream& aws_refresh_impl::print(std::ostream& os) const { - fmt::print(os, "aws_refresh_impl{{address:{}}}", address()); - return os; +fmt::iterator aws_refresh_impl::format_to(fmt::iterator it) const { + return fmt::format_to(it, "aws_refresh_impl{{address:{}}}", address()); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/aws_refresh_impl.h b/src/v/cloud_roles/aws_refresh_impl.h index c8a00fcb75399..8fb85b7c4091a 100644 --- a/src/v/cloud_roles/aws_refresh_impl.h +++ b/src/v/cloud_roles/aws_refresh_impl.h @@ -27,7 +27,7 @@ class aws_refresh_impl final : public refresh_credentials::impl { retry_params retry_params = default_retry_params); ss::future fetch_credentials() override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; protected: api_response_parse_result parse_response(iobuf resp) override; diff --git a/src/v/cloud_roles/aws_sts_refresh_impl.cc b/src/v/cloud_roles/aws_sts_refresh_impl.cc index 26704e1aa8219..05a198b341107 100644 --- a/src/v/cloud_roles/aws_sts_refresh_impl.cc +++ b/src/v/cloud_roles/aws_sts_refresh_impl.cc @@ -197,9 +197,8 @@ api_response_parse_result aws_sts_refresh_impl::parse_response(iobuf resp) { }; } -std::ostream& aws_sts_refresh_impl::print(std::ostream& os) const { - fmt::print(os, "aws_sts_refresh_impl{{address:{}}}", address()); - return os; +fmt::iterator aws_sts_refresh_impl::format_to(fmt::iterator it) const { + return fmt::format_to(it, "aws_sts_refresh_impl{{address:{}}}", address()); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/aws_sts_refresh_impl.h b/src/v/cloud_roles/aws_sts_refresh_impl.h index 24e63d4515eeb..4bd1a30f3c785 100644 --- a/src/v/cloud_roles/aws_sts_refresh_impl.h +++ b/src/v/cloud_roles/aws_sts_refresh_impl.h @@ -27,7 +27,7 @@ class aws_sts_refresh_impl final : public refresh_credentials::impl { retry_params retry_params = default_retry_params); ss::future fetch_credentials() override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; protected: api_response_parse_result parse_response(iobuf resp) override; diff --git a/src/v/cloud_roles/azure_aks_refresh_impl.cc b/src/v/cloud_roles/azure_aks_refresh_impl.cc index 9bc659bdb6308..b71e301cbe020 100644 --- a/src/v/cloud_roles/azure_aks_refresh_impl.cc +++ b/src/v/cloud_roles/azure_aks_refresh_impl.cc @@ -84,9 +84,9 @@ azure_aks_refresh_impl::azure_aks_refresh_impl( , tenant_id_{load_from_env(env_var_azure_tenant_id)} , federated_token_file_{load_from_env(env_var_azure_federated_token_file)} {} -std::ostream& azure_aks_refresh_impl::print(std::ostream& os) const { - fmt::print(os, "azure_aks_refresh_impl{{address:{}}}", address()); - return os; +fmt::iterator azure_aks_refresh_impl::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "azure_aks_refresh_impl{{address:{}}}", address()); } ss::future azure_aks_refresh_impl::fetch_credentials() { diff --git a/src/v/cloud_roles/azure_aks_refresh_impl.h b/src/v/cloud_roles/azure_aks_refresh_impl.h index f71182da43fc4..a8edc5db39f15 100644 --- a/src/v/cloud_roles/azure_aks_refresh_impl.h +++ b/src/v/cloud_roles/azure_aks_refresh_impl.h @@ -34,7 +34,7 @@ class azure_aks_refresh_impl final : public refresh_credentials::impl { /// encountered during the fetch operation ss::future fetch_credentials() override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; protected: /// Helper to parse the iobuf returned from API into a credentials diff --git a/src/v/cloud_roles/azure_vm_refresh_impl.cc b/src/v/cloud_roles/azure_vm_refresh_impl.cc index 64c516af762d2..d567071202b9f 100644 --- a/src/v/cloud_roles/azure_vm_refresh_impl.cc +++ b/src/v/cloud_roles/azure_vm_refresh_impl.cc @@ -27,9 +27,8 @@ azure_vm_refresh_impl::azure_vm_refresh_impl( : refresh_credentials::impl( std::move(address), std::move(region), as, retry_params) {} -std::ostream& azure_vm_refresh_impl::print(std::ostream& os) const { - fmt::print(os, "azure_vm_refresh_impl{{address:{}}}", address()); - return os; +fmt::iterator azure_vm_refresh_impl::format_to(fmt::iterator it) const { + return fmt::format_to(it, "azure_vm_refresh_impl{{address:{}}}", address()); } ss::future azure_vm_refresh_impl::fetch_credentials() { diff --git a/src/v/cloud_roles/azure_vm_refresh_impl.h b/src/v/cloud_roles/azure_vm_refresh_impl.h index 4b0250c13d21d..da0077591ec3f 100644 --- a/src/v/cloud_roles/azure_vm_refresh_impl.h +++ b/src/v/cloud_roles/azure_vm_refresh_impl.h @@ -30,7 +30,7 @@ class azure_vm_refresh_impl final : public refresh_credentials::impl { retry_params retry_params = default_retry_params); ss::future fetch_credentials() override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; protected: api_response_parse_result parse_response(iobuf response) override; diff --git a/src/v/cloud_roles/gcp_refresh_impl.cc b/src/v/cloud_roles/gcp_refresh_impl.cc index 90501b428a9ae..93e9888189080 100644 --- a/src/v/cloud_roles/gcp_refresh_impl.cc +++ b/src/v/cloud_roles/gcp_refresh_impl.cc @@ -79,9 +79,8 @@ api_response_parse_result gcp_refresh_impl::parse_response(iobuf response) { doc[gcp_response_schema::token_field.data()].GetString()}}; } -std::ostream& gcp_refresh_impl::print(std::ostream& os) const { - fmt::print(os, "gcp_refresh_impl{{address:{}}}", address()); - return os; +fmt::iterator gcp_refresh_impl::format_to(fmt::iterator it) const { + return fmt::format_to(it, "gcp_refresh_impl{{address:{}}}", address()); } } // namespace cloud_roles diff --git a/src/v/cloud_roles/gcp_refresh_impl.h b/src/v/cloud_roles/gcp_refresh_impl.h index 2406f3f052d43..3affe43ab391b 100644 --- a/src/v/cloud_roles/gcp_refresh_impl.h +++ b/src/v/cloud_roles/gcp_refresh_impl.h @@ -27,7 +27,7 @@ class gcp_refresh_impl final : public refresh_credentials::impl { retry_params retry_params = default_retry_params); ss::future fetch_credentials() override; - std::ostream& print(std::ostream& os) const override; + fmt::iterator format_to(fmt::iterator it) const override; protected: api_response_parse_result parse_response(iobuf response) override; diff --git a/src/v/cloud_roles/refresh_credentials.cc b/src/v/cloud_roles/refresh_credentials.cc index 3693d6bb3e048..361f49eea4097 100644 --- a/src/v/cloud_roles/refresh_credentials.cc +++ b/src/v/cloud_roles/refresh_credentials.cc @@ -466,8 +466,4 @@ refresh_credentials make_refresh_credentials( } } -std::ostream& operator<<(std::ostream& os, const refresh_credentials& rc) { - return rc.print(os); -} - } // namespace cloud_roles diff --git a/src/v/cloud_roles/refresh_credentials.h b/src/v/cloud_roles/refresh_credentials.h index 99fd866cd9af8..037aedf26e29d 100644 --- a/src/v/cloud_roles/refresh_credentials.h +++ b/src/v/cloud_roles/refresh_credentials.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_roles/logger.h" #include "cloud_roles/probe.h" #include "cloud_roles/types.h" @@ -61,7 +62,7 @@ class refresh_credentials { ss::future<> sleep_until_expiry() const; - virtual std::ostream& print(std::ostream& os) const = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; /// When a retryable error is seen, increment retries and set a small /// backoff before attempting to fetch credentials again. If the retries @@ -136,7 +137,9 @@ class refresh_credentials { void start(); - std::ostream& print(std::ostream& os) const { return _impl->print(os); } + fmt::iterator format_to(fmt::iterator it) const { + return _impl->format_to(it); + } ss::future fetch_credentials() { return _impl->fetch_credentials(); @@ -176,8 +179,6 @@ class refresh_credentials { std::unique_ptr _probe; }; -std::ostream& operator<<(std::ostream& os, const refresh_credentials& rc); - static constexpr retry_params default_retry_params{ .backoff_ms = std::chrono::milliseconds{500}, .max_retries = 8}; diff --git a/src/v/cloud_roles/signature.cc b/src/v/cloud_roles/signature.cc index a3450f7c4f05e..8226342cc5585 100644 --- a/src/v/cloud_roles/signature.cc +++ b/src/v/cloud_roles/signature.cc @@ -28,17 +28,10 @@ #include #include #include -#include +#include #include -namespace { -constexpr auto iso_8061_date_fmt = FMT_COMPILE("{:%Y%m%d}"); -constexpr auto iso_8061_datetime_fmt = FMT_COMPILE("{:%Y%m%dT%H%M%SZ}"); -constexpr auto rfc_9110_datetime_fmt = FMT_COMPILE( - "{:%a, %d %b %Y %H:%M:%S %Z}"); -} // namespace - namespace cloud_roles { constexpr int sha256_digest_length = 32; @@ -74,6 +67,12 @@ time_source::time_source() time_source::time_source(timestamp instant) : time_source([instant]() { return instant; }, 0) {} +namespace { +constexpr auto iso_8061_date_fmt = "{:%Y%m%d}"; +constexpr auto iso_8061_datetime_fmt = "{:%Y%m%dT%H%M%SZ}"; +constexpr auto rfc_9110_datetime_fmt = "{:%a, %d %b %Y %H:%M:%S %Z}"; +} // namespace + ss::sstring time_source::format_date() const { return format(iso_8061_date_fmt); } @@ -137,7 +136,7 @@ inline void tolower(ss::sstring& str) { } } -ss::sstring time_source::format(auto fmt) const { +ss::sstring time_source::format(fmt::format_string fmt) const { const auto point = _gettime_fn(); const std::time_t time = std::chrono::system_clock::to_time_t(point); const std::tm gm = fmt::gmtime(time); diff --git a/src/v/cloud_roles/signature.h b/src/v/cloud_roles/signature.h index 3f179c674a4f3..ea125b1264815 100644 --- a/src/v/cloud_roles/signature.h +++ b/src/v/cloud_roles/signature.h @@ -73,7 +73,7 @@ class time_source { explicit time_source(Fn&& fn, int); /// Format date-time according to format string - ss::sstring format(auto fmt) const; + ss::sstring format(fmt::format_string fmt) const; static timestamp default_source(); ss::noncopyable_function _gettime_fn; diff --git a/src/v/cloud_roles/types.cc b/src/v/cloud_roles/types.cc index 5e0f951f4f722..f1d406a93ce1d 100644 --- a/src/v/cloud_roles/types.cc +++ b/src/v/cloud_roles/types.cc @@ -14,78 +14,8 @@ namespace cloud_roles { -std::ostream& operator<<(std::ostream& os, api_request_error_kind kind) { - switch (kind) { - case api_request_error_kind::failed_abort: - return os << "failed_abort"; - case api_request_error_kind::failed_retryable: - return os << "failed_retryable"; - } -} - -std::ostream& -operator<<(std::ostream& os, const api_request_error& request_error) { - fmt::print( - os, - "api_request_error{{reason:{}, error_kind:{}}}", - request_error.reason, - request_error.error_kind); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const malformed_api_response_error& err) { - fmt::print( - os, - "malformed_api_response_error{{missing_fields:{}}}", - err.missing_fields); - return os; -} - -std::ostream& operator<<(std::ostream& os, const gcp_credentials& gc) { - fmt::print( - os, "gcp_credentials{{oauth_token:**{}**}}", gc.oauth_token().size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const aws_credentials& ac) { - fmt::print( - os, - "aws_credentials{{access_key_id: **{}**, secret_access_key: **{}**, " - "session_token: **{}**, region: {}, service: {}}}", - ac.access_key_id().size(), - ac.secret_access_key().size(), - ac.session_token.value_or(session_token{})().size(), - ac.region(), - ac.service()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const abs_credentials& ac) { - fmt::print( - os, - "abs_credentials{{storage_account: **{}**, shared_key: **{}**}}", - ac.storage_account().size(), - ac.shared_key().size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const abs_oauth_credentials& ac) { - fmt::print( - os, - "abs_oauth_credentials{{oauth_token:**{}**}}", - ac.oauth_token().size()); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const api_response_parse_error& err) { - fmt::print(os, "api_response_parse_error{{reason:{}}}", err.reason); - return os; -} - // tmp trick to ensure that we are not calling into infinite recursion if -// there is a new credential but no operator<< +// there is a new credential but no format_to template Cred> std::ostream& operator<<(std::ostream& os, const Cred& c) { ss::visit(c, [&os](const auto& creds) { os << creds; }); diff --git a/src/v/cloud_roles/types.h b/src/v/cloud_roles/types.h index c9dc3c107599a..c2b389a130f0b 100644 --- a/src/v/cloud_roles/types.h +++ b/src/v/cloud_roles/types.h @@ -10,6 +10,8 @@ #pragma once +#include "base/external_fmt.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" #include "utils/named_type.h" @@ -18,6 +20,7 @@ #include #include +#include #include @@ -41,12 +44,27 @@ bool is_retryable(boost::beast::http::status status); enum class api_request_error_kind { failed_abort, failed_retryable }; -std::ostream& operator<<(std::ostream& os, api_request_error_kind kind); +inline fmt::iterator format_to(api_request_error_kind kind, fmt::iterator out) { + switch (kind) { + case api_request_error_kind::failed_abort: + return fmt::format_to(out, "failed_abort"); + case api_request_error_kind::failed_retryable: + return fmt::format_to(out, "failed_retryable"); + } +} struct api_request_error { boost::beast::http::status status{boost::beast::http::status::ok}; ss::sstring reason; api_request_error_kind error_kind; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "api_request_error{{reason:{}, error_kind:{}}}", + reason, + error_kind); + } }; api_request_error make_abort_error(const std::exception& ex); @@ -57,31 +75,38 @@ api_request_error make_retryable_error(const std::exception& ex); api_request_error make_retryable_error(ss::sstring reason, boost::beast::http::status status); -std::ostream& -operator<<(std::ostream& os, const api_request_error& request_error); - using api_response = std::variant; struct malformed_api_response_error { std::vector missing_fields; -}; -std::ostream& -operator<<(std::ostream& os, const malformed_api_response_error& err); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "malformed_api_response_error{{missing_fields:{}}}", + missing_fields); + } +}; struct api_response_parse_error { ss::sstring reason; -}; -std::ostream& operator<<(std::ostream& os, const api_response_parse_error& err); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "api_response_parse_error{{reason:{}}}", reason); + } +}; using oauth_token_str = named_type; struct gcp_credentials { oauth_token_str oauth_token; -}; -std::ostream& operator<<(std::ostream& os, const gcp_credentials& gc); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "gcp_credentials{{oauth_token:**{}**}}", oauth_token().size()); + } +}; using aws_service_name = named_type; using aws_region_name = named_type; @@ -97,24 +122,46 @@ struct aws_credentials { std::optional session_token; aws_region_name region; aws_service_name service; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "aws_credentials{{access_key_id: **{}**, secret_access_key: " + "**{}**, " + "session_token: **{}**, region: {}, service: {}}}", + access_key_id().size(), + secret_access_key().size(), + session_token.value_or(cloud_roles::session_token{})().size(), + region(), + service()); + } }; struct abs_credentials { storage_account storage_account; private_key_str shared_key; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "abs_credentials{{storage_account: **{}**, shared_key: **{}**}}", + storage_account().size(), + shared_key().size()); + } }; // AKS federated OpenID Credentials uses Azure's managed identities to retrieve // a Oauth authorization token struct abs_oauth_credentials { oauth_token_str oauth_token; -}; -std::ostream& operator<<(std::ostream& os, const abs_oauth_credentials& ac); - -std::ostream& operator<<(std::ostream& os, const abs_credentials& ac); - -std::ostream& operator<<(std::ostream& os, const aws_credentials& ac); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "abs_oauth_credentials{{oauth_token:**{}**}}", + oauth_token().size()); + } +}; using credentials = std::variant< aws_credentials, @@ -123,7 +170,7 @@ using credentials = std::variant< abs_oauth_credentials>; // tmp trick to ensure that we are not calling into infinite recursion if -// there is a new credential but no operator<< +// there is a new credential but no format_to template Cred> std::ostream& operator<<(std::ostream& os, const Cred& c); diff --git a/src/v/cloud_storage/async_manifest_view.cc b/src/v/cloud_storage/async_manifest_view.cc index 4db66488d5c77..5eb7566fea4f8 100644 --- a/src/v/cloud_storage/async_manifest_view.cc +++ b/src/v/cloud_storage/async_manifest_view.cc @@ -20,7 +20,6 @@ #include "config/configuration.h" #include "model/fundamental.h" #include "model/timestamp.h" -#include "ssx/sformat.h" #include "utils/retry_chain_node.h" #include @@ -52,21 +51,18 @@ ss::log_level log_level_for_error(cloud_storage::error_outcome err) { namespace cloud_storage { -static ss::sstring to_string(const async_view_search_query_t& t) { +fmt::iterator format_to(const async_view_search_query_t& q, fmt::iterator out) { return ss::visit( - t, - [&](model::offset ro) { return ssx::sformat("[offset: {}]", ro); }, - [&](kafka::offset ko) { return ssx::sformat("[kafka offset: {}]", ko); }, + q, + [&](model::offset ro) { return fmt::format_to(out, "[offset: {}]", ro); }, + [&](kafka::offset ko) { + return fmt::format_to(out, "[kafka offset: {}]", ko); + }, [&](const async_view_timestamp_query& ts) { - return ssx::sformat("{}", ts); + return fmt::format_to(out, "{}", ts); }); } -std::ostream& operator<<(std::ostream& s, const async_view_search_query_t& q) { - s << to_string(q); - return s; -} - static bool contains(const partition_manifest& m, const async_view_search_query_t& query) { return ss::visit( @@ -103,24 +99,6 @@ contains(const partition_manifest& m, const async_view_search_query_t& query) { }); } -std::ostream& operator<<(std::ostream& o, async_manifest_view_cursor_status s) { - switch (s) { - case async_manifest_view_cursor_status::empty: - fmt::print(o, "empty"); - break; - case async_manifest_view_cursor_status::evicted: - fmt::print(o, "evicted"); - break; - case async_manifest_view_cursor_status::materialized_stm: - fmt::print(o, "materialized_stm"); - break; - case async_manifest_view_cursor_status::materialized_spillover: - fmt::print(o, "materialized_spillover"); - break; - } - return o; -} - async_manifest_view_cursor::async_manifest_view_cursor( async_manifest_view& view, model::offset begin, diff --git a/src/v/cloud_storage/async_manifest_view.h b/src/v/cloud_storage/async_manifest_view.h index 4aaadfbbbae71..1fa5cb7634c7f 100644 --- a/src/v/cloud_storage/async_manifest_view.h +++ b/src/v/cloud_storage/async_manifest_view.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "cloud_storage/async_manifest_materializer.h" #include "cloud_storage/fwd.h" @@ -41,15 +42,14 @@ struct async_view_timestamp_query { , ts(ts) , max_offset(max_offset) {} - friend std::ostream& - operator<<(std::ostream& o, const async_view_timestamp_query& q) { - fmt::print( - o, - "async_view_timestamp_query{{min_offset:{}, ts:{}, max_offset:{}}}", - q.min_offset, - q.ts, - q.max_offset); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "async_view_timestamp_query{{min_offset:{}, ts:{}, " + "max_offset:{}}}", + min_offset, + ts, + max_offset); } kafka::offset min_offset; @@ -61,7 +61,23 @@ struct async_view_timestamp_query { using async_view_search_query_t = std::variant; -std::ostream& operator<<(std::ostream&, const async_view_search_query_t&); +fmt::iterator format_to(const async_view_search_query_t& q, fmt::iterator out); + +} // namespace cloud_storage + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + fmt::iterator format( + const cloud_storage::async_view_search_query_t& q, + fmt::format_context& ctx) const { + return cloud_storage::format_to(q, ctx.out()); + } +}; + +namespace cloud_storage { class async_manifest_view; @@ -219,7 +235,19 @@ enum class async_manifest_view_cursor_status { evicted, }; -std::ostream& operator<<(std::ostream&, async_manifest_view_cursor_status); +inline fmt::iterator +format_to(async_manifest_view_cursor_status s, fmt::iterator out) { + switch (s) { + case async_manifest_view_cursor_status::empty: + return fmt::format_to(out, "empty"); + case async_manifest_view_cursor_status::evicted: + return fmt::format_to(out, "evicted"); + case async_manifest_view_cursor_status::materialized_stm: + return fmt::format_to(out, "materialized_stm"); + case async_manifest_view_cursor_status::materialized_spillover: + return fmt::format_to(out, "materialized_spillover"); + } +} /// The cursor can be used to traverse manifest /// asynchronously. The full content of the manifest diff --git a/src/v/cloud_storage/base_manifest.cc b/src/v/cloud_storage/base_manifest.cc index dc6aa04f8aff5..44b90523ed3bb 100644 --- a/src/v/cloud_storage/base_manifest.cc +++ b/src/v/cloud_storage/base_manifest.cc @@ -14,39 +14,6 @@ namespace cloud_storage { base_manifest::~base_manifest() = default; -std::ostream& operator<<(std::ostream& s, manifest_type t) { - switch (t) { - case manifest_type::topic: - s << "topic"; - break; - case manifest_type::partition: - s << "partition"; - break; - case manifest_type::tx_range: - s << "tx-range"; - break; - case manifest_type::cluster_metadata: - s << "cluster-metadata"; - break; - case manifest_type::spillover: - s << "spillover"; - break; - case manifest_type::topic_mount: - s << "topic_mount"; - break; - } - return s; -} - -std::ostream& operator<<(std::ostream& os, manifest_format f) { - switch (f) { - case manifest_format::json: - return os << "json"; - case manifest_format::serde: - return os << "serde"; - } -} - ss::future base_manifest::serialize() const { auto buf = co_await serialize_buf(); auto size = buf.size_bytes(); diff --git a/src/v/cloud_storage/base_manifest.h b/src/v/cloud_storage/base_manifest.h index 93b71edc15330..789b36e2938bd 100644 --- a/src/v/cloud_storage/base_manifest.h +++ b/src/v/cloud_storage/base_manifest.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" #include "bytes/iostream.h" @@ -34,14 +35,36 @@ enum class manifest_type { topic_mount }; -std::ostream& operator<<(std::ostream& s, manifest_type t); +inline fmt::iterator format_to(manifest_type t, fmt::iterator out) { + switch (t) { + case manifest_type::topic: + return fmt::format_to(out, "topic"); + case manifest_type::partition: + return fmt::format_to(out, "partition"); + case manifest_type::tx_range: + return fmt::format_to(out, "tx-range"); + case manifest_type::cluster_metadata: + return fmt::format_to(out, "cluster-metadata"); + case manifest_type::spillover: + return fmt::format_to(out, "spillover"); + case manifest_type::topic_mount: + return fmt::format_to(out, "topic_mount"); + } +} enum class manifest_format { json, serde, }; -std::ostream& operator<<(std::ostream& s, manifest_format t); +inline fmt::iterator format_to(manifest_format f, fmt::iterator out) { + switch (f) { + case manifest_format::json: + return fmt::format_to(out, "json"); + case manifest_format::serde: + return fmt::format_to(out, "serde"); + } +} class base_manifest { public: virtual ~base_manifest(); diff --git a/src/v/cloud_storage/configuration.cc b/src/v/cloud_storage/configuration.cc index 35d0360d1b650..962685080c61a 100644 --- a/src/v/cloud_storage/configuration.cc +++ b/src/v/cloud_storage/configuration.cc @@ -199,16 +199,15 @@ configuration::get_bucket_config() { } } -std::ostream& operator<<(std::ostream& o, const configuration& cfg) { - fmt::print( - o, +fmt::iterator configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{connection_limit: {}, client_config: {}, " "bucket_name: {}, cloud_credentials_source: {}}}", - cfg.connection_limit, - cfg.client_config, - cfg.bucket_name, - cfg.cloud_credentials_source); - return o; + connection_limit, + client_config, + bucket_name, + cloud_credentials_source); } }; // namespace cloud_storage diff --git a/src/v/cloud_storage/configuration.h b/src/v/cloud_storage/configuration.h index bcd0d1c0f946c..15e18820cae68 100644 --- a/src/v/cloud_storage/configuration.h +++ b/src/v/cloud_storage/configuration.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "cloud_storage/types.h" #include "cloud_storage_clients/configuration.h" #include "config/property.h" @@ -25,7 +26,7 @@ struct configuration { model::cloud_credentials_source cloud_credentials_source; - friend std::ostream& operator<<(std::ostream& o, const configuration& cfg); + fmt::iterator format_to(fmt::iterator it) const; static ss::future get_config(); static ss::future get_s3_config(); diff --git a/src/v/cloud_storage/inventory/types.cc b/src/v/cloud_storage/inventory/types.cc index 497c4fda4bf36..dbc895608dbc4 100644 --- a/src/v/cloud_storage/inventory/types.cc +++ b/src/v/cloud_storage/inventory/types.cc @@ -19,29 +19,6 @@ constexpr auto supported_backends = {model::cloud_storage_backend::aws}; } namespace cloud_storage::inventory { -std::ostream& operator<<(std::ostream& os, report_generation_frequency rgf) { - switch (rgf) { - case report_generation_frequency::daily: - return os << "Daily"; - } -} - -std::ostream& operator<<(std::ostream& os, report_format rf) { - switch (rf) { - case report_format::csv: - return os << "CSV"; - } -} - -std::ostream& operator<<(std::ostream& os, inventory_creation_result icr) { - switch (icr) { - using enum inventory_creation_result; - case success: - return os << "success"; - case already_exists: - return os << "already-exists"; - } -} bool validate_backend_supported_for_inventory_scrub( model::cloud_storage_backend backend) { diff --git a/src/v/cloud_storage/inventory/types.h b/src/v/cloud_storage/inventory/types.h index d2ddaabc0a57a..8d9a744ee7c95 100644 --- a/src/v/cloud_storage/inventory/types.h +++ b/src/v/cloud_storage/inventory/types.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "cloud_storage_clients/types.h" #include "model/fundamental.h" @@ -76,20 +77,28 @@ inline std::error_code make_error_code(error_outcome e) noexcept { return {static_cast(e), inventory_error_category()}; } -inline std::ostream& operator<<(std::ostream& o, error_outcome e) { - o << inventory_error_category().message(static_cast(e)); - return o; -} - // The identifier for a specific report configuration scheduled to run at a // fixed frequency and producing files of a fixed format. using inventory_config_id = named_type; enum class report_generation_frequency { daily }; -std::ostream& operator<<(std::ostream&, report_generation_frequency); + +inline fmt::iterator +format_to(report_generation_frequency rgf, fmt::iterator out) { + switch (rgf) { + case report_generation_frequency::daily: + return fmt::format_to(out, "Daily"); + } +} enum class report_format { csv }; -std::ostream& operator<<(std::ostream&, report_format); + +inline fmt::iterator format_to(report_format rf, fmt::iterator out) { + switch (rf) { + case report_format::csv: + return fmt::format_to(out, "CSV"); + } +} // A string is used instead of a chrono type because the strings returned by the // vendor APIs are already roughly ISO-8601 formatted. This format is well @@ -115,7 +124,16 @@ enum class inventory_creation_result { already_exists, }; -std::ostream& operator<<(std::ostream&, inventory_creation_result); +inline fmt::iterator +format_to(inventory_creation_result icr, fmt::iterator out) { + switch (icr) { + using enum inventory_creation_result; + case success: + return fmt::format_to(out, "success"); + case already_exists: + return fmt::format_to(out, "already-exists"); + } +} template using op_result = result; @@ -158,3 +176,10 @@ template<> struct is_error_code_enum : true_type {}; } // namespace std + +namespace cloud_storage::inventory { +inline fmt::iterator format_to(error_outcome e, fmt::iterator out) { + return fmt::format_to( + out, "{}", inventory_error_category().message(static_cast(e))); +} +} // namespace cloud_storage::inventory diff --git a/src/v/cloud_storage/partition_manifest.cc b/src/v/cloud_storage/partition_manifest.cc index 06a934318f1d9..88540fa3834da 100644 --- a/src/v/cloud_storage/partition_manifest.cc +++ b/src/v/cloud_storage/partition_manifest.cc @@ -44,7 +44,6 @@ #include #include -#include #include #include @@ -59,35 +58,11 @@ #include #include -namespace fmt { -template<> -struct fmt::formatter { - using segment_meta = cloud_storage::partition_manifest::segment_meta; - - template - constexpr auto parse(ParseContext& ctx) { - return ctx.begin(); - } - - template - auto format(const segment_meta& m, FormatContext& ctx) { - return fmt::format_to( - ctx.out(), - "{{o={}-{} t={}-{}}}", - m.base_offset, - m.committed_offset, - m.base_timestamp, - m.max_timestamp); - } -}; -} // namespace fmt - namespace cloud_storage { -std::ostream& -operator<<(std::ostream& s, const partition_manifest_path_components& c) { - fmt::print( - s, "{{{}: {}-{}-{}-{}}}", c._origin, c._ns, c._topic, c._part, c._rev); - return s; + +fmt::iterator segment_name_components::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{base_offset: {}, term: {}}}", base_offset, term); } std::optional @@ -2874,11 +2849,23 @@ std::optional partition_manifest::do_repair_state() const { return std::nullopt; } -std::ostream& operator<<(std::ostream& o, const partition_manifest& pm) { - o << "{manifest: "; - pm.serialize_json(o, false); - o << "; last segment: " << pm.last_segment() << "}"; - return o; +namespace { +struct manifest_json_view { + const partition_manifest& m; + friend std::ostream& + operator<<(std::ostream& os, const manifest_json_view& v) { + v.m.serialize_json(os, false); + return os; + } +}; +} // namespace + +fmt::iterator partition_manifest::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{manifest: {}; last segment: {}}}", + fmt_streamed(manifest_json_view{*this}), + last_segment()); } } // namespace cloud_storage diff --git a/src/v/cloud_storage/partition_manifest.h b/src/v/cloud_storage/partition_manifest.h index 9a11712a58b41..07210a784f934 100644 --- a/src/v/cloud_storage/partition_manifest.h +++ b/src/v/cloud_storage/partition_manifest.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_storage/base_manifest.h" #include "cloud_storage/fwd.h" #include "cloud_storage/segment_meta_cstore.h" @@ -39,8 +40,10 @@ struct partition_manifest_path_components { model::partition_id _part; model::initial_revision_id _rev; - friend std::ostream& - operator<<(std::ostream& s, const partition_manifest_path_components& c); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{{}: {}-{}-{}-{}}}", _origin, _ns, _topic, _part, _rev); + } }; struct segment_name_components { @@ -49,8 +52,7 @@ struct segment_name_components { auto operator<=>(const segment_name_components&) const = default; - friend std::ostream& - operator<<(std::ostream& o, const segment_name_components& k); + fmt::iterator format_to(fmt::iterator it) const; }; std::optional @@ -599,6 +601,8 @@ class partition_manifest : public base_manifest { /// defects. Returns `std::nullopt` otherwise. std::optional repair_state() const; + fmt::iterator format_to(fmt::iterator it) const; + private: ss::sstring display_name() const; std::optional compute_start_kafka_offset_local() const; @@ -711,6 +715,8 @@ class partition_manifest : public base_manifest { model::offset _applied_offset; }; -std::ostream& operator<<(std::ostream& o, const partition_manifest& f); - } // namespace cloud_storage + +template<> +struct fmt::range_format_kind + : std::integral_constant {}; diff --git a/src/v/cloud_storage/recovery_request.cc b/src/v/cloud_storage/recovery_request.cc index 9d0a3956fb049..c358827a92822 100644 --- a/src/v/cloud_storage/recovery_request.cc +++ b/src/v/cloud_storage/recovery_request.cc @@ -134,17 +134,4 @@ void recovery_request::parse_request_body(const ss::sstring& body) { } } -std::ostream& operator<<(std::ostream& os, const recovery_request& r) { - fmt::print( - os, - "{{topic_names_pattern: {}, retention_bytes: {}, retention_ms: {}}}", - r.topic_names_pattern().value_or("none"), - r.retention_bytes().has_value() - ? std::to_string(r.retention_bytes().value()) - : "none", - r.retention_ms().has_value() ? std::to_string(r.retention_ms()->count()) - : "none"); - return os; -} - } // namespace cloud_storage diff --git a/src/v/cloud_storage/recovery_request.h b/src/v/cloud_storage/recovery_request.h index 2c650d8d7f204..6c278bca78023 100644 --- a/src/v/cloud_storage/recovery_request.h +++ b/src/v/cloud_storage/recovery_request.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -36,6 +37,19 @@ struct recovery_request { std::optional retention_ms() const; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{topic_names_pattern: {}, retention_bytes: {}, retention_ms: " + "{}}}", + topic_names_pattern().value_or("none"), + retention_bytes().has_value() + ? std::to_string(retention_bytes().value()) + : "none", + retention_ms().has_value() ? std::to_string(retention_ms()->count()) + : "none"); + } + private: recovery_request() = default; @@ -46,6 +60,4 @@ struct recovery_request { std::optional _retention_ms; }; -std::ostream& operator<<(std::ostream&, const recovery_request&); - } // namespace cloud_storage diff --git a/src/v/cloud_storage/remote.cc b/src/v/cloud_storage/remote.cc index b6f2628aca41d..c9cdde6d8b8e6 100644 --- a/src/v/cloud_storage/remote.cc +++ b/src/v/cloud_storage/remote.cc @@ -510,7 +510,7 @@ ss::future remote::object_exists( _as.check(); auto holder = _gate.hold(); co_return co_await io().object_exists( - bucket, path, parent, fmt::to_string(object_type)); + bucket, path, parent, fmt::format("{}", object_type)); } ss::future remote::segment_exists( @@ -523,7 +523,7 @@ ss::future remote::segment_exists( bucket, cloud_storage_clients::object_key{segment_path}, parent, - fmt::to_string(existence_check_type::segment)); + fmt::format("{}", existence_check_type::segment)); } ss::future remote::delete_object( diff --git a/src/v/cloud_storage/remote_label.h b/src/v/cloud_storage/remote_label.h index 6478717f84bdf..381caf4489a8d 100644 --- a/src/v/cloud_storage/remote_label.h +++ b/src/v/cloud_storage/remote_label.h @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "model/fundamental.h" #include "serde/rw/envelope.h" #include "serde/rw/uuid.h" @@ -29,10 +30,8 @@ struct remote_label // collisions when multiple clusters use the same bucket. model::cluster_uuid cluster_uuid{}; - friend std::ostream& - operator<<(std::ostream& os, const remote_label& label) { - fmt::print(os, "{{cluster_uuid: {}}}", label.cluster_uuid); - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{cluster_uuid: {}}}", cluster_uuid); } }; diff --git a/src/v/cloud_storage/remote_partition.cc b/src/v/cloud_storage/remote_partition.cc index 80be143d0b7cd..2c5b8df17d0c4 100644 --- a/src/v/cloud_storage/remote_partition.cc +++ b/src/v/cloud_storage/remote_partition.cc @@ -530,8 +530,9 @@ class partition_record_batch_reader_impl final co_return storage_t{}; } - void print(std::ostream& o) override { - o << "cloud_storage_partition_record_batch_reader"; + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to( + it, "cloud_storage_partition_record_batch_reader"); } private: diff --git a/src/v/cloud_storage/remote_segment.cc b/src/v/cloud_storage/remote_segment.cc index d4b9dabfb7522..de6459e53874c 100644 --- a/src/v/cloud_storage/remote_segment.cc +++ b/src/v/cloud_storage/remote_segment.cc @@ -1452,8 +1452,8 @@ class remote_segment_batch_consumer : public storage::batch_consumer { co_return stop_parser::no; } - void print(std::ostream& o) const override { - o << "remote_segment_batch_consumer"; + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to(it, "remote_segment_batch_consumer"); } private: @@ -1627,17 +1627,6 @@ remote_segment_batch_reader::~remote_segment_batch_reader() noexcept { _ts_probe.segment_reader_destroyed(); } -std::ostream& operator<<(std::ostream& os, hydration_request::kind kind) { - switch (kind) { - case hydration_request::kind::segment: - return os << "segment"; - case hydration_request::kind::tx: - return os << "tx-range"; - case hydration_request::kind::index: - return os << "index"; - } -} - hydration_loop_state::hydration_loop_state( cloud_io::cache& c, remote_segment_path root, retry_chain_logger& ctxlog) : _cache{c} diff --git a/src/v/cloud_storage/remote_segment.h b/src/v/cloud_storage/remote_segment.h index 0d8c33c09d690..b4402b55437b6 100644 --- a/src/v/cloud_storage/remote_segment.h +++ b/src/v/cloud_storage/remote_segment.h @@ -495,7 +495,16 @@ struct hydration_request { kind path_kind; }; -std::ostream& operator<<(std::ostream&, hydration_request::kind); +inline fmt::iterator format_to(hydration_request::kind k, fmt::iterator out) { + switch (k) { + case hydration_request::kind::segment: + return fmt::format_to(out, "segment"); + case hydration_request::kind::tx: + return fmt::format_to(out, "tx-range"); + case hydration_request::kind::index: + return fmt::format_to(out, "index"); + } +} struct hydration_loop_state { using hydrate_action_t = hydration_request::hydrate_action_t; diff --git a/src/v/cloud_storage/remote_segment_index.cc b/src/v/cloud_storage/remote_segment_index.cc index feb120bdd2def..e7f189405f6cf 100644 --- a/src/v/cloud_storage/remote_segment_index.cc +++ b/src/v/cloud_storage/remote_segment_index.cc @@ -500,8 +500,8 @@ remote_segment_index_builder::consume_batch_end() { co_return stop_parser::no; } -void remote_segment_index_builder::print(std::ostream& o) const { - o << "remote_segment_index_builder"; +fmt::iterator remote_segment_index_builder::format_to(fmt::iterator it) const { + return fmt::format_to(it, "remote_segment_index_builder"); } } // namespace cloud_storage diff --git a/src/v/cloud_storage/remote_segment_index.h b/src/v/cloud_storage/remote_segment_index.h index ae897fb89a973..7fd2cdab40274 100644 --- a/src/v/cloud_storage/remote_segment_index.h +++ b/src/v/cloud_storage/remote_segment_index.h @@ -212,22 +212,22 @@ class remote_segment_index_builder : public storage::batch_consumer { size_t sampling_step, std::optional> maybe_stats); - virtual consume_result - accept_batch_start(const model::record_batch_header&) const; + consume_result + accept_batch_start(const model::record_batch_header&) const override; - virtual void consume_batch_start( + void consume_batch_start( model::record_batch_header, size_t physical_base_offset, - size_t size_on_disk); + size_t size_on_disk) override; - virtual void skip_batch_start( + void skip_batch_start( model::record_batch_header, size_t physical_base_offset, - size_t size_on_disk); + size_t size_on_disk) override; - virtual void consume_records(iobuf&&); - virtual ss::future consume_batch_end(); - virtual void print(std::ostream&) const; + void consume_records(iobuf&&) override; + ss::future consume_batch_end() override; + fmt::iterator format_to(fmt::iterator it) const override; private: offset_index& _ix; diff --git a/src/v/cloud_storage/segment_chunk.cc b/src/v/cloud_storage/segment_chunk.cc index 6594a9db19edb..fb0f951cdf8c3 100644 --- a/src/v/cloud_storage/segment_chunk.cc +++ b/src/v/cloud_storage/segment_chunk.cc @@ -12,17 +12,6 @@ namespace cloud_storage { -std::ostream& operator<<(std::ostream& os, chunk_state c) { - switch (c) { - case chunk_state::not_available: - return os << "not available"; - case chunk_state::download_in_progress: - return os << "download in progress"; - case chunk_state::hydrated: - return os << "hydrated"; - } -} - std::strong_ordering segment_chunk::operator<=>(const segment_chunk& chunk) const { const auto cmp = required_by_readers_in_future diff --git a/src/v/cloud_storage/segment_chunk.h b/src/v/cloud_storage/segment_chunk.h index b4ad480b3bbbf..fa9575dd8d65e 100644 --- a/src/v/cloud_storage/segment_chunk.h +++ b/src/v/cloud_storage/segment_chunk.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -30,7 +31,16 @@ enum class chunk_state { hydrated, }; -std::ostream& operator<<(std::ostream& os, chunk_state); +inline fmt::iterator format_to(chunk_state c, fmt::iterator out) { + switch (c) { + case chunk_state::not_available: + return fmt::format_to(out, "not available"); + case chunk_state::download_in_progress: + return fmt::format_to(out, "download in progress"); + case chunk_state::hydrated: + return fmt::format_to(out, "hydrated"); + } +} struct segment_chunk { chunk_state current_state; diff --git a/src/v/cloud_storage/tests/BUILD b/src/v/cloud_storage/tests/BUILD index 0b68e52a12c13..f58b5cafd080d 100644 --- a/src/v/cloud_storage/tests/BUILD +++ b/src/v/cloud_storage/tests/BUILD @@ -59,6 +59,7 @@ redpanda_test_cc_library( "//src/v/utils:lazy_abort_source", "//src/v/utils:stream_provider", "@boost//:numeric_conversion", + "@fmt", "@rapidjson", "@seastar", ], diff --git a/src/v/cloud_storage/tests/common_def.h b/src/v/cloud_storage/tests/common_def.h index af414aac5d878..49357a5d0a163 100644 --- a/src/v/cloud_storage/tests/common_def.h +++ b/src/v/cloud_storage/tests/common_def.h @@ -155,8 +155,8 @@ class recording_batch_consumer : public storage::batch_consumer { co_return stop_parser::no; } - void print(std::ostream& o) const override { - o << "counting_record_consumer"; + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to(it, "counting_record_consumer"); } std::vector& headers; diff --git a/src/v/cloud_storage/tests/partition_manifest_test.cc b/src/v/cloud_storage/tests/partition_manifest_test.cc index 8666f57f203e2..d03ab565d26bc 100644 --- a/src/v/cloud_storage/tests/partition_manifest_test.cc +++ b/src/v/cloud_storage/tests/partition_manifest_test.cc @@ -2540,7 +2540,9 @@ SEASTAR_THREAD_TEST_CASE(test_estimate_size_empty) { SEASTAR_THREAD_TEST_CASE(test_partition_manifest_outofbound_trigger) { BOOST_TEST_INFO( - fmt::format("random_seed: [{}]", random_generators::global().engine())); + fmt::format( + "random_seed: [{}]", + fmt_streamed(random_generators::global().engine()))); auto m = partition_manifest{manifest_ntp, model::initial_revision_id(0)}; BOOST_REQUIRE(m.get_start_offset() == std::nullopt); auto max_committed_offset = random_generators::get_int(0, 100000); diff --git a/src/v/cloud_storage/tests/segment_meta_cstore_fuzz.cc b/src/v/cloud_storage/tests/segment_meta_cstore_fuzz.cc index 2359b612cb218..7b157df3e2a6c 100644 --- a/src/v/cloud_storage/tests/segment_meta_cstore_fuzz.cc +++ b/src/v/cloud_storage/tests/segment_meta_cstore_fuzz.cc @@ -422,39 +422,6 @@ using cstore_operation = std::variant< serialize_op, deserialize_op>; -// SIN SECTION -// this is a sections of ODR sins to appease the daemons of the linker - -auto cloud_storage::operator<<(std::ostream& os, const segment_meta& s) - -> std::ostream& { - return os << fmt::format( - "{{is_compacted: {}, size_bytes: {}, base_offset: {}, " - "committed_offset: " - "{}, base_timestamp: {}, max_timestamp: {}, delta_offset: {}, " - "ntp_revision: {}, archiver_term: {}, segment_term: {}, " - "delta_offset_end: {}, sname_format: {}, metadata_size_hint: {}}}", - s.is_compacted, - s.size_bytes, - s.base_offset, - s.committed_offset, - s.base_timestamp, - s.max_timestamp, - s.delta_offset, - s.ntp_revision, - s.archiver_term, - s.segment_term, - s.delta_offset_end, - s.sname_format, - s.metadata_size_hint); -} - -auto cloud_storage::operator<<(std::ostream& os, const segment_name_format& sn) - -> std::ostream& { - return os << fmt::format("{}", unsigned(sn)); -} - -// END OF SIN SECTION - template<> struct fmt::formatter : public fmt::formatter { diff --git a/src/v/cloud_storage/tests/segment_meta_cstore_test.cc b/src/v/cloud_storage/tests/segment_meta_cstore_test.cc index 46cc7402bbfdb..d7f795efa4210 100644 --- a/src/v/cloud_storage/tests/segment_meta_cstore_test.cc +++ b/src/v/cloud_storage/tests/segment_meta_cstore_test.cc @@ -279,7 +279,7 @@ void test_cstore_prefix_truncate(size_t test_size, size_t max_truncate_ix) { BOOST_TEST_INFO( fmt::format( "random_generators::global().engine(): [{}]", - random_generators::global().engine())); + fmt_streamed(random_generators::global().engine()))); segment_meta_cstore store; auto manifest = generate_metadata(test_size); @@ -466,7 +466,7 @@ BOOST_AUTO_TEST_CASE(test_segment_meta_cstore_insert_replacements) { BOOST_TEST_INFO( fmt::format( "random_generators::global().engine(): [{}]", - random_generators::global().engine())); + fmt_streamed(random_generators::global().engine()))); segment_meta_cstore store{}; auto manifest = generate_metadata(9973); diff --git a/src/v/cloud_storage/tests/topic_manifest_test.cc b/src/v/cloud_storage/tests/topic_manifest_test.cc index 7297dfcfe2750..d365af817e9c3 100644 --- a/src/v/cloud_storage/tests/topic_manifest_test.cc +++ b/src/v/cloud_storage/tests/topic_manifest_test.cc @@ -359,7 +359,7 @@ SEASTAR_THREAD_TEST_CASE(wrong_version_throws) { std::runtime_error, [](std::runtime_error ex) { return std::string(ex.what()).find( - "topic manifest version {99} is not supported") + "topic manifest version 99 is not supported") != std::string::npos; }); } diff --git a/src/v/cloud_storage/tests/util.cc b/src/v/cloud_storage/tests/util.cc index 7859c42bb80e8..dacf1085199c0 100644 --- a/src/v/cloud_storage/tests/util.cc +++ b/src/v/cloud_storage/tests/util.cc @@ -84,36 +84,36 @@ segment_layout generate_segment_layout( return {.segments = all_batches, .num_data_batches = num_data_batches}; } -std::ostream& operator<<(std::ostream& o, const in_memory_segment& ims) { - fmt::print( - o, +fmt::iterator in_memory_segment::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "name {}, base-offset {}, max-offset {}, do-not-reupload {}, " "num-config-batches {}, num-config-records {}, delta-offset-overlap {}\n", - ims.sname, - ims.base_offset, - ims.max_offset, - ims.do_not_reupload, - ims.num_config_batches, - ims.num_config_records, - ims.delta_offset_overlap); - for (size_t i = 0; i < ims.headers.size(); i++) { - if (is_internal_record_batch(ims.headers[i].type)) { - const auto& h = ims.headers[i]; - fmt::print( - o, + sname, + base_offset, + max_offset, + do_not_reupload, + num_config_batches, + num_config_records, + delta_offset_overlap); + for (size_t i = 0; i < headers.size(); i++) { + if (is_internal_record_batch(headers[i].type)) { + const auto& h = headers[i]; + it = fmt::format_to( + it, "\tconfiguration-batch {{ base_offset:{}, record_count:{} }}\n", h.base_offset, h.record_count); } else { - const auto& h = ims.headers[i]; - fmt::print( - o, + const auto& h = headers[i]; + it = fmt::format_to( + it, "\tdata-batch {{ base_offset:{}, record_count:{} }}\n", h.base_offset, h.record_count); } } - return o; + return it; } std::unique_ptr make_recording_batch_parser( diff --git a/src/v/cloud_storage/tests/util.h b/src/v/cloud_storage/tests/util.h index e26aa2cece57d..aa09c61a2634c 100644 --- a/src/v/cloud_storage/tests/util.h +++ b/src/v/cloud_storage/tests/util.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "bytes/iostream.h" #include "bytes/streambuf.h" #include "cloud_storage/async_manifest_view.h" @@ -84,9 +85,9 @@ struct in_memory_segment { int delta_offset_overlap{0}; std::optional base_timestamp; std::optional last_timestamp; -}; -std::ostream& operator<<(std::ostream& o, const in_memory_segment& ims); + fmt::iterator format_to(fmt::iterator it) const; +}; std::unique_ptr make_recording_batch_parser( iobuf buf, diff --git a/src/v/cloud_storage/topic_manifest.cc b/src/v/cloud_storage/topic_manifest.cc index f882201ecd677..169602f3bbbd4 100644 --- a/src/v/cloud_storage/topic_manifest.cc +++ b/src/v/cloud_storage/topic_manifest.cc @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -243,7 +242,7 @@ void topic_manifest::do_update(const topic_manifest_handler& handler) { throw std::runtime_error(fmt_with_ctx( fmt::format, "topic manifest version {} is not supported", - handler._version)); + handler._version.value_or(-1))); } _rev = handler._revision_id.value(); diff --git a/src/v/cloud_storage/topic_manifest_downloader.cc b/src/v/cloud_storage/topic_manifest_downloader.cc index 517522d2c0a3d..89794e1fa01da 100644 --- a/src/v/cloud_storage/topic_manifest_downloader.cc +++ b/src/v/cloud_storage/topic_manifest_downloader.cc @@ -33,21 +33,6 @@ bool bin_manifest_filter( }; } // namespace -std::ostream& operator<<(std::ostream& os, find_topic_manifest_outcome o) { - switch (o) { - case find_topic_manifest_outcome::success: - os << "find_topic_manifest_outcome::success"; - break; - case find_topic_manifest_outcome::no_matching_manifest: - os << "find_topic_manifest_outcome::no_matching_manifest"; - break; - case find_topic_manifest_outcome::multiple_matching_manifests: - os << "find_topic_manifest_outcome::multiple_matching_manifests"; - break; - } - return os; -} - topic_manifest_downloader::topic_manifest_downloader( const cloud_storage_clients::bucket_name bucket, std::optional hint, diff --git a/src/v/cloud_storage/topic_manifest_downloader.h b/src/v/cloud_storage/topic_manifest_downloader.h index d3284838a12ae..60e342f3ec55c 100644 --- a/src/v/cloud_storage/topic_manifest_downloader.h +++ b/src/v/cloud_storage/topic_manifest_downloader.h @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "cloud_storage/remote.h" #include "cloud_storage/remote_label.h" @@ -27,7 +28,20 @@ enum class find_topic_manifest_outcome { // and a hint must be provided to determine winner. multiple_matching_manifests, }; -std::ostream& operator<<(std::ostream&, find_topic_manifest_outcome); + +inline fmt::iterator +format_to(find_topic_manifest_outcome o, fmt::iterator out) { + switch (o) { + case find_topic_manifest_outcome::success: + return fmt::format_to(out, "find_topic_manifest_outcome::success"); + case find_topic_manifest_outcome::no_matching_manifest: + return fmt::format_to( + out, "find_topic_manifest_outcome::no_matching_manifest"); + case find_topic_manifest_outcome::multiple_matching_manifests: + return fmt::format_to( + out, "find_topic_manifest_outcome::multiple_matching_manifests"); + } +} // Encapsulates downloading manifests for a given topic. // diff --git a/src/v/cloud_storage/topic_mount_handler.cc b/src/v/cloud_storage/topic_mount_handler.cc index 9f9cb32fa1b68..88876a78b74d7 100644 --- a/src/v/cloud_storage/topic_mount_handler.cc +++ b/src/v/cloud_storage/topic_mount_handler.cc @@ -22,30 +22,6 @@ namespace cloud_storage { -std::ostream& operator<<(std::ostream& o, const topic_mount_result& r) { - switch (r) { - case topic_mount_result::mount_manifest_does_not_exist: - return o << "{mount_manifest_does_not_exist}"; - case topic_mount_result::mount_manifest_not_deleted: - return o << "{mount_manifest_not_deleted}"; - case topic_mount_result::mount_manifest_exists: - return o << "{topic_manifest_exists}"; - case topic_mount_result::success: - return o << "{success}"; - } - return o; -} - -std::ostream& operator<<(std::ostream& o, const topic_unmount_result& r) { - switch (r) { - case topic_unmount_result::mount_manifest_not_created: - return o << "{mount_manifest_not_created}"; - case topic_unmount_result::success: - return o << "{success}"; - } - return o; -} - topic_mount_handler::topic_mount_handler( const cloud_storage_clients::bucket_name& bucket, remote& remote) : _bucket(bucket) diff --git a/src/v/cloud_storage/topic_mount_handler.h b/src/v/cloud_storage/topic_mount_handler.h index 1152aebf2f370..85bc6ab7b607c 100644 --- a/src/v/cloud_storage/topic_mount_handler.h +++ b/src/v/cloud_storage/topic_mount_handler.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "cloud_storage/fwd.h" #include "cloud_storage/topic_mount_manifest_path.h" @@ -28,11 +29,29 @@ enum class topic_mount_result { success }; -std::ostream& operator<<(std::ostream& o, const topic_mount_result& r); +inline fmt::iterator format_to(topic_mount_result r, fmt::iterator out) { + switch (r) { + case topic_mount_result::mount_manifest_does_not_exist: + return fmt::format_to(out, "{{mount_manifest_does_not_exist}}"); + case topic_mount_result::mount_manifest_not_deleted: + return fmt::format_to(out, "{{mount_manifest_not_deleted}}"); + case topic_mount_result::mount_manifest_exists: + return fmt::format_to(out, "{{topic_manifest_exists}}"); + case topic_mount_result::success: + return fmt::format_to(out, "{{success}}"); + } +} enum class topic_unmount_result { mount_manifest_not_created, success }; -std::ostream& operator<<(std::ostream& o, const topic_unmount_result& r); +inline fmt::iterator format_to(topic_unmount_result r, fmt::iterator out) { + switch (r) { + case topic_unmount_result::mount_manifest_not_created: + return fmt::format_to(out, "{{mount_manifest_not_created}}"); + case topic_unmount_result::success: + return fmt::format_to(out, "{{success}}"); + } +} class topic_mount_handler { public: diff --git a/src/v/cloud_storage/types.cc b/src/v/cloud_storage/types.cc index c3a92b3e08022..e0e53b12e2ea7 100644 --- a/src/v/cloud_storage/types.cc +++ b/src/v/cloud_storage/types.cc @@ -15,108 +15,6 @@ namespace cloud_storage { -std::ostream& operator<<(std::ostream& o, const segment_meta& s) { - fmt::print( - o, - "{{is_compacted: {}, size_bytes: {}, base_offset: {}, committed_offset: " - "{}, base_timestamp: {}, max_timestamp: {}, delta_offset: {}, " - "ntp_revision: {}, archiver_term: {}, segment_term: {}, " - "delta_offset_end: {}, sname_format: {}, metadata_size_hint: {}}}", - s.is_compacted, - s.size_bytes, - s.base_offset, - s.committed_offset, - s.base_timestamp, - s.max_timestamp, - s.delta_offset, - s.ntp_revision, - s.archiver_term, - s.segment_term, - s.delta_offset_end, - s.sname_format, - s.metadata_size_hint); - return o; -} - -std::ostream& operator<<(std::ostream& o, const segment_name_format& r) { - switch (r) { - case segment_name_format::v1: - o << "{v1}"; - break; - case segment_name_format::v2: - o << "{v2}"; - break; - case segment_name_format::v3: - o << "{v3}"; - break; - } - return o; -} - -std::ostream& -operator<<(std::ostream& o, const spillover_manifest_path_components& c) { - fmt::print( - o, - "{{base: {}, last: {}, base_kafka: {}, next_kafka: {}, base_ts: {}, " - "last_ts: {}}}", - c.base, - c.last, - c.base_kafka, - c.next_kafka, - c.base_ts, - c.last_ts); - return o; -} - -std::ostream& operator<<(std::ostream& o, const scrub_status& s) { - switch (s) { - case scrub_status::full: - o << "{full}"; - break; - case scrub_status::partial: - o << "{partial}"; - break; - case scrub_status::failed: - o << "{failed}"; - break; - } - return o; -} - -std::ostream& operator<<(std::ostream& o, const anomaly_type& t) { - switch (t) { - case anomaly_type::missing_delta: - o << "{missing_delta}"; - break; - case anomaly_type::non_monotonical_delta: - o << "{non_monotonical_delta}"; - break; - case anomaly_type::end_delta_smaller: - o << "{end_delta_smaller}"; - break; - case anomaly_type::committed_smaller: - o << "{committed_smaller}"; - break; - case anomaly_type::offset_gap: - o << "{offset_gap}"; - break; - case anomaly_type::offset_overlap: - o << "{offset_overlap}"; - break; - } - return o; -} - -std::ostream& operator<<(std::ostream& o, const anomaly_meta& meta) { - fmt::print( - o, - "{{type: {}, at: {}, previous: {}}}", - meta.type, - meta.at, - meta.previous); - return o; -} - void scrub_segment_meta( const segment_meta& current, const std::optional& previous, @@ -263,42 +161,20 @@ anomalies& anomalies::operator+=(anomalies&& other) { return *this; } -std::ostream& operator<<(std::ostream& o, const anomalies& a) { - if (!a.has_value()) { - return o << "{}"; +fmt::iterator anomalies::format_to(fmt::iterator it) const { + if (!has_value()) { + return fmt::format_to(it, "{{}}"); } - fmt::print( - o, + return fmt::format_to( + it, "{{missing_partition_manifest: {}, missing_spillover_manifests: {}, " "missing_segments: {}, segment_metadata_anomalies: {}}}", - a.missing_partition_manifest, - a.missing_spillover_manifests.size() - + a.num_discarded_missing_spillover_manifests, - a.missing_segments.size() + a.num_discarded_missing_segments, - a.segment_metadata_anomalies.size() + a.num_discarded_metadata_anomalies); - - return o; -} - -std::ostream& operator<<(std::ostream& os, upload_type upload) { - return os << to_string(upload); -} - -std::ostream& operator<<(std::ostream& os, download_type download) { - return os << to_string(download); -} - -std::ostream& operator<<(std::ostream& os, existence_check_type head) { - switch (head) { - using enum cloud_storage::existence_check_type; - case object: - return os << "object"; - case segment: - return os << "segment"; - case manifest: - return os << "manifest"; - } + missing_partition_manifest, + missing_spillover_manifests.size() + + num_discarded_missing_spillover_manifests, + missing_segments.size() + num_discarded_missing_segments, + segment_metadata_anomalies.size() + num_discarded_metadata_anomalies); } fmt::iterator cloud_log_reader_config::format_to(fmt::iterator it) const { diff --git a/src/v/cloud_storage/types.h b/src/v/cloud_storage/types.h index 248b3b11ad19c..f6b61f5c70f05 100644 --- a/src/v/cloud_storage/types.h +++ b/src/v/cloud_storage/types.h @@ -64,7 +64,17 @@ enum class segment_name_format : int16_t { v3 = 3 }; -std::ostream& operator<<(std::ostream& o, const segment_name_format& r); +inline fmt::iterator format_to(segment_name_format r, fmt::iterator out) { + switch (r) { + case segment_name_format::v1: + return fmt::format_to(out, "{{v1}}"); + case segment_name_format::v2: + return fmt::format_to(out, "{{v2}}"); + case segment_name_format::v3: + return fmt::format_to(out, "{{v3}}"); + } +} + enum class manifest_version : int32_t { v1 = 1, v2 = 2, @@ -166,8 +176,30 @@ struct segment_meta meta.delta_offset(), meta.delta_offset_end()); } + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{is_compacted: {}, size_bytes: {}, base_offset: {}, " + "committed_offset: " + "{}, base_timestamp: {}, max_timestamp: {}, delta_offset: {}, " + "ntp_revision: {}, archiver_term: {}, segment_term: {}, " + "delta_offset_end: {}, sname_format: {}, metadata_size_hint: {}}}", + is_compacted, + size_bytes, + base_offset, + committed_offset, + base_timestamp, + max_timestamp, + delta_offset, + ntp_revision, + archiver_term, + segment_term, + delta_offset_end, + sname_format, + metadata_size_hint); + } }; -std::ostream& operator<<(std::ostream& o, const segment_meta& r); enum class error_outcome { // Represent general failure that can't be handled and doesn't fit into @@ -286,14 +318,34 @@ struct spillover_manifest_path_components c.base_ts(), c.last_ts()); } -}; -std::ostream& -operator<<(std::ostream& o, const spillover_manifest_path_components& c); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{base: {}, last: {}, base_kafka: {}, next_kafka: {}, base_ts: " + "{}, " + "last_ts: {}}}", + base, + last, + base_kafka, + next_kafka, + base_ts, + last_ts); + } +}; enum class scrub_status : uint8_t { full, partial, failed }; -std::ostream& operator<<(std::ostream& o, const scrub_status&); +inline fmt::iterator format_to(scrub_status s, fmt::iterator out) { + switch (s) { + case scrub_status::full: + return fmt::format_to(out, "{{full}}"); + case scrub_status::partial: + return fmt::format_to(out, "{{partial}}"); + case scrub_status::failed: + return fmt::format_to(out, "{{failed}}"); + } +} enum class anomaly_type : int8_t { missing_delta, @@ -304,7 +356,22 @@ enum class anomaly_type : int8_t { offset_overlap }; -std::ostream& operator<<(std::ostream& o, const anomaly_type&); +inline fmt::iterator format_to(anomaly_type t, fmt::iterator out) { + switch (t) { + case anomaly_type::missing_delta: + return fmt::format_to(out, "{{missing_delta}}"); + case anomaly_type::non_monotonical_delta: + return fmt::format_to(out, "{{non_monotonical_delta}}"); + case anomaly_type::end_delta_smaller: + return fmt::format_to(out, "{{end_delta_smaller}}"); + case anomaly_type::committed_smaller: + return fmt::format_to(out, "{{committed_smaller}}"); + case anomaly_type::offset_gap: + return fmt::format_to(out, "{{offset_gap}}"); + case anomaly_type::offset_overlap: + return fmt::format_to(out, "{{offset_overlap}}"); + } +} struct anomaly_meta : serde::envelope, serde::compat_version<0>> { @@ -320,9 +387,12 @@ struct anomaly_meta friend H AbslHashValue(H h, const anomaly_meta& am) { return H::combine(std::move(h), am.type, am.at); } -}; -std::ostream& operator<<(std::ostream& o, const anomaly_meta&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{type: {}, at: {}, previous: {}}}", type, at, previous); + } +}; using segment_meta_anomalies = absl::node_hash_set; @@ -376,9 +446,9 @@ struct anomalies anomalies& operator+=(anomalies&&); friend bool operator==(const anomalies& lhs, const anomalies& rhs); -}; -std::ostream& operator<<(std::ostream& o, const anomalies& a); + fmt::iterator format_to(fmt::iterator it) const; +}; enum class upload_type { object, @@ -409,7 +479,10 @@ constexpr std::string_view to_string(upload_type t) { return "inventory-configuration"; } } -std::ostream& operator<<(std::ostream&, upload_type); + +inline fmt::iterator format_to(upload_type t, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string(t)); +} enum class download_type { object, segment_index, inventory_report_manifest }; @@ -425,11 +498,23 @@ constexpr std::string_view to_string(download_type t) { } } -std::ostream& operator<<(std::ostream&, download_type); +inline fmt::iterator format_to(download_type t, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string(t)); +} enum class existence_check_type { object, segment, manifest }; -std::ostream& operator<<(std::ostream&, existence_check_type); +inline fmt::iterator format_to(existence_check_type t, fmt::iterator out) { + switch (t) { + using enum existence_check_type; + case object: + return fmt::format_to(out, "object"); + case segment: + return fmt::format_to(out, "segment"); + case manifest: + return fmt::format_to(out, "manifest"); + } +} class remote_probe; struct upload_request { @@ -525,3 +610,16 @@ namespace std { template<> struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter + : fmt::formatter { + auto + format(cloud_storage::error_outcome e, fmt::format_context& ctx) const { + return fmt::format_to( + ctx.out(), + "{}:{}", + cloud_storage::error_outcome_category{}.name(), + static_cast(e)); + } +}; diff --git a/src/v/cloud_storage_clients/abs_client.cc b/src/v/cloud_storage_clients/abs_client.cc index ac1e409261d54..4d1f02cf3201d 100644 --- a/src/v/cloud_storage_clients/abs_client.cc +++ b/src/v/cloud_storage_clients/abs_client.cc @@ -1108,12 +1108,12 @@ ss::future abs_client::do_get_object( && status == boost::beast::http::status::not_found) { vlog( abs_log.debug, - "ABS replied with expected error: {:l}", + "ABS replied with expected error: {}", response_stream->get_headers()); } else { vlog( abs_log.warn, - "ABS replied with error: {:l}", + "ABS replied with error: {}", response_stream->get_headers()); } diff --git a/src/v/cloud_storage_clients/configuration.cc b/src/v/cloud_storage_clients/configuration.cc index 3270dfe9f4141..f8ccfb151f5e0 100644 --- a/src/v/cloud_storage_clients/configuration.cc +++ b/src/v/cloud_storage_clients/configuration.cc @@ -143,17 +143,19 @@ ss::shared_ptr s3_configuration::make_probe() const { endpoint_url{server_addr.host()}); } -std::ostream& operator<<(std::ostream& o, const s3_configuration& c) { - o << "{access_key:" - << c.access_key.value_or(cloud_roles::public_key_str{""}) - << ",region:" << c.region() << ",service:" << c.service() - << ",secret_key:****" - << ",url_style:" << c.url_style << ",access_point_uri:" << c.uri() - << ",server_addr:" << c.server_addr << ",max_idle_time:" - << std::chrono::duration_cast(c.max_idle_time) - .count() - << "}"; - return o; +fmt::iterator s3_configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{access_key:{},region:{},service:{},secret_key:****,url_style:{}," + "access_point_uri:{},server_addr:{},max_idle_time:{}}}", + access_key.value_or(cloud_roles::public_key_str{""}), + region(), + service(), + url_style, + uri(), + server_addr, + std::chrono::duration_cast(max_idle_time) + .count()); } ss::future abs_configuration::make_configuration( @@ -235,40 +237,23 @@ void apply_self_configuration_result( }); } -std::ostream& operator<<(std::ostream& o, const abs_configuration& c) { - o << "{storage_account_name: " << c.storage_account_name() - << ", shared_key:" << (c.shared_key.has_value() ? "****" : "none") - << ", access_point_uri:" << c.uri() << ", server_addr:" << c.server_addr - << ", max_idle_time:" - << std::chrono::duration_cast(c.max_idle_time) - .count() - << ", is_hns_enabled:" << c.is_hns_enabled << "}"; - return o; -} - -std::ostream& -operator<<(std::ostream& o, const abs_self_configuration_result& r) { - o << "{is_hns_enabled: " << r.is_hns_enabled << "}"; - return o; -} - -std::ostream& -operator<<(std::ostream& o, const s3_self_configuration_result& r) { - o << "{s3_url_style: " << r.url_style << "}"; - return o; +fmt::iterator abs_configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{storage_account_name: {}, shared_key:{}, access_point_uri:{}, " + "server_addr:{}, max_idle_time:{}, is_hns_enabled:{}}}", + storage_account_name(), + (shared_key.has_value() ? "****" : "none"), + uri(), + server_addr, + std::chrono::duration_cast(max_idle_time) + .count(), + is_hns_enabled); } std::ostream& operator<<(std::ostream& o, const client_self_configuration_output& r) { - ss::visit( - r, - [&o](const s3_self_configuration_result& self_cfg) { - o << "{s3_self_configuration_result: " << self_cfg << "}"; - }, - [&o](const abs_self_configuration_result& self_cfg) { - o << "{abs_self_configuration_result: " << self_cfg << "}"; - }); - + fmt::print(o, "{}", r); return o; } @@ -350,15 +335,7 @@ model::cloud_storage_backend infer_backend_from_configuration( } std::ostream& operator<<(std::ostream& o, const client_configuration& c) { - ss::visit( - c, - [&o](const s3_configuration& cfg) { - o << "{s3_configuration: " << cfg << "}"; - }, - [&o](const abs_configuration& cfg) { - o << "{abs_configuration: " << cfg << "}"; - }); - + fmt::print(o, "{}", c); return o; } diff --git a/src/v/cloud_storage_clients/configuration.h b/src/v/cloud_storage_clients/configuration.h index 61070e03a25f4..bba9e0d0ed219 100644 --- a/src/v/cloud_storage_clients/configuration.h +++ b/src/v/cloud_storage_clients/configuration.h @@ -10,12 +10,16 @@ #pragma once +#include "base/format_to.h" #include "cloud_roles/auth_refresh_bg_op.h" #include "cloud_storage_clients/client_probe.h" #include "cloud_storage_clients/types.h" #include "model/metadata.h" #include "net/types.h" +#include +#include + namespace cloud_storage_clients { /// List of default overrides that can be used to workaround issues @@ -106,7 +110,7 @@ struct s3_configuration : common_configuration { ss::shared_ptr make_probe() const; - friend std::ostream& operator<<(std::ostream& o, const s3_configuration& c); + fmt::iterator format_to(fmt::iterator it) const; }; struct abs_configuration : common_configuration { @@ -125,8 +129,7 @@ struct abs_configuration : common_configuration { ss::shared_ptr make_probe() const; - friend std::ostream& - operator<<(std::ostream& o, const abs_configuration& c); + fmt::iterator format_to(fmt::iterator it) const; }; template @@ -141,12 +144,48 @@ using client_configuration std::ostream& operator<<(std::ostream&, const client_configuration&); +} // namespace cloud_storage_clients + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + const cloud_storage_clients::client_configuration& c, + fmt::format_context& ctx) const { + return std::visit( + [&ctx](const auto& cfg) { + using T = std::decay_t; + if constexpr ( + std::is_same_v) { + return fmt::format_to( + ctx.out(), "{{s3_configuration: {}}}", cfg); + } else { + return fmt::format_to( + ctx.out(), "{{abs_configuration: {}}}", cfg); + } + }, + c); + } +}; + +namespace cloud_storage_clients { + struct abs_self_configuration_result { bool is_hns_enabled; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{is_hns_enabled: {}}}", is_hns_enabled); + } }; struct s3_self_configuration_result { s3_url_style url_style; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{s3_url_style: {}}}", url_style); + } }; using client_self_configuration_output @@ -155,11 +194,39 @@ using client_self_configuration_output void apply_self_configuration_result( client_configuration&, const client_self_configuration_output&); -std::ostream& operator<<(std::ostream&, const abs_self_configuration_result&); -std::ostream& operator<<(std::ostream&, const s3_self_configuration_result&); std::ostream& operator<<(std::ostream&, const client_self_configuration_output&); +} // namespace cloud_storage_clients + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + const cloud_storage_clients::client_self_configuration_output& r, + fmt::format_context& ctx) const { + return std::visit( + [&ctx](const auto& cfg) { + using T = std::decay_t; + if constexpr ( + std::is_same_v< + T, + cloud_storage_clients::s3_self_configuration_result>) { + return fmt::format_to( + ctx.out(), "{{s3_self_configuration_result: {}}}", cfg); + } else { + return fmt::format_to( + ctx.out(), "{{abs_self_configuration_result: {}}}", cfg); + } + }, + r); + } +}; + +namespace cloud_storage_clients { + // In the case of S3-compatible providers, all that is needed to infer the // backend is the access point/uri. model::cloud_storage_backend diff --git a/src/v/cloud_storage_clients/s3_client.cc b/src/v/cloud_storage_clients/s3_client.cc index 3d303bedeae20..4faa63fc1fed5 100644 --- a/src/v/cloud_storage_clients/s3_client.cc +++ b/src/v/cloud_storage_clients/s3_client.cc @@ -10,6 +10,7 @@ #include "cloud_storage_clients/s3_client.h" +#include "base/external_fmt.h" #include "base/units.h" #include "base/vlog.h" #include "bytes/bytes.h" @@ -1066,14 +1067,14 @@ ss::future s3_client::do_get_object( vlog( s3_log.debug, "S3 GET request with expected error for key {}: {} " - "{:l}", + "{}", key, ref->get_headers().result(), ref->get_headers()); } else { vlog( s3_log.warn, - "S3 GET request failed for key {}: {} {:l}", + "S3 GET request failed for key {}: {} {}", key, ref->get_headers().result(), ref->get_headers()); @@ -1121,7 +1122,7 @@ ss::future s3_client::do_head_object( if (status == boost::beast::http::status::not_found) { vlog( s3_log.debug, - "Object {} not available, error: {:l}", + "Object {} not available, error: {}", key, ref->get_headers()); return parse_head_error_response( @@ -1129,7 +1130,7 @@ ss::future s3_client::do_head_object( } else if (status != boost::beast::http::status::ok) { vlog( s3_log.warn, - "S3 HEAD request failed for key {}: {} {:l}", + "S3 HEAD request failed for key {}: {} {}", key, status, ref->get_headers()); @@ -1208,7 +1209,7 @@ ss::future<> s3_client::do_put_object( status != ok && !is_no_content_and_accepted) { vlog( s3_log.warn, - "S3 PUT request failed for key {}: {} {:l}", + "S3 PUT request failed for key {}: {} {}", id, status, ref->get_headers()); @@ -1302,7 +1303,7 @@ ss::future s3_client::do_list_objects_v2( if (header.result() != boost::beast::http::status::ok) { vlog( s3_log.warn, - "S3 ListObjectsv2 request failed: {} {:l}", + "S3 ListObjectsv2 request failed: {} {}", header.result(), header); @@ -1377,7 +1378,7 @@ ss::future<> s3_client::do_delete_object( != boost::beast::http::status::no_content) { // expect 204 vlog( s3_log.warn, - "S3 DeleteObject request failed for key {}: {} {:l}", + "S3 DeleteObject request failed for key {}: {} {}", key, status, ref->get_headers()); diff --git a/src/v/cloud_storage_clients/s3_error.cc b/src/v/cloud_storage_clients/s3_error.cc index 2add45b46e5db..b93ceb844d09e 100644 --- a/src/v/cloud_storage_clients/s3_error.cc +++ b/src/v/cloud_storage_clients/s3_error.cc @@ -16,409 +16,277 @@ namespace cloud_storage_clients { -std::ostream& operator<<(std::ostream& o, s3_error_code code) { +fmt::iterator format_to(s3_error_code code, fmt::iterator out) { switch (code) { case s3_error_code::access_denied: - o << "AccessDenied"; - break; + return fmt::format_to(out, "AccessDenied"); case s3_error_code::account_problem: - o << "AccountProblem"; - break; + return fmt::format_to(out, "AccountProblem"); case s3_error_code::all_access_disabled: - o << "AllAccessDisabled"; - break; + return fmt::format_to(out, "AllAccessDisabled"); case s3_error_code::ambiguous_grant_by_email_address: - o << "AmbiguousGrantByEmailAddress"; - break; + return fmt::format_to(out, "AmbiguousGrantByEmailAddress"); case s3_error_code::authentication_required: - o << "AuthenticationRequired"; - break; + return fmt::format_to(out, "AuthenticationRequired"); case s3_error_code::authorization_header_malformed: - o << "AuthorizationHeaderMalformed"; - break; + return fmt::format_to(out, "AuthorizationHeaderMalformed"); case s3_error_code::bad_digest: - o << "BadDigest"; - break; + return fmt::format_to(out, "BadDigest"); case s3_error_code::bucket_already_exists: - o << "BucketAlreadyExists"; - break; + return fmt::format_to(out, "BucketAlreadyExists"); case s3_error_code::bucket_already_owned_by_you: - o << "BucketAlreadyOwnedByYou"; - break; + return fmt::format_to(out, "BucketAlreadyOwnedByYou"); case s3_error_code::bucket_not_empty: - o << "BucketNotEmpty"; - break; + return fmt::format_to(out, "BucketNotEmpty"); case s3_error_code::credentials_not_supported: - o << "CredentialsNotSupported"; - break; + return fmt::format_to(out, "CredentialsNotSupported"); case s3_error_code::cross_location_logging_prohibited: - o << "CrossLocationLoggingProhibited"; - break; + return fmt::format_to(out, "CrossLocationLoggingProhibited"); case s3_error_code::entity_too_small: - o << "EntityTooSmall"; - break; + return fmt::format_to(out, "EntityTooSmall"); case s3_error_code::entity_too_large: - o << "EntityTooLarge"; - break; + return fmt::format_to(out, "EntityTooLarge"); case s3_error_code::expired_token: - o << "ExpiredToken"; - break; + return fmt::format_to(out, "ExpiredToken"); case s3_error_code::illegal_location_constraint_exception: - o << "IllegalLocationConstraintException"; - break; + return fmt::format_to(out, "IllegalLocationConstraintException"); case s3_error_code::illegal_versioning_configuration_exception: - o << "IllegalVersioningConfigurationException"; - break; + return fmt::format_to(out, "IllegalVersioningConfigurationException"); case s3_error_code::incomplete_body: - o << "IncompleteBody"; - break; + return fmt::format_to(out, "IncompleteBody"); case s3_error_code::incorrect_number_of_files_in_post_request: - o << "IncorrectNumberOfFilesInPostRequest"; - break; + return fmt::format_to(out, "IncorrectNumberOfFilesInPostRequest"); case s3_error_code::inline_data_too_large: - o << "InlineDataTooLarge"; - break; + return fmt::format_to(out, "InlineDataTooLarge"); case s3_error_code::internal_error: - o << "InternalError"; - break; + return fmt::format_to(out, "InternalError"); case s3_error_code::invalid_access_key_id: - o << "InvalidAccessKeyId"; - break; + return fmt::format_to(out, "InvalidAccessKeyId"); case s3_error_code::invalid_access_point: - o << "InvalidAccessPoint"; - break; + return fmt::format_to(out, "InvalidAccessPoint"); case s3_error_code::invalid_addressing_header: - o << "InvalidAddressingHeader"; - break; + return fmt::format_to(out, "InvalidAddressingHeader"); case s3_error_code::invalid_argument: - o << "InvalidArgument"; - break; + return fmt::format_to(out, "InvalidArgument"); case s3_error_code::invalid_bucket_name: - o << "InvalidBucketName"; - break; + return fmt::format_to(out, "InvalidBucketName"); case s3_error_code::invalid_bucket_state: - o << "InvalidBucketState"; - break; + return fmt::format_to(out, "InvalidBucketState"); case s3_error_code::invalid_digest: - o << "InvalidDigest"; - break; + return fmt::format_to(out, "InvalidDigest"); case s3_error_code::invalid_encryption_algorithm_error: - o << "InvalidEncryptionAlgorithmError"; - break; + return fmt::format_to(out, "InvalidEncryptionAlgorithmError"); case s3_error_code::invalid_location_constraint: - o << "InvalidLocationConstraint"; - break; + return fmt::format_to(out, "InvalidLocationConstraint"); case s3_error_code::invalid_object_state: - o << "InvalidObjectState"; - break; + return fmt::format_to(out, "InvalidObjectState"); case s3_error_code::invalid_part: - o << "InvalidPart"; - break; + return fmt::format_to(out, "InvalidPart"); case s3_error_code::invalid_part_order: - o << "InvalidPartOrder"; - break; + return fmt::format_to(out, "InvalidPartOrder"); case s3_error_code::invalid_payer: - o << "InvalidPayer"; - break; + return fmt::format_to(out, "InvalidPayer"); case s3_error_code::invalid_policy_document: - o << "InvalidPolicyDocument"; - break; + return fmt::format_to(out, "InvalidPolicyDocument"); case s3_error_code::invalid_range: - o << "InvalidRange"; - break; + return fmt::format_to(out, "InvalidRange"); case s3_error_code::invalid_request: - o << "InvalidRequest"; - break; + return fmt::format_to(out, "InvalidRequest"); case s3_error_code::invalid_security: - o << "InvalidSecurity"; - break; + return fmt::format_to(out, "InvalidSecurity"); case s3_error_code::invalid_soaprequest: - o << "InvalidSOAPRequest"; - break; + return fmt::format_to(out, "InvalidSOAPRequest"); case s3_error_code::invalid_storage_class: - o << "InvalidStorageClass"; - break; + return fmt::format_to(out, "InvalidStorageClass"); case s3_error_code::invalid_target_bucket_for_logging: - o << "InvalidTargetBucketForLogging"; - break; + return fmt::format_to(out, "InvalidTargetBucketForLogging"); case s3_error_code::invalid_token: - o << "InvalidToken"; - break; + return fmt::format_to(out, "InvalidToken"); case s3_error_code::invalid_uri: - o << "InvalidURI"; - break; + return fmt::format_to(out, "InvalidURI"); case s3_error_code::key_too_long_error: - o << "KeyTooLongError"; - break; + return fmt::format_to(out, "KeyTooLongError"); case s3_error_code::malformed_aclerror: - o << "MalformedACLError"; - break; + return fmt::format_to(out, "MalformedACLError"); case s3_error_code::malformed_postrequest: - o << "MalformedPOSTRequest"; - break; + return fmt::format_to(out, "MalformedPOSTRequest"); case s3_error_code::malformed_xml: - o << "MalformedXML"; - break; + return fmt::format_to(out, "MalformedXML"); case s3_error_code::max_message_length_exceeded: - o << "MaxMessageLengthExceeded"; - break; + return fmt::format_to(out, "MaxMessageLengthExceeded"); case s3_error_code::max_post_pre_data_length_exceeded_error: - o << "MaxPostPreDataLengthExceededError"; - break; + return fmt::format_to(out, "MaxPostPreDataLengthExceededError"); case s3_error_code::metadata_too_large: - o << "MetadataTooLarge"; - break; + return fmt::format_to(out, "MetadataTooLarge"); case s3_error_code::method_not_allowed: - o << "MethodNotAllowed"; - break; + return fmt::format_to(out, "MethodNotAllowed"); case s3_error_code::missing_attachment: - o << "MissingAttachment"; - break; + return fmt::format_to(out, "MissingAttachment"); case s3_error_code::missing_content_length: - o << "MissingContentLength"; - break; + return fmt::format_to(out, "MissingContentLength"); case s3_error_code::missing_request_body_error: - o << "MissingRequestBodyError"; - break; + return fmt::format_to(out, "MissingRequestBodyError"); case s3_error_code::missing_security_element: - o << "MissingSecurityElement"; - break; + return fmt::format_to(out, "MissingSecurityElement"); case s3_error_code::missing_security_header: - o << "MissingSecurityHeader"; - break; + return fmt::format_to(out, "MissingSecurityHeader"); case s3_error_code::no_logging_status_for_key: - o << "NoLoggingStatusForKey"; - break; + return fmt::format_to(out, "NoLoggingStatusForKey"); case s3_error_code::no_such_bucket: - o << "NoSuchBucket"; - break; + return fmt::format_to(out, "NoSuchBucket"); case s3_error_code::no_such_bucket_policy: - o << "NoSuchBucketPolicy"; - break; + return fmt::format_to(out, "NoSuchBucketPolicy"); case s3_error_code::no_such_key: - o << "NoSuchKey"; - break; + return fmt::format_to(out, "NoSuchKey"); case s3_error_code::no_such_lifecycle_configuration: - o << "NoSuchLifecycleConfiguration"; - break; + return fmt::format_to(out, "NoSuchLifecycleConfiguration"); case s3_error_code::no_such_tag_set: - o << "NoSuchTagSet"; - break; + return fmt::format_to(out, "NoSuchTagSet"); case s3_error_code::no_such_upload: - o << "NoSuchUpload"; - break; + return fmt::format_to(out, "NoSuchUpload"); case s3_error_code::no_such_version: - o << "NoSuchVersion"; - break; + return fmt::format_to(out, "NoSuchVersion"); case s3_error_code::not_implemented: - o << "NotImplemented"; - break; + return fmt::format_to(out, "NotImplemented"); case s3_error_code::not_signed_up: - o << "NotSignedUp"; - break; + return fmt::format_to(out, "NotSignedUp"); case s3_error_code::operation_aborted: - o << "OperationAborted"; - break; + return fmt::format_to(out, "OperationAborted"); case s3_error_code::permanent_redirect: - o << "PermanentRedirect"; - break; + return fmt::format_to(out, "PermanentRedirect"); case s3_error_code::precondition_failed: - o << "PreconditionFailed"; - break; + return fmt::format_to(out, "PreconditionFailed"); case s3_error_code::redirect: - o << "Redirect"; - break; + return fmt::format_to(out, "Redirect"); case s3_error_code::request_header_section_too_large: - o << "RequestHeaderSectionTooLarge"; - break; + return fmt::format_to(out, "RequestHeaderSectionTooLarge"); case s3_error_code::request_is_not_multi_part_content: - o << "RequestIsNotMultiPartContent"; - break; + return fmt::format_to(out, "RequestIsNotMultiPartContent"); case s3_error_code::request_timeout: - o << "RequestTimeout"; - break; + return fmt::format_to(out, "RequestTimeout"); case s3_error_code::request_time_too_skewed: - o << "RequestTimeTooSkewed"; - break; + return fmt::format_to(out, "RequestTimeTooSkewed"); case s3_error_code::request_torrent_of_bucket_error: - o << "RequestTorrentOfBucketError"; - break; + return fmt::format_to(out, "RequestTorrentOfBucketError"); case s3_error_code::restore_already_in_progress: - o << "RestoreAlreadyInProgress"; - break; + return fmt::format_to(out, "RestoreAlreadyInProgress"); case s3_error_code::server_side_encryption_configuration_not_found_error: - o << "ServerSideEncryptionConfigurationNotFoundError"; - break; + return fmt::format_to( + out, "ServerSideEncryptionConfigurationNotFoundError"); case s3_error_code::service_unavailable: - o << "ServiceUnavailable"; - break; + return fmt::format_to(out, "ServiceUnavailable"); case s3_error_code::signature_does_not_match: - o << "SignatureDoesNotMatch"; - break; + return fmt::format_to(out, "SignatureDoesNotMatch"); case s3_error_code::slow_down: - o << "SlowDown"; - break; + return fmt::format_to(out, "SlowDown"); case s3_error_code::temporary_redirect: - o << "TemporaryRedirect"; - break; + return fmt::format_to(out, "TemporaryRedirect"); case s3_error_code::token_refresh_required: - o << "TokenRefreshRequired"; - break; + return fmt::format_to(out, "TokenRefreshRequired"); case s3_error_code::too_many_access_points: - o << "TooManyAccessPoints"; - break; + return fmt::format_to(out, "TooManyAccessPoints"); case s3_error_code::too_many_buckets: - o << "TooManyBuckets"; - break; + return fmt::format_to(out, "TooManyBuckets"); case s3_error_code::unexpected_content: - o << "UnexpectedContent"; - break; + return fmt::format_to(out, "UnexpectedContent"); case s3_error_code::unresolvable_grant_by_email_address: - o << "UnresolvableGrantByEmailAddress"; - break; + return fmt::format_to(out, "UnresolvableGrantByEmailAddress"); case s3_error_code::user_key_must_be_specified: - o << "UserKeyMustBeSpecified"; - break; + return fmt::format_to(out, "UserKeyMustBeSpecified"); case s3_error_code::no_such_access_point: - o << "NoSuchAccessPoint"; - break; + return fmt::format_to(out, "NoSuchAccessPoint"); case s3_error_code::invalid_tag: - o << "InvalidTag"; - break; + return fmt::format_to(out, "InvalidTag"); case s3_error_code::malformed_policy: - o << "MalformedPolicy"; - break; + return fmt::format_to(out, "MalformedPolicy"); case s3_error_code::no_such_configuration: - o << "NoSuchConfiguration"; - break; + return fmt::format_to(out, "NoSuchConfiguration"); case s3_error_code::authorization_query_parameters_error: - o << "AuthorizationQueryParametersError"; - break; + return fmt::format_to(out, "AuthorizationQueryParametersError"); case s3_error_code::access_point_already_owned_by_you: - o << "AccessPointAlreadyOwnedByYou"; - break; + return fmt::format_to(out, "AccessPointAlreadyOwnedByYou"); case s3_error_code::access_control_list_not_supported: - o << "AccessControlListNotSupported"; - break; + return fmt::format_to(out, "AccessControlListNotSupported"); case s3_error_code::endpoint_not_found: - o << "EndpointNotFound"; - break; + return fmt::format_to(out, "EndpointNotFound"); case s3_error_code::device_not_active_error: - o << "DeviceNotActiveError"; - break; + return fmt::format_to(out, "DeviceNotActiveError"); case s3_error_code::conditional_request_conflict: - o << "ConditionalRequestConflict"; - break; + return fmt::format_to(out, "ConditionalRequestConflict"); case s3_error_code::connection_closed_by_requester: - o << "ConnectionClosedByRequester"; - break; + return fmt::format_to(out, "ConnectionClosedByRequester"); case s3_error_code::client_token_conflict: - o << "ClientTokenConflict"; - break; + return fmt::format_to(out, "ClientTokenConflict"); case s3_error_code::bucket_has_access_points_attached: - o << "BucketHasAccessPointsAttached"; - break; + return fmt::format_to(out, "BucketHasAccessPointsAttached"); case s3_error_code::invalid_access_point_alias_error: - o << "InvalidAccessPointAliasError"; - break; + return fmt::format_to(out, "InvalidAccessPointAliasError"); case s3_error_code::incorrect_endpoint: - o << "IncorrectEndpoint"; - break; + return fmt::format_to(out, "IncorrectEndpoint"); case s3_error_code::invalid_http_method: - o << "InvalidHttpMethod"; - break; + return fmt::format_to(out, "InvalidHttpMethod"); case s3_error_code::invalid_host_header: - o << "InvalidHostHeader"; - break; + return fmt::format_to(out, "InvalidHostHeader"); case s3_error_code::invalid_bucket_owner_aws_account_id: - o << "InvalidBucketOwnerAWSAccountID"; - break; + return fmt::format_to(out, "InvalidBucketOwnerAWSAccountID"); case s3_error_code::invalid_bucket_acl_with_object_ownership: - o << "InvalidBucketAclWithObjectOwnership"; - break; + return fmt::format_to(out, "InvalidBucketAclWithObjectOwnership"); case s3_error_code::invalid_session_exception: - o << "InvalidSessionException"; - break; + return fmt::format_to(out, "InvalidSessionException"); case s3_error_code::invalid_signature: - o << "InvalidSignature"; - break; + return fmt::format_to(out, "InvalidSignature"); case s3_error_code::kms_disabled_exception: - o << "KMS.DisabledException"; - break; + return fmt::format_to(out, "KMS.DisabledException"); case s3_error_code::kms_invalid_key_usage_exception: - o << "KMS.InvalidKeyUsageException"; - break; + return fmt::format_to(out, "KMS.InvalidKeyUsageException"); case s3_error_code::kms_invalid_state_exception: - o << "KMS.KMSInvalidStateException"; - break; + return fmt::format_to(out, "KMS.KMSInvalidStateException"); case s3_error_code::kms_not_found_exception: - o << "KMS.NotFoundException"; - break; + return fmt::format_to(out, "KMS.NotFoundException"); case s3_error_code::missing_authentication_token: - o << "MissingAuthenticationToken"; - break; + return fmt::format_to(out, "MissingAuthenticationToken"); case s3_error_code::no_such_async_request: - o << "NoSuchAsyncRequest"; - break; + return fmt::format_to(out, "NoSuchAsyncRequest"); case s3_error_code::no_such_cors_configuration: - o << "NoSuchCORSConfiguration"; - break; + return fmt::format_to(out, "NoSuchCORSConfiguration"); case s3_error_code::no_such_multi_region_access_point: - o << "NoSuchMultiRegionAccessPoint"; - break; + return fmt::format_to(out, "NoSuchMultiRegionAccessPoint"); case s3_error_code::no_such_object_lock_configuration: - o << "NoSuchObjectLockConfiguration"; - break; + return fmt::format_to(out, "NoSuchObjectLockConfiguration"); case s3_error_code::no_such_website_configuration: - o << "NoSuchWebsiteConfiguration"; - break; + return fmt::format_to(out, "NoSuchWebsiteConfiguration"); case s3_error_code::not_modified: - o << "NotModified"; - break; + return fmt::format_to(out, "NotModified"); case s3_error_code::not_device_owner_error: - o << "NotDeviceOwnerError"; - break; + return fmt::format_to(out, "NotDeviceOwnerError"); case s3_error_code::no_transformation_defined: - o << "NoTransformationDefined"; - break; + return fmt::format_to(out, "NoTransformationDefined"); case s3_error_code::object_lock_configuration_not_found_error: - o << "ObjectLockConfigurationNotFoundError"; - break; + return fmt::format_to(out, "ObjectLockConfigurationNotFoundError"); case s3_error_code::ownership_controls_not_found_error: - o << "OwnershipControlsNotFoundError"; - break; + return fmt::format_to(out, "OwnershipControlsNotFoundError"); case s3_error_code::permanent_redirect_control_error: - o << "PermanentRedirectControlError"; - break; + return fmt::format_to(out, "PermanentRedirectControlError"); case s3_error_code::response_interrupted: - o << "ResponseInterrupted"; - break; + return fmt::format_to(out, "ResponseInterrupted"); case s3_error_code::token_code_invalid_error: - o << "TokenCodeInvalidError"; - break; + return fmt::format_to(out, "TokenCodeInvalidError"); case s3_error_code::too_many_multi_region_access_pointregions_error: - o << "TooManyMultiRegionAccessPointregionsError"; - break; + return fmt::format_to(out, "TooManyMultiRegionAccessPointregionsError"); case s3_error_code::too_many_multi_region_access_points: - o << "TooManyMultiRegionAccessPoints"; - break; + return fmt::format_to(out, "TooManyMultiRegionAccessPoints"); case s3_error_code::unauthorized_access_error: - o << "UnauthorizedAccessError"; - break; + return fmt::format_to(out, "UnauthorizedAccessError"); case s3_error_code::unexpected_ip_error: - o << "UnexpectedIPError"; - break; + return fmt::format_to(out, "UnexpectedIPError"); case s3_error_code::unsupported_signature: - o << "UnsupportedSignature"; - break; + return fmt::format_to(out, "UnsupportedSignature"); case s3_error_code::unsupported_argument: - o << "UnsupportedArgument"; - break; + return fmt::format_to(out, "UnsupportedArgument"); case s3_error_code::_unknown: - o << "_unknown_error_code_"; - break; + return fmt::format_to(out, "_unknown_error_code_"); } - return o; + return fmt::format_to(out, "_unknown_error_code_"); } // NOLINTNEXTLINE @@ -629,12 +497,4 @@ std::string_view rest_error_response::resource() const noexcept { return _resource; } -std::ostream& operator<<(std::ostream& o, const rest_error_response& err) { - static constexpr auto format - = "code: {}, message: {}, request_id: {}, resource: {}"; - fmt::print( - o, format, err._code_str, err._message, err._request_id, err._resource); - return o; -} - } // namespace cloud_storage_clients diff --git a/src/v/cloud_storage_clients/s3_error.h b/src/v/cloud_storage_clients/s3_error.h index 3fa2fcefb597f..625261d68a916 100644 --- a/src/v/cloud_storage_clients/s3_error.h +++ b/src/v/cloud_storage_clients/s3_error.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" @@ -162,8 +163,9 @@ enum class s3_error_code { _unknown }; -/// Operators to use with lexical_cast -std::ostream& operator<<(std::ostream& o, s3_error_code code); +fmt::iterator format_to(s3_error_code code, fmt::iterator); + +/// Operator to use with lexical_cast std::istream& operator>>(std::istream& i, s3_error_code& code); /// Error received in a response from the server @@ -183,8 +185,15 @@ class rest_error_response : public std::exception { std::string_view request_id() const noexcept; std::string_view resource() const noexcept; - friend std::ostream& - operator<<(std::ostream& o, const rest_error_response& err); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "code: {}, message: {}, request_id: {}, resource: {}", + _code_str, + _message, + _request_id, + _resource); + } private: s3_error_code _code; @@ -197,3 +206,15 @@ class rest_error_response : public std::exception { }; } // namespace cloud_storage_clients + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + const cloud_storage_clients::rest_error_response& v, + fmt::format_context& ctx) const { + return v.format_to(ctx.out()); + } +}; diff --git a/src/v/cloud_storage_clients/tests/BUILD b/src/v/cloud_storage_clients/tests/BUILD index d3ddf3dcfe2cf..016e6d7383ad7 100644 --- a/src/v/cloud_storage_clients/tests/BUILD +++ b/src/v/cloud_storage_clients/tests/BUILD @@ -96,6 +96,7 @@ redpanda_cc_gtest( "util_test.cc", ], deps = [ + "//src/v/base", "//src/v/bytes:iobuf", "//src/v/bytes:iobuf_parser", "//src/v/cloud_storage_clients", diff --git a/src/v/cloud_storage_clients/tests/util_test.cc b/src/v/cloud_storage_clients/tests/util_test.cc index cd14e45a25d48..07738f9ce89c1 100644 --- a/src/v/cloud_storage_clients/tests/util_test.cc +++ b/src/v/cloud_storage_clients/tests/util_test.cc @@ -1,3 +1,4 @@ +#include "base/external_fmt.h" #include "bytes/iobuf.h" #include "bytes/iobuf_parser.h" #include "cloud_storage_clients/util.h" diff --git a/src/v/cloud_storage_clients/types.h b/src/v/cloud_storage_clients/types.h index df1b22e335f67..11f7e28ff29b3 100644 --- a/src/v/cloud_storage_clients/types.h +++ b/src/v/cloud_storage_clients/types.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/types.h" #include "utils/named_type.h" @@ -75,12 +76,12 @@ inline std::error_code make_error_code(error_outcome e) noexcept { enum class s3_url_style { virtual_host = 0, path }; -inline std::ostream& operator<<(std::ostream& os, const s3_url_style& us) { +inline fmt::iterator format_to(s3_url_style us, fmt::iterator out) { switch (us) { case s3_url_style::virtual_host: - return os << "virtual_host"; + return fmt::format_to(out, "virtual_host"); case s3_url_style::path: - return os << "path"; + return fmt::format_to(out, "path"); } } @@ -105,3 +106,18 @@ namespace std { template<> struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + cloud_storage_clients::error_outcome e, fmt::format_context& ctx) const { + return fmt::format_to( + ctx.out(), + "{}", + cloud_storage_clients::error_outcome_category{}.message( + static_cast(e))); + } +}; diff --git a/src/v/cloud_storage_clients/util.cc b/src/v/cloud_storage_clients/util.cc index 1df3874b92721..dc9c78a0e0864 100644 --- a/src/v/cloud_storage_clients/util.cc +++ b/src/v/cloud_storage_clients/util.cc @@ -10,6 +10,7 @@ #include "cloud_storage_clients/util.h" +#include "base/external_fmt.h" #include "base/vlog.h" #include "bytes/streambuf.h" #include "container/chunked_vector.h" diff --git a/src/v/cloud_topics/errc.h b/src/v/cloud_topics/errc.h index 3fc52dd45c01a..b1bdbc251b39d 100644 --- a/src/v/cloud_topics/errc.h +++ b/src/v/cloud_topics/errc.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include namespace cloud_topics { @@ -71,3 +73,11 @@ namespace std { template<> struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter : fmt::formatter { + auto format(cloud_topics::errc e, fmt::format_context& ctx) const { + return fmt::formatter::format( + cloud_topics::errc_category{}.message(static_cast(e)), ctx); + } +}; diff --git a/src/v/cloud_topics/level_one/compaction/meta.h b/src/v/cloud_topics/level_one/compaction/meta.h index 86df0078d5cda..5db79f883eb06 100644 --- a/src/v/cloud_topics/level_one/compaction/meta.h +++ b/src/v/cloud_topics/level_one/compaction/meta.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_topics/level_one/metastore/metastore.h" #include "cloud_topics/level_one/metastore/offset_interval_set.h" #include "container/chunked_hash_map.h" @@ -125,16 +126,16 @@ enum class compaction_job_state { hard_stop }; -inline std::ostream& operator<<(std::ostream& o, compaction_job_state s) { +inline fmt::iterator format_to(compaction_job_state s, fmt::iterator out) { switch (s) { case compaction_job_state::idle: - return o << "idle"; + return fmt::format_to(out, "idle"); case compaction_job_state::running: - return o << "running"; + return fmt::format_to(out, "running"); case compaction_job_state::soft_stop: - return o << "soft_stop"; + return fmt::format_to(out, "soft_stop"); case compaction_job_state::hard_stop: - return o << "hard_stop"; + return fmt::format_to(out, "hard_stop"); } } diff --git a/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.cc b/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.cc index a829f004c13ae..fbe104b23e6b4 100644 --- a/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.cc +++ b/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.cc @@ -515,8 +515,8 @@ level_one_log_reader_impl::return_or_close(std::optional r) { } } -void level_one_log_reader_impl::print(std::ostream& o) { - o << "level_one_cloud_topics_reader"; +fmt::iterator level_one_log_reader_impl::format_to(fmt::iterator it) const { + return fmt::format_to(it, "level_one_cloud_topics_reader"); } void level_one_log_reader_impl::set_end_of_stream() { _end_of_stream = true; } diff --git a/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.h b/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.h index 1fc7952e2e5e6..dd7b3ea948389 100644 --- a/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.h +++ b/src/v/cloud_topics/level_one/frontend_reader/level_one_reader.h @@ -81,7 +81,7 @@ class level_one_log_reader_impl : public model::record_batch_reader::impl { ss::future do_load_slice(model::timeout_clock::time_point) final; - void print(std::ostream& o) final; + fmt::iterator format_to(fmt::iterator it) const final; private: struct object_info { diff --git a/src/v/cloud_topics/level_one/metastore/lsm/BUILD b/src/v/cloud_topics/level_one/metastore/lsm/BUILD index 78ce7cdc46404..dea68e2c7d5d8 100644 --- a/src/v/cloud_topics/level_one/metastore/lsm/BUILD +++ b/src/v/cloud_topics/level_one/metastore/lsm/BUILD @@ -18,6 +18,7 @@ redpanda_cc_library( visibility = ["//visibility:public"], deps = [ "//proto/redpanda/core/admin/internal/cloud_topics/v1:metastore_redpanda_proto", + "//src/v/base", "//src/v/utils:detailed_error", "@fmt", "@seastar", @@ -177,6 +178,7 @@ redpanda_cc_library( deps = [ ":lsm_update", ":state", + "//src/v/base", "//src/v/cluster:state_machine_registry", "//src/v/config", "//src/v/model", diff --git a/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.cc b/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.cc index f5396c7c9c2fc..90c0d875d46aa 100644 --- a/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.cc +++ b/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.cc @@ -53,6 +53,10 @@ std::string_view to_string_view(debug_serde_errc e) { return "unknown"; } +fmt::iterator format_to(debug_serde_errc e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); +} + namespace { std::optional parse_uuid(const std::string& s) { diff --git a/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.h b/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.h index e866162bca1c2..53107fa7e69fe 100644 --- a/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.h +++ b/src/v/cloud_topics/level_one/metastore/lsm/debug_serde.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "proto/redpanda/core/admin/internal/cloud_topics/v1/metastore.proto.h" #include "utils/detailed_error.h" @@ -28,6 +29,7 @@ enum class debug_serde_errc { using debug_serde_error = detailed_error; std::string_view to_string_view(debug_serde_errc e); +fmt::iterator format_to(debug_serde_errc, fmt::iterator); /// Encode a typed proto key into the raw LSM key string. std::expected @@ -38,7 +40,3 @@ std::expected debug_encode_value(const proto::admin::metastore::row_value&); } // namespace cloud_topics::l1 - -inline auto format_as(cloud_topics::l1::debug_serde_errc e) { - return cloud_topics::l1::to_string_view(e); -} diff --git a/src/v/cloud_topics/level_one/metastore/lsm/garbage_collector.h b/src/v/cloud_topics/level_one/metastore/lsm/garbage_collector.h index 4789a4fad6698..dcd381ed01f6a 100644 --- a/src/v/cloud_topics/level_one/metastore/lsm/garbage_collector.h +++ b/src/v/cloud_topics/level_one/metastore/lsm/garbage_collector.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "cloud_topics/level_one/common/object_id.h" #include "container/chunked_vector.h" @@ -19,7 +20,6 @@ #include #include -#include namespace cloud_topics::l1 { @@ -79,14 +79,15 @@ class db_garbage_collector { domain_manager_probe* probe_; }; -inline std::ostream& operator<<(std::ostream& o, db_garbage_collector::errc e) { +inline fmt::iterator +format_to(db_garbage_collector::errc e, fmt::iterator out) { switch (e) { case db_garbage_collector::errc::db_needs_reopen: - return o << "db_needs_reopen"; + return fmt::format_to(out, "db_needs_reopen"); case db_garbage_collector::errc::io_error: - return o << "io_error"; + return fmt::format_to(out, "io_error"); } - return o << "unknown"; + return fmt::format_to(out, "unknown"); } } // namespace cloud_topics::l1 diff --git a/src/v/cloud_topics/level_one/metastore/lsm/stm.cc b/src/v/cloud_topics/level_one/metastore/lsm/stm.cc index 87e1a037d9fa6..b8cb9a3b51929 100644 --- a/src/v/cloud_topics/level_one/metastore/lsm/stm.cc +++ b/src/v/cloud_topics/level_one/metastore/lsm/stm.cc @@ -47,16 +47,6 @@ void maybe_log_update_error( r.error()); } -std::string_view to_string_view(stm::errc e) { - switch (e) { - case stm::errc::not_leader: - return "stm::errc::not_leader"; - case stm::errc::raft_error: - return "stm::errc::raft_error"; - case stm::errc::shutting_down: - return "stm::errc::shutting_down"; - } -} } // namespace stm::stm( @@ -268,12 +258,4 @@ void lsm_stm_factory::create( raft->log()->stm_hookset()->add_stm(std::move(s)); } -std::ostream& operator<<(std::ostream& os, stm::errc e) { - return os << to_string_view(e); -} - } // namespace cloud_topics::l1 - -auto format_as(cloud_topics::l1::stm::errc e) { - return cloud_topics::l1::to_string_view(e); -} diff --git a/src/v/cloud_topics/level_one/metastore/lsm/stm.h b/src/v/cloud_topics/level_one/metastore/lsm/stm.h index f339bc0e5e893..69c145a511585 100644 --- a/src/v/cloud_topics/level_one/metastore/lsm/stm.h +++ b/src/v/cloud_topics/level_one/metastore/lsm/stm.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "cloud_topics/level_one/metastore/lsm/state.h" #include "cluster/state_machine_registry.h" #include "model/fundamental.h" @@ -90,6 +91,17 @@ class stm final : public metastore_stm_base { ss::timer snapshot_timer_; }; +inline fmt::iterator format_to(stm::errc e, fmt::iterator out) { + switch (e) { + case stm::errc::not_leader: + return fmt::format_to(out, "stm::errc::not_leader"); + case stm::errc::raft_error: + return fmt::format_to(out, "stm::errc::raft_error"); + case stm::errc::shutting_down: + return fmt::format_to(out, "stm::errc::shutting_down"); + } +} + class lsm_stm_factory : public cluster::state_machine_factory { public: lsm_stm_factory() = default; @@ -100,6 +112,4 @@ class lsm_stm_factory : public cluster::state_machine_factory { const cluster::stm_instance_config&) final; }; -std::ostream& operator<<(std::ostream& os, stm::errc e); - } // namespace cloud_topics::l1 diff --git a/src/v/cloud_topics/level_one/metastore/offset_interval_set.cc b/src/v/cloud_topics/level_one/metastore/offset_interval_set.cc index 779cb9625311c..80131b7aa649c 100644 --- a/src/v/cloud_topics/level_one/metastore/offset_interval_set.cc +++ b/src/v/cloud_topics/level_one/metastore/offset_interval_set.cc @@ -14,12 +14,6 @@ namespace cloud_topics::l1 { -std::ostream& -operator<<(std::ostream& o, const offset_interval_set::interval& iv) { - fmt::print(o, "{}", iv); - return o; -} - bool offset_interval_set::empty() const { return iset_.empty(); } bool offset_interval_set::insert(kafka::offset base, kafka::offset last) { diff --git a/src/v/cloud_topics/level_one/metastore/offset_interval_set.h b/src/v/cloud_topics/level_one/metastore/offset_interval_set.h index dcaccf73c6048..858728acd8059 100644 --- a/src/v/cloud_topics/level_one/metastore/offset_interval_set.h +++ b/src/v/cloud_topics/level_one/metastore/offset_interval_set.h @@ -33,7 +33,6 @@ class offset_interval_set struct interval { kafka::offset base_offset; kafka::offset last_offset; - friend std::ostream& operator<<(std::ostream&, const interval&); }; template class stream { diff --git a/src/v/cloud_topics/level_one/metastore/partition_validator.cc b/src/v/cloud_topics/level_one/metastore/partition_validator.cc index 4923cedad35ed..be582b9cffabc 100644 --- a/src/v/cloud_topics/level_one/metastore/partition_validator.cc +++ b/src/v/cloud_topics/level_one/metastore/partition_validator.cc @@ -55,6 +55,10 @@ std::string_view to_string_view(anomaly_type t) { return "unknown_anomaly"; } +fmt::iterator format_to(anomaly_type t, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(t)); +} + std::string_view to_string_view(partition_validator::errc e) { switch (e) { case partition_validator::errc::io_error: @@ -65,6 +69,10 @@ std::string_view to_string_view(partition_validator::errc e) { return "partition_validator::errc::unknown"; } +fmt::iterator format_to(partition_validator::errc e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); +} + namespace { partition_validator::error diff --git a/src/v/cloud_topics/level_one/metastore/partition_validator.h b/src/v/cloud_topics/level_one/metastore/partition_validator.h index 9bcc63bc7266c..8c9b45207a1f5 100644 --- a/src/v/cloud_topics/level_one/metastore/partition_validator.h +++ b/src/v/cloud_topics/level_one/metastore/partition_validator.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "cloud_storage_clients/types.h" #include "cloud_topics/level_one/metastore/lsm/state_reader.h" @@ -49,8 +50,7 @@ enum class anomaly_type : int { }; std::string_view to_string_view(anomaly_type t); - -inline auto format_as(anomaly_type t) { return to_string_view(t); } +fmt::iterator format_to(anomaly_type, fmt::iterator); struct metastore_anomaly { anomaly_type type; @@ -160,7 +160,6 @@ class partition_validator { }; std::string_view to_string_view(partition_validator::errc e); - -inline auto format_as(partition_validator::errc e) { return to_string_view(e); } +fmt::iterator format_to(partition_validator::errc, fmt::iterator); } // namespace cloud_topics::l1 diff --git a/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.cc b/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.cc index 66fb31c0d9dcb..8242634b5e3d0 100644 --- a/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.cc +++ b/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.cc @@ -528,8 +528,8 @@ bool level_zero_log_reader_impl::cache_enabled() const { return true; } -void level_zero_log_reader_impl::print(std::ostream& o) { - o << "cloud_topics_reader"; +fmt::iterator level_zero_log_reader_impl::format_to(fmt::iterator it) const { + return fmt::format_to(it, "cloud_topics_reader"); } void level_zero_log_reader_impl::register_with_stm(ctp_stm_api* api) { diff --git a/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.h b/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.h index e3dda19d7d467..d3d03adb637ca 100644 --- a/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.h +++ b/src/v/cloud_topics/level_zero/frontend_reader/level_zero_reader.h @@ -81,7 +81,7 @@ class level_zero_log_reader_impl : public model::record_batch_reader::impl { ss::future do_load_slice(model::timeout_clock::time_point) final; - void print(std::ostream& o) final; + fmt::iterator format_to(fmt::iterator it) const final; // Register this reader with the STM - this is needed so that L0 doesn't GC // any active data during reads. diff --git a/src/v/cloud_topics/level_zero/gc/level_zero_gc.cc b/src/v/cloud_topics/level_zero/gc/level_zero_gc.cc index 9eedccb7b2a27..149febf48994d 100644 --- a/src/v/cloud_topics/level_zero/gc/level_zero_gc.cc +++ b/src/v/cloud_topics/level_zero/gc/level_zero_gc.cc @@ -842,7 +842,9 @@ std::string_view to_string_view(state s) { vunreachable("Unrecognized GC state: {}", s); } -auto format_as(state s) { return to_string_view(s); } +fmt::iterator format_to(state s, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(s)); +} std::string_view to_string_view(collection_outcome::status s) { using enum collection_outcome::status; @@ -862,7 +864,9 @@ std::string_view to_string_view(collection_outcome::status s) { "Unrecognized collection_outcome::status: {}", static_cast(s)); } -auto format_as(collection_outcome::status s) { return to_string_view(s); } +fmt::iterator format_to(collection_outcome::status s, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(s)); +} fmt::iterator collection_outcome::format_to(fmt::iterator it) const { return fmt::format_to(it, "{{st={}, eligible={}}}", st, eligible_); diff --git a/src/v/cloud_topics/level_zero/gc/level_zero_gc_types.h b/src/v/cloud_topics/level_zero/gc/level_zero_gc_types.h index a74ba91b088d0..853609a9582a0 100644 --- a/src/v/cloud_topics/level_zero/gc/level_zero_gc_types.h +++ b/src/v/cloud_topics/level_zero/gc/level_zero_gc_types.h @@ -286,6 +286,8 @@ enum class state : uint8_t { }; std::string_view to_string_view(state s); +fmt::iterator format_to(state, fmt::iterator); std::string_view to_string_view(collection_outcome::status s); +fmt::iterator format_to(collection_outcome::status, fmt::iterator); } // namespace cloud_topics::l0::gc diff --git a/src/v/cloud_topics/level_zero/pipeline/BUILD b/src/v/cloud_topics/level_zero/pipeline/BUILD index e27c1e9bfc673..2c59141b615a7 100644 --- a/src/v/cloud_topics/level_zero/pipeline/BUILD +++ b/src/v/cloud_topics/level_zero/pipeline/BUILD @@ -61,6 +61,7 @@ redpanda_cc_library( deps = [ "//src/v/base", "//src/v/utils:named_type", + "@fmt", ], ) diff --git a/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.cc b/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.cc index 4ec14f2ac1a7c..9c4f12c898e0c 100644 --- a/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.cc +++ b/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.cc @@ -76,9 +76,3 @@ auto fmt::formatter::format( return formatter::format( fmt::format("pipeline_stage{{id:{}}}", o()->get_numeric_id()), ctx); } - -std::ostream& -operator<<(std::ostream& o, cloud_topics::l0::pipeline_stage stage) { - fmt::print(o, "{}", stage); - return o; -} diff --git a/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.h b/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.h index 4dd84e7627030..8f002cb6669d1 100644 --- a/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.h +++ b/src/v/cloud_topics/level_zero/pipeline/pipeline_stage.h @@ -12,6 +12,8 @@ #include "utils/named_type.h" +#include + #include namespace cloud_topics::l0 { @@ -71,6 +73,3 @@ struct fmt::formatter const cloud_topics::l0::pipeline_stage&, fmt::format_context& ctx) const -> decltype(ctx.out()); }; - -std::ostream& -operator<<(std::ostream& o, cloud_topics::l0::pipeline_stage stage); diff --git a/src/v/cloud_topics/level_zero/stm/ctp_stm_api.cc b/src/v/cloud_topics/level_zero/stm/ctp_stm_api.cc index 00afe7c1af0ac..8cacce168ea74 100644 --- a/src/v/cloud_topics/level_zero/stm/ctp_stm_api.cc +++ b/src/v/cloud_topics/level_zero/stm/ctp_stm_api.cc @@ -25,19 +25,6 @@ namespace cloud_topics { -std::ostream& operator<<(std::ostream& o, ctp_stm_api_errc errc) { - switch (errc) { - case ctp_stm_api_errc::timeout: - return o << "timeout"; - case ctp_stm_api_errc::not_leader: - return o << "not_leader"; - case ctp_stm_api_errc::shutdown: - return o << "shutdown"; - case ctp_stm_api_errc::failure: - return o << "failure"; - } -} - ctp_stm_api::ctp_stm_api(ss::shared_ptr stm) : _stm(std::move(stm)) , _log(_stm->log()) {} diff --git a/src/v/cloud_topics/level_zero/stm/ctp_stm_api.h b/src/v/cloud_topics/level_zero/stm/ctp_stm_api.h index 14597f04f0ec0..3d51a8de16c63 100644 --- a/src/v/cloud_topics/level_zero/stm/ctp_stm_api.h +++ b/src/v/cloud_topics/level_zero/stm/ctp_stm_api.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_topics/level_zero/common/producer_queue.h" #include "cloud_topics/level_zero/stm/types.h" #include "cloud_topics/types.h" @@ -20,7 +21,6 @@ #include #include -#include struct ctp_stm_api_accessor; class prefix_logger; @@ -36,7 +36,18 @@ enum class ctp_stm_api_errc : uint8_t { failure, }; -std::ostream& operator<<(std::ostream& o, ctp_stm_api_errc errc); +inline fmt::iterator format_to(ctp_stm_api_errc errc, fmt::iterator out) { + switch (errc) { + case ctp_stm_api_errc::timeout: + return fmt::format_to(out, "timeout"); + case ctp_stm_api_errc::not_leader: + return fmt::format_to(out, "not_leader"); + case ctp_stm_api_errc::shutdown: + return fmt::format_to(out, "shutdown"); + case ctp_stm_api_errc::failure: + return fmt::format_to(out, "failure"); + } +} class ctp_stm_api { friend struct ::ctp_stm_api_accessor; diff --git a/src/v/cloud_topics/tests/cluster_recovery_test.cc b/src/v/cloud_topics/tests/cluster_recovery_test.cc index 3ce06129b02a4..89287fa5eb443 100644 --- a/src/v/cloud_topics/tests/cluster_recovery_test.cc +++ b/src/v/cloud_topics/tests/cluster_recovery_test.cc @@ -47,11 +47,12 @@ struct test_params { bool unstable_controller{false}; bool unstable_metastore{false}; - friend std::ostream& operator<<(std::ostream& os, const test_params& p) { - return os << fmt::format( - "controller_{}_metastore_{}", - p.unstable_controller ? "unstable" : "stable", - p.unstable_metastore ? "unstable" : "stable"); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "controller_{}_metastore_{}", + unstable_controller ? "unstable" : "stable", + unstable_metastore ? "unstable" : "stable"); } }; diff --git a/src/v/cluster/BUILD b/src/v/cluster/BUILD index f878a53cf846c..9b909d973c4e4 100644 --- a/src/v/cluster/BUILD +++ b/src/v/cluster/BUILD @@ -163,6 +163,7 @@ redpanda_cc_library( ], visibility = ["//visibility:public"], deps = [ + "//src/v/base", "//src/v/cloud_storage:remote_label", "//src/v/model", "//src/v/pandaproxy/schema_registry:subject_name_strategy", diff --git a/src/v/cluster/archival/adjacent_segment_run.cc b/src/v/cluster/archival/adjacent_segment_run.cc index 1a8a82d234ac1..df26558c904ab 100644 --- a/src/v/cluster/archival/adjacent_segment_run.cc +++ b/src/v/cluster/archival/adjacent_segment_run.cc @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #include "cluster/archival/adjacent_segment_run.h" +#include "base/format_to.h" #include "base/vlog.h" #include "cloud_storage/partition_manifest.h" #include "cloud_storage/remote_path_provider.h" @@ -82,24 +83,22 @@ bool adjacent_segment_run::maybe_add_segment( } return false; } - -std::ostream& operator<<(std::ostream& os, const adjacent_segment_run& run) { +fmt::iterator adjacent_segment_run::format_to(fmt::iterator it) const { std::vector names; - names.reserve(run.segments.size()); + names.reserve(segments.size()); std::transform( - run.segments.begin(), - run.segments.end(), + segments.begin(), + segments.end(), std::back_inserter(names), [](const cloud_storage::remote_segment_path& rsp) { return rsp().native(); }); - fmt::print( - os, + return fmt::format_to( + it, "{{meta: {}, num_segments: {}, segments: {}}}", - run.meta, - run.num_segments, + meta, + num_segments, names); - return os; } } // namespace archival diff --git a/src/v/cluster/archival/adjacent_segment_run.h b/src/v/cluster/archival/adjacent_segment_run.h index d635f838ba2d8..ba2f14bc3f2af 100644 --- a/src/v/cluster/archival/adjacent_segment_run.h +++ b/src/v/cluster/archival/adjacent_segment_run.h @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "cloud_storage/fwd.h" #include "cloud_storage/types.h" #include "model/metadata.h" @@ -43,8 +44,8 @@ struct adjacent_segment_run { const cloud_storage::segment_meta& s, size_t max_size, const cloud_storage::remote_path_provider& path_provider); -}; -std::ostream& operator<<(std::ostream& o, const adjacent_segment_run& run); + fmt::iterator format_to(fmt::iterator it) const; +}; } // namespace archival diff --git a/src/v/cluster/archival/archiver_manager.cc b/src/v/cluster/archival/archiver_manager.cc index 5b45ffe47b031..62a7d645a2b01 100644 --- a/src/v/cluster/archival/archiver_manager.cc +++ b/src/v/cluster/archival/archiver_manager.cc @@ -10,6 +10,7 @@ #include "cluster/archival/archiver_manager.h" +#include "base/format_to.h" #include "cloud_io/cache_service.h" #include "cluster/archival/logger.h" #include "cluster/archival/ntp_archiver_service.h" @@ -37,7 +38,6 @@ #include #include #include -#include #include #include @@ -94,41 +94,34 @@ enum class managed_partition_event_t { // Shutdown initiated shutdown, }; - -std::ostream& operator<<(std::ostream& o, managed_partition_event_t e) { +fmt::iterator format_to(managed_partition_event_t e, fmt::iterator out) { switch (e) { case managed_partition_event_t::leadership_acquired: - return o << "leadership_acquired"; + return fmt::format_to(out, "leadership_acquired"); case managed_partition_event_t::leadership_lost: - return o << "leadership_lost"; + return fmt::format_to(out, "leadership_lost"); case managed_partition_event_t::archiver_started: - return o << "archiver_started"; + return fmt::format_to(out, "archiver_started"); case managed_partition_event_t::archiver_failure: - return o << "archiver_failure"; + return fmt::format_to(out, "archiver_failure"); case managed_partition_event_t::archiver_stopped: - return o << "archiver_stopped"; + return fmt::format_to(out, "archiver_stopped"); case managed_partition_event_t::shutdown: - return o << "shutdown"; + return fmt::format_to(out, "shutdown"); } } // This is a base class for all events that FSM can handle. // The events may trigger state transitions. template -struct managed_partition_event_base : auto_fmt {}; -} // namespace archival - -/// Automagically prints any event object derived from -/// managed_partition_event_base -template -struct fmt::formatter> { - template - auto format( - const archival::managed_partition_event_base& event, - FormatContext& ctx) const -> decltype(ctx.out()) { - return fmt::format_to(ctx.out(), "[{}..{}]", id, event); +struct managed_partition_event_base : auto_fmt { + fmt::iterator format_to(fmt::iterator it) const { + it = fmt::format_to(it, "[{}..", event); + it = auto_fmt::format_to(it); + return fmt::format_to(it, "]"); } }; +} // namespace archival namespace archival { std::ostream& @@ -143,17 +136,16 @@ enum class managed_partition_state_t { active, stopping, }; - -std::ostream& operator<<(std::ostream& o, managed_partition_state_t s) { - switch (s) { +fmt::iterator format_to(managed_partition_state_t e, fmt::iterator out) { + switch (e) { case managed_partition_state_t::passive: - return o << "passive"; + return fmt::format_to(out, "passive"); case managed_partition_state_t::active: - return o << "active"; + return fmt::format_to(out, "active"); case managed_partition_state_t::starting: - return o << "starting"; + return fmt::format_to(out, "starting"); case managed_partition_state_t::stopping: - return o << "stopping"; + return fmt::format_to(out, "stopping"); } } diff --git a/src/v/cluster/archival/async_data_uploader.h b/src/v/cluster/archival/async_data_uploader.h index 16a661d88b684..58fc93e84b84c 100644 --- a/src/v/cluster/archival/async_data_uploader.h +++ b/src/v/cluster/archival/async_data_uploader.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/vassert.h" #include "cluster/partition.h" #include "model/fundamental.h" @@ -63,6 +64,10 @@ struct inclusive_offset_range { auto cit = boost::make_counting_iterator(last() + 1); return boost::make_transform_iterator(cit, &detail::cast_to_offset); } + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "[{}-{}]", base, last); + } }; /// Representation of the inclusive monotonic offset range limited by @@ -83,11 +88,6 @@ struct size_limited_offset_range { bool operator<=>(const size_limited_offset_range&) const = default; }; -inline std::ostream& operator<<(std::ostream& o, inclusive_offset_range r) { - fmt::print(o, "[{}-{}]", r.base, r.last); - return o; -} - /// Result of the upload size calculation. /// Contains size of the region in bytes and locks. struct upload_reconciliation_result { @@ -238,3 +238,7 @@ class segment_upload { }; } // namespace archival + +template<> +struct fmt::range_format_kind + : std::integral_constant {}; diff --git a/src/v/cluster/archival/ntp_archiver_service.cc b/src/v/cluster/archival/ntp_archiver_service.cc index 979ef640c9e29..de0121fe05ae1 100644 --- a/src/v/cluster/archival/ntp_archiver_service.cc +++ b/src/v/cluster/archival/ntp_archiver_service.cc @@ -10,6 +10,7 @@ #include "cluster/archival/ntp_archiver_service.h" +#include "base/format_to.h" #include "base/vlog.h" #include "cloud_storage/async_manifest_view.h" #include "cloud_storage/partition_manifest.h" @@ -410,6 +411,10 @@ ntp_archiver::ntp_archiver( } } +fmt::iterator ntp_archiver::format_to(fmt::iterator it) const { + return fmt::format_to(it, ""); +} + archival_stm_fence ntp_archiver::emit_rw_fence() { return { .read_write_fence @@ -898,7 +903,7 @@ ss::future<> ntp_archiver::upload_topic_manifest() { _rtclog.debug, "Uploading topic manifest for {}, topic config {}", _parent.ntp(), - topic_cfg); + topic_cfg.get()); auto replication_factor = cluster::replication_factor( _parent.raft()->config().current_config().voters.size()); @@ -2702,48 +2707,39 @@ ntp_archiver::maybe_truncate_manifest() { } co_return result; } - -std::ostream& operator<<(std::ostream& os, segment_upload_kind upload_kind) { - switch (upload_kind) { +fmt::iterator format_to(segment_upload_kind e, fmt::iterator out) { + switch (e) { case segment_upload_kind::non_compacted: - fmt::print(os, "non-compacted"); - break; + return fmt::format_to(out, "non-compacted"); case segment_upload_kind::compacted: - fmt::print(os, "compacted"); - break; + return fmt::format_to(out, "compacted"); } - return os; + return out; } - -std::ostream& operator<<(std::ostream& os, flush_response fr) { - switch (fr) { +fmt::iterator format_to(flush_response e, fmt::iterator out) { + switch (e) { case flush_response::accepted: - fmt::print(os, "accepted"); - break; + return fmt::format_to(out, "accepted"); case flush_response::rejected: - fmt::print(os, "rejected"); - break; + return fmt::format_to(out, "rejected"); } - return os; + return out; } - -std::ostream& operator<<(std::ostream& os, flush_result fr) { - fmt::print(os, "response: {}, offset: {}", fr.response, fr.offset); - return os; +fmt::iterator flush_result::format_to(fmt::iterator it) const { + return fmt::format_to(it, "response: {}, offset: {}", response, offset); } - -std::ostream& operator<<(std::ostream& os, wait_result fr) { - switch (fr) { +fmt::iterator format_to(wait_result e, fmt::iterator out) { + switch (e) { case wait_result::not_in_progress: - return os << "not in progress"; + return fmt::format_to(out, "not in progress"); case wait_result::complete: - return os << "complete"; + return fmt::format_to(out, "complete"); case wait_result::lost_leadership: - return os << "lost leadership"; + return fmt::format_to(out, "lost leadership"); case wait_result::failed: - return os << "failed"; + return fmt::format_to(out, "failed"); } - return os; + return out; } ss::future ntp_archiver::housekeeping() { diff --git a/src/v/cluster/archival/ntp_archiver_service.h b/src/v/cluster/archival/ntp_archiver_service.h index 7f392fffa21c7..7af3fcc457c0c 100644 --- a/src/v/cluster/archival/ntp_archiver_service.h +++ b/src/v/cluster/archival/ntp_archiver_service.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "cloud_io/cache_service.h" #include "cloud_storage/fwd.h" #include "cloud_storage/partition_manifest.h" @@ -48,7 +49,7 @@ using namespace std::chrono_literals; enum class segment_upload_kind { compacted, non_compacted }; -std::ostream& operator<<(std::ostream& os, segment_upload_kind upload_kind); +fmt::iterator format_to(segment_upload_kind upload_kind, fmt::iterator); class ntp_archiver_upload_result { public: @@ -97,23 +98,23 @@ class ntp_archiver_upload_result { // re-dispatched to the partition leader by caller. enum class flush_response { rejected, accepted }; -std::ostream& operator<<(std::ostream& os, flush_response fr); +fmt::iterator format_to(flush_response fr, fmt::iterator); struct flush_result { flush_response response; // The inclusive offset which the archiver will flush() to, if response is // accepted. std::optional offset; -}; -std::ostream& operator<<(std::ostream& os, flush_result fr); + fmt::iterator format_to(fmt::iterator it) const; +}; // Indicates whether a flush is still in progress, if it is complete, or if // the flush needs to be retried (in case of a leadership change during // flush()) enum class wait_result { not_in_progress, complete, lost_leadership, failed }; -std::ostream& operator<<(std::ostream& os, wait_result wr); +fmt::iterator format_to(wait_result wr, fmt::iterator); /// Fence value for the archival STM. /// The value is used to implement optimistic @@ -170,6 +171,8 @@ class ntp_archiver { /// completed ss::future<> stop(); + fmt::iterator format_to(fmt::iterator it) const; + /// Get NTP const model::ntp& get_ntp() const; diff --git a/src/v/cluster/archival/segment_reupload.cc b/src/v/cluster/archival/segment_reupload.cc index cb7c85191f157..547e092222077 100644 --- a/src/v/cluster/archival/segment_reupload.cc +++ b/src/v/cluster/archival/segment_reupload.cc @@ -10,6 +10,7 @@ #include "segment_reupload.h" +#include "base/format_to.h" #include "base/vlog.h" #include "cloud_storage/partition_manifest.h" #include "cluster/archival/logger.h" @@ -46,88 +47,82 @@ bool eligible_for_compacted_reupload(const storage::segment& s) { } return s.has_self_compact_timestamp(); } - -std::ostream& operator<<(std::ostream& s, const upload_candidate& c) { +fmt::iterator upload_candidate::format_to(fmt::iterator it) const { vassert( - c.sources.empty() || c.remote_sources.empty(), + sources.empty() || remote_sources.empty(), "The upload candidate could have only local or only remote source"); - if (c.sources.empty() && c.remote_sources.empty()) { - s << "{empty}"; - return s; + if (sources.empty() && remote_sources.empty()) { + return fmt::format_to(it, "{{empty}}"); } std::vector source_names; - source_names.reserve(std::max(c.sources.size(), c.remote_sources.size())); - if (c.remote_sources.empty()) { + source_names.reserve(std::max(sources.size(), remote_sources.size())); + if (remote_sources.empty()) { std::transform( - c.sources.begin(), - c.sources.end(), + sources.begin(), + sources.end(), std::back_inserter(source_names), [](const auto& src) { return src->filename(); }); - } else if (c.sources.empty()) { + } else if (sources.empty()) { std::transform( - c.remote_sources.begin(), - c.remote_sources.end(), + remote_sources.begin(), + remote_sources.end(), std::back_inserter(source_names), [](const auto& src) { return src().native(); }); } - fmt::print( - s, + return fmt::format_to( + it, "{{source segment offsets: {}, exposed_name: {}, starting_offset: {}, " "file_offset: {}, content_length: {}, final_offset: {}, " "final_file_offset: {}, term: {}, source names: {}}}", - c.sources.front()->offsets(), - c.exposed_name, - c.starting_offset, - c.file_offset, - c.content_length, - c.final_offset, - c.final_file_offset, - c.term, + sources.front()->offsets(), + exposed_name, + starting_offset, + file_offset, + content_length, + final_offset, + final_file_offset, + term, source_names); - return s; } - -std::ostream& operator<<(std::ostream& s, const segment_collector_stream& c) { - if (c.size == 0) { - return s << "{empty}"; +fmt::iterator segment_collector_stream::format_to(fmt::iterator it) const { + if (size == 0) { + return fmt::format_to(it, "{{empty}}"); } - fmt::print( - s, + return fmt::format_to( + it, "{{starting_offset: {}, content_length: {}, final_offset: {}, term: {}}}", - c.start_offset, - c.size, - c.end_offset, - c.term); - return s; + start_offset, + size, + end_offset, + term); } - -std::ostream& operator<<(std::ostream& os, candidate_creation_error err) { - os << "candidate creation error: "; - switch (err) { +fmt::iterator format_to(candidate_creation_error e, fmt::iterator out) { + switch (e) { case candidate_creation_error::no_segments_collected: - return os << "no segments collected"; + return fmt::format_to(out, "no segments collected"); case candidate_creation_error::begin_offset_seek_error: - return os << "failed to seek begin offset"; + return fmt::format_to(out, "failed to seek begin offset"); case candidate_creation_error::end_offset_seek_error: - return os << "failed to seek end offset"; + return fmt::format_to(out, "failed to seek end offset"); case candidate_creation_error::offset_inside_batch: - return os << "offset inside batch"; + return fmt::format_to(out, "offset inside batch"); case candidate_creation_error::upload_size_unchanged: - return os << "size of candidate unchanged"; + return fmt::format_to(out, "size of candidate unchanged"); case candidate_creation_error::cannot_replace_manifest_entry: - return os << "candidate cannot replace manifest entry"; + return fmt::format_to(out, "candidate cannot replace manifest entry"); case candidate_creation_error::no_segment_for_begin_offset: - return os << "no segment for begin offset"; + return fmt::format_to(out, "no segment for begin offset"); case candidate_creation_error::missing_ntp_config: - return os << "missing config for NTP"; + return fmt::format_to(out, "missing config for NTP"); case candidate_creation_error::failed_to_get_file_range: - return os << "failed to get file range for candidate"; + return fmt::format_to(out, "failed to get file range for candidate"); case candidate_creation_error::zero_content_length: - return os << "candidate has no content"; + return fmt::format_to(out, "candidate has no content"); case candidate_creation_error::concurrency_error: - return os << "collected segments are modified concurrently"; + return fmt::format_to( + out, "collected segments are modified concurrently"); } } @@ -148,16 +143,13 @@ ss::log_level log_level_for_error(const candidate_creation_error& error) { return ss::log_level::warn; } } - -std::ostream& -operator<<(std::ostream& os, const skip_offset_range& skip_range) { - fmt::print( - os, +fmt::iterator skip_offset_range::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "skip_offset_range{{begin: {}, end: {}, error: {}}}", - skip_range.begin_offset, - skip_range.end_offset, - skip_range.reason); - return os; + begin_offset, + end_offset, + reason); } segment_collector::segment_collector( diff --git a/src/v/cluster/archival/segment_reupload.h b/src/v/cluster/archival/segment_reupload.h index 99b5c9709b8b3..3d12a0f5197f4 100644 --- a/src/v/cluster/archival/segment_reupload.h +++ b/src/v/cluster/archival/segment_reupload.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "cloud_storage/partition_manifest.h" #include "cloud_storage/types.h" @@ -50,8 +51,7 @@ enum class candidate_creation_error { zero_content_length, concurrency_error, }; - -std::ostream& operator<<(std::ostream&, candidate_creation_error); +fmt::iterator format_to(candidate_creation_error, fmt::iterator); ss::log_level log_level_for_error(const candidate_creation_error& error); @@ -68,7 +68,7 @@ struct upload_candidate { std::vector> sources; std::vector remote_sources; - friend std::ostream& operator<<(std::ostream& s, const upload_candidate& c); + fmt::iterator format_to(fmt::iterator it) const; }; struct upload_candidate_with_locks { @@ -83,7 +83,7 @@ struct skip_offset_range { model::offset end_offset; candidate_creation_error reason; - friend std::ostream& operator<<(std::ostream&, const skip_offset_range&); + fmt::iterator format_to(fmt::iterator it) const; }; using candidate_creation_result = std::variant< @@ -119,8 +119,7 @@ struct segment_collector_stream { model::term_id term; - friend std::ostream& - operator<<(std::ostream& s, const segment_collector_stream&); + fmt::iterator format_to(fmt::iterator it) const; }; using segment_collector_stream_result = std::variant< diff --git a/src/v/cluster/archival/tests/ntp_archiver_test.cc b/src/v/cluster/archival/tests/ntp_archiver_test.cc index e9493ba0cef93..483948aea1865 100644 --- a/src/v/cluster/archival/tests/ntp_archiver_test.cc +++ b/src/v/cluster/archival/tests/ntp_archiver_test.cc @@ -1400,9 +1400,9 @@ class counting_batch_consumer : public storage::batch_consumer { ss::future consume_batch_end() override { co_return stop_parser::no; } - void print(std::ostream& o) const override { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to( + it, "counting_batch_consumer, min_offset: {}, max_offset: {}, {} batches " "consumed", _stats.min_offset, diff --git a/src/v/cluster/archival/types.cc b/src/v/cluster/archival/types.cc index 53d4afafdbfe5..d3a279bdbf901 100644 --- a/src/v/cluster/archival/types.cc +++ b/src/v/cluster/archival/types.cc @@ -10,6 +10,7 @@ #include "cluster/archival/types.h" +#include "base/format_to.h" #include "cloud_storage/configuration.h" #include "cloud_storage/partition_manifest.h" #include "cloud_storage/types.h" @@ -19,13 +20,13 @@ #include #include +#include #include using namespace std::chrono_literals; namespace archival { - std::ostream& operator<<(std::ostream& o, const std::optional& tl) { if (tl) { @@ -38,19 +39,17 @@ operator<<(std::ostream& o, const std::optional& tl) { } return o; } - -std::ostream& operator<<(std::ostream& o, const configuration& cfg) { - fmt::print( - o, +fmt::iterator configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{bucket_name: {}, initial_backoff: {}, " "segment_upload_timeout: {}, " "manifest_upload_timeout: {}, time_limit: {}}}", - cfg.bucket_name, - cfg.cloud_storage_initial_backoff(), - cfg.segment_upload_timeout(), - cfg.manifest_upload_timeout(), - cfg.time_limit); - return o; + bucket_name, + cloud_storage_initial_backoff(), + segment_upload_timeout(), + manifest_upload_timeout(), + time_limit); } static ss::sstring get_value_or_throw( diff --git a/src/v/cluster/archival/types.h b/src/v/cluster/archival/types.h index 10311c95beb69..54c04dee90b08 100644 --- a/src/v/cluster/archival/types.h +++ b/src/v/cluster/archival/types.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "cloud_storage/types.h" #include "cluster/archival/adjacent_segment_run.h" @@ -66,7 +67,7 @@ struct configuration { ss::scheduling_group upload_scheduling_group{ ss::default_scheduling_group()}; - friend std::ostream& operator<<(std::ostream& o, const configuration& cfg); + fmt::iterator format_to(fmt::iterator it) const; }; /// \brief create scheduler service config diff --git a/src/v/cluster/archival/upload_housekeeping_service.cc b/src/v/cluster/archival/upload_housekeeping_service.cc index 5159f9162654f..fadf31122c32f 100644 --- a/src/v/cluster/archival/upload_housekeeping_service.cc +++ b/src/v/cluster/archival/upload_housekeeping_service.cc @@ -10,6 +10,7 @@ #include "cluster/archival/upload_housekeeping_service.h" +#include "base/format_to.h" #include "cloud_storage/remote.h" #include "cluster/archival/fwd.h" #include "cluster/archival/logger.h" @@ -30,26 +31,20 @@ using namespace std::chrono_literals; namespace archival { - -std::ostream& operator<<(std::ostream& o, housekeeping_state s) { - switch (s) { +fmt::iterator format_to(housekeeping_state e, fmt::iterator out) { + switch (e) { case housekeeping_state::idle: - o << "idle"; - break; + return fmt::format_to(out, "idle"); case housekeeping_state::active: - o << "active"; - break; + return fmt::format_to(out, "active"); case housekeeping_state::pause: - o << "pause"; - break; + return fmt::format_to(out, "pause"); case housekeeping_state::draining: - o << "draining"; - break; + return fmt::format_to(out, "draining"); case housekeeping_state::stopped: - o << "stopped"; - break; - }; - return o; + return fmt::format_to(out, "stopped"); + } + return fmt::format_to(out, ""); } upload_housekeeping_service::upload_housekeeping_service( diff --git a/src/v/cluster/archival/upload_housekeeping_service.h b/src/v/cluster/archival/upload_housekeeping_service.h index 71a2c4ffec4ea..2e59b279a86d6 100644 --- a/src/v/cluster/archival/upload_housekeeping_service.h +++ b/src/v/cluster/archival/upload_housekeeping_service.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "cloud_storage/remote.h" #include "cluster/archival/fwd.h" #include "cluster/archival/probe.h" @@ -48,7 +49,7 @@ enum class housekeeping_state { stopped, }; -std::ostream& operator<<(std::ostream& o, housekeeping_state s); +fmt::iterator format_to(housekeeping_state s, fmt::iterator); /// Controls housekeeping jobs /// diff --git a/src/v/cluster/bootstrap_types.h b/src/v/cluster/bootstrap_types.h index 75e8e0fddbdab..b6599437a2141 100644 --- a/src/v/cluster/bootstrap_types.h +++ b/src/v/cluster/bootstrap_types.h @@ -8,12 +8,11 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "cluster/types.h" #include "model/fundamental.h" #include "serde/envelope.h" -#include - #include namespace cluster { @@ -23,10 +22,8 @@ struct cluster_bootstrap_info_request cluster_bootstrap_info_request, serde::version<0>, serde::compat_version<0>> { - friend std::ostream& - operator<<(std::ostream& o, const cluster_bootstrap_info_request&) { - fmt::print(o, "{{}}"); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{}}"); } auto serde_fields() { return std::tie(); } @@ -53,20 +50,17 @@ struct cluster_bootstrap_info_reply cluster_uuid, node_uuid); } - - friend std::ostream& - operator<<(std::ostream& o, const cluster_bootstrap_info_reply& v) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{broker: {}, version: {}, seed_servers: {}, " "empty_seed_starts_cluster: {}, cluster_uuid: {}, node_uuid: {}}}", - v.broker, - v.version, - v.seed_servers, - v.empty_seed_starts_cluster, - v.cluster_uuid, - v.node_uuid); - return o; + broker, + version, + seed_servers, + empty_seed_starts_cluster, + cluster_uuid, + node_uuid); } }; diff --git a/src/v/cluster/client_quota_serde.cc b/src/v/cluster/client_quota_serde.cc index ea6279bb6db6a..e0cd4c7d5510f 100644 --- a/src/v/cluster/client_quota_serde.cc +++ b/src/v/cluster/client_quota_serde.cc @@ -10,6 +10,7 @@ #include "client_quota_serde.h" +#include "base/format_to.h" #include "serde/rw/envelope.h" #include "serde/rw/set.h" // IWYU pragma: keep #include "serde/rw/sstring.h" // IWYU pragma: keep @@ -23,84 +24,56 @@ #include namespace cluster::client_quota { - -std::ostream& -operator<<(std::ostream& os, const entity_key::part::client_id_default_match&) { - fmt::print(os, "client_id_default_match{{}}"); - return os; +fmt::iterator +entity_key::part::client_id_default_match::format_to(fmt::iterator it) const { + return fmt::format_to(it, "client_id_default_match{{}}"); } - -std::ostream& -operator<<(std::ostream& os, const entity_key::part::client_id_match& c) { - fmt::print(os, "client_id_match{{value:{}}}", c.value); - return os; +fmt::iterator +entity_key::part::client_id_match::format_to(fmt::iterator it) const { + return fmt::format_to(it, "client_id_match{{value:{}}}", value); } - -std::ostream& -operator<<(std::ostream& os, const entity_key::part::user_default_match&) { - fmt::print(os, "user_default_match{{}}"); - return os; +fmt::iterator +entity_key::part::user_default_match::format_to(fmt::iterator it) const { + return fmt::format_to(it, "user_default_match{{}}"); } - -std::ostream& -operator<<(std::ostream& os, const entity_key::part::user_match& u) { - fmt::print(os, "user_match{{value:{}}}", u.value); - return os; +fmt::iterator entity_key::part::user_match::format_to(fmt::iterator it) const { + return fmt::format_to(it, "user_match{{value:{}}}", value); } - -std::ostream& operator<<( - std::ostream& os, const entity_key::part::client_id_prefix_match& c) { - fmt::print(os, "client_id_prefix_match{{value:{}}}", c.value); - return os; +fmt::iterator +entity_key::part::client_id_prefix_match::format_to(fmt::iterator it) const { + return fmt::format_to(it, "client_id_prefix_match{{value:{}}}", value); } - -std::ostream& operator<<(std::ostream& os, const entity_key::part& part) { - fmt::print(os, "{}", part.part); - return os; +fmt::iterator entity_key::part::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", part); } - -std::ostream& operator<<(std::ostream& os, const entity_key::part_v0& part) { - fmt::print(os, "{}", part.part); - return os; +fmt::iterator entity_key::part_v0::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", part); } - -std::ostream& operator<<(std::ostream& os, const entity_key::part_t& part) { - fmt::print(os, "{}", part.part); - return os; +fmt::iterator entity_key::part_t::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", part); } - -std::ostream& operator<<(std::ostream& os, const entity_key& key) { - fmt::print(os, "{{parts: {}}}", key.parts); - return os; +fmt::iterator entity_key::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{parts: {}}}", parts); } - -std::ostream& operator<<(std::ostream& os, const entity_value& value) { - fmt::print( - os, +fmt::iterator entity_value::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{producer_byte_rate: {}, consumer_byte_rate: {}, " "controller_mutation_rate: {}}}", - value.producer_byte_rate, - value.consumer_byte_rate, - value.controller_mutation_rate); - return os; + producer_byte_rate, + consumer_byte_rate, + controller_mutation_rate); } - -std::ostream& -operator<<(std::ostream& os, const entity_value_diff::entry& entry) { - switch (entry.op) { +fmt::iterator entity_value_diff::entry::format_to(fmt::iterator it) const { + switch (op) { case entity_value_diff::operation::upsert: - fmt::print( - os, "upsert: {}={}", to_string_view(entry.type), entry.value); - return os; + return fmt::format_to(it, "upsert: {}={}", to_string_view(type), value); case entity_value_diff::operation::remove: - fmt::print(os, "remove: {}", to_string_view(entry.type)); - return os; + return fmt::format_to(it, "remove: {}", to_string_view(type)); } } - -std::ostream& operator<<(std::ostream& os, const entity_value_diff& value) { - fmt::print(os, "{}", value.entries); - return os; +fmt::iterator entity_value_diff::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", entries); } bool operator==( diff --git a/src/v/cluster/client_quota_serde.h b/src/v/cluster/client_quota_serde.h index 765317710fd8f..9da046b8a7d4b 100644 --- a/src/v/cluster/client_quota_serde.h +++ b/src/v/cluster/client_quota_serde.h @@ -10,6 +10,7 @@ #pragma once #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "cluster/errc.h" #include "model/timeout_clock.h" @@ -46,7 +47,7 @@ struct entity_key struct part : serde::envelope, serde::compat_version<0>> { friend bool operator==(const part&, const part&) = default; - friend std::ostream& operator<<(std::ostream&, const part&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const part& e) { @@ -66,8 +67,7 @@ struct entity_key auto serde_fields() { return std::tie(); } - friend std::ostream& - operator<<(std::ostream&, const client_id_default_match&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const client_id_default_match&) { @@ -88,8 +88,7 @@ struct entity_key auto serde_fields() { return std::tie(); } - friend std::ostream& - operator<<(std::ostream&, const user_default_match&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const user_default_match&) { @@ -108,8 +107,7 @@ struct entity_key friend bool operator==( const client_id_match&, const client_id_match&) = default; - friend std::ostream& - operator<<(std::ostream&, const client_id_match&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const client_id_match& c) { @@ -132,7 +130,7 @@ struct entity_key friend bool operator==(const user_match&, const user_match&) = default; - friend std::ostream& operator<<(std::ostream&, const user_match&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const user_match& u) { @@ -157,8 +155,7 @@ struct entity_key const client_id_prefix_match&, const client_id_prefix_match&) = default; - friend std::ostream& - operator<<(std::ostream&, const client_id_prefix_match&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const client_id_prefix_match& c) { @@ -194,7 +191,7 @@ struct entity_key struct part_v0 : serde::envelope, serde::compat_version<0>> { friend bool operator==(const part_v0&, const part_v0&) = default; - friend std::ostream& operator<<(std::ostream&, const part_v0&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const part_v0& e) { @@ -242,7 +239,7 @@ struct entity_key auto serde_fields() { return std::tie(parts); } friend bool operator==(const entity_key&, const entity_key&) = default; - friend std::ostream& operator<<(std::ostream&, const entity_key&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const entity_key& e) { @@ -265,7 +262,7 @@ struct entity_key : base{.part = std::forward(t)} {} friend bool operator==(const part_t&, const part_t&) = default; - friend std::ostream& operator<<(std::ostream&, const part_t&); + fmt::iterator format_to(fmt::iterator it) const; }; absl::flat_hash_set parts; @@ -284,7 +281,7 @@ void tag_invoke( struct entity_value : serde::envelope, serde::compat_version<0>> { friend bool operator==(const entity_value&, const entity_value&) = default; - friend std::ostream& operator<<(std::ostream&, const entity_value&); + fmt::iterator format_to(fmt::iterator it) const; bool is_empty() const { return !producer_byte_rate && !consumer_byte_rate @@ -328,7 +325,7 @@ struct entity_value_diff // Custom equality to match the hash function friend bool operator==(const entry&, const entry&); - friend std::ostream& operator<<(std::ostream&, const entry&); + fmt::iterator format_to(fmt::iterator it) const; constexpr auto serde_fields() { return std::tie(op, type, value); } @@ -349,7 +346,7 @@ struct entity_value_diff friend bool operator==(const entity_value_diff&, const entity_value_diff&) = default; - friend std::ostream& operator<<(std::ostream&, const entity_value_diff&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(entries); } @@ -376,9 +373,9 @@ struct alter_delta_cmd_data const alter_delta_cmd_data&, const alter_delta_cmd_data&) = default; }; +/// Note: the string values of these enums need to match the values used in +/// the kafka client quota handlers constexpr std::string_view to_string_view(entity_value_diff::key e) { - /// Note: the string values of these enums need to match the values used in - /// the kafka client quota handlers switch (e) { case entity_value_diff::key::producer_byte_rate: return "producer_byte_rate"; @@ -389,6 +386,10 @@ constexpr std::string_view to_string_view(entity_value_diff::key e) { } } +inline fmt::iterator format_to(entity_value_diff::key e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); +} + template std::enable_if_t, std::optional> from_string_view(std::string_view); diff --git a/src/v/cluster/cloud_metadata/cluster_manifest.cc b/src/v/cluster/cloud_metadata/cluster_manifest.cc index 7b7bb35cda194..c598d4b09835b 100644 --- a/src/v/cluster/cloud_metadata/cluster_manifest.cc +++ b/src/v/cluster/cloud_metadata/cluster_manifest.cc @@ -160,18 +160,18 @@ cluster_metadata_manifest::get_manifest_path() const { return cluster_manifest_key(cluster_uuid, metadata_id); } -std::ostream& operator<<(std::ostream& os, const cluster_metadata_manifest& m) { - os << fmt::format( +fmt::iterator cluster_metadata_manifest::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{upload_time_since_epoch: {}, cluster_uuid: {}, metadata_id: {}, " "controller_snapshot_offset: {}, controller_snapshot_path: '{}', " "offsets_snapshots_by_partition: '{}'}}", - m.upload_time_since_epoch, - m.cluster_uuid, - m.metadata_id, - m.controller_snapshot_offset, - m.controller_snapshot_path, - m.offsets_snapshots_by_partition); - return os; + upload_time_since_epoch, + cluster_uuid, + metadata_id, + controller_snapshot_offset, + controller_snapshot_path, + offsets_snapshots_by_partition); } } // namespace cluster::cloud_metadata diff --git a/src/v/cluster/cloud_metadata/cluster_manifest.h b/src/v/cluster/cloud_metadata/cluster_manifest.h index 5d3edb02d6409..00e5fcbf16a0c 100644 --- a/src/v/cluster/cloud_metadata/cluster_manifest.h +++ b/src/v/cluster/cloud_metadata/cluster_manifest.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include "cloud_storage/base_manifest.h" @@ -95,13 +96,13 @@ struct cluster_metadata_manifest other.offsets_snapshots_by_partition); } + fmt::iterator format_to(fmt::iterator it) const; + private: void load_from_json(const rapidjson::Document& doc); void to_json(std::ostream& out) const; }; -std::ostream& operator<<(std::ostream&, const cluster_metadata_manifest&); - using cluster_manifest_result = result; diff --git a/src/v/cluster/cloud_metadata/error_outcome.h b/src/v/cluster/cloud_metadata/error_outcome.h index cab235407b35c..5dc70a628d067 100644 --- a/src/v/cluster/cloud_metadata/error_outcome.h +++ b/src/v/cluster/cloud_metadata/error_outcome.h @@ -9,6 +9,8 @@ */ #pragma once +#include "base/format_to.h" + #include #include @@ -69,9 +71,30 @@ inline std::error_code make_error_code(error_outcome e) noexcept { return {static_cast(e), error_category()}; } -inline std::ostream& operator<<(std::ostream& o, error_outcome e) { - o << error_category().message(static_cast(e)); - return o; +inline fmt::iterator format_to(error_outcome e, fmt::iterator out) { + switch (e) { + case error_outcome::success: + return fmt::format_to(out, "Success"); + case error_outcome::list_failed: + return fmt::format_to(out, "List objects failed"); + case error_outcome::download_failed: + return fmt::format_to(out, "Download object failed"); + case error_outcome::upload_failed: + return fmt::format_to(out, "Upload object failed"); + case error_outcome::no_matching_metadata: + return fmt::format_to(out, "No matching metadata"); + case error_outcome::term_has_changed: + return fmt::format_to(out, "Term has changed"); + case error_outcome::not_ready: + return fmt::format_to(out, "Not ready"); + case error_outcome::ntp_not_found: + return fmt::format_to(out, "NTP not found"); + case error_outcome::rpc_error: + return fmt::format_to(out, "RPC error"); + case error_outcome::misconfiguration: + return fmt::format_to(out, "Cluster misconfiguration"); + } + return fmt::format_to(out, "Unknown outcome"); } } // namespace cluster::cloud_metadata diff --git a/src/v/cluster/cloud_metadata/offsets_recovery_rpc_types.h b/src/v/cluster/cloud_metadata/offsets_recovery_rpc_types.h index 139f71b98d025..a8d6c6c90b31e 100644 --- a/src/v/cluster/cloud_metadata/offsets_recovery_rpc_types.h +++ b/src/v/cluster/cloud_metadata/offsets_recovery_rpc_types.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "cluster/cloud_metadata/error_outcome.h" #include "cluster/errc.h" #include "model/fundamental.h" @@ -38,14 +39,13 @@ struct offsets_recovery_request operator==(const offsets_recovery_request&, const offsets_recovery_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const offsets_recovery_request& r) { - o << ssx::sformat( + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, bucket: {}, offsets_snapshot_paths: {}}}", - r.offsets_ntp, - r.bucket, - r.offsets_snapshot_paths); - return o; + offsets_ntp, + bucket, + offsets_snapshot_paths); } }; @@ -62,10 +62,8 @@ struct offsets_recovery_reply friend bool operator==( const offsets_recovery_reply&, const offsets_recovery_reply&) = default; - friend std::ostream& - operator<<(std::ostream& o, const offsets_recovery_reply& r) { - o << ssx::sformat("{{ec: {}}}", r.ec); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ec: {}}}", ec); } }; diff --git a/src/v/cluster/controller_backend.cc b/src/v/cluster/controller_backend.cc index d32e9914eec37..64ebe992c7233 100644 --- a/src/v/cluster/controller_backend.cc +++ b/src/v/cluster/controller_backend.cc @@ -12,6 +12,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "base/outcome.h" #include "base/vassert.h" #include "cloud_storage/remote_path_provider.h" @@ -246,20 +247,22 @@ struct controller_backend::ntp_reconciliation_state { cur_operation->assignment = std::move(p_as); } - friend std::ostream& - operator<<(std::ostream& o, const ntp_reconciliation_state& rs) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{pending_notifies: {}, properties_changed_at: {}, removed_at: {}, " "cur_operation: {}}}", - rs.pending_notifies, - rs.properties_changed_at, - rs.removed_at, - rs.cur_operation); - return o; + pending_notifies, + properties_changed_at, + removed_at, + cur_operation); } }; +} // namespace cluster + +namespace cluster { + controller_backend::controller_backend( ss::sharded& tp_state, ss::sharded& shard_placement, @@ -2147,20 +2150,18 @@ controller_backend::split_voters_learners_for_force_reconfiguration( command_revision); return std::make_pair(std::move(voters), std::move(learners)); } - -std::ostream& operator<<( - std::ostream& o, const controller_backend::in_progress_operation& op) { - fmt::print( - o, +fmt::iterator +controller_backend::in_progress_operation::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{revision: {}, type: {}, assignment: {}, retries: {}, " "last_error: {} ({})}}", - op.revision, - op.type, - op.assignment, - op.retries, - op.last_error, - std::error_code{op.last_error}.message()); - return o; + revision, + type, + assignment, + retries, + last_error, + std::error_code{last_error}.message()); } } // namespace cluster diff --git a/src/v/cluster/controller_backend.h b/src/v/cluster/controller_backend.h index 557ad3fe2ccf6..06bf5ebedc049 100644 --- a/src/v/cluster/controller_backend.h +++ b/src/v/cluster/controller_backend.h @@ -13,6 +13,7 @@ #include "absl/container/btree_map.h" #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "base/outcome.h" #include "cluster/errc.h" #include "cluster/fwd.h" @@ -232,8 +233,7 @@ class controller_backend uint64_t retries = 0; cluster::errc last_error = errc::success; - friend std::ostream& - operator<<(std::ostream&, const in_progress_operation&); + fmt::iterator format_to(fmt::iterator it) const; }; std::optional diff --git a/src/v/cluster/data_migration_backend.cc b/src/v/cluster/data_migration_backend.cc index 2089c5ef05f23..d4348c5ed3d38 100644 --- a/src/v/cluster/data_migration_backend.cc +++ b/src/v/cluster/data_migration_backend.cc @@ -10,6 +10,7 @@ */ #include "cluster/data_migration_backend.h" +#include "base/format_to.h" #include "cloud_storage/topic_manifest.h" #include "cloud_storage/topic_manifest_downloader.h" #include "cloud_storage/topic_mount_handler.h" @@ -2323,16 +2324,13 @@ void backend::topic_scoped_work_state::set_value(errc ec) { ss::future backend::topic_scoped_work_state::future() { return _promise.get_shared_future(); } - -std::ostream& -operator<<(std::ostream& os, const backend::replica_work_state& rws) { - fmt::print( - os, +fmt::iterator backend::replica_work_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{sought_state: {}, shard: {}, status: {}}}", - rws.sought_state, - rws.shard, - rws.status); - return os; + sought_state, + shard, + status); } chunked_vector diff --git a/src/v/cluster/data_migration_backend.h b/src/v/cluster/data_migration_backend.h index b89918918e7fe..e15f5826abf93 100644 --- a/src/v/cluster/data_migration_backend.h +++ b/src/v/cluster/data_migration_backend.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "cloud_storage/fwd.h" #include "cloud_storage/topic_manifest.h" #include "cloud_storage/topic_mount_handler.h" @@ -161,9 +162,9 @@ class backend { status); return *revision_id; } - }; - friend std::ostream& operator<<(std::ostream&, const replica_work_state&); + fmt::iterator format_to(fmt::iterator it) const; + }; struct topic_work_result { model::topic_namespace nt; diff --git a/src/v/cluster/data_migration_frontend.cc b/src/v/cluster/data_migration_frontend.cc index d34b7d69e5ce0..65d48aa7f9ef2 100644 --- a/src/v/cluster/data_migration_frontend.cc +++ b/src/v/cluster/data_migration_frontend.cc @@ -30,8 +30,6 @@ #include "ssx/single_sharded.h" #include "utils/retry_chain_node.h" -#include - #include namespace cluster::data_migrations { diff --git a/src/v/cluster/data_migration_router.cc b/src/v/cluster/data_migration_router.cc index 48049bb702a1e..8923ef0eb21b1 100644 --- a/src/v/cluster/data_migration_router.cc +++ b/src/v/cluster/data_migration_router.cc @@ -27,8 +27,6 @@ #include #include -#include - #include namespace cluster::data_migrations { diff --git a/src/v/cluster/data_migration_types.cc b/src/v/cluster/data_migration_types.cc index 3e0739c7d4984..13eae257d0500 100644 --- a/src/v/cluster/data_migration_types.cc +++ b/src/v/cluster/data_migration_types.cc @@ -10,6 +10,7 @@ */ #include "cluster/data_migration_types.h" +#include "base/format_to.h" #include "model/namespace.h" #include "ssx/sformat.h" #include "utils/to_string.h" @@ -60,117 +61,101 @@ outbound_migration outbound_migration::copy() const { .topic_locations = topic_locations.copy(), }; } - -std::ostream& operator<<(std::ostream& o, state state) { - switch (state) { +fmt::iterator format_to(state e, fmt::iterator out) { + switch (e) { case state::planned: - return o << "planned"; + return fmt::format_to(out, "planned"); case state::preparing: - return o << "preparing"; + return fmt::format_to(out, "preparing"); case state::prepared: - return o << "prepared"; + return fmt::format_to(out, "prepared"); case state::executing: - return o << "executing"; + return fmt::format_to(out, "executing"); case state::executed: - return o << "executed"; + return fmt::format_to(out, "executed"); case state::cut_over: - return o << "cut_over"; + return fmt::format_to(out, "cut_over"); case state::finished: - return o << "finished"; + return fmt::format_to(out, "finished"); case state::canceling: - return o << "canceling"; + return fmt::format_to(out, "canceling"); case state::cancelled: - return o << "cancelled"; + return fmt::format_to(out, "cancelled"); case state::deleted: - return o << "deleted"; + return fmt::format_to(out, "deleted"); } } - -std::ostream& operator<<(std::ostream& o, migrated_replica_status status) { - switch (status) { +fmt::iterator format_to(migrated_replica_status e, fmt::iterator out) { + switch (e) { case migrated_replica_status::waiting_for_rpc: - return o << "waiting_for_rpc"; + return fmt::format_to(out, "waiting_for_rpc"); case migrated_replica_status::waiting_for_controller_update: - return o << "waiting_for_controller_update"; + return fmt::format_to(out, "waiting_for_controller_update"); case migrated_replica_status::can_run: - return o << "can_run"; + return fmt::format_to(out, "can_run"); case migrated_replica_status::done: - return o << "done"; + return fmt::format_to(out, "done"); } } - -std::ostream& operator<<(std::ostream& o, migrated_resource_state state) { - switch (state) { +fmt::iterator format_to(migrated_resource_state e, fmt::iterator out) { + switch (e) { case migrated_resource_state::non_restricted: - return o << "non_restricted"; + return fmt::format_to(out, "non_restricted"); case migrated_resource_state::metadata_locked: - return o << "restricted"; + return fmt::format_to(out, "restricted"); case migrated_resource_state::read_only: - return o << "read_only"; + return fmt::format_to(out, "read_only"); case migrated_resource_state::create_only: - return o << "create_only"; + return fmt::format_to(out, "create_only"); case migrated_resource_state::fully_blocked: - return o << "fully_blocked"; + return fmt::format_to(out, "fully_blocked"); } } - -std::ostream& operator<<(std::ostream& o, const inbound_topic& topic) { - fmt::print( - o, +fmt::iterator inbound_topic::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{source_topic_name: {}, alias: {}, cloud_storage_location: {}}}", - topic.source_topic_name, - topic.alias, - topic.cloud_storage_location); - return o; + source_topic_name, + alias, + cloud_storage_location); } - -std::ostream& operator<<(std::ostream& o, const cloud_storage_location& l) { - fmt::print(o, "{{hint: {}}}", l.hint); - return o; +fmt::iterator cloud_storage_location::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{hint: {}}}", hint); } - -std::ostream& operator<<(std::ostream& o, const copy_target& t) { - fmt::print(o, "{{bucket: {}}}", t.bucket); - return o; +fmt::iterator copy_target::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{bucket: {}}}", bucket); } - -std::ostream& operator<<(std::ostream& o, const topic_location& tl) { - fmt::print( - o, "{{remote_topic: {}, location: {}}}", tl.remote_topic, tl.location); - return o; +fmt::iterator topic_location::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{remote_topic: {}, location: {}}}", remote_topic, location); } - -std::ostream& operator<<(std::ostream& o, const inbound_migration& dm) { - fmt::print( - o, +fmt::iterator inbound_migration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topics: {}, consumer_groups: {}, auto_advance: {}}}", - fmt::join(dm.topics, ", "), - fmt::join(dm.groups, ", "), - dm.auto_advance); - return o; + fmt::join(topics, ", "), + fmt::join(groups, ", "), + auto_advance); } - -std::ostream& operator<<(std::ostream& o, const outbound_migration& dm) { - fmt::print( - o, +fmt::iterator outbound_migration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topics: {}, consumer_groups: {}, copy_to: {}, auto_advance: {}, " "topic_locations: {}}}", - fmt::join(dm.topics, ", "), - fmt::join(dm.groups, ", "), - dm.copy_to, - dm.auto_advance, - fmt::join(dm.topic_locations, ", ")); - return o; -} - -std::ostream& operator<<(std::ostream& o, const topic_work& tw) { - fmt::print( - o, + fmt::join(topics, ", "), + fmt::join(groups, ", "), + copy_to, + auto_advance, + fmt::join(topic_locations, ", ")); +} +fmt::iterator topic_work::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{migration: {}, sought_state: {}, info: {}}}", - tw.migration_id, - tw.sought_state, + migration_id, + sought_state, ss::visit( - tw.info, + info, [&](const inbound_topic_work_info& itwi) { return ssx::sformat( "{{inbound; source: {}, cloud_storage_location: {}}}", @@ -180,112 +165,82 @@ std::ostream& operator<<(std::ostream& o, const topic_work& tw) { [&](const outbound_topic_work_info& otwi) { return ssx::sformat("{{outbound; copy_to: {}}}", otwi.copy_to); })); - return o; } - -std::ostream& operator<<(std::ostream& o, const migration_metadata& m) { - fmt::print( - o, +fmt::iterator migration_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, migration: {}, state: {}, created_timestamp: {}, " "completed_timestamp: {}, revision_id: {}}}", - m.id, - print_migration(m.migration), - m.state, - m.created_timestamp, - m.completed_timestamp, - m.revision_id); - return o; -} - -std::ostream& operator<<(std::ostream& o, const data_migration_ntp_state& r) { - fmt::print( - o, + id, + print_migration(migration), + state, + created_timestamp, + completed_timestamp, + revision_id); +} +fmt::iterator data_migration_ntp_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, migration: {}, sought_state: {}}}", - r.ntp, - r.migration, - r.state); - return o; + ntp, + migration, + state); } - -std::ostream& operator<<(std::ostream& o, const create_migration_cmd_data& d) { - fmt::print( - o, +fmt::iterator create_migration_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, migration: {}, op_timestamp: {}, " "fill_outbound_topic_locations: {}}}", - d.id, - print_migration(d.migration), - d.op_timestamp, - d.fill_outbound_topic_locations); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const update_migration_state_cmd_data& d) { - fmt::print( - o, + id, + print_migration(migration), + op_timestamp, + fill_outbound_topic_locations); +} +fmt::iterator +update_migration_state_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, requested_state: {}, op_timestamp: {}}}", - d.id, - d.requested_state, - d.op_timestamp); - return o; + id, + requested_state, + op_timestamp); } - -std::ostream& operator<<(std::ostream& o, const remove_migration_cmd_data& d) { - fmt::print(o, "{{id: {}}}", d.id); - return o; +fmt::iterator remove_migration_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{id: {}}}", id); } - -std::ostream& operator<<(std::ostream& o, const create_migration_request& r) { - fmt::print(o, "{{migration: {}}}", print_migration(r.migration)); - return o; +fmt::iterator create_migration_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{migration: {}}}", print_migration(migration)); } - -std::ostream& operator<<(std::ostream& o, const create_migration_reply& r) { - fmt::print(o, "{{id: {}, error_code: {}}}", r.id, r.ec); - return o; +fmt::iterator create_migration_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{id: {}, error_code: {}}}", id, ec); } - -std::ostream& -operator<<(std::ostream& o, const update_migration_state_request& r) { - fmt::print(o, "{{id: {}, state: {}}}", r.id, r.state); - return o; +fmt::iterator +update_migration_state_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{id: {}, state: {}}}", id, state); } - -std::ostream& -operator<<(std::ostream& o, const update_migration_state_reply& r) { - fmt::print(o, "{{error_code: {}}}", r.ec); - return o; +fmt::iterator update_migration_state_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{error_code: {}}}", ec); } - -std::ostream& operator<<(std::ostream& o, const remove_migration_request& r) { - fmt::print(o, "{{id: {}}}", r.id); - return o; +fmt::iterator remove_migration_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{id: {}}}", id); } - -std::ostream& operator<<(std::ostream& o, const remove_migration_reply& r) { - fmt::print(o, "{{error_code: {}}}", r.ec); - return o; +fmt::iterator remove_migration_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{error_code: {}}}", ec); } - -std::ostream& operator<<(std::ostream& o, const check_ntp_states_request& r) { - fmt::print(o, "{{sought_states: {}}}", r.sought_states); - return o; +fmt::iterator check_ntp_states_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{sought_states: {}}}", sought_states); } - -std::ostream& operator<<(std::ostream& o, const check_ntp_states_reply& r) { - fmt::print(o, "{{actual_states: {}}}", r.actual_states); - return o; +fmt::iterator check_ntp_states_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{actual_states: {}}}", actual_states); } - -std::ostream& operator<<(std::ostream& o, const entities_status& es) { - fmt::print( - o, +fmt::iterator entities_status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{groups: {}}}", fmt::join( std::views::transform( - es.groups, [](const group_offsets& g) { return g.group_id; }), + groups, [](const group_offsets& g) { return g.group_id; }), ", ")); - return o; } } // namespace cluster::data_migrations diff --git a/src/v/cluster/data_migration_types.h b/src/v/cluster/data_migration_types.h index ae0aca2d0d136..f5ee962f3a419 100644 --- a/src/v/cluster/data_migration_types.h +++ b/src/v/cluster/data_migration_types.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "cluster/errc.h" #include "cluster/offsets_snapshot.h" #include "container/chunked_vector.h" @@ -81,7 +82,7 @@ enum class state { cancelled, deleted // a migration cannot use it }; -std::ostream& operator<<(std::ostream& o, state); +fmt::iterator format_to(state s, fmt::iterator); /** * For each migration state transition that requires work on partitions @@ -100,7 +101,7 @@ enum class migrated_replica_status { can_run, done }; -std::ostream& operator<<(std::ostream& o, migrated_replica_status); +fmt::iterator format_to(migrated_replica_status s, fmt::iterator); /** * State of migrated resource i.e. either topic or consumer group, when resource @@ -115,7 +116,7 @@ enum class migrated_resource_state { fully_blocked }; -std::ostream& operator<<(std::ostream& o, migrated_resource_state); +fmt::iterator format_to(migrated_resource_state s, fmt::iterator); /** * All migration related services other than data_migrations_frontend are only @@ -132,16 +133,12 @@ struct cloud_storage_location cloud_storage_location, serde::version<0>, serde::compat_version<0>> { - friend std::ostream& - operator<<(std::ostream&, const cloud_storage_location&); - ss::sstring hint; friend bool operator==( const cloud_storage_location&, const cloud_storage_location&) = default; - friend std::ostream& - operator<<(std::ostream&, const cloud_storage_location&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(hint); } }; @@ -180,7 +177,7 @@ struct inbound_topic friend bool operator==(const inbound_topic&, const inbound_topic&) = default; - friend std::ostream& operator<<(std::ostream&, const inbound_topic&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -203,7 +200,7 @@ struct inbound_migration friend bool operator==(const inbound_migration&, const inbound_migration&) = default; - friend std::ostream& operator<<(std::ostream&, const inbound_migration&); + fmt::iterator format_to(fmt::iterator it) const; auto topic_nts() const { auto pieces = std::vector{std::as_const(topics) | std::views::all}; @@ -232,7 +229,7 @@ struct copy_target auto serde_fields() { return std::tie(bucket); } friend bool operator==(const copy_target&, const copy_target&) = default; - friend std::ostream& operator<<(std::ostream&, const copy_target&); + fmt::iterator format_to(fmt::iterator it) const; }; // A struct with information needed to unambiguously find topic data in cloud @@ -251,7 +248,7 @@ struct topic_location friend bool operator==(const topic_location&, const topic_location&) = default; - friend std::ostream& operator<<(std::ostream&, const topic_location&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -283,7 +280,7 @@ struct outbound_migration friend bool operator==(const outbound_migration&, const outbound_migration&) = default; - friend std::ostream& operator<<(std::ostream&, const outbound_migration&); + fmt::iterator format_to(fmt::iterator it) const; auto topic_nts() const { auto pieces = std::vector{std::as_const(topics) | std::views::all}; @@ -340,8 +337,9 @@ struct topic_work { id migration_id; state sought_state; topic_work_info info; + + fmt::iterator format_to(fmt::iterator it) const; }; -std::ostream& operator<<(std::ostream& o, const topic_work& tw); /** * Data migration metadata containing a migration definition, its id and current @@ -388,7 +386,7 @@ struct migration_metadata friend bool operator==(const migration_metadata&, const migration_metadata&) = default; - friend std::ostream& operator<<(std::ostream&, const migration_metadata&); + fmt::iterator format_to(fmt::iterator it) const; }; struct data_migration_ntp_state @@ -405,7 +403,7 @@ struct data_migration_ntp_state auto serde_fields() { return std::tie(ntp, migration, state); } friend bool operator==(const self&, const self&) = default; - friend std::ostream& operator<<(std::ostream&, const self&); + fmt::iterator format_to(fmt::iterator it) const; }; struct create_migration_cmd_data @@ -425,8 +423,7 @@ struct create_migration_cmd_data friend bool operator==( const create_migration_cmd_data&, const create_migration_cmd_data&) = default; - friend std::ostream& - operator<<(std::ostream&, const create_migration_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct update_migration_state_cmd_data @@ -442,8 +439,7 @@ struct update_migration_state_cmd_data friend bool operator==( const update_migration_state_cmd_data&, const update_migration_state_cmd_data&) = default; - friend std::ostream& - operator<<(std::ostream&, const update_migration_state_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct remove_migration_cmd_data @@ -457,8 +453,7 @@ struct remove_migration_cmd_data friend bool operator==( const remove_migration_cmd_data&, const remove_migration_cmd_data&) = default; - friend std::ostream& - operator<<(std::ostream&, const remove_migration_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct create_migration_request @@ -472,8 +467,7 @@ struct create_migration_request friend bool operator==(const create_migration_request&, const create_migration_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const create_migration_request&); + fmt::iterator format_to(fmt::iterator it) const; }; struct create_migration_reply : serde::envelope< @@ -487,8 +481,7 @@ struct create_migration_reply friend bool operator==( const create_migration_reply&, const create_migration_reply&) = default; - friend std::ostream& - operator<<(std::ostream&, const create_migration_reply&); + fmt::iterator format_to(fmt::iterator it) const; }; struct update_migration_state_request @@ -503,8 +496,7 @@ struct update_migration_state_request const update_migration_state_request&, const update_migration_state_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const update_migration_state_request&); + fmt::iterator format_to(fmt::iterator it) const; }; struct update_migration_state_reply @@ -519,8 +511,7 @@ struct update_migration_state_reply const update_migration_state_reply&, const update_migration_state_reply&) = default; - friend std::ostream& - operator<<(std::ostream&, const update_migration_state_reply&); + fmt::iterator format_to(fmt::iterator it) const; }; struct remove_migration_request : serde::envelope< @@ -532,8 +523,7 @@ struct remove_migration_request friend bool operator==(const remove_migration_request&, const remove_migration_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const remove_migration_request&); + fmt::iterator format_to(fmt::iterator it) const; }; struct remove_migration_reply @@ -548,8 +538,7 @@ struct remove_migration_reply friend bool operator==( const remove_migration_reply&, const remove_migration_reply&) = default; - friend std::ostream& - operator<<(std::ostream&, const remove_migration_reply&); + fmt::iterator format_to(fmt::iterator it) const; }; struct check_ntp_states_request @@ -565,7 +554,7 @@ struct check_ntp_states_request friend bool operator==(const self&, const self&) = default; - friend std::ostream& operator<<(std::ostream&, const self&); + fmt::iterator format_to(fmt::iterator it) const; }; struct check_ntp_states_reply @@ -581,13 +570,13 @@ struct check_ntp_states_reply friend bool operator==(const self&, const self&) = default; - friend std::ostream& operator<<(std::ostream&, const self&); + fmt::iterator format_to(fmt::iterator it) const; }; struct entities_status { chunked_vector groups; - friend std::ostream& operator<<(std::ostream&, const entities_status&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace cluster::data_migrations diff --git a/src/v/cluster/data_migration_worker.cc b/src/v/cluster/data_migration_worker.cc index 168324bbced4b..53ad410725f4d 100644 --- a/src/v/cluster/data_migration_worker.cc +++ b/src/v/cluster/data_migration_worker.cc @@ -32,8 +32,6 @@ #include #include -#include - #include #include #include diff --git a/src/v/cluster/drain_manager.cc b/src/v/cluster/drain_manager.cc index f4eb6fb8ece9c..101db9a57af8c 100644 --- a/src/v/cluster/drain_manager.cc +++ b/src/v/cluster/drain_manager.cc @@ -1,5 +1,6 @@ #include "cluster/drain_manager.h" +#include "base/format_to.h" #include "base/vlog.h" #include "cluster/controller_service.h" #include "cluster/logger.h" @@ -284,20 +285,17 @@ ss::future<> drain_manager::do_restore() { _partition_manager.local().unblock_new_leadership(); co_return; } - -std::ostream& -operator<<(std::ostream& os, const drain_manager::drain_status& ds) { - fmt::print( - os, +fmt::iterator drain_manager::drain_status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{finished: {}, errors: {}, partitions: {}, eligible: {}, transferring: " "{}, failed: {}}}", - ds.finished, - ds.errors, - ds.partitions, - ds.eligible, - ds.transferring, - ds.failed); - return os; + finished, + errors, + partitions, + eligible, + transferring, + failed); } } // namespace cluster diff --git a/src/v/cluster/drain_manager.h b/src/v/cluster/drain_manager.h index b26217e19fce8..ede82357e35ea 100644 --- a/src/v/cluster/drain_manager.h +++ b/src/v/cluster/drain_manager.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "cluster/fwd.h" #include "reflection/adl.h" @@ -55,7 +56,7 @@ class drain_manager : public ss::peering_sharded_service { std::optional transferring; std::optional failed; - friend std::ostream& operator<<(std::ostream&, const drain_status&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const drain_status&, const drain_status&) = default; diff --git a/src/v/cluster/errc.h b/src/v/cluster/errc.h index b410b82354b9e..4c8c49a25c23e 100644 --- a/src/v/cluster/errc.h +++ b/src/v/cluster/errc.h @@ -11,7 +11,9 @@ #pragma once #include "base/compiler_utils.h" +#include "base/format_to.h" +#include #include namespace cluster { @@ -104,7 +106,7 @@ enum class errc : int16_t { feature_sanctioned, }; -std::ostream& operator<<(std::ostream& o, errc err); +fmt::iterator format_to(errc err, fmt::iterator); struct errc_category final : public std::error_category { const char* name() const noexcept final { return "cluster::errc"; } diff --git a/src/v/cluster/errors.cc b/src/v/cluster/errors.cc index 32d8d94d1748d..d533eec447596 100644 --- a/src/v/cluster/errors.cc +++ b/src/v/cluster/errors.cc @@ -12,189 +12,215 @@ #include "base/compiler_utils.h" #include "cluster/errc.h" -#include - namespace cluster { -std::ostream& operator<<(std::ostream& o, cluster::errc err) { +fmt::iterator format_to(errc err, fmt::iterator out) { switch (err) { case errc::success: - return o << "cluster::errc::success"; + return fmt::format_to(out, "cluster::errc::success"); case errc::notification_wait_timeout: - return o << "cluster::errc::notification_wait_timeout"; + return fmt::format_to(out, "cluster::errc::notification_wait_timeout"); case errc::topic_invalid_partitions: - return o << "cluster::errc::topic_invalid_partitions"; + return fmt::format_to(out, "cluster::errc::topic_invalid_partitions"); case errc::topic_invalid_replication_factor: - return o << "cluster::errc::topic_invalid_replication_factor"; + return fmt::format_to( + out, "cluster::errc::topic_invalid_replication_factor"); case errc::topic_invalid_config: - return o << "cluster::errc::topic_invalid_config"; + return fmt::format_to(out, "cluster::errc::topic_invalid_config"); case errc::not_leader_controller: - return o << "cluster::errc::not_leader_controller"; + return fmt::format_to(out, "cluster::errc::not_leader_controller"); case errc::topic_already_exists: - return o << "cluster::errc::topic_already_exists"; + return fmt::format_to(out, "cluster::errc::topic_already_exists"); case errc::replication_error: - return o << "cluster::errc::replication_error"; + return fmt::format_to(out, "cluster::errc::replication_error"); case errc::shutting_down: - return o << "cluster::errc::shutting_down"; + return fmt::format_to(out, "cluster::errc::shutting_down"); case errc::no_leader_controller: - return o << "cluster::errc::no_leader_controller"; + return fmt::format_to(out, "cluster::errc::no_leader_controller"); case errc::join_request_dispatch_error: - return o << "cluster::errc::join_request_dispatch_error"; + return fmt::format_to( + out, "cluster::errc::join_request_dispatch_error"); case errc::seed_servers_exhausted: - return o << "cluster::errc::seed_servers_exhausted"; + return fmt::format_to(out, "cluster::errc::seed_servers_exhausted"); case errc::auto_create_topics_exception: - return o << "cluster::errc::auto_create_topics_exception"; + return fmt::format_to( + out, "cluster::errc::auto_create_topics_exception"); case errc::timeout: - return o << "cluster::errc::timeout"; + return fmt::format_to(out, "cluster::errc::timeout"); case errc::topic_not_exists: - return o << "cluster::errc::topic_not_exists"; + return fmt::format_to(out, "cluster::errc::topic_not_exists"); case errc::invalid_topic_name: - return o << "cluster::errc::invalid_topic_name"; + return fmt::format_to(out, "cluster::errc::invalid_topic_name"); case errc::partition_not_exists: - return o << "cluster::errc::partition_not_exists"; + return fmt::format_to(out, "cluster::errc::partition_not_exists"); case errc::not_leader: - return o << "cluster::errc::not_leader"; + return fmt::format_to(out, "cluster::errc::not_leader"); case errc::partition_already_exists: - return o << "cluster::errc::partition_already_exists"; + return fmt::format_to(out, "cluster::errc::partition_already_exists"); case errc::waiting_for_recovery: - return o << "cluster::errc::waiting_for_recovery"; + return fmt::format_to(out, "cluster::errc::waiting_for_recovery"); case errc::waiting_for_reconfiguration_finish: - return o << "cluster::errc::waiting_for_reconfiguration_finish"; + return fmt::format_to( + out, "cluster::errc::waiting_for_reconfiguration_finish"); case errc::update_in_progress: - return o << "cluster::errc::update_in_progress"; + return fmt::format_to(out, "cluster::errc::update_in_progress"); case errc::user_exists: - return o << "cluster::errc::user_exists"; + return fmt::format_to(out, "cluster::errc::user_exists"); case errc::user_does_not_exist: - return o << "cluster::errc::user_does_not_exist"; + return fmt::format_to(out, "cluster::errc::user_does_not_exist"); case errc::invalid_producer_epoch: - return o << "cluster::errc::invalid_producer_epoch"; + return fmt::format_to(out, "cluster::errc::invalid_producer_epoch"); case errc::sequence_out_of_order: - return o << "cluster::errc::sequence_out_of_order"; + return fmt::format_to(out, "cluster::errc::sequence_out_of_order"); case errc::generic_tx_error: - return o << "cluster::errc::generic_tx_error"; + return fmt::format_to(out, "cluster::errc::generic_tx_error"); case errc::node_does_not_exists: - return o << "cluster::errc::node_does_not_exists"; + return fmt::format_to(out, "cluster::errc::node_does_not_exists"); case errc::invalid_node_operation: - return o << "cluster::errc::invalid_node_operation"; + return fmt::format_to(out, "cluster::errc::invalid_node_operation"); case errc::invalid_configuration_update: - return o << "cluster::errc::invalid_configuration_update"; + return fmt::format_to( + out, "cluster::errc::invalid_configuration_update"); case errc::topic_operation_error: - return o << "cluster::errc::topic_operation_error"; + return fmt::format_to(out, "cluster::errc::topic_operation_error"); case errc::no_eligible_allocation_nodes: - return o << "cluster::errc::no_eligible_allocation_nodes"; + return fmt::format_to( + out, "cluster::errc::no_eligible_allocation_nodes"); case errc::allocation_error: - return o << "cluster::errc::allocation_error"; + return fmt::format_to(out, "cluster::errc::allocation_error"); case errc::partition_configuration_revision_not_updated: - return o - << "cluster::errc::partition_configuration_revision_not_updated"; + return fmt::format_to( + out, "cluster::errc::partition_configuration_revision_not_updated"); case errc::partition_configuration_in_joint_mode: - return o << "cluster::errc::partition_configuration_in_joint_mode"; + return fmt::format_to( + out, "cluster::errc::partition_configuration_in_joint_mode"); case errc::partition_configuration_leader_config_not_committed: - return o << "cluster::errc::partition_configuration_leader_config_not_" - "committed"; + return fmt::format_to( + out, + "cluster::errc::partition_configuration_leader_config_not_committed"); case errc::partition_configuration_differs: - return o << "cluster::errc::partition_configuration_differs"; + return fmt::format_to( + out, "cluster::errc::partition_configuration_differs"); case errc::data_policy_already_exists: - return o << "cluster::errc::data_policy_already_exists"; + return fmt::format_to(out, "cluster::errc::data_policy_already_exists"); case errc::data_policy_not_exists: - return o << "cluster::errc::data_policy_not_exists"; + return fmt::format_to(out, "cluster::errc::data_policy_not_exists"); case errc::source_topic_not_exists: - return o << "cluster::errc::source_topic_not_exists"; + return fmt::format_to(out, "cluster::errc::source_topic_not_exists"); case errc::source_topic_still_in_use: - return o << "cluster::errc::source_topic_still_in_use"; + return fmt::format_to(out, "cluster::errc::source_topic_still_in_use"); case errc::waiting_for_partition_shutdown: - return o << "cluster::errc::waiting_for_partition_shutdown"; + return fmt::format_to( + out, "cluster::errc::waiting_for_partition_shutdown"); case errc::error_collecting_health_report: - return o << "cluster::errc::error_collecting_health_report"; + return fmt::format_to( + out, "cluster::errc::error_collecting_health_report"); case errc::leadership_changed: - return o << "cluster::errc::leadership_changed"; + return fmt::format_to(out, "cluster::errc::leadership_changed"); case errc::feature_disabled: - return o << "cluster::errc::feature_disabled"; + return fmt::format_to(out, "cluster::errc::feature_disabled"); case errc::invalid_request: - return o << "cluster::errc::invalid_request"; + return fmt::format_to(out, "cluster::errc::invalid_request"); case errc::no_update_in_progress: - return o << "cluster::errc::no_update_in_progress"; + return fmt::format_to(out, "cluster::errc::no_update_in_progress"); case errc::unknown_update_interruption_error: - return o << "cluster::errc::unknown_update_interruption_error"; + return fmt::format_to( + out, "cluster::errc::unknown_update_interruption_error"); case errc::throttling_quota_exceeded: - return o << "cluster::errc::throttling_quota_exceeded"; + return fmt::format_to(out, "cluster::errc::throttling_quota_exceeded"); case errc::cluster_already_exists: - return o << "cluster::errc::cluster_already_exists"; + return fmt::format_to(out, "cluster::errc::cluster_already_exists"); case errc::no_partition_assignments: - return o << "cluster::errc::no_partition_assignments"; + return fmt::format_to(out, "cluster::errc::no_partition_assignments"); case errc::failed_to_create_partition: - return o << "cluster::errc::failed_to_create_partition"; + return fmt::format_to(out, "cluster::errc::failed_to_create_partition"); case errc::partition_operation_failed: - return o << "cluster::errc::partition_operation_failed"; + return fmt::format_to(out, "cluster::errc::partition_operation_failed"); case errc::transform_does_not_exist: - return o << "cluster::errc::transform_does_not_exist"; + return fmt::format_to(out, "cluster::errc::transform_does_not_exist"); case errc::transform_invalid_update: - return o << "cluster::errc::transform_invalid_update"; + return fmt::format_to(out, "cluster::errc::transform_invalid_update"); case errc::transform_invalid_create: - return o << "cluster::errc::transform_invalid_create"; + return fmt::format_to(out, "cluster::errc::transform_invalid_create"); case errc::transform_invalid_source: - return o << "cluster::errc::transform_invalid_source"; + return fmt::format_to(out, "cluster::errc::transform_invalid_source"); case errc::transform_invalid_environment: - return o << "cluster::errc::transform_invalid_environment"; + return fmt::format_to( + out, "cluster::errc::transform_invalid_environment"); case errc::trackable_keys_limit_exceeded: - return o << "cluster::errc::trackable_keys_limit_exceeded"; + return fmt::format_to( + out, "cluster::errc::trackable_keys_limit_exceeded"); case errc::topic_disabled: - return o << "cluster::errc::topic_disabled"; + return fmt::format_to(out, "cluster::errc::topic_disabled"); case errc::partition_disabled: - return o << "cluster::errc::partition_disabled"; + return fmt::format_to(out, "cluster::errc::partition_disabled"); case errc::invalid_partition_operation: - return o << "cluster::errc::invalid_partition_operation"; + return fmt::format_to( + out, "cluster::errc::invalid_partition_operation"); case errc::concurrent_modification_error: - return o << "cluster::errc::concurrent_modification_error"; + return fmt::format_to( + out, "cluster::errc::concurrent_modification_error"); case errc::transform_count_limit_exceeded: - return o << "cluster::errc::transform_count_limit_exceeded"; + return fmt::format_to( + out, "cluster::errc::transform_count_limit_exceeded"); case errc::role_exists: - return o << "cluster::errc::role_exists"; + return fmt::format_to(out, "cluster::errc::role_exists"); case errc::role_does_not_exist: - return o << "cluster::errc::role_does_not_exist"; + return fmt::format_to(out, "cluster::errc::role_does_not_exist"); case errc::waiting_for_shard_placement_update: - return o << "cluster::errc::waiting_for_shard_placement_update"; + return fmt::format_to( + out, "cluster::errc::waiting_for_shard_placement_update"); case errc::topic_invalid_partitions_core_limit: - return o << "cluster::errc::topic_invalid_partitions_core_limit"; + return fmt::format_to( + out, "cluster::errc::topic_invalid_partitions_core_limit"); case errc::topic_invalid_partitions_memory_limit: - return o << "cluster::errc::topic_invalid_partitions_memory_limit"; + return fmt::format_to( + out, "cluster::errc::topic_invalid_partitions_memory_limit"); case errc::topic_invalid_partitions_fd_limit: - return o << "cluster::errc::topic_invalid_partitions_fd_limit"; + return fmt::format_to( + out, "cluster::errc::topic_invalid_partitions_fd_limit"); case errc::topic_invalid_partitions_decreased: - return o << "cluster::errc::topic_invalid_partitions_decreased"; + return fmt::format_to( + out, "cluster::errc::topic_invalid_partitions_decreased"); case errc::producer_ids_vcluster_limit_exceeded: - return o << "cluster::errc::producer_ids_vcluster_limit_exceeded"; + return fmt::format_to( + out, "cluster::errc::producer_ids_vcluster_limit_exceeded"); case errc::validation_of_recovery_topic_failed: - return o << "cluster::errc::validation_of_recovery_topic_failed"; + return fmt::format_to( + out, "cluster::errc::validation_of_recovery_topic_failed"); case errc::replica_does_not_exist: - return o << "cluster::errc::replica_does_not_exist"; + return fmt::format_to(out, "cluster::errc::replica_does_not_exist"); case errc::invalid_data_migration_state: - return o << "cluster::errc::invalid_data_migration_state"; + return fmt::format_to( + out, "cluster::errc::invalid_data_migration_state"); case errc::data_migration_not_exists: - return o << "cluster::errc::data_migration_not_exists"; + return fmt::format_to(out, "cluster::errc::data_migration_not_exists"); case errc::data_migration_already_exists: - return o << "cluster::errc::data_migration_already_exists"; + return fmt::format_to( + out, "cluster::errc::data_migration_already_exists"); case errc::data_migration_invalid_resources: - return o << "cluster::errc::data_migration_invalid_resources"; + return fmt::format_to( + out, "cluster::errc::data_migration_invalid_resources"); case errc::data_migration_invalid_definition: - return o << "cluster::errc::data_migration_invalid_definition"; + return fmt::format_to( + out, "cluster::errc::data_migration_invalid_definition"); case errc::data_migrations_disabled: - return o << "cluster::errc::data_migrations_disabled"; + return fmt::format_to(out, "cluster::errc::data_migrations_disabled"); case errc::resource_is_being_migrated: - return o << "cluster::errc::resource_is_being_migrated"; + return fmt::format_to(out, "cluster::errc::resource_is_being_migrated"); case errc::invalid_target_node_id: - return o << "cluster::errc::invalid_target_node_id"; + return fmt::format_to(out, "cluster::errc::invalid_target_node_id"); case errc::topic_id_already_exists: - return o << "cluster::errc::topic_id_already_exists"; + return fmt::format_to(out, "cluster::errc::topic_id_already_exists"); case errc::feature_sanctioned: - return o << "cluster::errc::feature_sanctioned"; + return fmt::format_to(out, "cluster::errc::feature_sanctioned"); REDPANDA_BEGIN_IGNORE_DEPRECATIONS case errc::inconsistent_stm_update: - return o << "cluster::errc::inconsistent_stm_update (deprecated)"; + return fmt::format_to( + out, "cluster::errc::inconsistent_stm_update (deprecated)"); REDPANDA_END_IGNORE_DEPRECATIONS } - // fallback path: we don't expect it, but this is better than just falling - // off the end without returning anything - return o << "cluster::errc::unknown(" << static_cast(err) << ")"; + return fmt::format_to( + out, "cluster::errc::unknown({})", static_cast(err)); } } // namespace cluster diff --git a/src/v/cluster/feature_update_action.h b/src/v/cluster/feature_update_action.h index 42a68e39528d1..33d72632080fb 100644 --- a/src/v/cluster/feature_update_action.h +++ b/src/v/cluster/feature_update_action.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "serde/rw/enum.h" #include "serde/rw/envelope.h" #include "serde/rw/sstring.h" @@ -46,8 +47,10 @@ struct feature_update_action auto serde_fields() { return std::tie(feature_name, action); } - friend std::ostream& - operator<<(std::ostream&, const feature_update_action&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{feature_name: {}, action: {}}}", feature_name, (int)action); + } }; } // namespace cluster diff --git a/src/v/cluster/health_monitor_backend.cc b/src/v/cluster/health_monitor_backend.cc index 19e2f5ee29daf..3ebed9a05823d 100644 --- a/src/v/cluster/health_monitor_backend.cc +++ b/src/v/cluster/health_monitor_backend.cc @@ -12,6 +12,7 @@ #include "absl/container/node_hash_map.h" #include "absl/container/node_hash_set.h" +#include "base/format_to.h" #include "cloud_topics/level_zero/stm/ctp_stm.h" #include "cluster/cloud_storage_size_reducer.h" #include "cluster/controller_service.h" @@ -324,7 +325,7 @@ struct partition_risk { return x; } constexpr explicit operator bool() const; - friend std::ostream& operator<<(std::ostream&, const partition_risk&); + fmt::iterator format_to(fmt::iterator it) const; }; struct partition_risk::c { @@ -335,24 +336,22 @@ struct partition_risk::c { constexpr partition_risk::operator bool() const { return *this != partition_risk::c::no_risk; } - -std::ostream& operator<<(std::ostream& o, const partition_risk& r) { +fmt::iterator partition_risk::format_to(fmt::iterator it) const { std::vector parts; - if (r & cluster::partition_risk::c::rf1_offline) { + if (*this & cluster::partition_risk::c::rf1_offline) { parts.emplace_back("rf1_offline"); } - if (r & cluster::partition_risk::c::full_acks_produce_unavailable) { + if (*this & cluster::partition_risk::c::full_acks_produce_unavailable) { parts.emplace_back("full_acks_produce_unavailable"); } - if (r & cluster::partition_risk::c::unavailable) { + if (*this & cluster::partition_risk::c::unavailable) { parts.emplace_back("unavailable"); } - if (r & cluster::partition_risk::c::acks1_data_loss) { + if (*this & cluster::partition_risk::c::acks1_data_loss) { parts.emplace_back("acks1_data_loss"); } - fmt::print(o, "{{{}}}", fmt::join(parts, ", ")); - return o; + return fmt::format_to(it, "{{{}}}", fmt::join(parts, ", ")); } void record_risks_in_report( diff --git a/src/v/cluster/health_monitor_types.cc b/src/v/cluster/health_monitor_types.cc index 2370323944bf3..3921d04ecd96a 100644 --- a/src/v/cluster/health_monitor_types.cc +++ b/src/v/cluster/health_monitor_types.cc @@ -10,6 +10,7 @@ */ #include "cluster/health_monitor_types.h" +#include "base/format_to.h" #include "cluster/drain_manager.h" #include "cluster/errc.h" #include "cluster/node/types.h" @@ -24,7 +25,6 @@ #include #include -#include #include #include @@ -66,19 +66,16 @@ node_state::node_state( : _id(id) , _membership_state(membership_state) , _is_alive(is_alive) {} - -std::ostream& operator<<(std::ostream& o, const node_state& s) { - fmt::print( - o, +fmt::iterator node_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{membership_state: {}, is_alive: {}}}", - s._membership_state, - s._is_alive); - return o; + _membership_state, + _is_alive); } - -std::ostream& operator<<(std::ostream& o, const node_liveness_report& nls) { - fmt::print(o, "{{node_id_to_last_seen: {}}}", nls.node_id_to_last_seen); - return o; +fmt::iterator node_liveness_report::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{node_id_to_last_seen: {}}}", node_id_to_last_seen); } bool operator==(const node_liveness_report& a, const node_liveness_report& b) { @@ -112,8 +109,8 @@ node_health_report node_health_report::copy() const { return ret; } -std::ostream& operator<<(std::ostream& o, const node_health_report& r) { - return o << node_health_report_serde{r}; +fmt::iterator node_health_report::format_to(fmt::iterator it) const { + return node_health_report_serde{*this}.format_to(it); } node_health_report_serde::node_health_report_serde(const node_health_report& hr) @@ -168,18 +165,16 @@ partition_statuses_map_t copy_to_map(const partition_statuses_t& ps_vec) { } return ret; } - -std::ostream& operator<<(std::ostream& o, const node_health_report_serde& r) { - fmt::print( - o, +fmt::iterator node_health_report_serde::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, topics: {}, local_state: {}, drain_status: {}, " "node_liveness_report {}}}", - r.id, - r.topics, - r.local_state, - r.drain_status, - r.node_liveness_report); - return o; + id, + topics, + local_state, + drain_status, + node_liveness_report); } bool operator==( @@ -194,59 +189,52 @@ bool operator==( b.topics.cend()) && a.node_liveness_report == b.node_liveness_report; } - -std::ostream& operator<<(std::ostream& o, const cluster_health_report& r) { - fmt::print( - o, - "{{raft0_leader: {}, node_states: {}, node_reports: {}, " +fmt::iterator cluster_health_report::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{raft0_leader: {}, node_states: {}, node_reports_count: {}, " "bytes_in_cloud_storage: {} }}", - r.raft0_leader, - r.node_states, - r.node_reports, - r.bytes_in_cloud_storage); - return o; + raft0_leader, + node_states, + node_reports.size(), + bytes_in_cloud_storage); } - -std::ostream& operator<<(std::ostream& o, follower_status status) { - switch (status) { +fmt::iterator format_to(follower_status e, fmt::iterator out) { + switch (e) { case follower_status::in_sync: - return o << "in_sync"; + return fmt::format_to(out, "in_sync"); case follower_status::out_of_sync: - return o << "out_of_sync"; + return fmt::format_to(out, "out_of_sync"); case follower_status::down: - return o << "down"; + return fmt::format_to(out, "down"); } } - -std::ostream& operator<<(std::ostream& o, const followers_stats& ls) { - fmt::print( - o, +fmt::iterator followers_stats::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{in_sync: {}, out_of_sync: {}, down: " "{}}}", - ls.in_sync, - ls.out_of_sync, - ls.down); - return o; + in_sync, + out_of_sync, + down); } - -std::ostream& operator<<(std::ostream& o, const partition_status& ps) { - fmt::print( - o, +fmt::iterator partition_status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, term: {}, leader_id: {}, revision_id: {}, size_bytes: {}, " "reclaimable_size_bytes: {}, under_replicated: {}, shard: {}, " "followers_stats: {}, kafka_highwatermark: {}, ct_max_gc_epoch: {}}}", - ps.id, - ps.term, - ps.leader_id, - ps.revision_id, - ps.size_bytes, - ps.reclaimable_size_bytes, - ps.under_replicated_replicas, - ps.shard, - ps.followers_stats, - ps.high_watermark, - ps.cloud_topic_max_gc_eligible_epoch); - return o; + id, + term, + leader_id, + revision_id, + size_bytes, + reclaimable_size_bytes, + under_replicated_replicas, + shard, + followers_stats, + high_watermark, + cloud_topic_max_gc_eligible_epoch); } topic_status& topic_status::operator=(const topic_status& rhs) { @@ -304,73 +292,58 @@ get_cluster_health_reply get_cluster_health_reply::copy() const { } return reply; } - -std::ostream& operator<<(std::ostream& o, const topic_status& tl) { - fmt::print(o, "{{topic: {}, partitions: {}}}", tl.tp_ns, tl.partitions); - return o; +fmt::iterator topic_status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, partitions: {}}}", tp_ns, partitions); } - -std::ostream& operator<<(std::ostream& o, const node_report_filter& s) { - fmt::print( - o, +fmt::iterator node_report_filter::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{include_partitions: {}, ntp_filters: {}}}", - s.include_partitions, - s.ntp_filters); - return o; + include_partitions, + ntp_filters); } - -std::ostream& operator<<(std::ostream& o, const cluster_report_filter& s) { - fmt::print( - o, "{{per_node_filter: {}, nodes: {}}}", s.node_report_filter, s.nodes); - return o; +fmt::iterator cluster_report_filter::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{per_node_filter: {}, nodes: {}}}", node_report_filter, nodes); } - -std::ostream& operator<<(std::ostream& o, const partitions_filter& filter) { - fmt::print(o, "{{"); - for (auto& [ns, tp_f] : filter.namespaces) { - fmt::print(o, "{{namespace: {}, topics: [", ns); +fmt::iterator partitions_filter::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{"); + for (auto& [ns, tp_f] : namespaces) { + it = fmt::format_to(it, "{{namespace: {}, topics: [", ns); for (auto& [tp, p_f] : tp_f) { - fmt::print(o, "{{topic: {}, partitions: [", tp); + it = fmt::format_to(it, "{{topic: {}, partitions: [", tp); if (!p_f.empty()) { - auto it = p_f.begin(); - fmt::print(o, "{}", *it); - ++it; - for (; it != p_f.end(); ++it) { - fmt::print(o, ",{}", *it); + auto pit = p_f.begin(); + it = fmt::format_to(it, "{}", *pit); + ++pit; + for (; pit != p_f.end(); ++pit) { + it = fmt::format_to(it, ",{}", *pit); } } - fmt::print(o, "] }},"); + it = fmt::format_to(it, "] }},"); } - fmt::print(o, "]}},"); + it = fmt::format_to(it, "]}},"); } - fmt::print(o, "}}"); - - return o; + return fmt::format_to(it, "}}"); } -std::ostream& operator<<(std::ostream& o, const get_node_health_request& r) { - fmt::print(o, "{{target_node_id: {}}}", r.get_target_node_id()); - return o; +fmt::iterator get_node_health_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{target_node_id: {}}}", get_target_node_id()); } - -std::ostream& operator<<(std::ostream& o, const get_node_health_reply& r) { - fmt::print(o, "{{error: {}, report: {}}}", r.error, r.report); - return o; +fmt::iterator get_node_health_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{error: {}, report: {}}}", error, report); } - -std::ostream& operator<<(std::ostream& o, const get_cluster_health_request& r) { - fmt::print( - o, +fmt::iterator get_cluster_health_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{filter: {}, refresh: {}, decoded_version: {}}}", - r.filter, - r.refresh, - r.decoded_version); - return o; + filter, + refresh, + decoded_version); } - -std::ostream& operator<<(std::ostream& o, const get_cluster_health_reply& r) { - fmt::print(o, "{{error: {}, report: {}}}", r.error, r.report); - return o; +fmt::iterator get_cluster_health_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{error: {}, report: {}}}", error, report); } void restart_risk_report::push( @@ -382,27 +355,25 @@ void restart_risk_report::push( list.emplace_back(nt.ns, nt.tp, pid); } } - -std::ostream& operator<<(std::ostream& o, const cluster_health_overview& ho) { - fmt::print( - o, +fmt::iterator cluster_health_overview::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{controller_id: {}, nodes: {}, unhealthy_reasons: {}, nodes_down: {}, " "high_disk_usage_nodes: {}, nodes_in_recovery_mode: {}, " "bytes_in_cloud_storage: {}, leaderless_count: {}, " "under_replicated_count: {}, leaderless_partitions: {}, " "under_replicated_partitions: {}}}", - ho.controller_id, - ho.all_nodes, - ho.unhealthy_reasons, - ho.nodes_down, - ho.high_disk_usage_nodes, - ho.nodes_in_recovery_mode, - ho.bytes_in_cloud_storage, - ho.leaderless_count, - ho.under_replicated_count, - ho.leaderless_partitions, - ho.under_replicated_partitions); - return o; + controller_id, + all_nodes, + unhealthy_reasons, + nodes_down, + high_disk_usage_nodes, + nodes_in_recovery_mode, + bytes_in_cloud_storage, + leaderless_count, + under_replicated_count, + leaderless_partitions, + under_replicated_partitions); } } // namespace cluster diff --git a/src/v/cluster/health_monitor_types.h b/src/v/cluster/health_monitor_types.h index e1c207927024b..4c26923886879 100644 --- a/src/v/cluster/health_monitor_types.h +++ b/src/v/cluster/health_monitor_types.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/node_hash_map.h" #include "absl/container/node_hash_set.h" +#include "base/format_to.h" #include "bytes/iobuf_parser.h" #include "cluster/drain_manager.h" #include "cluster/errc.h" @@ -75,7 +76,7 @@ struct node_state return _is_alive; } // clang-format on - friend std::ostream& operator<<(std::ostream&, const node_state&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const node_state&, const node_state&) = default; @@ -88,7 +89,7 @@ struct node_state }; enum class follower_status { in_sync, out_of_sync, down }; -std::ostream& operator<<(std::ostream& o, follower_status); +fmt::iterator format_to(follower_status s, fmt::iterator); struct followers_stats : serde:: @@ -100,7 +101,7 @@ struct followers_stats auto serde_fields() { return std::tie(in_sync, out_of_sync, down); } - friend std::ostream& operator<<(std::ostream&, const followers_stats&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const followers_stats&, const followers_stats&) = default; }; @@ -169,7 +170,7 @@ struct partition_status cloud_topic_max_gc_eligible_epoch); } - friend std::ostream& operator<<(std::ostream&, const partition_status&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const partition_status&, const partition_status&) = default; }; @@ -192,7 +193,7 @@ struct topic_status model::topic_namespace tp_ns; partition_statuses_t partitions; - friend std::ostream& operator<<(std::ostream&, const topic_status&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const topic_status&, const topic_status&); auto serde_fields() { return std::tie(tp_ns, partitions); } @@ -226,7 +227,7 @@ struct node_liveness_report sizeof(node_liveness_report_data) == sizeof(node_liveness_report)); } - friend std::ostream& operator<<(std::ostream&, const node_liveness_report&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(node_id_to_last_seen); } @@ -260,7 +261,7 @@ struct node_health_report { node_health_report copy() const; - friend std::ostream& operator<<(std::ostream&, const node_health_report&); + fmt::iterator format_to(fmt::iterator it) const; }; using node_health_report_ptr @@ -317,8 +318,7 @@ struct node_health_report_serde std::move(node_liveness_report)}; } - friend std::ostream& - operator<<(std::ostream&, const node_health_report_serde&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const node_health_report_serde& a, const node_health_report_serde& b); @@ -340,8 +340,7 @@ struct cluster_health_report // cluster-wide cached information about total cloud storage usage std::optional bytes_in_cloud_storage; - friend std::ostream& - operator<<(std::ostream&, const cluster_health_report&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const cluster_health_report&, const cluster_health_report&) = default; @@ -478,8 +477,7 @@ struct cluster_health_overview { size_t under_replicated_count{}; std::optional bytes_in_cloud_storage; - friend std::ostream& - operator<<(std::ostream&, const cluster_health_overview&); + fmt::iterator format_to(fmt::iterator it) const; }; using include_partitions_info = ss::bool_class; @@ -504,7 +502,7 @@ struct partitions_filter friend bool operator==(const partitions_filter&, const partitions_filter&) = default; - friend std::ostream& operator<<(std::ostream& o, const partitions_filter&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(namespaces); } }; @@ -523,7 +521,7 @@ struct node_report_filter friend bool operator==(const node_report_filter&, const node_report_filter&) = default; - friend std::ostream& operator<<(std::ostream&, const node_report_filter&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(include_partitions, ntp_filters); } }; @@ -539,8 +537,7 @@ struct cluster_report_filter // list of requested nodes, if empty report will contain all nodes std::vector nodes; - friend std::ostream& - operator<<(std::ostream&, const cluster_report_filter&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const cluster_report_filter&, const cluster_report_filter&) = default; @@ -567,8 +564,7 @@ class get_node_health_request friend bool operator==( const get_node_health_request&, const get_node_health_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const get_node_health_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(_filter, _target_node_id); } static constexpr model::node_id node_id_not_set{-1}; @@ -603,8 +599,7 @@ struct get_node_health_reply }; } - friend std::ostream& - operator<<(std::ostream&, const get_node_health_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(error, report); } }; @@ -632,8 +627,7 @@ struct get_cluster_health_request const get_cluster_health_request&, const get_cluster_health_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const get_cluster_health_request&); + fmt::iterator format_to(fmt::iterator it) const; void serde_write(iobuf& out) { using serde::write; @@ -667,8 +661,7 @@ struct get_cluster_health_reply operator==(const get_cluster_health_reply&, const get_cluster_health_reply&) = default; - friend std::ostream& - operator<<(std::ostream&, const get_cluster_health_reply&); + fmt::iterator format_to(fmt::iterator it) const; get_cluster_health_reply copy() const; diff --git a/src/v/cluster/log_eviction_stm.cc b/src/v/cluster/log_eviction_stm.cc index 47b67ded61772..5bc8f9f15f511 100644 --- a/src/v/cluster/log_eviction_stm.cc +++ b/src/v/cluster/log_eviction_stm.cc @@ -9,6 +9,7 @@ #include "cluster/log_eviction_stm.h" +#include "base/format_to.h" #include "cluster/errc.h" #include "cluster/logger.h" #include "cluster/prefix_truncate_record.h" @@ -29,10 +30,9 @@ struct snapshot_data auto serde_fields() { return std::tie(effective_start_offset); } - friend std::ostream& operator<<(std::ostream& os, const snapshot_data& d) { - fmt::print( - os, "{{ effective_start_offset: {} }}", d.effective_start_offset); - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{effective_start_offset: {}}}", effective_start_offset); } }; diff --git a/src/v/cluster/members_backend.cc b/src/v/cluster/members_backend.cc index d4d5f3670ae0a..77d416bb1edb8 100644 --- a/src/v/cluster/members_backend.cc +++ b/src/v/cluster/members_backend.cc @@ -1,5 +1,6 @@ #include "cluster/members_backend.h" +#include "base/format_to.h" #include "cluster/controller_api.h" #include "cluster/errc.h" #include "cluster/fwd.h" @@ -18,6 +19,7 @@ #include "model/namespace.h" #include "model/timeout_clock.h" #include "random/generators.h" +#include "utils/to_string.h" #include #include @@ -583,7 +585,7 @@ ss::future<> members_backend::reconcile_reallocation_state( meta.current_replica_set, meta.new_replica_set, reconciliation_state.status(), - reconciliation_state.pending_operations()); + fmt::join(reconciliation_state.pending_operations(), ", ")); if (reconciliation_state.status() != reconciliation_status::done) { co_return; } @@ -738,26 +740,24 @@ ss::future members_backend::remove_from_raft0( co_return co_await _raft0->remove_member( raft::vnode(id, raft0_revision), revision); } - -std::ostream& -operator<<(std::ostream& o, const members_backend::partition_reallocation& r) { - fmt::print( - o, +fmt::iterator +members_backend::partition_reallocation::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{current: {}, new: {}, state: {}}}", - r.current_replica_set, - r.new_replica_set, - r.state); - return o; + current_replica_set, + new_replica_set, + state); } -std::ostream& -operator<<(std::ostream& o, const members_backend::cancellation_state& state) { - switch (state) { +fmt::iterator +format_to(members_backend::cancellation_state e, fmt::iterator out) { + switch (e) { case members_backend::cancellation_state::request_cancel: - return o << "request_cancel"; + return fmt::format_to(out, "request_cancel"); case members_backend::cancellation_state::cancelled: - return o << "cancelled"; + return fmt::format_to(out, "cancelled"); case members_backend::cancellation_state::finished: - return o << "finished"; + return fmt::format_to(out, "finished"); } __builtin_unreachable(); diff --git a/src/v/cluster/members_backend.h b/src/v/cluster/members_backend.h index 75eaec4b86f2c..ca31de8cdd535 100644 --- a/src/v/cluster/members_backend.h +++ b/src/v/cluster/members_backend.h @@ -2,6 +2,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/node_hash_set.h" +#include "base/format_to.h" #include "cluster/fwd.h" #include "cluster/members_manager.h" #include "cluster/scheduling/types.h" @@ -22,6 +23,7 @@ class members_backend { cancelled, finished, }; + friend fmt::iterator format_to(cancellation_state, fmt::iterator); struct partition_reallocation { partition_reallocation( @@ -37,8 +39,7 @@ class members_backend { // partition_balancer. cancellation_state state; - friend std::ostream& - operator<<(std::ostream&, const partition_reallocation&); + fmt::iterator format_to(fmt::iterator it) const; }; /** * struct describing partition reallocation @@ -128,7 +129,5 @@ class members_backend { metrics::public_metric_groups _metrics; config::binding _max_concurrent_reallocations; }; -std::ostream& -operator<<(std::ostream&, const members_backend::cancellation_state&); } // namespace cluster diff --git a/src/v/cluster/members_manager.cc b/src/v/cluster/members_manager.cc index 95648a0364070..387f00806981b 100644 --- a/src/v/cluster/members_manager.cc +++ b/src/v/cluster/members_manager.cc @@ -9,6 +9,7 @@ #include "cluster/members_manager.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "cluster/cluster_utils.h" #include "cluster/commands.h" @@ -1586,19 +1587,16 @@ members_manager::handle_configuration_update_request( co_return errc::join_request_dispatch_error; } } - -std::ostream& -operator<<(std::ostream& o, const members_manager::node_update& u) { - fmt::print( - o, +fmt::iterator members_manager::node_update::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{node_id: {}, type: {}, offset: {}, update_raft0: {}, " "decom_upd_revision: {}}}", - u.id, - u.type, - u.offset, - u.need_raft0_update, - u.decommission_update_revision); - return o; + id, + type, + offset, + need_raft0_update, + decommission_update_revision); } ss::future<> diff --git a/src/v/cluster/members_manager.h b/src/v/cluster/members_manager.h index d4a58d8987aeb..ff49d7c0faa32 100644 --- a/src/v/cluster/members_manager.h +++ b/src/v/cluster/members_manager.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "cluster/commands.h" #include "cluster/controller_stm.h" #include "cluster/fwd.h" @@ -99,7 +100,7 @@ class members_manager { friend bool operator==(const node_update&, const node_update&) = default; - friend std::ostream& operator<<(std::ostream&, const node_update&); + fmt::iterator format_to(fmt::iterator it) const; }; using uuid_map_t = absl::flat_hash_map; diff --git a/src/v/cluster/metadata_dissemination_types.h b/src/v/cluster/metadata_dissemination_types.h index 9bd4c0bbc817b..c0f66a934e1c1 100644 --- a/src/v/cluster/metadata_dissemination_types.h +++ b/src/v/cluster/metadata_dissemination_types.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "container/chunked_vector.h" #include "model/fundamental.h" #include "model/metadata.h" @@ -24,8 +25,6 @@ #include #include -#include - #include #include @@ -48,15 +47,13 @@ struct ntp_leader , leader_id(leader_id) {} friend bool operator==(const ntp_leader&, const ntp_leader&) = default; - - friend std::ostream& operator<<(std::ostream& o, const ntp_leader& l) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, term: {}, leader: {}}}", - l.ntp, - l.term, - l.leader_id ? l.leader_id.value()() : -1); - return o; + ntp, + term, + leader_id ? leader_id.value()() : -1); } auto serde_fields() { return std::tie(ntp, term, leader_id); } @@ -86,17 +83,14 @@ struct ntp_leader_revision friend bool operator==( const ntp_leader_revision&, const ntp_leader_revision&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const ntp_leader_revision& r) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, term: {}, leader: {}, revision: {}}}", - r.ntp, - r.term, - r.leader_id, - r.revision); - return o; + ntp, + term, + leader_id, + revision); } auto serde_fields() { return std::tie(ntp, term, leader_id, revision); } @@ -130,11 +124,8 @@ struct update_leadership_request_v2 return update_leadership_request_v2(std::move(leaders_cp)); } - - friend std::ostream& - operator<<(std::ostream& o, const update_leadership_request_v2& r) { - fmt::print(o, "leaders {}", r.leaders); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "leaders {}", leaders); } explicit update_leadership_request_v2( @@ -151,10 +142,8 @@ struct update_leadership_reply serde::compat_version<0>> { update_leadership_reply() noexcept = default; - friend std::ostream& - operator<<(std::ostream& o, const update_leadership_reply&) { - fmt::print(o, "{{}}"); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{}}"); } auto serde_fields() { return std::tie(); } @@ -167,10 +156,8 @@ struct get_leadership_request serde::compat_version<0>> { get_leadership_request() noexcept = default; - friend std::ostream& - operator<<(std::ostream& o, const get_leadership_request&) { - fmt::print(o, "{{}}"); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{}}"); } auto serde_fields() { return std::tie(); } @@ -194,11 +181,8 @@ struct get_leadership_reply friend bool operator==( const get_leadership_reply&, const get_leadership_reply&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const get_leadership_reply& r) { - fmt::print(o, "leaders {}, success: {}", r.leaders, r.success); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "leaders {}, success: {}", leaders, success); } auto serde_fields() { return std::tie(leaders, success); } diff --git a/src/v/cluster/migrations/tx_manager_migrator.cc b/src/v/cluster/migrations/tx_manager_migrator.cc index 2197435aed115..4b248b7288fac 100644 --- a/src/v/cluster/migrations/tx_manager_migrator.cc +++ b/src/v/cluster/migrations/tx_manager_migrator.cc @@ -11,6 +11,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "base/vlog.h" #include "cluster/controller_api.h" #include "cluster/errc.h" @@ -659,24 +660,23 @@ tx_manager_migrator::copy_from_temporary_to_tx_manager_topic( } ss::future<> tx_manager_migrator::stop() { return ss::now(); } - -std::ostream& -operator<<(std::ostream& o, tx_manager_migrator::migration_step step) { - switch (step) { +fmt::iterator +format_to(tx_manager_migrator::migration_step e, fmt::iterator out) { + switch (e) { case tx_manager_migrator::migration_step::create_new_temp_topic: - return o << "create_new_temp_topic"; + return fmt::format_to(out, "create_new_temp_topic"); case tx_manager_migrator::migration_step::rehash_tx_manager_topic: - return o << "rehash_tx_manager_topic"; + return fmt::format_to(out, "rehash_tx_manager_topic"); case tx_manager_migrator::migration_step::delete_old_tx_manager_topic: - return o << "delete_old_tx_manager_topic"; + return fmt::format_to(out, "delete_old_tx_manager_topic"); case tx_manager_migrator::migration_step::create_new_tx_manager_topic: - return o << "create_new_tx_manager_topic"; + return fmt::format_to(out, "create_new_tx_manager_topic"); case tx_manager_migrator::migration_step::copy_temp_to_new_tx_manger: - return o << "copy_temp_to_new_tx_manger"; + return fmt::format_to(out, "copy_temp_to_new_tx_manger"); case tx_manager_migrator::migration_step::delete_temp_topic: - return o << "delete_temp_topic"; + return fmt::format_to(out, "delete_temp_topic"); case tx_manager_migrator::migration_step::finished: - return o << "finished"; + return fmt::format_to(out, "finished"); } __builtin_unreachable(); } diff --git a/src/v/cluster/migrations/tx_manager_migrator.h b/src/v/cluster/migrations/tx_manager_migrator.h index 742fa94d894db..ba14cb879f5e0 100644 --- a/src/v/cluster/migrations/tx_manager_migrator.h +++ b/src/v/cluster/migrations/tx_manager_migrator.h @@ -214,7 +214,7 @@ class tx_manager_migrator { bool is_migration_required() const; - friend std::ostream& operator<<(std::ostream&, migration_step); + friend fmt::iterator format_to(migration_step, fmt::iterator); ss::sharded& _topics_frontend; ss::sharded& _controller_api; ss::sharded& _topics; diff --git a/src/v/cluster/node/types.cc b/src/v/cluster/node/types.cc index 96d9a75c17832..2907cd3723de4 100644 --- a/src/v/cluster/node/types.cc +++ b/src/v/cluster/node/types.cc @@ -10,39 +10,34 @@ */ #include "types.h" +#include "base/format_to.h" #include "utils/human.h" #include "utils/to_string.h" #include -#include #include namespace cluster::node { - -std::ostream& -operator<<(std::ostream& o, const local_state::log_data_state& s) { - fmt::print( - o, +fmt::iterator local_state::log_data_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{target: {} current: {} reclaimable: {}}}", - human::bytes(s.data_target_size), - human::bytes(s.data_current_size), - human::bytes(s.data_reclaimable_size)); - return o; + human::bytes(data_target_size), + human::bytes(data_current_size), + human::bytes(data_reclaimable_size)); } - -std::ostream& operator<<(std::ostream& o, const local_state& s) { - fmt::print( - o, +fmt::iterator local_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{redpanda_version: {}, uptime: {}, data_disk: {}, cache_disk: {} log " "data {}, recovery_mode_enabled: {}}}", - s.redpanda_version, - s.uptime, - s.data_disk, - s.cache_disk, - s.log_data_size, - s.recovery_mode_enabled); - return o; + redpanda_version, + uptime, + data_disk, + cache_disk, + log_data_size, + recovery_mode_enabled); } void local_state::serde_read(iobuf_parser& in, const serde::header& h) { diff --git a/src/v/cluster/node/types.h b/src/v/cluster/node/types.h index 3560e9c1c42ac..042ba6bd138f4 100644 --- a/src/v/cluster/node/types.h +++ b/src/v/cluster/node/types.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "cluster/types.h" #include "model/metadata.h" #include "reflection/adl.h" @@ -19,8 +20,6 @@ #include -#include - namespace cluster::node { // @@ -112,7 +111,7 @@ struct local_state } friend bool operator==(const log_data_state&, const log_data_state&) = default; - friend std::ostream& operator<<(std::ostream&, const log_data_state&); + fmt::iterator format_to(fmt::iterator it) const; }; std::optional log_data_size{std::nullopt}; @@ -130,7 +129,7 @@ struct local_state void set_disks(std::vector); friend bool operator==(const local_state&, const local_state&) = default; - friend std::ostream& operator<<(std::ostream&, const local_state&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace cluster::node diff --git a/src/v/cluster/node_status_rpc_types.h b/src/v/cluster/node_status_rpc_types.h index ebe8f272c0fa4..d96c3b8668480 100644 --- a/src/v/cluster/node_status_rpc_types.h +++ b/src/v/cluster/node_status_rpc_types.h @@ -11,11 +11,10 @@ #pragma once +#include "base/format_to.h" #include "model/metadata.h" #include "serde/rw/envelope.h" -#include - namespace cluster { struct node_status_metadata @@ -26,11 +25,8 @@ struct node_status_metadata model::node_id node_id; auto serde_fields() { return std::tie(node_id); } - - friend std::ostream& - operator<<(std::ostream& o, const node_status_metadata& nsm) { - fmt::print(o, "{{node_id:{}}}", nsm.node_id); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{node_id:{}}}", node_id); } }; @@ -42,11 +38,8 @@ struct node_status_request node_status_metadata sender_metadata; auto serde_fields() { return std::tie(sender_metadata); } - - friend std::ostream& - operator<<(std::ostream& o, const node_status_request& r) { - fmt::print(o, "{{sender_metadata: {}}}", r.sender_metadata); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{sender_metadata: {}}}", sender_metadata); } }; @@ -56,11 +49,8 @@ struct node_status_reply node_status_metadata replier_metadata; auto serde_fields() { return std::tie(replier_metadata); } - - friend std::ostream& - operator<<(std::ostream& o, const node_status_reply& r) { - fmt::print(o, "{{replier_metadata: {}}}", r.replier_metadata); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{replier_metadata: {}}}", replier_metadata); } }; diff --git a/src/v/cluster/nt_revision.h b/src/v/cluster/nt_revision.h index 21aa140da1e2e..d3283e277c118 100644 --- a/src/v/cluster/nt_revision.h +++ b/src/v/cluster/nt_revision.h @@ -11,6 +11,7 @@ #pragma once #include "absl/hash/hash.h" +#include "base/format_to.h" #include "model/fundamental.h" #include "model/metadata.h" #include "serde/rw/envelope.h" @@ -34,7 +35,7 @@ struct nt_revision } friend bool operator==(const nt_revision& lhs, const nt_revision& rhs); - friend std::ostream& operator<<(std::ostream&, const nt_revision&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(nt, initial_revision_id); } }; diff --git a/src/v/cluster/partition.cc b/src/v/cluster/partition.cc index a736f45fa8a7c..c5fd5a91be75d 100644 --- a/src/v/cluster/partition.cc +++ b/src/v/cluster/partition.cc @@ -1551,8 +1551,8 @@ ss::shared_ptr partition::id_allocator_stm() const { return _raft->stm_manager()->get(); } -std::ostream& operator<<(std::ostream& o, const partition& x) { - return o << x._raft; +fmt::iterator partition::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", *_raft); } ss::shared_ptr partition::tm_stm() { return _raft->stm_manager()->get(); diff --git a/src/v/cluster/partition.h b/src/v/cluster/partition.h index eef38da6117be..9953a42e61cfe 100644 --- a/src/v/cluster/partition.h +++ b/src/v/cluster/partition.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_storage/fwd.h" #include "cluster/archival/archival_metadata_stm.h" #include "cluster/archival/fwd.h" @@ -401,6 +402,8 @@ class partition : public ss::enable_lw_shared_from_this { ss::sharded* get_cloud_topics_state() noexcept; + fmt::iterator format_to(fmt::iterator it) const; + private: ss::future<> replicate_unsafe_reset(cloud_storage::partition_manifest manifest); @@ -466,8 +469,6 @@ class partition : public ss::enable_lw_shared_from_this { = partition_flush_hook_id_invalid; bool _started{false}; - - friend std::ostream& operator<<(std::ostream& o, const partition& x); }; } // namespace cluster namespace std { diff --git a/src/v/cluster/partition_balancer_planner.cc b/src/v/cluster/partition_balancer_planner.cc index bfbe6b48920ac..036360acd7fdc 100644 --- a/src/v/cluster/partition_balancer_planner.cc +++ b/src/v/cluster/partition_balancer_planner.cc @@ -10,6 +10,7 @@ #include "cluster/partition_balancer_planner.h" +#include "base/format_to.h" #include "base/vlog.h" #include "cluster/cluster_utils.h" #include "cluster/health_monitor_types.h" @@ -248,14 +249,12 @@ class partition_balancer_planner::request_context { return 0; } - friend std::ostream& - operator<<(std::ostream& o, const partition_sizes& ps) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{current: {}, non_reclaimable: {}}}", - ps.current, - ps.non_reclaimable); - return o; + current, + non_reclaimable); } }; @@ -284,6 +283,10 @@ class partition_balancer_planner::request_context { ss::abort_source& _as; }; +} // namespace cluster + +namespace cluster { + std::pair partition_balancer_planner::get_node_bytes_info( const node::local_state& node_state) { const auto& state = node_state.log_data_size; diff --git a/src/v/cluster/partition_balancer_planner.h b/src/v/cluster/partition_balancer_planner.h index c1ab34b394fed..add1463fc7c00 100644 --- a/src/v/cluster/partition_balancer_planner.h +++ b/src/v/cluster/partition_balancer_planner.h @@ -175,3 +175,11 @@ class partition_balancer_planner { }; } // namespace cluster + +template<> +struct fmt::formatter : fmt::formatter { + auto + format(cluster::ntp_reassignment_type t, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(t), ctx); + } +}; diff --git a/src/v/cluster/partition_balancer_types.cc b/src/v/cluster/partition_balancer_types.cc index cb53cffd05c6e..adbb9ef55b0f7 100644 --- a/src/v/cluster/partition_balancer_types.cc +++ b/src/v/cluster/partition_balancer_types.cc @@ -10,6 +10,7 @@ #include "cluster/partition_balancer_types.h" +#include "base/format_to.h" #include "utils/human.h" #include "utils/to_string.h" @@ -31,43 +32,37 @@ double node_disk_space::final_used_ratio() const { return double(used + assigned - released) / total; } - -std::ostream& operator<<(std::ostream& o, const node_disk_space& d) { - fmt::print( - o, +fmt::iterator node_disk_space::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{total: {}, used: {}, assigned: {}, released: {}; " "used ratios: orig: {:.4}, peak: {:.4}, final: {:.4}}}", - human::bytes(d.total), - human::bytes(d.used), - human::bytes(d.assigned), - human::bytes(d.released), - d.original_used_ratio(), - d.peak_used_ratio(), - d.final_used_ratio()); - return o; + human::bytes(total), + human::bytes(used), + human::bytes(assigned), + human::bytes(released), + original_used_ratio(), + peak_used_ratio(), + final_used_ratio()); } partition_balancer_violations::unavailable_node::unavailable_node( model::node_id id, model::timestamp unavailable_since) : id(id) , unavailable_since(unavailable_since) {} - -std::ostream& operator<<( - std::ostream& o, const partition_balancer_violations::unavailable_node& u) { - fmt::print(o, "{{ id: {} since: {} }}", u.id, u.unavailable_since); - return o; +fmt::iterator partition_balancer_violations::unavailable_node::format_to( + fmt::iterator it) const { + return fmt::format_to(it, "{{ id: {} since: {} }}", id, unavailable_since); } partition_balancer_violations::full_node::full_node( model::node_id id, uint32_t disk_used_percent) : id(id) , disk_used_percent(disk_used_percent) {} - -std::ostream& -operator<<(std::ostream& o, const partition_balancer_violations::full_node& f) { - fmt::print( - o, "{{ id: {} disk_used_percent: {} }}", f.id, f.disk_used_percent); - return o; +fmt::iterator +partition_balancer_violations::full_node::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ id: {} disk_used_percent: {} }}", id, disk_used_percent); } partition_balancer_violations::partition_balancer_violations( @@ -111,110 +106,96 @@ partition_balancer_overview_reply::copy() const { } return copy; } - -std::ostream& -operator<<(std::ostream& o, const partition_balancer_violations& v) { - fmt::print( - o, +fmt::iterator partition_balancer_violations::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ unavailable_nodes: {} full_nodes: {} }}", - v.unavailable_nodes, - v.full_nodes); - return o; + unavailable_nodes, + full_nodes); } - -std::ostream& operator<<(std::ostream& os, partition_balancer_status status) { - switch (status) { +fmt::iterator format_to(partition_balancer_status e, fmt::iterator out) { + switch (e) { case partition_balancer_status::off: - os << "off"; - break; + return fmt::format_to(out, "off"); case partition_balancer_status::starting: - os << "starting"; - break; + return fmt::format_to(out, "starting"); case partition_balancer_status::ready: - os << "ready"; - break; + return fmt::format_to(out, "ready"); case partition_balancer_status::in_progress: - os << "in_progress"; - break; + return fmt::format_to(out, "in_progress"); case partition_balancer_status::stalled: - os << "stalled"; - break; + return fmt::format_to(out, "stalled"); } - return os; + return fmt::format_to(out, ""); } - -std::ostream& -operator<<(std::ostream& o, const partition_balancer_overview_request&) { - fmt::print(o, "{{}}"); - return o; +fmt::iterator +partition_balancer_overview_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{}}"); } - -std::ostream& -operator<<(std::ostream& o, const partition_balancer_overview_reply& rep) { - fmt::print( - o, +fmt::iterator +partition_balancer_overview_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ error: {} last_tick_time: {} status: {} violations: {}, " "partitions_pending_force_recovery: {}, " "decommission_reallocation_failures_count: {}, reallocation_failures: " "{}}}", - rep.error, - rep.last_tick_time, - rep.status, - rep.violations, - rep.partitions_pending_force_recovery_count, - rep.decommission_realloc_failures.size(), - fmt::join(rep.reallocation_failures | std::views::values, ", ")); - return o; + error, + last_tick_time, + status, + violations, + partitions_pending_force_recovery_count, + decommission_realloc_failures.size(), + fmt::join(reallocation_failures | std::views::values, ", ")); } - -std::ostream& operator<<(std::ostream& o, change_reason reason) { - switch (reason) { +fmt::iterator format_to(change_reason e, fmt::iterator out) { + switch (e) { case change_reason::rack_constraint_repair: - return o << "rack_constraint_repair"; + return fmt::format_to(out, "rack_constraint_repair"); case change_reason::partition_count_rebalancing: - return o << "partition_count_rebalancing"; + return fmt::format_to(out, "partition_count_rebalancing"); case change_reason::node_decommissioning: - return o << "node_decommissioning"; + return fmt::format_to(out, "node_decommissioning"); case change_reason::node_unavailable: - return o << "node_unavailable"; + return fmt::format_to(out, "node_unavailable"); case change_reason::disk_full: - return o << "disk_full"; + return fmt::format_to(out, "disk_full"); } } - -std::ostream& operator<<(std::ostream& o, reallocation_error err) { - switch (err) { +fmt::iterator format_to(reallocation_error e, fmt::iterator out) { + switch (e) { case reallocation_error::missing_partition_size_info: - return o << "Missing partition size information, all replicas may be " - "offline"; + return fmt::format_to( + out, + "Missing partition size information, all replicas may be offline"); case reallocation_error::no_eligible_node_found: - return o << "No eligible node found to move replica"; + return fmt::format_to(out, "No eligible node found to move replica"); case reallocation_error::over_partition_fd_limit: - return o << "Over the total partition file descriptor limit"; + return fmt::format_to( + out, "Over the total partition file descriptor limit"); case reallocation_error::over_partition_memory_limit: - return o << "Over the total partition memory limit"; + return fmt::format_to(out, "Over the total partition memory limit"); case reallocation_error::over_partition_core_limit: - return o << "Over the partition per core limit"; + return fmt::format_to(out, "Over the partition per core limit"); case reallocation_error::no_quorum: - return o << "No quorum, majority of replicas are offline"; + return fmt::format_to( + out, "No quorum, majority of replicas are offline"); case reallocation_error::reconfiguration_in_progress: - return o << "Non cancellable reconfiguration in progress"; + return fmt::format_to( + out, "Non cancellable reconfiguration in progress"); case reallocation_error::partition_disabled: - return o << "Partition is disabled"; + return fmt::format_to(out, "Partition is disabled"); case reallocation_error::unknown_error: - return o << "Unknown error or error not reported"; + return fmt::format_to(out, "Unknown error or error not reported"); } } - -std::ostream& -operator<<(std::ostream& o, const reallocation_failure_details& details) { - fmt::print( - o, +fmt::iterator reallocation_failure_details::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{replica_to_move: {}, reason: {}, error: {}}}", - details.replica_to_move, - details.reason, - details.error); - return o; + replica_to_move, + reason, + error); } } // namespace cluster diff --git a/src/v/cluster/partition_balancer_types.h b/src/v/cluster/partition_balancer_types.h index f36a9fba9cbd1..1dff41a360e24 100644 --- a/src/v/cluster/partition_balancer_types.h +++ b/src/v/cluster/partition_balancer_types.h @@ -12,6 +12,7 @@ #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "cluster/errc.h" #include "container/chunked_hash_map.h" #include "model/fundamental.h" @@ -42,7 +43,7 @@ struct node_disk_space { double final_used_ratio() const; - friend std::ostream& operator<<(std::ostream& o, const node_disk_space& d); + fmt::iterator format_to(fmt::iterator it) const; }; struct partition_balancer_violations @@ -61,8 +62,7 @@ struct partition_balancer_violations unavailable_node() noexcept = default; unavailable_node(model::node_id id, model::timestamp unavailable_since); - friend std::ostream& - operator<<(std::ostream& o, const unavailable_node& u); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(id, unavailable_since); } @@ -79,7 +79,7 @@ struct partition_balancer_violations full_node() noexcept = default; full_node(model::node_id id, uint32_t disk_used_percent); - friend std::ostream& operator<<(std::ostream& o, const full_node& f); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(id, disk_used_percent); } @@ -94,8 +94,7 @@ struct partition_balancer_violations partition_balancer_violations( std::vector un, std::vector fn); - friend std::ostream& - operator<<(std::ostream& o, const partition_balancer_violations& v); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(unavailable_nodes, full_nodes); } @@ -116,15 +115,14 @@ enum class partition_balancer_status { stalled, }; -std::ostream& operator<<(std::ostream& os, partition_balancer_status status); +fmt::iterator format_to(partition_balancer_status status, fmt::iterator); struct partition_balancer_overview_request : serde::envelope< partition_balancer_overview_request, serde::version<0>, serde::compat_version<0>> { - friend std::ostream& - operator<<(std::ostream& o, const partition_balancer_overview_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(); } }; @@ -139,7 +137,7 @@ enum class change_reason { disk_full, }; -std::ostream& operator<<(std::ostream& o, change_reason rep); +fmt::iterator format_to(change_reason rep, fmt::iterator); /** * Enum providing a details about partition replica reallocation failure. */ @@ -155,7 +153,7 @@ enum class reallocation_error : int8_t { unknown_error, }; -std::ostream& operator<<(std::ostream& o, reallocation_error rep); +fmt::iterator format_to(reallocation_error rep, fmt::iterator); /** * Struct providing details about partition replica reallocation failure. @@ -176,8 +174,7 @@ struct reallocation_failure_details const reallocation_failure_details&, const reallocation_failure_details&) = default; - friend std::ostream& - operator<<(std::ostream& o, const reallocation_failure_details& rep); + fmt::iterator format_to(fmt::iterator it) const; }; struct partition_balancer_overview_reply @@ -226,8 +223,7 @@ struct partition_balancer_overview_reply const partition_balancer_overview_reply&, const partition_balancer_overview_reply&) = default; - friend std::ostream& - operator<<(std::ostream& o, const partition_balancer_overview_reply& rep); + fmt::iterator format_to(fmt::iterator it) const; partition_balancer_overview_reply copy() const; }; diff --git a/src/v/cluster/partition_manager.cc b/src/v/cluster/partition_manager.cc index 7f36d8de6d572..1fa871f45142d 100644 --- a/src/v/cluster/partition_manager.cc +++ b/src/v/cluster/partition_manager.cc @@ -561,11 +561,14 @@ uint64_t partition_manager::upload_backlog_size() const { return size; } -std::ostream& operator<<(std::ostream& o, const partition_manager& pm) { - return o << "{shard:" << ss::this_shard_id() << ", mngr:{}" - << pm._storage.log_mgr() - << ", ntp_table.size:" << pm._ntp_table.size() - << ", raft_table.size:" << pm._raft_table.size() << "}"; +fmt::iterator partition_manager::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{shard:{}, mngr:{{{}}}, ntp_table.size:{}, raft_table.size:{}}}", + ss::this_shard_id(), + fmt_streamed(_storage.log_mgr()), + _ntp_table.size(), + _raft_table.size()); } ss::future partition_manager::non_log_disk_size_bytes() const { @@ -606,26 +609,33 @@ partition_manager::get_cloud_cache_disk_usage_target() const { [](auto acc, auto update) { return acc + update; }); } -std::ostream& operator<<( - std::ostream& o, const partition_manager::partition_shutdown_stage& stage) { +std::string_view +partition_manager::shutdown_stage_string(partition_shutdown_stage stage) { switch (stage) { - case partition_manager::partition_shutdown_stage::shutdown_requested: - return o << "shutdown_requested"; - case partition_manager::partition_shutdown_stage::stopping_raft: - return o << "stopping_raft"; - case partition_manager::partition_shutdown_stage::removing_raft: - return o << "removing_raft"; - case partition_manager::partition_shutdown_stage::stopping_partition: - return o << "stopping_partition"; - case partition_manager::partition_shutdown_stage::removing_persistent_state: - return o << "removing_persistent_state"; - case partition_manager::partition_shutdown_stage::stopping_storage: - return o << "stopping_storage"; - case partition_manager::partition_shutdown_stage::removing_storage: - return o << "removing_storage"; - case partition_manager::partition_shutdown_stage::finalizing_remote_storage: - return o << "finalizing_remote_storage"; + case partition_shutdown_stage::shutdown_requested: + return "shutdown_requested"; + case partition_shutdown_stage::stopping_raft: + return "stopping_raft"; + case partition_shutdown_stage::removing_raft: + return "removing_raft"; + case partition_shutdown_stage::stopping_partition: + return "stopping_partition"; + case partition_shutdown_stage::removing_persistent_state: + return "removing_persistent_state"; + case partition_shutdown_stage::stopping_storage: + return "stopping_storage"; + case partition_shutdown_stage::removing_storage: + return "removing_storage"; + case partition_shutdown_stage::finalizing_remote_storage: + return "finalizing_remote_storage"; } + return "unknown"; +} + +fmt::iterator +format_to(partition_manager::partition_shutdown_stage stage, fmt::iterator it) { + return fmt::format_to( + it, "{}", partition_manager::shutdown_stage_string(stage)); } } // namespace cluster diff --git a/src/v/cluster/partition_manager.h b/src/v/cluster/partition_manager.h index 077b2594726df..edd4691df0e1f 100644 --- a/src/v/cluster/partition_manager.h +++ b/src/v/cluster/partition_manager.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "cloud_storage/fwd.h" #include "cloud_storage/remote_path_provider.h" #include "cloud_storage/types.h" @@ -220,6 +221,8 @@ class partition_manager _stm_registry.register_factory(std::forward(args)...); } + fmt::iterator format_to(fmt::iterator it) const; + private: enum class partition_shutdown_stage { shutdown_requested, @@ -312,8 +315,9 @@ class partition_manager // The sharded app may not be initialized if cloud topics isn't enabled. ss::sharded* _cloud_topics_state; - friend std::ostream& operator<<(std::ostream&, const partition_manager&); - friend std::ostream& operator<<( - std::ostream&, const partition_manager::partition_shutdown_stage&); + static std::string_view + shutdown_stage_string(partition_shutdown_stage stage); + + friend fmt::iterator format_to(partition_shutdown_stage, fmt::iterator); }; } // namespace cluster diff --git a/src/v/cluster/partition_properties_stm.cc b/src/v/cluster/partition_properties_stm.cc index f40f89dd0d7cd..b93ee6506f38d 100644 --- a/src/v/cluster/partition_properties_stm.cc +++ b/src/v/cluster/partition_properties_stm.cc @@ -367,42 +367,38 @@ void partition_properties_stm_factory::create( raft->log()->stm_hookset()->add_stm(stm); } -std::ostream& operator<<( - std::ostream& o, const partition_properties_stm::raft_snapshot& snap) { - fmt::print( - o, +fmt::iterator +partition_properties_stm::raft_snapshot::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{writes_disabled: {}, writes_revision_id: {}}}", - snap.writes_disabled, - snap.writes_revision_id); - return o; + writes_disabled, + writes_revision_id); } -std::ostream& operator<<( - std::ostream& o, - const partition_properties_stm::update_writes_disabled_cmd& update) { - fmt::print( - o, +fmt::iterator partition_properties_stm::update_writes_disabled_cmd::format_to( + fmt::iterator it) const { + return fmt::format_to( + it, "{{writes_disabled: {}, writes_revision_id: {}}}", - update.writes_disabled, - update.writes_revision_id); - return o; + writes_disabled, + writes_revision_id); } -std::ostream& operator<<( - std::ostream& o, const partition_properties_stm::local_snapshot& snap) { - fmt::print(o, "{{state_updates: {}}}", fmt::join(snap.state_updates, ", ")); - return o; +fmt::iterator +partition_properties_stm::state_snapshot::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{update_offset: {}, writes_disabled: {}, writes_revision_id: {}}}", + update_offset, + writes_disabled, + writes_revision_id); } -std::ostream& operator<<( - std::ostream& o, const partition_properties_stm::state_snapshot& update) { - fmt::print( - o, - "{{update_offset: {}, writes_disabled: {}, writes_revision_id: {}}}", - update.update_offset, - update.writes_disabled, - update.writes_revision_id); - return o; +fmt::iterator +partition_properties_stm::local_snapshot::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{state_updates: {}}}", fmt::join(state_updates, ", ")); } } // namespace cluster diff --git a/src/v/cluster/partition_properties_stm.h b/src/v/cluster/partition_properties_stm.h index 1e31f4fdbb024..32d4f635978cb 100644 --- a/src/v/cluster/partition_properties_stm.h +++ b/src/v/cluster/partition_properties_stm.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "cluster/state_machine_registry.h" #include "container/chunked_vector.h" #include "model/fundamental.h" @@ -73,6 +74,7 @@ class partition_properties_stm auto serde_fields() { return std::tie(writes_disabled, writes_revision_id); } + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const update_writes_disabled_cmd&, const update_writes_disabled_cmd&) = default; @@ -92,6 +94,7 @@ class partition_properties_stm auto serde_fields() { return std::tie(writes_disabled, update_offset, writes_revision_id); } + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const state_snapshot&, const state_snapshot&) = default; }; @@ -105,6 +108,7 @@ class partition_properties_stm auto serde_fields() { return std::tie(writes_disabled, writes_revision_id); } + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const raft_snapshot&, const raft_snapshot&) = default; @@ -125,14 +129,9 @@ class partition_properties_stm lhs.state_updates.end(), rhs.state_updates.begin()); } + fmt::iterator format_to(fmt::iterator it) const; }; - friend std::ostream& operator<<(std::ostream&, const raft_snapshot&); - friend std::ostream& - operator<<(std::ostream&, const update_writes_disabled_cmd&); - friend std::ostream& operator<<(std::ostream&, const local_snapshot&); - friend std::ostream& operator<<(std::ostream&, const state_snapshot&); - enum class operation_type { update_writes_disabled = 0, }; diff --git a/src/v/cluster/partition_recovery_manager.cc b/src/v/cluster/partition_recovery_manager.cc index 3aaaa3e9d3dbe..c5e0a471b8228 100644 --- a/src/v/cluster/partition_recovery_manager.cc +++ b/src/v/cluster/partition_recovery_manager.cc @@ -11,6 +11,7 @@ #include "cluster/partition_recovery_manager.h" #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "bytes/streambuf.h" #include "cloud_storage/logger.h" #include "cloud_storage/partition_manifest_downloader.h" @@ -240,19 +241,31 @@ using retention = std::variant< std::monostate, size_bound_deletion_parameters, time_bound_deletion_parameters>; +} // namespace cloud_storage -std::ostream& operator<<(std::ostream& o, const retention& r) { - if (std::holds_alternative(r)) { - fmt::print(o, "{{none}}"); - } else if (std::holds_alternative(r)) { - auto p = std::get(r); - fmt::print(o, "{{size-bytes: {}}}", p.bytes); - } else if (std::holds_alternative(r)) { - auto p = std::get(r); - fmt::print(o, "{{time-ms: {}}}", p.duration.count()); +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); } - return o; -} + fmt::iterator + format(const cloud_storage::retention& r, fmt::format_context& ctx) const { + auto it = ctx.out(); + if (std::holds_alternative(r)) { + return fmt::format_to(it, "{{none}}"); + } else if ( + std::holds_alternative( + r)) { + auto p = std::get(r); + return fmt::format_to(it, "{{size-bytes: {}}}", p.bytes); + } else { + auto p = std::get(r); + return fmt::format_to(it, "{{time-ms: {}}}", p.duration.count()); + } + } +}; + +namespace cloud_storage { static retention get_retention_policy(const storage::ntp_config& prop) { if (prop.is_remotely_collectable()) { diff --git a/src/v/cluster/producer_state.cc b/src/v/cluster/producer_state.cc index 2cee5a744fb83..db7160decdb69 100644 --- a/src/v/cluster/producer_state.cc +++ b/src/v/cluster/producer_state.cc @@ -11,19 +11,19 @@ #include "producer_state.h" +#include "base/format_to.h" #include "base/vassert.h" #include "cluster/logger.h" namespace cluster::tx { - -std::ostream& operator<<(std::ostream& os, request_state state) { - switch (state) { +fmt::iterator format_to(request_state e, fmt::iterator out) { + switch (e) { case request_state::initialized: - return os << "initialized"; + return fmt::format_to(out, "initialized"); case request_state::in_progress: - return os << "in_progress"; + return fmt::format_to(out, "in_progress"); case request_state::completed: - return os << "completed"; + return fmt::format_to(out, "completed"); } } @@ -285,46 +285,40 @@ bool producer_state::operator==(const producer_state& other) const { && _requests == other._requests && _transaction_state == other._transaction_state; } - -std::ostream& operator<<(std::ostream& o, const request& request) { - fmt::print( - o, +fmt::iterator request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ first: {}, last: {}, term: {}, result_available: {}, state: {} }}", - request._first_sequence, - request._last_sequence, - request._term, - request._result.available(), - request._state); - return o; -} - -std::ostream& operator<<(std::ostream& o, const requests& requests) { - fmt::print( - o, + _first_sequence, + _last_sequence, + _term, + _result.available(), + _state); +} +fmt::iterator requests::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ inflight: {}, finished: {} }}", - requests._inflight_requests.size(), - requests._finished_requests.size()); - return o; + _inflight_requests.size(), + _finished_requests.size()); } - -std::ostream& operator<<(std::ostream& o, const producer_state& state) { - fmt::print( - o, +fmt::iterator producer_state::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "{{ id: {}, group: {}, requests: {}, " "ms_since_last_update: {}, evicted: {}, last_known_sequence: {}, ", - state._id, - state._group, - state._requests, - state.ms_since_last_update(), - state._evicted, - state.last_sequence_number()); - if (state._transaction_state) { - fmt::print(o, "transaction_state: {}", *state._transaction_state); + _id, + _group, + _requests, + ms_since_last_update(), + _evicted, + last_sequence_number()); + if (_transaction_state) { + it = fmt::format_to(it, "transaction_state: {}", *_transaction_state); } else { - fmt::print(o, "transaction_state: {{ null }}"); + it = fmt::format_to(it, "transaction_state: {{ null }}"); } - fmt::print(o, "}}"); - return o; + return fmt::format_to(it, "}}"); } void producer_state::shutdown_input() { diff --git a/src/v/cluster/producer_state.h b/src/v/cluster/producer_state.h index c014d6dade7f9..c7bef61b4b54d 100644 --- a/src/v/cluster/producer_state.h +++ b/src/v/cluster/producer_state.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "cluster/namespaced_cache.h" #include "cluster/rm_stm_types.h" #include "cluster/types.h" @@ -50,8 +51,7 @@ enum class request_state : uint8_t { in_progress = 1, completed = 2 }; - -std::ostream& operator<<(std::ostream&, request_state); +fmt::iterator format_to(request_state, fmt::iterator); /// A request for a given sequence range, both inclusive. /// The sequence numbers are stamped by the client and are a part @@ -82,7 +82,7 @@ class request { bool operator==(const request&) const; - friend std::ostream& operator<<(std::ostream&, const request&); + fmt::iterator format_to(fmt::iterator it) const; private: request_state _state{request_state::initialized}; @@ -128,7 +128,7 @@ class requests { void shutdown(); bool operator==(const requests&) const; - friend std::ostream& operator<<(std::ostream&, const requests&); + fmt::iterator format_to(fmt::iterator it) const; const request_queue& inflight_requests() const { return _inflight_requests; @@ -188,7 +188,7 @@ class producer_state { ~producer_state() noexcept = default; bool operator==(const producer_state& other) const; - friend std::ostream& operator<<(std::ostream& o, const producer_state&); + fmt::iterator format_to(fmt::iterator it) const; /// Runs the passed async function under the op_lock scope. diff --git a/src/v/cluster/remote_topic_properties.cc b/src/v/cluster/remote_topic_properties.cc index 3fe475971b0c9..2a155a4896c1f 100644 --- a/src/v/cluster/remote_topic_properties.cc +++ b/src/v/cluster/remote_topic_properties.cc @@ -9,17 +9,16 @@ #include "cluster/remote_topic_properties.h" +#include "base/format_to.h" #include "reflection/adl.h" namespace cluster { - -std::ostream& operator<<(std::ostream& o, const remote_topic_properties& rtps) { - fmt::print( - o, +fmt::iterator remote_topic_properties::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{remote_revision: {} remote_partition_count: {}}}", - rtps.remote_revision, - rtps.remote_partition_count); - return o; + remote_revision, + remote_partition_count); } } // namespace cluster diff --git a/src/v/cluster/remote_topic_properties.h b/src/v/cluster/remote_topic_properties.h index 06dfdd7e75e47..66a7e42bbcee7 100644 --- a/src/v/cluster/remote_topic_properties.h +++ b/src/v/cluster/remote_topic_properties.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "model/adl_serde.h" #include "model/fundamental.h" #include "reflection/adl.h" @@ -38,8 +39,7 @@ struct remote_topic_properties friend bool operator==( const remote_topic_properties&, const remote_topic_properties&) = default; - friend std::ostream& - operator<<(std::ostream&, const remote_topic_properties&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace cluster diff --git a/src/v/cluster/rm_stm.h b/src/v/cluster/rm_stm.h index 9723f0df7aa1e..88eae87280161 100644 --- a/src/v/cluster/rm_stm.h +++ b/src/v/cluster/rm_stm.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "bytes/iobuf.h" #include "cluster/fwd.h" #include "cluster/producer_state.h" @@ -368,7 +369,7 @@ class rm_stm final : public raft::persisted_stm<> { uint8_t active_snapshot_version(); - friend std::ostream& operator<<(std::ostream&, const aborted_tx_state&); + fmt::iterator format_to(fmt::iterator it) const; // Defines the commit offset range for the stm bootstrap. // Set on first apply upcall and used to identify if the diff --git a/src/v/cluster/rm_stm_types.cc b/src/v/cluster/rm_stm_types.cc index 05c91cc8bcd2b..9dd90d18ec903 100644 --- a/src/v/cluster/rm_stm_types.cc +++ b/src/v/cluster/rm_stm_types.cc @@ -9,6 +9,7 @@ #include "cluster/rm_stm_types.h" +#include "base/format_to.h" #include "model/record_batch_types.h" #include "storage/record_batch_builder.h" @@ -86,24 +87,18 @@ bool producer_partition_transaction_state::is_in_progress() const { return status == partition_transaction_status::ongoing || status == partition_transaction_status::initialized; } - -std::ostream& -operator<<(std::ostream& o, const partition_transaction_status& status) { - switch (status) { +fmt::iterator format_to(partition_transaction_status e, fmt::iterator out) { + switch (e) { case partition_transaction_status::ongoing: - o << "ongoing"; - break; + return fmt::format_to(out, "ongoing"); case partition_transaction_status::initialized: - o << "initialized"; - break; + return fmt::format_to(out, "initialized"); case partition_transaction_status::committed: - o << "committed"; - break; + return fmt::format_to(out, "committed"); case partition_transaction_status::aborted: - o << "aborted"; - break; + return fmt::format_to(out, "aborted"); } - return o; + return fmt::format_to(out, ""); } ss::sstring partition_transaction_info::get_status() const { @@ -130,30 +125,26 @@ std::optional partition_transaction_info::get_timeout() const { return info->timeout; } - -std::ostream& operator<<(std::ostream& o, const abort_snapshot& as) { - fmt::print( - o, +fmt::iterator abort_snapshot::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{first: {}, last: {}, aborted tx count: {}}}", - as.first, - as.last, - as.aborted.size()); - return o; + first, + last, + aborted.size()); } - -std::ostream& operator<<( - std::ostream& o, const producer_partition_transaction_state& tx_state) { - fmt::print( - o, +fmt::iterator +producer_partition_transaction_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{first: {}, last: {}, sequence: {}, timeout: {}, coordinator " "partition: {}, status: {} }}", - tx_state.first, - tx_state.last, - tx_state.sequence, - tx_state.timeout, - tx_state.coordinator_partition, - tx_state.status); - return o; + first, + last, + sequence, + timeout, + coordinator_partition, + status); } model::record_batch make_fence_batch( @@ -382,17 +373,15 @@ tx_snapshot_v5 tx_snapshot_v6::downgrade_to_v5() && { } return result; } - -std::ostream& operator<<(std::ostream& o, const tx_snapshot_v6& snapshot) { - fmt::print( - o, +fmt::iterator tx_snapshot_v6::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ version: {}, producers: {}, aborted transactions: {}, abort indexes: " "{} }}", tx_snapshot_v6::version, - snapshot.producers.size(), - snapshot.aborted.size(), - snapshot.abort_indexes.size()); - return o; + producers.size(), + aborted.size(), + abort_indexes.size()); } }; // namespace cluster::tx diff --git a/src/v/cluster/rm_stm_types.h b/src/v/cluster/rm_stm_types.h index 9c9689f23e86a..6c4f5be055cb5 100644 --- a/src/v/cluster/rm_stm_types.h +++ b/src/v/cluster/rm_stm_types.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "cluster/types.h" #include "kafka/protocol/wire.h" #include "reflection/async_adl.h" @@ -63,8 +64,7 @@ enum class partition_transaction_status : int8_t { committed = 4, aborted = 5 }; - -std::ostream& operator<<(std::ostream&, const partition_transaction_status&); +fmt::iterator format_to(partition_transaction_status, fmt::iterator); // Captures the information about the transaction within a single data // partition. A user initiated transaction can span multiple data partitions but @@ -138,7 +138,7 @@ struct abort_snapshot { bool match(abort_index idx) { return idx.first == first && idx.last == last; } - friend std::ostream& operator<<(std::ostream&, const abort_snapshot&); + fmt::iterator format_to(fmt::iterator it) const; bool operator==(const abort_snapshot&) const = default; }; @@ -177,15 +177,14 @@ struct producer_partition_transaction_state bool is_in_progress() const; + fmt::iterator format_to(fmt::iterator it) const; + auto serde_fields() { return std::tie( first, last, sequence, timeout, coordinator_partition, status); } }; -std::ostream& -operator<<(std::ostream& o, const producer_partition_transaction_state&); - struct producer_state_snapshot : serde::envelope< producer_state_snapshot, @@ -352,7 +351,7 @@ struct tx_snapshot_v6 tx_snapshot_v5 downgrade_to_v5() &&; - friend std::ostream& operator<<(std::ostream&, const tx_snapshot_v6&); + fmt::iterator format_to(fmt::iterator it) const; bool operator==(const tx_snapshot_v6&) const = default; diff --git a/src/v/cluster/scheduling/allocation_node.cc b/src/v/cluster/scheduling/allocation_node.cc index 2ac023bbc17f1..9c600d42a707e 100644 --- a/src/v/cluster/scheduling/allocation_node.cc +++ b/src/v/cluster/scheduling/allocation_node.cc @@ -11,6 +11,7 @@ #include "cluster/scheduling/allocation_node.h" +#include "base/format_to.h" #include "cluster/logger.h" #include @@ -136,35 +137,31 @@ void allocation_node::update_core_count(uint32_t core_count) { _max_capacity = allocation_capacity( (core_count * _partitions_per_shard()) - _partitions_reserve_shard0()); } - -std::ostream& operator<<(std::ostream& o, allocation_node::state s) { - switch (s) { +fmt::iterator format_to(allocation_node::state e, fmt::iterator out) { + switch (e) { case allocation_node::state::active: - return o << "active"; + return fmt::format_to(out, "active"); case allocation_node::state::decommissioned: - return o << "decommissioned"; + return fmt::format_to(out, "decommissioned"); case allocation_node::state::deleted: - return o << "deleted"; + return fmt::format_to(out, "deleted"); } - return o << "unknown"; + return fmt::format_to(out, "unknown"); } - -std::ostream& operator<<(std::ostream& o, const allocation_node& n) { - fmt::print( - o, +fmt::iterator allocation_node::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "{{node: {}, max_partitions_per_core: {}, state: {}, allocated: {}, " "partition_capacity: {}, weights: [", - n._id, - n._partitions_per_shard(), - n._state, - n._allocated_partitions, - n.partition_capacity()); - - for (auto w : n._weights) { - fmt::print(o, "({})", w); + _id, + _partitions_per_shard(), + _state, + _allocated_partitions, + partition_capacity()); + for (auto w : _weights) { + it = fmt::format_to(it, "({})", w); } - fmt::print(o, "], allocated: {}}}", n._allocated_partitions); - return o; + return fmt::format_to(it, "], allocated: {}}}", _allocated_partitions); } } // namespace cluster diff --git a/src/v/cluster/scheduling/allocation_node.h b/src/v/cluster/scheduling/allocation_node.h index 98bc657e48e94..b893a14f6e48a 100644 --- a/src/v/cluster/scheduling/allocation_node.h +++ b/src/v/cluster/scheduling/allocation_node.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "cluster/scheduling/types.h" #include "cluster/types.h" #include "config/property.h" @@ -57,6 +58,8 @@ class allocation_node { // Overall partition space of the node, less reserved partitions allocation_capacity max_capacity() const { return _max_capacity; } + fmt::iterator format_to(fmt::iterator it) const; + void decommission() { vassert( _state == state::active, @@ -132,7 +135,6 @@ class allocation_node { int32_t _shard0_reserved{0}; uint32_t _cpus; - friend std::ostream& operator<<(std::ostream&, const allocation_node&); - friend std::ostream& operator<<(std::ostream& o, state s); + friend fmt::iterator format_to(state s, fmt::iterator); }; } // namespace cluster diff --git a/src/v/cluster/scheduling/leader_balancer.cc b/src/v/cluster/scheduling/leader_balancer.cc index 7dafcc832cd14..2e246344cc819 100644 --- a/src/v/cluster/scheduling/leader_balancer.cc +++ b/src/v/cluster/scheduling/leader_balancer.cc @@ -41,7 +41,6 @@ #include #include -#include #include #include diff --git a/src/v/cluster/scheduling/types.cc b/src/v/cluster/scheduling/types.cc index 2bfc77b9101d3..aaf4bfd8fe77c 100644 --- a/src/v/cluster/scheduling/types.cc +++ b/src/v/cluster/scheduling/types.cc @@ -11,22 +11,19 @@ #include "cluster/scheduling/types.h" +#include "base/format_to.h" #include "cluster/logger.h" #include "cluster/scheduling/allocation_state.h" #include "utils/exceptions.h" -#include "utils/to_string.h" - -#include namespace cluster { -std::ostream& operator<<(std::ostream& o, const allocation_constraints& a) { - fmt::print( - o, +fmt::iterator allocation_constraints::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{hard_constraints: {}, soft_constraints: {}}}", - a.hard_constraints, - a.soft_constraints); - return o; + hard_constraints, + soft_constraints); } void allocation_constraints::add(allocation_constraints other) { @@ -228,31 +225,27 @@ allocated_partition::~allocated_partition() { _state->add_final_count(bs); } } - -std::ostream& operator<<(std::ostream& o, const partition_constraints& pc) { - fmt::print( - o, +fmt::iterator partition_constraints::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition_id: {}, replication_factor: {}, constraints: {}, " "existing_group: {}, existing_replicas: {}}}", - pc.partition_id, - pc.replication_factor, - pc.constraints, - pc.existing_group, - pc.existing_replicas); - return o; + partition_id, + replication_factor, + constraints, + existing_group, + existing_replicas); } -std::ostream& operator<<(std::ostream& o, const allocation_request& req) { - fmt::print(o, "{{partition_constraints: {}}}", req.partitions); - return o; +fmt::iterator allocation_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition_constraints: [{}]}}", fmt::join(partitions, ", ")); } -std::ostream& -operator<<(std::ostream& o, const simple_allocation_request& req) { - fmt::print( - o, +fmt::iterator simple_allocation_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, additional_partitions: {}, replication_factor: {}}}", - req.tp_ns, - req.additional_partitions, - req.replication_factor); - return o; + tp_ns, + additional_partitions, + replication_factor); } } // namespace cluster diff --git a/src/v/cluster/scheduling/types.h b/src/v/cluster/scheduling/types.h index 6f713d55a70ac..0658c99aabaeb 100644 --- a/src/v/cluster/scheduling/types.h +++ b/src/v/cluster/scheduling/types.h @@ -13,6 +13,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/node_hash_set.h" +#include "base/format_to.h" #include "base/oncore.h" #include "base/vassert.h" #include "cluster/types.h" @@ -76,11 +77,11 @@ class hard_constraint { ss::sstring name() const { return _impl->name(); } -private: - friend std::ostream& operator<<(std::ostream& o, const hard_constraint& c) { - fmt::print(o, "hard: [{}]", c.name()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "hard: [{}]", name()); } + +private: std::unique_ptr _impl; }; @@ -114,12 +115,11 @@ class soft_constraint final { ss::sstring name() const { return _impl->name(); } -private: - friend std::ostream& operator<<(std::ostream& o, const soft_constraint& c) { - fmt::print(o, "soft: [{}]", c.name()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "soft: [{}]", name()); } +private: std::unique_ptr _impl; }; @@ -177,8 +177,8 @@ struct allocation_constraints { } void add(allocation_constraints); - friend std::ostream& - operator<<(std::ostream&, const allocation_constraints&); + + fmt::iterator format_to(fmt::iterator it) const; }; /** * RAII based helper holding allocated partitions, allocation is reverted @@ -336,8 +336,7 @@ struct partition_constraints { std::optional existing_group; replicas_t existing_replicas; - friend std::ostream& - operator<<(std::ostream&, const partition_constraints&); + fmt::iterator format_to(fmt::iterator it) const; }; using node2count_t = absl::flat_hash_map; @@ -358,7 +357,7 @@ struct allocation_request { // objective. std::optional existing_replica_counts; - friend std::ostream& operator<<(std::ostream&, const allocation_request&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -393,8 +392,7 @@ struct simple_allocation_request { // objective. std::optional existing_replica_counts; - friend std::ostream& - operator<<(std::ostream&, const simple_allocation_request&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace cluster diff --git a/src/v/cluster/self_test_rpc_types.cc b/src/v/cluster/self_test_rpc_types.cc index 055cde93331e3..ad4f7f3aa9ec9 100644 --- a/src/v/cluster/self_test_rpc_types.cc +++ b/src/v/cluster/self_test_rpc_types.cc @@ -11,6 +11,7 @@ #include "cluster/self_test_rpc_types.h" +#include "base/format_to.h" #include "random/generators.h" #include "ssx/future-util.h" @@ -30,10 +31,16 @@ ss::sstring self_test_status_as_string(self_test_status sts) { __builtin_unreachable(); } } - -std::ostream& operator<<(std::ostream& o, self_test_status sts) { - fmt::print(o, "{}", self_test_status_as_string(sts)); - return o; +fmt::iterator format_to(self_test_status sts, fmt::iterator out) { + switch (sts) { + case self_test_status::idle: + return fmt::format_to(out, "idle"); + case self_test_status::running: + return fmt::format_to(out, "running"); + case self_test_status::unreachable: + return fmt::format_to(out, "unreachable"); + } + __builtin_unreachable(); } ss::sstring self_test_stage_as_string(self_test_stage sts) { @@ -48,10 +55,18 @@ ss::sstring self_test_stage_as_string(self_test_stage sts) { return "cloud"; } } - -std::ostream& operator<<(std::ostream& o, self_test_stage sts) { - fmt::print(o, "{}", self_test_stage_as_string(sts)); - return o; +fmt::iterator format_to(self_test_stage sts, fmt::iterator out) { + switch (sts) { + case self_test_stage::idle: + return fmt::format_to(out, "idle"); + case self_test_stage::disk: + return fmt::format_to(out, "disk"); + case self_test_stage::net: + return fmt::format_to(out, "net"); + case self_test_stage::cloud: + return fmt::format_to(out, "cloud"); + } + return fmt::format_to(out, ""); } ss::future diff --git a/src/v/cluster/self_test_rpc_types.h b/src/v/cluster/self_test_rpc_types.h index 57f1e7695371a..a2ea85ce23b40 100644 --- a/src/v/cluster/self_test_rpc_types.h +++ b/src/v/cluster/self_test_rpc_types.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "config/node_config.h" #include "json/document.h" #include "model/metadata.h" @@ -33,13 +34,13 @@ enum class self_test_status : int8_t { idle = 0, running, unreachable }; ss::sstring self_test_status_as_string(self_test_status sts); -std::ostream& operator<<(std::ostream& o, self_test_status sts); +fmt::iterator format_to(self_test_status sts, fmt::iterator); enum class self_test_stage : int8_t { idle = 0, disk, net, cloud }; ss::sstring self_test_stage_as_string(self_test_stage sts); -std::ostream& operator<<(std::ostream& o, self_test_stage sts); +fmt::iterator format_to(self_test_stage sts, fmt::iterator); struct diskcheck_opts : serde:: @@ -112,22 +113,19 @@ struct diskcheck_opts duration, parallelism); } - - friend std::ostream& - operator<<(std::ostream& o, const diskcheck_opts& opts) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{name: {} dsync: {} skip_write: {} skip_read: {} data_size: {} " "request_size: {} duration: {} parallelism: {}}}", - opts.name, - opts.dsync, - opts.skip_write, - opts.skip_read, - opts.data_size, - opts.request_size, - opts.duration, - opts.parallelism); - return o; + name, + dsync, + skip_write, + skip_read, + data_size, + request_size, + duration, + parallelism); } }; @@ -175,20 +173,17 @@ struct netcheck_opts return std::tie( name, peers, request_size, duration, max_duration, parallelism); } - - friend std::ostream& - operator<<(std::ostream& o, const netcheck_opts& opts) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{name: {} peers: {} request_size: {} duration: " "{} max_duration: {} parallelism: {}}}", - opts.name, - opts.peers, - opts.request_size, - opts.duration.count(), - opts.max_duration.count(), - opts.parallelism); - return o; + name, + peers, + request_size, + duration.count(), + max_duration.count(), + parallelism); } }; @@ -225,16 +220,9 @@ struct cloudcheck_opts } auto serde_fields() { return std::tie(name, timeout, backoff); } - - friend std::ostream& - operator<<(std::ostream& o, const cloudcheck_opts& opts) { - fmt::print( - o, - "{{name: {} timeout: {} backoff: {}}}", - opts.name, - opts.timeout, - opts.backoff); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{name: {} timeout: {} backoff: {}}}", name, timeout, backoff); } }; @@ -245,15 +233,9 @@ struct unparsed_check ss::sstring test_type; ss::sstring test_json; auto serde_fields() { return std::tie(test_type, test_json); } - - friend std::ostream& - operator<<(std::ostream& o, const unparsed_check& unparsed_check) { - fmt::print( - o, - "{{test_type: {}, test_json: {}}}", - unparsed_check.test_type, - unparsed_check.test_json); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{test_type: {}, test_json: {}}}", test_type, test_json); } }; @@ -278,34 +260,31 @@ struct self_test_result ss::lowres_clock::duration duration{}; std::optional warning; std::optional error; - - friend std::ostream& - operator<<(std::ostream& o, const self_test_result& r) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{p50: {} p90: {} p99: {} p999: {} max: {} rps: {} bps: {} " "timeouts: {} test_id: {} name: {} info: {} type: {} start_time: {} " "end_time: {} duration: {}ms " "warning: {} error: {}}}", - r.p50, - r.p90, - r.p99, - r.p999, - r.max, - r.rps, - r.bps, - r.timeouts, - r.test_id, - r.name, - r.info, - r.test_type, - r.start_time, - r.end_time, - std::chrono::duration_cast(r.duration) + p50, + p90, + p99, + p999, + max, + rps, + bps, + timeouts, + test_id, + name, + info, + test_type, + start_time, + end_time, + std::chrono::duration_cast(duration) .count(), - r.warning ? *r.warning : "", - r.error ? *r.error : ""); - return o; + warning ? *warning : "", + error ? *error : ""); } auto serde_fields() { @@ -350,24 +329,21 @@ struct start_test_request auto serde_fields() { return std::tie(id, dtos, ntos, unparsed_checks, ctos); } - - friend std::ostream& - operator<<(std::ostream& o, const start_test_request& r) { + fmt::iterator format_to(fmt::iterator it) const { std::stringstream ss; - for (const auto& v : r.dtos) { + for (const auto& v : dtos) { fmt::print(ss, "diskcheck_opts: {}", v); } - for (const auto& v : r.ntos) { + for (const auto& v : ntos) { fmt::print(ss, "netcheck_opts: {}", v); } - for (const auto& v : r.ctos) { + for (const auto& v : ctos) { fmt::print(ss, "cloudcheck_opts: {}", v); } - for (const auto& v : r.unparsed_checks) { + for (const auto& v : unparsed_checks) { fmt::print(ss, "unparsed_check: {}", v); } - fmt::print(o, "{{id: {} {}}}", r.id, ss.str()); - return o; + return fmt::format_to(it, "{{id: {} {}}}", id, ss.str()); } }; @@ -382,17 +358,14 @@ struct get_status_response self_test_stage stage{}; auto serde_fields() { return std::tie(id, status, results, stage); } - - friend std::ostream& - operator<<(std::ostream& o, const get_status_response& r) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {} status: {} stage: {} test_results: {}}}", - r.id, - r.status, - r.stage, - r.results); - return o; + id, + status, + stage, + results); } }; @@ -402,10 +375,9 @@ struct netcheck_request model::node_id source; iobuf buf; auto serde_fields() { return std::tie(source, buf); } - friend std::ostream& - operator<<(std::ostream& o, const netcheck_request& r) { - fmt::print(o, "{{source: {} buf: {}}}", r.source, r.buf.size_bytes()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{source: {} buf: {}}}", source, buf.size_bytes()); } }; @@ -415,11 +387,8 @@ struct netcheck_response size_t bytes_read{0}; auto serde_fields() { return std::tie(bytes_read); } - - friend std::ostream& - operator<<(std::ostream& o, const netcheck_response& r) { - fmt::print(o, "{{bytes_read: {}}}", r.bytes_read); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{bytes_read: {}}}", bytes_read); } }; diff --git a/src/v/cluster/shard_placement_table.cc b/src/v/cluster/shard_placement_table.cc index ce9e0a072e7c8..ce5fe1da46d58 100644 --- a/src/v/cluster/shard_placement_table.cc +++ b/src/v/cluster/shard_placement_table.cc @@ -11,6 +11,7 @@ #include "cluster/shard_placement_table.h" +#include "base/format_to.h" #include "cluster/cluster_utils.h" #include "cluster/logger.h" #include "cluster/topic_table.h" @@ -67,41 +68,36 @@ class shard_placement_table::probe { metrics::internal_metric_groups _metrics; }; - -std::ostream& operator<<( - std::ostream& o, const shard_placement_table::shard_local_assignment& as) { - fmt::print( - o, +fmt::iterator shard_placement_table::shard_local_assignment::format_to( + fmt::iterator it) const { + return fmt::format_to( + it, "{{group: {}, log_revision: {}, shard_revision: {}}}", - as.group, - as.log_revision, - as.shard_revision); - return o; + group, + log_revision, + shard_revision); } - -std::ostream& -operator<<(std::ostream& o, shard_placement_table::hosted_status s) { - switch (s) { +fmt::iterator +format_to(shard_placement_table::hosted_status e, fmt::iterator out) { + switch (e) { case shard_placement_table::hosted_status::receiving: - return o << "receiving"; + return fmt::format_to(out, "receiving"); case shard_placement_table::hosted_status::hosted: - return o << "hosted"; + return fmt::format_to(out, "hosted"); case shard_placement_table::hosted_status::obsolete: - return o << "obsolete"; + return fmt::format_to(out, "obsolete"); } __builtin_unreachable(); } - -std::ostream& operator<<( - std::ostream& o, const shard_placement_table::shard_local_state& ls) { - fmt::print( - o, +fmt::iterator +shard_placement_table::shard_local_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{group: {}, log_revision: {}, status: {}, shard_revision: {}}}", - ls.group, - ls.log_revision, - ls.status, - ls.shard_revision); - return o; + group, + log_revision, + status, + shard_revision); } shard_placement_table::reconciliation_action diff --git a/src/v/cluster/shard_placement_table.h b/src/v/cluster/shard_placement_table.h index 687c97e44ac4b..52598579e5217 100644 --- a/src/v/cluster/shard_placement_table.h +++ b/src/v/cluster/shard_placement_table.h @@ -55,8 +55,7 @@ class shard_placement_table model::revision_id log_revision; model::shard_revision_id shard_revision; - friend std::ostream& - operator<<(std::ostream&, const shard_local_assignment&); + fmt::iterator format_to(fmt::iterator it) const; }; enum class hosted_status { @@ -68,6 +67,7 @@ class shard_placement_table /// deleted. obsolete, }; + friend fmt::iterator format_to(hosted_status, fmt::iterator); // unused. This is from a reverted piece of code, retained for serde // compatibility @@ -102,8 +102,7 @@ class shard_placement_table : shard_local_state( as.group, as.log_revision, status, as.shard_revision) {} - friend std::ostream& - operator<<(std::ostream&, const shard_local_state&); + fmt::iterator format_to(fmt::iterator it) const; }; enum class reconciliation_action { @@ -127,9 +126,7 @@ class shard_placement_table reconciliation_action get_reconciliation_action( std::optional expected_log_revision) const; - friend std::ostream& operator<<(std::ostream&, const placement_state&); - - fmt::iterator format_to(fmt::iterator) const; + fmt::iterator format_to(fmt::iterator it) const; placement_state() = default; @@ -310,8 +307,6 @@ class shard_placement_table std::unique_ptr _probe; }; -std::ostream& operator<<(std::ostream&, shard_placement_table::hosted_status); - /// Enum with all key types in the shard_placement key space. All keys in this /// key space must be prefixed with the serialized type. Enum type is /// irrelevant, as serde will serialize to 32 bit anyway. diff --git a/src/v/cluster/tests/client_quota_store_test.cc b/src/v/cluster/tests/client_quota_store_test.cc index 44ccc96b67a29..984cb9b6f4aab 100644 --- a/src/v/cluster/tests/client_quota_store_test.cc +++ b/src/v/cluster/tests/client_quota_store_test.cc @@ -16,7 +16,6 @@ #include #include #include -#include namespace cluster::client_quota { diff --git a/src/v/cluster/tests/tx_compaction_utils.h b/src/v/cluster/tests/tx_compaction_utils.h index 3af79217bbb8d..ae801887041fb 100644 --- a/src/v/cluster/tests/tx_compaction_utils.h +++ b/src/v/cluster/tests/tx_compaction_utils.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "cluster/logger.h" #include "cluster/rm_stm.h" #include "model/record.h" @@ -71,17 +72,16 @@ class tx_executor { bool _interleave = false; bool _compact = true; - friend std::ostream& operator<<(std::ostream& os, const spec& s) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ num_txes: {}, num_rolls: {}, type: {}, interleave: {}, " "compact: {} }}", - s._num_txes, - s._num_rolls, - s._types, - s._interleave, - s._compact); - return os; + _num_txes, + _num_rolls, + static_cast(_types), + _interleave, + _compact); } }; diff --git a/src/v/cluster/tm_stm.h b/src/v/cluster/tm_stm.h index 65d9064eaedb0..4964c99db69b3 100644 --- a/src/v/cluster/tm_stm.h +++ b/src/v/cluster/tm_stm.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/btree_set.h" +#include "base/format_to.h" #include "cluster/fwd.h" #include "cluster/logger.h" #include "cluster/state_machine_registry.h" @@ -140,16 +141,13 @@ class tm_stm final : public raft::persisted_stm<> { operator==(const draining_txs&, const draining_txs&) = default; auto serde_fields() { return std::tie(id, ranges, transactions); } - - friend std::ostream& - operator<<(std::ostream& o, const draining_txs& txes) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ id: {}, ranges: {}, transactions: {} }}", - txes.id, - txes.ranges, - txes.transactions.size()); - return o; + id, + ranges, + transactions.size()); } }; @@ -193,19 +191,16 @@ class tm_stm final : public raft::persisted_stm<> { included_transactions, draining); } - - friend std::ostream& - operator<<(std::ostream& o, const locally_hosted_txs& txes) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ inited: {}, hash ranges: {}, excluded: {}, included: {}, " "draining: {} }}", - txes.inited, - txes.hash_ranges, - txes.excluded_transactions.size(), - txes.included_transactions.size(), - txes.draining); - return o; + inited, + hash_ranges, + excluded_transactions.size(), + included_transactions.size(), + draining); } }; @@ -513,3 +508,13 @@ struct adl { }; } // namespace reflection + +template<> +struct fmt::formatter + : fmt::formatter> { + auto format(cluster::tm_stm::op_status s, fmt::format_context& ctx) const { + return fmt:: + formatter>::format( + static_cast(s), ctx); + } +}; diff --git a/src/v/cluster/tm_stm_types.cc b/src/v/cluster/tm_stm_types.cc index 513861a88a9d4..b7bd866ab2ee4 100644 --- a/src/v/cluster/tm_stm_types.cc +++ b/src/v/cluster/tm_stm_types.cc @@ -10,6 +10,7 @@ */ #include "cluster/tm_stm_types.h" +#include "base/format_to.h" #include "model/timeout_clock.h" #include @@ -162,60 +163,50 @@ tx_metadata::try_update_status(tx_status requested) { last_update_ts = ss::lowres_system_clock::now(); return std::nullopt; } - -std::ostream& operator<<(std::ostream& o, tx_status status) { - switch (status) { +fmt::iterator format_to(tx_status e, fmt::iterator out) { + switch (e) { case tx_status::ongoing: - return o << "ongoing"; + return fmt::format_to(out, "ongoing"); case tx_status::preparing_abort: - return o << "preparing_abort"; + return fmt::format_to(out, "preparing_abort"); case tx_status::preparing_commit: - return o << "preparing_commit"; + return fmt::format_to(out, "preparing_commit"); case tx_status::completed_commit: - return o << "completed_commit"; + return fmt::format_to(out, "completed_commit"); case tx_status::preparing_internal_abort: - return o << "expired"; + return fmt::format_to(out, "expired"); case tx_status::empty: - return o << "empty"; + return fmt::format_to(out, "empty"); case tx_status::tombstone: - return o << "tombstone"; + return fmt::format_to(out, "tombstone"); case tx_status::completed_abort: - return o << "completed_abort"; + return fmt::format_to(out, "completed_abort"); } } -std::ostream& operator<<(std::ostream& o, const tx_metadata::tx_partition& tp) { - fmt::print( - o, - "{{ntp: {}, etag: {}, revision: {}}}", - tp.ntp, - tp.etag, - tp.topic_revision); - return o; +fmt::iterator tx_metadata::tx_partition::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, etag: {}, revision: {}}}", ntp, etag, topic_revision); } - -std::ostream& operator<<(std::ostream& o, const tx_metadata& tx) { - fmt::print( - o, +fmt::iterator tx_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, status: {}, pid: {}, last_pid: {}, etag: {}, seq: {}, " "partitions: {}}}", - tx.id, - tx.status, - tx.pid, - tx.last_pid, - tx.etag, - tx.tx_seq, - fmt::join(tx.partitions, ", ")); - return o; + id, + status, + pid, + last_pid, + etag, + tx_seq, + fmt::join(partitions, ", ")); } - -std::ostream& operator<<(std::ostream& o, const state_transition_error& err) { - fmt::print( - o, +fmt::iterator state_transition_error::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "Can not update transaction state from {} to {} as this transition is " "invalid", - err.from, - err.to); - return o; + from, + to); } } // namespace cluster diff --git a/src/v/cluster/tm_stm_types.h b/src/v/cluster/tm_stm_types.h index e738f7e6df5ae..47a4357d25b11 100644 --- a/src/v/cluster/tm_stm_types.h +++ b/src/v/cluster/tm_stm_types.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "kafka/protocol/types.h" #include "model/fundamental.h" #include "model/record.h" @@ -103,8 +104,8 @@ enum class tx_status : int32_t { */ tombstone = 6, }; +fmt::iterator format_to(tx_status, fmt::iterator); -std::ostream& operator<<(std::ostream&, tx_status); /** * Simple tuple representing state transition error. */ @@ -114,8 +115,7 @@ struct state_transition_error { tx_status from; tx_status to; - friend std::ostream& - operator<<(std::ostream&, const state_transition_error&); + fmt::iterator format_to(fmt::iterator it) const; }; struct tx_metadata { @@ -133,7 +133,7 @@ struct tx_metadata { model::revision_id topic_revision; bool operator==(const tx_partition& other) const = default; - friend std::ostream& operator<<(std::ostream&, const tx_partition&); + fmt::iterator format_to(fmt::iterator it) const; }; struct tx_group { @@ -193,7 +193,7 @@ struct tx_metadata { */ bool is_finished() const; - friend std::ostream& operator<<(std::ostream&, const tx_metadata&); + fmt::iterator format_to(fmt::iterator it) const; }; bool is_state_transition_valid(const tx_metadata&, tx_status); @@ -369,3 +369,23 @@ struct transaction_metadata_v0 { }; }; } // namespace cluster + +template<> +struct fmt::formatter + : fmt::formatter { + auto format( + cluster::transaction_metadata_v1::tx_status s, + fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(s), ctx); + } +}; + +template<> +struct fmt::formatter + : fmt::formatter { + auto format( + cluster::transaction_metadata_v0::tx_status s, + fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(s), ctx); + } +}; diff --git a/src/v/cluster/topic_configuration.cc b/src/v/cluster/topic_configuration.cc index 3f34606a4ea25..c26d57587c3ff 100644 --- a/src/v/cluster/topic_configuration.cc +++ b/src/v/cluster/topic_configuration.cc @@ -9,6 +9,7 @@ #include "cluster/topic_configuration.h" +#include "base/format_to.h" #include "model/fundamental.h" #include "model/metadata.h" #include "model/namespace.h" @@ -110,20 +111,17 @@ void topic_configuration::serde_read(iobuf_parser& in, const serde::header& h) { properties.remote_delete = storage::ntp_config::legacy_remote_delete; } } - -std::ostream& operator<<(std::ostream& o, const topic_configuration& cfg) { - fmt::print( - o, +fmt::iterator topic_configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ topic: {}, partition_count: {}, replication_factor: {}, is_migrated: " "{}, topic_id: {}, properties: {}}}", - cfg.tp_ns, - cfg.partition_count, - cfg.replication_factor, - cfg.is_migrated, - cfg.tp_id, - cfg.properties); - - return o; + tp_ns, + partition_count, + replication_factor, + is_migrated, + tp_id, + properties); } } // namespace cluster diff --git a/src/v/cluster/topic_configuration.h b/src/v/cluster/topic_configuration.h index e91f5867d39b4..490ce6968cc50 100644 --- a/src/v/cluster/topic_configuration.h +++ b/src/v/cluster/topic_configuration.h @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "cluster/topic_properties.h" #include "model/fundamental.h" #include "model/metadata.h" @@ -95,7 +96,7 @@ struct topic_configuration void serde_write(iobuf& out) const; void serde_read(iobuf_parser& in, const serde::header& h); - friend std::ostream& operator<<(std::ostream&, const topic_configuration&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const topic_configuration&, const topic_configuration&) = default; diff --git a/src/v/cluster/topic_properties.cc b/src/v/cluster/topic_properties.cc index 55a52bed7ffdd..97c95dd0d3d5d 100644 --- a/src/v/cluster/topic_properties.cc +++ b/src/v/cluster/topic_properties.cc @@ -9,15 +9,15 @@ #include "cluster/topic_properties.h" +#include "base/format_to.h" #include "model/adl_serde.h" #include "model/metadata.h" #include "reflection/adl.h" namespace cluster { - -std::ostream& operator<<(std::ostream& o, const topic_properties& properties) { - fmt::print( - o, +fmt::iterator topic_properties::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ compression: {}, cleanup_policy_bitflags: {}, compaction_strategy: " "{}, retention_bytes: {}, retention_duration_ms: {}, segment_size: {}, " "timestamp_type: {}, recovery_enabled: {}, shadow_indexing: {}, " @@ -53,58 +53,54 @@ std::ostream& operator<<(std::ostream& o, const topic_properties& properties) { "max_compaction_lag_ms: {}, " "message_timestamp_before_max_ms: {}, " "message_timestamp_after_max_ms: {}, " - "redpanda_storage_mode: {}", - properties.compression, - properties.cleanup_policy_bitflags, - properties.compaction_strategy, - properties.retention_bytes, - properties.retention_duration, - properties.segment_size, - properties.timestamp_type, - properties.recovery, - properties.shadow_indexing, - properties.read_replica, - properties.read_replica_bucket, - properties.remote_topic_namespace_override, - properties.remote_topic_properties, - properties.remote_topic_allow_gaps, - properties.batch_max_bytes, - properties.retention_local_target_bytes, - properties.retention_local_target_ms, - properties.remote_delete, - properties.segment_ms, - properties.record_key_schema_id_validation, - properties.record_key_schema_id_validation_compat, - properties.record_key_subject_name_strategy, - properties.record_key_subject_name_strategy_compat, - properties.record_value_schema_id_validation, - properties.record_value_schema_id_validation_compat, - properties.record_value_subject_name_strategy, - properties.record_value_subject_name_strategy_compat, - properties.initial_retention_local_target_bytes, - properties.initial_retention_local_target_ms, - properties.mpx_virtual_cluster_id, - properties.write_caching, - properties.flush_ms, - properties.flush_bytes, - properties.remote_label, - properties.iceberg_mode, - properties.leaders_preference, - properties.delete_retention_ms, - properties.iceberg_delete, - properties.iceberg_partition_spec, - properties.iceberg_invalid_record_action, - properties.iceberg_target_lag_ms, - properties.min_cleanable_dirty_ratio, - properties.min_compaction_lag_ms, - properties.max_compaction_lag_ms, - properties.message_timestamp_before_max_ms, - properties.message_timestamp_after_max_ms, - properties.storage_mode); - - o << "}"; - - return o; + "redpanda_storage_mode: {}}}", + compression, + cleanup_policy_bitflags, + compaction_strategy, + retention_bytes, + retention_duration, + segment_size, + timestamp_type, + recovery, + shadow_indexing, + read_replica, + read_replica_bucket, + remote_topic_namespace_override, + remote_topic_properties, + remote_topic_allow_gaps, + batch_max_bytes, + retention_local_target_bytes, + retention_local_target_ms, + remote_delete, + segment_ms, + record_key_schema_id_validation, + record_key_schema_id_validation_compat, + record_key_subject_name_strategy, + record_key_subject_name_strategy_compat, + record_value_schema_id_validation, + record_value_schema_id_validation_compat, + record_value_subject_name_strategy, + record_value_subject_name_strategy_compat, + initial_retention_local_target_bytes, + initial_retention_local_target_ms, + mpx_virtual_cluster_id, + write_caching, + flush_ms, + flush_bytes, + remote_label, + iceberg_mode, + leaders_preference, + delete_retention_ms, + iceberg_delete, + iceberg_partition_spec, + iceberg_invalid_record_action, + iceberg_target_lag_ms, + min_cleanable_dirty_ratio, + min_compaction_lag_ms, + max_compaction_lag_ms, + message_timestamp_before_max_ms, + message_timestamp_after_max_ms, + storage_mode); } bool topic_properties::is_compacted() const { if (!cleanup_policy_bitflags) { diff --git a/src/v/cluster/topic_properties.h b/src/v/cluster/topic_properties.h index a465f944411e4..cadad747f41d3 100644 --- a/src/v/cluster/topic_properties.h +++ b/src/v/cluster/topic_properties.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "cloud_storage/remote_label.h" #include "cluster/remote_topic_properties.h" #include "model/compression.h" @@ -264,7 +265,7 @@ struct topic_properties storage::ntp_config::default_overrides get_ntp_cfg_overrides() const; - friend std::ostream& operator<<(std::ostream&, const topic_properties&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie( compression, diff --git a/src/v/cluster/topic_recovery_service.cc b/src/v/cluster/topic_recovery_service.cc index 78fd9b1ea310d..415c2e0de79da 100644 --- a/src/v/cluster/topic_recovery_service.cc +++ b/src/v/cluster/topic_recovery_service.cc @@ -10,6 +10,7 @@ #include "cluster/topic_recovery_service.h" +#include "base/format_to.h" #include "cloud_storage/logger.h" #include "cloud_storage/recovery_request.h" #include "cloud_storage/recovery_utils.h" @@ -70,25 +71,21 @@ constexpr int status_log_size{5}; } // namespace namespace cloud_storage { - -std::ostream& operator<<(std::ostream& os, const init_recovery_result& result) { - fmt::print( - os, +fmt::iterator init_recovery_result::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{status_code: {}, message: {}}}", - static_cast(result.status_code), - result.message); - return os; + static_cast(status_code), + message); } - -std::ostream& operator<<(std::ostream& os, const topic_download_counts& tds) { - fmt::print( - os, +fmt::iterator topic_download_counts::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{pending_downloads: {}, successful_downloads: {}, failed_downloads: " "{}}}", - tds.pending_downloads, - tds.successful_downloads, - tds.failed_downloads); - return os; + pending_downloads, + successful_downloads, + failed_downloads); } static ss::sstring get_value_or_throw( @@ -585,43 +582,34 @@ void topic_recovery_service::populate_recovery_status() { ntp_cfg->tp_ns, topic_download_counts{expected, 0, 0}); } } - -std::ostream& -operator<<(std::ostream& os, const topic_recovery_service::state& state) { - switch (state) { +fmt::iterator format_to(topic_recovery_service::state e, fmt::iterator out) { + switch (e) { case topic_recovery_service::state::inactive: - os << "inactive"; - break; + return fmt::format_to(out, "inactive"); case topic_recovery_service::state::starting: - os << "starting"; - break; + return fmt::format_to(out, "starting"); case topic_recovery_service::state::scanning_bucket: - os << "scanning_bucket"; - break; + return fmt::format_to(out, "scanning_bucket"); case topic_recovery_service::state::creating_topics: - os << "creating_topics"; - break; + return fmt::format_to(out, "creating_topics"); case topic_recovery_service::state::recovering_data: - os << "recovering_data"; - break; + return fmt::format_to(out, "recovering_data"); } - return os; + return fmt::format_to(out, ""); } - -std::ostream& -operator<<(std::ostream& os, const topic_recovery_service::recovery_status& r) { - std::string request = "none"; - if (r.request.has_value()) { - request = fmt::format("{}", r.request); +fmt::iterator +topic_recovery_service::recovery_status::format_to(fmt::iterator it) const { + std::string req_str = "none"; + if (request.has_value()) { + req_str = fmt::format("{}", request); } - fmt::print( - os, + return fmt::format_to( + it, "{{state: {}, topics being downloaded: {}, recovery request: {}}}", - r.state, - r.download_counts.size(), - request); - return os; + state, + download_counts.size(), + req_str); } } // namespace cloud_storage diff --git a/src/v/cluster/topic_recovery_service.h b/src/v/cluster/topic_recovery_service.h index 659cf386460b7..7ed003c194b5d 100644 --- a/src/v/cluster/topic_recovery_service.h +++ b/src/v/cluster/topic_recovery_service.h @@ -38,10 +38,9 @@ struct init_recovery_result { ss::sstring message; bool operator==(const init_recovery_result&) const = default; + fmt::iterator format_to(fmt::iterator it) const; }; -std::ostream& operator<<(std::ostream&, const init_recovery_result&); - struct recovery_task_config { cloud_storage_clients::bucket_name bucket; ss::lowres_clock::duration operation_timeout_ms; @@ -53,9 +52,9 @@ struct topic_download_counts { int pending_downloads; int successful_downloads; int failed_downloads; -}; -std::ostream& operator<<(std::ostream&, const topic_download_counts&); + fmt::iterator format_to(fmt::iterator it) const; +}; struct topic_recovery_service : ss::peering_sharded_service { @@ -66,6 +65,7 @@ struct topic_recovery_service creating_topics, recovering_data, }; + friend fmt::iterator format_to(state, fmt::iterator); using download_counts = absl::flat_hash_map; @@ -74,6 +74,8 @@ struct topic_recovery_service state state; download_counts download_counts; std::optional request; + + fmt::iterator format_to(fmt::iterator it) const; }; static constexpr int shard_id = 0; @@ -193,9 +195,4 @@ struct topic_recovery_service boost::circular_buffer _status_log; }; -std::ostream& -operator<<(std::ostream&, const topic_recovery_service::recovery_status&); - -std::ostream& operator<<(std::ostream&, const topic_recovery_service::state&); - } // namespace cloud_storage diff --git a/src/v/cluster/topic_recovery_status_types.cc b/src/v/cluster/topic_recovery_status_types.cc index e897cb40f6486..8f86848bdd38c 100644 --- a/src/v/cluster/topic_recovery_status_types.cc +++ b/src/v/cluster/topic_recovery_status_types.cc @@ -11,6 +11,8 @@ #include "cluster/topic_recovery_status_types.h" +#include "base/format_to.h" + namespace cluster { void recovery_request_params::populate( @@ -21,18 +23,15 @@ void recovery_request_params::populate( retention_ms = r->retention_ms(); } } - -std::ostream& operator<<(std::ostream& os, const recovery_request_params& req) { - fmt::print( +fmt::iterator recovery_request_params::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic_names_pattern: {}, retention_bytes: {}, retention_ms: {}}}", - req.topic_names_pattern.has_value() ? req.topic_names_pattern.value() - : "none", - req.retention_bytes.has_value() - ? fmt::format("{}", req.retention_bytes.value()) - : "none", - req.retention_ms.has_value() ? fmt::format("{}", req.retention_ms.value()) - : "none"); - return os; + topic_names_pattern.has_value() ? topic_names_pattern.value() : "none", + retention_bytes.has_value() ? fmt::format("{}", retention_bytes.value()) + : "none", + retention_ms.has_value() ? fmt::format("{}", retention_ms.value()) + : "none"); } bool status_response::is_active() const { diff --git a/src/v/cluster/topic_recovery_status_types.h b/src/v/cluster/topic_recovery_status_types.h index b740c5cfb728e..9dd57483364ab 100644 --- a/src/v/cluster/topic_recovery_status_types.h +++ b/src/v/cluster/topic_recovery_status_types.h @@ -58,9 +58,9 @@ struct recovery_request_params friend bool operator==( const recovery_request_params&, const recovery_request_params&) = default; -}; -std::ostream& operator<<(std::ostream&, const recovery_request_params&); + fmt::iterator format_to(fmt::iterator it) const; +}; struct single_status : serde:: diff --git a/src/v/cluster/topic_table.cc b/src/v/cluster/topic_table.cc index 1ab67dca83de5..0e576790689a0 100644 --- a/src/v/cluster/topic_table.cc +++ b/src/v/cluster/topic_table.cc @@ -9,6 +9,7 @@ #include "cluster/topic_table.h" +#include "base/format_to.h" #include "cluster/cluster_utils.h" #include "cluster/commands.h" #include "cluster/controller_snapshot.h" @@ -2203,34 +2204,30 @@ void topic_table::on_partition_deletion(const model::ntp& ntp) { it->first); } } - -std::ostream& -operator<<(std::ostream& o, const topic_table::in_progress_update& u) { - fmt::print( - o, +fmt::iterator +topic_table::in_progress_update::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{state: {}, update_rev: {}, last_cmd_rev: {}, previous: {}, target: " "{}, policy: {}}}", - u._state, - u._update_revision, - u._last_cmd_revision, - u._previous_replicas, - u._target_replicas, - u._policy); - return o; + _state, + _update_revision, + _last_cmd_revision, + _previous_replicas, + _target_replicas, + _policy); } - -std::ostream& -operator<<(std::ostream& o, const topic_table::partition_replicas_view& ri) { - fmt::print( - o, +fmt::iterator +topic_table::partition_replicas_view::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "{{orig_replicas: {}, last_update_finished_revision: {}", - ri.orig_replicas(), - ri.last_update_finished_revision()); - if (ri.update) { - fmt::print(o, ", update: {}", *ri.update); + orig_replicas(), + last_update_finished_revision()); + if (update) { + it = fmt::format_to(it, ", update: {}", *update); } - fmt::print(o, "}}"); - return o; + return fmt::format_to(it, "}}"); } } // namespace cluster diff --git a/src/v/cluster/topic_table.h b/src/v/cluster/topic_table.h index d7d9d16e6a233..5eb6a7425e697 100644 --- a/src/v/cluster/topic_table.h +++ b/src/v/cluster/topic_table.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "cluster/commands.h" #include "cluster/fwd.h" #include "cluster/notification.h" @@ -212,8 +213,7 @@ class topic_table { || _state == reconfiguration_state::force_update; } - friend std::ostream& - operator<<(std::ostream&, const in_progress_update&); + fmt::iterator format_to(fmt::iterator it) const; private: replicas_t _previous_replicas; @@ -490,8 +490,7 @@ class topic_table { : last_update_finished_revision(); } - friend std::ostream& - operator<<(std::ostream&, const partition_replicas_view&); + fmt::iterator format_to(fmt::iterator it) const; const partition_meta& partition_meta; const partition_assignment& assignment; diff --git a/src/v/cluster/tx_errc.cc b/src/v/cluster/tx_errc.cc index 4c159153c0a5f..2188415d48bec 100644 --- a/src/v/cluster/tx_errc.cc +++ b/src/v/cluster/tx_errc.cc @@ -12,72 +12,68 @@ #include "cluster/tx_errc.h" #include -#include - -#include namespace cluster::tx { - -std::ostream& operator<<(std::ostream& o, errc err) { +fmt::iterator format_to(errc err, fmt::iterator out) { switch (err) { case errc::none: - return o << "tx::errc::none"; + return fmt::format_to(out, "tx::errc::none"); case errc::leader_not_found: - return o << "tx::errc::leader_not_found"; + return fmt::format_to(out, "tx::errc::leader_not_found"); case errc::shard_not_found: - return o << "tx::errc::shard_not_found"; + return fmt::format_to(out, "tx::errc::shard_not_found"); case errc::partition_not_found: - return o << "tx::errc::partition_not_found"; + return fmt::format_to(out, "tx::errc::partition_not_found"); case errc::stm_not_found: - return o << "tx::errc::stm_not_found"; + return fmt::format_to(out, "tx::errc::stm_not_found"); case errc::partition_not_exists: - return o << "tx::errc::partition_not_exists"; + return fmt::format_to(out, "tx::errc::partition_not_exists"); case errc::pid_not_found: - return o << "tx::errc::pid_not_found"; + return fmt::format_to(out, "tx::errc::pid_not_found"); case errc::timeout: - return o << "tx::errc::timeout"; + return fmt::format_to(out, "tx::errc::timeout"); case errc::conflict: - return o << "tx::errc::conflict"; + return fmt::format_to(out, "tx::errc::conflict"); case errc::fenced: - return o << "tx::errc::fenced"; + return fmt::format_to(out, "tx::errc::fenced"); case errc::stale: - return o << "tx::errc::stale"; + return fmt::format_to(out, "tx::errc::stale"); case errc::not_coordinator: - return o << "tx::errc::not_coordinator"; + return fmt::format_to(out, "tx::errc::not_coordinator"); case errc::coordinator_not_available: - return o << "tx::errc::coordinator_not_available"; + return fmt::format_to(out, "tx::errc::coordinator_not_available"); case errc::preparing_rebalance: - return o << "tx::errc::preparing_rebalance"; + return fmt::format_to(out, "tx::errc::preparing_rebalance"); case errc::rebalance_in_progress: - return o << "tx::errc::rebalance_in_progress"; + return fmt::format_to(out, "tx::errc::rebalance_in_progress"); case errc::coordinator_load_in_progress: - return o << "tx::errc::coordinator_load_in_progress"; + return fmt::format_to(out, "tx::errc::coordinator_load_in_progress"); case errc::unknown_server_error: - return o << "tx::errc::unknown_server_error"; + return fmt::format_to(out, "tx::errc::unknown_server_error"); case errc::request_rejected: - return o << "tx::errc::request_rejected"; + return fmt::format_to(out, "tx::errc::request_rejected"); case errc::invalid_producer_epoch: - return o << "tx::errc::invalid_producer_epoch"; + return fmt::format_to(out, "tx::errc::invalid_producer_epoch"); case errc::invalid_txn_state: - return o << "tx::errc::invalid_txn_state"; + return fmt::format_to(out, "tx::errc::invalid_txn_state"); case errc::invalid_producer_id_mapping: - return o << "tx::errc::invalid_producer_id_mapping"; + return fmt::format_to(out, "tx::errc::invalid_producer_id_mapping"); case errc::tx_not_found: - return o << "tx::errc::tx_not_found"; + return fmt::format_to(out, "tx::errc::tx_not_found"); case errc::tx_id_not_found: - return o << "tx::errc::tx_id_not_found"; + return fmt::format_to(out, "tx::errc::tx_id_not_found"); case errc::partition_disabled: - return o << "tx::errc::partition_disabled"; + return fmt::format_to(out, "tx::errc::partition_disabled"); case errc::concurrent_transactions: - return o << "tx::errc::concurrent_transactions"; + return fmt::format_to(out, "tx::errc::concurrent_transactions"); case errc::invalid_timeout: - return o << "tx::errc::invalid_timeout"; + return fmt::format_to(out, "tx::errc::invalid_timeout"); case errc::producer_creation_error: - return o << "tx::errc::producer_creation_error"; + return fmt::format_to(out, "tx::errc::producer_creation_error"); case errc::partition_writes_locked: - return o << "tx::errc::partition_writes_locked"; + return fmt::format_to(out, "tx::errc::partition_writes_locked"); } - return o; + return fmt::format_to(out, ""); } std::string errc_category::message(int ec) const { diff --git a/src/v/cluster/tx_errc.h b/src/v/cluster/tx_errc.h index ff34a9d29e6d9..74a6a3d909ef8 100644 --- a/src/v/cluster/tx_errc.h +++ b/src/v/cluster/tx_errc.h @@ -12,6 +12,9 @@ */ #pragma once +#include "base/format_to.h" + +#include #include namespace cluster::tx { @@ -57,7 +60,7 @@ enum class errc { partition_writes_locked }; -std::ostream& operator<<(std::ostream& o, errc err); +fmt::iterator format_to(errc err, fmt::iterator); struct errc_category final : public std::error_category { const char* name() const noexcept final { return "cluster::tx::errc"; } diff --git a/src/v/cluster/tx_hash_ranges.h b/src/v/cluster/tx_hash_ranges.h index 33c22af840401..7e56c07e71cb4 100644 --- a/src/v/cluster/tx_hash_ranges.h +++ b/src/v/cluster/tx_hash_ranges.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/node_hash_set.h" +#include "base/format_to.h" #include "hashing/murmur.h" #include "kafka/protocol/types.h" #include "model/fundamental.h" @@ -73,11 +74,8 @@ struct tx_hash_range return (r.first >= first && r.first <= last) || (r.last >= first && r.last <= last) || r.contains(*this); } - - friend std::ostream& - operator<<(std::ostream& o, const tx_hash_range& range) { - fmt::print(o, "[{}, {}]", range.first, range.last); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "[{}, {}]", first, last); } }; @@ -163,11 +161,8 @@ struct tx_hash_ranges_set return range1.intersects(range2); }); } - - friend std::ostream& - operator<<(std::ostream& o, const tx_hash_ranges_set& ranges) { - fmt::print(o, "{{ {} }}", ranges.ranges); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ {} }}", ranges); } }; @@ -196,14 +191,13 @@ struct hosted_txs hash_ranges, excluded_transactions, included_transactions); } - friend std::ostream& operator<<(std::ostream& o, hosted_txs h) { - fmt::print( - o, - "{{ ranges: {}, excluded: {}, included: {} }}", - h.hash_ranges, - h.excluded_transactions.size(), - h.included_transactions.size()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ranges: {}, excluded: {}, included: {}}}", + hash_ranges, + excluded_transactions.size(), + included_transactions.size()); } }; diff --git a/src/v/cluster/tx_protocol_types.cc b/src/v/cluster/tx_protocol_types.cc index 1c1e6ec79dc05..53e57e251f52f 100644 --- a/src/v/cluster/tx_protocol_types.cc +++ b/src/v/cluster/tx_protocol_types.cc @@ -10,280 +10,206 @@ */ #include "cluster/tx_protocol_types.h" +#include "base/format_to.h" #include "utils/to_string.h" #include namespace cluster { - -std::ostream& operator<<(std::ostream& o, const commit_tx_request& r) { - fmt::print( - o, - "{{ntp {} pid {} tx_seq {} timeout {}}}", - r.ntp, - r.pid, - r.tx_seq, - r.timeout); - return o; +fmt::iterator commit_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp {} pid {} tx_seq {} timeout {}}}", ntp, pid, tx_seq, timeout); } - -std::ostream& operator<<(std::ostream& o, const commit_tx_reply& r) { - fmt::print(o, "{{ec {}}}", r.ec); - return o; +fmt::iterator commit_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ec {}}}", ec); } - -std::ostream& operator<<(std::ostream& o, const abort_tx_request& r) { - fmt::print( - o, - "{{ntp {} pid {} tx_seq {} timeout {}}}", - r.ntp, - r.pid, - r.tx_seq, - r.timeout); - return o; +fmt::iterator abort_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp {} pid {} tx_seq {} timeout {}}}", ntp, pid, tx_seq, timeout); } - -std::ostream& operator<<(std::ostream& o, const abort_tx_reply& r) { - fmt::print(o, "{{ec {}}}", r.ec); - return o; +fmt::iterator abort_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ec {}}}", ec); } - -std::ostream& operator<<(std::ostream& o, const begin_group_tx_request& r) { - fmt::print( - o, +fmt::iterator begin_group_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp {} group_id {} pid {} tx_seq {} timeout {} tm_partition: {}}}", - r.ntp, - r.group_id, - r.pid, - r.tx_seq, - r.timeout, - r.tm_partition); - return o; -} - -std::ostream& operator<<(std::ostream& o, const begin_group_tx_reply& r) { - fmt::print(o, "{{etag {} ec {}}}", r.etag, r.ec); - return o; -} - -std::ostream& operator<<(std::ostream& o, const prepare_group_tx_request& r) { - fmt::print( - o, + ntp, + group_id, + pid, + tx_seq, + timeout, + tm_partition); +} +fmt::iterator begin_group_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{etag {} ec {}}}", etag, ec); +} +fmt::iterator prepare_group_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp {} group_id {} etag {} pid {} tx_seq {} timeout {}}}", - r.ntp, - r.group_id, - r.etag, - r.pid, - r.tx_seq, - r.timeout); - return o; -} - -std::ostream& operator<<(std::ostream& o, const prepare_group_tx_reply& r) { - fmt::print(o, "{{ec {}}}", r.ec); - return o; -} - -std::ostream& operator<<(std::ostream& o, const commit_group_tx_request& r) { - fmt::print( - o, + ntp, + group_id, + etag, + pid, + tx_seq, + timeout); +} +fmt::iterator prepare_group_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ec {}}}", ec); +} +fmt::iterator commit_group_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp {} pid {} tx_seq {} group_id {} timeout {}}}", - r.ntp, - r.pid, - r.tx_seq, - r.group_id, - r.timeout); - return o; -} - -std::ostream& operator<<(std::ostream& o, const commit_group_tx_reply& r) { - fmt::print(o, "{{ec {}}}", r.ec); - return o; -} - -std::ostream& operator<<(std::ostream& o, const abort_group_tx_request& r) { - fmt::print( - o, + ntp, + pid, + tx_seq, + group_id, + timeout); +} +fmt::iterator commit_group_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ec {}}}", ec); +} +fmt::iterator abort_group_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp {} pid {} tx_seq {} group_id {} timeout {}}}", - r.ntp, - r.pid, - r.tx_seq, - r.group_id, - r.timeout); - return o; + ntp, + pid, + tx_seq, + group_id, + timeout); } - -std::ostream& operator<<(std::ostream& o, const abort_group_tx_reply& r) { - fmt::print(o, "{{ec {}}}", r.ec); - return o; +fmt::iterator abort_group_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ec {}}}", ec); } - -std::ostream& operator<<(std::ostream& o, const find_coordinator_request& r) { - fmt::print(o, "{{tid {}}}", r.tid); - return o; +fmt::iterator find_coordinator_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{tid {}}}", tid); } - -std::ostream& operator<<(std::ostream& o, const find_coordinator_reply& r) { - fmt::print( - o, "{{coordinator {} ntp {} ec {}}}", r.coordinator, r.ntp, r.ec); - return o; +fmt::iterator find_coordinator_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{coordinator {} ntp {} ec {}}}", coordinator, ntp, ec); } - -std::ostream& operator<<(std::ostream& o, const fetch_tx_reply& r) { - fmt::print( - o, +fmt::iterator fetch_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ec: {}, pid: {}, last_pid: {}, tx_seq: {}, timeout_ms: {}, status: " "{}, partitions: {}, groups: {}}}", - r.ec, - r.pid, - r.last_pid, - r.tx_seq, - r.timeout_ms.count(), - static_cast(r.status), - r.partitions, - r.groups); - return o; -} - -std::ostream& operator<<(std::ostream& o, const fetch_tx_reply::tx_group& g) { - fmt::print(o, "{{etag: {}, group_id: {}}}", g.etag, g.group_id); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const fetch_tx_reply::tx_partition& p) { - fmt::print( - o, - "{{etag: {}, ntp: {}, revision: {}}}", - p.etag, - p.ntp, - p.topic_revision); - - return o; -} - -std::ostream& -operator<<(std::ostream& o, const add_partitions_tx_request::topic& t) { - fmt::print(o, "{{topic: {}, partitions: {}}}", t.name, t.partitions); - return o; -} - -std::ostream& operator<<(std::ostream& o, const begin_tx_request& r) { - fmt::print( - o, + ec, + pid, + last_pid, + tx_seq, + timeout_ms.count(), + static_cast(status), + partitions, + groups); +} +fmt::iterator fetch_tx_reply::tx_group::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{etag: {}, group_id: {}}}", etag, group_id); +} +fmt::iterator fetch_tx_reply::tx_partition::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{etag: {}, ntp: {}, revision: {}}}", etag, ntp, topic_revision); +} +fmt::iterator +add_partitions_tx_request::topic::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, partitions: {}}}", name, partitions); +} +fmt::iterator begin_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ ntp: {}, pid: {}, tx_seq: {}, tm_partition: {}}}", - r.ntp, - r.pid, - r.tx_seq, - r.tm_partition); - return o; + ntp, + pid, + tx_seq, + tm_partition); } - -std::ostream& operator<<(std::ostream& o, const begin_tx_reply& r) { - fmt::print(o, "{{ ntp: {}, etag: {}, ec: {} }}", r.ntp, r.etag, r.ec); - return o; +fmt::iterator begin_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ ntp: {}, etag: {}, ec: {} }}", ntp, etag, ec); } - -std::ostream& operator<<(std::ostream& o, const prepare_tx_request& r) { - fmt::print( - o, +fmt::iterator prepare_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ ntp: {}, etag: {}, tm: {}, pid: {}, tx_seq: {}, timeout: {} }}", - r.ntp, - r.etag, - r.tm, - r.pid, - r.tx_seq, - r.timeout); - return o; -} - -std::ostream& operator<<(std::ostream& o, const prepare_tx_reply& r) { - fmt::print(o, "{{ ec: {} }}", r.ec); - return o; -} - -std::ostream& operator<<(std::ostream& o, const init_tm_tx_request& r) { - fmt::print( - o, + ntp, + etag, + tm, + pid, + tx_seq, + timeout); +} +fmt::iterator prepare_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ ec: {} }}", ec); +} +fmt::iterator init_tm_tx_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ tx_id: {}, transaction_timeout_ms: {}, timeout: {} }}", - r.tx_id, - r.transaction_timeout_ms, - r.timeout); - return o; + tx_id, + transaction_timeout_ms, + timeout); } - -std::ostream& operator<<(std::ostream& o, const init_tm_tx_reply& r) { - fmt::print(o, "{{ pid: {}, ec: {} }}", r.pid, r.ec); - return o; +fmt::iterator init_tm_tx_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ pid: {}, ec: {} }}", pid, ec); } - -std::ostream& operator<<(std::ostream& o, const try_abort_request& r) { - fmt::print( - o, +fmt::iterator try_abort_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ tm: {}, pid: {}, tx_seq: {}, timeout: {} }}", - r.tm, - r.pid, - r.tx_seq, - r.timeout); - return o; -} - -std::ostream& operator<<(std::ostream& o, const try_abort_reply& r) { - fmt::print( - o, + tm, + pid, + tx_seq, + timeout); +} +fmt::iterator try_abort_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ commited: {}, aborted: {}, ec: {} }}", - bool(r.commited), - bool(r.aborted), - r.ec); - return o; + bool(commited), + bool(aborted), + ec); } - -std::ostream& operator<<(std::ostream& o, const idempotent_request_info& info) { - fmt::print( - o, +fmt::iterator idempotent_request_info::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ first: {}, last: {}, term: {} }}", - info.first_sequence, - info.last_sequence, - info.term); - return o; + first_sequence, + last_sequence, + term); } - -std::ostream& operator<<(std::ostream& o, const producer_state_info& info) { - fmt::print( - o, +fmt::iterator producer_state_info::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ pid: {}, inflight_requests: {}, finished: {}, begin offset: {}, end " "offset: {}, sequence: {}, timeout: " "{}, coordinator: {}, last_update: {}, group: {} }}", - info.pid, - info.inflight_requests, - info.finished_requests, - info.tx_begin_offset, - info.tx_end_offset, - info.tx_seq, - info.tx_timeout, - info.coordinator_partition, - info.last_update, - info.group_id); - return o; -} - -std::ostream& operator<<(std::ostream& o, const get_producers_reply& r) { - fmt::print( - o, + pid, + inflight_requests, + finished_requests, + tx_begin_offset, + tx_end_offset, + tx_seq, + tx_timeout, + coordinator_partition, + last_update, + group_id); +} +fmt::iterator get_producers_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ ec: {}, producers: {} , count: {} }}", - r.error_code, - r.producers, - r.producer_count); - return o; + error_code, + producers, + producer_count); } - -std::ostream& operator<<(std::ostream& o, const get_producers_request& r) { - fmt::print( - o, +fmt::iterator get_producers_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ ntp: {} timeout: {}, max_producers_to_include: {} }}", - r.ntp, - r.timeout, - r.max_producers_to_include); - return o; + ntp, + timeout, + max_producers_to_include); } } // namespace cluster diff --git a/src/v/cluster/tx_protocol_types.h b/src/v/cluster/tx_protocol_types.h index 73b1b22499113..abbc2f2d38efb 100644 --- a/src/v/cluster/tx_protocol_types.h +++ b/src/v/cluster/tx_protocol_types.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "cluster/errc.h" #include "cluster/tx_errc.h" #include "container/chunked_vector.h" @@ -47,7 +48,7 @@ struct try_abort_reply friend bool operator==(const try_abort_reply&, const try_abort_reply&) = default; - friend std::ostream& operator<<(std::ostream& o, const try_abort_reply& r); + fmt::iterator format_to(fmt::iterator it) const; static try_abort_reply make_aborted() { return {committed_type::no, aborted_type::yes, tx::errc::none}; @@ -86,8 +87,7 @@ struct try_abort_request friend bool operator==(const try_abort_request&, const try_abort_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const try_abort_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tm, pid, tx_seq, timeout); } }; @@ -114,8 +114,7 @@ struct init_tm_tx_request friend bool operator==(const init_tm_tx_request&, const init_tm_tx_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const init_tm_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tx_id, transaction_timeout_ms, timeout); @@ -138,7 +137,7 @@ struct init_tm_tx_reply friend bool operator==(const init_tm_tx_reply&, const init_tm_tx_reply&) = default; - friend std::ostream& operator<<(std::ostream& o, const init_tm_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; explicit init_tm_tx_reply(tx::errc ec) : ec(ec) {} @@ -150,7 +149,7 @@ struct add_partitions_tx_request { struct topic { model::topic name{}; std::vector partitions{}; - friend std::ostream& operator<<(std::ostream&, const topic&); + fmt::iterator format_to(fmt::iterator it) const; }; kafka::transactional_id transactional_id{}; kafka::producer_id producer_id{}; @@ -206,7 +205,7 @@ struct fetch_tx_request friend bool operator==(const fetch_tx_request&, const fetch_tx_request&) = default; - friend std::ostream& operator<<(std::ostream& o, const fetch_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tx_id, term, tm); } }; @@ -244,7 +243,7 @@ struct fetch_tx_reply friend bool operator==(const tx_partition&, const tx_partition&) = default; - friend std::ostream& operator<<(std::ostream& o, const tx_partition& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, etag, topic_revision); } }; @@ -262,7 +261,7 @@ struct fetch_tx_reply friend bool operator==(const tx_group&, const tx_group&) = default; - friend std::ostream& operator<<(std::ostream& o, const tx_group& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(group_id, etag); } }; @@ -302,7 +301,7 @@ struct fetch_tx_reply friend bool operator==(const fetch_tx_reply&, const fetch_tx_reply&) = default; - friend std::ostream& operator<<(std::ostream& o, const fetch_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie( @@ -336,7 +335,7 @@ struct begin_tx_request friend bool operator==(const begin_tx_request&, const begin_tx_request&) = default; - friend std::ostream& operator<<(std::ostream& o, const begin_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, pid, tx_seq, transaction_timeout_ms, tm_partition); @@ -375,7 +374,7 @@ struct begin_tx_reply friend bool operator==(const begin_tx_reply&, const begin_tx_reply&) = default; - friend std::ostream& operator<<(std::ostream& o, const begin_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, etag, ec, topic_revision); } }; @@ -411,8 +410,7 @@ struct prepare_tx_request friend bool operator==(const prepare_tx_request&, const prepare_tx_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const prepare_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, etag, tm, pid, tx_seq, timeout); @@ -432,7 +430,7 @@ struct prepare_tx_reply friend bool operator==(const prepare_tx_reply&, const prepare_tx_reply&) = default; - friend std::ostream& operator<<(std::ostream& o, const prepare_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ec); } }; @@ -462,8 +460,7 @@ struct commit_tx_request auto serde_fields() { return std::tie(ntp, pid, tx_seq, timeout); } - friend std::ostream& - operator<<(std::ostream& o, const commit_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct commit_tx_reply @@ -481,7 +478,7 @@ struct commit_tx_reply auto serde_fields() { return std::tie(ec); } - friend std::ostream& operator<<(std::ostream& o, const commit_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct abort_tx_request @@ -509,7 +506,7 @@ struct abort_tx_request auto serde_fields() { return std::tie(ntp, pid, tx_seq, timeout); } - friend std::ostream& operator<<(std::ostream& o, const abort_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct abort_tx_reply @@ -527,7 +524,7 @@ struct abort_tx_reply auto serde_fields() { return std::tie(ec); } - friend std::ostream& operator<<(std::ostream& o, const abort_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct begin_group_tx_request @@ -578,8 +575,7 @@ struct begin_group_tx_request return std::tie(ntp, group_id, pid, tx_seq, timeout, tm_partition); } - friend std::ostream& - operator<<(std::ostream& o, const begin_group_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct begin_group_tx_reply @@ -604,8 +600,7 @@ struct begin_group_tx_reply auto serde_fields() { return std::tie(etag, ec); } - friend std::ostream& - operator<<(std::ostream& o, const begin_group_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct prepare_group_tx_request @@ -653,8 +648,7 @@ struct prepare_group_tx_request return std::tie(ntp, group_id, etag, pid, tx_seq, timeout); } - friend std::ostream& - operator<<(std::ostream& o, const prepare_group_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const prepare_group_tx_request&, const prepare_group_tx_request&) @@ -678,8 +672,7 @@ struct prepare_group_tx_reply auto serde_fields() { return std::tie(ec); } - friend std::ostream& - operator<<(std::ostream& o, const prepare_group_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct commit_group_tx_request @@ -722,8 +715,7 @@ struct commit_group_tx_request friend bool operator==( const commit_group_tx_request&, const commit_group_tx_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const commit_group_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, pid, tx_seq, group_id, timeout); @@ -745,8 +737,7 @@ struct commit_group_tx_reply friend bool operator==( const commit_group_tx_reply&, const commit_group_tx_reply&) = default; - friend std::ostream& - operator<<(std::ostream& o, const commit_group_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ec); } }; @@ -791,8 +782,7 @@ struct abort_group_tx_request friend bool operator==( const abort_group_tx_request&, const abort_group_tx_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const abort_group_tx_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, group_id, pid, tx_seq, timeout); @@ -814,8 +804,7 @@ struct abort_group_tx_reply friend bool operator==( const abort_group_tx_reply&, const abort_group_tx_reply&) = default; - friend std::ostream& - operator<<(std::ostream& o, const abort_group_tx_reply& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ec); } }; @@ -860,8 +849,7 @@ struct find_coordinator_reply friend bool operator==( const find_coordinator_reply&, const find_coordinator_reply&) = default; - friend std::ostream& - operator<<(std::ostream& o, const find_coordinator_reply& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(coordinator, ntp, ec); } }; @@ -885,8 +873,7 @@ struct find_coordinator_request operator==(const find_coordinator_request&, const find_coordinator_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const find_coordinator_request& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tid); } }; @@ -903,8 +890,7 @@ struct idempotent_request_info friend bool operator==( const idempotent_request_info&, const idempotent_request_info&) = default; - friend std::ostream& - operator<<(std::ostream& o, const idempotent_request_info&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(first_sequence, last_sequence, term); @@ -935,8 +921,7 @@ struct producer_state_info friend bool operator==( const producer_state_info&, const producer_state_info&) = default; - friend std::ostream& - operator<<(std::ostream& o, const producer_state_info& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie( @@ -968,8 +953,7 @@ struct get_producers_reply friend bool operator==( const get_producers_reply&, const get_producers_reply&) = default; - friend std::ostream& - operator<<(std::ostream& o, const get_producers_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(error_code, producers, producer_count); @@ -1000,8 +984,7 @@ struct get_producers_request friend bool operator==( const get_producers_request&, const get_producers_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const get_producers_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(ntp, timeout, max_producers_to_include); @@ -1009,3 +992,12 @@ struct get_producers_request }; } // namespace cluster + +template<> +struct fmt::formatter + : fmt::formatter { + auto format( + cluster::fetch_tx_reply::tx_status s, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(s), ctx); + } +}; diff --git a/src/v/cluster/types.cc b/src/v/cluster/types.cc index 6899c75e262ba..8b87b1d0c5f0f 100644 --- a/src/v/cluster/types.cc +++ b/src/v/cluster/types.cc @@ -9,6 +9,7 @@ #include "cluster/types.h" +#include "base/format_to.h" #include "cluster/topic_properties.h" #include "model/compression.h" #include "model/fundamental.h" @@ -16,69 +17,56 @@ #include "model/timestamp.h" #include "reflection/adl.h" #include "security/acl.h" +#include "utils/to_string.h" #include "utils/tristate.h" #include #include -#include - #include #include #include #include namespace cluster { - -std::ostream& operator<<(std::ostream& o, const recovery_stage& s) { - switch (s) { +fmt::iterator format_to(recovery_stage e, fmt::iterator out) { + switch (e) { case recovery_stage::initialized: - o << "recovery_stage::initialized"; - break; + return fmt::format_to(out, "recovery_stage::initialized"); case recovery_stage::starting: - o << "recovery_stage::starting"; - break; + return fmt::format_to(out, "recovery_stage::starting"); case recovery_stage::recovered_license: - o << "recovery_stage::recovered_license"; - break; + return fmt::format_to(out, "recovery_stage::recovered_license"); case recovery_stage::recovered_cluster_config: - o << "recovery_stage::recovered_cluster_config"; - break; + return fmt::format_to(out, "recovery_stage::recovered_cluster_config"); case recovery_stage::recovered_users: - o << "recovery_stage::recovered_users"; - break; + return fmt::format_to(out, "recovery_stage::recovered_users"); case recovery_stage::recovered_acls: - o << "recovery_stage::recovered_acls"; - break; + return fmt::format_to(out, "recovery_stage::recovered_acls"); case recovery_stage::recovered_remote_topic_data: - o << "recovery_stage::recovered_remote_topic_data"; - break; + return fmt::format_to( + out, "recovery_stage::recovered_remote_topic_data"); case recovery_stage::recovered_cloud_topics_metastore: - o << "recovery_stage::recovered_cloud_topics_metastore"; - break; + return fmt::format_to( + out, "recovery_stage::recovered_cloud_topics_metastore"); case recovery_stage::recovered_cloud_topic_data: - o << "recovery_stage::recovered_cloud_topic_data"; - break; + return fmt::format_to( + out, "recovery_stage::recovered_cloud_topic_data"); case recovery_stage::recovered_topic_data: - o << "recovery_stage::recovered_topic_data"; - break; + return fmt::format_to(out, "recovery_stage::recovered_topic_data"); case recovery_stage::recovered_controller_snapshot: - o << "recovery_stage::recovered_controller_snapshot"; - break; + return fmt::format_to( + out, "recovery_stage::recovered_controller_snapshot"); case recovery_stage::recovered_offsets_topic: - o << "recovery_stage::recovered_offsets_topic"; - break; + return fmt::format_to(out, "recovery_stage::recovered_offsets_topic"); case recovery_stage::recovered_tx_coordinator: - o << "recovery_stage::recovered_tx_coordinator"; - break; + return fmt::format_to(out, "recovery_stage::recovered_tx_coordinator"); case recovery_stage::complete: - o << "recovery_stage::complete"; - break; + return fmt::format_to(out, "recovery_stage::complete"); case recovery_stage::failed: - o << "recovery_stage::failed"; - break; + return fmt::format_to(out, "recovery_stage::failed"); } - return o; + return fmt::format_to(out, "recovery_stage::unknown"); } kafka_stages::kafka_stages( @@ -117,224 +105,176 @@ create_partitions_configuration::create_partitions_configuration( model::topic_namespace tp_ns, int32_t cnt) : tp_ns(std::move(tp_ns)) , new_total_partition_count(cnt) {} - -std::ostream& -operator<<(std::ostream& o, const update_topic_properties_request& r) { - fmt::print(o, "{{updates: {}}}", r.updates); - return o; +fmt::iterator +update_topic_properties_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{updates: {}}}", updates); } - -std::ostream& -operator<<(std::ostream& o, const update_topic_properties_reply& r) { - fmt::print(o, "{{results: {}}}", r.results); - return o; +fmt::iterator update_topic_properties_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{results: {}}}", results); } - -std::ostream& operator<<(std::ostream& o, const topic_properties_update& tpu) { - fmt::print( - o, +fmt::iterator topic_properties_update::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "tp_ns: {} properties: {} custom_properties: {}", - tpu.tp_ns, - tpu.properties, - tpu.custom_properties); - return o; + tp_ns, + properties, + custom_properties); } - -std::ostream& operator<<(std::ostream& o, const topic_purge_domain& d) { +fmt::iterator format_to(topic_purge_domain d, fmt::iterator out) { switch (d) { case topic_purge_domain::cloud_storage: - return o << "cloud_storage"; + return fmt::format_to(out, "cloud_storage"); case topic_purge_domain::iceberg: - return o << "iceberg"; + return fmt::format_to(out, "iceberg"); case topic_purge_domain::cloud_topic: - return o << "cloud_topic"; + return fmt::format_to(out, "cloud_topic"); } - fmt::print(o, "unknown({})", static_cast(d)); - return o; + return fmt::format_to(out, "unknown({})", static_cast(d)); } - -std::ostream& operator<<(std::ostream& o, const topic_result& r) { - fmt::print(o, "topic: {}, result: {}", r.tp_ns, r.ec); - return o; +fmt::iterator topic_result::format_to(fmt::iterator it) const { + return fmt::format_to(it, "topic: {}, result: {}", tp_ns, ec); } - -std::ostream& -operator<<(std::ostream& o, const finish_partition_update_request& r) { - fmt::print(o, "{{ntp: {}, new_replica_set: {}}}", r.ntp, r.new_replica_set); - return o; +fmt::iterator +finish_partition_update_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, new_replica_set: {}}}", ntp, new_replica_set); } - -std::ostream& -operator<<(std::ostream& o, const finish_partition_update_reply& r) { - fmt::print(o, "{{result: {}}}", r.result); - return o; +fmt::iterator finish_partition_update_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{result: {}}}", result); } - -std::ostream& operator<<(std::ostream& o, const configuration_invariants& c) { - fmt::print( - o, +fmt::iterator configuration_invariants::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ version: {}, node_id: {}, core_count: {} }}", - c.version, - c.node_id, - c.core_count); - return o; + version, + node_id, + core_count); } - -std::ostream& operator<<(std::ostream& o, const partition_bootstrap_params& p) { - fmt::print( - o, +fmt::iterator partition_bootstrap_params::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{start_offset: {}, next_offset: {}, initial_term: {}}}", - p.start_offset, - p.next_offset, - p.initial_term); - return o; + start_offset, + next_offset, + initial_term); } - -std::ostream& operator<<(std::ostream& o, const partition_assignment& p_as) { - fmt::print( - o, - "{{ id: {}, group_id: {}, replicas: {} }}", - p_as.id, - p_as.group, - p_as.replicas); - return o; +fmt::iterator partition_assignment::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ id: {}, group_id: {}, replicas: {} }}", id, group, replicas); } - -std::ostream& operator<<(std::ostream& o, const shard_placement_target& spt) { - fmt::print( - o, +fmt::iterator shard_placement_target::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{group: {}, log_revision: {}, shard: {}}}", - spt.group, - spt.log_revision, - spt.shard); - return o; + group, + log_revision, + shard); } - -std::ostream& operator<<(std::ostream& o, const partition_operation_type& tp) { - switch (tp) { +fmt::iterator format_to(partition_operation_type e, fmt::iterator out) { + switch (e) { case partition_operation_type::add: - return o << "addition"; + return fmt::format_to(out, "addition"); case partition_operation_type::remove: - return o << "deletion"; + return fmt::format_to(out, "deletion"); case partition_operation_type::reset: - return o << "reset"; + return fmt::format_to(out, "reset"); case partition_operation_type::update: - return o << "update"; + return fmt::format_to(out, "update"); case partition_operation_type::force_update: - return o << "force_update"; + return fmt::format_to(out, "force_update"); case partition_operation_type::finish_update: - return o << "update_finished"; + return fmt::format_to(out, "update_finished"); case partition_operation_type::update_properties: - return o << "update_properties"; + return fmt::format_to(out, "update_properties"); case partition_operation_type::add_non_replicable: - return o << "add_non_replicable_addition"; + return fmt::format_to(out, "add_non_replicable_addition"); case partition_operation_type::del_non_replicable: - return o << "del_non_replicable_deletion"; + return fmt::format_to(out, "del_non_replicable_deletion"); case partition_operation_type::cancel_update: - return o << "cancel_update"; + return fmt::format_to(out, "cancel_update"); case partition_operation_type::force_cancel_update: - return o << "force_abort_update"; + return fmt::format_to(out, "force_abort_update"); } __builtin_unreachable(); } - -std::ostream& -operator<<(std::ostream& o, const topic_table_topic_delta_type& tp) { - switch (tp) { +fmt::iterator format_to(topic_table_topic_delta_type e, fmt::iterator out) { + switch (e) { case topic_table_topic_delta_type::added: - return o << "added"; + return fmt::format_to(out, "added"); case topic_table_topic_delta_type::removed: - return o << "removed"; + return fmt::format_to(out, "removed"); case topic_table_topic_delta_type::properties_updated: - return o << "properties_updated"; + return fmt::format_to(out, "properties_updated"); } __builtin_unreachable(); } - -std::ostream& operator<<(std::ostream& o, const topic_table_topic_delta& d) { - fmt::print( - o, +fmt::iterator topic_table_topic_delta::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{creation_revision:{}, ns_tp: {}, type: {}, revision: {}}}", - d.creation_revision, - d.ns_tp, - d.type, - d.revision); - return o; + creation_revision, + ns_tp, + type, + revision); } - -std::ostream& -operator<<(std::ostream& o, const topic_table_ntp_delta_type& tp) { - switch (tp) { +fmt::iterator format_to(topic_table_ntp_delta_type e, fmt::iterator out) { + switch (e) { case topic_table_ntp_delta_type::added: - return o << "added"; + return fmt::format_to(out, "added"); case topic_table_ntp_delta_type::removed: - return o << "removed"; + return fmt::format_to(out, "removed"); case topic_table_ntp_delta_type::replicas_updated: - return o << "replicas_updated"; + return fmt::format_to(out, "replicas_updated"); case topic_table_ntp_delta_type::properties_updated: - return o << "properties_updated"; + return fmt::format_to(out, "properties_updated"); case topic_table_ntp_delta_type::disabled_flag_updated: - return o << "disabled_flag_updated"; + return fmt::format_to(out, "disabled_flag_updated"); } __builtin_unreachable(); } - -std::ostream& operator<<(std::ostream& o, const topic_table_ntp_delta& d) { - fmt::print( - o, "{{ntp: {}, type: {}, revision: {}}}", d.ntp, d.type, d.revision); - return o; +fmt::iterator topic_table_ntp_delta::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, type: {}, revision: {}}}", ntp, type, revision); } - -std::ostream& operator<<(std::ostream& o, const backend_operation& op) { - fmt::print( - o, +fmt::iterator backend_operation::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition_assignment: {}, shard: {}, type: {}, " "recovery_state: {}}}", - op.p_as, - op.source_shard, - op.type, - op.recovery_state); - return o; -} -std::ostream& operator<<(std::ostream& o, const recovery_state& r) { - fmt::print( - o, + p_as, + source_shard, + type, + recovery_state); +} +fmt::iterator recovery_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{local_last_offset: {}, local_size: {}, replicas: {}}}", - r.local_last_offset, - r.local_size, - r.replicas); - return o; -} -std::ostream& operator<<(std::ostream& o, const replica_recovery_state& rrs) { - fmt::print( - o, - "{{last_offset: {}, bytes_left: {}}}", - rrs.last_offset, - rrs.bytes_left); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const cluster_config_delta_cmd_data& data) { - fmt::print( - o, + local_last_offset, + local_size, + replicas); +} +fmt::iterator replica_recovery_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{last_offset: {}, bytes_left: {}}}", last_offset, bytes_left); +} +fmt::iterator cluster_config_delta_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{cluster_config_delta_cmd_data: {} upserts, {} removes)}}", - data.upsert.size(), - data.remove.size()); - return o; + upsert.size(), + remove.size()); } - -std::ostream& operator<<(std::ostream& o, const config_status& s) { - fmt::print( - o, +fmt::iterator config_status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{cluster_status: node {}, version: {}, restart: {} ({} invalid, {} " "unknown)}}", - s.node, - s.version, - s.restart, - s.invalid.size(), - s.unknown.size()); - return o; + node, + version, + restart, + invalid.size(), + unknown.size()); } bool config_status::operator==(const config_status& rhs) const { @@ -342,64 +282,43 @@ bool config_status::operator==(const config_status& rhs) const { == std::tie( rhs.node, rhs.version, rhs.restart, rhs.unknown, rhs.invalid); } - -std::ostream& operator<<(std::ostream& o, const cluster_property_kv& kv) { - fmt::print( - o, "{{cluster_property_kv: key {}, value: {})}}", kv.key, kv.value); - return o; +fmt::iterator cluster_property_kv::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{cluster_property_kv: key {}, value: {})}}", key, value); } - -std::ostream& operator<<(std::ostream& o, const config_update_request& crq) { - fmt::print( - o, - "{{config_update_request: upsert {}, remove: {})}}", - crq.upsert, - crq.remove); - return o; +fmt::iterator config_update_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{config_update_request: upsert {}, remove: {})}}", upsert, remove); } - -std::ostream& operator<<(std::ostream& o, const config_update_reply& crr) { - fmt::print( - o, +fmt::iterator config_update_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{config_update_reply: error {}, latest_version: {})}}", - crr.error, - crr.latest_version); - return o; + error, + latest_version); } - -std::ostream& operator<<(std::ostream& o, const hello_request& h) { - fmt::print( - o, "{{hello_request: peer {}, start_time: {})}}", h.peer, h.start_time); - return o; +fmt::iterator hello_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{hello_request: peer {}, start_time: {})}}", peer, start_time); } - -std::ostream& operator<<(std::ostream& o, const hello_reply& h) { - fmt::print(o, "{{hello_reply: error {}}}", h.error); - return o; +fmt::iterator hello_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{hello_reply: error {}}}", error); } - -std::ostream& operator<<(std::ostream& o, const create_topics_request& r) { - fmt::print( - o, - "{{create_topics_request: topics: {} timeout: {}}}", - r.topics, - r.timeout); - return o; +fmt::iterator create_topics_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{create_topics_request: topics: {} timeout: {}}}", topics, timeout); } - -std::ostream& operator<<(std::ostream& o, const create_topics_reply& r) { - fmt::print( - o, +fmt::iterator create_topics_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{create_topics_reply: results: {} metadata: {} configs: {}}}", - r.results, - r.metadata, - r.configs); - return o; + results, + metadata, + configs); } - -std::ostream& operator<<(std::ostream& o, const incremental_topic_updates& i) { - fmt::print( - o, +fmt::iterator incremental_topic_updates::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{incremental_topic_custom_updates: compression: {} " "cleanup_policy_bitflags: {} compaction_strategy: {} timestamp_type: {} " "segment_size: {} retention_bytes: {} retention_duration: {} " @@ -422,43 +341,42 @@ std::ostream& operator<<(std::ostream& o, const incremental_topic_updates& i) { "iceberg_target_lag_ms: {}, " "remote_allow_gaps: {}, " "topic_id: {}}}", - i.compression, - i.cleanup_policy_bitflags, - i.compaction_strategy, - i.timestamp_type, - i.segment_size, - i.retention_bytes, - i.retention_duration, - i.get_shadow_indexing(), - i.batch_max_bytes, - i.retention_local_target_bytes, - i.retention_local_target_ms, - i.remote_delete, - i.segment_ms, - i.record_key_schema_id_validation, - i.record_key_schema_id_validation_compat, - i.record_key_subject_name_strategy, - i.record_key_subject_name_strategy_compat, - i.record_value_schema_id_validation, - i.record_value_schema_id_validation_compat, - i.record_value_subject_name_strategy, - i.record_value_subject_name_strategy_compat, - i.initial_retention_local_target_bytes, - i.initial_retention_local_target_ms, - i.write_caching, - i.flush_ms, - i.flush_bytes, - i.iceberg_mode, - i.leaders_preference, - i.remote_read, - i.remote_write, - i.iceberg_delete, - i.iceberg_partition_spec, - i.iceberg_invalid_record_action, - i.iceberg_target_lag_ms, - i.remote_allow_gaps, - i.topic_id); - return o; + compression, + cleanup_policy_bitflags, + compaction_strategy, + timestamp_type, + segment_size, + retention_bytes, + retention_duration, + get_shadow_indexing(), + batch_max_bytes, + retention_local_target_bytes, + retention_local_target_ms, + remote_delete, + segment_ms, + record_key_schema_id_validation, + record_key_schema_id_validation_compat, + record_key_subject_name_strategy, + record_key_subject_name_strategy_compat, + record_value_schema_id_validation, + record_value_schema_id_validation_compat, + record_value_subject_name_strategy, + record_value_subject_name_strategy_compat, + initial_retention_local_target_bytes, + initial_retention_local_target_ms, + write_caching, + flush_ms, + flush_bytes, + iceberg_mode, + leaders_preference, + remote_read, + remote_write, + iceberg_delete, + iceberg_partition_spec, + iceberg_invalid_record_action, + iceberg_target_lag_ms, + remote_allow_gaps, + topic_id); } std::istream& operator>>(std::istream& i, replication_factor& cs) { @@ -478,16 +396,14 @@ replication_factor parsing_replication_factor(const ss::sstring& value) { return cluster::replication_factor(raw_value); } - -std::ostream& -operator<<(std::ostream& o, const incremental_topic_custom_updates& i) { - fmt::print( - o, +fmt::iterator +incremental_topic_custom_updates::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{incremental_topic_custom_updates: data_policy: {}, " "replication_factor: {}}}", - i.data_policy, - i.replication_factor); - return o; + data_policy, + replication_factor); } namespace { @@ -580,280 +496,219 @@ replication_factor topic_metadata::get_replication_factor() const { topic_metadata topic_metadata::copy() const { return {_fields, _assignments.copy()}; } - -std::ostream& operator<<(std::ostream& o, const reconciliation_status& s) { - switch (s) { +fmt::iterator format_to(reconciliation_status e, fmt::iterator out) { + switch (e) { case reconciliation_status::done: - return o << "done"; + return fmt::format_to(out, "done"); case reconciliation_status::error: - return o << "error"; + return fmt::format_to(out, "error"); case reconciliation_status::in_progress: - return o << "in_progress"; + return fmt::format_to(out, "in_progress"); } __builtin_unreachable(); } - -std::ostream& -operator<<(std::ostream& o, const ntp_reconciliation_state& state) { - fmt::print( - o, - "{{ntp: {}, backend_operations: {}, error: {}, status: {}}}", - state._ntp, - state._backend_operations, - state._error, - state._status); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const create_partitions_configuration& cfg) { - fmt::print( - o, +fmt::iterator ntp_reconciliation_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ntp: {}, backend_operations: [{}], error: {}, status: {}}}", + _ntp, + fmt::join(_backend_operations, ", "), + _error, + _status); +} +fmt::iterator +create_partitions_configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, new total partition count: {}, custom assignments: {}}}", - cfg.tp_ns, - cfg.new_total_partition_count, - cfg.custom_assignments); - return o; + tp_ns, + new_total_partition_count, + custom_assignments); } - -std::ostream& -operator<<(std::ostream& o, const custom_assignable_topic_configuration& catc) { - fmt::print( - o, +fmt::iterator +custom_assignable_topic_configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{configuration: {}, custom_assignments: {}}}", - catc.cfg, - catc.custom_assignments); - return o; + cfg, + custom_assignments); } - -std::ostream& -operator<<(std::ostream& o, const custom_partition_assignment& cas) { - fmt::print(o, "{{partition_id: {}, replicas: {}}}", cas.id, cas.replicas); - return o; +fmt::iterator custom_partition_assignment::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition_id: {}, replicas: {}}}", id, replicas); } - -std::ostream& operator<<(std::ostream& o, const leader_term& lt) { - fmt::print(o, "{{leader: {}, term: {}}}", lt.leader, lt.term); - return o; +fmt::iterator leader_term::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{leader: {}, term: {}}}", leader, term); } - -std::ostream& operator<<(std::ostream& o, const partition_move_direction& s) { - switch (s) { +fmt::iterator format_to(partition_move_direction e, fmt::iterator out) { + switch (e) { case partition_move_direction::to_node: - return o << "to_node"; + return fmt::format_to(out, "to_node"); case partition_move_direction::from_node: - return o << "from_node"; + return fmt::format_to(out, "from_node"); case partition_move_direction::all: - return o << "all"; + return fmt::format_to(out, "all"); } __builtin_unreachable(); } - -std::ostream& operator<<(std::ostream& o, const move_cancellation_result& r) { - fmt::print(o, "{{ntp: {}, result: {}}}", r.ntp, r.result); - return o; +fmt::iterator move_cancellation_result::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ntp: {}, result: {}}}", ntp, result); } - -std::ostream& -operator<<(std::ostream& o, const cancel_node_partition_movements_request& r) { - fmt::print(o, "{{node_id: {}, direction: {}}}", r.node_id, r.direction); - return o; +fmt::iterator +cancel_node_partition_movements_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{node_id: {}, direction: {}}}", node_id, direction); } - -std::ostream& -operator<<(std::ostream& o, const cancel_partition_movements_reply& r) { - fmt::print( - o, +fmt::iterator +cancel_partition_movements_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{general_error: {}, partition_results: {}}}", - r.general_error, - r.partition_results); - return o; + general_error, + partition_results); } - -std::ostream& operator<<(std::ostream& o, const feature_action_request& far) { - fmt::print(o, "{{feature_update_request: {}}}", far.action); - return o; +fmt::iterator feature_action_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{feature_update_request: {}}}", action); } - -std::ostream& operator<<(std::ostream& o, const feature_action_response& far) { - fmt::print(o, "{{error: {}}}", far.error); - return o; +fmt::iterator feature_action_response::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{error: {}}}", error); } - -std::ostream& operator<<(std::ostream& o, const feature_barrier_request& fbr) { - fmt::print( - o, "{{tag: {} peer: {} entered: {}}}", fbr.tag, fbr.peer, fbr.entered); - return o; +fmt::iterator feature_barrier_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{tag: {} peer: {} entered: {}}}", tag, peer, entered); } - -std::ostream& operator<<(std::ostream& o, const feature_barrier_response& fbr) { - fmt::print(o, "{{entered: {} complete: {}}}", fbr.entered, fbr.complete); - return o; +fmt::iterator feature_barrier_response::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{entered: {} complete: {}}}", entered, complete); } - -std::ostream& operator<<(std::ostream& o, const move_topic_replicas_data& r) { - fmt::print(o, "{{partition: {}, replicas: {}}}", r.partition, r.replicas); - return o; +fmt::iterator move_topic_replicas_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition: {}, replicas: {}}}", partition, replicas); } - -std::ostream& -operator<<(std::ostream& o, const force_partition_reconfiguration_cmd_data& r) { - fmt::print(o, "{{target replicas: {}}}", r.replicas); - return o; +fmt::iterator +force_partition_reconfiguration_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{target replicas: {}}}", replicas); } - -std::ostream& -operator<<(std::ostream& o, const set_topic_partitions_disabled_cmd_data& r) { - fmt::print( - o, +fmt::iterator +set_topic_partitions_disabled_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, partition_id: {}, disabled: {}}}", - r.ns_tp, - r.partition_id, - r.disabled); - return o; + ns_tp, + partition_id, + disabled); } - -std::ostream& operator<<( - std::ostream& o, const feature_update_license_update_cmd_data& fulu) { - fmt::print(o, "{{redpanda_license {}}}", fulu.redpanda_license); - return o; +fmt::iterator +feature_update_license_update_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{redpanda_license {}}}", redpanda_license); } - -std::ostream& operator<<(std::ostream& o, const config_status_request& r) { - fmt::print(o, "{{status: {}}}", r.status); - return o; +fmt::iterator config_status_request::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{status: {}}}", status); } - -std::ostream& operator<<(std::ostream& o, const config_status_reply& r) { - fmt::print(o, "{{error: {}}}", r.error); - return o; +fmt::iterator config_status_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{error: {}}}", error); } - -std::ostream& -operator<<(std::ostream& o, const configuration_update_request& cr) { - fmt::print(o, "{{broker: {} target_node: {}}}", cr.node, cr.target_node); - return o; +fmt::iterator configuration_update_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{broker: {} target_node: {}}}", node, target_node); } - -std::ostream& -operator<<(std::ostream& o, const configuration_update_reply& cr) { - fmt::print(o, "{{success: {}}}", cr.success); - return o; +fmt::iterator configuration_update_reply::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{success: {}}}", success); } - -std::ostream& operator<<(std::ostream& o, const broker_state& state) { - fmt::print( - o, +fmt::iterator broker_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{membership_state: {}, maintenance_state: {}}}", - state._membership_state, - state._maintenance_state); - return o; + _membership_state, + _maintenance_state); } - -std::ostream& operator<<(std::ostream& o, const node_metadata& nm) { - fmt::print(o, "{{broker: {}, state: {} }}", nm.broker, nm.state); - return o; +fmt::iterator node_metadata::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{broker: {}, state: {} }}", broker, state); } - -std::ostream& operator<<(std::ostream& o, const node_update_type& tp) { - switch (tp) { +fmt::iterator format_to(node_update_type e, fmt::iterator out) { + switch (e) { case node_update_type::added: - return o << "added"; + return fmt::format_to(out, "added"); case node_update_type::decommissioned: - return o << "decommissioned"; + return fmt::format_to(out, "decommissioned"); case node_update_type::recommissioned: - return o << "recommissioned"; + return fmt::format_to(out, "recommissioned"); case node_update_type::reallocation_finished: - return o << "reallocation_finished"; + return fmt::format_to(out, "reallocation_finished"); case node_update_type::removed: - return o << "removed"; + return fmt::format_to(out, "removed"); case node_update_type::interrupted: - return o << "interrupted"; + return fmt::format_to(out, "interrupted"); } - return o << "unknown"; + return fmt::format_to(out, "unknown"); } - -std::ostream& operator<<(std::ostream& o, reconfiguration_state update) { - switch (update) { +fmt::iterator format_to(reconfiguration_state e, fmt::iterator out) { + switch (e) { case reconfiguration_state::in_progress: - return o << "in_progress"; + return fmt::format_to(out, "in_progress"); case reconfiguration_state::force_update: - return o << "force_update"; + return fmt::format_to(out, "force_update"); case reconfiguration_state::cancelled: - return o << "cancelled"; + return fmt::format_to(out, "cancelled"); case reconfiguration_state::force_cancelled: - return o << "force_cancelled"; + return fmt::format_to(out, "force_cancelled"); } __builtin_unreachable(); } - -std::ostream& operator<<(std::ostream& o, const cloud_storage_mode& mode) { - switch (mode) { +fmt::iterator format_to(cloud_storage_mode e, fmt::iterator out) { + switch (e) { case cloud_storage_mode::disabled: - return o << "disabled"; + return fmt::format_to(out, "disabled"); case cloud_storage_mode::write_only: - return o << "write_only"; + return fmt::format_to(out, "write_only"); case cloud_storage_mode::read_only: - return o << "read_only"; + return fmt::format_to(out, "read_only"); case cloud_storage_mode::full: - return o << "full"; + return fmt::format_to(out, "full"); case cloud_storage_mode::read_replica: - return o << "read_replica"; + return fmt::format_to(out, "read_replica"); case cloud_storage_mode::cloud_topic: - return o << "cloud_topic"; + return fmt::format_to(out, "cloud_topic"); case cloud_storage_mode::cloud_topic_read_replica: - return o << "cloud_topic_read_replica"; + return fmt::format_to(out, "cloud_topic_read_replica"); case cloud_storage_mode::tiered_cloud_topic: - return o << "tiered_cloud_topic"; + return fmt::format_to(out, "tiered_cloud_topic"); } __builtin_unreachable(); } - -std::ostream& operator<<(std::ostream& o, const nt_revision& ntr) { - fmt::print( - o, +fmt::iterator nt_revision::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ns: {}, topic: {}, revision: {}}}", - ntr.nt.ns, - ntr.nt.tp, - ntr.initial_revision_id); - return o; + nt.ns, + nt.tp, + initial_revision_id); } - -std::ostream& operator<<(std::ostream& o, reconfiguration_policy policy) { - switch (policy) { +fmt::iterator format_to(reconfiguration_policy e, fmt::iterator out) { + switch (e) { case reconfiguration_policy::full_local_retention: - return o << "full_local_retention"; + return fmt::format_to(out, "full_local_retention"); case reconfiguration_policy::target_initial_retention: - return o << "target_initial_retention"; + return fmt::format_to(out, "target_initial_retention"); case reconfiguration_policy::min_local_retention: - return o << "min_local_retention"; + return fmt::format_to(out, "min_local_retention"); } __builtin_unreachable(); } - -std::ostream& -operator<<(std::ostream& o, const update_partition_replicas_cmd_data& data) { - fmt::print( - o, - "{{ntp: {}, replicas: {} policy: {}}}", - data.ntp, - data.replicas, - data.policy); - return o; +fmt::iterator +update_partition_replicas_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {}, replicas: {} policy: {}}}", ntp, replicas, policy); } - -std::ostream& -operator<<(std::ostream& o, const topic_disabled_partitions_set& disabled) { - if (disabled.partitions) { - fmt::print( - o, +fmt::iterator topic_disabled_partitions_set::format_to(fmt::iterator it) const { + if (partitions) { + return fmt::format_to( + it, "{{partitions: {}}}", - std::vector( - disabled.partitions->begin(), disabled.partitions->end())); + std::vector(partitions->begin(), partitions->end())); } else { - fmt::print(o, "{{partitions: all}}"); + return fmt::format_to(it, "{{partitions: all}}"); } - return o; } void topic_disabled_partitions_set::add(model::partition_id id) { @@ -879,16 +734,14 @@ void topic_disabled_partitions_set::remove( } partitions->erase(id); } - -std::ostream& operator<<(std::ostream& o, const ntp_with_majority_loss& entry) { - fmt::print( - o, +fmt::iterator ntp_with_majority_loss::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ ntp: {}, topic_revision: {}, replicas: {}, dead nodes: {} }}", - entry.ntp, - entry.topic_revision, - entry.assignment, - entry.dead_nodes); - return o; + ntp, + topic_revision, + assignment, + dead_nodes); } bulk_force_reconfiguration_cmd_data& @@ -908,12 +761,10 @@ bulk_force_reconfiguration_cmd_data::bulk_force_reconfiguration_cmd_data( user_approved_force_recovery_partitions = other.user_approved_force_recovery_partitions.copy(); } - -std::ostream& -operator<<(std::ostream& o, const cluster::feature_update_cmd_data& r) { - fmt::print( - o, "{{logical_version: {}, actions: {}}}", r.logical_version, r.actions); - return o; +fmt::iterator +cluster::feature_update_cmd_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{logical_version: {}, actions: {}}}", logical_version, actions); } fmt::iterator error_info::format_to(fmt::iterator it) const { diff --git a/src/v/cluster/types.h b/src/v/cluster/types.h index f322fe52f262e..c51dd00000c3e 100644 --- a/src/v/cluster/types.h +++ b/src/v/cluster/types.h @@ -13,6 +13,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "cloud_storage/remote_label.h" #include "cluster/cloud_metadata/cluster_manifest.h" #include "cluster/cluster_link/errc.h" @@ -83,11 +84,8 @@ struct allocate_id_request friend bool operator==( const allocate_id_request&, const allocate_id_request&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const allocate_id_request& req) { - fmt::print(o, "timeout: {}", req.timeout.count()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "timeout: {}", timeout.count()); } auto serde_fields() { return std::tie(timeout); } @@ -107,11 +105,8 @@ struct allocate_id_reply friend bool operator==(const allocate_id_reply&, const allocate_id_reply&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const allocate_id_reply& rep) { - fmt::print(o, "id: {}, ec: {}", rep.id, rep.ec); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "id: {}, ec: {}", id, ec); } auto serde_fields() { return std::tie(id, ec); } @@ -135,15 +130,9 @@ struct reset_id_allocator_request friend bool operator==( const reset_id_allocator_request&, const reset_id_allocator_request&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const reset_id_allocator_request& req) { - fmt::print( - o, - "timeout: {}, producer_id: {}", - req.timeout.count(), - req.producer_id); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "timeout: {}, producer_id: {}", timeout.count(), producer_id); } auto serde_fields() { return std::tie(timeout, producer_id); } @@ -164,11 +153,8 @@ struct reset_id_allocator_reply friend bool operator==(const reset_id_allocator_reply&, const reset_id_allocator_reply&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const reset_id_allocator_reply& rep) { - fmt::print(o, "ec: {}", rep.ec); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "ec: {}", ec); } auto serde_fields() { return std::tie(ec); } @@ -241,17 +227,14 @@ struct join_node_request friend bool operator==(const join_node_request&, const join_node_request&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const join_node_request& r) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "logical_version {}-{} node_uuid {} node {}", - r.earliest_logical_version, - r.latest_logical_version, - r.node_uuid, - r.node); - return o; + earliest_logical_version, + latest_logical_version, + node_uuid, + node); } auto serde_fields() { @@ -379,19 +362,17 @@ struct join_node_reply friend bool operator==( const join_node_reply& lhs, const join_node_reply& rhs) = default; - - friend std::ostream& operator<<(std::ostream& o, const join_node_reply& r) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "status {} ({:02x}, success={}) id {} snap {}", - r.status_msg(), - static_cast(r.raw_status.value_or(status_code{0xff})), - r.success, - r.id, - r.controller_snapshot.has_value() - ? r.controller_snapshot.value().size_bytes() + status_msg(), + static_cast(raw_status.value_or(status_code{0xff})), + success, + id, + controller_snapshot.has_value() + ? controller_snapshot.value().size_bytes() : 0); - return o; } auto serde_fields() { @@ -416,8 +397,7 @@ struct configuration_update_request const configuration_update_request&, const configuration_update_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const configuration_update_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(node, target_node); } }; @@ -437,8 +417,7 @@ struct configuration_update_reply const configuration_update_reply&, const configuration_update_reply&) = default; - friend std::ostream& - operator<<(std::ostream&, const configuration_update_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(success); } }; @@ -477,8 +456,7 @@ struct partition_bootstrap_params const partition_bootstrap_params&, const partition_bootstrap_params&) = default; - friend std::ostream& - operator<<(std::ostream&, const partition_bootstrap_params&); + fmt::iterator format_to(fmt::iterator it) const; }; using pending_bootstrap_params_t @@ -497,8 +475,7 @@ struct set_partition_bootstrap_params_cmd_data auto serde_fields() { return std::tie(tp_ns, partition_params); } - friend std::ostream& - operator<<(std::ostream&, const set_partition_bootstrap_params_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; /// Partition assignment describes an assignment of all replicas for single NTP. @@ -526,7 +503,7 @@ struct partition_assignment } auto serde_fields() { return std::tie(group, id, replicas); } - friend std::ostream& operator<<(std::ostream&, const partition_assignment&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const partition_assignment&, const partition_assignment&) = default; @@ -548,9 +525,10 @@ incremental_update_operation_as_string(incremental_update_operation op) { } } -inline std::ostream& -operator<<(std::ostream& os, const incremental_update_operation& op) { - return os << incremental_update_operation_as_string(op); +inline fmt::iterator +format_to(incremental_update_operation op, fmt::iterator out) { + return fmt::format_to( + out, "{}", incremental_update_operation_as_string(op)); } template @@ -568,15 +546,12 @@ struct property_update incremental_update_operation op = incremental_update_operation::none; auto serde_fields() { return std::tie(value, op); } - - friend std::ostream& - operator<<(std::ostream& o, const property_update& p) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "property_update: value: {} op: {}", - p.value, - incremental_update_operation_as_string(p.op)); - return o; + value, + incremental_update_operation_as_string(op)); } friend bool @@ -599,15 +574,12 @@ struct property_update> incremental_update_operation op = incremental_update_operation::none; auto serde_fields() { return std::tie(value, op); } - - friend std::ostream& - operator<<(std::ostream& o, const property_update>& p) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "property_update: value: {} op: {}", - p.value, - incremental_update_operation_as_string(p.op)); - return o; + value, + incremental_update_operation_as_string(op)); } friend bool operator==( @@ -775,8 +747,7 @@ struct incremental_topic_updates storage_mode); } - friend std::ostream& - operator<<(std::ostream&, const incremental_topic_updates&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const incremental_topic_updates&, @@ -808,8 +779,7 @@ struct incremental_topic_custom_updates // Replication factor is custom handled. property_update> replication_factor; - friend std::ostream& - operator<<(std::ostream&, const incremental_topic_custom_updates&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const incremental_topic_custom_updates&, @@ -850,8 +820,7 @@ struct topic_properties_update // they have custom services for replication. incremental_topic_custom_updates custom_properties; - friend std::ostream& - operator<<(std::ostream&, const topic_properties_update&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const topic_properties_update&, const topic_properties_update&) = default; @@ -866,8 +835,7 @@ using topic_properties_update_vector = chunked_vector; struct custom_partition_assignment { model::partition_id id; std::vector replicas; - friend std::ostream& - operator<<(std::ostream&, const custom_partition_assignment&); + fmt::iterator format_to(fmt::iterator it) const; }; /** * custom_assignable_topic_configuration type represents topic configuration @@ -888,8 +856,7 @@ struct custom_assignable_topic_configuration { return cfg.is_schema_id_validation_enabled(); } - friend std::ostream& - operator<<(std::ostream&, const custom_assignable_topic_configuration&); + fmt::iterator format_to(fmt::iterator it) const; }; using custom_assignable_topic_configuration_vector @@ -921,8 +888,7 @@ struct create_partitions_configuration return std::tie(tp_ns, new_total_partition_count, custom_assignments); } - friend std::ostream& - operator<<(std::ostream&, const create_partitions_configuration&); + fmt::iterator format_to(fmt::iterator it) const; }; template @@ -973,9 +939,10 @@ struct configuration_with_assignment rhs.assignments.end()); }; - template - friend std::ostream& - operator<<(std::ostream&, const configuration_with_assignment&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{configuration: {}, assignments: {}}}", cfg, assignments); + } }; using create_partitions_configuration_assignment @@ -988,8 +955,7 @@ enum class topic_purge_domain { iceberg = 1, cloud_topic = 2, }; - -std::ostream& operator<<(std::ostream&, const topic_purge_domain&); +fmt::iterator format_to(topic_purge_domain, fmt::iterator); /** * Soft-deleting a topic may put it into different modes: initially this is @@ -1098,7 +1064,7 @@ struct topic_result friend bool operator==(const topic_result&, const topic_result&) = default; - friend std::ostream& operator<<(std::ostream& o, const topic_result& r); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tp_ns, ec, error_message); } }; @@ -1114,8 +1080,7 @@ struct create_topics_request friend bool operator==( const create_topics_request&, const create_topics_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const create_topics_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(topics, timeout); } @@ -1145,7 +1110,7 @@ struct create_topics_reply friend bool operator==( const create_topics_reply&, const create_topics_reply&) = default; - friend std::ostream& operator<<(std::ostream&, const create_topics_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(results, metadata, configs); } @@ -1166,7 +1131,7 @@ struct purged_topic_request friend bool operator==( const purged_topic_request&, const purged_topic_request&) = default; - friend std::ostream& operator<<(std::ostream&, const purged_topic_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(topic, timeout, domain); } }; @@ -1185,7 +1150,7 @@ struct purged_topic_reply friend bool operator==(const purged_topic_reply&, const purged_topic_reply&) = default; - friend std::ostream& operator<<(std::ostream&, const purged_topic_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(result); } }; @@ -1204,8 +1169,7 @@ struct finish_partition_update_request auto serde_fields() { return std::tie(ntp, new_replica_set); } - friend std::ostream& - operator<<(std::ostream& o, const finish_partition_update_request& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct finish_partition_update_reply @@ -1221,8 +1185,7 @@ struct finish_partition_update_reply auto serde_fields() { return std::tie(result); } - friend std::ostream& - operator<<(std::ostream& o, const finish_partition_update_reply& r); + fmt::iterator format_to(fmt::iterator it) const; }; struct update_topic_properties_request @@ -1232,8 +1195,7 @@ struct update_topic_properties_request serde::compat_version<0>> { topic_properties_update_vector updates; - friend std::ostream& - operator<<(std::ostream&, const update_topic_properties_request&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const update_topic_properties_request&, @@ -1253,8 +1215,7 @@ struct update_topic_properties_reply serde::compat_version<0>> { chunked_vector results; - friend std::ostream& - operator<<(std::ostream&, const update_topic_properties_reply&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const update_topic_properties_reply&, @@ -1280,8 +1241,7 @@ struct configuration_invariants { model::node_id node_id; uint16_t core_count; - friend std::ostream& - operator<<(std::ostream&, const configuration_invariants&); + fmt::iterator format_to(fmt::iterator it) const; }; class configuration_invariants_changed final : public std::exception { @@ -1330,6 +1290,7 @@ enum class reconfiguration_policy { */ min_local_retention = 2 }; +fmt::iterator format_to(reconfiguration_policy, fmt::iterator); /** * Replicas revision map is used to track revision of brokers in a replica @@ -1352,8 +1313,7 @@ struct shard_placement_target { model::revision_id log_revision; ss::shard_id shard; - friend std::ostream& - operator<<(std::ostream&, const shard_placement_target&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const shard_placement_target&, const shard_placement_target&) = default; @@ -1376,7 +1336,7 @@ enum class partition_operation_type { force_cancel_update, reset, }; -std::ostream& operator<<(std::ostream&, const partition_operation_type&); +fmt::iterator format_to(partition_operation_type, fmt::iterator); /// Notification of topic table state change related to a topic as a whole @@ -1385,7 +1345,7 @@ enum class topic_table_topic_delta_type { removed, properties_updated, }; -std::ostream& operator<<(std::ostream&, const topic_table_topic_delta_type&); +fmt::iterator format_to(topic_table_topic_delta_type, fmt::iterator); struct topic_table_topic_delta { // revision of topic creation command (can be used to identify a topic @@ -1406,8 +1366,7 @@ struct topic_table_topic_delta { , revision(rev) , type(type) {} - friend std::ostream& - operator<<(std::ostream&, const topic_table_topic_delta&); + fmt::iterator format_to(fmt::iterator it) const; }; /// Notification of topic table state change related to a single ntp @@ -1419,7 +1378,7 @@ enum class topic_table_ntp_delta_type { properties_updated, disabled_flag_updated, }; -std::ostream& operator<<(std::ostream&, const topic_table_ntp_delta_type&); +fmt::iterator format_to(topic_table_ntp_delta_type, fmt::iterator); struct topic_table_ntp_delta { model::ntp ntp; @@ -1437,8 +1396,7 @@ struct topic_table_ntp_delta { , revision(rev) , type(type) {} - friend std::ostream& - operator<<(std::ostream&, const topic_table_ntp_delta&); + fmt::iterator format_to(fmt::iterator it) const; }; struct create_acls_cmd_data @@ -1451,11 +1409,8 @@ struct create_acls_cmd_data friend bool operator==( const create_acls_cmd_data&, const create_acls_cmd_data&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const create_acls_cmd_data& r) { - fmt::print(o, "{{ bindings: {} }}", r.bindings); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ bindings: {} }}", bindings); } auto serde_fields() { return std::tie(bindings); } @@ -1477,11 +1432,9 @@ struct create_acls_request friend bool operator==( const create_acls_request&, const create_acls_request&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const create_acls_request& r) { - fmt::print(o, "{{ data: {}, timeout: {} }}", r.data, r.timeout.count()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ data: {}, timeout: {} }}", data, timeout.count()); } auto serde_fields() { return std::tie(data, timeout); } @@ -1494,11 +1447,8 @@ struct create_acls_reply friend bool operator==(const create_acls_reply&, const create_acls_reply&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const create_acls_reply& r) { - fmt::print(o, "{{ results: {} }}", r.results); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ results: {} }}", results); } auto serde_fields() { return std::tie(results); } @@ -1514,11 +1464,8 @@ struct delete_acls_cmd_data friend bool operator==( const delete_acls_cmd_data&, const delete_acls_cmd_data&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const delete_acls_cmd_data& d) { - fmt::print(o, "{{ filters: {} }}", d.filters); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ filters: {} }}", filters); } auto serde_fields() { return std::tie(filters); } @@ -1535,11 +1482,9 @@ struct delete_acls_result friend bool operator==(const delete_acls_result&, const delete_acls_result&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const delete_acls_result& r) { - fmt::print(o, "{{ error: {} bindings: {} }}", r.error, r.bindings); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ error: {} bindings: {} }}", error, bindings); } auto serde_fields() { return std::tie(error, bindings); } @@ -1561,11 +1506,8 @@ struct delete_acls_request friend bool operator==( const delete_acls_request&, const delete_acls_request&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const delete_acls_request& r) { - fmt::print(o, "{{ data: {} timeout: {} }}", r.data, r.timeout); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ data: {} timeout: {} }}", data, timeout); } auto serde_fields() { return std::tie(data, timeout); } @@ -1578,11 +1520,8 @@ struct delete_acls_reply friend bool operator==(const delete_acls_reply&, const delete_acls_reply&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const delete_acls_reply& r) { - fmt::print(o, "{{ results: {} }}", r.results); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ results: {} }}", results); } auto serde_fields() { return std::tie(results); } @@ -1598,8 +1537,7 @@ struct replica_recovery_state serde::compat_version<0>> { model::offset last_offset; size_t bytes_left; - friend std::ostream& - operator<<(std::ostream&, const replica_recovery_state&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const replica_recovery_state&, const replica_recovery_state&) = default; @@ -1614,7 +1552,7 @@ struct recovery_state absl::flat_hash_map replicas; - friend std::ostream& operator<<(std::ostream&, const recovery_state&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const recovery_state&, const recovery_state&) = default; @@ -1636,7 +1574,7 @@ struct backend_operation model::revision_id revision_of_operation; std::optional recovery_state; - friend std::ostream& operator<<(std::ostream&, const backend_operation&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const backend_operation&, const backend_operation&) = default; @@ -1670,7 +1608,7 @@ struct config_status } bool operator==(const config_status&) const; - friend std::ostream& operator<<(std::ostream&, const config_status&); + fmt::iterator format_to(fmt::iterator it) const; }; struct cluster_property_kv @@ -1690,7 +1628,7 @@ struct cluster_property_kv friend bool operator==( const cluster_property_kv&, const cluster_property_kv&) = default; - friend std::ostream& operator<<(std::ostream&, const cluster_property_kv&); + fmt::iterator format_to(fmt::iterator it) const; }; struct cluster_config_delta_cmd_data @@ -1708,8 +1646,7 @@ struct cluster_config_delta_cmd_data auto serde_fields() { return std::tie(upsert, remove); } - friend std::ostream& - operator<<(std::ostream&, const cluster_config_delta_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct cluster_config_status_cmd_data @@ -1746,8 +1683,7 @@ struct feature_update_cmd_data auto serde_fields() { return std::tie(logical_version, actions); } - friend std::ostream& - operator<<(std::ostream&, const feature_update_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; using force_abort_update = ss::bool_class; @@ -1782,8 +1718,7 @@ struct force_partition_reconfiguration_cmd_data const force_partition_reconfiguration_cmd_data&, const force_partition_reconfiguration_cmd_data&) = default; - friend std::ostream& - operator<<(std::ostream&, const force_partition_reconfiguration_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct move_topic_replicas_data @@ -1802,8 +1737,7 @@ struct move_topic_replicas_data auto serde_fields() { return std::tie(partition, replicas); } - friend std::ostream& - operator<<(std::ostream&, const move_topic_replicas_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct set_topic_partitions_disabled_cmd_data @@ -1822,8 +1756,7 @@ struct set_topic_partitions_disabled_cmd_data const set_topic_partitions_disabled_cmd_data&, const set_topic_partitions_disabled_cmd_data&) = default; - friend std::ostream& - operator<<(std::ostream&, const set_topic_partitions_disabled_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct feature_update_license_update_cmd_data @@ -1838,8 +1771,7 @@ struct feature_update_license_update_cmd_data auto serde_fields() { return std::tie(redpanda_license); } - friend std::ostream& - operator<<(std::ostream&, const feature_update_license_update_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct user_and_credential @@ -1964,7 +1896,7 @@ enum class recovery_stage : int8_t { // Recovery has failed. This is a terminal state. failed = 101, }; -std::ostream& operator<<(std::ostream& o, const recovery_stage& s); +fmt::iterator format_to(recovery_stage s, fmt::iterator); struct cluster_recovery_update_cmd_data : serde::envelope< @@ -1989,7 +1921,7 @@ enum class reconciliation_status : int8_t { in_progress, error, }; -std::ostream& operator<<(std::ostream&, const reconciliation_status&); +fmt::iterator format_to(reconciliation_status, fmt::iterator); class ntp_reconciliation_state : public serde::envelope< @@ -2049,8 +1981,7 @@ class ntp_reconciliation_state return {_ntp, std::move(backend_operations), _status, _error}; } - friend std::ostream& - operator<<(std::ostream&, const ntp_reconciliation_state&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(_ntp, _backend_operations, _status, _error); @@ -2108,11 +2039,8 @@ struct reconciliation_state_request friend bool operator==( const reconciliation_state_request&, const reconciliation_state_request&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const reconciliation_state_request& req) { - fmt::print(o, "{{ ntps: {} }}", fmt::join(req.ntps, ", ")); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ ntps: {} }}", fmt::join(ntps, ", ")); } auto serde_fields() { return std::tie(ntps); } @@ -2148,8 +2076,7 @@ struct ntp_with_majority_loss std::move(h), s.ntp, s.topic_revision, s.assignment, s.dead_nodes); } - friend std::ostream& - operator<<(std::ostream& o, const ntp_with_majority_loss&); + fmt::iterator format_to(fmt::iterator it) const; bool operator==(const ntp_with_majority_loss& other) const = default; auto serde_fields() { return std::tie(ntp, topic_revision, assignment, dead_nodes); @@ -2197,11 +2124,8 @@ struct reconciliation_state_reply friend bool operator==( const reconciliation_state_reply&, const reconciliation_state_reply&) = default; - - friend std::ostream& - operator<<(std::ostream& o, const reconciliation_state_reply& rep) { - fmt::print(o, "{{ results {} }}", fmt::join(rep.results, ", ")); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ results {} }}", fmt::join(results, ", ")); } reconciliation_state_reply copy() const { @@ -2229,11 +2153,8 @@ struct decommission_node_request const decommission_node_request&) = default; auto serde_fields() { return std::tie(id); } - - friend std::ostream& - operator<<(std::ostream& o, const decommission_node_request& r) { - fmt::print(o, "id {}", r.id); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "id {}", id); } }; @@ -2248,11 +2169,8 @@ struct decommission_node_reply const decommission_node_reply&, const decommission_node_reply&) = default; auto serde_fields() { return std::tie(error); } - - friend std::ostream& - operator<<(std::ostream& o, const decommission_node_reply& r) { - fmt::print(o, "error {}", r.error); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "error {}", error); } }; @@ -2268,11 +2186,8 @@ struct recommission_node_request const recommission_node_request&) = default; auto serde_fields() { return std::tie(id); } - - friend std::ostream& - operator<<(std::ostream& o, const recommission_node_request& r) { - fmt::print(o, "id {}", r.id); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "id {}", id); } }; @@ -2287,11 +2202,8 @@ struct recommission_node_reply const recommission_node_reply&, const recommission_node_reply&) = default; auto serde_fields() { return std::tie(error); } - - friend std::ostream& - operator<<(std::ostream& o, const recommission_node_reply& r) { - fmt::print(o, "error {}", r.error); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "error {}", error); } }; @@ -2307,11 +2219,8 @@ struct finish_reallocation_request const finish_reallocation_request&) = default; auto serde_fields() { return std::tie(id); } - - friend std::ostream& - operator<<(std::ostream& o, const finish_reallocation_request& r) { - fmt::print(o, "id {}", r.id); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "id {}", id); } }; @@ -2327,11 +2236,8 @@ struct finish_reallocation_reply const finish_reallocation_reply&) = default; auto serde_fields() { return std::tie(error); } - - friend std::ostream& - operator<<(std::ostream& o, const finish_reallocation_reply& r) { - fmt::print(o, "error {}", r.error); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "error {}", error); } }; @@ -2349,11 +2255,8 @@ struct set_maintenance_mode_request const set_maintenance_mode_request&) = default; auto serde_fields() { return std::tie(id, enabled); } - - friend std::ostream& - operator<<(std::ostream& o, const set_maintenance_mode_request& r) { - fmt::print(o, "id {} enabled {}", r.id, r.enabled); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "id {} enabled {}", id, enabled); } }; @@ -2370,11 +2273,8 @@ struct set_maintenance_mode_reply const set_maintenance_mode_reply&) = default; auto serde_fields() { return std::tie(error); } - - friend std::ostream& - operator<<(std::ostream& o, const set_maintenance_mode_reply& r) { - fmt::print(o, "error {}", r.error); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "error {}", error); } }; @@ -2385,8 +2285,7 @@ struct config_status_request serde::compat_version<0>> { config_status status; - friend std::ostream& - operator<<(std::ostream&, const config_status_request&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const config_status_request&, const config_status_request&) = default; @@ -2401,7 +2300,7 @@ struct config_status_reply serde::compat_version<0>> { errc error; - friend std::ostream& operator<<(std::ostream&, const config_status_reply&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const config_status_reply&, const config_status_reply&) = default; @@ -2419,8 +2318,7 @@ struct feature_action_request friend bool operator==( const feature_action_request&, const feature_action_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const feature_action_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(action); } }; @@ -2435,8 +2333,7 @@ struct feature_action_response friend bool operator==( const feature_action_response&, const feature_action_response&) = default; - friend std::ostream& - operator<<(std::ostream&, const feature_action_response&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(error); } }; @@ -2457,8 +2354,7 @@ struct feature_barrier_request friend bool operator==( const feature_barrier_request&, const feature_barrier_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const feature_barrier_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tag, peer, entered); } }; @@ -2476,8 +2372,7 @@ struct feature_barrier_response operator==(const feature_barrier_response&, const feature_barrier_response&) = default; - friend std::ostream& - operator<<(std::ostream&, const feature_barrier_response&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(entered, complete); } }; @@ -2493,8 +2388,7 @@ struct config_update_request final friend bool operator==( const config_update_request&, const config_update_request&) = default; - friend std::ostream& - operator<<(std::ostream&, const config_update_request&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(upsert, remove); } }; @@ -2510,7 +2404,7 @@ struct config_update_reply friend bool operator==( const config_update_reply&, const config_update_reply&) = default; - friend std::ostream& operator<<(std::ostream&, const config_update_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(error, latest_version); } }; @@ -2528,7 +2422,7 @@ struct hello_request final auto serde_fields() { return std::tie(peer, start_time); } - friend std::ostream& operator<<(std::ostream&, const hello_request&); + fmt::iterator format_to(fmt::iterator it) const; }; struct hello_reply @@ -2537,7 +2431,7 @@ struct hello_reply friend bool operator==(const hello_reply&, const hello_reply&) = default; - friend std::ostream& operator<<(std::ostream&, const hello_reply&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(error); } }; @@ -2552,7 +2446,7 @@ struct leader_term { std::optional leader; std::optional term; friend auto operator<=>(const leader_term&, const leader_term&) = default; - friend std::ostream& operator<<(std::ostream&, const leader_term&); + fmt::iterator format_to(fmt::iterator it) const; }; using assignments_set @@ -2644,15 +2538,14 @@ struct move_cancellation_result operator==(const move_cancellation_result&, const move_cancellation_result&) = default; - friend std::ostream& - operator<<(std::ostream& o, const move_cancellation_result&); + fmt::iterator format_to(fmt::iterator it) const; model::ntp ntp; cluster::errc result; }; enum class partition_move_direction { to_node, from_node, all }; -std::ostream& operator<<(std::ostream&, const partition_move_direction&); +fmt::iterator format_to(partition_move_direction, fmt::iterator); struct cancel_all_partition_movements_request : serde::envelope< @@ -2667,10 +2560,8 @@ struct cancel_all_partition_movements_request const cancel_all_partition_movements_request&, const cancel_all_partition_movements_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const cancel_all_partition_movements_request&) { - fmt::print(o, "{{}}"); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{}}"); } }; struct cancel_node_partition_movements_request @@ -2687,8 +2578,7 @@ struct cancel_node_partition_movements_request const cancel_node_partition_movements_request&, const cancel_node_partition_movements_request&) = default; - friend std::ostream& - operator<<(std::ostream& o, const cancel_node_partition_movements_request&); + fmt::iterator format_to(fmt::iterator it) const; }; struct cancel_partition_movements_reply @@ -2702,8 +2592,7 @@ struct cancel_partition_movements_reply auto serde_fields() { return std::tie(general_error, partition_results); } - friend std::ostream& - operator<<(std::ostream& o, const cancel_partition_movements_reply& r); + fmt::iterator format_to(fmt::iterator it) const; errc general_error; std::vector partition_results; @@ -3089,7 +2978,7 @@ class broker_state friend bool operator==(const broker_state&, const broker_state&) = default; - friend std::ostream& operator<<(std::ostream&, const broker_state&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(_membership_state, _maintenance_state); @@ -3110,7 +2999,7 @@ struct node_metadata { friend bool operator==(const node_metadata&, const node_metadata&) = default; - friend std::ostream& operator<<(std::ostream&, const node_metadata&); + fmt::iterator format_to(fmt::iterator it) const; }; // Node update types, used for communication between members_manager and @@ -3139,8 +3028,7 @@ enum class node_update_type : int8_t { // previous updates must be interrupted interrupted, }; - -std::ostream& operator<<(std::ostream&, const node_update_type&); +fmt::iterator format_to(node_update_type, fmt::iterator); /** * Reconfiguration state indicates if ongoing reconfiguration is a result of @@ -3152,6 +3040,7 @@ enum class reconfiguration_state { cancelled = 2, force_cancelled = 3 }; +fmt::iterator format_to(reconfiguration_state, fmt::iterator); inline bool is_cancelled_state(reconfiguration_state rs) { switch (rs) { @@ -3166,8 +3055,6 @@ inline bool is_cancelled_state(reconfiguration_state rs) { } } -std::ostream& operator<<(std::ostream&, reconfiguration_state); - struct replica_bytes { model::node_id node; size_t bytes_left{0}; @@ -3212,8 +3099,7 @@ enum class cloud_storage_mode : uint8_t { cloud_topic_read_replica = 6, tiered_cloud_topic = 7 }; - -std::ostream& operator<<(std::ostream&, const cloud_storage_mode&); +fmt::iterator format_to(cloud_storage_mode, fmt::iterator); struct partition_cloud_storage_status { cloud_storage_mode mode; @@ -3288,17 +3174,6 @@ struct controller_committed_offset_reply auto serde_fields() { return std::tie(last_committed, result); } }; -template -std::ostream& operator<<( - std::ostream& o, const configuration_with_assignment& with_assignment) { - fmt::print( - o, - "{{configuration: {}, assignments: {}}}", - with_assignment.cfg, - with_assignment.assignments); - return o; -} - /** * Create/update a (Wasm) plugin. */ @@ -3358,8 +3233,6 @@ struct remove_plugin_response auto serde_fields() { return std::tie(uuid, ec); } }; -std::ostream& operator<<(std::ostream&, reconfiguration_policy); - struct update_partition_replicas_cmd_data : serde::envelope< update_partition_replicas_cmd_data, @@ -3375,8 +3248,7 @@ struct update_partition_replicas_cmd_data auto serde_fields() { return std::tie(ntp, replicas, policy); } - friend std::ostream& - operator<<(std::ostream&, const update_partition_replicas_cmd_data&); + fmt::iterator format_to(fmt::iterator it) const; }; struct topic_disabled_partitions_set @@ -3399,8 +3271,7 @@ struct topic_disabled_partitions_set auto serde_fields() { return std::tie(partitions); } - friend std::ostream& - operator<<(std::ostream&, const topic_disabled_partitions_set&); + fmt::iterator format_to(fmt::iterator it) const; bool is_disabled(model::partition_id id) const { return !partitions || partitions->contains(id); diff --git a/src/v/cluster_link/BUILD b/src/v/cluster_link/BUILD index 18065e4cb8f79..c7e540207c0a6 100644 --- a/src/v/cluster_link/BUILD +++ b/src/v/cluster_link/BUILD @@ -33,6 +33,7 @@ redpanda_cc_library( visibility = ["//visibility:public"], deps = [ "//src/v/base", + "@fmt", ], ) diff --git a/src/v/cluster_link/errc.h b/src/v/cluster_link/errc.h index 54aa59d15aac1..ca2a5d27545cb 100644 --- a/src/v/cluster_link/errc.h +++ b/src/v/cluster_link/errc.h @@ -13,6 +13,8 @@ #include "base/outcome.h" +#include + #include namespace cluster_link { @@ -75,3 +77,10 @@ namespace std { template<> struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter : fmt::formatter { + auto format(cluster_link::errc e, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(e), ctx); + } +}; diff --git a/src/v/cluster_link/link_probe.cc b/src/v/cluster_link/link_probe.cc index ac4780e2b5fe1..95bfa88df5d8a 100644 --- a/src/v/cluster_link/link_probe.cc +++ b/src/v/cluster_link/link_probe.cc @@ -118,7 +118,7 @@ void link_probe::setup_status_metrics() { auto status_to_metric = std::views::transform( [this, &sl_name](const model::mirror_topic_status status) { auto status_label = sm::label_instance( - "status", to_string_view(status)); + "status", fmt::format("{}", status)); return sm::make_gauge( "shadow_topic_state", [this, status] { return count_topics(status); }, diff --git a/src/v/cluster_link/model/types.cc b/src/v/cluster_link/model/types.cc index d180cde5b737c..6de8ebc4ed57b 100644 --- a/src/v/cluster_link/model/types.cc +++ b/src/v/cluster_link/model/types.cc @@ -57,18 +57,6 @@ bool is_valid_status_transition( } } -std::ostream& operator<<(std::ostream& os, const link_status& s) { - return os << fmt::format("{}", s); -} - -std::ostream& operator<<(std::ostream& os, mirror_topic_status s) { - return os << fmt::format("{}", s); -} - -std::ostream& operator<<(std::ostream& os, task_state s) { - return os << fmt::format("{}", s); -} - std::ostream& operator<<(std::ostream& os, const scram_credentials& creds) { return os << fmt::format("{}", creds); } @@ -93,14 +81,6 @@ std::ostream& operator<<(std::ostream& os, const mirror_topic_metadata& md) { return os << fmt::format("{}", md); } -std::ostream& operator<<(std::ostream& os, filter_pattern_type f) { - return os << fmt::format("{}", f); -} - -std::ostream& operator<<(std::ostream& os, filter_type f) { - return os << fmt::format("{}", f); -} - std::ostream& operator<<(std::ostream& os, const resource_name_filter_pattern& p) { return os << fmt::format("{}", p); @@ -277,10 +257,6 @@ update_cluster_link_configuration_cmd::copy() const { fmt::iterator delete_mirror_topic_cmd::format_to(fmt::iterator it) const { return fmt::format_to(it, "{{topic: {}}}", topic); } -auto format_as(acl_resource r) { return to_string_view(r); } -auto format_as(acl_pattern p) { return to_string_view(p); } -auto format_as(acl_operation o) { return to_string_view(o); } -auto format_as(acl_permission_type p) { return to_string_view(p); } fmt::iterator delete_shadow_link_cmd::format_to(fmt::iterator it) const { return fmt::format_to( @@ -406,11 +382,11 @@ fmt::iterator shadow_link_status_report::format_to(fmt::iterator it) const { auto fmt::formatter::format( cluster_link::model::task_state st, format_context& ctx) const -> decltype(ctx.out()) { - return fmt::format_to(ctx.out(), "{}", to_string_view(st)); + return cluster_link::model::format_to(st, ctx.out()); } auto fmt::formatter::format( - const cluster_link::model::scram_credentials& c, format_context& ctx) + const cluster_link::model::scram_credentials& c, format_context& ctx) const -> decltype(ctx.out()) { auto time = std::format( "{:%FT%H:%M:%S}", ::model::to_time_point(c.password_last_updated)); @@ -428,7 +404,7 @@ auto fmt::formatter< format( const std::optional& m, - format_context& ctx) -> decltype(ctx.out()) { + format_context& ctx) const -> decltype(ctx.out()) { if (!m) { return fmt::format_to(ctx.out(), "none"); } @@ -438,7 +414,7 @@ auto fmt::formatter< } auto fmt::formatter::format( - const cluster_link::model::tls_file_or_value& t, format_context& ctx) + const cluster_link::model::tls_file_or_value& t, format_context& ctx) const -> decltype(ctx.out()) { return ss::visit( t, @@ -458,7 +434,7 @@ auto fmt::formatter::format( auto fmt::formatter>:: format( const std::optional& m, - format_context& ctx) -> decltype(ctx.out()) { + format_context& ctx) const -> decltype(ctx.out()) { if (!m) { return fmt::format_to(ctx.out(), "not-set"); } @@ -469,7 +445,7 @@ auto fmt::formatter>:: } auto fmt::formatter::format( - const cluster_link::model::connection_config& c, format_context& ctx) + const cluster_link::model::connection_config& c, format_context& ctx) const -> decltype(ctx.out()) { return fmt::format_to( ctx.out(), @@ -494,7 +470,7 @@ auto fmt::formatter::format( } auto fmt::formatter>::format( - const std::optional& m, format_context& ctx) + const std::optional& m, format_context& ctx) const -> decltype(ctx.out()) { if (!m) { return fmt::format_to(ctx.out(), "none"); @@ -533,13 +509,13 @@ auto fmt::formatter< auto fmt::formatter::format( cluster_link::model::filter_pattern_type s, format_context& ctx) const -> decltype(ctx.out()) { - return fmt::format_to(ctx.out(), "{}", to_string_view(s)); + return cluster_link::model::format_to(s, ctx.out()); } auto fmt::formatter::format( cluster_link::model::filter_type s, format_context& ctx) const -> decltype(ctx.out()) { - return fmt::format_to(ctx.out(), "{}", to_string_view(s)); + return cluster_link::model::format_to(s, ctx.out()); } auto fmt::formatter::format( @@ -593,7 +569,7 @@ auto fmt::formatter::format( } auto fmt::formatter::format( - const cluster_link::model::metadata& m, format_context& ctx) + const cluster_link::model::metadata& m, format_context& ctx) const -> decltype(ctx.out()) { return fmt::format_to( ctx.out(), @@ -606,7 +582,7 @@ auto fmt::formatter::format( } auto fmt::formatter::format( - const cluster_link::model::add_mirror_topic_cmd& m, format_context& ctx) + const cluster_link::model::add_mirror_topic_cmd& m, format_context& ctx) const -> decltype(ctx.out()) { return fmt::format_to( ctx.out(), "{{topic: {}, metadata: {}}}", m.topic, m.metadata); @@ -615,7 +591,7 @@ auto fmt::formatter::format( auto fmt::formatter:: format( const cluster_link::model::update_mirror_topic_status_cmd& m, - format_context& ctx) -> decltype(ctx.out()) { + format_context& ctx) const -> decltype(ctx.out()) { return fmt::format_to( ctx.out(), "{{topic: {}, state: {}}}", m.topic, m.status); } @@ -623,7 +599,7 @@ auto fmt::formatter:: auto fmt::formatter:: format( const cluster_link::model::update_mirror_topic_properties_cmd& m, - format_context& ctx) -> decltype(ctx.out()) { + format_context& ctx) const -> decltype(ctx.out()) { return fmt::format_to( ctx.out(), "{{topic={}, partition_count={}, replication_factor={}, " diff --git a/src/v/cluster_link/model/types.h b/src/v/cluster_link/model/types.h index d08adc7c4bf82..b18842a470a44 100644 --- a/src/v/cluster_link/model/types.h +++ b/src/v/cluster_link/model/types.h @@ -151,11 +151,14 @@ static constexpr std::string_view to_string_view(mirror_topic_status s) { } } +static inline fmt::iterator +format_to(mirror_topic_status s, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(s)); +} + bool is_valid_status_transition( mirror_topic_status current, mirror_topic_status target) noexcept; -std::ostream& operator<<(std::ostream& os, mirror_topic_status s); - enum class task_state : uint8_t { /// The task is currently active and processing active, @@ -187,7 +190,9 @@ static constexpr std::string_view to_string_view(task_state st) { } } -std::ostream& operator<<(std::ostream& os, task_state s); +static inline fmt::iterator format_to(task_state st, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(st)); +} /** * @brief SCRAM credentials to use for authentication @@ -413,7 +418,10 @@ static constexpr std::string_view to_string_view(filter_pattern_type f) { return "unknown"; } -std::ostream& operator<<(std::ostream& os, filter_pattern_type f); +static inline fmt::iterator +format_to(filter_pattern_type f, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(f)); +} /// Whether or not the filter is an inclusive or exclusive filter enum class filter_type : uint8_t { include, exclude }; @@ -428,7 +436,9 @@ static constexpr std::string_view to_string_view(filter_type f) { return "unknown"; } -std::ostream& operator<<(std::ostream& os, filter_type f); +static inline fmt::iterator format_to(filter_type f, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(f)); +} struct resource_name_filter_pattern : serde::envelope< @@ -603,6 +613,10 @@ static constexpr std::string_view to_string_view(acl_resource r) { return "unknown"; } +static inline fmt::iterator format_to(acl_resource r, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(r)); +} + enum class acl_pattern : uint8_t { any, literal, prefixed, match }; static constexpr std::string_view to_string_view(acl_pattern p) { @@ -619,6 +633,10 @@ static constexpr std::string_view to_string_view(acl_pattern p) { return "unknown"; } +static inline fmt::iterator format_to(acl_pattern p, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(p)); +} + enum class acl_operation : uint8_t { any, all, @@ -664,6 +682,10 @@ static constexpr std::string_view to_string_view(acl_operation op) { return "unknown"; } +static inline fmt::iterator format_to(acl_operation op, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(op)); +} + enum class acl_permission_type : uint8_t { any, allow, deny }; static constexpr std::string_view to_string_view(acl_permission_type p) { @@ -678,6 +700,11 @@ static constexpr std::string_view to_string_view(acl_permission_type p) { return "unknown"; } +static inline fmt::iterator +format_to(acl_permission_type p, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(p)); +} + struct acl_resource_filter : serde::envelope< acl_resource_filter, @@ -805,8 +832,10 @@ static constexpr std::string_view to_string_view(link_status s) { return "paused"; } } -std::ostream& operator<<(std::ostream& os, const link_status& s); +static inline fmt::iterator format_to(link_status s, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(s)); +} /** * Link state. The state is modified by the cluster link tasks and is * persisted to the cluster link table. @@ -1245,15 +1274,21 @@ using status_report_ret_t = std::expected; } // namespace cluster_link::model template<> -struct fmt::formatter - : fmt::formatter { +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } auto format(cluster_link::model::mirror_topic_status s, format_context& ctx) - -> decltype(ctx.out()); + const -> decltype(ctx.out()) { + return cluster_link::model::format_to(s, ctx.out()); + } }; template<> -struct fmt::formatter - : fmt::formatter { +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } auto format(cluster_link::model::task_state, format_context& ctx) const -> decltype(ctx.out()); }; @@ -1261,9 +1296,9 @@ struct fmt::formatter template<> struct fmt::formatter : fmt::formatter { - auto - format(const cluster_link::model::scram_credentials& m, format_context& ctx) - -> decltype(ctx.out()); + auto format( + const cluster_link::model::scram_credentials& m, + format_context& ctx) const -> decltype(ctx.out()); }; template<> @@ -1273,7 +1308,7 @@ struct fmt::formatter< auto format( const std::optional< cluster_link::model::connection_config::authn_variant>& m, - format_context& ctx) -> decltype(ctx.out()); + format_context& ctx) const -> decltype(ctx.out()); }; template<> @@ -1297,9 +1332,9 @@ struct fmt::formatter return it; } - auto - format(const cluster_link::model::tls_file_or_value& m, format_context& ctx) - -> decltype(ctx.out()); + auto format( + const cluster_link::model::tls_file_or_value& m, + format_context& ctx) const -> decltype(ctx.out()); private: bool _is_sensitive{false}; @@ -1326,7 +1361,7 @@ struct fmt::formatter> } auto format( const std::optional& m, - format_context& ctx) -> decltype(ctx.out()); + format_context& ctx) const -> decltype(ctx.out()); private: bool _is_sensitive{false}; @@ -1335,15 +1370,16 @@ struct fmt::formatter> template<> struct fmt::formatter : fmt::formatter { - auto - format(const cluster_link::model::connection_config& m, format_context& ctx) - -> decltype(ctx.out()); + auto format( + const cluster_link::model::connection_config& m, + format_context& ctx) const -> decltype(ctx.out()); }; template<> struct fmt::formatter> : fmt::formatter { - auto format(const std::optional& m, format_context& ctx) + auto + format(const std::optional& m, format_context& ctx) const -> decltype(ctx.out()); }; @@ -1356,15 +1392,19 @@ struct fmt::formatter }; template<> -struct fmt::formatter - : fmt::formatter { +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } auto format(cluster_link::model::filter_pattern_type s, format_context& ctx) const -> decltype(ctx.out()); }; template<> -struct fmt::formatter - : fmt::formatter { +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } auto format(cluster_link::model::filter_type s, format_context& ctx) const -> decltype(ctx.out()); }; @@ -1414,7 +1454,8 @@ struct fmt::formatter template<> struct fmt::formatter : fmt::formatter { - auto format(const cluster_link::model::metadata& m, format_context& ctx) + auto + format(const cluster_link::model::metadata& m, format_context& ctx) const -> decltype(ctx.out()); }; @@ -1422,8 +1463,8 @@ template<> struct fmt::formatter : fmt::formatter { auto format( - const cluster_link::model::add_mirror_topic_cmd& m, format_context& ctx) - -> decltype(ctx.out()); + const cluster_link::model::add_mirror_topic_cmd& m, + format_context& ctx) const -> decltype(ctx.out()); }; template<> @@ -1431,7 +1472,7 @@ struct fmt::formatter : fmt::formatter { auto format( const cluster_link::model::update_mirror_topic_status_cmd& m, - format_context& ctx) -> decltype(ctx.out()); + format_context& ctx) const -> decltype(ctx.out()); }; template<> @@ -1439,7 +1480,7 @@ struct fmt::formatter : fmt::formatter { auto format( const cluster_link::model::update_mirror_topic_properties_cmd& m, - format_context& ctx) -> decltype(ctx.out()); + format_context& ctx) const -> decltype(ctx.out()); }; template<> diff --git a/src/v/cluster_link/replication/BUILD b/src/v/cluster_link/replication/BUILD index 244cbd6eb0e32..6491942ed037f 100644 --- a/src/v/cluster_link/replication/BUILD +++ b/src/v/cluster_link/replication/BUILD @@ -92,6 +92,7 @@ redpanda_cc_library( "//src/v/model", "//src/v/ssx:work_queue", "//src/v/utils:to_string", + "@fmt", "@seastar", ], ) diff --git a/src/v/cluster_link/replication/link_replication_mgr.cc b/src/v/cluster_link/replication/link_replication_mgr.cc index 0fe1caf5aeb71..420134cd58392 100644 --- a/src/v/cluster_link/replication/link_replication_mgr.cc +++ b/src/v/cluster_link/replication/link_replication_mgr.cc @@ -14,7 +14,6 @@ #include "cluster_link/logger.h" #include "ssx/async_algorithm.h" #include "ssx/future-util.h" -#include "utils/to_string.h" using namespace std::chrono_literals; @@ -82,15 +81,16 @@ void link_replication_manager::unset_data_probe() { } } -std::ostream& -operator<<(std::ostream& os, link_replication_manager::op_type op) { +fmt::iterator +format_to(link_replication_manager::op_type op, fmt::iterator out) { + using op_type = link_replication_manager::op_type; switch (op) { - case link_replication_manager::op_type::start: - return os << "start"; - case link_replication_manager::op_type::stop: - return os << "stop"; + case op_type::start: + return fmt::format_to(out, "start"); + case op_type::stop: + return fmt::format_to(out, "stop"); default: - return os << "unknown"; + return fmt::format_to(out, "unknown"); } } diff --git a/src/v/cluster_link/replication/link_replication_mgr.h b/src/v/cluster_link/replication/link_replication_mgr.h index ba30c6569d2d2..d41fd7e2e750c 100644 --- a/src/v/cluster_link/replication/link_replication_mgr.h +++ b/src/v/cluster_link/replication/link_replication_mgr.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cluster_link/replication/partition_replicator.h" #include "cluster_link/replication/replication_probe.h" #include "cluster_link/replication/types.h" @@ -64,7 +65,7 @@ class link_replication_manager { // Allowed operations on a replicator enum class op_type : uint8_t { start, stop }; - friend std::ostream& operator<<(std::ostream& os, op_type); + friend fmt::iterator format_to(op_type op, fmt::iterator out); struct ntp_target_state { op_type op; std::optional<::model::term_id> term; diff --git a/src/v/compaction/types.cc b/src/v/compaction/types.cc index bc360cae7a2f5..6fdf2bf582eb6 100644 --- a/src/v/compaction/types.cc +++ b/src/v/compaction/types.cc @@ -12,46 +12,41 @@ #include "utils/to_string.h" #include -#include - -#include namespace compaction { -std::ostream& operator<<(std::ostream& o, const compaction_config& c) { - fmt::print( - o, +fmt::iterator compaction_config::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{max_removable_local_log_offset:{}, " "max_tombstone_remove_offset:{}, " "max_tx_end_remove_offset:{}, " "should_sanitize:{}, " "tombstone_retention_ms:{}, " "tx_retention_ms:{}}}", - c.max_removable_local_log_offset, - c.max_tombstone_remove_offset, - c.max_tx_end_remove_offset, - c.sanitizer_config, - c.tombstone_retention_ms, - c.tx_retention_ms); - return o; + max_removable_local_log_offset, + max_tombstone_remove_offset, + max_tx_end_remove_offset, + sanitizer_config, + tombstone_retention_ms, + tx_retention_ms); } -std::ostream& operator<<(std::ostream& o, const stats& s) { - fmt::print( - o, +fmt::iterator stats::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ batches_processed: {}, batches_discarded: {}, " "records_discarded: {}, expired_tombstones_discarded: {}, " "non_compactible_batches: {}{}}}", - s.batches_processed, - s.batches_discarded, - s.records_discarded, - s.expired_tombstones_discarded, - s.non_compactible_batches, - s.control_batches_discarded > 0 + batches_processed, + batches_discarded, + records_discarded, + expired_tombstones_discarded, + non_compactible_batches, + control_batches_discarded > 0 ? fmt::format( - ", control_batches_discarded: {}", s.control_batches_discarded) + ", control_batches_discarded: {}", control_batches_discarded) : ""); - return o; } } // namespace compaction diff --git a/src/v/compaction/types.h b/src/v/compaction/types.h index 4dd1ff17e92ce..4ef73f321e00d 100644 --- a/src/v/compaction/types.h +++ b/src/v/compaction/types.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "compaction/key_offset_map.h" #include "model/fundamental.h" #include "storage/file_sanitizer_types.h" @@ -99,7 +100,7 @@ struct compaction_config { // abort source for compaction task ss::abort_source* asrc; - friend std::ostream& operator<<(std::ostream&, const compaction_config&); + fmt::iterator format_to(fmt::iterator it) const; }; // POD containing compaction statistics from a `compaction::filter`'s run. @@ -128,7 +129,7 @@ struct stats { || expired_tombstones_discarded > 0; } - friend std::ostream& operator<<(std::ostream& os, const stats& s); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace compaction diff --git a/src/v/compression/compression.cc b/src/v/compression/compression.cc index d1c3695075917..6105d3ed5eb72 100644 --- a/src/v/compression/compression.cc +++ b/src/v/compression/compression.cc @@ -91,18 +91,4 @@ ss::future stream_compressor::uncompress(iobuf io, type t) { } } -std::ostream& operator<<(std::ostream& os, const type& c) { - switch (c) { - case type::gzip: - return os << "gzip"; - case type::java_snappy: - return os << "java_snappy"; - case type::lz4: - return os << "lz4"; - case type::zstd: - return os << "zstd"; - } - return os << "compression::type::unknown(" << std::to_underlying(c) << ")"; -} - } // namespace compression diff --git a/src/v/compression/compression.h b/src/v/compression/compression.h index 0691386b946bb..572d8c79f3639 100644 --- a/src/v/compression/compression.h +++ b/src/v/compression/compression.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" namespace compression { @@ -23,7 +24,21 @@ enum class type : uint8_t { lz4, zstd, }; -std::ostream& operator<<(std::ostream& os, const type& c); + +inline fmt::iterator format_to(type c, fmt::iterator out) { + switch (c) { + case type::gzip: + return fmt::format_to(out, "gzip"); + case type::java_snappy: + return fmt::format_to(out, "java_snappy"); + case type::lz4: + return fmt::format_to(out, "lz4"); + case type::zstd: + return fmt::format_to(out, "zstd"); + } + return fmt::format_to( + out, "compression::type::unknown({})", std::to_underlying(c)); +} // a very simple compressor. Exposes virtually no knobs and uses // the defaults for all compressors. In the future, we can make these diff --git a/src/v/compression/lz4_decompression_buffers.cc b/src/v/compression/lz4_decompression_buffers.cc index 2b899c278c1ba..89610ee0ace56 100644 --- a/src/v/compression/lz4_decompression_buffers.cc +++ b/src/v/compression/lz4_decompression_buffers.cc @@ -18,20 +18,22 @@ namespace compression { -std::ostream& operator<<( - std::ostream& os, lz4_decompression_buffers::alloc_ctx::allocation_state st) { +fmt::iterator format_to( + lz4_decompression_buffers::alloc_ctx::allocation_state st, + fmt::iterator out) { switch (st) { using enum compression::lz4_decompression_buffers::alloc_ctx:: allocation_state; case no_buffers_allocated: - return os << "no buffers allocated"; + return fmt::format_to(out, "no buffers allocated"); case input_buffer_allocated: - return os << "input buffer allocated"; + return fmt::format_to(out, "input buffer allocated"); case output_buffer_allocated: - return os << "output buffer allocated"; + return fmt::format_to(out, "output buffer allocated"); case both_buffers_allocated: - return os << "both buffers allocated"; + return fmt::format_to(out, "both buffers allocated"); } + return fmt::format_to(out, ""); } lz4_decompression_buffers::lz4_decompression_buffers( diff --git a/src/v/compression/lz4_decompression_buffers.h b/src/v/compression/lz4_decompression_buffers.h index d1352ea0493f7..db8f415efc6d2 100644 --- a/src/v/compression/lz4_decompression_buffers.h +++ b/src/v/compression/lz4_decompression_buffers.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "base/units.h" #define LZ4F_STATIC_LINKING_ONLY @@ -109,8 +110,8 @@ class lz4_decompression_buffers { stats _allocation_stats; }; -std::ostream& operator<<( - std::ostream&, lz4_decompression_buffers::alloc_ctx::allocation_state); +fmt::iterator format_to( + lz4_decompression_buffers::alloc_ctx::allocation_state, fmt::iterator); // Initializes the buffer instance. If preallocation is disabled the instance // will pass through all calls to malloc and free. Two buffers of size diff --git a/src/v/config/base_property.cc b/src/v/config/base_property.cc index 7fafa2a53e1a7..e85e2b558ab75 100644 --- a/src/v/config/base_property.cc +++ b/src/v/config/base_property.cc @@ -12,8 +12,6 @@ #include "base/vassert.h" #include "config/config_store.h" -#include - namespace config { base_property::base_property( config_store& conf, @@ -31,11 +29,6 @@ base_property::base_property( } } -std::ostream& operator<<(std::ostream& o, const base_property& p) { - p.print(o); - return o; -} - std::string_view to_string_view(visibility v) { switch (v) { case config::visibility::tunable: @@ -48,6 +41,9 @@ std::string_view to_string_view(visibility v) { return "{invalid}"; } +fmt::iterator format_to(visibility v, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(v)); +} /** * Helper for property methods that should only be used diff --git a/src/v/config/base_property.h b/src/v/config/base_property.h index 204f3d6148041..7174d3683aba8 100644 --- a/src/v/config/base_property.h +++ b/src/v/config/base_property.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/validation_error.h" #include "json/stringbuffer.h" @@ -63,6 +64,7 @@ enum class odd_even_constraint { using legacy_version = named_type; std::string_view to_string_view(visibility v); +fmt::iterator format_to(visibility v, fmt::iterator); class base_property { public: @@ -111,7 +113,7 @@ class base_property { virtual void to_json( json::Writer& w, redact_secrets redact) const = 0; - virtual void print(std::ostream&) const = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; virtual bool set_value(YAML::Node) = 0; virtual void set_value(std::any) = 0; virtual void reset() = 0; @@ -171,7 +173,6 @@ class base_property { virtual void notify_original_version(legacy_version) = 0; private: - friend std::ostream& operator<<(std::ostream&, const base_property&); std::string_view _name; std::string_view _desc; diff --git a/src/v/config/broker_authn_endpoint.cc b/src/v/config/broker_authn_endpoint.cc index e444f7457d722..a51945018a637 100644 --- a/src/v/config/broker_authn_endpoint.cc +++ b/src/v/config/broker_authn_endpoint.cc @@ -29,6 +29,9 @@ std::string_view to_string_view(broker_authn_method m) { return "mtls_identity"; } } +fmt::iterator format_to(broker_authn_method m, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(m)); +} template<> std::optional @@ -40,9 +43,13 @@ from_string_view(std::string_view sv) { .default_match(broker_authn_method::none); } -std::ostream& operator<<(std::ostream& os, const broker_authn_endpoint& ep) { - fmt::print(os, "{{{}:{}:{}}}", ep.name, ep.address, ep.authn_method); - return os; +fmt::iterator broker_authn_endpoint::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{{}:{}:{}}}", + name, + address, + authn_method ? to_string_view(*authn_method) : "none"); } bool kafka_authz_enabled() { diff --git a/src/v/config/broker_authn_endpoint.h b/src/v/config/broker_authn_endpoint.h index 081b79d3c6638..2a74b6ee163bb 100644 --- a/src/v/config/broker_authn_endpoint.h +++ b/src/v/config/broker_authn_endpoint.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "config/convert.h" #include "config/from_string_view.h" #include "config/property.h" @@ -34,6 +35,7 @@ enum class broker_authn_method { }; std::string_view to_string_view(broker_authn_method m); +fmt::iterator format_to(broker_authn_method m, fmt::iterator); template<> std::optional @@ -47,8 +49,7 @@ struct broker_authn_endpoint { friend bool operator==( const broker_authn_endpoint&, const broker_authn_endpoint&) = default; - friend std::ostream& - operator<<(std::ostream& os, const broker_authn_endpoint& ep); + fmt::iterator format_to(fmt::iterator it) const; }; namespace detail { diff --git a/src/v/config/config_store.h b/src/v/config/config_store.h index 12fe7914f9eda..2d45b6e846cd2 100644 --- a/src/v/config/config_store.h +++ b/src/v/config/config_store.h @@ -221,12 +221,12 @@ class config_store { return all; } - friend std::ostream& - operator<<(std::ostream& o, const config::config_store& c) { - o << "{ "; - c.for_each([&o](const auto& property) { o << property << " "; }); - o << "}"; - return o; + fmt::iterator format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{ "); + for_each([&it](const auto& property) { + it = fmt::format_to(it, "{} ", property); + }); + return fmt::format_to(it, "}}"); } void notify_original_version(legacy_version ov) { diff --git a/src/v/config/convert.h b/src/v/config/convert.h index ca9dd563c7d44..6c11e68f5a2e2 100644 --- a/src/v/config/convert.h +++ b/src/v/config/convert.h @@ -696,7 +696,7 @@ template<> struct convert { using type = config::leaders_preference; - static Node encode(const type& rhs) { return Node(fmt::to_string(rhs)); } + static Node encode(const type& rhs) { return Node(fmt::format("{}", rhs)); } static bool decode(const Node& node, type& rhs) { auto node_str = node.as(); diff --git a/src/v/config/data_directory_path.h b/src/v/config/data_directory_path.h index 250a85dc9c99a..aaf486b0c1d18 100644 --- a/src/v/config/data_directory_path.h +++ b/src/v/config/data_directory_path.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -30,9 +31,8 @@ struct data_directory_path { friend bool operator==( const data_directory_path&, const data_directory_path&) = default; - friend std::ostream& - operator<<(std::ostream& o, const config::data_directory_path& p) { - return o << "{data_directory=" << p.path << "}"; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{data_directory={}}}", path); } }; diff --git a/src/v/config/endpoint_tls_config.h b/src/v/config/endpoint_tls_config.h index 08279aa60ac33..12a519b71299c 100644 --- a/src/v/config/endpoint_tls_config.h +++ b/src/v/config/endpoint_tls_config.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/convert.h" #include "config/tls_config.h" @@ -25,10 +26,8 @@ struct endpoint_tls_config { ss::sstring name; tls_config config; - friend std::ostream& - operator<<(std::ostream& o, const endpoint_tls_config& cfg) { - fmt::print(o, "{{name: {}, tls_config: {}}}", cfg.name, cfg.config); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{name: {}, tls_config: {}}}", name, config); } bool operator==(const endpoint_tls_config& rhs) const = default; diff --git a/src/v/config/leaders_preference.cc b/src/v/config/leaders_preference.cc index 663e9a708ee49..ec87204c66dcd 100644 --- a/src/v/config/leaders_preference.cc +++ b/src/v/config/leaders_preference.cc @@ -17,14 +17,9 @@ namespace config { -std::ostream& operator<<(std::ostream& os, leaders_preference::type_t type) { - os << leaders_preference::type_to_string(type); - return os; -} - -std::ostream& operator<<(std::ostream& os, const leaders_preference& lp) { - auto prefix = leaders_preference::type_to_prefix(lp.type); - return os << prefix << fmt::format("{}", fmt::join(lp.racks, ",")); +fmt::iterator leaders_preference::format_to(fmt::iterator it) const { + auto prefix = type_to_prefix(type); + return fmt::format_to(it, "{}{}", prefix, fmt::join(racks, ",")); } leaders_preference leaders_preference::parse(std::string_view s) { diff --git a/src/v/config/leaders_preference.h b/src/v/config/leaders_preference.h index 5b2a5da8b8355..ca3a28e71ac71 100644 --- a/src/v/config/leaders_preference.h +++ b/src/v/config/leaders_preference.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "model/metadata.h" #include "serde/envelope.h" @@ -78,8 +79,8 @@ struct leaders_preference static leaders_preference parse(std::string_view); - friend std::ostream& operator<<(std::ostream&, type_t); - friend std::ostream& operator<<(std::ostream&, const leaders_preference&); + fmt::iterator format_to(fmt::iterator it) const; + friend std::istream& operator>>(std::istream&, leaders_preference&); friend bool @@ -88,4 +89,9 @@ struct leaders_preference auto serde_fields() { return std::tie(type, racks); } }; +inline fmt::iterator +format_to(leaders_preference::type_t t, fmt::iterator out) { + return fmt::format_to(out, "{}", leaders_preference::type_to_string(t)); +} + } // namespace config diff --git a/src/v/config/node_overrides.cc b/src/v/config/node_overrides.cc index 9fa3766348373..8be3cf002c58f 100644 --- a/src/v/config/node_overrides.cc +++ b/src/v/config/node_overrides.cc @@ -18,12 +18,12 @@ namespace config { -std::ostream& operator<<(std::ostream& os, const config::node_id_override& v) { - if (v.ignore_existing_node_id) { - return os << ssx::sformat( - "{}:{}:{}/ignore_existing_node_id", v.key, v.uuid, v.id); +fmt::iterator node_id_override::format_to(fmt::iterator it) const { + if (ignore_existing_node_id) { + return fmt::format_to( + it, "{}:{}:{}/ignore_existing_node_id", key, uuid, id); } - return os << ssx::sformat("{}:{}:{}", v.key, v.uuid, v.id); + return fmt::format_to(it, "{}:{}:{}", key, uuid, id); } std::istream& operator>>(std::istream& is, config::node_id_override& v) { diff --git a/src/v/config/node_overrides.h b/src/v/config/node_overrides.h index 0eef0d027481b..187b644144b81 100644 --- a/src/v/config/node_overrides.h +++ b/src/v/config/node_overrides.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "config/convert.h" #include "model/fundamental.h" #include "ssx/sformat.h" @@ -54,9 +55,9 @@ struct node_id_override { */ bool ignore_existing_node_id{false}; + fmt::iterator format_to(fmt::iterator it) const; + private: - friend std::ostream& - operator<<(std::ostream& os, const node_id_override& v); friend std::istream& operator>>(std::istream& is, node_id_override& v); friend bool operator==(const node_id_override&, const node_id_override&) = default; diff --git a/src/v/config/property.h b/src/v/config/property.h index 94e5554a04af2..8c7d891ae647b 100644 --- a/src/v/config/property.h +++ b/src/v/config/property.h @@ -148,14 +148,11 @@ class property : public base_property { operator value_type() const { return value(); } // NOLINT - void print(std::ostream& o) const override { - o << name() << ":"; - + fmt::iterator format_to(fmt::iterator it) const override { if (is_secret() && !is_default()) { - o << secret_placeholder; - } else { - o << _value; + return fmt::format_to(it, "{}:{}", name(), secret_placeholder); } + return fmt::format_to(it, "{}:{}", name(), _value); } // serialize the value. the key is taken from the property name at the @@ -991,9 +988,9 @@ class retention_duration_property final return update_value(n.as()); } - void print(std::ostream& o) const final { + fmt::iterator format_to(fmt::iterator it) const final { vassert(!is_secret(), "{} must not be a secret", name()); - o << name() << ":" << _value.value_or(-1ms); + return fmt::format_to(it, "{}:{}", name(), _value.value_or(-1ms)); } // serialize the value. the key is taken from the property name at the diff --git a/src/v/config/rest_authn_endpoint.cc b/src/v/config/rest_authn_endpoint.cc index 69538c2280909..ee0b3a6c646d2 100644 --- a/src/v/config/rest_authn_endpoint.cc +++ b/src/v/config/rest_authn_endpoint.cc @@ -23,6 +23,9 @@ std::string_view to_string_view(rest_authn_method m) { } return "invalid"; } +fmt::iterator format_to(rest_authn_method m, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(m)); +} template<> std::optional @@ -33,14 +36,13 @@ from_string_view(std::string_view sv) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& os, const rest_authn_endpoint& ep) { +fmt::iterator rest_authn_endpoint::format_to(fmt::iterator it) const { std::string_view authn_method_str{""}; - if (ep.authn_method) { - authn_method_str = to_string_view(*ep.authn_method); + if (authn_method) { + authn_method_str = to_string_view(*authn_method); } - fmt::print(os, "{{{}:{}:{}}}", ep.name, ep.address, authn_method_str); - return os; + return fmt::format_to(it, "{{{}:{}:{}}}", name, address, authn_method_str); } rest_authn_method get_authn_method( diff --git a/src/v/config/rest_authn_endpoint.h b/src/v/config/rest_authn_endpoint.h index 04c7e91c638e8..7d9ffd34a0bcd 100644 --- a/src/v/config/rest_authn_endpoint.h +++ b/src/v/config/rest_authn_endpoint.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "config/convert.h" #include "config/from_string_view.h" #include "config/property.h" @@ -34,6 +35,7 @@ enum class rest_authn_method { }; std::string_view to_string_view(rest_authn_method m); +fmt::iterator format_to(rest_authn_method m, fmt::iterator); template<> std::optional @@ -47,8 +49,7 @@ struct rest_authn_endpoint { friend bool operator==( const rest_authn_endpoint&, const rest_authn_endpoint&) = default; - friend std::ostream& - operator<<(std::ostream& os, const rest_authn_endpoint& ep); + fmt::iterator format_to(fmt::iterator it) const; }; // A helper method that searches for the listener within a vector of diff --git a/src/v/config/rjson_serialization.cc b/src/v/config/rjson_serialization.cc index b9039434a37d7..9e8385affcdf3 100644 --- a/src/v/config/rjson_serialization.cc +++ b/src/v/config/rjson_serialization.cc @@ -153,7 +153,7 @@ void rjson_serialize( } /** - * Helper for enum/bitfield types that implement operator<< for ostream. + * Helper for enum/bitfield types that are formattable via fmt. * Otherwise they would be JSON-ized as their integer representation. */ template diff --git a/src/v/config/rjson_serialization.h b/src/v/config/rjson_serialization.h index 811a7b320a1db..d68926dc8662f 100644 --- a/src/v/config/rjson_serialization.h +++ b/src/v/config/rjson_serialization.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/data_directory_path.h" #include "config/endpoint_tls_config.h" @@ -42,6 +43,11 @@ struct custom_aggregate { } static consteval std::string_view type_name() { return "custom_aggregate"; } + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{string: {}, int: {}}}", string_value, int_value); + } }; } // namespace testing diff --git a/src/v/config/sasl_mechanisms.cc b/src/v/config/sasl_mechanisms.cc index e3edf4ec12715..31ce2b0e4299b 100644 --- a/src/v/config/sasl_mechanisms.cc +++ b/src/v/config/sasl_mechanisms.cc @@ -50,10 +50,8 @@ get_sasl_mechanisms(const std::string_view listener) { return it->sasl_mechanisms; } -std::ostream& -operator<<(std::ostream& os, const sasl_mechanisms_override& rhs) { - fmt::print(os, "{{{}:{}}}", rhs.listener, rhs.sasl_mechanisms); - return os; +fmt::iterator sasl_mechanisms_override::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}:{}}}", listener, sasl_mechanisms); } bool is_enterprise_sasl_mechanisms_override( diff --git a/src/v/config/sasl_mechanisms.h b/src/v/config/sasl_mechanisms.h index 703b00645f419..3122245f3b0b0 100644 --- a/src/v/config/sasl_mechanisms.h +++ b/src/v/config/sasl_mechanisms.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/property.h" #include "json/stringbuffer.h" @@ -57,8 +58,7 @@ struct sasl_mechanisms_override { operator==(const sasl_mechanisms_override&, const sasl_mechanisms_override&) = default; - friend std::ostream& - operator<<(std::ostream& os, const sasl_mechanisms_override& ep); + fmt::iterator format_to(fmt::iterator it) const; }; // Checks if there are any enterprise sasl mechanisms in this override diff --git a/src/v/config/seed_server.h b/src/v/config/seed_server.h index 51a249adc03aa..3662fd6a8742a 100644 --- a/src/v/config/seed_server.h +++ b/src/v/config/seed_server.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/convert.h" #include "utils/unresolved_address.h" @@ -29,10 +30,8 @@ struct seed_server { return lhs.addr < rhs.addr; } - friend std::ostream& - operator<<(std::ostream& o, const config::seed_server& s) { - fmt::print(o, "addr: {}", s.addr); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "addr: {}", addr); } }; } // namespace config diff --git a/src/v/config/tests/BUILD b/src/v/config/tests/BUILD index aa701ac12ecd6..92b5f4c0f8a75 100644 --- a/src/v/config/tests/BUILD +++ b/src/v/config/tests/BUILD @@ -247,6 +247,7 @@ redpanda_cc_gtest( deps = [ "//src/v/config", "//src/v/test_utils:gtest", + "@fmt", "@googletest//:gtest", ], ) diff --git a/src/v/config/tests/config_store_test.cc b/src/v/config/tests/config_store_test.cc index a4c8ef77ae45f..e8e92d2eeba9d 100644 --- a/src/v/config/tests/config_store_test.cc +++ b/src/v/config/tests/config_store_test.cc @@ -147,12 +147,6 @@ YAML::Node valid_configuration() { } // namespace namespace std { -static inline ostream& -operator<<(ostream& o, const testing::custom_aggregate& c) { - o << "int_value=" << c.int_value << ", string_value=" << c.string_value; - return o; -} - static inline std::ostream& operator<<(std::ostream& ostr, const std::optional& rhs) { if (rhs.has_value()) { diff --git a/src/v/config/tests/configuration_test.cc b/src/v/config/tests/configuration_test.cc index a5caef84f2bb5..5cb8d6cebfdf0 100644 --- a/src/v/config/tests/configuration_test.cc +++ b/src/v/config/tests/configuration_test.cc @@ -10,6 +10,8 @@ #include "config/base_property.h" #include "config/configuration.h" +#include +#include #include ss::logger lg("config_test"); // NOLINT @@ -23,14 +25,17 @@ TEST(ConfigurationTest, Roundtrip) { auto& cfg = config::shard_local_cfg(); YAML::Node root_out = to_yaml(cfg, redact_secrets::no); - lg.debug("Configuration as YAML: {}", root_out); + lg.debug( + "Configuration as YAML: {}", fmt::format("{}", fmt_streamed(root_out))); try { cfg.read_yaml(root_out); YAML::Node root_in = to_yaml(cfg, redact_secrets::no); // Compare the two YAML strings. - EXPECT_EQ(fmt::format("{}", root_out), fmt::format("{}", root_in)); + auto yaml_out = fmt::format("{}", fmt_streamed(root_out)); + auto yaml_in = fmt::format("{}", fmt_streamed(root_in)); + EXPECT_EQ(yaml_out, yaml_in); } catch (const std::exception& e) { FAIL() << e.what(); } diff --git a/src/v/config/tests/leaders_preference_test.cc b/src/v/config/tests/leaders_preference_test.cc index cef39a9486976..dcfb00eca271e 100644 --- a/src/v/config/tests/leaders_preference_test.cc +++ b/src/v/config/tests/leaders_preference_test.cc @@ -27,7 +27,7 @@ SEASTAR_THREAD_TEST_CASE(parse_leaders_preference) { { // default round trip lp -> str -> lp lp orig{}; - auto parsed = lp::parse(fmt::to_string(orig)); + auto parsed = lp::parse(fmt::format("{}", orig)); BOOST_CHECK_EQUAL(orig, parsed); } @@ -60,7 +60,7 @@ SEASTAR_THREAD_TEST_CASE(parse_leaders_preference) { lp orig{}; orig.type = pref_type; orig.racks = {rack_a, rack_b}; - auto parsed = lp::parse(fmt::to_string(orig)); + auto parsed = lp::parse(fmt::format("{}", orig)); BOOST_CHECK_EQUAL(orig, parsed); } @@ -98,7 +98,7 @@ SEASTAR_THREAD_TEST_CASE(parse_leaders_preference) { lp orig{}; orig.type = lp_t::racks; orig.racks = {rack_a, rack_b}; - auto parsed = lp::parse(fmt::to_string(orig)); + auto parsed = lp::parse(fmt::format("{}", orig)); BOOST_CHECK_EQUAL(orig, parsed); } } diff --git a/src/v/config/throughput_control_group.cc b/src/v/config/throughput_control_group.cc index ca34b199b7032..92860986c8a08 100644 --- a/src/v/config/throughput_control_group.cc +++ b/src/v/config/throughput_control_group.cc @@ -68,9 +68,8 @@ struct copyable_RE2 : re2::RE2 { copyable_RE2& operator=(copyable_RE2&&) noexcept = delete; explicit copyable_RE2(const std::string& s) : RE2(s) {} - friend std::ostream& operator<<(std::ostream& os, const copyable_RE2& re) { - fmt::print(os, "{}", re.pattern()); - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", pattern()); } }; @@ -80,15 +79,6 @@ struct client_id_matcher_type { client_id_matcher_type() = default; explicit client_id_matcher_type(const copyable_RE2& d) : v(d) {} - friend std::ostream& operator<<( - std::ostream& os, const std::unique_ptr& mt) { - if (mt) { - fmt::print(os, "{{v: {}}}", mt->v); - } else { - fmt::print(os, "null"); - } - return os; - } }; throughput_control_group::throughput_control_group() = default; @@ -113,17 +103,22 @@ throughput_control_group::operator=(const throughput_control_group& other) { return *this = throughput_control_group(other); } -std::ostream& -operator<<(std::ostream& os, const throughput_control_group& tcg) { - fmt::print( - os, +fmt::iterator throughput_control_group::format_to(fmt::iterator it) const { + auto format_matcher = + [](const std::unique_ptr& mt) -> std::string { + if (mt) { + return fmt::format("{{v: {}}}", mt->v); + } + return "null"; + }; + return fmt::format_to( + it, "{{group_name: {}, client_id: {}, throughput_limit_node_in_bps: {}, " "throughput_limit_node_out_bps: {}}}", - tcg.is_noname() ? ""s : fmt::format("{{{}}}", tcg.name), - tcg.client_id_matcher, - tcg.throughput_limit_node_in_bps, - tcg.throughput_limit_node_out_bps); - return os; + is_noname() ? ""s : fmt::format("{{{}}}", name), + format_matcher(client_id_matcher), + throughput_limit_node_in_bps, + throughput_limit_node_out_bps); } bool throughput_control_group::match_client_id( diff --git a/src/v/config/throughput_control_group.h b/src/v/config/throughput_control_group.h index a5e2f5f7b7c9e..e8920dae23607 100644 --- a/src/v/config/throughput_control_group.h +++ b/src/v/config/throughput_control_group.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/property.h" #include "json/_include_first.h" @@ -45,8 +46,7 @@ struct throughput_control_group { operator==(const throughput_control_group&, const throughput_control_group&) = default; - friend std::ostream& - operator<<(std::ostream& os, const throughput_control_group& tcg); + fmt::iterator format_to(fmt::iterator it) const; bool match_client_id(std::optional client_id) const; bool is_noname() const noexcept; diff --git a/src/v/config/tls_config.cc b/src/v/config/tls_config.cc index ea9d239dc85a1..3069344f57011 100644 --- a/src/v/config/tls_config.cc +++ b/src/v/config/tls_config.cc @@ -111,27 +111,26 @@ std::optional tls_config::validate(const tls_config& c) { return std::nullopt; } -std::ostream& operator<<(std::ostream& o, const config::p12_container& p) { - fmt::print(o, "{{ p12 file: {}, p12 password: REDACTED }}", p.p12_path); - return o; +fmt::iterator p12_container::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ p12 file: {}, p12 password: REDACTED }}", p12_path); } -std::ostream& operator<<(std::ostream& o, const config::key_cert& c) { - o << "{ " - << "key_file: " << c.key_file << " " - << "cert_file: " << c.cert_file << " }"; - return o; +fmt::iterator key_cert::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ key_file: {} cert_file: {} }}", key_file, cert_file); } -std::ostream& operator<<(std::ostream& o, const config::tls_config& c) { - o << "{ " - << "enabled: " << c.is_enabled() << " " - << "key/cert files: " << c.get_key_cert_files() << " " - << "ca file: " << c.get_truststore_file() << " " - << "crl file: " << c.get_crl_file() << " " - << "client_auth_required: " << c.get_require_client_auth() << "" - << " }"; - return o; +fmt::iterator tls_config::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ enabled: {} key/cert files: {} ca file: {} crl file: {} " + "client_auth_required: {} }}", + is_enabled(), + get_key_cert_files(), + get_truststore_file(), + get_crl_file(), + get_require_client_auth()); } bool validate_tls_v1_2_cipher_suites(const ss::sstring& s) { diff --git a/src/v/config/tls_config.h b/src/v/config/tls_config.h index af458bd75b444..b35e77c48fa2c 100644 --- a/src/v/config/tls_config.h +++ b/src/v/config/tls_config.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/types.h" @@ -46,7 +47,7 @@ struct key_cert { return key_file == rhs.key_file && cert_file == rhs.cert_file; } - friend std::ostream& operator<<(std::ostream& o, const key_cert& c); + fmt::iterator format_to(fmt::iterator it) const; }; struct p12_container { @@ -56,7 +57,7 @@ struct p12_container { friend bool operator==(const p12_container&, const p12_container&) = default; - friend std::ostream& operator<<(std::ostream& os, const p12_container& p); + fmt::iterator format_to(fmt::iterator it) const; }; using key_cert_container = std::variant; @@ -140,8 +141,7 @@ class tls_config { bool operator==(const tls_config& rhs) const = default; - friend std::ostream& - operator<<(std::ostream& o, const config::tls_config& c); + fmt::iterator format_to(fmt::iterator it) const; private: bool _enabled{false}; diff --git a/src/v/config/types.cc b/src/v/config/types.cc index b8c3cc353d9dc..cf26355476d03 100644 --- a/src/v/config/types.cc +++ b/src/v/config/types.cc @@ -13,10 +13,6 @@ namespace config { -std::ostream& operator<<(std::ostream& os, audit_failure_policy policy) { - return os << to_string_view(policy); -} - std::istream& operator>>(std::istream& is, audit_failure_policy& policy) { ss::sstring s; is >> s; diff --git a/src/v/config/types.h b/src/v/config/types.h index 91b4d9c7c479a..745df0a84d09a 100644 --- a/src/v/config/types.h +++ b/src/v/config/types.h @@ -10,10 +10,9 @@ */ #pragma once +#include "base/format_to.h" #include "strings/string_switch.h" -#include - /* * Because `config::` is used across every part of Redpanda, it's easy to create * accidental circular dependencies by including sub-system specific types in @@ -48,12 +47,12 @@ namespace config { enum class s3_url_style { virtual_host = 0, path }; -inline std::ostream& operator<<(std::ostream& os, const s3_url_style& us) { +inline fmt::iterator format_to(s3_url_style us, fmt::iterator out) { switch (us) { case s3_url_style::virtual_host: - return os << "virtual_host"; + return fmt::format_to(out, "virtual_host"); case s3_url_style::path: - return os << "path"; + return fmt::format_to(out, "path"); } } @@ -76,9 +75,8 @@ constexpr std::string_view to_string_view(fips_mode_flag f) { return "permissive"; } } - -inline std::ostream& operator<<(std::ostream& o, fips_mode_flag f) { - return o << to_string_view(f); +inline fmt::iterator format_to(fips_mode_flag f, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(f)); } inline std::istream& operator>>(std::istream& i, fips_mode_flag& f) { @@ -113,9 +111,8 @@ constexpr std::string_view to_string_view(tls_version v) { return "v1.3"; } } - -inline std::ostream& operator<<(std::ostream& os, const tls_version& v) { - return os << to_string_view(v); +inline fmt::iterator format_to(tls_version v, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(v)); } enum class datalake_catalog_type { object_storage, rest }; @@ -128,16 +125,15 @@ constexpr std::string_view to_string_view(datalake_catalog_type ct) { return "rest"; } } +inline fmt::iterator format_to(datalake_catalog_type ct, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(ct)); +} static constexpr auto acceptable_datalake_catalog_types() { return std::to_array( {to_string_view(datalake_catalog_type::rest), to_string_view(datalake_catalog_type::object_storage)}); } -inline std::ostream& operator<<(std::ostream& o, datalake_catalog_type ct) { - return o << to_string_view(ct); -} - inline std::istream& operator>>(std::istream& is, datalake_catalog_type& ct) { ss::sstring s; is >> s; @@ -167,10 +163,9 @@ constexpr std::string_view to_string_view(datalake_catalog_auth_mode cam) { return "gcp"; } } - -inline std::ostream& -operator<<(std::ostream& os, datalake_catalog_auth_mode cam) { - return os << to_string_view(cam); +inline fmt::iterator +format_to(datalake_catalog_auth_mode cam, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(cam)); } inline std::istream& @@ -206,6 +201,9 @@ constexpr std::string_view to_string_view(tls_name_format format) { return "rfc2253"; } } +inline fmt::iterator format_to(tls_name_format format, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(format)); +} static constexpr auto acceptable_tls_name_format_values() { return std::to_array( @@ -213,10 +211,6 @@ static constexpr auto acceptable_tls_name_format_values() { to_string_view(tls_name_format::rfc2253)}); } -inline std::ostream& operator<<(std::ostream& os, tls_name_format format) { - return os << to_string_view(format); -} - inline std::istream& operator>>(std::istream& is, tls_name_format& format) { ss::sstring s; is >> s; @@ -247,6 +241,9 @@ constexpr std::string_view to_string_view(audit_failure_policy policy) { return "permit"; } } +inline fmt::iterator format_to(audit_failure_policy policy, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(policy)); +} static constexpr auto acceptable_audit_log_failure_policy_values() { return std::to_array( @@ -254,6 +251,5 @@ static constexpr auto acceptable_audit_log_failure_policy_values() { to_string_view(audit_failure_policy::permit)}); } -std::ostream& operator<<(std::ostream&, audit_failure_policy); std::istream& operator>>(std::istream&, audit_failure_policy&); } // namespace config diff --git a/src/v/container/chunked_hash_map.h b/src/v/container/chunked_hash_map.h index f8c8e32ac09ea..2ac0eee94697d 100644 --- a/src/v/container/chunked_hash_map.h +++ b/src/v/container/chunked_hash_map.h @@ -15,6 +15,7 @@ #include #include +#include #include @@ -113,31 +114,66 @@ using chunked_hash_set = ankerl::unordered_dense::segmented_set< ankerl::unordered_dense::bucket_type::standard, chunked_vector>; -template -std::ostream& operator<<(std::ostream& o, const chunked_hash_map& r) { - o << "{"; - bool first = true; - for (const auto& [k, v] : r) { - if (!first) { - o << ", "; - } - o << "{" << k << " -> " << v << "}"; - first = false; - } - o << "}"; - return o; -} -template -struct fmt::formatter> { - using type = chunked_hash_map; +// Disable fmt's range formatter for chunked_hash_map/set to use our custom +// formatters instead. +template< + typename K, + typename V, + typename H, + typename E, + typename AllocatorOrContainer, + typename Bucket, + typename BucketContainer, + bool IsSegmented, + typename Char> +struct fmt::range_format_kind< + ankerl::unordered_dense::detail::table< + K, + V, + H, + E, + AllocatorOrContainer, + Bucket, + BucketContainer, + IsSegmented>, + Char> + : std::integral_constant {}; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } +template< + typename K, + typename V, + typename H, + typename E, + typename AllocatorOrContainer, + typename Bucket, + typename BucketContainer, + bool IsSegmented> +struct fmt::formatter> { + using type = ankerl::unordered_dense::detail::table< + K, + V, + H, + E, + AllocatorOrContainer, + Bucket, + BucketContainer, + IsSegmented>; + + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator format(const type& map, FormatContext& ctx) const { - // Map formatting is broken until version 11: - // https://github.com/fmtlib/fmt/issues/3685 auto out = ctx.out(); out = fmt::format_to(out, "["); auto it = map.begin(); @@ -168,11 +204,36 @@ memory_usage_lower_bound(const chunked_hash_map& m) { + m.values().capacity() * sizeof(m.values()[0]); } -template -struct fmt::formatter> { - using type = chunked_hash_set; - - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } +template< + typename K, + typename H, + typename E, + typename AllocatorOrContainer, + typename Bucket, + typename BucketContainer, + bool IsSegmented> +struct fmt::formatter> { + using type = ankerl::unordered_dense::detail::table< + K, + void, + H, + E, + AllocatorOrContainer, + Bucket, + BucketContainer, + IsSegmented>; + + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator diff --git a/src/v/container/chunked_vector.h b/src/v/container/chunked_vector.h index 01560a9d5dd5d..1008607597ede 100644 --- a/src/v/container/chunked_vector.h +++ b/src/v/container/chunked_vector.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -531,7 +532,7 @@ class chunked_vector { friend std::ostream& operator<<(std::ostream& os, const chunked_vector& v) { - fmt::print(os, "[{}]", fmt::join(v, ",")); + fmt::print(os, "{}", v); return os; } diff --git a/src/v/container/interval_set.h b/src/v/container/interval_set.h index 05a1c68487de7..aeab425c9e5ec 100644 --- a/src/v/container/interval_set.h +++ b/src/v/container/interval_set.h @@ -17,6 +17,8 @@ #include "serde/rw/rw.h" #include "utils/to_string.h" +#include + /** * A container that contains non-empty, open intervals. * @@ -122,10 +124,6 @@ class interval_set { return write(out, std::move(is.set_)); } - fmt::iterator format_to(fmt::iterator it) const { - return fmt::format_to(it, "{}", set_); - } - private: /** * Extend the interval being pointed at with any intervals that overlap @@ -275,3 +273,26 @@ template size_t interval_set::size() const { return set_.size(); } + +template +struct fmt::range_format_kind, Char> + : std::integral_constant {}; + +template +struct fmt::formatter> { + constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } + template + auto format(const interval_set& v, FormatContext& ctx) const { + auto out = ctx.out(); + out = fmt::format_to(out, "{{"); + bool first = true; + for (const auto& [start, end] : v) { + if (!first) { + out = fmt::format_to(out, ", "); + } + out = fmt::format_to(out, "{{{} -> {}}}", start, end); + first = false; + } + return fmt::format_to(out, "}}"); + } +}; diff --git a/src/v/crash_tracker/prepared_writer.cc b/src/v/crash_tracker/prepared_writer.cc index 82ca1cc838829..a94b2af803af1 100644 --- a/src/v/crash_tracker/prepared_writer.cc +++ b/src/v/crash_tracker/prepared_writer.cc @@ -35,24 +35,10 @@ using namespace std::chrono_literals; namespace crash_tracker { -std::ostream& operator<<(std::ostream& os, prepared_writer::state s) { - switch (s) { - case prepared_writer::state::uninitialized: - return os << "uninitialized"; - case prepared_writer::state::initialized: - return os << "initialized"; - case prepared_writer::state::filled: - return os << "filled"; - case prepared_writer::state::written: - return os << "written"; - case prepared_writer::state::released: - return os << "released"; - } -} - ss::future<> prepared_writer::initialize(std::filesystem::path crash_file_path) { - vassert(_state == state::uninitialized, "Unexpected state: {}", _state); + vassert( + _state == state::uninitialized, "Unexpected state: {}", _state.load()); _crash_report_file_name = std::move(crash_file_path); _serde_output.reserve_memory(crash_description::serde_size_overestimate); diff --git a/src/v/crash_tracker/prepared_writer.h b/src/v/crash_tracker/prepared_writer.h index 6fb3b15adaeae..24643e12b49e3 100644 --- a/src/v/crash_tracker/prepared_writer.h +++ b/src/v/crash_tracker/prepared_writer.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" #include "crash_tracker/types.h" @@ -71,7 +72,20 @@ class prepared_writer { private: enum class state { uninitialized, initialized, filled, written, released }; - friend std::ostream& operator<<(std::ostream&, state); + friend fmt::iterator format_to(state s, fmt::iterator out) { + switch (s) { + case state::uninitialized: + return fmt::format_to(out, "uninitialized"); + case state::initialized: + return fmt::format_to(out, "initialized"); + case state::filled: + return fmt::format_to(out, "filled"); + case state::written: + return fmt::format_to(out, "written"); + case state::released: + return fmt::format_to(out, "released"); + } + } // Returns true on success, false on failure bool try_write_crash(); diff --git a/src/v/crash_tracker/types.cc b/src/v/crash_tracker/types.cc index 5b4e9c04dcad1..11bedba495f16 100644 --- a/src/v/crash_tracker/types.cc +++ b/src/v/crash_tracker/types.cc @@ -15,40 +15,21 @@ namespace crash_tracker { -std::ostream& operator<<(std::ostream& os, crash_type ct) { - switch (ct) { - case crash_type::unknown: - return os << "unknown"; - case crash_type::startup_exception: - return os << "startup_exception"; - case crash_type::segfault: - return os << "segfault"; - case crash_type::abort: - return os << "abort"; - case crash_type::illegal_instruction: - return os << "illegal_instruction"; - case crash_type::assertion: - return os << "assertion"; - case crash_type::oom: - return os << "oom"; - } -} - -std::ostream& operator<<(std::ostream& os, const crash_description& cd) { - fmt::print( - os, +fmt::iterator crash_description::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "Redpanda version: {}. Arch: {}. {}", - cd.app_version, - cd.arch, - cd.crash_message.c_str()); + app_version, + arch, + crash_message.c_str()); - const auto opt_stacktrace = cd.stacktrace.c_str(); + const auto opt_stacktrace = stacktrace.c_str(); const auto has_stacktrace = strlen(opt_stacktrace) > 0; if (has_stacktrace) { - fmt::print(os, " Backtrace: {}.", opt_stacktrace); + it = fmt::format_to(it, " Backtrace: {}.", opt_stacktrace); } - return os; + return it; } bool is_crash_loop_limit_reached(std::exception_ptr eptr) { diff --git a/src/v/crash_tracker/types.h b/src/v/crash_tracker/types.h index d57e1da887752..7d355e5d32736 100644 --- a/src/v/crash_tracker/types.h +++ b/src/v/crash_tracker/types.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "model/timestamp.h" #include "serde/envelope.h" @@ -19,7 +20,6 @@ #include "serde/rw/sstring.h" #include -#include namespace crash_tracker { @@ -33,7 +33,24 @@ enum class crash_type { oom }; -std::ostream& operator<<(std::ostream&, crash_type); +inline fmt::iterator format_to(crash_type ct, fmt::iterator out) { + switch (ct) { + case crash_type::unknown: + return fmt::format_to(out, "unknown"); + case crash_type::startup_exception: + return fmt::format_to(out, "startup_exception"); + case crash_type::segfault: + return fmt::format_to(out, "segfault"); + case crash_type::abort: + return fmt::format_to(out, "abort"); + case crash_type::illegal_instruction: + return fmt::format_to(out, "illegal_instruction"); + case crash_type::assertion: + return fmt::format_to(out, "assertion"); + case crash_type::oom: + return fmt::format_to(out, "oom"); + } +} /// reserved_string is a simple wrapper around a std::array that allows /// pre-allocating a string of a certain size with trailing '\0's and allows @@ -138,7 +155,7 @@ struct crash_description type, crash_time, crash_message, stacktrace, app_version, arch); } - friend std::ostream& operator<<(std::ostream&, const crash_description&); + fmt::iterator format_to(fmt::iterator it) const; }; struct crash_tracker_metadata diff --git a/src/v/crypto/crypto.cc b/src/v/crypto/crypto.cc index 7193e8ba2b561..1a8d31be14c58 100644 --- a/src/v/crypto/crypto.cc +++ b/src/v/crypto/crypto.cc @@ -38,34 +38,6 @@ auto& get_mac() { } // namespace namespace crypto { -std::ostream& operator<<(std::ostream& os, digest_type type) { - switch (type) { - case digest_type::MD5: - return os << "MD5"; - case digest_type::SHA256: - return os << "SHA256"; - case digest_type::SHA512: - return os << "SHA512"; - } - - return os; -} - -std::ostream& operator<<(std::ostream& os, key_type type) { - switch (type) { - case key_type::RSA: - return os << "RSA"; - } -} - -std::ostream& operator<<(std::ostream& os, format_type type) { - switch (type) { - case format_type::PEM: - return os << "PEM"; - case format_type::DER: - return os << "DER"; - } -} namespace internal { // This function returns true if the provided digest type is expected to use the diff --git a/src/v/crypto/types.h b/src/v/crypto/types.h index c318b647aaf57..c6c403e4c434b 100644 --- a/src/v/crypto/types.h +++ b/src/v/crypto/types.h @@ -11,21 +11,49 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include -#include +#include namespace crypto { enum class digest_type { MD5, SHA256, SHA512 }; -std::ostream& operator<<(std::ostream&, digest_type); + +inline fmt::iterator format_to(digest_type t, fmt::iterator out) { + switch (t) { + case digest_type::MD5: + return fmt::format_to(out, "MD5"); + case digest_type::SHA256: + return fmt::format_to(out, "SHA256"); + case digest_type::SHA512: + return fmt::format_to(out, "SHA512"); + } + return fmt::format_to(out, "unknown_digest_type"); +} enum class key_type { RSA }; -std::ostream& operator<<(std::ostream&, key_type); + +inline fmt::iterator format_to(key_type t, fmt::iterator out) { + switch (t) { + case key_type::RSA: + return fmt::format_to(out, "RSA"); + } + return fmt::format_to(out, "unknown_key_type"); +} enum class format_type { PEM, DER }; -std::ostream& operator<<(std::ostream&, format_type); + +inline fmt::iterator format_to(format_type t, fmt::iterator out) { + switch (t) { + case format_type::PEM: + return fmt::format_to(out, "PEM"); + case format_type::DER: + return fmt::format_to(out, "DER"); + } + return fmt::format_to(out, "unknown_format_type"); +} using is_private_key_t = ss::bool_class; } // namespace crypto diff --git a/src/v/datalake/BUILD b/src/v/datalake/BUILD index 2a779af61b855..9547f5fac3fd7 100644 --- a/src/v/datalake/BUILD +++ b/src/v/datalake/BUILD @@ -10,7 +10,6 @@ redpanda_cc_library( ], implementation_deps = [ ":logger", - "//src/v/base", "//src/v/features", "//src/v/iceberg:compatibility", "//src/v/iceberg:compatibility_types", @@ -20,6 +19,7 @@ redpanda_cc_library( ], visibility = [":__subpackages__"], deps = [ + "//src/v/base", "//src/v/iceberg:catalog", "//src/v/iceberg:datatypes", "//src/v/iceberg:table_identifier", @@ -62,13 +62,13 @@ redpanda_cc_library( ], implementation_deps = [ ":logger", - "//src/v/base", "//src/v/iceberg:struct_accessor", ], visibility = [":__subpackages__"], deps = [ ":partition_key_path", ":writer", + "//src/v/base", "//src/v/container:chunked_hash_map", "//src/v/iceberg:datatypes", "//src/v/iceberg:partition_key", @@ -248,14 +248,12 @@ redpanda_cc_library( redpanda_cc_library( name = "base_types", - srcs = [ - "base_types.cc", - ], hdrs = [ "base_types.h", ], visibility = [":__subpackages__"], deps = [ + "//src/v/base", "//src/v/utils:named_type", "@fmt", ], @@ -293,13 +291,14 @@ redpanda_cc_library( hdrs = ["schema_registry.h"], implementation_deps = [ ":logger", - "//src/v/base", "//src/v/bytes:iobuf", "//src/v/bytes:iobuf_parser", ], visibility = [":__subpackages__"], deps = [ + "//src/v/base", "//src/v/pandaproxy/schema_registry:types", + "@fmt", ], ) @@ -455,15 +454,13 @@ redpanda_cc_library( redpanda_cc_library( name = "table_creator", - srcs = [ - "table_creator.cc", - ], hdrs = [ "table_creator.h", ], visibility = [":__subpackages__"], deps = [ ":schema_identifier", + "//src/v/base", ], ) diff --git a/src/v/datalake/base_types.cc b/src/v/datalake/base_types.cc deleted file mode 100644 index 4dbaa34e2e547..0000000000000 --- a/src/v/datalake/base_types.cc +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2024 Redpanda Data, Inc. - * - * Licensed as a Redpanda Enterprise file under the Redpanda Community - * License (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md - */ -#include "datalake/base_types.h" - -#include -namespace datalake { -std::ostream& operator<<(std::ostream& o, const local_file_metadata& f_meta) { - fmt::print( - o, - "{{relative_path: {}, size_bytes: {}, row_count: {}}}", - f_meta.path, - f_meta.size_bytes, - f_meta.row_count); - return o; -} -} // namespace datalake diff --git a/src/v/datalake/base_types.h b/src/v/datalake/base_types.h index 005d0f9b9e448..2c86334822732 100644 --- a/src/v/datalake/base_types.h +++ b/src/v/datalake/base_types.h @@ -8,6 +8,7 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #pragma once +#include "base/format_to.h" #include "utils/named_type.h" #include @@ -29,7 +30,13 @@ struct local_file_metadata { size_t row_count = 0; size_t size_bytes = 0; - friend std::ostream& - operator<<(std::ostream& o, const local_file_metadata& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{relative_path: {}, size_bytes: {}, row_count: {}}}", + path, + size_bytes, + row_count); + } }; } // namespace datalake diff --git a/src/v/datalake/catalog_schema_manager.cc b/src/v/datalake/catalog_schema_manager.cc index d8c7c60e0d629..d8473a9900780 100644 --- a/src/v/datalake/catalog_schema_manager.cc +++ b/src/v/datalake/catalog_schema_manager.cc @@ -131,17 +131,6 @@ checked apply_evolution_rules( } // namespace -std::ostream& operator<<(std::ostream& o, const schema_manager::errc& e) { - switch (e) { - case schema_manager::errc::not_supported: - return o << "schema_manager::errc::not_supported"; - case schema_manager::errc::failed: - return o << "schema_manager::errc::failed"; - case schema_manager::errc::shutting_down: - return o << "schema_manager::errc::shutting_down"; - } -} - bool schema_manager::table_info::fill_registered_ids( iceberg::struct_type& type) { return iceberg::try_fill_field_ids(schema.schema_struct, type) diff --git a/src/v/datalake/catalog_schema_manager.h b/src/v/datalake/catalog_schema_manager.h index 4fbf6483da352..9514c178d718e 100644 --- a/src/v/datalake/catalog_schema_manager.h +++ b/src/v/datalake/catalog_schema_manager.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "iceberg/catalog.h" #include "iceberg/datatypes.h" #include "iceberg/partition.h" @@ -32,7 +33,16 @@ class schema_manager { // The system is shutting down. shutting_down, }; - friend std::ostream& operator<<(std::ostream&, const errc&); + friend fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::not_supported: + return fmt::format_to(out, "schema_manager::errc::not_supported"); + case errc::failed: + return fmt::format_to(out, "schema_manager::errc::failed"); + case errc::shutting_down: + return fmt::format_to(out, "schema_manager::errc::shutting_down"); + } + } virtual ss::future> ensure_table_schema( const iceberg::table_identifier&, diff --git a/src/v/datalake/cloud_data_io.cc b/src/v/datalake/cloud_data_io.cc index 2fec5a9d33557..8538e01a77e26 100644 --- a/src/v/datalake/cloud_data_io.cc +++ b/src/v/datalake/cloud_data_io.cc @@ -150,14 +150,4 @@ cloud_data_io::delete_data_files( co_return map_upload_result(r_fut.get()); } -std::ostream& operator<<(std::ostream& o, cloud_data_io::errc ec) { - switch (ec) { - case cloud_data_io::errc::file_io_error: - return o << "cloud operation local file io error"; - case cloud_data_io::errc::cloud_op_error: - return o << "cloud operation error"; - case cloud_data_io::errc::cloud_op_timeout: - return o << "cloud operation timeout"; - } -} } // namespace datalake diff --git a/src/v/datalake/cloud_data_io.h b/src/v/datalake/cloud_data_io.h index cb4eaaf51ed50..c646483ce55af 100644 --- a/src/v/datalake/cloud_data_io.h +++ b/src/v/datalake/cloud_data_io.h @@ -8,6 +8,7 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "container/chunked_vector.h" #include "datalake/base_types.h" @@ -42,7 +43,16 @@ class cloud_data_io { chunked_vector files_to_delete, retry_chain_node& rtc_parent); - friend std::ostream& operator<<(std::ostream&, errc); + friend fmt::iterator format_to(errc ec, fmt::iterator out) { + switch (ec) { + case errc::file_io_error: + return fmt::format_to(out, "cloud operation local file io error"); + case errc::cloud_op_error: + return fmt::format_to(out, "cloud operation error"); + case errc::cloud_op_timeout: + return fmt::format_to(out, "cloud operation timeout"); + } + } private: cloud_io::remote* _cloud_io; diff --git a/src/v/datalake/coordinator/BUILD b/src/v/datalake/coordinator/BUILD index 1a2281708baee..a362e8dbdadc3 100644 --- a/src/v/datalake/coordinator/BUILD +++ b/src/v/datalake/coordinator/BUILD @@ -36,7 +36,6 @@ redpanda_cc_library( ], implementation_deps = [ ":iceberg_snapshot_remover", - "//src/v/base", "//src/v/datalake:catalog_schema_manager", "//src/v/datalake:logger", "//src/v/datalake:partition_spec_parser", @@ -55,6 +54,7 @@ redpanda_cc_library( ":snapshot_remover", ":state_update", ":stm", + "//src/v/base", "//src/v/cluster", "//src/v/config", "//src/v/container:chunked_hash_map", @@ -147,9 +147,6 @@ redpanda_cc_library( redpanda_cc_library( name = "data_file", - srcs = [ - "data_file.cc", - ], hdrs = [ "data_file.h", ], @@ -257,13 +254,11 @@ redpanda_cc_library( redpanda_cc_library( name = "partition_state_override", - srcs = [ - "partition_state_override.cc", - ], hdrs = [ "partition_state_override.h", ], deps = [ + "//src/v/base", "//src/v/model", "//src/v/serde", "@fmt", @@ -281,6 +276,7 @@ redpanda_cc_library( visibility = ["//visibility:public"], deps = [ ":translated_offset_range", + "//src/v/base", "//src/v/container:chunked_hash_map", "//src/v/iceberg:manifest_entry", "//src/v/model", @@ -290,9 +286,6 @@ redpanda_cc_library( redpanda_cc_library( name = "model", - srcs = [ - "types.cc", - ], hdrs = [ "types.h", ], @@ -300,6 +293,7 @@ redpanda_cc_library( deps = [ ":partition_state_override", ":translated_offset_range", + "//src/v/base", "//src/v/datalake:schema_identifier", "//src/v/datalake:types", "//src/v/datalake/coordinator:state", @@ -366,9 +360,6 @@ redpanda_cc_library( redpanda_cc_library( name = "translated_offset_range", - srcs = [ - "translated_offset_range.cc", - ], hdrs = [ "translated_offset_range.h", ], diff --git a/src/v/datalake/coordinator/coordinator.cc b/src/v/datalake/coordinator/coordinator.cc index 45598646ad3af..95a031e234599 100644 --- a/src/v/datalake/coordinator/coordinator.cc +++ b/src/v/datalake/coordinator/coordinator.cc @@ -80,25 +80,6 @@ coordinator::maybe_add_waiter( return std::nullopt; } -std::ostream& operator<<(std::ostream& o, coordinator::errc e) { - switch (e) { - case coordinator::errc::not_leader: - return o << "coordinator::errc::not_leader"; - case coordinator::errc::shutting_down: - return o << "coordinator::errc::shutting_down"; - case coordinator::errc::stm_apply_error: - return o << "coordinator::errc::stm_apply_error"; - case coordinator::errc::revision_mismatch: - return o << "coordinator::errc::revision_mismatch"; - case coordinator::errc::incompatible_schema: - return o << "coordinator::errc::incompatible_schema"; - case coordinator::errc::timedout: - return o << "coordinator::errc::timedout"; - case coordinator::errc::failed: - return o << "coordinator::errc::failed"; - } -} - void coordinator::start() { ssx::spawn_with_gate(gate_, [this] { return run_until_abort(); }); } diff --git a/src/v/datalake/coordinator/coordinator.h b/src/v/datalake/coordinator/coordinator.h index ee9eefa51ce86..c4dc0a6bfeaa1 100644 --- a/src/v/datalake/coordinator/coordinator.h +++ b/src/v/datalake/coordinator/coordinator.h @@ -10,6 +10,7 @@ #pragma once #include "absl/hash/hash.h" +#include "base/format_to.h" #include "cluster/fwd.h" #include "config/property.h" #include "container/chunked_hash_map.h" @@ -187,6 +188,23 @@ class coordinator { ensure_table_map_t in_flight_main_; ensure_table_map_t in_flight_dlq_; }; -std::ostream& operator<<(std::ostream&, coordinator::errc); +inline fmt::iterator format_to(coordinator::errc e, fmt::iterator out) { + switch (e) { + case coordinator::errc::not_leader: + return fmt::format_to(out, "coordinator::errc::not_leader"); + case coordinator::errc::shutting_down: + return fmt::format_to(out, "coordinator::errc::shutting_down"); + case coordinator::errc::stm_apply_error: + return fmt::format_to(out, "coordinator::errc::stm_apply_error"); + case coordinator::errc::revision_mismatch: + return fmt::format_to(out, "coordinator::errc::revision_mismatch"); + case coordinator::errc::incompatible_schema: + return fmt::format_to(out, "coordinator::errc::incompatible_schema"); + case coordinator::errc::timedout: + return fmt::format_to(out, "coordinator::errc::timedout"); + case coordinator::errc::failed: + return fmt::format_to(out, "coordinator::errc::failed"); + } +} } // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/data_file.cc b/src/v/datalake/coordinator/data_file.cc deleted file mode 100644 index d0fd366c369d6..0000000000000 --- a/src/v/datalake/coordinator/data_file.cc +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 Redpanda Data, Inc. - * - * Licensed as a Redpanda Enterprise file under the Redpanda Community - * License (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md - */ -#include "datalake/coordinator/data_file.h" - -namespace datalake::coordinator { - -std::ostream& operator<<(std::ostream& o, const data_file& f) { - o << fmt::format( - "{{remote_path: {}, row_count: {}, file_size_bytes: {}, hour_deprecated: " - "{}, table_schema_id: {}, partition_spec_id: {}}}", - f.remote_path, - f.row_count, - f.file_size_bytes, - f.hour_deprecated, - f.table_schema_id, - f.partition_spec_id); - return o; -} - -} // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/data_file.h b/src/v/datalake/coordinator/data_file.h index 5ca4816ca13e2..9b90fe7a86ee9 100644 --- a/src/v/datalake/coordinator/data_file.h +++ b/src/v/datalake/coordinator/data_file.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/bytes.h" #include "container/chunked_vector.h" @@ -61,9 +62,20 @@ struct data_file }; } + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{remote_path: {}, row_count: {}, file_size_bytes: {}, " + "hour_deprecated: {}, table_schema_id: {}, partition_spec_id: {}}}", + remote_path, + row_count, + file_size_bytes, + hour_deprecated, + table_schema_id, + partition_spec_id); + } + friend bool operator==(const data_file&, const data_file&) = default; }; -std::ostream& operator<<(std::ostream& o, const data_file& f); - } // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/partition_state_override.cc b/src/v/datalake/coordinator/partition_state_override.cc deleted file mode 100644 index 942fae8ef0ec3..0000000000000 --- a/src/v/datalake/coordinator/partition_state_override.cc +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2026 Redpanda Data, Inc. - * - * Licensed as a Redpanda Enterprise file under the Redpanda Community - * License (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md - */ - -#include "datalake/coordinator/partition_state_override.h" - -#include - -namespace datalake::coordinator { - -std::ostream& operator<<(std::ostream& o, const partition_state_override& p) { - if (p.last_committed.has_value()) { - fmt::print(o, "{{last_committed: {}}}", p.last_committed.value()); - } else { - fmt::print(o, "{{last_committed: nullopt}}"); - } - return o; -} - -} // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/partition_state_override.h b/src/v/datalake/coordinator/partition_state_override.h index 964bc4dd0aa59..7b8e32522e947 100644 --- a/src/v/datalake/coordinator/partition_state_override.h +++ b/src/v/datalake/coordinator/partition_state_override.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "model/fundamental.h" #include "serde/envelope.h" @@ -24,8 +25,13 @@ struct partition_state_override auto serde_fields() { return std::tie(last_committed); } - friend std::ostream& - operator<<(std::ostream&, const partition_state_override&); + fmt::iterator format_to(fmt::iterator it) const { + if (last_committed.has_value()) { + return fmt::format_to( + it, "{{last_committed: {}}}", last_committed.value()); + } + return fmt::format_to(it, "{{last_committed: nullopt}}"); + } }; } // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/state.cc b/src/v/datalake/coordinator/state.cc index 534fef3224851..08c41007f96b7 100644 --- a/src/v/datalake/coordinator/state.cc +++ b/src/v/datalake/coordinator/state.cc @@ -24,17 +24,6 @@ partition_state partition_state::copy() const { return result; } -std::ostream& operator<<(std::ostream& o, topic_state::lifecycle_state_t s) { - switch (s) { - case topic_state::lifecycle_state_t::live: - return o << "live"; - case topic_state::lifecycle_state_t::closed: - return o << "closed"; - case topic_state::lifecycle_state_t::purged: - return o << "purged"; - } -} - topic_state topic_state::copy() const { topic_state result; result.revision = revision; diff --git a/src/v/datalake/coordinator/state.h b/src/v/datalake/coordinator/state.h index e4b483d7d82ae..5dce5ad740eab 100644 --- a/src/v/datalake/coordinator/state.h +++ b/src/v/datalake/coordinator/state.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "container/chunked_hash_map.h" #include "datalake/coordinator/translated_offset_range.h" #include "iceberg/manifest_entry.h" @@ -102,8 +103,18 @@ struct topic_state // TODO: GC purged topic states purged, }; - friend std::ostream& - operator<<(std::ostream&, topic_state::lifecycle_state_t); + friend fmt::iterator + format_to(topic_state::lifecycle_state_t s, fmt::iterator out) { + switch (s) { + case topic_state::lifecycle_state_t::live: + return fmt::format_to(out, "live"); + case topic_state::lifecycle_state_t::closed: + return fmt::format_to(out, "closed"); + case topic_state::lifecycle_state_t::purged: + return fmt::format_to(out, "purged"); + } + __builtin_unreachable(); + } bool has_pending_entries() const; bool has_pending_main_entries() const; diff --git a/src/v/datalake/coordinator/state_update.cc b/src/v/datalake/coordinator/state_update.cc index f565ed143f106..6a085e42e8b9e 100644 --- a/src/v/datalake/coordinator/state_update.cc +++ b/src/v/datalake/coordinator/state_update.cc @@ -14,19 +14,6 @@ namespace datalake::coordinator { -std::ostream& operator<<(std::ostream& o, const update_key& u) { - switch (u) { - case update_key::add_files: - return o << "update_key::add_files"; - case update_key::mark_files_committed: - return o << "update_key::mark_files_committed"; - case update_key::topic_lifecycle_update: - return o << "update_key::topic_lifecycle_update"; - case update_key::reset_topic_state: - return o << "update_key::reset_topic_state"; - } -} - checked add_files_update::build( const topics_state& state, const model::topic_partition& tp, @@ -334,54 +321,52 @@ reset_topic_state_update::apply(topics_state& state) { return outcome::success(); } -std::ostream& operator<<(std::ostream& o, const add_files_update& u) { - fmt::print(o, "{{tp: {}, revision: {}, entries: [", u.tp, u.topic_revision); +fmt::iterator add_files_update::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "{{tp: {}, revision: {}, entries: [", tp, topic_revision); static constexpr size_t max_to_log = 6; static constexpr size_t halved = max_to_log / 2; - const auto& e = u.entries; + const auto& e = entries; if (e.size() <= max_to_log) { - fmt::print(o, "{}", fmt::join(e, ", ")); + it = fmt::format_to(it, "{}", fmt::join(e, ", ")); } else { - fmt::print(o, "{}", fmt::join(e.begin(), e.begin() + halved, ", ")); - o << "..."; - fmt::print(o, "{}", fmt::join(e.end() - halved, e.end(), ", ")); + it = fmt::format_to( + it, "{}", fmt::join(e.begin(), e.begin() + halved, ", ")); + it = fmt::format_to(it, "..."); + it = fmt::format_to( + it, "{}", fmt::join(e.end() - halved, e.end(), ", ")); } - fmt::print(o, "] ({} entries)}}", e.size()); - return o; + return fmt::format_to(it, "] ({} entries)}}", e.size()); } -std::ostream& -operator<<(std::ostream& o, const mark_files_committed_update& u) { - fmt::print( - o, +fmt::iterator mark_files_committed_update::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{tp: {}, revision: {}, new_committed: {}, kafka_bytes_processed: {}}}", - u.tp, - u.topic_revision, - u.new_committed, - u.kafka_bytes_processed); - return o; + tp, + topic_revision, + new_committed, + kafka_bytes_processed); } -std::ostream& operator<<(std::ostream& o, const topic_lifecycle_update& u) { - fmt::print( - o, +fmt::iterator topic_lifecycle_update::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, revision: {}, new_state: {}}}", - u.topic, - u.revision, - u.new_state); - return o; + topic, + revision, + new_state); } -std::ostream& operator<<(std::ostream& o, const reset_topic_state_update& u) { - fmt::print( - o, +fmt::iterator reset_topic_state_update::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, revision: {}, reset_all_partitions: {}, " "partition_overrides: {} entries}}", - u.topic, - u.topic_revision, - u.reset_all_partitions, - u.partition_overrides.size()); - return o; + topic, + topic_revision, + reset_all_partitions, + partition_overrides.size()); } } // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/state_update.h b/src/v/datalake/coordinator/state_update.h index 0ea80931c739b..96cfb4cded2f3 100644 --- a/src/v/datalake/coordinator/state_update.h +++ b/src/v/datalake/coordinator/state_update.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "container/chunked_hash_map.h" #include "container/chunked_vector.h" @@ -30,7 +31,19 @@ enum class update_key : uint8_t { topic_lifecycle_update = 2, reset_topic_state = 3, }; -std::ostream& operator<<(std::ostream&, const update_key&); + +inline fmt::iterator format_to(update_key u, fmt::iterator out) { + switch (u) { + case update_key::add_files: + return fmt::format_to(out, "update_key::add_files"); + case update_key::mark_files_committed: + return fmt::format_to(out, "update_key::mark_files_committed"); + case update_key::topic_lifecycle_update: + return fmt::format_to(out, "update_key::topic_lifecycle_update"); + case update_key::reset_topic_state: + return fmt::format_to(out, "update_key::reset_topic_state"); + } +} using stm_update_error = named_type; @@ -49,7 +62,7 @@ struct add_files_update checked can_apply(const topics_state&); checked apply(topics_state&, model::offset); - friend std::ostream& operator<<(std::ostream&, const add_files_update&); + fmt::iterator format_to(fmt::iterator it) const; model::topic_partition tp; model::revision_id topic_revision; @@ -81,8 +94,7 @@ struct mark_files_committed_update checked can_apply(const topics_state&); checked apply(topics_state&); - friend std::ostream& - operator<<(std::ostream&, const mark_files_committed_update&); + fmt::iterator format_to(fmt::iterator it) const; model::topic_partition tp; model::revision_id topic_revision; @@ -111,8 +123,7 @@ struct topic_lifecycle_update checked can_apply(const topics_state&); checked apply(topics_state&); - friend std::ostream& - operator<<(std::ostream&, const topic_lifecycle_update&); + fmt::iterator format_to(fmt::iterator it) const; model::topic topic; model::revision_id revision; @@ -140,8 +151,7 @@ struct reset_topic_state_update checked can_apply(const topics_state&); checked apply(topics_state&); - friend std::ostream& - operator<<(std::ostream&, const reset_topic_state_update&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/translated_offset_range.cc b/src/v/datalake/coordinator/translated_offset_range.cc deleted file mode 100644 index 16c9c1500d3aa..0000000000000 --- a/src/v/datalake/coordinator/translated_offset_range.cc +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 Redpanda Data, Inc. - * - * Licensed as a Redpanda Enterprise file under the Redpanda Community - * License (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md - */ -#include "datalake/coordinator/translated_offset_range.h" - -namespace datalake::coordinator { - -std::ostream& operator<<(std::ostream& o, const translated_offset_range& r) { - o << fmt::format( - "{{start_offset: {}, last_offset: {}, files: {}, dlq_files: {}, " - "kafka_bytes_processed: {}}}", - r.start_offset, - r.last_offset, - r.files, - r.dlq_files, - r.kafka_bytes_processed); - return o; -} - -} // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/translated_offset_range.h b/src/v/datalake/coordinator/translated_offset_range.h index 79643730c6683..0a086a8a7ba38 100644 --- a/src/v/datalake/coordinator/translated_offset_range.h +++ b/src/v/datalake/coordinator/translated_offset_range.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "container/chunked_vector.h" #include "datalake/coordinator/data_file.h" #include "model/fundamental.h" @@ -56,8 +57,18 @@ struct translated_offset_range } return range; } -}; -std::ostream& operator<<(std::ostream& o, const translated_offset_range& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{start_offset: {}, last_offset: {}, files: {}, dlq_files: {}, " + "kafka_bytes_processed: {}}}", + start_offset, + last_offset, + files, + dlq_files, + kafka_bytes_processed); + } +}; } // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/types.cc b/src/v/datalake/coordinator/types.cc deleted file mode 100644 index d1e031fc20f03..0000000000000 --- a/src/v/datalake/coordinator/types.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2024 Redpanda Data, Inc. - * - * Licensed as a Redpanda Enterprise file under the Redpanda Community - * License (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md - */ - -#include "datalake/coordinator/types.h" - -#include "utils/to_string.h" - -namespace datalake::coordinator { - -std::ostream& operator<<(std::ostream& o, const errc& errc) { - switch (errc) { - case errc::ok: - o << "errc::ok"; - break; - case errc::coordinator_topic_not_exists: - o << "errc::coordinator_topic_not_exists"; - break; - case errc::not_leader: - o << "errc::not_leader"; - break; - case errc::timeout: - o << "errc::timeout"; - break; - case errc::fenced: - o << "errc::fenced"; - break; - case errc::stale: - o << "errc::stale"; - break; - case errc::concurrent_requests: - o << "errc::concurrent_requests"; - break; - case errc::revision_mismatch: - o << "errc::revision_mismatch"; - break; - case errc::incompatible_schema: - o << "errc::incompatible_schema"; - break; - case errc::failed: - o << "errc::failed"; - break; - } - return o; -} - -std::ostream& operator<<(std::ostream& o, const ensure_table_exists_reply& r) { - fmt::print(o, "{{errc: {}}}", r.errc); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const ensure_table_exists_request& r) { - fmt::print( - o, "{{topic: {}, topic_revision: {}}}", r.topic, r.topic_revision); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const add_translated_data_files_reply& reply) { - fmt::print(o, "{{errc: {}}}", reply.errc); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const add_translated_data_files_request& request) { - fmt::print( - o, - "{{partition: {}, topic_revision: {}, files: {}, translation term: {}}}", - request.tp, - request.topic_revision, - request.ranges, - request.translator_term); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const fetch_latest_translated_offset_reply& reply) { - fmt::print( - o, "{{errc: {}, offset: {}}}", reply.errc, reply.last_added_offset); - return o; -} - -std::ostream& operator<<( - std::ostream& o, const fetch_latest_translated_offset_request& request) { - fmt::print( - o, - "{{partition: {}, topic_revision: {}}}", - request.tp, - request.topic_revision); - return o; -} - -std::ostream& operator<<(std::ostream& o, const per_topic_usage_stats& stats) { - fmt::print( - o, - "{{topic: {}, revision: {}, total_kafka_bytes_processed: {}}}", - stats.topic, - stats.revision, - stats.total_kafka_bytes_processed); - return o; -} - -std::ostream& operator<<(std::ostream& o, const datalake_usage_stats& stats) { - fmt::print(o, "{{topic_usages: {} }}", stats.topic_usages); - return o; -} - -std::ostream& operator<<(std::ostream& o, const usage_stats_reply& resp) { - fmt::print(o, "{{errc: {}, stats: {}}}", resp.errc, resp.stats); - return o; -} - -std::ostream& operator<<(std::ostream& o, const usage_stats_request& req) { - fmt::print(o, "{{coordinator_partition: {}}}", req.coordinator_partition); - return o; -} - -std::ostream& operator<<(std::ostream& o, const get_topic_state_reply& reply) { - fmt::print( - o, - "{{errc: {}, topic_states size: {}}}", - reply.errc, - reply.topic_states.size()); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const get_topic_state_request& request) { - fmt::print( - o, - "{{coordinator_partition: {}, topics_filter: {}}}", - request.coordinator_partition, - request.topics_filter); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const reset_topic_state_reply& reply) { - fmt::print(o, "{{errc: {}}}", reply.errc); - return o; -} - -} // namespace datalake::coordinator diff --git a/src/v/datalake/coordinator/types.h b/src/v/datalake/coordinator/types.h index e52f979ed94bc..2ca56b2776f62 100644 --- a/src/v/datalake/coordinator/types.h +++ b/src/v/datalake/coordinator/types.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "container/chunked_hash_map.h" #include "container/chunked_vector.h" #include "datalake/coordinator/partition_state_override.h" @@ -42,7 +43,30 @@ constexpr bool is_retriable(errc errc) { || errc == errc::concurrent_requests; } -std::ostream& operator<<(std::ostream&, const errc&); +inline fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::ok: + return fmt::format_to(out, "errc::ok"); + case errc::coordinator_topic_not_exists: + return fmt::format_to(out, "errc::coordinator_topic_not_exists"); + case errc::not_leader: + return fmt::format_to(out, "errc::not_leader"); + case errc::timeout: + return fmt::format_to(out, "errc::timeout"); + case errc::fenced: + return fmt::format_to(out, "errc::fenced"); + case errc::stale: + return fmt::format_to(out, "errc::stale"); + case errc::concurrent_requests: + return fmt::format_to(out, "errc::concurrent_requests"); + case errc::revision_mismatch: + return fmt::format_to(out, "errc::revision_mismatch"); + case errc::incompatible_schema: + return fmt::format_to(out, "errc::incompatible_schema"); + case errc::failed: + return fmt::format_to(out, "errc::failed"); + } +} struct ensure_table_exists_reply : serde::envelope< @@ -53,8 +77,9 @@ struct ensure_table_exists_reply explicit ensure_table_exists_reply(errc err) : errc(err) {} - friend std::ostream& - operator<<(std::ostream&, const ensure_table_exists_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{errc: {}}}", errc); + } errc errc; @@ -80,8 +105,10 @@ struct ensure_table_exists_request model::revision_id topic_revision; record_schema_components schema_components; - friend std::ostream& - operator<<(std::ostream&, const ensure_table_exists_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, topic_revision: {}}}", topic, topic_revision); + } const model::topic& get_topic() const { return topic; } @@ -99,8 +126,9 @@ struct ensure_dlq_table_exists_reply explicit ensure_dlq_table_exists_reply(errc err) : errc(err) {} - friend std::ostream& - operator<<(std::ostream&, const ensure_dlq_table_exists_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{errc: {}}}", errc); + } errc errc; @@ -123,8 +151,10 @@ struct ensure_dlq_table_exists_request model::topic topic; model::revision_id topic_revision; - friend std::ostream& - operator<<(std::ostream&, const ensure_dlq_table_exists_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic: {}, topic_revision: {}}}", topic, topic_revision); + } const model::topic& get_topic() const { return topic; } @@ -140,8 +170,9 @@ struct add_translated_data_files_reply explicit add_translated_data_files_reply(errc err) : errc(err) {} - friend std::ostream& - operator<<(std::ostream&, const add_translated_data_files_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{errc: {}}}", errc); + } errc errc; @@ -186,8 +217,16 @@ struct add_translated_data_files_request }; } - friend std::ostream& - operator<<(std::ostream&, const add_translated_data_files_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{partition: {}, topic_revision: {}, files: {}, translation " + "term: {}}}", + tp, + topic_revision, + ranges, + translator_term); + } const model::topic& get_topic() const { return tp.topic; } @@ -219,8 +258,10 @@ struct fetch_latest_translated_offset_reply // If not ok, the request processing has a problem. errc errc; - friend std::ostream& - operator<<(std::ostream&, const fetch_latest_translated_offset_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{errc: {}, offset: {}}}", errc, last_added_offset); + } auto serde_fields() { return std::tie(last_added_offset, errc, last_iceberg_committed_offset); @@ -243,8 +284,10 @@ struct fetch_latest_translated_offset_request const model::topic& get_topic() const { return tp.topic; } - friend std::ostream& - operator<<(std::ostream&, const fetch_latest_translated_offset_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition: {}, topic_revision: {}}}", tp, topic_revision); + } auto serde_fields() { return std::tie(tp, topic_revision); } }; @@ -275,8 +318,14 @@ struct per_topic_usage_stats model::revision_id revision; uint64_t total_kafka_bytes_processed{0}; - friend std::ostream& - operator<<(std::ostream&, const per_topic_usage_stats&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{topic: {}, revision: {}, total_kafka_bytes_processed: {}}}", + topic, + revision, + total_kafka_bytes_processed); + } auto serde_fields() { return std::tie(topic, revision, total_kafka_bytes_processed); @@ -288,7 +337,9 @@ struct datalake_usage_stats datalake_usage_stats, serde::version<0>, serde::compat_version<0>> { - friend std::ostream& operator<<(std::ostream&, const datalake_usage_stats&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{topic_usages: {} }}", topic_usages); + } chunked_vector topic_usages; @@ -302,7 +353,9 @@ struct usage_stats_reply explicit usage_stats_reply(errc err) : errc(err) {} - friend std::ostream& operator<<(std::ostream&, const usage_stats_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{errc: {}, stats: {}}}", errc, stats); + } errc errc; // only valid if errc == errc::ok @@ -325,7 +378,10 @@ struct usage_stats_request usage_stats_request() = default; explicit usage_stats_request(model::partition_id coordinator_partition) : coordinator_partition(coordinator_partition) {} - friend std::ostream& operator<<(std::ostream&, const usage_stats_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{coordinator_partition: {}}}", coordinator_partition); + } model::partition_id get_coordinator_partition() { return coordinator_partition; @@ -343,8 +399,10 @@ struct get_topic_state_reply explicit get_topic_state_reply(errc err) : errc(err) {} - friend std::ostream& - operator<<(std::ostream&, const get_topic_state_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{errc: {}, topic_states size: {}}}", errc, topic_states.size()); + } errc errc; // Map from topic to its state. Only valid if errc == errc::ok @@ -376,8 +434,13 @@ struct get_topic_state_request return coordinator_partition; } - friend std::ostream& - operator<<(std::ostream&, const get_topic_state_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{coordinator_partition: {}, topics_filter: {}}}", + coordinator_partition, + topics_filter); + } auto serde_fields() { return std::tie(coordinator_partition, topics_filter); @@ -392,8 +455,9 @@ struct reset_topic_state_reply reset_topic_state_reply() = default; explicit reset_topic_state_reply(errc err) : errc(err) {} - friend std::ostream& - operator<<(std::ostream&, const reset_topic_state_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{errc: {}}}", errc); + } errc errc; auto serde_fields() { return std::tie(errc); } }; @@ -431,18 +495,16 @@ struct reset_topic_state_request return coordinator_partition; } - friend std::ostream& - operator<<(std::ostream& o, const reset_topic_state_request& req) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{coordinator_partition: {}, topic: {}, topic_revision: {}, " "reset_all_partitions: {}, partition_overrides: {} entries}}", - req.coordinator_partition, - req.topic, - req.topic_revision, - req.reset_all_partitions, - req.partition_overrides.size()); - return o; + coordinator_partition, + topic, + topic_revision, + reset_all_partitions, + partition_overrides.size()); } auto serde_fields() { diff --git a/src/v/datalake/data_writer_interface.cc b/src/v/datalake/data_writer_interface.cc index 3c1a4677f7abf..afd4bd00f9b6a 100644 --- a/src/v/datalake/data_writer_interface.cc +++ b/src/v/datalake/data_writer_interface.cc @@ -12,34 +12,8 @@ #include namespace datalake { -std::ostream& operator<<(std::ostream& os, const writer_error& ev) { - switch (ev) { - case writer_error::ok: - return os << "Ok"; - case writer_error::parquet_conversion_error: - return os << "Parquet Conversion Error"; - case writer_error::file_io_error: - return os << "File IO Error"; - case writer_error::no_data: - return os << "No data"; - case writer_error::flush_error: - return os << "Flush failed"; - case writer_error::oom_error: - return os << "Memory exhausted"; - case writer_error::time_limit_exceeded: - return os << "Time limit exceeded"; - case writer_error::shutting_down: - return os << "Shutting down"; - case writer_error::out_of_disk: - return os << "Disk exhausted"; - case writer_error::unknown_error: - return os << "Unknown error"; - case writer_error::retryable_type_resolution_error: - return os << "Retryable type resolution error"; - } -} std::string data_writer_error_category::message(int ev) const { - return fmt::to_string(static_cast(ev)); + return fmt::format("{}", static_cast(ev)); } writer_error map_to_writer_error(reservation_error reservation_err) { diff --git a/src/v/datalake/data_writer_interface.h b/src/v/datalake/data_writer_interface.h index 0d25cc12c6d3b..dbb084b9728e7 100644 --- a/src/v/datalake/data_writer_interface.h +++ b/src/v/datalake/data_writer_interface.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "datalake/base_types.h" #include "iceberg/datatypes.h" @@ -33,7 +34,32 @@ enum class writer_error { out_of_disk, unknown_error, }; -std::ostream& operator<<(std::ostream&, const writer_error&); +inline fmt::iterator format_to(writer_error ev, fmt::iterator out) { + switch (ev) { + case writer_error::ok: + return fmt::format_to(out, "Ok"); + case writer_error::parquet_conversion_error: + return fmt::format_to(out, "Parquet Conversion Error"); + case writer_error::file_io_error: + return fmt::format_to(out, "File IO Error"); + case writer_error::no_data: + return fmt::format_to(out, "No data"); + case writer_error::flush_error: + return fmt::format_to(out, "Flush failed"); + case writer_error::oom_error: + return fmt::format_to(out, "Memory exhausted"); + case writer_error::time_limit_exceeded: + return fmt::format_to(out, "Time limit exceeded"); + case writer_error::shutting_down: + return fmt::format_to(out, "Shutting down"); + case writer_error::out_of_disk: + return fmt::format_to(out, "Disk exhausted"); + case writer_error::unknown_error: + return fmt::format_to(out, "Unknown error"); + case writer_error::retryable_type_resolution_error: + return fmt::format_to(out, "Retryable type resolution error"); + } +} // Recoverable errors are the class of errors that donot leave the underlying // writers in a bad shape. Upon recoverable errors the translator may choose to diff --git a/src/v/datalake/partition_key_path.cc b/src/v/datalake/partition_key_path.cc index 9ab5c7ff544c5..e6780f0c742fd 100644 --- a/src/v/datalake/partition_key_path.cc +++ b/src/v/datalake/partition_key_path.cc @@ -55,24 +55,28 @@ bool has_millisecond_fraction(std::chrono::microseconds sub_seconds_us) { ss::sstring format_timestamp(size_t value, bool include_zone) { std::chrono::system_clock::time_point tp{std::chrono::microseconds(value)}; const auto sub_seconds_us = get_sub_seconds(tp); + // Truncate to seconds so that %S does not emit fractional seconds + // (fmt >=10 includes sub-second digits in %S when the time_point + // carries sub-second precision). + const auto tp_s = std::chrono::floor(tp); if (sub_seconds_us == std::chrono::microseconds(0)) { if (include_zone) { - return ssx::sformat("{:%Y-%m-%dT%H:%M:%S}{:%z}", tp, tp); + return ssx::sformat("{:%Y-%m-%dT%H:%M:%S}{:%z}", tp_s, tp_s); } - return ssx::sformat("{:%Y-%m-%dT%H:%M:%S}Z", tp); + return ssx::sformat("{:%Y-%m-%dT%H:%M:%S}Z", tp_s); } if (has_millisecond_fraction(sub_seconds_us)) { if (include_zone) { return ssx::sformat( "{:%Y-%m-%dT%H:%M:%S}.{:06}{:%z}", - tp, + tp_s, get_sub_seconds(tp).count(), - tp); + tp_s); } return ssx::sformat( - "{:%Y-%m-%dT%H:%M:%S}.{:06}Z", tp, get_sub_seconds(tp).count()); + "{:%Y-%m-%dT%H:%M:%S}.{:06}Z", tp_s, get_sub_seconds(tp).count()); } // no millisecond fraction @@ -81,10 +85,13 @@ ss::sstring format_timestamp(size_t value, bool include_zone) { if (include_zone) { return ssx::sformat( - "{:%Y-%m-%dT%H:%M:%S}.{:03}{:%z}", tp, sub_seconds_ms.count(), tp); + "{:%Y-%m-%dT%H:%M:%S}.{:03}{:%z}", + tp_s, + sub_seconds_ms.count(), + tp_s); } return ssx::sformat( - "{:%Y-%m-%dT%H:%M:%S}.{:03}Z", tp, sub_seconds_ms.count()); + "{:%Y-%m-%dT%H:%M:%S}.{:03}Z", tp_s, sub_seconds_ms.count()); } /** * Default formatting rules used for identity type diff --git a/src/v/datalake/partitioning_writer.cc b/src/v/datalake/partitioning_writer.cc index 6ae7ddf0f9e16..5be7262dbbfbc 100644 --- a/src/v/datalake/partitioning_writer.cc +++ b/src/v/datalake/partitioning_writer.cc @@ -18,17 +18,16 @@ namespace datalake { -std::ostream& -operator<<(std::ostream& o, const partitioning_writer::partitioned_file& f) { - fmt::print( - o, +fmt::iterator +partitioning_writer::partitioned_file::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{local_file: {}, schema_id: {}, partition_spec_id: {}, " "partition_key: {}}}", - f.local_file, - f.schema_id, - f.partition_spec_id, - f.partition_key.val); - return o; + local_file, + schema_id, + partition_spec_id, + partition_key.val); } ss::future<> partitioning_writer::flush() { diff --git a/src/v/datalake/partitioning_writer.h b/src/v/datalake/partitioning_writer.h index 7af81952d2633..c7c00b17554a4 100644 --- a/src/v/datalake/partitioning_writer.h +++ b/src/v/datalake/partitioning_writer.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "container/chunked_hash_map.h" #include "datalake/data_writer_interface.h" #include "iceberg/datatypes.h" @@ -64,7 +65,7 @@ class partitioning_writer { iceberg::partition_key partition_key; remote_path partition_key_path; - friend std::ostream& operator<<(std::ostream&, const partitioned_file&); + fmt::iterator format_to(fmt::iterator it) const; }; // Finishes and returns the list of local files written by the underlying diff --git a/src/v/datalake/record_schema_resolver.cc b/src/v/datalake/record_schema_resolver.cc index 10fb2f98af6fb..656bcbf9fd7ec 100644 --- a/src/v/datalake/record_schema_resolver.cc +++ b/src/v/datalake/record_schema_resolver.cc @@ -334,19 +334,6 @@ resolved_schema::resolved_schema(ss::shared_ptr ir) , schema_( *std::get>(shared_schema_)) {} -std::ostream& operator<<(std::ostream& o, const type_resolver::errc& e) { - switch (e) { - case type_resolver::errc::registry_error: - return o << "type_resolver::errc::registry_error"; - case type_resolver::errc::translation_error: - return o << "type_resolver::errc::translation_error"; - case type_resolver::errc::bad_input: - return o << "type_resolver::errc::bad_input"; - case type_resolver::errc::invalid_config: - return o << "type_resolver::errc::invalid_config"; - } -} - type_and_buf type_and_buf::make_raw_binary(std::optional b) { return type_and_buf{ .type = {}, diff --git a/src/v/datalake/record_schema_resolver.h b/src/v/datalake/record_schema_resolver.h index 11c2573856264..ce2a3c74b96f9 100644 --- a/src/v/datalake/record_schema_resolver.h +++ b/src/v/datalake/record_schema_resolver.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/property.h" #include "datalake/schema_identifier.h" @@ -154,7 +155,19 @@ class type_resolver { bad_input, invalid_config, }; - friend std::ostream& operator<<(std::ostream&, const errc&); + friend fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::registry_error: + return fmt::format_to(out, "type_resolver::errc::registry_error"); + case errc::translation_error: + return fmt::format_to( + out, "type_resolver::errc::translation_error"); + case errc::bad_input: + return fmt::format_to(out, "type_resolver::errc::bad_input"); + case errc::invalid_config: + return fmt::format_to(out, "type_resolver::errc::invalid_config"); + } + } virtual ss::future> resolve_buf_type(std::optional b) const = 0; // TODO(iceberg): This should be it's own interface. diff --git a/src/v/datalake/record_translator.cc b/src/v/datalake/record_translator.cc index f090c2e8fdf4f..b989afbb1efb9 100644 --- a/src/v/datalake/record_translator.cc +++ b/src/v/datalake/record_translator.cc @@ -123,15 +123,6 @@ std::unique_ptr build_rp_struct( } // namespace -std::ostream& operator<<(std::ostream& o, const record_translator::errc& e) { - switch (e) { - case record_translator::errc::translation_error: - return o << "record_translator::errc::translation_error"; - case record_translator::errc::unexpected_schema: - return o << "record_translator::errc::unexpected_schema"; - } -} - record_type default_translator::build_type(std::optional val_type) { if (val_type.has_value()) { diff --git a/src/v/datalake/record_translator.h b/src/v/datalake/record_translator.h index 9dcd29ed9086a..f318d395ed91f 100644 --- a/src/v/datalake/record_translator.h +++ b/src/v/datalake/record_translator.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "datalake/record_schema_resolver.h" #include "datalake/schema_identifier.h" @@ -32,7 +33,16 @@ class record_translator { translation_error, unexpected_schema, }; - friend std::ostream& operator<<(std::ostream&, const errc&); + friend fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::translation_error: + return fmt::format_to( + out, "record_translator::errc::translation_error"); + case errc::unexpected_schema: + return fmt::format_to( + out, "record_translator::errc::unexpected_schema"); + } + } virtual record_type build_type(std::optional val_type) = 0; diff --git a/src/v/datalake/schema_registry.h b/src/v/datalake/schema_registry.h index d7b21ecded183..0e2190000e804 100644 --- a/src/v/datalake/schema_registry.h +++ b/src/v/datalake/schema_registry.h @@ -11,6 +11,8 @@ #include "pandaproxy/schema_registry/types.h" +#include + #include #include @@ -87,3 +89,17 @@ namespace std { template<> struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(datalake::get_schema_error e, fmt::format_context& ctx) const { + return fmt::format_to( + ctx.out(), + "{}", + datalake::get_schema_error_category::error_category().message( + static_cast(e))); + } +}; diff --git a/src/v/datalake/table_creator.cc b/src/v/datalake/table_creator.cc deleted file mode 100644 index c999c29455229..0000000000000 --- a/src/v/datalake/table_creator.cc +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Redpanda Data, Inc. - * - * Licensed as a Redpanda Enterprise file under the Redpanda Community - * License (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md - */ -#include "datalake/table_creator.h" - -namespace datalake { - -std::ostream& operator<<(std::ostream& o, const table_creator::errc& e) { - switch (e) { - case table_creator::errc::incompatible_schema: - return o << "table_creator::errc::incompatible_schema"; - case table_creator::errc::failed: - return o << "table_creator::errc::failed"; - case table_creator::errc::shutting_down: - return o << "table_creator::errc::shutting_down"; - } -} - -} // namespace datalake diff --git a/src/v/datalake/table_creator.h b/src/v/datalake/table_creator.h index 09bf1293366ee..64bfbb854190d 100644 --- a/src/v/datalake/table_creator.h +++ b/src/v/datalake/table_creator.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "datalake/schema_identifier.h" namespace datalake { @@ -22,7 +23,17 @@ class table_creator { // The system is shutting down. shutting_down, }; - friend std::ostream& operator<<(std::ostream&, const errc&); + friend fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::incompatible_schema: + return fmt::format_to( + out, "table_creator::errc::incompatible_schema"); + case errc::failed: + return fmt::format_to(out, "table_creator::errc::failed"); + case errc::shutting_down: + return fmt::format_to(out, "table_creator::errc::shutting_down"); + } + } virtual ss::future> ensure_table( const model::topic&, diff --git a/src/v/datalake/tests/credential_manager_test.cc b/src/v/datalake/tests/credential_manager_test.cc index 788dbbecb525a..5a3e8a3242974 100644 --- a/src/v/datalake/tests/credential_manager_test.cc +++ b/src/v/datalake/tests/credential_manager_test.cc @@ -39,8 +39,8 @@ class mock_apply_credentials_impl _reset_creds_called = true; } - std::ostream& print(std::ostream& os) const override { - return os << "mock_apply_credentials_impl"; + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to(it, "mock_apply_credentials_impl"); } bool is_oauth() const override { return false; } diff --git a/src/v/datalake/translation/BUILD b/src/v/datalake/translation/BUILD index 23fc0336b10a4..3ce2616f64174 100644 --- a/src/v/datalake/translation/BUILD +++ b/src/v/datalake/translation/BUILD @@ -41,6 +41,7 @@ redpanda_cc_library( ], visibility = ["//visibility:public"], deps = [ + "//src/v/base", "//src/v/config", "//src/v/container:chunked_hash_map", "//src/v/container:chunked_vector", @@ -164,6 +165,7 @@ redpanda_cc_library( "//src/v/datalake:__subpackages__", ], deps = [ + "//src/v/base", "//src/v/config", "//src/v/metrics", "//src/v/model", diff --git a/src/v/datalake/translation/deps.cc b/src/v/datalake/translation/deps.cc index b88380fe8d683..44d154feb7d18 100644 --- a/src/v/datalake/translation/deps.cc +++ b/src/v/datalake/translation/deps.cc @@ -317,13 +317,10 @@ struct timestamped_offset { kafka::offset offset; model::timestamp ts; - friend std::ostream& - operator<<(std::ostream& o, const timestamped_offset& to); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{offset: {}, timestamp: {}}}", offset, ts); + } }; -std::ostream& operator<<(std::ostream& o, const timestamped_offset& to) { - fmt::print(o, "{{offset: {}, timestamp: {}}}", to.offset, to.ts); - return o; -} } // namespace @@ -443,31 +440,6 @@ std::unique_ptr data_source::make_default_data_source( return std::make_unique(std::move(partition)); } -std::ostream& operator<<(std::ostream& o, translation_errc ec) { - switch (ec) { - case no_data: - return o << "translation_errc::no_data"; - case file_io_error: - return o << "translation_errc::file_io_error"; - case cloud_io_error: - return o << "translation_errc::cloud_io_error"; - case flush_error: - return o << "translation_errc::flush_error"; - case discard_error: - return o << "translation_errc::discard_error"; - case oom_error: - return o << "translation_errc::oom_error"; - case time_limit_exceeded: - return o << "translation_errc::time_limit_exceeded"; - case shutting_down: - return o << "translation_errc::shutting_down"; - case out_of_disk: - return o << "translation_errc::out_of_disk"; - case type_resolution_error: - return o << "translation_errc::type_resolution_error"; - } -} - class partition_translation_context : public translation_context { public: explicit partition_translation_context( diff --git a/src/v/datalake/translation/deps.h b/src/v/datalake/translation/deps.h index 4ee233acab831..67985449707b3 100644 --- a/src/v/datalake/translation/deps.h +++ b/src/v/datalake/translation/deps.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "cluster/fwd.h" #include "datalake/coordinator/types.h" #include "datalake/data_writer_interface.h" @@ -215,7 +216,30 @@ enum translation_errc { type_resolution_error, }; -std::ostream& operator<<(std::ostream&, translation_errc); +inline fmt::iterator format_to(translation_errc ec, fmt::iterator out) { + switch (ec) { + case no_data: + return fmt::format_to(out, "translation_errc::no_data"); + case file_io_error: + return fmt::format_to(out, "translation_errc::file_io_error"); + case cloud_io_error: + return fmt::format_to(out, "translation_errc::cloud_io_error"); + case flush_error: + return fmt::format_to(out, "translation_errc::flush_error"); + case discard_error: + return fmt::format_to(out, "translation_errc::discard_error"); + case oom_error: + return fmt::format_to(out, "translation_errc::oom_error"); + case time_limit_exceeded: + return fmt::format_to(out, "translation_errc::time_limit_exceeded"); + case shutting_down: + return fmt::format_to(out, "translation_errc::shutting_down"); + case out_of_disk: + return fmt::format_to(out, "translation_errc::out_of_disk"); + case type_resolution_error: + return fmt::format_to(out, "translation_errc::type_resolution_error"); + } +} class translation_context { public: diff --git a/src/v/datalake/translation/scheduling.cc b/src/v/datalake/translation/scheduling.cc index 0aad72804ee93..c4a48e23d4b98 100644 --- a/src/v/datalake/translation/scheduling.cc +++ b/src/v/datalake/translation/scheduling.cc @@ -229,20 +229,18 @@ class default_reservations_tracker : public reservations_tracker { disk_manager& _disk_manager; }; -std::ostream& operator<<(std::ostream& os, const translation_status& status) { - fmt::print( - os, +fmt::iterator translation_status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{target_lag: {}, next_checkpoint: {}, memory_reserved: {}}}", - std::chrono::duration_cast(status.target_lag), + std::chrono::duration_cast(target_lag), std::chrono::duration_cast( - status.next_checkpoint_deadline - clock::now()), - status.memory_bytes_reserved); - return os; + next_checkpoint_deadline - clock::now()), + memory_bytes_reserved); } -std::ostream& operator<<(std::ostream& os, const translator& t) { - fmt::print(os, "{{id: {}, status: {}}}", t.id(), t.status()); - return os; +fmt::iterator translator::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{id: {}, status: {}}}", id(), status()); } translator_executable::translator_executable( @@ -271,39 +269,37 @@ translation_status translator_executable::status() const { return _translator->status(); } -std::ostream& operator<<(std::ostream& os, const translator_executable& state) { +fmt::iterator translator_executable::format_to(fmt::iterator it) const { std::optional current_wait_time; - if (state._current_wait_begin_time) { + if (_current_wait_begin_time) { current_wait_time = std::chrono::duration_cast( - clock::now() - state._current_wait_begin_time.value()); + clock::now() - _current_wait_begin_time.value()); } std::optional current_running_time; - if (state._current_running_begin_time) { + if (_current_running_begin_time) { current_running_time = std::chrono::duration_cast( - clock::now() - state._current_running_begin_time.value()); + clock::now() - _current_running_begin_time.value()); } - fmt::print( - os, + return fmt::format_to( + it, "{{translator: {}, time_since_start: {}, current_wait_time: {}, " "total_wait_time: {}, current_running_time: {}, total_running_time: {}, " "translations_scheduled: {}, stop_in_progress: {}, " "running: {}, waiting: {}}}", - *state._translator, + *_translator, std::chrono::duration_cast( - clock::now() - state._start_time), + clock::now() - _start_time), current_wait_time, - std::chrono::duration_cast( - state.total_wait_time()), + std::chrono::duration_cast(total_wait_time()), current_running_time, std::chrono::duration_cast( - state.total_running_time()), - state._translations_scheduled, - state._stop_in_progress, - state._running_hook.is_linked(), - state._waiting_hook.is_linked()); - return os; + total_running_time()), + _translations_scheduled, + _stop_in_progress, + _running_hook.is_linked(), + _waiting_hook.is_linked()); } clock::duration translator_executable::total_running_time() const { diff --git a/src/v/datalake/translation/scheduling.h b/src/v/datalake/translation/scheduling.h index 2d57dfd4727ec..c917d1b16d75e 100644 --- a/src/v/datalake/translation/scheduling.h +++ b/src/v/datalake/translation/scheduling.h @@ -10,6 +10,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "config/property.h" #include "container/chunked_hash_map.h" #include "container/intrusive_list_helpers.h" @@ -141,8 +142,9 @@ struct translation_status { std::optional disk_bytes_flushed; std::optional translation_backlog; + + fmt::iterator format_to(fmt::iterator it) const; }; -std::ostream& operator<<(std::ostream&, const translation_status&); /** * A single schedulable translator instance. Translation interacts with @@ -235,10 +237,9 @@ class translator { * then eventually return false. */ virtual bool get_finish_translation() { return false; } + fmt::iterator format_to(fmt::iterator it) const; }; -std::ostream& operator<<(std::ostream&, const translator&); - struct executor; class scheduler; @@ -293,8 +294,7 @@ class translator_executable { // hook into the running queue intrusive_list_hook _running_hook; - friend std::ostream& - operator<<(std::ostream&, const translator_executable&); + fmt::iterator format_to(fmt::iterator it) const; private: friend struct executor; diff --git a/src/v/datalake/translation/scheduling_policies.cc b/src/v/datalake/translation/scheduling_policies.cc index 47e1b00fbb2f0..31c39c5a7c55b 100644 --- a/src/v/datalake/translation/scheduling_policies.cc +++ b/src/v/datalake/translation/scheduling_policies.cc @@ -83,20 +83,6 @@ fair_scheduling_policy::fair_scheduling_policy( initialize_group_shares(); } -std::ostream& -operator<<(std::ostream& os, fair_scheduling_policy::translator_group group) { - switch (group) { - case fair_scheduling_policy::translator_group::other: - return os << "translator_group::other"; - case fair_scheduling_policy::translator_group::unfulfilled_quota: - return os << "translator_group::unfulfilled_quota"; - case fair_scheduling_policy::translator_group::about_to_expire: - return os << "translator_group::about_to_expire"; - case fair_scheduling_policy::translator_group::expired: - return os << "translator_group::expired"; - } -} - void fair_scheduling_policy::initialize_group_shares() { _group_to_shares = { {other, fair_scheduling_policy::default_other_shares}, diff --git a/src/v/datalake/translation/scheduling_policies.h b/src/v/datalake/translation/scheduling_policies.h index 110b48b1c4a76..f773930223083 100644 --- a/src/v/datalake/translation/scheduling_policies.h +++ b/src/v/datalake/translation/scheduling_policies.h @@ -90,7 +90,18 @@ class fair_scheduling_policy : public scheduling_policy { other, }; - friend std::ostream& operator<<(std::ostream&, translator_group); + friend fmt::iterator format_to(translator_group g, fmt::iterator out) { + switch (g) { + case translator_group::other: + return fmt::format_to(out, "translator_group::other"); + case translator_group::unfulfilled_quota: + return fmt::format_to(out, "translator_group::unfulfilled_quota"); + case translator_group::about_to_expire: + return fmt::format_to(out, "translator_group::about_to_expire"); + case translator_group::expired: + return fmt::format_to(out, "translator_group::expired"); + } + } /** * Picks a random translator group category with probability proportional diff --git a/src/v/datalake/translation/translation_probe.cc b/src/v/datalake/translation/translation_probe.cc index 7d09e94356a93..414669dd1c1e6 100644 --- a/src/v/datalake/translation/translation_probe.cc +++ b/src/v/datalake/translation/translation_probe.cc @@ -207,17 +207,4 @@ void translation_probe::register_invalid_record_metric() { } } -std::ostream& -operator<<(std::ostream& os, translation_probe::invalid_record_cause cause) { - using enum translation_probe::invalid_record_cause; - - switch (cause) { - case failed_kafka_schema_resolution: - return os << "failed_kafka_schema_resolution"; - case failed_data_translation: - return os << "failed_data_translation"; - case failed_iceberg_schema_resolution: - return os << "failed_iceberg_schema_resolution"; - } -} }; // namespace datalake diff --git a/src/v/datalake/translation/translation_probe.h b/src/v/datalake/translation/translation_probe.h index e80b04c8e6ac5..ebf6d6780e20a 100644 --- a/src/v/datalake/translation/translation_probe.h +++ b/src/v/datalake/translation/translation_probe.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "metrics/metrics.h" #include "model/fundamental.h" @@ -103,7 +104,19 @@ class translation_probe final { size_t _decompressed_bytes_processed = 0; }; -std::ostream& -operator<<(std::ostream& os, translation_probe::invalid_record_cause cause); +inline fmt::iterator +format_to(translation_probe::invalid_record_cause cause, fmt::iterator out) { + switch (cause) { + case translation_probe::invalid_record_cause:: + failed_kafka_schema_resolution: + return fmt::format_to(out, "failed_kafka_schema_resolution"); + case translation_probe::invalid_record_cause::failed_data_translation: + return fmt::format_to(out, "failed_data_translation"); + case translation_probe::invalid_record_cause:: + failed_iceberg_schema_resolution: + return fmt::format_to(out, "failed_iceberg_schema_resolution"); + } + __builtin_unreachable(); +} }; // namespace datalake diff --git a/src/v/datalake/translation_task.cc b/src/v/datalake/translation_task.cc index 5e0392c7bb490..b71db599b3049 100644 --- a/src/v/datalake/translation_task.cc +++ b/src/v/datalake/translation_task.cc @@ -408,26 +408,4 @@ size_t translation_task::buffered_bytes() const { return _multiplexer.buffered_bytes(); } -std::ostream& operator<<(std::ostream& o, translation_task::errc ec) { - switch (ec) { - case translation_task::errc::file_io_error: - return o << "local file IO error"; - case translation_task::errc::cloud_io_error: - return o << "cloud IO error"; - case translation_task::errc::flush_error: - return o << "writer flush error"; - case translation_task::errc::no_data: - return o << "no data to translate"; - case translation_task::errc::oom_error: - return o << "memory exhausted"; - case translation_task::errc::time_limit_exceeded: - return o << "time limit exceeded"; - case translation_task::errc::shutting_down: - return o << "shutting down"; - case translation_task::errc::out_of_disk: - return o << "disk exhausted"; - case translation_task::errc::type_resolution_error: - return o << "type resolution error"; - } -} } // namespace datalake diff --git a/src/v/datalake/translation_task.h b/src/v/datalake/translation_task.h index ff94b27ce2409..51116feb00946 100644 --- a/src/v/datalake/translation_task.h +++ b/src/v/datalake/translation_task.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "datalake/cloud_data_io.h" #include "datalake/coordinator/translated_offset_range.h" @@ -106,7 +107,28 @@ class translation_task { size_t buffered_bytes() const; private: - friend std::ostream& operator<<(std::ostream&, errc); + friend fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::file_io_error: + return fmt::format_to(out, "local file IO error"); + case errc::cloud_io_error: + return fmt::format_to(out, "cloud IO error"); + case errc::flush_error: + return fmt::format_to(out, "writer flush error"); + case errc::no_data: + return fmt::format_to(out, "no data to translate"); + case errc::oom_error: + return fmt::format_to(out, "memory exhausted"); + case errc::time_limit_exceeded: + return fmt::format_to(out, "time limit exceeded"); + case errc::shutting_down: + return fmt::format_to(out, "shutting down"); + case errc::out_of_disk: + return fmt::format_to(out, "disk exhausted"); + case errc::type_resolution_error: + return fmt::format_to(out, "type resolution error"); + } + } ss::future delete_remote_files( chunked_vector, retry_chain_node& parent_rcn); diff --git a/src/v/debug_bundle/types.cc b/src/v/debug_bundle/types.cc index 837d678e7b78c..2552e60f87667 100644 --- a/src/v/debug_bundle/types.cc +++ b/src/v/debug_bundle/types.cc @@ -27,9 +27,6 @@ #include namespace debug_bundle { -std::ostream& operator<<(std::ostream& o, const special_date& d) { - return o << to_string_view(d); -} std::istream& operator>>(std::istream& i, special_date& d) { ss::sstring s; @@ -44,20 +41,6 @@ std::istream& operator>>(std::istream& i, special_date& d) { return i; } -std::ostream& operator<<(std::ostream& o, const debug_bundle_status& s) { - return o << to_string_view(s); -} - -std::ostream& operator<<(std::ostream& o, const partition_selection& p) { - fmt::print(o, "{}/{}/{}", p.tn.ns, p.tn.tp, fmt::join(p.partitions, ",")); - return o; -} - -std::ostream& operator<<(std::ostream& o, const label_selection& l) { - fmt::print(o, "{}={}", l.key, l.value); - return o; -} - std::optional partition_selection::from_string_view(std::string_view str) { try { @@ -92,31 +75,3 @@ partition_selection::from_string_view(std::string_view str) { } } // namespace debug_bundle - -auto fmt::formatter::format( - debug_bundle::special_date d, format_context& ctx) const - -> format_context::iterator { - return formatter::format(debug_bundle::to_string_view(d), ctx); -} - -auto fmt::formatter::format( - const debug_bundle::time_variant& t, format_context& ctx) const - -> format_context::iterator { - return ss::visit( - t, - [&ctx](const debug_bundle::clock::time_point& t) { - auto tt = debug_bundle::clock::to_time_t(t); - std::tm tm = *std::localtime(&tt); - return fmt::format_to(ctx.out(), "{:%FT%T}", tm); - }, - [&ctx](const debug_bundle::special_date& d) { - return fmt::format_to(ctx.out(), "{}", d); - }); -} - -auto fmt::formatter::format( - const debug_bundle::partition_selection& p, format_context& ctx) const - -> format_context::iterator { - return fmt::format_to( - ctx.out(), "{}/{}/{}", p.tn.ns, p.tn.tp, fmt::join(p.partitions, ",")); -} diff --git a/src/v/debug_bundle/types.h b/src/v/debug_bundle/types.h index 885b73d93b421..07fe82076706e 100644 --- a/src/v/debug_bundle/types.h +++ b/src/v/debug_bundle/types.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/btree_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" #include "model/metadata.h" @@ -23,12 +24,14 @@ #include #include +#include #include #include #include -#include +#include #include +#include #include namespace debug_bundle { @@ -51,8 +54,10 @@ constexpr std::string_view to_string_view(special_date d) { return "tomorrow"; } } +inline fmt::iterator format_to(special_date d, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(d)); +} -std::ostream& operator<<(std::ostream& o, const special_date& d); std::istream& operator>>(std::istream& i, special_date& d); /// Defines which clock the debug bundle will use @@ -82,9 +87,12 @@ struct partition_selection { friend bool operator==( const partition_selection&, const partition_selection&) = default; -}; -std::ostream& operator<<(std::ostream& o, const partition_selection& p); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{}/{}/{}", tn.ns, tn.tp, fmt::join(partitions, ",")); + } +}; struct label_selection { ss::sstring key; @@ -93,7 +101,9 @@ struct label_selection { friend bool operator==(const label_selection&, const label_selection&) = default; - friend std::ostream& operator<<(std::ostream& o, const label_selection& l); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}={}", key, value); + } }; /// Parameters used to spawn rpk debug bundle @@ -131,8 +141,9 @@ constexpr std::string_view to_string_view(debug_bundle_status s) { return "expired"; } } - -std::ostream& operator<<(std::ostream& o, const debug_bundle_status& s); +inline fmt::iterator format_to(debug_bundle_status s, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(s)); +} /// Status of the debug bundle process struct debug_bundle_status_data { @@ -150,24 +161,23 @@ struct debug_bundle_status_data { }; } // namespace debug_bundle -template<> -struct fmt::formatter - : formatter { - auto format(debug_bundle::special_date d, format_context& ctx) const - -> format_context::iterator; -}; - template<> struct fmt::formatter : formatter { - auto format(const debug_bundle::time_variant&, format_context& ctx) const - -> format_context::iterator; -}; - -template<> -struct fmt::formatter - : formatter { - auto - format(const debug_bundle::partition_selection&, format_context& ctx) const - -> format_context::iterator; + auto format(const debug_bundle::time_variant& t, format_context& ctx) const + -> format_context::iterator { + return std::visit( + [&ctx](const auto& value) -> format_context::iterator { + using T = std::decay_t; + if constexpr ( + std::is_same_v) { + auto tt = debug_bundle::clock::to_time_t(value); + auto tm = *std::localtime(&tt); + return fmt::format_to(ctx.out(), "{:%FT%T}", tm); + } else { + return fmt::format_to(ctx.out(), "{}", value); + } + }, + t); + } }; diff --git a/src/v/features/enterprise_features.cc b/src/v/features/enterprise_features.cc index d183932a9e72f..19d47cb8244e6 100644 --- a/src/v/features/enterprise_features.cc +++ b/src/v/features/enterprise_features.cc @@ -13,44 +13,8 @@ #include "base/vassert.h" -#include - namespace features { -std::ostream& operator<<(std::ostream& os, license_required_feature f) { - switch (f) { - case license_required_feature::audit_logging: - return os << "audit_logging"; - case license_required_feature::cloud_storage: - return os << "cloud_storage"; - case license_required_feature::partition_auto_balancing_continuous: - return os << "partition_auto_balancing_continuous"; - case license_required_feature::core_balancing_continuous: - return os << "core_balancing_continuous"; - case license_required_feature::gssapi: - return os << "gssapi"; - case license_required_feature::oidc: - return os << "oidc"; - case license_required_feature::schema_id_validation: - return os << "schema_id_validation"; - case license_required_feature::rbac: - return os << "rbac"; - case license_required_feature::fips: - return os << "fips"; - case license_required_feature::datalake_iceberg: - return os << "datalake_iceberg"; - case license_required_feature::leadership_pinning: - return os << "leadership_pinning"; - case license_required_feature::shadow_linking: - return os << "shadow_linking"; - case license_required_feature::cloud_topics: - return os << "cloud_topics"; - case license_required_feature::topic_deletion_disabled: - return os << "topic_deletion_disabled"; - } - __builtin_unreachable(); -} - void enterprise_feature_report::set( license_required_feature feat, bool enabled) { auto insert = [feat](vtype& dest, const vtype& other) { diff --git a/src/v/features/enterprise_features.h b/src/v/features/enterprise_features.h index 421859c3debde..0cf218f966b5b 100644 --- a/src/v/features/enterprise_features.h +++ b/src/v/features/enterprise_features.h @@ -12,13 +12,12 @@ #pragma once #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "config/configuration.h" #include "config/property.h" #include -#include - namespace features { enum class license_required_feature { @@ -38,7 +37,38 @@ enum class license_required_feature { topic_deletion_disabled, }; -std::ostream& operator<<(std::ostream&, license_required_feature); +inline fmt::iterator format_to(license_required_feature f, fmt::iterator out) { + switch (f) { + case license_required_feature::audit_logging: + return fmt::format_to(out, "audit_logging"); + case license_required_feature::cloud_storage: + return fmt::format_to(out, "cloud_storage"); + case license_required_feature::partition_auto_balancing_continuous: + return fmt::format_to(out, "partition_auto_balancing_continuous"); + case license_required_feature::core_balancing_continuous: + return fmt::format_to(out, "core_balancing_continuous"); + case license_required_feature::gssapi: + return fmt::format_to(out, "gssapi"); + case license_required_feature::oidc: + return fmt::format_to(out, "oidc"); + case license_required_feature::schema_id_validation: + return fmt::format_to(out, "schema_id_validation"); + case license_required_feature::rbac: + return fmt::format_to(out, "rbac"); + case license_required_feature::fips: + return fmt::format_to(out, "fips"); + case license_required_feature::datalake_iceberg: + return fmt::format_to(out, "datalake_iceberg"); + case license_required_feature::leadership_pinning: + return fmt::format_to(out, "leadership_pinning"); + case license_required_feature::shadow_linking: + return fmt::format_to(out, "shadow_linking"); + case license_required_feature::cloud_topics: + return fmt::format_to(out, "cloud_topics"); + case license_required_feature::topic_deletion_disabled: + return fmt::format_to(out, "topic_deletion_disabled"); + } +} /** * Thin wrapper around two sets to indicate the current state of enterprise diff --git a/src/v/features/feature_table.cc b/src/v/features/feature_table.cc index eef7051a11a17..f278f73c9f93e 100644 --- a/src/v/features/feature_table.cc +++ b/src/v/features/feature_table.cc @@ -11,6 +11,7 @@ #include "feature_table.h" +#include "base/format_to.h" #include "cluster/feature_update_action.h" #include "cluster/version.h" #include "config/configuration.h" @@ -152,6 +153,10 @@ std::string_view to_string_view(feature f) { __builtin_unreachable(); } +fmt::iterator format_to(feature f, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(f)); +} + std::string_view to_string_view(feature_state::state s) { switch (s) { case feature_state::state::unavailable: @@ -172,6 +177,10 @@ std::string_view to_string_view(feature_state::state s) { __builtin_unreachable(); }; +fmt::iterator format_to(feature_state::state s, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(s)); +} + // The version that this redpanda node will report: increment this // on protocol changes to raft0 structures, like adding new services. constexpr cluster_version latest_version = to_cluster_version( diff --git a/src/v/features/feature_table.h b/src/v/features/feature_table.h index b90f14f211f9d..1e28eed824e1a 100644 --- a/src/v/features/feature_table.h +++ b/src/v/features/feature_table.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "features/feature_state.h" #include "security/license.h" #include "storage/record_batch_builder.h" @@ -567,7 +568,9 @@ inline constexpr std::array feature_schema{ }; std::string_view to_string_view(feature); +fmt::iterator format_to(feature, fmt::iterator); std::string_view to_string_view(feature_state::state); +fmt::iterator format_to(feature_state::state, fmt::iterator); /** * To enable all shards to efficiently check enablement of features @@ -843,13 +846,3 @@ class feature_table { }; } // namespace features - -template<> -struct fmt::formatter final - : fmt::formatter { - template - auto - format(const features::feature_state::state& s, FormatContext& ctx) const { - return formatter::format(features::to_string_view(s), ctx); - } -}; diff --git a/src/v/http/BUILD b/src/v/http/BUILD index d1128e41f2661..4341f196b6fa5 100644 --- a/src/v/http/BUILD +++ b/src/v/http/BUILD @@ -47,6 +47,7 @@ redpanda_cc_library( "@boost//:asio", "@boost//:beast", "@boost//:optional", + "@fmt", "@seastar", ], ) diff --git a/src/v/http/client.cc b/src/v/http/client.cc index f5999445eb5e2..a43ea95c65783 100644 --- a/src/v/http/client.cc +++ b/src/v/http/client.cc @@ -9,6 +9,7 @@ #include "http/client.h" +#include "base/external_fmt.h" #include "base/likely.h" #include "base/vlog.h" #include "bytes/details/io_iterator_consumer.h" diff --git a/src/v/http/client.h b/src/v/http/client.h index e6d70e1690a52..bd94522de68de 100644 --- a/src/v/http/client.h +++ b/src/v/http/client.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -387,37 +388,20 @@ struct fmt::formatter { } template - auto format(const http::client::request_header& h, FormatContext& ctx) + auto format(const http::client::request_header& h, FormatContext& ctx) const -> decltype(ctx.out()) { auto redacted = http::redacted_header(h); - std::stringstream s; - s << redacted; - return fmt::format_to(ctx.out(), "{}", s.str()); + return fmt::format_to(ctx.out(), "{}", fmt_streamed(redacted)); } }; template<> struct fmt::formatter { - char presentation = 'u'; // 'u' for unchanged, 'l' for one line - constexpr auto parse(format_parse_context& ctx) { - auto it = ctx.begin(); - auto end = ctx.end(); - if (it != end && (*it == 'l' || *it == 'u')) presentation = *it++; - if (it != end && *it != '}') throw format_error("invalid format"); - return it; + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); } - auto format(http::client::response_header& h, auto& ctx) const { - if (presentation == 'u') { - std::stringstream s; - s << h; - return fmt::format_to(ctx.out(), "{}", s.str()); - } - // format one line - auto out = ctx.out(); - for (auto& f : h) { - out = fmt::format_to(out, "[{}: {}];", f.name_string(), f.value()); - } - return out; + auto format(const http::client::response_header& h, auto& ctx) const { + return fmt::format_to(ctx.out(), "{}", fmt_streamed(h)); } }; diff --git a/src/v/http/tests/BUILD b/src/v/http/tests/BUILD index 9a70cffb01a12..ff1f1f4e3776f 100644 --- a/src/v/http/tests/BUILD +++ b/src/v/http/tests/BUILD @@ -19,6 +19,7 @@ redpanda_test_cc_library( "//src/v/utils:uuid", "@abseil-cpp//absl/container:flat_hash_map", "@boost//:beast", + "@fmt", "@seastar", ], ) diff --git a/src/v/http/tests/registered_urls.cc b/src/v/http/tests/registered_urls.cc index 997810d3afd85..21520d28b75e5 100644 --- a/src/v/http/tests/registered_urls.cc +++ b/src/v/http/tests/registered_urls.cc @@ -141,13 +141,4 @@ response registered_urls::lookup(const request_info& req) const { return content_mapping[default_content.data()]; } -std::ostream& operator<<(std::ostream& os, const response& resp) { - fmt::print( - os, - "{{status: {}, body: {}}}", - static_cast(resp.status), - resp.body); - return os; -} - } // namespace http_test_utils diff --git a/src/v/http/tests/registered_urls.h b/src/v/http/tests/registered_urls.h index 1e1fb61ace4b8..1733f446456dc 100644 --- a/src/v/http/tests/registered_urls.h +++ b/src/v/http/tests/registered_urls.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -18,8 +19,6 @@ #include -#include - namespace http_test_utils { struct response { using status_type = ss::http::reply::status_type; @@ -27,6 +26,11 @@ struct response { std::vector> headers; status_type status; std::optional content_type; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{status: {}, body: {}}}", static_cast(status), body); + } }; struct request_info { @@ -67,8 +71,6 @@ struct request_info { } }; -std::ostream& operator<<(std::ostream& os, const response& resp); - struct registered_urls { using content_reply_map = absl::flat_hash_map; diff --git a/src/v/iceberg/BUILD b/src/v/iceberg/BUILD index 30d6eadf0763d..642b0a0b943ca 100644 --- a/src/v/iceberg/BUILD +++ b/src/v/iceberg/BUILD @@ -502,7 +502,6 @@ redpanda_cc_library( ], implementation_deps = [ ":logger", - "//src/v/base", "@seastar", ], visibility = ["//visibility:public"], @@ -511,6 +510,7 @@ redpanda_cc_library( ":transform", ":transform_utils", ":unresolved_partition_spec", + "//src/v/base", "//src/v/container:chunked_vector", "//src/v/utils:named_type", ], @@ -991,6 +991,7 @@ redpanda_cc_library( visibility = ["//visibility:public"], deps = [ ":datatypes", + "//src/v/base", "//src/v/utils:fixed_string", ], ) @@ -1073,7 +1074,6 @@ redpanda_cc_library( "unresolved_partition_spec.h", ], implementation_deps = [ - "//src/v/base", "@fmt", ], visibility = ["//visibility:public"], @@ -1142,6 +1142,7 @@ redpanda_cc_library( visibility = ["//visibility:public"], deps = [ ":datatypes", + "//src/v/base", "//src/v/bytes:iobuf", "//src/v/container:chunked_vector", "//src/v/utils:uuid", diff --git a/src/v/iceberg/action.cc b/src/v/iceberg/action.cc index 7e35c0b1290ed..bccc04130a230 100644 --- a/src/v/iceberg/action.cc +++ b/src/v/iceberg/action.cc @@ -9,18 +9,4 @@ */ #include "iceberg/action.h" -namespace iceberg { - -std::ostream& operator<<(std::ostream& o, action::errc e) { - switch (e) { - using enum action::errc; - case unexpected_state: - return o << "action::errc::unexpected_state"; - case io_failed: - return o << "action::errc::io_failed"; - case shutting_down: - return o << "action::errc::shutting_down"; - } -} - -} // namespace iceberg +namespace iceberg {} // namespace iceberg diff --git a/src/v/iceberg/action.h b/src/v/iceberg/action.h index a06ca448f0f56..a4e18b99dfc56 100644 --- a/src/v/iceberg/action.h +++ b/src/v/iceberg/action.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include "iceberg/table_requirement.h" @@ -43,6 +44,15 @@ class action { virtual ~action() = default; }; -std::ostream& operator<<(std::ostream& o, action::errc e); +inline fmt::iterator format_to(action::errc e, fmt::iterator out) { + switch (e) { + case action::errc::unexpected_state: + return fmt::format_to(out, "action::errc::unexpected_state"); + case action::errc::io_failed: + return fmt::format_to(out, "action::errc::io_failed"); + case action::errc::shutting_down: + return fmt::format_to(out, "action::errc::shutting_down"); + } +} } // namespace iceberg diff --git a/src/v/iceberg/catalog_errors.h b/src/v/iceberg/catalog_errors.h index 5a7a1875c0d46..4f4dc84e18fbe 100644 --- a/src/v/iceberg/catalog_errors.h +++ b/src/v/iceberg/catalog_errors.h @@ -9,11 +9,11 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include -#include #include namespace iceberg { @@ -56,9 +56,8 @@ constexpr std::string_view to_string_view(catalog_errc e) { return "not_found"; } } - -inline std::ostream& operator<<(std::ostream& o, catalog_errc e) { - return o << to_string_view(e); +inline fmt::iterator format_to(catalog_errc e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); } /// Rich error for catalog describe, carrying a human-readable message diff --git a/src/v/iceberg/compatibility_types.cc b/src/v/iceberg/compatibility_types.cc index e3098f07f8673..80be85612a154 100644 --- a/src/v/iceberg/compatibility_types.cc +++ b/src/v/iceberg/compatibility_types.cc @@ -10,6 +10,8 @@ #include "iceberg/compatibility_types.h" +#include "base/format_to.h" + #include namespace iceberg { @@ -25,6 +27,10 @@ std::string_view to_string_view(type_promoted tp) { } } +fmt::iterator format_to(type_promoted tp, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(tp)); +} + std::string_view to_string_view(schema_evolution_errc ec) { switch (ec) { case schema_evolution_errc::type_mismatch: @@ -46,6 +52,10 @@ std::string_view to_string_view(schema_evolution_errc ec) { } } +fmt::iterator format_to(schema_evolution_errc ec, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(ec)); +} + schema_transform_state& operator+=(schema_transform_state& lhs, const schema_transform_state& rhs) { lhs.n_removed += rhs.n_removed; @@ -56,15 +66,3 @@ operator+=(schema_transform_state& lhs, const schema_transform_state& rhs) { } } // namespace iceberg - -auto fmt::formatter::format( - iceberg::schema_evolution_errc ec, format_context& ctx) const - -> format_context::iterator { - return formatter::format(iceberg::to_string_view(ec), ctx); -} - -auto fmt::formatter::format( - iceberg::type_promoted ec, format_context& ctx) const - -> format_context::iterator { - return formatter::format(iceberg::to_string_view(ec), ctx); -} diff --git a/src/v/iceberg/compatibility_types.h b/src/v/iceberg/compatibility_types.h index 895b51883ad2f..d62e6406c7d8d 100644 --- a/src/v/iceberg/compatibility_types.h +++ b/src/v/iceberg/compatibility_types.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" @@ -89,17 +90,9 @@ using schema_changed = ss::bool_class; using schema_evolution_result = checked; using ids_filled = ss::bool_class; -} // namespace iceberg - -template<> -struct fmt::formatter - : formatter { - auto format(iceberg::schema_evolution_errc d, format_context& ctx) const - -> format_context::iterator; -}; +std::string_view to_string_view(type_promoted); +fmt::iterator format_to(type_promoted, fmt::iterator); +std::string_view to_string_view(schema_evolution_errc); +fmt::iterator format_to(schema_evolution_errc, fmt::iterator); -template<> -struct fmt::formatter : formatter { - auto format(iceberg::type_promoted d, format_context& ctx) const - -> format_context::iterator; -}; +} // namespace iceberg diff --git a/src/v/iceberg/conversion/schema_avro.cc b/src/v/iceberg/conversion/schema_avro.cc index 085e3380d0cac..c66dcee7843bb 100644 --- a/src/v/iceberg/conversion/schema_avro.cc +++ b/src/v/iceberg/conversion/schema_avro.cc @@ -86,7 +86,7 @@ conversion_outcome> inner_field_type_from_avro(const avro::NodePtr& node, state& state) { if (is_recursive_type(state, node)) { return conversion_exception( - fmt::format("Unsupported recursive type: {}", *node)); + fmt::format("Unsupported recursive type: {}", fmt_streamed(*node))); } if (state.type_hierarchy.size() >= max_schema_depth) { return conversion_exception( @@ -184,7 +184,7 @@ inner_field_type_from_avro(const avro::NodePtr& node, state& state) { return conversion_exception( fmt::format( "AVRO_MAP is expected to be a string. Current key type: {}", - node->leafAt(0)->type())); + fmt_streamed(node->leafAt(0)->type()))); } auto value_t_result = field_type_from_avro(node->leafAt(1), state); if (value_t_result.has_error()) { @@ -284,8 +284,8 @@ field_type_from_avro(const avro::NodePtr& node, state& state) { fmt::format( "exception thrown while converting AVRO {} schema of type {} to " "iceberg - {}", - *node, - node->type(), + fmt_streamed(*node), + fmt_streamed(node->type()), std::current_exception())); } } diff --git a/src/v/iceberg/conversion/tests/iceberg_protobuf_tests.cc b/src/v/iceberg/conversion/tests/iceberg_protobuf_tests.cc index 266684036d7f6..269c63fd5b063 100644 --- a/src/v/iceberg/conversion/tests/iceberg_protobuf_tests.cc +++ b/src/v/iceberg/conversion/tests/iceberg_protobuf_tests.cc @@ -20,7 +20,6 @@ #include #include -#include #include #include #include diff --git a/src/v/iceberg/conversion/values_avro.cc b/src/v/iceberg/conversion/values_avro.cc index 4bbbe01cfc98d..b55deeffbc27c 100644 --- a/src/v/iceberg/conversion/values_avro.cc +++ b/src/v/iceberg/conversion/values_avro.cc @@ -31,11 +31,9 @@ convert_primitive(T v, avro::Type expected_type, const avro::NodePtr& node) { if (node->type() != expected_type) { return value_conversion_exception( fmt::format( - "Schema mismatch detected expected type {} got {}, current node: " - "{}", - expected_type, - node->type(), - node)); + "Schema mismatch detected expected type {} got {}", + static_cast(expected_type), + static_cast(node->type()))); } return ValueT{std::move(v)}; } diff --git a/src/v/iceberg/datatypes.cc b/src/v/iceberg/datatypes.cc index dd9501d615728..b4538ab33d965 100644 --- a/src/v/iceberg/datatypes.cc +++ b/src/v/iceberg/datatypes.cc @@ -98,7 +98,7 @@ ss::sstring format_nested_field_ptr_type(const iceberg::nested_field_ptr& ptr) { if (ptr == nullptr) { return "null"; } - return fmt::to_string(ptr->type); + return fmt::format("{}", ptr->type); } ss::sstring @@ -123,118 +123,70 @@ field_type make_copy(const field_type& type) { return std::visit(type_copying_visitor{}, type); } -std::ostream& operator<<(std::ostream& o, const boolean_type&) { - o << "boolean"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const int_type&) { - o << "int"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const long_type&) { - o << "long"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const float_type&) { - o << "float"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const double_type&) { - o << "double"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const decimal_type& t) { - o << fmt::format("decimal({}, {})", t.precision, t.scale); - return o; -} - -std::ostream& operator<<(std::ostream& o, const date_type&) { - o << "date"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const time_type&) { - o << "time"; - return o; -} - -std::ostream& operator<<(std::ostream& o, const timestamp_type&) { - o << "timestamp"; - return o; +fmt::iterator format_struct_type(fmt::iterator it, const struct_type& st) { + it = fmt::format_to(it, "struct["); + if (!st.fields.empty()) { + auto fit = st.fields.begin(); + it = fmt::format_to(it, "{}", format_nested_field_ptr_name_type(*fit)); + ++fit; + for (; fit != st.fields.end(); ++fit) { + it = fmt::format_to( + it, ", {}", format_nested_field_ptr_name_type(*fit)); + } + } + return fmt::format_to(it, "]"); } -std::ostream& operator<<(std::ostream& o, const timestamptz_type&) { - o << "timestamptz"; - return o; +fmt::iterator format_list_type(fmt::iterator it, const list_type& lt) { + return fmt::format_to( + it, "list<{}>", format_nested_field_ptr_type(lt.element_field)); } -std::ostream& operator<<(std::ostream& o, const string_type&) { - o << "string"; - return o; +fmt::iterator format_map_type(fmt::iterator it, const map_type& mt) { + return fmt::format_to( + it, + "map<{},{}>", + format_nested_field_ptr_type(mt.key_field), + format_nested_field_ptr_type(mt.value_field)); } -std::ostream& operator<<(std::ostream& o, const uuid_type&) { - o << "uuid"; - return o; +fmt::iterator format_nested_field(fmt::iterator it, const nested_field& nf) { + return fmt::format_to( + it, + "{{id: {}, name: {}, required: {}, type: {}}}", + nf.id, + nf.name, + nf.required, + nf.type); } -std::ostream& operator<<(std::ostream& o, const fixed_type& t) { - // NOTE: square brackets to match how fixed type is serialized as JSON, - // though this matching isn't necessarily important for operator<<. - o << fmt::format("fixed[{}]", t.length); - return o; +fmt::iterator format_primitive_type(fmt::iterator it, const primitive_type& t) { + return std::visit( + [it](const auto& v) { return fmt::format_to(it, "{}", v); }, t); } -std::ostream& operator<<(std::ostream& o, const binary_type&) { - o << "binary"; - return o; +fmt::iterator format_field_type(fmt::iterator it, const field_type& t) { + return std::visit( + [it](const auto& v) { return fmt::format_to(it, "{}", v); }, t); } std::ostream& operator<<(std::ostream& o, const struct_type& st) { - /** - * Struct is printed as struct[field_1_name,...] - */ - fmt::print(o, "struct["); - if (!st.fields.empty()) { - auto it = st.fields.begin(); - fmt::print(o, "{}", format_nested_field_ptr_name_type(*it)); - ++it; - for (; it != st.fields.end(); ++it) { - fmt::print(o, ", {}", format_nested_field_ptr_name_type(*it)); - } - } - - fmt::print(o, "]"); + fmt::print(o, "{}", st); return o; } std::ostream& operator<<(std::ostream& o, const list_type& lt) { - fmt::print(o, "list<{}>", format_nested_field_ptr_type(lt.element_field)); + fmt::print(o, "{}", lt); return o; } std::ostream& operator<<(std::ostream& o, const map_type& mt) { - fmt::print( - o, - "map<{},{}>", - format_nested_field_ptr_type(mt.key_field), - format_nested_field_ptr_type(mt.value_field)); + fmt::print(o, "{}", mt); return o; } std::ostream& operator<<(std::ostream& o, const nested_field& nf) { - fmt::print( - o, - "{{id: {}, name: {}, required: {}, type: {}}}", - nf.id, - nf.name, - nf.required, - nf.type); + fmt::print(o, "{}", nf); return o; } @@ -289,26 +241,13 @@ bool operator==(const map_type& lhs, const map_type& rhs) { return true; } -namespace { -struct ostream_visitor { - explicit ostream_visitor(std::ostream& o) - : os(o) {} - std::ostream& os; - - template - void operator()(const T& v) const { - os << v; - } -}; -} // namespace - std::ostream& operator<<(std::ostream& o, const primitive_type& t) { - std::visit(ostream_visitor{o}, t); + fmt::print(o, "{}", t); return o; } std::ostream& operator<<(std::ostream& o, const field_type& t) { - std::visit(ostream_visitor{o}, t); + fmt::print(o, "{}", t); return o; } diff --git a/src/v/iceberg/datatypes.h b/src/v/iceberg/datatypes.h index 54d473f866d8e..723c5c99a3aa7 100644 --- a/src/v/iceberg/datatypes.h +++ b/src/v/iceberg/datatypes.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" #include "utils/named_type.h" @@ -20,25 +21,79 @@ namespace iceberg { -struct boolean_type {}; -struct int_type {}; -struct long_type {}; -struct float_type {}; -struct double_type {}; +struct boolean_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "boolean"); + } +}; +struct int_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "int"); + } +}; +struct long_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "long"); + } +}; +struct float_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "float"); + } +}; +struct double_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "double"); + } +}; struct decimal_type { uint32_t precision; uint32_t scale; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "decimal({}, {})", precision, scale); + } +}; +struct date_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "date"); + } +}; +struct time_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "time"); + } +}; +struct timestamp_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "timestamp"); + } +}; +struct timestamptz_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "timestamptz"); + } +}; +struct string_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "string"); + } +}; +struct uuid_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "uuid"); + } }; -struct date_type {}; -struct time_type {}; -struct timestamp_type {}; -struct timestamptz_type {}; -struct string_type {}; -struct uuid_type {}; struct fixed_type { uint64_t length; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "fixed[{}]", length); + } +}; +struct binary_type { + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "binary"); + } }; -struct binary_type {}; using primitive_type = std::variant< boolean_type, int_type, @@ -65,20 +120,12 @@ bool operator==(const field_type& lhs, const field_type& rhs); field_type make_copy(const field_type&); primitive_type make_copy(const primitive_type&); -std::ostream& operator<<(std::ostream&, const boolean_type&); -std::ostream& operator<<(std::ostream&, const int_type&); -std::ostream& operator<<(std::ostream&, const long_type&); -std::ostream& operator<<(std::ostream&, const float_type&); -std::ostream& operator<<(std::ostream&, const double_type&); -std::ostream& operator<<(std::ostream&, const decimal_type&); -std::ostream& operator<<(std::ostream&, const date_type&); -std::ostream& operator<<(std::ostream&, const time_type&); -std::ostream& operator<<(std::ostream&, const timestamp_type&); -std::ostream& operator<<(std::ostream&, const timestamptz_type&); -std::ostream& operator<<(std::ostream&, const string_type&); -std::ostream& operator<<(std::ostream&, const uuid_type&); -std::ostream& operator<<(std::ostream&, const fixed_type&); -std::ostream& operator<<(std::ostream&, const binary_type&); +fmt::iterator format_struct_type(fmt::iterator it, const struct_type&); +fmt::iterator format_list_type(fmt::iterator it, const list_type&); +fmt::iterator format_map_type(fmt::iterator it, const map_type&); +fmt::iterator format_primitive_type(fmt::iterator it, const primitive_type&); +fmt::iterator format_field_type(fmt::iterator it, const field_type&); + std::ostream& operator<<(std::ostream&, const struct_type&); std::ostream& operator<<(std::ostream&, const list_type&); std::ostream& operator<<(std::ostream&, const map_type&); @@ -184,4 +231,75 @@ struct nested_field { friend std::ostream& operator<<(std::ostream&, const nested_field&); }; +fmt::iterator format_nested_field(fmt::iterator it, const nested_field&); + } // namespace iceberg + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const iceberg::struct_type& v, fmt::format_context& ctx) const { + fmt::memory_buffer buf; + iceberg::format_struct_type(fmt::appender(buf), v); + return std::copy(buf.begin(), buf.end(), ctx.out()); + } +}; +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const iceberg::list_type& v, fmt::format_context& ctx) const { + fmt::memory_buffer buf; + iceberg::format_list_type(fmt::appender(buf), v); + return std::copy(buf.begin(), buf.end(), ctx.out()); + } +}; +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const iceberg::map_type& v, fmt::format_context& ctx) const { + fmt::memory_buffer buf; + iceberg::format_map_type(fmt::appender(buf), v); + return std::copy(buf.begin(), buf.end(), ctx.out()); + } +}; +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto + format(const iceberg::primitive_type& v, fmt::format_context& ctx) const { + fmt::memory_buffer buf; + iceberg::format_primitive_type(fmt::appender(buf), v); + return std::copy(buf.begin(), buf.end(), ctx.out()); + } +}; +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const iceberg::field_type& v, fmt::format_context& ctx) const { + fmt::memory_buffer buf; + iceberg::format_field_type(fmt::appender(buf), v); + return std::copy(buf.begin(), buf.end(), ctx.out()); + } +}; +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto + format(const iceberg::nested_field& v, fmt::format_context& ctx) const { + fmt::memory_buffer buf; + iceberg::format_nested_field(fmt::appender(buf), v); + return std::copy(buf.begin(), buf.end(), ctx.out()); + } +}; diff --git a/src/v/iceberg/json_utils.cc b/src/v/iceberg/json_utils.cc index e2e865ba16a89..83ae446867b6d 100644 --- a/src/v/iceberg/json_utils.cc +++ b/src/v/iceberg/json_utils.cc @@ -34,8 +34,8 @@ parse_string_map(const json::Value& map_json, std::string_view member_name) { "Expected '{}' field to be a string map. Current type " "map<{},{}>", member_name, - property.name.GetType(), - property.value.GetType())); + static_cast(property.name.GetType()), + static_cast(property.value.GetType()))); } ret.emplace(property.name.GetString(), property.value.GetString()); @@ -82,7 +82,7 @@ parse_required_array(const json::Value& v, std::string_view member_name) { fmt::format( "Expected array for field '{}': {}", member_name, - array_json.GetType())); + static_cast(array_json.GetType()))); } return array_json.GetArray(); } @@ -97,7 +97,9 @@ parse_optional_array(const json::Value& v, std::string_view member_name) { if (!val.IsArray()) { throw std::invalid_argument( fmt::format( - "Expected array for field '{}': {}", member_name, val.GetType())); + "Expected array for field '{}': {}", + member_name, + static_cast(val.GetType()))); } return val.GetArray(); } @@ -110,7 +112,7 @@ parse_required_object(const json::Value& v, std::string_view member_name) { fmt::format( "Expected object for field '{}': {}", member_name, - obj_json.GetType())); + static_cast(obj_json.GetType()))); } return obj_json.GetObject(); } @@ -125,7 +127,9 @@ parse_optional_object(const json::Value& v, std::string_view member_name) { if (!val.IsObject()) { throw std::invalid_argument( fmt::format( - "Expected object for field '{}': {}", member_name, val.GetType())); + "Expected object for field '{}': {}", + member_name, + static_cast(val.GetType()))); } return val.GetObject(); } diff --git a/src/v/iceberg/manifest_avro.cc b/src/v/iceberg/manifest_avro.cc index dfbd7709062ef..1aa01dccf485c 100644 --- a/src/v/iceberg/manifest_avro.cc +++ b/src/v/iceberg/manifest_avro.cc @@ -102,7 +102,7 @@ metadata_to_map(const manifest_metadata& meta) { {"schema", to_json_str(meta.schema)}, {"content", std::string{content_type_to_str(meta.manifest_content_type)}}, {"partition-spec", to_json_str(meta.partition_spec.fields)}, - {"partition-spec-id", fmt::to_string(meta.partition_spec.spec_id())}, + {"partition-spec-id", fmt::format("{}", meta.partition_spec.spec_id())}, {"format-version", std::string{format_to_str(meta.format_version)}}}; } // TODO: make DataFileReader::getMetadata const! diff --git a/src/v/iceberg/partition.cc b/src/v/iceberg/partition.cc index 4bbd933b165e9..5e3cfe2e56465 100644 --- a/src/v/iceberg/partition.cc +++ b/src/v/iceberg/partition.cc @@ -16,20 +16,18 @@ namespace iceberg { -std::ostream& operator<<(std::ostream& o, const partition_field& f) { - fmt::print( - o, +fmt::iterator partition_field::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{source_id: {}, transform: {}, field_id: {}, name: {}}}", - f.source_id, - f.transform, - f.field_id, - f.name); - return o; + source_id, + transform, + field_id, + name); } -std::ostream& operator<<(std::ostream& o, const partition_spec& ps) { - fmt::print(o, "{{spec_id: {}, fields: {}}}", ps.spec_id, ps.fields); - return o; +fmt::iterator partition_spec::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{spec_id: {}, fields: {}}}", spec_id, fields); } std::optional partition_spec::resolve( diff --git a/src/v/iceberg/partition.h b/src/v/iceberg/partition.h index 3ac045c9a9437..6800a4a589fe4 100644 --- a/src/v/iceberg/partition.h +++ b/src/v/iceberg/partition.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "container/chunked_vector.h" #include "iceberg/datatypes.h" #include "iceberg/transform.h" @@ -26,10 +27,10 @@ struct partition_field { ss::sstring name; transform transform; + fmt::iterator format_to(fmt::iterator it) const; + friend bool operator==(const partition_field&, const partition_field&) = default; - - friend std::ostream& operator<<(std::ostream&, const partition_field&); }; struct partition_spec { @@ -47,11 +48,11 @@ struct partition_spec { const partition_field* get_field(nested_field::id_t source_id) const; + fmt::iterator format_to(fmt::iterator it) const; + friend bool operator==(const partition_spec&, const partition_spec&) = default; - friend std::ostream& operator<<(std::ostream&, const partition_spec&); - partition_spec copy() const { return { .spec_id = spec_id, diff --git a/src/v/iceberg/rest_client/error.cc b/src/v/iceberg/rest_client/error.cc index 421b160b4e57a..eb8d13c783221 100644 --- a/src/v/iceberg/rest_client/error.cc +++ b/src/v/iceberg/rest_client/error.cc @@ -9,52 +9,3 @@ */ #include "iceberg/rest_client/error.h" - -namespace { -struct domain_error_printing_visitor { - fmt::format_context* ctx; - - auto operator()(const http::url_build_error& err) const { - return fmt::format_to(ctx->out(), "url_build_error: {}", err); - } - - auto operator()(const iceberg::rest_client::json_parse_error& err) const { - return fmt::format_to( - ctx->out(), - "json_parse_error: context: {}, error: {}", - err.context, - err.error); - } - - auto operator()(const iceberg::rest_client::http_call_error& err) const { - return fmt::format_to(ctx->out(), "{}", err); - } - - auto operator()(const iceberg::rest_client::retries_exhausted& err) const { - auto it = fmt::format_to( - ctx->out(), - "retries_exhausted:[reasons=[{}]", - fmt::join(err.reasons, ", ")); - if (err.last_error.has_value()) { - it = fmt::format_to(it, ", last_error={}", *err.last_error); - } - return fmt::format_to(it, "]"); - } -}; -} // namespace - -auto fmt::formatter::format( - const iceberg::rest_client::http_call_error& err, - fmt::format_context& ctx) const -> decltype(ctx.out()) { - return std::visit( - [&ctx](const auto& v) { - return fmt::format_to(ctx.out(), "http_call_error: {}", v); - }, - err); -} - -auto fmt::formatter::format( - const iceberg::rest_client::domain_error& err, fmt::format_context& ctx) const - -> decltype(ctx.out()) { - return std::visit(domain_error_printing_visitor{.ctx = &ctx}, err); -} diff --git a/src/v/iceberg/rest_client/error.h b/src/v/iceberg/rest_client/error.h index 122609b50b421..954098791d359 100644 --- a/src/v/iceberg/rest_client/error.h +++ b/src/v/iceberg/rest_client/error.h @@ -10,6 +10,7 @@ #pragma once +#include "base/external_fmt.h" #include "base/format_to.h" #include "base/seastarx.h" #include "http/request_builder.h" @@ -21,6 +22,8 @@ #include #include +#include +#include namespace iceberg::rest_client { @@ -72,6 +75,9 @@ constexpr std::string_view to_string_view(error_kind r) { return "timeout"; } } +inline fmt::iterator format_to(error_kind r, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(r)); +} constexpr bool is_retriable(error_kind r) { switch (r) { @@ -109,14 +115,6 @@ template using expected = tl::expected; } // namespace iceberg::rest_client -template<> -struct fmt::formatter - : fmt::formatter { - auto format( - const iceberg::rest_client::domain_error&, fmt::format_context& ctx) const - -> decltype(ctx.out()); -}; - template<> struct fmt::formatter : fmt::formatter { @@ -132,6 +130,53 @@ template<> struct fmt::formatter : fmt::formatter { auto format( - const iceberg::rest_client::http_call_error&, - fmt::format_context& ctx) const -> decltype(ctx.out()); + const iceberg::rest_client::http_call_error& err, + fmt::format_context& ctx) const -> decltype(ctx.out()) { + return std::visit( + [&ctx](const auto& value) { + return fmt::format_to(ctx.out(), "http_call_error: {}", value); + }, + err); + } +}; + +template<> +struct fmt::formatter + : fmt::formatter { + auto format( + const iceberg::rest_client::domain_error& err, + fmt::format_context& ctx) const -> decltype(ctx.out()) { + return std::visit( + [&ctx](const auto& value) -> decltype(ctx.out()) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return fmt::format_to( + ctx.out(), "url_build_error: {}", value); + } else if constexpr ( + std::is_same_v) { + return fmt::format_to( + ctx.out(), + "json_parse_error: context: {}, error: {}", + value.context, + value.error); + } else if constexpr ( + std::is_same_v) { + return fmt::format_to(ctx.out(), "{}", value); + } else if constexpr ( + std::is_same_v) { + auto it = fmt::format_to( + ctx.out(), + "retries_exhausted:[reasons=[{}]", + fmt::join(value.reasons, ", ")); + if (value.last_error.has_value()) { + it = fmt::format_to( + it, ", last_error={}", *value.last_error); + } + return fmt::format_to(it, "]"); + } else { + return fmt::format_to(ctx.out(), "aborted_error: {}", value); + } + }, + err); + } }; diff --git a/src/v/iceberg/schema_avro.cc b/src/v/iceberg/schema_avro.cc index 381aae6cb664a..82b68564edc41 100644 --- a/src/v/iceberg/schema_avro.cc +++ b/src/v/iceberg/schema_avro.cc @@ -25,7 +25,7 @@ namespace { avro::CustomAttributes field_attrs(int field_id) { avro::CustomAttributes attrs; - attrs.addAttribute("field-id", fmt::to_string(field_id)); + attrs.addAttribute("field-id", fmt::format("{}", field_id)); return attrs; } diff --git a/src/v/iceberg/snapshot_json.cc b/src/v/iceberg/snapshot_json.cc index cfb98dafb7239..3cfb273d2b3fa 100644 --- a/src/v/iceberg/snapshot_json.cc +++ b/src/v/iceberg/snapshot_json.cc @@ -198,7 +198,7 @@ void rjson_serialize(iceberg::json_writer& w, const iceberg::snapshot& s) { return; } w.Key(ss::sstring(metric_name)); - w.String(fmt::to_string(*metric)); + w.String(fmt::format("{}", *metric)); // TODO: is it important to make sure we don't double-serialize if // it's in `other`? diff --git a/src/v/iceberg/table_identifier.cc b/src/v/iceberg/table_identifier.cc index 8fd038868a639..22dc2e5e45c52 100644 --- a/src/v/iceberg/table_identifier.cc +++ b/src/v/iceberg/table_identifier.cc @@ -9,12 +9,9 @@ */ #include "iceberg/table_identifier.h" -#include -#include - namespace iceberg { -std::ostream& operator<<(std::ostream& o, const table_identifier& id) { - fmt::print(o, "{{ns: {}, table: {}}}", fmt::join(id.ns, "/"), id.table); - return o; +fmt::iterator table_identifier::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ns: {}, table: {}}}", fmt::join(ns, "/"), table); } } // namespace iceberg diff --git a/src/v/iceberg/table_identifier.h b/src/v/iceberg/table_identifier.h index 7adbcc9508b53..0044e77aa3476 100644 --- a/src/v/iceberg/table_identifier.h +++ b/src/v/iceberg/table_identifier.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" @@ -28,9 +29,10 @@ struct table_identifier { }; } + fmt::iterator format_to(fmt::iterator it) const; + bool operator==(const table_identifier& other) const = default; }; -std::ostream& operator<<(std::ostream& o, const table_identifier& id); } // namespace iceberg namespace std { diff --git a/src/v/iceberg/table_io.cc b/src/v/iceberg/table_io.cc index 0585041bb55ac..3d44555a5e6cf 100644 --- a/src/v/iceberg/table_io.cc +++ b/src/v/iceberg/table_io.cc @@ -51,7 +51,7 @@ ss::future> table_io::upload_version_hint(const version_hint_path& path, int version) { return upload_object( path(), version, "iceberg::version_hint", [](int v) { - return iobuf::from(fmt::to_string(v)); + return iobuf::from(fmt::format("{}", v)); }); } diff --git a/src/v/iceberg/table_metadata_json.cc b/src/v/iceberg/table_metadata_json.cc index 3216f0d53efef..50268759f1d61 100644 --- a/src/v/iceberg/table_metadata_json.cc +++ b/src/v/iceberg/table_metadata_json.cc @@ -245,7 +245,7 @@ void rjson_serialize( w.Key("format-version"); w.Int(iceberg::format_version_to_int(m.format_version)); w.Key("table-uuid"); - w.String(fmt::to_string(m.table_uuid)); + w.String(fmt::format("{}", m.table_uuid)); w.Key("location"); w.String(m.location()); w.Key("last-sequence-number"); diff --git a/src/v/iceberg/table_requirement_json.cc b/src/v/iceberg/table_requirement_json.cc index e91c8da5e7a67..45d64ba39cf7d 100644 --- a/src/v/iceberg/table_requirement_json.cc +++ b/src/v/iceberg/table_requirement_json.cc @@ -39,7 +39,7 @@ struct requirement_json_serializing_visitor { void operator()(const iceberg::table_requirement::assert_table_uuid& req) { serialize_type("assert-table-uuid"); w.Key("uuid"); - w.String(fmt::to_string(req.uuid)); + w.String(fmt::format("{}", req.uuid)); } void operator()( diff --git a/src/v/iceberg/tests/datatypes_test.cc b/src/v/iceberg/tests/datatypes_test.cc index 98a90c43fa174..9b48534d268e4 100644 --- a/src/v/iceberg/tests/datatypes_test.cc +++ b/src/v/iceberg/tests/datatypes_test.cc @@ -302,7 +302,7 @@ TEST(DatatypeTest, TestStruct) { auto t6 = struct_single(0, "foo", field_required::yes, boolean_type{}); std::get(t6).fields.push_back( nested_field::create(2, "bar", field_required::yes, int_type{})); - ASSERT_EQ("struct[0:foo, 2:bar]", fmt::to_string(t6)); + ASSERT_EQ("struct[0:foo, 2:bar]", fmt::format("{}", t6)); } TEST(DatatypeTest, TestBasicCopy) { diff --git a/src/v/iceberg/tests/values_test.cc b/src/v/iceberg/tests/values_test.cc index 74d97f424f14a..d01a17659ccd4 100644 --- a/src/v/iceberg/tests/values_test.cc +++ b/src/v/iceberg/tests/values_test.cc @@ -258,171 +258,164 @@ TEST(ValuesTest, TestNestedValueEquality) { } TEST(ValuesTest, TestPrimitiveTypesOStream) { - EXPECT_STREQ("boolean(true)", fmt::to_string(boolean_value{true}).c_str()); - EXPECT_STREQ( - "boolean(false)", fmt::to_string(boolean_value{false}).c_str()); + EXPECT_EQ("boolean(true)", fmt::format("{}", boolean_value{true})); + EXPECT_EQ("boolean(false)", fmt::format("{}", boolean_value{false})); - EXPECT_STREQ( + EXPECT_EQ( "int(-2147483648)", - fmt::to_string(int_value{std::numeric_limits::min()}).c_str()); - EXPECT_STREQ( + fmt::format("{}", int_value{std::numeric_limits::min()})); + EXPECT_EQ( "int(2147483647)", - fmt::to_string(int_value{std::numeric_limits::max()}).c_str()); + fmt::format("{}", int_value{std::numeric_limits::max()})); - EXPECT_STREQ( + EXPECT_EQ( "long(-9223372036854775808)", - fmt::to_string(long_value{std::numeric_limits::min()}).c_str()); - EXPECT_STREQ( + fmt::format("{}", long_value{std::numeric_limits::min()})); + EXPECT_EQ( "long(9223372036854775807)", - fmt::to_string(long_value{std::numeric_limits::max()}).c_str()); + fmt::format("{}", long_value{std::numeric_limits::max()})); - EXPECT_STREQ( + EXPECT_EQ( "double(-1.7976931348623157e+308)", - fmt::to_string(double_value{-std::numeric_limits::max()}) - .c_str()); - EXPECT_STREQ( + fmt::format("{}", double_value{-std::numeric_limits::max()})); + EXPECT_EQ( "double(2.2250738585072014e-308)", - fmt::to_string(double_value{std::numeric_limits::min()}).c_str()); - EXPECT_STREQ( + fmt::format("{}", double_value{std::numeric_limits::min()})); + EXPECT_EQ( "double(1.7976931348623157e+308)", - fmt::to_string(double_value{std::numeric_limits::max()}).c_str()); + fmt::format("{}", double_value{std::numeric_limits::max()})); - EXPECT_STREQ( + EXPECT_EQ( "decimal(-170141183460469231731687303715884105728)", - fmt::to_string(decimal_value{std::numeric_limits::min()}) - .c_str()); - EXPECT_STREQ( + fmt::format( + "{}", decimal_value{std::numeric_limits::min()})); + EXPECT_EQ( "decimal(170141183460469231731687303715884105727)", - fmt::to_string(decimal_value{std::numeric_limits::max()}) - .c_str()); + fmt::format( + "{}", decimal_value{std::numeric_limits::max()})); - EXPECT_STREQ( + EXPECT_EQ( "date(-2147483648)", - fmt::to_string(date_value{std::numeric_limits::min()}).c_str()); - EXPECT_STREQ( + fmt::format("{}", date_value{std::numeric_limits::min()})); + EXPECT_EQ( "date(2147483647)", - fmt::to_string(date_value{std::numeric_limits::max()}).c_str()); + fmt::format("{}", date_value{std::numeric_limits::max()})); - EXPECT_STREQ( + EXPECT_EQ( "time(-9223372036854775808)", - fmt::to_string(time_value{std::numeric_limits::min()}).c_str()); - EXPECT_STREQ( + fmt::format("{}", time_value{std::numeric_limits::min()})); + EXPECT_EQ( "time(9223372036854775807)", - fmt::to_string(time_value{std::numeric_limits::max()}).c_str()); - EXPECT_STREQ( + fmt::format("{}", time_value{std::numeric_limits::max()})); + EXPECT_EQ( "timestamp(-9223372036854775808)", - fmt::to_string(timestamp_value{std::numeric_limits::min()}) - .c_str()); - EXPECT_STREQ( + fmt::format("{}", timestamp_value{std::numeric_limits::min()})); + EXPECT_EQ( "timestamp(9223372036854775807)", - fmt::to_string(timestamp_value{std::numeric_limits::max()}) - .c_str()); - EXPECT_STREQ( + fmt::format("{}", timestamp_value{std::numeric_limits::max()})); + EXPECT_EQ( "timestamptz(-9223372036854775808)", - fmt::to_string(timestamptz_value{std::numeric_limits::min()}) - .c_str()); - EXPECT_STREQ( + fmt::format("{}", timestamptz_value{std::numeric_limits::min()})); + EXPECT_EQ( "timestamptz(9223372036854775807)", - fmt::to_string(timestamptz_value{std::numeric_limits::max()}) - .c_str()); + fmt::format("{}", timestamptz_value{std::numeric_limits::max()})); - EXPECT_STREQ("string(\"\")", fmt::to_string(string_value{}).c_str()); - EXPECT_STREQ( + EXPECT_EQ("string(\"\")", fmt::format("{}", string_value{})); + EXPECT_EQ( "string(\"0000000000000000\")", - fmt::to_string(string_value{iobuf::from("0000000000000000")}).c_str()); - EXPECT_STREQ( + fmt::format("{}", string_value{iobuf::from("0000000000000000")})); + EXPECT_EQ( "string(\"0000000000000000...\")", - fmt::to_string(string_value{iobuf::from("00000000000000000")}).c_str()); + fmt::format("{}", string_value{iobuf::from("00000000000000000")})); - EXPECT_STREQ( + EXPECT_EQ( "uuid(00000000-0000-0000-0000-000000000000)", - fmt::to_string(uuid_value{}).c_str()); - EXPECT_STREQ( + fmt::format("{}", uuid_value{})); + EXPECT_EQ( "uuid(deadbeef-0000-0000-0000-000000000000)", - fmt::to_string( - uuid_value{uuid_t::from_string("deadbeef-0000-0000-0000-000000000000")}) - .c_str()); + fmt::format( + "{}", + uuid_value{ + uuid_t::from_string("deadbeef-0000-0000-0000-000000000000")})); - EXPECT_STREQ( - "binary(size_bytes=0)", fmt::to_string(binary_value{}).c_str()); - EXPECT_STREQ( + EXPECT_EQ("binary(size_bytes=0)", fmt::format("{}", binary_value{})); + EXPECT_EQ( "binary(size_bytes=16)", - fmt::to_string(binary_value{iobuf::from("0000000000000000")}).c_str()); + fmt::format("{}", binary_value{iobuf::from("0000000000000000")})); - EXPECT_STREQ("fixed(size_bytes=0)", fmt::to_string(fixed_value{}).c_str()); - EXPECT_STREQ( + EXPECT_EQ("fixed(size_bytes=0)", fmt::format("{}", fixed_value{})); + EXPECT_EQ( "fixed(size_bytes=16)", - fmt::to_string(fixed_value{iobuf::from("0000000000000000")}).c_str()); + fmt::format("{}", fixed_value{iobuf::from("0000000000000000")})); } TEST(ValuesTest, TestStructOStream) { struct_value val; - EXPECT_STREQ("struct{}", fmt::to_string(val).c_str()); + EXPECT_EQ("struct{}", fmt::format("{}", val)); val.fields.emplace_back(std::nullopt); - EXPECT_STREQ("struct{none, }", fmt::to_string(val).c_str()); + EXPECT_EQ("struct{none, }", fmt::format("{}", val)); val.fields.emplace_back(int_value{1}); - EXPECT_STREQ("struct{none, int(1), }", fmt::to_string(val).c_str()); + EXPECT_EQ("struct{none, int(1), }", fmt::format("{}", val)); val.fields.emplace_back(long_value{1}); val.fields.emplace_back(long_value{1}); val.fields.emplace_back(long_value{1}); - EXPECT_STREQ( + EXPECT_EQ( "struct{none, int(1), long(1), long(1), long(1), }", - fmt::to_string(val).c_str()); + fmt::format("{}", val)); val.fields.emplace_back(int_value{1}); - EXPECT_STREQ( + EXPECT_EQ( "struct{none, int(1), long(1), long(1), long(1), ...}", - fmt::to_string(val).c_str()); + fmt::format("{}", val)); } TEST(ValuesTest, TestListOStream) { list_value val; - EXPECT_STREQ("list{}", fmt::to_string(val).c_str()); + EXPECT_EQ("list{}", fmt::format("{}", val)); val.elements.emplace_back(std::nullopt); - EXPECT_STREQ("list{none, }", fmt::to_string(val).c_str()); + EXPECT_EQ("list{none, }", fmt::format("{}", val)); val.elements.emplace_back(int_value{1}); - EXPECT_STREQ("list{none, int(1), }", fmt::to_string(val).c_str()); + EXPECT_EQ("list{none, int(1), }", fmt::format("{}", val)); val.elements.emplace_back(int_value{1}); val.elements.emplace_back(int_value{1}); val.elements.emplace_back(int_value{1}); - EXPECT_STREQ( - "list{none, int(1), int(1), int(1), int(1), }", - fmt::to_string(val).c_str()); + EXPECT_EQ( + "list{none, int(1), int(1), int(1), int(1), }", fmt::format("{}", val)); val.elements.emplace_back(int_value{1}); - EXPECT_STREQ( + EXPECT_EQ( "list{none, int(1), int(1), int(1), int(1), ...}", - fmt::to_string(val).c_str()); + fmt::format("{}", val)); } TEST(ValuesTest, TestMapOStream) { map_value val; - EXPECT_STREQ("map{}", fmt::to_string(val).c_str()); + EXPECT_EQ("map{}", fmt::format("{}", val)); val.kvs.emplace_back(kv_value{int_value{0}, std::nullopt}); - EXPECT_STREQ("map{(k=int(0), v=none), }", fmt::to_string(val).c_str()); + EXPECT_EQ("map{(k=int(0), v=none), }", fmt::format("{}", val)); val.kvs.emplace_back(kv_value{int_value{0}, int_value{1}}); - EXPECT_STREQ( + EXPECT_EQ( "map{(k=int(0), v=none), (k=int(0), v=int(1)), }", - fmt::to_string(val).c_str()); + fmt::format("{}", val)); val.kvs.emplace_back(kv_value{int_value{0}, std::nullopt}); val.kvs.emplace_back(kv_value{int_value{0}, std::nullopt}); val.kvs.emplace_back(kv_value{int_value{0}, std::nullopt}); - EXPECT_STREQ( + EXPECT_EQ( "map{(k=int(0), v=none), (k=int(0), v=int(1)), (k=int(0), v=none), " "(k=int(0), v=none), (k=int(0), v=none), }", - fmt::to_string(val).c_str()); + fmt::format("{}", val)); val.kvs.emplace_back(kv_value{int_value{0}, std::nullopt}); - EXPECT_STREQ( + EXPECT_EQ( "map{(k=int(0), v=none), (k=int(0), v=int(1)), (k=int(0), v=none), " "(k=int(0), v=none), (k=int(0), v=none), ...}", - fmt::to_string(val).c_str()); + fmt::format("{}", val)); } TEST(ValuesTest, TestValueOStream) { auto schema_field_type = test_nested_schema_type(); auto zero_val = tests::make_value({.max_elements = 1}, schema_field_type); - EXPECT_STREQ( + EXPECT_EQ( "struct{string(\"\"), int(0), boolean(false), list{string(\"\"), }, " "map{(k=string(\"\"), v=map{(k=string(\"\"), v=int(0)), }), }, ...}", - fmt::to_string(zero_val).c_str()); + fmt::format("{}", zero_val)); } TEST(ValuesTest, TestHashContainerStructs) { diff --git a/src/v/iceberg/transform.cc b/src/v/iceberg/transform.cc index be93348ab8129..5ff4328c4970f 100644 --- a/src/v/iceberg/transform.cc +++ b/src/v/iceberg/transform.cc @@ -35,23 +35,6 @@ struct transform_comparison_visitor { } }; -struct print_visitor { - std::ostream& os; - - void operator()(const identity_transform&) { os << "identity"; } - void operator()(const bucket_transform& bt) { - fmt::print(os, "bucket({})", bt.n); - } - void operator()(const truncate_transform& tt) { - fmt::print(os, "truncate({})", tt.length); - } - void operator()(const year_transform&) { os << "year"; } - void operator()(const month_transform&) { os << "month"; } - void operator()(const day_transform&) { os << "day"; } - void operator()(const hour_transform&) { os << "hour"; } - void operator()(const void_transform&) { os << "void"; } -}; - } // namespace bool operator==(const transform& lhs, const transform& rhs) { @@ -59,7 +42,7 @@ bool operator==(const transform& lhs, const transform& rhs) { } std::ostream& operator<<(std::ostream& os, const transform& t) { - std::visit(print_visitor{os}, t); + fmt::print(os, "{}", t); return os; } diff --git a/src/v/iceberg/transform.h b/src/v/iceberg/transform.h index 71c74aea51758..b96dda867825b 100644 --- a/src/v/iceberg/transform.h +++ b/src/v/iceberg/transform.h @@ -9,39 +9,71 @@ */ #pragma once +#include "base/format_to.h" #include "utils/fixed_string.h" #include -#include #include namespace iceberg { struct identity_transform { static constexpr fixed_string key{"identity"}; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "identity"); + } }; struct bucket_transform { static constexpr fixed_string key{"bucket"}; uint32_t n; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "bucket({})", n); + } }; struct truncate_transform { static constexpr fixed_string key{"truncate"}; uint32_t length; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "truncate({})", length); + } }; struct year_transform { static constexpr fixed_string key{"year"}; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "year"); + } }; struct month_transform { static constexpr fixed_string key{"month"}; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "month"); + } }; struct day_transform { static constexpr fixed_string key{"day"}; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "day"); + } }; struct hour_transform { static constexpr fixed_string key{"hour"}; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "hour"); + } }; struct void_transform { static constexpr fixed_string key{"void"}; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "void"); + } }; using transform = std::variant< @@ -54,7 +86,18 @@ using transform = std::variant< hour_transform, void_transform>; bool operator==(const transform& lhs, const transform& rhs); - std::ostream& operator<<(std::ostream&, const transform&); } // namespace iceberg + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const iceberg::transform& t, fmt::format_context& ctx) const { + return std::visit( + [&ctx](const auto& v) { return fmt::format_to(ctx.out(), "{}", v); }, + t); + } +}; diff --git a/src/v/iceberg/unresolved_partition_spec.cc b/src/v/iceberg/unresolved_partition_spec.cc index cbb04f45fc694..2e60c6092c699 100644 --- a/src/v/iceberg/unresolved_partition_spec.cc +++ b/src/v/iceberg/unresolved_partition_spec.cc @@ -13,7 +13,6 @@ #include "iceberg/transform.h" #include -#include #include @@ -63,20 +62,18 @@ void unresolved_partition_spec::field::autogenerate_name() { transform)); } -std::ostream& -operator<<(std::ostream& o, const unresolved_partition_spec::field& f) { - fmt::print( - o, +fmt::iterator +unresolved_partition_spec::field::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{source_name: {}, transform: {}, name: {}}}", - f.source_name, - f.transform, - f.name); - return o; + source_name, + transform, + name); } -std::ostream& operator<<(std::ostream& o, const unresolved_partition_spec& ps) { - fmt::print(o, "{{fields: {}}}", ps.fields); - return o; +fmt::iterator unresolved_partition_spec::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{fields: {}}}", fields); } } // namespace iceberg diff --git a/src/v/iceberg/unresolved_partition_spec.h b/src/v/iceberg/unresolved_partition_spec.h index a6ba22e3ec0b3..de466ca586c50 100644 --- a/src/v/iceberg/unresolved_partition_spec.h +++ b/src/v/iceberg/unresolved_partition_spec.h @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" #include "iceberg/transform.h" @@ -27,8 +28,9 @@ struct unresolved_partition_spec { void autogenerate_name(); + fmt::iterator format_to(fmt::iterator it) const; + friend bool operator==(const field&, const field&) = default; - friend std::ostream& operator<<(std::ostream&, const field&); }; chunked_vector fields; @@ -41,14 +43,13 @@ struct unresolved_partition_spec { const unresolved_partition_spec&, const unresolved_partition_spec&) = default; + fmt::iterator format_to(fmt::iterator it) const; + unresolved_partition_spec copy() const { return { .fields = fields.copy(), }; } - - friend std::ostream& - operator<<(std::ostream&, const unresolved_partition_spec&); }; } // namespace iceberg diff --git a/src/v/iceberg/values.cc b/src/v/iceberg/values.cc index 2841757a5632b..dc8f929257463 100644 --- a/src/v/iceberg/values.cc +++ b/src/v/iceberg/values.cc @@ -119,12 +119,11 @@ struct hashing_visitor { } }; -void ostream_val_ptr(std::ostream& o, const std::optional& v) { +fmt::iterator format_val_ptr(const std::optional& v, fmt::iterator it) { if (v) { - o << *v; - return; + return fmt::format_to(it, "{}", *v); } - o << "none"; + return fmt::format_to(it, "none"); } struct primitive_value_comparison_visitor { @@ -352,167 +351,102 @@ bool operator==(const value& lhs, const value& rhs) { return std::visit(comparison_visitor{lhs}, rhs); } -std::ostream& operator<<(std::ostream& o, const boolean_value& v) { - o << fmt::format("boolean({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const int_value& v) { - o << fmt::format("int({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const long_value& v) { - o << fmt::format("long({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const float_value& v) { - o << fmt::format("float({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const double_value& v) { - o << fmt::format("double({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const date_value& v) { - o << fmt::format("date({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const time_value& v) { - o << fmt::format("time({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const timestamp_value& v) { - o << fmt::format("timestamp({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const timestamptz_value& v) { - o << fmt::format("timestamptz({})", v.val); - return o; -} -std::ostream& operator<<(std::ostream& o, const string_value& v) { - iobuf_const_parser buf_parser{v.val}; +fmt::iterator string_value::format_to(fmt::iterator it) const { + iobuf_const_parser buf_parser{val}; static constexpr auto max_len = 16; - auto size_bytes = v.val.size_bytes(); + auto size_bytes = val.size_bytes(); if (size_bytes > max_len) { - o << fmt::format("string(\"{}...\")", buf_parser.read_string(max_len)); - } else { - o << fmt::format("string(\"{}\")", buf_parser.read_string(size_bytes)); + return fmt::format_to( + it, "string(\"{}...\")", buf_parser.read_string(max_len)); } - return o; -} -std::ostream& operator<<(std::ostream& o, const uuid_value& v) { - o << fmt::format("uuid({})", ss::sstring(v.val)); - return o; -} -std::ostream& operator<<(std::ostream& o, const fixed_value& v) { - o << fmt::format("fixed(size_bytes={})", v.val.size_bytes()); - return o; + return fmt::format_to( + it, "string(\"{}\")", buf_parser.read_string(size_bytes)); } -std::ostream& operator<<(std::ostream& o, const binary_value& v) { - o << fmt::format("binary(size_bytes={})", v.val.size_bytes()); - return o; +fmt::iterator fixed_value::format_to(fmt::iterator it) const { + return fmt::format_to(it, "fixed(size_bytes={})", val.size_bytes()); } -std::ostream& operator<<(std::ostream& o, const decimal_value& v) { - o << fmt::format("decimal({})", v.val); - return o; +fmt::iterator binary_value::format_to(fmt::iterator it) const { + return fmt::format_to(it, "binary(size_bytes={})", val.size_bytes()); } namespace { -struct value_ostream_visitor { - explicit value_ostream_visitor(std::ostream& o) - : o_(o) {} - - std::ostream& o_; +struct primitive_format_visitor { + fmt::iterator it; template - void operator()(const T& v) { - o_ << v; + fmt::iterator operator()(const T& v) { + return fmt::format_to(it, "{}", v); } }; } // namespace -std::ostream& operator<<(std::ostream& o, const primitive_value& v) { - std::visit(value_ostream_visitor{o}, v); - return o; +fmt::iterator format_to(const primitive_value& v, fmt::iterator it) { + return std::visit(primitive_format_visitor{it}, v); } -std::ostream& operator<<(std::ostream& o, const list_value& v) { - o << "list{"; +fmt::iterator list_value::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "list{{"); static constexpr size_t max_to_log = 5; size_t logged = 0; - for (const auto& e : v.elements) { + for (const auto& e : elements) { if (logged == max_to_log) { - o << "..."; + it = fmt::format_to(it, "..."); break; } - ostream_val_ptr(o, e); - o << ", "; + it = format_val_ptr(e, it); + it = fmt::format_to(it, ", "); logged++; } - o << "}"; - return o; + return fmt::format_to(it, "}}"); } -std::ostream& operator<<(std::ostream& o, const map_value& v) { - o << "map{"; +fmt::iterator map_value::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "map{{"); static constexpr size_t max_to_log = 5; size_t logged = 0; - for (const auto& kv : v.kvs) { + for (const auto& kv : kvs) { if (logged == max_to_log) { - o << "..."; + it = fmt::format_to(it, "..."); break; } - o << fmt::format("(k={}, v=", kv.key); - ostream_val_ptr(o, kv.val); - o << "), "; + it = fmt::format_to(it, "(k={}, v=", kv.key); + it = format_val_ptr(kv.val, it); + it = fmt::format_to(it, "), "); logged++; } - o << "}"; - return o; + return fmt::format_to(it, "}}"); } -std::ostream& operator<<(std::ostream& o, const struct_value& v) { - o << "struct{"; +fmt::iterator struct_value::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "struct{{"); static constexpr size_t max_to_log = 5; size_t logged = 0; - for (const auto& f : v.fields) { + for (const auto& f : fields) { if (logged == max_to_log) { - o << "..."; + it = fmt::format_to(it, "..."); break; } - ostream_val_ptr(o, f); - o << ", "; + it = format_val_ptr(f, it); + it = fmt::format_to(it, ", "); logged++; } - o << "}"; - return o; + return fmt::format_to(it, "}}"); } -std::ostream& -operator<<(std::ostream& o, const std::unique_ptr& v) { - if (!v) { - o << "struct{nullptr}"; - return o; - } - return o << *v; -} +namespace { +struct value_format_visitor { + fmt::iterator it; -std::ostream& -operator<<(std::ostream& o, const std::unique_ptr& v) { - if (!v) { - o << "list{nullptr}"; - return o; + fmt::iterator operator()(const primitive_value& v) { + return format_to(v, it); } - return o << *v; -} -std::ostream& operator<<(std::ostream& o, const std::unique_ptr& v) { - if (!v) { - o << "map{nullptr}"; - return o; + template + fmt::iterator operator()(const T& v) { + return fmt::format_to(it, "{}", v); } - return o << *v; -} +}; +} // namespace -std::ostream& operator<<(std::ostream& o, const value& v) { - std::visit(value_ostream_visitor{o}, v); - return o; +fmt::iterator format_to(const value& v, fmt::iterator it) { + return std::visit(value_format_visitor{it}, v); } size_t value_hash(const struct_value& v) { diff --git a/src/v/iceberg/values.h b/src/v/iceberg/values.h index c79cc8ef4e9a9..6da2ce0f9c3bf 100644 --- a/src/v/iceberg/values.h +++ b/src/v/iceberg/values.h @@ -10,6 +10,7 @@ #pragma once #include "absl/numeric/int128.h" +#include "base/format_to.h" #include "bytes/iobuf.h" #include "container/chunked_vector.h" #include "iceberg/datatypes.h" @@ -18,80 +19,122 @@ #include #include +template<> +struct fmt::formatter : fmt::ostream_formatter {}; + +template<> +struct fmt::formatter : fmt::ostream_formatter {}; + namespace iceberg { struct boolean_value { static std::string_view name() { return "boolean"; } bool val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "boolean({})", val); + } }; struct int_value { static std::string_view name() { return "int"; } int32_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "int({})", val); + } }; struct long_value { static std::string_view name() { return "long"; } int64_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "long({})", val); + } }; struct float_value { static std::string_view name() { return "float"; } float val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "float({})", val); + } }; struct double_value { static std::string_view name() { return "double"; } double val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "double({})", val); + } }; struct date_value { static std::string_view name() { return "date"; } // Days since 1970-01-01. int32_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "date({})", val); + } }; struct time_value { static std::string_view name() { return "time"; } // Microseconds since midnight. int64_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "time({})", val); + } }; struct timestamp_value { static std::string_view name() { return "timestamp"; } // Microseconds since 1970-01-01 00:00:00. int64_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "timestamp({})", val); + } }; struct timestamptz_value { static std::string_view name() { return "timestamptz"; } // Microseconds since 1970-01-01 00:00:00 UTC. int64_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "timestamptz({})", val); + } }; struct string_value { static std::string_view name() { return "string"; } iobuf val; + fmt::iterator format_to(fmt::iterator it) const; }; struct uuid_value { static std::string_view name() { return "uuid"; } uuid_t val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "uuid({})", ss::sstring(val)); + } }; struct fixed_value { static std::string_view name() { return "fixed"; } iobuf val; + fmt::iterator format_to(fmt::iterator it) const; }; struct binary_value { static std::string_view name() { return "binary"; } iobuf val; + fmt::iterator format_to(fmt::iterator it) const; }; struct decimal_value { static std::string_view name() { return "decimal"; } absl::int128 val; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "decimal({})", val); + } }; using primitive_value = std::variant< @@ -126,6 +169,7 @@ struct struct_value { // The order of these fields must align with the corresponding struct type // as defined in the schema, see `iceberg::struct_type`. chunked_vector> fields; + fmt::iterator format_to(fmt::iterator it) const; }; bool operator==(const struct_value&, const struct_value&); bool operator==( @@ -133,6 +177,7 @@ bool operator==( struct list_value { chunked_vector> elements; + fmt::iterator format_to(fmt::iterator it) const; }; bool operator==(const list_value&, const list_value&); bool operator==( @@ -149,6 +194,7 @@ bool operator==(const kv_value&, const kv_value&); struct map_value { chunked_vector kvs; + fmt::iterator format_to(fmt::iterator it) const; }; bool operator==(const map_value&, const map_value&); bool operator==( @@ -157,29 +203,6 @@ bool operator==(const value&, const value&); value make_copy(const value&); -std::ostream& operator<<(std::ostream&, const boolean_value&); -std::ostream& operator<<(std::ostream&, const int_value&); -std::ostream& operator<<(std::ostream&, const long_value&); -std::ostream& operator<<(std::ostream&, const float_value&); -std::ostream& operator<<(std::ostream&, const double_value&); -std::ostream& operator<<(std::ostream&, const date_value&); -std::ostream& operator<<(std::ostream&, const time_value&); -std::ostream& operator<<(std::ostream&, const timestamp_value&); -std::ostream& operator<<(std::ostream&, const timestamptz_value&); -std::ostream& operator<<(std::ostream&, const string_value&); -std::ostream& operator<<(std::ostream&, const uuid_value&); -std::ostream& operator<<(std::ostream&, const fixed_value&); -std::ostream& operator<<(std::ostream&, const binary_value&); -std::ostream& operator<<(std::ostream&, const decimal_value&); -std::ostream& operator<<(std::ostream&, const primitive_value&); -std::ostream& operator<<(std::ostream&, const struct_value&); -std::ostream& operator<<(std::ostream&, const list_value&); -std::ostream& operator<<(std::ostream&, const map_value&); -std::ostream& operator<<(std::ostream&, const std::unique_ptr&); -std::ostream& operator<<(std::ostream&, const std::unique_ptr&); -std::ostream& operator<<(std::ostream&, const std::unique_ptr&); -std::ostream& operator<<(std::ostream&, const value&); - size_t value_hash(const struct_value&); size_t value_hash(const value&); @@ -247,8 +270,33 @@ struct primitive_value_type { template using primitive_value_type_t = typename primitive_value_type::type; +fmt::iterator format_to(const primitive_value& v, fmt::iterator it); +fmt::iterator format_to(const value& v, fmt::iterator it); + } // namespace iceberg +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + fmt::iterator + format(const iceberg::primitive_value& v, fmt::format_context& ctx) const { + return iceberg::format_to(v, ctx.out()); + } +}; + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + fmt::iterator + format(const iceberg::value& v, fmt::format_context& ctx) const { + return iceberg::format_to(v, ctx.out()); + } +}; + namespace std { template<> diff --git a/src/v/iceberg/values_avro.cc b/src/v/iceberg/values_avro.cc index 61fc901534231..0660b4458af69 100644 --- a/src/v/iceberg/values_avro.cc +++ b/src/v/iceberg/values_avro.cc @@ -52,10 +52,10 @@ void maybe_throw_wrong_logical_type( "Expected (type: {}, precision: {}, scale: {}) logical_type but " "got: " "(type: {}, precision: {}, scale: {})", - expected.type(), + static_cast(expected.type()), expected.precision(), expected.scale(), - actual.type(), + static_cast(actual.type()), actual.precision(), actual.scale())); } @@ -149,7 +149,7 @@ struct primitive_value_avro_visitor { } avro::GenericDatum operator()(const uuid_value& uuid) { maybe_throw_invalid_schema(avro_schema_, avro::AVRO_STRING); - return {avro_schema_, fmt::to_string(uuid.val)}; + return {avro_schema_, fmt::format("{}", uuid.val)}; } }; diff --git a/src/v/iceberg/values_json.cc b/src/v/iceberg/values_json.cc index 7519cc80bde7b..11677668d4e06 100644 --- a/src/v/iceberg/values_json.cc +++ b/src/v/iceberg/values_json.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -34,7 +35,9 @@ parse_string_view(const json::Value& data, std::string_view context = "") { if (!data.IsString()) { throw std::invalid_argument( fmt::format( - "Expected JSON string for {}, got {}", context, data.GetType())); + "Expected JSON string for {}, got {}", + context, + static_cast(data.GetType()))); } return std::string_view{data.GetString(), data.GetStringLength()}; } @@ -112,28 +115,36 @@ struct primitive_value_parsing_visitor { value operator()(const boolean_type&) { if (!data_.IsBool()) { throw std::invalid_argument( - fmt::format("Expected bool value, got {}", data_.GetType())); + fmt::format( + "Expected bool value, got {}", + static_cast(data_.GetType()))); } return boolean_value{data_.GetBool()}; } value operator()(const int_type&) { if (!data_.IsInt()) { throw std::invalid_argument( - fmt::format("Expected int value, got {}", data_.GetType())); + fmt::format( + "Expected int value, got {}", + static_cast(data_.GetType()))); } return int_value{data_.GetInt()}; } value operator()(const long_type&) { if (!data_.IsInt64()) { throw std::invalid_argument( - fmt::format("Expected long value, got {}", data_.GetType())); + fmt::format( + "Expected long value, got {}", + static_cast(data_.GetType()))); } return long_value{data_.GetInt64()}; } value operator()(const float_type&) { if (!data_.IsDouble()) { throw std::invalid_argument( - fmt::format("Expected float value, got {}", data_.GetType())); + fmt::format( + "Expected float value, got {}", + static_cast(data_.GetType()))); } auto v = data_.GetDouble(); if ( @@ -147,7 +158,9 @@ struct primitive_value_parsing_visitor { value operator()(const double_type&) { if (!data_.IsDouble()) { throw std::invalid_argument( - fmt::format("Expected double value, got {}", data_.GetType())); + fmt::format( + "Expected double value, got {}", + static_cast(data_.GetType()))); } return double_value{data_.GetDouble()}; } @@ -424,9 +437,11 @@ struct rjson_visitor { void operator()(const iceberg::date_value& v, const iceberg::date_type&) { const std::chrono::system_clock::time_point tp{ std::chrono::days(v.val)}; - const std::chrono::year_month_day ymd{ - std::chrono::floor(tp)}; - w.String(fmt::to_string(ymd)); + const auto tt = std::chrono::system_clock::to_time_t(tp); + auto tm = *std::gmtime(&tt); + std::array buf{}; + std::strftime(buf.data(), buf.size(), "%F", &tm); + w.String(buf.data()); } void operator()(const iceberg::time_value& v, const iceberg::time_type&) { @@ -437,8 +452,10 @@ struct rjson_visitor { const std::chrono::system_clock::time_point tp{s}; const auto tt = std::chrono::system_clock::to_time_t(tp); - const auto tm = *std::gmtime(&tt); - w.String(fmt::format("{}.{:06}", std::put_time(&tm, "%T"), rest)); + auto tm = *std::gmtime(&tt); + std::array buf{}; + std::strftime(buf.data(), buf.size(), "%T", &tm); + w.String(fmt::format("{}.{:06}", buf.data(), rest)); } void operator()( @@ -450,8 +467,10 @@ struct rjson_visitor { const std::chrono::system_clock::time_point tp{s}; const auto tt = std::chrono::system_clock::to_time_t(tp); - const auto tm = *std::gmtime(&tt); - w.String(fmt::format("{}.{:06}", std::put_time(&tm, "%FT%T"), rest)); + auto tm = *std::gmtime(&tt); + std::array buf{}; + std::strftime(buf.data(), buf.size(), "%FT%T", &tm); + w.String(fmt::format("{}.{:06}", buf.data(), rest)); } void operator()( const iceberg::timestamptz_value& v, const iceberg::timestamptz_type&) { @@ -465,9 +484,10 @@ struct rjson_visitor { const std::chrono::system_clock::time_point tp{s}; const auto tt = std::chrono::system_clock::to_time_t(tp); - const auto tm = *std::gmtime(&tt); - w.String( - fmt::format("{}.{:06}+00:00", std::put_time(&tm, "%FT%T"), rest)); + auto tm = *std::gmtime(&tt); + std::array buf{}; + std::strftime(buf.data(), buf.size(), "%FT%T", &tm); + w.String(fmt::format("{}.{:06}+00:00", buf.data(), rest)); } void @@ -475,7 +495,7 @@ struct rjson_visitor { w.String(v.val); } void operator()(const iceberg::uuid_value& v, const iceberg::uuid_type&) { - w.String(fmt::to_string(v.val)); + w.String(fmt::format("{}", v.val)); } void operator()(const iceberg::fixed_value& v, const iceberg::fixed_type& t) { @@ -611,7 +631,7 @@ void value_to_json( fmt::format( "Found null nested field in struct type for value {}", s)); } - w.Key(fmt::to_string(t_field->id)); + w.Key(fmt::format("{}", t_field->id)); if (v_field.has_value()) { value_to_json(w, v_field.value(), t_field->type); } else if (!t_field->required) { diff --git a/src/v/json/document.h b/src/v/json/document.h index f25d089ad04b8..e66ffa63f3584 100644 --- a/src/v/json/document.h +++ b/src/v/json/document.h @@ -12,6 +12,7 @@ #include "json/_include_first.h" #include "json/allocator.h" #include "json/encodings.h" +#include "json/types.h" #include diff --git a/src/v/json/types.h b/src/v/json/types.h index 2403b6e5a5e34..10b56f0843514 100644 --- a/src/v/json/types.h +++ b/src/v/json/types.h @@ -11,6 +11,9 @@ #include "json/_include_first.h" +#include +#include +#include #include namespace json { @@ -19,3 +22,25 @@ using SizeType = rapidjson::SizeType; using Type = rapidjson::Type; } // namespace json + +template<> +struct fmt::formatter : fmt::formatter { + auto format(rapidjson::Type t, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(t), ctx); + } +}; + +template<> +struct fmt::formatter : fmt::formatter { + auto format(rapidjson::ParseErrorCode e, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(e), ctx); + } +}; + +template<> +struct fmt::formatter : fmt::formatter { + auto + format(rapidjson::PointerParseErrorCode e, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(e), ctx); + } +}; diff --git a/src/v/kafka/client/broker.cc b/src/v/kafka/client/broker.cc index 43c3aa0df9f83..69122210a4f7a 100644 --- a/src/v/kafka/client/broker.cc +++ b/src/v/kafka/client/broker.cc @@ -578,9 +578,8 @@ api_version remote_broker::get_sasl_handshake_request_version() const { _node_id, _supported_versions); } -std::ostream& operator<<(std::ostream& os, const api_version_range& r) { - fmt::print(os, "{{min: {}, max: {}}}", r.min, r.max); - return os; +fmt::iterator api_version_range::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{min: {}, max: {}}}", min, max); } } // namespace kafka::client diff --git a/src/v/kafka/client/broker.h b/src/v/kafka/client/broker.h index 96ed8c5496f5c..fd2e6811225d9 100644 --- a/src/v/kafka/client/broker.h +++ b/src/v/kafka/client/broker.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/client/api_types.h" #include "kafka/client/configuration.h" #include "kafka/client/exceptions.h" @@ -31,8 +32,7 @@ struct api_version_range { bool is_supported(api_version v) const { return v >= min && v <= max; } - friend std::ostream& - operator<<(std::ostream& os, const api_version_range& r); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const api_version_range& lhs, const api_version_range& rhs) = default; diff --git a/src/v/kafka/client/client_fetch_batch_reader.cc b/src/v/kafka/client/client_fetch_batch_reader.cc index 7488069bbcfd1..bbae67842aa82 100644 --- a/src/v/kafka/client/client_fetch_batch_reader.cc +++ b/src/v/kafka/client/client_fetch_batch_reader.cc @@ -84,8 +84,9 @@ class client_fetcher final : public model::record_batch_reader::impl { } // Implements model::record_batch_reader::impl - void print(std::ostream& os) final { - os << "{pandaproxy::schema_registry::client_fetcher}"; + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to( + it, "{{pandaproxy::schema_registry::client_fetcher}}"); } private: diff --git a/src/v/kafka/client/consumer.cc b/src/v/kafka/client/consumer.cc index 724ff67e3d730..66d1ebf9f82c5 100644 --- a/src/v/kafka/client/consumer.cc +++ b/src/v/kafka/client/consumer.cc @@ -632,4 +632,13 @@ ss::future make_consumer( return c->initialize().then([c]() mutable { return std::move(c); }); } +fmt::iterator consumer::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "type={}, member_id={}, name={}", + is_leader() ? "leader" : "member", + _member_id, + _name); +} + } // namespace kafka::client diff --git a/src/v/kafka/client/consumer.h b/src/v/kafka/client/consumer.h index b640f561546c4..11a87a4b00ef4 100644 --- a/src/v/kafka/client/consumer.h +++ b/src/v/kafka/client/consumer.h @@ -13,6 +13,7 @@ #include "absl/container/node_hash_map.h" #include "absl/hash/hash.h" +#include "base/format_to.h" #include "container/chunked_vector.h" #include "kafka/client/assignment_plans.h" #include "kafka/client/brokers.h" @@ -80,6 +81,8 @@ class consumer final : public ss::enable_lw_shared_from_this { ss::future fetch(std::chrono::milliseconds timeout, std::optional max_bytes); + fmt::iterator format_to(fmt::iterator it) const; + private: bool is_leader() const { return _member_id != no_member && _leader_id == _member_id; @@ -183,16 +186,6 @@ class consumer final : public ss::enable_lw_shared_from_this { ss::noncopyable_function(std::exception_ptr)> _external_mitigate; prefix_logger* _logger; - - friend std::ostream& operator<<(std::ostream& os, const consumer& c) { - fmt::print( - os, - "type={}, member_id={}, name={}", - c.is_leader() ? "leader" : "member", - c._member_id, - c._name); - return os; - } }; using shared_consumer_t = ss::lw_shared_ptr; diff --git a/src/v/kafka/client/direct_consumer/api_types.h b/src/v/kafka/client/direct_consumer/api_types.h index 105f14f279bb8..d0013dda6c902 100644 --- a/src/v/kafka/client/direct_consumer/api_types.h +++ b/src/v/kafka/client/direct_consumer/api_types.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "container/chunked_hash_map.h" #include "container/chunked_vector.h" @@ -30,12 +31,12 @@ enum class offset_reset_policy : int8_t { latest, }; -inline std::ostream& operator<<(std::ostream& os, offset_reset_policy p) { +inline fmt::iterator format_to(offset_reset_policy p, fmt::iterator out) { switch (p) { case offset_reset_policy::earliest: - return os << "earliest"; + return fmt::format_to(out, "earliest"); case offset_reset_policy::latest: - return os << "latest"; + return fmt::format_to(out, "latest"); } } diff --git a/src/v/kafka/client/direct_consumer/direct_consumer.cc b/src/v/kafka/client/direct_consumer/direct_consumer.cc index 88caa55c0e4c7..02454f30a6e24 100644 --- a/src/v/kafka/client/direct_consumer/direct_consumer.cc +++ b/src/v/kafka/client/direct_consumer/direct_consumer.cc @@ -491,22 +491,20 @@ void direct_consumer::on_metadata_update(const metadata_update&) { direct_consumer::~direct_consumer() = default; -std::ostream& -operator<<(std::ostream& o, const direct_consumer::configuration& cfg) { - fmt::print( - o, +fmt::iterator +direct_consumer::configuration::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ max_fetch_size: {}, partition_max_bytes: {}, reset_policy: {}, " "max_wait_time: {}ms, isolation_level: {}, max_buffered_bytes: {}, " "max_buffered_elements: {} , sessions_enabled: {}}}", - cfg.max_fetch_size, - cfg.partition_max_bytes, - cfg.reset_policy, - cfg.max_wait_time.count(), - cfg.isolation_level, - cfg.max_buffered_bytes, - cfg.max_buffered_elements, - cfg.with_sessions); - - return o; + max_fetch_size, + partition_max_bytes, + reset_policy, + max_wait_time.count(), + isolation_level, + max_buffered_bytes, + max_buffered_elements, + with_sessions); } } // namespace kafka::client diff --git a/src/v/kafka/client/direct_consumer/direct_consumer.h b/src/v/kafka/client/direct_consumer/direct_consumer.h index 39ac8c44d8111..4206b5cabdb9b 100644 --- a/src/v/kafka/client/direct_consumer/direct_consumer.h +++ b/src/v/kafka/client/direct_consumer/direct_consumer.h @@ -67,7 +67,7 @@ class direct_consumer { size_t max_buffered_elements{10}; // fetch sessions enabled by default fetch_sessions_enabled with_sessions{fetch_sessions_enabled::yes}; - friend std::ostream& operator<<(std::ostream&, const configuration&); + fmt::iterator format_to(fmt::iterator it) const; }; direct_consumer( diff --git a/src/v/kafka/client/fetch_session.cc b/src/v/kafka/client/fetch_session.cc index 38bca520bdbde..6b2d2545c0160 100644 --- a/src/v/kafka/client/fetch_session.cc +++ b/src/v/kafka/client/fetch_session.cc @@ -12,10 +12,6 @@ #include "kafka/protocol/fetch.h" #include "kafka/protocol/schemata/offset_commit_request.h" -#include - -#include - namespace kafka::client { model::offset fetch_session::offset(model::topic_partition_view tpv) const { @@ -81,9 +77,8 @@ fetch_session::make_offset_commit_request() const { return res; } -std::ostream& operator<<(std::ostream& os, const fetch_session& fs) { - fmt::print(os, "{{id={}, epoch={}}}", fs.id(), fs.epoch()); - return os; +fmt::iterator fetch_session::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{id={}, epoch={}}}", id(), epoch()); } } // namespace kafka::client diff --git a/src/v/kafka/client/fetch_session.h b/src/v/kafka/client/fetch_session.h index ae8688023d341..147c4dd9af922 100644 --- a/src/v/kafka/client/fetch_session.h +++ b/src/v/kafka/client/fetch_session.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "kafka/protocol/schemata/offset_commit_request.h" #include "model/fundamental.h" @@ -42,7 +43,7 @@ class fetch_session { std::vector make_offset_commit_request() const; - friend std::ostream& operator<<(std::ostream& os, const fetch_session&); + fmt::iterator format_to(fmt::iterator it) const; private: kafka::fetch_session_id _id{kafka::invalid_fetch_session_id}; diff --git a/src/v/kafka/client/test/BUILD b/src/v/kafka/client/test/BUILD index 47b34aa5bf796..b720d3f8cc1f1 100644 --- a/src/v/kafka/client/test/BUILD +++ b/src/v/kafka/client/test/BUILD @@ -353,6 +353,7 @@ redpanda_cc_btest( "//src/v/security", "//src/v/test_utils:seastar_boost", "@boost//:test", + "@fmt", "@seastar", "@seastar//:testing", "@yaml-cpp", diff --git a/src/v/kafka/client/test/test_config_utils.cc b/src/v/kafka/client/test/test_config_utils.cc index 802943be26e5d..334b29196d4eb 100644 --- a/src/v/kafka/client/test/test_config_utils.cc +++ b/src/v/kafka/client/test/test_config_utils.cc @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include @@ -35,9 +37,11 @@ namespace kafka::client { // BOOST_REQURE_EQUAL fails to find this if it's in the global namespace bool operator==(const configuration& lhs, const configuration& rhs) { - return fmt::format("{}", config::to_yaml(lhs, config::redact_secrets::no)) - == fmt::format( - "{}", config::to_yaml(rhs, config::redact_secrets::no)); + auto to_str = [](const configuration& c) { + return fmt::format( + "{}", fmt_streamed(config::to_yaml(c, config::redact_secrets::no))); + }; + return to_str(lhs) == to_str(rhs); } std::ostream& operator<<(std::ostream& os, const configuration& c) { diff --git a/src/v/kafka/data/cloud_topic_read_replica.cc b/src/v/kafka/data/cloud_topic_read_replica.cc index 5f48b61b4cc3a..5b469522be9b9 100644 --- a/src/v/kafka/data/cloud_topic_read_replica.cc +++ b/src/v/kafka/data/cloud_topic_read_replica.cc @@ -50,7 +50,9 @@ class snapshot_level_one_reader : public model::record_batch_reader::impl { return reader_->do_load_slice(deadline); } - void print(std::ostream& o) override { reader_->print(o); } + fmt::iterator format_to(fmt::iterator it) const override { + return reader_->format_to(it); + } private: // The metastore must be kept alive for the lifetime of the reader diff --git a/src/v/kafka/data/rpc/test/deps.h b/src/v/kafka/data/rpc/test/deps.h index 3fe6288769c01..b2d5fdfc5ca9d 100644 --- a/src/v/kafka/data/rpc/test/deps.h +++ b/src/v/kafka/data/rpc/test/deps.h @@ -49,7 +49,7 @@ struct record_batches { } friend std::ostream& operator<<(std::ostream& os, const record_batches& b) { - return os << ss::format("{}", b.underlying); + return os << "record_batches{size=" << b.underlying.size() << "}"; } bool empty() const { return underlying.empty(); } diff --git a/src/v/kafka/protocol/add_offsets_to_txn.h b/src/v/kafka/protocol/add_offsets_to_txn.h index a8fcc06fa81be..7dee1c71a0975 100644 --- a/src/v/kafka/protocol/add_offsets_to_txn.h +++ b/src/v/kafka/protocol/add_offsets_to_txn.h @@ -35,12 +35,11 @@ struct add_offsets_to_txn_request final { void decode(protocol::decoder& reader, api_version version) { data.decode(reader, version); } -}; -inline std::ostream& -operator<<(std::ostream& os, const add_offsets_to_txn_request& r) { - return os << r.data; -} + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); + } +}; struct add_offsets_to_txn_response final { using api_type = add_offsets_to_txn_api; @@ -58,11 +57,10 @@ struct add_offsets_to_txn_response final { void decode(iobuf buf, api_version version) { data.decode(std::move(buf), version); } -}; -inline std::ostream& -operator<<(std::ostream& os, const add_offsets_to_txn_response& r) { - return os << r.data; -} + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); + } +}; } // namespace kafka diff --git a/src/v/kafka/protocol/add_partitions_to_txn.h b/src/v/kafka/protocol/add_partitions_to_txn.h index d85f73909149f..f55b41fd674d9 100644 --- a/src/v/kafka/protocol/add_partitions_to_txn.h +++ b/src/v/kafka/protocol/add_partitions_to_txn.h @@ -36,9 +36,8 @@ struct add_partitions_to_txn_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const add_partitions_to_txn_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -79,9 +78,8 @@ struct add_partitions_to_txn_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const add_partitions_to_txn_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/alter_client_quotas.h b/src/v/kafka/protocol/alter_client_quotas.h index c515280a48267..f36ce0399aefa 100644 --- a/src/v/kafka/protocol/alter_client_quotas.h +++ b/src/v/kafka/protocol/alter_client_quotas.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" #include "kafka/protocol/schemata/alter_client_quotas_request.h" #include "kafka/protocol/schemata/alter_client_quotas_response.h" @@ -28,9 +29,8 @@ struct alter_client_quotas_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const alter_client_quotas_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -47,9 +47,8 @@ struct alter_client_quotas_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const alter_client_quotas_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/alter_configs.h b/src/v/kafka/protocol/alter_configs.h index 396f39490affa..f66b4cc86b3e1 100644 --- a/src/v/kafka/protocol/alter_configs.h +++ b/src/v/kafka/protocol/alter_configs.h @@ -35,9 +35,8 @@ struct alter_configs_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const alter_configs_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -54,9 +53,8 @@ struct alter_configs_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const alter_configs_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/alter_partition_reassignments.h b/src/v/kafka/protocol/alter_partition_reassignments.h index 4390e7fa78fe6..82dc2f3f6bf65 100644 --- a/src/v/kafka/protocol/alter_partition_reassignments.h +++ b/src/v/kafka/protocol/alter_partition_reassignments.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/alter_partition_reassignments_request.h" #include "kafka/protocol/schemata/alter_partition_reassignments_response.h" @@ -29,9 +30,8 @@ struct alter_partition_reassignments_request final { data.decode(reader, version); } - friend std::ostream& operator<<( - std::ostream& os, const alter_partition_reassignments_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -48,9 +48,8 @@ struct alter_partition_reassignments_response final { data.decode(std::move(buf), version); } - friend std::ostream& operator<<( - std::ostream& os, const alter_partition_reassignments_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/alter_user_scram_credentials.h b/src/v/kafka/protocol/alter_user_scram_credentials.h index 8f574f2d8c951..c60c3d0c4b33a 100644 --- a/src/v/kafka/protocol/alter_user_scram_credentials.h +++ b/src/v/kafka/protocol/alter_user_scram_credentials.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/alter_user_scram_credentials_request.h" #include "kafka/protocol/schemata/alter_user_scram_credentials_response.h" #include "kafka/protocol/wire.h" @@ -24,9 +25,8 @@ struct alter_user_scram_credentials_request final { void decode(protocol::decoder& reader, api_version version) { data.decode(reader, version); } - friend std::ostream& operator<<( - std::ostream& os, const alter_user_scram_credentials_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; struct alter_user_scram_credentials_response final { @@ -38,9 +38,8 @@ struct alter_user_scram_credentials_response final { void decode(iobuf buf, api_version version) { data.decode(std::move(buf), version); } - friend std::ostream& operator<<( - std::ostream& os, const alter_user_scram_credentials_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; } // namespace kafka diff --git a/src/v/kafka/protocol/api_versions.h b/src/v/kafka/protocol/api_versions.h index 55ffc27f3f3b3..ea316245902f3 100644 --- a/src/v/kafka/protocol/api_versions.h +++ b/src/v/kafka/protocol/api_versions.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/api_versions_request.h" #include "kafka/protocol/schemata/api_versions_response.h" @@ -32,9 +33,8 @@ struct api_versions_request final { data.encode(writer, version); } - friend std::ostream& - operator<<(std::ostream& os, const api_versions_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -51,9 +51,8 @@ struct api_versions_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const api_versions_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/batch_reader.h b/src/v/kafka/protocol/batch_reader.h index 2fd1799c85175..976fba5d67272 100644 --- a/src/v/kafka/protocol/batch_reader.h +++ b/src/v/kafka/protocol/batch_reader.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" #include "kafka/protocol/kafka_batch_adapter.h" #include "model/fundamental.h" @@ -66,19 +67,14 @@ class batch_reader final : public model::record_batch_reader::impl { ss::future do_load_slice(model::timeout_clock::time_point) final; // Implements model::record_batch_reader::impl - // NOTE: this stream is intentially devoid of user data. - void print(std::ostream& os) final { os << "{kafka::consumer_records}"; } + // NOTE: this stream is intentionally devoid of user data. + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to(it, "{{size {}}}", size_bytes()); + } // Release any remaining iobuf that hasn't been consumed iobuf release() && { return std::move(_buf); } - friend std::ostream& - operator<<(std::ostream& os, const batch_reader& reader) { - // NOTE: this stream is intentially devoid of user data. - fmt::print(os, "{{size {}}}", reader.size_bytes()); - return os; - } - private: iobuf _buf; bool _do_load_slice_failed{false}; diff --git a/src/v/kafka/protocol/create_acls.h b/src/v/kafka/protocol/create_acls.h index d1bf4acfc3034..daf19571b071b 100644 --- a/src/v/kafka/protocol/create_acls.h +++ b/src/v/kafka/protocol/create_acls.h @@ -34,9 +34,8 @@ struct create_acls_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const create_acls_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -53,9 +52,8 @@ struct create_acls_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const create_acls_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/create_partitions.h b/src/v/kafka/protocol/create_partitions.h index f2bbecda07b26..f4dea187b66f2 100644 --- a/src/v/kafka/protocol/create_partitions.h +++ b/src/v/kafka/protocol/create_partitions.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/create_partitions_request.h" #include "kafka/protocol/schemata/create_partitions_response.h" @@ -32,9 +33,8 @@ struct create_partitions_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const create_partitions_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -84,9 +84,8 @@ struct create_partitions_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const create_partitions_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/create_topics.h b/src/v/kafka/protocol/create_topics.h index bf83821980a4b..9c4a5f130ec1f 100644 --- a/src/v/kafka/protocol/create_topics.h +++ b/src/v/kafka/protocol/create_topics.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/create_topics_request.h" #include "kafka/protocol/schemata/create_topics_response.h" @@ -34,9 +35,8 @@ struct create_topics_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const create_topics_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -88,9 +88,8 @@ struct create_topics_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const create_topics_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/delete_acls.h b/src/v/kafka/protocol/delete_acls.h index b9be539b2560f..b0ac09ad8743c 100644 --- a/src/v/kafka/protocol/delete_acls.h +++ b/src/v/kafka/protocol/delete_acls.h @@ -34,9 +34,8 @@ struct delete_acls_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_acls_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -53,9 +52,8 @@ struct delete_acls_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_acls_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/delete_groups.h b/src/v/kafka/protocol/delete_groups.h index 3f9978f69052e..8644a707cf709 100644 --- a/src/v/kafka/protocol/delete_groups.h +++ b/src/v/kafka/protocol/delete_groups.h @@ -28,9 +28,8 @@ struct delete_groups_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_groups_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -53,9 +52,8 @@ struct delete_groups_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_groups_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/delete_records.h b/src/v/kafka/protocol/delete_records.h index 18d9ecf6d2b20..18fdbb3bf6a4f 100644 --- a/src/v/kafka/protocol/delete_records.h +++ b/src/v/kafka/protocol/delete_records.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/delete_records_request.h" #include "kafka/protocol/schemata/delete_records_response.h" @@ -29,9 +30,8 @@ struct delete_records_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_records_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -48,9 +48,8 @@ struct delete_records_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_records_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/delete_topics.h b/src/v/kafka/protocol/delete_topics.h index 26d14298cb9e7..6ef0694a31776 100644 --- a/src/v/kafka/protocol/delete_topics.h +++ b/src/v/kafka/protocol/delete_topics.h @@ -36,9 +36,8 @@ struct delete_topics_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_topics_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -55,9 +54,8 @@ struct delete_topics_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const delete_topics_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_acls.h b/src/v/kafka/protocol/describe_acls.h index 8e5c281ef7f96..cfddfbd03482a 100644 --- a/src/v/kafka/protocol/describe_acls.h +++ b/src/v/kafka/protocol/describe_acls.h @@ -34,9 +34,8 @@ struct describe_acls_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_acls_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -53,9 +52,8 @@ struct describe_acls_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_acls_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_client_quotas.h b/src/v/kafka/protocol/describe_client_quotas.h index d12d6ef8d5e25..5da45c678a831 100644 --- a/src/v/kafka/protocol/describe_client_quotas.h +++ b/src/v/kafka/protocol/describe_client_quotas.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" #include "kafka/protocol/schemata/describe_client_quotas_request.h" #include "kafka/protocol/schemata/describe_client_quotas_response.h" @@ -28,9 +29,8 @@ struct describe_client_quotas_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_client_quotas_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -47,9 +47,8 @@ struct describe_client_quotas_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_client_quotas_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_cluster.h b/src/v/kafka/protocol/describe_cluster.h index 9571b6c29658e..c0304c79332ff 100644 --- a/src/v/kafka/protocol/describe_cluster.h +++ b/src/v/kafka/protocol/describe_cluster.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/describe_cluster_request.h" #include "kafka/protocol/schemata/describe_cluster_response.h" @@ -30,9 +31,8 @@ struct describe_cluster_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_cluster_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -50,9 +50,8 @@ struct describe_cluster_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_cluster_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_configs.h b/src/v/kafka/protocol/describe_configs.h index 83328c5c84a1a..fca846d7dd421 100644 --- a/src/v/kafka/protocol/describe_configs.h +++ b/src/v/kafka/protocol/describe_configs.h @@ -35,9 +35,8 @@ struct describe_configs_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_configs_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -54,9 +53,8 @@ struct describe_configs_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_configs_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_groups.h b/src/v/kafka/protocol/describe_groups.h index 6a97093740418..02e0097dc602f 100644 --- a/src/v/kafka/protocol/describe_groups.h +++ b/src/v/kafka/protocol/describe_groups.h @@ -31,9 +31,8 @@ struct describe_groups_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_groups_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -71,9 +70,8 @@ struct describe_groups_response final { }; } - friend std::ostream& - operator<<(std::ostream& os, const describe_groups_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_log_dirs.h b/src/v/kafka/protocol/describe_log_dirs.h index 9ffa43da0d5fc..22ac1567fce55 100644 --- a/src/v/kafka/protocol/describe_log_dirs.h +++ b/src/v/kafka/protocol/describe_log_dirs.h @@ -34,9 +34,8 @@ struct describe_log_dirs_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_log_dirs_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -53,9 +52,8 @@ struct describe_log_dirs_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_log_dirs_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_producers.h b/src/v/kafka/protocol/describe_producers.h index 9b0f5f11b5760..4a6450fe64e32 100644 --- a/src/v/kafka/protocol/describe_producers.h +++ b/src/v/kafka/protocol/describe_producers.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/describe_producers_request.h" #include "kafka/protocol/schemata/describe_producers_response.h" @@ -31,9 +32,8 @@ struct describe_producers_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_producers_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -50,9 +50,8 @@ struct describe_producers_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_producers_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_transactions.h b/src/v/kafka/protocol/describe_transactions.h index eeea017ee6ae1..a6f3ddb33ebdd 100644 --- a/src/v/kafka/protocol/describe_transactions.h +++ b/src/v/kafka/protocol/describe_transactions.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/describe_transactions_request.h" #include "kafka/protocol/schemata/describe_transactions_response.h" @@ -31,9 +32,8 @@ struct describe_transactions_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_transactions_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -50,9 +50,8 @@ struct describe_transactions_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const describe_transactions_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/describe_user_scram_credentials.h b/src/v/kafka/protocol/describe_user_scram_credentials.h index 58134cf0ec650..6d878a52eb820 100644 --- a/src/v/kafka/protocol/describe_user_scram_credentials.h +++ b/src/v/kafka/protocol/describe_user_scram_credentials.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/describe_user_scram_credentials_request.h" #include "kafka/protocol/schemata/describe_user_scram_credentials_response.h" @@ -28,9 +29,8 @@ struct describe_user_scram_credentials_request final { data.decode(reader, version); } - friend std::ostream& operator<<( - std::ostream& os, const describe_user_scram_credentials_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -47,9 +47,8 @@ struct describe_user_scram_credentials_response final { data.decode(std::move(buf), version); } - friend std::ostream& operator<<( - std::ostream& os, const describe_user_scram_credentials_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; } // namespace kafka diff --git a/src/v/kafka/protocol/end_txn.h b/src/v/kafka/protocol/end_txn.h index a8d247ea8556b..ee52c39b24f5b 100644 --- a/src/v/kafka/protocol/end_txn.h +++ b/src/v/kafka/protocol/end_txn.h @@ -36,9 +36,8 @@ struct end_txn_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const end_txn_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -58,9 +57,8 @@ struct end_txn_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const end_txn_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/errors.cc b/src/v/kafka/protocol/errors.cc index 258c4034b81ba..6eb0d6c4ab9dd 100644 --- a/src/v/kafka/protocol/errors.cc +++ b/src/v/kafka/protocol/errors.cc @@ -9,8 +9,6 @@ #include "kafka/protocol/errors.h" -#include - namespace kafka { std::string_view error_code_to_str(error_code error) { @@ -208,11 +206,6 @@ std::string_view error_code_to_str(error_code error) { } } -std::ostream& operator<<(std::ostream& o, error_code code) { - return o << "{ error_code: " << error_code_to_str(code) << " [" - << (int16_t)code << "] }"; -} - std::error_code make_error_code(kafka::error_code ec) { return {static_cast(ec), error_category()}; } diff --git a/src/v/kafka/protocol/errors.h b/src/v/kafka/protocol/errors.h index a76e0c01c6990..36210c4f33068 100644 --- a/src/v/kafka/protocol/errors.h +++ b/src/v/kafka/protocol/errors.h @@ -10,8 +10,9 @@ */ #pragma once +#include "base/format_to.h" + #include -#include #include #include @@ -241,12 +242,19 @@ enum class error_code : int16_t { transactional_id_not_found = 105, }; -std::ostream& operator<<(std::ostream&, error_code); std::string_view error_code_to_str(error_code error); std::error_code make_error_code(error_code); const std::error_category& error_category() noexcept; bool is_retriable(error_code); +inline fmt::iterator format_to(error_code code, fmt::iterator it) { + return fmt::format_to( + it, + "{{ error_code: {} [{}] }}", + error_code_to_str(code), + static_cast(code)); +} + } // namespace kafka namespace std { diff --git a/src/v/kafka/protocol/fetch.h b/src/v/kafka/protocol/fetch.h index 480eed1c2b647..139c78deaadee 100644 --- a/src/v/kafka/protocol/fetch.h +++ b/src/v/kafka/protocol/fetch.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/likely.h" #include "base/seastarx.h" #include "container/chunked_vector.h" @@ -172,8 +173,8 @@ struct fetch_request final { return const_iterator(data.topics.cend(), data.topics.cend()); } - friend std::ostream& operator<<(std::ostream& os, const fetch_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -312,8 +313,8 @@ struct fetch_response final { iterator end() { return {data.responses.end(), data.responses.end()}; } - friend std::ostream& operator<<(std::ostream& os, const fetch_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/find_coordinator.h b/src/v/kafka/protocol/find_coordinator.h index d847771003a8c..2056f671ca4ea 100644 --- a/src/v/kafka/protocol/find_coordinator.h +++ b/src/v/kafka/protocol/find_coordinator.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/find_coordinator_request.h" #include "kafka/protocol/schemata/find_coordinator_response.h" @@ -49,9 +50,8 @@ struct find_coordinator_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const find_coordinator_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -124,9 +124,8 @@ struct find_coordinator_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const find_coordinator_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/heartbeat.h b/src/v/kafka/protocol/heartbeat.h index 634b20598af5a..6a3103f29c932 100644 --- a/src/v/kafka/protocol/heartbeat.h +++ b/src/v/kafka/protocol/heartbeat.h @@ -36,9 +36,8 @@ struct heartbeat_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const heartbeat_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -65,9 +64,8 @@ struct heartbeat_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const heartbeat_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/incremental_alter_configs.h b/src/v/kafka/protocol/incremental_alter_configs.h index 5e26db3c04b55..a07b509689a57 100644 --- a/src/v/kafka/protocol/incremental_alter_configs.h +++ b/src/v/kafka/protocol/incremental_alter_configs.h @@ -35,9 +35,8 @@ struct incremental_alter_configs_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const incremental_alter_configs_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -54,9 +53,8 @@ struct incremental_alter_configs_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const incremental_alter_configs_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/init_producer_id.h b/src/v/kafka/protocol/init_producer_id.h index 66256634e0c4e..1df61cb5b9b0f 100644 --- a/src/v/kafka/protocol/init_producer_id.h +++ b/src/v/kafka/protocol/init_producer_id.h @@ -36,9 +36,8 @@ struct init_producer_id_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const init_producer_id_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -55,9 +54,8 @@ struct init_producer_id_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const init_producer_id_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/join_group.h b/src/v/kafka/protocol/join_group.h index 1ced1ed84d709..1a857e25468f9 100644 --- a/src/v/kafka/protocol/join_group.h +++ b/src/v/kafka/protocol/join_group.h @@ -50,9 +50,8 @@ struct join_group_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const join_group_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -102,9 +101,8 @@ struct join_group_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const join_group_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/kafka_batch_adapter.cc b/src/v/kafka/protocol/kafka_batch_adapter.cc index 043905aef88e6..5b99d7115e96c 100644 --- a/src/v/kafka/protocol/kafka_batch_adapter.cc +++ b/src/v/kafka/protocol/kafka_batch_adapter.cc @@ -22,8 +22,6 @@ #include -#include - #include namespace kafka { diff --git a/src/v/kafka/protocol/kafka_batch_adapter.h b/src/v/kafka/protocol/kafka_batch_adapter.h index bd0cbe4494f59..8e8dec7218441 100644 --- a/src/v/kafka/protocol/kafka_batch_adapter.h +++ b/src/v/kafka/protocol/kafka_batch_adapter.h @@ -99,18 +99,16 @@ struct produce_request_record_data { adapter.batch = std::move(batch); } - friend std::ostream& - operator<<(std::ostream& os, const produce_request_record_data& data) { - // NOTE: this stream is intentially devoid of user data. - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "batch {{records: {}, size: {}}} v2_format {} valid_crc {}", - data.adapter.batch ? data.adapter.batch->header().record_count : -1, - data.adapter.batch ? data.adapter.batch->size_bytes() : -1, - data.adapter.v2_format, - data.adapter.valid_crc); - return os; + adapter.batch ? adapter.batch->header().record_count : -1, + adapter.batch ? adapter.batch->size_bytes() : -1, + adapter.v2_format, + adapter.valid_crc); } + kafka_batch_adapter adapter; }; diff --git a/src/v/kafka/protocol/leave_group.h b/src/v/kafka/protocol/leave_group.h index 06bdfebb5cac4..b3464e4a237fe 100644 --- a/src/v/kafka/protocol/leave_group.h +++ b/src/v/kafka/protocol/leave_group.h @@ -38,9 +38,8 @@ struct leave_group_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const leave_group_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -67,9 +66,8 @@ struct leave_group_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const leave_group_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/list_groups.h b/src/v/kafka/protocol/list_groups.h index 35409d5a5112f..0f21e9ad11612 100644 --- a/src/v/kafka/protocol/list_groups.h +++ b/src/v/kafka/protocol/list_groups.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/list_groups_request.h" #include "kafka/protocol/schemata/list_groups_response.h" @@ -33,9 +34,8 @@ struct list_groups_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const list_groups_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -52,9 +52,8 @@ struct list_groups_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const list_groups_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/list_offset.h b/src/v/kafka/protocol/list_offset.h index 5b87f6cb0a5d4..7918a3d5190eb 100644 --- a/src/v/kafka/protocol/list_offset.h +++ b/src/v/kafka/protocol/list_offset.h @@ -48,9 +48,8 @@ struct list_offsets_request final { return tp_dups.find(tp) != tp_dups.end(); } - friend std::ostream& - operator<<(std::ostream& os, const list_offsets_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -110,9 +109,8 @@ struct list_offsets_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const list_offsets_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/list_partition_reassignments.h b/src/v/kafka/protocol/list_partition_reassignments.h index 525b62c9a4e52..f7a37938e43dd 100644 --- a/src/v/kafka/protocol/list_partition_reassignments.h +++ b/src/v/kafka/protocol/list_partition_reassignments.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/list_partition_reassignments_request.h" #include "kafka/protocol/schemata/list_partition_reassignments_response.h" @@ -32,9 +33,8 @@ struct list_partition_reassignments_request final { data.decode(reader, version); } - friend std::ostream& operator<<( - std::ostream& os, const list_partition_reassignments_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -51,9 +51,8 @@ struct list_partition_reassignments_response final { data.decode(std::move(buf), version); } - friend std::ostream& operator<<( - std::ostream& os, const list_partition_reassignments_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/list_transactions.h b/src/v/kafka/protocol/list_transactions.h index 586a116386deb..dcd6a1d3a280a 100644 --- a/src/v/kafka/protocol/list_transactions.h +++ b/src/v/kafka/protocol/list_transactions.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/list_transactions_request.h" #include "kafka/protocol/schemata/list_transactions_response.h" @@ -30,9 +31,8 @@ struct list_transactions_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const list_transactions_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -49,9 +49,8 @@ struct list_transactions_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const list_transactions_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/metadata.h b/src/v/kafka/protocol/metadata.h index 377ad9e7fd19f..48daccf56bed9 100644 --- a/src/v/kafka/protocol/metadata.h +++ b/src/v/kafka/protocol/metadata.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/metadata_request.h" #include "kafka/protocol/schemata/metadata_response.h" @@ -70,9 +71,8 @@ struct metadata_request { }; } - friend std::ostream& - operator<<(std::ostream& os, const metadata_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -91,21 +91,10 @@ struct metadata_response { void decode(iobuf buf, api_version version) { data.decode(std::move(buf), version); } -}; - -} // namespace kafka -template<> -struct fmt::formatter { - template - constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } - - template - auto format( - [[maybe_unused]] const kafka::metadata_response& v, - FormatContext& ctx) const -> decltype(ctx.out()) { - return fmt::format_to(ctx.out(), "{}", v.data); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; + +} // namespace kafka diff --git a/src/v/kafka/protocol/offset_commit.h b/src/v/kafka/protocol/offset_commit.h index 472183411f115..fadf3658a3b43 100644 --- a/src/v/kafka/protocol/offset_commit.h +++ b/src/v/kafka/protocol/offset_commit.h @@ -39,9 +39,8 @@ struct offset_commit_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_commit_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -74,9 +73,8 @@ struct offset_commit_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_commit_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/offset_delete.h b/src/v/kafka/protocol/offset_delete.h index a57b32176490d..0c26755b09247 100644 --- a/src/v/kafka/protocol/offset_delete.h +++ b/src/v/kafka/protocol/offset_delete.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "kafka/protocol/schemata/offset_delete_request.h" #include "kafka/protocol/schemata/offset_delete_response.h" #include "model/fundamental.h" @@ -33,9 +34,8 @@ struct offset_delete_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_delete_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -69,9 +69,8 @@ struct offset_delete_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_delete_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/offset_fetch.h b/src/v/kafka/protocol/offset_fetch.h index 5b69bb0b8ea17..8c1d469917ebc 100644 --- a/src/v/kafka/protocol/offset_fetch.h +++ b/src/v/kafka/protocol/offset_fetch.h @@ -39,9 +39,8 @@ struct offset_fetch_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_fetch_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -109,9 +108,8 @@ struct offset_fetch_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_fetch_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/offset_for_leader_epoch.h b/src/v/kafka/protocol/offset_for_leader_epoch.h index 917de16abce3e..20b3250f07a99 100644 --- a/src/v/kafka/protocol/offset_for_leader_epoch.h +++ b/src/v/kafka/protocol/offset_for_leader_epoch.h @@ -31,9 +31,8 @@ struct offset_for_leader_epoch_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_for_leader_epoch_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -119,9 +118,8 @@ struct offset_for_leader_epoch_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const offset_for_leader_epoch_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/produce.h b/src/v/kafka/protocol/produce.h index 8db304b61276c..f618117467fac 100644 --- a/src/v/kafka/protocol/produce.h +++ b/src/v/kafka/protocol/produce.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" #include "kafka/protocol/errors.h" @@ -56,9 +57,8 @@ struct produce_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const produce_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } /** @@ -104,9 +104,8 @@ struct produce_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const produce_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/sasl_authenticate.h b/src/v/kafka/protocol/sasl_authenticate.h index ae349cfff1fee..618551d060c19 100644 --- a/src/v/kafka/protocol/sasl_authenticate.h +++ b/src/v/kafka/protocol/sasl_authenticate.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "kafka/protocol/schemata/sasl_authenticate_request.h" #include "kafka/protocol/schemata/sasl_authenticate_response.h" @@ -33,9 +34,8 @@ struct sasl_authenticate_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const sasl_authenticate_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -57,9 +57,8 @@ struct sasl_authenticate_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const sasl_authenticate_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/sasl_handshake.h b/src/v/kafka/protocol/sasl_handshake.h index 910ceaede3f40..ba06646928485 100644 --- a/src/v/kafka/protocol/sasl_handshake.h +++ b/src/v/kafka/protocol/sasl_handshake.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" #include "kafka/protocol/schemata/sasl_handshake_request.h" @@ -34,9 +35,8 @@ struct sasl_handshake_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const sasl_handshake_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -61,9 +61,8 @@ struct sasl_handshake_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const sasl_handshake_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/schemata/generator.py b/src/v/kafka/protocol/schemata/generator.py index bc043587b4f5a..c880e4752fff0 100755 --- a/src/v/kafka/protocol/schemata/generator.py +++ b/src/v/kafka/protocol/schemata/generator.py @@ -1265,11 +1265,9 @@ def is_error_code(self): #include "model/fundamental.h" #include "model/metadata.h" #include "kafka/protocol/errors.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" -{%- if not struct.is_streamable %} -#include -{%- endif %} {%- for header in struct.headers("header") %} {%- if header.startswith("<") %} @@ -1353,7 +1351,7 @@ class response; {% for struct in struct.structs() %} {{ render_struct(struct) }} {%- if struct.is_streamable %} - friend std::ostream& operator<<(std::ostream&, const {{ struct.name }}&); + fmt::iterator format_to(fmt::iterator) const; {%- endif %} }; @@ -1368,7 +1366,7 @@ class response; void decode(iobuf, api_version); {%- endif %} {%- if struct.is_streamable %} - friend std::ostream& operator<<(std::ostream&, const {{ struct.name }}&); + fmt::iterator format_to(fmt::iterator) const; {%- endif %} {%- if first_flex > 0 %} private: @@ -1417,9 +1415,10 @@ class response; #include +#include "base/format_to.h" + #include #include -#include {%- for header in extra_headers %} {%- if header.startswith("<") %} @@ -1896,19 +1895,18 @@ class response; {% for struct in structs %} {%- if struct.is_streamable %} {%- if struct.fields %} -std::ostream& operator<<(std::ostream& o, [[maybe_unused]] const {{ struct.name }}& v) { - fmt::print(o, +fmt::iterator {{ struct.name }}::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{'{{' + struct.format + '}}'}}", {%- for field in struct.fields %} - {%- if field.is_sensitive %}"****"{% else %}v.{{ field.name }}{% endif %}{% if not loop.last %},{% endif %} + {%- if field.is_sensitive %}"****"{% else %}{{ field.name }}{% endif %}{% if not loop.last %},{% endif %} {%- endfor %} ); - return o; } {%- else %} -std::ostream& operator<<(std::ostream& o, const {{ struct.name }}&) { - return o << "{}"; +fmt::iterator {{ struct.name }}::format_to([[maybe_unused]] fmt::iterator it) const { + return fmt::format_to(it, "{{ '{{}}' }}"); } {%- endif %} {%- endif %} diff --git a/src/v/kafka/protocol/sync_group.h b/src/v/kafka/protocol/sync_group.h index f521d1246834e..4b58103272d22 100644 --- a/src/v/kafka/protocol/sync_group.h +++ b/src/v/kafka/protocol/sync_group.h @@ -38,9 +38,8 @@ struct sync_group_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const sync_group_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -71,9 +70,8 @@ struct sync_group_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const sync_group_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/tests/BUILD b/src/v/kafka/protocol/tests/BUILD index 655724aef874b..7d7dabfc919ba 100644 --- a/src/v/kafka/protocol/tests/BUILD +++ b/src/v/kafka/protocol/tests/BUILD @@ -40,6 +40,7 @@ redpanda_cc_btest( # because then we could use bazel_tools runfiles library env = {"GENERATOR_BIN": "$(rootpath //src/v/test_utils/go/kreq-gen:kafka_request_generator)"}, deps = [ + "//src/v/base", "//src/v/kafka/protocol", "//src/v/kafka/protocol:messages", "//src/v/test_utils:seastar_boost", diff --git a/src/v/kafka/protocol/tests/protocol_test.cc b/src/v/kafka/protocol/tests/protocol_test.cc index d0010a28c5e56..5cdf4f286b828 100644 --- a/src/v/kafka/protocol/tests/protocol_test.cc +++ b/src/v/kafka/protocol/tests/protocol_test.cc @@ -7,6 +7,7 @@ // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0 +#include "base/external_fmt.h" #include "kafka/protocol/add_offsets_to_txn.h" #include "kafka/protocol/add_partitions_to_txn.h" #include "kafka/protocol/alter_client_quotas.h" diff --git a/src/v/kafka/protocol/tests/security_test.cc b/src/v/kafka/protocol/tests/security_test.cc index 9ce92d8a16d76..3ceab708458f5 100644 --- a/src/v/kafka/protocol/tests/security_test.cc +++ b/src/v/kafka/protocol/tests/security_test.cc @@ -16,7 +16,6 @@ #include #include -#include namespace kafka { @@ -127,13 +126,13 @@ BOOST_AUTO_TEST_CASE(to_acl_permission) { BOOST_AUTO_TEST_CASE(redact_sensitive_messages) { BOOST_REQUIRE_EQUAL( - "{auth_bytes=****}", fmt::to_string(sasl_authenticate_request_data{})); + "{auth_bytes=****}", fmt::format("{}", sasl_authenticate_request_data{})); BOOST_REQUIRE_EQUAL( - "{error_code={ error_code: none [0] } error_message={nullopt} " + "{error_code={ error_code: none [0] } error_message=none " "auth_bytes=**** " "session_lifetime_ms=0}", - fmt::to_string(sasl_authenticate_response_data{})); + fmt::format("{}", sasl_authenticate_response_data{})); } } // namespace kafka diff --git a/src/v/kafka/protocol/txn_offset_commit.h b/src/v/kafka/protocol/txn_offset_commit.h index e9a5e286c934f..10a6f889e95b0 100644 --- a/src/v/kafka/protocol/txn_offset_commit.h +++ b/src/v/kafka/protocol/txn_offset_commit.h @@ -39,9 +39,8 @@ struct txn_offset_commit_request final { data.decode(reader, version); } - friend std::ostream& - operator<<(std::ostream& os, const txn_offset_commit_request& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; @@ -74,9 +73,8 @@ struct txn_offset_commit_response final { data.decode(std::move(buf), version); } - friend std::ostream& - operator<<(std::ostream& os, const txn_offset_commit_response& r) { - return os << r.data; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", data); } }; diff --git a/src/v/kafka/protocol/types.cc b/src/v/kafka/protocol/types.cc index 2e26f8c0850e4..e954c25ea8c5e 100644 --- a/src/v/kafka/protocol/types.cc +++ b/src/v/kafka/protocol/types.cc @@ -9,106 +9,3 @@ * by the Apache License, Version 2.0 */ #include "kafka/protocol/types.h" - -namespace kafka { - -std::ostream& operator<<(std::ostream& os, describe_configs_type t) { - switch (t) { - case describe_configs_type::unknown: - return os << "{unknown}"; - case describe_configs_type::boolean: - return os << "{boolean}"; - case describe_configs_type::string: - return os << "{string}"; - case describe_configs_type::int_type: - return os << "{int}"; - case describe_configs_type::short_type: - return os << "{short}"; - case describe_configs_type::long_type: - return os << "{long}"; - case describe_configs_type::double_type: - return os << "{double}"; - case describe_configs_type::list: - return os << "{list}"; - case describe_configs_type::class_type: - return os << "{class}"; - case describe_configs_type::password: - return os << "{password}"; - } - return os << "{unsupported type}"; -} - -std::ostream& -operator<<(std::ostream& os, describe_client_quotas_match_type t) { - switch (t) { - case describe_client_quotas_match_type::exact_name: - return os << "{exact_name}"; - case describe_client_quotas_match_type::default_name: - return os << "{default_name}"; - case describe_client_quotas_match_type::any_specified_name: - return os << "{any_specified_name}"; - } - return os << "{unsupported type}"; -} - -std::ostream& operator<<(std::ostream& os, coordinator_type t) { - switch (t) { - case coordinator_type::group: - return os << "{group}"; - case coordinator_type::transaction: - return os << "{transaction}"; - }; - return os << "{unknown type}"; -} - -std::ostream& operator<<(std::ostream& os, config_resource_type t) { - switch (t) { - case config_resource_type::topic: - return os << "{topic}"; - case config_resource_type::broker: - [[fallthrough]]; - case config_resource_type::broker_logger: - break; - } - return os << "{unknown type}"; -} - -std::ostream& operator<<(std::ostream& os, describe_configs_source s) { - switch (s) { - case describe_configs_source::topic: - return os << "{topic}"; - case describe_configs_source::static_broker_config: - return os << "{static_broker_config}"; - case describe_configs_source::default_config: - return os << "{default_config}"; - } - return os << "{unknown type}"; -} - -std::ostream& operator<<(std::ostream& os, config_resource_operation t) { - switch (t) { - case config_resource_operation::set: - return os << "set"; - case config_resource_operation::append: - return os << "append"; - case config_resource_operation::remove: - return os << "remove"; - case config_resource_operation::subtract: - return os << "subtract"; - } - return os << "unknown type"; -} - -std::ostream& operator<<(std::ostream& os, scram_mechanism m) { - switch (m) { - case scram_mechanism::scram_sha_256: - return os << "SCRAM-SHA-256"; - case scram_mechanism::scram_sha_512: - return os << "SCRAM-SHA-512"; - case scram_mechanism::unknown: - return os << "unknown"; - } - return os << "unsupported type"; -} - -} // namespace kafka diff --git a/src/v/kafka/protocol/types.h b/src/v/kafka/protocol/types.h index f730b3004c2ce..e2a07f252cbd9 100644 --- a/src/v/kafka/protocol/types.h +++ b/src/v/kafka/protocol/types.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "bytes/bytes.h" #include "model/fundamental.h" #include "utils/named_type.h" @@ -135,7 +136,31 @@ enum class describe_configs_type : int8_t { password = 9 }; -std::ostream& operator<<(std::ostream&, describe_configs_type t); +inline fmt::iterator format_to(describe_configs_type t, fmt::iterator out) { + switch (t) { + case describe_configs_type::unknown: + return fmt::format_to(out, "{{unknown}}"); + case describe_configs_type::boolean: + return fmt::format_to(out, "{{boolean}}"); + case describe_configs_type::string: + return fmt::format_to(out, "{{string}}"); + case describe_configs_type::int_type: + return fmt::format_to(out, "{{int}}"); + case describe_configs_type::short_type: + return fmt::format_to(out, "{{short}}"); + case describe_configs_type::long_type: + return fmt::format_to(out, "{{long}}"); + case describe_configs_type::double_type: + return fmt::format_to(out, "{{double}}"); + case describe_configs_type::list: + return fmt::format_to(out, "{{list}}"); + case describe_configs_type::class_type: + return fmt::format_to(out, "{{class}}"); + case describe_configs_type::password: + return fmt::format_to(out, "{{password}}"); + } + return fmt::format_to(out, "{{unsupported type}}"); +} inline const kafka::protocol_type consumer_group_protocol_type("consumer"); @@ -155,7 +180,18 @@ enum class describe_client_quotas_match_type : int8_t { any_specified_name = 2, }; -std::ostream& operator<<(std::ostream&, describe_client_quotas_match_type t); +inline fmt::iterator +format_to(describe_client_quotas_match_type t, fmt::iterator out) { + switch (t) { + case describe_client_quotas_match_type::exact_name: + return fmt::format_to(out, "{{exact_name}}"); + case describe_client_quotas_match_type::default_name: + return fmt::format_to(out, "{{default_name}}"); + case describe_client_quotas_match_type::any_specified_name: + return fmt::format_to(out, "{{any_specified_name}}"); + } + return fmt::format_to(out, "{{unsupported type}}"); +} /* * The names of group states. @@ -171,11 +207,39 @@ inline constexpr std::string_view group_state_name_dead = "Dead"; /// An unknown / missing generation id (Kafka protocol specific) inline constexpr generation_id unknown_generation_id(-1); -std::ostream& operator<<(std::ostream& os, coordinator_type t); +inline fmt::iterator format_to(coordinator_type t, fmt::iterator out) { + switch (t) { + case coordinator_type::group: + return fmt::format_to(out, "{{group}}"); + case coordinator_type::transaction: + return fmt::format_to(out, "{{transaction}}"); + } + return fmt::format_to(out, "{{unknown type}}"); +} -std::ostream& operator<<(std::ostream& os, config_resource_type t); +inline fmt::iterator format_to(config_resource_type t, fmt::iterator out) { + switch (t) { + case config_resource_type::topic: + return fmt::format_to(out, "{{topic}}"); + case config_resource_type::broker: + [[fallthrough]]; + case config_resource_type::broker_logger: + break; + } + return fmt::format_to(out, "{{unknown type}}"); +} -std::ostream& operator<<(std::ostream& os, describe_configs_source s); +inline fmt::iterator format_to(describe_configs_source s, fmt::iterator out) { + switch (s) { + case describe_configs_source::topic: + return fmt::format_to(out, "{{topic}}"); + case describe_configs_source::static_broker_config: + return fmt::format_to(out, "{{static_broker_config}}"); + case describe_configs_source::default_config: + return fmt::format_to(out, "{{default_config}}"); + } + return fmt::format_to(out, "{{unknown type}}"); +} /* * TODO this can be moved out of the protocol library and into the server if the @@ -225,7 +289,19 @@ enum class config_resource_operation : int8_t { subtract = 3, }; -std::ostream& operator<<(std::ostream& os, config_resource_operation); +inline fmt::iterator format_to(config_resource_operation t, fmt::iterator out) { + switch (t) { + case config_resource_operation::set: + return fmt::format_to(out, "set"); + case config_resource_operation::append: + return fmt::format_to(out, "append"); + case config_resource_operation::remove: + return fmt::format_to(out, "remove"); + case config_resource_operation::subtract: + return fmt::format_to(out, "subtract"); + } + return fmt::format_to(out, "unknown type"); +} using scram_user_name = named_type; @@ -235,7 +311,17 @@ enum class scram_mechanism : int8_t { scram_sha_512 = 2, }; -std::ostream& operator<<(std::ostream& os, scram_mechanism); +inline fmt::iterator format_to(scram_mechanism m, fmt::iterator out) { + switch (m) { + case scram_mechanism::scram_sha_256: + return fmt::format_to(out, "SCRAM-SHA-256"); + case scram_mechanism::scram_sha_512: + return fmt::format_to(out, "SCRAM-SHA-512"); + case scram_mechanism::unknown: + return fmt::format_to(out, "unknown"); + } + return fmt::format_to(out, "unsupported type"); +} using topic_authorized_operations = named_type; diff --git a/src/v/kafka/server/BUILD b/src/v/kafka/server/BUILD index 46be093f3b868..71bdac71b629d 100644 --- a/src/v/kafka/server/BUILD +++ b/src/v/kafka/server/BUILD @@ -430,9 +430,9 @@ redpanda_cc_library( ], visibility = ["//visibility:public"], deps = [ + "//src/v/base", "//src/v/container:chunked_vector", "//src/v/model", - "//src/v/utils:to_string", "@seastar", ], ) @@ -503,6 +503,7 @@ redpanda_cc_library( visibility = ["//visibility:public"], deps = [ ":logger", + "//src/v/base", "//src/v/cluster:snapshot", "//src/v/cluster:state_machine_registry", "//src/v/model:batch_utils", diff --git a/src/v/kafka/server/client_quota_translator.cc b/src/v/kafka/server/client_quota_translator.cc index 543bafd94b15c..e8b7897caa652 100644 --- a/src/v/kafka/server/client_quota_translator.cc +++ b/src/v/kafka/server/client_quota_translator.cc @@ -49,94 +49,31 @@ tracker_key make_tracker_key( } } // namespace std::ostream& operator<<(std::ostream& os, const tracker_key& k) { - ss::visit( - k, - [&os](const std::pair& p) mutable { - fmt::print( - os, "k_user{{{}}}, k_client_id{{{}}}", p.first(), p.second()); - }, - [&os](const std::pair& p) mutable { - fmt::print( - os, "k_user{{{}}}, k_group_name{{{}}}", p.first(), p.second()); - }, - [&os](const k_user& u) mutable { fmt::print(os, "k_user{{{}}}", u()); }, - [&os](const k_client_id& c) mutable { - fmt::print(os, "k_client_id{{{}}}", c()); - }, - [&os](const k_group_name& g) mutable { - fmt::print(os, "k_group_name{{{}}}", g()); - }, - [&os](const k_not_applicable&) mutable { - fmt::print(os, "k_not_applicable"); - }); + fmt::print(os, "{}", k); return os; } -std::ostream& operator<<(std::ostream& os, client_quota_type quota_type) { - switch (quota_type) { - case client_quota_type::produce_quota: - return os << "produce_quota"; - case client_quota_type::fetch_quota: - return os << "fetch_quota"; - case client_quota_type::partition_mutation_quota: - return os << "partition_mutation_quota"; - } -} - -std::ostream& operator<<(std::ostream& os, const client_quota_limits& l) { - fmt::print( - os, +fmt::iterator client_quota_limits::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "limits{{produce_limit: {}, fetch_limit: {}, " "partition_mutation_limit: {}}}", - l.produce_limit, - l.fetch_limit, - l.partition_mutation_limit); - return os; + produce_limit, + fetch_limit, + partition_mutation_limit); } -std::ostream& -operator<<(std::ostream& os, const client_quota_request_ctx& ctx) { - fmt::print( - os, +fmt::iterator client_quota_request_ctx::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{quota_type: {}, user: {}, client_id: {}}}", - ctx.q_type, - ctx.user, - ctx.client_id); - return os; + q_type, + user, + client_id); } -std::ostream& operator<<(std::ostream& os, client_quota_rule r) { - switch (r) { - case client_quota_rule::not_applicable: - return os << "not_applicable"; - case client_quota_rule::kafka_client_default: - return os << "kafka_client_default"; - case client_quota_rule::kafka_client_prefix: - return os << "kafka_client_prefix"; - case client_quota_rule::kafka_client_id: - return os << "kafka_client_id"; - case client_quota_rule::kafka_user_default: - return os << "kafka_user_default"; - case client_quota_rule::kafka_user_default_client_default: - return os << "kafka_user_default_client_default"; - case client_quota_rule::kafka_user_default_client_prefix: - return os << "kafka_user_default_client_prefix"; - case client_quota_rule::kafka_user_default_client_id: - return os << "kafka_user_default_client_id"; - case client_quota_rule::kafka_user: - return os << "kafka_user"; - case client_quota_rule::kafka_user_client_default: - return os << "kafka_user_client_default"; - case client_quota_rule::kafka_user_client_prefix: - return os << "kafka_user_client_prefix"; - case client_quota_rule::kafka_user_client_id: - return os << "kafka_user_client_id"; - } -} - -std::ostream& operator<<(std::ostream& os, client_quota_value value) { - fmt::print(os, "{{limit: {}, rule: {}}}", value.limit, value.rule); - return os; +fmt::iterator client_quota_value::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{limit: {}, rule: {}}}", limit, rule); } client_quota_translator::client_quota_translator( diff --git a/src/v/kafka/server/client_quota_translator.h b/src/v/kafka/server/client_quota_translator.h index 26c9bf28a4e04..2094b231bda71 100644 --- a/src/v/kafka/server/client_quota_translator.h +++ b/src/v/kafka/server/client_quota_translator.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "cluster/fwd.h" #include "utils/named_type.h" @@ -19,6 +20,7 @@ #include #include +#include namespace kafka { @@ -61,8 +63,7 @@ struct client_quota_limits { friend bool operator==( const client_quota_limits&, const client_quota_limits&) = default; - friend std::ostream& - operator<<(std::ostream& os, const client_quota_limits& l); + fmt::iterator format_to(fmt::iterator it) const; }; enum class client_quota_type { @@ -76,15 +77,24 @@ inline constexpr std::array all_client_quota_types = { client_quota_type::fetch_quota, client_quota_type::partition_mutation_quota}; -std::ostream& operator<<(std::ostream&, client_quota_type); +inline fmt::iterator format_to(client_quota_type t, fmt::iterator out) { + switch (t) { + case client_quota_type::produce_quota: + return fmt::format_to(out, "produce_quota"); + case client_quota_type::fetch_quota: + return fmt::format_to(out, "fetch_quota"); + case client_quota_type::partition_mutation_quota: + return fmt::format_to(out, "partition_mutation_quota"); + } +} struct client_quota_request_ctx { client_quota_type q_type; std::optional user; std::optional client_id; -}; -std::ostream& operator<<(std::ostream&, const client_quota_request_ctx&); + fmt::iterator format_to(fmt::iterator it) const; +}; /// client_quota_rule is used for reporting metrics to show which type of rule /// is being used for limiting clients @@ -117,14 +127,41 @@ inline constexpr std::array all_client_quota_rules = { client_quota_rule::kafka_user_client_prefix, client_quota_rule::kafka_user_client_id}; -std::ostream& operator<<(std::ostream&, client_quota_rule); +inline fmt::iterator format_to(client_quota_rule r, fmt::iterator out) { + switch (r) { + case client_quota_rule::not_applicable: + return fmt::format_to(out, "not_applicable"); + case client_quota_rule::kafka_client_default: + return fmt::format_to(out, "kafka_client_default"); + case client_quota_rule::kafka_client_prefix: + return fmt::format_to(out, "kafka_client_prefix"); + case client_quota_rule::kafka_client_id: + return fmt::format_to(out, "kafka_client_id"); + case client_quota_rule::kafka_user_default: + return fmt::format_to(out, "kafka_user_default"); + case client_quota_rule::kafka_user_default_client_default: + return fmt::format_to(out, "kafka_user_default_client_default"); + case client_quota_rule::kafka_user_default_client_prefix: + return fmt::format_to(out, "kafka_user_default_client_prefix"); + case client_quota_rule::kafka_user_default_client_id: + return fmt::format_to(out, "kafka_user_default_client_id"); + case client_quota_rule::kafka_user: + return fmt::format_to(out, "kafka_user"); + case client_quota_rule::kafka_user_client_default: + return fmt::format_to(out, "kafka_user_client_default"); + case client_quota_rule::kafka_user_client_prefix: + return fmt::format_to(out, "kafka_user_client_prefix"); + case client_quota_rule::kafka_user_client_id: + return fmt::format_to(out, "kafka_user_client_id"); + } +} struct client_quota_value { std::optional limit; client_quota_rule rule; -}; -std::ostream& operator<<(std::ostream&, client_quota_value); + fmt::iterator format_to(fmt::iterator it) const; +}; /// client_quota_translator is responsible for providing quota_manager with a /// simplified interface to the quota configurations @@ -171,3 +208,48 @@ class client_quota_translator { }; } // namespace kafka + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + + template + auto format(const kafka::tracker_key& key, Ctx& ctx) const { + return std::visit( + [&ctx](const auto& value) { + using value_t = std::remove_cvref_t; + if constexpr ( + std::is_same_v< + value_t, + std::pair>) { + return fmt::format_to( + ctx.out(), + "k_user{{{}}}, k_client_id{{{}}}", + value.first, + value.second); + } else if constexpr ( + std::is_same_v< + value_t, + std::pair>) { + return fmt::format_to( + ctx.out(), + "k_user{{{}}}, k_group_name{{{}}}", + value.first, + value.second); + } else if constexpr (std::is_same_v) { + return fmt::format_to(ctx.out(), "k_user{{{}}}", value); + } else if constexpr ( + std::is_same_v) { + return fmt::format_to(ctx.out(), "k_client_id{{{}}}", value); + } else if constexpr ( + std::is_same_v) { + return fmt::format_to(ctx.out(), "k_group_name{{{}}}", value); + } else { + return fmt::format_to(ctx.out(), "k_not_applicable"); + } + }, + key); + } +}; diff --git a/src/v/kafka/server/connection_context.cc b/src/v/kafka/server/connection_context.cc index 053660d4614d5..0ef94b404cf12 100644 --- a/src/v/kafka/server/connection_context.cc +++ b/src/v/kafka/server/connection_context.cc @@ -1233,13 +1233,12 @@ ss::future<> connection_context::client_protocol_state::maybe_process_responses( }); } -std::ostream& operator<<(std::ostream& o, const virtual_connection_id& id) { - fmt::print( - o, +fmt::iterator virtual_connection_id::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{virtual_cluster_id: {}, connection_id: {}}}", - id.virtual_cluster_id, - id.connection_id); - return o; + virtual_cluster_id, + connection_id); } void last_value::update(std::optional new_value) { diff --git a/src/v/kafka/server/connection_context.h b/src/v/kafka/server/connection_context.h index 0d4eb2ac1829d..56b8e02b098f1 100644 --- a/src/v/kafka/server/connection_context.h +++ b/src/v/kafka/server/connection_context.h @@ -143,8 +143,7 @@ struct virtual_connection_id { friend bool operator==( const virtual_connection_id&, const virtual_connection_id&) = default; - friend std::ostream& - operator<<(std::ostream& o, const virtual_connection_id& id); + fmt::iterator format_to(fmt::iterator it) const; }; class last_value { diff --git a/src/v/kafka/server/datalake_throttle_manager.cc b/src/v/kafka/server/datalake_throttle_manager.cc index aac2c391e3705..4b296d4319434 100644 --- a/src/v/kafka/server/datalake_throttle_manager.cc +++ b/src/v/kafka/server/datalake_throttle_manager.cc @@ -65,14 +65,13 @@ bool datalake_throttle_manager::needs_throttling() const { > ratio.value() * _disk_space_info->total; } -std::ostream& -operator<<(std::ostream& o, const datalake_throttle_manager::status& s) { - fmt::print( - o, +fmt::iterator +datalake_throttle_manager::status::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{max_shares_assigned: {}, total_translation_backlog: {}}}", - s.max_shares_assigned, - human::bytes(s.total_translation_backlog)); - return o; + max_shares_assigned, + human::bytes(total_translation_backlog)); } datalake_throttle_manager::datalake_throttle_manager( diff --git a/src/v/kafka/server/datalake_throttle_manager.h b/src/v/kafka/server/datalake_throttle_manager.h index db37b344432db..6948b5c248af4 100644 --- a/src/v/kafka/server/datalake_throttle_manager.h +++ b/src/v/kafka/server/datalake_throttle_manager.h @@ -8,6 +8,7 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/property.h" #include "container/chunked_hash_map.h" @@ -41,7 +42,7 @@ class datalake_throttle_manager status operator+(const status& o) const; - friend std::ostream& operator<<(std::ostream&, const status&); + fmt::iterator format_to(fmt::iterator it) const; }; using clock_type = ss::lowres_clock; diff --git a/src/v/kafka/server/datalake_usage_api.cc b/src/v/kafka/server/datalake_usage_api.cc index 993d461378bdd..aecbf1eda98bb 100644 --- a/src/v/kafka/server/datalake_usage_api.cc +++ b/src/v/kafka/server/datalake_usage_api.cc @@ -10,8 +10,6 @@ #include "kafka/server/datalake_usage_api.h" -#include "utils/to_string.h" - namespace kafka { datalake_usage_api::usage_stats::usage_stats(const usage_stats& other) { @@ -36,39 +34,23 @@ datalake_usage_api::usage_stats& datalake_usage_api::usage_stats::operator=( return *this; } -std::ostream& operator<<( - std::ostream& os, const datalake_usage_api::stats_missing_reason& r) { - switch (r) { - case datalake_usage_api::stats_missing_reason::none: - return os << "none"; - case datalake_usage_api::stats_missing_reason::feature_disabled: - return os << "feature_disabled"; - case datalake_usage_api::stats_missing_reason::collection_error: - return os << "collection_error"; - case datalake_usage_api::stats_missing_reason::not_controller_leader: - return os << "not_controller_leader"; - } -} - -std::ostream& -operator<<(std::ostream& os, const datalake_usage_api::topic_usage& u) { - fmt::print( - os, +fmt::iterator +datalake_usage_api::topic_usage::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ topic: {} revision: {} kafka_bytes_processed: {} }}", - u.topic, - u.revision, - u.kafka_bytes_processed); - return os; + topic, + revision, + kafka_bytes_processed); } -std::ostream& -operator<<(std::ostream& os, const datalake_usage_api::usage_stats& u) { - fmt::print( - os, +fmt::iterator +datalake_usage_api::usage_stats::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ topic_stats: {}, missing_stats_reason: {} }}", - u.topic_stats, - u.missing_reason); - return os; + topic_stats, + missing_reason); } } // namespace kafka diff --git a/src/v/kafka/server/datalake_usage_api.h b/src/v/kafka/server/datalake_usage_api.h index ec07f28754141..bf27d41128d46 100644 --- a/src/v/kafka/server/datalake_usage_api.h +++ b/src/v/kafka/server/datalake_usage_api.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "container/chunked_vector.h" #include "model/fundamental.h" @@ -39,7 +40,7 @@ class datalake_usage_api { friend bool operator==(const topic_usage&, const topic_usage&) = default; - friend std::ostream& operator<<(std::ostream& os, const topic_usage& u); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(topic, revision, kafka_bytes_processed); @@ -57,7 +58,18 @@ class datalake_usage_api { not_controller_leader = 3, }; - friend std::ostream& operator<<(std::ostream&, const stats_missing_reason&); + friend fmt::iterator format_to(stats_missing_reason r, fmt::iterator out) { + switch (r) { + case stats_missing_reason::none: + return fmt::format_to(out, "none"); + case stats_missing_reason::feature_disabled: + return fmt::format_to(out, "feature_disabled"); + case stats_missing_reason::collection_error: + return fmt::format_to(out, "collection_error"); + case stats_missing_reason::not_controller_leader: + return fmt::format_to(out, "not_controller_leader"); + } + } /** * Usage statistics for a datalake @@ -80,7 +92,7 @@ class datalake_usage_api { friend bool operator==(const usage_stats&, const usage_stats&) = default; - friend std::ostream& operator<<(std::ostream& os, const usage_stats& u); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(topic_stats, missing_reason); } }; diff --git a/src/v/kafka/server/group.cc b/src/v/kafka/server/group.cc index eeab9226b9630..8be8ece341db1 100644 --- a/src/v/kafka/server/group.cc +++ b/src/v/kafka/server/group.cc @@ -48,7 +48,6 @@ #include #include #include -#include #include #include @@ -2823,55 +2822,49 @@ error_code group::validate_existing_member( return error_code::none; } -std::ostream& operator<<(std::ostream& o, const group& g) { - const auto ntp = [&g] { - if (g._partition) { - return fmt::format("{}", g._partition->ntp()); +fmt::iterator group::format_to(fmt::iterator it) const { + const auto ntp_str = [this] { + if (_partition) { + return fmt::format("{}", _partition->ntp()); } else { return std::string(""); } }(); - auto timer_expires = - [](const auto& timer) -> std::optional { + auto timer_expires = [](const auto& timer) -> std::optional { if (timer.armed()) { - return timer.get_timeout() - group::clock_type::now(); + return timer.get_timeout() - clock_type::now(); } return std::nullopt; }; - fmt::print( - o, + it = fmt::format_to( + it, "id={} state={} gen={} proto_type={} proto={} leader={} " "empty={} ntp={} num_members_joining={} new_member_added={} " "join_timer={}", - g.id(), - g.state(), - g.generation(), - g.protocol_type(), - g.protocol(), - g.leader(), - !g.has_members(), - ntp, - g._num_members_joining, - g._new_member_added, - timer_expires(g._join_timer)); + id(), + state(), + generation(), + protocol_type(), + protocol(), + leader(), + !has_members(), + ntp_str, + _num_members_joining, + _new_member_added, + timer_expires(_join_timer)); - fmt::print(o, " pending members ["); - for (const auto& m : g._pending_members) { - fmt::print(o, "{} expires={} ", m.first, timer_expires(m.second)); + it = fmt::format_to(it, " pending members ["); + for (const auto& m : _pending_members) { + it = fmt::format_to( + it, "{} expires={} ", m.first, timer_expires(m.second)); } - fmt::print(o, "] full members ["); - for (const auto& m : g._members) { - fmt::print(o, "{} ", m.second); + it = fmt::format_to(it, "] full members ["); + for (const auto& m : _members) { + it = fmt::format_to(it, "{} ", m.second); } - fmt::print(o, "]"); - - return o; -} - -std::ostream& operator<<(std::ostream& o, group_state gs) { - return o << group_state_to_kafka_name(gs); + return fmt::format_to(it, "]"); } ss::sstring group_state_to_kafka_name(group_state gs) { @@ -3383,16 +3376,15 @@ void group::try_arm(time_point_type deadline) { } } -std::ostream& operator<<(std::ostream& o, const group::offset_metadata& md) { - fmt::print( - o, +fmt::iterator group::offset_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{log_offset:{}, offset:{}, metadata:{}, " "committed_leader_epoch:{}}}", - md.log_offset, - md.offset, - md.metadata, - md.committed_leader_epoch); - return o; + log_offset, + offset, + metadata, + committed_leader_epoch); } bool group::subscribed(const model::topic& topic) const { diff --git a/src/v/kafka/server/group.h b/src/v/kafka/server/group.h index f1403fb314749..40adceff6df5d 100644 --- a/src/v/kafka/server/group.h +++ b/src/v/kafka/server/group.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/node_hash_map.h" #include "absl/container/node_hash_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "cluster/fwd.h" #include "cluster/simple_batch_builder.h" @@ -42,6 +43,7 @@ #include #include +#include #include #include #include @@ -107,7 +109,25 @@ enum class group_state { dead, }; -std::ostream& operator<<(std::ostream&, group_state gs); +inline std::string_view to_string_view(group_state gs) { + switch (gs) { + case group_state::empty: + return group_state_name_empty; + case group_state::preparing_rebalance: + return group_state_name_preparing_rebalance; + case group_state::completing_rebalance: + return group_state_name_completing_rebalance; + case group_state::stable: + return group_state_name_stable; + case group_state::dead: + return group_state_name_dead; + } + std::terminate(); +} + +inline fmt::iterator format_to(group_state gs, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(gs)); +} ss::sstring group_state_to_kafka_name(group_state); std::optional group_state_from_kafka_name(std::string_view); @@ -222,7 +242,7 @@ class group final : public ss::enable_lw_shared_from_this { */ bool non_reclaimable{false}; - friend std::ostream& operator<<(std::ostream&, const offset_metadata&); + fmt::iterator format_to(fmt::iterator it) const; }; struct offset_metadata_with_probe { @@ -729,12 +749,12 @@ class group final : public ss::enable_lw_shared_from_this { */ ss::future abort_txes(bool expired_only); + fmt::iterator format_to(fmt::iterator it) const; + private: using member_map = absl::node_hash_map; using protocol_support = absl::node_hash_map; - friend std::ostream& operator<<(std::ostream&, const group&); - class ctx_log { public: explicit ctx_log(ss::logger& logger, const group& group) diff --git a/src/v/kafka/server/group_metadata.cc b/src/v/kafka/server/group_metadata.cc index 1a19eea773a12..862487cba8733 100644 --- a/src/v/kafka/server/group_metadata.cc +++ b/src/v/kafka/server/group_metadata.cc @@ -25,7 +25,6 @@ #include "utils/to_string.h" #include -#include #include #include @@ -336,18 +335,13 @@ iobuf maybe_unwrap_from_iobuf(iobuf buffer) { } } // namespace -std::ostream& operator<<(std::ostream& o, const group_block_info& gbi) { - fmt::print( - o, - "{{is_blocked: {}, revision_id: {}}}", - gbi.is_blocked, - gbi.revision_id); - return o; +fmt::iterator group_block_info::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{is_blocked: {}, revision_id: {}}}", is_blocked, revision_id); } -std::ostream& operator<<(std::ostream& o, const group_block& block) { - fmt::print(o, "{{group_id: {}, info: {}}}", block.group_id, block.info); - return o; +fmt::iterator group_block::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{group_id: {}, info: {}}}", group_id, info); } group_block::group_block(kafka::group_id group_id, group_block_info info) @@ -422,105 +416,100 @@ offset_metadata_kv decode_offset_metadata(model::record record) { } } // namespace group_metadata_serializer -std::ostream& operator<<(std::ostream& o, const member_state& v) { - fmt::print( - o, +fmt::iterator member_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, instance_id: {}, client_id: {}, client_host: {}, " "rebalance_timeout: {}, session_timeout: {}, subscription_size: {}, " "assignment_size: {}}}", - v.id, - v.instance_id, - v.client_id, - v.client_host, - v.rebalance_timeout, - v.session_timeout, - v.subscription.size_bytes(), - v.assignment.size_bytes()); - return o; -} -std::ostream& operator<<(std::ostream& o, const group_metadata_key& v) { - fmt::print(o, "{{group_id: {}}}", v.group_id); - return o; -} -std::ostream& operator<<(std::ostream& o, const group_metadata_value& v) { - fmt::print( - o, + id, + instance_id, + client_id, + client_host, + rebalance_timeout, + session_timeout, + subscription.size_bytes(), + assignment.size_bytes()); +} + +fmt::iterator group_metadata_key::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{group_id: {}}}", group_id); +} + +fmt::iterator group_metadata_value::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{protocol_type: {}, generation: {}, protocol: {}, leader: {}, " "state_timestamp: {}, member count: {}}}", - v.protocol_type, - v.generation, - v.protocol, - v.leader, - v.state_timestamp, - v.members.size()); - return o; -} -std::ostream& operator<<(std::ostream& o, const offset_metadata_key& v) { - fmt::print( - o, + protocol_type, + generation, + protocol, + leader, + state_timestamp, + members.size()); +} + +fmt::iterator offset_metadata_key::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{group_id: {}, topic: {}, partition: {}}}", - v.group_id, - v.topic, - v.partition); - return o; -} -std::ostream& operator<<(std::ostream& o, const offset_metadata_value& v) { - fmt::print( - o, + group_id, + topic, + partition); +} +fmt::iterator offset_metadata_value::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{offset: {}, leader_epoch: {}, metadata: {}, commit_timestamp: {}, " "expiry_timestamp: {}}}", - v.offset, - v.leader_epoch, - v.metadata, - v.commit_timestamp, - v.expiry_timestamp); - return o; + offset, + leader_epoch, + metadata, + commit_timestamp, + expiry_timestamp); } namespace group_tx { -std::ostream& operator<<(std::ostream& o, const offsets_metadata& md) { - fmt::print( - o, +fmt::iterator offsets_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{group_id: {}, pid: {}, tx_seq: {}, offsets: {}}}", - md.group_id, - md.pid, - md.tx_seq, - fmt::join(md.offsets, ", ")); - return o; + group_id, + pid, + tx_seq, + fmt::join(offsets, ", ")); } -std::ostream& operator<<(std::ostream& o, const partition_offset& po) { - fmt::print( - o, +fmt::iterator partition_offset::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{partition: {}, offset: {}, leader_epoch: {}, metadata: {}}}", - po.tp, - po.offset, - po.leader_epoch, - po.metadata); - return o; -} -std::ostream& operator<<(std::ostream& o, const fence_metadata_v0& fence) { - fmt::print(o, "{{group_id: {}}}", fence.group_id); - return o; -} -std::ostream& operator<<(std::ostream& o, const fence_metadata_v1& fence) { - fmt::print( - o, + tp, + offset, + leader_epoch, + metadata); +} + +fmt::iterator fence_metadata_v0::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{group_id: {}}}", group_id); +} + +fmt::iterator fence_metadata_v1::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{group_id: {}, tx_seq: {}, tx_timeout: {} ms}}", - fence.group_id, - fence.tx_seq, - fence.transaction_timeout_ms.count()); - return o; + group_id, + tx_seq, + transaction_timeout_ms.count()); } -std::ostream& operator<<(std::ostream& o, const fence_metadata& fence) { - fmt::print( - o, +fmt::iterator fence_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{tm_partition: {}, group_id: {}, tx_seq: {}, tx_timeout: {} ms}}", - fence.tm_partition, - fence.group_id, - fence.tx_seq, - fence.transaction_timeout_ms); - return o; + tm_partition, + group_id, + tx_seq, + transaction_timeout_ms); } } // namespace group_tx } // namespace kafka diff --git a/src/v/kafka/server/group_metadata.h b/src/v/kafka/server/group_metadata.h index 970bc9b6b1040..d0dc9e19fdcb8 100644 --- a/src/v/kafka/server/group_metadata.h +++ b/src/v/kafka/server/group_metadata.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" #include "kafka/protocol/wire.h" @@ -57,7 +58,7 @@ struct member_state { .assignment = assignment.copy(), }; } - friend std::ostream& operator<<(std::ostream&, const member_state&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const member_state&, const member_state&) = default; @@ -72,7 +73,7 @@ struct group_metadata_key { static constexpr group_metadata_version version{2}; kafka::group_id group_id; - friend std::ostream& operator<<(std::ostream&, const group_metadata_key&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const group_metadata_key&, const group_metadata_key&) = default; static group_metadata_key decode(protocol::decoder&); @@ -109,7 +110,7 @@ struct group_metadata_value { return ret; } - friend std::ostream& operator<<(std::ostream&, const group_metadata_value&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const group_metadata_value&, const group_metadata_value&) = default; @@ -123,7 +124,7 @@ struct offset_metadata_key { model::topic topic; model::partition_id partition; - friend std::ostream& operator<<(std::ostream&, const offset_metadata_key&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const offset_metadata_key&, const offset_metadata_key&) = default; static offset_metadata_key decode(protocol::decoder&); @@ -150,8 +151,7 @@ struct offset_metadata_value { */ bool non_reclaimable{true}; - friend std::ostream& - operator<<(std::ostream&, const offset_metadata_value&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==( const offset_metadata_value&, const offset_metadata_value&) = default; static offset_metadata_value decode(protocol::decoder&); @@ -186,7 +186,7 @@ struct group_block_info friend bool operator==(const group_block_info&, const group_block_info&) = default; - friend std::ostream& operator<<(std::ostream&, const group_block_info&); + fmt::iterator format_to(fmt::iterator it) const; }; struct group_block { kafka::group_id group_id; @@ -196,7 +196,7 @@ struct group_block { explicit group_block(model::record record); void add_to_batch_builder(storage::record_batch_builder&) const; - friend std::ostream& operator<<(std::ostream&, const group_block&); + fmt::iterator format_to(fmt::iterator it) const; }; namespace group_metadata_serializer { struct key_value { @@ -214,14 +214,14 @@ namespace group_tx { struct fence_metadata_v0 { kafka::group_id group_id; - friend std::ostream& operator<<(std::ostream&, const fence_metadata_v0&); + fmt::iterator format_to(fmt::iterator it) const; }; struct fence_metadata_v1 { kafka::group_id group_id; model::tx_seq tx_seq; model::timeout_clock::duration transaction_timeout_ms; - friend std::ostream& operator<<(std::ostream&, const fence_metadata_v1&); + fmt::iterator format_to(fmt::iterator it) const; }; /** * Fence is set by the transaction manager when consumer adds an offset to @@ -232,7 +232,7 @@ struct fence_metadata { model::tx_seq tx_seq; model::timeout_clock::duration transaction_timeout_ms; model::partition_id tm_partition; - friend std::ostream& operator<<(std::ostream&, const fence_metadata&); + fmt::iterator format_to(fmt::iterator it) const; }; /** * Single partition committed offset @@ -242,7 +242,7 @@ struct partition_offset { model::offset offset; int32_t leader_epoch; std::optional metadata; - friend std::ostream& operator<<(std::ostream&, const partition_offset&); + fmt::iterator format_to(fmt::iterator it) const; }; /** * Consumer offsets commited as a part of transaction @@ -252,7 +252,7 @@ struct offsets_metadata { model::producer_identity pid; model::tx_seq tx_seq; std::vector offsets; - friend std::ostream& operator<<(std::ostream&, const offsets_metadata&); + fmt::iterator format_to(fmt::iterator it) const; }; /** diff --git a/src/v/kafka/server/handlers/alter_configs.cc b/src/v/kafka/server/handlers/alter_configs.cc index 02a22235353af..1a69d32eeea7d 100644 --- a/src/v/kafka/server/handlers/alter_configs.cc +++ b/src/v/kafka/server/handlers/alter_configs.cc @@ -36,8 +36,6 @@ #include #include -#include - #include namespace kafka { diff --git a/src/v/kafka/server/handlers/create_topics.cc b/src/v/kafka/server/handlers/create_topics.cc index 1fab43552a1d1..7dcf2eec0a20a 100644 --- a/src/v/kafka/server/handlers/create_topics.cc +++ b/src/v/kafka/server/handlers/create_topics.cc @@ -33,8 +33,6 @@ #include #include -#include - #include #include #include diff --git a/src/v/kafka/server/handlers/delete_acls.cc b/src/v/kafka/server/handlers/delete_acls.cc index a973fcd523095..b311ffce26e1d 100644 --- a/src/v/kafka/server/handlers/delete_acls.cc +++ b/src/v/kafka/server/handlers/delete_acls.cc @@ -24,8 +24,6 @@ #include #include -#include - using namespace std::chrono_literals; namespace kafka { diff --git a/src/v/kafka/server/handlers/describe_acls.cc b/src/v/kafka/server/handlers/describe_acls.cc index 32bb52e54d28e..c97e30a99ec5c 100644 --- a/src/v/kafka/server/handlers/describe_acls.cc +++ b/src/v/kafka/server/handlers/describe_acls.cc @@ -21,8 +21,6 @@ #include #include -#include - namespace kafka { static void fill_response( diff --git a/src/v/kafka/server/handlers/describe_log_dirs.cc b/src/v/kafka/server/handlers/describe_log_dirs.cc index 0d61717818313..ba74cf98dcdac 100644 --- a/src/v/kafka/server/handlers/describe_log_dirs.cc +++ b/src/v/kafka/server/handlers/describe_log_dirs.cc @@ -23,8 +23,6 @@ #include #include -#include - namespace kafka { struct log_partition_data { diff --git a/src/v/kafka/server/handlers/fetch.cc b/src/v/kafka/server/handlers/fetch.cc index 6b398d016deb0..16ffb15f25010 100644 --- a/src/v/kafka/server/handlers/fetch.cc +++ b/src/v/kafka/server/handlers/fetch.cc @@ -51,7 +51,6 @@ #include #include -#include #include #include @@ -1913,8 +1912,8 @@ fetch_scheduling_group_provider(const connection_context& conn_ctx) { return conn_ctx.server().fetch_scheduling_group(); } -std::ostream& operator<<(std::ostream& o, const consumer_info& ci) { - fmt::print(o, "rack_id: {}, fetch_offset: {}", ci.rack_id, ci.fetch_offset); - return o; +fmt::iterator consumer_info::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "rack_id: {}, fetch_offset: {}", rack_id, fetch_offset); } } // namespace kafka diff --git a/src/v/kafka/server/handlers/fetch.h b/src/v/kafka/server/handlers/fetch.h index b71b09372c0ad..2a92dd1f64f37 100644 --- a/src/v/kafka/server/handlers/fetch.h +++ b/src/v/kafka/server/handlers/fetch.h @@ -239,26 +239,25 @@ struct fetch_config { abort_source; model::opt_client_address_t client_address; - friend std::ostream& operator<<(std::ostream& o, const fetch_config& cfg) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, R"({{"start_offset": {}, "max_offset": {}, "isolation_lvl": {}, "max_bytes": {}, "max_batch_size": {}, "strict_max_bytes": {}, "skip_read": {}, "current_leader_epoch:" {}, "follower_read:" {}, "consumer_rack_id": {}, "abortable": {}, "aborted": {}, "client_address": {}}})", - cfg.start_offset, - cfg.max_offset, - cfg.isolation_level, - cfg.max_bytes, - cfg.max_batch_size, - cfg.strict_max_bytes, - cfg.skip_read, - cfg.current_leader_epoch, - cfg.read_from_follower, - cfg.consumer_rack_id, - cfg.abort_source.has_value(), - cfg.abort_source.has_value() - ? cfg.abort_source.value().get().abort_requested() + start_offset, + max_offset, + isolation_level, + max_bytes, + max_batch_size, + strict_max_bytes, + skip_read, + current_leader_epoch, + read_from_follower, + consumer_rack_id, + abort_source.has_value(), + abort_source.has_value() + ? abort_source.value().get().abort_requested() : false, - cfg.client_address.value_or(model::client_address_t{})); - return o; + client_address.value_or(model::client_address_t{})); } }; @@ -271,10 +270,8 @@ struct ntp_fetch_config { const model::ktp& ktp() const { return _ktp; } - friend std::ostream& - operator<<(std::ostream& o, const ntp_fetch_config& ntp_fetch) { - fmt::print(o, R"({{"{}": {}}})", ntp_fetch.ktp(), ntp_fetch.cfg); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, R"({{"{}": {}}})", ktp(), cfg); } }; @@ -400,9 +397,8 @@ struct shard_fetch { chunked_vector responses; op_context::latency_point start_time; - friend std::ostream& operator<<(std::ostream& o, const shard_fetch& sf) { - fmt::print(o, "{}", sf.requests); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", requests); } }; @@ -426,23 +422,20 @@ struct fetch_plan { } } - friend std::ostream& operator<<(std::ostream& o, const fetch_plan& plan) { - fmt::print(o, "{{["); - if (!plan.fetches_per_shard.empty()) { - fmt::print( - o, - R"({{"shard": 0, "requests": [{}]}})", - plan.fetches_per_shard[0]); - for (size_t i = 1; i < plan.fetches_per_shard.size(); ++i) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{["); + if (!fetches_per_shard.empty()) { + it = fmt::format_to( + it, R"({{"shard": 0, "requests": [{}]}})", fetches_per_shard[0]); + for (size_t i = 1; i < fetches_per_shard.size(); ++i) { + it = fmt::format_to( + it, R"(, {{"shard": {}, "requests": [{}]}})", i, - plan.fetches_per_shard[i]); + fetches_per_shard[i]); } } - fmt::print(o, "]}}"); - return o; + return fmt::format_to(it, "]}}"); } }; diff --git a/src/v/kafka/server/handlers/fetch/replica_selector.h b/src/v/kafka/server/handlers/fetch/replica_selector.h index 4d186fa20ab14..0f41b7f0b7d2a 100644 --- a/src/v/kafka/server/handlers/fetch/replica_selector.h +++ b/src/v/kafka/server/handlers/fetch/replica_selector.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "cluster/fwd.h" #include "kafka/data/partition_proxy.h" #include "model/fundamental.h" @@ -22,7 +23,7 @@ namespace kafka { struct consumer_info { model::offset fetch_offset; std::optional rack_id; - friend std::ostream& operator<<(std::ostream&, const consumer_info&); + fmt::iterator format_to(fmt::iterator it) const; }; struct replica_selector { diff --git a/src/v/kafka/server/handlers/find_coordinator.cc b/src/v/kafka/server/handlers/find_coordinator.cc index 7ece4d409d552..161005bf19464 100644 --- a/src/v/kafka/server/handlers/find_coordinator.cc +++ b/src/v/kafka/server/handlers/find_coordinator.cc @@ -21,8 +21,6 @@ #include -#include - #include #include diff --git a/src/v/kafka/server/handlers/incremental_alter_configs.cc b/src/v/kafka/server/handlers/incremental_alter_configs.cc index 371541f3dc444..09a9215502219 100644 --- a/src/v/kafka/server/handlers/incremental_alter_configs.cc +++ b/src/v/kafka/server/handlers/incremental_alter_configs.cc @@ -32,8 +32,6 @@ #include #include -#include - #include namespace kafka { diff --git a/src/v/kafka/server/handlers/metadata.cc b/src/v/kafka/server/handlers/metadata.cc index c8e6254837f08..efa06107a4b9a 100644 --- a/src/v/kafka/server/handlers/metadata.cc +++ b/src/v/kafka/server/handlers/metadata.cc @@ -38,7 +38,6 @@ #include #include -#include #include #include diff --git a/src/v/kafka/server/handlers/produce.cc b/src/v/kafka/server/handlers/produce.cc index bd447882b36c1..304d8c2e324ea 100644 --- a/src/v/kafka/server/handlers/produce.cc +++ b/src/v/kafka/server/handlers/produce.cc @@ -35,7 +35,6 @@ #include #include -#include #include #include diff --git a/src/v/kafka/server/handlers/produce_validation.cc b/src/v/kafka/server/handlers/produce_validation.cc index 32699a6b0b713..46308e8a0a306 100644 --- a/src/v/kafka/server/handlers/produce_validation.cc +++ b/src/v/kafka/server/handlers/produce_validation.cc @@ -336,7 +336,7 @@ std::optional validate_batch( "is set to '{}'. It is strongly recommended that you update your " "client to set the max_timestamp when producing: {}.", ntp, - client_id, + client_id.value_or(""), config::shard_local_cfg().kafka_produce_batch_validation.name(), validation_mode, batch.header()); @@ -367,7 +367,7 @@ std::optional validate_batch( "strongly recommended that you update your client to set the " "max_timestamp when producing: {}.", ntp, - client_id, + client_id.value_or(""), config::shard_local_cfg().kafka_produce_batch_validation.name(), validation_mode, batch.header()); diff --git a/src/v/kafka/server/member.cc b/src/v/kafka/server/member.cc index a608e686d9c38..690772f53a435 100644 --- a/src/v/kafka/server/member.cc +++ b/src/v/kafka/server/member.cc @@ -22,8 +22,6 @@ #include -#include - #include #include @@ -75,34 +73,32 @@ bool group_member::should_keep_alive( return false; } -std::ostream& operator<<(std::ostream& o, const group_member& m) { - auto timer_expires = - [](const auto& timer) -> std::optional { +fmt::iterator group_member::format_to(fmt::iterator it) const { + auto timer_expires = [](const auto& timer) -> std::optional { if (timer.armed()) { - return timer.get_timeout() - group_member::clock_type::now(); + return timer.get_timeout() - clock_type::now(); } return std::nullopt; }; - fmt::print( - o, + return fmt::format_to( + it, "id={} group={} group_inst={} proto_type={} assignment_len={} " "timeouts={}/{} protocols={} is_new={} joining={} syncing={} " "latest_heartbeat={} expires={}", - m.id(), - m.group_id(), - m.group_instance_id(), - m.protocol_type(), - m.assignment().size(), - m.session_timeout(), - m.rebalance_timeout(), - m._protocols, - m._is_new, - m.is_joining(), - m.is_syncing(), - m._latest_heartbeat.time_since_epoch(), - timer_expires(m._expire_timer)); - return o; + id(), + group_id(), + group_instance_id(), + protocol_type(), + assignment().size(), + session_timeout(), + rebalance_timeout(), + _protocols, + _is_new, + is_joining(), + is_syncing(), + _latest_heartbeat.time_since_epoch(), + timer_expires(_expire_timer)); } described_group_member diff --git a/src/v/kafka/server/member.h b/src/v/kafka/server/member.h index 5a0d6c431cd82..70457a77321f7 100644 --- a/src/v/kafka/server/member.h +++ b/src/v/kafka/server/member.h @@ -10,7 +10,9 @@ */ #pragma once + #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "bytes/bytes.h" #include "bytes/iobuf.h" #include "container/chunked_vector.h" @@ -44,9 +46,8 @@ struct member_protocol { return name == o.name && metadata == o.metadata; } - friend std::ostream& - operator<<(std::ostream& os, const member_protocol& p) { - return os << p.name << ":" << p.metadata.size(); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}:{}", name, metadata.size()); } }; @@ -248,12 +249,12 @@ class group_member { described_group_member describe(const kafka::protocol_name&) const; described_group_member describe_without_metadata() const; + fmt::iterator format_to(fmt::iterator it) const; + private: using join_promise = ss::promise; using sync_promise = ss::promise; - friend std::ostream& operator<<(std::ostream&, const group_member&); - member_state _state; kafka::group_id _group_id; diff --git a/src/v/kafka/server/quota_manager.cc b/src/v/kafka/server/quota_manager.cc index dea18958ef57e..d357d1797739c 100644 --- a/src/v/kafka/server/quota_manager.cc +++ b/src/v/kafka/server/quota_manager.cc @@ -74,7 +74,8 @@ class quota_manager::client_quotas_probe { sm::description( "Client quota throttling delay per rule and " "quota type (in seconds)"), - {rule_label(rule), quota_type_label(quota_type)}) + {rule_label(fmt::format("{}", rule)), + quota_type_label(fmt::format("{}", quota_type))}) .aggregate({sm::shard_label})); metric_defs.emplace_back( sm::make_histogram( @@ -84,7 +85,9 @@ class quota_manager::client_quotas_probe { }, sm::description( "Client quota throughput per rule and quota type"), - {rule_label(rule), quota_type_label(quota_type)}) + {rule_label(fmt::format("{}", rule)), + quota_type_label(fmt::format("{}", quota_type))}) + .aggregate({sm::shard_label})); } } diff --git a/src/v/kafka/server/request_context.h b/src/v/kafka/server/request_context.h index d3ca2125a99be..7ddde3601abee 100644 --- a/src/v/kafka/server/request_context.h +++ b/src/v/kafka/server/request_context.h @@ -10,6 +10,8 @@ */ #pragma once + +#include "base/format_to.h" #include "base/seastarx.h" #include "base/vlog.h" #include "bytes/iobuf.h" @@ -75,7 +77,7 @@ struct request_header { size_t tags_size_bytes{0}; bool is_flexible() const { return tags_size_bytes > 0; } - friend std::ostream& operator<<(std::ostream&, const request_header&); + fmt::iterator format_to(fmt::iterator it) const; }; template diff --git a/src/v/kafka/server/requests.cc b/src/v/kafka/server/requests.cc index bad53c40c5680..fa6aa94fbc372 100644 --- a/src/v/kafka/server/requests.cc +++ b/src/v/kafka/server/requests.cc @@ -345,18 +345,17 @@ process_result_stages process_request( fmt::format("Unsupported API {}", ctx.header().key)); } -std::ostream& operator<<(std::ostream& os, const request_header& header) { - fmt::print( - os, +fmt::iterator request_header::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{key:{}, version:{}, correlation_id:{}, client_id:{}, " "number_of_tagged_fields: {}, tags_size_bytes:{}}}", - header.key, - header.version, - header.correlation, - header.client_id.value_or(std::string_view("nullopt")), - (header.tags ? (*header.tags)().size() : 0), - header.tags_size_bytes); - return os; + key, + version, + correlation, + client_id.value_or(std::string_view("none")), + (tags ? (*tags)().size() : 0), + tags_size_bytes); } } // namespace kafka diff --git a/src/v/kafka/server/tests/BUILD b/src/v/kafka/server/tests/BUILD index bed380900f374..2c718974f67d7 100644 --- a/src/v/kafka/server/tests/BUILD +++ b/src/v/kafka/server/tests/BUILD @@ -741,6 +741,7 @@ redpanda_cc_gtest( ], cpu = 1, deps = [ + "//src/v/base", "//src/v/config", "//src/v/kafka/server", "//src/v/model", diff --git a/src/v/kafka/server/tests/client_quota_translator_test.cc b/src/v/kafka/server/tests/client_quota_translator_test.cc index 06cef53cdc636..a9b3890b0b032 100644 --- a/src/v/kafka/server/tests/client_quota_translator_test.cc +++ b/src/v/kafka/server/tests/client_quota_translator_test.cc @@ -98,6 +98,20 @@ SEASTAR_THREAD_TEST_CASE(quota_translator_modified_default_test) { BOOST_CHECK(!f.tr.is_empty()); } +SEASTAR_THREAD_TEST_CASE(quota_translator_tracker_key_formatting) { + BOOST_CHECK_EQUAL( + fmt::format("{}", tracker_key{k_user{"alice"}}), "k_user{alice}"); + BOOST_CHECK_EQUAL( + fmt::format("{}", tracker_key{k_client_id{"client-a"}}), + "k_client_id{client-a}"); + BOOST_CHECK_EQUAL( + fmt::format( + "{}", tracker_key{std::pair{k_user{"alice"}, k_group_name{"group-a"}}}), + "k_user{alice}, k_group_name{group-a}"); + BOOST_CHECK_EQUAL( + fmt::format("{}", tracker_key{k_not_applicable{}}), "k_not_applicable"); +} + void run_quota_translator_client_group_test(fixture& f) { // Stage 1 - Start by checking that tracker_key's are correctly detected // for various client ids diff --git a/src/v/kafka/server/tests/fetch_test.cc b/src/v/kafka/server/tests/fetch_test.cc index 668943054911b..5ad977d3194b9 100644 --- a/src/v/kafka/server/tests/fetch_test.cc +++ b/src/v/kafka/server/tests/fetch_test.cc @@ -24,7 +24,6 @@ #include #include -#include #include #include diff --git a/src/v/kafka/server/tests/group_tx_compaction_test.cc b/src/v/kafka/server/tests/group_tx_compaction_test.cc index 6da75e440ce94..aefb32a6d4775 100644 --- a/src/v/kafka/server/tests/group_tx_compaction_test.cc +++ b/src/v/kafka/server/tests/group_tx_compaction_test.cc @@ -7,6 +7,7 @@ // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0 +#include "base/format_to.h" #include "kafka/server/group_manager.h" #include "kafka/server/group_tx_tracker_stm.h" #include "kafka/server/rm_group_frontend.h" @@ -239,16 +240,14 @@ struct workload_parameters { int num_rolls; tx_types tx_workload_type; - friend std::ostream& - operator<<(std::ostream& os, const workload_parameters& params) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{groups: {}, tx_per_group: {}, rolls: {}, tx_workload_type: {}}}", - params.num_groups, - params.num_tx_per_group, - params.num_rolls, - static_cast(params.tx_workload_type)); - return os; + num_groups, + num_tx_per_group, + num_rolls, + static_cast(tx_workload_type)); } }; diff --git a/src/v/kafka/server/tests/member_test.cc b/src/v/kafka/server/tests/member_test.cc index d5064a58afd22..ba72afedc40a4 100644 --- a/src/v/kafka/server/tests/member_test.cc +++ b/src/v/kafka/server/tests/member_test.cc @@ -17,7 +17,6 @@ #include #include -#include namespace kafka { diff --git a/src/v/kafka/server/usage_aggregator.h b/src/v/kafka/server/usage_aggregator.h index ddfdb4829a0b4..38721f6964f05 100644 --- a/src/v/kafka/server/usage_aggregator.h +++ b/src/v/kafka/server/usage_aggregator.h @@ -71,17 +71,15 @@ struct usage bytes_sent, bytes_received, bytes_cloud_storage, datalake_usage); } friend bool operator==(const usage&, const usage&) = default; - friend std::ostream& operator<<(std::ostream& os, const usage& u) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ bytes_sent: {} bytes_received: {} bytes_cloud_storage: {} " "datalake_usage: {} }}", - u.bytes_sent, - u.bytes_received, - u.bytes_cloud_storage ? std::to_string(*u.bytes_cloud_storage) - : "n/a", - u.datalake_usage); - return os; + bytes_sent, + bytes_received, + bytes_cloud_storage ? std::to_string(*bytes_cloud_storage) : "n/a", + datalake_usage); } }; diff --git a/src/v/kafka/server/write_at_offset_stm.cc b/src/v/kafka/server/write_at_offset_stm.cc index 361d0953a22aa..881c2af7ddb1e 100644 --- a/src/v/kafka/server/write_at_offset_stm.cc +++ b/src/v/kafka/server/write_at_offset_stm.cc @@ -418,11 +418,10 @@ ss::future<> write_at_offset_stm::do_apply(const model::record_batch& b) { } co_return; } -std::ostream& -operator<<(std::ostream& o, const write_at_offset_stm::term_offset& to) { - fmt::print( - o, "{{offset: {}, in_sync_term: {}}}", to.offset, to.in_sync_term); - return o; +fmt::iterator +write_at_offset_stm::term_offset::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{offset: {}, in_sync_term: {}}}", offset, in_sync_term); } ss::future diff --git a/src/v/kafka/server/write_at_offset_stm.h b/src/v/kafka/server/write_at_offset_stm.h index 7798bee120800..1a965318c1837 100644 --- a/src/v/kafka/server/write_at_offset_stm.h +++ b/src/v/kafka/server/write_at_offset_stm.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "cluster/state_machine_registry.h" #include "raft/persisted_stm.h" #include "raft/replicate.h" @@ -187,8 +188,8 @@ class write_at_offset_stm struct term_offset { kafka::offset offset; model::term_id in_sync_term; + fmt::iterator format_to(fmt::iterator it) const; }; - friend std::ostream& operator<<(std::ostream&, const term_offset&); std::optional _inflight_last_offset; ssx::mutex _sync_lock; diff --git a/src/v/kafka/utils/tests/txn_reader_test.cc b/src/v/kafka/utils/tests/txn_reader_test.cc index ff2c138eed90b..52dc6e10b7552 100644 --- a/src/v/kafka/utils/tests/txn_reader_test.cc +++ b/src/v/kafka/utils/tests/txn_reader_test.cc @@ -393,7 +393,7 @@ TEST_F_CORO(seastar_test, UncleanDestroy) { std::move(result)); } - void print(std::ostream&) final {} + fmt::iterator format_to(fmt::iterator it) const final { return it; } ss::future<> finally() noexcept final { _resources_initalized = false; diff --git a/src/v/kafka/utils/txn_reader.cc b/src/v/kafka/utils/txn_reader.cc index 11a9c95a1ca5f..d87b93dbad571 100644 --- a/src/v/kafka/utils/txn_reader.cc +++ b/src/v/kafka/utils/txn_reader.cc @@ -185,8 +185,8 @@ read_committed_reader::read_committed_reader( : _tracker(std::move(tracker)) , _underlying(std::move(reader).release()) {} -void read_committed_reader::print(std::ostream& os) { - os << "transform::txn_reader{}"; +fmt::iterator read_committed_reader::format_to(fmt::iterator it) const { + return fmt::format_to(it, "transform::txn_reader{{}}"); } bool read_committed_reader::is_end_of_stream() const { diff --git a/src/v/kafka/utils/txn_reader.h b/src/v/kafka/utils/txn_reader.h index d3e1b67edbfa0..d7e1c29f6d536 100644 --- a/src/v/kafka/utils/txn_reader.h +++ b/src/v/kafka/utils/txn_reader.h @@ -71,7 +71,7 @@ class read_committed_reader : public model::record_batch_reader::impl { ss::future do_load_slice(model::timeout_clock::time_point deadline) override; - void print(std::ostream& os) override; + fmt::iterator format_to(fmt::iterator it) const override; ss::future<> finally() noexcept final; diff --git a/src/v/lsm/sst/reader.cc b/src/v/lsm/sst/reader.cc index 660106d054aad..602606a4dd745 100644 --- a/src/v/lsm/sst/reader.cc +++ b/src/v/lsm/sst/reader.cc @@ -56,7 +56,7 @@ read_block(io::random_access_file_reader* file, block::handle handle) { actual_crc.value(), expected_crc, handle, - fmt::streamed(*file)); + *file); } data.trim_back(sizeof(compression_type)); if (compression != compression_type::none) { diff --git a/src/v/model/compression.h b/src/v/model/compression.h index 0fcdbec33798b..dc031732aa008 100644 --- a/src/v/model/compression.h +++ b/src/v/model/compression.h @@ -11,11 +11,13 @@ #pragma once +#include "base/format_to.h" + #include #include #include #include -#include +#include #include namespace model { @@ -60,33 +62,23 @@ constexpr auto all_batch_compression_types = [] { return types; }(); -/// operators needed for boost::lexical_cast -/// inline to prevent library depdency with the v::compression module -inline std::ostream& operator<<(std::ostream& os, const compression& c) { +inline fmt::iterator format_to(compression c, fmt::iterator out) { switch (c) { case compression::none: - os << "none"; - break; + return fmt::format_to(out, "none"); case compression::gzip: - os << "gzip"; - break; + return fmt::format_to(out, "gzip"); case compression::snappy: - os << "snappy"; - break; + return fmt::format_to(out, "snappy"); case compression::lz4: - os << "lz4"; - break; + return fmt::format_to(out, "lz4"); case compression::zstd: - os << "zstd"; - break; + return fmt::format_to(out, "zstd"); case compression::producer: - os << "producer"; - break; + return fmt::format_to(out, "producer"); default: - os << "ERROR"; - break; + return fmt::format_to(out, "ERROR"); } - return os; } std::istream& operator>>(std::istream&, compression&); diff --git a/src/v/model/fundamental.h b/src/v/model/fundamental.h index e8649e2945b37..8b134f8acc10d 100644 --- a/src/v/model/fundamental.h +++ b/src/v/model/fundamental.h @@ -187,7 +187,16 @@ inline bool is_deletion_enabled(cleanup_policy_bitflags flags) { == cleanup_policy_bitflags::deletion; } -std::ostream& operator<<(std::ostream&, cleanup_policy_bitflags); +inline std::string_view to_string_view(cleanup_policy_bitflags c) { + if (c == cleanup_policy_bitflags::none) return "none"; + if (is_compaction_enabled(c) && is_deletion_enabled(c)) + return "compact,delete"; + if (is_compaction_enabled(c)) return "compact"; + if (is_deletion_enabled(c)) return "delete"; + return "none"; +} + +fmt::iterator format_to(cleanup_policy_bitflags c, fmt::iterator out); std::istream& operator>>(std::istream&, cleanup_policy_bitflags&); // Named after Kafka compaction.strategy topic property @@ -200,7 +209,7 @@ enum class compaction_strategy : int8_t { /// \brief header field compaction is not yet supported header, }; -std::ostream& operator<<(std::ostream&, compaction_strategy); +fmt::iterator format_to(compaction_strategy c, fmt::iterator out); std::istream& operator>>(std::istream&, compaction_strategy&); using term_id = named_type; @@ -315,7 +324,8 @@ struct topic_partition_view { model::topic_view topic; model::partition_id partition; - friend std::ostream& operator<<(std::ostream&, const topic_partition_view&); + + fmt::iterator format_to(fmt::iterator it) const; friend auto operator<=>( const topic_partition_view&, const topic_partition_view&) = default; template @@ -358,7 +368,7 @@ struct topic_partition { return topic_partition_view(topic, partition); } - friend std::ostream& operator<<(std::ostream&, const topic_partition&); + fmt::iterator format_to(fmt::iterator it) const; friend void read_nested( iobuf_parser& in, topic_partition& tp, const size_t bytes_left_limit) { @@ -422,7 +432,7 @@ struct ntp { ss::sstring path() const; std::filesystem::path topic_path() const; - friend std::ostream& operator<<(std::ostream&, const ntp&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -439,7 +449,8 @@ enum class control_record_type : int16_t { tx_commit = 1, unknown = -1 }; -std::ostream& operator<<(std::ostream&, const control_record_type&); + +fmt::iterator format_to(control_record_type crt, fmt::iterator out); using control_record_version = named_type; @@ -523,7 +534,7 @@ static_assert( shadow_indexing_mode::full, shadow_indexing_mode::drop_full) == shadow_indexing_mode::disabled); -std::ostream& operator<<(std::ostream&, const shadow_indexing_mode&); +fmt::iterator format_to(shadow_indexing_mode si, fmt::iterator out); using client_address_t = ss::socket_address; @@ -553,8 +564,8 @@ constexpr std::string_view to_string_view(fips_mode_flag f) { return "enabled"; } } +fmt::iterator format_to(fips_mode_flag f, fmt::iterator out); -std::ostream& operator<<(std::ostream& os, const fips_mode_flag& f); std::istream& operator>>(std::istream& is, fips_mode_flag& f); struct topic_id : named_type { @@ -592,7 +603,7 @@ struct topic_id_partition { bool operator==(const topic_id_partition& other) const = default; auto operator<=>(const topic_id_partition& other) const noexcept = default; - friend std::ostream& operator<<(std::ostream&, const topic_id_partition&); + fmt::iterator format_to(fmt::iterator it) const; friend void read_nested( iobuf_parser& in, topic_id_partition& tp, const size_t bytes_left_limit) { diff --git a/src/v/model/kitp.h b/src/v/model/kitp.h index fa7c1737ae29a..d3fcc9eb46933 100644 --- a/src/v/model/kitp.h +++ b/src/v/model/kitp.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "model/ktp.h" #include "model/metadata.h" #include "model/namespace.h" @@ -46,6 +47,10 @@ class kitp_view { topic_partition_view as_tp_view() const { return _tp_view; } ktp as_ktp() const { return {_tp_view.topic, _tp_view.partition}; } + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}, topic_id: {}", _tp_view, _id); + } + private: // Note: the id is not hashed, as it's only available if the request // specified a non-default topic_id @@ -65,12 +70,6 @@ class kitp_view { == std::tie(rhs._id, rhs._tp_view); }; - friend inline std::ostream& - operator<<(std::ostream& os, const kitp_view& v) { - fmt::print(os, "{}, topic_id: {}", v._tp_view, v._id); - return os; - } - topic_id _id; topic_partition_view _tp_view; }; @@ -119,6 +118,10 @@ class kitp : private model::ktp { const ktp& as_ktp() const { return *this; } + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", as_kitp_view()); + } + private: template friend inline H AbslHashValue(H h, const kitp& kitp) { @@ -129,10 +132,6 @@ class kitp : private model::ktp { return lhs.as_kitp_view() == rhs.as_kitp_view(); } - friend inline std::ostream& operator<<(std::ostream& os, const kitp& v) { - return os << v.as_kitp_view(); - } - topic_id _id; }; diff --git a/src/v/model/ktp.h b/src/v/model/ktp.h index ff4babba53875..01193798b5412 100644 --- a/src/v/model/ktp.h +++ b/src/v/model/ktp.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "container/chunked_hash_map.h" #include "model/fundamental.h" #include "model/metadata.h" @@ -195,9 +196,8 @@ class ktp { return tp <=> other.tp; } - friend std::ostream& operator<<(std::ostream& os, const ktp& t) { - os << t.to_ntp(); - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", to_ntp()); } protected: diff --git a/src/v/model/metadata.h b/src/v/model/metadata.h index c17c02af9e5e5..8da74e6175c9f 100644 --- a/src/v/model/metadata.h +++ b/src/v/model/metadata.h @@ -12,6 +12,7 @@ #pragma once #include "absl/hash/hash.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "base/units.h" #include "model/fundamental.h" @@ -57,8 +58,7 @@ struct broker_properties bool operator==(const broker_properties& other) const = default; - friend std::ostream& - operator<<(std::ostream&, const model::broker_properties&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie( @@ -88,7 +88,7 @@ struct broker_endpoint final : address(std::move(address)) {} bool operator==(const broker_endpoint&) const = default; - friend std::ostream& operator<<(std::ostream&, const broker_endpoint&); + fmt::iterator format_to(fmt::iterator it) const; static std::optional validate_not_is_addr_any(const broker_endpoint& ep) { @@ -169,8 +169,8 @@ enum class membership_state : int8_t { active, draining, removed }; */ enum class maintenance_state { active, inactive }; -std::ostream& operator<<(std::ostream&, membership_state); -std::ostream& operator<<(std::ostream&, maintenance_state); +fmt::iterator format_to(membership_state st, fmt::iterator out); +fmt::iterator format_to(maintenance_state st, fmt::iterator out); class broker : public serde:: @@ -242,14 +242,14 @@ class broker _id, _kafka_advertised_listeners, _rpc_address, _rack, _properties); } + fmt::iterator format_to(fmt::iterator it) const; + private: node_id _id; std::vector _kafka_advertised_listeners; net::unresolved_address _rpc_address; std::optional _rack; broker_properties _properties; - - friend std::ostream& operator<<(std::ostream&, const broker&); }; /// type representing single replica assignment it contains the id of a broker @@ -260,7 +260,7 @@ struct broker_shard { /// however, seastar uses unsized-ints (unsigned) /// and for predictability we need fixed-sized ints uint32_t shard; - friend std::ostream& operator<<(std::ostream&, const broker_shard&); + fmt::iterator format_to(fmt::iterator it) const; bool operator==(const broker_shard&) const = default; auto operator<=>(const model::broker_shard&) const = default; @@ -295,7 +295,7 @@ struct partition_metadata std::vector replicas; std::optional leader_node; - friend std::ostream& operator<<(std::ostream&, const partition_metadata&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const partition_metadata&, const partition_metadata&) = default; @@ -307,6 +307,8 @@ enum class isolation_level : int8_t { read_committed = 1, }; +fmt::iterator format_to(isolation_level l, fmt::iterator out); + struct topic_namespace_view { topic_namespace_view(const model::ns& n, const model::topic& t) : ns(n) @@ -331,7 +333,7 @@ struct topic_namespace_view { const model::ns& ns; const model::topic& tp; - friend std::ostream& operator<<(std::ostream&, const topic_namespace_view&); + fmt::iterator format_to(fmt::iterator it) const; }; struct topic_namespace { @@ -390,7 +392,7 @@ struct topic_namespace { ss::sstring path() const; - friend std::ostream& operator<<(std::ostream&, const topic_namespace&); + fmt::iterator format_to(fmt::iterator it) const; }; struct topic_namespace_hash { @@ -437,7 +439,7 @@ struct topic_metadata topic_namespace tp_ns; std::vector partitions; - friend std::ostream& operator<<(std::ostream&, const topic_metadata&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const topic_metadata&, const topic_metadata&) = default; @@ -453,7 +455,7 @@ enum class cloud_credentials_source { azure_vm_instance_metadata = 5, }; -std::ostream& operator<<(std::ostream& os, const cloud_credentials_source& cs); +fmt::iterator format_to(cloud_credentials_source cs, fmt::iterator out); enum class partition_autobalancing_mode { off = 0, @@ -461,16 +463,17 @@ enum class partition_autobalancing_mode { continuous, }; -inline std::ostream& -operator<<(std::ostream& o, const partition_autobalancing_mode& m) { +inline fmt::iterator +format_to(partition_autobalancing_mode m, fmt::iterator out) { switch (m) { - case model::partition_autobalancing_mode::off: - return o << "off"; - case model::partition_autobalancing_mode::node_add: - return o << "node_add"; - case model::partition_autobalancing_mode::continuous: - return o << "continuous"; - } + case partition_autobalancing_mode::off: + return fmt::format_to(out, "off"); + case partition_autobalancing_mode::node_add: + return fmt::format_to(out, "node_add"); + case partition_autobalancing_mode::continuous: + return fmt::format_to(out, "continuous"); + } + return fmt::format_to(out, "unknown"); } enum class cloud_storage_backend : uint8_t { @@ -483,23 +486,24 @@ enum class cloud_storage_backend : uint8_t { unknown }; -inline std::ostream& operator<<(std::ostream& os, cloud_storage_backend csb) { +inline fmt::iterator format_to(cloud_storage_backend csb, fmt::iterator out) { switch (csb) { case cloud_storage_backend::aws: - return os << "aws"; + return fmt::format_to(out, "aws"); case cloud_storage_backend::google_s3_compat: - return os << "google_s3_compat"; + return fmt::format_to(out, "google_s3_compat"); case cloud_storage_backend::azure: - return os << "azure"; + return fmt::format_to(out, "azure"); case cloud_storage_backend::minio: - return os << "minio"; + return fmt::format_to(out, "minio"); case cloud_storage_backend::oracle_s3_compat: - return os << "oracle_s3_compat"; + return fmt::format_to(out, "oracle_s3_compat"); case cloud_storage_backend::linode_s3_compat: - return os << "linode_s3_compat"; + return fmt::format_to(out, "linode_s3_compat"); case cloud_storage_backend::unknown: - return os << "unknown"; + return fmt::format_to(out, "unknown"); } + return fmt::format_to(out, "unknown"); } enum class leader_balancer_mode : uint8_t { @@ -522,8 +526,8 @@ leader_balancer_mode_to_string(leader_balancer_mode mode) { } } -inline std::ostream& operator<<(std::ostream& os, leader_balancer_mode mode) { - return os << leader_balancer_mode_to_string(mode); +inline fmt::iterator format_to(leader_balancer_mode mode, fmt::iterator out) { + return fmt::format_to(out, "{}", leader_balancer_mode_to_string(mode)); } enum class cloud_storage_chunk_eviction_strategy { @@ -532,16 +536,17 @@ enum class cloud_storage_chunk_eviction_strategy { predictive = 2, }; -inline std::ostream& -operator<<(std::ostream& os, cloud_storage_chunk_eviction_strategy st) { +inline fmt::iterator +format_to(cloud_storage_chunk_eviction_strategy st, fmt::iterator out) { switch (st) { case cloud_storage_chunk_eviction_strategy::eager: - return os << "eager"; + return fmt::format_to(out, "eager"); case cloud_storage_chunk_eviction_strategy::capped: - return os << "capped"; + return fmt::format_to(out, "capped"); case cloud_storage_chunk_eviction_strategy::predictive: - return os << "predictive"; + return fmt::format_to(out, "predictive"); } + return fmt::format_to(out, "unknown"); } enum class fetch_read_strategy : uint8_t { @@ -566,7 +571,7 @@ constexpr const char* fetch_read_strategy_to_string(fetch_read_strategy s) { } } -std::ostream& operator<<(std::ostream&, fetch_read_strategy); +fmt::iterator format_to(fetch_read_strategy s, fmt::iterator out); std::istream& operator>>(std::istream&, fetch_read_strategy&); /** @@ -603,7 +608,7 @@ constexpr const char* write_caching_mode_to_string(write_caching_mode s) { std::optional write_caching_mode_from_string(std::string_view); -std::ostream& operator<<(std::ostream&, write_caching_mode); +fmt::iterator format_to(write_caching_mode s, fmt::iterator out); std::istream& operator>>(std::istream&, write_caching_mode&); // Storage mode for a Redpanda topic @@ -637,7 +642,7 @@ constexpr const char* redpanda_storage_mode_to_string(redpanda_storage_mode m) { std::optional redpanda_storage_mode_from_string(std::string_view); -std::ostream& operator<<(std::ostream&, redpanda_storage_mode); +fmt::iterator format_to(redpanda_storage_mode m, fmt::iterator out); std::istream& operator>>(std::istream&, redpanda_storage_mode&); enum class recovery_validation_mode : std::uint16_t { @@ -651,7 +656,7 @@ enum class recovery_validation_mode : std::uint16_t { no_check = 0xff, }; -std::ostream& operator<<(std::ostream&, recovery_validation_mode); +fmt::iterator format_to(recovery_validation_mode vm, fmt::iterator out); std::istream& operator>>(std::istream&, recovery_validation_mode&); // Iceberg enablement options for a topic @@ -722,6 +727,7 @@ class iceberg_mode { } bool operator==(const iceberg_mode&) const = default; + fmt::iterator format_to(fmt::iterator it) const; friend void write_nested(iobuf& out, const iceberg_mode& m); @@ -770,7 +776,6 @@ class iceberg_mode { _impl; }; -std::ostream& operator<<(std::ostream&, const iceberg_mode&); std::istream& operator>>(std::istream&, iceberg_mode&); // How to handle invalid records during Iceberg translation. @@ -781,7 +786,7 @@ enum class iceberg_invalid_record_action : uint8_t { dlq_table = 1, }; -std::ostream& operator<<(std::ostream&, const iceberg_invalid_record_action&); +fmt::iterator format_to(iceberg_invalid_record_action a, fmt::iterator out); std::istream& operator>>(std::istream&, iceberg_invalid_record_action&); enum class kafka_batch_validation_mode : uint8_t { @@ -805,32 +810,11 @@ kafka_batch_validation_mode_to_string(const kafka_batch_validation_mode& m) { std::optional kafka_batch_validation_mode_from_string(std::string_view s); -std::ostream& -operator<<(std::ostream& o, const kafka_batch_validation_mode& mode); - +fmt::iterator format_to(kafka_batch_validation_mode m, fmt::iterator out); std::istream& operator>>(std::istream& i, kafka_batch_validation_mode& mode); } // namespace model -template<> -struct fmt::formatter final - : fmt::formatter { - using isolation_level = model::isolation_level; - template - auto format(const isolation_level& s, FormatContext& ctx) const { - std::string_view str = "unknown"; - switch (s) { - case isolation_level::read_uncommitted: - str = "read_uncommitted"; - break; - case isolation_level::read_committed: - str = "read_committed"; - break; - } - return fmt::format_to(ctx.out(), "{}", str); - } -}; - namespace std { template<> struct hash { diff --git a/src/v/model/model.cc b/src/v/model/model.cc index 0ccae01aaf682..d08c62b6ec5ef 100644 --- a/src/v/model/model.cc +++ b/src/v/model/model.cc @@ -32,20 +32,17 @@ #include #include -#include - #include #include #include namespace model { -std::ostream& operator<<(std::ostream& os, timestamp ts) { - if (ts != timestamp::missing()) { - fmt::print(os, "{{timestamp: {}}}", ts.value()); - return os; +fmt::iterator timestamp::format_to(fmt::iterator it) const { + if (*this != missing()) { + return fmt::format_to(it, "{{timestamp: {}}}", _v); } - return os << "{timestamp: missing}"; + return fmt::format_to(it, "{{timestamp: missing}}"); } void read_nested( @@ -55,133 +52,142 @@ void read_nested( void write_nested(iobuf& out, timestamp ts) { serde::write(out, ts._v); } -std::ostream& operator<<(std::ostream& os, const topic_partition_view& tp) { - fmt::print(os, "{{{}/{}}}", tp.topic(), tp.partition()); - return os; +fmt::iterator topic_partition_view::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}/{}}}", topic(), partition()); } -std::ostream& operator<<(std::ostream& os, const topic_partition& tp) { - fmt::print(os, "{{{}/{}}}", tp.topic(), tp.partition()); - return os; +fmt::iterator topic_partition::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}/{}}}", topic(), partition()); } -std::ostream& operator<<(std::ostream& os, const topic_id_partition& tp) { - fmt::print(os, "{{{}/{}}}", tp.topic_id(), tp.partition()); - return os; +fmt::iterator topic_id_partition::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}/{}}}", topic_id(), partition()); } -std::ostream& operator<<(std::ostream& os, const ntp& n) { - fmt::print(os, "{{{}/{}/{}}}", n.ns(), n.tp.topic(), n.tp.partition()); - return os; +fmt::iterator ntp::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}/{}/{}}}", ns(), tp.topic(), tp.partition()); } -std::ostream& operator<<(std::ostream& o, const model::topic_namespace& tp_ns) { - fmt::print(o, "{{{}/{}}}", tp_ns.ns(), tp_ns.tp()); - return o; +fmt::iterator topic_namespace::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}/{}}}", ns(), tp()); } -std::ostream& -operator<<(std::ostream& o, const model::topic_namespace_view& tp_ns) { - fmt::print(o, "{{{}/{}}}", tp_ns.ns(), tp_ns.tp()); - return o; +fmt::iterator topic_namespace_view::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}/{}}}", ns(), tp()); } -std::ostream& operator<<(std::ostream& os, timestamp_type ts) { +fmt::iterator format_to(timestamp_type ts, fmt::iterator out) { /** * We need to use specific string representations of timestamp_type as this * is related with protocol correctness */ switch (ts) { case timestamp_type::append_time: - return os << "LogAppendTime"; + return fmt::format_to(out, "LogAppendTime"); case timestamp_type::create_time: - return os << "CreateTime"; + return fmt::format_to(out, "CreateTime"); } - return os << "{unknown timestamp:" << static_cast(ts) << "}"; + return fmt::format_to( + out, "{{unknown timestamp:{}}}", static_cast(ts)); } -std::ostream& operator<<(std::ostream& o, const record_header& h) { - return o << "{key_size=" << h.key_size() << ", key=" << h.key() - << ", value_size=" << h.value_size() << ", value=" << h.value() - << "}"; +fmt::iterator record_header::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{key_size={}, key={}, value_size={}, value={}}}", + _key_size, + _key, + _val_size, + _value); } -std::ostream& operator<<(std::ostream& o, const record_attributes& a) { - return o << "{" << a._attributes << "}"; +fmt::iterator record_attributes::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}}}", _attributes); } -std::ostream& operator<<(std::ostream& o, const record& r) { - o << "{record: size_bytes=" << r.size_bytes() - << ", attributes=" << r.attributes() - << ", timestamp_delta=" << r._timestamp_delta - << ", offset_delta=" << r._offset_delta << ", key_size=" << r._key_size - << ", key=" << r.key() << ", value_size=" << r.value_size() - << ", value=" << r.value() << ", header_size:" << r.headers().size() - << ", headers=["; - for (auto& h : r.headers()) { - o << h; +fmt::iterator record::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, + "{{record: size_bytes={}, attributes={}, timestamp_delta={}, " + "offset_delta={}, key_size={}, key={}, value_size={}, value={}, " + "header_size:{}, headers=[", + _size_bytes, + _attributes, + _timestamp_delta, + _offset_delta, + _key_size, + _key, + _val_size, + _value, + _headers.size()); + for (const auto& h : _headers) { + it = fmt::format_to(it, "{}", h); } - return o << "]}"; + return fmt::format_to(it, "]}}"); } -std::ostream& operator<<(std::ostream& o, const producer_identity& pid) { - fmt::print(o, "{{producer_identity: id={}, epoch={}}}", pid.id, pid.epoch); - return o; +fmt::iterator producer_identity::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{producer_identity: id={}, epoch={}}}", id, epoch); } -std::ostream& -operator<<(std::ostream& o, const record_batch_attributes& attrs) { - o << "{compression:"; - if (attrs.is_valid_compression()) { +fmt::iterator record_batch_attributes::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{compression:"); + if (is_valid_compression()) { // this method... sadly, just throws - o << attrs.compression(); + it = fmt::format_to(it, "{}", compression()); } else { - o << "invalid compression"; - } - return o << ", type:" << attrs.timestamp_type() - << ", transactional: " << attrs.is_transactional() - << ", control: " << attrs.is_control() << "}"; -} - -std::ostream& operator<<(std::ostream& o, const record_batch_header& h) { - o << "{header_crc:" << h.header_crc << ", size_bytes:" << h.size_bytes - << ", base_offset:" << h.base_offset << ", type:" << h.type - << ", crc:" << h.crc << ", attrs:" << h.attrs - << ", last_offset_delta:" << h.last_offset_delta - << ", first_timestamp:" << h.first_timestamp - << ", max_timestamp:" << h.max_timestamp - << ", producer_id:" << h.producer_id - << ", producer_epoch:" << h.producer_epoch - << ", base_sequence:" << h.base_sequence - << ", record_count:" << h.record_count; - o << ", ctx:{term:" << h.ctx.term << ", owner_shard:"; - if (h.ctx.owner_shard) { - o << h.ctx.owner_shard << "}"; + it = fmt::format_to(it, "invalid compression"); + } + return fmt::format_to( + it, + ", type:{}, transactional: {}, control: {}}}", + timestamp_type(), + is_transactional(), + is_control()); +} + +fmt::iterator record_batch_header::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, + "{{header_crc:{}, size_bytes:{}, base_offset:{}, type:{}, crc:{}, " + "attrs:{}, last_offset_delta:{}, first_timestamp:{}, " + "max_timestamp:{}, producer_id:{}, producer_epoch:{}, " + "base_sequence:{}, record_count:{}, ctx:{{term:{}, owner_shard:", + header_crc, + size_bytes, + base_offset, + type, + crc, + attrs, + last_offset_delta, + first_timestamp, + max_timestamp, + producer_id, + producer_epoch, + base_sequence, + record_count, + ctx.term); + if (ctx.owner_shard) { + it = fmt::format_to(it, "{}}}}}", *ctx.owner_shard); } else { - o << "nullopt}"; + it = fmt::format_to(it, "nullopt}}}}"); } - o << "}"; - return o; -} - -std::ostream& -operator<<(std::ostream& os, const record_batch::compressed_records& records) { - fmt::print( - os, "{{compressed_records: size_bytes={}}}", records.size_bytes()); - return os; + return it; } -std::ostream& operator<<(std::ostream& os, const record_batch& batch) { - os << "{record_batch=" << batch.header() << ", records="; - if (batch.compressed()) { - os << "{compressed=" << batch.data().size_bytes() << " bytes}"; +fmt::iterator record_batch::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{record_batch={}, records=", _header); + if (_compressed) { + it = fmt::format_to( + it, "{{compressed={} bytes}}", _records.size_bytes()); } else { - os << "{"; - batch.for_each_record([&os](const model::record& r) { os << r; }); - os << "}"; + it = fmt::format_to(it, "{{"); + for_each_record( + [&it](const model::record& r) { it = fmt::format_to(it, "{}", r); }); + it = fmt::format_to(it, "}}"); } - os << "}"; - return os; + return fmt::format_to(it, "}}"); } ss::sstring ntp::path() const { @@ -217,61 +223,50 @@ std::istream& operator>>(std::istream& i, compression& c) { return i; } -std::ostream& operator<<(std::ostream& o, const model::broker_properties& b) { - fmt::print( - o, +fmt::iterator broker_properties::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{cores {}, mem_available {}, disk_available {}, in_fips_mode {}}}", - b.cores, - b.available_memory_bytes, - b.available_disk_gb, - b.in_fips_mode, - b.mount_paths, - b.etc_props); - return o; -} - -std::ostream& operator<<(std::ostream& o, const model::broker& b) { - fmt::print( - o, + cores, + available_memory_bytes, + available_disk_gb, + in_fips_mode); +} + +fmt::iterator broker::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, kafka_advertised_listeners: {}, rpc_address: {}, rack: {}, " "properties: {}}}", - b.id(), - b.kafka_advertised_listeners(), - b.rpc_address(), - b.rack(), - b.properties()); - return o; + _id, + _kafka_advertised_listeners, + _rpc_address, + _rack, + _properties); } -std::ostream& operator<<(std::ostream& o, const topic_metadata& t_md) { - fmt::print( - o, "{{topic_namespace: {}, partitons: {}}}", t_md.tp_ns, t_md.partitions); - return o; +fmt::iterator topic_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{topic_namespace: {}, partitons: {}}}", tp_ns, partitions); } -std::ostream& operator<<(std::ostream& o, const partition_metadata& p_md) { - fmt::print( - o, - "{{id: {}, leader_id: {}, replicas: {}}}", - p_md.id, - p_md.leader_node, - p_md.replicas); - return o; +fmt::iterator partition_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, leader_id: {}, replicas: {}}}", id, leader_node, replicas); } -std::ostream& operator<<(std::ostream& o, const broker_shard& bs) { - fmt::print(o, "{{node_id: {}, shard: {}}}", bs.node_id, bs.shard); - return o; +fmt::iterator broker_shard::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{node_id: {}, shard: {}}}", node_id, shard); } -std::ostream& operator<<(std::ostream& o, compaction_strategy c) { +fmt::iterator format_to(compaction_strategy c, fmt::iterator out) { switch (c) { case compaction_strategy::offset: - return o << "offset"; + return fmt::format_to(out, "offset"); case compaction_strategy::timestamp: - return o << "timestamp"; + return fmt::format_to(out, "timestamp"); case compaction_strategy::header: - return o << "header"; + return fmt::format_to(out, "header"); } __builtin_unreachable(); } @@ -295,27 +290,8 @@ std::istream& operator>>(std::istream& i, timestamp_type& ts_type) { return i; }; -std::ostream& operator<<(std::ostream& o, cleanup_policy_bitflags c) { - if (c == model::cleanup_policy_bitflags::none) { - o << "none"; - return o; - } - - auto compaction = model::is_compaction_enabled(c); - auto deletion = model::is_deletion_enabled(c); - - if (compaction && deletion) { - o << "compact,delete"; - return o; - } - - if (compaction) { - o << "compact"; - } else if (deletion) { - o << "delete"; - } - - return o; +fmt::iterator format_to(cleanup_policy_bitflags c, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(c)); } std::istream& operator>>(std::istream& i, cleanup_policy_bitflags& cp) { @@ -332,196 +308,188 @@ std::istream& operator>>(std::istream& i, cleanup_policy_bitflags& cp) { return i; } -std::ostream& operator<<(std::ostream& os, const model::broker_endpoint& ep) { - fmt::print(os, "{{{}:{}}}", ep.name, ep.address); - return os; +fmt::iterator broker_endpoint::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}:{}}}", name, address); } -std::ostream& operator<<(std::ostream& o, record_batch_type bt) { +fmt::iterator format_to(record_batch_type bt, fmt::iterator out) { switch (bt) { case record_batch_type::raft_data: - return o << "batch_type::raft_data"; + return fmt::format_to(out, "batch_type::raft_data"); case record_batch_type::raft_configuration: - return o << "batch_type::raft_configuration"; + return fmt::format_to(out, "batch_type::raft_configuration"); case record_batch_type::controller: - return o << "batch_type::controller"; + return fmt::format_to(out, "batch_type::controller"); case record_batch_type::kvstore: - return o << "batch_type::kvstore"; + return fmt::format_to(out, "batch_type::kvstore"); case record_batch_type::checkpoint: - return o << "batch_type::checkpoint"; + return fmt::format_to(out, "batch_type::checkpoint"); case record_batch_type::topic_management_cmd: - return o << "batch_type::topic_management_cmd"; + return fmt::format_to(out, "batch_type::topic_management_cmd"); case record_batch_type::ghost_batch: - return o << "batch_type::ghost_batch"; + return fmt::format_to(out, "batch_type::ghost_batch"); case record_batch_type::id_allocator: - return o << "batch_type::id_allocator"; + return fmt::format_to(out, "batch_type::id_allocator"); case record_batch_type::tx_prepare: - return o << "batch_type::tx_prepare"; + return fmt::format_to(out, "batch_type::tx_prepare"); case record_batch_type::tx_fence: - return o << "batch_type::tx_fence"; + return fmt::format_to(out, "batch_type::tx_fence"); case record_batch_type::tm_update: - return o << "batch_type::tm_update"; + return fmt::format_to(out, "batch_type::tm_update"); case record_batch_type::user_management_cmd: - return o << "batch_type::user_management_cmd"; + return fmt::format_to(out, "batch_type::user_management_cmd"); case record_batch_type::acl_management_cmd: - return o << "batch_type::acl_management_cmd"; + return fmt::format_to(out, "batch_type::acl_management_cmd"); case record_batch_type::group_prepare_tx: - return o << "batch_type::group_prepare_tx"; + return fmt::format_to(out, "batch_type::group_prepare_tx"); case record_batch_type::group_commit_tx: - return o << "batch_type::group_commit_tx"; + return fmt::format_to(out, "batch_type::group_commit_tx"); case record_batch_type::group_abort_tx: - return o << "batch_type::group_abort_tx"; + return fmt::format_to(out, "batch_type::group_abort_tx"); case record_batch_type::node_management_cmd: - return o << "batch_type::node_management_cmd"; + return fmt::format_to(out, "batch_type::node_management_cmd"); case record_batch_type::data_policy_management_cmd: - return o << "batch_type::data_policy_management_cmd"; + return fmt::format_to(out, "batch_type::data_policy_management_cmd"); case record_batch_type::archival_metadata: - return o << "batch_type::archival_metadata"; + return fmt::format_to(out, "batch_type::archival_metadata"); case record_batch_type::cluster_config_cmd: - return o << "batch_type::cluster_config_cmd"; + return fmt::format_to(out, "batch_type::cluster_config_cmd"); case record_batch_type::feature_update: - return o << "batch_type::feature_update"; + return fmt::format_to(out, "batch_type::feature_update"); case record_batch_type::cluster_bootstrap_cmd: - return o << "batch_type::cluster_bootstrap_cmd"; + return fmt::format_to(out, "batch_type::cluster_bootstrap_cmd"); case record_batch_type::version_fence: - return o << "batch_type::version_fence"; + return fmt::format_to(out, "batch_type::version_fence"); case record_batch_type::tx_tm_hosted_trasactions: - return o << "batch_type::tx_tm_hosted_trasactions"; + return fmt::format_to(out, "batch_type::tx_tm_hosted_trasactions"); case record_batch_type::prefix_truncate: - return o << "batch_type::prefix_truncate"; + return fmt::format_to(out, "batch_type::prefix_truncate"); case record_batch_type::plugin_update: - return o << "batch_type::plugin_update"; + return fmt::format_to(out, "batch_type::plugin_update"); case record_batch_type::tx_registry: - return o << "batch_type::tx_registry"; + return fmt::format_to(out, "batch_type::tx_registry"); case record_batch_type::cluster_recovery_cmd: - return o << "batch_type::cluster_recovery_cmd"; + return fmt::format_to(out, "batch_type::cluster_recovery_cmd"); case record_batch_type::compaction_placeholder: - return o << "batch_type::compaction_placeholder"; + return fmt::format_to(out, "batch_type::compaction_placeholder"); case record_batch_type::role_management_cmd: - return o << "batch_type::role_management_cmd"; + return fmt::format_to(out, "batch_type::role_management_cmd"); case record_batch_type::client_quota: - return o << "batch_type::client_quota"; + return fmt::format_to(out, "batch_type::client_quota"); case record_batch_type::data_migration_cmd: - return o << "batch_type::data_migration_cmd"; + return fmt::format_to(out, "batch_type::data_migration_cmd"); case record_batch_type::group_fence_tx: - return o << "batch_type::group_fence_tx"; + return fmt::format_to(out, "batch_type::group_fence_tx"); case record_batch_type::partition_properties_update: - return o << "batch_type::partition_properties_update"; + return fmt::format_to(out, "batch_type::partition_properties_update"); case record_batch_type::datalake_coordinator: - return o << "batch_type::datalake_coordinator"; + return fmt::format_to(out, "batch_type::datalake_coordinator"); case record_batch_type::ctp_placeholder: - return o << "batch_type::ctp_placeholder"; + return fmt::format_to(out, "batch_type::ctp_placeholder"); case record_batch_type::ctp_stm_command: - return o << "batch_type::ctp_stm_command"; + return fmt::format_to(out, "batch_type::ctp_stm_command"); case record_batch_type::datalake_translation_state: - return o << "datalake_translation_state"; + return fmt::format_to(out, "datalake_translation_state"); case record_batch_type::cluster_link: - return o << "cluster_link"; + return fmt::format_to(out, "cluster_link"); case record_batch_type::group_block: - return o << "group_block"; + return fmt::format_to(out, "group_block"); case record_batch_type::l1_stm: - return o << "l1_stm"; + return fmt::format_to(out, "l1_stm"); case record_batch_type::ct_read_replica_stm: - return o << "ct_read_replica_stm"; + return fmt::format_to(out, "ct_read_replica_stm"); } - - return o << "batch_type::unknown{" << static_cast(bt) << "}"; + return fmt::format_to( + out, "batch_type::unknown{{{}}}", static_cast(bt)); } -std::ostream& operator<<(std::ostream& o, membership_state st) { +fmt::iterator format_to(membership_state st, fmt::iterator out) { switch (st) { case membership_state::active: - return o << "active"; + return fmt::format_to(out, "active"); case membership_state::draining: - return o << "draining"; + return fmt::format_to(out, "draining"); case membership_state::removed: - return o << "removed"; + return fmt::format_to(out, "removed"); } - return o << "unknown membership state {" << static_cast(st) << "}"; + return fmt::format_to( + out, "unknown membership state {{{}}}", static_cast(st)); } -std::ostream& operator<<(std::ostream& o, maintenance_state st) { +fmt::iterator format_to(maintenance_state st, fmt::iterator out) { switch (st) { case maintenance_state::active: - return o << "active"; + return fmt::format_to(out, "active"); case maintenance_state::inactive: - return o << "inactive"; + return fmt::format_to(out, "inactive"); } - __builtin_unreachable(); } -std::ostream& operator<<(std::ostream& os, const cloud_credentials_source& cs) { +fmt::iterator format_to(cloud_credentials_source cs, fmt::iterator out) { switch (cs) { case cloud_credentials_source::config_file: - return os << "config_file"; + return fmt::format_to(out, "config_file"); case cloud_credentials_source::aws_instance_metadata: - return os << "aws_instance_metadata"; + return fmt::format_to(out, "aws_instance_metadata"); case cloud_credentials_source::sts: - return os << "sts"; + return fmt::format_to(out, "sts"); case cloud_credentials_source::gcp_instance_metadata: - return os << "gcp_instance_metadata"; + return fmt::format_to(out, "gcp_instance_metadata"); case cloud_credentials_source::azure_aks_oidc_federation: - return os << "azure_aks_oidc_federation"; + return fmt::format_to(out, "azure_aks_oidc_federation"); case cloud_credentials_source::azure_vm_instance_metadata: - return os << "azure_vm_instance_metadata"; + return fmt::format_to(out, "azure_vm_instance_metadata"); } + return fmt::format_to(out, "unknown"); } -std::ostream& operator<<(std::ostream& o, const shadow_indexing_mode& si) { +fmt::iterator format_to(shadow_indexing_mode si, fmt::iterator out) { switch (si) { case shadow_indexing_mode::disabled: - o << "disabled"; - break; + return fmt::format_to(out, "disabled"); case shadow_indexing_mode::archival: - o << "archival"; - break; + return fmt::format_to(out, "archival"); case shadow_indexing_mode::fetch: - o << "fetch"; - break; + return fmt::format_to(out, "fetch"); case shadow_indexing_mode::full: - o << "full"; - break; + return fmt::format_to(out, "full"); case shadow_indexing_mode::drop_archival: - o << "drop_archival"; - break; + return fmt::format_to(out, "drop_archival"); case shadow_indexing_mode::drop_fetch: - o << "drop_fetch"; - break; + return fmt::format_to(out, "drop_fetch"); case shadow_indexing_mode::drop_full: - o << "drop_full"; - break; + return fmt::format_to(out, "drop_full"); } - return o; + return fmt::format_to(out, "unknown"); } -std::ostream& operator<<(std::ostream& o, const control_record_type& crt) { +fmt::iterator format_to(control_record_type crt, fmt::iterator out) { switch (crt) { case control_record_type::tx_abort: - return o << "tx_abort"; + return fmt::format_to(out, "tx_abort"); case control_record_type::tx_commit: - return o << "tx_commit"; + return fmt::format_to(out, "tx_commit"); case control_record_type::unknown: - return o << "unknown"; + return fmt::format_to(out, "unknown"); } + return fmt::format_to(out, "unknown"); } -std::ostream& operator<<(std::ostream& o, const batch_identity& bid) { - fmt::print( - o, +fmt::iterator batch_identity::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{pid: {}, first_seq: {}, is_transactional: {}, record_count: {}, " "last_seq: {}}}", - bid.pid, - bid.first_seq, - bid.is_transactional, - bid.record_count, - bid.last_seq); - return o; + pid, + first_seq, + is_transactional, + record_count, + last_seq); } -std::ostream& operator<<(std::ostream& o, fetch_read_strategy s) { - o << fetch_read_strategy_to_string(s); - return o; +fmt::iterator format_to(fetch_read_strategy s, fmt::iterator out) { + return fmt::format_to(out, "{}", fetch_read_strategy_to_string(s)); } std::istream& operator>>(std::istream& i, fetch_read_strategy& strat) { @@ -545,9 +513,8 @@ std::istream& operator>>(std::istream& i, fetch_read_strategy& strat) { return i; } -std::ostream& operator<<(std::ostream& o, write_caching_mode mode) { - o << write_caching_mode_to_string(mode); - return o; +fmt::iterator format_to(write_caching_mode mode, fmt::iterator out) { + return fmt::format_to(out, "{}", write_caching_mode_to_string(mode)); } std::istream& operator>>(std::istream& i, write_caching_mode& mode) { @@ -580,9 +547,8 @@ write_caching_mode_from_string(std::string_view s) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& o, redpanda_storage_mode mode) { - o << redpanda_storage_mode_to_string(mode); - return o; +fmt::iterator format_to(redpanda_storage_mode mode, fmt::iterator out) { + return fmt::format_to(out, "{}", redpanda_storage_mode_to_string(mode)); } std::istream& operator>>(std::istream& i, redpanda_storage_mode& mode) { @@ -623,16 +589,17 @@ redpanda_storage_mode_from_string(std::string_view s) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& os, recovery_validation_mode vm) { +fmt::iterator format_to(recovery_validation_mode vm, fmt::iterator out) { using enum recovery_validation_mode; switch (vm) { case check_manifest_existence: - return os << "check_manifest_existence"; + return fmt::format_to(out, "check_manifest_existence"); case check_manifest_and_segment_metadata: - return os << "check_manifest_and_segment_metadata"; + return fmt::format_to(out, "check_manifest_and_segment_metadata"); case no_check: - return os << "no_check"; + return fmt::format_to(out, "no_check"); } + return fmt::format_to(out, "unknown"); } std::istream& operator>>(std::istream& is, recovery_validation_mode& vm) { @@ -695,31 +662,32 @@ void read_nested( fmt::format("unknown iceberg_mode variant: {}", std::to_underlying(v))); } -std::ostream& operator<<(std::ostream& os, const iceberg_mode& mode) { - switch (mode.kind()) { - case iceberg_mode::variant::disabled: - return os << "disabled"; - case iceberg_mode::variant::key_value: - return os << "key_value"; - case iceberg_mode::variant::value_schema_id_prefix: - return os << "value_schema_id_prefix"; - case iceberg_mode::variant::value_schema_latest: - os << "value_schema_latest"; +fmt::iterator iceberg_mode::format_to(fmt::iterator it) const { + switch (kind()) { + case variant::disabled: + return fmt::format_to(it, "disabled"); + case variant::key_value: + return fmt::format_to(it, "key_value"); + case variant::value_schema_id_prefix: + return fmt::format_to(it, "value_schema_id_prefix"); + case variant::value_schema_latest: + it = fmt::format_to(it, "value_schema_latest"); bool delimiter = false; - auto emit_delimiter = [&delimiter, &os]() { - os << (delimiter ? "," : ":"); + auto emit_delimiter = [&delimiter, &it]() { + it = fmt::format_to(it, "{}", delimiter ? "," : ":"); delimiter = true; }; - if (auto protobuf_name = mode.protobuf_full_name()) { + if (auto pname = protobuf_full_name()) { emit_delimiter(); - os << "protobuf_name=" << protobuf_name.value(); + it = fmt::format_to(it, "protobuf_name={}", pname.value()); } - if (auto subject = mode.subject_name()) { + if (auto subj = subject_name()) { emit_delimiter(); - os << "subject=" << subject.value(); + it = fmt::format_to(it, "subject={}", subj.value()); } - return os; + return it; } + return it; } namespace { @@ -788,14 +756,14 @@ std::istream& operator>>(std::istream& is, iceberg_mode& mode) { return is; } -std::ostream& -operator<<(std::ostream& os, const iceberg_invalid_record_action& a) { +fmt::iterator format_to(iceberg_invalid_record_action a, fmt::iterator out) { switch (a) { case iceberg_invalid_record_action::drop: - return os << "drop"; + return fmt::format_to(out, "drop"); case iceberg_invalid_record_action::dlq_table: - return os << "dlq_table"; + return fmt::format_to(out, "dlq_table"); } + return fmt::format_to(out, "unknown"); } std::istream& operator>>(std::istream& is, iceberg_invalid_record_action& a) { @@ -812,8 +780,8 @@ std::istream& operator>>(std::istream& is, iceberg_invalid_record_action& a) { return is; } -std::ostream& operator<<(std::ostream& os, const fips_mode_flag& f) { - return os << to_string_view(f); +fmt::iterator format_to(fips_mode_flag f, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(f)); } std::istream& operator>>(std::istream& is, fips_mode_flag& f) { @@ -870,10 +838,9 @@ kafka_batch_validation_mode_from_string(std::string_view s) { .default_match(std::nullopt); } -std::ostream& -operator<<(std::ostream& o, const kafka_batch_validation_mode& mode) { - o << kafka_batch_validation_mode_to_string(mode); - return o; +fmt::iterator format_to(kafka_batch_validation_mode mode, fmt::iterator out) { + return fmt::format_to( + out, "{}", kafka_batch_validation_mode_to_string(mode)); } std::istream& operator>>(std::istream& i, kafka_batch_validation_mode& mode) { @@ -888,4 +855,14 @@ std::istream& operator>>(std::istream& i, kafka_batch_validation_mode& mode) { return i; } +fmt::iterator format_to(isolation_level l, fmt::iterator out) { + switch (l) { + case isolation_level::read_uncommitted: + return fmt::format_to(out, "read_uncommitted"); + case isolation_level::read_committed: + return fmt::format_to(out, "read_committed"); + } + return fmt::format_to(out, "unknown"); +} + } // namespace model diff --git a/src/v/model/offset_interval.h b/src/v/model/offset_interval.h index 5052fdb434914..c2f36f2370937 100644 --- a/src/v/model/offset_interval.h +++ b/src/v/model/offset_interval.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "base/vassert.h" #include "model/fundamental.h" @@ -59,10 +60,8 @@ class bounded_offset_interval { return _min <= o && o <= _max; } - friend std::ostream& - operator<<(std::ostream& o, const bounded_offset_interval& r) { - fmt::print(o, "{{min: {}, max: {}}}", r._min, r._max); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{min: {}, max: {}}}", _min, _max); } inline model::offset min() const noexcept { return _min; } diff --git a/src/v/model/record.cc b/src/v/model/record.cc index 3ce2cfe4b517b..e1e802aba2790 100644 --- a/src/v/model/record.cc +++ b/src/v/model/record.cc @@ -67,10 +67,8 @@ record_batch_iterator::record_batch_iterator(int32_t rc, iobuf_parser p) : _record_count(rc) , _parser(std::move(p)) {} -std::ostream& operator<<(std::ostream& os, const tx_range& range) { - fmt::print( - os, "pid: {}, range: [{}, {}]", range.pid, range.first, range.last); - return os; +fmt::iterator tx_range::format_to(fmt::iterator it) const { + return fmt::format_to(it, "pid: {}, range: [{}, {}]", pid, first, last); } void record_batch_header::reset_size_checksum_metadata(const iobuf& records) { diff --git a/src/v/model/record.h b/src/v/model/record.h index bcc2f504cf8e1..b2baac54dae28 100644 --- a/src/v/model/record.h +++ b/src/v/model/record.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/vassert.h" #include "bytes/iobuf.h" #include "bytes/iobuf_parser.h" @@ -36,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -73,7 +73,7 @@ class record_attributes final { return !(*this == other); } - friend std::ostream& operator<<(std::ostream&, const record_attributes&); + fmt::iterator format_to(fmt::iterator it) const; private: std::bitset<8> _attributes; @@ -126,7 +126,7 @@ class record_header { && _key == rhs._key && _value == rhs._value; } - friend std::ostream& operator<<(std::ostream&, const record_header&); + fmt::iterator format_to(fmt::iterator it) const; private: // If negative, the key is nil. @@ -335,7 +335,7 @@ class record { bool operator!=(const record& other) const { return !(*this == other); } - friend std::ostream& operator<<(std::ostream&, const record&); + fmt::iterator format_to(fmt::iterator it) const; private: int32_t _size_bytes{0}; @@ -456,8 +456,7 @@ class record_batch_attributes final { serde::write(out, static_cast(el._attributes.to_ullong())); } - friend std::ostream& - operator<<(std::ostream&, const record_batch_attributes&); + fmt::iterator format_to(fmt::iterator it) const; private: uint16_t maskable_value() const { @@ -601,7 +600,7 @@ struct record_batch_header /// \brief resets the size, header crc and payload crc void reset_size_checksum_metadata(const iobuf& records); - friend std::ostream& operator<<(std::ostream&, const record_batch_header&); + fmt::iterator format_to(fmt::iterator it) const; }; using tx_seq = named_type; @@ -644,7 +643,7 @@ struct producer_identity return H::combine(std::move(h), pid.id, pid.epoch); } - friend std::ostream& operator<<(std::ostream&, const producer_identity&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(id, epoch); } }; @@ -668,7 +667,8 @@ struct tx_range auto serde_fields() { return std::tie(pid, first, last); } auto operator<=>(const tx_range&) const = default; - friend std::ostream& operator<<(std::ostream&, const tx_range&); + + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const tx_range& range) { @@ -714,7 +714,7 @@ struct batch_identity { bool is_idempotent() const { return pid.id > no_producer_id; } - friend std::ostream& operator<<(std::ostream&, const batch_identity&); + fmt::iterator format_to(fmt::iterator it) const; }; // A simple iterator for model::record_batch. Note that this iterator makes a @@ -889,7 +889,7 @@ class record_batch return !(*this == other); } - friend std::ostream& operator<<(std::ostream&, const record_batch&); + fmt::iterator format_to(fmt::iterator it) const; record_batch share() { return record_batch( diff --git a/src/v/model/record_batch_reader.cc b/src/v/model/record_batch_reader.cc index 22df158573438..1832047c53e2b 100644 --- a/src/v/model/record_batch_reader.cc +++ b/src/v/model/record_batch_reader.cc @@ -21,6 +21,7 @@ #include #include +#include #include namespace model { @@ -45,12 +46,12 @@ record_batch_reader make_foreign_record_batch_reader(record_batch_reader&& r) { return _ptr->is_end_of_stream(); } - void print(std::ostream& os) final { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const final { + it = fmt::format_to( + it, "foreign_record_batch_reader. remote_core:{} - proxy for:", _ptr.get_owner_shard()); - _ptr->print(os); + return _ptr->format_to(it); } ss::future do_load_slice(timeout_clock::time_point t) final { @@ -87,12 +88,12 @@ record_batch_reader make_memory_record_batch_reader(storage_t batches) { }); } - void print(std::ostream& os) final { + fmt::iterator format_to(fmt::iterator it) const final { auto size = ss::visit( _batches, [](const data_t& d) { return d.size(); }, [](const foreign_data_t& d) { return d.buffer->size(); }); - fmt::print(os, "memory reader {} batches", size); + return fmt::format_to(it, "memory reader {} batches", size); } protected: @@ -129,8 +130,8 @@ record_batch_reader make_empty_record_batch_reader() { co_return data_t{}; } - void print(std::ostream& os) final { - os << "{empty_record_batch_reader}"; + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to(it, "{{empty_record_batch_reader}}"); } }; return make_record_batch_reader(); @@ -147,8 +148,8 @@ record_batch_reader make_generating_record_batch_reader( bool is_end_of_stream() const final { return _end_of_stream; } - void print(std::ostream& os) final { - os << "{generating batch reader}"; + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to(it, "{{generating batch reader}}"); } protected: @@ -192,12 +193,12 @@ make_readahead_record_batch_reader(record_batch_reader&& reader) { && !_readahead_future.has_value(); } - void print(std::ostream& os) final { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const final { + it = fmt::format_to( + it, "readahead_reader(buffered={}) wrapping: ", _readahead_future.has_value() ? 1 : 0); - _underlying->print(os); + return _underlying->format_to(it); } ss::future @@ -251,9 +252,9 @@ record_batch_reader make_chunked_memory_record_batch_reader( bool is_end_of_stream() const final { return _index >= _data.size(); } - void print(std::ostream& os) final { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to( + it, "fragmented memory reader {} batches of batches", _data.size()); } @@ -374,9 +375,8 @@ consume_reader_to_chunked_vector( chunked_vector>(std::move(reader), timeout); } -std::ostream& operator<<(std::ostream& os, const record_batch_reader& r) { - r._impl->print(os); - return os; +fmt::iterator record_batch_reader::format_to(fmt::iterator it) const { + return _impl->format_to(it); } } // namespace model diff --git a/src/v/model/record_batch_reader.h b/src/v/model/record_batch_reader.h index 2b03af69b1f3d..0cc8ce9ce052d 100644 --- a/src/v/model/record_batch_reader.h +++ b/src/v/model/record_batch_reader.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/likely.h" #include "base/seastarx.h" #include "container/chunked_circular_buffer.h" @@ -73,7 +74,7 @@ class record_batch_reader final { virtual ss::future do_load_slice(timeout_clock::time_point) = 0; - virtual void print(std::ostream&) = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; virtual std::optional get_flags() const { return {}; } @@ -381,6 +382,8 @@ class record_batch_reader final { std::unique_ptr release() && { return std::move(_impl); } + fmt::iterator format_to(fmt::iterator it) const; + // record batch readers may expose these private flags for testing // purposes struct private_flags { @@ -401,9 +404,6 @@ class record_batch_reader final { explicit operator bool() const noexcept { return bool(_impl); } friend class ss::optimized_optional; - friend std::ostream& - operator<<(std::ostream& os, const record_batch_reader& r); - std::optional get_flags() const { return _impl->get_flags(); } diff --git a/src/v/model/record_batch_types.h b/src/v/model/record_batch_types.h index 1ffa700c43d2e..fb7b67f1fdbb1 100644 --- a/src/v/model/record_batch_types.h +++ b/src/v/model/record_batch_types.h @@ -11,10 +11,11 @@ #pragma once +#include "base/format_to.h" #include "utils/named_type.h" #include -#include +#include #include namespace model { @@ -66,7 +67,7 @@ enum class record_batch_type : int8_t { MAX = ct_read_replica_stm, }; -std::ostream& operator<<(std::ostream& o, record_batch_type bt); +fmt::iterator format_to(record_batch_type bt, fmt::iterator out); // The set of batch types that may appear in a data partition that aren't // assigned a new translated offset. When translated, such batches are given an diff --git a/src/v/model/timestamp.h b/src/v/model/timestamp.h index 825d621f59afe..f0f8471d742bb 100644 --- a/src/v/model/timestamp.h +++ b/src/v/model/timestamp.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -29,7 +30,8 @@ namespace model { enum class timestamp_type : uint8_t { create_time = 0, append_time = 1 }; -std::ostream& operator<<(std::ostream&, timestamp_type); +fmt::iterator format_to(timestamp_type ts, fmt::iterator out); + std::istream& operator>>(std::istream&, timestamp_type&); class timestamp { @@ -80,7 +82,7 @@ class timestamp { return lhs; } - friend std::ostream& operator<<(std::ostream&, timestamp); + fmt::iterator format_to(fmt::iterator it) const; // ADL helpers for interfacing with the serde library. friend void write_nested(iobuf& out, timestamp ts); diff --git a/src/v/model/transform.cc b/src/v/model/transform.cc index 449005b7168ab..b224e8ba861d5 100644 --- a/src/v/model/transform.cc +++ b/src/v/model/transform.cc @@ -22,7 +22,6 @@ #include "serde/rw/vector.h" #include "utils/vint.h" -#include #include #include #include @@ -79,33 +78,21 @@ void append_vint_to_iobuf(iobuf& b, int64_t v) { } // namespace -std::ostream& operator<<(std::ostream& os, const transform_from_start& o) { - fmt::print(os, "{{ start + {} }}", o.delta); - return os; -} - -std::ostream& operator<<(std::ostream& os, const transform_from_end& o) { - fmt::print(os, "{{ end - {} }}", o.delta); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const transform_offset_options& opts) { - ss::visit( - opts.position, - [&os](transform_offset_options::latest_offset) { - fmt::print(os, "{{ latest_offset }}"); +fmt::iterator transform_offset_options::format_to(fmt::iterator it) const { + return ss::visit( + position, + [&it](transform_offset_options::latest_offset) { + return fmt::format_to(it, "{{ latest_offset }}"); }, - [&os](model::timestamp ts) { - fmt::print(os, "{{ timequery: {} }}", ts.value()); + [&it](model::timestamp ts) { + return fmt::format_to(it, "{{ timequery: {} }}", ts.value()); }, - [&os](model::transform_from_start off) { - fmt::print(os, "{{ offset: {} }}", off); + [&it](model::transform_from_start off) { + return fmt::format_to(it, "{{ offset: {} }}", off); }, - [&os](model::transform_from_end off) { - fmt::print(os, "{{ offset: {} }}", off); + [&it](model::transform_from_end off) { + return fmt::format_to(it, "{{ offset: {} }}", off); }); - return os; } /** @@ -156,19 +143,18 @@ void transform_offset_options::serde_write(iobuf& out) const { serde::write(out, position); } -std::ostream& operator<<(std::ostream& os, const transform_metadata& meta) { - fmt::print( - os, +fmt::iterator transform_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{name: \"{}\", input: {}, outputs: {}, " "env: , uuid: {}, source_ptr: {}, is_paused: {} }}", - meta.name, - meta.input_topic, - meta.output_topics, - // skip env becuase of pii - meta.uuid, - meta.source_ptr, - meta.paused); - return os; + name, + input_topic, + output_topics, + // skip env because of pii + uuid, + source_ptr, + paused); } void transform_metadata::serde_write(iobuf& out) const { @@ -218,14 +204,13 @@ bool transform_metadata_patch::empty() const noexcept { && !compression_mode.has_value(); } -std::ostream& operator<<(std::ostream& os, const transform_offsets_key& key) { - fmt::print( - os, +fmt::iterator transform_offsets_key::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ transform id: {}, partition: {}, output_topic: {} }}", - key.id, - key.partition, - key.output_topic); - return os; + id, + partition, + output_topic); } void transform_offsets_key::serde_read( @@ -251,24 +236,6 @@ void transform_offsets_key::serde_write(iobuf& out) const { serde::write(out, output_topic); } -std::ostream& -operator<<(std::ostream& os, const transform_offsets_value& value) { - fmt::print(os, "{{ offset: {} }}", value.offset); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const transform_report::processor& p) { - fmt::print( - os, - "{{id: {}, status: {}, node: {}, lag: {}}}", - p.id, - p.status, - p.node, - p.lag); - return os; -} - transform_report::transform_report(transform_metadata meta) : metadata(std::move(meta)) , processors() {} @@ -298,26 +265,6 @@ void cluster_transform_report::merge(const cluster_transform_report& other) { } } -std::ostream& -operator<<(std::ostream& os, transform_report::processor::state s) { - return os << processor_state_to_string(s); -} - -std::string_view -processor_state_to_string(transform_report::processor::state state) { - switch (state) { - case transform_report::processor::state::inactive: - return "inactive"; - case transform_report::processor::state::running: - return "running"; - case transform_report::processor::state::errored: - return "errored"; - case transform_report::processor::state::unknown: - break; - } - return "unknown"; -} - transformed_data::transformed_data(iobuf d) : _data(std::move(d)) {} diff --git a/src/v/model/transform.h b/src/v/model/transform.h index 88274306ed2b1..e82bb9af74288 100644 --- a/src/v/model/transform.h +++ b/src/v/model/transform.h @@ -13,6 +13,7 @@ #include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "model/fundamental.h" #include "model/metadata.h" @@ -106,7 +107,9 @@ struct transform_from_start auto serde_fields() { return std::tie(delta); } - friend std::ostream& operator<<(std::ostream&, const transform_from_start&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ start + {} }}", delta); + } bool operator==(const transform_from_start&) const = default; }; @@ -134,7 +137,9 @@ struct transform_from_end auto serde_fields() { return std::tie(delta); } - friend std::ostream& operator<<(std::ostream&, const transform_from_end&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ end - {} }}", delta); + } bool operator==(const transform_from_end&) const = default; }; @@ -183,8 +188,7 @@ struct transform_offset_options bool operator==(const transform_offset_options&) const = default; - friend std::ostream& - operator<<(std::ostream&, const transform_offset_options&); + fmt::iterator format_to(fmt::iterator it) const; void serde_read(iobuf_parser& in, const serde::header& h); void serde_write(iobuf& out) const; @@ -224,7 +228,7 @@ struct transform_metadata friend bool operator==(const transform_metadata&, const transform_metadata&) = default; - friend std::ostream& operator<<(std::ostream&, const transform_metadata&); + fmt::iterator format_to(fmt::iterator it) const; void serde_write(iobuf& out) const; void serde_read(iobuf_parser& in, const serde::header& h); @@ -267,8 +271,7 @@ struct transform_offsets_key auto operator<=>(const transform_offsets_key&) const = default; - friend std::ostream& - operator<<(std::ostream&, const transform_offsets_key&); + fmt::iterator format_to(fmt::iterator it) const; template friend H AbslHashValue(H h, const transform_offsets_key& k) { @@ -287,8 +290,9 @@ struct transform_offsets_value serde::compat_version<0>> { kafka::offset offset; - friend std::ostream& - operator<<(std::ostream&, const transform_offsets_value&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ offset: {} }}", offset); + } auto serde_fields() { return std::tie(offset); } }; @@ -328,7 +332,7 @@ struct transform_report int64_t lag; friend bool operator==(const processor&, const processor&) = default; - friend std::ostream& operator<<(std::ostream&, const processor&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(id, status, node, lag); } }; @@ -356,11 +360,37 @@ struct transform_report void add(processor); }; -std::string_view -processor_state_to_string(transform_report::processor::state state); - -std::ostream& -operator<<(std::ostream& os, transform_report::processor::state s); +inline constexpr std::string_view +to_string_view(transform_report::processor::state state) { + switch (state) { + case transform_report::processor::state::inactive: + return "inactive"; + case transform_report::processor::state::running: + return "running"; + case transform_report::processor::state::errored: + return "errored"; + case transform_report::processor::state::unknown: + break; + } + return "unknown"; +} + +inline fmt::iterator +format_to(transform_report::processor::state state, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(state)); +} + +/// Kept as an alias for existing callers. +inline std::string_view +processor_state_to_string(transform_report::processor::state state) { + return to_string_view(state); +} + +inline fmt::iterator +transform_report::processor::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, status: {}, node: {}, lag: {}}}", id, status, node, lag); +} /** * A cluster wide view of all the currently running transforms. diff --git a/src/v/net/conn_quota.h b/src/v/net/conn_quota.h index 97f6ed6f7d485..51b79b8863980 100644 --- a/src/v/net/conn_quota.h +++ b/src/v/net/conn_quota.h @@ -11,6 +11,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/hash/hash.h" +#include "base/format_to.h" #include "base/oncore.h" #include "base/seastarx.h" #include "base/vassert.h" @@ -125,13 +126,12 @@ class conn_quota : public ss::peering_sharded_service { // do reclaims (would happen if multiple incoming connections // on the same shard when available==0) ssx::mutex reclaim_lock{"conn_quota::reclaim_lock"}; - }; - friend std::ostream& operator<<(std::ostream& o, const home_allowance& ha) { - fmt::print( - o, "{{ {}/{} reclaim={}}}", ha.available, ha.max, ha.reclaim); - return o; - } + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ {}/{} reclaim={}}}", available, max, reclaim); + } + }; /** * State on a non-home core for an address: records how many tokens @@ -162,6 +162,11 @@ class conn_quota : public ss::peering_sharded_service { return H::combine( std::move(h), std::hash{}(k)); } + + fmt::iterator format_to(fmt::iterator it) const { + const auto& base = static_cast(*this); + return fmt::format_to(it, "{}", fmt_streamed(base)); + } }; // Which shard holds home_allowance for this address? diff --git a/src/v/net/server.cc b/src/v/net/server.cc index 9cd3a4409a4e0..1bd6c75de35b9 100644 --- a/src/v/net/server.cc +++ b/src/v/net/server.cc @@ -369,33 +369,27 @@ void server::setup_public_metrics() { .aggregate({sm::shard_label})}); } -std::ostream& operator<<(std::ostream& o, const server_configuration& c) { - o << "{"; - for (auto& a : c.addrs) { - o << a; +fmt::iterator server_configuration::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{"); + for (auto& a : addrs) { + it = fmt::format_to(it, "{}", a); } - o << ", max_service_memory_per_core: " << c.max_service_memory_per_core - << ", metrics_enabled:" << !c.disable_metrics - << ", listen_backlog:" << c.listen_backlog - << ", tcp_recv_buf:" << c.tcp_recv_buf - << ", tcp_send_buf:" << c.tcp_send_buf - << ", stream_recv_buf:" << c.stream_recv_buf; - return o << "}"; + return fmt::format_to( + it, + ", max_service_memory_per_core: {}, metrics_enabled:{}" + ", listen_backlog:{}, tcp_recv_buf:{}, tcp_send_buf:{}" + ", stream_recv_buf:{}}}", + max_service_memory_per_core, + !disable_metrics, + listen_backlog, + tcp_recv_buf, + tcp_send_buf, + stream_recv_buf); } -std::ostream& operator<<(std::ostream& os, const server_endpoint& ep) { - /** - * We use simmillar syntax to kafka to indicate if endpoint is secured f.e.: - * - * SECURED://127.0.0.1:9092 - */ - fmt::print( - os, - "{{{}://{}:{}}}", - ep.name, - ep.addr, - ep.credentials ? "SECURED" : "PLAINTEXT"); - return os; +fmt::iterator server_endpoint::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{{}://{}:{}}}", name, addr, credentials ? "SECURED" : "PLAINTEXT"); } } // namespace net diff --git a/src/v/net/server.h b/src/v/net/server.h index 506b5d39ce9b8..8001d08dba884 100644 --- a/src/v/net/server.h +++ b/src/v/net/server.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "config/property.h" #include "metrics/metrics.h" #include "net/conn_quota.h" @@ -64,7 +65,7 @@ struct server_endpoint { explicit server_endpoint(ss::socket_address addr) : server_endpoint("", addr) {} - friend std::ostream& operator<<(std::ostream&, const server_endpoint&); + fmt::iterator format_to(fmt::iterator it) const; }; struct config_connection_rate_bindings { @@ -100,7 +101,7 @@ struct server_configuration { explicit server_configuration(ss::sstring n) : name(std::move(n)) {} - friend std::ostream& operator<<(std::ostream&, const server_configuration&); + fmt::iterator format_to(fmt::iterator it) const; }; class server { diff --git a/src/v/pandaproxy/schema_registry/avro.cc b/src/v/pandaproxy/schema_registry/avro.cc index 9e0eca19f8eaf..4aa60c3f2c0e6 100644 --- a/src/v/pandaproxy/schema_registry/avro.cc +++ b/src/v/pandaproxy/schema_registry/avro.cc @@ -515,15 +515,14 @@ bool operator==( return lhs.raw() == rhs.raw(); } -std::ostream& operator<<(std::ostream& os, const avro_schema_definition& def) { - fmt::print( - os, +fmt::iterator avro_schema_definition::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "type: {}, definition: {}, references: {}, metadata: {}", - to_string_view(def.type()), - def().toJson(false), - def.refs(), - def.meta()); - return os; + to_string_view(type()), + (*this)().toJson(false), + refs(), + meta()); } schema_definition::raw_string avro_schema_definition::raw() const { diff --git a/src/v/pandaproxy/schema_registry/compatibility.cc b/src/v/pandaproxy/schema_registry/compatibility.cc index 958e835ba816e..bd8955199ea82 100644 --- a/src/v/pandaproxy/schema_registry/compatibility.cc +++ b/src/v/pandaproxy/schema_registry/compatibility.cc @@ -13,26 +13,6 @@ namespace pandaproxy::schema_registry { -std::ostream& operator<<(std::ostream& os, const avro_incompatibility_type& t) { - switch (t) { - case avro_incompatibility_type::name_mismatch: - return os << "NAME_MISMATCH"; - case avro_incompatibility_type::fixed_size_mismatch: - return os << "FIXED_SIZE_MISMATCH"; - case avro_incompatibility_type::missing_enum_symbols: - return os << "MISSING_ENUM_SYMBOLS"; - case avro_incompatibility_type::reader_field_missing_default_value: - return os << "READER_FIELD_MISSING_DEFAULT_VALUE"; - case avro_incompatibility_type::type_mismatch: - return os << "TYPE_MISMATCH"; - case avro_incompatibility_type::missing_union_branch: - return os << "MISSING_UNION_BRANCH"; - case avro_incompatibility_type::unknown: - return os << "UNKNOWN"; - }; - __builtin_unreachable(); -} - std::string_view description_for_type(avro_incompatibility_type t) { switch (t) { case avro_incompatibility_type::name_mismatch: @@ -60,16 +40,6 @@ std::string_view description_for_type(avro_incompatibility_type t) { __builtin_unreachable(); } -std::ostream& operator<<(std::ostream& os, const avro_incompatibility& v) { - fmt::print( - os, - "{{errorType:'{}', description:'{}', additionalInfo:'{}'}}", - v._type, - v.describe(), - v._additional_info); - return os; -} - ss::sstring avro_incompatibility::describe() const { return fmt::format( fmt::runtime(description_for_type(_type)), @@ -77,31 +47,6 @@ ss::sstring avro_incompatibility::describe() const { fmt::arg("additional", _additional_info)); } -std::ostream& -operator<<(std::ostream& os, const proto_incompatibility_type& t) { - switch (t) { - case proto_incompatibility_type::message_removed: - return os << "MESSAGE_REMOVED"; - case proto_incompatibility_type::field_kind_changed: - return os << "FIELD_KIND_CHANGED"; - case proto_incompatibility_type::field_scalar_kind_changed: - return os << "FIELD_SCALAR_KIND_CHANGED"; - case proto_incompatibility_type::field_named_type_changed: - return os << "FIELD_NAMED_TYPE_CHANGED"; - case proto_incompatibility_type::required_field_added: - return os << "REQUIRED_FIELD_ADDED"; - case proto_incompatibility_type::required_field_removed: - return os << "REQUIRED_FIELD_REMOVED"; - case proto_incompatibility_type::oneof_field_removed: - return os << "ONEOF_FIELD_REMOVED"; - case proto_incompatibility_type::multiple_fields_moved_to_oneof: - return os << "MULTIPLE_FIELDS_MOVED_TO_ONEOF"; - case proto_incompatibility_type::unknown: - return os << "UNKNOWN"; - } - __builtin_unreachable(); -} - std::string_view description_for_type(proto_incompatibility_type t) { switch (t) { case proto_incompatibility_type::message_removed: @@ -136,141 +81,139 @@ std::string_view description_for_type(proto_incompatibility_type t) { __builtin_unreachable(); } -std::ostream& operator<<(std::ostream& os, const proto_incompatibility& v) { - fmt::print( - os, R"({{errorType:"{}", description:"{}"}})", v._type, v.describe()); - return os; -} - ss::sstring proto_incompatibility::describe() const { return fmt::format( fmt::runtime(description_for_type(_type)), fmt::arg("path", _path.string())); } -std::ostream& operator<<(std::ostream& os, const json_incompatibility_type& t) { +fmt::iterator format_to(json_incompatibility_type t, fmt::iterator out) { switch (t) { case json_incompatibility_type::type_narrowed: - return os << "TYPE_NARROWED"; + return fmt::format_to(out, "TYPE_NARROWED"); case json_incompatibility_type::type_changed: - return os << "TYPE_CHANGED"; + return fmt::format_to(out, "TYPE_CHANGED"); case json_incompatibility_type::max_length_added: - return os << "MAX_LENGTH_ADDED"; + return fmt::format_to(out, "MAX_LENGTH_ADDED"); case json_incompatibility_type::max_length_decreased: - return os << "MAX_LENGTH_DECREASED"; + return fmt::format_to(out, "MAX_LENGTH_DECREASED"); case json_incompatibility_type::min_length_added: - return os << "MIN_LENGTH_ADDED"; + return fmt::format_to(out, "MIN_LENGTH_ADDED"); case json_incompatibility_type::min_length_increased: - return os << "MIN_LENGTH_INCREASED"; + return fmt::format_to(out, "MIN_LENGTH_INCREASED"); case json_incompatibility_type::pattern_added: - return os << "PATTERN_ADDED"; + return fmt::format_to(out, "PATTERN_ADDED"); case json_incompatibility_type::pattern_changed: - return os << "PATTERN_CHANGED"; + return fmt::format_to(out, "PATTERN_CHANGED"); case json_incompatibility_type::maximum_added: - return os << "MAXIMUM_ADDED"; + return fmt::format_to(out, "MAXIMUM_ADDED"); case json_incompatibility_type::maximum_decreased: - return os << "MAXIMUM_DECREASED"; + return fmt::format_to(out, "MAXIMUM_DECREASED"); case json_incompatibility_type::minimum_added: - return os << "MINIMUM_ADDED"; + return fmt::format_to(out, "MINIMUM_ADDED"); case json_incompatibility_type::minimum_increased: - return os << "MINIMUM_INCREASED"; + return fmt::format_to(out, "MINIMUM_INCREASED"); case json_incompatibility_type::exclusive_maximum_added: - return os << "EXCLUSIVE_MAXIMUM_ADDED"; + return fmt::format_to(out, "EXCLUSIVE_MAXIMUM_ADDED"); case json_incompatibility_type::exclusive_maximum_decreased: - return os << "EXCLUSIVE_MAXIMUM_DECREASED"; + return fmt::format_to(out, "EXCLUSIVE_MAXIMUM_DECREASED"); case json_incompatibility_type::exclusive_minimum_added: - return os << "EXCLUSIVE_MINIMUM_ADDED"; + return fmt::format_to(out, "EXCLUSIVE_MINIMUM_ADDED"); case json_incompatibility_type::exclusive_minimum_increased: - return os << "EXCLUSIVE_MINIMUM_INCREASED"; + return fmt::format_to(out, "EXCLUSIVE_MINIMUM_INCREASED"); case json_incompatibility_type::multiple_of_added: - return os << "MULTIPLE_OF_ADDED"; + return fmt::format_to(out, "MULTIPLE_OF_ADDED"); case json_incompatibility_type::multiple_of_expanded: - return os << "MULTIPLE_OF_EXPANDED"; + return fmt::format_to(out, "MULTIPLE_OF_EXPANDED"); case json_incompatibility_type::multiple_of_changed: - return os << "MULTIPLE_OF_CHANGED"; + return fmt::format_to(out, "MULTIPLE_OF_CHANGED"); case json_incompatibility_type::required_attribute_added: - return os << "REQUIRED_ATTRIBUTE_ADDED"; + return fmt::format_to(out, "REQUIRED_ATTRIBUTE_ADDED"); case json_incompatibility_type::max_properties_added: - return os << "MAX_PROPERTIES_ADDED"; + return fmt::format_to(out, "MAX_PROPERTIES_ADDED"); case json_incompatibility_type::max_properties_decreased: - return os << "MAX_PROPERTIES_DECREASED"; + return fmt::format_to(out, "MAX_PROPERTIES_DECREASED"); case json_incompatibility_type::min_properties_added: - return os << "MIN_PROPERTIES_ADDED"; + return fmt::format_to(out, "MIN_PROPERTIES_ADDED"); case json_incompatibility_type::min_properties_increased: - return os << "MIN_PROPERTIES_INCREASED"; + return fmt::format_to(out, "MIN_PROPERTIES_INCREASED"); case json_incompatibility_type::additional_properties_removed: - return os << "ADDITIONAL_PROPERTIES_REMOVED"; + return fmt::format_to(out, "ADDITIONAL_PROPERTIES_REMOVED"); case json_incompatibility_type::additional_properties_narrowed: - return os << "ADDITIONAL_PROPERTIES_NARROWED"; + return fmt::format_to(out, "ADDITIONAL_PROPERTIES_NARROWED"); case json_incompatibility_type::dependency_array_added: - return os << "DEPENDENCY_ARRAY_ADDED"; + return fmt::format_to(out, "DEPENDENCY_ARRAY_ADDED"); case json_incompatibility_type::dependency_array_extended: - return os << "DEPENDENCY_ARRAY_EXTENDED"; + return fmt::format_to(out, "DEPENDENCY_ARRAY_EXTENDED"); case json_incompatibility_type::dependency_array_changed: - return os << "DEPENDENCY_ARRAY_CHANGED"; + return fmt::format_to(out, "DEPENDENCY_ARRAY_CHANGED"); case json_incompatibility_type::dependency_schema_added: - return os << "DEPENDENCY_SCHEMA_ADDED"; + return fmt::format_to(out, "DEPENDENCY_SCHEMA_ADDED"); case json_incompatibility_type::property_added_to_open_content_model: - return os << "PROPERTY_ADDED_TO_OPEN_CONTENT_MODEL"; + return fmt::format_to(out, "PROPERTY_ADDED_TO_OPEN_CONTENT_MODEL"); case json_incompatibility_type:: required_property_added_to_unopen_content_model: - return os << "REQUIRED_PROPERTY_ADDED_TO_UNOPEN_CONTENT_MODEL"; + return fmt::format_to( + out, "REQUIRED_PROPERTY_ADDED_TO_UNOPEN_CONTENT_MODEL"); case json_incompatibility_type::property_removed_from_closed_content_model: - return os << "PROPERTY_REMOVED_FROM_CLOSED_CONTENT_MODEL"; + return fmt::format_to( + out, "PROPERTY_REMOVED_FROM_CLOSED_CONTENT_MODEL"); case json_incompatibility_type:: property_removed_not_covered_by_partially_open_content_model: - return os << "PROPERTY_REMOVED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_" - "MODEL"; + return fmt::format_to( + out, "PROPERTY_REMOVED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"); case json_incompatibility_type:: property_added_not_covered_by_partially_open_content_model: - return os - << "PROPERTY_ADDED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"; + return fmt::format_to( + out, "PROPERTY_ADDED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"); case json_incompatibility_type::reserved_property_removed: - return os << "RESERVED_PROPERTY_REMOVED"; + return fmt::format_to(out, "RESERVED_PROPERTY_REMOVED"); case json_incompatibility_type::reserved_property_conflicts_with_property: - return os << "RESERVED_PROPERTY_CONFLICTS_WITH_PROPERTY"; + return fmt::format_to(out, "RESERVED_PROPERTY_CONFLICTS_WITH_PROPERTY"); case json_incompatibility_type::max_items_added: - return os << "MAX_ITEMS_ADDED"; + return fmt::format_to(out, "MAX_ITEMS_ADDED"); case json_incompatibility_type::max_items_decreased: - return os << "MAX_ITEMS_DECREASED"; + return fmt::format_to(out, "MAX_ITEMS_DECREASED"); case json_incompatibility_type::min_items_added: - return os << "MIN_ITEMS_ADDED"; + return fmt::format_to(out, "MIN_ITEMS_ADDED"); case json_incompatibility_type::min_items_increased: - return os << "MIN_ITEMS_INCREASED"; + return fmt::format_to(out, "MIN_ITEMS_INCREASED"); case json_incompatibility_type::unique_items_added: - return os << "UNIQUE_ITEMS_ADDED"; + return fmt::format_to(out, "UNIQUE_ITEMS_ADDED"); case json_incompatibility_type::additional_items_removed: - return os << "ADDITIONAL_ITEMS_REMOVED"; + return fmt::format_to(out, "ADDITIONAL_ITEMS_REMOVED"); case json_incompatibility_type::additional_items_narrowed: - return os << "ADDITIONAL_ITEMS_NARROWED"; + return fmt::format_to(out, "ADDITIONAL_ITEMS_NARROWED"); case json_incompatibility_type::item_added_to_open_content_model: - return os << "ITEM_ADDED_TO_OPEN_CONTENT_MODEL"; + return fmt::format_to(out, "ITEM_ADDED_TO_OPEN_CONTENT_MODEL"); case json_incompatibility_type::item_removed_from_closed_content_model: - return os << "ITEM_REMOVED_FROM_CLOSED_CONTENT_MODEL"; + return fmt::format_to(out, "ITEM_REMOVED_FROM_CLOSED_CONTENT_MODEL"); case json_incompatibility_type:: item_removed_not_covered_by_partially_open_content_model: - return os << "ITEM_REMOVED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"; + return fmt::format_to( + out, "ITEM_REMOVED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"); case json_incompatibility_type:: item_added_not_covered_by_partially_open_content_model: - return os << "ITEM_ADDED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"; + return fmt::format_to( + out, "ITEM_ADDED_NOT_COVERED_BY_PARTIALLY_OPEN_CONTENT_MODEL"); case json_incompatibility_type::enum_array_narrowed: - return os << "ENUM_ARRAY_NARROWED"; + return fmt::format_to(out, "ENUM_ARRAY_NARROWED"); case json_incompatibility_type::enum_array_changed: - return os << "ENUM_ARRAY_CHANGED"; + return fmt::format_to(out, "ENUM_ARRAY_CHANGED"); case json_incompatibility_type::combined_type_changed: - return os << "COMBINED_TYPE_CHANGED"; + return fmt::format_to(out, "COMBINED_TYPE_CHANGED"); case json_incompatibility_type::product_type_extended: - return os << "PRODUCT_TYPE_EXTENDED"; + return fmt::format_to(out, "PRODUCT_TYPE_EXTENDED"); case json_incompatibility_type::sum_type_extended: - return os << "SUM_TYPE_EXTENDED"; + return fmt::format_to(out, "SUM_TYPE_EXTENDED"); case json_incompatibility_type::sum_type_narrowed: - return os << "SUM_TYPE_NARROWED"; + return fmt::format_to(out, "SUM_TYPE_NARROWED"); case json_incompatibility_type::combined_type_subschemas_changed: - return os << "COMBINED_TYPE_SUBSCHEMAS_CHANGED"; + return fmt::format_to(out, "COMBINED_TYPE_SUBSCHEMAS_CHANGED"); case json_incompatibility_type::not_type_extended: - return os << "NOT_TYPE_EXTENDED"; + return fmt::format_to(out, "NOT_TYPE_EXTENDED"); case json_incompatibility_type::unknown: - return os << "UNKNOWN"; + return fmt::format_to(out, "UNKNOWN"); } __builtin_unreachable(); } @@ -380,12 +323,6 @@ std::string_view description_for_type(json_incompatibility_type t) { __builtin_unreachable(); } -std::ostream& operator<<(std::ostream& os, const json_incompatibility& v) { - fmt::print( - os, R"({{errorType:"{}", description:"{}"}})", v._type, v.describe()); - return os; -} - ss::sstring json_incompatibility::describe() const { return fmt::format( fmt::runtime(description_for_type(_type)), diff --git a/src/v/pandaproxy/schema_registry/compatibility.h b/src/v/pandaproxy/schema_registry/compatibility.h index 90fa01c8a26ad..edf1203784e94 100644 --- a/src/v/pandaproxy/schema_registry/compatibility.h +++ b/src/v/pandaproxy/schema_registry/compatibility.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/vassert.h" #include "pandaproxy/schema_registry/types.h" @@ -38,6 +39,25 @@ enum class avro_incompatibility_type { unknown, }; +inline fmt::iterator format_to(avro_incompatibility_type t, fmt::iterator out) { + switch (t) { + case avro_incompatibility_type::name_mismatch: + return fmt::format_to(out, "NAME_MISMATCH"); + case avro_incompatibility_type::fixed_size_mismatch: + return fmt::format_to(out, "FIXED_SIZE_MISMATCH"); + case avro_incompatibility_type::missing_enum_symbols: + return fmt::format_to(out, "MISSING_ENUM_SYMBOLS"); + case avro_incompatibility_type::reader_field_missing_default_value: + return fmt::format_to(out, "READER_FIELD_MISSING_DEFAULT_VALUE"); + case avro_incompatibility_type::type_mismatch: + return fmt::format_to(out, "TYPE_MISMATCH"); + case avro_incompatibility_type::missing_union_branch: + return fmt::format_to(out, "MISSING_UNION_BRANCH"); + case avro_incompatibility_type::unknown: + return fmt::format_to(out, "UNKNOWN"); + } +} + /** * avro_incompatibility - A single incompatibility between Avro schemas. * @@ -64,13 +84,19 @@ class avro_incompatibility { ss::sstring describe() const; -private: - friend std::ostream& - operator<<(std::ostream& os, const avro_incompatibility& v); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{errorType:'{}', description:'{}', additionalInfo:'{}'}}", + _type, + describe(), + _additional_info); + } friend bool operator==( const avro_incompatibility&, const avro_incompatibility&) = default; +private: // Useful for unit testing template friend H AbslHashValue(H h, const avro_incompatibility& e) { @@ -95,6 +121,30 @@ enum class proto_incompatibility_type { unknown, }; +inline fmt::iterator +format_to(proto_incompatibility_type t, fmt::iterator out) { + switch (t) { + case proto_incompatibility_type::message_removed: + return fmt::format_to(out, "MESSAGE_REMOVED"); + case proto_incompatibility_type::field_kind_changed: + return fmt::format_to(out, "FIELD_KIND_CHANGED"); + case proto_incompatibility_type::field_scalar_kind_changed: + return fmt::format_to(out, "FIELD_SCALAR_KIND_CHANGED"); + case proto_incompatibility_type::field_named_type_changed: + return fmt::format_to(out, "FIELD_NAMED_TYPE_CHANGED"); + case proto_incompatibility_type::required_field_added: + return fmt::format_to(out, "REQUIRED_FIELD_ADDED"); + case proto_incompatibility_type::required_field_removed: + return fmt::format_to(out, "REQUIRED_FIELD_REMOVED"); + case proto_incompatibility_type::oneof_field_removed: + return fmt::format_to(out, "ONEOF_FIELD_REMOVED"); + case proto_incompatibility_type::multiple_fields_moved_to_oneof: + return fmt::format_to(out, "MULTIPLE_FIELDS_MOVED_TO_ONEOF"); + case proto_incompatibility_type::unknown: + return fmt::format_to(out, "UNKNOWN"); + } +} + /** * proto_incompatibility - A single incompatibility between Protobuf schemas. * @@ -116,13 +166,15 @@ class proto_incompatibility { ss::sstring describe() const; Type type() const { return _type; } -private: - friend std::ostream& - operator<<(std::ostream& os, const proto_incompatibility& v); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, R"({{errorType:"{}", description:"{}"}})", _type, describe()); + } friend bool operator==( const proto_incompatibility&, const proto_incompatibility&) = default; +private: // Helpful for unit testing template friend H AbslHashValue(H h, const proto_incompatibility& e) { @@ -193,6 +245,8 @@ enum class json_incompatibility_type { unknown, }; +fmt::iterator format_to(json_incompatibility_type t, fmt::iterator); + /** * json_incompatibility - A single incompatibility between JSON schemas. * @@ -214,8 +268,10 @@ class json_incompatibility { ss::sstring describe() const; Type type() const { return _type; } - friend std::ostream& - operator<<(std::ostream& os, const json_incompatibility& v); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, R"({{errorType:"{}", description:"{}"}})", _type, describe()); + } friend bool operator==( const json_incompatibility&, const json_incompatibility&) = default; diff --git a/src/v/pandaproxy/schema_registry/error.h b/src/v/pandaproxy/schema_registry/error.h index f7dd7fc936e5c..dfe00f126049f 100644 --- a/src/v/pandaproxy/schema_registry/error.h +++ b/src/v/pandaproxy/schema_registry/error.h @@ -11,6 +11,8 @@ #pragma once +#include + #include namespace pandaproxy::schema_registry { @@ -60,3 +62,18 @@ struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + pandaproxy::schema_registry::error_code e, + fmt::format_context& ctx) const { + return fmt::format_to( + ctx.out(), + "{}", + pandaproxy::schema_registry::make_error_code(e).message()); + } +}; diff --git a/src/v/pandaproxy/schema_registry/json.cc b/src/v/pandaproxy/schema_registry/json.cc index 5dabbfb54c0e8..148b83bf59518 100644 --- a/src/v/pandaproxy/schema_registry/json.cc +++ b/src/v/pandaproxy/schema_registry/json.cc @@ -46,6 +46,10 @@ #include #include #include + +template +struct fmt::formatter> + : fmt::ostream_formatter {}; #include #include @@ -241,15 +245,14 @@ bool operator==( return lhs.raw() == rhs.raw(); } -std::ostream& operator<<(std::ostream& os, const json_schema_definition& def) { - fmt::print( - os, +fmt::iterator json_schema_definition::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "type: {}, definition: {}, references: {}, metadata: {}", - to_string_view(def.type()), - def().to_json(), - def.refs(), - def.meta()); - return os; + to_string_view(type()), + (*this)().to_json(), + refs(), + meta()); } schema_definition::raw_string json_schema_definition::raw() const { diff --git a/src/v/pandaproxy/schema_registry/protobuf.cc b/src/v/pandaproxy/schema_registry/protobuf.cc index d124e3a006241..5f05025c91c75 100644 --- a/src/v/pandaproxy/schema_registry/protobuf.cc +++ b/src/v/pandaproxy/schema_registry/protobuf.cc @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -625,16 +624,14 @@ bool operator==( return lhs.raw() == rhs.raw(); } -std::ostream& -operator<<(std::ostream& os, const protobuf_schema_definition& def) { - fmt::print( - os, +fmt::iterator protobuf_schema_definition::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "type: {}, definition: {}, references: {}, metadata: {}", - to_string_view(def.type()), - def.raw(), - def.refs(), - def.meta()); - return os; + to_string_view(type()), + raw(), + refs(), + meta()); } ss::future make_protobuf_schema_definition( @@ -970,7 +967,9 @@ template<> struct fmt::formatter { using type = pandaproxy::schema_registry::io_error_collector; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template auto format(const type::err& e, FormatContext& ctx) const { @@ -988,7 +987,9 @@ template<> struct fmt::formatter { using type = pandaproxy::schema_registry::dp_error_collector; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template auto format(const type::err& e, FormatContext& ctx) const { @@ -1000,7 +1001,7 @@ struct fmt::formatter { e.filename, e.element_name, e.descriptor->DebugString(), - e.location, + static_cast(e.location), e.message); } }; diff --git a/src/v/pandaproxy/schema_registry/requests/test/post_subject_versions.cc b/src/v/pandaproxy/schema_registry/requests/test/post_subject_versions.cc index 5147097dca364..7a46765e1800b 100644 --- a/src/v/pandaproxy/schema_registry/requests/test/post_subject_versions.cc +++ b/src/v/pandaproxy/schema_registry/requests/test/post_subject_versions.cc @@ -15,8 +15,6 @@ #include #include -#include - #include namespace ppj = pandaproxy::json; diff --git a/src/v/pandaproxy/schema_registry/schema_id_validation.h b/src/v/pandaproxy/schema_registry/schema_id_validation.h index 47347c9209f33..7ddfa71d4bd71 100644 --- a/src/v/pandaproxy/schema_registry/schema_id_validation.h +++ b/src/v/pandaproxy/schema_registry/schema_id_validation.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "strings/string_switch.h" #include @@ -38,9 +39,8 @@ constexpr std::string_view to_string_view(schema_id_validation_mode m) { return "compat"; } } - -inline std::ostream& operator<<(std::ostream& o, schema_id_validation_mode m) { - return o << to_string_view(m); +inline fmt::iterator format_to(schema_id_validation_mode m, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(m)); } inline std::istream& operator>>(std::istream& i, schema_id_validation_mode& m) { diff --git a/src/v/pandaproxy/schema_registry/storage.h b/src/v/pandaproxy/schema_registry/storage.h index 17962d0f4c16d..a7b26475f5862 100644 --- a/src/v/pandaproxy/schema_registry/storage.h +++ b/src/v/pandaproxy/schema_registry/storage.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/vlog.h" #include "json/iobuf_writer.h" #include "json/json.h" @@ -64,6 +65,9 @@ constexpr std::string_view to_string_view(topic_key_type kt) { } return "{invalid}"; }; +inline fmt::iterator format_to(topic_key_type kt, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(kt)); +} template<> constexpr std::optional from_string_view(std::string_view sv) { @@ -148,28 +152,27 @@ struct schema_key { friend bool operator==(const schema_key&, const schema_key&) = default; - friend std::ostream& operator<<(std::ostream& os, const schema_key& v) { - if (v.seq.has_value() && v.node.has_value()) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + if (seq.has_value() && node.has_value()) { + return fmt::format_to( + it, "seq: {}, node: {}, keytype: {}, subject: {}, version: {}, " "magic: {}", - *v.seq, - *v.node, - to_string_view(v.keytype), - v.sub, - v.version, - v.magic); + *seq, + *node, + to_string_view(keytype), + sub, + version, + magic); } else { - fmt::print( - os, + return fmt::format_to( + it, "unsequenced keytype: {}, subject: {}, version: {}, magic: {}", - to_string_view(v.keytype), - v.sub, - v.version, - v.magic); + to_string_view(keytype), + sub, + version, + magic); } - return os; } }; @@ -320,15 +323,14 @@ struct schema_value { friend bool operator==(const schema_value&, const schema_value&) = default; - friend std::ostream& operator<<(std::ostream& os, const schema_value& v) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{}, version: {}, id: {}, deleted: {}", - v.schema, - v.version, - v.id, - v.deleted); - return os; + schema, + version, + id, + deleted); } }; @@ -698,25 +700,24 @@ struct config_key { friend bool operator==(const config_key&, const config_key&) = default; - friend std::ostream& operator<<(std::ostream& os, const config_key& v) { - if (v.seq.has_value() && v.node.has_value()) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + if (seq.has_value() && node.has_value()) { + return fmt::format_to( + it, "seq: {} node: {} keytype: {}, subject: {}, magic: {}", - *v.seq, - *v.node, - to_string_view(v.keytype), - v.sub.value_or(invalid_subject), - v.magic); + *seq, + *node, + to_string_view(keytype), + sub.value_or(invalid_subject), + magic); } else { - fmt::print( - os, + return fmt::format_to( + it, "unsequenced keytype: {}, subject: {}, magic: {}", - to_string_view(v.keytype), - v.sub.value_or(invalid_subject), - v.magic); + to_string_view(keytype), + sub.value_or(invalid_subject), + magic); } - return os; } }; @@ -848,13 +849,11 @@ struct config_value { friend bool operator==(const config_value&, const config_value&) = default; - friend std::ostream& operator<<(std::ostream& os, const config_value& v) { - if (v.sub.has_value()) { - fmt::print(os, "subject: {}, ", v.sub.value()); + fmt::iterator format_to(fmt::iterator it) const { + if (sub.has_value()) { + it = fmt::format_to(it, "subject: {}, ", sub.value()); } - fmt::print(os, "compatibility: {}", to_string_view(v.compat)); - - return os; + return fmt::format_to(it, "compatibility: {}", compat); } }; @@ -934,25 +933,24 @@ struct mode_key { friend bool operator==(const mode_key&, const mode_key&) = default; - friend std::ostream& operator<<(std::ostream& os, const mode_key& v) { - if (v.seq.has_value() && v.node.has_value()) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + if (seq.has_value() && node.has_value()) { + return fmt::format_to( + it, "seq: {} node: {} keytype: {}, subject: {}, magic: {}", - *v.seq, - *v.node, - to_string_view(v.keytype), - v.sub.value_or(invalid_subject), - v.magic); + *seq, + *node, + to_string_view(keytype), + sub.value_or(invalid_subject), + magic); } else { - fmt::print( - os, + return fmt::format_to( + it, "unsequenced keytype: {}, subject: {}, magic: {}", - to_string_view(v.keytype), - v.sub.value_or(invalid_subject), - v.magic); + to_string_view(keytype), + sub.value_or(invalid_subject), + magic); } - return os; } }; @@ -1084,13 +1082,11 @@ struct mode_value { friend bool operator==(const mode_value&, const mode_value&) = default; - friend std::ostream& operator<<(std::ostream& os, const mode_value& v) { - if (v.sub.has_value()) { - fmt::print(os, "subject: {}, ", v.sub.value()); + fmt::iterator format_to(fmt::iterator it) const { + if (sub.has_value()) { + it = fmt::format_to(it, "subject: {}, ", sub.value()); } - fmt::print(os, "mode: {}", to_string_view(v.mode)); - - return os; + return fmt::format_to(it, "mode: {}", mode); } }; @@ -1170,26 +1166,24 @@ struct delete_subject_key { friend bool operator==(const delete_subject_key&, const delete_subject_key&) = default; - friend std::ostream& - operator<<(std::ostream& os, const delete_subject_key& v) { - if (v.seq.has_value() && v.node.has_value()) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + if (seq.has_value() && node.has_value()) { + return fmt::format_to( + it, "seq: {}, node: {}, keytype: {}, subject: {}, magic: {}", - *v.seq, - *v.node, - to_string_view(v.keytype), - v.sub, - v.magic); + *seq, + *node, + to_string_view(keytype), + sub, + magic); } else { - fmt::print( - os, + return fmt::format_to( + it, "unsequenced keytype: {}, subject: {}, magic: {}", - to_string_view(v.keytype), - v.sub, - v.magic); + to_string_view(keytype), + sub, + magic); } - return os; } }; @@ -1326,10 +1320,8 @@ struct delete_subject_value { friend bool operator==( const delete_subject_value&, const delete_subject_value&) = default; - friend std::ostream& - operator<<(std::ostream& os, const delete_subject_value& v) { - fmt::print(os, "subject: {}", v.sub); - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "subject: {}", sub); } }; @@ -1431,28 +1423,27 @@ struct context_key { friend bool operator==(const context_key&, const context_key&) = default; - friend std::ostream& operator<<(std::ostream& os, const context_key& v) { - if (v.seq.has_value() && v.node.has_value()) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + if (seq.has_value() && node.has_value()) { + return fmt::format_to( + it, "seq: {}, node: {}, keytype: {}, tenant: {}, context: {}, magic: " "{}", - *v.seq, - *v.node, - to_string_view(v.keytype), - v.tenant, - v.ctx, - v.magic); + *seq, + *node, + to_string_view(keytype), + tenant, + ctx, + magic); } else { - fmt::print( - os, + return fmt::format_to( + it, "unsequenced keytype: {}, tenant: {}, context: {}, magic: {}", - to_string_view(v.keytype), - v.tenant, - v.ctx, - v.magic); + to_string_view(keytype), + tenant, + ctx, + magic); } - return os; } }; @@ -1463,9 +1454,8 @@ struct context_value { friend bool operator==(const context_value&, const context_value&) = default; - friend std::ostream& operator<<(std::ostream& os, const context_value& v) { - fmt::print(os, "tenant: {}, context: {}", v.tenant, v.ctx); - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "tenant: {}, context: {}", tenant, ctx); } }; diff --git a/src/v/pandaproxy/schema_registry/subject_name_strategy.h b/src/v/pandaproxy/schema_registry/subject_name_strategy.h index f87b509274ac4..04f35666c8407 100644 --- a/src/v/pandaproxy/schema_registry/subject_name_strategy.h +++ b/src/v/pandaproxy/schema_registry/subject_name_strategy.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "strings/string_switch.h" @@ -24,7 +25,7 @@ enum class subject_name_strategy : uint8_t { topic_record_name, }; -inline constexpr std::string_view to_string_view(subject_name_strategy e) { +constexpr std::string_view to_string_view(subject_name_strategy e) { switch (e) { case subject_name_strategy::topic_name: return "TopicNameStrategy"; @@ -35,6 +36,9 @@ inline constexpr std::string_view to_string_view(subject_name_strategy e) { } return "{invalid}"; } +inline fmt::iterator format_to(subject_name_strategy e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); +} inline constexpr std::string_view to_string_view_compat(subject_name_strategy e) { @@ -49,11 +53,6 @@ to_string_view_compat(subject_name_strategy e) { return "{invalid}"; } -inline constexpr std::ostream& -operator<<(std::ostream& os, subject_name_strategy e) { - return os << to_string_view(e); -} - inline std::istream& operator>>(std::istream& i, subject_name_strategy& e) { ss::sstring s; i >> s; diff --git a/src/v/pandaproxy/schema_registry/test/BUILD b/src/v/pandaproxy/schema_registry/test/BUILD index 77aee20a089b2..1840a5e1410e5 100644 --- a/src/v/pandaproxy/schema_registry/test/BUILD +++ b/src/v/pandaproxy/schema_registry/test/BUILD @@ -451,6 +451,7 @@ redpanda_cc_gtest( ":compatibility_common", ":compatibility_protobuf", ":protobuf_utils", + "//src/v/base", "//src/v/bytes:iobuf_parser", "//src/v/pandaproxy/schema_registry:core", "//src/v/test_utils:gtest", diff --git a/src/v/pandaproxy/schema_registry/test/protobuf_rendering.cc b/src/v/pandaproxy/schema_registry/test/protobuf_rendering.cc index 58e50d8684618..8d3e71aebde54 100644 --- a/src/v/pandaproxy/schema_registry/test/protobuf_rendering.cc +++ b/src/v/pandaproxy/schema_registry/test/protobuf_rendering.cc @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "bytes/iobuf_parser.h" #include "pandaproxy/schema_registry/protobuf.h" #include "pandaproxy/schema_registry/sharded_store.h" diff --git a/src/v/pandaproxy/schema_registry/test/test_json_schema.cc b/src/v/pandaproxy/schema_registry/test/test_json_schema.cc index 961f91363f2bb..2a469e0da1d6d 100644 --- a/src/v/pandaproxy/schema_registry/test/test_json_schema.cc +++ b/src/v/pandaproxy/schema_registry/test/test_json_schema.cc @@ -2477,7 +2477,7 @@ SEASTAR_THREAD_TEST_CASE(test_refs_fixing) { pps::make_canonical_json_schema( f.store(), {pps::context_subject::unqualified("test"), - {fmt::format("{}", jsoncons::print(input_schema)), + {fmt::format("{}", fmt_streamed(jsoncons::print(input_schema))), pps::schema_type::json}}) .get()) .get(); @@ -2527,9 +2527,9 @@ SEASTAR_THREAD_TEST_CASE(test_refs_fixing) { fmt::format( "input_schema:\n{}\n\nexpected_schema:\n{}\n\nprocessed_schema:\n{}" "\n\n", - jsoncons::pretty_print(input_schema), - jsoncons::pretty_print(expected_schema), - jsoncons::pretty_print(processed_schema))) { + fmt_streamed(jsoncons::pretty_print(input_schema)), + fmt_streamed(jsoncons::pretty_print(expected_schema)), + fmt_streamed(jsoncons::pretty_print(processed_schema)))) { // check that the processed schema is the same as the expected schema, // output the difference if not auto jpatch = jsoncons::jsonpatch::from_diff( @@ -2538,6 +2538,6 @@ SEASTAR_THREAD_TEST_CASE(test_refs_fixing) { expected_schema == processed_schema, fmt::format( "differences expected_schema-processed_schema:\n{}\n", - jsoncons::pretty_print(jpatch))); + fmt_streamed(jsoncons::pretty_print(jpatch)))); } } diff --git a/src/v/pandaproxy/schema_registry/types.cc b/src/v/pandaproxy/schema_registry/types.cc index d429033ddf31c..b0476eba0aa2f 100644 --- a/src/v/pandaproxy/schema_registry/types.cc +++ b/src/v/pandaproxy/schema_registry/types.cc @@ -21,47 +21,30 @@ namespace pandaproxy::schema_registry { -std::ostream& operator<<(std::ostream& os, const schema_type& v) { - return os << to_string_view(v); -} - -std::ostream& operator<<(std::ostream& os, const output_format& v) { - return os << to_string_view(v); -} - -std::ostream& operator<<(std::ostream& os, const reference_format& v) { - return os << to_string_view(v); -} - -std::ostream& operator<<(std::ostream& os, const seq_marker& v) { - if (v.seq.has_value() && v.node.has_value()) { - fmt::print( - os, +fmt::iterator seq_marker::format_to(fmt::iterator it) const { + if (seq.has_value() && node.has_value()) { + return fmt::format_to( + it, "seq={} node={} version={} key_type={}", - *v.seq, - *v.node, - v.version, - to_string_view(v.key_type)); + *seq, + *node, + version, + key_type); } else { - fmt::print( - os, - "unsequenced version={} key_type={}", - v.version, - to_string_view(v.key_type)); + return fmt::format_to( + it, "unsequenced version={} key_type={}", version, key_type); } - return os; } -std::ostream& operator<<(std::ostream& os, const schema_definition& def) { - fmt::print( - os, +fmt::iterator schema_definition::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "type: {}, definition: {}, references: {}, metadata: {}", - to_string_view(def.type()), + to_string_view(type()), // TODO BP: Prevent this linearization - to_string(def.shared_raw()), - def.refs(), - def.meta()); - return os; + to_string(shared_raw()), + refs(), + meta()); } std::ostream& operator<<(std::ostream& os, const schema_reference& ref) { @@ -74,14 +57,8 @@ bool operator<(const schema_reference& lhs, const schema_reference& rhs) { < std::tie(rhs.name, rhs.sub, rhs.version); } -std::ostream& operator<<(std::ostream& os, const subject_schema& schema) { - fmt::print(os, "subject: {}, {}", schema.sub(), schema.def()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const compatibility_result& res) { - fmt::print(os, "is_compat: {}, messages: {}", res.is_compat, res.messages); - return os; +fmt::iterator subject_schema::format_to(fmt::iterator it) const { + return fmt::format_to(it, "subject: {}, {}", sub(), def()); } fmt::iterator schema_metadata::format_to(fmt::iterator it) const { diff --git a/src/v/pandaproxy/schema_registry/types.h b/src/v/pandaproxy/schema_registry/types.h index 4445e796b8e6a..0c6fcc4a95251 100644 --- a/src/v/pandaproxy/schema_registry/types.h +++ b/src/v/pandaproxy/schema_registry/types.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include "config/startup_config.h" @@ -59,6 +60,9 @@ constexpr std::string_view to_string_view(mode e) { } return "{invalid}"; } +inline fmt::iterator format_to(mode e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); +} template<> constexpr std::optional from_string_view(std::string_view sv) { return string_switch>(sv) @@ -81,6 +85,9 @@ constexpr std::string_view to_string_view(schema_type e) { } return "{invalid}"; } +inline fmt::iterator format_to(schema_type e, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(e)); +} template<> constexpr std::optional from_string_view(std::string_view sv) { @@ -91,8 +98,6 @@ from_string_view(std::string_view sv) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& os, const schema_type& v); - enum class output_format { none = 0, resolved, ignore_extensions, serialized }; constexpr std::string_view to_string_view(output_format of) { @@ -108,6 +113,9 @@ constexpr std::string_view to_string_view(output_format of) { } return ""; } +inline fmt::iterator format_to(output_format of, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(of)); +} template<> inline std::optional @@ -123,8 +131,6 @@ from_string_view(std::string_view sv) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& os, const output_format& of); - enum class reference_format { none = 0, qualified }; constexpr std::string_view to_string_view(reference_format rf) { @@ -136,6 +142,9 @@ constexpr std::string_view to_string_view(reference_format rf) { } return ""; } +inline fmt::iterator format_to(reference_format rf, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(rf)); +} template<> inline std::optional @@ -148,8 +157,6 @@ from_string_view(std::string_view sv) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& os, const reference_format& rf); - ///\brief Type representing a global resource for ACLs. using registry_resource = named_type; @@ -355,7 +362,7 @@ class schema_definition { friend bool operator==( const schema_definition& lhs, const schema_definition& rhs) = default; - friend std::ostream& operator<<(std::ostream& os, const schema_definition&); + fmt::iterator format_to(fmt::iterator it) const; schema_type type() const { return _type; } @@ -406,8 +413,7 @@ class avro_schema_definition { friend bool operator==( const avro_schema_definition& lhs, const avro_schema_definition& rhs); - friend std::ostream& - operator<<(std::ostream& os, const avro_schema_definition& rhs); + fmt::iterator format_to(fmt::iterator it) const; constexpr schema_type type() const { return schema_type::avro; } @@ -447,8 +453,7 @@ class protobuf_schema_definition { const protobuf_schema_definition& lhs, const protobuf_schema_definition& rhs); - friend std::ostream& - operator<<(std::ostream& os, const protobuf_schema_definition& rhs); + fmt::iterator format_to(fmt::iterator it) const; constexpr schema_type type() const { return schema_type::protobuf; } @@ -482,8 +487,7 @@ class json_schema_definition { friend bool operator==( const json_schema_definition& lhs, const json_schema_definition& rhs); - friend std::ostream& - operator<<(std::ostream& os, const json_schema_definition& rhs); + fmt::iterator format_to(fmt::iterator it) const; constexpr schema_type type() const { return schema_type::json; } @@ -550,9 +554,9 @@ class valid_schema { }); } - friend std::ostream& operator<<(std::ostream& os, const valid_schema& def) { - def.visit([&os](const auto& def) { os << def; }); - return os; + fmt::iterator format_to(fmt::iterator it) const { + visit([&it](const auto& def) { it = fmt::format_to(it, "{}", def); }); + return it; } private: @@ -613,6 +617,9 @@ constexpr std::string_view to_string_view(seq_marker_key_type v) { } return "invalid"; } +inline fmt::iterator format_to(seq_marker_key_type v, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(v)); +} // Record the sequence+node where updates were made to a subject, // in order to later generate tombstone keys when doing a permanent @@ -628,7 +635,7 @@ struct seq_marker { // them optional provides compatibility with non-rp schema registries. If // either is not present, we can assume a collision has not occurred. friend bool operator==(const seq_marker&, const seq_marker&) = default; - friend std::ostream& operator<<(std::ostream& os, const seq_marker& v); + fmt::iterator format_to(fmt::iterator it) const; }; ///\brief A schema with its subject @@ -643,8 +650,7 @@ class subject_schema { friend bool operator==(const subject_schema& lhs, const subject_schema& rhs) = default; - friend std::ostream& - operator<<(std::ostream& os, const subject_schema& schema); + fmt::iterator format_to(fmt::iterator it) const; const context_subject& sub() const { return _sub; } schema_type type() const { return _def.type(); } @@ -725,6 +731,9 @@ constexpr std::string_view to_string_view(compatibility_level v) { } return "{invalid}"; } +inline fmt::iterator format_to(compatibility_level v, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(v)); +} template<> constexpr std::optional from_string_view(std::string_view sv) { @@ -754,7 +763,11 @@ from_string_view(std::string_view sv) { struct compatibility_result { friend bool operator==( const compatibility_result&, const compatibility_result&) = default; - friend std::ostream& operator<<(std::ostream&, const compatibility_result&); + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "is_compat: {}, messages: {}", is_compat, messages); + } bool is_compat; chunked_vector messages; diff --git a/src/v/raft/configuration_bootstrap_state.h b/src/v/raft/configuration_bootstrap_state.h index fc1056316df4f..3678840ce40c3 100644 --- a/src/v/raft/configuration_bootstrap_state.h +++ b/src/v/raft/configuration_bootstrap_state.h @@ -11,11 +11,10 @@ #pragma once +#include "base/format_to.h" #include "model/record.h" #include "raft/group_configuration.h" -#include - namespace raft { class configuration_bootstrap_state { @@ -45,24 +44,22 @@ class configuration_bootstrap_state { uint32_t data_batches_seen() const { return _data_batches_seen; } uint32_t config_batches_seen() const { return _configurations.size(); } - friend std::ostream& - operator<<(std::ostream& o, const configuration_bootstrap_state& s) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "data_seen {} config_seen {} eol {} commit {} term {} prev_idx {} " "prev_term {} config_tracker {} commit_base_tracker {} " "configurations {}", - s._data_batches_seen, - s._configurations.size(), - s._end_of_log, - s._commit_index, - s._term, - s._prev_log_index, - s._prev_log_term, - s._log_config_offset_tracker, - s._commit_index_base_batch_offset, - s._configurations); - return o; + _data_batches_seen, + _configurations.size(), + _end_of_log, + _commit_index, + _term, + _prev_log_index, + _prev_log_term, + _log_config_offset_tracker, + _commit_index_base_batch_offset, + _configurations); } private: diff --git a/src/v/raft/configuration_manager.cc b/src/v/raft/configuration_manager.cc index 5a8d62f029d7e..64df2ef9948bb 100644 --- a/src/v/raft/configuration_manager.cc +++ b/src/v/raft/configuration_manager.cc @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -522,32 +521,28 @@ ss::future<> configuration_manager::adjust_configuration_idx( }); } -std::ostream& operator<<(std::ostream& o, const configuration_manager& m) { - fmt::print(o, "{{configurations: ["); - - static auto print_cfg = - []( - std::ostream& o, - const configuration_manager::underlying_t::value_type& p) { - fmt::print( - o, - "{{offset: {}, index: {}, cfg: {}}}", - p.first, - p.second.idx, - p.second.cfg); - }; - - if (!m._configurations.empty()) { - auto it = m._configurations.begin(); - print_cfg(o, *it); - ++it; - for (; it != m._configurations.end(); ++it) { - fmt::print(o, ","); - print_cfg(o, *it); +fmt::iterator configuration_manager::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{configurations: ["); + + if (!_configurations.empty()) { + auto cfg_it = _configurations.begin(); + it = fmt::format_to( + it, + "{{offset: {}, index: {}, cfg: {}}}", + cfg_it->first, + cfg_it->second.idx, + cfg_it->second.cfg); + ++cfg_it; + for (; cfg_it != _configurations.end(); ++cfg_it) { + it = fmt::format_to( + it, + ",{{offset: {}, index: {}, cfg: {}}}", + cfg_it->first, + cfg_it->second.idx, + cfg_it->second.cfg); } } - fmt::print(o, "]}}"); - return o; + return fmt::format_to(it, "]}}"); } } // namespace raft diff --git a/src/v/raft/configuration_manager.h b/src/v/raft/configuration_manager.h index 1e9df3628466a..206e0509ae0e8 100644 --- a/src/v/raft/configuration_manager.h +++ b/src/v/raft/configuration_manager.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "base/units.h" #include "group_configuration.h" #include "model/fundamental.h" @@ -183,8 +184,7 @@ class configuration_manager { return _configuration_force_override != nullptr; } - friend std::ostream& - operator<<(std::ostream&, const configuration_manager&); + fmt::iterator format_to(fmt::iterator it) const; private: void reset_override(model::revision_id); diff --git a/src/v/raft/consensus.cc b/src/v/raft/consensus.cc index c65d3b45ddeb8..297c4460ac78c 100644 --- a/src/v/raft/consensus.cc +++ b/src/v/raft/consensus.cc @@ -53,8 +53,6 @@ #include #include -#include - #include #include #include @@ -3322,16 +3320,15 @@ void consensus::trigger_leadership_notification() { _leadership_changed.broadcast(); } -std::ostream& operator<<(std::ostream& o, const consensus& c) { - fmt::print( - o, +fmt::iterator consensus::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{log:{}, group_id:{}, term: {}, commit_index: {}, voted_for:{}}}", - c._log, - c._group, - c._term, - c._commit_index, - c._voted_for); - return o; + _log, + _group, + _term, + _commit_index, + _voted_for); } const group_configuration& consensus::config() const { diff --git a/src/v/raft/consensus.h b/src/v/raft/consensus.h index ed9a51ff3f016..385143c8a6d40 100644 --- a/src/v/raft/consensus.h +++ b/src/v/raft/consensus.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/likely.h" #include "base/seastarx.h" #include "config/property.h" @@ -992,7 +993,8 @@ class consensus { // simulate storage issues bool _inject_error_in_append_entries = false; - friend std::ostream& operator<<(std::ostream&, const consensus&); +public: + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace raft diff --git a/src/v/raft/consensus_utils.h b/src/v/raft/consensus_utils.h index 7e66a0209717f..c7eb9f6bc8db0 100644 --- a/src/v/raft/consensus_utils.h +++ b/src/v/raft/consensus_utils.h @@ -99,7 +99,9 @@ class term_assigning_reader : public model::record_batch_reader::impl { }); } - void print(std::ostream& os) final { os << "{term assigning reader}"; } + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to(it, "{{term assigning reader}}"); + } private: std::unique_ptr _source; diff --git a/src/v/raft/errc.h b/src/v/raft/errc.h index eeeb4e700fed4..c887a96a0894e 100644 --- a/src/v/raft/errc.h +++ b/src/v/raft/errc.h @@ -11,6 +11,9 @@ #pragma once +#include "base/format_to.h" + +#include #include namespace raft { @@ -41,6 +44,64 @@ enum class errc : int16_t { replicate_first_stage_exception, invalid_input_records, }; + +inline fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::success: + return fmt::format_to(out, "raft::errc::success"); + case errc::disconnected_endpoint: + return fmt::format_to(out, "raft::errc::disconnected_endpoint"); + case errc::exponential_backoff: + return fmt::format_to(out, "raft::errc::exponential_backoff"); + case errc::non_majority_replication: + return fmt::format_to(out, "raft::errc::non_majority_replication"); + case errc::not_leader: + return fmt::format_to(out, "raft::errc::not_leader"); + case errc::vote_dispatch_error: + return fmt::format_to(out, "raft::errc::vote_dispatch_error"); + case errc::append_entries_dispatch_error: + return fmt::format_to(out, "raft::errc::append_entries_dispatch_error"); + case errc::replicated_entry_truncated: + return fmt::format_to(out, "raft::errc::replicated_entry_truncated"); + case errc::leader_flush_failed: + return fmt::format_to(out, "raft::errc::leader_flush_failed"); + case errc::leader_append_failed: + return fmt::format_to(out, "raft::errc::leader_append_failed"); + case errc::timeout: + return fmt::format_to(out, "raft::errc::timeout"); + case errc::configuration_change_in_progress: + return fmt::format_to( + out, "raft::errc::configuration_change_in_progress"); + case errc::node_does_not_exists: + return fmt::format_to(out, "raft::errc::node_does_not_exists"); + case errc::leadership_transfer_in_progress: + return fmt::format_to( + out, "raft::errc::leadership_transfer_in_progress"); + case errc::transfer_to_current_leader: + return fmt::format_to(out, "raft::errc::transfer_to_current_leader"); + case errc::node_already_exists: + return fmt::format_to(out, "raft::errc::node_already_exists"); + case errc::invalid_configuration_update: + return fmt::format_to(out, "raft::errc::invalid_configuration_update"); + case errc::not_voter: + return fmt::format_to(out, "raft::errc::not_voter"); + case errc::invalid_target_node: + return fmt::format_to(out, "raft::errc::invalid_target_node"); + case errc::shutting_down: + return fmt::format_to(out, "raft::errc::shutting_down"); + case errc::replicate_batcher_cache_error: + return fmt::format_to(out, "raft::errc::replicate_batcher_cache_error"); + case errc::group_not_exists: + return fmt::format_to(out, "raft::errc::group_not_exists"); + case errc::replicate_first_stage_exception: + return fmt::format_to( + out, "raft::errc::replicate_first_stage_exception"); + case errc::invalid_input_records: + return fmt::format_to(out, "raft::errc::invalid_input_records"); + } + return fmt::format_to(out, "raft::errc::unknown"); +} + struct errc_category final : public std::error_category { const char* name() const noexcept final { return "raft::errc"; } diff --git a/src/v/raft/follower_states.cc b/src/v/raft/follower_states.cc index ac1e15864ba67..8cd0bf3bb356a 100644 --- a/src/v/raft/follower_states.cc +++ b/src/v/raft/follower_states.cc @@ -47,12 +47,12 @@ void follower_states::update_with_configuration( } } -std::ostream& operator<<(std::ostream& o, const follower_states& s) { - o << "{followers:" << s._followers.size() << ", ["; - for (auto& f : s) { - o << f.second; +fmt::iterator follower_states::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{followers:{}, [", _followers.size()); + for (const auto& f : *this) { + it = fmt::format_to(it, "{}", f.second); } - return o << "]}"; + return fmt::format_to(it, "]}}"); } void follower_index_metadata::reset() { @@ -73,28 +73,27 @@ void follower_index_metadata::reset() { coordinated_compaction_offsets_sender = std::make_unique(); } -std::ostream& operator<<(std::ostream& o, const follower_index_metadata& i) { - fmt::print( - o, +fmt::iterator follower_index_metadata::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{node_id: {}, last_flushed_log_index: {}, last_dirty_log_index: {}, " "match_index: {}, next_index: {}, expected_log_end_offset: {}, " "heartbeats_failed: {}, last_sent_seq: {}, last_received_seq: {}, " "last_successful_received_seq: {}, is_learner: {}, is_recovering: {}, " "max_cleanly_compacted_offset: {}}}", - i.node_id, - i.last_flushed_log_index, - i.last_dirty_log_index, - i.match_index, - i.next_index, - i.expected_log_end_offset, - i.heartbeats_failed, - i.last_sent_seq, - i.last_received_seq, - i.last_successful_received_seq, - i.is_learner, - i.is_recovering, - i.max_cleanly_compacted_offset); - return o; + node_id, + last_flushed_log_index, + last_dirty_log_index, + match_index, + next_index, + expected_log_end_offset, + heartbeats_failed, + last_sent_seq, + last_received_seq, + last_successful_received_seq, + is_learner, + is_recovering, + max_cleanly_compacted_offset); } } // namespace raft diff --git a/src/v/raft/follower_states.h b/src/v/raft/follower_states.h index 5a6de36bc2b40..512cbbee6fed7 100644 --- a/src/v/raft/follower_states.h +++ b/src/v/raft/follower_states.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "base/vassert.h" #include "raft/types.h" #include "ssx/single_fiber_executor.h" @@ -150,8 +151,7 @@ struct follower_index_metadata { // state for sending latest MTRO and MXRO to the follower void_executor_ptr coordinated_compaction_offsets_sender; - friend std::ostream& - operator<<(std::ostream& o, const follower_index_metadata& i); + fmt::iterator format_to(fmt::iterator it) const; }; class follower_states { public: @@ -213,10 +213,33 @@ class follower_states { } } + fmt::iterator format_to(fmt::iterator it) const; + private: - friend std::ostream& operator<<(std::ostream&, const follower_states&); vnode _self; container_t _followers; }; } // namespace raft + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + const raft::follower_index_metadata& v, fmt::format_context& ctx) const { + return v.format_to(ctx.out()); + } +}; + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto + format(const raft::follower_states& v, fmt::format_context& ctx) const { + return v.format_to(ctx.out()); + } +}; diff --git a/src/v/raft/fundamental.h b/src/v/raft/fundamental.h index fb3c5adc6be67..fab34f1046c9c 100644 --- a/src/v/raft/fundamental.h +++ b/src/v/raft/fundamental.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "model/fundamental.h" #include "serde/envelope.h" @@ -35,6 +36,20 @@ enum class reply_result : uint8_t { follower_busy }; +inline fmt::iterator format_to(reply_result r, fmt::iterator out) { + switch (r) { + case reply_result::success: + return fmt::format_to(out, "success"); + case reply_result::failure: + return fmt::format_to(out, "failure"); + case reply_result::group_unavailable: + return fmt::format_to(out, "group_unavailable"); + case reply_result::follower_busy: + return fmt::format_to(out, "follower_busy"); + } + __builtin_unreachable(); +} + /** * Class representing single incarnation of a node being a member of Raft group. * This class allows Raft to recognize members with the same id coming from @@ -52,7 +67,10 @@ class vnode bool operator==(const vnode& other) const = default; bool operator!=(const vnode& other) const = default; - friend std::ostream& operator<<(std::ostream& o, const vnode& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{id: {}, revision: {}}}", _node_id, _revision); + } template friend H AbslHashValue(H h, const vnode& node) { diff --git a/src/v/raft/group_configuration.cc b/src/v/raft/group_configuration.cc index 5166b90bb683c..c095c492afc63 100644 --- a/src/v/raft/group_configuration.cc +++ b/src/v/raft/group_configuration.cc @@ -1460,52 +1460,21 @@ void group_configuration::maybe_set_initial_revision( update_all_replicas(); } -std::ostream& operator<<(std::ostream& o, const group_configuration& c) { - fmt::print( - o, - "{{current: {}, old:{}, revision: {}, update: {}, version: {}}}", - c._current, - c._old, - c._revision, - c._configuration_update, - c._version); - if (c._version < group_configuration::v_5) { - fmt::print(o, ", brokers: {}}}", c._brokers); +fmt::iterator group_configuration::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, + "{{current: {}, old:{}, revision: {}, update: {}, version: {}", + _current, + _old, + _revision, + _configuration_update, + _version); + if (_version < group_configuration::v_5) { + it = fmt::format_to(it, ", brokers: {}}}", _brokers); } else { - fmt::print(o, "}}"); + it = fmt::format_to(it, "}}"); } - return o; -} - -std::ostream& operator<<(std::ostream& o, const group_nodes& n) { - fmt::print(o, "{{voters: {}, learners: {}}}", n.voters, n.learners); - return o; -} - -std::ostream& operator<<(std::ostream& o, const offset_configuration& c) { - fmt::print(o, "{{offset: {}, group_configuration: {}}}", c.offset, c.cfg); - return o; -} - -std::ostream& operator<<(std::ostream& o, configuration_state t) { - switch (t) { - case configuration_state::simple: - return o << "simple"; - case configuration_state::joint: - return o << "joint"; - case configuration_state::transitional: - return o << "transitional"; - } - __builtin_unreachable(); -} -std::ostream& operator<<(std::ostream& o, const configuration_update& u) { - fmt::print( - o, - "{{to_add: {}, to_remove: {}, learner_start_offset: {}}}", - u.replicas_to_add, - u.replicas_to_remove, - u.learner_start_offset); - return o; + return it; } group_configuration group_configuration::serde_direct_read( iobuf_parser& p, const serde::header& h) { diff --git a/src/v/raft/group_configuration.h b/src/v/raft/group_configuration.h index 97ead611a142f..5f171a336e105 100644 --- a/src/v/raft/group_configuration.h +++ b/src/v/raft/group_configuration.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "model/metadata.h" #include "raft/fundamental.h" #include "reflection/adl.h" @@ -60,7 +61,17 @@ inline constexpr model::revision_id no_revision{}; */ enum class configuration_state : uint8_t { simple, transitional, joint }; -std::ostream& operator<<(std::ostream& o, configuration_state t); +inline fmt::iterator format_to(configuration_state t, fmt::iterator out) { + switch (t) { + case configuration_state::simple: + return fmt::format_to(out, "simple"); + case configuration_state::joint: + return fmt::format_to(out, "joint"); + case configuration_state::transitional: + return fmt::format_to(out, "transitional"); + } + __builtin_unreachable(); +} struct group_nodes : serde::envelope, serde::compat_version<0>> { @@ -71,7 +82,10 @@ struct group_nodes std::optional find(model::node_id) const; - friend std::ostream& operator<<(std::ostream&, const group_nodes&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{voters: {}, learners: {}}}", voters, learners); + } friend bool operator==(const group_nodes&, const group_nodes&) = default; auto serde_fields() { return std::tie(voters, learners); } @@ -101,7 +115,14 @@ struct configuration_update replicas_to_add, replicas_to_remove, learner_start_offset); } - friend std::ostream& operator<<(std::ostream&, const configuration_update&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{to_add: {}, to_remove: {}, learner_start_offset: {}}}", + replicas_to_add, + replicas_to_remove, + learner_start_offset); + } }; class group_configuration @@ -329,7 +350,7 @@ class group_configuration friend bool operator==( const group_configuration&, const group_configuration&) = default; - friend std::ostream& operator<<(std::ostream&, const group_configuration&); + fmt::iterator format_to(fmt::iterator it) const; struct configuration_change_strategy { /** @@ -530,7 +551,10 @@ struct offset_configuration { model::offset offset; group_configuration cfg; - friend std::ostream& operator<<(std::ostream&, const offset_configuration&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{offset: {}, group_configuration: {}}}", offset, cfg); + } }; } // namespace raft diff --git a/src/v/raft/heartbeats.h b/src/v/raft/heartbeats.h index 26a73ee97fa37..deeced36fe493 100644 --- a/src/v/raft/heartbeats.h +++ b/src/v/raft/heartbeats.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" #include "model/metadata.h" #include "raft/fundamental.h" @@ -365,10 +366,17 @@ class heartbeat_request_v2 model::node_id target() const { return _target_node; } uint64_t lw_heartbeats_count() const { return _lw_cnt; } -private: - friend std::ostream& - operator<<(std::ostream& o, const heartbeat_request_v2& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{source: {}, target: {}, lw_count: {}, full_count: {}}}", + _source_node, + _target_node, + _lw_cnt, + _full_heartbeats.size()); + } +private: uint64_t _lw_cnt{0}; model::node_id _source_node; model::node_id _target_node; @@ -409,8 +417,14 @@ class heartbeat_reply_v2 return _full_replies; }; - friend std::ostream& - operator<<(std::ostream& o, const heartbeat_reply_v2& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{source: {}, target: {}, full_replies_count: {}}}", + _source_node, + _target_node, + _full_replies.size()); + } friend bool operator==(const heartbeat_reply_v2& lhs, const heartbeat_reply_v2& rhs) { diff --git a/src/v/raft/recovery_scheduler.cc b/src/v/raft/recovery_scheduler.cc index 4d2e7dd9bc34a..ac4492477822c 100644 --- a/src/v/raft/recovery_scheduler.cc +++ b/src/v/raft/recovery_scheduler.cc @@ -108,15 +108,14 @@ follower_recovery_state::~follower_recovery_state() noexcept { } } -std::ostream& operator<<(std::ostream& o, const follower_recovery_state& frs) { - fmt::print( - o, +fmt::iterator follower_recovery_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ntp: {} is_active: {}, our_last_offset: {} leader_last_offset: {}}}", - frs.ntp(), - frs._is_active, - frs._our_last_offset, - frs._leader_last_offset); - return o; + ntp(), + _is_active, + _our_last_offset, + _leader_last_offset); } recovery_scheduler_base::recovery_scheduler_base( diff --git a/src/v/raft/recovery_scheduler.h b/src/v/raft/recovery_scheduler.h index bcca5a339f3b3..d0f6c14f91589 100644 --- a/src/v/raft/recovery_scheduler.h +++ b/src/v/raft/recovery_scheduler.h @@ -9,6 +9,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "config/property.h" #include "container/intrusive_list_helpers.h" @@ -75,8 +76,7 @@ class follower_recovery_state { bool is_active() const { return _is_active; } int64_t pending_offset_count() const; - friend std::ostream& - operator<<(std::ostream&, const follower_recovery_state&); + fmt::iterator format_to(fmt::iterator it) const; private: friend class recovery_scheduler_base; diff --git a/src/v/raft/replicate.h b/src/v/raft/replicate.h index a2f744719d31c..77dad05dd07e5 100644 --- a/src/v/raft/replicate.h +++ b/src/v/raft/replicate.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "model/fundamental.h" #include "raft/errc.h" @@ -22,6 +23,18 @@ namespace raft { enum class consistency_level { quorum_ack, leader_ack, no_ack }; +inline fmt::iterator format_to(consistency_level l, fmt::iterator out) { + switch (l) { + case consistency_level::quorum_ack: + return fmt::format_to(out, "consistency_level::quorum_ack"); + case consistency_level::leader_ack: + return fmt::format_to(out, "consistency_level::leader_ack"); + case consistency_level::no_ack: + return fmt::format_to(out, "consistency_level::no_ack"); + } + return fmt::format_to(out, "unknown consistency_level"); +} + struct replicate_options { explicit replicate_options( consistency_level l, diff --git a/src/v/raft/replication_monitor.cc b/src/v/raft/replication_monitor.cc index 8264f155397a3..cd02a15b94b8b 100644 --- a/src/v/raft/replication_monitor.cc +++ b/src/v/raft/replication_monitor.cc @@ -46,38 +46,25 @@ replication_monitor::replication_monitor(consensus* r) }); } -std::ostream& operator<<(std::ostream& o, const replication_monitor& mon) { +fmt::iterator replication_monitor::format_to(fmt::iterator it) const { static constexpr long max_waiters_to_print = 3; - auto view = std::views::values(mon._waiters); + it = fmt::format_to( + it, + "{{count: {}, pending_majority_waiters: {}, waiters: [", + _waiters.size(), + _pending_majority_replication_waiters); + auto view = std::views::values(_waiters); auto end = std::ranges::next( view.begin(), max_waiters_to_print, view.end()); - fmt::print( - o, - "{{count: {}, pending_majority_waiters: {}, waiters: [{}]}}", - mon._waiters.size(), - mon._pending_majority_replication_waiters, - fmt::join(view.begin(), end, ",")); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const replication_monitor::waiter& entry) { - fmt::print( - o, "{{type: {}, append result: {}}}", entry.type, entry.append_info); - return o; -} - -std::ostream& -operator<<(std::ostream& o, const replication_monitor::wait_type& type) { - switch (type) { - case replication_monitor::commit: - fmt::print(o, "commit"); - break; - case replication_monitor::majority_replication: - fmt::print(o, "majority_replication"); - break; + bool first = true; + for (auto cur = view.begin(); cur != end; ++cur) { + if (!first) { + it = fmt::format_to(it, ","); + } + it = fmt::format_to(it, "{}", **cur); + first = false; } - return o; + return fmt::format_to(it, "]}}"); } ss::future<> replication_monitor::stop() { diff --git a/src/v/raft/replication_monitor.h b/src/v/raft/replication_monitor.h index 99f69ea713aad..b99c9631c03d3 100644 --- a/src/v/raft/replication_monitor.h +++ b/src/v/raft/replication_monitor.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "model/fundamental.h" #include "storage/types.h" @@ -69,6 +70,17 @@ class replication_monitor { private: enum wait_type : int8_t { commit, majority_replication }; + + static constexpr std::string_view wait_type_to_string(wait_type t) { + switch (t) { + case commit: + return "commit"; + case majority_replication: + return "majority_replication"; + } + return ""; + } + struct waiter { waiter( replication_monitor*, storage::append_result, wait_type) noexcept; @@ -77,6 +89,14 @@ class replication_monitor { replication_monitor* monitor; storage::append_result append_info; wait_type type; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{type: {}, append result: {}}}", + wait_type_to_string(type), + append_info); + } }; ss::future<> do_notify_replicated(); @@ -107,9 +127,7 @@ class replication_monitor { size_t _pending_majority_replication_waiters{0}; public: - friend std::ostream& operator<<(std::ostream&, const replication_monitor&); - friend std::ostream& operator<<(std::ostream&, const waiter&); - friend std::ostream& operator<<(std::ostream&, const wait_type&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace raft diff --git a/src/v/raft/state_machine_base.cc b/src/v/raft/state_machine_base.cc index 2cb311ff349c8..2011b337020a0 100644 --- a/src/v/raft/state_machine_base.cc +++ b/src/v/raft/state_machine_base.cc @@ -45,14 +45,4 @@ ss::future<> state_machine_base::wait( co_await _waiters.wait(offset, timeout, as); } -std::ostream& -operator<<(std::ostream& o, const stm_initial_recovery_policy& p) { - switch (p) { - case stm_initial_recovery_policy::read_everything: - return o << "read_everything"; - case stm_initial_recovery_policy::skip_to_end: - return o << "skip_to_end"; - } -} - } // namespace raft diff --git a/src/v/raft/state_machine_base.h b/src/v/raft/state_machine_base.h index a27c6711ff944..af453c5673f34 100644 --- a/src/v/raft/state_machine_base.h +++ b/src/v/raft/state_machine_base.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" #include "model/fundamental.h" @@ -155,7 +156,16 @@ class state_machine_base { mutable offset_monitor _waiters; model::offset _next{0}; }; -std::ostream& operator<<(std::ostream&, const stm_initial_recovery_policy&); +inline fmt::iterator +format_to(stm_initial_recovery_policy p, fmt::iterator out) { + switch (p) { + case stm_initial_recovery_policy::read_everything: + return fmt::format_to(out, "read_everything"); + case stm_initial_recovery_policy::skip_to_end: + return fmt::format_to(out, "skip_to_end"); + } + return fmt::format_to(out, ""); +} /** * This flavor of state machine base allows implementer to opt out from taking * snapshot at arbitrary offset. This way a partition that the STM is based on diff --git a/src/v/raft/state_machine_manager.cc b/src/v/raft/state_machine_manager.cc index b7131c5b05f13..8385c2f3b307d 100644 --- a/src/v/raft/state_machine_manager.cc +++ b/src/v/raft/state_machine_manager.cc @@ -890,12 +890,4 @@ ss::future<> state_machine_manager::write_initial_recovery_snapshot( co_await _initial_recovery_snapshot_mgr.finish_snapshot(writer); } -std::ostream& operator<<( - std::ostream& o, const state_machine_manager::initial_recovery_snapshot& s) { - fmt::print( - o, - "{{ initial_recovery_next_offsets: {} }}", - s.initial_recovery_next_offsets); - return o; -} } // namespace raft diff --git a/src/v/raft/state_machine_manager.h b/src/v/raft/state_machine_manager.h index 5f073d09e25fe..744aa7655264a 100644 --- a/src/v/raft/state_machine_manager.h +++ b/src/v/raft/state_machine_manager.h @@ -164,10 +164,14 @@ class state_machine_manager final { // each state machine that has been added to the manager. absl::flat_hash_map initial_recovery_next_offsets; - }; - friend std::ostream& - operator<<(std::ostream&, const initial_recovery_snapshot&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{initial_recovery_next_offsets: {}}}", + initial_recovery_next_offsets); + } + }; state_machine_manager( consensus* raft, diff --git a/src/v/raft/tests/failure_injectable_log.cc b/src/v/raft/tests/failure_injectable_log.cc index bc6cce688ba7f..fc1b23e0f1b17 100644 --- a/src/v/raft/tests/failure_injectable_log.cc +++ b/src/v/raft/tests/failure_injectable_log.cc @@ -137,9 +137,8 @@ model::timestamp failure_injectable_log::start_timestamp() const { return _underlying_log->start_timestamp(); } -std::ostream& -failure_injectable_log::failure_injectable_log::print(std::ostream& o) const { - return _underlying_log->print(o); +fmt::iterator failure_injectable_log::format_to(fmt::iterator it) const { + return _underlying_log->format_to(it); } std::optional diff --git a/src/v/raft/tests/failure_injectable_log.h b/src/v/raft/tests/failure_injectable_log.h index fdaf975153f95..51b0ce6047d64 100644 --- a/src/v/raft/tests/failure_injectable_log.h +++ b/src/v/raft/tests/failure_injectable_log.h @@ -80,7 +80,7 @@ class failure_injectable_log final : public storage::log { model::timestamp start_timestamp() const final; - std::ostream& print(std::ostream& o) const final; + fmt::iterator format_to(fmt::iterator it) const final; std::optional get_term(model::offset) const final; @@ -158,9 +158,5 @@ class failure_injectable_log final : public storage::log { private: ss::shared_ptr _underlying_log; std::optional _append_delay_generator; - friend std::ostream& - operator<<(std::ostream& o, const failure_injectable_log& lg) { - return lg.print(o); - } }; } // namespace raft diff --git a/src/v/raft/tests/group_configuration_tests.cc b/src/v/raft/tests/group_configuration_tests.cc index c6b3c2767cd45..3738e27220b3d 100644 --- a/src/v/raft/tests/group_configuration_tests.cc +++ b/src/v/raft/tests/group_configuration_tests.cc @@ -480,7 +480,7 @@ struct configuration_advancement_state_machine { vlog( logger.info, "Cancelling reconfiguration in state: {}", - current_state); + fmt_streamed(current_state)); cfg.cancel_configuration_change(model::revision_id{0}); if (dir == direction::original_to_target) { dir = direction::target_to_original; @@ -491,7 +491,9 @@ struct configuration_advancement_state_machine { } void abort_reconfiguration() { vlog( - logger.info, "Aborting reconfiguration in state: {}", current_state); + logger.info, + "Aborting reconfiguration in state: {}", + fmt_streamed(current_state)); cfg.abort_configuration_change(model::revision_id{0}); if (dir == direction::original_to_target) { diff --git a/src/v/raft/tests/heartbeats_test.cc b/src/v/raft/tests/heartbeats_test.cc index ecfdf4976f52a..1faf40eec7550 100644 --- a/src/v/raft/tests/heartbeats_test.cc +++ b/src/v/raft/tests/heartbeats_test.cc @@ -19,7 +19,6 @@ #include #include -#include #include #include diff --git a/src/v/raft/tests/raft_reconfiguration_test.cc b/src/v/raft/tests/raft_reconfiguration_test.cc index 21d351c6f74c3..49c758b0323f2 100644 --- a/src/v/raft/tests/raft_reconfiguration_test.cc +++ b/src/v/raft/tests/raft_reconfiguration_test.cc @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "base/outcome.h" #include "bytes/bytes.h" #include "gtest/gtest.h" @@ -62,16 +63,16 @@ enum class isolated_t { random, }; -std::ostream& operator<<(std::ostream& o, isolated_t pt) { +fmt::iterator format_to(isolated_t pt, fmt::iterator it) { switch (pt) { case isolated_t::none: - return o << "isolated::none"; + return fmt::format_to(it, "isolated::none"); case isolated_t::old_leader: - return o << "isolated::old_leader"; + return fmt::format_to(it, "isolated::old_leader"); case isolated_t::old_followers: - return o << "isolated::old_followers"; + return fmt::format_to(it, "isolated::old_followers"); case isolated_t::random: - return o << "isolated::random"; + return fmt::format_to(it, "isolated::random"); } __builtin_unreachable(); } diff --git a/src/v/raft/tests/stm_test_fixture.h b/src/v/raft/tests/stm_test_fixture.h index 2378ea613759e..ec2b1997cd090 100644 --- a/src/v/raft/tests/stm_test_fixture.h +++ b/src/v/raft/tests/stm_test_fixture.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "bytes/iostream.h" #include "model/fundamental.h" #include "model/metadata.h" @@ -37,8 +38,6 @@ #include #include -#include - using namespace raft; namespace { /** @@ -58,9 +57,9 @@ struct value_entry auto serde_fields() { return std::tie(value, update_cnt); } - friend std::ostream& operator<<(std::ostream& o, const value_entry& ve) { - fmt::print(o, "{{value: {}, update_cnt: {}}}", ve.value, ve.update_cnt); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{value: {}, update_cnt: {}}}", value, update_cnt); } }; } // namespace diff --git a/src/v/raft/transfer_leadership.h b/src/v/raft/transfer_leadership.h index d141fc4a5c9a9..0eaab85980d48 100644 --- a/src/v/raft/transfer_leadership.h +++ b/src/v/raft/transfer_leadership.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "model/fundamental.h" #include "raft/errc.h" #include "raft/fundamental.h" @@ -40,8 +41,10 @@ struct transfer_leadership_request auto serde_fields() { return std::tie(group, target, timeout); } - friend std::ostream& - operator<<(std::ostream& o, const transfer_leadership_request& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "group {} target {} timeout {}", group, target, timeout); + } }; struct transfer_leadership_reply @@ -58,10 +61,8 @@ struct transfer_leadership_reply auto serde_fields() { return std::tie(success, result); } - friend std::ostream& - operator<<(std::ostream& o, const transfer_leadership_reply& r) { - fmt::print(o, "success {} result {}", r.success, r.result); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "success {} result {}", success, result); } }; diff --git a/src/v/raft/types.cc b/src/v/raft/types.cc index 81de7f18748a6..fd51e71b5f531 100644 --- a/src/v/raft/types.cc +++ b/src/v/raft/types.cc @@ -18,14 +18,12 @@ #include "raft/consensus_utils.h" #include "raft/errc.h" #include "raft/group_configuration.h" -#include "raft/transfer_leadership.h" #include "reflection/adl.h" #include "reflection/async_adl.h" #include #include -#include #include namespace { @@ -130,156 +128,27 @@ replicate_stages::replicate_stages(raft::errc ec) , replicate_finished( ss::make_ready_future>(make_error_code(ec))) {}; -std::ostream& operator<<(std::ostream& o, const vnode& id) { - fmt::print(o, "{{id: {}, revision: {}}}", id.id(), id.revision()); - return o; -} - -std::ostream& operator<<(std::ostream& o, const append_entries_reply& r) { - fmt::print( - o, - "{{node_id: {}, target_node_id: {}, group: {}, term: {}, " - "last_dirty_log_index: {}, last_flushed_log_index: {}, " - "last_term_base_offset: {}, result: {}, may_recover: {}}}", - r.node_id, - r.target_node_id, - r.group, - r.term, - r.last_dirty_log_index, - r.last_flushed_log_index, - r.last_term_base_offset, - r.result, - r.may_recover); - return o; -} - -std::ostream& operator<<(std::ostream& o, const vote_request& r) { - fmt::print( - o, - "{{node_id: {}, target_node_id: {}, group: {}, term: {}, prev_log_index: " - "{}, prev_log_term: {}, leadership_xfer: {}}}", - r.node_id, - r.target_node_id, - r.group, - r.term, - r.prev_log_index, - r.prev_log_term, - r.leadership_transfer); - return o; -} -std::ostream& operator<<(std::ostream& o, const follower_metrics& i) { - fmt::print( - o, - "{{node_id: {}, is_learner: {}, committed_log_index: {}, " - "dirty_log_index: {}, match_index: {}, is_live: {}, " - "under_replicated: {}}}", - i.id, - i.is_learner, - i.committed_log_index, - i.dirty_log_index, - i.match_index, - i.is_live, - i.under_replicated); - return o; -} -std::ostream& operator<<(std::ostream& o, const heartbeat_metadata& hm) { - fmt::print( - o, - "{{node_id: {}, target_node_id: {}, protocol_metadata: {}}}", - hm.node_id, - hm.target_node_id, - hm.meta); - return o; -} - -std::ostream& operator<<(std::ostream& o, const consistency_level& l) { - switch (l) { - case consistency_level::quorum_ack: - o << "consistency_level::quorum_ack"; - break; - case consistency_level::leader_ack: - o << "consistency_level::leader_ack"; - break; - case consistency_level::no_ack: - o << "consistency_level::no_ack"; - break; - default: - o << "unknown consistency_level"; - } - return o; -} -std::ostream& operator<<(std::ostream& o, const protocol_metadata& m) { - fmt::print( - o, - "{{group: {}, commit_index: {}, term: {}, prev_log_index: {}, " - "prev_log_term: {}, last_visible_index: {}, dirty_offset: {}, " - "prev_log_delta: {}}}", - m.group, - m.commit_index, - m.term, - m.prev_log_index, - m.prev_log_term, - m.last_visible_index, - m.dirty_offset, - m.prev_log_delta); - return o; -} - -std::ostream& operator<<(std::ostream& o, const vote_reply& r) { - fmt::print( - o, - "{{term: {}, target_node: {}, vote_granted: {}, log_ok: {}}}", - r.term, - r.target_node_id, - r.granted, - r.log_ok); - return o; -} -std::ostream& operator<<(std::ostream& o, const reply_result& r) { - switch (r) { - case reply_result::success: - o << "success"; - return o; - case reply_result::failure: - o << "failure"; - return o; - case reply_result::group_unavailable: - o << "group_unavailable"; - return o; - case reply_result::follower_busy: - o << "follower_busy"; - return o; +fmt::iterator append_entries_request::format_to(fmt::iterator it) const { + if (_batches.empty()) { + return fmt::format_to( + it, + "node_id: {}, target_node_id: {}, protocol metadata: {}, batches: " + "{{}}", + _source_node, + _target_node_id, + _meta); + } else { + return fmt::format_to( + it, + "node_id: {}, target_node_id: {}, protocol metadata: {}, batch " + "count: {}, offset range: [{},{}]", + _source_node, + _target_node_id, + _meta, + _batches.size(), + _batches.front().base_offset(), + _batches.back().last_offset()); } - __builtin_unreachable(); -} - -std::ostream& operator<<(std::ostream& o, const install_snapshot_request& r) { - fmt::print( - o, - "{{term: {}, group: {}, target_node_id: {}, node_id: {}, " - "last_included_index: {}, " - "file_offset: {}, chunk_size: {}, done: {}, dirty_offset: {}}}", - r.term, - r.group, - r.target_node_id, - r.node_id, - r.last_included_index, - r.file_offset, - r.chunk.size_bytes(), - r.done, - r.dirty_offset); - return o; -} - -std::ostream& operator<<(std::ostream& o, const install_snapshot_reply& r) { - fmt::print( - o, - "{{term: {}, target_node_id: {}, bytes_stored: {}, success: {}}}", - r.term, - r.target_node_id, - r.bytes_stored, - r.success); - return o; } append_entries_request::append_entries_request( @@ -393,37 +262,6 @@ append_entries_request_serde_wrapper::serde_async_direct_read( node_id, target_node_id, meta, std::move(batches), batches_size, flush); } -std::ostream& operator<<(std::ostream& o, const append_entries_request& r) { - if (r._batches.empty()) { - fmt::print( - o, - "node_id: {}, target_node_id: {}, protocol metadata: {}, batches: " - "{{}}", - r._source_node, - r._target_node_id, - r._meta); - } else { - fmt::print( - o, - "node_id: {}, target_node_id: {}, protocol metadata: {}, batch " - "count: {}, offset range: [{},{}]", - r._source_node, - r._target_node_id, - r._meta, - r._batches.size(), - r._batches.front().base_offset(), - r._batches.back().last_offset()); - } - return o; -} - -std::ostream& -operator<<(std::ostream& o, const transfer_leadership_request& r) { - fmt::print( - o, "group {} target {} timeout {}", r.group, r.target, r.timeout); - return o; -} - } // namespace raft namespace reflection { diff --git a/src/v/raft/types.h b/src/v/raft/types.h index a3fb989133e21..ff7c349b09f6e 100644 --- a/src/v/raft/types.h +++ b/src/v/raft/types.h @@ -51,8 +51,21 @@ struct protocol_metadata // offset delta corresponding to the prev_log_index model::offset_delta prev_log_delta{}; - friend std::ostream& - operator<<(std::ostream& o, const protocol_metadata& m); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{group: {}, commit_index: {}, term: {}, prev_log_index: {}, " + "prev_log_term: {}, last_visible_index: {}, dirty_offset: {}, " + "prev_log_delta: {}}}", + group, + commit_index, + term, + prev_log_index, + prev_log_term, + last_visible_index, + dirty_offset, + prev_log_delta); + } friend bool operator==(const protocol_metadata&, const protocol_metadata&) = default; @@ -87,7 +100,20 @@ struct follower_metrics { bool is_live; bool under_replicated; - friend std::ostream& operator<<(std::ostream& o, const follower_metrics& i); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{node_id: {}, is_learner: {}, committed_log_index: {}, " + "dirty_log_index: {}, match_index: {}, is_live: {}, " + "under_replicated: {}}}", + id, + is_learner, + committed_log_index, + dirty_log_index, + match_index, + is_live, + under_replicated); + } }; using flush_after_append = ss::bool_class; @@ -140,8 +166,7 @@ struct append_entries_request return _batches; } - friend std::ostream& - operator<<(std::ostream& o, const append_entries_request& r); + fmt::iterator format_to(fmt::iterator it) const; ss::future<> serde_async_write(iobuf& out); @@ -220,8 +245,22 @@ struct append_entries_reply // older nodes are always ready for recovery. bool may_recover = true; - friend std::ostream& - operator<<(std::ostream& o, const append_entries_reply& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{node_id: {}, target_node_id: {}, group: {}, term: {}, " + "last_dirty_log_index: {}, last_flushed_log_index: {}, " + "last_term_base_offset: {}, result: {}, may_recover: {}}}", + node_id, + target_node_id, + group, + term, + last_dirty_log_index, + last_flushed_log_index, + last_term_base_offset, + result, + may_recover); + } friend bool operator==( const append_entries_reply&, const append_entries_reply&) = default; @@ -247,8 +286,15 @@ struct heartbeat_metadata { friend bool operator==(const heartbeat_metadata&, const heartbeat_metadata&) = default; - friend std::ostream& - operator<<(std::ostream& o, const heartbeat_metadata& r); + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{node_id: {}, target_node_id: {}, protocol_metadata: {}}}", + node_id, + target_node_id, + meta); + } }; struct vote_request @@ -269,7 +315,19 @@ struct vote_request vnode source_node() const { return node_id; } vnode target_node() const { return target_node_id; } - friend std::ostream& operator<<(std::ostream& o, const vote_request& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{node_id: {}, target_node_id: {}, group: {}, term: {}, " + "prev_log_index: {}, prev_log_term: {}, leadership_xfer: {}}}", + node_id, + target_node_id, + group, + term, + prev_log_index, + prev_log_term, + leadership_transfer); + } friend bool operator==(const vote_request&, const vote_request&) = default; @@ -303,7 +361,15 @@ struct vote_reply // replying node vnode node_id; - friend std::ostream& operator<<(std::ostream& o, const vote_reply& r); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{term: {}, target_node: {}, vote_granted: {}, log_ok: {}}}", + term, + target_node_id, + granted, + log_ok); + } friend bool operator==(const vote_reply&, const vote_reply&) = default; @@ -378,8 +444,22 @@ struct install_snapshot_request raft::group_id target_group() const { return group; } vnode source_node() const { return node_id; } vnode target_node() const { return target_node_id; } - friend std::ostream& - operator<<(std::ostream&, const install_snapshot_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{term: {}, group: {}, target_node_id: {}, node_id: {}, " + "last_included_index: {}, " + "file_offset: {}, chunk_size: {}, done: {}, dirty_offset: {}}}", + term, + group, + target_node_id, + node_id, + last_included_index, + file_offset, + chunk.size_bytes(), + done, + dirty_offset); + } friend bool operator==(const install_snapshot_request&, const install_snapshot_request&) @@ -453,8 +533,15 @@ struct install_snapshot_reply // replying node vnode node_id; - friend std::ostream& - operator<<(std::ostream&, const install_snapshot_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{term: {}, target_node_id: {}, bytes_stored: {}, success: {}}}", + term, + target_node_id, + bytes_stored, + success); + } friend bool operator==( const install_snapshot_reply&, const install_snapshot_reply&) = default; @@ -501,16 +588,14 @@ struct timeout_now_request return std::tie(target_node_id, node_id, group, term); } - friend std::ostream& - operator<<(std::ostream& o, const timeout_now_request& r) { - fmt::print( - o, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "target_node_id {} node_id {} group {} term {}", - r.target_node_id, - r.node_id, - r.group, - r.term); - return o; + target_node_id, + node_id, + group, + term); } }; @@ -529,18 +614,18 @@ struct timeout_now_reply auto serde_fields() { return std::tie(target_node_id, term, result); } - friend std::ostream& - operator<<(std::ostream& o, const timeout_now_reply& r) { - fmt::print( - o, - "target_node_id {} term {} result {}", - r.target_node_id, - r.term, - static_cast>(r.result)); - return o; + friend constexpr uint8_t format_as(status s) { + return static_cast(s); } + + fmt::iterator format_to(fmt::iterator it) const; }; +inline fmt::iterator timeout_now_reply::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "target_node_id {} term {} result {}", target_node_id, term, result); +} + // key types used to store data in key-value store enum class metadata_key : int8_t { voted_for = 0, @@ -566,9 +651,6 @@ struct scheduling_config { ss::scheduling_group send_sg; }; -std::ostream& operator<<(std::ostream& o, const consistency_level& l); -std::ostream& operator<<(std::ostream& o, const reply_result&); - using with_learner_recovery_throttle = ss::bool_class; diff --git a/src/v/raft/vote_stm.cc b/src/v/raft/vote_stm.cc index 7f01d00797631..367e7ed339611 100644 --- a/src/v/raft/vote_stm.cc +++ b/src/v/raft/vote_stm.cc @@ -23,19 +23,19 @@ #include namespace raft { -std::ostream& operator<<(std::ostream& o, const vote_stm::vmeta& m) { - o << "{value: "; - if (m.value) { - if (m.value->has_error()) { - auto& e = m.value->error(); - o << "{" << *m.value << ", message: " << e.message() << "}"; +fmt::iterator vote_stm::vmeta::format_to(fmt::iterator it) const { + it = fmt::format_to(it, "{{value: "); + if (value) { + if (value->has_error()) { + auto& e = value->error(); + it = fmt::format_to(it, "{{{}, message: {}}}", *value, e.message()); } else { - o << "[" << *m.value << "]"; + it = fmt::format_to(it, "[{}]", *value); } } else { - o << "nullptr"; + it = fmt::format_to(it, "nullptr"); } - return o << "}"; + return fmt::format_to(it, "}}"); } vote_stm::vote_stm(consensus* p, is_prevote prevote) : _ptr(p) diff --git a/src/v/raft/vote_stm.h b/src/v/raft/vote_stm.h index 7975ca143ecbb..383f702dae35f 100644 --- a/src/v/raft/vote_stm.h +++ b/src/v/raft/vote_stm.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "base/outcome.h" #include "model/fundamental.h" #include "raft/fwd.h" @@ -109,6 +110,8 @@ class vote_stm { } std::unique_ptr> value; vote_stm& _vote_stm; + + fmt::iterator format_to(fmt::iterator it) const; }; bool has_request_in_progress() const; @@ -121,8 +124,6 @@ class vote_stm { void fail_election(); - friend std::ostream& operator<<(std::ostream&, const vmeta&); - ss::future do_vote(); ss::future<> self_vote(); ss::future<> dispatch_one(vnode); diff --git a/src/v/random/generators.cc b/src/v/random/generators.cc index da114a33cb10b..2c03ae07d865d 100644 --- a/src/v/random/generators.cc +++ b/src/v/random/generators.cc @@ -14,6 +14,8 @@ #include "absl/random/random.h" #include "base/vassert.h" +#include + #include #include #include @@ -116,7 +118,7 @@ fmt::iterator rng::format_to(fmt::iterator it) const { // the only way to get the internal pcg64 state is to parse it out of the // ostream<< output for the engine - auto gen_state = fmt::format("{}", gen_); + auto gen_state = fmt::format("{}", fmt_streamed(gen_)); std::stringstream ss(gen_state); std::vector state_vector; uint64_t temp{}; diff --git a/src/v/redpanda/admin/migrations.cc b/src/v/redpanda/admin/migrations.cc index 9c923472a753a..60acd45b081ba 100644 --- a/src/v/redpanda/admin/migrations.cc +++ b/src/v/redpanda/admin/migrations.cc @@ -146,7 +146,7 @@ auto to_admin_type( const cluster::data_migrations::migration_metadata& meta) { typename StateAdmin::type ret; ret.id = meta.id; - ret.state = fmt::to_string(meta.state); + ret.state = fmt::format("{}", meta.state); ret.migration = to_admin_type(migration); ret.created_timestamp = meta.created_timestamp.value(); if (meta.completed_timestamp != model::timestamp::missing()) { diff --git a/src/v/redpanda/admin/security.cc b/src/v/redpanda/admin/security.cc index 405acf3f70f7c..62862ff7172b4 100644 --- a/src/v/redpanda/admin/security.cc +++ b/src/v/redpanda/admin/security.cc @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "cluster/controller.h" #include "cluster/security_frontend.h" #include "config/broker_authn_endpoint.h" @@ -240,23 +241,22 @@ enum class role_errc { role_name_conflict = 40902, }; -// NOTE(oren): bogus -Wunneeded-internal-declaration here from clang-tidy (?) -std::ostream& operator<<(std::ostream& os, role_errc code) { +fmt::iterator format_to(role_errc code, fmt::iterator out) { switch (code) { case role_errc::malformed_def: - return os << "Malformed request"; + return fmt::format_to(out, "Malformed request"); case role_errc::invalid_name: - return os << "Invalid role name"; + return fmt::format_to(out, "Invalid role name"); case role_errc::unrecognized_field: - return os << "Unrecognized field"; + return fmt::format_to(out, "Unrecognized field"); case role_errc::member_list_conflict: - return os << "Conflict between 'add' and 'remove' lists"; + return fmt::format_to(out, "Conflict between 'add' and 'remove' lists"); case role_errc::role_not_found: - return os << "Role not found"; + return fmt::format_to(out, "Role not found"); case role_errc::role_already_exists: - return os << "Role already exists"; + return fmt::format_to(out, "Role already exists"); case role_errc::role_name_conflict: - return os << "Role name conflict"; + return fmt::format_to(out, "Role name conflict"); } __builtin_unreachable(); } @@ -1525,7 +1525,7 @@ admin_server::get_security_report(std::unique_ptr) { alert.description = ssx::sformat( "TLS minimum version is set to {} which is less than {}. This is " "insecure and not recommended.", - config::shard_local_cfg().tls_min_version, + config::shard_local_cfg().tls_min_version(), min_secure_tls); alerts.push_back(std::move(alert)); } diff --git a/src/v/redpanda/admin/server.cc b/src/v/redpanda/admin/server.cc index 45d8f40dec478..09e2710f77b8f 100644 --- a/src/v/redpanda/admin/server.cc +++ b/src/v/redpanda/admin/server.cc @@ -2927,7 +2927,7 @@ admin_server::get_decommission_progress_handler( f_details.ns = ntp.ns; f_details.topic = ntp.tp.topic; f_details.partition = ntp.tp.partition; - f_details.error = fmt::to_string(details.error); + f_details.error = fmt::format("{}", details.error); ret.reallocation_failure_details.push(f_details); } @@ -5064,6 +5064,10 @@ constexpr std::string_view to_string_view(service_kind kind) { return "invalid"; } +fmt::iterator format_to(service_kind kind, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(kind)); +} + template std::enable_if_t, std::optional> from_string_view(std::string_view); diff --git a/src/v/rpc/test/netbuf_tests.cc b/src/v/rpc/test/netbuf_tests.cc index 2807e0ec2416c..0d5c9026b23a1 100644 --- a/src/v/rpc/test/netbuf_tests.cc +++ b/src/v/rpc/test/netbuf_tests.cc @@ -20,8 +20,6 @@ // utils #include "test_types.h" -#include - struct envelope_pod : serde::envelope, serde::compat_version<0>> { int16_t x = 1; diff --git a/src/v/rpc/transport.cc b/src/v/rpc/transport.cc index 98845a84e9a3b..3291899df1f84 100644 --- a/src/v/rpc/transport.cc +++ b/src/v/rpc/transport.cc @@ -475,14 +475,13 @@ transport::~transport() { *this); } -std::ostream& operator<<(std::ostream& o, const transport& t) { - fmt::print( - o, +fmt::iterator transport::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "(server:{}, _correlations:{}, _correlation_idx:{})", - t.server_address(), - t._correlations.size(), - t._correlation_idx); - return o; + server_address(), + _correlations.size(), + _correlation_idx); } std::vector client_probe::defs( @@ -583,22 +582,30 @@ std::vector client_probe::defs( return ret; } -std::ostream& operator<<(std::ostream& o, const client_probe& p) { - o << "{" - << " requests_sent: " << p._requests - << ", requests_pending: " << p._requests_pending - << ", requests_completed: " << p._requests_completed - << ", request_errors: " << p._request_errors - << ", request_timeouts: " << p._request_timeouts - << ", in_bytes: " << p._in_bytes << ", out_bytes: " << p._out_bytes - << ", connects: " << p._connects << ", connections: " << p._connections - << ", connection_errors: " << p._connection_errors - << ", read_dispatch_errors: " << p._read_dispatch_errors - << ", corrupted_headers: " << p._corrupted_headers - << ", server_correlation_errors: " << p._server_correlation_errors - << ", client_correlation_errors: " << p._client_correlation_errors - << ", requests_blocked_memory: " << p._requests_blocked_memory << " }"; - return o; +fmt::iterator client_probe::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ requests_sent: {}, requests_pending: {}, requests_completed: {}, " + "request_errors: {}, request_timeouts: {}, in_bytes: {}, out_bytes: {}, " + "connects: {}, connections: {}, connection_errors: {}, " + "read_dispatch_errors: {}, corrupted_headers: {}, " + "server_correlation_errors: {}, client_correlation_errors: {}, " + "requests_blocked_memory: {} }}", + _requests, + _requests_pending, + _requests_completed, + _request_errors, + _request_timeouts, + _in_bytes, + _out_bytes, + _connects, + _connections, + _connection_errors, + _read_dispatch_errors, + _corrupted_headers, + _server_correlation_errors, + _client_correlation_errors, + _requests_blocked_memory); } } // namespace rpc diff --git a/src/v/rpc/transport.h b/src/v/rpc/transport.h index 82e01fdebb298..7e990d31e01a6 100644 --- a/src/v/rpc/transport.h +++ b/src/v/rpc/transport.h @@ -13,6 +13,7 @@ #include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include "container/chunked_hash_map.h" @@ -166,7 +167,10 @@ class client_probe : public net::client_probe { uint32_t _client_correlation_errors = 0; uint32_t _requests_blocked_memory = 0; - friend std::ostream& operator<<(std::ostream& o, const client_probe& p); + friend struct fmt::formatter; + +public: + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -208,6 +212,8 @@ class transport final : public net::base_transport { transport_version version() const { return _version; } + fmt::iterator format_to(fmt::iterator it) const; + private: using sequence_t = named_type; struct entry { @@ -296,8 +302,6 @@ class transport final : public net::base_transport { friend class ::rpc_integration_fixture_oc_ns_adl_only_no_upgrade; void set_version(transport_version v) { _version = v; } - friend std::ostream& operator<<(std::ostream&, const transport&); - std::unique_ptr _probe; }; @@ -453,3 +457,23 @@ transport::send_typed_versioned( } } // namespace rpc + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const rpc::client_probe& v, fmt::format_context& ctx) const { + return v.format_to(ctx.out()); + } +}; + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const rpc::transport& v, fmt::format_context& ctx) const { + return v.format_to(ctx.out()); + } +}; diff --git a/src/v/rpc/types.cc b/src/v/rpc/types.cc index 7d2cf7e5ae582..61f03fbf2338d 100644 --- a/src/v/rpc/types.cc +++ b/src/v/rpc/types.cc @@ -38,42 +38,20 @@ uint32_t checksum_header_only(const header& h) { return crc.value(); } -std::ostream& operator<<(std::ostream& o, const header& h) { +fmt::iterator header::format_to(fmt::iterator it) const { // NOTE: if we use the int8_t types, ostream doesn't print 0's - // artificially ast version and compression as ints - return o << "{version:" << int(h.version) - << ", header_checksum:" << h.header_checksum - << ", compression:" << static_cast(h.compression) - << ", payload_size:" << h.payload_size << ", meta:" << h.meta - << ", correlation_id:" << h.correlation_id - << ", payload_checksum:" << h.payload_checksum << "}"; -} - -std::ostream& operator<<(std::ostream& o, const status& s) { - switch (s) { - case status::success: - return o << "rpc::status::success"; - case status::method_not_found: - return o << "rpc::status::method_not_found"; - case status::request_timeout: - return o << "rpc::status::request_timeout"; - case status::server_error: - return o << "rpc::status::server_error"; - case status::version_not_supported: - return o << "rpc::status::version_not_supported"; - case status::service_unavailable: - return o << "rpc::status::service_unavailable"; - default: - return o << "rpc::status::unknown"; - } -} - -std::ostream& operator<<(std::ostream& o, transport_version v) { - fmt::print( - o, - "rpc::transport_version::v{}", - static_cast>(v)); - return o; + // artificially cast version and compression as ints + return fmt::format_to( + it, + "{{version:{}, header_checksum:{}, compression:{}, payload_size:{}, " + "meta:{}, correlation_id:{}, payload_checksum:{}}}", + int(version), + header_checksum, + static_cast(compression), + payload_size, + meta, + correlation_id, + payload_checksum); } } // namespace rpc diff --git a/src/v/rpc/types.h b/src/v/rpc/types.h index e399b31eb6a1d..8ea9ac47ea187 100644 --- a/src/v/rpc/types.h +++ b/src/v/rpc/types.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/likely.h" #include "base/outcome.h" #include "base/seastarx.h" @@ -242,7 +243,7 @@ struct header { /// \brief xxhash64 uint64_t payload_checksum{0}; - friend std::ostream& operator<<(std::ostream&, const header&); + fmt::iterator format_to(fmt::iterator it) const; }; static constexpr size_t size_of_rpc_header @@ -481,6 +482,34 @@ struct transport_configuration { transport_version version{transport_version::v2}; }; -std::ostream& operator<<(std::ostream&, const status&); -std::ostream& operator<<(std::ostream&, transport_version); +inline fmt::iterator format_to(status s, fmt::iterator out) { + switch (s) { + case status::success: + return fmt::format_to(out, "rpc::status::success"); + case status::method_not_found: + return fmt::format_to(out, "rpc::status::method_not_found"); + case status::request_timeout: + return fmt::format_to(out, "rpc::status::request_timeout"); + case status::server_error: + return fmt::format_to(out, "rpc::status::server_error"); + case status::version_not_supported: + return fmt::format_to(out, "rpc::status::version_not_supported"); + case status::service_unavailable: + return fmt::format_to(out, "rpc::status::service_unavailable"); + } + return fmt::format_to(out, "rpc::status::unknown"); +} } // namespace rpc + +template<> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(rpc::transport_version v, format_context& ctx) const { + return fmt::format_to( + ctx.out(), + "rpc::transport_version::v{}", + static_cast>(v)); + } +}; diff --git a/src/v/security/BUILD b/src/v/security/BUILD index 7d1de369f22c9..8e963f456725a 100644 --- a/src/v/security/BUILD +++ b/src/v/security/BUILD @@ -82,7 +82,6 @@ redpanda_cc_library( srcs = [ "acl.cc", "authorizer.cc", - "credential.cc", "gssapi_authenticator.cc", "gssapi_principal_mapper.cc", "jwt.cc", diff --git a/src/v/security/acl.cc b/src/v/security/acl.cc index 685b09a333a95..9a4b72a169912 100644 --- a/src/v/security/acl.cc +++ b/src/v/security/acl.cc @@ -414,128 +414,6 @@ from_string_view(std::string_view str) { .default_match(std::nullopt); } -std::ostream& operator<<(std::ostream& os, acl_operation op) { - return os << to_string_view(op); -} - -std::ostream& operator<<(std::ostream& os, acl_permission perm) { - return os << to_string_view(perm); -} - -std::ostream& operator<<(std::ostream& os, resource_type type) { - return os << to_string_view(type); -} - -std::ostream& operator<<(std::ostream& os, pattern_type type) { - return os << to_string_view(type); -} - -std::ostream& operator<<(std::ostream& os, principal_type type) { - return os << to_string_view(type); -} - -std::ostream& -operator<<(std::ostream& os, const acl_principal_base& principal) { - fmt::print(os, "{:l}", principal); - return os; -} - -std::ostream& operator<<(std::ostream& os, const resource_pattern& r) { - fmt::print( - os, - "type {{{}}} name {{{}}} pattern {{{}}}", - r._resource, - r._name, - r._pattern); - return os; -} - -std::ostream& operator<<(std::ostream& os, const acl_host& host) { - if (host._addr) { - fmt::print(os, "{{{}}}", *host._addr); - } else { - // we can log whatever representation we want for a wildcard host, - // but kafka expects "*" as the wildcard representation. - os << "{{any_host}}"; - } - return os; -} - -std::ostream& operator<<(std::ostream& os, const acl_entry& entry) { - fmt::print( - os, - "{{principal {} host {} op {} perm {}}}", - entry._principal, - entry._host, - entry._operation, - entry._permission); - return os; -} - -std::ostream& operator<<(std::ostream& os, const acl_binding& binding) { - fmt::print(os, "{{pattern {} entry {}}}", binding._pattern, binding._entry); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const resource_pattern_filter::pattern_match&) { - fmt::print(os, "{{}}"); - return os; -} - -std::ostream& -operator<<(std::ostream& os, resource_pattern_filter::resource_subsystem s) { - using resource_subsystem = resource_pattern_filter::resource_subsystem; - switch (s) { - case resource_subsystem::kafka: - return os << "kafka"; - case resource_subsystem::schema_registry: - return os << "schema_registry"; - } - __builtin_unreachable(); -} - -std::ostream& operator<<(std::ostream& o, const resource_pattern_filter& f) { - fmt::print( - o, - "{{ resource: {} name: {} pattern: {} subsystem: {}}}", - f._resource, - f._name, - f._pattern, - f._subsystem); - return o; -} - -std::ostream& operator<<( - std::ostream& os, resource_pattern_filter::serialized_pattern_type type) { - using pattern_type = resource_pattern_filter::serialized_pattern_type; - switch (type) { - case pattern_type::literal: - return os << "literal"; - case pattern_type::match: - return os << "match"; - case pattern_type::prefixed: - return os << "prefixed"; - } - __builtin_unreachable(); -} - -std::ostream& operator<<(std::ostream& o, const acl_entry_filter& f) { - fmt::print( - o, - "{{ pattern: {} host: {} operation: {}, permission: {} }}", - f._principal, - f._host, - f._operation, - f._permission); - return o; -} - -std::ostream& operator<<(std::ostream& o, const acl_binding_filter& f) { - fmt::print(o, "{{ pattern: {} acl: {} }}", f._pattern, f._acl); - return o; -} - bool acl_entry_filter::matches(const acl_entry& other) const { if (_principal && _principal != other.principal()) { return false; diff --git a/src/v/security/acl.h b/src/v/security/acl.h index 3dd460d8649c3..7a4466760a01b 100644 --- a/src/v/security/acl.h +++ b/src/v/security/acl.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/btree_map.h" #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "base/type_traits.h" #include "kafka/protocol/types.h" @@ -32,7 +33,6 @@ #include -#include #include namespace security { @@ -96,6 +96,9 @@ constexpr std::string_view to_string_view(resource_type type) { } __builtin_unreachable(); } +inline fmt::iterator format_to(resource_type type, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(type)); +} template<> std::optional @@ -140,6 +143,9 @@ constexpr std::string_view to_string_view(pattern_type type) { } __builtin_unreachable(); } +inline fmt::iterator format_to(pattern_type type, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(type)); +} template<> std::optional @@ -191,8 +197,10 @@ constexpr std::string_view to_string_view(acl_operation op) { } __builtin_unreachable(); } +inline fmt::iterator format_to(acl_operation op, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(op)); +} -std::ostream& operator<<(std::ostream&, acl_operation); template<> std::optional from_string_view(std::string_view str); @@ -216,8 +224,10 @@ constexpr std::string_view to_string_view(acl_permission perm) { } __builtin_unreachable(); } +inline fmt::iterator format_to(acl_permission perm, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(perm)); +} -std::ostream& operator<<(std::ostream&, acl_permission); template<> std::optional from_string_view(std::string_view str); @@ -250,15 +260,14 @@ constexpr std::string_view to_string_view(principal_type type) { } __builtin_unreachable(); } +inline fmt::iterator format_to(principal_type type, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(type)); +} template<> std::optional from_string_view(std::string_view str); -std::ostream& operator<<(std::ostream&, resource_type); -std::ostream& operator<<(std::ostream&, pattern_type); -std::ostream& operator<<(std::ostream&, principal_type); - /** * Abstract interface for Kafka principals. * @@ -292,7 +301,8 @@ class acl_principal_base { return l.type() == r.type() && l.name_view() == r.name_view(); } - friend std::ostream& operator<<(std::ostream&, const acl_principal_base&); + friend std::ostream& + operator<<(std::ostream& os, const acl_principal_base& p); }; /** @@ -409,6 +419,11 @@ struct fmt::formatter namespace security { +inline std::ostream& operator<<(std::ostream& os, const acl_principal_base& p) { + fmt::print(os, "{}", p); + return os; +} + /** * Concrete instance of a Kafka principal. * @@ -466,7 +481,14 @@ class resource_pattern return H::combine(std::move(h), e._resource, e._name, e._pattern); } - friend std::ostream& operator<<(std::ostream&, const resource_pattern&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "type {{{}}} name {{{}}} pattern {{{}}}", + _resource, + _name, + _pattern); + } resource_type resource() const { return _resource; } const ss::sstring& name() const { return _name; } @@ -507,7 +529,12 @@ class acl_host } } - friend std::ostream& operator<<(std::ostream&, const acl_host&); + fmt::iterator format_to(fmt::iterator it) const { + if (_addr) { + return fmt::format_to(it, "{{{}}}", *_addr); + } + return fmt::format_to(it, "{{{{any_host}}}}"); + } std::optional address() const { return _addr; } @@ -547,7 +574,15 @@ class acl_entry std::move(h), e._principal, e._host, e._operation, e._permission); } - friend std::ostream& operator<<(std::ostream&, const acl_entry&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{principal {} host {} op {} perm {}}}", + _principal, + _host, + _operation, + _permission); + } const acl_principal& principal() const { return _principal; } const acl_host& host() const { return _host; } @@ -585,7 +620,9 @@ class acl_binding return H::combine(std::move(h), e._pattern, e._entry); } - friend std::ostream& operator<<(std::ostream&, const acl_binding&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{pattern {} entry {}}}", _pattern, _entry); + } const resource_pattern& pattern() const { return _pattern; } const acl_entry& entry() const { return _entry; } @@ -615,11 +652,34 @@ class resource_pattern_filter match = 2, }; + friend fmt::iterator + format_to(serialized_pattern_type type, fmt::iterator out) { + switch (type) { + case serialized_pattern_type::literal: + return fmt::format_to(out, "literal"); + case serialized_pattern_type::prefixed: + return fmt::format_to(out, "prefixed"); + case serialized_pattern_type::match: + return fmt::format_to(out, "match"); + } + __builtin_unreachable(); + } + enum class resource_subsystem : uint8_t { kafka = 0, schema_registry = 1, }; + friend fmt::iterator format_to(resource_subsystem s, fmt::iterator out) { + switch (s) { + case resource_subsystem::kafka: + return fmt::format_to(out, "kafka"); + case resource_subsystem::schema_registry: + return fmt::format_to(out, "schema_registry"); + } + __builtin_unreachable(); + } + static serialized_pattern_type to_pattern(security::pattern_type from) { switch (from) { case security::pattern_type::literal: @@ -636,7 +696,9 @@ class resource_pattern_filter friend bool operator==(const pattern_match&, const pattern_match&) = default; - friend std::ostream& operator<<(std::ostream&, const pattern_match&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{}}"); + } auto serde_fields() { return std::tie(); } }; @@ -701,8 +763,18 @@ class resource_pattern_filter friend bool operator==( const resource_pattern_filter&, const resource_pattern_filter&) = default; - friend std::ostream& - operator<<(std::ostream&, const resource_pattern_filter&); + fmt::iterator format_to(fmt::iterator it) const { + it = fmt::format_to( + it, "{{ resource: {} name: {} pattern: ", _resource, _name); + if (_pattern) { + std::visit( + [&it](const auto& v) { it = fmt::format_to(it, "{}", v); }, + *_pattern); + } else { + it = fmt::format_to(it, "none"); + } + return fmt::format_to(it, " subsystem: {}}}", _subsystem); + } auto serde_fields() { return std::tie(_resource, _name, _pattern, _subsystem); @@ -715,9 +787,6 @@ class resource_pattern_filter resource_subsystem _subsystem{resource_subsystem::kafka}; }; -std::ostream& -operator<<(std::ostream&, resource_pattern_filter::serialized_pattern_type); - /* * A filter for matching ACL entries. */ @@ -773,7 +842,15 @@ class acl_entry_filter friend bool operator==(const acl_entry_filter&, const acl_entry_filter&) = default; - friend std::ostream& operator<<(std::ostream&, const acl_entry_filter&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ pattern: {} host: {} operation: {}, permission: {} }}", + _principal, + _host, + _operation, + _permission); + } private: std::optional _principal; @@ -835,7 +912,9 @@ class acl_binding_filter friend bool operator==(const acl_binding_filter&, const acl_binding_filter&) = default; - friend std::ostream& operator<<(std::ostream&, const acl_binding_filter&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ pattern: {} acl: {} }}", _pattern, _acl); + } void serde_write(iobuf&) const; void serde_read(iobuf_parser&, const serde::header&); diff --git a/src/v/security/audit/audit_log_manager.cc b/src/v/security/audit/audit_log_manager.cc index 22fd4b74abaf3..5cc85990479e5 100644 --- a/src/v/security/audit/audit_log_manager.cc +++ b/src/v/security/audit/audit_log_manager.cc @@ -50,31 +50,6 @@ namespace security::audit { -std::ostream& operator<<(std::ostream& os, event_type t) { - switch (t) { - case event_type::management: - return os << "management"; - case event_type::produce: - return os << "produce"; - case event_type::consume: - return os << "consume"; - case event_type::describe: - return os << "describe"; - case event_type::heartbeat: - return os << "heartbeat"; - case event_type::authenticate: - return os << "authenticate"; - case event_type::admin: - return os << "admin"; - case event_type::schema_registry: - return os << "schema_registry"; - case event_type::unknown: - return os << "unknown"; - default: - return os << "invalid"; - } -} - /// audit_log_manager void audit_log_manager::set_enabled_events() { diff --git a/src/v/security/audit/schemas/schemas.h b/src/v/security/audit/schemas/schemas.h index 096a2ee96ef27..3bf6165f847fb 100644 --- a/src/v/security/audit/schemas/schemas.h +++ b/src/v/security/audit/schemas/schemas.h @@ -9,6 +9,7 @@ */ #pragma once +#include "base/format_to.h" #include "config/node_config.h" #include "reflection/type_traits.h" #include "security/audit/schemas/types.h" @@ -56,8 +57,7 @@ class ocsf_base_impl { virtual class_uid get_class_uid() const = 0; virtual type_uid get_type_uid() const = 0; -private: - friend std::ostream& operator<<(std::ostream&, const ocsf_base_impl&); + fmt::iterator format_to(fmt::iterator it) const; }; template @@ -218,3 +218,18 @@ class ocsf_base_event : public ocsf_base_impl { } }; } // namespace security::audit + +template<> +struct fmt::formatter> { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format( + const std::unique_ptr& v, + fmt::format_context& ctx) const { + if (v) { + return fmt::format_to(ctx.out(), "{}", *v); + } + return fmt::format_to(ctx.out(), "{{nullptr}}"); + } +}; diff --git a/src/v/security/audit/schemas/types.h b/src/v/security/audit/schemas/types.h index a7b984aec9245..362dc9840c7eb 100644 --- a/src/v/security/audit/schemas/types.h +++ b/src/v/security/audit/schemas/types.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/chunked_vector.h" #include "container/json.h" @@ -47,7 +48,23 @@ enum class category_uid : uint8_t { application_activity = 6 }; -std::ostream& operator<<(std::ostream&, const category_uid&); +inline fmt::iterator format_to(category_uid uid, fmt::iterator out) { + switch (uid) { + case category_uid::system_activity: + return fmt::format_to(out, "system_activity"); + case category_uid::findings: + return fmt::format_to(out, "findings"); + case category_uid::iam: + return fmt::format_to(out, "iam"); + case category_uid::network_activity: + return fmt::format_to(out, "network_activity"); + case category_uid::discovery: + return fmt::format_to(out, "discovery"); + case category_uid::application_activity: + return fmt::format_to(out, "application_activity"); + } + return fmt::format_to(out, ""); +} // Defines the class of the event // https://schema.ocsf.io/ @@ -86,7 +103,75 @@ enum class class_uid : uint16_t { web_resource_access_activity = 6004 }; -std::ostream& operator<<(std::ostream&, const class_uid&); +inline fmt::iterator format_to(class_uid uid, fmt::iterator out) { + switch (uid) { + case class_uid::file_system_activity: + return fmt::format_to(out, "file_system_activity"); + case class_uid::kernel_extension_activity: + return fmt::format_to(out, "kernel_extension_activity"); + case class_uid::kernel_activity: + return fmt::format_to(out, "kernel_activity"); + case class_uid::memory_activity: + return fmt::format_to(out, "memory_activity"); + case class_uid::module_activity: + return fmt::format_to(out, "module_activity"); + case class_uid::scheduled_job_activity: + return fmt::format_to(out, "scheduled_job_activity"); + case class_uid::process_activity: + return fmt::format_to(out, "process_activity"); + case class_uid::security_finding: + return fmt::format_to(out, "security_finding"); + case class_uid::account_change: + return fmt::format_to(out, "account_change"); + case class_uid::authentication: + return fmt::format_to(out, "authentication"); + case class_uid::authorize_session: + return fmt::format_to(out, "authorize_session"); + case class_uid::entity_management: + return fmt::format_to(out, "entity_management"); + case class_uid::user_access_management: + return fmt::format_to(out, "user_access_management"); + case class_uid::group_management: + return fmt::format_to(out, "group_management"); + case class_uid::network_activity: + return fmt::format_to(out, "network_activity"); + case class_uid::http_activity: + return fmt::format_to(out, "http_activity"); + case class_uid::dns_activity: + return fmt::format_to(out, "dns_activity"); + case class_uid::dhcp_activity: + return fmt::format_to(out, "dhcp_activity"); + case class_uid::rdp_activity: + return fmt::format_to(out, "rdp_activity"); + case class_uid::smb_activity: + return fmt::format_to(out, "smb_activity"); + case class_uid::ssh_activity: + return fmt::format_to(out, "ssh_activity"); + case class_uid::ftp_activity: + return fmt::format_to(out, "ftp_activity"); + case class_uid::email_activity: + return fmt::format_to(out, "email_activity"); + case class_uid::network_file_activity: + return fmt::format_to(out, "network_file_activity"); + case class_uid::email_file_activity: + return fmt::format_to(out, "email_file_activity"); + case class_uid::email_url_activity: + return fmt::format_to(out, "email_url_activity"); + case class_uid::device_inventory_info: + return fmt::format_to(out, "device_inventory_info"); + case class_uid::device_config_state: + return fmt::format_to(out, "device_config_state"); + case class_uid::web_resource_activity: + return fmt::format_to(out, "web_resource_activity"); + case class_uid::application_lifecycle: + return fmt::format_to(out, "application_lifecycle"); + case class_uid::api_activity: + return fmt::format_to(out, "api_activity"); + case class_uid::web_resource_access_activity: + return fmt::format_to(out, "web_resource_access_activity"); + } + return fmt::format_to(out, ""); +} // Severity of the event // Each class defines the same severity fields diff --git a/src/v/security/audit/schemas/utils.cc b/src/v/security/audit/schemas/utils.cc index 0d3e07d27249c..e142ec9067239 100644 --- a/src/v/security/audit/schemas/utils.cc +++ b/src/v/security/audit/schemas/utils.cc @@ -269,14 +269,14 @@ actor result_to_actor(const security::auth_result& result) { } else if (result.acl.has_value() || result.resource_pattern.has_value()) { ss::sstring desc; if (result.acl.has_value()) { - desc += fmt::format("acl: {}", result.acl.value()); + desc += fmt::format("acl: {}", result.acl.value().get()); } if (result.resource_pattern.has_value()) { if (!desc.empty()) { desc += ", "; } desc += fmt::format( - "resource: {}", result.resource_pattern.value()); + "resource: {}", result.resource_pattern.value().get()); } policy.desc = std::move(desc); @@ -298,114 +298,14 @@ api_activity::activity_id http_method_to_activity_id(std::string_view method) { .default_match(api_activity::activity_id::unknown); } -std::ostream& operator<<(std::ostream& os, audit_resource_type type) { - switch (type) { - case audit_resource_type::topic: - return os << "topic"; - case audit_resource_type::group: - return os << "group"; - case audit_resource_type::cluster: - return os << "cluster"; - case audit_resource_type::transactional_id: - return os << "transactional_id"; - case audit_resource_type::acl_binding: - return os << "acl_binding"; - case audit_resource_type::acl_binding_filter: - return os << "acl_binding_filter"; - } -} - -std::ostream& operator<<(std::ostream& os, const category_uid& uid) { - switch (uid) { - case category_uid::system_activity: - return os << "system_activity"; - case category_uid::findings: - return os << "findings"; - case category_uid::iam: - return os << "iam"; - case category_uid::network_activity: - return os << "network_activity"; - case category_uid::discovery: - return os << "discovery"; - case category_uid::application_activity: - return os << "application_activity"; - } -} - -std::ostream& operator<<(std::ostream& os, const class_uid& uid) { - switch (uid) { - case class_uid::file_system_activity: - return os << "file_system_activity"; - case class_uid::kernel_extension_activity: - return os << "kernel_extension_activity"; - case class_uid::kernel_activity: - return os << "kernel_activity"; - case class_uid::memory_activity: - return os << "memory_activity"; - case class_uid::module_activity: - return os << "module_activity"; - case class_uid::scheduled_job_activity: - return os << "scheduled_job_activity"; - case class_uid::process_activity: - return os << "process_activity"; - case class_uid::security_finding: - return os << "security_finding"; - case class_uid::account_change: - return os << "account_change"; - case class_uid::authentication: - return os << "authentication"; - case class_uid::authorize_session: - return os << "authorize_session"; - case class_uid::entity_management: - return os << "entity_management"; - case class_uid::user_access_management: - return os << "user_access_management"; - case class_uid::group_management: - return os << "group_management"; - case class_uid::network_activity: - return os << "network_activity"; - case class_uid::http_activity: - return os << "http_activity"; - case class_uid::dns_activity: - return os << "dns_activity"; - case class_uid::dhcp_activity: - return os << "dhcp_activity"; - case class_uid::rdp_activity: - return os << "rdp_activity"; - case class_uid::smb_activity: - return os << "smb_activity"; - case class_uid::ssh_activity: - return os << "ssh_activity"; - case class_uid::ftp_activity: - return os << "ftp_activity"; - case class_uid::email_activity: - return os << "email_activity"; - case class_uid::network_file_activity: - return os << "network_file_activity"; - case class_uid::email_file_activity: - return os << "email_file_activity"; - case class_uid::email_url_activity: - return os << "email_url_activity"; - case class_uid::device_inventory_info: - return os << "device_inventory_info"; - case class_uid::device_config_state: - return os << "device_config_state"; - case class_uid::web_resource_activity: - return os << "web_resource_activity"; - case class_uid::application_lifecycle: - return os << "application_lifecycle"; - case class_uid::api_activity: - return os << "api_activity"; - case class_uid::web_resource_access_activity: - return os << "web_resource_access_activity"; - } -} - -std::ostream& operator<<(std::ostream& os, const ocsf_base_impl& impl) { - return os << "{category: " << impl.get_category_uid() - << ", class: " << impl.get_class_uid() - << ", type_uid: " << impl.get_type_uid()() - << ", detail: " << impl.api_info() << "}"; +fmt::iterator ocsf_base_impl::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{category: {}, class: {}, type_uid: {}, detail: {}}}", + get_category_uid(), + get_class_uid(), + get_type_uid()(), + api_info()); } event_type kafka_api_to_event_type(kafka::api_key key) { diff --git a/src/v/security/audit/schemas/utils.h b/src/v/security/audit/schemas/utils.h index d67ca432f494a..dc66868c77860 100644 --- a/src/v/security/audit/schemas/utils.h +++ b/src/v/security/audit/schemas/utils.h @@ -39,6 +39,24 @@ enum class audit_resource_type : int8_t { acl_binding_filter }; +inline fmt::iterator format_to(audit_resource_type type, fmt::iterator out) { + switch (type) { + case audit_resource_type::topic: + return fmt::format_to(out, "topic"); + case audit_resource_type::group: + return fmt::format_to(out, "group"); + case audit_resource_type::cluster: + return fmt::format_to(out, "cluster"); + case audit_resource_type::transactional_id: + return fmt::format_to(out, "transactional_id"); + case audit_resource_type::acl_binding: + return fmt::format_to(out, "acl_binding"); + case audit_resource_type::acl_binding_filter: + return fmt::format_to(out, "acl_binding_filter"); + } + return fmt::format_to(out, ""); +} + template timestamp_t create_timestamp_t(std::chrono::time_point time_point) { return timestamp_t( @@ -57,8 +75,6 @@ api_activity_unmapped unmapped_data(const security::auth_result& auth_result); actor result_to_actor(const security::auth_result& result); -std::ostream& operator<<(std::ostream&, audit_resource_type); - template concept AuditableResource = std::is_same_v || std::is_same_v diff --git a/src/v/security/audit/types.h b/src/v/security/audit/types.h index 8cff251dd7ab4..497c167941798 100644 --- a/src/v/security/audit/types.h +++ b/src/v/security/audit/types.h @@ -8,6 +8,7 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #pragma once +#include "base/format_to.h" #include "strings/string_switch.h" #include @@ -27,7 +28,31 @@ enum class event_type : std::uint8_t { num_elements }; -std::ostream& operator<<(std::ostream&, event_type); +inline fmt::iterator format_to(event_type t, fmt::iterator out) { + switch (t) { + case event_type::management: + return fmt::format_to(out, "management"); + case event_type::produce: + return fmt::format_to(out, "produce"); + case event_type::consume: + return fmt::format_to(out, "consume"); + case event_type::describe: + return fmt::format_to(out, "describe"); + case event_type::heartbeat: + return fmt::format_to(out, "heartbeat"); + case event_type::authenticate: + return fmt::format_to(out, "authenticate"); + case event_type::admin: + return fmt::format_to(out, "admin"); + case event_type::schema_registry: + return fmt::format_to(out, "schema_registry"); + case event_type::unknown: + return fmt::format_to(out, "unknown"); + case event_type::num_elements: + return fmt::format_to(out, "num_elements"); + } + return fmt::format_to(out, "invalid"); +} inline event_type string_to_event_type(const std::string_view s) { return string_switch(s) diff --git a/src/v/security/authorizer.cc b/src/v/security/authorizer.cc index 2c2f0681bcde6..8f77bef058861 100644 --- a/src/v/security/authorizer.cc +++ b/src/v/security/authorizer.cc @@ -137,29 +137,6 @@ authorizer::reset_bindings(const chunked_vector& bindings) { acl_store& authorizer::store() & { return *_store; } const acl_store& authorizer::store() const& { return *_store; } -std::ostream& operator<<(std::ostream& os, const auth_result& a) { - fmt::print( - os, - "{{authorized:{}, authorization_disabled:{}, is_superuser:{}, " - "operation: {}, empty_matches:{}, principal:{}, role:{}, host:{}, " - "resource_type:{}, " - "resource_name:{}, resource_pattern:{}, acl:{}}}", - a.authorized, - a.authorization_disabled, - a.is_superuser, - a.operation, - a.empty_matches, - a.principal, - a.role, - a.host, - a.resource_type, - a.resource_name, - a.resource_pattern, - a.acl); - - return os; -} - template auth_result authorizer::authorized( const T& resource_name, diff --git a/src/v/security/authorizer.h b/src/v/security/authorizer.h index c96dc8b360950..09255aa005b54 100644 --- a/src/v/security/authorizer.h +++ b/src/v/security/authorizer.h @@ -10,6 +10,7 @@ */ #pragma once #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "base/vlog.h" #include "config/property.h" @@ -23,8 +24,6 @@ #include #include -#include - namespace security { // Flag to tell the authorizer that a superuser is required for the operation @@ -67,7 +66,36 @@ struct auth_result { // If found, the group that was matched to provide authz decision std::optional group; - friend std::ostream& operator<<(std::ostream& os, const auth_result& a); + fmt::iterator format_to(fmt::iterator it) const { + it = fmt::format_to( + it, + "{{authorized:{}, authorization_disabled:{}, is_superuser:{}, " + "operation: {}, empty_matches:{}, principal:{}, role:{}, host:{}, " + "resource_type:{}, " + "resource_name:{}, resource_pattern:", + authorized, + authorization_disabled, + is_superuser, + operation, + empty_matches, + principal, + role, + host, + resource_type, + resource_name); + if (resource_pattern) { + it = fmt::format_to(it, "{}", resource_pattern->get()); + } else { + it = fmt::format_to(it, "none"); + } + it = fmt::format_to(it, ", acl:"); + if (acl) { + it = fmt::format_to(it, "{}", acl->get()); + } else { + it = fmt::format_to(it, "none"); + } + return fmt::format_to(it, "}}"); + } explicit operator bool() const noexcept { return is_authorized(); } diff --git a/src/v/security/config.h b/src/v/security/config.h index 5da255b85e664..4fac1814152a5 100644 --- a/src/v/security/config.h +++ b/src/v/security/config.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -50,6 +51,9 @@ constexpr std::string_view to_string_view(nested_group_behavior b) { } return "unknown"; } +inline fmt::iterator format_to(nested_group_behavior b, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(b)); +} static constexpr auto acceptable_nested_group_behavior_values() { return std::to_array( @@ -57,7 +61,6 @@ static constexpr auto acceptable_nested_group_behavior_values() { to_string_view(nested_group_behavior::suffix)}); } -std::ostream& operator<<(std::ostream& os, nested_group_behavior b); std::istream& operator>>(std::istream& is, nested_group_behavior& b); std::optional diff --git a/src/v/security/config_rcl.cc b/src/v/security/config_rcl.cc index e939fc8599dfd..66f5c590351e4 100644 --- a/src/v/security/config_rcl.cc +++ b/src/v/security/config_rcl.cc @@ -130,10 +130,6 @@ validate_kerberos_mapping_rules(const std::vector& r) noexcept { } namespace oidc { -std::ostream& operator<<(std::ostream& os, const parsed_url& url) { - fmt::print(os, "{}://{}:{}{}", url.scheme, url.host, url.port, url.target); - return os; -} result parse_url(std::string_view url_view) { parsed_url result; auto url = ada::parse(url_view); @@ -238,13 +234,6 @@ validate_principal_mapping_rule(const ss::sstring& rule) { return std::nullopt; } -auto format_as(nested_group_behavior b) { return to_string_view(b); } - -std::ostream& operator<<(std::ostream& os, nested_group_behavior b) { - os << to_string_view(b); - return os; -} - std::istream& operator>>(std::istream& is, nested_group_behavior& b) { ss::sstring s; is >> s; diff --git a/src/v/security/credential.cc b/src/v/security/credential.cc deleted file mode 100644 index ede048f1fddb0..0000000000000 --- a/src/v/security/credential.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2021 Redpanda Data, Inc. - * - * Use of this software is governed by the Business Source License - * included in the file licenses/BSL.md - * - * As of the Change Date specified in that file, in accordance with - * the Business Source License, use of this software will be governed - * by the Apache License, Version 2.0 - */ -#include "security/ephemeral_credential.h" -#include "security/scram_credential.h" - -#include - -namespace security { - -std::ostream& operator<<(std::ostream& os, const scram_credential&) { - // NOTE: this stream is intentially left minimal to err away from exposing - // anything that may be useful for an attacker to use. - return os << "{scram_credential}"; -} - -std::ostream& operator<<(std::ostream& os, const ephemeral_credential& c) { - fmt::print(os, "principal: {}, user: {}", c.principal(), c.user()); - return os; -} - -} // namespace security diff --git a/src/v/security/ephemeral_credential.h b/src/v/security/ephemeral_credential.h index 22d99b7a2148a..cfe0d5f09919a 100644 --- a/src/v/security/ephemeral_credential.h +++ b/src/v/security/ephemeral_credential.h @@ -9,13 +9,12 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "security/acl.h" #include "security/types.h" #include -#include - namespace security { /* @@ -41,12 +40,14 @@ class ephemeral_credential { const credential_password& password() const { return _password; } const ss::sstring& mechanism() const { return _mechanism; } + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "principal: {}, user: {}", _principal, _user); + } + private: friend bool operator==( const ephemeral_credential&, const ephemeral_credential&) = default; - friend std::ostream& operator<<(std::ostream&, const ephemeral_credential&); - acl_principal _principal; credential_user _user; credential_password _password; diff --git a/src/v/security/gssapi_authenticator.cc b/src/v/security/gssapi_authenticator.cc index db9b4c5d06352..0b56c088bde2a 100644 --- a/src/v/security/gssapi_authenticator.cc +++ b/src/v/security/gssapi_authenticator.cc @@ -34,25 +34,6 @@ namespace security { -std::ostream& -operator<<(std::ostream& os, const gssapi_authenticator::state s) { - using state = gssapi_authenticator::state; - switch (s) { - case state::init: - return os << "init"; - case state::more: - return os << "more"; - case state::ssfcap: - return os << "ssfcap"; - case state::ssfreq: - return os << "ssfreq"; - case state::complete: - return os << "complete"; - case state::failed: - return os << "failed"; - } -} - static void display_status_1(std::string_view m, OM_uint32 code, int type) { while (true) { OM_uint32 msg_ctx{}; diff --git a/src/v/security/gssapi_authenticator.h b/src/v/security/gssapi_authenticator.h index c262bf57c3640..acd45c1f1f2a4 100644 --- a/src/v/security/gssapi_authenticator.h +++ b/src/v/security/gssapi_authenticator.h @@ -8,6 +8,7 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #pragma once +#include "base/format_to.h" #include "security/acl.h" #include "security/gssapi_principal_mapper.h" #include "security/sasl_authentication.h" @@ -25,6 +26,25 @@ class gssapi_authenticator final : public sasl_mechanism { public: enum class state { init = 0, more, ssfcap, ssfreq, complete, failed }; + + friend fmt::iterator format_to(state s, fmt::iterator out) { + switch (s) { + case state::init: + return fmt::format_to(out, "init"); + case state::more: + return fmt::format_to(out, "more"); + case state::ssfcap: + return fmt::format_to(out, "ssfcap"); + case state::ssfreq: + return fmt::format_to(out, "ssfreq"); + case state::complete: + return fmt::format_to(out, "complete"); + case state::failed: + return fmt::format_to(out, "failed"); + } + return fmt::format_to(out, ""); + } + static constexpr const char* name = "GSSAPI"; gssapi_authenticator( @@ -57,9 +77,6 @@ class gssapi_authenticator final : public sasl_mechanism { const char* mechanism_name() const override { return name; } private: - friend std::ostream& - operator<<(std::ostream& os, const gssapi_authenticator::state s); - ssx::singleton_thread_worker& _worker; security::acl_principal _principal; std::optional _session_expiry; diff --git a/src/v/security/gssapi_principal_mapper.cc b/src/v/security/gssapi_principal_mapper.cc index 5857149ae34e9..a3f687383611e 100644 --- a/src/v/security/gssapi_principal_mapper.cc +++ b/src/v/security/gssapi_principal_mapper.cc @@ -17,7 +17,7 @@ #include #include -#include +#include #include @@ -129,16 +129,6 @@ std::optional gssapi_principal_mapper::apply( return std::nullopt; } -std::ostream& operator<<(std::ostream& os, const gssapi_name& n) { - fmt::print(os, "{}", n); - return os; -} - -std::ostream& operator<<(std::ostream& os, const gssapi_principal_mapper& m) { - fmt::print(os, "{}", m); - return os; -} - } // namespace security // explicit instantiations so as to avoid bringing in in the diff --git a/src/v/security/gssapi_principal_mapper.h b/src/v/security/gssapi_principal_mapper.h index 0532f730fc1ae..0eefee179c0ea 100644 --- a/src/v/security/gssapi_principal_mapper.h +++ b/src/v/security/gssapi_principal_mapper.h @@ -37,8 +37,6 @@ class gssapi_name { private: friend struct fmt::formatter; - friend std::ostream& operator<<(std::ostream& os, const gssapi_name& n); - ss::sstring _primary; ss::sstring _host_name; ss::sstring _realm; @@ -60,9 +58,6 @@ class gssapi_principal_mapper { private: friend struct fmt::formatter; - friend std::ostream& - operator<<(std::ostream& os, const gssapi_principal_mapper& p); - config::binding> _principal_to_local_rules_binding; std::vector _rules; }; @@ -73,7 +68,9 @@ template<> struct fmt::formatter { using type = security::gssapi_name; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator @@ -84,7 +81,9 @@ template<> struct fmt::formatter { using type = security::gssapi_principal_mapper; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator diff --git a/src/v/security/gssapi_rule.cc b/src/v/security/gssapi_rule.cc index bb37dd0105eaf..42bd0aa77d22e 100644 --- a/src/v/security/gssapi_rule.cc +++ b/src/v/security/gssapi_rule.cc @@ -245,11 +245,6 @@ ss::sstring gssapi_rule::replace_substitution( return replace_value; } -std::ostream& operator<<(std::ostream& os, const gssapi_rule& r) { - fmt::print(os, "{}", r); - return os; -} - } // namespace security template<> diff --git a/src/v/security/gssapi_rule.h b/src/v/security/gssapi_rule.h index a04169313d49b..f66fa94cfc5e2 100644 --- a/src/v/security/gssapi_rule.h +++ b/src/v/security/gssapi_rule.h @@ -52,8 +52,6 @@ class gssapi_rule { private: friend struct fmt::formatter; - friend std::ostream& operator<<(std::ostream& os, const gssapi_rule& r); - static std::optional replace_parameters( std::string_view format, std::vector params); static ss::sstring replace_substitution( @@ -88,7 +86,9 @@ template<> struct fmt::formatter { using type = security::gssapi_rule; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator diff --git a/src/v/security/jwt.h b/src/v/security/jwt.h index 13599cdeb87b7..efebbeefff822 100644 --- a/src/v/security/jwt.h +++ b/src/v/security/jwt.h @@ -10,14 +10,15 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "base/oncore.h" #include "base/outcome.h" #include "base/type_traits.h" #include "container/chunked_vector.h" #include "crypto/crypto.h" #include "json/document.h" -#include "json/ostreamwrapper.h" #include "json/pointer.h" +#include "json/stringbuffer.h" #include "json/writer.h" #include "security/oidc_error.h" #include "strings/string_switch.h" @@ -30,7 +31,6 @@ #include #include -#include #include #include @@ -358,19 +358,20 @@ class jwt { }); } -private: - friend std::ostream& operator<<(std::ostream& os, const jwt& jwt) { - const auto write = [](std::ostream& os, const auto& doc) { - json::OStreamWrapper osw(os); - json::Writer h{osw}; - doc.Accept(h); + fmt::iterator format_to(fmt::iterator it) const { + const auto write = [](json::StringBuffer& sb, const auto& doc) { + json::Writer w{sb}; + doc.Accept(w); }; - write(os, jwt._header); - os << '.'; - write(os, jwt._payload); - return os; + json::StringBuffer sb; + write(sb, _header); + sb.Put('.'); + write(sb, _payload); + return fmt::format_to( + it, "{}", std::string_view{sb.GetString(), sb.GetSize()}); } +private: jwt(json::Document header, json::Document payload) : _header{std::move(header)} , _payload{std::move(payload)} {} diff --git a/src/v/security/krb5.h b/src/v/security/krb5.h index e65da8061e871..99256b684c48e 100644 --- a/src/v/security/krb5.h +++ b/src/v/security/krb5.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include "thirdparty/krb5/krb5.h" @@ -24,9 +25,8 @@ struct error_impl { ::krb5_error_code ec{0}; ss::sstring msg; - friend std::ostream& operator<<(std::ostream& os, const error_impl& impl) { - os << impl.msg << " (ec: " << impl.ec << ")"; - return os; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{} (ec: {})", msg, ec); } }; diff --git a/src/v/security/license.cc b/src/v/security/license.cc index 22c0e642dd2ef..2fd3901f6518a 100644 --- a/src/v/security/license.cc +++ b/src/v/security/license.cc @@ -255,11 +255,6 @@ ss::sstring calculate_sha256_checksum(std::string_view raw_license) { } // namespace -std::ostream& operator<<(std::ostream& os, const license& lic) { - fmt::print(os, "{}", lic); - return os; -} - license make_license(std::string_view raw_license) { try { license lc{}; diff --git a/src/v/security/license.h b/src/v/security/license.h index 42500dc6268e2..a0862337629d5 100644 --- a/src/v/security/license.h +++ b/src/v/security/license.h @@ -101,8 +101,6 @@ struct license friend struct fmt::formatter; friend bool operator==(const license& a, const license& b) = default; - - friend std::ostream& operator<<(std::ostream& os, const license& lic); }; /// Returns a license or an exception indicating the reason why the method @@ -118,7 +116,9 @@ template<> struct formatter { using type = security::license; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator diff --git a/src/v/security/mtls.cc b/src/v/security/mtls.cc index db724e6ea31e5..44481ad82f8d9 100644 --- a/src/v/security/mtls.cc +++ b/src/v/security/mtls.cc @@ -12,7 +12,6 @@ #include "security/mtls.h" #include -#include #include #include @@ -27,11 +26,6 @@ parse_rules(std::optional> unparsed_rules); } // namespace detail -std::ostream& operator<<(std::ostream& os, const principal_mapper& p) { - fmt::print(os, "{}", p); - return os; -} - principal_mapper::principal_mapper( config::binding>> cb) : _binding(std::move(cb)) diff --git a/src/v/security/mtls.h b/src/v/security/mtls.h index 63d79f65405ab..f52998d42ed55 100644 --- a/src/v/security/mtls.h +++ b/src/v/security/mtls.h @@ -36,9 +36,6 @@ class principal_mapper { private: friend struct fmt::formatter; - friend std::ostream& - operator<<(std::ostream& os, const principal_mapper& p); - config::binding>> _binding; std::vector _rules; }; @@ -65,7 +62,9 @@ template<> struct fmt::formatter { using type = security::tls::principal_mapper; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator diff --git a/src/v/security/mtls_rule.cc b/src/v/security/mtls_rule.cc index 787be40f9b546..faaa5623191c4 100644 --- a/src/v/security/mtls_rule.cc +++ b/src/v/security/mtls_rule.cc @@ -11,7 +11,6 @@ #include "security/mtls_rule.h" #include -#include #include @@ -38,11 +37,6 @@ rule::rule( , _to_lower{to_lower} , _to_upper{to_upper} {} -std::ostream& operator<<(std::ostream& os, const rule& r) { - fmt::print(os, "{}", r); - return os; -} - } // namespace security::tls template<> diff --git a/src/v/security/mtls_rule.h b/src/v/security/mtls_rule.h index 50080fea4b372..4f808d14cf4f3 100644 --- a/src/v/security/mtls_rule.h +++ b/src/v/security/mtls_rule.h @@ -41,8 +41,6 @@ class rule { private: friend struct fmt::formatter; - friend std::ostream& operator<<(std::ostream& os, const rule& r); - std::regex _regex; std::optional _pattern; std::optional _replacement; @@ -57,7 +55,9 @@ template<> struct fmt::formatter { using type = security::tls::rule; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator diff --git a/src/v/security/oidc_authenticator.cc b/src/v/security/oidc_authenticator.cc index 077a88221af8f..26371808df684 100644 --- a/src/v/security/oidc_authenticator.cc +++ b/src/v/security/oidc_authenticator.cc @@ -159,18 +159,6 @@ authenticator::authenticate(std::string_view bearer_token) { return _impl->authenticate(bearer_token); } -std::ostream& operator<<(std::ostream& os, const sasl_authenticator::state s) { - using state = sasl_authenticator::state; - switch (s) { - case state::init: - return os << "init"; - case state::complete: - return os << "complete"; - case state::failed: - return os << "failed"; - } -} - sasl_authenticator::sasl_authenticator(service& service) : _authenticator{service} , _audit_user() {} diff --git a/src/v/security/oidc_authenticator.h b/src/v/security/oidc_authenticator.h index cd6eaacd7e01d..0b4917528452a 100644 --- a/src/v/security/oidc_authenticator.h +++ b/src/v/security/oidc_authenticator.h @@ -8,6 +8,7 @@ * https://github.com/redpanda-data/redpanda/blob/master/licenses/rcl.md */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "security/acl.h" #include "security/fwd.h" @@ -63,6 +64,19 @@ class authenticator { class sasl_authenticator final : public sasl_mechanism { public: enum class state { init = 0, complete, failed }; + + friend fmt::iterator format_to(state s, fmt::iterator out) { + switch (s) { + case state::init: + return fmt::format_to(out, "init"); + case state::complete: + return fmt::format_to(out, "complete"); + case state::failed: + return fmt::format_to(out, "failed"); + } + return fmt::format_to(out, ""); + } + static constexpr const char* name = "OAUTHBEARER"; explicit sasl_authenticator(oidc::service& service); @@ -95,9 +109,6 @@ class sasl_authenticator final : public sasl_mechanism { } private: - friend std::ostream& - operator<<(std::ostream& os, const sasl_authenticator::state s); - authenticator _authenticator; authentication_data _auth_data; security::audit::user _audit_user; diff --git a/src/v/security/oidc_error.h b/src/v/security/oidc_error.h index be7bd9e76c8f0..a83f2f6780d44 100644 --- a/src/v/security/oidc_error.h +++ b/src/v/security/oidc_error.h @@ -9,6 +9,8 @@ */ #pragma once +#include + #include namespace security::oidc { @@ -108,3 +110,10 @@ template<> struct is_error_code_enum : true_type {}; } // namespace std + +template<> +struct fmt::formatter : fmt::formatter { + auto format(security::oidc::errc e, fmt::format_context& ctx) const { + return fmt::formatter::format(static_cast(e), ctx); + } +}; diff --git a/src/v/security/oidc_url_parser.h b/src/v/security/oidc_url_parser.h index e021625d266a6..4e737b728ec50 100644 --- a/src/v/security/oidc_url_parser.h +++ b/src/v/security/oidc_url_parser.h @@ -9,13 +9,12 @@ */ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include -#include - namespace security::oidc { struct parsed_url { @@ -24,7 +23,10 @@ struct parsed_url { uint16_t port; ss::sstring target; friend bool operator==(const parsed_url&, const parsed_url&) = default; - friend std::ostream& operator<<(std::ostream&, const parsed_url&); + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}://{}:{}{}", scheme, host, port, target); + } }; result parse_url(std::string_view); diff --git a/src/v/security/role.cc b/src/v/security/role.cc index 2057dd954349f..34c18741ae5ec 100644 --- a/src/v/security/role.cc +++ b/src/v/security/role.cc @@ -32,25 +32,6 @@ role_member_type member_type_for_principal_type(security::principal_type p) { } } // namespace -std::ostream& operator<<(std::ostream& os, role_member_type t) { - switch (t) { - case role_member_type::user: - return os << "User"; - case role_member_type::group: - return os << "Group"; - } - __builtin_unreachable(); -} - -std::ostream& operator<<(std::ostream& os, const role_member_view& m) { - fmt::print(os, "{{{}}}:{{{}}}", m.type(), m.name()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const role_member& m) { - return os << role_member_view{m.type(), m.name()}; -} - role_member_view::role_member_view(const role_member& m) : _type(m.type()) , _name(m.name()) {} @@ -64,11 +45,6 @@ role_member role_member::from_principal(const security::acl_principal& p) { return role_member{role_member_view::from_principal(p)}; } -std::ostream& operator<<(std::ostream& os, const role& r) { - fmt::print(os, "role members: {{{}}}", fmt::join(r.members(), ",")); - return os; -} - security::acl_principal role::to_principal(std::string_view role_name) { return { security::principal_type::role, {role_name.data(), role_name.size()}}; diff --git a/src/v/security/role.h b/src/v/security/role.h index 3af8e849d0f45..14cda95cf4d6e 100644 --- a/src/v/security/role.h +++ b/src/v/security/role.h @@ -11,13 +11,13 @@ #pragma once #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "security/acl.h" #include "security/types.h" #include "serde/envelope.h" #include -#include #include namespace security { @@ -27,7 +27,15 @@ enum class role_member_type { group = 1, }; -std::ostream& operator<<(std::ostream& os, role_member_type t); +inline fmt::iterator format_to(role_member_type t, fmt::iterator out) { + switch (t) { + case role_member_type::user: + return fmt::format_to(out, "User"); + case role_member_type::group: + return fmt::format_to(out, "Group"); + } + __builtin_unreachable(); +} class role_member; @@ -60,10 +68,13 @@ class role_member_view { */ static role_member_view from_principal(const security::acl_principal& p); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{{}}}:{{{}}}", _type, _name); + } + private: friend bool operator==(const role_member_view&, const role_member_view&) = default; - friend std::ostream& operator<<(std::ostream&, const role_member_view&); template friend H AbslHashValue(H h, const role_member_view& e) { @@ -99,9 +110,12 @@ class role_member static role_member from_principal(const security::acl_principal& p); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", role_member_view{_type, _name}); + } + private: friend bool operator==(const role_member&, const role_member&) = default; - friend std::ostream& operator<<(std::ostream&, const role_member&); template friend H AbslHashValue(H h, const role_member& e) { @@ -142,13 +156,44 @@ class role static security::acl_principal_view to_principal_view(std::string_view role_name); + fmt::iterator format_to(fmt::iterator it) const { + it = fmt::format_to(it, "role members: {{"); + bool first = true; + for (const auto& m : _members) { + if (!first) { + it = fmt::format_to(it, ","); + } + it = fmt::format_to(it, "{}", m); + first = false; + } + return fmt::format_to(it, "}}"); + } + private: friend bool operator==(const role&, const role&) = default; - friend std::ostream& operator<<(std::ostream&, const role&); container_type _members; }; +} // namespace security + +/// role has begin()/end() so fmt sees it as a range. Disable that and provide +/// an explicit formatter that delegates to role::format_to(). +template<> +struct fmt::range_format_kind + : std::integral_constant {}; + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + auto format(const security::role& v, fmt::format_context& ctx) const { + return v.format_to(ctx.out()); + } +}; + +namespace security { /** * Require that some type 'T' provide the role_member{_view} interface. */ diff --git a/src/v/security/scram_algorithm.cc b/src/v/security/scram_algorithm.cc index 65f4b5a4cdc81..723a149d9883c 100644 --- a/src/v/security/scram_algorithm.cc +++ b/src/v/security/scram_algorithm.cc @@ -6,7 +6,6 @@ #include "ssx/sformat.h" #include "strings/utf8.h" #include "utils/base64.h" -#include "utils/to_string.h" #include @@ -260,12 +259,6 @@ bool client_first_message::token_authenticated() const { return false; } -std::ostream& operator<<(std::ostream& os, const client_first_message&) { - // NOTE: this stream is intentially left minimal to err away from exposing - // anything that may be useful for an attacker to use. - return os << "{client_first_message}"; -} - ss::sstring server_first_message::sasl_message() const { return ssx::sformat( "r={},s={},i={}", _nonce, bytes_to_base64(_salt), _iterations); @@ -299,12 +292,6 @@ ss::sstring client_final_message::msg_no_proof() const { return ssx::sformat("c={},r={}", bytes_to_base64(_channel_binding), _nonce); } -std::ostream& operator<<(std::ostream& os, const client_final_message&) { - // NOTE: this stream is intentially left minimal to err away from exposing - // anything that may be useful for an attacker to use. - return os << "{client_final_message}"; -} - ss::sstring server_final_message::sasl_message() const { if (_error) { return ssx::sformat("e={}", *_error); diff --git a/src/v/security/scram_algorithm.h b/src/v/security/scram_algorithm.h index 4d99f8353a12c..ca5dab003d933 100644 --- a/src/v/security/scram_algorithm.h +++ b/src/v/security/scram_algorithm.h @@ -9,7 +9,9 @@ * by the Apache License, Version 2.0 */ #pragma once + #include "absl/container/node_hash_map.h" +#include "base/format_to.h" #include "bytes/bytes.h" #include "bytes/random.h" #include "hashing/secure.h" @@ -75,9 +77,11 @@ class client_first_message { bool token_authenticated() const; -private: - friend std::ostream& operator<<(std::ostream&, const client_first_message&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{client_first_message}}"); + } +private: ss::sstring _authzid; ss::sstring _username; ss::sstring _nonce; @@ -148,9 +152,11 @@ class client_final_message { return ssx::sformat("{},p={}", msg_no_proof(), bytes_to_base64(_proof)); } -private: - friend std::ostream& operator<<(std::ostream&, const client_final_message&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{client_final_message}}"); + } +private: bytes _channel_binding; ss::sstring _nonce; ss::sstring _extensions; diff --git a/src/v/security/scram_credential.h b/src/v/security/scram_credential.h index c6ea348610d75..8b4b664b1564f 100644 --- a/src/v/security/scram_credential.h +++ b/src/v/security/scram_credential.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ #pragma once +#include "base/format_to.h" #include "bytes/bytes.h" #include "model/timestamp.h" #include "reflection/adl.h" @@ -16,8 +17,6 @@ #include "serde/envelope.h" #include "serde/rw/bytes.h" -#include - namespace security { enum class scram_algorithm_t { @@ -68,9 +67,13 @@ class scram_credential _salt, _server_key, _stored_key, _iterations, _password_set_at); } -private: - friend std::ostream& operator<<(std::ostream&, const scram_credential&); + fmt::iterator format_to(fmt::iterator it) const { + // NOTE: this is intentionally left minimal to err away from exposing + // anything that may be useful for an attacker to use. + return fmt::format_to(it, "{{scram_credential}}"); + } +private: bytes _salt; bytes _server_key; bytes _stored_key; diff --git a/src/v/security/tests/authorizer_test.cc b/src/v/security/tests/authorizer_test.cc index 23065fb51c7f4..0e0d68ca14479 100644 --- a/src/v/security/tests/authorizer_test.cc +++ b/src/v/security/tests/authorizer_test.cc @@ -19,7 +19,6 @@ #include #include -#include #include namespace security { diff --git a/src/v/security/tests/credential_store_test.cc b/src/v/security/tests/credential_store_test.cc index 2acd3a35e9819..bfc999bc4abb8 100644 --- a/src/v/security/tests/credential_store_test.cc +++ b/src/v/security/tests/credential_store_test.cc @@ -21,7 +21,6 @@ #include #include #include -#include namespace security { diff --git a/src/v/security/tests/role_test.cc b/src/v/security/tests/role_test.cc index a54892134d362..6f54521c359b2 100644 --- a/src/v/security/tests/role_test.cc +++ b/src/v/security/tests/role_test.cc @@ -11,7 +11,6 @@ #include "security/role.h" #include -#include namespace security { diff --git a/src/v/security/tests/scram_algorithm_test.cc b/src/v/security/tests/scram_algorithm_test.cc index e7c61a34d1e8c..67b131743ef5c 100644 --- a/src/v/security/tests/scram_algorithm_test.cc +++ b/src/v/security/tests/scram_algorithm_test.cc @@ -17,7 +17,6 @@ #include #include -#include namespace security { diff --git a/src/v/serde/avro/tests/data_generator.cc b/src/v/serde/avro/tests/data_generator.cc index f2833475c4758..1849edac96337 100644 --- a/src/v/serde/avro/tests/data_generator.cc +++ b/src/v/serde/avro/tests/data_generator.cc @@ -17,7 +17,6 @@ #include #include -#include namespace testing { static constexpr auto seconds_in_day = 24 * 60 * 60; diff --git a/src/v/serde/json/parser.h b/src/v/serde/json/parser.h index 12ac0ff572324..10e7b6eb5c1a8 100644 --- a/src/v/serde/json/parser.h +++ b/src/v/serde/json/parser.h @@ -11,12 +11,10 @@ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" -#include - #include -#include #include #include @@ -46,42 +44,38 @@ enum class token { eof, }; -inline constexpr std::string_view format_as(token t) { +inline fmt::iterator format_to(token t, fmt::iterator out) { switch (t) { case token::error: - return "error"; + return fmt::format_to(out, "error"); case token::value_null: - return "value_null"; + return fmt::format_to(out, "value_null"); case token::value_true: - return "value_true"; + return fmt::format_to(out, "value_true"); case token::value_false: - return "value_false"; + return fmt::format_to(out, "value_false"); case token::value_double: - return "value_double"; + return fmt::format_to(out, "value_double"); case token::value_int: - return "value_int"; + return fmt::format_to(out, "value_int"); case token::value_string: - return "value_string"; + return fmt::format_to(out, "value_string"); case token::start_object: - return "start_object"; + return fmt::format_to(out, "start_object"); case token::key: - return "key"; + return fmt::format_to(out, "key"); case token::end_object: - return "end_object"; + return fmt::format_to(out, "end_object"); case token::start_array: - return "start_array"; + return fmt::format_to(out, "start_array"); case token::end_array: - return "end_array"; + return fmt::format_to(out, "end_array"); case token::eof: - return "eof"; + return fmt::format_to(out, "eof"); } std::unreachable(); } -inline std::ostream& operator<<(std::ostream& os, token t) { - return os << format_as(t); -} - class parser { public: explicit parser(iobuf buf, parser_config config = {}); diff --git a/src/v/serde/protobuf/wire_format.h b/src/v/serde/protobuf/wire_format.h index ff761e622b39c..baf33035b0ede 100644 --- a/src/v/serde/protobuf/wire_format.h +++ b/src/v/serde/protobuf/wire_format.h @@ -106,7 +106,7 @@ void write_length(int32_t length, iobuf* out); template<> struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) + constexpr auto parse(format_parse_context& ctx) const -> format_parse_context::iterator { return ctx.begin(); } diff --git a/src/v/serde/rw/variant.h b/src/v/serde/rw/variant.h index a5d45c326b7f5..8ea7d407731d1 100644 --- a/src/v/serde/rw/variant.h +++ b/src/v/serde/rw/variant.h @@ -171,3 +171,20 @@ void tag_invoke( } } // namespace serde + +/// Formatter for serde::variant - visits the variant and formats the active +/// alternative. +template +struct fmt::formatter> { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + template + auto format(const serde::variant& v, Ctx& ctx) const { + return std::visit( + [&ctx](const auto& val) { + return fmt::format_to(ctx.out(), "{}", val); + }, + static_cast&>(v)); + } +}; diff --git a/src/v/ssx/sformat.h b/src/v/ssx/sformat.h index 3424816668476..24a7918fb7cc4 100644 --- a/src/v/ssx/sformat.h +++ b/src/v/ssx/sformat.h @@ -14,7 +14,6 @@ #include #include -#include #if FMT_VERSION < 90100 #error fmt library version is too old, must be >= 90100 diff --git a/src/v/storage/BUILD b/src/v/storage/BUILD index 9b76286e99e23..77f2963ca6cc3 100644 --- a/src/v/storage/BUILD +++ b/src/v/storage/BUILD @@ -76,7 +76,6 @@ redpanda_cc_library( "index_state.h", ], implementation_deps = [ - "//src/v/base", "//src/v/bytes:iobuf_parser", "//src/v/hashing:crc32c", "//src/v/hashing:xx", @@ -89,6 +88,7 @@ redpanda_cc_library( "@fmt", ], deps = [ + "//src/v/base", "//src/v/bytes:iobuf", "//src/v/container:chunked_vector", "//src/v/model", @@ -430,6 +430,7 @@ redpanda_cc_library( ], visibility = ["//visibility:public"], deps = [ + "//src/v/base", "//src/v/json", "//src/v/model", "@abseil-cpp//absl/container:flat_hash_map", diff --git a/src/v/storage/batch_cache.cc b/src/v/storage/batch_cache.cc index 10af3c917df07..24b6577266e3f 100644 --- a/src/v/storage/batch_cache.cc +++ b/src/v/storage/batch_cache.cc @@ -22,8 +22,6 @@ #include #include -#include - namespace storage { batch_cache::range::range(batch_cache_index& index) @@ -580,39 +578,4 @@ ss::future<> batch_cache::background_reclaimer::reclaim_loop() { co_return; } -std::ostream& -operator<<(std::ostream& os, const batch_cache::reclaim_options& opts) { - fmt::print( - os, - "growth window {} stable window {} min_size {} max_size {}", - opts.growth_window, - opts.stable_window, - opts.min_size, - opts.max_size); - return os; -} - -std::ostream& operator<<(std::ostream& o, const batch_cache& b) { - // NOTE: intrusive list have a O(N) for size. - // Do _not_ print size of _lru - return o << "{is_reclaiming:" << b.is_memory_reclaiming() - << ", size_bytes: " << b._size_bytes - << ", lru_empty:" << b._lru.empty() << "}"; -} -std::ostream& -operator<<(std::ostream& o, const batch_cache_index::read_result& c) { - o << "{batches:" << c.batches.size() << ", memory_usage:" << c.memory_usage - << ", next_batch:" << c.next_batch << ", next_cache_batch:"; - if (c.next_cached_batch) { - o << *c.next_cached_batch; - } else { - o << "nullopt"; - } - return o << "}"; -} -std::ostream& operator<<(std::ostream& o, const batch_cache_index& c) { - return o << "{cache_size=" << c._index.size() - << ", dirty tracker: " << c._dirty_tracker << "}"; -} - } // namespace storage diff --git a/src/v/storage/batch_cache.h b/src/v/storage/batch_cache.h index 3363e60c21ae3..ada6f66a9d5c8 100644 --- a/src/v/storage/batch_cache.h +++ b/src/v/storage/batch_cache.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "base/units.h" #include "base/vassert.h" #include "container/chunked_circular_buffer.h" @@ -117,6 +118,16 @@ class batch_cache { // background reclaimer settings ss::scheduling_group background_reclaimer_sg; size_t min_free_memory = 64_MiB; + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "growth window {} stable window {} min_size {} max_size {}", + growth_window, + stable_window, + min_size, + max_size); + } }; /* @@ -450,8 +461,15 @@ class batch_cache { background_reclaimer _background_reclaimer; resources::available_memory::deregister_holder _available_mem_deregister; - friend std::ostream& operator<<(std::ostream&, const reclaim_options&); - friend std::ostream& operator<<(std::ostream&, const batch_cache&); +public: + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{is_reclaiming:{}, size_bytes: {}, lru_empty:{}}}", + is_memory_reclaiming(), + _size_bytes, + _lru.empty()); + } }; class batch_cache_index { @@ -489,10 +507,8 @@ class batch_cache_index { bool clean() const { return _min == model::offset{}; } - friend std::ostream& - operator<<(std::ostream& o, const dirty_tracker& t) { - o << "{min: " << t._min << ", max: " << t._max << "}"; - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{min: {}, max: {}}}", _min, _max); } private: @@ -509,7 +525,16 @@ class batch_cache_index { model::offset next_batch; std::optional next_cached_batch; - friend std::ostream& operator<<(std::ostream&, const read_result&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{batches:{}, memory_usage:{}, next_batch:{}, " + "next_cache_batch:{}}}", + batches.size(), + memory_usage, + next_batch, + next_cached_batch); + } }; explicit batch_cache_index(batch_cache& cache) @@ -518,8 +543,10 @@ class batch_cache_index { lock_guard lk(*this); vassert( _dirty_tracker.clean(), - "Destroying batch_cache_index ({}) tracking dirty batches.", - *this); + "Destroying batch_cache_index (cache_size={}, dirty tracker: {}) " + "tracking dirty batches.", + _index.size(), + _dirty_tracker); std::for_each( _index.begin(), _index.end(), [this](index_type::value_type& e) { _cache->evict(std::move(e.second.range())); @@ -727,7 +754,14 @@ class batch_cache_index { dirty_tracker _dirty_tracker; - friend std::ostream& operator<<(std::ostream&, const batch_cache_index&); +public: + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{cache_size={}, dirty tracker: {}}}", + _index.size(), + _dirty_tracker); + } }; using batch_cache_index_ptr = std::unique_ptr; diff --git a/src/v/storage/batch_consumer_utils.h b/src/v/storage/batch_consumer_utils.h index b05b25c65cd10..3e101856d8171 100644 --- a/src/v/storage/batch_consumer_utils.h +++ b/src/v/storage/batch_consumer_utils.h @@ -152,8 +152,8 @@ class chained_batch_consumer : public storage::batch_consumer { co_return stop; } - void print(std::ostream& o) const override { - o << "chained_batch_consumer"; + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to(it, "chained_batch_consumer"); } private: diff --git a/src/v/storage/compacted_index.h b/src/v/storage/compacted_index.h index de528517ebf55..1a737b588c6a2 100644 --- a/src/v/storage/compacted_index.h +++ b/src/v/storage/compacted_index.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "bytes/bytes.h" #include "compaction/key.h" #include "model/fundamental.h" @@ -71,11 +72,15 @@ struct compacted_index { + sizeof(flags) + sizeof(crc) + sizeof(version); - friend std::ostream& - operator<<(std::ostream& o, const compacted_index::footer& f) { - return o << "{size:" << f.size << ", keys:" << f.keys - << ", flags:" << (uint32_t)f.flags << ", crc:" << f.crc - << ", version: " << (int)f.version << "}"; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{size:{}, keys:{}, flags:{}, crc:{}, version: {}}}", + size, + keys, + (uint32_t)flags, + crc, + (int)version); } }; @@ -136,7 +141,20 @@ struct compacted_index { }; }; -std::ostream& operator<<(std::ostream&, compacted_index::recovery_state); +inline fmt::iterator +format_to(compacted_index::recovery_state state, fmt::iterator out) { + switch (state) { + case compacted_index::recovery_state::index_missing: + return fmt::format_to(out, "index_missing"); + case compacted_index::recovery_state::already_compacted: + return fmt::format_to(out, "already_compacted"); + case compacted_index::recovery_state::index_needs_rebuild: + return fmt::format_to(out, "index_needs_rebuild"); + case compacted_index::recovery_state::index_recovered: + return fmt::format_to(out, "index_recovered"); + } + __builtin_unreachable(); +} [[gnu::always_inline]] inline compacted_index::footer_flags operator|(compacted_index::footer_flags a, compacted_index::footer_flags b) { diff --git a/src/v/storage/compacted_index_chunk_reader.cc b/src/v/storage/compacted_index_chunk_reader.cc index 278f0473310d9..ac05e686bb63e 100644 --- a/src/v/storage/compacted_index_chunk_reader.cc +++ b/src/v/storage/compacted_index_chunk_reader.cc @@ -175,8 +175,6 @@ compacted_index_chunk_reader::load_footer() { co_return _footer.value(); } -void compacted_index_chunk_reader::print(std::ostream& o) const { o << *this; } - bool compacted_index_chunk_reader::is_end_of_stream() const { return _end_of_stream || (_footer && _byte_index == _footer->size) || (_cursor && _cursor->eof()); @@ -249,20 +247,18 @@ compacted_index_chunk_reader::load_slice(model::timeout_clock::time_point t) { }); } -std::ostream& -operator<<(std::ostream& o, const compacted_index_chunk_reader& r) { - fmt::print( - o, +fmt::iterator compacted_index_chunk_reader::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{type:compacted_index_chunk_reader, _max_chunk_memory:{}, " "_data_size:{}, _footer:{}, active_cursor:{}, end_of_stream:{}, " "_byte_index:{}}}", - r._max_chunk_memory, - r._data_size, - r._footer, - (r._cursor ? "yes" : "no"), - r.is_end_of_stream(), - r._byte_index); - return o; + _max_chunk_memory, + _data_size, + _footer, + (_cursor ? "yes" : "no"), + is_end_of_stream(), + _byte_index); } } // namespace storage::internal diff --git a/src/v/storage/compacted_index_chunk_reader.h b/src/v/storage/compacted_index_chunk_reader.h index be7a268642e5d..158d19092375c 100644 --- a/src/v/storage/compacted_index_chunk_reader.h +++ b/src/v/storage/compacted_index_chunk_reader.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "storage/compacted_index_reader.h" #include @@ -38,8 +39,6 @@ class compacted_index_chunk_reader final : public compacted_index_reader::impl { void reset() final; - void print(std::ostream&) const final; - bool is_end_of_stream() const final; ss::future> @@ -58,8 +57,8 @@ class compacted_index_chunk_reader final : public compacted_index_reader::impl { std::optional> _cursor; ss::abort_source* _as; - friend std::ostream& - operator<<(std::ostream&, const compacted_index_chunk_reader&); +public: + fmt::iterator format_to(fmt::iterator it) const final; }; } // namespace storage::internal diff --git a/src/v/storage/compacted_index_reader.h b/src/v/storage/compacted_index_reader.h index 0b958b0523347..11bdbca94eaa2 100644 --- a/src/v/storage/compacted_index_reader.h +++ b/src/v/storage/compacted_index_reader.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "model/timeout_clock.h" #include "storage/compacted_index.h" #include "storage/fs_utils.h" @@ -50,7 +51,7 @@ class compacted_index_reader { virtual void reset() = 0; - virtual void print(std::ostream&) const = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; const ss::sstring filename() const { return path(); } const segment_full_path& path() const { return _path; } @@ -133,7 +134,9 @@ class compacted_index_reader { return _impl->load_footer(); } - void print(std::ostream& o) const { _impl->print(o); } + fmt::iterator format_to(fmt::iterator it) const { + return _impl->format_to(it); + } void reset() { _impl->reset(); } @@ -152,12 +155,6 @@ class compacted_index_reader { return _impl->for_each_async(std::move(f), timeout); } - friend std::ostream& - operator<<(std::ostream& o, const compacted_index_reader& r) { - r.print(o); - return o; - } - private: ss::shared_ptr _impl; }; diff --git a/src/v/storage/compacted_index_writer.h b/src/v/storage/compacted_index_writer.h index 322761f2785cf..88df2293059bf 100644 --- a/src/v/storage/compacted_index_writer.h +++ b/src/v/storage/compacted_index_writer.h @@ -10,6 +10,8 @@ */ #pragma once + +#include "base/format_to.h" #include "bytes/bytes.h" #include "compaction/key.h" #include "model/fundamental.h" @@ -74,17 +76,11 @@ class compacted_index_writer { virtual ss::future<> close() = 0; virtual void set_flag(compacted_index::footer_flags) = 0; - virtual void print(std::ostream&) const = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; const ss::sstring& filename() const { return _name; } virtual size_t size_bytes() const = 0; private: - friend std::ostream& - operator<<(std::ostream& o, const compacted_index_writer& c) { - c.print(o); - return o; - } - ss::sstring _name; }; diff --git a/src/v/storage/compaction_reducers.h b/src/v/storage/compaction_reducers.h index 41567b59213a5..0d9850efd5baf 100644 --- a/src/v/storage/compaction_reducers.h +++ b/src/v/storage/compaction_reducers.h @@ -272,15 +272,14 @@ class tx_reducer : public compaction_reducer { size_t num_aborted_txes{0}; size_t batches_processed{0}; - friend std::ostream& operator<<(std::ostream& os, const stats& s) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ batches processed: {}, aborted_txs: {}, " "discarded batches: {} }}", - s.batches_processed, - s.num_aborted_txes, - s.batches_discarded); - return os; + batches_processed, + num_aborted_txes, + batches_discarded); } }; diff --git a/src/v/storage/disk.cc b/src/v/storage/disk.cc index 33fca80c4e9f1..99924ef83407e 100644 --- a/src/v/storage/disk.cc +++ b/src/v/storage/disk.cc @@ -14,20 +14,18 @@ #include "utils/human.h" #include -#include namespace storage { -std::ostream& operator<<(std::ostream& o, const disk& d) { - fmt::print( - o, +fmt::iterator disk::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{path: {}, free: {}, total: {}, alert: {}, fsid: {}}}", - d.path, - human::bytes(d.free), - human::bytes(d.total), - d.alert, - d.fsid); - return o; + path, + human::bytes(free), + human::bytes(total), + alert, + fsid); } } // namespace storage diff --git a/src/v/storage/disk.h b/src/v/storage/disk.h index aee0284b0ee57..ac9996a9bdd6e 100644 --- a/src/v/storage/disk.h +++ b/src/v/storage/disk.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "serde/envelope.h" @@ -26,19 +27,16 @@ inline disk_space_alert max_severity(disk_space_alert a, disk_space_alert b) { return std::max(a, b); } -inline std::ostream& operator<<(std::ostream& o, const disk_space_alert d) { +inline fmt::iterator format_to(disk_space_alert d, fmt::iterator out) { switch (d) { case disk_space_alert::ok: - o << "ok"; - break; + return fmt::format_to(out, "ok"); case disk_space_alert::low_space: - o << "low_space"; - break; + return fmt::format_to(out, "low_space"); case disk_space_alert::degraded: - o << "degraded"; - break; + return fmt::format_to(out, "degraded"); } - return o; + return fmt::format_to(out, ""); } struct disk @@ -57,7 +55,7 @@ struct disk // to represent a disk not only for marshalling data to disk/network. unsigned long int fsid; - friend std::ostream& operator<<(std::ostream&, const disk&); + fmt::iterator format_to(fmt::iterator it) const; friend bool operator==(const disk&, const disk&) = default; }; diff --git a/src/v/storage/disk_log_appender.cc b/src/v/storage/disk_log_appender.cc index 2a14e9d5f909e..9f07d50893939 100644 --- a/src/v/storage/disk_log_appender.cc +++ b/src/v/storage/disk_log_appender.cc @@ -176,13 +176,17 @@ ss::future disk_log_appender::end_of_stream() { co_return retval; } -std::ostream& operator<<(std::ostream& o, const disk_log_appender& a) { - return o << "{offset_idx:" << a._idx - << ", active_segment:" << (a._seg_lock ? "yes" : "no") - << ", _bytes_left_in_segment:" << a._bytes_left_in_segment - << ", _base_offset:" << a._base_offset - << ", _last_offset:" << a._last_offset - << ", _last_term:" << a._last_term - << ", _byte_size:" << a._byte_size << "}"; +fmt::iterator disk_log_appender::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{offset_idx:{}, active_segment:{}, _bytes_left_in_segment:{}, " + "_base_offset:{}, _last_offset:{}, _last_term:{}, _byte_size:{}}}", + _idx, + (_seg_lock ? "yes" : "no"), + _bytes_left_in_segment, + _base_offset, + _last_offset, + _last_term, + _byte_size); } } // namespace storage diff --git a/src/v/storage/disk_log_appender.h b/src/v/storage/disk_log_appender.h index 003662d481db5..e925fb42810de 100644 --- a/src/v/storage/disk_log_appender.h +++ b/src/v/storage/disk_log_appender.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "storage/log_appender.h" #include @@ -58,7 +59,8 @@ class disk_log_appender final : public log_appender::impl { model::term_id _last_term; size_t _byte_size{0}; - friend std::ostream& operator<<(std::ostream&, const disk_log_appender&); +public: + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace storage diff --git a/src/v/storage/disk_log_impl.cc b/src/v/storage/disk_log_impl.cc index e0cca0e7ff9a3..3fb39d2ea72a2 100644 --- a/src/v/storage/disk_log_impl.cc +++ b/src/v/storage/disk_log_impl.cc @@ -158,9 +158,9 @@ class single_segment_reader final : public model::record_batch_reader::impl { ss::future<> finally() noexcept final { return _rdr.close(); } - void print(std::ostream& os) final { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to( + it, "storage::single_segment_reader for {}, config {}", _seg->filename(), _config); @@ -3796,20 +3796,15 @@ void disk_log_impl::wrote_stm_bytes(size_t byte_size) { storage_resources& disk_log_impl::resources() { return _manager.resources(); } -std::ostream& disk_log_impl::print(std::ostream& o) const { - fmt::print( - o, +fmt::iterator disk_log_impl::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{offsets: {}, is_closed: {}, segments: " "[{}], config: {}}}", offsets(), _closed, _segs, config()); - return o; -} - -std::ostream& operator<<(std::ostream& o, const disk_log_impl& d) { - return d.print(o); } ss::shared_ptr make_disk_backed_log( diff --git a/src/v/storage/disk_log_impl.h b/src/v/storage/disk_log_impl.h index 086018396d792..dd7e713820e93 100644 --- a/src/v/storage/disk_log_impl.h +++ b/src/v/storage/disk_log_impl.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "compaction/fwd.h" #include "features/feature_table.h" #include "model/fundamental.h" @@ -148,7 +149,7 @@ class disk_log_impl final : public log { std::optional index_batch_base_offset_lower_bound(model::offset o) const final; - std::ostream& print(std::ostream&) const final; + fmt::iterator format_to(fmt::iterator it) const final; // Must be called while _segments_rolling_lock is held. ss::future<> maybe_roll_unlocked(model::term_id, model::offset next_offset); @@ -333,7 +334,6 @@ class disk_log_impl final : public log { friend class disk_log_builder; // for tests friend ::storage_e2e_fixture; friend ::reupload_fixture; // for tests - friend std::ostream& operator<<(std::ostream& o, const disk_log_impl& d); ss::future make_unchecked_reader(local_log_reader_config); diff --git a/src/v/storage/file_sanitizer.h b/src/v/storage/file_sanitizer.h index 5579a900145bc..6fe57ae252b38 100644 --- a/src/v/storage/file_sanitizer.h +++ b/src/v/storage/file_sanitizer.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/vassert.h" #include "config/node_config.h" #include "ssx/sformat.h" @@ -46,9 +47,12 @@ struct sanitizer_op : name_op(std::move(operation)) , bt(ss::current_backtrace()) {} - friend std::ostream& operator<<(std::ostream& o, const sanitizer_op& s) { - return o << "{sanitizer_op: " << s.name_op << ", backtrace:\n" - << s.bt << "\n}"; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{sanitizer_op: {}, backtrace:\n{}\n}}", + name_op, + fmt_streamed(bt)); } }; @@ -431,7 +435,7 @@ class file_io_sanitizer final : public ss::file_impl { return "unknown"; } - return fmt::format("{}", _appender_ptr->_inflight); + return fmt::format("[{}]", fmt::join(_appender_ptr->_inflight, ", ")); } ss::sstring maybe_dump_appender_pending_flushes() const { @@ -439,7 +443,7 @@ class file_io_sanitizer final : public ss::file_impl { return "unknown"; } - return fmt::format("{}", _appender_ptr->_flush_ops); + return fmt::format("[{}]", fmt::join(_appender_ptr->_flush_ops, ", ")); } private: diff --git a/src/v/storage/file_sanitizer_types.cc b/src/v/storage/file_sanitizer_types.cc index 923cc581ec1b9..d75db66c5c909 100644 --- a/src/v/storage/file_sanitizer_types.cc +++ b/src/v/storage/file_sanitizer_types.cc @@ -179,36 +179,6 @@ file_sanitize_config::get_config_for_ntp(const model::ntp& ntp) const { return std::nullopt; } -std::ostream& operator<<(std::ostream& o, const ntp_sanitizer_config& cfg) { - o << "{sanitize_only=" << cfg.sanitize_only - << ", failure_injection=" << static_cast(cfg.finjection_cfg) << "}"; - - return o; -} - -std::ostream& operator<<(std::ostream& o, const file_sanitize_config& cfg) { - o << "{sanitize_only=" << cfg._sanitize_only - << ", ntps_with_failure_injection_count: " - << cfg._ntp_failure_configs.size() << "}"; - - return o; -} - -std::ostream& operator<<(std::ostream& o, failable_op_type op) { - switch (op) { - case failable_op_type::write: - return o << "write"; - case failable_op_type::falloc: - return o << "falloc"; - case failable_op_type::flush: - return o << "flush"; - case failable_op_type::truncate: - return o << "truncate"; - case failable_op_type::close: - return o << "close"; - } -} - std::istream& operator>>(std::istream& i, failable_op_type& op) { ss::sstring s; i >> s; @@ -237,7 +207,7 @@ make_finjector_file_config(std::filesystem::path config_path) { storage::stlog.warn, "Failed to read failure injection config file at {}: {}", config_path, - doc.GetParseError()); + static_cast(doc.GetParseError())); } try { diff --git a/src/v/storage/file_sanitizer_types.h b/src/v/storage/file_sanitizer_types.h index 9c289e72e09f8..50d8946026801 100644 --- a/src/v/storage/file_sanitizer_types.h +++ b/src/v/storage/file_sanitizer_types.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/flat_hash_map.h" +#include "base/format_to.h" #include "json/document.h" #include "model/fundamental.h" #include "model/record_batch_types.h" @@ -95,7 +96,23 @@ constexpr std::string_view failure_injector_schema = R"( )"; enum class failable_op_type : uint8_t { write, falloc, truncate, flush, close }; -std::ostream& operator<<(std::ostream& o, failable_op_type op); + +inline fmt::iterator format_to(failable_op_type op, fmt::iterator out) { + switch (op) { + case failable_op_type::write: + return fmt::format_to(out, "write"); + case failable_op_type::falloc: + return fmt::format_to(out, "falloc"); + case failable_op_type::truncate: + return fmt::format_to(out, "truncate"); + case failable_op_type::flush: + return fmt::format_to(out, "flush"); + case failable_op_type::close: + return fmt::format_to(out, "close"); + } + return fmt::format_to(out, "unknown"); +} + std::istream& operator>>(std::istream& o, failable_op_type op); struct failable_op_config { @@ -117,9 +134,15 @@ struct ntp_failure_injection_config { struct ntp_sanitizer_config { bool sanitize_only; std::optional finjection_cfg; -}; -std::ostream& operator<<(std::ostream& o, const ntp_sanitizer_config& cfg); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{sanitize_only={}, failure_injection={}}}", + sanitize_only, + static_cast(finjection_cfg)); + } +}; // Held by log manager class file_sanitize_config { @@ -130,8 +153,13 @@ class file_sanitize_config { std::optional get_config_for_ntp(const model::ntp&) const; - friend std::ostream& - operator<<(std::ostream& o, const file_sanitize_config& cfg); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{sanitize_only={}, ntps_with_failure_injection_count: {}}}", + _sanitize_only, + _ntp_failure_configs.size()); + } private: bool _sanitize_only{false}; diff --git a/src/v/storage/fs_utils.cc b/src/v/storage/fs_utils.cc index 54be4afc002d3..821543f7c678b 100644 --- a/src/v/storage/fs_utils.cc +++ b/src/v/storage/fs_utils.cc @@ -127,14 +127,4 @@ segment_full_path segment_full_path::to_staging() const { } } -std::ostream& operator<<(std::ostream& o, const partition_path& p) { - o << ss::format("{}_{}", p.ntp.path(), p.revision_id); - return o; -} - -std::ostream& operator<<(std::ostream& o, const segment_full_path& p) { - o << p.string(); - return o; -} - } // namespace storage diff --git a/src/v/storage/fs_utils.h b/src/v/storage/fs_utils.h index 6b05679fa6349..ffa2185017238 100644 --- a/src/v/storage/fs_utils.h +++ b/src/v/storage/fs_utils.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "model/fundamental.h" #include "storage/ntp_config.h" @@ -111,8 +112,12 @@ class partition_path { return std::filesystem::path(make_string()); } ss::sstring make_string() const; - friend std::ostream& operator<<(std::ostream&, const partition_path&); friend class segment_full_path; + +public: + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}_{}", ntp.path(), revision_id); + } }; class segment_full_path { @@ -215,9 +220,10 @@ class segment_full_path { , dir_part(dir_part) , file_part(file_part) {} - friend std::ostream& operator<<(std::ostream&, const segment_full_path&); +public: + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", string()); + } }; -std::ostream& operator<<(std::ostream&, const segment_full_path&); - } // namespace storage diff --git a/src/v/storage/index_state.cc b/src/v/storage/index_state.cc index 971df654ab88b..13b32c0f55efe 100644 --- a/src/v/storage/index_state.cc +++ b/src/v/storage/index_state.cc @@ -26,7 +26,6 @@ #include "utils/to_string.h" #include -#include namespace storage { @@ -169,14 +168,13 @@ void index_columns::pop_back(int n) { _position_index.pop_back_n(n); } -std::ostream& operator<<(std::ostream& o, const index_columns& s) { - fmt::print( - o, +fmt::iterator index_columns::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "index({}, {}, {})", - s._relative_offset_index.size(), - s._relative_time_index.size(), - s._position_index.size()); - return o; + _relative_offset_index.size(), + _relative_time_index.size(), + _position_index.size()); } offset_time_index::offset_time_index( @@ -217,11 +215,6 @@ index_state index_state::make_empty_index( return idx; } -std::ostream& operator<<(std::ostream& o, const index_state::entry& e) { - return o << "{offset:" << e.offset << ", time:" << e.timestamp - << ", filepos:" << e.filepos << "}"; -} - bool index_state::maybe_index( size_t accumulator, size_t step, @@ -332,27 +325,33 @@ bool index_state::maybe_index( return false; } -std::ostream& operator<<(std::ostream& o, const index_state& s) { - return o << "{header_bitflags:" << s.bitflags - << ", base_offset:" << s.base_offset - << ", max_offset:" << s.max_offset - << ", base_timestamp:" << s.base_timestamp - << ", max_timestamp:" << s.max_timestamp - << ", batch_timestamps_are_monotonic:" - << s.batch_timestamps_are_monotonic - << ", with_offset:" << s.with_offset - << ", non_data_timestamps:" << s.non_data_timestamps - << ", broker_timestamp:" << s.broker_timestamp - << ", num_compactible_records_appended:" - << s.num_compactible_records_appended - << ", clean_compact_timestamp:" << s.clean_compact_timestamp - << ", may_have_tombstone_records:" << s.may_have_tombstone_records - << ", self_compact_timestamp:" << s.self_compact_timestamp - << ", may_have_transaction_control_batches:" - << s.may_have_transaction_control_batches - << ", may_have_transaction_data_or_fence_batches:" - << s.may_have_transaction_data_or_fence_batches << ", " << s.index - << "}"; +fmt::iterator index_state::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{header_bitflags:{}, base_offset:{}, max_offset:{}, " + "base_timestamp:{}, max_timestamp:{}, " + "batch_timestamps_are_monotonic:{}, with_offset:{}, " + "non_data_timestamps:{}, broker_timestamp:{}, " + "num_compactible_records_appended:{}, clean_compact_timestamp:{}, " + "may_have_tombstone_records:{}, self_compact_timestamp:{}, " + "may_have_transaction_control_batches:{}, " + "may_have_transaction_data_or_fence_batches:{}, {}}}", + bitflags, + base_offset, + max_offset, + base_timestamp, + max_timestamp, + batch_timestamps_are_monotonic, + with_offset, + non_data_timestamps, + broker_timestamp, + num_compactible_records_appended, + clean_compact_timestamp, + may_have_tombstone_records, + self_compact_timestamp, + may_have_transaction_control_batches, + may_have_transaction_data_or_fence_batches, + index); } void index_state::serde_write(iobuf& out) const { diff --git a/src/v/storage/index_state.h b/src/v/storage/index_state.h index af475fc8a53f4..82b0c48073669 100644 --- a/src/v/storage/index_state.h +++ b/src/v/storage/index_state.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" #include "container/chunked_vector.h" #include "model/fundamental.h" @@ -76,7 +77,7 @@ class index_columns { friend bool operator==(const index_columns&, const index_columns&) = default; - friend std::ostream& operator<<(std::ostream&, const index_columns&); + fmt::iterator format_to(fmt::iterator it) const; private: chunked_vector _relative_offset_index; @@ -150,7 +151,15 @@ struct index_state model::offset offset; model::timestamp timestamp; size_t filepos; - friend std::ostream& operator<<(std::ostream&, const entry&); + + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{offset:{}, time:{}, filepos:{}}}", + offset, + timestamp, + filepos); + } }; index_state() = default; @@ -277,7 +286,7 @@ struct index_state friend bool operator==(const index_state&, const index_state&) = default; - friend std::ostream& operator<<(std::ostream&, const index_state&); + fmt::iterator format_to(fmt::iterator it) const; void serde_write(iobuf&) const; friend void read_nested(iobuf_parser&, index_state&, const size_t); diff --git a/src/v/storage/kvstore.cc b/src/v/storage/kvstore.cc index 5446e0b4b7544..0941f014f2c23 100644 --- a/src/v/storage/kvstore.cc +++ b/src/v/storage/kvstore.cc @@ -678,8 +678,8 @@ kvstore::replay_consumer::consume_batch_end() { co_return stop_parser::no; } -void kvstore::replay_consumer::print(std::ostream& os) const { - os << "storage::kvstore"; +fmt::iterator kvstore::replay_consumer::format_to(fmt::iterator it) const { + return fmt::format_to(it, "storage::kvstore"); } ss::future kvstore::disk_usage() const { diff --git a/src/v/storage/kvstore.h b/src/v/storage/kvstore.h index 9c9e1b06f5277..6445a1732c888 100644 --- a/src/v/storage/kvstore.h +++ b/src/v/storage/kvstore.h @@ -217,7 +217,7 @@ class kvstore { model::record_batch_header header, size_t, size_t) override; void consume_records(iobuf&&) override; ss::future consume_batch_end() override; - void print(std::ostream&) const override; + fmt::iterator format_to(fmt::iterator it) const override; private: kvstore* _store; diff --git a/src/v/storage/log.h b/src/v/storage/log.h index cb8f2c68fadcc..bfe620f6f468e 100644 --- a/src/v/storage/log.h +++ b/src/v/storage/log.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "features/feature_table.h" #include "model/fundamental.h" @@ -129,7 +130,7 @@ class log { // Base offset of the first batch in the most recent term stored in log virtual model::offset find_last_term_start_offset() const = 0; virtual model::timestamp start_timestamp() const = 0; - virtual std::ostream& print(std::ostream& o) const = 0; + virtual fmt::iterator format_to(fmt::iterator it) const = 0; virtual std::optional get_term(model::offset) const = 0; virtual std::optional get_term_last_offset(model::term_id) const = 0; @@ -272,10 +273,6 @@ class log { private: ntp_config _config; - friend std::ostream& operator<<(std::ostream& o, const log& lg) { - return lg.print(o); - } - protected: ntp_config& mutable_config() { return _config; } }; diff --git a/src/v/storage/log_manager.cc b/src/v/storage/log_manager.cc index dd0ceb801102d..a28022f53c351 100644 --- a/src/v/storage/log_manager.cc +++ b/src/v/storage/log_manager.cc @@ -1194,22 +1194,22 @@ int64_t log_manager::compaction_backlog() const { }); } -std::ostream& operator<<(std::ostream& o, const log_config& c) { - o << "{base_dir:" << c.base_dir - << ", max_segment.size:" << c.max_segment_size() - << ", file_sanitize_config:" << c.file_config << ", retention_bytes:"; - if (c.retention_bytes()) { - o << *(c.retention_bytes()); - } else { - o << "nullopt"; - } - return o - << ", compaction_interval_ms:" << c.compaction_interval().count() - << ", log_retention_ms:" - << c.log_retention().value_or(std::chrono::milliseconds(-1)).count() - << ", with_cache:" << c.cache << ", reclaim_opts:" << c.reclaim_opts - << "}"; +fmt::iterator log_config::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{base_dir:{}, max_segment.size:{}, file_sanitize_config:{}, " + "retention_bytes:{}, compaction_interval_ms:{}, log_retention_ms:{}, " + "with_cache:{}, reclaim_opts:{}}}", + base_dir, + max_segment_size(), + file_config, + retention_bytes(), + compaction_interval().count(), + log_retention().value_or(std::chrono::milliseconds(-1)).count(), + cache, + reclaim_opts); } + std::ostream& operator<<(std::ostream& o, const log_manager& m) { return o << "{config:" << m._config << ", logs.size:" << m._logs.size() << ", cache:" << m._batch_cache << "}"; diff --git a/src/v/storage/log_manager.h b/src/v/storage/log_manager.h index 492de303511da..5f90bdb105311 100644 --- a/src/v/storage/log_manager.h +++ b/src/v/storage/log_manager.h @@ -13,6 +13,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include "base/units.h" #include "compaction/key_offset_map.h" @@ -127,7 +128,7 @@ struct log_config { return std::nullopt; } - friend std::ostream& operator<<(std::ostream& o, const log_config&); + fmt::iterator format_to(fmt::iterator it) const; }; // namespace storage /** diff --git a/src/v/storage/log_reader.cc b/src/v/storage/log_reader.cc index a94bc0cb1c620..1371cd64bcf24 100644 --- a/src/v/storage/log_reader.cc +++ b/src/v/storage/log_reader.cc @@ -26,8 +26,6 @@ #include #include -#include - #include template<> @@ -166,8 +164,9 @@ ss::future skipping_consumer::consume_batch_end() { co_return stop_parser(_reader._state.is_full()); } -void skipping_consumer::print(std::ostream& os) const { - fmt::print(os, "storage::skipping_consumer segment {}", _reader._seg); +fmt::iterator skipping_consumer::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "storage::skipping_consumer segment {}", _reader._seg); } log_segment_batch_reader::log_segment_batch_reader( diff --git a/src/v/storage/log_reader.h b/src/v/storage/log_reader.h index 8bc19ad1c669c..f8639c7b67122 100644 --- a/src/v/storage/log_reader.h +++ b/src/v/storage/log_reader.h @@ -75,7 +75,7 @@ class skipping_consumer final : public batch_consumer { size_t bytes_on_disk) override; void consume_records(iobuf&&) override; ss::future consume_batch_end() override; - void print(std::ostream&) const override; + fmt::iterator format_to(fmt::iterator it) const override; private: log_segment_batch_reader& _reader; @@ -161,8 +161,8 @@ class log_reader final : public model::record_batch_reader::impl { ss::future<> finally() noexcept final { return _iterator.close(); } - void print(std::ostream& os) final { - fmt::print(os, "storage::log_reader. config {}", _config); + fmt::iterator format_to(fmt::iterator it) const final { + return fmt::format_to(it, "storage::log_reader. config {}", _config); } /** diff --git a/src/v/storage/log_replayer.cc b/src/v/storage/log_replayer.cc index 84eb67d030a37..b82399eaafa56 100644 --- a/src/v/storage/log_replayer.cc +++ b/src/v/storage/log_replayer.cc @@ -83,8 +83,9 @@ class checksumming_consumer final : public batch_consumer { return (uint32_t)_header.crc == _crc.value(); } - void print(std::ostream& os) const override { - fmt::print(os, "storage::checksumming_consumer segment {}", *_seg); + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to( + it, "storage::checksumming_consumer segment {}", *_seg); } private: @@ -117,19 +118,12 @@ log_replayer::checkpoint log_replayer::recover_in_thread() { return _ckpt; } -std::ostream& operator<<(std::ostream& o, const log_replayer::checkpoint& c) { - o << "{ last_offset: "; - if (c.last_offset) { - o << *c.last_offset; - } else { - o << "null"; - } - o << ", truncate_file_pos:"; - if (c.truncate_file_pos) { - o << *c.truncate_file_pos; - } else { - o << "null"; - } - return o << "}"; +fmt::iterator log_replayer::checkpoint::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{last_offset: {}, truncate_file_pos: {}}}", + last_offset, + truncate_file_pos); } + } // namespace storage diff --git a/src/v/storage/log_replayer.h b/src/v/storage/log_replayer.h index 26739d2c016ba..f48f420e420b5 100644 --- a/src/v/storage/log_replayer.h +++ b/src/v/storage/log_replayer.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "model/fundamental.h" #include "model/timestamp.h" @@ -33,6 +34,8 @@ class log_replayer { explicit operator bool() const { return last_offset && truncate_file_pos && last_max_timestamp; } + + fmt::iterator format_to(fmt::iterator it) const; }; const checkpoint& last_checkpoint() const { return _ckpt; } @@ -43,8 +46,6 @@ class log_replayer { private: checkpoint _ckpt; segment* _seg; - - friend std::ostream& operator<<(std::ostream&, const checkpoint&); }; } // namespace storage diff --git a/src/v/storage/mvlog/batch_collecting_stream_utils.cc b/src/v/storage/mvlog/batch_collecting_stream_utils.cc index 18b7322035033..e45c8c59297e8 100644 --- a/src/v/storage/mvlog/batch_collecting_stream_utils.cc +++ b/src/v/storage/mvlog/batch_collecting_stream_utils.cc @@ -15,17 +15,6 @@ namespace storage::experimental::mvlog { -std::ostream& operator<<(std::ostream& o, collect_stream_outcome out) { - switch (out) { - case collect_stream_outcome::buffer_full: - return o << "collect_stream_outcome::buffer_full"; - case collect_stream_outcome::end_of_stream: - return o << "collect_stream_outcome::end_of_stream"; - case collect_stream_outcome::stop: - return o << "collect_stream_outcome::stop"; - } -} - ss::future> collect_batches_from_stream(entry_stream& entries, batch_collector& collector) { while (true) { diff --git a/src/v/storage/mvlog/batch_collecting_stream_utils.h b/src/v/storage/mvlog/batch_collecting_stream_utils.h index 09e304444f4df..a4df08b9ea978 100644 --- a/src/v/storage/mvlog/batch_collecting_stream_utils.h +++ b/src/v/storage/mvlog/batch_collecting_stream_utils.h @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "storage/mvlog/entry_stream.h" #include "storage/mvlog/reader_outcome.h" @@ -27,7 +28,17 @@ enum class collect_stream_outcome { // desired offset. stop, }; -std::ostream& operator<<(std::ostream&, collect_stream_outcome); +inline fmt::iterator format_to(collect_stream_outcome o, fmt::iterator out) { + switch (o) { + case collect_stream_outcome::buffer_full: + return fmt::format_to(out, "collect_stream_outcome::buffer_full"); + case collect_stream_outcome::end_of_stream: + return fmt::format_to(out, "collect_stream_outcome::end_of_stream"); + case collect_stream_outcome::stop: + return fmt::format_to(out, "collect_stream_outcome::stop"); + } + return fmt::format_to(out, ""); +} // Parses and collects the record batches from the given entry stream. // Ignores other kinds of entries. diff --git a/src/v/storage/mvlog/entry.cc b/src/v/storage/mvlog/entry.cc index 5a32a62d5ae2f..3d3769e3c76aa 100644 --- a/src/v/storage/mvlog/entry.cc +++ b/src/v/storage/mvlog/entry.cc @@ -9,29 +9,15 @@ #include "storage/mvlog/entry.h" -#include - -#include - namespace storage::experimental::mvlog { -std::ostream& operator<<(std::ostream& o, entry_type t) { - switch (t) { - case entry_type::record_batch: - return o << "record_batch"; - } - fmt::print(o, "unknown ({})", static_cast(t)); - return o; -} - -std::ostream& operator<<(std::ostream& o, const entry_header& h) { - fmt::print( - o, +fmt::iterator entry_header::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{header_crc: {}, body_size: {}, entry_type: {}}}", - h.header_crc, - h.body_size, - h.type); - return o; + header_crc, + body_size, + type); } } // namespace storage::experimental::mvlog diff --git a/src/v/storage/mvlog/entry.h b/src/v/storage/mvlog/entry.h index 3f427a5d7ba5e..7328d6aaabc4f 100644 --- a/src/v/storage/mvlog/entry.h +++ b/src/v/storage/mvlog/entry.h @@ -8,10 +8,12 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" #include "bytes/iobuf.h" #include "model/fundamental.h" #include +#include namespace storage::experimental::mvlog { @@ -19,14 +21,20 @@ enum class entry_type : int8_t { record_batch = 0, max = record_batch, }; -std::ostream& operator<<(std::ostream&, entry_type); +inline std::string format_as(entry_type t) { + switch (t) { + case entry_type::record_batch: + return "record_batch"; + } + return fmt::format("unknown ({})", static_cast(t)); +} struct entry_header { uint32_t header_crc; int32_t body_size; entry_type type; - friend std::ostream& operator<<(std::ostream&, const entry_header&); + fmt::iterator format_to(fmt::iterator it) const; bool operator==(const entry_header& other) const = default; }; diff --git a/src/v/storage/mvlog/errc.h b/src/v/storage/mvlog/errc.h index 99c2d61efd334..49ed2c4bfb623 100644 --- a/src/v/storage/mvlog/errc.h +++ b/src/v/storage/mvlog/errc.h @@ -8,6 +8,8 @@ // by the Apache License, Version 2.0 #pragma once +#include "base/format_to.h" + #include namespace storage::experimental::mvlog { @@ -54,9 +56,18 @@ inline std::error_code make_error_code(errc e) noexcept { return {static_cast(e), error_category()}; } -inline std::ostream& operator<<(std::ostream& o, errc e) { - o << error_category().message(static_cast(e)); - return o; +inline fmt::iterator format_to(errc e, fmt::iterator out) { + switch (e) { + case errc::none: + return fmt::format_to(out, "none"); + case errc::broken_data_invariant: + return fmt::format_to(out, "broken_data_invariant"); + case errc::checksum_mismatch: + return fmt::format_to(out, "checksum_mismatch"); + case errc::short_read: + return fmt::format_to(out, "short_read"); + } + return fmt::format_to(out, ""); } } // namespace storage::experimental::mvlog diff --git a/src/v/storage/ntp_config.h b/src/v/storage/ntp_config.h index db4e7e055d0d6..219d571d7caad 100644 --- a/src/v/storage/ntp_config.h +++ b/src/v/storage/ntp_config.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "config/configuration.h" #include "model/fundamental.h" #include "model/metadata.h" @@ -95,8 +96,7 @@ class ntp_config { // Storage mode for the topic (local, tiered, or cloud) model::redpanda_storage_mode storage_mode{default_storage_mode}; - friend std::ostream& - operator<<(std::ostream&, const default_overrides&); + fmt::iterator format_to(fmt::iterator it) const; }; ntp_config(model::ntp n, ss::sstring base_dir) noexcept @@ -520,8 +520,9 @@ class ntp_config { /// _topic_revision in case of recovered topics or read replicas. model::initial_revision_id _remote_rev{0}; +public: // in storage/types.cc - friend std::ostream& operator<<(std::ostream&, const ntp_config&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace storage diff --git a/src/v/storage/offset_translator_state.cc b/src/v/storage/offset_translator_state.cc index 8a4f9015f3714..68a82bb114556 100644 --- a/src/v/storage/offset_translator_state.cc +++ b/src/v/storage/offset_translator_state.cc @@ -437,17 +437,18 @@ offset_translator_state offset_translator_state::from_bootstrap_state( return state; } -std::ostream& -operator<<(std::ostream& os, const offset_translator_state& state) { - const auto& map = state._last_offset2batch; - +fmt::iterator offset_translator_state::format_to(fmt::iterator it) const { + const auto& map = _last_offset2batch; if (map.empty()) { - return os << "{empty}"; + return fmt::format_to(it, "{{empty}}"); } - - return os << "{base offset/delta: " << map.begin()->first << "/" - << map.begin()->second.next_delta << ", map size: " << map.size() - << ", last delta: " << map.rbegin()->second.next_delta << "}"; + return fmt::format_to( + it, + "{{base offset/delta: {}/{}, map size: {}, last delta: {}}}", + map.begin()->first, + map.begin()->second.next_delta, + map.size(), + map.rbegin()->second.next_delta); } } // namespace storage diff --git a/src/v/storage/offset_translator_state.h b/src/v/storage/offset_translator_state.h index 1e40856c07920..d11917748a16c 100644 --- a/src/v/storage/offset_translator_state.h +++ b/src/v/storage/offset_translator_state.h @@ -12,6 +12,7 @@ #pragma once #include "absl/container/btree_map.h" +#include "base/format_to.h" #include "model/fundamental.h" namespace storage { @@ -99,8 +100,8 @@ class offset_translator_state { static offset_translator_state from_bootstrap_state( model::ntp, const absl::btree_map&); - friend std::ostream& - operator<<(std::ostream&, const offset_translator_state&); +public: + fmt::iterator format_to(fmt::iterator it) const; private: // Represents a non-data batch in the log - a batch that contributes to the diff --git a/src/v/storage/parser.cc b/src/v/storage/parser.cc index 20b36b8bbb28c..d1a9a9da90970 100644 --- a/src/v/storage/parser.cc +++ b/src/v/storage/parser.cc @@ -214,7 +214,7 @@ ss::future> continuous_batch_parser::consume_records() { _recovery ? ss::log_level::debug : ss::log_level::error, "parser::consume_records error: {} (record_batch_header: {}, " "batch consumer: {}) ", - to_string(record.error()), + to_string_view(record.error()), *_header, *_consumer); return ss::make_ready_future>( diff --git a/src/v/storage/parser.h b/src/v/storage/parser.h index 2c1cebdb834f8..1854f7e288a94 100644 --- a/src/v/storage/parser.h +++ b/src/v/storage/parser.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/outcome.h" #include "base/seastarx.h" #include "bytes/iobuf.h" @@ -79,13 +80,7 @@ class batch_consumer { virtual void consume_records(iobuf&&) = 0; virtual ss::future consume_batch_end() = 0; - virtual void print(std::ostream&) const = 0; - -private: - friend std::ostream& operator<<(std::ostream& os, const batch_consumer& c) { - c.print(os); - return os; - } + virtual fmt::iterator format_to(fmt::iterator it) const = 0; }; class continuous_batch_parser { diff --git a/src/v/storage/parser_errc.h b/src/v/storage/parser_errc.h index be78ee54a00af..5f5cb8749a8b1 100644 --- a/src/v/storage/parser_errc.h +++ b/src/v/storage/parser_errc.h @@ -11,6 +11,8 @@ #pragma once +#include "base/format_to.h" + #include namespace storage { @@ -24,7 +26,7 @@ enum class parser_errc { not_enough_bytes_in_parser_for_one_record, }; -inline std::string to_string(parser_errc err) { +inline std::string_view to_string_view(parser_errc err) { switch (err) { case parser_errc::none: return "storage::parser_errc::success"; @@ -47,7 +49,7 @@ struct parser_errc_category final : public std::error_category { const char* name() const noexcept final { return "storage::parser_errc"; } std::string message(int c) const final { - return to_string(static_cast(c)); + return std::string(to_string_view(static_cast(c))); } }; inline const std::error_category& error_category() noexcept { @@ -58,8 +60,8 @@ inline std::error_code make_error_code(parser_errc e) noexcept { return std::error_code(static_cast(e), error_category()); } -inline std::ostream& operator<<(std::ostream& os, parser_errc err) { - return os << to_string(err); +inline fmt::iterator format_to(parser_errc err, fmt::iterator out) { + return fmt::format_to(out, "{}", to_string_view(err)); } } // namespace storage diff --git a/src/v/storage/readers_cache.cc b/src/v/storage/readers_cache.cc index c12ad58025be5..d30197667cc9e 100644 --- a/src/v/storage/readers_cache.cc +++ b/src/v/storage/readers_cache.cc @@ -277,7 +277,9 @@ readers_cache::entry::make_cached_reader(readers_cache* cache) { ss::future<> finally() noexcept final { return ss::now(); } - void print(std::ostream& o) final { return _underlying->print(o); }; + fmt::iterator format_to(fmt::iterator it) const final { + return _underlying->format_to(it); + } ~cached_reader_impl() final = default; private: diff --git a/src/v/storage/segment.cc b/src/v/storage/segment.cc index 81ca7a67ed5c9..4c6b6f8b2b6b3 100644 --- a/src/v/storage/segment.cc +++ b/src/v/storage/segment.cc @@ -728,52 +728,59 @@ void segment::advance_stable_offset(size_t filepos) { clear_cached_disk_usage(); } -std::ostream& operator<<(std::ostream& o, const segment::offset_tracker& t) { - fmt::print( - o, +fmt::iterator segment::offset_tracker::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{term:{}, base_offset:{}, committed_offset:{}, stable_offset:{}, " "dirty_offset:{}}}", - t.get_term(), - t.get_base_offset(), - t.get_committed_offset(), - t.get_stable_offset(), - t.get_dirty_offset()); - return o; -} - -std::ostream& operator<<(std::ostream& o, const segment& h) { - o << "{offset_tracker:" << h._tracker - << ", compacted_segment=" << h.is_compacted_segment() - << ", finished_self_compaction=" << h.has_self_compact_timestamp() - << ", finished_windowed_compaction=" << h.finished_windowed_compaction() - << ", generation=" << h.get_generation_id() << ", reader="; - if (h._reader) { - o << *h._reader; + get_term(), + get_base_offset(), + get_committed_offset(), + get_stable_offset(), + get_dirty_offset()); +} + +fmt::iterator segment::format_to(fmt::iterator it) const { + it = fmt::format_to( + it, + "{{offset_tracker:{}, compacted_segment={}" + ", finished_self_compaction={}" + ", finished_windowed_compaction={}" + ", generation={}, reader=", + _tracker, + is_compacted_segment(), + has_self_compact_timestamp(), + finished_windowed_compaction(), + get_generation_id()); + if (_reader) { + it = fmt::format_to(it, "{}", *_reader); } else { - o << "nullptr"; + it = fmt::format_to(it, "nullptr"); } - - o << ", writer="; - if (h.has_appender()) { - o << *h._appender; + it = fmt::format_to(it, ", writer="); + if (has_appender()) { + it = fmt::format_to(it, "{}", *_appender); } else { - o << "nullptr"; + it = fmt::format_to(it, "nullptr"); } - o << ", cache="; - if (h._cache) { - o << *h._cache; + it = fmt::format_to(it, ", cache="); + if (_cache) { + it = fmt::format_to(it, "{}", *_cache); } else { - o << "nullptr"; + it = fmt::format_to(it, "nullptr"); } - o << ", compaction_index:"; - if (h._compaction_index) { - o << *h._compaction_index; + it = fmt::format_to(it, ", compaction_index:"); + if (_compaction_index) { + it = fmt::format_to(it, "{}", *_compaction_index); } else { - o << "nullopt"; - } - return o << ", closed=" << h.is_closed() - << ", tombstone=" << h.is_tombstone() << ", index=" << h.index() - << "}"; + it = fmt::format_to(it, "none"); + } + return fmt::format_to( + it, + ", closed={}, tombstone={}, index={}}}", + is_closed(), + is_tombstone(), + index()); } template diff --git a/src/v/storage/segment.h b/src/v/storage/segment.h index 1ac18263ff4c4..2b323a844695c 100644 --- a/src/v/storage/segment.h +++ b/src/v/storage/segment.h @@ -114,7 +114,10 @@ class segment { // Offset of last message written to this log, may not yet be stable. model::offset _dirty_offset; - friend std::ostream& operator<<(std::ostream&, const offset_tracker&); + public: + fmt::iterator format_to(fmt::iterator it) const; + + private: friend class testing_details::offset_tracker_accessor; }; enum class bitflags : uint32_t { @@ -362,7 +365,9 @@ class segment { // Used to implement segment.ms rolling std::optional _first_write; - friend std::ostream& operator<<(std::ostream&, const segment&); +public: + fmt::iterator format_to(fmt::iterator it) const; + friend class testing_details::segment_accessor; }; diff --git a/src/v/storage/segment_appender.cc b/src/v/storage/segment_appender.cc index 1e2ec94e6d187..4b83848f90330 100644 --- a/src/v/storage/segment_appender.cc +++ b/src/v/storage/segment_appender.cc @@ -828,15 +828,4 @@ fmt::iterator segment_appender::stats::format_to(fmt::iterator it) const { writes_completed); } -std::ostream& -operator<<(std::ostream& s, const segment_appender::inflight_write& op) { - fmt::print( - s, - "{{state: {}, committed_offset: {}, alignment: {}}}", - (int)op.state, - op.committed_offset, - op.alignment); - return s; -} - } // namespace storage diff --git a/src/v/storage/segment_appender.h b/src/v/storage/segment_appender.h index bd290afc5688a..639ae73779821 100644 --- a/src/v/storage/segment_appender.h +++ b/src/v/storage/segment_appender.h @@ -220,9 +220,8 @@ class segment_appender { size_t offset; ss::promise<> p; - friend std::ostream& operator<<(std::ostream& s, const flush_op& op) { - fmt::print(s, "{{offset: {}}}", op.offset); - return s; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{offset: {}}}", offset); } }; @@ -319,12 +318,16 @@ class segment_appender { return committed_offset % alignment == 0; } - friend std::ostream& - operator<<(std::ostream& s, const inflight_write& op); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{state: {}, committed_offset: {}, alignment: {}}}", + (int)state, + committed_offset, + alignment); + } }; - friend std::ostream& operator<<(std::ostream& s, const inflight_write& op); - ss::chunked_fifo> _inflight; // A gauge of the current number of oustanding dispatched writes, equal to // the count of elements in the _inflight container which have state == diff --git a/src/v/storage/segment_appender_chunk.h b/src/v/storage/segment_appender_chunk.h index 8c3cac3b71e8f..d0418964f316f 100644 --- a/src/v/storage/segment_appender_chunk.h +++ b/src/v/storage/segment_appender_chunk.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "base/units.h" #include "container/intrusive_list_helpers.h" @@ -133,10 +134,15 @@ class segment_appender_chunk { size_t _pos{0}; size_t _flushed_pos{0}; std::unique_ptr _buf; - friend std::ostream& - operator<<(std::ostream& o, const segment_appender_chunk& c) { - return o << "{_alignment:" << c._alignment << ", _pos:" << c._pos - << ", _flushed_pos:" << c._flushed_pos << "}"; + +public: + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{_alignment:{}, _pos:{}, _flushed_pos:{}}}", + _alignment, + _pos, + _flushed_pos); } }; diff --git a/src/v/storage/segment_index.cc b/src/v/storage/segment_index.cc index a575db7e389ea..2451b0f7bffff 100644 --- a/src/v/storage/segment_index.cc +++ b/src/v/storage/segment_index.cc @@ -259,23 +259,32 @@ ss::future<> segment_index::flush_to_file(ss::file backing_file) { co_await out.flush(); } -std::ostream& operator<<(std::ostream& o, const segment_index& i) { - return o << "{file:" << i.path() << ", offsets:" << i.base_offset() - << ", index:" << i._state << ", step:" << i._step - << ", needs_persistence:" << i._needs_persistence << "}"; +fmt::iterator segment_index::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{file:{}, offsets:{}, index:{}, step:{}, needs_persistence:{}}}", + path(), + base_offset(), + _state, + _step, + _needs_persistence); } std::ostream& operator<<(std::ostream& o, const segment_index_ptr& i) { if (i) { - return o << "{ptr=" << *i << "}"; + fmt::print(o, "{{ptr={}}}", *i); + } else { + o << "{ptr=nullptr}"; } - return o << "{ptr=nullptr}"; + return o; } std::ostream& operator<<(std::ostream& o, const std::optional& e) { if (e) { - return o << *e; + fmt::print(o, "{}", *e); + } else { + o << "{empty segment_index::entry}"; } - return o << "{empty segment_index::entry}"; + return o; } ss::future segment_index::disk_usage() { diff --git a/src/v/storage/segment_index.h b/src/v/storage/segment_index.h index 0ea5f32043f26..69e7bd5eb432b 100644 --- a/src/v/storage/segment_index.h +++ b/src/v/storage/segment_index.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "features/feature_table.h" #include "model/fundamental.h" #include "model/record.h" @@ -369,7 +370,8 @@ class segment_index { friend class log_replayer_fixture; friend class segment_index_observer; - friend std::ostream& operator<<(std::ostream&, const segment_index&); +public: + fmt::iterator format_to(fmt::iterator it) const; }; using segment_index_ptr = std::unique_ptr; diff --git a/src/v/storage/segment_reader.cc b/src/v/storage/segment_reader.cc index 8b2be4b879bd1..958cee05f3d26 100644 --- a/src/v/storage/segment_reader.cc +++ b/src/v/storage/segment_reader.cc @@ -194,18 +194,6 @@ ss::future<> segment_reader::close() { } } -std::ostream& operator<<(std::ostream& os, const segment_reader& seg) { - return os << "{" << seg.filename() << ", (" << seg.file_size() - << " bytes)}"; -} - -std::ostream& operator<<(std::ostream& os, const segment_reader_ptr& seg) { - if (seg) { - return os << *seg; - } - return os << "{{log_segment: null}}"; -} - segment_reader_handle::segment_reader_handle(segment_reader* parent) : _parent(parent) { _parent->_streams.push_back(*this); diff --git a/src/v/storage/segment_reader.h b/src/v/storage/segment_reader.h index b95c0e063410d..6b36ed517beaf 100644 --- a/src/v/storage/segment_reader.h +++ b/src/v/storage/segment_reader.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "container/intrusive_list_helpers.h" #include "model/fundamental.h" @@ -177,7 +178,12 @@ class segment_reader { ss::future<> put(); friend class segment_reader_handle; - friend std::ostream& operator<<(std::ostream&, const segment_reader&); + +public: + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{{}, ({} bytes)}}", filename(), file_size()); + } }; /** diff --git a/src/v/storage/spill_key_index.cc b/src/v/storage/spill_key_index.cc index febb6119f5e0a..0521a29832b78 100644 --- a/src/v/storage/spill_key_index.cc +++ b/src/v/storage/spill_key_index.cc @@ -26,8 +26,6 @@ #include #include -#include - #include using namespace std::chrono_literals; @@ -390,19 +388,16 @@ ss::future<> spill_key_index::close() { } } -void spill_key_index::print(std::ostream& o) const { o << *this; } - -std::ostream& operator<<(std::ostream& o, const spill_key_index& k) { - fmt::print( - o, +fmt::iterator spill_key_index::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{name:{}, key_mem_usage:{}, persisted_entries:{}, " "in_memory_entries:{}, file_appender:{}}}", - k.filename(), - k._keys_mem_usage, - k._footer.keys, - k._midx.size(), - k._appender); - return o; + filename(), + _keys_mem_usage, + _footer.keys, + _midx.size(), + _appender); } size_t spill_key_index::size_bytes() const { diff --git a/src/v/storage/spill_key_index.h b/src/v/storage/spill_key_index.h index 99916c7bcb437..ba442bc4f086c 100644 --- a/src/v/storage/spill_key_index.h +++ b/src/v/storage/spill_key_index.h @@ -79,7 +79,7 @@ class spill_key_index final : public compacted_index_writer { int32_t) final; ss::future<> append(compacted_index::entry) final; ss::future<> close() final; - void print(std::ostream&) const final; + fmt::iterator format_to(fmt::iterator it) const final; void set_flag(compacted_index::footer_flags) final; size_t size_bytes() const final; @@ -159,8 +159,6 @@ class spill_key_index final : public compacted_index_writer { compacted_index::footer _footer; crc::crc32c _crc; ss::gate _gate; - - friend std::ostream& operator<<(std::ostream&, const spill_key_index&); }; } // namespace storage::internal diff --git a/src/v/storage/tests/BUILD b/src/v/storage/tests/BUILD index 377bbe65f6f7e..40acc44e26b71 100644 --- a/src/v/storage/tests/BUILD +++ b/src/v/storage/tests/BUILD @@ -163,8 +163,10 @@ redpanda_test_cc_library( "utils/log_gap_analysis.h", ], deps = [ + "//src/v/base", "//src/v/model", "//src/v/model:batch_compression", + "@fmt", ], ) diff --git a/src/v/storage/tests/batch_cache_test.cc b/src/v/storage/tests/batch_cache_test.cc index d9f59a093766f..7eeef35bb1f98 100644 --- a/src/v/storage/tests/batch_cache_test.cc +++ b/src/v/storage/tests/batch_cache_test.cc @@ -19,7 +19,6 @@ #include #include -#include #include static storage::batch_cache::reclaim_options opts = { diff --git a/src/v/storage/tests/batch_consumer_utils_test.cc b/src/v/storage/tests/batch_consumer_utils_test.cc index 952d4e25b9729..ce0c078a5627c 100644 --- a/src/v/storage/tests/batch_consumer_utils_test.cc +++ b/src/v/storage/tests/batch_consumer_utils_test.cc @@ -82,7 +82,9 @@ struct round_robin_consumer : storage::batch_consumer { co_return stop_parser::yes; } - void print(std::ostream& o) const override { o << "test_consumer"; } + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to(it, "test_consumer"); + } size_t side{0}; size_t size_limit{0}; @@ -119,8 +121,8 @@ struct throwing_consumer : storage::batch_consumer { co_return stop_parser::yes; } - void print(std::ostream& o) const override { - o << "throwing_test_consumer"; + fmt::iterator format_to(fmt::iterator it) const override { + return fmt::format_to(it, "throwing_test_consumer"); } }; diff --git a/src/v/storage/tests/readers_cache_test.cc b/src/v/storage/tests/readers_cache_test.cc index c2f87d49ce74d..ed6b1284c9aa4 100644 --- a/src/v/storage/tests/readers_cache_test.cc +++ b/src/v/storage/tests/readers_cache_test.cc @@ -25,7 +25,6 @@ #include -#include #include #include diff --git a/src/v/storage/tests/utils/log_gap_analysis.cc b/src/v/storage/tests/utils/log_gap_analysis.cc index 6aeb6a5541acd..2a6a6d7b30abc 100644 --- a/src/v/storage/tests/utils/log_gap_analysis.cc +++ b/src/v/storage/tests/utils/log_gap_analysis.cc @@ -73,14 +73,13 @@ log_gap_analysis make_log_gap_analysis( return ga; } -std::ostream& operator<<(std::ostream& o, const log_gap_analysis& a) { - fmt::print( - o, +fmt::iterator log_gap_analysis::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{first_gap_start:{}, last_gap_end:{}, num_gaps:{}}}", - a.first_gap_start, - a.last_gap_end, - a.num_gaps); - return o; + first_gap_start, + last_gap_end, + num_gaps); } } // namespace storage diff --git a/src/v/storage/tests/utils/log_gap_analysis.h b/src/v/storage/tests/utils/log_gap_analysis.h index 211304914994c..af91e9d043571 100644 --- a/src/v/storage/tests/utils/log_gap_analysis.h +++ b/src/v/storage/tests/utils/log_gap_analysis.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "model/fundamental.h" #include "model/record_batch_reader.h" @@ -30,7 +31,7 @@ struct log_gap_analysis { // For the last gap detected, stores the last offset that was missing. model::offset last_gap_end{-1}; size_t num_gaps{0}; - friend std::ostream& operator<<(std::ostream& o, const log_gap_analysis& r); + fmt::iterator format_to(fmt::iterator it) const; }; /** diff --git a/src/v/storage/types.cc b/src/v/storage/types.cc index 5f49f3e958e04..fe3b53ff1a12e 100644 --- a/src/v/storage/types.cc +++ b/src/v/storage/types.cc @@ -18,7 +18,6 @@ #include "utils/human.h" #include -#include namespace storage { @@ -116,41 +115,37 @@ fmt::iterator local_log_reader_config::format_to(fmt::iterator it) const { client_address); } -std::ostream& operator<<(std::ostream& o, const append_result& a) { +fmt::iterator append_result::format_to(fmt::iterator it) const { auto append_dur = std::chrono::duration_cast( - log_clock::now() - a.append_time); - fmt::print( - o, + log_clock::now() - append_time); + return fmt::format_to( + it, "{{time_since_append: {}, base_offset: {}, last_offset: {}, last_term: " "{}, " "byte_size: {}}}", append_dur, - a.base_offset, - a.last_offset, - a.last_term, - a.byte_size); - return o; + base_offset, + last_offset, + last_term, + byte_size); } -std::ostream& operator<<(std::ostream& o, const timequery_result& r) { - return o << "{term:" << r.term << ", offset:" << r.offset - << ", time:" << r.time << "}"; +fmt::iterator timequery_result::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{term:{}, offset:{}, time:{}}}", term, offset, time); } -std::ostream& operator<<(std::ostream& o, const timequery_config& a) { - o << "{min_offset: " << a.min_offset << ", max_offset: " << a.max_offset - << ", time:" << a.time << ", type_filter:"; - if (a.type_filter) { - o << *a.type_filter; - } else { - o << "nullopt"; - } - o << "}"; - return o; +fmt::iterator timequery_config::format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{min_offset: {}, max_offset: {}, time:{}, type_filter:{}}}", + min_offset, + max_offset, + time, + type_filter); } -std::ostream& -operator<<(std::ostream& o, const ntp_config::default_overrides& v) { - fmt::print( - o, +fmt::iterator ntp_config::default_overrides::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{compaction_strategy: {}, cleanup_policy_bitflags: {}, segment_size: " "{}, retention_bytes: {}, retention_time_ms: {}, recovery_enabled: {}, " "retention_local_target_bytes: {}, retention_local_target_ms: {}, " @@ -161,108 +156,97 @@ operator<<(std::ostream& o, const ntp_config::default_overrides& v) { "delete_retention_ms: {}, min_cleanable_dirty_ratio: {}, " "min_compaction_lag_ms: {}, max_compaction_lag_ms: {}, storage_mode: {} " "}}", - v.compaction_strategy, - v.cleanup_policy_bitflags, - v.segment_size, - v.retention_bytes, - v.retention_time, - v.recovery_enabled, - v.retention_local_target_bytes, - v.retention_local_target_ms, - v.remote_delete, - v.segment_ms, - v.initial_retention_local_target_bytes, - v.initial_retention_local_target_ms, - v.write_caching, - v.flush_ms, - v.flush_bytes, - v.iceberg_mode, - v.remote_allow_gaps, - v.delete_retention_ms, - v.min_cleanable_dirty_ratio, - v.min_compaction_lag_ms, - v.max_compaction_lag_ms, - v.storage_mode); - - o << "}"; - - return o; + compaction_strategy, + cleanup_policy_bitflags, + segment_size, + retention_bytes, + retention_time, + recovery_enabled, + retention_local_target_bytes, + retention_local_target_ms, + remote_delete, + segment_ms, + initial_retention_local_target_bytes, + initial_retention_local_target_ms, + write_caching, + flush_ms, + flush_bytes, + iceberg_mode, + remote_allow_gaps, + delete_retention_ms, + min_cleanable_dirty_ratio, + min_compaction_lag_ms, + max_compaction_lag_ms, + storage_mode); } -std::ostream& operator<<(std::ostream& o, const ntp_config& v) { - if (v.has_overrides()) { - fmt::print( - o, +fmt::iterator ntp_config::format_to(fmt::iterator it) const { + if (has_overrides()) { + return fmt::format_to( + it, "{{ntp: {}, base_dir: {}, overrides: {}, revision: {}, " "topic_revision: {}, remote_revision: {}}}", - v.ntp(), - v.base_directory(), - v.get_overrides(), - v.get_revision(), - v.get_topic_revision(), - v.get_remote_revision()); + ntp(), + base_directory(), + get_overrides(), + get_revision(), + get_topic_revision(), + get_remote_revision()); } else { - fmt::print( - o, + return fmt::format_to( + it, "{{ntp: {}, base_dir: {}, overrides: nullptr, revision: {}, " "topic_revision: {}, remote_revision: {}}}", - v.ntp(), - v.base_directory(), - v.get_revision(), - v.get_topic_revision(), - v.get_remote_revision()); + ntp(), + base_directory(), + get_revision(), + get_topic_revision(), + get_remote_revision()); } - return o; } -std::ostream& operator<<(std::ostream& o, const truncate_config& cfg) { - fmt::print(o, "{{base_offset:{}}}", cfg.base_offset); - return o; +fmt::iterator truncate_config::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{base_offset:{}}}", base_offset); } -std::ostream& operator<<(std::ostream& o, const truncate_prefix_config& cfg) { - fmt::print( - o, +fmt::iterator truncate_prefix_config::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{start_offset:{}, force_truncate_delta:{}}}", - cfg.start_offset, - cfg.force_truncate_delta); - return o; + start_offset, + force_truncate_delta); } -std::ostream& operator<<(std::ostream& o, const offset_stats& s) { - fmt::print( - o, +fmt::iterator offset_stats::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{start_offset:{}, committed_offset:{}, " "committed_offset_term:{}, dirty_offset:{}, dirty_offset_term:{}}}", - s.start_offset, - s.committed_offset, - s.committed_offset_term, - s.dirty_offset, - s.dirty_offset_term); - return o; + start_offset, + committed_offset, + committed_offset_term, + dirty_offset, + dirty_offset_term); } -std::ostream& operator<<(std::ostream& os, const gc_config& cfg) { - fmt::print( - os, +fmt::iterator gc_config::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{eviction_time:{}, max_bytes:{}}}", - cfg.eviction_time, - cfg.max_bytes.value_or(-1)); - return os; + eviction_time, + max_bytes.value_or(-1)); } -std::ostream& operator<<(std::ostream& os, const housekeeping_config& cfg) { - fmt::print(os, "{{compact:{}, gc:{}}}", cfg.compact, cfg.gc); - return os; +fmt::iterator housekeeping_config::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{compact:{}, gc:{}}}", compact, gc); } -std::ostream& operator<<(std::ostream& o, const compaction_result& r) { - fmt::print( - o, +fmt::iterator compaction_result::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{executed_compaction: {}, size_before: {}, size_after: {}}}", - r.executed_compaction, - r.size_before, - r.size_after); - return o; + executed_compaction, + size_before, + size_after); } std::ostream& diff --git a/src/v/storage/types.h b/src/v/storage/types.h index 8c13a29873cae..a947037763b4a 100644 --- a/src/v/storage/types.h +++ b/src/v/storage/types.h @@ -290,7 +290,7 @@ struct offset_stats { model::offset dirty_offset; model::term_id dirty_offset_term; - friend std::ostream& operator<<(std::ostream&, const offset_stats&); + fmt::iterator format_to(fmt::iterator it) const; }; struct log_append_config { @@ -305,7 +305,7 @@ struct append_result { size_t byte_size; model::term_id last_term; - friend std::ostream& operator<<(std::ostream& o, const append_result&); + fmt::iterator format_to(fmt::iterator it) const; }; /// A timequery configuration specifies the range of offsets to search for a @@ -331,7 +331,7 @@ struct timequery_config { model::opt_abort_source_t abort_source; model::opt_client_address_t client_address; - friend std::ostream& operator<<(std::ostream& o, const timequery_config&); + fmt::iterator format_to(fmt::iterator it) const; }; struct timequery_result { timequery_result( @@ -346,7 +346,7 @@ struct timequery_result { bool operator==(const timequery_result& other) const = default; - friend std::ostream& operator<<(std::ostream& o, const timequery_result&); + fmt::iterator format_to(fmt::iterator it) const; }; struct truncate_config { @@ -354,7 +354,8 @@ struct truncate_config { : base_offset(o) {} // Lowest offset to remove. model::offset base_offset; - friend std::ostream& operator<<(std::ostream&, const truncate_config&); + + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -382,8 +383,7 @@ struct truncate_prefix_config { // an error. std::optional force_truncate_delta; - friend std::ostream& - operator<<(std::ostream&, const truncate_prefix_config&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -529,7 +529,7 @@ struct gc_config { // remove one segment if log is > max_bytes std::optional max_bytes; - friend std::ostream& operator<<(std::ostream&, const gc_config&); + fmt::iterator format_to(fmt::iterator it) const; }; /* @@ -594,7 +594,7 @@ struct housekeeping_config { return cfg; } - friend std::ostream& operator<<(std::ostream&, const housekeeping_config&); + fmt::iterator format_to(fmt::iterator it) const; }; struct compaction_result { @@ -624,7 +624,8 @@ struct compaction_result { size_t size_after; // The size of the new compacted index, if one was made. std::optional cmp_idx_size_after{std::nullopt}; - friend std::ostream& operator<<(std::ostream&, const compaction_result&); + + fmt::iterator format_to(fmt::iterator it) const; }; /* diff --git a/src/v/storage/version.h b/src/v/storage/version.h index 75b9e4887296a..c805c600baa2d 100644 --- a/src/v/storage/version.h +++ b/src/v/storage/version.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -24,6 +25,14 @@ namespace storage { enum class record_version_type { v1 }; +inline fmt::iterator format_to(record_version_type version, fmt::iterator out) { + switch (version) { + case record_version_type::v1: + return fmt::format_to(out, "v1"); + } + throw std::runtime_error("Wrong record version"); +} + inline record_version_type from_string(std::string_view version) { if (version == "v1") { return record_version_type::v1; @@ -33,15 +42,7 @@ inline record_version_type from_string(std::string_view version) { } inline ss::sstring to_string(record_version_type version) { - switch (version) { - case record_version_type::v1: - return "v1"; - } - throw std::runtime_error("Wrong record version"); -} - -inline std::ostream& operator<<(std::ostream& o, record_version_type v) { - return o << to_string(v); + return ss::sstring(fmt::to_string(version)); } } // namespace storage diff --git a/src/v/syschecks/pidfile.cc b/src/v/syschecks/pidfile.cc index 4611d7ca050cc..a1cb4e485b812 100644 --- a/src/v/syschecks/pidfile.cc +++ b/src/v/syschecks/pidfile.cc @@ -7,6 +7,7 @@ // the Business Source License, use of this software will be governed // by the Apache License, Version 2.0 +#include "base/format_to.h" #include "base/seastarx.h" #include "base/vassert.h" #include "syschecks/syschecks.h" diff --git a/src/v/test_utils/archival.h b/src/v/test_utils/archival.h index 882d84ec76e69..b39d234ec53f5 100644 --- a/src/v/test_utils/archival.h +++ b/src/v/test_utils/archival.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "bytes/iostream.h" #include "cloud_storage/partition_manifest.h" #include "cluster/archival/archival_policy.h" @@ -53,19 +54,17 @@ struct segment_spec { , start_kafka_offset(start_kafka >= 0 ? start_kafka : start) , end_kafka_offset(end_kafka >= 0 ? end_kafka : end) {} - friend std::ostream& - operator<<(std::ostream& os, const segment_spec& spec) { - fmt::print( - os, + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ start_offset={}, end_offset={}, size_bytes={}, timestamp={}, " "start_kafka_offset={}, end_kafka_offset={} }}", - spec.start_offset, - spec.end_offset, - spec.size_bytes, - spec.timestamp, - spec.start_kafka_offset, - spec.end_kafka_offset); - return os; + start_offset, + end_offset, + size_bytes, + timestamp, + start_kafka_offset, + end_kafka_offset); } }; diff --git a/src/v/transform/rpc/client.cc b/src/v/transform/rpc/client.cc index 368855c1147a7..e1d8a7506dd07 100644 --- a/src/v/transform/rpc/client.cc +++ b/src/v/transform/rpc/client.cc @@ -443,9 +443,9 @@ client::do_load_wasm_binary_once( : do_remote_load_wasm_binary(*leader, offset, timeout)); vlog( log.trace, - "do_load_wasm_binary_once_response(node={}): {}", + "do_load_wasm_binary_once_response(node={}): has_value={}", *leader, - reply); + reply.has_value()); co_return reply; } diff --git a/src/v/transform/rpc/serde.cc b/src/v/transform/rpc/serde.cc index 7e20c36915b84..f59a87b927bb4 100644 --- a/src/v/transform/rpc/serde.cc +++ b/src/v/transform/rpc/serde.cc @@ -11,7 +11,6 @@ #include "transform/rpc/serde.h" #include "model/record.h" -#include "utils/to_string.h" #include @@ -48,67 +47,6 @@ produce_request produce_request::share() { return {std::move(shared), timeout}; } -std::ostream& operator<<(std::ostream& os, const offset_commit_request& req) { - fmt::print( - os, "{{ kvs: {}, coordinator: {} }}", req.kvs.size(), req.coordinator); - return os; -} - -std::ostream& operator<<(std::ostream& os, const offset_commit_response& resp) { - fmt::print(os, "{{ errc: {} }}", resp.errc); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const find_coordinator_request& req) { - fmt::print(os, "{{ num_keys: {} }}", req.keys.size()); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const find_coordinator_response& resp) { - fmt::print( - os, - "{{ coordinators: {}, errors: {} }}", - resp.coordinators.size(), - resp.errors.size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const generate_report_request&) { - fmt::print(os, "{{ }}"); - return os; -} - -std::ostream& operator<<(std::ostream& os, const generate_report_reply& reply) { - fmt::print(os, "{{ transforms: {} }}", reply.report.transforms.size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const offset_fetch_request& resp) { - fmt::print( - os, - "{{ keys: {}, coordinator: {} }}", - resp.keys.size(), - resp.coordinator); - return os; -} - -std::ostream& operator<<(std::ostream& os, const offset_fetch_response& resp) { - fmt::print( - os, - "{{ errc: {}, results: {} }}", - resp.errors.size(), - resp.results.size()); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const load_wasm_binary_request& req) { - fmt::print(os, "{{ offset: {}, timeout: {} }}", req.offset, req.timeout); - return os; -} - std::ostream& operator<<(std::ostream& os, const load_wasm_binary_reply& reply) { fmt::print( @@ -119,30 +57,6 @@ operator<<(std::ostream& os, const load_wasm_binary_reply& reply) { return os; } -std::ostream& -operator<<(std::ostream& os, const delete_wasm_binary_reply& reply) { - fmt::print(os, "{{ errc: {} }}", reply.ec); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const delete_wasm_binary_request& req) { - fmt::print(os, "{{ key: {}, timeout: {} }}", req.key, req.timeout); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const store_wasm_binary_reply& reply) { - fmt::print(os, "{{ errc: {}, stored: {} }}", reply.ec, reply.stored); - return os; -} - -std::ostream& -operator<<(std::ostream& os, const stored_wasm_binary_metadata& meta) { - fmt::print(os, "{{ key: {}, offset: {} }}", meta.key, meta.offset); - return os; -} - std::ostream& operator<<(std::ostream& os, const store_wasm_binary_request& req) { fmt::print( @@ -153,13 +67,12 @@ operator<<(std::ostream& os, const store_wasm_binary_request& req) { return os; } -std::ostream& operator<<(std::ostream& os, const produce_request& req) { - fmt::print( - os, +fmt::iterator produce_request::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ topic_data: {}, timeout: {} }}", - fmt::join(req.topic_data, ", "), - req.timeout); - return os; + fmt::join(topic_data, ", "), + timeout); } std::ostream& operator<<(std::ostream& os, const produce_reply& reply) { @@ -167,40 +80,9 @@ std::ostream& operator<<(std::ostream& os, const produce_reply& reply) { return os; } -std::ostream& -operator<<(std::ostream& os, const transformed_topic_data_result& result) { - fmt::print(os, "{{ errc: {}, tp: {} }}", result.err, result.tp); - return os; -} - -std::ostream& operator<<(std::ostream& os, const transformed_topic_data& data) { - fmt::print( - os, "{{ tp: {}, batches_size: {} }}", data.tp, data.batches.size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const list_commits_request& req) { - fmt::print(os, "{{ partition: {} }}", req.partition); - return os; -} - -std::ostream& operator<<(std::ostream& os, const list_commits_reply& reply) { - fmt::print(os, "{{ ec: {}, map_size: {} }}", reply.errc, reply.map.size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const delete_commits_request& req) { - fmt::print( - os, - "{{ partition: {}, transform_ids_size: {} }}", - req.partition, - req.ids.size()); - return os; -} - -std::ostream& operator<<(std::ostream& os, const delete_commits_reply& reply) { - fmt::print(os, "{{ ec: {} }}", reply.errc); - return os; +fmt::iterator transformed_topic_data::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ tp: {}, batches_size: {} }}", tp, batches.size()); } } // namespace transform::rpc diff --git a/src/v/transform/rpc/serde.h b/src/v/transform/rpc/serde.h index f8fb450361d13..18040369dff15 100644 --- a/src/v/transform/rpc/serde.h +++ b/src/v/transform/rpc/serde.h @@ -13,6 +13,7 @@ #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "base/format_to.h" #include "base/outcome.h" #include "cluster/errc.h" #include "container/chunked_vector.h" @@ -41,8 +42,7 @@ struct transformed_topic_data ss::chunked_fifo batches; transformed_topic_data share(); - friend std::ostream& - operator<<(std::ostream&, const transformed_topic_data&); + fmt::iterator format_to(fmt::iterator it) const; auto serde_fields() { return std::tie(tp, batches); } }; @@ -60,7 +60,7 @@ struct produce_request auto serde_fields() { return std::tie(topic_data, timeout); } produce_request share(); - friend std::ostream& operator<<(std::ostream&, const produce_request&); + fmt::iterator format_to(fmt::iterator it) const; ss::chunked_fifo topic_data; model::timeout_clock::duration timeout{}; @@ -80,8 +80,9 @@ struct transformed_topic_data_result cluster::errc err{cluster::errc::success}; auto serde_fields() { return std::tie(tp, err); } - friend std::ostream& - operator<<(std::ostream&, const transformed_topic_data_result&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ errc: {}, tp: {} }}", err, tp); + } }; struct produce_reply @@ -130,8 +131,9 @@ struct stored_wasm_binary_metadata auto serde_fields() { return std::tie(key, offset); } - friend std::ostream& - operator<<(std::ostream&, const stored_wasm_binary_metadata&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ key: {}, offset: {} }}", key, offset); + } uuid_t key{}; model::offset offset; @@ -150,8 +152,9 @@ struct store_wasm_binary_reply auto serde_fields() { return std::tie(ec, stored); } - friend std::ostream& - operator<<(std::ostream&, const store_wasm_binary_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ errc: {}, stored: {} }}", ec, stored); + } cluster::errc ec = cluster::errc::success; stored_wasm_binary_metadata stored; @@ -170,8 +173,9 @@ struct delete_wasm_binary_request auto serde_fields() { return std::tie(key, timeout); } - friend std::ostream& - operator<<(std::ostream&, const delete_wasm_binary_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ key: {}, timeout: {} }}", key, timeout); + } uuid_t key{}; model::timeout_clock::duration timeout{}; @@ -188,8 +192,9 @@ struct delete_wasm_binary_reply auto serde_fields() { return std::tie(ec); } - friend std::ostream& - operator<<(std::ostream&, const delete_wasm_binary_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ errc: {} }}", ec); + } cluster::errc ec = cluster::errc::success; }; @@ -207,8 +212,10 @@ struct load_wasm_binary_request auto serde_fields() { return std::tie(offset, timeout); } - friend std::ostream& - operator<<(std::ostream&, const load_wasm_binary_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ offset: {}, timeout: {} }}", offset, timeout); + } model::offset offset; model::timeout_clock::duration timeout{}; @@ -244,8 +251,9 @@ struct find_coordinator_request absl::flat_hash_set keys; - friend std::ostream& - operator<<(std::ostream&, const find_coordinator_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ num_keys: {} }}", keys.size()); + } auto serde_fields() { return std::tie(keys); } }; @@ -261,8 +269,13 @@ struct find_coordinator_response coordinators; absl::flat_hash_map errors; - friend std::ostream& - operator<<(std::ostream&, const find_coordinator_response&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ coordinators: {}, errors: {} }}", + coordinators.size(), + errors.size()); + } auto serde_fields() { return std::tie(coordinators, errors); } }; @@ -286,8 +299,10 @@ struct offset_commit_request btree_map kvs; - friend std::ostream& - operator<<(std::ostream&, const offset_commit_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ kvs: {}, coordinator: {} }}", kvs.size(), coordinator); + } auto serde_fields() { return std::tie(kvs, coordinator); } }; @@ -303,8 +318,9 @@ struct offset_commit_response cluster::errc errc{cluster::errc::success}; - friend std::ostream& - operator<<(std::ostream&, const offset_commit_response&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ errc: {} }}", errc); + } auto serde_fields() { return std::tie(errc); } }; @@ -329,7 +345,10 @@ struct offset_fetch_request auto serde_fields() { return std::tie(keys, coordinator); } - friend std::ostream& operator<<(std::ostream&, const offset_fetch_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ keys: {}, coordinator: {} }}", keys.size(), coordinator); + } }; struct offset_fetch_response @@ -347,8 +366,10 @@ struct offset_fetch_response auto serde_fields() { return std::tie(errors, results); } - friend std::ostream& - operator<<(std::ostream&, const offset_fetch_response&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ errc: {}, results: {} }}", errors.size(), results.size()); + } }; struct generate_report_request @@ -360,8 +381,9 @@ struct generate_report_request auto serde_fields() { return std::tie(); } - friend std::ostream& - operator<<(std::ostream&, const generate_report_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ }}"); + } }; struct generate_report_reply @@ -375,8 +397,10 @@ struct generate_report_reply auto serde_fields() { return std::tie(report); } - friend std::ostream& - operator<<(std::ostream&, const generate_report_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ transforms: {} }}", report.transforms.size()); + } model::cluster_transform_report report; }; @@ -392,7 +416,9 @@ struct list_commits_request auto serde_fields() { return std::tie(partition); } - friend std::ostream& operator<<(std::ostream&, const list_commits_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ partition: {} }}", partition); + } model::partition_id partition; }; @@ -410,7 +436,10 @@ struct list_commits_reply auto serde_fields() { return std::tie(errc, map); } - friend std::ostream& operator<<(std::ostream&, const list_commits_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{ ec: {}, map_size: {} }}", errc, map.size()); + } cluster::errc errc{cluster::errc::success}; model::transform_offsets_map map; @@ -435,8 +464,13 @@ struct delete_commits_request auto serde_fields() { return std::tie(partition, ids); } - friend std::ostream& - operator<<(std::ostream&, const delete_commits_request&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "{{ partition: {}, transform_ids_size: {} }}", + partition, + ids.size()); + } model::partition_id partition; absl::btree_set ids; @@ -455,7 +489,9 @@ struct delete_commits_reply auto serde_fields() { return std::tie(errc); } - friend std::ostream& operator<<(std::ostream&, const delete_commits_reply&); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{ ec: {} }}", errc); + } cluster::errc errc{cluster::errc::success}; }; diff --git a/src/v/utils/BUILD b/src/v/utils/BUILD index d41f17f07e7d3..3023a770dc2f5 100644 --- a/src/v/utils/BUILD +++ b/src/v/utils/BUILD @@ -15,8 +15,8 @@ redpanda_cc_library( "auto_fmt.h", ], deps = [ - "//src/v/reflection:to_tuple", - "@fmt", + "//src/v/base", + "//src/v/reflection:arity", ], ) diff --git a/src/v/utils/auto_fmt.h b/src/v/utils/auto_fmt.h index cf40e2e9c9562..250e6fedddcc4 100644 --- a/src/v/utils/auto_fmt.h +++ b/src/v/utils/auto_fmt.h @@ -8,9 +8,8 @@ // by the Apache License, Version 2.0 #pragma once -#include "reflection/to_tuple.h" - -#include +#include "base/format_to.h" +#include "reflection/arity.h" /// Base class for small value-types that adds automatic formatting of all /// fields. The code: @@ -22,93 +21,85 @@ /// S second; /// }; /// pair value{ .first = 42, .second = 137 }; -/// std::cout << "pair = (" << value << ")" << std::endl; +/// fmt::print("pair = ({})", value); /// \endcode /// -/// Will print: "pair = (42, 137)" +/// Will print: "pair = (42,137)" template -struct auto_fmt {}; - -/// Automagically prints any event object derived from auto_fmt -template -std::ostream& -operator<<(std::ostream& o, const auto_fmt& e) { - constexpr const auto a = reflection::arity() - 1; - static_assert(a <= 16, "Too many fields"); - constexpr char d = delimiter; - if constexpr (a == 0) { - return o; - } else if constexpr (a == 1) { - auto& [f0] = static_cast(e); - return o << f0; - } else if constexpr (a == 2) { - auto& [f0, f1] = static_cast(e); - return o << f0 << d << f1; - } else if constexpr (a == 3) { - auto& [f0, f1, f2] = static_cast(e); - return o << f0 << d << f1 << d << f2; - } else if constexpr (a == 4) { - auto& [f0, f1, f2, f3] = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3; - } else if constexpr (a == 5) { - auto& [f0, f1, f2, f3, f4] = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4; - } else if constexpr (a == 6) { - auto& [f0, f1, f2, f3, f4, f5] = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5; - } else if constexpr (a == 7) { - auto& [f0, f1, f2, f3, f4, f5, f6] = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6; - } else if constexpr (a == 8) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7] = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7; - } else if constexpr (a == 9) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8; - } else if constexpr (a == 10) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9; - } else if constexpr (a == 11) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9 << d << f10; - } else if constexpr (a == 12) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9 << d << f10 << d - << f11; - } else if constexpr (a == 13) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9 << d << f10 << d - << f11 << d << f12; - } else if constexpr (a == 14) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9 << d << f10 << d - << f11 << d << f12 << d << f13; - } else if constexpr (a == 15) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9 << d << f10 << d - << f11 << d << f12 << d << f13 << d << f14; - } else if constexpr (a == 16) { - auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15] - = static_cast(e); - return o << f0 << d << f1 << d << f2 << d << f3 << d << f4 << d << f5 - << d << f6 << d << f7 << d << f8 << d << f9 << d << f10 << d - << f11 << d << f12 << d << f13 << d << f14 << d << f15; +struct auto_fmt { + fmt::iterator format_to(fmt::iterator it) const { + constexpr auto a = reflection::arity() - 1; + static_assert(a <= 16, "Too many fields"); + auto format_fields = [&](const auto&... fields) { + bool first = true; + auto write = [&](const auto& f) { + if (!first) { + *it++ = delimiter; + } + first = false; + if constexpr ( + fmt::is_formattable< + std::remove_cvref_t>::value) { + it = fmt::format_to(it, "{}", f); + } else { + it = fmt::format_to(it, "{}", fmt::streamed(f)); + } + }; + (write(fields), ...); + }; + // clang-format off + if constexpr (a == 0) { + return it; + } else if constexpr (a == 1) { + auto& [f0] = static_cast(*this); + format_fields(f0); + } else if constexpr (a == 2) { + auto& [f0, f1] = static_cast(*this); + format_fields(f0, f1); + } else if constexpr (a == 3) { + auto& [f0, f1, f2] = static_cast(*this); + format_fields(f0, f1, f2); + } else if constexpr (a == 4) { + auto& [f0, f1, f2, f3] = static_cast(*this); + format_fields(f0, f1, f2, f3); + } else if constexpr (a == 5) { + auto& [f0, f1, f2, f3, f4] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4); + } else if constexpr (a == 6) { + auto& [f0, f1, f2, f3, f4, f5] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5); + } else if constexpr (a == 7) { + auto& [f0, f1, f2, f3, f4, f5, f6] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6); + } else if constexpr (a == 8) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7); + } else if constexpr (a == 9) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8); + } else if constexpr (a == 10) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); + } else if constexpr (a == 11) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10); + } else if constexpr (a == 12) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11); + } else if constexpr (a == 13) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12); + } else if constexpr (a == 14) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13); + } else if constexpr (a == 15) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14); + } else if constexpr (a == 16) { + auto& [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15] = static_cast(*this); + format_fields(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15); + } + // clang-format on + return it; } - return o; -} +}; diff --git a/src/v/utils/fixed_string.h b/src/v/utils/fixed_string.h index 1db32bee1e2df..a1915d2574df8 100644 --- a/src/v/utils/fixed_string.h +++ b/src/v/utils/fixed_string.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include #include @@ -81,3 +83,11 @@ operator+(const fixed_string& a, const fixed_string& b) noexcept { template // NOLINTNEXTLINE(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) fixed_string(const char (&)[N]) -> fixed_string; + +template +struct fmt::formatter> : fmt::formatter { + auto format(const fixed_string& s, fmt::format_context& ctx) const { + return fmt::formatter::format( + std::string_view(s), ctx); + } +}; diff --git a/src/v/utils/human.cc b/src/v/utils/human.cc index 6382615444d7b..a49ea90524361 100644 --- a/src/v/utils/human.cc +++ b/src/v/utils/human.cc @@ -9,41 +9,33 @@ #include "utils/human.h" -#include "base/seastarx.h" - -#include - -#include - namespace human { -std::ostream& operator<<(std::ostream& o, const ::human::latency& l) { +fmt::iterator latency::format_to(fmt::iterator it) const { constexpr static std::array units{"μs", "ms", "secs"}; static constexpr double step = 1000; - double x = l.value; + double x = value; for (const char* unit : units) { if (x <= step) { - fmt::print(o, "{:03.3f}{}", x, unit); - return o; + return fmt::format_to(it, "{:03.3f}{}", x, unit); } x /= step; } - return o << x << "unknown_units"; + return fmt::format_to(it, "{}unknown_units", x); } -std::ostream& operator<<(std::ostream& o, const ::human::bytes& l) { +fmt::iterator bytes::format_to(fmt::iterator it) const { constexpr static std::array units = { "bytes", "KiB", "MiB", "GiB", "TiB", "PiB"}; static constexpr double step = 1024; - double x = l.value; + double x = value; for (auto& unit : units) { if (x <= step) { - fmt::print(o, "{:03.3f}{}", x, unit); - return o; + return fmt::format_to(it, "{:03.3f}{}", x, unit); } x /= step; } - return o << x << "unknown_units"; + return fmt::format_to(it, "{}unknown_units", x); } } // namespace human diff --git a/src/v/utils/human.h b/src/v/utils/human.h index 5996dea850561..f10a08611b671 100644 --- a/src/v/utils/human.h +++ b/src/v/utils/human.h @@ -11,8 +11,9 @@ #pragma once +#include "base/format_to.h" + #include -#include /// \brief usage: fmt::format("{}", human::bytes(3234.234)); // or fmt::format("{}", human::latency(321.048)); @@ -21,7 +22,7 @@ struct bytes { explicit bytes(double x) : value(x) {} double value; - friend std::ostream& operator<<(std::ostream& o, const bytes&); + fmt::iterator format_to(fmt::iterator it) const; }; struct latency { // the input should be in microseconds @@ -32,6 +33,6 @@ struct latency { : value( std::chrono::duration_cast(d).count()) {} double value; - friend std::ostream& operator<<(std::ostream& o, const latency&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace human diff --git a/src/v/utils/named_type.h b/src/v/utils/named_type.h index 8918bd60eacac..f7097b3c3bb03 100644 --- a/src/v/utils/named_type.h +++ b/src/v/utils/named_type.h @@ -9,9 +9,11 @@ * by the Apache License, Version 2.0 */ #pragma once -#include + +#include "base/format_to.h" #include +#include #include #include #include @@ -26,6 +28,7 @@ template class base_named_type { public: using type = T; + using named_type_tag = void; constexpr base_named_type() = default; constexpr explicit base_named_type(const type& v) : _value(v) {} @@ -98,10 +101,11 @@ class base_named_type { return base_named_type(std::numeric_limits::max()); } - friend std::ostream& operator<<(std::ostream& o, const base_named_type& t) { - fmt::print(o, "{}", t._value); - return o; - }; + friend std::ostream& + operator<<(std::ostream& os, const base_named_type& t) { + fmt::print(os, "{}", t._value); + return os; + } friend std::istream& operator>>(std::istream& i, base_named_type& t) { return i >> t._value; @@ -114,6 +118,7 @@ template class base_named_type { public: using type = T; + using named_type_tag = void; static constexpr bool move_noexcept = std::is_nothrow_move_constructible::value; @@ -173,10 +178,12 @@ class base_named_type { constexpr operator const type&() const& { return _value; } constexpr operator type() && { return std::move(_value); } - friend std::ostream& operator<<(std::ostream& o, const base_named_type& t) { - fmt::print(o, "{}", t._value); - return o; - }; + friend std::ostream& operator<<(std::ostream& os, const base_named_type& t) + requires fmt::is_formattable::value + { + fmt::print(os, "{}", t._value); + return os; + } friend std::istream& operator>>(std::istream& i, base_named_type& t) { return i >> t._value; @@ -194,6 +201,27 @@ using named_type = detail::base_named_type< Tag, std::conditional_t, std::true_type, std::false_type>>; +template + struct fmt::formatter < T, + Char, std::enable_if_t < requires { + typename T::named_type_tag; + typename T::type; +} + // Named-type-derived classes may provide their own format_to for a + // domain-specific representation. In that case, let base/format_to.h's + // generic formatter handle them instead of collapsing back to the wrapped + // type's formatter. + && !fmt::HasFormatToMethod + && fmt::is_formattable::value >> + : fmt::formatter { + using base_t = fmt::formatter; + + template + auto format(const T& value, FormatContext& ctx) const { + return base_t::format(value(), ctx); + } +}; + namespace std { template struct hash> { diff --git a/src/v/utils/retry_chain_node.h b/src/v/utils/retry_chain_node.h index f5c36d3f71c45..f21a662682540 100644 --- a/src/v/utils/retry_chain_node.h +++ b/src/v/utils/retry_chain_node.h @@ -145,7 +145,6 @@ #include #include -#include #include #include diff --git a/src/v/utils/tests/human_test.cc b/src/v/utils/tests/human_test.cc index eeaa13a1a1bbc..72eeeff846993 100644 --- a/src/v/utils/tests/human_test.cc +++ b/src/v/utils/tests/human_test.cc @@ -11,7 +11,6 @@ #include #include -#include BOOST_AUTO_TEST_CASE(human_bytes) { BOOST_CHECK_EQUAL(fmt::format("{}", human::bytes(-1)), "-1.000bytes"); diff --git a/src/v/utils/tests/named_type_tests.cc b/src/v/utils/tests/named_type_tests.cc index be5a798fe5232..ee05bdad54570 100644 --- a/src/v/utils/tests/named_type_tests.cc +++ b/src/v/utils/tests/named_type_tests.cc @@ -13,8 +13,10 @@ #include #include +#include #include +#include #include BOOST_AUTO_TEST_CASE(named_type_basic) { @@ -83,13 +85,60 @@ BOOST_AUTO_TEST_CASE(named_type_rvalue_overload) { BOOST_AUTO_TEST_CASE(named_type_stream_operators) { using int_alias = named_type; int_alias value{123}; - std::stringstream stream; - fmt::print(stream, "{}", value); + std::stringstream stream{fmt::format("{}", value)}; int_alias from_str_value; stream >> from_str_value; BOOST_REQUIRE_EQUAL(from_str_value, value); } +struct fmt_only_type {}; +struct unformattable_type {}; + +template<> +struct fmt::formatter : formatter { + template + auto format(const fmt_only_type&, FormatContext& ctx) const { + return formatter::format("fmt-only", ctx); + } +}; + +BOOST_AUTO_TEST_CASE(named_type_8bit_stream_output_is_numeric) { + using byte_alias = named_type; + + auto out = fmt::format("{}", byte_alias{65}); + + BOOST_REQUIRE_EQUAL(out, "65"); +} + +BOOST_AUTO_TEST_CASE(named_type_streams_fmt_only_underlying_type) { + using fmt_only_alias + = named_type; + + auto out = fmt::format("{}", fmt_only_alias{fmt_only_type{}}); + + BOOST_REQUIRE_EQUAL(out, "fmt-only"); +} + +template +concept ostream_insertable = requires(std::ostream& os, const T& value) { + { os << value } -> std::same_as; +}; + +using unformattable_alias + = named_type; +static_assert(!ostream_insertable); + +BOOST_AUTO_TEST_CASE(named_type_formats_like_its_underlying_type) { + using int_alias = named_type; + BOOST_REQUIRE_EQUAL(fmt::format("{:>4}", int_alias{7}), " 7"); + + using monostate_alias + = named_type; + BOOST_REQUIRE_EQUAL( + fmt::format("{}", monostate_alias{}), + fmt::format("{}", std::monostate{})); +} + static_assert( !std::equality_comparable_with< named_type, diff --git a/src/v/utils/to_string.cc b/src/v/utils/to_string.cc index 91224cd6e0f5d..2b5635317b61e 100644 --- a/src/v/utils/to_string.cc +++ b/src/v/utils/to_string.cc @@ -12,18 +12,18 @@ #include "utils/to_string.h" fmt::format_context::iterator fmt::formatter::format( - const std::monostate&, format_context& ctx) { + const std::monostate&, format_context& ctx) const { return fmt::format_to(ctx.out(), "{{}}"); } fmt::format_context::iterator fmt::formatter::format( - const absl::Duration& d, fmt::format_context& ctx) { + const absl::Duration& d, fmt::format_context& ctx) const { std::string s = absl::FormatDuration(d); return fmt::format_to(ctx.out(), "{}", s); } fmt::format_context::iterator fmt::formatter::format( - const absl::Time& t, fmt::format_context& ctx) { + const absl::Time& t, fmt::format_context& ctx) const { std::string s = absl::FormatTime(t); return fmt::format_to(ctx.out(), "{}", s); } diff --git a/src/v/utils/to_string.h b/src/v/utils/to_string.h index 0f7066fa120c6..c63c8cfd6a455 100644 --- a/src/v/utils/to_string.h +++ b/src/v/utils/to_string.h @@ -52,7 +52,9 @@ template struct fmt::formatter> { using type = ss::chunked_fifo; - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { + return ctx.begin(); + } template typename FormatContext::iterator @@ -132,28 +134,32 @@ std::ostream& operator<<(std::ostream& o, const absl::btree_set& s) { template<> struct fmt::formatter { - constexpr format_parse_context::iterator parse(format_parse_context& ctx) { + constexpr format_parse_context::iterator + parse(format_parse_context& ctx) const { return ctx.begin(); } format_context::iterator - format(const absl::Time& t, fmt::format_context& ctx); + format(const absl::Time& t, fmt::format_context& ctx) const; }; template<> struct fmt::formatter { - constexpr format_parse_context::iterator parse(format_parse_context& ctx) { + constexpr format_parse_context::iterator + parse(format_parse_context& ctx) const { return ctx.begin(); } format_context::iterator - format(const absl::Duration& d, fmt::format_context& ctx); + format(const absl::Duration& d, fmt::format_context& ctx) const; }; template<> struct fmt::formatter { - constexpr format_parse_context::iterator parse(format_parse_context& ctx) { + constexpr format_parse_context::iterator + parse(format_parse_context& ctx) const { return ctx.begin(); } - format_context::iterator format(const std::monostate&, format_context& ctx); + format_context::iterator + format(const std::monostate&, format_context& ctx) const; }; diff --git a/src/v/utils/tristate.h b/src/v/utils/tristate.h index d0164551bf2a5..aaaf10fde3f82 100644 --- a/src/v/utils/tristate.h +++ b/src/v/utils/tristate.h @@ -11,10 +11,9 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" -#include - #include #include @@ -112,17 +111,15 @@ class tristate { return lhs._value >= rhs._value; } - friend std::ostream& operator<<(std::ostream& o, tristate t) { - if (t.is_disabled()) { - fmt::print(o, "{{disabled}}"); - return o; + fmt::iterator format_to(fmt::iterator it) const { + if (is_disabled()) { + return fmt::format_to(it, "{{disabled}}"); } - if (t.has_optional_value()) { - fmt::print(o, "{{{}}}", t.value()); - return o; + if (has_optional_value()) { + return fmt::format_to(it, "{{{}}}", value()); } - return o << "{{nullopt}}"; - }; + return fmt::format_to(it, "{{nullopt}}"); + } std::optional& get_optional() { return std::get>(_value); diff --git a/src/v/utils/unresolved_address.h b/src/v/utils/unresolved_address.h index 2283edf9f85c7..40115d3bf9055 100644 --- a/src/v/utils/unresolved_address.h +++ b/src/v/utils/unresolved_address.h @@ -10,6 +10,7 @@ */ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "serde/envelope.h" @@ -18,7 +19,6 @@ #include #include -#include namespace net { @@ -58,13 +58,11 @@ class unresolved_address /// format static unresolved_address from_string(std::string_view maybe_address); -private: - friend std::ostream& - operator<<(std::ostream& o, const unresolved_address& s) { - fmt::print(o, "{{host: {}, port: {}}}", s.host(), s.port()); - return o; + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{host: {}, port: {}}}", host(), port()); } +private: ss::sstring _host; uint16_t _port{0}; inet_family _family; diff --git a/src/v/utils/uuid.cc b/src/v/utils/uuid.cc index 9cc36cce8f14c..2e600334764d9 100644 --- a/src/v/utils/uuid.cc +++ b/src/v/utils/uuid.cc @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -39,8 +38,8 @@ uuid_t uuid_t::from_string(std::string_view str) { return uuid_t(gen(str.begin(), str.end())); } -std::ostream& operator<<(std::ostream& os, const uuid_t& u) { - return os << fmt::format("{}", u._uuid); +fmt::iterator uuid_t::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{}", boost::uuids::to_string(_uuid)); } std::istream& operator>>(std::istream& is, uuid_t& u) { diff --git a/src/v/utils/uuid.h b/src/v/utils/uuid.h index db4eaa5c45376..d5c4c4bc11ee9 100644 --- a/src/v/utils/uuid.h +++ b/src/v/utils/uuid.h @@ -9,11 +9,13 @@ #pragma once #include "absl/hash/hash.h" +#include "base/format_to.h" #include "base/seastarx.h" #include #include +#include #include #include @@ -42,7 +44,8 @@ class uuid_t { return {_uuid.begin(), _uuid.end()}; } - friend std::ostream& operator<<(std::ostream& os, const uuid_t& u); + fmt::iterator format_to(fmt::iterator it) const; + friend std::istream& operator>>(std::istream& is, uuid_t& u); friend bool operator==(const uuid_t& u, const uuid_t& v) = default; friend std::strong_ordering operator<=>(const uuid_t& u, const uuid_t& v); @@ -76,3 +79,14 @@ bool operator<(const uuid_t& l, const uuid_t& r); // Returns the given UUID incremented by 1. // Returns nullopt on overflow (all 0xFF). std::optional next_uuid(const uuid_t& u); + +template<> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) const { + return ctx.begin(); + } + template + auto format(const boost::uuids::uuid& v, FormatContext& ctx) const { + return fmt::format_to(ctx.out(), "{}", boost::uuids::to_string(v)); + } +}; diff --git a/src/v/v8_engine/data_policy.h b/src/v/v8_engine/data_policy.h index cb2d70600fea2..61487d3929353 100644 --- a/src/v/v8_engine/data_policy.h +++ b/src/v/v8_engine/data_policy.h @@ -10,6 +10,7 @@ #pragma once +#include "base/format_to.h" #include "base/seastarx.h" #include "base/vassert.h" #include "reflection/adl.h" @@ -40,8 +41,13 @@ struct data_policy ss::sstring fn_name; ss::sstring sct_name; - friend std::ostream& - operator<<(std::ostream& os, const data_policy& datapolicy); + fmt::iterator format_to(fmt::iterator it) const { + return fmt::format_to( + it, + "function_name: {} script_name: {}", + function_name(), + script_name()); + } }; } // namespace v8_engine @@ -67,17 +73,3 @@ struct adl { }; } // namespace reflection - -namespace v8_engine { - -inline std::ostream& -operator<<(std::ostream& os, const data_policy& datapolicy) { - fmt::print( - os, - "function_name: {} script_name: {}", - datapolicy.function_name(), - datapolicy.script_name()); - return os; -} - -} // namespace v8_engine diff --git a/src/v/wasm/allocator.cc b/src/v/wasm/allocator.cc index ebbd0d45934ef..ddb35a671d93f 100644 --- a/src/v/wasm/allocator.cc +++ b/src/v/wasm/allocator.cc @@ -145,10 +145,8 @@ void stack_allocator::deallocate(stack_memory mem) { _memory_pool.push_back(std::move(mem)); } -std::ostream& operator<<(std::ostream& os, const stack_bounds& bounds) { - return os << ss::format( - "{{.top = {}, .bottom = {}}}", - fmt::ptr(bounds.top), - fmt::ptr(bounds.bottom)); +fmt::iterator stack_bounds::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{.top = {}, .bottom = {}}}", fmt::ptr(top), fmt::ptr(bottom)); } } // namespace wasm diff --git a/src/v/wasm/allocator.h b/src/v/wasm/allocator.h index 177b46dd324d0..11f03d057a05a 100644 --- a/src/v/wasm/allocator.h +++ b/src/v/wasm/allocator.h @@ -11,6 +11,7 @@ #pragma once #include "absl/container/btree_set.h" +#include "base/format_to.h" #include "base/seastarx.h" #include @@ -120,7 +121,7 @@ struct stack_bounds { uint8_t* bottom; auto operator<=>(const stack_bounds&) const = default; - friend std::ostream& operator<<(std::ostream&, const stack_bounds&); + fmt::iterator format_to(fmt::iterator it) const; }; /** diff --git a/src/v/wasm/ffi.cc b/src/v/wasm/ffi.cc index 3e6c0db365c45..dd9624181c670 100644 --- a/src/v/wasm/ffi.cc +++ b/src/v/wasm/ffi.cc @@ -184,16 +184,4 @@ array reader::slice_remainder() const { return _input.subspan(_offset); } -std::ostream& operator<<(std::ostream& o, val_type vt) { - switch (vt) { - case val_type::i32: - o << "i32"; - break; - case val_type::i64: - o << "i64"; - break; - } - return o; -} - } // namespace wasm::ffi diff --git a/src/v/wasm/ffi.h b/src/v/wasm/ffi.h index ffeca52bba999..ec64f59cc0a28 100644 --- a/src/v/wasm/ffi.h +++ b/src/v/wasm/ffi.h @@ -11,6 +11,7 @@ #pragma once +#include "base/format_to.h" #include "base/type_traits.h" #include "base/vassert.h" #include "bytes/bytes.h" @@ -207,7 +208,15 @@ class memory { /** The values we support passing via FFI right now. */ enum class val_type { i32, i64 }; -std::ostream& operator<<(std::ostream& o, val_type vt); + +inline fmt::iterator format_to(val_type vt, fmt::iterator out) { + switch (vt) { + case val_type::i32: + return fmt::format_to(out, "i32"); + case val_type::i64: + return fmt::format_to(out, "i64"); + } +} namespace detail { diff --git a/src/v/wasm/parser/parser.cc b/src/v/wasm/parser/parser.cc index d8bb6640e849d..0471c1b1824f1 100644 --- a/src/v/wasm/parser/parser.cc +++ b/src/v/wasm/parser/parser.cc @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -56,13 +55,12 @@ std::string_view to_string(const wasm::val_type& vt) { } } // namespace -std::ostream& operator<<(std::ostream& os, const function_signature& sig) { - fmt::print( - os, +fmt::iterator function_signature::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{params:[{}],results:[{}]}}", - fmt::join(sig.parameters | std::views::transform(to_string), ","), - fmt::join(sig.results | std::views::transform(to_string), ",")); - return os; + fmt::join(parameters | std::views::transform(to_string), ","), + fmt::join(results | std::views::transform(to_string), ",")); } } // namespace wasm @@ -70,55 +68,46 @@ std::ostream& operator<<(std::ostream& os, const function_signature& sig) { namespace wasm::parser { namespace declaration { -std::ostream& operator<<(std::ostream& os, const limits& l) { - fmt::print(os, "{{min:{},max:{}}}", l.min, l.max); - return os; +fmt::iterator limits::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{min:{},max:{}}}", min, max); } -std::ostream& operator<<(std::ostream& os, const function& f) { - fmt::print(os, "{{signature:{}}}", f.signature); - return os; +fmt::iterator function::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{signature:{}}}", signature); } -std::ostream& operator<<(std::ostream& os, const table& t) { - fmt::print(os, "{{reftype:{},limits:{}}}", to_string(t.reftype), t.limits); - return os; +fmt::iterator table::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{reftype:{},limits:{}}}", to_string(reftype), limits); } -std::ostream& operator<<(std::ostream& os, const memory& m) { - fmt::print(os, "{{limits:{}}}", m.limits); - return os; +fmt::iterator memory::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{limits:{}}}", limits); } -std::ostream& operator<<(std::ostream& os, const global& m) { - fmt::print(os, "{{valtype:{},mut:{}}}", to_string(m.valtype), m.is_mutable); - return os; +fmt::iterator global::format_to(fmt::iterator it) const { + return fmt::format_to( + it, "{{valtype:{},mut:{}}}", to_string(valtype), is_mutable); } } // namespace declaration -std::ostream& operator<<(std::ostream& os, const module_import& m_import) { +fmt::iterator module_import::format_to(fmt::iterator it) const { std::visit( [&](auto desc) { - fmt::print( - os, - "{{name:{}/{},desc:{}}}", - m_import.module_name, - m_import.item_name, - desc); + it = fmt::format_to( + it, "{{name:{}/{},desc:{}}}", module_name, item_name, desc); }, - m_import.description); - return os; + description); + return it; } -std::ostream& operator<<(std::ostream& os, const module_export& m_export) { +fmt::iterator module_export::format_to(fmt::iterator it) const { std::visit( [&](auto desc) { - fmt::print(os, "{{name:{},desc:{}}}", m_export.item_name, desc); + it = fmt::format_to(it, "{{name:{},desc:{}}}", item_name, desc); }, - m_export.description); - return os; + description); + return it; } -std::ostream& operator<<(std::ostream& os, const module_declarations& m_decls) { - fmt::print( - os, "{{imports:{},exports:{}}}", m_decls.imports, m_decls.exports); - return os; +fmt::iterator module_declarations::format_to(fmt::iterator it) const { + return fmt::format_to(it, "{{imports:{},exports:{}}}", imports, exports); } namespace { diff --git a/src/v/wasm/parser/parser.h b/src/v/wasm/parser/parser.h index 09c8f0b1d899c..44bdbc94d7bff 100644 --- a/src/v/wasm/parser/parser.h +++ b/src/v/wasm/parser/parser.h @@ -9,6 +9,7 @@ * by the Apache License, Version 2.0 */ +#include "base/format_to.h" #include "base/seastarx.h" #include "bytes/iobuf.h" #include "container/chunked_vector.h" @@ -43,7 +44,7 @@ struct function_signature { std::vector results; bool operator==(const function_signature&) const = default; - friend std::ostream& operator<<(std::ostream&, const function_signature&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace wasm @@ -59,7 +60,7 @@ struct limits { uint32_t max = std::numeric_limits::max(); bool operator==(const limits&) const = default; - friend std::ostream& operator<<(std::ostream&, const limits&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -69,7 +70,7 @@ struct function { function_signature signature; bool operator==(const function&) const = default; - friend std::ostream& operator<<(std::ostream&, const function&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -80,7 +81,7 @@ struct table { limits limits; bool operator==(const table&) const = default; - friend std::ostream& operator<<(std::ostream&, const table&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -90,7 +91,7 @@ struct memory { limits limits; bool operator==(const memory&) const = default; - friend std::ostream& operator<<(std::ostream&, const memory&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -101,7 +102,7 @@ struct global { bool is_mutable; bool operator==(const global&) const = default; - friend std::ostream& operator<<(std::ostream&, const global&); + fmt::iterator format_to(fmt::iterator it) const; }; } // namespace declaration @@ -125,7 +126,7 @@ struct module_import { import_description description; bool operator==(const module_import&) const = default; - friend std::ostream& operator<<(std::ostream&, const module_import&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -146,7 +147,7 @@ struct module_export { export_description description; bool operator==(const module_export&) const = default; - friend std::ostream& operator<<(std::ostream&, const module_export&); + fmt::iterator format_to(fmt::iterator it) const; }; /** @@ -157,7 +158,7 @@ struct module_declarations { chunked_vector imports; bool operator==(const module_declarations&) const = default; - friend std::ostream& operator<<(std::ostream&, const module_declarations&); + fmt::iterator format_to(fmt::iterator it) const; }; /** diff --git a/src/v/wasm/wasmtime.cc b/src/v/wasm/wasmtime.cc index f030cd528bdc0..e1e6d792b2f06 100644 --- a/src/v/wasm/wasmtime.cc +++ b/src/v/wasm/wasmtime.cc @@ -50,8 +50,6 @@ #include #include -#include - #include #include #include diff --git a/tests/rptest/tests/acls_test.py b/tests/rptest/tests/acls_test.py index 3e47a1066bb3e..42000a0d34ebb 100644 --- a/tests/rptest/tests/acls_test.py +++ b/tests/rptest/tests/acls_test.py @@ -524,7 +524,7 @@ def test_security_feature_migration(self, authn_method: str): ) # Check that the authn_method was set on all brokers - pattern = "Started Kafka API server.*:{" + authn_method + "}.*" + pattern = "Started Kafka API server.*:" + authn_method assert self.redpanda.search_log_all(pattern) # Once restart is complete, check permissions should succeed when authn_method diff --git a/tests/rptest/tests/compatibility/sarama_produce_test.py b/tests/rptest/tests/compatibility/sarama_produce_test.py index 4cb888fc2458f..56e39c6afc04e 100644 --- a/tests/rptest/tests/compatibility/sarama_produce_test.py +++ b/tests/rptest/tests/compatibility/sarama_produce_test.py @@ -90,11 +90,11 @@ class SaramaLegacyProduceTest(RedpandaTest): expected_log_lines = defaultdict(dict) expected_log_lines[ValidationMode.RELAXED][False] = None expected_log_lines[ValidationMode.RELAXED][True] = ( - "Produced batch for partition {kafka/topic/0} has max_timestamp left unset ({-1}) by client (client_id: {sarama}). Decompressing batch and setting max_timestamp manually since 'kafka_produce_batch_validation' is set to 'relaxed'. It is strongly recommended that you update your client to set the max_timestamp when producing" + "Produced batch for partition {kafka/topic/0} has max_timestamp left unset ({-1}) by client (client_id: sarama). Decompressing batch and setting max_timestamp manually since 'kafka_produce_batch_validation' is set to 'relaxed'. It is strongly recommended that you update your client to set the max_timestamp when producing" ) expected_log_lines[ValidationMode.LEGACY][False] = None expected_log_lines[ValidationMode.LEGACY][True] = ( - "Produced batch for partition {kafka/topic/0} has max_timestamp left unset ({-1}) by client (client_id: {sarama}). Accepting batch since 'kafka_produce_batch_validation' is set to 'legacy'. It is strongly recommended that you update your client to set the max_timestamp when producing" + "Produced batch for partition {kafka/topic/0} has max_timestamp left unset ({-1}) by client (client_id: sarama). Accepting batch since 'kafka_produce_batch_validation' is set to 'legacy'. It is strongly recommended that you update your client to set the max_timestamp when producing" ) expected_log_lines[ValidationMode.STRICT][False] = None expected_log_lines[ValidationMode.STRICT][True] = None diff --git a/tests/rptest/tests/rpk_start_test.py b/tests/rptest/tests/rpk_start_test.py index bd42a9f439fcf..2782653097266 100644 --- a/tests/rptest/tests/rpk_start_test.py +++ b/tests/rptest/tests/rpk_start_test.py @@ -291,7 +291,7 @@ def start_cluster(node): # We check that rpc_server_tls is enabled on start: assert self.redpanda.search_log_node( - node, "redpanda.rpc_server_tls:{ enabled: 1" + node, "redpanda.rpc_server_tls:{ enabled: true" ) self.redpanda.for_nodes(self.redpanda.nodes, start_cluster) @@ -348,7 +348,7 @@ def setup_and_start(node): # Check that we don't enable rpc and print a warning assert self.redpanda.search_log_all( - "redpanda.rpc_server_tls:{ enabled: 0", self.redpanda.nodes + "redpanda.rpc_server_tls:{ enabled: false", self.redpanda.nodes ) assert self.redpanda.search_log_all( "WARNING: Due to an old rpk bug, your redpanda.yaml's redpanda.rpc_server_tls property is an array", @@ -388,7 +388,7 @@ def start_cluster(node): # to a topic. self.redpanda.for_nodes(self.redpanda.nodes, start_cluster) assert self.redpanda.search_log_all( - "redpanda.rpc_server_tls:{ enabled: 0", self.redpanda.nodes + "redpanda.rpc_server_tls:{ enabled: false", self.redpanda.nodes ) try: @@ -418,7 +418,7 @@ def start_cluster(node): self.redpanda.stop() self.redpanda.for_nodes(self.redpanda.nodes, start_cluster) assert self.redpanda.search_log_all( - "redpanda.rpc_server_tls:{ enabled: 1", self.redpanda.nodes + "redpanda.rpc_server_tls:{ enabled: true", self.redpanda.nodes ) try: