2019-06-26 16:11:22 -04:00
|
|
|
#include "LogUtils.h"
|
|
|
|
#include "LogSinks.h"
|
|
|
|
#include <iomanip>
|
|
|
|
#include <fstream>
|
|
|
|
#include <map>
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <experimental/filesystem>
|
|
|
|
#include <StringVariable.h>
|
|
|
|
#include <mutex>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace std::chrono;
|
|
|
|
using namespace spdlog;
|
|
|
|
namespace fs = std::experimental::filesystem;
|
|
|
|
|
|
|
|
#define ASYNC_LOG
|
|
|
|
namespace logger {
|
|
|
|
recursive_mutex loggerLock;
|
2019-09-22 10:57:01 -04:00
|
|
|
map<size_t, std::shared_ptr<spdlog::logger>> loggers;
|
2019-06-26 16:11:22 -04:00
|
|
|
shared_ptr<LoggerConfig> logConfig;
|
|
|
|
shared_ptr<::logger::TerminalSink> terminalSink;
|
|
|
|
shared_ptr<::logger::CostumeFormatter> costumeFormatter;
|
|
|
|
|
|
|
|
shared_ptr<spdlog::logger> logger(int serverId) {
|
|
|
|
if(!::logger::currentConfig())
|
|
|
|
return nullptr;
|
|
|
|
|
2019-09-22 10:57:01 -04:00
|
|
|
size_t group = 0;
|
2019-06-26 16:11:22 -04:00
|
|
|
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
|
2019-10-13 14:26:12 -04:00
|
|
|
if(group != 0 && group != -1)
|
2019-06-26 16:11:22 -04:00
|
|
|
logger(0)->debug("Creating new grouped logger for group {}", group);
|
|
|
|
|
|
|
|
vector<spdlog::sink_ptr> 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<std::string> value) -> std::string {
|
|
|
|
auto pattern = !value.empty() ? value[0] : "%Y-%m-%d_%H:%M:%S";
|
|
|
|
|
|
|
|
auto secs = duration_cast<seconds>(logConfig->timestamp.time_since_epoch()).count();
|
2019-09-22 10:57:01 -04:00
|
|
|
tm* tm_info;
|
|
|
|
#ifdef WIN32
|
|
|
|
tm _tm_info{};
|
|
|
|
localtime_s(&_tm_info, &secs);
|
|
|
|
tm_info = &_tm_info;
|
|
|
|
#else
|
2019-06-26 16:11:22 -04:00
|
|
|
tm_info = localtime((time_t*) &secs);
|
2019-09-22 10:57:01 -04:00
|
|
|
#endif
|
2019-06-26 16:11:22 -04:00
|
|
|
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<ColoredFileSink>(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<TerminalSink>(sink)) {
|
|
|
|
sink->set_level(::logger::currentConfig()->terminalLevel);
|
|
|
|
} else if(dynamic_pointer_cast<ColoredFileSink>(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<LoggerConfig>& currentConfig() {
|
|
|
|
return logConfig;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void setup(const shared_ptr<LoggerConfig>& config) {
|
|
|
|
logConfig = config;
|
|
|
|
config->timestamp = system_clock::now();
|
|
|
|
|
|
|
|
terminalSink = make_shared<TerminalSink>();
|
|
|
|
terminalSink->set_level(::logger::currentConfig()->terminalLevel);
|
|
|
|
costumeFormatter = make_shared<CostumeFormatter>();
|
|
|
|
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<TerminalSink>(sink))
|
|
|
|
sink->set_level(::logger::currentConfig()->terminalLevel);
|
|
|
|
else if(dynamic_pointer_cast<ColoredFileSink>(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());
|
|
|
|
}
|