Skip to content

feature request: support quill_(format|encode)_as #813

@egnchen

Description

@egnchen

Is your feature request related to a problem? Please describe.
I use this library extensively and I wonder if there's some more convenient way to log UDT with this library.

For a relatively large/not trivially copyable UDT, typically we only need to log several essential fields.

eg.

struct SuperBigStruct {
std::map<std::string, int> superBigMap;
}

template<>
struct fmt::formatter<SuperBigStruct> {
  template <typename FCtx>
  auto format(const SuperBigStruct &obj, FCtx &ctx) const {
  return fmt::format(ctx.out(), "SuperBigStruct(size={})", obj.superBigMap.size());
  }
};

So logically if we want to log this UDT we only need to encode & decode one size_t (which is obj.superBigMap.size()), but this is impossible under the current design structure(which always requires you to encode and decode the entire struct).

It is possible to do this though:

struct SuperBigStruct {
std::map<std::string, int> superBigMap;
}

struct SuperBigStructFormatProxy {
  int size;
};

auto format_as(const SuperBigStruct &obj) { return SuperBigStructFormatProxy{obj.size()}; }

template<>
struct fmt::formatter<SuperBigStructFormatProxy> {
  template <typename FCtx>
  auto format(const SuperBigStructFormatProxy&obj, FCtx &ctx) const {
  return fmt::format(ctx.out(), "SuperBigStruct(size={})", obj.size);
  }
};

but this is too verbose.

Describe the solution you'd like

I think a way to automatically extract necessary fields from the format string to do encode/decode would be very nice.

I was browsing through the documentation of fmt the other day and I came across this std::make_format_args. Maybe we can support codec for the returned fmt::basic_format_args (which is essentially a list of references to the arguments).

So maybe something like this(but I don't know how to exactly implement it yet):

struct SuperBigStruct {
  std::map<std::string, int> superBigMap;
  int size;
  // here we define the format string and pack of args for the format string
  auto GetFormatArgs() const { return std::make_format_args(size); }
  static inline constexpr std::string_view kFmtStr{"SuperBigStruct(size={})"};
}

using ArgsType = std::invoke_result_t<&SuperBigStruct::GetFormatArgs, SuperBigStruct>;

auto format_as(const SuperBigStruct &obj) { return obj.GetFormatArgs(); }

// implement codec for the pack of args
// we get the list of args and encode those basic members one by one
// since basic_format_args is list of references, maybe we need to allocate & make the list of args point to
// the decoded value in backend thread.
template<>
quill::Codec<SuperBigStruct> : quill::FormatArgsCodec<ArgsType> {};

//  formatter for ArgsType
template<>
fmtquill::formatter<ArgsType> {
  template <typename FCtx>
  auto format(const ArgsType &args, FCtx &ctx) const {
  return fmt::vformat(ctx.out(), "SuperBigStruct(size={})", args);
  }
}

Additional context

This is just my immature two cents, I'm open to discussion.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions