9 #include <spdlog/spdlog.h>
11 #include <unordered_map>
20 #include "phonebook.hpp"
31 record_header(
const std::string& name_, std::vector<std::pair<std::string, const std::type_info&>> columns_)
32 :
id{std::hash<std::string>{}(name_)}
34 , columns{std::move(columns_)} { }
45 if (name != other.name || columns.size() != other.columns.size() ||
id != other.id) {
48 for (std::size_t i = 0; i < columns.size(); ++i) {
49 if (columns[i] != other.columns[i]) {
57 return !(*
this == other);
60 [[nodiscard]] std::size_t get_id()
const {
64 [[nodiscard]]
const std::string& get_name()
const {
68 [[nodiscard]]
const std::string& get_column_name(
unsigned column)
const {
69 return columns[column].first;
72 [[nodiscard]]
const std::type_info& get_column_type(
unsigned column)
const {
73 return columns[column].second;
76 [[nodiscard]]
unsigned get_columns()
const {
77 return columns.size();
80 [[nodiscard]] std::string to_string()
const {
81 std::string ret = std::string{
"record_header "} + name + std::string{
" { "};
82 for (
const auto& pair : columns) {
83 ret += std::string{pair.second.name()} + std::string{
" "} + pair.first + std::string{
"; "};
85 ret.erase(ret.size() - 2);
86 ret += std::string{
" }"};
93 const std::vector<std::pair<std::string, const std::type_info&>> columns;
112 if (&other !=
this) {
123 bool is_used()
const {
127 void mark_used()
const {
131 void mark_unused()
const {
148 , values(std::move(values_)) {
151 if (values.size() != rh->get().get_columns()) {
152 spdlog::get(
"illixr")->error(
"[record_logger] {} elements passed, but rh for {} only specifies {}.", values.size(),
153 rh->get().get_name(), rh->get().get_columns());
156 for (std::size_t column = 0; column < values.size(); ++column) {
157 if (values[column].type() != rh->get().get_column_type(column)) {
158 spdlog::get(
"illixr")->error(
"[record_logger] Caller got wrong type for column {} of {}.", column,
159 rh->get().get_name());
160 spdlog::get(
"illixr")->error(
"[record_logger] Caller passed: {}; record_header specifies: {}",
161 values[column].type().name(), rh->get().get_column_type(column).name());
172 if (rh && !data_use_indicator_.is_used()) {
173 spdlog::get(
"illixr")->error(
"[record_logger] Record was deleted without being logged.");
180 T get_value(
unsigned column)
const {
183 data_use_indicator_.mark_used();
184 if (rh->get().get_column_type(column) !=
typeid(T)) {
185 std::ostringstream ss;
186 ss <<
"Caller column type for " << column <<
" of " << rh->get().get_name() <<
". "
187 <<
"Caller passed: " <<
typeid(T).name() <<
"; "
188 <<
"record_header specifies: " << rh->get().get_column_type(column).name() <<
". ";
189 throw std::runtime_error{ss.str()};
192 return std::any_cast<T>(values[column]);
200 void mark_used()
const {
203 data_use_indicator_.mark_used();
211 std::optional<std::reference_wrapper<const record_header>> rh;
212 std::vector<std::any> values;
240 virtual void log(
const std::vector<record>& rs) {
241 for (
const record& r : rs) {
267 std::size_t
get(std::size_t namespace_ = 0, std::size_t subnamespace = 0, std::size_t subsubnamespace = 0) {
268 if (guid_starts[namespace_][subnamespace].count(subsubnamespace) == 0) {
269 guid_starts[namespace_][subnamespace][subsubnamespace].store(1);
271 return guid_starts[namespace_][subnamespace][subsubnamespace]++;
275 std::unordered_map<std::size_t, std::unordered_map<std::size_t, std::unordered_map<std::size_t, std::atomic<std::size_t>>>>
279 static std::chrono::milliseconds LOG_BUFFER_DELAY{1000};
309 std::shared_ptr<record_logger> logger;
310 std::chrono::time_point<std::chrono::high_resolution_clock> last_log;
311 std::vector<record> buffer;
315 : logger{std::move(logger_)}
316 , last_log{std::chrono::high_resolution_clock::now()} { }
332 if (&r.get_record_header() != &buffer[0].get_record_header() &&
333 r.get_record_header() == buffer[0].get_record_header()) {
334 spdlog::get(
"illixr")->error(
"[record_logger] Tried to push a record of type {} to a record logger for type {}",
335 r.get_record_header().to_string(), buffer[0].get_record_header().to_string());
347 if (std::chrono::high_resolution_clock::now() > last_log + LOG_BUFFER_DELAY) {
357 std::vector<record> buffer2;
358 buffer.swap(buffer2);
359 logger->log(buffer2);
360 last_log = std::chrono::high_resolution_clock::now();
364 explicit operator bool()
const {
A helper class that lets one dynamically determine if some data gets used.
Definition: record_logger.hpp:101
This class generates unique IDs.
Definition: record_logger.hpp:262
std::size_t get(std::size_t namespace_=0, std::size_t subnamespace=0, std::size_t subsubnamespace=0)
Generate a number, unique from other calls to the same namespace/subnamespace/subsubnamepsace.
Definition: record_logger.hpp:267
A 'service' that can be registered in the phonebook.
Definition: phonebook.hpp:86
Coalesces logs of the same type to be written back as a single-transaction.
Definition: record_logger.hpp:307
void log(const record &r)
Appends a log to the buffer, which will eventually be written.
Definition: record_logger.hpp:325
void flush()
Flush buffer of logs to the underlying logger.
Definition: record_logger.hpp:355
void maybe_flush()
Use internal decision process, and possibly trigger flush.
Definition: record_logger.hpp:346
The ILLIXR logging service for structured records.
Definition: record_logger.hpp:226
virtual void log(const record &r)=0
Writes one log record.
virtual void log(const std::vector< record > &rs)
Writes many of the same type of log record.
Definition: record_logger.hpp:240
This class represents a tuple of fields which get logged by record_logger.
Definition: record_logger.hpp:144
RAC_ERRNO_MSG.
Definition: data_format.hpp:15
void abort(const std::string &msg="", [[maybe_unused]] const int error_val=1)
Exits the application during a fatal error.
Definition: error_util.hpp:61