#include "LogUtils.h" #include "LogSinks.h" #include #include #include #include #include #include #include using namespace std; using namespace std::chrono; using namespace spdlog; namespace fs = std::experimental::filesystem; #define ASYNC_LOG namespace logger { recursive_mutex loggerLock; map> loggers; shared_ptr logConfig; shared_ptr<::logger::TerminalSink> terminalSink; shared_ptr<::logger::CostumeFormatter> costumeFormatter; shared_ptr logger(int serverId) { if(!::logger::currentConfig()) return nullptr; int group = 0; if(::logger::currentConfig()->vs_group_size > 0 && serverId > 0) group = serverId / ::logger::currentConfig()->vs_group_size; else group = -1; if(loggers.count(group) == 0) { lock_guard lock(loggerLock); if(loggers.count(group) > 0) return loggers[group]; //Create a new logger if(group > 0){ logger(0)->debug("Creating new grouped logger for group {}", group); } vector sinks; string path; if(logConfig->logfileLevel != spdlog::level::off) { path = strvar::transform(logConfig->logPath, strvar::StringValue{"group", group != -1 ? to_string(group) : "general"}, strvar::FunctionValue("time", (strvar::FunctionValue::FValueFNEasy) [](std::deque value) -> std::string { auto pattern = !value.empty() ? value[0] : "%Y-%m-%d_%H:%M:%S"; tm* tm_info; auto secs = duration_cast(logConfig->timestamp.time_since_epoch()).count(); tm_info = localtime((time_t*) &secs); char timeBuffer[1024]; if(strftime(timeBuffer, 1024, pattern.c_str(), tm_info) == 0) { return string("string is longer than the buffer"); } return string(timeBuffer); }) ); auto logFile = fs::u8path(path); if(!logFile.parent_path().empty()) fs::create_directories(logFile.parent_path()); auto fileSink = make_shared(logFile.string(), 1024 * 1024 * 50, 12); sinks.push_back(fileSink); } else { path = "/dev/null (" + to_string(serverId) + ")"; } sinks.push_back(terminalSink); #ifdef ASYNC_LOG auto logger = create_async("Logger (" + path + ")", sinks.begin(), sinks.end(), 8192, async_overflow_policy::discard_log_msg, [](){}, std::chrono::milliseconds(500)); #else auto logger = create("Logger (" + path + ")", sinks.begin(), sinks.end()); #endif logger->set_formatter(costumeFormatter); for(const auto& sink : logger->sinks()) if(dynamic_pointer_cast(sink)) { sink->set_level(::logger::currentConfig()->terminalLevel); } else if(dynamic_pointer_cast(sink)) { sink->set_level(::logger::currentConfig()->logfileLevel); } else { sink->set_level(min(::logger::currentConfig()->logfileLevel, ::logger::currentConfig()->terminalLevel)); } logger->set_level(min(::logger::currentConfig()->logfileLevel, ::logger::currentConfig()->terminalLevel)); loggers[group] = logger; } return loggers[group]; } const std::shared_ptr& currentConfig() { return logConfig; } extern void setup(const shared_ptr& config) { logConfig = config; config->timestamp = system_clock::now(); terminalSink = make_shared(); terminalSink->set_level(::logger::currentConfig()->terminalLevel); costumeFormatter = make_shared(); logger(0)->debug("Log successfully started!"); } void updateLogLevels() { lock_guard lock(loggerLock); for(const auto& loggerEntry : loggers) { auto logger = loggerEntry.second; for(const auto& sink : logger->sinks()) if(dynamic_pointer_cast(sink)) sink->set_level(::logger::currentConfig()->terminalLevel); else if(dynamic_pointer_cast(sink)) sink->set_level(::logger::currentConfig()->logfileLevel); else sink->set_level(min(::logger::currentConfig()->logfileLevel, ::logger::currentConfig()->terminalLevel)); logger->set_level(min(::logger::currentConfig()->logfileLevel, ::logger::currentConfig()->terminalLevel)); } } void flush() { unique_lock lock(loggerLock); auto _loggers = loggers; lock.unlock(); for(const auto& loggerEntry : _loggers) { loggerEntry.second->flush(); } } extern void uninstall() { lock_guard lock(loggerLock); for(auto& loggerEntry : loggers) { loggerEntry.second->flush(); loggerEntry.second.reset(); } loggers.clear(); logConfig = nullptr; terminalSink = nullptr; costumeFormatter = nullptr; } } void hexDump(void *addr, int len, int pad,int columnLength, void (*print)(string)); void hexDump(void *addr, int len, int pad,int columnLength) { hexDump(addr, len, pad, columnLength, [](string str){ logMessage(0, "\n{}", str); }); } void hexDump(void *addr, int len, int pad,int columnLength, void (*print)(string)) { int i; uint8_t* buff = new uint8_t[pad+1]; unsigned char* pc = (unsigned char*)addr; if (len <= 0) { return; } stringstream line; line << uppercase << hex << setfill('0'); // Process every byte in the data. for (i = 0; i < len; i++) { // Multiple of 16 means new line (with line offset). if ((i % pad) == 0) { // Just don't print ASCII for the zeroth line. if (i != 0) { line << buff; print(line.str()); line = stringstream{}; line << hex; }; // Output the offset. line << setw(4) << i << " "; } if(i % columnLength == 0 && i % pad != 0){ line << "| "; } // Now the hex code for the specific character. line << setw(2) << (int) pc[i] << " "; // And store a printable ASCII character for later. if ((pc[i] < 0x20) || (pc[i] > 0x7e)) buff[i % pad] = '.'; else buff[i % pad] = pc[i]; buff[(i % pad) + 1] = '\0'; } // Pad out last line if not exactly 16 characters. while ((i % pad) != 0) { line << " "; i++; } line << buff; delete[] buff; print(line.str()); line = stringstream{}; line << "Length: " << dec << len << " Addr: " << addr; print(line.str()); }