humanus.cpp/include/logger.h

181 lines
6.0 KiB
C++

#ifndef HUMANUSlogger_H
#define HUMANUSlogger_H
#include "config.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/dist_sink.h"
#include <string>
#include <filesystem>
#include <mutex>
#include <unordered_set>
namespace humanus {
static spdlog::level::level_enum _print_level = spdlog::level::info;
static spdlog::level::level_enum _logfile_level = spdlog::level::debug;
extern std::shared_ptr<spdlog::logger> set_log_level(spdlog::level::level_enum print_level, spdlog::level::level_enum logfile_level);
class SessionSink : public spdlog::sinks::base_sink<std::mutex> {
private:
inline static std::unordered_map<std::string, std::vector<std::string>> buffers_; // session_id -> buffer
inline static std::unordered_map<std::string, std::vector<std::string>> histories_; // session_id -> history
inline static std::unordered_map<std::string, std::string> sessions_; // thread_id -> session_id
inline static std::mutex mutex_;
SessionSink() = default;
SessionSink(const SessionSink&) = delete;
SessionSink& operator=(const SessionSink&) = delete;
public:
~SessionSink() = default;
static std::shared_ptr<SessionSink> get_instance() {
static SessionSink instance;
static std::shared_ptr<SessionSink> shared_instance(&instance, [](SessionSink*){});
return shared_instance;
}
void sink_it_(const spdlog::details::log_msg& msg) override {
std::lock_guard<std::mutex> lock(mutex_);
if (sessions_.find(get_thread_id()) == sessions_.end()) { // Ignore messages if session_id is not set
return;
}
auto session_id = sessions_[get_thread_id()];
auto time_t = std::chrono::system_clock::to_time_t(msg.time);
auto tm = fmt::localtime(time_t);
std::string log_message = fmt::format("[{:%Y-%m-%d %H:%M:%S}] {}", tm, msg.payload);
buffers_[session_id].push_back(log_message);
}
void flush_() override {
std::lock_guard<std::mutex> lock(mutex_);
if (sessions_.find(get_thread_id()) == sessions_.end()) { // Ignore messages if session_id is not set
return;
}
auto session_id = sessions_[get_thread_id()];
if (!buffers_[session_id].empty()) {
histories_[session_id].insert(histories_[session_id].end(),
buffers_[session_id].begin(),
buffers_[session_id].end());
buffers_[session_id].clear();
}
}
inline std::string get_thread_id() {
std::stringstream ss;
ss << std::this_thread::get_id();
return ss.str();
}
void set_session_id(std::string session_id) {
if (session_id.empty()) {
throw std::invalid_argument("session_id is empty");
}
std::lock_guard<std::mutex> lock(mutex_);
sessions_[get_thread_id()] = session_id;
}
// Messages in buffer are flushed to the history and cleared from the buffer.
std::vector<std::string> get_buffer(const std::string& session_id) {
std::lock_guard<std::mutex> lock(mutex_);
if (buffers_.find(session_id) == buffers_.end()) {
throw std::invalid_argument("Invalid session_id: " + session_id);
}
std::vector<std::string> buffer = buffers_[session_id];
if (!buffers_[session_id].empty()) {
histories_[session_id].insert(histories_[session_id].end(),
buffers_[session_id].begin(),
buffers_[session_id].end());
buffers_[session_id].clear();
}
return buffer;
}
std::vector<std::string> get_history(const std::string& session_id) {
std::lock_guard<std::mutex> lock(mutex_);
if (histories_.find(session_id) == histories_.end()) {
throw std::invalid_argument("Invalid session_id: " + session_id);
}
return histories_[session_id];
}
void clear_buffer() {
std::lock_guard<std::mutex> lock(mutex_);
if (sessions_.find(get_thread_id()) == sessions_.end()) { // Ignore messages if session_id is not set
return;
}
auto session_id = sessions_[get_thread_id()];
if (!buffers_[session_id].empty()) {
histories_[session_id].insert(histories_[session_id].end(),
buffers_[session_id].begin(),
buffers_[session_id].end());
buffers_[session_id].clear();
}
}
void clear_history() {
std::lock_guard<std::mutex> lock(mutex_);
if (sessions_.find(get_thread_id()) == sessions_.end()) { // Ignore messages if session_id is not set
return;
}
auto session_id = sessions_[get_thread_id()];
histories_[session_id].clear();
}
void cleanup_session(const std::string& session_id) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto it = sessions_.begin(); it != sessions_.end();) {
if (it->second == session_id) {
it = sessions_.erase(it);
} else {
++it;
}
}
buffers_.erase(session_id);
histories_.erase(session_id);
}
std::vector<std::string> get_active_sessions() {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<std::string> result;
std::unordered_set<std::string> unique_sessions;
for (const auto& [thread_id, session_id] : sessions_) {
if (unique_sessions.insert(session_id).second) {
result.push_back(session_id);
}
}
return result;
}
};
static std::shared_ptr<spdlog::logger> logger = set_log_level(_print_level, _logfile_level);
} // namespace humanus
#endif