#pragma once #include #include #include #include #include #include #include #include namespace vptyp { static constexpr std::string_view configErrorMsg = "Bruh, incorrect configuration"; class MetricsLogger { using map_type = std::unordered_map>; public: virtual ~MetricsLogger(); MetricsLogger(); explicit MetricsLogger(std::ostream& out); MetricsLogger(MetricsLogger&) = delete; MetricsLogger(MetricsLogger&&) = delete; MetricsLogger& operator=(MetricsLogger&) = delete; MetricsLogger& operator=(MetricsLogger&&) = delete; /// @brief you should make logger configuration before logging /// After logger will be ready for holding metrics. /// @return success or not (already configured) bool configure(const std::vector& d); template void add(const std::string& field, Metric metric) requires(std::is_arithmetic_v) { refs.fetch_add(1, std::memory_order_release); map_type* locked = active.load(std::memory_order_acquire); auto it = locked->find(field); if (it == locked->end()) { refs.fetch_sub(1, std::memory_order_release); throw configErrorMsg; // additional 30ns on bench ? } it->second = metric; refs.fetch_sub(1, std::memory_order_release); } bool isConfigured() { return configured == CONFIGURED; } private: // helper class for handling worker thread class Worker; friend Worker; enum Configuration { NOT_CONFIGURED, CONFIG_IN_PROGRESS, CONFIGURED }; std::atomic configured{NOT_CONFIGURED}; std::unique_ptr worker; std::unique_ptr m1, m2; std::atomic active; std::atomic refs{0}; // degradation on worker side (waiting for no // one to be in refs section) }; } // namespace vptyp